diff --git a/libafl/src/corpus/minset.rs b/libafl/src/corpus/minset.rs new file mode 100644 index 0000000000..713573ceb6 --- /dev/null +++ b/libafl/src/corpus/minset.rs @@ -0,0 +1,225 @@ +use alloc::{borrow::ToOwned, vec::Vec}; +use core::{cell::RefCell, iter::Iterator, marker::PhantomData}; +use serde::{Deserialize, Serialize}; + +use crate::{ + corpus::Corpus, corpus::HasTestcaseVec, corpus::Testcase, inputs::{HasLen, Input}, utils::Rand, Error, +}; + +pub trait FavFactor: Serialize + serde::de::DeserializeOwned + 'static +{ + fn compute(testcase: &Testcase) -> Result + where + I: Input; +} + +pub struct LenTimeMulFavFactor {} + +// TODO time as Duration and put len into Testcase +impl FavFactor for LenTimeMulFavFactor { + fn compute(entry: &Testcase) -> Result + where + I: Input + HasLen + { + entry.exec_time() * entry.load_input().len() + } +} + +pub trait CorpusMinimizer { + fn update_score(corpus: &mut C, idx: usize) -> Result<(), Error> + where + C: Corpus, + I: Input, + R: Rand; + + fn cull(corpus: &mut C) -> Result<(), Error> + where + C: Corpus, + I: Input, + R: Rand; +} + +pub struct FavCorpusMinimizer +where + F: FavFactor +{ + phantom: PhantomData +} + +impl CorpusMinimizer for FavCorpusMinimizer +where + F: FavFactor +{ + fn update_score(corpus: &mut C, idx: usize) -> Result<(), Error> + where + C: Corpus, + I: Input, + R: Rand + { + + } + + fn cull(corpus: &mut C) -> Result<(), Error> + where + C: Corpus, + I: Input, + R: Rand + { + + } +} + + +#[derive(Serialize, Deserialize)] +pub struct NoveltiesMeta { + novelties: Vec, +} +// impl Iterator + +/// A Queue-like corpus, wrapping an existing Corpus instance +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "I: serde::de::DeserializeOwned")] +pub struct MinSetCorpus +where + C: Corpus, + F: FavFactor, + I: Input, + IT: Iterator, + R: Rand, +{ + corpus: C, + pos: usize, + // TODO rebase minset on remove() + minset: HashSet, + top_rated: HashMap, + phantom: PhantomData<(F, I, IT, R)>, +} + +impl HasTestcaseVec for MinSetCorpus +where + C: Corpus, + I: Input, + R: Rand, +{ + #[inline] + fn entries(&self) -> &[RefCell>] { + self.corpus.entries() + } + #[inline] + fn entries_mut(&mut self) -> &mut Vec>> { + self.corpus.entries_mut() + } +} + +impl Corpus for MinSetCorpus +where + C: Corpus, + I: Input, + R: Rand, +{ + /// Returns the number of elements + #[inline] + fn count(&self) -> usize { + self.corpus.count() + } + + // TODO change add to return Result + #[inline] + fn add(&mut self, entry: Testcase) -> usize { + let idx = self.corpus.add(entry); + self.update_score(idx).unwrap(); + idx + } + + /// Removes an entry from the corpus, returning it if it was present. + #[inline] + fn remove(&mut self, entry: &Testcase) -> Option> { + self.corpus.remove(entry) + } + + /// Gets a random entry + #[inline] + fn random_entry(&self, rand: &mut R) -> Result<(&RefCell>, usize), Error> { + self.corpus.random_entry(rand) + } + + /// Returns the testacase we currently use + #[inline] + fn current_testcase(&self) -> (&RefCell>, usize) { + (self.get(self.pos - 1), self.pos - 1) + } + + /// Gets the next entry + #[inline] + fn next(&mut self, _rand: &mut R) -> Result<(&RefCell>, usize), Error> { + self.cull(); + self.pos += 1; + if self.corpus.count() == 0 { + return Err(Error::Empty("Corpus".to_owned())); + } + if self.pos > self.corpus.count() { + // TODO: Always loop or return informational error? + self.pos = 1; + self.cycles += 1; + } + Ok((&self.corpus.entries()[self.pos - 1], self.pos - 1)) + } +} + +impl MinSetCorpus +where + C: Corpus, + I: Input, + R: Rand, +{ + pub fn new(corpus: C) -> Self { + Self { + corpus: corpus, + phantom: PhantomData, + } + } + + #[inline] + pub fn corpus(&self) -> &C { + &self.corpus + } + + #[inline] + pub fn corpus_mut(&mut self) -> &mut C { + &mut self.corpus + } + + // TODO move this functions and top rated to another struct + // create something like MinSetCorpus<.., FavMinimizer> + + pub fn update_score(&mut self, idx: usize) -> Result<(), Error> { + let factor = F::compute(self.entries()[idx].borrow())?; + for elem in entry.get::() { + if let val = self.top_rated.get_mut(elem) { + if factor > F::compute(self.entries()[val].borrow())? { + continue + } + } + + let _ = self.top_rated.insert(elem, idx); + } + } + + pub fn cull(&mut self) { + let mut acc = HashSet::new(); + self.minset.clear(); + + for key in self.top_rated.keys() { + if !acc.contains(key) { + let idx = self.top_rated.get(key).unwrap(); + let entry = self.entries()[idx].borrow(); + for elem in entry.get::() { + acc.insert(elem); + } + + self.minset.insert(idx); + } + } + } +} + diff --git a/libafl/src/corpus/mod old.rs b/libafl/src/corpus/mod old.rs new file mode 100644 index 0000000000..86eeaf961f --- /dev/null +++ b/libafl/src/corpus/mod old.rs @@ -0,0 +1,103 @@ +//! Corpuses contain the testcases, either in mem, on disk, or somewhere else. +//! They will hand out the next fuzz target, potentially doing basic scheduling. + +pub mod testcase; +pub use testcase::Testcase; + +pub mod inmemory; +pub use inmemory::InMemoryCorpus; + +#[cfg(feature = "std")] +pub mod ondisk; +#[cfg(feature = "std")] +pub use ondisk::OnDiskCorpus; + +pub mod queue; +pub use queue::QueueCorpus; + +use alloc::{borrow::ToOwned, vec::Vec}; +use core::{cell::RefCell, ptr}; + +use crate::{inputs::Input, utils::Rand, Error}; + +/// A way to obtain the containing testcase entries +pub trait HasTestcaseVec +where + I: Input, +{ + /// Get the entries vector field + fn entries(&self) -> &[RefCell>]; + + /// Get the entries vector field (mutable) + fn entries_mut(&mut self) -> &mut Vec>>; +} + +/// Corpus with all current testcases +pub trait Corpus: HasTestcaseVec + serde::Serialize + serde::de::DeserializeOwned +where + I: Input, + R: Rand, +{ + /// Returns the number of elements + #[inline] + fn count(&self) -> usize { + self.entries().len() + } + + // TODO implement a was_fuzzed counter + + /// Add an entry to the corpus and return its index + #[inline] + fn add(&mut self, testcase: Testcase) -> usize { + self.entries_mut().push(RefCell::new(testcase)); + self.entries().len() - 1 + } + + /// Replaces the testcase at the given idx + fn replace(&mut self, idx: usize, testcase: Testcase) -> Result<(), Error> { + if self.entries_mut().len() < idx { + return Err(Error::KeyNotFound(format!("Index {} out of bounds", idx))); + } + self.entries_mut()[idx] = RefCell::new(testcase); + Ok(()) + } + + /// Get by id + #[inline] + fn get(&self, idx: usize) -> &RefCell> { + &self.entries()[idx] + } + + /// Removes an entry from the corpus, returning it if it was present. + #[inline] + fn remove(&mut self, entry: &Testcase) -> Option> { + match self + .entries() + .iter() + .position(|x| ptr::eq(x.as_ptr(), entry)) + { + Some(i) => Some(self.entries_mut().remove(i).into_inner()), + None => None, + } + } + + /// Gets a random entry + #[inline] + fn random_entry(&self, rand: &mut R) -> Result<(&RefCell>, usize), Error> { + if self.count() == 0 { + Err(Error::Empty("No entries in corpus".to_owned())) + } else { + let len = { self.entries().len() }; + let id = rand.below(len as u64) as usize; + Ok((self.get(id), id)) + } + } + + // TODO: IntoIter + /// Gets the next entry + fn next(&mut self, rand: &mut R) -> Result<(&RefCell>, usize), Error>; + + /// Returns the testacase we currently use + fn current_testcase(&self) -> (&RefCell>, usize); +} + diff --git a/libafl/src/corpus/mod.rs b/libafl/src/corpus/mod.rs index 14b87341bd..3ac1b49265 100644 --- a/libafl/src/corpus/mod.rs +++ b/libafl/src/corpus/mod.rs @@ -1,102 +1,204 @@ //! Corpuses contain the testcases, either in mem, on disk, or somewhere else. -//! They will hand out the next fuzz target, potentially doing basic scheduling. pub mod testcase; pub use testcase::Testcase; -pub mod inmemory; -pub use inmemory::InMemoryCorpus; +use alloc::{vec::Vec}; +use core::{cell::RefCell}; -#[cfg(feature = "std")] -pub mod ondisk; -#[cfg(feature = "std")] -pub use ondisk::OnDiskCorpus; - -pub mod queue; -pub use queue::QueueCorpus; - -use alloc::{borrow::ToOwned, vec::Vec}; -use core::{cell::RefCell, ptr}; - -use crate::{inputs::Input, utils::Rand, Error}; - -/// A way to obtain the containing testcase entries -pub trait HasTestcaseVec -where - I: Input, -{ - /// Get the entries vector field - fn entries(&self) -> &[RefCell>]; - - /// Get the entries vector field (mutable) - fn entries_mut(&mut self) -> &mut Vec>>; -} +use crate::{ + inputs::Input, + state::{HasCorpus, HasRand}, + utils::Rand, + Error, +}; /// Corpus with all current testcases -pub trait Corpus: HasTestcaseVec + serde::Serialize + serde::de::DeserializeOwned +pub trait Corpus: serde::Serialize + serde::de::DeserializeOwned where I: Input, - R: Rand, { /// Returns the number of elements - #[inline] - fn count(&self) -> usize { - self.entries().len() - } - - // TODO implement a was_fuzzed counter + fn count(&self) -> usize; /// Add an entry to the corpus and return its index - #[inline] - fn add(&mut self, testcase: Testcase) -> usize { - self.entries_mut().push(RefCell::new(testcase)); - self.entries().len() - 1 - } + fn add(&mut self, testcase: Testcase) -> Result; /// Replaces the testcase at the given idx - fn replace(&mut self, idx: usize, testcase: Testcase) -> Result<(), Error> { - if self.entries_mut().len() < idx { - return Err(Error::KeyNotFound(format!("Index {} out of bounds", idx))); - } - self.entries_mut()[idx] = RefCell::new(testcase); + fn replace(&mut self, idx: usize, testcase: Testcase) -> Result<(), Error>; + + /// Removes an entry from the corpus, returning it if it was present. + fn remove(&mut self, idx: usize) -> Result>, Error>; + + /// Get by id + fn get(&self, idx: usize) -> Result<&RefCell>, Error>; +} + +pub trait CorpusScheduler { + + /// Add an entry to the corpus and return its index + fn on_add(&self, state: &mut S, idx: usize, testcase: &Testcase) -> Result<(), Error> + where + S: HasCorpus + HasRand, + C: Corpus, + I: Input, + R: Rand + { Ok(()) } - /// Get by id - #[inline] - fn get(&self, idx: usize) -> &RefCell> { - &self.entries()[idx] + /// Replaces the testcase at the given idx + fn on_replace(&self, state: &mut S, idx: usize, testcase: &Testcase) -> Result<(), Error> + where + S: HasCorpus + HasRand, + C: Corpus, + I: Input, + R: Rand + { + Ok(()) } /// Removes an entry from the corpus, returning it if it was present. - #[inline] - fn remove(&mut self, entry: &Testcase) -> Option> { - match self - .entries() - .iter() - .position(|x| ptr::eq(x.as_ptr(), entry)) - { - Some(i) => Some(self.entries_mut().remove(i).into_inner()), - None => None, - } - } - - /// Gets a random entry - #[inline] - fn random_entry(&self, rand: &mut R) -> Result<(&RefCell>, usize), Error> { - if self.count() == 0 { - Err(Error::Empty("No entries in corpus".to_owned())) - } else { - let len = { self.entries().len() }; - let id = rand.below(len as u64) as usize; - Ok((self.get(id), id)) - } + fn on_remove(&self, state: &mut S, idx: usize, testcase: &Option>) -> Result<(), Error> + where + S: HasCorpus + HasRand, + C: Corpus, + I: Input, + R: Rand + { + Ok(()) } // TODO: IntoIter /// Gets the next entry - fn next(&mut self, rand: &mut R) -> Result<(&RefCell>, usize), Error>; + fn next(&self, state: &mut S) -> Result + where + S: HasCorpus + HasRand, + C: Corpus, + I: Input, + R: Rand; - /// Returns the testacase we currently use - fn current_testcase(&self) -> (&RefCell>, usize); +} + +pub struct RandCorpusScheduler {} + +impl CorpusScheduler for RandCorpusScheduler { + /// Gets the next entry at random + fn next(state: &mut S) -> Result + where + S: HasCorpus + HasRand, + C: Corpus, + I: Input, + R: Rand + { + if state.corpus().count() == 0 { + Err(Error::Empty("No entries in corpus".to_owned())) + } else { + let len = state.corpus().count(); + let id = state.rand_mut().below(len as u64) as usize; + Ok(id) + } + } +} + +pub struct InMemoryCorpus +where + I: Input, +{ + entries: Vec>>, +} + +impl Corpus for InMemoryCorpus +where + I: Input, +{ + + /// Returns the number of elements + #[inline] + fn count(&self) -> usize { + self.entries.len() + } + + /// Add an entry to the corpus and return its index + #[inline] + fn add(&mut self, testcase: Testcase) -> Result { + self.entries.push(RefCell::new(testcase)); + Ok(self.entries.len() - 1) + } + + /// Replaces the testcase at the given idx + #[inline] + fn replace(&mut self, idx: usize, testcase: Testcase) -> Result<(), Error> { + if idx >= self.entries.len() { + return Err(Error::KeyNotFound(format!("Index {} out of bounds", idx))); + } + self.entries[idx] = RefCell::new(testcase); + Ok(()) + } + + /// Removes an entry from the corpus, returning it if it was present. + #[inline] + fn remove(&mut self, idx: usize) -> Result>, Error> { + if idx >= self.entries.len() { + Ok(None) + } else { + Ok(Some(self.entries.remove(idx).into_inner())) + } + } + + /// Get by id + #[inline] + fn get(&self, idx: usize) -> Result<&RefCell>, Error> { + Ok(&self.entries[idx]) + } + + /*/// Add an entry to the corpus and return its index + #[inline] + fn add(state: &mut S, testcase: Testcase) -> Result + where + S: HasCorpus + HasRand, + R: Rand + { + state.corpus_mut().entries.push(RefCell::new(testcase)); + let idx = state.corpus().entries.len() - 1; + // Scheduler hook + SC::on_add(state, idx, state.corpus().entries[idx].borrow())?; + Ok(idx) + } + + /// Replaces the testcase at the given idx + #[inline] + fn replace(state: &mut S, idx: usize, testcase: Testcase) -> Result<(), Error> + where + S: HasCorpus + HasRand, + R: Rand + { + if state.corpus().entries.len() < idx { + return Err(Error::KeyNotFound(format!("Index {} out of bounds", idx))); + } + state.corpus_mut().entries[idx] = RefCell::new(testcase); + // Scheduler hook + SC::on_replace(state, idx, state.corpus().entries[idx])?; + Ok(()) + } + + /// Removes an entry from the corpus, returning it if it was present. + #[inline] + fn remove(state: &mut S, idx: usize) -> Result>, Error> + where + S: HasCorpus + HasRand, + R: Rand + { + let testcase = match state.corpus_mut() + .entries + .iter() + .position(|x| ptr::eq(x.as_ptr(), entry)) + { + Some(i) => Some(state.corpus_mut().entries.remove(i).into_inner()), + None => None, + }; + // Scheduler hook + SC::on_remove(state, idx, &testcase)?; + Ok(testcase) + }*/ } diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 724b0d4d41..76ac3abad5 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -148,13 +148,6 @@ where self.0.discard_metadata(input)?; self.1.discard_metadata_all(input) } - - /* - fn restore_state_from_all(&mut self, restore_from: &Self) -> Result<(), Error> { - self.0.restore_from(restore_from.0)?; - self.1.restore_state_from_all(restore_from.1)?; - } - */ } /// Is a crash feedback diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs new file mode 100644 index 0000000000..50cc535425 --- /dev/null +++ b/libafl/src/fuzzer.rs @@ -0,0 +1,142 @@ +use core::{marker::PhantomData}; + +use crate::{ + corpus::{CorpusScheduler}, + events::{Event, EventManager}, + executors::{Executor}, + inputs::Input, + stages::StagesTuple, + utils::{current_milliseconds, current_time}, + Error +}; + +/// Holds a set of stages +pub trait HasStages +where + ST: StagesTuple, +{ + fn stages(&self) -> &ST; + + fn stages_mut(&mut self) -> &mut ST; +} + +/// Holds a set of stages +pub trait HasCorpusScheduler +where + CS: CorpusScheduler, +{ + fn scheduler(&self) -> &CS; + + fn scheduler_mut(&mut self) -> &mut CS; +} + +/// The main fuzzer trait. +pub trait Fuzzer: HasCorpusScheduler + HasStages +where + CS: CorpusScheduler, + ST: StagesTuple, + I: Input +{ + fn fuzz_one( + &mut self, + executor: &mut E, + state: &mut S, + manager: &mut EM, + ) -> Result + where + EM: EventManager, + E: Executor, + { + let idx = self.scheduler().next(state)?; + + self.stages() + .perform_all(executor, state, manager, idx)?; + + manager.process(state, executor)?; + Ok(idx) + } + + fn fuzz_loop( + &mut self, + executor: &mut E, + state: &mut S, + manager: &mut EM, + ) -> Result + where + EM: EventManager, + E: Executor, + { + let mut last = current_milliseconds(); + loop { + self.fuzz_one(executor, state, manager)?; + let cur = current_milliseconds(); + if cur - last > 60 * 100 { + last = cur; + manager.fire( + state, + Event::UpdateStats { + executions: state.executions(), + time: current_time(), + phantom: PhantomData, + }, + )? + } + } + } +} + +/// Your default fuzzer instance, for everyday use. +#[derive(Clone, Debug)] +pub struct StdFuzzer +where + CS: CorpusScheduler, + ST: StagesTuple, + I: Input +{ + scheduler: CS, + stages: ST, +} + +impl HasStages for StdFuzzer +where + CS: CorpusScheduler, + ST: StagesTuple, + I: Input +{ + fn stages(&self) -> &ST { + &self.stages + } + + fn stages_mut(&mut self) -> &mut ST { + &mut self.stages + } +} + +impl HasCorpusScheduler for StdFuzzer +where + CS: CorpusScheduler, + ST: StagesTuple, + I: Input +{ + fn scheduler(&self) -> &CS { + &self.scheduler + } + + fn scheduler_mut(&mut self) -> &mut CS { + &mut self.scheduler + } +} + +impl StdFuzzer +where + CS: CorpusScheduler, + ST: StagesTuple, + I: Input +{ + pub fn new(scheduler: CS, stages: ST) -> Self { + Self { + scheduler: scheduler, + stages: stages, + } + } +} diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index 2906185e91..e1e11c07c1 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -25,144 +25,15 @@ pub mod state; pub mod stats; pub mod utils; +pub mod fuzzer; +pub use fuzzer::*; + use alloc::string::String; -use core::{fmt, marker::PhantomData}; -use corpus::Corpus; -use events::{Event, EventManager}; -use executors::{Executor, HasObservers}; -use feedbacks::FeedbacksTuple; -use inputs::Input; -use observers::ObserversTuple; -use stages::StagesTuple; -use state::{HasCorpus, State}; -use utils::{current_milliseconds, current_time, Rand}; +use core::{fmt}; #[cfg(feature = "std")] use std::{env::VarError, io, num::ParseIntError, string::FromUtf8Error}; -/// The main fuzzer trait. -pub trait Fuzzer -where - ST: StagesTuple, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, -{ - fn stages(&self) -> &ST; - - fn stages_mut(&mut self) -> &mut ST; - - fn fuzz_one( - &mut self, - rand: &mut R, - executor: &mut E, - state: &mut State, - manager: &mut EM, - ) -> Result { - let (_, idx) = state.corpus_mut().next(rand)?; - - self.stages_mut() - .perform_all(rand, executor, state, manager, idx)?; - - manager.process(state, executor)?; - Ok(idx) - } - - fn fuzz_loop( - &mut self, - rand: &mut R, - executor: &mut E, - state: &mut State, - manager: &mut EM, - ) -> Result<(), Error> { - let mut last = current_milliseconds(); - loop { - self.fuzz_one(rand, executor, state, manager)?; - let cur = current_milliseconds(); - if cur - last > 60 * 100 { - last = cur; - manager.fire( - state, - Event::UpdateStats { - executions: state.executions(), - time: current_time(), - phantom: PhantomData, - }, - )? - } - } - } -} - -/// Your default fuzzer instance, for everyday use. -#[derive(Clone, Debug)] -pub struct StdFuzzer -where - ST: StagesTuple, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, -{ - stages: ST, - phantom: PhantomData<(EM, E, OC, OFT, OT, FT, C, I, R)>, -} - -impl Fuzzer - for StdFuzzer -where - ST: StagesTuple, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, -{ - fn stages(&self) -> &ST { - &self.stages - } - - fn stages_mut(&mut self) -> &mut ST { - &mut self.stages - } -} - -impl StdFuzzer -where - ST: StagesTuple, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, -{ - pub fn new(stages: ST) -> Self { - Self { - stages: stages, - phantom: PhantomData, - } - } -} - /// Main error struct for AFL #[derive(Debug)] pub enum Error { diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index d8f0bd9ac2..8498b700ea 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -8,10 +8,7 @@ pub mod token_mutations; pub use token_mutations::*; use crate::{ - corpus::Corpus, inputs::Input, - state::{HasCorpus, HasMetadata}, - utils::Rand, Error, }; @@ -20,24 +17,20 @@ use crate::{ /// A mutator takes input, and mutates it. /// Simple as that. -pub trait Mutator +pub trait Mutator where - C: Corpus, I: Input, - R: Rand, - S: HasCorpus + HasMetadata, { /// Mutate a given input - fn mutate( + fn mutate( &mut self, - rand: &mut R, state: &mut S, input: &mut I, stage_idx: i32, ) -> Result<(), Error>; /// Post-process given the outcome of the execution - fn post_exec( + fn post_exec( &mut self, _state: &mut S, _is_interesting: u32, diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 2ea67a3a03..805e4e7ca9 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -5,125 +5,83 @@ use crate::{ bolts::tuples::TupleList, corpus::Corpus, events::EventManager, - executors::{Executor, HasObservers}, - feedbacks::FeedbacksTuple, + executors::{Executor}, inputs::Input, - observers::ObserversTuple, - state::State, - utils::Rand, Error, }; /// A stage is one step in the fuzzing process. /// Multiple stages will be scheduled one by one for each input. -pub trait Stage +pub trait Stage where - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, + I: Input { /// Run the stage - fn perform( - &mut self, - rand: &mut R, + fn perform( + &self, executor: &mut E, - state: &mut State, + state: &mut S, manager: &mut EM, corpus_idx: usize, - ) -> Result<(), Error>; + ) -> Result<(), Error> + where + EM: EventManager, + E: Executor; } -pub trait StagesTuple +pub trait StagesTuple where - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, + I: Input { - fn perform_all( - &mut self, - rand: &mut R, + fn perform_all( + &self, executor: &mut E, - state: &mut State, + state: &mut S, manager: &mut EM, corpus_idx: usize, - ) -> Result<(), Error>; - fn for_each(&self, f: fn(&dyn Stage)); - fn for_each_mut(&mut self, f: fn(&mut dyn Stage)); + ) -> Result<(), Error> + where + EM: EventManager, + E: Executor; } -impl StagesTuple for () +impl StagesTuple for () where - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, + I: Input { - fn perform_all( - &mut self, - _rand: &mut R, - _executor: &mut E, - _state: &mut State, - _manager: &mut EM, - _corpus_idx: usize, - ) -> Result<(), Error> { + fn perform_all( + &self, + executor: &mut E, + state: &mut S, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> + where + EM: EventManager, + E: Executor + { Ok(()) } - fn for_each(&self, _f: fn(&dyn Stage)) {} - fn for_each_mut(&mut self, _f: fn(&mut dyn Stage)) {} } -impl StagesTuple - for (Head, Tail) +impl StagesTuple for (Head, Tail) where - Head: Stage, - Tail: StagesTuple + TupleList, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, + Head: Stage, + Tail: StagesTuple + TupleList, + I: Input { - fn perform_all( - &mut self, - rand: &mut R, + fn perform_all( + &self, executor: &mut E, - state: &mut State, + state: &mut S, manager: &mut EM, corpus_idx: usize, - ) -> Result<(), Error> { - self.0.perform(rand, executor, state, manager, corpus_idx)?; - self.1 - .perform_all(rand, executor, state, manager, corpus_idx) - } - - fn for_each(&self, f: fn(&dyn Stage)) { - f(&self.0); - self.1.for_each(f) - } - - fn for_each_mut(&mut self, f: fn(&mut dyn Stage)) { - f(&mut self.0); - self.1.for_each_mut(f) + ) -> Result<(), Error> + where + EM: EventManager, + E: Executor + { + self.0.perform(executor, state, manager, corpus_idx)?; + self.1 .perform_all(executor, state, manager, corpus_idx) } } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 9c6c7fbf3a..7255e957bc 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -2,14 +2,12 @@ use core::marker::PhantomData; use crate::{ events::EventManager, - executors::{Executor, HasObservers}, - feedbacks::FeedbacksTuple, + executors::{Executor}, inputs::Input, mutators::Mutator, - observers::ObserversTuple, stages::Corpus, stages::Stage, - state::{HasCorpus, State}, + state::{HasRand}, utils::Rand, Error, }; @@ -19,19 +17,10 @@ use crate::{ /// A Mutational stage is the stage in a fuzzing run that mutates inputs. /// Mutational stages will usually have a range of mutations that are /// being applied to the input one by one, between executions. -pub trait MutationalStage: - Stage +pub trait MutationalStage: Stage where - M: Mutator>, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, + M: Mutator, I: Input, - R: Rand, { /// The mutator registered for this stage fn mutator(&self) -> &M; @@ -40,22 +29,21 @@ where fn mutator_mut(&mut self) -> &mut M; /// Gets the number of iterations this mutator should run for. - /// This call uses internal mutability, so it may change for each call - #[inline] - fn iterations(&mut self, rand: &mut R) -> usize { - 1 + rand.below(128) as usize - } + fn iterations(&mut self, state: &mut S) -> usize; /// Runs this (mutational) stage for the given testcase - fn perform_mutational( - &mut self, - rand: &mut R, + fn perform_mutational( + &self, executor: &mut E, - state: &mut State, + state: &mut S, manager: &mut EM, corpus_idx: usize, - ) -> Result<(), Error> { - let num = self.iterations(rand); + ) -> Result<(), Error> + where + EM: EventManager, + E: Executor + { + let num = self.iterations(state); for i in 0..num { let mut input_mut = state .corpus() @@ -64,7 +52,7 @@ where .load_input()? .clone(); self.mutator_mut() - .mutate(rand, state, &mut input_mut, i as i32)?; + .mutate(state, &mut input_mut, i as i32)?; let fitness = state.process_input(input_mut, executor, manager)?; @@ -74,38 +62,23 @@ where } } -#[derive(Clone, Debug)] +pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128; + /// The default mutational stage -pub struct StdMutationalStage +#[derive(Clone, Debug)] +pub struct StdMutationalStage where - C: Corpus, - E: Executor + HasObservers, - EM: EventManager, - FT: FeedbacksTuple, + M: Mutator, I: Input, - M: Mutator>, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - R: Rand, { mutator: M, - phantom: PhantomData<(EM, E, OC, OFT, OT, FT, C, I, R)>, + phantom: PhantomData, } -impl MutationalStage - for StdMutationalStage +impl MutationalStage for StdMutationalStage where - C: Corpus, - E: Executor + HasObservers, - EM: EventManager, - FT: FeedbacksTuple, + M: Mutator, I: Input, - M: Mutator>, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - R: Rand, { /// The mutator, added to this stage #[inline] @@ -118,47 +91,42 @@ where fn mutator_mut(&mut self) -> &mut M { &mut self.mutator } -} -impl Stage - for StdMutationalStage -where - M: Mutator>, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, -{ - #[inline] - fn perform( - &mut self, - rand: &mut R, - executor: &mut E, - state: &mut State, - manager: &mut EM, - corpus_idx: usize, - ) -> Result<(), Error> { - self.perform_mutational(rand, executor, state, manager, corpus_idx) + /// Gets the number of iterations as a random number + fn iterations(&mut self, state: &mut S) -> usize + where + S: HasRand, + R: Rand + { + 1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS) as usize } } -impl StdMutationalStage +impl Stage for StdMutationalStage where - M: Mutator>, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, + M: Mutator, + I: Input, +{ + #[inline] + fn perform( + &self, + executor: &mut E, + state: &mut S, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> + where + EM: EventManager, + E: Executor + { + self.perform_mutational(executor, state, manager, corpus_idx) + } +} + +impl StdMutationalStage +where + M: Mutator, I: Input, - R: Rand, { /// Creates a new default mutational stage pub fn new(mutator: M) -> Self { diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 983b556509..9337ad98a0 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -25,11 +25,10 @@ use crate::{ use crate::inputs::bytes::BytesInput; /// Trait for elements offering a corpus -pub trait HasCorpus +pub trait HasCorpus where - C: Corpus, + C: Corpus, I: Input, - R: Rand, { /// The testcase corpus fn corpus(&self) -> &C; @@ -37,6 +36,17 @@ where fn corpus_mut(&mut self) -> &mut C; } +/// Trait for elements offering a rand +pub trait HasRand +where + R: Rand, +{ + /// The rand instance + fn rand(&self) -> &R; + /// The rand instance (mut) + fn rand_mut(&mut self) -> &mut R; +} + /// Trait for elements offering metadata pub trait HasMetadata { /// A map, storing all metadata @@ -59,13 +69,15 @@ pub trait HasMetadata { #[serde(bound = "FT: serde::de::DeserializeOwned")] pub struct State where - C: Corpus, + C: Corpus, I: Input, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + OC: Corpus, OFT: FeedbacksTuple, { + /// RNG instance + rand: R, /// How many times the executor ran the harness/target executions: usize, /// The corpus @@ -82,16 +94,16 @@ where /// Objective Feedbacks objective_feedbacks: OFT, - phantom: PhantomData<(R, I)>, + phantom: PhantomData, } #[cfg(feature = "std")] impl State where - C: Corpus, + C: Corpus, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + OC: Corpus, OFT: FeedbacksTuple, { pub fn load_from_directory( @@ -101,7 +113,7 @@ where in_dir: &Path, ) -> Result<(), Error> where - C: Corpus, + C: Corpus, E: Executor + HasObservers, OT: ObserversTuple, EM: EventManager, @@ -143,7 +155,7 @@ where in_dirs: &[PathBuf], ) -> Result<(), Error> where - C: Corpus, + C: Corpus, E: Executor + HasObservers, OT: ObserversTuple, EM: EventManager, @@ -164,13 +176,34 @@ where } } -impl HasCorpus for State +impl HasRand for State where - C: Corpus, + C: Corpus, I: Input, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + OC: Corpus, + OFT: FeedbacksTuple, +{ + /// The rand instance + fn rand(&self) -> &R { + &self.rand + } + + /// The rand instance (mut) + fn rand_mut(&mut self) -> &mut R { + &mut self.rand + } +} + + +impl HasCorpus for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + OC: Corpus, OFT: FeedbacksTuple, { /// Returns the corpus @@ -187,11 +220,11 @@ where /// Trait for elements offering metadata impl HasMetadata for State where - C: Corpus, + C: Corpus, I: Input, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + OC: Corpus, OFT: FeedbacksTuple, { /// Get all the metadata into an HashMap @@ -209,11 +242,11 @@ where impl State where - C: Corpus, + C: Corpus, I: Input, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + OC: Corpus, OFT: FeedbacksTuple, { /// Get executions @@ -299,7 +332,7 @@ where where E: Executor + HasObservers, OT: ObserversTuple, - C: Corpus, + C: Corpus, EM: EventManager, { executor.pre_exec_observers()?; @@ -356,11 +389,11 @@ where #[inline] pub fn add_if_interesting(&mut self, input: I, fitness: u32) -> Result, Error> where - C: Corpus, + C: Corpus, { if fitness > 0 { let testcase = self.input_to_testcase(input, fitness)?; - Ok(Some(self.corpus_mut().add(testcase))) + Ok(Some(C::add(self, testcase)?)) } else { self.discard_input(&input)?; Ok(None) @@ -371,7 +404,7 @@ where #[inline] pub fn add_if_objective(&mut self, input: I, fitness: u32) -> Result, Error> where - C: Corpus, + C: Corpus, { if fitness > 0 { let testcase = self.input_to_testcase(input, fitness)?; @@ -394,7 +427,7 @@ where where E: Executor + HasObservers, OT: ObserversTuple, - C: Corpus, + C: Corpus, EM: EventManager, { let (fitness, obj_fitness) = self.evaluate_input(&input, executor, manager)?; @@ -435,7 +468,7 @@ where ) -> Result<(), Error> where G: Generator, - C: Corpus, + C: Corpus, E: Executor + HasObservers, OT: ObserversTuple, EM: EventManager, diff --git a/libafl/src/utils.rs b/libafl/src/utils.rs index 3b206f22f3..b0eff77df9 100644 --- a/libafl/src/utils.rs +++ b/libafl/src/utils.rs @@ -1,7 +1,7 @@ //! Utility functions for AFL use core::{cell::RefCell, debug_assert, fmt::Debug, time}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; use xxhash_rust::xxh3::xxh3_64_with_seed; #[cfg(feature = "std")] @@ -10,7 +10,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; pub type StdRand = RomuTrioRand; /// Ways to get random around here -pub trait Rand: Debug + Serialize { +pub trait Rand: Debug + Serialize + DeserializeOwned { // Sets the seed of this Rand fn set_seed(&mut self, seed: u64);