Add a seeded robustness harness for the untrusted-input parsers
Five parsers that take bytes straight off a disc are now swept with generated input asserting one property: they return Ok or Err and never panic. That is this crate's own hard rule, and the class seven rounds of reading is worst at. Written in-crate rather than with cargo-fuzz, which needs a nightly toolchain this project does not use, and without proptest or arbitrary, because one dev-dependency is a deliberate posture and the parsers take plain byte slices. What is given up is coverage-guided mutation, which is the real loss. What is gained is determinism: the same seed replays the same cases anywhere, so a CI failure reproduces locally verbatim. Three generators, and the second is the one that matters. Pure random bytes die at the magic check and exercise the entry guards only; prefixing valid magic is what reaches the parser body; mutating a mostly-zero record is what reaches the offset and count arithmetic a hostile image would lie about. That claim is MEASURED, not asserted. A harness whose cases all bounce off the entry guards is the fuzzing equivalent of a test that cannot fail, so one test counts how many generated cases parse to completion: 15,606 of 60,000, about 26%. If a future change to a guard drops that to zero, the test fails rather than continuing to report a meaningless pass. Two further tests pin that the three generators produce different bytes and that a seed replays identically. 1.2M cases across all five targets found nothing. On this evidence that is a real negative rather than an empty one. The first version of this file was itself broken in the way this audit keeps finding: its two meta-tests set FREEMKV_HARNESS_CASES and raced, because the test harness runs them in parallel and env mutation is unsound there. The budget is a parameter now, and the environment is read once at the call site. Two crate-internal parsers widened from private to pub(crate) so the harness can reach them. No public API change.
This commit is contained in:
@@ -1401,7 +1401,7 @@ fn lace_svint(d: &[u8]) -> Option<(i64, usize)> {
|
||||
///
|
||||
/// The Lacing Head is "number of frames in the lace minus 1" on one octet, so
|
||||
/// the frame count is bounded by 256 and no allocation here is attacker-scaled.
|
||||
fn split_lacing(lacing: u8, body: &[u8]) -> Option<Vec<&[u8]>> {
|
||||
pub(crate) fn split_lacing(lacing: u8, body: &[u8]) -> Option<Vec<&[u8]>> {
|
||||
let (&count_minus_one, rest) = body.split_first()?;
|
||||
let n = count_minus_one as usize + 1;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user