Skip to content

Commit

Permalink
Merge pull request #53 from tweedegolf/item-too-big
Browse files Browse the repository at this point in the history
Item too big
  • Loading branch information
diondokter authored May 7, 2024
2 parents 839fc1a + 1d0983f commit 09ecbd8
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

## Unreleased

- Added check for too big items that won't ever fit in flash so it returns a good clear error.

# 2.0.1 06-05-24

- Implemented the `get_len` function for all built-in key types
Expand Down
3 changes: 3 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
println!("cargo::rustc-check-cfg=cfg(fuzzing_repro)");
}
6 changes: 3 additions & 3 deletions src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,19 +178,19 @@ impl ItemHeader {
}

/// Get the address of the start of the data for this item
pub fn data_address<S: NorFlash>(address: u32) -> u32 {
pub const fn data_address<S: NorFlash>(address: u32) -> u32 {
address + round_up_to_alignment::<S>(Self::LENGTH as u32)
}

/// Get the location of the next item in flash
pub fn next_item_address<S: NorFlash>(&self, address: u32) -> u32 {
pub const fn next_item_address<S: NorFlash>(&self, address: u32) -> u32 {
let data_address = ItemHeader::data_address::<S>(address);
data_address + round_up_to_alignment::<S>(self.length as u32)
}

/// Calculates the amount of bytes available for data.
/// Essentially, it's the given amount minus the header and minus some alignment padding.
pub fn available_data_bytes<S: NorFlash>(total_available: u32) -> Option<u32> {
pub const fn available_data_bytes<S: NorFlash>(total_available: u32) -> Option<u32> {
let data_start = Self::data_address::<S>(0);
let data_end = round_down_to_alignment::<S>(total_available);

Expand Down
12 changes: 11 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ const fn calculate_page_index<S: NorFlash>(flash_range: Range<u32>, address: u32
(address - flash_range.start) as usize / S::ERASE_SIZE
}

const fn calculate_page_size<S: NorFlash>() -> usize {
// Page minus the two page status words
S::ERASE_SIZE - S::WORD_SIZE * 2
}

/// The marker being used for page states
const MARKER: u8 = 0;

Expand Down Expand Up @@ -366,7 +371,6 @@ pub enum Error<S> {
backtrace: std::backtrace::Backtrace,
},
/// The item cannot be stored anymore because the storage is full.
/// If you get this error some data may be lost.
FullStorage,
/// It's been detected that the memory is likely corrupted.
/// You may want to erase the memory to recover.
Expand All @@ -381,6 +385,11 @@ pub enum Error<S> {
BufferTooSmall(usize),
/// A serialization error (from the key or value)
SerializationError(SerializationError),
/// The item does not fit in flash, ever.
/// This is different from [Error::FullStorage] because this item is too big to fit even in empty flash.
///
/// See the readme for more info about the constraints on item sizes.
ItemTooBig,
}

impl<S> From<SerializationError> for Error<S> {
Expand Down Expand Up @@ -416,6 +425,7 @@ where
"A provided buffer was to small to be used. Needed was {needed}"
),
Error::SerializationError(value) => write!(f, "Map value error: {value}"),
Error::ItemTooBig => write!(f, "The item is too big to fit in the flash"),
}
}
}
Expand Down
39 changes: 39 additions & 0 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,15 @@ async fn store_item_inner<'d, K: Key, S: NorFlash>(
.serialize_into(&mut data_buffer[key_len..])
.map_err(Error::SerializationError)?;

if item_data_length > u16::MAX as usize
|| item_data_length
> calculate_page_size::<S>()
.saturating_sub(ItemHeader::data_address::<S>(0) as usize)
{
cache.unmark_dirty();
return Err(Error::ItemTooBig);
}

let free_spot_address = find_next_free_item_spot(
flash,
flash_range.clone(),
Expand Down Expand Up @@ -1347,4 +1356,34 @@ mod tests {
.is_none());
}
}

#[test]
async fn store_too_big_item() {
let mut flash = MockFlashBig::new(mock_flash::WriteCountCheck::Twice, None, true);
const FLASH_RANGE: Range<u32> = 0x000..0x1000;

store_item(
&mut flash,
FLASH_RANGE,
&mut cache::NoCache::new(),
&mut [0; 1024],
0u8,
&[0; 1024 - 4 * 2 - 8 - 1],
)
.await
.unwrap();

assert_eq!(
store_item(
&mut flash,
FLASH_RANGE,
&mut cache::NoCache::new(),
&mut [0; 1024],
0u8,
&[0; 1024 - 4 * 2 - 8 - 1 + 1],
)
.await,
Err(Error::ItemTooBig)
);
}
}
36 changes: 32 additions & 4 deletions src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ async fn push_inner<S: NorFlash>(
}

// Data must fit in a single page
if data.len()
> ItemHeader::available_data_bytes::<S>((S::ERASE_SIZE - S::WORD_SIZE * 2) as u32).unwrap()
as usize
if data.len() > u16::MAX as usize
|| data.len()
> calculate_page_size::<S>().saturating_sub(ItemHeader::data_address::<S>(0) as usize)
{
cache.unmark_dirty();
return Err(Error::BufferTooBig);
return Err(Error::ItemTooBig);
}

let current_page = find_youngest_page(flash, flash_range.clone(), cache).await?;
Expand Down Expand Up @@ -1300,4 +1300,32 @@ mod tests {
0
);
}

#[test]
async fn store_too_big_item() {
let mut flash = MockFlashBig::new(WriteCountCheck::Twice, None, true);
const FLASH_RANGE: Range<u32> = 0x000..0x1000;

push(
&mut flash,
FLASH_RANGE,
&mut cache::NoCache::new(),
&[0; 1024 - 4 * 2 - 8],
false,
)
.await
.unwrap();

assert_eq!(
push(
&mut flash,
FLASH_RANGE,
&mut cache::NoCache::new(),
&[0; 1024 - 4 * 2 - 8 + 1],
false,
)
.await,
Err(Error::ItemTooBig)
);
}
}

0 comments on commit 09ecbd8

Please sign in to comment.