hayro-syntax recovered a stream with a wrong /Length by scanning ahead
for the bytes `stream`. An indirect appearance-state dictionary
(`<< /Yes … /Off … >>`, as Quartz writes a checkbox's `/AP /N`) was
read as whatever stream came later in the file. That dictionary has no
`/BBox`, so the appearance was dropped and the widget was skipped.
`stream` now has to be the next token. A wrong `/Length` still
recovers, and the renderer already selects the `/AS` state once `/N`
is a dictionary.
Fixes#180
Co-authored-by: Eri Bastos <eb@fluxmail.xyz>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The app, crates, binaries, bundle id, packaging, icons, screenshots and docs are now
PdfCraft / pdfcraft (repo storytold/pdfcraft). On first launch the desktop app moves the
PrintCraft settings and crash-recovery folders to the new name, and both apps read settings
saved under the old storage key, so upgrades keep recent files, preferences and unsaved work.
The signing test fixtures keep their PrintCraft subject names (they are binary).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Every hang the nightly fuzz job found (11 findings, 8 source files) now
finishes in 0.1-2 s; each cause has a synthetic regression test that times
out without its fix.
- cos (7 findings, our own parser): a stream whose /Length pointed at
itself, with no endstream, made every parse attempt fail and each attempt
reload the same object: ~2^32 parses. Objects being loaded are a seen-set,
/Length values are memoised per load, and the shifted-header retry only
runs when the header is shifted.
- hayro-syntax (3): deciding where an inline image's data ends re-parsed the
rest of the stream, nesting through every inline image; 50 images ran for
over ten minutes. The look-ahead no longer nests and is bounded (256).
- hayro-interpret (1): a Type 3 glyph showing eight glyphs of its own font,
sixteen deep. Nested paints share a per-page budget (50,000).
The pdf.js corpus sweep matches its baseline (963 ok, same 20 known).
xtask check writes its report under CARGO_TARGET_DIR too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The second full nightly fuzz run on main found two more:
- hayro-syntax computed a Flate/LZW predictor row as columns × bits in
wrapping arithmetic: /Columns 9223372036854775807 asked for 2^61 bytes and
aborted on any machine. Row widths are now saturating usize (colors × bits
also overflowed u8), a zero width is refused (it divided by zero), and a
row longer than the data is not allocated (empty output, as before).
- hayro stroked /LW 9223372036854775807 as given, and stroke expansion
allocated 10 GB. Widths are clamped to 4 × (canvas width + height) device
pixels, which covers the same pixels.
Both findings now run in 8 MB. Synthetic regression test in render.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first full nightly fuzz run on main (now that the runner survives) found
two 4 GiB allocations, caught by the 4 GiB child cap:
- an inline stencil mask claiming /W 4294967295 was decoded in hayro-interpret
before the renderer's MAX_IMAGE_PIXELS check could skip it (locally 9.6 GB,
42 s). Vendored patch image_size_ok: images and masks over 2^28 pixels are
not decoded.
- a CCITT image whose /DecodeParms claimed /Columns 4294967295 (with a small
/Width) sized the decoder's line buffers (locally 4.3 GB). Vendored patch
ccitt_size_ok in hayro-syntax: at most 2^20 columns and 2^28 pixels.
Both findings now run in 7 MB. Synthetic regression test in render.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>