Skip to content

stb_image: zero-init paletted-PNG palette buffer#1954

Closed
iliaal wants to merge 1 commit into
nothings:masterfrom
iliaal:fix/png-palette-zero-init
Closed

stb_image: zero-init paletted-PNG palette buffer#1954
iliaal wants to merge 1 commit into
nothings:masterfrom
iliaal:fix/png-palette-zero-init

Conversation

@iliaal

@iliaal iliaal commented May 21, 2026

Copy link
Copy Markdown

stbi__parse_png_file declares palette[1024] on the stack. The PLTE chunk fills the first pal_len*4 bytes. stbi__expand_png_palette then reads palette[idx*4 .. idx*4+4] for every pixel index in [0,255] without bounds-checking idx against pal_len. A paletted PNG referencing idx >= pal_len reads uninitialized stack into the output raster (return addresses, canaries, prior locals). Bug #5 in #1928.

-   stbi_uc palette[1024], pal_img_n=0;
+   stbi_uc palette[1024]={0}, pal_img_n=0;

Complements #1927, doesn't compete with it. #1927 adds idx >= len checks in stbi__expand_png_palette and rejects malformed PNGs. This PR zero-inits the buffer instead. With #1927 alone, out-of-range indices error out. With this alone, they render black. With both, the leak is gone even if the index check regresses later. Cost: one C99 brace-init at function entry; the 1024 stack bytes were already there. Matches the existing tc[3]={0} style two lines down.

Repro: PoC at poc/poc_palette_leak.c from #1927. Without this patch, 161/255 pixels contain stack bytes. With it, those pixels are zero.

Hit by fastchart 1.1.1, a PHP extension that rasterizes user SVG through plutosvg, which vendors stb_image. fastchart 1.1.1 carries this as a local patch; sending upstream so plutosvg and other consumers pick it up.

v2.30, master HEAD. No API change. No allocator paths touched.

`stbi__parse_png_file` declares a 1024-byte palette on the stack but
only fills the first `pal_len * 4` entries from the PNG's PLTE chunk.
`stbi__expand_png_palette` then dereferences `palette[idx*4..+4]` for
every pixel index in [0, 255] without bounds-checking `idx` against
`pal_len`, so a paletted PNG referencing indices >= pal_len reads
uninitialised stack content into the output raster — a stack info-leak
primitive for any caller that emits the decoded image (server-side
thumbnailing, screenshot APIs, image-conversion services).

Documented as bug nothings#5 in nothings#1928 ("PNG palette index out-of-bounds"); a
fix that adds explicit `idx >= len` checks in `stbi__expand_png_palette`
is open at nothings#1927.

This patch is complementary, not competing: zero-init costs nothing
(one bracket-init at declaration, 1024 stack bytes that were already
allocated), keeps semantics identical for valid inputs, and makes
out-of-range indices render as deterministic black for invalid inputs.
The change persists as defence-in-depth even if a future refactor
regresses the index check in nothings#1927 — the leak primitive is gone for
good. Hit by fastchart (php-ext rasterising user-supplied SVGs via
plutosvg → vendored stb_image); shipped as a local patch in fastchart
1.1.1, now offered upstream.
@NBickford-NV

Copy link
Copy Markdown
Contributor

I think this is a subset of #1863 .

@iliaal

iliaal commented May 21, 2026

Copy link
Copy Markdown
Author

@NBickford-NV right, this is a strict subset of #1863 (same single-char palette[1024]={0} change; #1863 also includes tc16[3]={0} for the 16-bit transparency case, which I missed). My search missed it before posting. Closing here in favour of #1863.

@iliaal

iliaal commented May 21, 2026

Copy link
Copy Markdown
Author

Closing in favour of #1863 (linked above).

@iliaal iliaal closed this May 21, 2026
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.

2 participants