From 0ff46caa0a31e1d4982a2db30a7190b318ad177b Mon Sep 17 00:00:00 2001 From: Sayed Kaif Date: Thu, 23 Jul 2026 11:56:38 +0530 Subject: [PATCH] reject jpeg scans referencing an undefined huffman table --- src/Simd/SimdAvx2ImageLoadJpeg.cpp | 21 +++++++++++++++++++++ src/Simd/SimdBaseImageLoadJpeg.cpp | 21 +++++++++++++++++++++ src/Simd/SimdImageLoadJpeg.h | 1 + 3 files changed, 43 insertions(+) diff --git a/src/Simd/SimdAvx2ImageLoadJpeg.cpp b/src/Simd/SimdAvx2ImageLoadJpeg.cpp index 6176f4d607..2564e774bd 100644 --- a/src/Simd/SimdAvx2ImageLoadJpeg.cpp +++ b/src/Simd/SimdAvx2ImageLoadJpeg.cpp @@ -267,6 +267,7 @@ namespace Simd jpeg__context* s; jpeg__huffman huff_dc[4]; jpeg__huffman huff_ac[4]; + bool huff_dc_defined[4], huff_ac_defined[4]; jpeg__uint16 dequant[4][64]; jpeg__int16 fast_ac[4][1 << FAST_BITS]; @@ -1238,10 +1239,12 @@ namespace Simd if (tc == 0) { if (!jpeg__build_huffman(z->huff_dc + th, sizes)) return 0; v = z->huff_dc[th].values; + z->huff_dc_defined[th] = true; } else { if (!jpeg__build_huffman(z->huff_ac + th, sizes)) return 0; v = z->huff_ac[th].values; + z->huff_ac_defined[th] = true; } for (i = 0; i < n; ++i) v[i] = jpeg__get8(z->s); @@ -1336,6 +1339,22 @@ namespace Simd } } + // The selectors above are bounded to 0..3 but never checked against the DHT markers + // that were actually seen, so a scan referencing a table no DHT defined runs the + // entropy decoder over an uninitialised jpeg__huffman and reads its garbage delta/values + // out of bounds. Reject such a scan before decoding. The DC table is used only for the + // first DC scan (spec_start == 0, succ_high == 0) and the AC table only for AC scans + // (spec_start != 0); a baseline scan uses both. + for (i = 0; i < z->scan_n; ++i) { + int which = z->order[i]; + int useDc = !z->progressive || (z->spec_start == 0 && z->succ_high == 0); + int useAc = !z->progressive || z->spec_start != 0; + if (useDc && !z->huff_dc_defined[z->img_comp[which].hd]) + return JpegLoadError("undefined DC huffman table", "Corrupt JPEG"); + if (useAc && !z->huff_ac_defined[z->img_comp[which].ha]) + return JpegLoadError("undefined AC huffman table", "Corrupt JPEG"); + } + return 1; } @@ -1464,6 +1483,8 @@ namespace Simd z->jfif = 0; z->app14_color_transform = -1; // valid values are 0,1,2 z->marker = JPEG__MARKER_none; // initialize cached marker to empty + for (int i = 0; i < 4; ++i) + z->huff_dc_defined[i] = z->huff_ac_defined[i] = false; m = jpeg__get_marker(z); if (!jpeg__SOI(m)) return JpegLoadError("no SOI", "Corrupt JPEG"); if (scan == JPEG__SCAN_type) return 1; diff --git a/src/Simd/SimdBaseImageLoadJpeg.cpp b/src/Simd/SimdBaseImageLoadJpeg.cpp index fcfa5d18f2..1afed4dcea 100644 --- a/src/Simd/SimdBaseImageLoadJpeg.cpp +++ b/src/Simd/SimdBaseImageLoadJpeg.cpp @@ -753,12 +753,14 @@ namespace Simd if (!z->huff_dc[th].Build(sizes)) return 0; v = z->huff_dc[th].values; + z->huffDcDefined[th] = true; } else { if (!z->huff_ac[th].Build(sizes)) return 0; v = z->huff_ac[th].values; + z->huffAcDefined[th] = true; } for (i = 0; i < n; ++i) v[i] = z->stream->Get8u(); @@ -857,6 +859,23 @@ namespace Simd return JpegLoadError("bad SOS", "Corrupt JPEG"); z->spec_end = 63; } + // Each scan component is decoded with the Huffman tables its selector points at + // (huff_dc[hd] / huff_ac[ha]). The selectors are only bounded to 0..3 above, never + // checked against the DHT markers that were actually seen, so a scan referencing an + // undefined table sends JpegHuffmanDecode into an uninitialised JpegHuffman and reads + // its garbage delta/values out of bounds. Reject the scan here, before decoding. The + // DC table is used only for the first DC scan (spec_start == 0, succ_high == 0) and the + // AC table only for AC scans (spec_start != 0); a baseline scan uses both. + for (int i = 0; i < z->scan_n; ++i) + { + const JpegImgComp& comp = z->img_comp[z->order[i]]; + bool useDc = !z->progressive || (z->spec_start == 0 && z->succ_high == 0); + bool useAc = !z->progressive || z->spec_start != 0; + if (useDc && !z->huffDcDefined[comp.hd]) + return JpegLoadError("undefined DC huffman table", "Corrupt JPEG"); + if (useAc && !z->huffAcDefined[comp.ha]) + return JpegLoadError("undefined AC huffman table", "Corrupt JPEG"); + } return 1; } @@ -950,6 +969,8 @@ namespace Simd z->jfif = 0; z->app14_color_transform = -1; z->marker = JpegMarkerNone; + for (int i = 0; i < 4; ++i) + z->huffDcDefined[i] = z->huffAcDefined[i] = false; int m = JpegGetMarker(z); if (m != JpegMarkerSoi) return JpegLoadError("no SOI", "Corrupt JPEG"); diff --git a/src/Simd/SimdImageLoadJpeg.h b/src/Simd/SimdImageLoadJpeg.h index 1e8e7f4393..c40f70f5b2 100644 --- a/src/Simd/SimdImageLoadJpeg.h +++ b/src/Simd/SimdImageLoadJpeg.h @@ -92,6 +92,7 @@ namespace Simd int img_n, img_out_n; JpegHuffman huff_dc[4]; JpegHuffman huff_ac[4]; + bool huffDcDefined[4], huffAcDefined[4]; uint16_t dequant[4][64]; int img_h_max, img_v_max;