From 08da33b4c88cfcd36e5a706558a8d7e0e4773643 Mon Sep 17 00:00:00 2001 From: Cosmin Truta 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 Analyzed-by: John Bowler CVE: CVE-2025-64720 Upstream-Status: Backport [https://github.com/pnggroup/libpng/commit/08da33b4c88cfcd36e5a706558a8d7e0e4773643] Signed-off-by: Peter Marko --- pngrtran.c | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/pngrtran.c b/pngrtran.c index 548780030..2f5202255 100644 --- a/pngrtran.c +++ b/pngrtran.c @@ -1698,19 +1698,51 @@ png_init_read_transformations(png_structrp png_ptr) } 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]; + 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]; - 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]; + 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]; - 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].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