|
| 1 | +#![no_main] |
| 2 | + |
| 3 | +extern crate libfuzzer_sys; |
| 4 | +extern crate std; |
| 5 | + |
| 6 | +use arbitrary::Arbitrary; |
| 7 | +use ec_slimloader_state::{ |
| 8 | + flash::{ |
| 9 | + self, |
| 10 | + mock::{MockFlashBase, MockFlashError::EarlyShutoff}, |
| 11 | + FlashJournal, |
| 12 | + }, |
| 13 | + state::State, |
| 14 | +}; |
| 15 | +use libfuzzer_sys::fuzz_target; |
| 16 | + |
| 17 | +fuzz_target!(|input: Input| fuzz(input.states, input.fail_at)); |
| 18 | + |
| 19 | +#[derive(Debug)] |
| 20 | +struct Input { |
| 21 | + /// Set of consecutive states to write to disk. |
| 22 | + pub states: Vec<State>, |
| 23 | + |
| 24 | + /// Byte number to fail at when doing disk operations. |
| 25 | + pub fail_at: usize, |
| 26 | +} |
| 27 | + |
| 28 | +impl<'a> Arbitrary<'a> for Input { |
| 29 | + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { |
| 30 | + let states: Vec<State> = Arbitrary::arbitrary(u)?; |
| 31 | + let fail_at = u.int_in_range(0..=states.len() * 2)?; |
| 32 | + Ok(Input { states, fail_at }) |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +const PAGES: usize = 4; |
| 37 | +const WORD_SIZE: usize = 2; |
| 38 | +const WORDS_PER_PAGE: usize = 16; |
| 39 | + |
| 40 | +/// Tests for 'any input disk, with valid or invalid data, does not cause a crash'. |
| 41 | +fn fuzz(states: Vec<State>, fail_at: usize) { |
| 42 | + let mut flash = MockFlashBase::<PAGES, WORD_SIZE, WORDS_PER_PAGE>::new(Some(fail_at as u32), false); |
| 43 | + let mut states = states.into_iter().peekable(); |
| 44 | + |
| 45 | + futures::executor::block_on(async { |
| 46 | + let mut journal = FlashJournal::new::<4>(&mut flash).await.unwrap(); |
| 47 | + |
| 48 | + let mut prev_state = None; |
| 49 | + while let Some(new_state) = states.peek() { |
| 50 | + match journal.set::<4>(new_state).await { |
| 51 | + Ok(_) => { |
| 52 | + assert_eq!(journal.get(), Some(new_state)); |
| 53 | + } |
| 54 | + Err(flash::Error::Other(EarlyShutoff(_, _))) => { |
| 55 | + drop(journal); |
| 56 | + flash.remove_shutoff(); |
| 57 | + journal = FlashJournal::new::<4>(&mut flash).await.unwrap(); |
| 58 | + let old_state = journal.get(); |
| 59 | + |
| 60 | + if old_state == prev_state.as_ref() { |
| 61 | + // Old state was at least kept, even though new state was not persisted. |
| 62 | + continue; |
| 63 | + } else if old_state == Some(new_state) { |
| 64 | + // New state was successfully persisted, even though we had a shutoff (probably stopped during some bookkeeping?). |
| 65 | + } else { |
| 66 | + panic!("State not maintained or persisted"); |
| 67 | + } |
| 68 | + break; |
| 69 | + } |
| 70 | + Err(e) => panic!("Unexpected error {:?}", e), |
| 71 | + } |
| 72 | + |
| 73 | + prev_state = states.next(); // Successfully persisted, drop the state. |
| 74 | + } |
| 75 | + }); |
| 76 | +} |
0 commit comments