diff --git a/crates/clawhdf5-format/fuzz/.gitignore b/crates/clawhdf5-format/fuzz/.gitignore index 2f7896d..fe68c97 100644 --- a/crates/clawhdf5-format/fuzz/.gitignore +++ b/crates/clawhdf5-format/fuzz/.gitignore @@ -1 +1,4 @@ target/ +corpus/ +artifacts/ +coverage/ diff --git a/crates/clawhdf5-format/fuzz/fuzz_targets/fuzz_btree_v2.rs b/crates/clawhdf5-format/fuzz/fuzz_targets/fuzz_btree_v2.rs index 370b2d8..a33b4af 100644 --- a/crates/clawhdf5-format/fuzz/fuzz_targets/fuzz_btree_v2.rs +++ b/crates/clawhdf5-format/fuzz/fuzz_targets/fuzz_btree_v2.rs @@ -1,15 +1,36 @@ #![no_main] +use clawhdf5_format::btree_v2::{BTreeV2Header, collect_btree_v2_records}; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { for &offset_size in &[4u8, 8] { for &length_size in &[4u8, 8] { - let _ = clawhdf5_format::btree_v2::BTreeV2Header::parse( - data, - 0, - offset_size, - length_size, - ); + if let Ok(header) = BTreeV2Header::parse(data, 0, offset_size, length_size) { + let _ = collect_btree_v2_records(data, &header, offset_size, length_size); + } } } + + // Parsing a header requires a valid checksum, which random input almost + // never has, so the traversal behind it went unfuzzed — and that is where + // a node listing itself as its own child overflowed the stack. Take the + // header fields straight from the input instead and walk the rest. + let Some((fields, file)) = data.split_first_chunk::<20>() else { + return; + }; + 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); });