diff --git a/fuzz/fuzz_targets/map.rs b/fuzz/fuzz_targets/map.rs index 2683304..c1377bc 100644 --- a/fuzz/fuzz_targets/map.rs +++ b/fuzz/fuzz_targets/map.rs @@ -35,6 +35,7 @@ enum Op { Store(StoreOp), Fetch(u8), Remove(u8), + RemoveAll, } #[derive(Arbitrary, Debug, Clone)] @@ -64,7 +65,11 @@ enum CacheType { fn fuzz(ops: Input, mut cache: impl KeyCacheImpl + Debug) { let mut flash = MockFlashBase::::new( - if ops.ops.iter().any(|op| matches!(op, Op::Remove(_))) { + if ops + .ops + .iter() + .any(|op| matches!(op, Op::Remove(_) | Op::RemoveAll)) + { WriteCountCheck::Twice } else { WriteCountCheck::OnceOnly @@ -190,6 +195,7 @@ fn fuzz(ops: Input, mut cache: impl KeyCacheImpl + Debug) { value: MockFlashError::EarlyShutoff(_), backtrace: _backtrace, }) => { + // Check if the item is still there. It might or it might not and either is fine match block_on(sequential_storage::map::fetch_item::( &mut flash, FLASH_RANGE, @@ -220,6 +226,54 @@ fn fuzz(ops: Input, mut cache: impl KeyCacheImpl + Debug) { Err(e) => panic!("{e:?}"), } } + Op::RemoveAll => { + match block_on(sequential_storage::map::remove_all_items::( + &mut flash, + FLASH_RANGE, + &mut cache, + &mut buf.0, + )) { + Ok(()) => { + map.clear(); + } + Err(Error::Storage { + value: MockFlashError::EarlyShutoff(_), + backtrace: _backtrace, + }) => { + for key in map.keys().copied().collect::>() { + // Check if the item is still there. It might or it might not and either is fine + match block_on(sequential_storage::map::fetch_item::( + &mut flash, + FLASH_RANGE, + &mut cache, + &mut buf.0, + key, + )) { + Ok(Some(_)) => { + #[cfg(fuzzing_repro)] + eprintln!("Early shutoff when removing item {key}! Originated from:\n{_backtrace:#}"); + } + _ => { + // Could not fetch the item we stored... + #[cfg(fuzzing_repro)] + eprintln!("Early shutoff when removing item {key}! (but it still removed fully). Originated from:\n{_backtrace:#}"); + // Even though we got a shutoff, it still managed to store well + map.remove(&key); + } + } + } + } + + Err(Error::Corrupted { + backtrace: _backtrace, + }) => { + #[cfg(fuzzing_repro)] + eprintln!("Corrupted when removing! Originated from:\n{_backtrace:#}"); + panic!("Corrupted!"); + } + Err(e) => panic!("{e:?}"), + } + } } } } diff --git a/src/map.rs b/src/map.rs index 40d197a..d26d512 100644 --- a/src/map.rs +++ b/src/map.rs @@ -599,6 +599,7 @@ pub async fn remove_all_items( ) } +/// If `search_key` is None, then all items will be removed async fn remove_item_inner( flash: &mut S, flash_range: Range, @@ -650,15 +651,13 @@ async fn remove_item_inner( item::MaybeItem::Corrupted(_, _) => continue, item::MaybeItem::Erased(_, _) => continue, item::MaybeItem::Present(item) => { - let item_match = if let Some(search_key) = &search_key { - let (item_key, _) = K::deserialize_from(item.data())?; - &item_key == search_key - } else { - false + let item_match = match &search_key { + Some(search_key) => &K::deserialize_from(item.data())?.0 == search_key, + _ => true, }; // If this item has the same key as the key we're trying to erase, then erase the item. // But keep going! We need to erase everything. - if search_key.is_none() || item_match { + if item_match { item.header .erase_data(flash, flash_range.clone(), cache, item_address) .await?;