Version in base suite: 1.6.48-1+deb13u3 Version in overlay suite: 1.6.48-1+deb13u4 Base version: libpng1.6_1.6.48-1+deb13u4 Target version: libpng1.6_1.6.48-1+deb13u5 Base file: /srv/ftp-master.debian.org/ftp/pool/main/libp/libpng1.6/libpng1.6_1.6.48-1+deb13u4.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/libp/libpng1.6/libpng1.6_1.6.48-1+deb13u5.dsc changelog | 8 patches/CVE-2026-33416-regression-fix-test.patch | 276 ++++++++++++ patches/CVE-2026-33416-regression-fix.patch | 62 ++ patches/CVE-2026-34757-part1.patch | 507 +++++++++++++++++++++++ patches/CVE-2026-34757-part2.patch | 478 +++++++++++++++++++++ patches/series | 4 6 files changed, 1335 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpb28typ4f/libpng1.6_1.6.48-1+deb13u4.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpb28typ4f/libpng1.6_1.6.48-1+deb13u5.dsc: no acceptable signature found diff -Nru libpng1.6-1.6.48/debian/changelog libpng1.6-1.6.48/debian/changelog --- libpng1.6-1.6.48/debian/changelog 2026-03-30 18:56:49.000000000 +0000 +++ libpng1.6-1.6.48/debian/changelog 2026-05-08 12:19:08.000000000 +0000 @@ -1,3 +1,11 @@ +libpng1.6 (1.6.48-1+deb13u5) trixie-security; urgency=high + + * Security upload targeting trixie. + * CVE-2026-34757 - Use after free. (Closes: #1133051) + * Cherry-pick upstream regression fix for previously fixed CVE 2026-33416. + + -- Tobias Frost Fri, 08 May 2026 14:19:08 +0200 + libpng1.6 (1.6.48-1+deb13u4) trixie-security; urgency=medium * Security upload targeting trixie. diff -Nru libpng1.6-1.6.48/debian/patches/CVE-2026-33416-regression-fix-test.patch libpng1.6-1.6.48/debian/patches/CVE-2026-33416-regression-fix-test.patch --- libpng1.6-1.6.48/debian/patches/CVE-2026-33416-regression-fix-test.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/CVE-2026-33416-regression-fix-test.patch 2026-05-08 12:19:08.000000000 +0000 @@ -0,0 +1,276 @@ +Description: Regression fix for CVE-2026-33416 - test + This was a regression from commit c1b0318 - see CVE-2026-33416-part4.patch + This part adds a test to the unit tests. +Origin: https://github.com/pnggroup/libpng/commit/571a89514399597a5f69740b5c3a719353a256fc + +From 571a89514399597a5f69740b5c3a719353a256fc Mon Sep 17 00:00:00 2001 +From: Cosmin Truta +Date: Wed, 15 Apr 2026 19:43:01 +0300 +Subject: [PATCH] test: Ensure the palette sync follows the gamma correction + +This tests commit d4c4e49eb5c8981075ec2cd946428758c0cda6ac. +--- + contrib/libtests/pnggetset.c | 232 +++++++++++++++++++++++++++++++++-- + 1 file changed, 223 insertions(+), 9 deletions(-) + +--- a/contrib/libtests/pnggetset.c ++++ b/contrib/libtests/pnggetset.c +@@ -6,12 +6,7 @@ + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * +- * Test the get-then-set roundtrip for chunk types whose getters return +- * a pointer to internal storage. +- * +- * Passing such a pointer back into the corresponding setter must not +- * cause a use-after-free. A previous version freed the internal buffer +- * before copying from the caller-supplied pointer. ++ * Test getter and setter correctness. + */ + + #include +@@ -99,9 +94,9 @@ + } + for (i = 0; i < 4; i++) + { +- if (got_palette[i].red != (png_byte)(i * 10) || +- got_palette[i].green != (png_byte)(i * 20) || +- got_palette[i].blue != (png_byte)(i * 30)) ++ if ((got_palette[i].red != (png_byte)(i * 10)) ++ || (got_palette[i].green != (png_byte)(i * 20)) ++ || (got_palette[i].blue != (png_byte)(i * 30))) + { + fprintf(stderr, + "pnggetset: PLTE entry %d corrupted after roundtrip\n", i); +@@ -569,6 +564,215 @@ + } + #endif /* PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED */ + ++/* Memory buffer for PNG I/O without temp files. */ ++#define MEM_BUF_SIZE 4096 ++ ++typedef struct ++{ ++ png_byte data[MEM_BUF_SIZE]; ++ size_t len; ++ size_t pos; ++} mem_buf; ++ ++static void PNGCBAPI ++mem_write(png_structp png_ptr, png_bytep buf, png_size_t length) ++{ ++ mem_buf *mb = (mem_buf *)png_get_io_ptr(png_ptr); ++ ++ if (mb->len + length > MEM_BUF_SIZE) ++ png_error(png_ptr, "pnggetset: write overflow"); ++ ++ memcpy(mb->data + mb->len, buf, length); ++ mb->len += length; ++} ++ ++static void PNGCBAPI ++mem_flush(png_structp png_ptr) ++{ ++ (void)png_ptr; ++} ++ ++static void PNGCBAPI ++mem_read(png_structp png_ptr, png_bytep buf, png_size_t length) ++{ ++ mem_buf *mb = (mem_buf *)png_get_io_ptr(png_ptr); ++ ++ if (mb->pos + length > mb->len) ++ png_error(png_ptr, "pnggetset: read overflow"); ++ ++ memcpy(buf, mb->data + mb->pos, length); ++ mb->pos += length; ++} ++ ++/* Palette sync after gamma correction. ++ * ++ * When info_ptr->palette and png_ptr->palette are separate buffers, ++ * in-place gamma correction of png_ptr->palette must be synced back ++ * to info_ptr->palette so that png_get_PLTE returns the corrected ++ * values. ++ */ ++#define PLTE_SYNC_NPALETTE 4 ++ ++static const png_color plte_sync_original[PLTE_SYNC_NPALETTE] = ++{ ++ { 64, 96, 128 }, ++ { 128, 160, 192 }, ++ { 192, 224, 240 }, ++ { 32, 48, 64 } ++}; ++ ++static int ++test_plte_palette_sync(void) ++{ ++ mem_buf buf; ++ png_structp png_ptr; ++ png_infop info_ptr; ++ png_colorp got_palette; ++ int num_palette; ++ double file_gamma; ++ png_byte row[1]; ++ int i; ++ int changed; ++ ++ /* Write a 1x1 palette PNG with gAMA = 1.0 (linear). */ ++ buf.len = 0; ++ buf.pos = 0; ++ ++ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, ++ NULL, NULL, NULL); ++ if (png_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_write_struct failed\n"); ++ return 1; ++ } ++ ++ info_ptr = png_create_info_struct(png_ptr); ++ if (info_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_info_struct failed\n"); ++ png_destroy_write_struct(&png_ptr, NULL); ++ return 1; ++ } ++ ++ if (setjmp(png_jmpbuf(png_ptr))) ++ { ++ fprintf(stderr, "pnggetset: libpng error in test_plte_palette_sync" ++ " (write)\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ png_set_write_fn(png_ptr, &buf, mem_write, mem_flush); ++ png_set_IHDR(png_ptr, info_ptr, 1, 1, 8, PNG_COLOR_TYPE_PALETTE, ++ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, ++ PNG_FILTER_TYPE_BASE); ++ png_set_PLTE(png_ptr, info_ptr, ++ (png_colorp)plte_sync_original, PLTE_SYNC_NPALETTE); ++ png_set_gAMA(png_ptr, info_ptr, 1.0); ++ png_write_info(png_ptr, info_ptr); ++ ++ row[0] = 0; ++ png_write_row(png_ptr, row); ++ png_write_end(png_ptr, info_ptr); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ ++ /* Read back with gamma correction as the sole transform. */ ++ buf.pos = 0; ++ ++ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, ++ NULL, NULL, NULL); ++ if (png_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_read_struct failed\n"); ++ return 1; ++ } ++ ++ info_ptr = png_create_info_struct(png_ptr); ++ if (info_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_info_struct failed\n"); ++ png_destroy_read_struct(&png_ptr, NULL, NULL); ++ return 1; ++ } ++ ++ if (setjmp(png_jmpbuf(png_ptr))) ++ { ++ fprintf(stderr, "pnggetset: libpng error in test_plte_palette_sync" ++ " (read)\n"); ++ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); ++ return 1; ++ } ++ ++ png_set_read_fn(png_ptr, &buf, mem_read); ++ png_read_info(png_ptr, info_ptr); ++ ++ if (png_get_gAMA(png_ptr, info_ptr, &file_gamma) == 0) ++ { ++ fprintf(stderr, "pnggetset: gAMA chunk not found\n"); ++ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); ++ return 1; ++ } ++ ++ png_set_gamma(png_ptr, 2.2, file_gamma); ++ png_read_update_info(png_ptr, info_ptr); ++ ++ if (png_get_PLTE(png_ptr, info_ptr, &got_palette, &num_palette) == 0) ++ { ++ fprintf(stderr, "pnggetset: png_get_PLTE failed after update\n"); ++ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); ++ return 1; ++ } ++ ++ if (num_palette != PLTE_SYNC_NPALETTE) ++ { ++ fprintf(stderr, "pnggetset: palette size %d, expected %d\n", ++ num_palette, PLTE_SYNC_NPALETTE); ++ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); ++ return 1; ++ } ++ ++ /* Every entry must differ from the original after gamma correction ++ * (file_gamma=1.0, screen_gamma=2.2). If the sync was skipped, ++ * info_ptr->palette still holds the stale pre-correction values. ++ */ ++ changed = 0; ++ ++ for (i = 0; i < PLTE_SYNC_NPALETTE; i++) ++ { ++ if ((got_palette[i].red != plte_sync_original[i].red) ++ || (got_palette[i].green != plte_sync_original[i].green) ++ || (got_palette[i].blue != plte_sync_original[i].blue)) ++ { ++ changed++; ++ } ++ else ++ { ++ fprintf(stderr, ++ "pnggetset: palette entry %d NOT gamma-corrected: " ++ "got {%u, %u, %u}, same as original\n", ++ i, ++ (unsigned)got_palette[i].red, ++ (unsigned)got_palette[i].green, ++ (unsigned)got_palette[i].blue); ++ } ++ } ++ ++ png_read_row(png_ptr, row, NULL); ++ png_read_end(png_ptr, NULL); ++ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); ++ ++ if (changed != PLTE_SYNC_NPALETTE) ++ { ++ fprintf(stderr, ++ "pnggetset: only %d of %d palette entries were " ++ "gamma-corrected (palette sync failed)\n", ++ changed, PLTE_SYNC_NPALETTE); ++ return 1; ++ } ++ ++ return 0; ++} ++ + int + main(void) + { +@@ -644,5 +848,15 @@ + printf("PASS\n"); + #endif + ++ printf("Testing PLTE sync after gamma correction... "); ++ fflush(stdout); ++ if (test_plte_palette_sync() != 0) ++ { ++ printf("FAIL\n"); ++ result = 1; ++ } ++ else ++ printf("PASS\n"); ++ + return result; + } diff -Nru libpng1.6-1.6.48/debian/patches/CVE-2026-33416-regression-fix.patch libpng1.6-1.6.48/debian/patches/CVE-2026-33416-regression-fix.patch --- libpng1.6-1.6.48/debian/patches/CVE-2026-33416-regression-fix.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/CVE-2026-33416-regression-fix.patch 2026-05-08 12:19:08.000000000 +0000 @@ -0,0 +1,62 @@ +Description: Regression fix for CVE-2026-33416 - actual fix + This was a regression from commit c1b0318 - see CVE-2026-33416-part4.patch +Origin: https://github.com/pnggroup/libpng/commit/d4c4e49eb5c8981075ec2cd946428758c0cda6ac +Bug: https://github.com/pnggroup/libpng/issues/848 + +From d4c4e49eb5c8981075ec2cd946428758c0cda6ac Mon Sep 17 00:00:00 2001 +From: Cosmin Truta +Date: Wed, 15 Apr 2026 14:58:35 +0300 +Subject: [PATCH] fix: Sync `info_ptr->palette` unconditionally after in-place + transforms + +The palette sync in `png_read_transform_info` was guarded by +`if (transformations != 0)`, but the palette-modifying transforms clear +their own bits before this function gets to run. When one of these was +the sole transform, the guard was false and the sync was skipped, which +caused `png_get_PLTE` to return stale palette data. + +Drop this guard. + +This was a regression from commit c1b0318b39 (version 1.6.56). + +Reported-by: ralfjunker +Resolves: https://github.com/pnggroup/libpng/issues/848 +--- + pngrtran.c | 20 ++++++++------------ + 1 file changed, 8 insertions(+), 12 deletions(-) + +diff --git a/pngrtran.c b/pngrtran.c +index 1b04cafa5..0ac8df749 100644 +--- a/pngrtran.c ++++ b/pngrtran.c +@@ -2070,19 +2070,15 @@ png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr) + { + png_debug(1, "in png_read_transform_info"); + +- if (png_ptr->transformations != 0) ++ if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE && ++ info_ptr->palette != NULL && png_ptr->palette != NULL) + { +- if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE && +- info_ptr->palette != NULL && png_ptr->palette != NULL) +- { +- /* Sync info_ptr->palette with png_ptr->palette. +- * The function png_init_read_transformations may have modified +- * png_ptr->palette in place (e.g. for gamma correction or for +- * background compositing). +- */ +- memcpy(info_ptr->palette, png_ptr->palette, +- PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))); +- } ++ /* Sync info_ptr->palette with png_ptr->palette, which may ++ * have been modified by png_init_read_transformations ++ * (e.g. for gamma correction or background compositing). ++ */ ++ memcpy(info_ptr->palette, png_ptr->palette, ++ PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))); + } + + #ifdef PNG_READ_EXPAND_SUPPORTED +-- +2.53.0 + diff -Nru libpng1.6-1.6.48/debian/patches/CVE-2026-34757-part1.patch libpng1.6-1.6.48/debian/patches/CVE-2026-34757-part1.patch --- libpng1.6-1.6.48/debian/patches/CVE-2026-34757-part1.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/CVE-2026-34757-part1.patch 2026-05-08 12:19:08.000000000 +0000 @@ -0,0 +1,507 @@ +Description: CVE-2026-34757 part 1 (main fix) - use after free +Origin: https://github.com/pnggroup/libpng/commit/398cbe3df03f4e11bb031e07f416dfdde3684e8a +Bug: https://github.com/pnggroup/libpng/security/advisories/GHSA-6fr7-g8h7-v645 +Bug: https://github.com/pnggroup/libpng/issues/836 +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1133051 + +From 398cbe3df03f4e11bb031e07f416dfdde3684e8a Mon Sep 17 00:00:00 2001 +From: Cosmin Truta +Date: Mon, 30 Mar 2026 17:35:30 +0300 +Subject: [PATCH] fix: Handle self-referencing pointers in getter-to-setter + aliasing + +Apply a robustness fix for a caller-side API usage pattern involving +the getters and the setters for PLTE, tRNS, and hIST. + +Passing a pointer returned by the PLTE, tRNS, or hIST getters back +into the corresponding setters used to cause the setters to read from +a stale pointer. The fix consists in snapshotting the caller's data +into a stack-local buffer before freeing the old internal storage. + +Fixes pnggroup/libpng#836 + +Reported-by: Iv4n +--- + CMakeLists.txt | 12 ++ + Makefile.am | 9 +- + contrib/libtests/pnggetset.c | 328 +++++++++++++++++++++++++++++++++++ + pngset.c | 29 +++- + tests/pnggetset | 5 + + 5 files changed, 380 insertions(+), 3 deletions(-) + create mode 100644 contrib/libtests/pnggetset.c + create mode 100755 tests/pnggetset + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -623,6 +623,9 @@ + set(pngstest_sources + contrib/libtests/pngstest.c + ) ++set(pnggetset_sources ++ contrib/libtests/pnggetset.c ++) + set(pngunknown_sources + contrib/libtests/pngunknown.c + ) +@@ -792,6 +795,15 @@ + COMMAND pngtest + FILES "${TEST_PNG3_PNGS}") + ++ # pnggetset test: ++ # Getter-to-setter roundtrips for various chunk types. ++ add_executable(pnggetset ${pnggetset_sources}) ++ target_link_libraries(pnggetset ++ PRIVATE png_shared) ++ ++ png_add_test(NAME pnggetset ++ COMMAND pnggetset) ++ + add_executable(pngvalid ${pngvalid_sources}) + target_link_libraries(pngvalid + PRIVATE png_shared) +--- a/Makefile.am ++++ b/Makefile.am +@@ -13,7 +13,7 @@ + + # test programs - run on make check, make distcheck + if ENABLE_TESTS +-check_PROGRAMS= pngtest pngunknown pngstest pngvalid pngimage pngcp ++check_PROGRAMS= pngtest pnggetset pngunknown pngstest pngvalid pngimage pngcp + if HAVE_CLOCK_GETTIME + check_PROGRAMS += timepng + endif +@@ -42,6 +42,9 @@ + pngtest_SOURCES = pngtest.c + pngtest_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la + ++pnggetset_SOURCES = contrib/libtests/pnggetset.c ++pnggetset_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la ++ + pngvalid_SOURCES = contrib/libtests/pngvalid.c + pngvalid_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la + +@@ -73,6 +76,7 @@ + if ENABLE_TESTS + TESTS =\ + tests/pngtest-all\ ++ tests/pnggetset\ + tests/pngvalid-gamma-16-to-8 tests/pngvalid-gamma-alpha-mode\ + tests/pngvalid-gamma-background tests/pngvalid-gamma-expand16-alpha-mode\ + tests/pngvalid-gamma-expand16-background\ +@@ -273,9 +277,10 @@ + pngtest.o: pnglibconf.h + + contrib/libtests/makepng.o: pnglibconf.h ++contrib/libtests/pnggetset.o: pnglibconf.h ++contrib/libtests/pngimage.o: pnglibconf.h + contrib/libtests/pngstest.o: pnglibconf.h + contrib/libtests/pngunknown.o: pnglibconf.h +-contrib/libtests/pngimage.o: pnglibconf.h + contrib/libtests/pngvalid.o: pnglibconf.h + contrib/libtests/readpng.o: pnglibconf.h + contrib/libtests/tarith.o: pnglibconf.h +--- /dev/null ++++ b/contrib/libtests/pnggetset.c +@@ -0,0 +1,328 @@ ++/* pnggetset.c ++ * ++ * Copyright (c) 2026 Cosmin Truta ++ * ++ * This code is released under the libpng license. ++ * For conditions of distribution and use, see the disclaimer ++ * and license in png.h ++ * ++ * Test the get-then-set roundtrip pattern for PLTE, tRNS, and hIST. ++ * ++ * Passing the internal pointer returned by a getter back into the ++ * corresponding setter is a natural API usage pattern. A previous ++ * version had a use-after-free on this path because the setter freed ++ * the internal buffer before copying from the caller-supplied pointer. ++ */ ++ ++#include ++#include ++#include ++ ++#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H) ++# include ++#endif ++ ++#ifdef PNG_FREESTANDING_TESTS ++# include ++#else ++# include "../../png.h" ++#endif ++ ++/* Test: get the PLTE, pass it straight back to set, verify roundtrip. */ ++static int ++test_plte_roundtrip(void) ++{ ++ png_structp png_ptr; ++ png_infop info_ptr; ++ png_color palette[4]; ++ png_colorp got_palette = NULL; ++ int num_palette = 0; ++ int i; ++ ++ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, ++ NULL, NULL, NULL); ++ if (png_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_write_struct failed\n"); ++ return 1; ++ } ++ ++ info_ptr = png_create_info_struct(png_ptr); ++ if (info_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_info_struct failed\n"); ++ png_destroy_write_struct(&png_ptr, NULL); ++ return 1; ++ } ++ ++ if (setjmp(png_jmpbuf(png_ptr))) ++ { ++ fprintf(stderr, "pnggetset: libpng error in test_plte_roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* Set up a palette-color image header. */ ++ png_set_IHDR(png_ptr, info_ptr, 1, 1, 8, PNG_COLOR_TYPE_PALETTE, ++ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); ++ ++ /* Populate with recognizable values. */ ++ for (i = 0; i < 4; i++) ++ { ++ palette[i].red = (png_byte)(i * 10); ++ palette[i].green = (png_byte)(i * 20); ++ palette[i].blue = (png_byte)(i * 30); ++ } ++ png_set_PLTE(png_ptr, info_ptr, palette, 4); ++ ++ /* Get the internal pointer and feed it straight back. */ ++ png_get_PLTE(png_ptr, info_ptr, &got_palette, &num_palette); ++ if (got_palette == NULL || num_palette != 4) ++ { ++ fprintf(stderr, "pnggetset: png_get_PLTE returned unexpected values\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* This is the critical call: the pointer aliases info_ptr->palette. */ ++ png_set_PLTE(png_ptr, info_ptr, got_palette, num_palette); ++ ++ /* Verify the data survived the roundtrip. */ ++ got_palette = NULL; ++ num_palette = 0; ++ png_get_PLTE(png_ptr, info_ptr, &got_palette, &num_palette); ++ if (got_palette == NULL || num_palette != 4) ++ { ++ fprintf(stderr, "pnggetset: PLTE lost after roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ for (i = 0; i < 4; i++) ++ { ++ if (got_palette[i].red != (png_byte)(i * 10) || ++ got_palette[i].green != (png_byte)(i * 20) || ++ got_palette[i].blue != (png_byte)(i * 30)) ++ { ++ fprintf(stderr, ++ "pnggetset: PLTE entry %d corrupted after roundtrip\n", i); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ } ++ ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 0; ++} ++ ++#ifdef PNG_hIST_SUPPORTED ++/* Test: get the hIST, pass it straight back to set, verify roundtrip. */ ++static int ++test_hist_roundtrip(void) ++{ ++ png_structp png_ptr; ++ png_infop info_ptr; ++ png_color palette[4]; ++ png_uint_16 hist[4]; ++ png_uint_16p got_hist = NULL; ++ int i; ++ ++ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, ++ NULL, NULL, NULL); ++ if (png_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_write_struct failed\n"); ++ return 1; ++ } ++ ++ info_ptr = png_create_info_struct(png_ptr); ++ if (info_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_info_struct failed\n"); ++ png_destroy_write_struct(&png_ptr, NULL); ++ return 1; ++ } ++ ++ if (setjmp(png_jmpbuf(png_ptr))) ++ { ++ fprintf(stderr, "pnggetset: libpng error in test_hist_roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* Set up a palette-color image header. */ ++ memset(palette, 0, sizeof palette); ++ png_set_IHDR(png_ptr, info_ptr, 1, 1, 8, PNG_COLOR_TYPE_PALETTE, ++ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); ++ png_set_PLTE(png_ptr, info_ptr, palette, 4); ++ ++ /* Populate with recognizable values. */ ++ for (i = 0; i < 4; i++) ++ hist[i] = (png_uint_16)(i * 100 + 42); ++ ++ png_set_hIST(png_ptr, info_ptr, hist); ++ ++ /* Get the internal pointer and feed it straight back. */ ++ if (png_get_hIST(png_ptr, info_ptr, &got_hist) == 0 || got_hist == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_get_hIST returned unexpected values\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* This is the critical call: the pointer aliases info_ptr->hist. */ ++ png_set_hIST(png_ptr, info_ptr, got_hist); ++ ++ /* Verify the data survived the roundtrip. */ ++ got_hist = NULL; ++ if (png_get_hIST(png_ptr, info_ptr, &got_hist) == 0 || got_hist == NULL) ++ { ++ fprintf(stderr, "pnggetset: hIST lost after roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ for (i = 0; i < 4; i++) ++ { ++ if (got_hist[i] != (png_uint_16)(i * 100 + 42)) ++ { ++ fprintf(stderr, ++ "pnggetset: hIST entry %d corrupted after roundtrip\n", i); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ } ++ ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 0; ++} ++#endif /* PNG_hIST_SUPPORTED */ ++ ++#ifdef PNG_tRNS_SUPPORTED ++/* Test: get the tRNS, pass it straight back to set, verify roundtrip. */ ++static int ++test_trns_roundtrip(void) ++{ ++ png_structp png_ptr; ++ png_infop info_ptr; ++ png_color palette[4]; ++ png_byte trans_alpha[4]; ++ png_color_16 trans_color; ++ png_bytep got_alpha = NULL; ++ png_color_16p got_color = NULL; ++ int num_trans = 0; ++ int i; ++ ++ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, ++ NULL, NULL, NULL); ++ if (png_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_write_struct failed\n"); ++ return 1; ++ } ++ ++ info_ptr = png_create_info_struct(png_ptr); ++ if (info_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_info_struct failed\n"); ++ png_destroy_write_struct(&png_ptr, NULL); ++ return 1; ++ } ++ ++ if (setjmp(png_jmpbuf(png_ptr))) ++ { ++ fprintf(stderr, "pnggetset: libpng error in test_trns_roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* Set up a palette-color image. */ ++ memset(palette, 0, sizeof palette); ++ png_set_IHDR(png_ptr, info_ptr, 1, 1, 8, PNG_COLOR_TYPE_PALETTE, ++ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); ++ png_set_PLTE(png_ptr, info_ptr, palette, 4); ++ ++ /* Populate tRNS with recognizable values. */ ++ for (i = 0; i < 4; i++) ++ trans_alpha[i] = (png_byte)(0xff - i * 0x11); ++ memset(&trans_color, 0, sizeof trans_color); ++ ++ png_set_tRNS(png_ptr, info_ptr, trans_alpha, 4, &trans_color); ++ ++ /* Get the internal pointer and feed it straight back. */ ++ png_get_tRNS(png_ptr, info_ptr, &got_alpha, &num_trans, &got_color); ++ if (got_alpha == NULL || num_trans != 4) ++ { ++ fprintf(stderr, "pnggetset: png_get_tRNS returned unexpected values\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* This is the critical call: the pointer aliases info_ptr->trans_alpha. */ ++ png_set_tRNS(png_ptr, info_ptr, got_alpha, num_trans, got_color); ++ ++ /* Verify the data survived the roundtrip. */ ++ got_alpha = NULL; ++ num_trans = 0; ++ png_get_tRNS(png_ptr, info_ptr, &got_alpha, &num_trans, &got_color); ++ if (got_alpha == NULL || num_trans != 4) ++ { ++ fprintf(stderr, "pnggetset: tRNS lost after roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ for (i = 0; i < 4; i++) ++ { ++ if (got_alpha[i] != (png_byte)(0xff - i * 0x11)) ++ { ++ fprintf(stderr, ++ "pnggetset: tRNS entry %d corrupted after roundtrip\n", i); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ } ++ ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 0; ++} ++#endif /* PNG_tRNS_SUPPORTED */ ++ ++int ++main(void) ++{ ++ int result = 0; ++ ++ printf("Testing PLTE get-then-set roundtrip... "); ++ fflush(stdout); ++ if (test_plte_roundtrip() != 0) ++ { ++ printf("FAIL\n"); ++ result = 1; ++ } ++ else ++ printf("PASS\n"); ++ ++#ifdef PNG_hIST_SUPPORTED ++ printf("Testing hIST get-then-set roundtrip... "); ++ fflush(stdout); ++ if (test_hist_roundtrip() != 0) ++ { ++ printf("FAIL\n"); ++ result = 1; ++ } ++ else ++ printf("PASS\n"); ++#endif ++ ++#ifdef PNG_tRNS_SUPPORTED ++ printf("Testing tRNS get-then-set roundtrip... "); ++ fflush(stdout); ++ if (test_trns_roundtrip() != 0) ++ { ++ printf("FAIL\n"); ++ result = 1; ++ } ++ else ++ printf("PASS\n"); ++#endif ++ ++ return result; ++} +--- a/pngset.c ++++ b/pngset.c +@@ -384,6 +384,7 @@ + png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_uint_16p hist) + { ++ png_uint_16 safe_hist[PNG_MAX_PALETTE_LENGTH]; + int i; + + png_debug1(1, "in %s storage function", "hIST"); +@@ -400,6 +401,13 @@ + return; + } + ++ /* Snapshot the caller's hist before freeing, in case it points to ++ * info_ptr->hist (getter-to-setter aliasing). ++ */ ++ memcpy(safe_hist, hist, (unsigned int)info_ptr->num_palette * ++ (sizeof (png_uint_16))); ++ hist = safe_hist; ++ + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); + + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in +@@ -741,7 +749,7 @@ + png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, + png_const_colorp palette, int num_palette) + { +- ++ png_color safe_palette[PNG_MAX_PALETTE_LENGTH]; + png_uint_32 max_palette_length; + + png_debug1(1, "in %s storage function", "PLTE"); +@@ -775,6 +783,15 @@ + png_error(png_ptr, "Invalid palette"); + } + ++ /* Snapshot the caller's palette before freeing, in case it points to ++ * info_ptr->palette (getter-to-setter aliasing). ++ */ ++ if (num_palette > 0) ++ memcpy(safe_palette, palette, (unsigned int)num_palette * ++ (sizeof (png_color))); ++ ++ palette = safe_palette; ++ + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead +@@ -1164,6 +1181,16 @@ + + if (trans_alpha != NULL) + { ++ /* Snapshot the caller's trans_alpha before freeing, in case it ++ * points to info_ptr->trans_alpha (getter-to-setter aliasing). ++ */ ++ png_byte safe_trans[PNG_MAX_PALETTE_LENGTH]; ++ ++ if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) ++ memcpy(safe_trans, trans_alpha, (size_t)num_trans); ++ ++ trans_alpha = safe_trans; ++ + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); + + if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) +--- /dev/null ++++ b/tests/pnggetset +@@ -0,0 +1,5 @@ ++#!/bin/sh ++ ++# pnggetset test: ++# Getter-to-setter roundtrips for various chunk types. ++exec ./pnggetset diff -Nru libpng1.6-1.6.48/debian/patches/CVE-2026-34757-part2.patch libpng1.6-1.6.48/debian/patches/CVE-2026-34757-part2.patch --- libpng1.6-1.6.48/debian/patches/CVE-2026-34757-part2.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/CVE-2026-34757-part2.patch 2026-05-08 12:19:08.000000000 +0000 @@ -0,0 +1,478 @@ +Description: CVE-2026-34757 part 2 (follow-up hardening) - use after free +https://github.com/pnggroup/libpng/commit/55d20aaa322c9274491cda82c5cd4f99b48c6bcc +Bug: https://github.com/pnggroup/libpng/security/advisories/GHSA-6fr7-g8h7-v645 +Bug: https://github.com/pnggroup/libpng/issues/836 +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1133051 + +From 55d20aaa322c9274491cda82c5cd4f99b48c6bcc Mon Sep 17 00:00:00 2001 +From: Cosmin Truta +Date: Mon, 30 Mar 2026 17:43:05 +0300 +Subject: [PATCH] fix: Handle getter-to-setter aliasing in append-style chunk + setters + +Apply the same class of robustness fix from the previous commit to +`png_set_text`, `png_set_sPLT` and `png_set_unknown_chunks`. These +append-style setters used `png_realloc_array` to grow the internal +array, then freed the old array before copying from the caller's +input. If the caller's pointer was obtained from the corresponding +getter, it aliased the freed array. + +The fix defers the freeing of the old array until after the copy loop. + +Also extend the pnggetset regression test to cover all three setters. +--- + contrib/libtests/pnggetset.c | 330 ++++++++++++++++++++++++++++++++++- + pngset.c | 25 ++- + 2 files changed, 347 insertions(+), 8 deletions(-) + +--- a/contrib/libtests/pnggetset.c ++++ b/contrib/libtests/pnggetset.c +@@ -6,12 +6,12 @@ + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * +- * Test the get-then-set roundtrip pattern for PLTE, tRNS, and hIST. ++ * Test the get-then-set roundtrip for chunk types whose getters return ++ * a pointer to internal storage. + * +- * Passing the internal pointer returned by a getter back into the +- * corresponding setter is a natural API usage pattern. A previous +- * version had a use-after-free on this path because the setter freed +- * the internal buffer before copying from the caller-supplied pointer. ++ * Passing such a pointer back into the corresponding setter must not ++ * cause a use-after-free. A previous version freed the internal buffer ++ * before copying from the caller-supplied pointer. + */ + + #include +@@ -285,6 +285,290 @@ + } + #endif /* PNG_tRNS_SUPPORTED */ + ++#ifdef PNG_TEXT_SUPPORTED ++/* Test: get the text array, pass it straight back to set, verify data. */ ++#define TEXT_COUNT 6 /* enough to trigger reallocation on the second set */ ++static int ++test_text_roundtrip(void) ++{ ++ png_structp png_ptr; ++ png_infop info_ptr; ++ png_text text_entries[TEXT_COUNT]; ++ png_textp got_text = NULL; ++ int got_num_text = 0; ++ int i; ++ ++ /* Recognizable keys and values. */ ++ static const char *keys[TEXT_COUNT] = { ++ "Title", "Author", "Desc", "Copyright", "Source", "Comment" ++ }; ++ static const char *vals[TEXT_COUNT] = { ++ "t0", "t1", "t2", "t3", "t4", "t5" ++ }; ++ ++ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, ++ NULL, NULL, NULL); ++ if (png_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_write_struct failed\n"); ++ return 1; ++ } ++ ++ info_ptr = png_create_info_struct(png_ptr); ++ if (info_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_info_struct failed\n"); ++ png_destroy_write_struct(&png_ptr, NULL); ++ return 1; ++ } ++ ++ if (setjmp(png_jmpbuf(png_ptr))) ++ { ++ fprintf(stderr, "pnggetset: libpng error in test_text_roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* Populate the text entries. */ ++ memset(text_entries, 0, sizeof text_entries); ++ for (i = 0; i < TEXT_COUNT; i++) ++ { ++ text_entries[i].compression = PNG_TEXT_COMPRESSION_NONE; ++ text_entries[i].key = (png_charp)keys[i]; ++ text_entries[i].text = (png_charp)vals[i]; ++ } ++ png_set_text(png_ptr, info_ptr, text_entries, TEXT_COUNT); ++ ++ /* Get the internal pointer and feed it straight back (append). */ ++ png_get_text(png_ptr, info_ptr, &got_text, &got_num_text); ++ if (got_text == NULL || got_num_text != TEXT_COUNT) ++ { ++ fprintf(stderr, "pnggetset: png_get_text returned unexpected values\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* This is the critical call: got_text aliases info_ptr->text. */ ++ png_set_text(png_ptr, info_ptr, got_text, got_num_text); ++ ++ /* Verify the original entries survived. */ ++ got_text = NULL; ++ got_num_text = 0; ++ png_get_text(png_ptr, info_ptr, &got_text, &got_num_text); ++ if (got_text == NULL || got_num_text != TEXT_COUNT * 2) ++ { ++ fprintf(stderr, "pnggetset: text count %d, expected %d after roundtrip\n", ++ got_num_text, TEXT_COUNT * 2); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ for (i = 0; i < TEXT_COUNT; i++) ++ { ++ if (got_text[i].key == NULL || ++ strcmp(got_text[i].key, keys[i]) != 0 || ++ got_text[i].text == NULL || ++ strcmp(got_text[i].text, vals[i]) != 0) ++ { ++ fprintf(stderr, ++ "pnggetset: text entry %d corrupted after roundtrip\n", i); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ } ++ ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 0; ++} ++#undef TEXT_COUNT ++#endif /* PNG_TEXT_SUPPORTED */ ++ ++#ifdef PNG_sPLT_SUPPORTED ++/* Test: get the sPLT array, pass it straight back to set, verify data. */ ++static int ++test_splt_roundtrip(void) ++{ ++ png_structp png_ptr; ++ png_infop info_ptr; ++ png_sPLT_t splt; ++ png_sPLT_entry splt_entries[4]; ++ png_sPLT_tp got_spalettes = NULL; ++ int got_num, i; ++ ++ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, ++ NULL, NULL, NULL); ++ if (png_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_write_struct failed\n"); ++ return 1; ++ } ++ ++ info_ptr = png_create_info_struct(png_ptr); ++ if (info_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_info_struct failed\n"); ++ png_destroy_write_struct(&png_ptr, NULL); ++ return 1; ++ } ++ ++ if (setjmp(png_jmpbuf(png_ptr))) ++ { ++ fprintf(stderr, "pnggetset: libpng error in test_splt_roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* Populate with recognizable values. */ ++ memset(splt_entries, 0, sizeof splt_entries); ++ for (i = 0; i < 4; i++) ++ { ++ splt_entries[i].red = (png_uint_16)(i * 1000); ++ splt_entries[i].green = (png_uint_16)(i * 2000); ++ splt_entries[i].blue = (png_uint_16)(i * 3000); ++ splt_entries[i].alpha = 0xffffU; ++ splt_entries[i].frequency = (png_uint_16)(i + 1); ++ } ++ memset(&splt, 0, sizeof splt); ++ splt.name = (png_charp)"test_sPLT"; ++ splt.depth = 16; ++ splt.entries = splt_entries; ++ splt.nentries = 4; ++ ++ png_set_sPLT(png_ptr, info_ptr, &splt, 1); ++ ++ /* Get the internal pointer and feed it straight back (append). */ ++ got_num = png_get_sPLT(png_ptr, info_ptr, &got_spalettes); ++ if (got_spalettes == NULL || got_num != 1) ++ { ++ fprintf(stderr, "pnggetset: png_get_sPLT returned unexpected values\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* This is the critical call: got_spalettes aliases internal storage. */ ++ png_set_sPLT(png_ptr, info_ptr, got_spalettes, got_num); ++ ++ /* Verify the original entry survived. */ ++ got_spalettes = NULL; ++ got_num = png_get_sPLT(png_ptr, info_ptr, &got_spalettes); ++ if (got_spalettes == NULL || got_num != 2) ++ { ++ fprintf(stderr, "pnggetset: sPLT count %d, expected 2 after roundtrip\n", ++ got_num); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ if (strcmp(got_spalettes[0].name, "test_sPLT") != 0 || ++ got_spalettes[0].nentries != 4 || ++ got_spalettes[0].depth != 16) ++ { ++ fprintf(stderr, ++ "pnggetset: sPLT entry 0 corrupted after roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ for (i = 0; i < 4; i++) ++ { ++ if (got_spalettes[0].entries[i].red != (png_uint_16)(i * 1000) || ++ got_spalettes[0].entries[i].green != (png_uint_16)(i * 2000) || ++ got_spalettes[0].entries[i].blue != (png_uint_16)(i * 3000)) ++ { ++ fprintf(stderr, ++ "pnggetset: sPLT[0] entry %d corrupted after roundtrip\n", i); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ } ++ ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 0; ++} ++#endif /* PNG_sPLT_SUPPORTED */ ++ ++#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED ++/* Test: get unknown chunks, pass them straight back to set, verify data. */ ++static int ++test_unknown_roundtrip(void) ++{ ++ png_structp png_ptr; ++ png_infop info_ptr; ++ png_unknown_chunk unk; ++ png_unknown_chunkp got_unknowns = NULL; ++ int got_num; ++ static const png_byte test_data[] = {0xde, 0xad, 0xbe, 0xef}; ++ ++ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, ++ NULL, NULL, NULL); ++ if (png_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_write_struct failed\n"); ++ return 1; ++ } ++ ++ info_ptr = png_create_info_struct(png_ptr); ++ if (info_ptr == NULL) ++ { ++ fprintf(stderr, "pnggetset: png_create_info_struct failed\n"); ++ png_destroy_write_struct(&png_ptr, NULL); ++ return 1; ++ } ++ ++ if (setjmp(png_jmpbuf(png_ptr))) ++ { ++ fprintf(stderr, ++ "pnggetset: libpng error in test_unknown_roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* Set up an unknown chunk with recognizable data. */ ++ memset(&unk, 0, sizeof unk); ++ memcpy(unk.name, "teSt", 5); ++ unk.data = (png_bytep)test_data; ++ unk.size = sizeof test_data; ++ unk.location = PNG_HAVE_IHDR; ++ ++ png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0); ++ png_set_unknown_chunks(png_ptr, info_ptr, &unk, 1); ++ ++ /* Get the internal pointer and feed it straight back (append). */ ++ got_num = png_get_unknown_chunks(png_ptr, info_ptr, &got_unknowns); ++ if (got_unknowns == NULL || got_num != 1) ++ { ++ fprintf(stderr, ++ "pnggetset: png_get_unknown_chunks returned unexpected values\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ /* This is the critical call: got_unknowns aliases internal storage. */ ++ png_set_unknown_chunks(png_ptr, info_ptr, got_unknowns, got_num); ++ ++ /* Verify the original entry survived. */ ++ got_unknowns = NULL; ++ got_num = png_get_unknown_chunks(png_ptr, info_ptr, &got_unknowns); ++ if (got_unknowns == NULL || got_num != 2) ++ { ++ fprintf(stderr, ++ "pnggetset: unknown_chunks count %d, expected 2 after roundtrip\n", ++ got_num); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ if (memcmp(got_unknowns[0].name, "teSt", 4) != 0 || ++ got_unknowns[0].size != sizeof test_data || ++ memcmp(got_unknowns[0].data, test_data, sizeof test_data) != 0) ++ { ++ fprintf(stderr, ++ "pnggetset: unknown chunk 0 corrupted after roundtrip\n"); ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 1; ++ } ++ ++ png_destroy_write_struct(&png_ptr, &info_ptr); ++ return 0; ++} ++#endif /* PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED */ ++ + int + main(void) + { +@@ -319,6 +603,42 @@ + { + printf("FAIL\n"); + result = 1; ++ } ++ else ++ printf("PASS\n"); ++#endif ++ ++#ifdef PNG_TEXT_SUPPORTED ++ printf("Testing tEXt get-then-set roundtrip... "); ++ fflush(stdout); ++ if (test_text_roundtrip() != 0) ++ { ++ printf("FAIL\n"); ++ result = 1; ++ } ++ else ++ printf("PASS\n"); ++#endif ++ ++#ifdef PNG_sPLT_SUPPORTED ++ printf("Testing sPLT get-then-set roundtrip... "); ++ fflush(stdout); ++ if (test_splt_roundtrip() != 0) ++ { ++ printf("FAIL\n"); ++ result = 1; ++ } ++ else ++ printf("PASS\n"); ++#endif ++ ++#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED ++ printf("Testing unknown chunks get-then-set roundtrip... "); ++ fflush(stdout); ++ if (test_unknown_roundtrip() != 0) ++ { ++ printf("FAIL\n"); ++ result = 1; + } + else + printf("PASS\n"); +--- a/pngset.c ++++ b/pngset.c +@@ -953,6 +953,7 @@ + png_const_textp text_ptr, int num_text) + { + int i; ++ png_textp old_text = NULL; + + png_debug1(1, "in text storage function, chunk typeid = 0x%lx", + png_ptr == NULL ? 0xabadca11UL : (unsigned long)png_ptr->chunk_name); +@@ -1000,7 +1001,10 @@ + return 1; + } + +- png_free(png_ptr, info_ptr->text); ++ /* Defer freeing the old array until after the copy loop below, ++ * in case text_ptr aliases info_ptr->text (getter-to-setter). ++ */ ++ old_text = info_ptr->text; + + info_ptr->text = new_text; + info_ptr->free_me |= PNG_FREE_TEXT; +@@ -1085,6 +1089,7 @@ + { + png_chunk_report(png_ptr, "text chunk: out of memory", + PNG_CHUNK_WRITE_ERROR); ++ png_free(png_ptr, old_text); + + return 1; + } +@@ -1138,6 +1143,8 @@ + png_debug1(3, "transferred text chunk %d", info_ptr->num_text); + } + ++ png_free(png_ptr, old_text); ++ + return 0; + } + #endif +@@ -1275,6 +1282,7 @@ + */ + { + png_sPLT_tp np; ++ png_sPLT_tp old_spalettes; + + png_debug1(1, "in %s storage function", "sPLT"); + +@@ -1295,7 +1303,10 @@ + return; + } + +- png_free(png_ptr, info_ptr->splt_palettes); ++ /* Defer freeing the old array until after the copy loop below, ++ * in case entries aliases info_ptr->splt_palettes (getter-to-setter). ++ */ ++ old_spalettes = info_ptr->splt_palettes; + + info_ptr->splt_palettes = np; + info_ptr->free_me |= PNG_FREE_SPLT; +@@ -1359,6 +1370,8 @@ + } + while (--nentries); + ++ png_free(png_ptr, old_spalettes); ++ + if (nentries > 0) + png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR); + } +@@ -1407,6 +1420,7 @@ + png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns) + { + png_unknown_chunkp np; ++ png_unknown_chunkp old_unknowns; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 || + unknowns == NULL) +@@ -1453,7 +1467,10 @@ + return; + } + +- png_free(png_ptr, info_ptr->unknown_chunks); ++ /* Defer freeing the old array until after the copy loop below, ++ * in case unknowns aliases info_ptr->unknown_chunks (getter-to-setter). ++ */ ++ old_unknowns = info_ptr->unknown_chunks; + + info_ptr->unknown_chunks = np; /* safe because it is initialized */ + info_ptr->free_me |= PNG_FREE_UNKN; +@@ -1499,6 +1516,8 @@ + ++np; + ++(info_ptr->unknown_chunks_num); + } ++ ++ png_free(png_ptr, old_unknowns); + } + + void PNGAPI diff -Nru libpng1.6-1.6.48/debian/patches/series libpng1.6-1.6.48/debian/patches/series --- libpng1.6-1.6.48/debian/patches/series 2026-03-30 17:29:23.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/series 2026-05-08 12:19:08.000000000 +0000 @@ -15,3 +15,7 @@ CVE-2026-33416-part3.patch CVE-2026-33416-part4.patch CVE-2026-33636.patch +CVE-2026-34757-part1.patch +CVE-2026-34757-part2.patch +CVE-2026-33416-regression-fix.patch +CVE-2026-33416-regression-fix-test.patch