diff --git a/fuzz/fuzz_targets/queue.rs b/fuzz/fuzz_targets/queue.rs index fe02bba..e916b99 100644 --- a/fuzz/fuzz_targets/queue.rs +++ b/fuzz/fuzz_targets/queue.rs @@ -5,19 +5,29 @@ use libfuzzer_sys::arbitrary::Arbitrary; use libfuzzer_sys::fuzz_target; use rand::{Rng, SeedableRng}; use sequential_storage::{ + cache::{CacheImpl, NoCache, PagePointerCache, PageStateCache}, mock_flash::{MockFlashBase, MockFlashError, WriteCountCheck}, Error, }; use std::{collections::VecDeque, ops::Range}; const MAX_VALUE_SIZE: usize = u8::MAX as usize; -fuzz_target!(|data: Input| fuzz(data)); +const PAGES: usize = 4; +const WORD_SIZE: usize = 4; +const WORDS_PER_PAGE: usize = 256; + +fuzz_target!(|data: Input| match data.cache_type { + CacheType::NoCache => fuzz(data, NoCache::new()), + CacheType::PageStateCache => fuzz(data, PageStateCache::::new()), + CacheType::PagePointerCache => fuzz(data, PagePointerCache::::new()), +}); #[derive(Arbitrary, Debug, Clone)] struct Input { seed: u64, fuel: u16, ops: Vec, + cache_type: CacheType, } #[derive(Arbitrary, Debug, Clone)] @@ -34,14 +44,17 @@ struct PushOp { value_len: u8, } +#[derive(Arbitrary, Debug, Clone)] +enum CacheType { + NoCache, + PageStateCache, + PagePointerCache, +} + #[repr(align(4))] struct AlignedBuf([u8; MAX_VALUE_SIZE + 1]); -fn fuzz(ops: Input) { - const PAGES: usize = 4; - const WORD_SIZE: usize = 4; - const WORDS_PER_PAGE: usize = 256; - +fn fuzz(ops: Input, mut cache: impl CacheImpl) { let mut flash = MockFlashBase::::new( WriteCountCheck::Twice, Some(ops.fuel as u32), @@ -49,8 +62,6 @@ fn fuzz(ops: Input) { ); const FLASH_RANGE: Range = 0x000..0x1000; - let mut cache = sequential_storage::cache::NoCache::new(); - let mut order = VecDeque::new(); let mut buf = AlignedBuf([0; MAX_VALUE_SIZE + 1]); @@ -67,7 +78,9 @@ fn fuzz(ops: Input) { #[cfg(fuzzing_repro)] eprintln!("{}", flash.print_items()); #[cfg(fuzzing_repro)] - eprintln!("=== OP: {op:?} ==="); + eprintln!("{:?}", cache); + #[cfg(fuzzing_repro)] + eprintln!("\n=== OP: {op:?} ===\n"); match &mut op { Op::Push(op) => { diff --git a/src/cache.rs b/src/cache.rs index d274f10..411a598 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,6 +1,6 @@ //! Module implementing all things cache related -use core::{num::NonZeroU32, ops::Range}; +use core::{fmt::Debug, num::NonZeroU32, ops::Range}; use embedded_storage_async::nor_flash::NorFlash; @@ -10,7 +10,7 @@ use crate::{ /// Trait implemented by all cache types #[allow(private_bounds)] -pub trait CacheImpl: PrivateCacheImpl {} +pub trait CacheImpl: PrivateCacheImpl + Debug {} impl CacheImpl for &mut T {} @@ -45,6 +45,7 @@ impl PrivateCacheImpl for Cache); impl NoCache { @@ -76,6 +77,7 @@ impl CacheImpl for NoCache {} /// `Create cache 1` -> `use 1` -> `create cache 2` -> `use 2` -> `❌ use 1 ❌` /// /// Make sure the page count is correct. If the number is lower than the actual amount, the code will panic at some point. +#[derive(Debug)] pub struct PageStateCache( Cache, UncachedPagePointers>, ); @@ -109,6 +111,7 @@ impl CacheImpl for PageStateCache {} /// `Create cache 1` -> `use 1` -> `create cache 2` -> `use 2` -> `❌ use 1 ❌` /// /// Make sure the page count is correct. If the number is lower than the actual amount, the code will panic at some point. +#[derive(Debug)] pub struct PagePointerCache( Cache, CachedPagePointers>, ); @@ -238,17 +241,36 @@ impl Cache { } } -pub(crate) trait PageStatesCache { +pub(crate) trait PageStatesCache: Debug { fn get_page_state(&self, page_index: usize) -> Option; fn notice_page_state(&mut self, page_index: usize, new_state: PageState); fn invalidate_cache_state(&mut self); } -#[derive(Debug)] pub(crate) struct CachedPageStates { pages: [Option; PAGE_COUNT], } +impl Debug for CachedPageStates { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "[")?; + for (i, val) in self.pages.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + + if let Some(val) = val { + write!(f, "{val:?}")?; + } else { + write!(f, "?")?; + } + } + write!(f, "]")?; + + Ok(()) + } +} + impl CachedPageStates { pub const fn new() -> Self { Self { @@ -284,7 +306,7 @@ impl PageStatesCache for UncachedPageSates { fn invalidate_cache_state(&mut self) {} } -pub(crate) trait PagePointersCache { +pub(crate) trait PagePointersCache: Debug { fn first_item_after_erased(&self, page_index: usize) -> Option; fn first_item_after_written(&self, page_index: usize) -> Option; @@ -307,12 +329,43 @@ pub(crate) trait PagePointersCache { // Use NoneZeroU32 because we never store 0's in here (because of the first page marker) // and so Option can make use of the niche so we save bytes -#[derive(Debug)] pub(crate) struct CachedPagePointers { after_erased_pointers: [Option; PAGE_COUNT], after_written_pointers: [Option; PAGE_COUNT], } +impl Debug for CachedPagePointers { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{{ after_erased_pointers: [")?; + for (i, val) in self.after_erased_pointers.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + + if let Some(val) = val { + write!(f, "{:?}", val.get())?; + } else { + write!(f, "?")?; + } + } + write!(f, "], after_written_pointers: [")?; + for (i, val) in self.after_written_pointers.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + + if let Some(val) = val { + write!(f, "{:?}", val.get())?; + } else { + write!(f, "?")?; + } + } + write!(f, "] }}")?; + + Ok(()) + } +} + impl CachedPagePointers { pub const fn new() -> Self { Self { diff --git a/src/item.rs b/src/item.rs index 30041f3..d803df6 100644 --- a/src/item.rs +++ b/src/item.rs @@ -138,13 +138,7 @@ impl ItemHeader { } } - async fn write( - &self, - flash: &mut S, - flash_range: Range, - cache: &mut Cache, - address: u32, - ) -> Result<(), Error> { + async fn write(&self, flash: &mut S, address: u32) -> Result<(), Error> { let mut buffer = AlignedBuf([0xFF; MAX_WORD_SIZE]); buffer[Self::DATA_CRC_FIELD] @@ -153,8 +147,6 @@ impl ItemHeader { buffer[Self::LENGTH_CRC_FIELD] .copy_from_slice(&crc16(&self.length.to_le_bytes()).to_le_bytes()); - cache.notice_item_written::(flash_range, address, self); - flash .write( address, @@ -177,7 +169,7 @@ impl ItemHeader { ) -> Result> { self.crc = None; cache.notice_item_erased::(flash_range.clone(), address, &self); - self.write(flash, flash_range, cache, address).await?; + self.write(flash, address).await?; Ok(self) } @@ -240,7 +232,8 @@ impl<'d> Item<'d> { data: &[u8], address: u32, ) -> Result<(), Error> { - header.write(flash, flash_range, cache, address).await?; + cache.notice_item_written::(flash_range, address, &header); + header.write(flash, address).await?; let (data_block, data_left) = data.split_at(round_down_to_alignment_usize::(data.len()));