Skip to content

Prevent PDF uploads from freezing on parser panics#653

Open
AnthonyRonning wants to merge 2 commits into
masterfrom
codex-maple-pdf-panic-recovery-maple
Open

Prevent PDF uploads from freezing on parser panics#653
AnthonyRonning wants to merge 2 commits into
masterfrom
codex-maple-pdf-panic-recovery-maple

Conversation

@AnthonyRonning

@AnthonyRonning AnthonyRonning commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Related to #571

Summary

  • Upgrade pdf-extract from 0.7.12 to 0.12.0 and lopdf to 0.42.0, fixing the reported crash on valid PDFs containing Type 4 calculator functions.
  • Run PDF extraction on a blocking worker and convert ordinary parser panics or task cancellation into Maple's existing document-processing error path, so the uploader recovers instead of remaining disabled.
  • Vendor the published pdf-extract 0.12.0 source with four documented Maple corrections:
    • make CFF/Type1 encoding behavior follow PDF encoding precedence, including embedded built-in bases, Symbol/ZapfDingbats, .notdef, absent glyphs, explicit /ToUnicode, and out-of-range Type1 codes
    • scope cached fonts by their resolved dictionary instead of reusable resource names
    • avoid logging user-controlled Type 4 stream contents
    • reject recursive Form XObject/resource pairs and cap nesting at 100 levels before native stack exhaustion
  • Add targeted PDF fixtures for the reported crash, encoding correctness, resource collisions, ordinary parser panics, recursive/deep XObjects, and valid finite XObject re-entry.

User impact

PDFs containing Type 4 functions can now be processed without freezing Maple. If pdf-extract returns an error or triggers an ordinary Rust unwind panic on another PDF edge case, Maple shows the existing Failed to process document message and immediately re-enables the attachment picker. Recursive Form XObjects are rejected before they can overflow the native stack.

Dependency and review notes

The vendored source starts from the published 0.12.0 crate, with every Maple-specific change documented in MAPLE_PATCHES.md. Moving to lopdf 0.42 also removes its known crafted-PDF stack-overflow advisory. The remaining ttf-parser RustSec notice is informational and has no patched release.

The recovery boundary catches ordinary unwind panics; process aborts, OOM, native faults, and parser hangs remain outside an in-process panic boundary. Native stack overflow is likewise not catchable, so the XObject traversal now prevents the demonstrated cycle/depth cases before overflow.

Independent reviews approached the diff from runtime safety, dependency/upstream parity, PDF specification and font-encoding correctness, fixture validity, and final-diff perspectives. Review findings were reproduced before changes were accepted, and the final reviewers reported no remaining major or critical issue. Broader pre-existing custom-CFF completeness limitations and vendored warning cleanup were intentionally left out because they would require unrelated complexity.

Validation

  • Managed OpenSecret and billing workspace smoke checks
  • nix develop -c ./scripts/ci/frontend.sh - 131 passed; lint completed with 12 pre-existing warnings
  • nix develop -c ./scripts/ci/rust.sh - 142 passed
  • Focused locked PDF extractor suite - 23 passed
  • Fourteen new PDF fixtures validated with both qpdf --check and pdfinfo
  • Standalone extractor matrix covered CFF/Type1 encodings, Symbol/ZapfDingbats, /ToUnicode, missing glyphs, recursive/deep XObjects, and finite re-entry
  • Repository pre-commit hook - passed frontend formatting/build/tests and all 142 Rust tests
  • Workspace-specific native build bundled the app and DMG; local updater signing then stopped at the expected missing TAURI_SIGNING_PRIVATE_KEY
  • Exact workspace-specific Maple app and bundle identifier:
    • recursive Form XObject PDF surfaced Failed to process document without terminating the app
    • a valid Symbol PDF attached successfully immediately afterward without restarting Maple
  • git diff --check

Open in Devin Review

Summary by CodeRabbit

  • Bug Fixes

    • Improved PDF text extraction for fonts, encodings, embedded content, and encrypted documents.
    • Prevented problematic PDFs from causing application crashes or exposing sensitive stream contents in logs.
    • Added clearer, consistent error reporting for invalid, unsupported, or failed PDF extraction.
  • Tests

    • Expanded PDF coverage for text extraction, malformed files, recursion handling, and panic prevention.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR pins pdf-extract to 0.12.0, vendors a patched implementation with expanded font decoding, adds PDF extraction output APIs, and routes Tauri extraction through blocking-task error handling with expanded fixture-based tests.

Changes

PDF extraction pipeline

Layer / File(s) Summary
Patched crate integration
.gitattributes, frontend/src-tauri/Cargo.toml, frontend/src-tauri/patches/pdf-extract-0.12.0/{Cargo.toml,Cargo.toml.orig,MAPLE_PATCHES.md,README.md}
Pins pdf-extract to =0.12.0, applies the local Cargo patch, and records the vendored crate metadata and patch details.
Font and glyph mappings
frontend/src-tauri/patches/pdf-extract-0.12.0/src/{core_fonts.rs,encodings.rs,glyphlist-export.py,glyphlist-extended.txt,glyphnames.rs,zapfglyphnames.rs}
Adds core font metrics, encoding tables, generated glyph-name mappings, and ZapfDingbats Unicode lookup data.
PDF parsing and output pipeline
frontend/src-tauri/patches/pdf-extract-0.12.0/src/lib.rs
Adds PDF object decoding, font handling, text and graphics processing, Form XObject cycle protection, color parsing, encryption wrappers, and plain-text, HTML, and SVG output APIs.
Frontend extraction and validation
frontend/src-tauri/src/pdf_extractor.rs
Runs PDF extraction in spawn_blocking, standardizes extraction failures, and expands fixture-based tests for text decoding, resource scoping, invalid PDFs, and parser panics.

