fix(doc): bound the LFO reservation by the records the table holds
parse_plf_lfo (src/formats/doc/lists.rs) passes the PlfLfo's u32 count straight to Vec::with_capacity. A malformed .doc whose LFO table declares 0xFFFF_FFFF records but holds none asks for about 64 GB (16-byte (u32, usize) elements) before the loop finds that the records are missing. Where the allocator refuses, Rust aborts the process instead of panicking, so a caller can't recover. The loop already stops at the first missing record, so the reservation was the only unbounded step.
lfo_capacityclamps the reservation to(plf.len() - 4) / LFO_SIZE, the number of records that fit in the table.- The regression test asserts on that capacity rather than by allocating. On an overcommitting allocator (macOS, and Linux by default) the unbounded 64 GB reservation "succeeds", so a test that only parsed the table would pass even without the fix. I confirmed that this test fails with the bound removed and passes with it.
I checked the other with_capacity calls under src/. Each is bounded by a u16/u8 count or by the input's length; this was the only one taking a full u32 from the file.
cargo test --workspace (297 passed), cargo clippy --all-targets -- -D warnings and cargo fmt --check are all clean.
🤖 Generated with Claude Code
Summary by cubic
Fixes a parser DoS where a malformed .doc with an oversized LFO count could abort the process by requesting ~64 GB of memory up front.
lfo_capacityclamps theVecreservation to the records the table can actually hold, matching the loop's existing stop-at-first-missing-record behavior.- Adds a regression test asserting on the reserved capacity, since overcommitting allocators would hide the bug in an allocation-based test.