From e2c4ed95e63ed31ae57c7f4b2f37fd395f4833b1 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 5 Feb 2021 00:33:33 +0100 Subject: [PATCH] merged state and corpus --- .gitignore | 1 + afl/src/events/mod.rs | 48 ++------ afl/src/executors/inmemory.rs | 19 +-- afl/src/executors/mod.rs | 6 +- afl/src/mutators/mod.rs | 14 ++- afl/src/mutators/mutations.rs | 127 ++++++++++--------- afl/src/mutators/scheduled.rs | 87 ++++++++----- afl/src/stages/mod.rs | 33 +++-- afl/src/stages/mutational.rs | 72 ++++++----- afl/src/state/mod.rs | 185 ++++++++++++++++------------ afl/src/utils.rs | 16 ++- fuzzers/libfuzzer_libpng/src/mod.rs | 32 ++--- 12 files changed, 335 insertions(+), 305 deletions(-) diff --git a/.gitignore b/.gitignore index 29ff1c7418..9ea899b9c0 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ perf.data perf.data.old .vscode +test.dict \ No newline at end of file diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index bd49f69515..d55063373d 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -213,11 +213,7 @@ where where ST: Stats; /// This method will be called in the clients after handle_in_broker (unless BrokerEventResult::Handled) was returned in handle_in_broker - fn handle_in_client( - self, - state: &mut State, - corpus: &mut C, - ) -> Result<(), AflError> + fn handle_in_client(self, state: &mut State) -> Result<(), AflError> where C: Corpus, FT: FeedbacksTuple, @@ -233,11 +229,7 @@ where /// Lookup for incoming events and process them. /// Return the number of processes events or an error - fn process( - &mut self, - state: &mut State, - corpus: &mut C, - ) -> Result + fn process(&mut self, state: &mut State) -> Result where C: Corpus, FT: FeedbacksTuple, @@ -298,11 +290,7 @@ impl EventManager for NopEventManager where I: Input, { - fn process( - &mut self, - _state: &mut State, - _corpus: &mut C, - ) -> Result + fn process(&mut self, _state: &mut State) -> Result where C: Corpus, FT: FeedbacksTuple, @@ -417,11 +405,7 @@ where } #[inline] - fn handle_in_client( - self, - _state: &mut State, - _corpus: &mut C, - ) -> Result<(), AflError> + fn handle_in_client(self, _state: &mut State) -> Result<(), AflError> where C: Corpus, FT: FeedbacksTuple, @@ -452,11 +436,7 @@ where ST: Stats, //CE: CustomEvent, { - fn process( - &mut self, - state: &mut State, - corpus: &mut C, - ) -> Result + fn process(&mut self, state: &mut State) -> Result where C: Corpus, FT: FeedbacksTuple, @@ -465,7 +445,7 @@ where let count = self.events.len(); self.events .drain(..) - .try_for_each(|event| event.handle_in_client(state, corpus))?; + .try_for_each(|event| event.handle_in_client(state))?; Ok(count) } @@ -682,11 +662,7 @@ where } #[inline] - fn handle_in_client( - self, - state: &mut State, - corpus: &mut C, - ) -> Result<(), AflError> + fn handle_in_client(self, state: &mut State) -> Result<(), AflError> where C: Corpus, FT: FeedbacksTuple, @@ -707,7 +683,7 @@ where let interestingness = state.is_interesting(input.as_ref(), &observers)?; match input { Ptr::Owned(b) => { - state.add_if_interesting(corpus, *b, interestingness)?; + state.add_if_interesting(*b, interestingness)?; } _ => {} }; @@ -894,11 +870,7 @@ where ST: Stats, //CE: CustomEvent, { - fn process( - &mut self, - state: &mut State, - corpus: &mut C, - ) -> Result + fn process(&mut self, state: &mut State) -> Result where C: Corpus, FT: FeedbacksTuple, @@ -919,7 +891,7 @@ where sender_id: sender_id, kind: kind, }; - event.handle_in_client(state, corpus)?; + event.handle_in_client(state)?; count += 1; } None => break count, diff --git a/afl/src/executors/inmemory.rs b/afl/src/executors/inmemory.rs index e4e28de457..a53f37b7ed 100644 --- a/afl/src/executors/inmemory.rs +++ b/afl/src/executors/inmemory.rs @@ -50,8 +50,7 @@ where #[inline] fn pre_exec( &mut self, - _state: &State, - _corpus: &C, + _state: &State, _event_mgr: &mut EM, _input: &I, ) -> Result<(), AflError> @@ -64,7 +63,7 @@ where #[cfg(unix)] #[cfg(feature = "std")] unsafe { - set_oncrash_ptrs::(_state, _corpus, _event_mgr, _input); + set_oncrash_ptrs::(_state, _event_mgr, _input); } Ok(()) } @@ -72,8 +71,7 @@ where #[inline] fn post_exec( &mut self, - _state: &State, - _corpus: &C, + _state: &State, _event_mgr: &mut EM, _input: &I, ) -> Result<(), AflError> @@ -142,8 +140,7 @@ where name: &'static str, harness_fn: HarnessFunction, observers: OT, - _state: &State, - _corpus: &C, + _state: &mut State, _event_mgr: &mut EM, ) -> Self where @@ -211,7 +208,6 @@ pub mod unix_signals { /// Pointers to values only needed on crash. As the program will not continue after a crash, /// we should (tm) be okay with raw pointers here, - static mut CORPUS_PTR: *const c_void = ptr::null_mut(); static mut STATE_PTR: *const c_void = ptr::null_mut(); static mut EVENT_MGR_PTR: *mut c_void = ptr::null_mut(); /// The (unsafe) pointer to the current inmem input, for the current run. @@ -245,7 +241,6 @@ pub mod unix_signals { let _ = stdout().flush(); /*let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap(); - let corpus = (CORPUS_PTR as *const C).as_ref().unwrap(); let state = (EVENT_MGR_PTR as *const State).as_ref().unwrap(); let manager = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap(); @@ -281,7 +276,6 @@ pub mod unix_signals { } /*let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap(); - let corpus = (CORPUS_PTR as *const C).as_ref().unwrap(); let state = (EVENT_MGR_PTR as *const State).as_ref().unwrap(); let manager = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap(); @@ -315,8 +309,7 @@ pub mod unix_signals { #[inline] pub unsafe fn set_oncrash_ptrs( - state: &State, - corpus: &C, + state: &State, event_mgr: &mut EM, input: &I, ) where @@ -328,7 +321,6 @@ pub mod unix_signals { R: Rand, { CURRENT_INPUT_PTR = input as *const _ as *const c_void; - CORPUS_PTR = corpus as *const _ as *const c_void; STATE_PTR = state as *const _ as *const c_void; EVENT_MGR_PTR = event_mgr as *mut _ as *mut c_void; } @@ -336,7 +328,6 @@ pub mod unix_signals { #[inline] pub unsafe fn reset_oncrash_ptrs() { CURRENT_INPUT_PTR = ptr::null(); - CORPUS_PTR = ptr::null(); STATE_PTR = ptr::null(); EVENT_MGR_PTR = ptr::null_mut(); } diff --git a/afl/src/executors/mod.rs b/afl/src/executors/mod.rs index 4d315a0ee7..d93e3e956a 100644 --- a/afl/src/executors/mod.rs +++ b/afl/src/executors/mod.rs @@ -84,8 +84,7 @@ where /// Called right before exexution starts fn pre_exec( &mut self, - _state: &State, - _corpus: &C, + _state: &State, _event_mgr: &mut EM, _input: &I, ) -> Result<(), AflError> @@ -102,8 +101,7 @@ where /// Called right after execution finished. fn post_exec( &mut self, - _state: &State, - _corpus: &C, + _state: &State, _event_mgr: &mut EM, _input: &I, ) -> Result<(), AflError> diff --git a/afl/src/mutators/mod.rs b/afl/src/mutators/mod.rs index fe34056766..a41aaf48bc 100644 --- a/afl/src/mutators/mod.rs +++ b/afl/src/mutators/mod.rs @@ -3,24 +3,31 @@ pub use scheduled::*; pub mod mutations; pub use mutations::*; -use crate::{corpus::Corpus, inputs::Input, utils::Rand, AflError}; +use crate::{ + corpus::Corpus, + inputs::Input, + state::{HasCorpus, HasMetadata}, + utils::Rand, + AflError, +}; // TODO mutator stats method that produces something that can be sent with the NewTestcase event // We can use it to report which mutations generated the testcase in the broker logs /// 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( &mut self, rand: &mut R, - corpus: &C, + state: &mut S, input: &mut I, stage_idx: i32, ) -> Result<(), AflError>; @@ -28,6 +35,7 @@ where /// Post-process given the outcome of the execution fn post_exec( &mut self, + _state: &mut S, _is_interesting: u32, _input: &I, _stage_idx: i32, diff --git a/afl/src/mutators/mutations.rs b/afl/src/mutators/mutations.rs index c5d2e928d4..a0d4c7893b 100644 --- a/afl/src/mutators/mutations.rs +++ b/afl/src/mutators/mutations.rs @@ -6,11 +6,9 @@ use crate::{ AflError, }; +use std::fs::File; #[cfg(feature = "std")] -use std::{ - fs::File, - io::{BufRead, BufReader}, -}; +use std::io::{BufRead, BufReader}; const ARITH_MAX: u64 = 35; @@ -59,23 +57,24 @@ pub enum MutationResult { // TODO maybe the mutator arg is not needed /// The generic function type that identifies mutations -pub type MutationFunction = - fn(&mut M, &mut R, &C, &mut I) -> Result; +pub type MutationFunction = + fn(&mut M, &mut R, &mut S, &mut I) -> Result; -pub trait ComposedByMutations +pub trait ComposedByMutations where C: Corpus, I: Input, R: Rand, + S: HasCorpus + HasMetadata, { /// Get a mutation by index - fn mutation_by_idx(&self, index: usize) -> MutationFunction; + fn mutation_by_idx(&self, index: usize) -> MutationFunction; /// Get the number of mutations fn mutations_count(&self) -> usize; /// Add a mutation - fn add_mutation(&mut self, mutation: MutationFunction); + fn add_mutation(&mut self, mutation: MutationFunction); } #[inline] @@ -109,10 +108,10 @@ fn mem_set(data: &mut [u8], from: usize, len: usize, val: u8) { } /// Bitflip mutation for inputs with a bytes vector -pub fn mutation_bitflip( +pub fn mutation_bitflip( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -131,10 +130,10 @@ where } } -pub fn mutation_byteflip( +pub fn mutation_byteflip( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -153,10 +152,10 @@ where } } -pub fn mutation_byteinc( +pub fn mutation_byteinc( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -176,10 +175,10 @@ where } } -pub fn mutation_bytedec( +pub fn mutation_bytedec( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -199,10 +198,10 @@ where } } -pub fn mutation_byteneg( +pub fn mutation_byteneg( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -221,10 +220,10 @@ where } } -pub fn mutation_byterand( +pub fn mutation_byterand( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -243,10 +242,10 @@ where } } -pub fn mutation_byteadd( +pub fn mutation_byteadd( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -270,10 +269,10 @@ where } } -pub fn mutation_wordadd( +pub fn mutation_wordadd( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -299,10 +298,10 @@ where } } -pub fn mutation_dwordadd( +pub fn mutation_dwordadd( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -328,10 +327,10 @@ where } } -pub fn mutation_qwordadd( +pub fn mutation_qwordadd( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -357,10 +356,10 @@ where } } -pub fn mutation_byteinteresting( +pub fn mutation_byteinteresting( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -380,10 +379,10 @@ where } } -pub fn mutation_wordinteresting( +pub fn mutation_wordinteresting( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -408,10 +407,10 @@ where } } -pub fn mutation_dwordinteresting( +pub fn mutation_dwordinteresting( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -436,10 +435,10 @@ where } } -pub fn mutation_bytesdelete( +pub fn mutation_bytesdelete( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -458,11 +457,11 @@ where Ok(MutationResult::Mutated) } -pub fn mutation_bytesexpand( +pub fn mutation_bytesexpand( // TODO: max_size instead of mutator? mutator: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -485,10 +484,10 @@ where /* // Insert a dictionary token -pub fn mutation_tokeninsert( +pub fn mutation_tokeninsert( mutator: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -512,10 +511,10 @@ where } */ -pub fn mutation_bytesinsert( +pub fn mutation_bytesinsert( mutator: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -538,10 +537,10 @@ where Ok(MutationResult::Mutated) } -pub fn mutation_bytesrandinsert( +pub fn mutation_bytesrandinsert( mutator: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -564,10 +563,10 @@ where Ok(MutationResult::Mutated) } -pub fn mutation_bytesset( +pub fn mutation_bytesset( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -590,10 +589,10 @@ where Ok(MutationResult::Mutated) } -pub fn mutation_bytesrandset( +pub fn mutation_bytesrandset( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -616,10 +615,10 @@ where Ok(MutationResult::Mutated) } -pub fn mutation_bytescopy( +pub fn mutation_bytescopy( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -639,10 +638,10 @@ where Ok(MutationResult::Mutated) } -pub fn mutation_bytesswap( +pub fn mutation_bytesswap( _: &mut M, rand: &mut R, - _: &C, + _: &mut S, input: &mut I, ) -> Result where @@ -681,20 +680,21 @@ fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) { } /// Splicing mutator -pub fn mutation_splice( +pub fn mutation_splice( _: &mut M, rand: &mut R, - corpus: &C, + state: &mut S, input: &mut I, ) -> Result where C: Corpus, I: Input + HasBytesVec, R: Rand, + S: HasCorpus, { // We don't want to use the testcase we're already using for splicing - let (other_testcase, idx) = corpus.random_entry(rand)?; - if idx == corpus.current_testcase().1 { + let (other_testcase, idx) = state.corpus().random_entry(rand)?; + if idx == state.corpus().current_testcase().1 { return Ok(MutationResult::Skipped); } // println!("Input: {:?}, other input: {:?}", input.bytes(), other.bytes()); @@ -847,14 +847,25 @@ pub fn read_dict_file(f: &str, dict: &mut Vec>) -> Result #[cfg(test)] mod tests { + use std::fs; + use crate::mutators::read_dict_file; #[test] fn test_read_dict() { - println!("For this testcase to success create \"test.dic\"."); + let _ = fs::remove_file("test.dict"); + let data = r###" +# comment +token1="AAA" +token1="A\x41A" +token2="B" + "###; + fs::write("test.dict", data).expect("Unable to write test.dict"); let mut v: Vec> = Vec::new(); let res = read_dict_file(&"test.dic".to_string(), &mut v).unwrap(); #[cfg(feature = "std")] println!("Dictionary entries: {:?}", res); + assert_eq!(res, 2); + let _ = fs::remove_file("test.dict"); } } diff --git a/afl/src/mutators/scheduled.rs b/afl/src/mutators/scheduled.rs index e238e42aaa..38934ca671 100644 --- a/afl/src/mutators/scheduled.rs +++ b/afl/src/mutators/scheduled.rs @@ -5,15 +5,18 @@ use fmt::Debug; use crate::{ inputs::{HasBytesVec, Input}, mutators::{Corpus, *}, + state::{HasCorpus, HasMetadata}, utils::Rand, AflError, }; -pub trait ScheduledMutator: Mutator + ComposedByMutations +pub trait ScheduledMutator: + Mutator + ComposedByMutations where C: Corpus, I: Input, R: Rand, + S: HasCorpus + HasMetadata, { /// Compute the number of iterations used to apply stacked mutations #[inline] @@ -33,35 +36,37 @@ where fn scheduled_mutate( &mut self, rand: &mut R, - corpus: &C, + state: &mut S, input: &mut I, _stage_idx: i32, ) -> Result<(), AflError> { let num = self.iterations(rand, input); for _ in 0..num { let idx = self.schedule(self.mutations_count(), rand, input); - self.mutation_by_idx(idx)(self, rand, corpus, input)?; + self.mutation_by_idx(idx)(self, rand, state, input)?; } Ok(()) } } #[derive(Clone)] -pub struct StdScheduledMutator +pub struct StdScheduledMutator where C: Corpus, I: Input, R: Rand, + S: HasCorpus + HasMetadata, { - mutations: Vec>, + mutations: Vec>, max_size: usize, } -impl Debug for StdScheduledMutator +impl Debug for StdScheduledMutator where C: Corpus, I: Input, R: Rand, + S: HasCorpus + HasMetadata, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -74,31 +79,33 @@ where } } -impl Mutator for StdScheduledMutator +impl Mutator for StdScheduledMutator where C: Corpus, I: Input, R: Rand, + S: HasCorpus + HasMetadata, { fn mutate( &mut self, rand: &mut R, - corpus: &C, + state: &mut S, input: &mut I, _stage_idx: i32, ) -> Result<(), AflError> { - self.scheduled_mutate(rand, corpus, input, _stage_idx) + self.scheduled_mutate(rand, state, input, _stage_idx) } } -impl ComposedByMutations for StdScheduledMutator +impl ComposedByMutations for StdScheduledMutator where C: Corpus, I: Input, R: Rand, + S: HasCorpus + HasMetadata, { #[inline] - fn mutation_by_idx(&self, index: usize) -> MutationFunction { + fn mutation_by_idx(&self, index: usize) -> MutationFunction { self.mutations[index] } @@ -108,25 +115,27 @@ where } #[inline] - fn add_mutation(&mut self, mutation: MutationFunction) { + fn add_mutation(&mut self, mutation: MutationFunction) { self.mutations.push(mutation) } } -impl ScheduledMutator for StdScheduledMutator +impl ScheduledMutator for StdScheduledMutator where C: Corpus, I: Input, R: Rand, + S: HasCorpus + HasMetadata, { // Just use the default methods } -impl HasMaxSize for StdScheduledMutator +impl HasMaxSize for StdScheduledMutator where C: Corpus, I: Input, R: Rand, + S: HasCorpus + HasMetadata, { #[inline] fn max_size(&self) -> usize { @@ -139,11 +148,12 @@ where } } -impl StdScheduledMutator +impl StdScheduledMutator where C: Corpus, I: Input, R: Rand, + S: HasCorpus + HasMetadata, { /// Create a new StdScheduledMutator instance without mutations and corpus pub fn new() -> Self { @@ -154,7 +164,7 @@ where } /// Create a new StdScheduledMutator instance specifying mutations - pub fn with_mutations(mutations: Vec>) -> Self { + pub fn with_mutations(mutations: Vec>) -> Self { StdScheduledMutator { mutations: mutations, max_size: DEFAULT_MAX_SIZE, @@ -164,29 +174,31 @@ where #[derive(Clone, Debug)] /// Schedule some selected byte level mutations given a ScheduledMutator type -pub struct HavocBytesMutator +pub struct HavocBytesMutator where - SM: ScheduledMutator + HasMaxSize, + SM: ScheduledMutator + HasMaxSize, C: Corpus, I: Input + HasBytesVec, R: Rand, + S: HasCorpus + HasMetadata, { scheduled: SM, - phantom: PhantomData<(I, R, C)>, + phantom: PhantomData<(C, I, R, S)>, } -impl Mutator for HavocBytesMutator +impl Mutator for HavocBytesMutator where - SM: ScheduledMutator + HasMaxSize, + SM: ScheduledMutator + HasMaxSize, C: Corpus, I: Input + HasBytesVec, R: Rand, + S: HasCorpus + HasMetadata, { /// Mutate bytes fn mutate( &mut self, rand: &mut R, - corpus: &C, + state: &mut S, input: &mut I, _stage_idx: i32, ) -> Result<(), AflError> { @@ -210,18 +222,19 @@ where 11 => mutation_dwordinteresting, _ => mutation_splice, }; - mutation(self, rand, corpus, input)?; + mutation(self, rand, state, input)?; } Ok(()) } } -impl HasMaxSize for HavocBytesMutator +impl HasMaxSize for HavocBytesMutator where - SM: ScheduledMutator + HasMaxSize, + SM: ScheduledMutator + HasMaxSize, C: Corpus, I: Input + HasBytesVec, R: Rand, + S: HasCorpus + HasMetadata, { #[inline] fn max_size(&self) -> usize { @@ -234,12 +247,13 @@ where } } -impl HavocBytesMutator +impl HavocBytesMutator where - SM: ScheduledMutator + HasMaxSize, + SM: ScheduledMutator + HasMaxSize, C: Corpus, I: Input + HasBytesVec, R: Rand, + S: HasCorpus + HasMetadata, { /// Create a new HavocBytesMutator instance given a ScheduledMutator to wrap pub fn new(mut scheduled: SM) -> Self { @@ -252,15 +266,16 @@ where } } -impl HavocBytesMutator, C, I, R> +impl HavocBytesMutator, C, I, R, S> where C: Corpus, I: Input + HasBytesVec, R: Rand, + S: HasCorpus + HasMetadata, { /// Create a new HavocBytesMutator instance wrapping StdScheduledMutator pub fn new_default() -> Self { - let mut scheduled = StdScheduledMutator::::new(); + let mut scheduled = StdScheduledMutator::::new(); scheduled.add_mutation(mutation_bitflip); scheduled.add_mutation(mutation_byteflip); scheduled.add_mutation(mutation_byteinc); @@ -309,6 +324,7 @@ mod tests { inputs::BytesInput, inputs::HasBytesVec, mutators::scheduled::{mutation_splice, StdScheduledMutator}, + state::State, utils::{Rand, XKCDRand}, }; @@ -325,10 +341,17 @@ mod tests { .expect("Corpus did not contain entries"); let mut input = testcase.borrow_mut().load_input().unwrap().clone(); - rand.set_seed(5); - let mut mutator = StdScheduledMutator::, _, _>::new(); + let mut state = State::new(corpus, ()); - mutation_splice(&mut mutator, &mut rand, &mut corpus, &mut input).unwrap(); + rand.set_seed(5); + let mut mutator = StdScheduledMutator::< + InMemoryCorpus, + _, + _, + State<_, _, _, ()>, + >::new(); + + mutation_splice(&mut mutator, &mut rand, &mut state, &mut input).unwrap(); #[cfg(feature = "std")] println!("{:?}", input.bytes()); diff --git a/afl/src/stages/mod.rs b/afl/src/stages/mod.rs index d654eee3c5..2d99a4e167 100644 --- a/afl/src/stages/mod.rs +++ b/afl/src/stages/mod.rs @@ -16,7 +16,7 @@ use crate::{ /// 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, @@ -31,8 +31,7 @@ where &mut self, rand: &mut R, executor: &mut E, - state: &mut State, - corpus: &mut C, + state: &mut State, manager: &mut EM, corpus_idx: usize, ) -> Result<(), AflError>; @@ -52,13 +51,12 @@ where &mut self, rand: &mut R, executor: &mut E, - state: &mut State, - corpus: &mut C, + state: &mut State, manager: &mut EM, corpus_idx: usize, ) -> Result<(), AflError>; - fn for_each(&self, f: fn(&dyn Stage)); - fn for_each_mut(&mut self, f: fn(&mut dyn Stage)); + fn for_each(&self, f: fn(&dyn Stage)); + fn for_each_mut(&mut self, f: fn(&mut dyn Stage)); } impl StagesTuple for () @@ -75,20 +73,19 @@ where &mut self, _rand: &mut R, _executor: &mut E, - _state: &mut State, - _corpus: &mut C, + _state: &mut State, _manager: &mut EM, _corpus_idx: usize, ) -> Result<(), AflError> { Ok(()) } - fn for_each(&self, _f: fn(&dyn Stage)) {} - fn for_each_mut(&mut self, _f: fn(&mut dyn Stage)) {} + fn for_each(&self, _f: fn(&dyn Stage)) {} + fn for_each_mut(&mut self, _f: fn(&mut dyn Stage)) {} } impl StagesTuple for (Head, Tail) where - Head: Stage, + Head: Stage, Tail: StagesTuple + TupleList, EM: EventManager, E: Executor + HasObservers, @@ -102,23 +99,21 @@ where &mut self, rand: &mut R, executor: &mut E, - state: &mut State, - corpus: &mut C, + state: &mut State, manager: &mut EM, corpus_idx: usize, ) -> Result<(), AflError> { - self.0 - .perform(rand, executor, state, corpus, manager, corpus_idx)?; + self.0.perform(rand, executor, state, manager, corpus_idx)?; self.1 - .perform_all(rand, executor, state, corpus, manager, corpus_idx) + .perform_all(rand, executor, state, manager, corpus_idx) } - fn for_each(&self, f: fn(&dyn Stage)) { + 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)) { + fn for_each_mut(&mut self, f: fn(&mut dyn Stage)) { f(&mut self.0); self.1.for_each_mut(f) } diff --git a/afl/src/stages/mutational.rs b/afl/src/stages/mutational.rs index c8ffeaa078..f6ce787a95 100644 --- a/afl/src/stages/mutational.rs +++ b/afl/src/stages/mutational.rs @@ -9,7 +9,7 @@ use crate::{ observers::ObserversTuple, stages::Corpus, stages::Stage, - state::State, + state::{HasCorpus, State}, utils::Rand, AflError, }; @@ -19,9 +19,9 @@ 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, + M: Mutator>, EM: EventManager, E: Executor + HasObservers, OT: ObserversTuple, @@ -48,21 +48,25 @@ where &mut self, rand: &mut R, executor: &mut E, - state: &mut State, - corpus: &mut C, + state: &mut State, manager: &mut EM, corpus_idx: usize, ) -> Result<(), AflError> { let num = self.iterations(rand); for i in 0..num { - let mut input_mut = corpus.get(corpus_idx).borrow_mut().load_input()?.clone(); + let mut input_mut = state + .corpus() + .get(corpus_idx) + .borrow_mut() + .load_input()? + .clone(); self.mutator_mut() - .mutate(rand, corpus, &mut input_mut, i as i32)?; + .mutate(rand, state, &mut input_mut, i as i32)?; - let fitness = state.evaluate_input(&input_mut, executor, corpus, manager)?; + let fitness = state.evaluate_input(&input_mut, executor, manager)?; self.mutator_mut() - .post_exec(fitness, &input_mut, i as i32)?; + .post_exec(state, fitness, &input_mut, i as i32)?; let observers = executor.observers(); @@ -73,8 +77,13 @@ where // if needed by particular cases if fitness > 0 { // TODO decouple events manager and engine - manager.new_testcase(&input_mut, observers, corpus.count() + 1, "test".into())?; - state.add_if_interesting(corpus, input_mut, fitness)?; + manager.new_testcase( + &input_mut, + observers, + state.corpus().count() + 1, + "test".into(), + )?; + state.add_if_interesting(input_mut, fitness)?; // let _ = corpus.add(testcase); } else { state.discard_input(&input_mut)?; @@ -86,31 +95,31 @@ where #[derive(Clone, Debug)] /// The default mutational stage -pub struct StdMutationalStage +pub struct StdMutationalStage where - M: Mutator, - EM: EventManager, - E: Executor + HasObservers, - OT: ObserversTuple, - FT: FeedbacksTuple, C: Corpus, + E: Executor + HasObservers, + EM: EventManager, + FT: FeedbacksTuple, I: Input, + M: Mutator>, + OT: ObserversTuple, R: Rand, { mutator: M, phantom: PhantomData<(EM, E, OT, FT, C, I, R)>, } -impl MutationalStage - for StdMutationalStage +impl MutationalStage + for StdMutationalStage where - M: Mutator, - EM: EventManager, - E: Executor + HasObservers, - OT: ObserversTuple, - FT: FeedbacksTuple, C: Corpus, + E: Executor + HasObservers, + EM: EventManager, + FT: FeedbacksTuple, I: Input, + M: Mutator>, + OT: ObserversTuple, R: Rand, { /// The mutator, added to this stage @@ -126,10 +135,10 @@ where } } -impl Stage - for StdMutationalStage +impl Stage + for StdMutationalStage where - M: Mutator, + M: Mutator>, EM: EventManager, E: Executor + HasObservers, OT: ObserversTuple, @@ -143,18 +152,17 @@ where &mut self, rand: &mut R, executor: &mut E, - state: &mut State, - corpus: &mut C, + state: &mut State, manager: &mut EM, corpus_idx: usize, ) -> Result<(), AflError> { - self.perform_mutational(rand, executor, state, corpus, manager, corpus_idx) + self.perform_mutational(rand, executor, state, manager, corpus_idx) } } -impl StdMutationalStage +impl StdMutationalStage where - M: Mutator, + M: Mutator>, EM: EventManager, E: Executor + HasObservers, OT: ObserversTuple, diff --git a/afl/src/state/mod.rs b/afl/src/state/mod.rs index 0808608cda..9f6ce208e4 100644 --- a/afl/src/state/mod.rs +++ b/afl/src/state/mod.rs @@ -26,24 +26,53 @@ use crate::{ use crate::inputs::bytes::BytesInput; pub trait StateMetadata: Debug { - /// The name of this metadata - used to find it in the list of avaliable metadatas + /// The name of this metadata - used to find it in the list of avaliable metadata fn name(&self) -> &'static str; } +/// Trait for elements offering a corpus +pub trait HasCorpus { + /// The testcase corpus + fn corpus(&self) -> &C; + /// The testcase corpus (mut) + fn corpus_mut(&mut self) -> &mut C; +} + +/// Trait for elements offering metadata +pub trait HasMetadata { + /// A map, storing all metadata + fn metadata(&self) -> &SerdeAnyMap; + /// A map, storing all metadata (mut) + fn metadata_mut(&mut self) -> &mut SerdeAnyMap; + + /// Add a metadata to the metadata map + #[inline] + fn add_metadata(&mut self, meta: M) + where + M: SerdeAny, + { + self.metadata_mut().insert(meta); + } +} + /// The state a fuzz run. #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(bound = "FT: serde::de::DeserializeOwned")] -pub struct State +pub struct State where + C: Corpus, I: Input, + R: Rand, FT: FeedbacksTuple, { /// How many times the executor ran the harness/target executions: usize, + /// The corpus + corpus: C, /// At what time the fuzzing started start_time: u64, /// Metadata stored for this state by one of the components - metadatas: SerdeAnyMap, + metadata: SerdeAnyMap, // additional_corpuses, maybe another TupleList? // Feedbacks used to evaluate an input feedbacks: FT, @@ -51,15 +80,15 @@ where } #[cfg(feature = "std")] -impl State +impl State where + C: Corpus, R: Rand, FT: FeedbacksTuple, { - pub fn load_from_directory( + pub fn load_from_directory( &mut self, executor: &mut E, - corpus: &mut C, generator: &mut G, manager: &mut EM, in_dir: &Path, @@ -86,22 +115,21 @@ where println!("Loading file {:?} ...", &path); let bytes = fs::read(&path)?; let input = BytesInput::new(bytes); - let fitness = self.evaluate_input(&input, executor, corpus, manager)?; - if self.add_if_interesting(corpus, input, fitness)?.is_none() { + let fitness = self.evaluate_input(&input, executor, manager)?; + if self.add_if_interesting(input, fitness)?.is_none() { println!("File {:?} was not interesting, skipped.", &path); } } else if attr.is_dir() { - self.load_from_directory(executor, corpus, generator, manager, &path)?; + self.load_from_directory(executor, generator, manager, &path)?; } } Ok(()) } - pub fn load_initial_inputs( + pub fn load_initial_inputs( &mut self, executor: &mut E, - corpus: &mut C, generator: &mut G, manager: &mut EM, in_dirs: &[PathBuf], @@ -114,19 +142,59 @@ where EM: EventManager, { for in_dir in in_dirs { - self.load_from_directory(executor, corpus, generator, manager, in_dir)?; + self.load_from_directory(executor, generator, manager, in_dir)?; } manager.log( 0, - format!("Loaded {} initial testcases.", corpus.count()), // get corpus count + format!("Loaded {} initial testcases.", self.corpus().count()), // get corpus count )?; - manager.process(self, corpus)?; + manager.process(self)?; Ok(()) } } -impl State +impl HasCorpus for State where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, +{ + /// Returns the corpus + fn corpus(&self) -> &C { + &self.corpus + } + + /// Returns the mutable corpus + fn corpus_mut(&mut self) -> &mut C { + &mut self.corpus + } +} + +/// Trait for elements offering metadata +impl HasMetadata for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, +{ + /// Get all the metadata into an HashMap + #[inline] + fn metadata(&self) -> &SerdeAnyMap { + &self.metadata + } + + /// Get all the metadata into an HashMap (mutable) + #[inline] + fn metadata_mut(&mut self) -> &mut SerdeAnyMap { + &mut self.metadata + } +} + +impl State +where + C: Corpus, I: Input, R: Rand, FT: FeedbacksTuple, @@ -151,7 +219,6 @@ where pub fn set_start_time(&mut self, ms: u64) { self.start_time = ms } - // TODO as this is done in the event manager, we can remove it #[inline] pub fn executions_over_seconds(&self) -> u64 { @@ -167,27 +234,6 @@ where } } - /// Get all the metadatas into an HashMap - #[inline] - pub fn metadatas(&self) -> &SerdeAnyMap { - &self.metadatas - } - - /// Get all the metadatas into an HashMap (mutable) - #[inline] - pub fn metadatas_mut(&mut self) -> &mut SerdeAnyMap { - &mut self.metadatas - } - - /// Add a metadata - #[inline] - pub fn add_metadata(&mut self, meta: M) - where - M: SerdeAny, - { - self.metadatas.insert(meta); - } - /// Returns vector of feebacks #[inline] pub fn feedbacks(&self) -> &FT { @@ -210,11 +256,10 @@ where } /// Runs the input and triggers observers and feedback - pub fn evaluate_input( + pub fn evaluate_input( &mut self, input: &I, executor: &mut E, - corpus: &C, event_mgr: &mut EM, ) -> Result where @@ -225,9 +270,9 @@ where { executor.pre_exec_observers()?; - executor.pre_exec(&self, corpus, event_mgr, input)?; + executor.pre_exec(&self, event_mgr, input)?; executor.run_target(input)?; - executor.post_exec(&self, corpus, event_mgr, input)?; + executor.post_exec(&self, event_mgr, input)?; self.set_executions(self.executions() + 1); executor.post_exec_observers()?; @@ -270,29 +315,23 @@ where /// Adds this input to the corpus, if it's intersting #[inline] - pub fn add_if_interesting( - &mut self, - corpus: &mut C, - input: I, - fitness: u32, - ) -> Result, AflError> + pub fn add_if_interesting(&mut self, input: I, fitness: u32) -> Result, AflError> where C: Corpus, { if fitness > 0 { let testcase = self.input_to_testcase(input, fitness)?; - Ok(Some(corpus.add(testcase))) + Ok(Some(self.corpus_mut().add(testcase))) } else { self.discard_input(&input)?; Ok(None) } } - pub fn generate_initial_inputs( + pub fn generate_initial_inputs( &mut self, rand: &mut R, executor: &mut E, - corpus: &mut C, generator: &mut G, manager: &mut EM, num: usize, @@ -307,8 +346,8 @@ where let mut added = 0; for _ in 0..num { let input = generator.generate(rand)?; - let fitness = self.evaluate_input(&input, executor, corpus, manager)?; - if !self.add_if_interesting(corpus, input, fitness)?.is_none() { + let fitness = self.evaluate_input(&input, executor, manager)?; + if !self.add_if_interesting(input, fitness)?.is_none() { added += 1; } } @@ -316,15 +355,16 @@ where 0, format!("Loaded {} over {} initial testcases", added, num), )?; - manager.process(self, corpus)?; + manager.process(self)?; Ok(()) } - pub fn new(feedbacks: FT) -> Self { + pub fn new(corpus: C, feedbacks: FT) -> Self { Self { + corpus, executions: 0, start_time: current_milliseconds(), - metadatas: SerdeAnyMap::default(), + metadata: SerdeAnyMap::default(), feedbacks: feedbacks, phantom: PhantomData, } @@ -350,16 +390,15 @@ where &mut self, rand: &mut R, executor: &mut E, - state: &mut State, - corpus: &mut C, + state: &mut State, manager: &mut EM, ) -> Result { - let (_, idx) = corpus.next(rand)?; + let (_, idx) = state.corpus_mut().next(rand)?; self.stages_mut() - .perform_all(rand, executor, state, corpus, manager, idx)?; + .perform_all(rand, executor, state, manager, idx)?; - manager.process(state, corpus)?; + manager.process(state)?; Ok(idx) } @@ -367,13 +406,12 @@ where &mut self, rand: &mut R, executor: &mut E, - state: &mut State, - corpus: &mut C, + state: &mut State, manager: &mut EM, ) -> Result<(), AflError> { let mut last = current_milliseconds(); loop { - self.fuzz_one(rand, executor, state, corpus, manager)?; + self.fuzz_one(rand, executor, state, manager)?; let cur = current_milliseconds(); if cur - last > 60 * 100 { last = cur; @@ -458,6 +496,8 @@ mod tests { #[cfg(feature = "std")] use crate::events::{LoggerEventManager, SimpleStats}; + use super::HasCorpus; + fn harness, I: Input>(_executor: &E, _buf: &[u8]) -> ExitKind { ExitKind::Ok } @@ -470,7 +510,7 @@ mod tests { let testcase = Testcase::new(vec![0; 4]).into(); corpus.add(testcase); - let mut state = State::new(tuple_list!()); + let mut state = State::new(corpus, tuple_list!()); let mut event_manager = LoggerEventManager::new(SimpleStats::new(|s| { println!("{}", s); @@ -481,8 +521,7 @@ mod tests { harness, tuple_list!(), //Box::new(|_, _, _, _, _| ()), - &state, - &corpus, + &mut state, &mut event_manager, ); @@ -493,24 +532,18 @@ mod tests { for i in 0..1000 { fuzzer - .fuzz_one( - &mut rand, - &mut executor, - &mut state, - &mut corpus, - &mut event_manager, - ) + .fuzz_one(&mut rand, &mut executor, &mut state, &mut event_manager) .expect(&format!("Error in iter {}", i)); } let state_serialized = postcard::to_allocvec(&state).unwrap(); - let state_deserialized: State = + let state_deserialized: State, BytesInput, StdRand, ()> = postcard::from_bytes(state_serialized.as_slice()).unwrap(); assert_eq!(state.executions, state_deserialized.executions); - let corpus_serialized = postcard::to_allocvec(&corpus).unwrap(); + let corpus_serialized = postcard::to_allocvec(state.corpus()).unwrap(); let corpus_deserialized: InMemoryCorpus = postcard::from_bytes(corpus_serialized.as_slice()).unwrap(); - assert_eq!(corpus.count(), corpus_deserialized.count()); + assert_eq!(state.corpus().count(), corpus_deserialized.count()); } } diff --git a/afl/src/utils.rs b/afl/src/utils.rs index f30fa48711..c1839e946f 100644 --- a/afl/src/utils.rs +++ b/afl/src/utils.rs @@ -25,8 +25,7 @@ pub type StdRand = RomuTrioRand; /// On top, add the current llmp event manager instance to be restored /// This method is needed when the fuzzer run crashes and has to restart. pub fn serialize_state_corpus_mgr( - state: &State, - corpus: &C, + state: &State, mgr: &LlmpEventManager, ) -> Result, AflError> where @@ -37,7 +36,7 @@ where SH: ShMem, ST: Stats, { - let ret_slice = postcard::to_allocvec(&(&state, &corpus, &mgr.describe()?))?; + let ret_slice = postcard::to_allocvec(&(&state, &mgr.describe()?))?; //let corpus_bytes = serde_json::to_string(&corpus).unwrap(); //println!("fun"); @@ -58,7 +57,7 @@ where pub fn deserialize_state_corpus_mgr( state_corpus_serialized: &[u8], stats: ST, -) -> Result<(State, C, LlmpEventManager), AflError> +) -> Result<(State, LlmpEventManager), AflError> where C: Corpus, FT: FeedbacksTuple, @@ -67,11 +66,10 @@ where SH: ShMem, ST: Stats, { - let tuple: (Vec, Vec, Vec) = postcard::from_bytes(&state_corpus_serialized)?; - let client_description = postcard::from_bytes(&tuple.2)?; + let tuple: (Vec, Vec) = postcard::from_bytes(&state_corpus_serialized)?; + let client_description = postcard::from_bytes(&tuple.1)?; Ok(( postcard::from_bytes(&tuple.0)?, - postcard::from_bytes(&tuple.1)?, LlmpEventManager::existing_client_from_description(&client_description, stats)?, )) } @@ -79,7 +77,7 @@ where /// Serialize the current state and corpus during an executiont to bytes. /// This method is needed when the fuzzer run crashes and has to restart. pub fn serialize_state_corpus( - state: &State, + state: &State, corpus: &C, ) -> Result, AflError> where @@ -96,7 +94,7 @@ where /// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)` pub fn deserialize_state_corpus( state_corpus_serialized: &[u8], -) -> Result<(State, C), AflError> +) -> Result<(State, C), AflError> where C: Corpus, FT: FeedbacksTuple, diff --git a/fuzzers/libfuzzer_libpng/src/mod.rs b/fuzzers/libfuzzer_libpng/src/mod.rs index 5435b78792..84eaeae9e1 100644 --- a/fuzzers/libfuzzer_libpng/src/mod.rs +++ b/fuzzers/libfuzzer_libpng/src/mod.rs @@ -16,7 +16,7 @@ use afl::{ observers::StdMapObserver, shmem::{AflShmem, ShMem}, stages::mutational::StdMutationalStage, - state::{Fuzzer, State, StdFuzzer}, + state::{Fuzzer, HasCorpus, State, StdFuzzer}, tuples::tuple_list, utils::{deserialize_state_corpus_mgr, StdRand}, AflError, @@ -185,7 +185,7 @@ fn fuzz(input: Option>, broker_port: u16) -> Result<(), AflError> { } // If we're restarting, deserialize the old state. - let (mut state, mut corpus, mut mgr) = match receiver.recv_buf()? { + let (mut state, mut mgr) = match receiver.recv_buf()? { None => { println!("First run. Let's set it all up"); // Mgr to send and receive msgs from/to all other fuzzer instances @@ -196,9 +196,9 @@ fn fuzz(input: Option>, broker_port: u16) -> Result<(), AflError> { // Initial execution, read or generate initial state, corpus, and feedbacks let edges_feedback = MaxMapFeedback::new_with_observer(&NAME_COV_MAP, &edges_observer); - let state = State::new(tuple_list!(edges_feedback)); let corpus = InMemoryCorpus::new(); - (state, corpus, mgr) + let state = State::new(corpus, tuple_list!(edges_feedback)); + (state, mgr) } // Restoring from a previous run, deserialize state and corpus. Some((_sender, _tag, msg)) => { @@ -233,34 +233,26 @@ fn fuzz(input: Option>, broker_port: u16) -> Result<(), AflError> { "Libfuzzer", harness, tuple_list!(edges_observer), - &state, - &corpus, + &mut state, &mut mgr, ); // in case the corpus is empty (on first run), reset - if corpus.count() < 1 { + if state.corpus().count() < 1 { match input { Some(x) => state - .load_initial_inputs(&mut executor, &mut corpus, &mut generator, &mut mgr, &x) + .load_initial_inputs(&mut executor, &mut generator, &mut mgr, &x) .expect(&format!("Failed to load initial corpus at {:?}", &x)), None => (), } - println!("We imported {} inputs from disk.", corpus.count()); + println!("We imported {} inputs from disk.", state.corpus().count()); } - if corpus.count() < 1 { + if state.corpus().count() < 1 { println!("Generating random inputs"); state - .generate_initial_inputs( - &mut rand, - &mut executor, - &mut corpus, - &mut generator, - &mut mgr, - 4, - ) + .generate_initial_inputs(&mut rand, &mut executor, &mut generator, &mut mgr, 4) .expect("Failed to generate initial inputs"); - println!("We generated {} inputs.", corpus.count()); + println!("We generated {} inputs.", state.corpus().count()); } let mut mutator = HavocBytesMutator::new_default(); @@ -269,5 +261,5 @@ fn fuzz(input: Option>, broker_port: u16) -> Result<(), AflError> { let stage = StdMutationalStage::new(mutator); let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); - fuzzer.fuzz_loop(&mut rand, &mut executor, &mut state, &mut corpus, &mut mgr) + fuzzer.fuzz_loop(&mut rand, &mut executor, &mut state, &mut mgr) }