Estimated code review effort: 5 (Critical) | ~90+ minutes

Sequence Diagram(s)

sequenceDiagram
  participant extract_document_content
  participant run_pdf_extraction
  participant spawn_blocking
  participant pdf_extract
  extract_document_content->>run_pdf_extraction: submit PDF extraction closure
  run_pdf_extraction->>spawn_blocking: execute blocking parser task
  spawn_blocking->>pdf_extract: call extract_text_from_mem
  pdf_extract-->>spawn_blocking: extracted text or extraction error
  spawn_blocking-->>run_pdf_extraction: task result or panic/cancellation
  run_pdf_extraction-->>extract_document_content: standardized extraction result
Loading

Poem

A rabbit thumps softly, “Fonts now align!”
Glyphs hop through tables in orderly lines.
PDFs unwind, page after page,
While blocking tasks keep errors in a cage.
“Patch complete!” twitches my nose.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: PDF uploads now avoid freezing when the parser panics.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex-maple-pdf-panic-recovery-maple

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploying maple with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8a9cfe2
Status: ✅  Deploy successful!
Preview URL: https://5ab906fc.maple-ca8.pages.dev
Branch Preview URL: https://codex-maple-pdf-panic-recove.maple-ca8.pages.dev

View logs

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src-tauri/patches/pdf-extract-0.12.0/src/glyphlist-extended.txt`:
- Line 4552: Update the glyph-list generation flow in glyphlist-export.py so the
corrected Germandbls mapping at the later entry is not discarded by the earlier
duplicate; replace the original mapping or add an explicit override, then
regenerate glyphnames.rs to include the corrected value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2601c926-a004-4477-9725-e5145740cec0

📥 Commits

Reviewing files that changed from the base of the PR and between 2aaa815 and 8a9cfe2.

⛔ Files ignored due to path filters (20)
  • frontend/src-tauri/Cargo.lock is excluded by !**/*.lock
  • frontend/src-tauri/patches/pdf-extract-0.12.0/Cargo.lock is excluded by !**/*.lock
  • frontend/src-tauri/tests/fixtures/absent-cff-charset-glyph.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/declared-encoding-overrides-builtin.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/differences-only-embedded-cff.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/embedded-non-zapf-a1.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/finite-form-reentry.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/fontawesome-tounicode-occupied.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/invalid.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/no-encoding-missing-slot-cff.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/non-zapf-differences.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/notdef-difference-embedded-cff.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/page-font-name-collision.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/recursive-form-xobject.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/symbol-differences.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/type1-encoding-code-300.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/type4-separation.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/xobject-depth-101.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/xobject-font-name-collision.pdf is excluded by !**/*.pdf
  • frontend/src-tauri/tests/fixtures/zapf-dingbats-differences.pdf is excluded by !**/*.pdf
📒 Files selected for processing (14)
  • .gitattributes
  • frontend/src-tauri/Cargo.toml
  • frontend/src-tauri/patches/pdf-extract-0.12.0/Cargo.toml
  • frontend/src-tauri/patches/pdf-extract-0.12.0/Cargo.toml.orig
  • frontend/src-tauri/patches/pdf-extract-0.12.0/MAPLE_PATCHES.md
  • frontend/src-tauri/patches/pdf-extract-0.12.0/README.md
  • frontend/src-tauri/patches/pdf-extract-0.12.0/src/core_fonts.rs
  • frontend/src-tauri/patches/pdf-extract-0.12.0/src/encodings.rs
  • frontend/src-tauri/patches/pdf-extract-0.12.0/src/glyphlist-export.py
  • frontend/src-tauri/patches/pdf-extract-0.12.0/src/glyphlist-extended.txt
  • frontend/src-tauri/patches/pdf-extract-0.12.0/src/glyphnames.rs
  • frontend/src-tauri/patches/pdf-extract-0.12.0/src/lib.rs
  • frontend/src-tauri/patches/pdf-extract-0.12.0/src/zapfglyphnames.rs
  • frontend/src-tauri/src/pdf_extractor.rs

d;0064
e;0065
f;0066
Germandbls;0053 0053

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the Germandbls correction effective.

This duplicate is ignored because glyphlist-export.py keeps the Line 4314 entry and skips later names. Replace the earlier mapping or add an explicit override step, then regenerate glyphnames.rs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src-tauri/patches/pdf-extract-0.12.0/src/glyphlist-extended.txt` at
line 4552, Update the glyph-list generation flow in glyphlist-export.py so the
corrected Germandbls mapping at the later entry is not discarded by the earlier
duplicate; replace the original mapping or add an explicit override, then
regenerate glyphnames.rs to include the corrected value.

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