Skip to main content

Module panic_guard

Module panic_guard 

Source
Expand description

Containment for panics raised inside third-party parsers.

§Why this exists

ADR-0006 requires strypt’s own parsing code never to panic: malformed input is expected input, and failures are typed Result values. That rule cannot be extended to a dependency by wishing. lopdf is a third-party PDF parser handling attacker-controlled bytes (ADR-0018), and docs/THREAT_MODEL.md §5.1 names a panic inside it as one of the realistic residual risks that forbid(unsafe_code) does not address.

That risk stopped being theoretical: a sustained fuzz run reached an integer overflow in lopdf 0.44.0’s cross-reference parser (parser/mod.rs:516, start + index where start comes from the file). Because Cargo.toml deliberately enables overflow-checks in release so that an overflow aborts rather than wrapping into a nonsensical offset, the shipped binary panicked — a user handed a hostile PDF got a stack trace and exit code 101 instead of “this file could not be processed”.

§What this does, and what it does not

guard runs a closure and converts an unwinding panic into a typed error, so a dependency’s panic reaches the user as an ordinary refusal. This is containment, not a fix. The defect stays in the dependency and is reported upstream; this only stops it reaching the user as a crash.

Three limits, stated because a guard that is trusted beyond its reach is worse than none:

  1. It requires unwinding panics. Built with panic = "abort" the process dies before any of this runs. strypt does not set panic = "abort", and this is a reason not to.
  2. It cannot catch what does not unwind — a stack overflow from deep recursion, an abort, or a SIGSEGV. Bounded recursion (ParseLimits) is the control for the first.
  3. It says nothing about correctness. A dependency that panicked may equally return a wrong answer without panicking, which no guard detects. Fail-closed refusal on panic is a floor, not a guarantee.

§Why the panic message is suppressed

The default panic hook prints to stderr. A panic message from a parser can quote the bytes it was parsing, and those bytes are the user’s document — the metadata they are trying to destroy. CLAUDE.md §3.8 forbids printing metadata values, so a guarded panic must not print the default message. The hook is installed once and delegates to the previous hook whenever the guard is not active, so unguarded panics elsewhere still report normally.

Functions§

guard
Run f, converting an unwinding panic into on_panic().