A .voldoc descriptor can expand to a much larger document. The decoder is
therefore treated as an attack surface from the beginning, not as an appendix.
Normative materialization emits bytes. It never executes or fetches:
- PDF JavaScript, actions, or embedded executables
- Office macros
- HTML/JavaScript or external resources
- external XML entities or network URLs
- TeX macros, shell escapes, or arbitrary plugins
- arbitrary code of any kind (no WASM, no scripting, no
eval)
Applications downstream decide what to do with the emitted bytes.
Malformed input may produce a typed error and malformed .voldoc may be
rejected. Malformed input must not:
- panic
- hang or loop unbounded
- allocate without bound
- overflow (all length/offset arithmetic is checked)
- write outside the requested output
- invoke external processes or touch the network (the sole child process is the decoder's own executable, spawned purely to isolate DEFLATE replay under an address-space cap; it runs no document content and touches no network)
All decode and encode paths take Limits. The descriptor
declares its expected output size; the implementation may impose stricter local
maxima. Candidates that would exceed a bound are rejected before catastrophic
work:
max_input_bytes,max_output_bytesmax_record_len,max_record_countmax_object_count,max_graph_ops,max_repeat_count
The graph analyzer computes the predicted output length with checked arithmetic before any allocation, and the coverage certificate is validated before materialization.
Exact DEFLATE replay calls preflate-rs, which can allocate unboundedly while
reconstructing from a hostile correction blob (fuzz finding F2: a 33-byte input
drives a multi-gigabyte allocation). catch_unwind bounds panics, not memory
growth, so on the decode path the replay engine runs in a separate child
process with an RLIMIT_AS address-space cap (ulimit -v) and a wall-clock
timeout; an abort (including an allocation failure), a timeout, a crash, or a
malformed reply becomes a typed CodecReplay error. Knobs and defaults:
VOLE_REPLAY_WORKER— worker executable. The CLI sets it to its own path, sodecode/verifyare isolated by default; a library embedder that sets neither this nor the programmatic default runs replay in-process (the residual).VOLE_REPLAY_MEM_MB— address-space cap. Defaultclamp((plaintext+corrections+declared) * 8 + 64 MiB, 256 MiB, limits.max_replay_bytes.min(2 GiB)).VOLE_REPLAY_TIMEOUT_MS— wall-clock budget, default30000.
When a worker path is configured, a spawn failure or a broken worker is a typed error and never silently falls back to the in-process path. The worker's stdin framing bounds every field length before allocating.
- CRC-32C guards the header and every record (corruption detection only).
- SHA-256 is the durable whole-source archival identity.
- The digest is not a security boundary against a malicious author; it is a correctness and archival boundary. Cryptographic authenticity (signing of descriptors) is a separate, future concern and is not claimed.
All parsing of untrusted lengths/offsets uses checked arithmetic or bounded
comparisons. reserved fields must be zero. Unknown mandatory feature bits
and record tags fail closed; only explicitly-optional records are skipped.
- The crate is
#![forbid(unsafe_code)]. - The only Phase-1 dependency is
sha2(RustCrypto), pinned exactly. ryg-rans-rs(Phase 2) is optional and must be used through its safe manual decode API; the panicking convenience decoder is banned on the hostile path.preflate-rs(Phase 6) reconstruction can panic on hostile stored state and can allocate without bound; on the decode path it runs in a process-isolated, memory- and time-capped worker (Phase 7.1b, see above), with a store-raw fallback when no replay candidate is admitted. In-processreplay_rawis used only by the encoder's own verification and the fuzz targets.
"Written in Rust" is not a security claim. The parser's safety comes from its bounds, checked arithmetic, fail-closed defaults, and hostile-input courts.
This is a research alpha. Report issues via the project issue tracker. Do not include non-redistributable document bytes in a report; provide hashes and a minimal reproducer.