test(format): keep the 2026-09-20 B-tree v2 fuzz crash as a regression
An 82-byte fuzz_btree_v2 crash input from 2026-09-20 was left untracked in fuzz/artifacts. Replayed today it runs cleanly: the depth cap and record budget added to B-tree v2 traversal that day fixed it. It is now in the committed fuzz corpus, and a robustness test replays the fuzz target's exact code path on it so a regression fails CI rather than waiting for someone to run the fuzzer. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Binary file not shown.
@@ -312,3 +312,49 @@ fn provenance_mismatch_on_corruption() {
|
|||||||
"corrupted data should produce hash mismatch"
|
"corrupted data should produce hash mismatch"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Fuzzer finds, kept as regression tests
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// `fuzz_btree_v2` crash input from 2026-09-20 (82 bytes): a B-tree v2 header
|
||||||
|
/// followed by internal nodes that point back into themselves. It predates the
|
||||||
|
/// depth cap and record budget added to B-tree v2 traversal that day and no
|
||||||
|
/// longer crashes; this replays the fuzz target's exact code path on it so a
|
||||||
|
/// regression fails CI rather than waiting for a fuzz run.
|
||||||
|
#[test]
|
||||||
|
fn fuzz_btree_v2_crash_f98c19dc_is_a_clean_result() {
|
||||||
|
use clawhdf5_format::btree_v2::{BTreeV2Header, collect_btree_v2_records};
|
||||||
|
let data: &[u8] = &[
|
||||||
|
0x42, 0x54, 0x48, 0x44, 0x00, 0x06, 0x00, 0xed, 0xef, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||||
|
0x00, 0x03, 0x40, 0x14, 0x93, 0x42, 0x54, 0x49, 0x4e, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x42, 0x54, 0x48, 0x44, 0x00, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x14, 0x93, 0x42, 0x54, 0x00, 0x49,
|
||||||
|
0x00, 0x01, 0x4e, 0x42, 0x42, 0x54, 0xbe,
|
||||||
|
];
|
||||||
|
assert_eq!(data.len(), 82);
|
||||||
|
for offset_size in [4u8, 8] {
|
||||||
|
for length_size in [4u8, 8] {
|
||||||
|
if let Ok(header) = BTreeV2Header::parse(data, 0, offset_size, length_size) {
|
||||||
|
let _ = collect_btree_v2_records(data, &header, offset_size, length_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (fields, file) = data.split_first_chunk::<20>().unwrap();
|
||||||
|
let header = BTreeV2Header {
|
||||||
|
tree_type: fields[0],
|
||||||
|
node_size: u32::from_le_bytes([fields[1], fields[2], fields[3], fields[4]]),
|
||||||
|
record_size: u16::from_le_bytes([fields[5], fields[6]]),
|
||||||
|
depth: u16::from_le_bytes([fields[7], fields[8]]),
|
||||||
|
root_node_address: u64::from(u32::from_le_bytes([
|
||||||
|
fields[9], fields[10], fields[11], fields[12],
|
||||||
|
])),
|
||||||
|
num_records_in_root: u16::from_le_bytes([fields[13], fields[14]]),
|
||||||
|
total_records: u64::from(u32::from_le_bytes([
|
||||||
|
fields[15], fields[16], fields[17], fields[18],
|
||||||
|
])),
|
||||||
|
};
|
||||||
|
let offset_size = if fields[19] & 1 == 0 { 4 } else { 8 };
|
||||||
|
let _ = collect_btree_v2_records(file, &header, offset_size, 8);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user