File libpng16-CVE-2025-64720.patch of Package libpng16.41903
From 08da33b4c88cfcd36e5a706558a8d7e0e4773643 Mon Sep 17 00:00:00 2001
From: Cosmin Truta <ctruta@gmail.com>
Date: Wed, 12 Nov 2025 13:46:23 +0200
Subject: [PATCH] Fix a buffer overflow in `png_init_read_transformations`
The palette compositing code in `png_init_read_transformations` was
incorrectly applying background compositing when PNG_FLAG_OPTIMIZE_ALPHA
was set. This violated the premultiplied alpha invariant
`component <= alpha` expected by `png_image_read_composite`, causing
values that exceeded the valid range for the PNG_sRGB_FROM_LINEAR lookup
tables.
When PNG_ALPHA_OPTIMIZED is active, palette entries should contain pure
premultiplied RGB values without background compositing. The background
compositing must happen later in `png_image_read_composite` where the
actual background color from the PNG file is available.
The fix consists in introducing conditional behavior based on
PNG_FLAG_OPTIMIZE_ALPHA: when set, the code performs only
premultiplication using the formula `component * alpha + 127) / 255`
with proper gamma correction. When not set, the original background
compositing calculation based on the `png_composite` macro is preserved.
This prevents buffer overflows in `png_image_read_composite` where
out-of-range premultiplied values would cause out-of-bounds array access
in `png_sRGB_base[]` and `png_sRGB_delta[]`.
Reported-by: Samsung-PENTEST <Samsung-PENTEST@users.noreply.github.com>
Analyzed-by: John Bowler <jbowler@acm.org>
---
pngrtran.c | 58 ++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 45 insertions(+), 13 deletions(-)
Index: libpng-1.6.34/pngrtran.c
===================================================================
--- libpng-1.6.34.orig/pngrtran.c
+++ libpng-1.6.34/pngrtran.c
@@ -1683,19 +1683,51 @@ png_init_read_transformations(png_struct
}
else /* if (png_ptr->trans_alpha[i] != 0xff) */
{
- png_byte v, w;
+ if ((png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0)
+ {
+ /* Premultiply only:
+ * component = round((component * alpha) / 255)
+ */
+ png_uint_32 component;
- v = png_ptr->gamma_to_1[palette[i].red];
- png_composite(w, v, png_ptr->trans_alpha[i], back_1.red);
- palette[i].red = png_ptr->gamma_from_1[w];
-
- v = png_ptr->gamma_to_1[palette[i].green];
- png_composite(w, v, png_ptr->trans_alpha[i], back_1.green);
- palette[i].green = png_ptr->gamma_from_1[w];
-
- v = png_ptr->gamma_to_1[palette[i].blue];
- png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue);
- palette[i].blue = png_ptr->gamma_from_1[w];
+ component = png_ptr->gamma_to_1[palette[i].red];
+ component =
+ (component * png_ptr->trans_alpha[i] + 128) / 255;
+ palette[i].red = png_ptr->gamma_from_1[component];
+
+ component = png_ptr->gamma_to_1[palette[i].green];
+ component =
+ (component * png_ptr->trans_alpha[i] + 128) / 255;
+ palette[i].green = png_ptr->gamma_from_1[component];
+
+ component = png_ptr->gamma_to_1[palette[i].blue];
+ component =
+ (component * png_ptr->trans_alpha[i] + 128) / 255;
+ palette[i].blue = png_ptr->gamma_from_1[component];
+ }
+ else
+ {
+ /* Composite with background color:
+ * component =
+ * alpha * component + (1 - alpha) * background
+ */
+ png_byte v, w;
+
+ v = png_ptr->gamma_to_1[palette[i].red];
+ png_composite(w, v,
+ png_ptr->trans_alpha[i], back_1.red);
+ palette[i].red = png_ptr->gamma_from_1[w];
+
+ v = png_ptr->gamma_to_1[palette[i].green];
+ png_composite(w, v,
+ png_ptr->trans_alpha[i], back_1.green);
+ palette[i].green = png_ptr->gamma_from_1[w];
+
+ v = png_ptr->gamma_to_1[palette[i].blue];
+ png_composite(w, v,
+ png_ptr->trans_alpha[i], back_1.blue);
+ palette[i].blue = png_ptr->gamma_from_1[w];
+ }
}
}
else