From 16b5e3823918840aae65c0a6da57c78a5a496a4d Mon Sep 17 00:00:00 2001
From: Cosmin Truta <ctruta@gmail.com>
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 <yosiimich@users.noreply.github.com>

CVE: CVE-2025-65018
Upstream-Status: Backport [https://github.com/pnggroup/libpng/commit/16b5e3823918840aae65c0a6da57c78a5a496a4d]
Signed-off-by: Peter Marko <peter.marko@siemens.com>
---
 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;
