Something went wrong. Try again.
STreaming ARchive format: simpler, stricter, smaller than CARs for a subset of use-cases.
star car archive atproto
Something went wrong. Try again.
2.8 kB · 97 lines
Rust
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798//! Reading from a source that short-reads and errors on a fuzzer-chosen//! schedule.//!//! The arbitrary-bytes targets read from a slice, which only ever succeeds or//! hits clean EOF. Real sources — sockets, pipes, failing disks — return//! `Interrupted`, short reads, and persistent errors, and those paths are//! where a reader is most likely to spin or lose its place. This target is the//! one that reaches them.#![no_main]
use arbitrary::Arbitrary;use libfuzzer_sys::fuzz_target;use star_lite::Reader;use std::io::{self, Read};
#[derive(Arbitrary, Debug)]enum Step { Ok(u8), Interrupted, WouldBlock, Broken,}
#[derive(Arbitrary, Debug)]struct Input { archive: Vec<u8>, schedule: Vec<Step>,}
/// Serves `data` according to `schedule`, cycling once the schedule runs out.struct FlakySource { data: Vec<u8>, pos: usize, schedule: Vec<Step>, step: usize,}
impl Read for FlakySource { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { if buf.is_empty() { return Ok(0); } let step = if self.schedule.is_empty() { &Step::Ok(u8::MAX) } else { let s = &self.schedule[self.step % self.schedule.len()]; self.step += 1; s }; match step { Step::Interrupted => Err(io::Error::from(io::ErrorKind::Interrupted)), Step::WouldBlock => Err(io::Error::from(io::ErrorKind::WouldBlock)), Step::Broken => Err(io::Error::from(io::ErrorKind::BrokenPipe)), Step::Ok(n) => { let remaining = self.data.len() - self.pos; if remaining == 0 { return Ok(0); // clean EOF } // A short read of at least one byte, capped by the buffer. let n = (*n as usize).clamp(1, buf.len()).min(remaining); buf[..n].copy_from_slice(&self.data[self.pos..self.pos + n]); self.pos += n; Ok(n) } } }}
fuzz_target!(|input: Input| { let bound = input.archive.len() + 2; let source = FlakySource { data: input.archive, pos: 0, schedule: input.schedule, step: 0, };
let Ok(mut reader) = Reader::new(source) else { return; };
let mut seen = 0usize; while let Some(item) = reader.next() { seen += 1; assert!( seen <= bound, "reader did not make progress: {seen} items from {bound} bytes" ); if item.is_err() { assert!( reader.next().is_none(), "an error must be terminal for the iterator" ); break; } }});