Skip to content

Commit

Permalink
file: treat unexpected EOF as corrupted.
Browse files Browse the repository at this point in the history
Fixes potentially infinite loop when compacting, if there's 1 stray byte
left at the end of the file. With the fuzz config, the record header is
2 bytes. Reading the record header would return EOF, making compaction think
it's finished, but the 1 byte would get left there so the file wouldn't go away,
so compaction didn't actually make progress.
  • Loading branch information
Dirbaio committed Apr 22, 2024
1 parent 49f268f commit f121681
Showing 1 changed file with 73 additions and 3 deletions.
76 changes: 73 additions & 3 deletions src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -886,24 +886,38 @@ impl<'a> FileReader<'a> {
Ok(())
}

/// Read an exact amount of bytes.
///
/// If we're at file end, return Eof.
/// If there's some data left at the file but not enough to fill `data`, return Corrupted.
pub async fn read<F: Flash>(
&mut self,
m: &mut FileManager<F>,
mut data: &mut [u8],
) -> Result<(), ReadError<F::Error>> {
let mut read_some = false;

while !data.is_empty() {
match &mut self.state {
ReaderState::Finished => return Err(ReadError::Eof),
ReaderState::Finished => {
if read_some {
corrupted!() // Unexpected EOF!
} else {
return Err(ReadError::Eof);
}
}
ReaderState::Created => {
self.next_page(m).await?;
continue;
}
ReaderState::Reading(s) => {
let n = self.r.read(&mut m.flash, data).await?;
data = &mut data[n..];
s.seq = s.seq.add(n)?;
if n == 0 {
self.next_page(m).await?;
} else {
data = &mut data[n..];
s.seq = s.seq.add(n)?;
read_some = true;
}
}
}
Expand Down Expand Up @@ -1624,6 +1638,62 @@ mod tests {
assert_eq!(data, buf);
}

#[test_log::test(tokio::test)]
async fn test_empty_eof() {
let mut f = MemFlash::new();
let mut m = FileManager::new(&mut f, 0);
let mut pr = PageReader::new();
m.format().await.unwrap();
m.mount(&mut pr).await.unwrap();

let mut r = m.read(&mut pr, 0);
let mut buf = vec![0; 1];
let res = r.read(&mut m, &mut buf).await;
assert_eq!(res, Err(ReadError::Eof));
}

#[test_log::test(tokio::test)]
async fn test_read_eof() {
let mut f = MemFlash::new();
let mut m = FileManager::new(&mut f, 0);
let mut pr = PageReader::new();
m.format().await.unwrap();
m.mount(&mut pr).await.unwrap();

let data = dummy_data(24);

let mut w = m.write(&mut pr, 0).await.unwrap();
w.write(&mut m, &data).await.unwrap();
m.commit(&mut w).await.unwrap();

let mut r = m.read(&mut pr, 0);
let mut buf = vec![0; data.len()];
r.read(&mut m, &mut buf).await.unwrap();
assert_eq!(data, buf);
let res = r.read(&mut m, &mut buf).await;
assert_eq!(res, Err(ReadError::Eof));
}

#[test_log::test(tokio::test)]
async fn test_read_unexpected_eof() {
let mut f = MemFlash::new();
let mut m = FileManager::new(&mut f, 0);
let mut pr = PageReader::new();
m.format().await.unwrap();
m.mount(&mut pr).await.unwrap();

let data = dummy_data(24);

let mut w = m.write(&mut pr, 0).await.unwrap();
w.write(&mut m, &data).await.unwrap();
m.commit(&mut w).await.unwrap();

let mut r = m.read(&mut pr, 0);
let mut buf = vec![0; data.len() + 1];
let res = r.read(&mut m, &mut buf).await;
assert_eq!(res, Err(ReadError::Corrupted));
}

#[test_log::test(tokio::test)]
async fn test_append() {
let mut f = MemFlash::new();
Expand Down

0 comments on commit f121681

Please sign in to comment.