Version in base suite: 1.6.48-1+deb13u3 Base version: libpng1.6_1.6.48-1+deb13u3 Target version: libpng1.6_1.6.48-1+deb13u4 Base file: /srv/ftp-master.debian.org/ftp/pool/main/libp/libpng1.6/libpng1.6_1.6.48-1+deb13u3.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/libp/libpng1.6/libpng1.6_1.6.48-1+deb13u4.dsc changelog | 8 + patches/CVE-2026-33416-part1.patch | 144 +++++++++++++++++++++++++++++++ patches/CVE-2026-33416-part2.patch | 55 ++++++++++++ patches/CVE-2026-33416-part3.patch | 169 +++++++++++++++++++++++++++++++++++++ patches/CVE-2026-33416-part4.patch | 54 +++++++++++ patches/CVE-2026-33636.patch | 98 +++++++++++++++++++++ patches/series | 5 + 7 files changed, 533 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmplga_3j2c/libpng1.6_1.6.48-1+deb13u3.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmplga_3j2c/libpng1.6_1.6.48-1+deb13u4.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-02-16 17:43:52.000000000 +0000 +++ libpng1.6-1.6.48/debian/changelog 2026-03-30 18:56:49.000000000 +0000 @@ -1,3 +1,11 @@ +libpng1.6 (1.6.48-1+deb13u4) trixie-security; urgency=medium + + * Security upload targeting trixie. + - CVE-2026-33416 - Use-after-free (Closes: #1132012) + - CVE-2026-33636 - OOB read/write on ARM plattforms (Closes: #1132013) + + -- Tobias Frost Mon, 30 Mar 2026 20:56:49 +0200 + libpng1.6 (1.6.48-1+deb13u3) trixie-security; urgency=high * Security upload targeting trixie. diff -Nru libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part1.patch libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part1.patch --- libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part1.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part1.patch 2026-03-30 18:56:49.000000000 +0000 @@ -0,0 +1,144 @@ +Description: CVE-2026-33416 part 1/4 - Use after free via pointer aliasing +Origin: https://github.com/pnggroup/libpng/commit/23019269764e35ed8458e517f1897bd3c54820eb +Bug: https://github.com/pnggroup/libpng/security/advisories/GHSA-m4pc-p4q3-4c7j +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1132012 + +From 23019269764e35ed8458e517f1897bd3c54820eb Mon Sep 17 00:00:00 2001 +From: Oblivionsage +Date: Sun, 15 Mar 2026 10:35:29 +0100 +Subject: [PATCH] fix: Resolve use-after-free on `png_ptr->trans_alpha` + +The function `png_set_tRNS` sets `png_ptr->trans_alpha` to point at +`info_ptr->trans_alpha` directly, so both structs share the same heap +buffer. If the application calls `png_free_data(PNG_FREE_TRNS)`, or if +`png_set_tRNS` is called a second time, the buffer is freed through +`info_ptr` while `png_ptr` still holds a dangling reference. Any +subsequent row read that hits the function `png_do_expand_palette` will +dereference freed memory. + +The fix gives `png_struct` its own allocation instead of aliasing the +`info_ptr` pointer. This was already flagged with a TODO in +`png_handle_tRNS` ("horrible side effect ... Fix this.") but it was +never addressed. + +Verified with AddressSanitizer. All 34 existing tests pass without +regressions. + +Reviewed-by: Cosmin Truta +Signed-off-by: Cosmin Truta +--- + pngread.c | 11 +++++------ + pngrutil.c | 4 ---- + pngset.c | 31 +++++++++++++++++++------------ + pngwrite.c | 6 ++++++ + 4 files changed, 30 insertions(+), 22 deletions(-) + +diff --git a/pngread.c b/pngread.c +index 01b731d8e..0086edf6c 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 366379b99..a19507bf1 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 handled_error; + } + +- /* 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)); + return handled_ok; +diff --git a/pngset.c b/pngset.c +index 4b78b8960..47883684e 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 5fc77d91f..84af1e73f 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. +-- +2.53.0 + diff -Nru libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part2.patch libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part2.patch --- libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part2.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part2.patch 2026-03-30 18:56:49.000000000 +0000 @@ -0,0 +1,55 @@ +Description: CVE-2026-33416 part 2/4 - Use after free via pointer aliasing +Origin: https://github.com/pnggroup/libpng/commit/a3a21443ed12bfa1ef46fa0d4fb2b74a0fa34a25 +Bug: https://github.com/pnggroup/libpng/security/advisories/GHSA-m4pc-p4q3-4c7j +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1132012 + +From a3a21443ed12bfa1ef46fa0d4fb2b74a0fa34a25 Mon Sep 17 00:00:00 2001 +From: Oblivionsage +Date: Tue, 17 Mar 2026 08:55:18 +0100 +Subject: [PATCH] fix: Initialize tail bytes in `trans_alpha` buffers + +Although the arrays `info_ptr->trans_alpha` and `png_ptr->trans_alpha` +are allocated 256 bytes, only `num_trans` bytes are copied. +The remaining entries were left uninitialized. Set them to 0xff (fully +opaque) before copying, which matches the conventional treatment of +entries beyond `num_trans`. + +This is a follow-up to the previous use-after-free fix. + +Reported-by: Cosmin Truta +Reviewed-by: Cosmin Truta +Signed-off-by: Cosmin Truta +--- + pngset.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/pngset.c b/pngset.c +index 47883684e..dccc6498d 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 +-- +2.53.0 + diff -Nru libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part3.patch libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part3.patch --- libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part3.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part3.patch 2026-03-30 18:56:49.000000000 +0000 @@ -0,0 +1,169 @@ +Description: CVE-2026-33416 part 3/4 - Use after free via pointer aliasing +Origin: https://github.com/pnggroup/libpng/commit/7ea9eea884a2328cc7fdcb3c0c00246a50d90667 +Bug: https://github.com/pnggroup/libpng/security/advisories/GHSA-m4pc-p4q3-4c7j +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1132012 + +From 7ea9eea884a2328cc7fdcb3c0c00246a50d90667 Mon Sep 17 00:00:00 2001 +From: Cosmin Truta +Date: Fri, 20 Mar 2026 17:37:22 +0200 +Subject: [PATCH] fix: Resolve use-after-free on `png_ptr->palette` + +Give `png_struct` its own independently-allocated copy of the palette +buffer, decoupling it from `info_struct`'s palette. Allocate both +copies with `png_calloc` to zero-fill, because the ARM NEON palette +riffle reads all 256 entries unconditionally. + +In function `png_set_PLTE`, `png_ptr->palette` was aliased directly to +`info_ptr->palette`: a single heap buffer shared across two structs +with independent lifetimes. If the buffer was freed through `info_ptr` +(via `png_free_data(PNG_FREE_PLTE)` or a second call to `png_set_PLTE`), +`png_ptr->palette` became a dangling pointer. Subsequent row reads, +performed in `png_do_expand_palette` and in other transform functions, +dereferenced (and in the bit-shift path, wrote to) freed memory. + +Also fix `png_set_quantize` to allocate an owned copy of the caller's +palette rather than aliasing the user pointer, so that the unconditional +free in `png_read_destroy` does not free unmanaged memory. +--- + pngread.c | 11 +++++------ + pngrtran.c | 8 +++++++- + pngrutil.c | 13 ------------- + pngset.c | 28 +++++++++++++++++++--------- + pngwrite.c | 4 ++++ + 5 files changed, 35 insertions(+), 29 deletions(-) + +diff --git a/pngread.c b/pngread.c +index 0086edf6c..e1d38d578 100644 +--- a/pngread.c ++++ b/pngread.c +@@ -779,12 +779,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 --git a/pngrtran.c b/pngrtran.c +index bfb7d423b..fd736ab67 100644 +--- a/pngrtran.c ++++ b/pngrtran.c +@@ -814,7 +814,13 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, + } + 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 --git a/pngrutil.c b/pngrutil.c +index a19507bf1..3a35fe9de 100644 +--- a/pngrutil.c ++++ b/pngrutil.c +@@ -1053,19 +1053,6 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) + /* A valid PLTE chunk has been read */ + png_ptr->mode |= PNG_HAVE_PLTE; + +- /* 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. +- * +- * REVIEW: there have been consistent bugs in the past about gamma and +- * similar transforms to colour mapped images being useless because the +- * modified palette cannot be accessed because of the above. +- * +- * CONSIDER: Fix this by not sharing the palette in this way. But does +- * this completely fix the problem? +- */ + png_set_PLTE(png_ptr, info_ptr, palette, num); + return handled_ok; + } +diff --git a/pngset.c b/pngset.c +index dccc6498d..b9ccb7fb1 100644 +--- a/pngset.c ++++ b/pngset.c +@@ -776,28 +776,38 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, + 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 --git a/pngwrite.c b/pngwrite.c +index 84af1e73f..348763e94 100644 +--- a/pngwrite.c ++++ b/pngwrite.c +@@ -1016,6 +1016,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. +-- +2.53.0 + diff -Nru libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part4.patch libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part4.patch --- libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part4.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/CVE-2026-33416-part4.patch 2026-03-30 06:49:44.000000000 +0000 @@ -0,0 +1,54 @@ +Description: CVE-2026-33416 part 4/4 - Use after free via pointer aliasing +Origin: https://github.com/pnggroup/libpng/commit/c1b0318b393c90679e6fa5bc1d329fd5d5012ec1 +Bug: https://github.com/pnggroup/libpng/security/advisories/GHSA-m4pc-p4q3-4c7j +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1132012 + +From c1b0318b393c90679e6fa5bc1d329fd5d5012ec1 Mon Sep 17 00:00:00 2001 +From: Cosmin Truta +Date: Fri, 20 Mar 2026 21:25:12 +0200 +Subject: [PATCH] fix: Sync `info_ptr->palette` after in-place transforms + +Copy `png_ptr->palette` into `info_ptr->palette` upon entering +the function that runs immediately after the in-place transforms. + +The palette decoupling in the previous commit gave `png_struct` +and `png_info` independently-allocated palette buffers, fixing a +use-after-free vulnerability. However, `png_init_read_transformations` +modifies `png_ptr->palette` in place (e.g. for gamma correction or +background compositing), and the old aliasing made those modifications +visible through `png_get_PLTE`. With independent buffers, +`info_ptr->palette` retained the original values, causing our tests to +fail on indexed-colour background compositing. +--- + pngrtran.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/pngrtran.c b/pngrtran.c +index fd736ab67..978dac588 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) + { +-- +2.53.0 + diff -Nru libpng1.6-1.6.48/debian/patches/CVE-2026-33636.patch libpng1.6-1.6.48/debian/patches/CVE-2026-33636.patch --- libpng1.6-1.6.48/debian/patches/CVE-2026-33636.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/CVE-2026-33636.patch 2026-03-30 18:56:49.000000000 +0000 @@ -0,0 +1,98 @@ +Description: CVE-2026-33636 - Out-Of-Bound Read/Write on ARM/AArch64 +Origin: https://github.com/pnggroup/libpng/commit/aba9f18eba870d14fb52c5ba5d73451349e339c3 +Bug: https://github.com/pnggroup/libpng/security/advisories/GHSA-wjr5-c57x-95m2 +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1132012 + +From aba9f18eba870d14fb52c5ba5d73451349e339c3 Mon Sep 17 00:00:00 2001 +From: Cosmin Truta +Date: Sat, 21 Mar 2026 23:48:49 +0200 +Subject: [PATCH] fix(arm): Resolve out-of-bounds read/write in NEON palette + expansion + +Both `png_do_expand_palette_rgba8_neon` and +`png_do_expand_palette_rgb8_neon` advanced in fixed-size chunks without +guarding the final iteration, allowing out-of-bounds reads and writes +when the row width is not a multiple of the chunk size. + +Restrict the NEON loop to full chunks only, remove the now-unnecessary +post-loop adjustment, and undo the `*ddp` pre-adjustment before the +pointer handoff to the scalar fallback. + +Reported-by: Amemoyoi +Co-authored-by: Amemoyoi +Signed-off-by: Cosmin Truta +--- + arm/palette_neon_intrinsics.c | 29 +++++++++++++---------------- + 1 file changed, 13 insertions(+), 16 deletions(-) + +diff --git a/arm/palette_neon_intrinsics.c b/arm/palette_neon_intrinsics.c +index 3068e9b6e..f3355bef5 100644 +--- a/arm/palette_neon_intrinsics.c ++++ b/arm/palette_neon_intrinsics.c +@@ -1,6 +1,6 @@ + /* palette_neon_intrinsics.c - NEON optimised palette expansion functions + * +- * Copyright (c) 2018-2019 Cosmin Truta ++ * Copyright (c) 2018-2026 Cosmin Truta + * Copyright (c) 2017-2018 Arm Holdings. All rights reserved. + * Written by Richard Townsend , February 2017. + * +@@ -79,7 +79,7 @@ png_do_expand_palette_rgba8_neon(png_structrp png_ptr, png_row_infop row_info, + */ + *ddp = *ddp - ((pixels_per_chunk * sizeof(png_uint_32)) - 1); + +- for (i = 0; i < row_width; i += pixels_per_chunk) ++ for (i = 0; i + pixels_per_chunk <= row_width; i += pixels_per_chunk) + { + uint32x4_t cur; + png_bytep sp = *ssp - i, dp = *ddp - (i << 2); +@@ -89,13 +89,12 @@ png_do_expand_palette_rgba8_neon(png_structrp png_ptr, png_row_infop row_info, + cur = vld1q_lane_u32(riffled_palette + *(sp - 0), cur, 3); + vst1q_u32((void *)dp, cur); + } +- if (i != row_width) +- { +- /* Remove the amount that wasn't processed. */ +- i -= pixels_per_chunk; +- } + +- /* Decrement output pointers. */ ++ /* Undo the pre-adjustment of *ddp before the pointer handoff, ++ * so the scalar fallback in pngrtran.c receives a dp that points ++ * to the correct position. ++ */ ++ *ddp = *ddp + (pixels_per_chunk * 4 - 1); + *ssp = *ssp - i; + *ddp = *ddp - (i << 2); + return i; +@@ -120,7 +119,7 @@ png_do_expand_palette_rgb8_neon(png_structrp png_ptr, png_row_infop row_info, + /* Seeking this back by 8 pixels x 3 bytes. */ + *ddp = *ddp - ((pixels_per_chunk * sizeof(png_color)) - 1); + +- for (i = 0; i < row_width; i += pixels_per_chunk) ++ for (i = 0; i + pixels_per_chunk <= row_width; i += pixels_per_chunk) + { + uint8x8x3_t cur; + png_bytep sp = *ssp - i, dp = *ddp - ((i << 1) + i); +@@ -135,13 +134,11 @@ png_do_expand_palette_rgb8_neon(png_structrp png_ptr, png_row_infop row_info, + vst3_u8((void *)dp, cur); + } + +- if (i != row_width) +- { +- /* Remove the amount that wasn't processed. */ +- i -= pixels_per_chunk; +- } +- +- /* Decrement output pointers. */ ++ /* Undo the pre-adjustment of *ddp before the pointer handoff, ++ * so the scalar fallback in pngrtran.c receives a dp that points ++ * to the correct position. ++ */ ++ *ddp = *ddp + (pixels_per_chunk * 3 - 1); + *ssp = *ssp - i; + *ddp = *ddp - ((i << 1) + i); + return i; +-- +2.53.0 + 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-02-16 17:43:52.000000000 +0000 +++ libpng1.6-1.6.48/debian/patches/series 2026-03-30 17:29:23.000000000 +0000 @@ -10,3 +10,8 @@ CVE-2026-22801.patch CVE-2026-22695.patch CVE-2026-25646.patch +CVE-2026-33416-part1.patch +CVE-2026-33416-part2.patch +CVE-2026-33416-part3.patch +CVE-2026-33416-part4.patch +CVE-2026-33636.patch