fix: guard DC writes in JPEGDEC MCU_SKIP path (#2058)

EIGHT_BIT_GRAYSCALE decode of a 3-component progressive JPEG calls
JPEGDecodeMCU_P with MCU_SKIP for Cb and Cr after every Y MCU. The
existing safe-pMCU patch redirects the wild pointer to &sMCUs[0] but
leaves the DC store unguarded, so each chroma skip overwrites the
just-decoded Y DC with the chroma DC predictor. Output reads sMCUs[0],
gets the trailing Cr DC (~0), and renders an all-black image.

Add `if (iMCU >= 0)` guards to the two pMCU[0] writes (main DC store and
successive-approximation update). The pointer redirect stays as the AC
wild-pointer defense; the new guards stop the silent corruption at
sMCUs[0]. The two fixes are independent and both required.

fixes the progressive 8bit grayscale jpeg regression in 1.3.0

Did you use AI tools to help write this code? partial
This commit is contained in:
Jeremy Klein
2026-05-19 09:52:29 -04:00
committed by GitHub
parent 08461c08e7
commit acc1ed4358
3 changed files with 141 additions and 48 deletions
@@ -0,0 +1,28 @@
From 5dff5afab0c68d0d0c4385d72e6f0c030b613960 Mon Sep 17 00:00:00 2001
From: patch <patch@local>
Date: Mon, 18 May 2026 20:57:54 -0700
Subject: [PATCH 1/2] Redirect pMCU to sMCUs[0] when iMCU < 0 (MCU_SKIP)
---
src/jpeg.inl | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/jpeg.inl b/src/jpeg.inl
index a60b548..26bcf6f 100644
--- a/src/jpeg.inl
+++ b/src/jpeg.inl
@@ -1824,7 +1824,10 @@ static int JPEGDecodeMCU_P(JPEGIMAGE *pJPEG, int iMCU, int *iDCPredictor)
unsigned short *pFast;
uint32_t usHuff; // this prevents an unnecessary & 65535 for shorts
signed int iPositive, iNegative, iCoeff;
- signed short *pMCU = &pJPEG->sMCUs[iMCU & 0xffffff];
+ // CrossPoint patch: redirect pMCU to sMCUs[0] when MCU_SKIP to avoid
+ // a wild pointer (~33 MB past sMCUs) that store-faults on AC writes.
+ signed short *pMCU = (iMCU < 0) ? pJPEG->sMCUs
+ : &pJPEG->sMCUs[iMCU & 0xffffff];
uint32_t ulBitOff;
my_ulong ulCode, ulBits, ulTemp; // local copies to allow compiler to use register vars
uint8_t *pBuf;
--
2.50.1 (Apple Git-155)
@@ -0,0 +1,41 @@
From f6238b54c2de34c8e29b0d371a7e902d9cf579bf Mon Sep 17 00:00:00 2001
From: patch <patch@local>
Date: Mon, 18 May 2026 20:58:22 -0700
Subject: [PATCH 2/2] Guard pMCU[0] DC writes against MCU_SKIP
---
src/jpeg.inl | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/jpeg.inl b/src/jpeg.inl
index 26bcf6f..1bd38b2 100644
--- a/src/jpeg.inl
+++ b/src/jpeg.inl
@@ -1855,7 +1855,11 @@ static int JPEGDecodeMCU_P(JPEGIMAGE *pJPEG, int iMCU, int *iDCPredictor)
{
// (*iDCPredictor) |= iPositive; // in case the scan is run more than once
// pMCU[0] = *iDCPredictor; // store in MCU[0]
- pMCU[0] |= iPositive;
+ // CrossPoint patch: guard against MCU_SKIP. The pMCU
+ // redirect makes &sMCUs[0] safe to dereference, but
+ // writing here would clobber the just-decoded Y DC.
+ if (iMCU >= 0)
+ pMCU[0] |= iPositive;
}
goto mcu_done; // that's it
}
@@ -1887,7 +1891,10 @@ static int JPEGDecodeMCU_P(JPEGIMAGE *pJPEG, int iMCU, int *iDCPredictor)
ulCode <<= pJPEG->cApproxBitsLow; // successive approximation shift value
(*iDCPredictor) += ulCode;
}
- pMCU[0] = (short)*iDCPredictor; // store in MCU[0]
+ // CrossPoint patch: guard against MCU_SKIP. See note on the
+ // matching SA write above.
+ if (iMCU >= 0)
+ pMCU[0] = (short)*iDCPredictor; // store in MCU[0]
}
// Now get the other 63 AC coefficients
pFast = &pJPEG->usHuffAC[pJPEG->ucACTable * HUFF11SIZE];
--
2.50.1 (Apple Git-155)