test(mft): malformed-input corpus for cache deserializer (WI-5.3)#350
Merged
Conversation
Completes WI-5.3 (Category 5, "Bugs Rust Won't Catch"). The parser malformed-record test landed with WI-5.2; this adds the cache-deserializer half. The deserializer (index/storage/deserialize.rs) parses untrusted on-disk bytes (a persisted index reloaded at startup) and the daemon runs `panic = "abort"`, so a panic on a truncated/corrupt cache is a whole-process DoS. It was already hardened (Result + .get().ok_or()? + checked_* sizing); this is purely additive tests locking that in — no production code change. Added to crates/uffs-mft/src/index/tests_storage.rs: - valid-blob round-trip baseline (sanity for the mutation tests) - truncation sweep over a *populated* serialized index at every length - empty / 1-byte / single-0xFF rejection - oversized section-length header fields (u64::MAX in record-count, names-size, links-count) -> clean error, no overflow/OOB - seeded deterministic fuzz loop (ChaCha8Rng::seed_from_u64, 5000 iterations: bit-flips, random truncation, trailing garbage, fully random blobs) The property under test is liveness: deserialize returns Ok or Err but never panics. The truncation sweep does NOT assert "every prefix errors" — the deserializer is intentionally lenient about trailing/optional sections, so a near-complete prefix may legitimately parse Ok (found empirically at 1570/1610 bytes); a strict-error assertion is kept only for a cut inside the fixed header. No cargo-fuzz target added (avoids a toolchain dep without sign-off); the seeded ChaCha corpus gives deterministic CI fuzz coverage. Verified: native + `cargo xwin` (Windows) `--all-targets -D warnings` clippy clean; `cargo nextest run -p uffs-mft` 196 passed (was 191; +5 tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Completes WI-5.3 (Category 5: panic = DoS) of the "Bugs Rust Won't Catch" audit. The parser malformed-record test already landed in #349 (WI-5.2); this PR adds the cache-deserializer half.
The deserializer (
index/storage/deserialize.rs) parses untrusted on-disk bytes — a persisted index reloaded at startup. The daemon runspanic = "abort", so a panic on a truncated/corrupt cache file is a whole-process DoS.The deserializer was already hardened (returns
Result<_, &'static str>, slices via.get(..).ok_or(..)?, sizes sections withchecked_mul/checked_add). This PR is purely additive tests that lock that in — no production code change.Added (
crates/uffs-mft/src/index/tests_storage.rs)0xFFrejectionu64::MAXin record-count / names-size / links-count) → clean error, no overflow/OOBChaCha8Rng::seed_from_u64, 5 000 iterations: bit-flips, random truncation, trailing garbage, fully random blobs)Property under test
Liveness —
deserializereturnsOkorErrbut never panics/aborts — except where a specific rejection is provable (tiny inputs, a cut inside the fixed header, oversized length fields).The truncation sweep deliberately does not assert "every prefix is an error": the deserializer is intentionally lenient about some trailing/optional sections, so a near-complete prefix may legitimately parse
Ok(found empirically — a 1570/1610-byte prefix parsed). Asserting otherwise would encode a false contract; the test was corrected to match the real, safe behaviour.No
cargo-fuzztarget added (would introduce a toolchain dependency without sign-off, per the plan's own caveat); the seededChaCha8Rngcorpus gives deterministic, CI-friendly fuzz coverage instead.Verification
cargo clippy -p uffs-mft --all-targets --all-features -D warnings(native) — cleancargo xwin clippy ... --target x86_64-pc-windows-msvc -D warnings(Windows cross) — cleancargo nextest run -p uffs-mft— 196 passed, 3 skipped (was 191; +5 deserializer tests)With this, Category 5 is 100% (5.1 ✅, 5.2 ✅ via #349, 5.3 ✅).