Skip to content

document: a malformed .pixi is an error, not a crash - #52

Closed
foxnne wants to merge 1 commit into
mainfrom
document/malformed-pixi
Closed

foxnne wants to merge 1 commit into
mainfrom
document/malformed-pixi

Conversation

@foxnne

@foxnne foxnne commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Opening bytes that are not a well-formed .pixi crashed pixi with a segfault at 0x0, in File.loadFizzyZip. This was seen with 0.1.47 in a ReleaseFast fizzy, when the restart hand-over (KeptDocuments.openKept → loadDocumentFromBytes) gave back an art.pixi that fizzy's text editor had held.

Cause. When fizzydata.json was missing or no version of the format parsed, ext stayed undefined, and the loader went on to read its grid and its layers slice. In ReleaseFast that slice's pointer is null.

Fix. Reading the archive moves to src/fizzy_zip.zig, which is std-only and tested by zig build test. Every malformed input is error.FileLoadError:

  • an archive that won't open (empty, garbage, truncated, a bad central directory), or that has no fizzydata.json/pixidata.json
  • data that is no version of the format
  • a zero or oversized grid (more than 16384² px), a v1/v2 grid that isn't whole tiles, no layers, an animation frame past the grid, or a v1–v3 fps ≤ 0 (@intFromFloat(inf) before)

In loadFizzyZip:

  • A .layer entry must be exactly the canvas's pixels. A short one used to be read past its end.
  • A .png layer must be the canvas's size.
  • selected_layer_index counts only the layers that loaded. A skipped layer could leave it out of range.
  • Zip entry buffers are freed (they leaked before).
  • Old-version parses go in one arena that is freed (they leaked before).
  • A document left with no layers is an error.
  • A failure after the document is built calls deinit on it.

Tests (src/fizzy_zip.zig): empty, 4 KiB of random bytes, text, a bare end record, and a lone local header. Also assets/src/misc.pixi cut at about 512 points, a built archive cut at every byte, a corrupt central directory, a zip without a data entry, and malformed data of each kind above. The real file, and a v1 file with pixidata.json, still load.

Verified (rebased onto main: Zig 0.17, SDK 0.2.32): zig build test in Debug and ReleaseFast (14/14), zig build, zig fmt --check. The wasm target was not built locally. Not yet reproduced in a live fizzy restart.

🤖 Generated with Claude Code

Opening bytes that are not a well-formed .pixi crashed pixi at address 0x0.
When no version of the data parsed, `loadFizzyZip` read the grid and
layers from an `undefined` result. Fizzy's restart hand-over hit it by
giving back an `art.pixi` its text editor had held. Reading the archive now
lives in `src/fizzy_zip.zig`, which is std-only and checks everything:
an archive that will not open, has no data entry, or holds data that is no
version of the format is `error.FileLoadError`. So is a zero or oversized
grid, no layers, a frame past the grid, or an fps of 0. A `.layer` entry
must be exactly the canvas's pixels and a `.png` the canvas's size, where
before a short one was read past its end. The selected layer is counted
among the layers that loaded, and the zip's entry buffers are freed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne force-pushed the document/malformed-pixi branch from 8b22ce8 to 22ec9b7 Compare October 19, 2026 14:04
@foxnne

foxnne commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

Closed by the maintainer's decision on 2026-10-21; the malformed .pixi guard is dropped for now. The branch stays.

@foxnne foxnne closed this Oct 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.

1 participant