Skip to content

Missing size_t promotion in ARM NEON palette RGBA dp calculation #862

Description

@Yanhaoxi

Severity: Low / latent correctness issue

Affected file: arm/arm_init.c, line 148

Description:

The RGBA path computes row + (4 * row_width - 1) without promoting row_width to size_t, while the RGB path and scalar implementation do promote. For very large legal PNG widths, the multiplication can wrap in 32-bit arithmetic before pointer arithmetic. This may be unreachable in normal builds due to row-size or allocation limits, but the inconsistency is a latent defect and can be fixed by matching the RGB/scalar code.

RGBA path (line 148) — BUGGY:

png_byte *dp = row + (4/*RGBA*/*row_width - 1);
//                      ^^^^^^^^^^^^^^^^^
//                      png_uint_32 × int → png_uint_32 (32-bit)
//                      Overflows for row_width > UINT32_MAX/4 ≈ 1,073,741,823

RGB path (line 197) — CORRECT:

png_byte *dp = row + (3/*RGB*/ * (size_t)row_width - 1);
//                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//                      Explicit (size_t) promotion → 64-bit on 64-bit platforms

The RGBA path performs 4 * row_width in png_uint_32 (32-bit) arithmetic. The PNG specification permits image widths up to 0x7FFFFFFF (2,147,483,647), and 4 * 0x7FFFFFFF overflows a 32-bit unsigned integer. The result wraps to a small value, producing a wildly incorrect dp pointer.

The same discrepancy exists in the scalar implementation png_do_expand_palette (pngrtran.c:4434), which uses (size_t)row_width << 2 correctly. The NEON RGB branch at line 197 also uses (size_t) correctly — only the RGBA branch at line 148 is missing it.

Proof (side-by-side comparison):

Location Code Width
arm_init.c:148 (NEON RGBA) 4/*RGBA*/*row_width - 1 32-bit (BUG)
arm_init.c:197 (NEON RGB) 3/*RGB*/ * (size_t)row_width - 1 64-bit ✓
pngrtran.c:4434 (scalar RGBA) ((size_t)row_width << 2) - 1 64-bit ✓
pngrtran.c:4458 (scalar RGB) (size_t)row_width * 3 - 1 64-bit ✓

Impact:

On 64-bit platforms, for an image with row_width > 1,073,741,823 and color type PALETTE with tRNS (RGBA expansion path), the dp pointer would be computed incorrectly due to 32-bit wrap-around, potentially causing out-of-bounds writes to the row buffer. While such widths exceed practical image dimensions today, the PNG specification permits them, and the inconsistency between the two branches is a latent defect.

Suggested fix:

-         png_byte *dp = row + (4/*RGBA*/*row_width - 1);
+         png_byte *dp = row + (4/*RGBA*/ * (size_t)row_width - 1);

This aligns the RGBA branch with the existing RGB branch at line 197 and the scalar implementation in pngrtran.c.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions