Skip to content

[Bug]: reproducible bugs outside the extraction path #994

Description

@tobocop2

Nondeterministic output, silent data loss, and malformed-input panics, in the write / edit / render / sign / redact / compliance / bindings subsystems. Each has a runnable reproducer.

Important

Runnable reproducers for all 35 bugs are in this gist: https://gist.github.com/tobocop2/5279a7f5b5df615e43a6dc5b18b08a63 — one test suite (cargo test --test audit_994_reproducers, plus --features rendering), verified to compile and run against v0.3.77. The table below is the index; the gist has the full tests and their observed output.

Note

One issue as an epic, sorry for the format — every row is an independent bug that can be split out. These were all reproduced (twin-run byte-diffs for non-determinism, crafted malformed bytes for panics, direct API/binding calls for the rest), and all blame to a commit at or before v0.3.77.

Severity: 14 high, 14 medium, 7 low.

Seven rendering, xref-reconstruction and structure-tree rows that sit on the read path were split out into #997.

Findings

Sorted by severity. Each row's runnable reproducer (with its observed output) is in the gist above, keyed by Location.

# Sev Area Class Location What's wrong
1 High compliance unguarded-cast src/compliance/pdf_x/validator.rs:1121 The PDF/X page-tree walk casts a file-derived /Count i64 to usize without a sign check, so a negative /Count makes *current_index + count overflow a…
2 High compliance silent-wrong src/compliance/validators.rs:356 validate_transparency never emits ErrorCode::TransparencyNotAllowed, so PDF/A-1 documents that use transparency validate as compliant and the converte…
3 High document forked-dup src/document.rs:20471 to_markdown_with_ocr renders through the deprecated MarkdownConverter while to_markdown uses the pipeline MarkdownOutputConverter, so the same text-la…
4 High editor non-determinism src/editor/document_editor.rs:3332 write_full_to_writer serializes page-resource Font/XObject/ExtGState objects in fdict
5 High editor silent-wrong src/editor/document_editor.rs:1546 write_incremental consumes only modified_objects + Info, silently dropping destructive redactions (redacted_content/redacted_orphan_ids), page removal…
6 High editor forked-dup src/editor/document_editor.rs:7786 get_page_info indexes the raw source page list (page_refs[index], :7796) with bounds checked against the raw list (:7788), never mapping through page_…
7 High ffi forked-dup src/ffi.rs:2094 pdf_document_search_page passes (page, page+1) to the INCLUSIVE SearchOptions::with_page_range (text_search
8 High pipeline forked-dup src/pipeline/page_order.rs:80 Article-thread (/Threads) bead order is wired only into page_reading_order (extract_words/extract_text_lines); extract_text, to_markdown and to_html b…
9 High python silent-wrong src/python.rs:357 Python remove_headers/remove_footers/remove_artifacts (python
10 High redaction silent-wrong src/redaction/text_engine.rs:480 redact_text_stream passes Do (form/image XObject) and inline-image operators through unchanged and never sets unsupported_font, so destructive redac…
11 High signatures unguarded-cast src/signatures/byterange.rs:100 ByteRangeCalculator::extract_signed_bytes casts the four file-derived /ByteRange i64 values straight to usize and adds them, so a negative element pan…
12 High signatures panic src/signatures/enumerate.rs:91 walk_field recurses into /Kids with no depth limit and no visited-set, so a self-referential AcroForm field tree makes enumerate_signatures overflow t…
13 High signatures silent-wrong src/signatures/cms.rs:43 extract_signer_certificate_der returns the FIRST certificate in the CMS certificates SET and never consults SignerInfo
14 High wasm silent-wrong src/wasm.rs:545 WASM eraseHeader/eraseFooter/eraseArtifacts (wasm
15 High writer non-determinism src/writer/pdf_writer.rs:1973 PdfWriter::finish() emits the 12 base-14 font objects by iterating self
16 Medium compliance silent-wrong src/compliance/pdf_ua.rs:673 validate_figures, validate_tables and validate_annotations are stubs that only add PartialCheck warnings, yet PdfUaValidator::validate sets is_complia…
17 Medium document silent-wrong src/document.rs:20673 to_html drops the preserve_input_order flag whenever the document is trustworthy-tagged (build_context branch never sets it), while to_markdown always…
18 Medium editor non-determinism src/editor/document_editor.rs:1571 write_incremental iterates `for (&obj_id, obj) in &self
19 Medium editor panic src/editor/document_editor.rs:1064 DocumentEditor::collect_page_refs recurses through /Pages Kids with no cycle detection (its twin PdfDocument::collect_page_refs at document
20 Medium editor forked-dup src/editor/document_editor.rs:1071 The editor page walker requires /Type == /Page exactly (:1069-1078, typeless leaves silently dropped), while the document walker (document
21 Medium editor silent-wrong src/editor/document_editor.rs:4583 get_page_media_box and get_page_info never implement MediaBox inheritance (ISO 32000-1 §7
22 Medium ffi silent-wrong src/ffi.rs:7711 pdf_validate_pdf_x_level maps ANY unknown level int to PdfXLevel::X4 (_ => PdfXLevel::X4) and returns a verdict with ERR_SUCCESS; wasm validatePdfX …
23 Medium redaction silent-wrong src/redaction/options.rs:48 RedactionOptions::optional_content (default OcgPolicy::StripHidden, documented as protecting against the "classic redaction leak") is never read by an…
24 Medium writer forked-dup src/writer/font_manager.rs:374 Writer-side base-14 width table (get_base14_widths) disagrees with the Adobe AFM table used by the extraction side (fonts/font_dict
25 Medium writer non-determinism src/writer/font_manager.rs:1249 generate_tounicode_cmap stable-sorts (gid, unicode) pairs by gid only, so when two used codepoints share a GID the bfchar emission order comes from Ha…
26 Medium writer non-determinism src/writer/xmp_metadata.rs:273 XmpWriter::to_xml emits metadata
27 Medium writer silent-wrong src/writer/xmp_metadata.rs:302 escape_xml escapes only the 5 XML entities and never strips XML-1
28 Medium writer forked-dup src/writer/watermark.rs:268 WatermarkAnnotation's appearance stream pushes raw UTF-8 chars into a PDF literal string shown with a simple Helvetica base font (escape_pdf_string at…
29 Low editor non-determinism src/editor/document_editor.rs:3554 Overlay Base-14 font objects are written by iterating for (name, font_id) in &overlay_font_ids (HashMap<String,u32>, :2479) directly into the output…
30 Low editor non-determinism src/editor/document_editor.rs:4106 write_full_to_writer's trailing block collects not-yet-written objects via self
31 Low editor non-determinism src/editor/document_editor.rs:5233 get_form_fields appends newly added (is_new) fields by iterating modified_form_fields (HashMap<String, FormFieldWrapper>, :509) with no sort (:5233-52…
32 Low ffi silent-wrong src/ffi.rs:4717 pdf_estimate_render_time (ffi
33 Low python poison src/python.rs:388 sync_editor_erasures iterates `self
34 Low signatures non-determinism src/signatures/pades/dss_read.rs:69 parse_dss iterates the /VRI dictionary (a std HashMap-backed Object::Dictionary) and pushes entries into DocumentSecurityStore
35 Low writer forked-dup src/writer/form_fields/form_appearance.rs:369 Push-button caption rendering duplicates the same broken escape fork: escape_pdf_string keeps non-ASCII verbatim into a WinAnsi literal string, and th…

The two recurring patterns

Warning

Same input produces different output bytes. Dictionaries, font tables, and metadata are serialized in HashMap iteration order, which is randomized per run — so building the same document twice yields different bytes. Breaks reproducible builds and byte-diff caching; on the signature path it changes hashed bytes. The same class was already fixed in redaction and compliance; these are the sites that were missed.

Caution

Wrong-but-plausible output, or a crash on a hostile file. Redaction that leaves content in place, validators that never emit the error they exist to raise, and bindings whose erase calls are silent no-ops — no exception, no warning.

Flagged but not executed

Structurally sound, but not reproduced safely (they need real concurrency or would be undefined behavior to trigger):

Sev Location What
Medium src/ffi.rs:986 Read-accessor FFI functions typed *const manufacture &mut by casting away const: editor_get_string_field! macro (ffi
Low src/ffi.rs:140 vec_to_ffi_bytes (ffi
Low src/ffi.rs:7521 Four byte-buffer returns write the length as `bytes

A separate set of malformed-input parser/decoder hardening findings from the same audit is tracked elsewhere and not included here. Full reproducers: https://gist.github.com/tobocop2/5279a7f5b5df615e43a6dc5b18b08a63

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions