Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libpng12 for openSUSE:Factory 
checked in at 2026-04-28 13:22:31
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libpng12 (Old)
 and      /work/SRC/openSUSE:Factory/.libpng12.new.11940 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libpng12"

Tue Apr 28 13:22:31 2026 rev:43 rq:1349750 version:1.2.59

Changes:
--------
--- /work/SRC/openSUSE:Factory/libpng12/libpng12.changes        2025-12-04 
11:27:30.000091370 +0100
+++ /work/SRC/openSUSE:Factory/.libpng12.new.11940/libpng12.changes     
2026-04-28 13:22:33.155156360 +0200
@@ -1,0 +2,9 @@
+Tue Apr 28 08:05:20 UTC 2026 - Petr Gajdos <[email protected]>
+
+- added patches
+  CVE-2026-33416: use-after-free via pointer aliasing in `png_set_tRNS` and 
`png_set_PLTE` can lead to arbitrary code execution [bsc#1260754]
+  * libpng12-CVE-2026-33416.patch
+  CVE-2026-34757: Information disclosure and data corruption via 
use-after-free vulnerability [bsc#1261957]
+  * libpng12-CVE-2026-34757.patch
+
+-------------------------------------------------------------------

New:
----
  libpng12-CVE-2026-33416.patch
  libpng12-CVE-2026-34757.patch

----------(New B)----------
  New:  CVE-2026-33416: use-after-free via pointer aliasing in `png_set_tRNS` 
and `png_set_PLTE` can lead to arbitrary code execution [bsc#1260754]
  * libpng12-CVE-2026-33416.patch
  CVE-2026-34757: Information disclosure and data corruption via use-after-free 
vulnerability [bsc#1261957]
  New:  CVE-2026-34757: Information disclosure and data corruption via 
use-after-free vulnerability [bsc#1261957]
  * libpng12-CVE-2026-34757.patch
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libpng12.spec ++++++
--- /var/tmp/diff_new_pack.QU0piQ/_old  2026-04-28 13:22:33.935188378 +0200
+++ /var/tmp/diff_new_pack.QU0piQ/_new  2026-04-28 13:22:33.939188543 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package libpng12
 #
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -36,6 +36,10 @@
 Patch1:         libpng-1.2.51-CVE-2013-7354.patch
 # CVE-2025-64505 [bsc#1254157], heap buffer over-read in `png_do_quantize` via 
malformed palette index
 Patch2:         libpng12-CVE-2025-64505.patch
+# CVE-2026-33416: use-after-free via pointer aliasing in `png_set_tRNS` and 
`png_set_PLTE` can lead to arbitrary code execution [bsc#1260754]
+Patch3:         libpng12-CVE-2026-33416.patch
+# CVE-2026-34757: Information disclosure and data corruption via 
use-after-free vulnerability [bsc#1261957]
+Patch4:         libpng12-CVE-2026-34757.patch
 BuildRequires:  libtool
 BuildRequires:  pkg-config
 BuildRequires:  zlib-devel
@@ -105,7 +109,7 @@
 # PNG_SAFE_LIMITS_SUPPORTED: 
http://www.openwall.com/lists/oss-security/2015/01/10/1
 export CFLAGS="%optflags -O3 -DPNG_SAFE_LIMITS_SUPPORTED 
-DPNG_SKIP_SETJMP_CHECK $(getconf LFS_CFLAGS)"
 export LDFLAGS="-Wl,-z,relro,-z,now"
-
+autoreconf -fi
 %configure \
               --disable-static \
               --with-libpng-compat=no

++++++ libpng12-CVE-2026-33416.patch ++++++
Index: libpng-1.2.59/Makefile.am
===================================================================
--- libpng-1.2.59.orig/Makefile.am
+++ libpng-1.2.59/Makefile.am
@@ -14,10 +14,12 @@ PNGLIB_BASENAME= libpng@PNGLIB_MAJOR@@PN
 AUTOMAKE_OPTIONS = foreign
 
 # test programs - run on make check, make distcheck
-check_PROGRAMS= pngtest
+check_PROGRAMS= pngtest cve-2026-33416
 pngtest_SOURCES = pngtest.c
 pngtest_LDADD = libpng12.la
-TESTS = test-pngtest.sh
+cve_2026_33416_SOURCES = cve-2026-33416.c
+cve_2026_33416_LDADD = libpng12.la
+TESTS = test-pngtest.sh cve-2026-33416
 TESTS_ENVIRONMENT= srcdir=$(srcdir)
 
 # man pages
@@ -89,6 +91,7 @@ EXTRA_DIST= \
        ${srcdir}/contrib/pngsuite/* \
        ${srcdir}/contrib/visupng/* \
        $(TESTS) \
+       cve-2026-33416.c \
        example.c libpng-1.2.59.txt pnggccrd.c pngvcrd.c
 
 CLEANFILES= pngout.png libpng12.pc libpng12-config libpng.vers \
Index: libpng-1.2.59/pngread.c
===================================================================
--- libpng-1.2.59.orig/pngread.c
+++ libpng-1.2.59/pngread.c
@@ -1233,35 +1233,30 @@ png_read_destroy(png_structp png_ptr, pn
    png_free(png_ptr, png_ptr->gamma_from_1);
    png_free(png_ptr, png_ptr->gamma_to_1);
 #endif
+   png_zfree(png_ptr, png_ptr->palette);
+   png_ptr->palette = NULL;
 #ifdef PNG_FREE_ME_SUPPORTED
-   if (png_ptr->free_me & PNG_FREE_PLTE)
-      png_zfree(png_ptr, png_ptr->palette);
    png_ptr->free_me &= ~PNG_FREE_PLTE;
 #else
-   if (png_ptr->flags & PNG_FLAG_FREE_PLTE)
-      png_zfree(png_ptr, png_ptr->palette);
    png_ptr->flags &= ~PNG_FLAG_FREE_PLTE;
 #endif
+
 #if defined(PNG_tRNS_SUPPORTED) || \
     defined(PNG_READ_EXPAND_SUPPORTED) || 
defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_free(png_ptr, png_ptr->trans);
+   png_ptr->trans = NULL;
 #ifdef PNG_FREE_ME_SUPPORTED
-   if (png_ptr->free_me & PNG_FREE_TRNS)
-      png_free(png_ptr, png_ptr->trans);
    png_ptr->free_me &= ~PNG_FREE_TRNS;
 #else
-   if (png_ptr->flags & PNG_FLAG_FREE_TRNS)
-      png_free(png_ptr, png_ptr->trans);
    png_ptr->flags &= ~PNG_FLAG_FREE_TRNS;
 #endif
 #endif
 #ifdef PNG_READ_hIST_SUPPORTED
+   png_free(png_ptr, png_ptr->hist);
+   png_ptr->hist = NULL;
 #ifdef PNG_FREE_ME_SUPPORTED
-   if (png_ptr->free_me & PNG_FREE_HIST)
-      png_free(png_ptr, png_ptr->hist);
    png_ptr->free_me &= ~PNG_FREE_HIST;
 #else
-   if (png_ptr->flags & PNG_FLAG_FREE_HIST)
-      png_free(png_ptr, png_ptr->hist);
    png_ptr->flags &= ~PNG_FLAG_FREE_HIST;
 #endif
 #endif
Index: libpng-1.2.59/pngrtran.c
===================================================================
--- libpng-1.2.59.orig/pngrtran.c
+++ libpng-1.2.59/pngrtran.c
@@ -471,7 +471,13 @@ png_set_dither(png_structp png_ptr, png_
    }
    if (png_ptr->palette == NULL)
    {
-      png_ptr->palette = palette;
+      /* Allocate an owned copy rather than aliasing the caller's pointer,
+       * so that png_read_destroy can free png_ptr->palette unconditionally.
+       */
+      png_ptr->palette = (png_colorp)png_calloc(png_ptr,
+        PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color));
+      memcpy(png_ptr->palette, palette, (unsigned int)num_palette *
+          (sizeof (png_color)));
    }
    png_ptr->num_palette = (png_uint_16)num_palette;
 
@@ -1208,6 +1214,21 @@ png_read_transform_info(png_structp png_
 {
    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)
+      {
+         /* 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).
+          */
+         png_memcpy(info_ptr->palette, png_ptr->palette,
+         PNG_MAX_PALETTE_LENGTH * (png_sizeof(png_color)));
+      }
+   }
+
 #ifdef PNG_READ_EXPAND_SUPPORTED
    if (png_ptr->transformations & PNG_EXPAND)
    {
Index: libpng-1.2.59/pngset.c
===================================================================
--- libpng-1.2.59.orig/pngset.c
+++ libpng-1.2.59/pngset.c
@@ -200,9 +200,11 @@ png_set_hIST(png_structp png_ptr, png_in
 #ifdef PNG_FREE_ME_SUPPORTED
    png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);
 #endif
+
    /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in
     * version 1.2.1
     */
+   png_free(png_ptr, png_ptr->hist);
    png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr,
       (png_uint_32)(PNG_MAX_PALETTE_LENGTH * png_sizeof(png_uint_16)));
    if (png_ptr->hist == NULL)
@@ -211,11 +213,19 @@ png_set_hIST(png_structp png_ptr, png_in
       return;
    }
 
+   info_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr,
+      (png_uint_32)(PNG_MAX_PALETTE_LENGTH * png_sizeof(png_uint_16)));
+   if (info_ptr->hist == NULL)
+   {
+      png_free(png_ptr, png_ptr->hist);
+      png_ptr->hist = NULL;
+      png_warning(png_ptr, "Insufficient memory for hIST chunk data.");
+      return;
+   }
+
    for (i = 0; i < info_ptr->num_palette; i++)
-      png_ptr->hist[i] = hist[i];
-   info_ptr->hist = png_ptr->hist;
+      png_ptr->hist[i] = info_ptr->hist[i] = hist[i];
    info_ptr->valid |= PNG_INFO_hIST;
-
 #ifdef PNG_FREE_ME_SUPPORTED
    info_ptr->free_me |= PNG_FREE_HIST;
 #else
@@ -224,6 +234,7 @@ png_set_hIST(png_structp png_ptr, png_in
 }
 #endif
 
+
 void PNGAPI
 png_set_IHDR(png_structp png_ptr, png_infop info_ptr,
    png_uint_32 width, png_uint_32 height, int bit_depth,
@@ -483,10 +494,17 @@ png_set_PLTE(png_structp png_ptr, png_in
     * of num_palette entries, in case of an invalid PNG file or incorrect
     * call to png_set_PLTE() with too-large sample values.
     */
+
+   /* Always separate png_ptr->palette from info_ptr->palette */
+   png_free(png_ptr, png_ptr->palette);
    png_ptr->palette = (png_colorp)png_calloc(png_ptr,
       PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color));
    png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof(png_color));
-   info_ptr->palette = png_ptr->palette;
+
+   info_ptr->palette = (png_colorp)png_calloc(png_ptr,
+      PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color));
+   png_memcpy(info_ptr->palette, palette, num_palette * png_sizeof(png_color));
+
    info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;
 
 #ifdef PNG_FREE_ME_SUPPORTED
@@ -896,10 +914,20 @@ png_set_tRNS(png_structp png_ptr, png_in
 #endif
 
        /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */
-       png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr,
+       png_free(png_ptr, png_ptr->trans);
+       png_ptr->trans = (png_bytep)png_malloc(png_ptr,
+           (png_uint_32)PNG_MAX_PALETTE_LENGTH);
+       info_ptr->trans = (png_bytep)png_malloc(png_ptr,
            (png_uint_32)PNG_MAX_PALETTE_LENGTH);
+
+       png_memset(png_ptr->trans, 0xff, PNG_MAX_PALETTE_LENGTH);
+       png_memset(info_ptr->trans, 0xff, PNG_MAX_PALETTE_LENGTH);
+
        if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
+       {
+          png_memcpy(png_ptr->trans, trans, (png_size_t)num_trans);
           png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans);
+       }
    }
 
    if (trans_values != NULL)
Index: libpng-1.2.59/pngwrite.c
===================================================================
--- libpng-1.2.59.orig/pngwrite.c
+++ libpng-1.2.59/pngwrite.c
@@ -1129,6 +1129,16 @@ png_write_destroy(png_structp png_ptr)
    /* Free our memory.  png_free checks NULL for us. */
    png_free(png_ptr, png_ptr->zbuf);
    png_free(png_ptr, png_ptr->row_buf);
+   png_zfree(png_ptr, png_ptr->palette);
+   png_ptr->palette = NULL;
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+   png_free(png_ptr, png_ptr->trans);
+   png_ptr->trans = NULL;
+#endif
+#ifdef PNG_WRITE_hIST_SUPPORTED
+   png_free(png_ptr, png_ptr->hist);
+   png_ptr->hist = NULL;
+#endif
 #ifdef PNG_WRITE_FILTER_SUPPORTED
    png_free(png_ptr, png_ptr->prev_row);
    png_free(png_ptr, png_ptr->sub_row);
Index: libpng-1.2.59/cve-2026-33416.c
===================================================================
--- /dev/null
+++ libpng-1.2.59/cve-2026-33416.c
@@ -0,0 +1,196 @@
+#include "png.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Test case for CVE-2026-33416: Use-after-free in png_set_PLTE, png_set_tRNS, 
png_set_hIST.
+ * This test verifies that info_ptr and png_ptr buffers are decoupled and
+ * that calling setter functions multiple times does not lead to leaks or UAF.
+ */
+
+static int test_PLTE(void) {
+    printf("Testing PLTE decoupling...\n");
+    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, 
NULL, NULL);
+    if (!png_ptr) return 1;
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        return 1;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return 1;
+    }
+
+    png_color palette[1];
+    palette[0].red = 100; palette[0].green = 150; palette[0].blue = 200;
+
+    // First set
+    png_set_PLTE(png_ptr, info_ptr, palette, 1);
+    if (info_ptr->palette == png_ptr->palette) {
+        fprintf(stderr, "FAIL: PLTE pointers are aliased after first set\n");
+        goto fail;
+    }
+
+    // Second set (should free old png_ptr->palette and info_ptr->palette)
+    palette[0].red = 255;
+    png_set_PLTE(png_ptr, info_ptr, palette, 1);
+    if (info_ptr->palette == png_ptr->palette) {
+        fprintf(stderr, "FAIL: PLTE pointers are aliased after second set\n");
+        goto fail;
+    }
+
+    // Verify independent freeing
+    png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, -1);
+    if (info_ptr->palette != NULL) {
+        fprintf(stderr, "FAIL: info_ptr->palette not NULL after free\n");
+        goto fail;
+    }
+    if (png_ptr->palette == NULL) {
+        fprintf(stderr, "FAIL: png_ptr->palette incorrectly NULL after 
info_ptr free\n");
+        goto fail;
+    }
+    if (png_ptr->palette[0].red != 255) {
+        fprintf(stderr, "FAIL: png_ptr->palette data corrupted after info_ptr 
free\n");
+        goto fail;
+    }
+
+    printf("PASS: PLTE\n");
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    return 0;
+
+fail:
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    return 1;
+}
+
+static int test_tRNS(void) {
+    printf("Testing tRNS decoupling...\n");
+    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, 
NULL, NULL);
+    if (!png_ptr) return 1;
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        return 1;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return 1;
+    }
+
+    png_byte trans[1] = {123};
+
+    // First set
+    png_set_tRNS(png_ptr, info_ptr, trans, 1, NULL);
+    if (info_ptr->trans == png_ptr->trans) {
+        fprintf(stderr, "FAIL: tRNS pointers are aliased after first set\n");
+        goto fail;
+    }
+
+    // Second set
+    trans[0] = 200;
+    png_set_tRNS(png_ptr, info_ptr, trans, 1, NULL);
+    if (info_ptr->trans == png_ptr->trans) {
+        fprintf(stderr, "FAIL: tRNS pointers are aliased after second set\n");
+        goto fail;
+    }
+
+    // Verify independent freeing
+    png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1);
+    if (info_ptr->trans != NULL) {
+        fprintf(stderr, "FAIL: info_ptr->trans not NULL after free\n");
+        goto fail;
+    }
+    if (png_ptr->trans == NULL) {
+        fprintf(stderr, "FAIL: png_ptr->trans incorrectly NULL after info_ptr 
free\n");
+        goto fail;
+    }
+    if (png_ptr->trans[0] != 200) {
+        fprintf(stderr, "FAIL: png_ptr->trans data corrupted after info_ptr 
free\n");
+        goto fail;
+    }
+
+    printf("PASS: tRNS\n");
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    return 0;
+
+fail:
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    return 1;
+}
+
+static int test_hIST(void) {
+    printf("Testing hIST decoupling...\n");
+    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, 
NULL, NULL);
+    if (!png_ptr) return 1;
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        return 1;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return 1;
+    }
+
+    // Must set palette before hIST in libpng-1.2
+    png_color palette[1] = {{0,0,0}};
+    png_set_PLTE(png_ptr, info_ptr, palette, 1);
+
+    png_uint_16 hist[1] = {500};
+
+    // First set
+    png_set_hIST(png_ptr, info_ptr, hist);
+    if (info_ptr->hist == png_ptr->hist) {
+        fprintf(stderr, "FAIL: hIST pointers are aliased after first set\n");
+        goto fail;
+    }
+
+    // Second set
+    hist[0] = 1000;
+    png_set_hIST(png_ptr, info_ptr, hist);
+    if (info_ptr->hist == png_ptr->hist) {
+        fprintf(stderr, "FAIL: hIST pointers are aliased after second set\n");
+        goto fail;
+    }
+
+    // Verify independent freeing
+    png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, -1);
+    if (info_ptr->hist != NULL) {
+        fprintf(stderr, "FAIL: info_ptr->hist not NULL after free\n");
+        goto fail;
+    }
+    if (png_ptr->hist == NULL) {
+        fprintf(stderr, "FAIL: png_ptr->hist incorrectly NULL after info_ptr 
free\n");
+        goto fail;
+    }
+    if (png_ptr->hist[0] != 1000) {
+        fprintf(stderr, "FAIL: png_ptr->hist data corrupted after info_ptr 
free\n");
+        goto fail;
+    }
+
+    printf("PASS: hIST\n");
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    return 0;
+
+fail:
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    return 1;
+}
+
+int main(void) {
+    int count = 0;
+    count += test_PLTE();
+    count += test_tRNS();
+    count += test_hIST();
+    if (count == 0) {
+        printf("ALL TESTS PASSED\n");
+        return 0;
+    }
+    printf("%d TESTS FAILED\n", count);
+    return 1;
+}

++++++ libpng12-CVE-2026-34757.patch ++++++
Index: libpng-1.2.59/Makefile.am
===================================================================
--- libpng-1.2.59.orig/Makefile.am
+++ libpng-1.2.59/Makefile.am
@@ -14,12 +14,14 @@ PNGLIB_BASENAME= libpng@PNGLIB_MAJOR@@PN
 AUTOMAKE_OPTIONS = foreign
 
 # test programs - run on make check, make distcheck
-check_PROGRAMS= pngtest cve-2026-33416
+check_PROGRAMS= pngtest cve-2026-33416 cve-2026-34757
 pngtest_SOURCES = pngtest.c
 pngtest_LDADD = libpng12.la
 cve_2026_33416_SOURCES = cve-2026-33416.c
 cve_2026_33416_LDADD = libpng12.la
-TESTS = test-pngtest.sh cve-2026-33416
+cve_2026_34757_SOURCES = cve-2026-34757.c
+cve_2026_34757_LDADD = libpng12.la
+TESTS = test-pngtest.sh cve-2026-33416 cve-2026-34757
 TESTS_ENVIRONMENT= srcdir=$(srcdir)
 
 # man pages
@@ -92,6 +94,7 @@ EXTRA_DIST= \
        ${srcdir}/contrib/visupng/* \
        $(TESTS) \
        cve-2026-33416.c \
+       cve-2026-34757.c \
        example.c libpng-1.2.59.txt pnggccrd.c pngvcrd.c
 
 CLEANFILES= pngout.png libpng12.pc libpng12-config libpng.vers \
Index: libpng-1.2.59/pngset.c
===================================================================
--- libpng-1.2.59.orig/pngset.c
+++ libpng-1.2.59/pngset.c
@@ -182,6 +182,7 @@ png_set_gAMA_fixed(png_structp png_ptr,
 void PNGAPI
 png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist)
 {
+   png_uint_16 safe_hist[PNG_MAX_PALETTE_LENGTH];
    int i;
 
    png_debug1(1, "in %s storage function", "hIST");
@@ -197,6 +198,13 @@ png_set_hIST(png_structp png_ptr, png_in
       return;
    }
 
+   /* Snapshot the caller's hist before freeing, in case it points to
+    * info_ptr->hist (getter-to-setter aliasing).
+    */
+   png_memcpy(safe_hist, hist, (unsigned int)info_ptr->num_palette *
+       (png_sizeof(png_uint_16)));
+   hist = safe_hist;
+
 #ifdef PNG_FREE_ME_SUPPORTED
    png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);
 #endif
@@ -460,7 +468,7 @@ void PNGAPI
 png_set_PLTE(png_structp png_ptr, png_infop info_ptr,
    png_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");
@@ -482,6 +490,15 @@ png_set_PLTE(png_structp png_ptr, png_in
       }
    }
 
+   /* Snapshot the caller's palette before freeing, in case it points to
+    * info_ptr->palette (getter-to-setter aliasing).
+    */
+   if (num_palette > 0)
+      png_memcpy(safe_palette, palette, (unsigned int)num_palette *
+          (png_sizeof(png_color)));
+
+   palette = safe_palette;
+
    /* It may not actually be necessary to set png_ptr->palette here;
     * we do it for backward compatibility with the way the png_handle_tRNS
     * function used to do the allocation.
@@ -680,6 +697,7 @@ png_set_text_2(png_structp png_ptr, png_
                int num_text)
 {
    int i;
+   png_textp old_text = NULL;
 
    png_debug1(1, "in %s storage function", ((png_ptr == NULL ||
       png_ptr->chunk_name[0] == '\0') ?
@@ -709,8 +727,6 @@ png_set_text_2(png_structp png_ptr, png_
 
       if (info_ptr->text != NULL)
       {
-         png_textp old_text;
-
          info_ptr->max_text = info_ptr->num_text + num_text + 8;
          old_text = info_ptr->text;
 
@@ -725,7 +741,6 @@ png_set_text_2(png_structp png_ptr, png_
          }
          png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max_text *
             png_sizeof(png_text)));
-         png_free(png_ptr, old_text);
       }
       else
       {
@@ -807,7 +822,10 @@ png_set_text_2(png_structp png_ptr, png_
          (png_uint_32)
          (key_len + text_length + lang_len + lang_key_len + 4));
       if (textp->key == NULL)
+      {
+         png_free(png_ptr, old_text);
          return(1);
+      }
       png_debug2(2, "Allocated %lu bytes at %p in png_set_text",
                  (png_uint_32)
                  (key_len + lang_len + lang_key_len + text_length + 4),
@@ -858,6 +876,7 @@ png_set_text_2(png_structp png_ptr, png_
       info_ptr->num_text++;
       png_debug1(3, "transferred text chunk %d", info_ptr->num_text);
    }
+   png_free(png_ptr, old_text);
    return(0);
 }
 #endif
@@ -904,6 +923,16 @@ png_set_tRNS(png_structp png_ptr, png_in
 
    if (trans != NULL)
    {
+       /* Snapshot the caller's trans before freeing, in case it
+        * points to info_ptr->trans (getter-to-setter aliasing).
+        */
+       png_byte safe_trans[PNG_MAX_PALETTE_LENGTH];
+
+       if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
+          png_memcpy(safe_trans, trans, (png_size_t)num_trans);
+
+       trans = safe_trans;
+
        /* It may not actually be necessary to set png_ptr->trans here;
         * we do it for backward compatibility with the way the png_handle_tRNS
         * function used to do the allocation.
@@ -973,6 +1002,7 @@ png_set_sPLT(png_structp png_ptr,
  */
 {
    png_sPLT_tp np;
+   png_sPLT_tp old_spalettes;
    int i;
 
    if (png_ptr == NULL || info_ptr == NULL)
@@ -999,7 +1029,11 @@ png_set_sPLT(png_structp png_ptr,
 
    png_memcpy(np, info_ptr->splt_palettes,
        info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t));
-   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=NULL;
 
    for (i = 0; i < nentries; i++)
@@ -1035,6 +1069,7 @@ png_set_sPLT(png_structp png_ptr,
 
    info_ptr->splt_palettes = np;
    info_ptr->splt_palettes_num += nentries;
+   png_free(png_ptr, old_spalettes);
    info_ptr->valid |= PNG_INFO_sPLT;
 #ifdef PNG_FREE_ME_SUPPORTED
    info_ptr->free_me |= PNG_FREE_SPLT;
@@ -1048,6 +1083,7 @@ png_set_unknown_chunks(png_structp png_p
    png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)
 {
    png_unknown_chunkp np;
+   png_unknown_chunkp old_unknowns;
    int i;
 
    if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0)
@@ -1073,7 +1109,11 @@ png_set_unknown_chunks(png_structp png_p
 
    png_memcpy(np, info_ptr->unknown_chunks,
        info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk));
-   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 = NULL;
 
    for (i = 0; i < num_unknowns; i++)
@@ -1107,6 +1147,7 @@ png_set_unknown_chunks(png_structp png_p
 
    info_ptr->unknown_chunks = np;
    info_ptr->unknown_chunks_num += num_unknowns;
+   png_free(png_ptr, old_unknowns);
 #ifdef PNG_FREE_ME_SUPPORTED
    info_ptr->free_me |= PNG_FREE_UNKN;
 #endif
Index: libpng-1.2.59/cve-2026-34757.c
===================================================================
--- /dev/null
+++ libpng-1.2.59/cve-2026-34757.c
@@ -0,0 +1,372 @@
+/* cve-2026-34757.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 various chunks.
+ *
+ * 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 "png.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int test_PLTE_aliasing(void) {
+    int i;
+    printf("Testing PLTE aliasing... ");
+    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 
NULL, NULL);
+    if (!png_ptr) return 1;
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_write_struct(&png_ptr, NULL);
+        return 1;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return 1;
+    }
+
+    png_color palette[4];
+    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_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);
+
+    png_colorp got_palette;
+    int num_palette;
+    png_get_PLTE(png_ptr, info_ptr, &got_palette, &num_palette);
+
+    /* Critical call: got_palette aliases internal info_ptr->palette */
+    png_set_PLTE(png_ptr, info_ptr, got_palette, num_palette);
+
+    /* Verify data */
+    png_get_PLTE(png_ptr, info_ptr, &got_palette, &num_palette);
+    if (num_palette != 4) {
+        fprintf(stderr, "FAIL: PLTE count mismatch\n");
+        goto fail;
+    }
+    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, "FAIL: PLTE data corruption at %d\n", i);
+            goto fail;
+        }
+    }
+
+    printf("PASS\n");
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 0;
+
+fail:
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 1;
+}
+
+#ifdef PNG_hIST_SUPPORTED
+static int test_hIST_aliasing(void) {
+    int i;
+    printf("Testing hIST aliasing... ");
+    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 
NULL, NULL);
+    if (!png_ptr) return 1;
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_write_struct(&png_ptr, NULL);
+        return 1;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return 1;
+    }
+
+    png_color palette[4] = {{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}};
+    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);
+
+    png_uint_16 hist[4] = {100, 200, 300, 400};
+    png_set_hIST(png_ptr, info_ptr, hist);
+
+    png_uint_16p got_hist;
+    png_get_hIST(png_ptr, info_ptr, &got_hist);
+
+    /* Critical call: got_hist aliases internal info_ptr->hist */
+    png_set_hIST(png_ptr, info_ptr, got_hist);
+
+    /* Verify data */
+    png_get_hIST(png_ptr, info_ptr, &got_hist);
+    for (i = 0; i < 4; i++) {
+        if (got_hist[i] != (png_uint_16)((i + 1) * 100)) {
+            fprintf(stderr, "FAIL: hIST data corruption at %d\n", i);
+            goto fail;
+        }
+    }
+
+    printf("PASS\n");
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 0;
+
+fail:
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 1;
+}
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+static int test_tRNS_aliasing(void) {
+    printf("Testing tRNS aliasing... ");
+    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 
NULL, NULL);
+    if (!png_ptr) return 1;
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_write_struct(&png_ptr, NULL);
+        return 1;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return 1;
+    }
+
+    png_color palette[4] = {{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}};
+    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);
+
+    png_byte trans[4] = {255, 128, 64, 0};
+    png_set_tRNS(png_ptr, info_ptr, trans, 4, NULL);
+
+    png_bytep got_trans;
+    int num_trans;
+    png_color_16p trans_values;
+    png_get_tRNS(png_ptr, info_ptr, &got_trans, &num_trans, &trans_values);
+
+    /* Critical call: got_trans aliases internal info_ptr->trans */
+    png_set_tRNS(png_ptr, info_ptr, got_trans, num_trans, trans_values);
+
+    /* Verify data */
+    png_get_tRNS(png_ptr, info_ptr, &got_trans, &num_trans, &trans_values);
+    if (num_trans != 4) {
+        fprintf(stderr, "FAIL: tRNS count mismatch\n");
+        goto fail;
+    }
+    if (got_trans[1] != 128 || got_trans[2] != 64 || got_trans[3] != 0) {
+        fprintf(stderr, "FAIL: tRNS data corruption\n");
+        goto fail;
+    }
+
+    printf("PASS\n");
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 0;
+
+fail:
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 1;
+}
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED
+static int test_text_aliasing(void) {
+    printf("Testing text aliasing... ");
+    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 
NULL, NULL);
+    if (!png_ptr) return 1;
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_write_struct(&png_ptr, NULL);
+        return 1;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return 1;
+    }
+
+    png_text text[2];
+    text[0].compression = PNG_TEXT_COMPRESSION_NONE;
+    text[0].key = (png_charp)"Key1";
+    text[0].text = (png_charp)"Value1";
+    text[1].compression = PNG_TEXT_COMPRESSION_NONE;
+    text[1].key = (png_charp)"Key2";
+    text[1].text = (png_charp)"Value2";
+
+    png_set_text(png_ptr, info_ptr, text, 2);
+
+    png_textp got_text;
+    int num_text;
+    png_get_text(png_ptr, info_ptr, &got_text, &num_text);
+
+    /* Trigger reallocation by adding enough entries to exceed max_text 
(initially 0+2+8=10, so let's add 10 more) */
+    /* This will use the deferred free path in png_set_text_2 */
+    {
+        int i;
+        for (i = 0; i < 5; i++) {
+            png_set_text(png_ptr, info_ptr, got_text, num_text);
+        }
+    }
+
+    /* Verify data */
+    png_get_text(png_ptr, info_ptr, &got_text, &num_text);
+    if (num_text != 12) {
+        fprintf(stderr, "FAIL: text count mismatch: %d\n", num_text);
+        goto fail;
+    }
+    if (strcmp(got_text[0].key, "Key1") != 0 || strcmp(got_text[11].text, 
"Value2") != 0) {
+        fprintf(stderr, "FAIL: text data corruption\n");
+        goto fail;
+    }
+
+    printf("PASS\n");
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 0;
+
+fail:
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 1;
+}
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+static int test_sPLT_aliasing(void) {
+    printf("Testing sPLT aliasing... ");
+    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 
NULL, NULL);
+    if (!png_ptr) return 1;
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_write_struct(&png_ptr, NULL);
+        return 1;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return 1;
+    }
+
+    png_sPLT_t splt;
+    png_sPLT_entry entries[1];
+    entries[0].red = 100; entries[0].green = 200; entries[0].blue = 300; 
entries[0].alpha = 400; entries[0].frequency = 500;
+    splt.name = (png_charp)"Test sPLT";
+    splt.depth = 8;
+    splt.entries = entries;
+    splt.nentries = 1;
+
+    png_set_sPLT(png_ptr, info_ptr, &splt, 1);
+
+    png_sPLT_tp got_splt;
+    int num_splt = (int)png_get_sPLT(png_ptr, info_ptr, &got_splt);
+
+    /* Critical call: got_splt aliases internal info_ptr->splt_palettes */
+    png_set_sPLT(png_ptr, info_ptr, got_splt, num_splt);
+
+    /* Verify data */
+    num_splt = (int)png_get_sPLT(png_ptr, info_ptr, &got_splt);
+    if (num_splt != 2) {
+        fprintf(stderr, "FAIL: sPLT count mismatch: %d\n", num_splt);
+        goto fail;
+    }
+    if (strcmp(got_splt[0].name, "Test sPLT") != 0 || 
got_splt[1].entries[0].red != 100) {
+        fprintf(stderr, "FAIL: sPLT data corruption\n");
+        goto fail;
+    }
+
+    printf("PASS\n");
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 0;
+
+fail:
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 1;
+}
+#endif
+
+#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+static int test_unknown_aliasing(void) {
+    printf("Testing unknown chunks aliasing... ");
+    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 
NULL, NULL);
+    if (!png_ptr) return 1;
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_write_struct(&png_ptr, NULL);
+        return 1;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return 1;
+    }
+
+    png_unknown_chunk unknowns[1];
+    memcpy(unknowns[0].name, "teSt", 5);
+    unknowns[0].data = (png_bytep)"TestData";
+    unknowns[0].size = 8;
+    unknowns[0].location = PNG_HAVE_IHDR;
+
+    png_set_unknown_chunks(png_ptr, info_ptr, unknowns, 1);
+
+    png_unknown_chunkp got_unknowns;
+    int num_unknowns = (int)png_get_unknown_chunks(png_ptr, info_ptr, 
&got_unknowns);
+
+    /* Critical call: got_unknowns aliases internal info_ptr->unknown_chunks */
+    png_set_unknown_chunks(png_ptr, info_ptr, got_unknowns, num_unknowns);
+
+    /* Verify data */
+    num_unknowns = (int)png_get_unknown_chunks(png_ptr, info_ptr, 
&got_unknowns);
+    if (num_unknowns != 2) {
+        fprintf(stderr, "FAIL: unknown chunks count mismatch: %d\n", 
num_unknowns);
+        goto fail;
+    }
+    if (memcmp(got_unknowns[0].name, "teSt", 4) != 0 || got_unknowns[1].size 
!= 8) {
+        fprintf(stderr, "FAIL: unknown chunks data corruption\n");
+        goto fail;
+    }
+
+    printf("PASS\n");
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 0;
+
+fail:
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return 1;
+}
+#endif
+
+int main(void) {
+    int count = 0;
+    count += test_PLTE_aliasing();
+#ifdef PNG_hIST_SUPPORTED
+    count += test_hIST_aliasing();
+#endif
+#ifdef PNG_tRNS_SUPPORTED
+    count += test_tRNS_aliasing();
+#endif
+#ifdef PNG_TEXT_SUPPORTED
+    count += test_text_aliasing();
+#endif
+#ifdef PNG_sPLT_SUPPORTED
+    count += test_sPLT_aliasing();
+#endif
+#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+    count += test_unknown_aliasing();
+#endif
+
+    if (count == 0) {
+        printf("ALL ALIASING TESTS PASSED\n");
+        return 0;
+    }
+    printf("%d ALIASING TESTS FAILED\n", count);
+    return 1;
+}

Reply via email to