From 9ff847dfcbb54f6dee3fd4e408150ae944278391 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. CVE: CVE-2026-33636 Upstream-Status: Backport [https://github.com/pnggroup/libpng/commit/aba9f18eba870d14fb52c5ba5d73451349e339c3] Reported-by: Amemoyoi Co-authored-by: Amemoyoi Signed-off-by: Cosmin Truta (cherry picked from commit aba9f18eba870d14fb52c5ba5d73451349e339c3) Signed-off-by: Sudhir Dumbhare --- 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 92c7d6f9f..bdd15849d 100644 --- a/arm/palette_neon_intrinsics.c +++ b/arm/palette_neon_intrinsics.c @@ -1,7 +1,7 @@ /* 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. * @@ -80,7 +80,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); @@ -90,13 +90,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; @@ -121,7 +120,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); @@ -136,13 +135,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.44.4