Skip to content

Fix buffer overflow in ucPixels for RGBA images at max width#43

Open
PE5PVB wants to merge 1 commit intobitbank2:masterfrom
PE5PVB:fix-rgba-buffer-overflow
Open

Fix buffer overflow in ucPixels for RGBA images at max width#43
PE5PVB wants to merge 1 commit intobitbank2:masterfrom
PE5PVB:fix-rgba-buffer-overflow

Conversation

@PE5PVB
Copy link

@PE5PVB PE5PVB commented Feb 14, 2026

Summary

DecodePNG() in png.inl aligns pCurr and pPrev to 16-byte boundaries within ucPixels, adding up to 15 bytes of padding per line. The default PNG_MAX_BUFFERED_PIXELS value does not account for this alignment overhead, causing a buffer overflow into ucFileBuf when decoding RGBA (color type 6) images at the maximum supported width.

Details

The alignment code in DecodePNG() (png.inl lines 776-782):

y = (int)(intptr_t)&pPage->ucPixels[0];
y &= 15;
y += (15 - y);                    // always results in y = 15
pCurr = &pPage->ucPixels[y];      // offset 15
y += pPage->iPitch + 1;           // + 1281 for 320px RGBA
y += (15 - (y & 15));             // + up to 15 more for alignment
pPrev = &pPage->ucPixels[y];      // offset up to 1311
// pPrev needs iPitch+1 = 1281 more bytes → total 2592

For a 320px wide RGBA image (Arduino default):

Bytes
iPitch 1280
Each line (iPitch + filter byte) 1281
Needed with alignment 15 + 1281 + 15 + 1281 = 2592
Old buffer size ((320*4+1)*2) 2562
Overflow 30 bytes

The 30-byte overflow writes past ucPixels into ucFileBuf (the file read buffer immediately following in the PNGIMAGE struct). This corrupts the buffered file data, causing zlib decompression errors after a few scanlines.

Symptoms

  • RGBA PNG images at maximum width only render the top portion (a few scanlines)
  • RGB PNG images of the same width render correctly (iPitch is smaller, no overflow)
  • The issue was found on ESP32 (Arduino) decoding 320x240 RGBA DAB broadcast slideshows

Fix

Add 16 bytes per line to the default PNG_MAX_BUFFERED_PIXELS to account for worst-case 16-byte alignment padding:

// Before:
#define PNG_MAX_BUFFERED_PIXELS ((320*4 + 1)*2)      // = 2562

// After:
#define PNG_MAX_BUFFERED_PIXELS ((320*4 + 1 + 16)*2)  // = 2594

The same fix is applied to the Linux/Mac default (2048px width).

The RAM increase is only 32 bytes, which is negligible.

DecodePNG() aligns pCurr and pPrev to 16-byte boundaries within
ucPixels, adding up to 15 bytes of padding per line. The default
PNG_MAX_BUFFERED_PIXELS value did not account for this alignment
overhead, causing a buffer overflow into ucFileBuf when decoding
RGBA (color type 6) images at the maximum supported width (320px
on Arduino, 2048px on Linux/Mac).

For a 320px wide RGBA image:
- iPitch = 1280, each line needs 1281 bytes (iPitch + filter byte)
- With alignment: 15 + 1281 + 15 + 1281 = 2592 bytes needed
- Old buffer size: ((320*4+1)*2) = 2562 bytes (30 bytes short)

The overflow corrupts ucFileBuf (the file read buffer immediately
following ucPixels in the PNGIMAGE struct), causing zlib
decompression errors after a few scanlines. This results in only
the top portion of the image being rendered.

Adding 16 bytes per line to the default accounts for worst-case
alignment padding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant