diff --git a/libpng-1.6-CVE-2026-33416_p1of5.patch b/libpng-1.6-CVE-2026-33416_p1of5.patch new file mode 100644 index 0000000..d7bd9db --- /dev/null +++ b/libpng-1.6-CVE-2026-33416_p1of5.patch @@ -0,0 +1,106 @@ +diff --git a/pngread.c b/pngread.c +index 01b731d8eb..0086edf6cf 100644 +--- a/pngread.c ++++ b/pngread.c +@@ -788,12 +788,11 @@ png_read_destroy(png_structrp png_ptr) + + #if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) +- if ((png_ptr->free_me & PNG_FREE_TRNS) != 0) +- { +- png_free(png_ptr, png_ptr->trans_alpha); +- png_ptr->trans_alpha = NULL; +- } +- png_ptr->free_me &= ~PNG_FREE_TRNS; ++ /* png_ptr->trans_alpha is always independently allocated (not aliased ++ * with info_ptr->trans_alpha), so free it unconditionally. ++ */ ++ png_free(png_ptr, png_ptr->trans_alpha); ++ png_ptr->trans_alpha = NULL; + #endif + + inflateEnd(&png_ptr->zstream); +diff --git a/pngrutil.c b/pngrutil.c +index 366379b991..a19507bf1b 100644 +--- a/pngrutil.c ++++ b/pngrutil.c +@@ -1772,10 +1772,6 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) + return; + } + +- /* TODO: this is a horrible side effect in the palette case because the +- * png_struct ends up with a pointer to the tRNS buffer owned by the +- * png_info. Fix this. +- */ + png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, + &(png_ptr->trans_color)); + } +diff --git a/pngset.c b/pngset.c +index 4b78b8960c..47883684e4 100644 +--- a/pngset.c ++++ b/pngset.c +@@ -1155,28 +1155,35 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, + + if (trans_alpha != NULL) + { +- /* It may not actually be necessary to set png_ptr->trans_alpha here; +- * we do it for backward compatibility with the way the png_handle_tRNS +- * function used to do the allocation. +- * +- * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively +- * relies on png_set_tRNS storing the information in png_struct +- * (otherwise it won't be there for the code in pngrtran.c). +- */ +- + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); + + if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) + { +- /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ ++ /* Allocate info_ptr's copy of the transparency data. */ + info_ptr->trans_alpha = png_voidcast(png_bytep, + png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); + memcpy(info_ptr->trans_alpha, trans_alpha, (size_t)num_trans); +- + info_ptr->free_me |= PNG_FREE_TRNS; + info_ptr->valid |= PNG_INFO_tRNS; ++ ++ /* Allocate an independent copy for png_struct, so that the ++ * lifetime of png_ptr->trans_alpha is decoupled from the ++ * lifetime of info_ptr->trans_alpha. Previously these two ++ * pointers were aliased, which caused a use-after-free if ++ * png_free_data freed info_ptr->trans_alpha while ++ * png_ptr->trans_alpha was still in use by the row transform ++ * functions (e.g. png_do_expand_palette). ++ */ ++ png_free(png_ptr, png_ptr->trans_alpha); ++ png_ptr->trans_alpha = png_voidcast(png_bytep, ++ png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); ++ memcpy(png_ptr->trans_alpha, trans_alpha, (size_t)num_trans); ++ } ++ else ++ { ++ png_free(png_ptr, png_ptr->trans_alpha); ++ png_ptr->trans_alpha = NULL; + } +- png_ptr->trans_alpha = info_ptr->trans_alpha; + } + + if (trans_color != NULL) +diff --git a/pngwrite.c b/pngwrite.c +index 5fc77d91f7..84af1e73fb 100644 +--- a/pngwrite.c ++++ b/pngwrite.c +@@ -1010,6 +1010,12 @@ png_write_destroy(png_structrp png_ptr) + png_ptr->chunk_list = NULL; + #endif + ++#if defined(PNG_tRNS_SUPPORTED) ++ /* Free the independent copy of trans_alpha owned by png_struct. */ ++ png_free(png_ptr, png_ptr->trans_alpha); ++ png_ptr->trans_alpha = NULL; ++#endif ++ + /* The error handling and memory handling information is left intact at this + * point: the jmp_buf may still have to be freed. See png_destroy_png_struct + * for how this happens. diff --git a/libpng-1.6-CVE-2026-33416_p2of5.patch b/libpng-1.6-CVE-2026-33416_p2of5.patch new file mode 100644 index 0000000..918dd57 --- /dev/null +++ b/libpng-1.6-CVE-2026-33416_p2of5.patch @@ -0,0 +1,27 @@ +diff --git a/pngset.c b/pngset.c +index 47883684e4..dccc6498d7 100644 +--- a/pngset.c ++++ b/pngset.c +@@ -1159,9 +1159,13 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, + + if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) + { +- /* Allocate info_ptr's copy of the transparency data. */ ++ /* Allocate info_ptr's copy of the transparency data. ++ * Initialize all entries to fully opaque (0xff), then overwrite ++ * the first num_trans entries with the actual values. ++ */ + info_ptr->trans_alpha = png_voidcast(png_bytep, + png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); ++ memset(info_ptr->trans_alpha, 0xff, PNG_MAX_PALETTE_LENGTH); + memcpy(info_ptr->trans_alpha, trans_alpha, (size_t)num_trans); + info_ptr->free_me |= PNG_FREE_TRNS; + info_ptr->valid |= PNG_INFO_tRNS; +@@ -1177,6 +1181,7 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, + png_free(png_ptr, png_ptr->trans_alpha); + png_ptr->trans_alpha = png_voidcast(png_bytep, + png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); ++ memset(png_ptr->trans_alpha, 0xff, PNG_MAX_PALETTE_LENGTH); + memcpy(png_ptr->trans_alpha, trans_alpha, (size_t)num_trans); + } + else diff --git a/libpng-1.6-CVE-2026-33416_p3of5.patch b/libpng-1.6-CVE-2026-33416_p3of5.patch new file mode 100644 index 0000000..37d2402 --- /dev/null +++ b/libpng-1.6-CVE-2026-33416_p3of5.patch @@ -0,0 +1,122 @@ +diff -up libpng-1.6.40/pngread.c.CVE-2026-33416_p3of5 libpng-1.6.40/pngread.c +--- libpng-1.6.40/pngread.c.CVE-2026-33416_p3of5 2026-05-13 17:57:13.486981492 +0200 ++++ libpng-1.6.40/pngread.c 2026-05-13 17:57:13.493075382 +0200 +@@ -959,12 +959,11 @@ png_read_destroy(png_structrp png_ptr) + png_ptr->quantize_index = NULL; + #endif + +- if ((png_ptr->free_me & PNG_FREE_PLTE) != 0) +- { +- png_zfree(png_ptr, png_ptr->palette); +- png_ptr->palette = NULL; +- } +- png_ptr->free_me &= ~PNG_FREE_PLTE; ++ /* png_ptr->palette is always independently allocated (not aliased ++ * with info_ptr->palette), so free it unconditionally. ++ */ ++ png_free(png_ptr, png_ptr->palette); ++ png_ptr->palette = NULL; + + #if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) +diff -up libpng-1.6.40/pngrtran.c.CVE-2026-33416_p3of5 libpng-1.6.40/pngrtran.c +--- libpng-1.6.40/pngrtran.c.CVE-2026-33416_p3of5 2026-05-13 17:57:13.483146844 +0200 ++++ libpng-1.6.40/pngrtran.c 2026-05-13 17:57:13.493827754 +0200 +@@ -745,7 +745,13 @@ png_set_quantize(png_structrp png_ptr, p + } + 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_voidcast(png_colorp, png_calloc(png_ptr, ++ PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); ++ memcpy(png_ptr->palette, palette, (unsigned int)num_palette * ++ (sizeof (png_color))); + } + png_ptr->num_palette = (png_uint_16)num_palette; + +diff -up libpng-1.6.40/pngrutil.c.CVE-2026-33416_p3of5 libpng-1.6.40/pngrutil.c +--- libpng-1.6.40/pngrutil.c.CVE-2026-33416_p3of5 2026-05-13 17:57:13.487752910 +0200 ++++ libpng-1.6.40/pngrutil.c 2026-05-13 18:02:45.747406991 +0200 +@@ -1048,14 +1048,6 @@ png_handle_PLTE(png_structrp png_ptr, pn + } + #endif + +- /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its +- * own copy of the palette. This has the side effect that when png_start_row +- * is called (this happens after any call to png_read_update_info) the +- * info_ptr palette gets changed. This is extremely unexpected and +- * confusing. +- * +- * Fix this by not sharing the palette in this way. +- */ + png_set_PLTE(png_ptr, info_ptr, palette, num); + + /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before +diff -up libpng-1.6.40/pngset.c.CVE-2026-33416_p3of5 libpng-1.6.40/pngset.c +--- libpng-1.6.40/pngset.c.CVE-2026-33416_p3of5 2026-05-13 17:57:13.490982521 +0200 ++++ libpng-1.6.40/pngset.c 2026-05-13 17:57:13.495080620 +0200 +@@ -595,28 +595,38 @@ png_set_PLTE(png_structrp png_ptr, png_i + png_error(png_ptr, "Invalid 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. +- * +- * 1.6.0: the above statement appears to be incorrect; something has to set +- * the palette inside png_struct on read. +- */ + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead + * of num_palette entries, in case of an invalid PNG file or incorrect + * call to png_set_PLTE() with too-large sample values. ++ * ++ * Allocate independent buffers for info_ptr and png_ptr so that the ++ * lifetime of png_ptr->palette is decoupled from the lifetime of ++ * info_ptr->palette. Previously, these two pointers were aliased, ++ * which caused a use-after-free vulnerability if png_free_data freed ++ * info_ptr->palette while png_ptr->palette was still in use by the ++ * row transform functions (e.g. png_do_expand_palette). ++ * ++ * Both buffers are allocated with png_calloc to zero-fill, because ++ * the ARM NEON palette riffle reads all 256 entries unconditionally, ++ * regardless of num_palette. + */ ++ png_free(png_ptr, png_ptr->palette); + png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); ++ info_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, ++ PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); ++ png_ptr->num_palette = info_ptr->num_palette = (png_uint_16)num_palette; + + if (num_palette > 0) ++ { ++ memcpy(info_ptr->palette, palette, (unsigned int)num_palette * ++ (sizeof (png_color))); + memcpy(png_ptr->palette, palette, (unsigned int)num_palette * + (sizeof (png_color))); ++ } + +- info_ptr->palette = png_ptr->palette; +- info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + info_ptr->free_me |= PNG_FREE_PLTE; + info_ptr->valid |= PNG_INFO_PLTE; + } +diff -up libpng-1.6.40/pngwrite.c.CVE-2026-33416_p3of5 libpng-1.6.40/pngwrite.c +--- libpng-1.6.40/pngwrite.c.CVE-2026-33416_p3of5 2026-05-13 17:57:13.488545337 +0200 ++++ libpng-1.6.40/pngwrite.c 2026-05-13 17:57:13.495368957 +0200 +@@ -982,6 +982,10 @@ png_write_destroy(png_structrp png_ptr) + png_ptr->trans_alpha = NULL; + #endif + ++ /* Free the independent copy of the palette owned by png_struct. */ ++ png_free(png_ptr, png_ptr->palette); ++ png_ptr->palette = NULL; ++ + /* The error handling and memory handling information is left intact at this + * point: the jmp_buf may still have to be freed. See png_destroy_png_struct + * for how this happens. diff --git a/libpng-1.6-CVE-2026-33416_p4of5.patch b/libpng-1.6-CVE-2026-33416_p4of5.patch new file mode 100644 index 0000000..08bfecb --- /dev/null +++ b/libpng-1.6-CVE-2026-33416_p4of5.patch @@ -0,0 +1,26 @@ +diff --git a/pngrtran.c b/pngrtran.c +index fd736ab672..978dac5888 100644 +--- a/pngrtran.c ++++ b/pngrtran.c +@@ -2070,6 +2070,21 @@ 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) ++ { ++ /* 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))); ++ } ++ } ++ + #ifdef PNG_READ_EXPAND_SUPPORTED + if ((png_ptr->transformations & PNG_EXPAND) != 0) + { diff --git a/libpng-1.6-CVE-2026-33416_p5of5.patch b/libpng-1.6-CVE-2026-33416_p5of5.patch new file mode 100644 index 0000000..ed75fe1 --- /dev/null +++ b/libpng-1.6-CVE-2026-33416_p5of5.patch @@ -0,0 +1,32 @@ +diff --git a/pngrtran.c b/pngrtran.c +index 1b04cafa56..0ac8df749e 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 diff --git a/libpng.spec b/libpng.spec index 1323df7..10fa441 100644 --- a/libpng.spec +++ b/libpng.spec @@ -4,7 +4,7 @@ Summary: A library of functions for manipulating PNG image format files Name: libpng Epoch: 2 Version: 1.6.40 -Release: 8%{?dist}.3 +Release: 8%{?dist}.4 License: zlib URL: http://www.libpng.org/pub/png/ @@ -42,6 +42,18 @@ Patch10: libpng-1.6-cve-2026-25646.patch # from upstream, for <1.6.56, RHEL-161208 # https://github.com/pnggroup/libpng/commit/aba9f18eba870d14fb52c5ba5d73451349e339c3 Patch11: libpng-1.6-CVE-2026-33636.patch +# from upstream, for <1.6.56 (fix), for <1.6.58 (regression fix), RHEL-161324 +# https://github.com/pnggroup/libpng/commit/23019269764e35ed8458e517f1897bd3c54820eb +Patch12: libpng-1.6-CVE-2026-33416_p1of5.patch +# https://github.com/pnggroup/libpng/commit/a3a21443ed12bfa1ef46fa0d4fb2b74a0fa34a25 +Patch13: libpng-1.6-CVE-2026-33416_p2of5.patch +# https://github.com/pnggroup/libpng/commit/7ea9eea884a2328cc7fdcb3c0c00246a50d90667 +Patch14: libpng-1.6-CVE-2026-33416_p3of5.patch +# https://github.com/pnggroup/libpng/commit/c1b0318b393c90679e6fa5bc1d329fd5d5012ec1 +Patch15: libpng-1.6-CVE-2026-33416_p4of5.patch +# regression fix for 7ea9eea8 (part 3) +# https://github.com/pnggroup/libpng/commit/d4c4e49eb5c8981075ec2cd946428758c0cda6ac +Patch16: libpng-1.6-CVE-2026-33416_p5of5.patch BuildRequires: gcc BuildRequires: zlib-devel @@ -104,6 +116,11 @@ cp -p %{SOURCE1} . %patch -P 9 -p1 -b .cve-2026-22801 %patch -P 10 -p1 -b .cve-2026-25646 %patch -P 11 -p1 -b .CVE-2026-33636 +%patch -P 12 -p1 -b .CVE-2026-33416_p1of5 +%patch -P 13 -p1 -b .CVE-2026-33416_p2of5 +%patch -P 14 -p1 -b .CVE-2026-33416_p3of5 +%patch -P 15 -p1 -b .CVE-2026-33416_p4of5 +%patch -P 16 -p1 -b .CVE-2026-33416_p5of5 %build autoreconf -vif @@ -144,6 +161,9 @@ make check %{_bindir}/pngfix %changelog +* Wed May 13 2026 Michal Hlavinka - 2:1.6.40-8.4 +- fix CVE-2026-33416: use-after-free via pointer aliasing in png_set_tRNS and png_set_PLTE (RHEL-161324) + * Mon Apr 27 2026 Michal Hlavinka - 2:1.6.40-8.3 - fix CVE-2026-33636: out-of-bounds R/W in the palette expansion on ARM Neon (RHEL-161208)