The nightly fuzz job found a Type 1 font that hung hayro: read-fonts 0.39 (through skrifa 0.42) looked ahead after every integer by parsing the next token, which looked ahead again, recursing through a whole run of numbers. A long run hung; 20,000 numbers overflowed the stack and aborted the process. Upstream fixed the tokenizer; the vendored hayro-interpret now uses skrifa 0.47 (read-fonts 0.44), with its one API change. The finding finishes in 0.5 s. Synthetic regression test (it aborts with the old skrifa). Corpus sweep: 0 regressions; text oracle unchanged (median F1 0.980, mean 0.866, every per-file score identical). Co-Authored-By: Claude Opus 5.5 <[email protected]>
Vendored crates
Permissively licensed crates we carry small patches for. Each directory keeps the upstream
licence files unchanged. Every patch is marked PrintCraft patch: in the source, is covered by a
PrintCraft test, and should be proposed upstream. Remove the vendored copy once upstream releases
the fix. Vendoring copyleft code is never allowed (plan/adr/0001).
| Crate | Version | Licence | Patches | Test |
|---|---|---|---|---|
| hayro-interpret | 0.7.0 | Apache-2.0 OR MIT | (1) select /AP /N appearance states via /AS (checkboxes, radios, stateful icons) and honour the NoView flag; (2) Type3 Unicode fallback to /Encoding glyph names, then printable ASCII; (3) Type3Glyph::advance_width; (4) InterpreterSettings::ocg_overrides for viewer layer toggles; (5) soft masks whose group lacks /CS were dropped (Chrome gradient text); (6) paint nesting cap MAX_PAINT_NESTING for tiling patterns and Type 3 glyphs (self-referencing content overflowed the stack); (7) CID width ranges in /W and /W2 clamped to MAX_CID (a 0 4294967295 w range hung); (8) InterpreterSettings::hide_comments skips markup annotations but keeps widgets and links (View ▸ Hide all comments); (9) image_size_ok: images and masks over 2^28 pixels are not decoded (a fuzzed /W 4294967295 stencil mask allocated 4 GiB); (10) MAX_NESTED_PAINTS: nested paints (forms, Type 3 glyphs and tiling patterns inside one another) share a per-page budget of 50,000 (a glyph showing eight glyphs of its own font, sixteen deep, hung); (11) depends on skrifa 0.47 instead of 0.42 (upstream read-fonts fixed a Type 1 tokenizer that recursed through runs of numbers: a fuzzed font hung or overflowed the stack), with the one API change (Charset::glyph_id returns Option) |
(1) raster::tests::widget_appearance_states; (2, 3) xtask text-oracle on corpus/pdfjs issue918; (4) raster::tests::layer_override_hides_content; (5) raster::tests::soft_mask_without_group_colour_space_is_applied; (6) raster::tests::self_referencing_tiling_pattern_terminates, raster::tests::self_referencing_type3_glyph_terminates; (7) raster::tests::huge_cid_width_ranges_terminate; (8) raster::tests::hiding_comments_keeps_fields; (9) raster::tests::absurd_mask_and_fax_sizes_are_refused_before_decoding; (10) raster::tests::type3_glyphs_that_fan_out_into_themselves_finish; (11) raster::tests::type1_font_programs_with_long_runs_of_numbers_load_quickly |
| hayro | 0.7.1 | Apache-2.0 OR MIT | (1) RenderSettings::{x_offset, y_offset} to render one tile of a large page; (2) images over MAX_IMAGE_PIXELS (2^28) are skipped (a fuzzed /W 4294967295 hung in resampling); (3) tiling_cell_scale keeps a tiling pattern's cell pixmap within 3000 px a side whatever its /XStep//YStep (a fuzzed step far beyond /BBox allocated 2 GB); (4) stroke widths clamped to 4 × (canvas width + height) device pixels (a fuzzed /LW 9223372036854775807 allocated 10 GB in stroke expansion) | (1) raster::tests::tiles_match_full_render; (2) raster::tests::absurd_image_dimensions_are_skipped; (3) raster::tests::tiling_cells_stay_small_whatever_the_step; (4) raster::tests::absurd_predictor_columns_and_line_widths_render |
| hayro-jbig2 | 0.3.0 | Apache-2.0 OR MIT | (1) at most 2^28 pixels per bitmap, page bitmap, halftone grid and grayscale image (each side could be 65535: 4.3 billion pixels decoded one by one); (2) at most 2^26 symbol pixels per symbol dictionary (a fuzzed dictionary decoded for minutes) | (1) raster::tests::jbig2_regions_of_billions_of_pixels_are_refused; (2) the nightly cargo xtask fuzz finding, which now finishes in about a second |
| lopdf | 0.45.0 | MIT | (1) PNG predictor rows longer than the data are refused before their buffers are allocated (a fuzzed /Columns 4294967295 allocated two 4 GiB rows); (2) predictor row widths use saturating arithmetic | (1) inspect::tests::png_predictor_rows_longer_than_the_data_are_refused_before_allocating |
| hayro-syntax | 0.7.2 | Apache-2.0 OR MIT | (1) Page-tree cycle guard in resolve_pages (visited set + depth cap): a /Kids loop among compressed objects overflowed the stack and aborted the process; (2) ccitt_size_ok: CCITT images wider than 2^20 columns or over 2^28 pixels are not decoded (a fuzzed /Columns 4294967295 allocated 4 GiB); (3) Flate/LZW predictor row widths are saturating usize, a zero width is refused and a row longer than the data is not allocated (a fuzzed /Columns 9223372036854775807 wrapped to a 2^61-byte allocation and aborted); (4) the inline-image end-of-data check (EI) doesn't nest inside its own look-ahead and examines at most 256 candidates and steps (50 images with "EI" in their data ran for over ten minutes) | (1) cos/tests/writer.rs::page_tree_loops_in_object_streams_do_not_crash_readers; (2) raster::tests::absurd_mask_and_fax_sizes_are_refused_before_decoding; (3) raster::tests::absurd_predictor_columns_and_line_widths_render; (4) raster::tests::inline_images_with_ei_in_their_data_parse_in_linear_time |
Temporary: hayro is the bootstrap renderer and lopdf the bootstrap inspector (ADR-0004); M2.6 replaces them with our own devices and cos/model.