From 6a8e9c80c18fb5c1de40d5945a0ced8acba86977 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 16 Mar 2023 16:12:56 +0100 Subject: [PATCH] add a new scheduler for systemtraces --- fuzzers/FRET/src/fuzzer.rs | 4 +- fuzzers/FRET/src/systemstate/mod.rs | 4 +- fuzzers/FRET/src/systemstate/schedulers.rs | 134 +++++++++++++++++++++ 3 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 fuzzers/FRET/src/systemstate/schedulers.rs diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 65984d6349..ff28de50c8 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -39,7 +39,7 @@ use rand::{SeedableRng, StdRng, Rng}; use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, qemustate::QemuStateRestoreHelper, - systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, + systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::LongestTraceScheduler}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, }; pub static mut RNG_SEED: u64 = 1; @@ -302,7 +302,7 @@ pub fn fuzz() { #[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); #[cfg(feature = "feed_systemtrace")] - let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()); + let scheduler = LongestTraceScheduler::new(TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new())); #[cfg(feature = "feed_systemgraph")] let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new()); diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 7d4f04e0d2..76ecf60bc6 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -14,6 +14,7 @@ pub mod helpers; pub mod observers; pub mod feedbacks; pub mod graph; +pub mod schedulers; // pub mod mutators; #[cfg(feature = "fuzz_interrupt")] @@ -125,13 +126,14 @@ impl RefinedFreeRTOSSystemState { #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { inner: Vec, + trace_length: usize, indices: Vec, // Hashed enumeration of States tcref: isize, } impl FreeRTOSSystemStateMetadata { pub fn new(inner: Vec) -> Self{ let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect(); - Self {inner: inner, indices: tmp, tcref: 0} + Self {trace_length: inner.len(), inner: inner, indices: tmp, tcref: 0} } } pub fn compute_hash(obj: T) -> u64 diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs new file mode 100644 index 0000000000..258e610391 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -0,0 +1,134 @@ +//! The Minimizer schedulers are a family of corpus schedulers that feed the fuzzer +//! with testcases only from a subset of the total corpus. + +use core::{marker::PhantomData}; +use std::cmp::max; + +use serde::{Deserialize, Serialize}; + +use libafl::{ + bolts::{rands::Rand, serdeany::SerdeAny, AsSlice, HasRefCnt}, + corpus::{Corpus, Testcase}, + inputs::UsesInput, + schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB }, + state::{HasCorpus, HasMetadata, HasRand, UsesState}, + Error, SerdeAny, + +}; + +use super::FreeRTOSSystemStateMetadata; + +/// A state metadata holding a map of favoreds testcases for each map entry +#[derive(Debug, Serialize, Deserialize, SerdeAny, Default)] +pub struct LongestTracesMetadata { + /// map index -> corpus index + pub max_trace_length: usize, +} + +impl LongestTracesMetadata { + fn new(l : usize) -> Self { + Self {max_trace_length: l} + } +} + +/// The [`MinimizerScheduler`] employs a genetic algorithm to compute a subset of the +/// corpus that exercise all the requested features (e.g. all the coverage seen so far) +/// prioritizing [`Testcase`]`s` using [`TestcaseScore`] +#[derive(Debug, Clone)] +pub struct LongestTraceScheduler { + base: CS, + skip_non_favored_prob: u64, +} + +impl UsesState for LongestTraceScheduler +where + CS: UsesState, +{ + type State = CS::State; +} + +impl Scheduler for LongestTraceScheduler +where + CS: Scheduler, + CS::State: HasCorpus + HasMetadata + HasRand, +{ + /// Add an entry to the corpus and return its index + fn on_add(&self, state: &mut CS::State, idx: usize) -> Result<(), Error> { + let l = state.corpus() + .get(idx)? + .borrow() + .metadata() + .get::().map_or(0, |x| x.trace_length); + self.get_update_trace_length(state,l); + self.base.on_add(state, idx) + } + + /// Replaces the testcase at the given idx + fn on_replace( + &self, + state: &mut CS::State, + idx: usize, + testcase: &Testcase<::Input>, + ) -> Result<(), Error> { + let l = state.corpus() + .get(idx)? + .borrow() + .metadata() + .get::().map_or(0, |x| x.trace_length); + self.get_update_trace_length(state, l); + self.base.on_replace(state, idx, testcase) + } + + /// Removes an entry from the corpus, returning M if M was present. + fn on_remove( + &self, + state: &mut CS::State, + idx: usize, + testcase: &Option::Input>>, + ) -> Result<(), Error> { + self.base.on_remove(state, idx, testcase)?; + Ok(()) + } + + /// Gets the next entry + fn next(&self, state: &mut CS::State) -> Result { + let mut idx = self.base.next(state)?; + while { + let l = state.corpus() + .get(idx)? + .borrow() + .metadata() + .get::().map_or(0, |x| x.trace_length); + let m = self.get_update_trace_length(state,l); + state.rand_mut().below(m) > l as u64 + } && state.rand_mut().below(100) < self.skip_non_favored_prob + { + idx = self.base.next(state)?; + } + Ok(idx) + } +} + +impl LongestTraceScheduler +where + CS: Scheduler, + CS::State: HasCorpus + HasMetadata + HasRand, +{ + pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 { + // Create a new top rated meta if not existing + if let Some(td) = state.metadata_mut().get_mut::() { + let m = max(td.max_trace_length, par); + td.max_trace_length = m; + m as u64 + } else { + state.add_metadata(LongestTracesMetadata::new(par)); + par as u64 + } + } + pub fn new(base: CS) -> Self { + Self { + base, + skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB, + } + } +} \ No newline at end of file