From 16b5e3823918840aae65c0a6da57c78a5a496a4d Mon Sep 17 00:00:00 2001 From: Cosmin Truta Date: Mon, 17 Nov 2025 20:38:47 +0200 Subject: [PATCH] Fix a buffer overflow in `png_image_finish_read` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reject bit-depth mismatches between IHDR and the requested output format. When a 16-bit PNG is processed with an 8-bit output format request, `png_combine_row` writes using the IHDR depth before transformation, causing writes beyond the buffer allocated via `PNG_IMAGE_SIZE(image)`. The validation establishes a safe API contract where `PNG_IMAGE_SIZE(image)` is guaranteed to be sufficient across the transformation pipeline. Example overflow (32×32 pixels, 16-bit RGB to 8-bit RGBA): - Input format: 16 bits/channel × 3 channels = 6144 bytes - Output buffer: 8 bits/channel × 4 channels = 4096 bytes - Overflow: 6144 bytes - 4096 bytes = 2048 bytes Larger images produce proportionally larger overflows. For example, for 256×256 pixels, the overflow is 131072 bytes. Reported-by: yosiimich CVE: CVE-2025-65018 Upstream-Status: Backport [https://github.com/pnggroup/libpng/commit/16b5e3823918840aae65c0a6da57c78a5a496a4d] Signed-off-by: Peter Marko --- pngread.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pngread.c b/pngread.c index 212afb7d2..92571ec33 100644 --- a/pngread.c +++ b/pngread.c @@ -4164,6 +4164,20 @@ png_image_finish_read(png_imagep image, png_const_colorp background, int result; png_image_read_control display; + /* Reject bit depth mismatches to avoid buffer overflows. */ + png_uint_32 ihdr_bit_depth = + image->opaque->png_ptr->bit_depth; + int requested_linear = + (image->format & PNG_FORMAT_FLAG_LINEAR) != 0; + if (ihdr_bit_depth == 16 && !requested_linear) + return png_image_error(image, + "png_image_finish_read: " + "16-bit PNG must use 16-bit output format"); + if (ihdr_bit_depth < 16 && requested_linear) + return png_image_error(image, + "png_image_finish_read: " + "8-bit PNG must not use 16-bit output format"); + memset(&display, 0, (sizeof display)); display.image = image; display.buffer = buffer;