jpeg_skip_scanlines: Fix UAF w/merged upsamp/quant

jpeg_skip_scanlines() (more specifically, read_and_discard_scanlines())
should check whether merged upsampling is disabled before attempting
to dereference cinfo->cconvert, and it should check whether color
quantization is enabled before attempting to dereference
cinfo->cquantize.  Otherwise, executing one of the following sequences
with the same libjpeg API instance and any 4:2:0 or 4:2:2 JPEG image
will cause a use-after-free issue:

- Disable merged upsampling (default)
- jpeg_start_decompress()
- jpeg_finish_decompress()
  (frees but doesn't zero cinfo->cconvert)
- Enable merged upsampling
- jpeg_start_decompress()
  (doesn't re-allocate cinfo->cconvert, because
  j*init_color_deconverter() isn't called)
- jpeg_skip_scanlines()

- Enable 1-pass color quantization
- jpeg_start_decompress()
- jpeg_finish_decompress()
  (frees but doesn't zero cinfo->cquantize)
- Disable 1-pass color quantization
- jpeg_start_decompress()
  (doesn't re-allocate cinfo->cquantize, because j*init_*_quantizer()
  isn't called)
- jpeg_skip_scanlines()

These sequences are very unlikely to occur in a real-world application.
In practice, this issue does not even cause a segfault or other
user-visible errant behavior, so it is only detectable with ASan.  That
is because the memory region is small enough that it doesn't get
reclaimed by either the application or the O/S between the point at
which it is freed and the point at which it is used (even though a
subsequent malloc() call requests exactly the same amount of memory.)
Thus, this is an undefined behavior issue, but it is unlikely to be
exploitable.
This commit is contained in:
DRC
2025-07-28 20:45:02 -04:00
parent 81feffa632
commit f158143ec0
2 changed files with 14 additions and 3 deletions

View File

@@ -11,6 +11,15 @@ compile source files for multiple data precisions. This improves code
readability and facilitates adapting the libjpeg-turbo source code to non-CMake
build systems.
3. Fixed an issue whereby decompressing a 4:2:0 or 4:2:2 JPEG image with merged
upsampling disabled/one-pass color quantization enabled, then reusing the same
API instance to decompress a 4:2:0 or 4:2:2 JPEG image with merged upsampling
enabled/color quantization disabled, caused `jpeg_skip_scanlines()` to use
freed memory. In practice, the freed memory was not reclaimed before it was
used. Thus, this issue did not cause a segfault or other user-visible errant
behavior (it was only detectable with ASan), and it did not likely pose a
security risk.
3.1.1
=====

View File

@@ -4,7 +4,7 @@
* This file was part of the Independent JPEG Group's software:
* Copyright (C) 1994-1996, Thomas G. Lane.
* libjpeg-turbo Modifications:
* Copyright (C) 2010, 2015-2020, 2022-2024, D. R. Commander.
* Copyright (C) 2010, 2015-2020, 2022-2025, D. R. Commander.
* Copyright (C) 2015, Google, Inc.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
@@ -408,7 +408,8 @@ read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
void (*color_quantize) (j_decompress_ptr cinfo, _JSAMPARRAY input_buf,
_JSAMPARRAY output_buf, int num_rows) = NULL;
if (cinfo->cconvert && cinfo->cconvert->_color_convert) {
if (!master->using_merged_upsample && cinfo->cconvert &&
cinfo->cconvert->_color_convert) {
color_convert = cinfo->cconvert->_color_convert;
cinfo->cconvert->_color_convert = noop_convert;
/* This just prevents UBSan from complaining about adding 0 to a NULL
@@ -417,7 +418,8 @@ read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
scanlines = &dummy_row;
}
if (cinfo->cquantize && cinfo->cquantize->_color_quantize) {
if (cinfo->quantize_colors && cinfo->cquantize &&
cinfo->cquantize->_color_quantize) {
color_quantize = cinfo->cquantize->_color_quantize;
cinfo->cquantize->_color_quantize = noop_quantize;
}