From b51936397b3ac428455a8b61d6a4f60f179bf50f Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 12 May 2021 23:53:27 +0200 Subject: [PATCH] Introduce FeedbackState and allow Feedbacks to process the entire State (#103) * save work * it builds * MutationalStage builds * compile lib.rs test * libafl tests work * adapt stb_image example * change fuzzer to not hold executor and event manager as type field * libfuzzer_stb_image running example * restore ReachabilityFeedback * restore introspection * adapt fuzzers except frida_libpng * format * compile on windows * clippy * fix libafl_frida * adapt frida_libpng --- fuzzers/baby_fuzzer/src/main.rs | 47 ++- fuzzers/frida_libpng/src/fuzzer.rs | 110 ++++-- fuzzers/libfuzzer_libmozjpeg/src/lib.rs | 57 ++- fuzzers/libfuzzer_libpng/src/lib.rs | 51 ++- fuzzers/libfuzzer_reachability/src/lib.rs | 44 ++- fuzzers/libfuzzer_stb_image/src/main.rs | 49 ++- libafl/src/corpus/queue.rs | 4 +- libafl/src/events/llmp.rs | 278 +++++++------ libafl/src/events/mod.rs | 99 +++-- libafl/src/events/simple.rs | 69 ++-- libafl/src/executors/inprocess.rs | 142 ++++--- libafl/src/executors/mod.rs | 112 ++++-- libafl/src/executors/timeout.rs | 28 +- libafl/src/feedbacks/map.rs | 216 ++++++---- libafl/src/feedbacks/mod.rs | 210 ++++++---- libafl/src/fuzzer.rs | 426 ++++++++++++++++---- libafl/src/lib.rs | 20 +- libafl/src/mutators/mutations.rs | 4 +- libafl/src/mutators/scheduled.rs | 6 +- libafl/src/observers/map.rs | 44 ++- libafl/src/observers/mod.rs | 18 +- libafl/src/stages/mod.rs | 57 +-- libafl/src/stages/mutational.rs | 84 ++-- libafl/src/state/mod.rs | 462 ++++------------------ libafl_frida/src/asan_rt.rs | 17 +- 25 files changed, 1508 insertions(+), 1146 deletions(-) diff --git a/fuzzers/baby_fuzzer/src/main.rs b/fuzzers/baby_fuzzer/src/main.rs index 250faa026f..132b3b3974 100644 --- a/fuzzers/baby_fuzzer/src/main.rs +++ b/fuzzers/baby_fuzzer/src/main.rs @@ -5,13 +5,13 @@ use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus, QueueCorpusScheduler}, events::SimpleEventManager, executors::{inprocess::InProcessExecutor, ExitKind}, - feedbacks::{CrashFeedback, MaxMapFeedback}, + feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, generators::RandPrintablesGenerator, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, observers::StdMapObserver, stages::mutational::StdMutationalStage, - state::State, + state::StdState, stats::SimpleStats, utils::{current_nanos, StdRand}, }; @@ -42,19 +42,27 @@ pub fn main() { // Create an observation channel using the signals map let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS }); + // The state of the edges feedback. + let feedback_state = MapFeedbackState::with_observer(&observer); + + // Feedback to rate the interestingness of an input + let feedback = MaxMapFeedback::new(&feedback_state, &observer); + + // A feedback to choose if an input is a solution or not + let objective = CrashFeedback::new(); + // create a State from scratch - let mut state = State::new( + let mut state = StdState::new( // RNG StdRand::with_seed(current_nanos()), // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), - // Feedback to rate the interestingness of an input - MaxMapFeedback::new_with_observer(&observer), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(), - // Feedbacks to recognize an input as solution - CrashFeedback::new(), + // States of the feedbacks. + // They are the data related to the feedbacks that you want to persist in the State. + tuple_list!(feedback_state), ); // The Stats trait define how the fuzzer stats are reported to the user @@ -67,27 +75,32 @@ pub fn main() { // A queue policy to get testcasess from the corpus let scheduler = QueueCorpusScheduler::new(); + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + // Create the executor for an in-process function with just one observer - let mut executor = - InProcessExecutor::new(&mut harness, tuple_list!(observer), &mut state, &mut mgr) - .expect("Failed to create the Executor".into()); + let mut executor = InProcessExecutor::new( + &mut harness, + tuple_list!(observer), + &mut fuzzer, + &mut state, + &mut mgr, + ) + .expect("Failed to create the Executor".into()); // Generator of printable bytearrays of max size 32 let mut generator = RandPrintablesGenerator::new(32); // Generate 8 initial inputs state - .generate_initial_inputs(&mut executor, &mut generator, &mut mgr, &scheduler, 8) + .generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8) .expect("Failed to generate the initial corpus".into()); - // Setup a basic mutator with a mutational stage + // Setup a mutational stage with a basic bytes mutator let mutator = StdScheduledMutator::new(havoc_mutations()); - let stage = StdMutationalStage::new(mutator); - - // A fuzzer with just one stage - let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); fuzzer - .fuzz_loop(&mut state, &mut executor, &mut mgr, &scheduler) + .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) .expect("Error in the fuzzing loop".into()); } diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index bddeaea626..99bf877210 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -13,14 +13,14 @@ use libafl::{ HasExecHooksTuple, HasObservers, HasObserversHooks, }, feedback_or, - feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, + feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{HasTargetBytes, Input}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, - observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver}, + observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, State}, + state::{HasCorpus, HasMetadata, StdState}, stats::SimpleStats, utils::{current_nanos, StdRand}, Error, @@ -39,14 +39,14 @@ use libafl_frida::{ FridaOptions, }; -struct FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> +struct FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { - base: TimeoutExecutor, I>, + base: TimeoutExecutor, I>, /// Frida's dynamic rewriting engine stalker: Stalker<'a>, /// User provided callback for instrumentation @@ -55,8 +55,8 @@ where _phantom: PhantomData<&'b u8>, } -impl<'a, 'b, 'c, EM, FH, H, I, OT, S> Executor - for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> +impl<'a, 'b, 'c, FH, H, I, OT, S> Executor + for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, @@ -91,8 +91,8 @@ where } } -impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasExecHooks - for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> +impl<'a, 'b, 'c, EM, FH, H, I, OT, S, Z> HasExecHooks + for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, @@ -101,21 +101,33 @@ where { /// Called right before exexution starts #[inline] - fn pre_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> { + fn pre_exec( + &mut self, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { self.helper.pre_exec(input); - self.base.pre_exec(state, event_mgr, input) + self.base.pre_exec(fuzzer, state, event_mgr, input) } /// Called right after execution finished. #[inline] - fn post_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> { + fn post_exec( + &mut self, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { self.helper.post_exec(input); - self.base.post_exec(state, event_mgr, input) + self.base.post_exec(fuzzer, state, event_mgr, input) } } -impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasObservers - for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> +impl<'a, 'b, 'c, FH, H, I, OT, S> HasObservers + for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, @@ -133,17 +145,17 @@ where } } -impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasObserversHooks - for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> +impl<'a, 'b, 'c, EM, FH, H, I, OT, S, Z> HasObserversHooks + for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, - OT: ObserversTuple + HasExecHooksTuple, + OT: ObserversTuple + HasExecHooksTuple, { } -impl<'a, 'b, 'c, EM, FH, H, I, OT, S> FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> +impl<'a, 'b, 'c, FH, H, I, OT, S> FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, @@ -152,7 +164,7 @@ where { pub fn new( gum: &'a Gum, - base: InProcessExecutor<'a, EM, H, I, OT, S>, + base: InProcessExecutor<'a, H, I, OT, S>, helper: &'c mut FH, timeout: Duration, ) -> Self { @@ -268,25 +280,45 @@ unsafe fn fuzz( MAP_SIZE, )); + // Create an observation channel to keep track of the execution time + let time_observer = TimeObserver::new("time"); + + // Create an observation channel for ASan violations + let asan_observer = AsanErrorsObserver::new(&ASAN_ERRORS); + + // The state of the edges feedback. + let feedback_state = MapFeedbackState::with_observer(&edges_observer); + + // Feedback to rate the interestingness of an input + // This one is composed by two Feedbacks in OR + let feedback = feedback_or!( + // New maximization map feedback linked to the edges observer and the feedback state + MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), + // Time feedback, this one does not need a feedback state + TimeFeedback::new_with_observer(&time_observer) + ); + + // Feedbacks to recognize an input as solution + let objective = feedback_or!( + CrashFeedback::new(), + TimeoutFeedback::new(), + AsanErrorsFeedback::new() + ); + // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { - State::new( + StdState::new( // RNG StdRand::with_seed(current_nanos()), // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), - // Feedbacks to rate the interestingness of an input - MaxMapFeedback::new_tracking_with_observer(&edges_observer, true, false), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new_save_meta(objective_dir, Some(OnDiskMetadataFormat::JsonPretty)) .unwrap(), - // Feedbacks to recognize an input as solution - feedback_or!( - CrashFeedback::new(), - TimeoutFeedback::new(), - AsanErrorsFeedback::new() - ), + // States of the feedbacks. + // They are the data related to the feedbacks that you want to persist in the State. + tuple_list!(feedback_state), ) }); @@ -305,11 +337,13 @@ unsafe fn fuzz( // Setup a basic mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations()); - let stage = StdMutationalStage::new(mutator); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); - // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus + // A minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); - let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); + + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); frida_helper.register_thread(); @@ -318,7 +352,8 @@ unsafe fn fuzz( &gum, InProcessExecutor::new( &mut frida_harness, - tuple_list!(edges_observer, AsanErrorsObserver::new(&ASAN_ERRORS)), + tuple_list!(edges_observer, time_observer, asan_observer), + &mut fuzzer, &mut state, &mut restarting_mgr, )?, @@ -338,7 +373,12 @@ unsafe fn fuzz( // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) + .load_initial_inputs( + &mut fuzzer, + &mut executor, + &mut restarting_mgr, + &corpus_dirs, + ) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -346,7 +386,7 @@ unsafe fn fuzz( println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; + fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?; // Never reached Ok(()) diff --git a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs index 4de98a3304..4801eb9d22 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs @@ -9,13 +9,13 @@ use libafl::{ events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, - feedbacks::{CrashFeedback, MaxMapFeedback}, + feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, observers::StdMapObserver, stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, State}, + state::{HasCorpus, HasMetadata, StdState}, stats::SimpleStats, utils::{current_nanos, StdRand}, Error, @@ -68,24 +68,42 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create an observation channel using the allocations map let allocs_observer = StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_map }); + // The state of the edges feedback. + let edges_feedback_state = MapFeedbackState::with_observer(&edges_observer); + + // The state of the cmps feedback. + let cmps_feedback_state = MapFeedbackState::with_observer(&cmps_observer); + + // The state of the allocs feedback. + let allocs_feedback_state = MapFeedbackState::with_observer(&allocs_observer); + + // Feedback to rate the interestingness of an input + let feedback = feedback_or!( + MaxMapFeedback::new(&edges_feedback_state, &edges_observer), + MaxMapFeedback::new(&cmps_feedback_state, &cmps_observer), + MaxMapFeedback::new(&allocs_feedback_state, &allocs_observer) + ); + + // A feedback to choose if an input is a solution or not + let objective = CrashFeedback::new(); + // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { - State::new( + StdState::new( // RNG StdRand::with_seed(current_nanos()), // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), - // Feedbacks to rate the interestingness of an input - feedback_or!( - MaxMapFeedback::new_with_observer(&edges_observer), - MaxMapFeedback::new_with_observer(&cmps_observer), - MaxMapFeedback::new_with_observer(&allocs_observer) - ), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(objective_dir).unwrap(), - // Feedbacks to recognize an input as solution - CrashFeedback::new(), + // States of the feedbacks. + // They are the data related to the feedbacks that you want to persist in the State. + tuple_list!( + edges_feedback_state, + cmps_feedback_state, + allocs_feedback_state + ), ) }); @@ -98,12 +116,13 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Setup a basic mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations()); - let stage = StdMutationalStage::new(mutator); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); // A random policy to get testcasess from the corpus let scheduler = RandCorpusScheduler::new(); - // A fuzzer with just one stage - let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); + + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { @@ -115,6 +134,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let mut executor = InProcessExecutor::new( &mut harness, tuple_list!(edges_observer, cmps_observer, allocs_observer), + &mut fuzzer, &mut state, &mut restarting_mgr, )?; @@ -129,7 +149,12 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) + .load_initial_inputs( + &mut fuzzer, + &mut executor, + &mut restarting_mgr, + &corpus_dirs, + ) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -137,7 +162,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; + fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?; // Never reached Ok(()) diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 35a1bc0bc7..41c4884e8e 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -10,16 +10,16 @@ use libafl::{ Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, }, - events::{setup_restarting_mgr_std, EventManager}, + events::{setup_restarting_mgr_std, EventRestarter}, executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, feedback_or, - feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, + feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, State}, + state::{HasCorpus, HasMetadata, StdState}, stats::SimpleStats, utils::{current_nanos, StdRand}, Error, @@ -71,23 +71,34 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); + // The state of the edges feedback. + let feedback_state = MapFeedbackState::with_observer(&edges_observer); + + // Feedback to rate the interestingness of an input + // This one is composed by two Feedbacks in OR + let feedback = feedback_or!( + // New maximization map feedback linked to the edges observer and the feedback state + MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), + // Time feedback, this one does not need a feedback state + TimeFeedback::new_with_observer(&time_observer) + ); + + // A feedback to choose if an input is a solution or not + let objective = feedback_or!(CrashFeedback::new(), TimeoutFeedback::new()); + // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { - State::new( + StdState::new( // RNG StdRand::with_seed(current_nanos()), // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), - // Feedbacks to rate the interestingness of an input - feedback_or!( - MaxMapFeedback::new_tracking_with_observer(&edges_observer, true, false), - TimeFeedback::new_with_observer(&time_observer) - ), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(objective_dir).unwrap(), - // Feedbacks to recognize an input as solution - feedback_or!(CrashFeedback::new(), TimeoutFeedback::new()), + // States of the feedbacks. + // They are the data related to the feedbacks that you want to persist in the State. + tuple_list!(feedback_state), ) }); @@ -106,14 +117,14 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Setup a basic mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations()); - let stage = StdMutationalStage::new(mutator); - - // A fuzzer with just one stage - let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); // A minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { libfuzzer_test_one_input(buf); @@ -125,6 +136,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> InProcessExecutor::new( &mut harness, tuple_list!(edges_observer, time_observer), + &mut fuzzer, &mut state, &mut restarting_mgr, )?, @@ -142,7 +154,12 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) + .load_initial_inputs( + &mut fuzzer, + &mut executor, + &mut restarting_mgr, + &corpus_dirs, + ) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -156,10 +173,10 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // However, you will lose a lot of performance that way. let iters = 1_000_000; fuzzer.fuzz_loop_for( + &mut stages, &mut state, &mut executor, &mut restarting_mgr, - &scheduler, iters, )?; diff --git a/fuzzers/libfuzzer_reachability/src/lib.rs b/fuzzers/libfuzzer_reachability/src/lib.rs index 4b8eb374c1..23c2fc51ab 100644 --- a/fuzzers/libfuzzer_reachability/src/lib.rs +++ b/fuzzers/libfuzzer_reachability/src/lib.rs @@ -6,15 +6,15 @@ use std::{env, path::PathBuf}; use libafl::{ bolts::tuples::tuple_list, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, - events::{setup_restarting_mgr_std, EventManager}, + events::{setup_restarting_mgr_std, EventRestarter}, executors::{inprocess::InProcessExecutor, ExitKind}, - feedbacks::{MaxMapFeedback, ReachabilityFeedback}, + feedbacks::{MapFeedbackState, MaxMapFeedback, ReachabilityFeedback}, fuzzer::{Fuzzer, StdFuzzer}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, observers::{HitcountsMapObserver, StdMapObserver}, stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, State}, + state::{HasCorpus, HasMetadata, StdState}, stats::SimpleStats, utils::{current_nanos, StdRand}, Error, @@ -71,20 +71,28 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let reachability_observer = unsafe { StdMapObserver::new_from_ptr("png.c", __libafl_target_list, TARGET_SIZE) }; + // The state of the edges feedback. + let feedback_state = MapFeedbackState::with_observer(&edges_observer); + + // Feedback to rate the interestingness of an input + let feedback = MaxMapFeedback::new(&feedback_state, &edges_observer); + + // A feedback to choose if an input is a solution or not + let objective = ReachabilityFeedback::new(&reachability_observer); + // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { - State::new( + StdState::new( // RNG StdRand::with_seed(current_nanos()), // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), - // Feedbacks to rate the interestingness of an input - MaxMapFeedback::new_tracking_with_observer(&edges_observer, true, false), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(objective_dir).unwrap(), - // Feedbacks to recognize an input as solution - ReachabilityFeedback::new_with_observer(&reachability_observer), + // States of the feedbacks. + // They are the data related to the feedbacks that you want to persist in the State. + tuple_list!(feedback_state), ) }); @@ -103,14 +111,14 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Setup a basic mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations()); - let stage = StdMutationalStage::new(mutator); - - // A fuzzer with just one stage - let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); // A random policy to get testcasess from the corpus let scheduler = RandCorpusScheduler::new(); + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { libfuzzer_test_one_input(buf); @@ -120,7 +128,8 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create the executor for an in-process function with one observer for edge coverage and one for the execution time let mut executor = InProcessExecutor::new( &mut harness, - tuple_list!(edges_observer, reachability_observer,), + tuple_list!(edges_observer, reachability_observer), + &mut fuzzer, &mut state, &mut restarting_mgr, )?; @@ -135,7 +144,12 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) + .load_initial_inputs( + &mut fuzzer, + &mut executor, + &mut restarting_mgr, + &corpus_dirs, + ) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -149,10 +163,10 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // However, you will lose a lot of performance that way. let iters = 1_000_000; fuzzer.fuzz_loop_for( + &mut stages, &mut state, &mut executor, &mut restarting_mgr, - &scheduler, iters, )?; diff --git a/fuzzers/libfuzzer_stb_image/src/main.rs b/fuzzers/libfuzzer_stb_image/src/main.rs index 0644f8785d..ba093c35e4 100644 --- a/fuzzers/libfuzzer_stb_image/src/main.rs +++ b/fuzzers/libfuzzer_stb_image/src/main.rs @@ -12,13 +12,13 @@ use libafl::{ events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, - feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, + feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, observers::{StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, State}, + state::{HasCorpus, HasMetadata, StdState}, stats::SimpleStats, utils::{current_nanos, StdRand}, Error, @@ -69,23 +69,34 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); + // The state of the edges feedback. + let feedback_state = MapFeedbackState::with_observer(&edges_observer); + + // Feedback to rate the interestingness of an input + // This one is composed by two Feedbacks in OR + let feedback = feedback_or!( + // New maximization map feedback linked to the edges observer and the feedback state + MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), + // Time feedback, this one does not need a feedback state + TimeFeedback::new_with_observer(&time_observer) + ); + + // A feedback to choose if an input is a solution or not + let objective = CrashFeedback::new(); + // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { - State::new( + StdState::new( // RNG StdRand::with_seed(current_nanos()), // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), - // Feedbacks to rate the interestingness of an input - feedback_or!( - MaxMapFeedback::new_tracking_with_observer(&edges_observer, true, false), - TimeFeedback::new_with_observer(&time_observer) - ), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(objective_dir).unwrap(), - // Feedback to recognize an input as solution - CrashFeedback::new(), + // States of the feedbacks. + // They are the data related to the feedbacks that you want to persist in the State. + tuple_list!(feedback_state), ) }); @@ -104,14 +115,14 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Setup a basic mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations()); - let stage = StdMutationalStage::new(mutator); - - // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus - let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); // A minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { libfuzzer_test_one_input(buf); @@ -122,6 +133,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let mut executor = InProcessExecutor::new( &mut harness, tuple_list!(edges_observer, time_observer), + &mut fuzzer, &mut state, &mut restarting_mgr, )?; @@ -136,7 +148,12 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) + .load_initial_inputs( + &mut fuzzer, + &mut executor, + &mut restarting_mgr, + &corpus_dirs, + ) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -144,7 +161,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; + fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?; // Never reached Ok(()) diff --git a/libafl/src/corpus/queue.rs b/libafl/src/corpus/queue.rs index 76c1d12899..6f997e1bf1 100644 --- a/libafl/src/corpus/queue.rs +++ b/libafl/src/corpus/queue.rs @@ -82,7 +82,7 @@ mod tests { use crate::{ corpus::{Corpus, CorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, Testcase}, inputs::bytes::BytesInput, - state::{HasCorpus, State}, + state::{HasCorpus, StdState}, utils::StdRand, }; @@ -103,7 +103,7 @@ mod tests { OnDiskCorpus::::new(PathBuf::from("target/.test/fancy/objective/path")) .unwrap(); - let mut state = State::new(rand, q, (), objective_q, ()); + let mut state = StdState::new(rand, q, objective_q, ()); let next_idx = scheduler.next(&mut state).unwrap(); let filename = state diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 8147140b76..ab1213de90 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -2,6 +2,7 @@ use alloc::{string::ToString, vec::Vec}; use core::{marker::PhantomData, time::Duration}; + use serde::{de::DeserializeOwned, Serialize}; #[cfg(feature = "std")] @@ -18,13 +19,12 @@ use crate::{ llmp::{self, Flags, LlmpClientDescription, LlmpSender, Tag}, shmem::ShMemProvider, }, - corpus::CorpusScheduler, - events::{BrokerEventResult, Event, EventManager}, + events::{BrokerEventResult, Event, EventFirer, EventManager, EventProcessor, EventRestarter}, + executors::Executor, executors::ExitKind, - executors::{Executor, HasObservers}, + fuzzer::{IfInteresting, IsInteresting}, inputs::Input, observers::ObserversTuple, - state::IfInteresting, stats::Stats, Error, }; @@ -57,10 +57,10 @@ const _LLMP_TAG_NO_RESTART: llmp::Tag = 0x57A7EE71; /// An [`EventManager`] that forwards all events to other attached fuzzers on shared maps or via tcp, /// using low-level message passing, [`crate::bolts::llmp`]. #[derive(Debug)] -pub struct LlmpEventManager +pub struct LlmpEventManager where I: Input, - S: IfInteresting, + OT: ObserversTuple, SP: ShMemProvider + 'static, ST: Stats, //CE: CustomEvent, @@ -70,18 +70,18 @@ where #[cfg(feature = "llmp_compression")] compressor: GzipCompressor, - phantom: PhantomData<(I, S)>, + phantom: PhantomData<(I, OT, S)>, } /// The minimum buffer size at which to compress LLMP IPC messages. #[cfg(feature = "llmp_compression")] const COMPRESS_THRESHOLD: usize = 1024; -impl Drop for LlmpEventManager +impl Drop for LlmpEventManager where I: Input, - S: IfInteresting, - SP: ShMemProvider, + OT: ObserversTuple, + SP: ShMemProvider + 'static, ST: Stats, { /// LLMP clients will have to wait until their pages are mapped by somebody. @@ -90,11 +90,11 @@ where } } -impl LlmpEventManager +impl LlmpEventManager where I: Input, - S: IfInteresting, - SP: ShMemProvider, + OT: ObserversTuple, + SP: ShMemProvider + 'static, ST: Stats, { /// Create llmp on a port @@ -293,18 +293,16 @@ where // Handle arriving events in the client #[allow(clippy::unused_self)] - fn handle_in_client( + fn handle_in_client( &mut self, + fuzzer: &mut Z, + _executor: &mut E, state: &mut S, _sender_id: u32, event: Event, - _executor: &mut E, - scheduler: &CS, ) -> Result<(), Error> where - CS: CorpusScheduler, - E: Executor + HasObservers, - OT: ObserversTuple, + Z: IfInteresting + IsInteresting, { match event { Event::NewTestcase { @@ -322,9 +320,10 @@ where let observers: OT = postcard::from_bytes(&observers_buf)?; // TODO include ExitKind in NewTestcase - let is_interesting = state.is_interesting(&input, &observers, &ExitKind::Ok)?; - if state - .add_if_interesting(&input, is_interesting, scheduler)? + let is_interesting = + fuzzer.is_interesting(state, &input, &observers, &ExitKind::Ok)?; + if fuzzer + .add_if_interesting(state, &input, is_interesting)? .is_some() { #[cfg(feature = "std")] @@ -340,12 +339,49 @@ where } } -impl EventManager for LlmpEventManager +impl EventFirer for LlmpEventManager where I: Input, - S: IfInteresting, + OT: ObserversTuple, SP: ShMemProvider, - ST: Stats, //CE: CustomEvent, + ST: Stats, + //CE: CustomEvent, +{ + #[cfg(feature = "llmp_compression")] + fn fire(&mut self, _state: &mut S, event: Event) -> Result<(), Error> { + let serialized = postcard::to_allocvec(&event)?; + let flags: Flags = LLMP_FLAG_INITIALIZED; + + match self.compressor.compress(&serialized)? { + Some(comp_buf) => { + self.llmp.send_buf_with_flags( + LLMP_TAG_EVENT_TO_BOTH, + &comp_buf, + flags | LLMP_FLAG_COMPRESSED, + )?; + } + None => { + self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?; + } + } + Ok(()) + } + + #[cfg(not(feature = "llmp_compression"))] + fn fire(&mut self, _state: &mut S, event: Event) -> Result<(), Error> { + let serialized = postcard::to_allocvec(&event)?; + self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?; + Ok(()) + } +} + +impl EventRestarter for LlmpEventManager +where + I: Input, + OT: ObserversTuple, + SP: ShMemProvider, + ST: Stats, + //CE: CustomEvent, { /// The llmp client needs to wait until a broker mapped all pages, before shutting down. /// Otherwise, the OS may already have removed the shared maps, @@ -355,18 +391,18 @@ where client.await_save_to_unmap_blocking(); } } +} - fn process( - &mut self, - state: &mut S, - executor: &mut E, - scheduler: &CS, - ) -> Result - where - CS: CorpusScheduler, - E: Executor + HasObservers, - OT: ObserversTuple, - { +impl EventProcessor for LlmpEventManager +where + SP: ShMemProvider, + ST: Stats, + E: Executor, + I: Input, + OT: ObserversTuple, + Z: IfInteresting + IsInteresting, //CE: CustomEvent, +{ + fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result { // TODO: Get around local event copy by moving handle_in_client let mut events = vec![]; match &mut self.llmp { @@ -397,49 +433,34 @@ where }; let count = events.len(); events.drain(..).try_for_each(|(sender_id, event)| { - self.handle_in_client(state, sender_id, event, executor, scheduler) + self.handle_in_client(fuzzer, executor, state, sender_id, event) })?; Ok(count) } +} - #[cfg(feature = "llmp_compression")] - fn fire(&mut self, _state: &mut S, event: Event) -> Result<(), Error> { - let serialized = postcard::to_allocvec(&event)?; - let flags: Flags = LLMP_FLAG_INITIALIZED; - - match self.compressor.compress(&serialized)? { - Some(comp_buf) => { - self.llmp.send_buf_with_flags( - LLMP_TAG_EVENT_TO_BOTH, - &comp_buf, - flags | LLMP_FLAG_COMPRESSED, - )?; - } - None => { - self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?; - } - } - Ok(()) - } - - #[cfg(not(feature = "llmp_compression"))] - fn fire(&mut self, _state: &mut S, event: Event) -> Result<(), Error> { - let serialized = postcard::to_allocvec(&event)?; - self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?; - Ok(()) - } +impl EventManager for LlmpEventManager +where + SP: ShMemProvider, + ST: Stats, + E: Executor, + I: Input, + OT: ObserversTuple, + Z: IfInteresting + IsInteresting, //CE: CustomEvent, +{ } /// Serialize the current state and corpus during an executiont to bytes. /// 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_mgr( +pub fn serialize_state_mgr( state: &S, - mgr: &LlmpEventManager, + mgr: &LlmpEventManager, ) -> Result, Error> where I: Input, - S: Serialize + IfInteresting, + OT: ObserversTuple, + S: Serialize, SP: ShMemProvider, ST: Stats, { @@ -448,13 +469,14 @@ where /// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)` #[allow(clippy::type_complexity)] -pub fn deserialize_state_mgr( +pub fn deserialize_state_mgr( shmem_provider: SP, state_corpus_serialized: &[u8], -) -> Result<(S, LlmpEventManager), Error> +) -> Result<(S, LlmpEventManager), Error> where I: Input, - S: DeserializeOwned + IfInteresting, + OT: ObserversTuple, + S: DeserializeOwned, SP: ShMemProvider, ST: Stats, { @@ -467,32 +489,49 @@ where /// A manager that can restart on the fly, storing states in-between (in `on_resatrt`) #[derive(Debug)] -pub struct LlmpRestartingEventManager +pub struct LlmpRestartingEventManager where I: Input, - S: IfInteresting, + OT: ObserversTuple, SP: ShMemProvider + 'static, ST: Stats, //CE: CustomEvent, { /// The embedded llmp event manager - llmp_mgr: LlmpEventManager, + llmp_mgr: LlmpEventManager, /// The sender to serialize the state for the next runner sender: LlmpSender, } -impl EventManager for LlmpRestartingEventManager +impl EventFirer for LlmpRestartingEventManager where I: Input, - S: IfInteresting + Serialize, + OT: ObserversTuple, + S: Serialize, SP: ShMemProvider, - ST: Stats, //CE: CustomEvent, + ST: Stats, + //CE: CustomEvent, +{ + fn fire(&mut self, state: &mut S, event: Event) -> Result<(), Error> { + // Check if we are going to crash in the event, in which case we store our current state for the next runner + self.llmp_mgr.fire(state, event) + } +} + +impl EventRestarter for LlmpRestartingEventManager +where + I: Input, + OT: ObserversTuple, + S: Serialize, + SP: ShMemProvider, + ST: Stats, + //CE: CustomEvent, { /// The llmp client needs to wait until a broker mapped all pages, before shutting down. /// Otherwise, the OS may already have removed the shared maps, #[inline] fn await_restart_safe(&mut self) { - self.llmp_mgr.await_restart_safe(); + self.llmp_mgr.await_restart_safe() } /// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner. @@ -503,25 +542,36 @@ where self.sender .send_buf(_LLMP_TAG_RESTART, &state_corpus_serialized) } +} - fn process( - &mut self, - state: &mut S, - executor: &mut E, - scheduler: &CS, - ) -> Result - where - CS: CorpusScheduler, - E: Executor + HasObservers, - OT: ObserversTuple, - { - self.llmp_mgr.process(state, executor, scheduler) +impl EventProcessor + for LlmpRestartingEventManager +where + E: Executor, + I: Input, + Z: IfInteresting + IsInteresting, + OT: ObserversTuple, + SP: ShMemProvider + 'static, + ST: Stats, + //CE: CustomEvent, +{ + fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result { + self.llmp_mgr.process(fuzzer, state, executor) } +} - fn fire(&mut self, state: &mut S, event: Event) -> Result<(), Error> { - // Check if we are going to crash in the event, in which case we store our current state for the next runner - self.llmp_mgr.fire(state, event) - } +impl EventManager + for LlmpRestartingEventManager +where + E: Executor, + I: Input, + S: Serialize, + Z: IfInteresting + IsInteresting, + OT: ObserversTuple, + SP: ShMemProvider + 'static, + ST: Stats, + //CE: CustomEvent, +{ } /// The llmp connection from the actual fuzzer to the process supervising it @@ -530,15 +580,16 @@ const _ENV_FUZZER_RECEIVER: &str = &"_AFL_ENV_FUZZER_RECEIVER"; /// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages) const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT"; -impl LlmpRestartingEventManager +impl LlmpRestartingEventManager where I: Input, - S: IfInteresting, - SP: ShMemProvider, - ST: Stats, //CE: CustomEvent, + OT: ObserversTuple, + SP: ShMemProvider + 'static, + ST: Stats, + //CE: CustomEvent, { /// Create a new runner, the executed child doing the actual fuzzing. - pub fn new(llmp_mgr: LlmpEventManager, sender: LlmpSender) -> Self { + pub fn new(llmp_mgr: LlmpEventManager, sender: LlmpSender) -> Self { Self { llmp_mgr, sender } } @@ -558,26 +609,26 @@ where /// The restarter will spawn a new process each time the child crashes or timeouts. #[cfg(feature = "std")] #[allow(clippy::type_complexity)] -pub fn setup_restarting_mgr_std( - //mgr: &mut LlmpEventManager, +pub fn setup_restarting_mgr_std( stats: ST, broker_port: u16, ) -> Result< ( Option, - LlmpRestartingEventManager, + LlmpRestartingEventManager, ), Error, > where I: Input, - S: DeserializeOwned + IfInteresting, + OT: ObserversTuple, + S: DeserializeOwned, ST: Stats, { #[cfg(target_os = "android")] AshmemService::start().expect("Error starting Ashmem Service"); - setup_restarting_mgr(StdShMemProvider::new()?, stats, broker_port) + setup_restarting_mgr::(StdShMemProvider::new()?, stats, broker_port) } /// A restarting state is a combination of restarter and runner, that can be used on systems with and without `fork` support. @@ -588,20 +639,25 @@ where clippy::type_complexity, clippy::similar_names )] // for { mgr = LlmpEventManager... } -pub fn setup_restarting_mgr( +pub fn setup_restarting_mgr( mut shmem_provider: SP, - //mgr: &mut LlmpEventManager, + //mgr: &mut LlmpEventManager, stats: ST, broker_port: u16, -) -> Result<(Option, LlmpRestartingEventManager), Error> +) -> Result<(Option, LlmpRestartingEventManager), Error> where I: Input, - S: DeserializeOwned + IfInteresting, - SP: ShMemProvider, + S: DeserializeOwned, + OT: ObserversTuple, + SP: ShMemProvider + 'static, ST: Stats, + //CE: CustomEvent, { - let mut mgr = - LlmpEventManager::::new_on_port(shmem_provider.clone(), stats, broker_port)?; + let mut mgr = LlmpEventManager::::new_on_port( + shmem_provider.clone(), + stats, + broker_port, + )?; // We start ourself as child process to actually fuzz let (sender, mut receiver, mut new_shmem_provider) = if std::env::var(_ENV_FUZZER_SENDER) @@ -684,7 +740,7 @@ where None => { println!("First run. Let's set it all up"); // Mgr to send and receive msgs from/to all other fuzzer instances - let client_mgr = LlmpEventManager::::existing_client_from_env( + let client_mgr = LlmpEventManager::::existing_client_from_env( new_shmem_provider, _ENV_FUZZER_BROKER_CLIENT_INITIAL, )?; @@ -694,7 +750,7 @@ where // Restoring from a previous run, deserialize state and corpus. Some((_sender, _tag, msg)) => { println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len()); - let (state, mgr): (S, LlmpEventManager) = + let (state, mgr): (S, LlmpEventManager) = deserialize_state_mgr(new_shmem_provider, &msg)?; (Some(state), LlmpRestartingEventManager::new(mgr, sender)) diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index dfabe1162d..97c94eddfc 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -9,13 +9,7 @@ use alloc::{string::String, vec::Vec}; use core::{fmt, marker::PhantomData, time::Duration}; use serde::{Deserialize, Serialize}; -use crate::{ - corpus::CorpusScheduler, - executors::{Executor, HasObservers}, - inputs::Input, - observers::ObserversTuple, - Error, -}; +use crate::{inputs::Input, observers::ObserversTuple, Error}; #[cfg(feature = "introspection")] use crate::stats::ClientPerfStats; @@ -175,27 +169,32 @@ where } } -/// [`EventManager`] is the main communications hub. -/// For the "normal" multi-processed mode, you may want to look into `RestartingEventManager` -pub trait EventManager +/// [`EventFirer`] fire an event. +pub trait EventFirer where I: Input, { - /// Fire an Event - //fn fire<'a>(&mut self, event: Event) -> Result<(), Error>; + /// Send off an event to the broker + fn fire(&mut self, state: &mut S, event: Event) -> Result<(), Error>; +} +pub trait EventRestarter { + /// For restarting event managers, implement a way to forward state to their next peers. + #[inline] + fn on_restart(&mut self, _state: &mut S) -> Result<(), Error> { + Ok(()) + } + + /// Block until we are safe to exit. + #[inline] + fn await_restart_safe(&mut self) {} +} + +/// [`EventProcessor`] process all the incoming messages +pub trait EventProcessor { /// Lookup for incoming events and process them. /// Return the number of processes events or an error - fn process( - &mut self, - state: &mut S, - executor: &mut E, - scheduler: &CS, - ) -> Result - where - CS: CorpusScheduler, - E: Executor + HasObservers, - OT: ObserversTuple; + fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result; /// Serialize all observers for this type and manager fn serialize_observers(&mut self, observers: &OT) -> Result, Error> @@ -212,49 +211,45 @@ where { Ok(postcard::from_bytes(observers_buf)?) } +} - /// For restarting event managers, implement a way to forward state to their next peers. - #[inline] - fn on_restart(&mut self, _state: &mut S) -> Result<(), Error> { - Ok(()) - } - - /// Block until we are safe to exit. - #[inline] - fn await_restart_safe(&mut self) {} - - /// Send off an event to the broker - fn fire(&mut self, state: &mut S, event: Event) -> Result<(), Error>; +/// [`EventManager`] is the main communications hub. +/// For the "normal" multi-processed mode, you may want to look into `RestartingEventManager` +pub trait EventManager: + EventFirer + EventProcessor + EventRestarter +where + I: Input, +{ } /// An eventmgr for tests, and as placeholder if you really don't need an event manager. #[derive(Copy, Clone, Debug)] -pub struct NopEventManager { - phantom: PhantomData<(I, S)>, -} -impl EventManager for NopEventManager +pub struct NopEventManager {} + +impl EventFirer for NopEventManager where I: Input, { - fn process( - &mut self, - _state: &mut S, - _executor: &mut E, - _scheduler: &CS, - ) -> Result - where - CS: CorpusScheduler, - E: Executor + HasObservers, - OT: ObserversTuple, - { - Ok(0) - } - fn fire(&mut self, _state: &mut S, _event: Event) -> Result<(), Error> { Ok(()) } } +impl EventRestarter for NopEventManager {} + +impl EventProcessor for NopEventManager { + fn process( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _executor: &mut E, + ) -> Result { + Ok(0) + } +} + +impl EventManager for NopEventManager where I: Input {} + #[cfg(test)] mod tests { diff --git a/libafl/src/events/simple.rs b/libafl/src/events/simple.rs index 6e702dde73..101117d995 100644 --- a/libafl/src/events/simple.rs +++ b/libafl/src/events/simple.rs @@ -1,20 +1,16 @@ //! A very simple event manager, that just supports log outputs, but no multiprocessing use alloc::{string::ToString, vec::Vec}; -use core::marker::PhantomData; use crate::{ - corpus::CorpusScheduler, - events::{BrokerEventResult, Event, EventManager}, - executors::{Executor, HasObservers}, + events::{BrokerEventResult, Event, EventFirer, EventManager, EventProcessor, EventRestarter}, inputs::Input, - observers::ObserversTuple, stats::Stats, Error, }; /// A simple, single-threaded event manager that just logs #[derive(Clone, Debug)] -pub struct SimpleEventManager +pub struct SimpleEventManager where I: Input, ST: Stats, //CE: CustomEvent, @@ -23,33 +19,13 @@ where stats: ST, /// The events that happened since the last handle_in_broker events: Vec>, - phantom: PhantomData, } -impl EventManager for SimpleEventManager +impl EventFirer for SimpleEventManager where I: Input, ST: Stats, //CE: CustomEvent, { - fn process( - &mut self, - state: &mut S, - _executor: &mut E, - _scheduler: &CS, - ) -> Result - where - CS: CorpusScheduler, - E: Executor + HasObservers, - OT: ObserversTuple, - { - let count = self.events.len(); - while !self.events.is_empty() { - let event = self.events.pop().unwrap(); - self.handle_in_client(state, event)?; - } - Ok(count) - } - fn fire(&mut self, _state: &mut S, event: Event) -> Result<(), Error> { match Self::handle_in_broker(&mut self.stats, &event)? { BrokerEventResult::Forward => self.events.push(event), @@ -59,7 +35,41 @@ where } } -impl SimpleEventManager +impl EventRestarter for SimpleEventManager +where + I: Input, + ST: Stats, //CE: CustomEvent, +{ +} + +impl EventProcessor for SimpleEventManager +where + I: Input, + ST: Stats, //CE: CustomEvent, +{ + fn process( + &mut self, + _fuzzer: &mut Z, + state: &mut S, + _executor: &mut E, + ) -> Result { + let count = self.events.len(); + while !self.events.is_empty() { + let event = self.events.pop().unwrap(); + self.handle_in_client(state, event)?; + } + Ok(count) + } +} + +impl EventManager for SimpleEventManager +where + I: Input, + ST: Stats, //CE: CustomEvent, +{ +} + +impl SimpleEventManager where I: Input, ST: Stats, //TODO CE: CustomEvent, @@ -69,7 +79,6 @@ where Self { stats, events: vec![], - phantom: PhantomData, } } @@ -141,7 +150,7 @@ where // Handle arriving events in the client #[allow(clippy::needless_pass_by_value, clippy::unused_self)] - fn handle_in_client(&mut self, _state: &mut S, event: Event) -> Result<(), Error> { + fn handle_in_client(&mut self, _state: &mut S, event: Event) -> Result<(), Error> { Err(Error::Unknown(format!( "Received illegal message that message should not have arrived: {:?}.", event diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 39a913f588..23a5de83d0 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -15,19 +15,20 @@ use crate::bolts::os::windows_exceptions::setup_exception_handler; use crate::{ corpus::Corpus, - events::EventManager, + events::{EventFirer, EventRestarter}, executors::{ Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, }, feedbacks::Feedback, + fuzzer::HasObjective, inputs::{HasTargetBytes, Input}, observers::ObserversTuple, - state::{HasObjective, HasSolutions}, + state::HasSolutions, Error, }; /// The inmem executor simply calls a target function, then returns afterwards. -pub struct InProcessExecutor<'a, EM, H, I, OT, S> +pub struct InProcessExecutor<'a, H, I, OT, S> where H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, @@ -37,10 +38,10 @@ where harness_fn: &'a mut H, /// The observers, observing each run observers: OT, - phantom: PhantomData<(EM, I, S)>, + phantom: PhantomData<(I, S)>, } -impl<'a, EM, H, I, OT, S> Executor for InProcessExecutor<'a, EM, H, I, OT, S> +impl<'a, H, I, OT, S> Executor for InProcessExecutor<'a, H, I, OT, S> where H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, @@ -54,14 +55,20 @@ where } } -impl<'a, EM, H, I, OT, S> HasExecHooks for InProcessExecutor<'a, EM, H, I, OT, S> +impl<'a, EM, H, I, OT, S, Z> HasExecHooks for InProcessExecutor<'a, H, I, OT, S> where H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { #[inline] - fn pre_exec(&mut self, _state: &mut S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> { + fn pre_exec( + &mut self, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { #[cfg(unix)] unsafe { let data = &mut unix_signal_handler::GLOBAL_STATE; @@ -75,8 +82,9 @@ where ); // Direct raw pointers access /aliasing is pretty undefined behavior. // Since the state and event may have moved in memory, refresh them right before the signal may happen - write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void); - write_volatile(&mut data.event_mgr_ptr, _event_mgr as *mut _ as *mut c_void); + write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void); + write_volatile(&mut data.event_mgr_ptr, event_mgr as *mut _ as *mut c_void); + write_volatile(&mut data.fuzzer_ptr, fuzzer as *mut _ as *mut c_void); compiler_fence(Ordering::SeqCst); } #[cfg(windows)] @@ -92,15 +100,22 @@ where ); // Direct raw pointers access /aliasing is pretty undefined behavior. // Since the state and event may have moved in memory, refresh them right before the signal may happen - write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void); - write_volatile(&mut data.event_mgr_ptr, _event_mgr as *mut _ as *mut c_void); + write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void); + write_volatile(&mut data.event_mgr_ptr, event_mgr as *mut _ as *mut c_void); + write_volatile(&mut data.fuzzer_ptr, fuzzer as *mut _ as *mut c_void); compiler_fence(Ordering::SeqCst); } Ok(()) } #[inline] - fn post_exec(&mut self, _state: &mut S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> { + fn post_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _event_mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { #[cfg(unix)] unsafe { write_volatile( @@ -121,7 +136,7 @@ where } } -impl<'a, EM, H, I, OT, S> HasObservers for InProcessExecutor<'a, EM, H, I, OT, S> +impl<'a, H, I, OT, S> HasObservers for InProcessExecutor<'a, H, I, OT, S> where H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, @@ -138,15 +153,16 @@ where } } -impl<'a, EM, H, I, OT, S> HasObserversHooks for InProcessExecutor<'a, EM, H, I, OT, S> +impl<'a, EM, H, I, OT, S, Z> HasObserversHooks + for InProcessExecutor<'a, H, I, OT, S> where H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, - OT: ObserversTuple + HasExecHooksTuple, + OT: ObserversTuple + HasExecHooksTuple, { } -impl<'a, EM, H, I, OT, S> InProcessExecutor<'a, EM, H, I, OT, S> +impl<'a, H, I, OT, S> InProcessExecutor<'a, H, I, OT, S> where H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, @@ -158,28 +174,30 @@ where /// * `harness_fn` - the harness, executiong the function /// * `observers` - the observers observing the target during execution /// This may return an error on unix, if signal handler setup fails - pub fn new( + pub fn new( harness_fn: &'a mut H, observers: OT, + _fuzzer: &mut Z, _state: &mut S, _event_mgr: &mut EM, ) -> Result where - EM: EventManager, + EM: EventFirer + EventRestarter, OC: Corpus, - OF: Feedback, - S: HasObjective + HasSolutions, + OF: Feedback, + S: HasSolutions, + Z: HasObjective, { #[cfg(unix)] unsafe { let data = &mut unix_signal_handler::GLOBAL_STATE; write_volatile( &mut data.crash_handler, - unix_signal_handler::inproc_crash_handler::, + unix_signal_handler::inproc_crash_handler::, ); write_volatile( &mut data.timeout_handler, - unix_signal_handler::inproc_timeout_handler::, + unix_signal_handler::inproc_timeout_handler::, ); setup_signal_handler(data)?; @@ -190,11 +208,11 @@ where let data = &mut windows_exception_handler::GLOBAL_STATE; write_volatile( &mut data.crash_handler, - windows_exception_handler::inproc_crash_handler::, + windows_exception_handler::inproc_crash_handler::, ); //write_volatile( // &mut data.timeout_handler, - // windows_exception_handler::inproc_timeout_handler::, + // windows_exception_handler::inproc_timeout_handler::, //); setup_exception_handler(data)?; @@ -232,12 +250,13 @@ mod unix_signal_handler { use crate::{ bolts::os::unix_signals::{Handler, Signal}, corpus::{Corpus, Testcase}, - events::{Event, EventManager}, + events::{Event, EventFirer, EventRestarter}, executors::ExitKind, feedbacks::Feedback, + fuzzer::HasObjective, inputs::{HasTargetBytes, Input}, observers::ObserversTuple, - state::{HasObjective, HasSolutions}, + state::HasSolutions, }; // TODO merge GLOBAL_STATE with the Windows one @@ -248,6 +267,8 @@ mod unix_signal_handler { state_ptr: ptr::null_mut(), /// The event manager ptr for signal handling event_mgr_ptr: ptr::null_mut(), + /// The fuzzer ptr for signal handling + fuzzer_ptr: ptr::null_mut(), /// The observers ptr for signal handling observers_ptr: ptr::null(), /// The current input for signal handling @@ -261,6 +282,7 @@ mod unix_signal_handler { pub struct InProcessExecutorHandlerData { pub state_ptr: *mut c_void, pub event_mgr_ptr: *mut c_void, + pub fuzzer_ptr: *mut c_void, pub observers_ptr: *const c_void, pub current_input_ptr: *const c_void, pub crash_handler: unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut Self), @@ -308,21 +330,23 @@ mod unix_signal_handler { } #[cfg(unix)] - pub unsafe fn inproc_timeout_handler( + pub unsafe fn inproc_timeout_handler( _signal: Signal, _info: siginfo_t, _context: &mut ucontext_t, data: &mut InProcessExecutorHandlerData, ) where - EM: EventManager, + EM: EventFirer + EventRestarter, OT: ObserversTuple, OC: Corpus, - OF: Feedback, - S: HasObjective + HasSolutions, + OF: Feedback, + S: HasSolutions, I: Input + HasTargetBytes, + Z: HasObjective, { let state = (data.state_ptr as *mut S).as_mut().unwrap(); let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); + let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); let observers = (data.observers_ptr as *const OT).as_ref().unwrap(); if data.current_input_ptr.is_null() { @@ -337,15 +361,16 @@ mod unix_signal_handler { let input = (data.current_input_ptr as *const I).as_ref().unwrap(); data.current_input_ptr = ptr::null(); - let interesting = state + let interesting = fuzzer .objective_mut() - .is_interesting(&input, observers, &ExitKind::Timeout) + .is_interesting(state, &input, observers, &ExitKind::Timeout) .expect("In timeout handler objective failure."); + if interesting { let mut new_testcase = Testcase::new(input.clone()); - state + fuzzer .objective_mut() - .append_metadata(&mut new_testcase) + .append_metadata(state, &mut new_testcase) .expect("Failed adding metadata"); state .solutions_mut() @@ -379,18 +404,19 @@ mod unix_signal_handler { /// Will be used for signal handling. /// It will store the current State to shmem, then exit. #[allow(clippy::too_many_lines)] - pub unsafe fn inproc_crash_handler( + pub unsafe fn inproc_crash_handler( _signal: Signal, _info: siginfo_t, _context: &mut ucontext_t, data: &mut InProcessExecutorHandlerData, ) where - EM: EventManager, + EM: EventFirer + EventRestarter, OT: ObserversTuple, OC: Corpus, - OF: Feedback, - S: HasObjective + HasSolutions, + OF: Feedback, + S: HasSolutions, I: Input + HasTargetBytes, + Z: HasObjective, { #[cfg(all(target_os = "android", target_arch = "aarch64"))] let _context = *(((_context as *mut _ as *mut c_void as usize) + 128) as *mut c_void @@ -433,6 +459,7 @@ mod unix_signal_handler { } else { let state = (data.state_ptr as *mut S).as_mut().unwrap(); let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); + let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); let observers = (data.observers_ptr as *const OT).as_ref().unwrap(); #[cfg(feature = "std")] @@ -483,16 +510,17 @@ mod unix_signal_handler { // Make sure we don't crash in the crash handler forever. data.current_input_ptr = ptr::null(); - let interesting = state + let interesting = fuzzer .objective_mut() - .is_interesting(&input, observers, &ExitKind::Crash) + .is_interesting(state, &input, observers, &ExitKind::Crash) .expect("In crash handler objective failure."); + if interesting { let new_input = input.clone(); let mut new_testcase = Testcase::new(new_input); - state + fuzzer .objective_mut() - .append_metadata(&mut new_testcase) + .append_metadata(state, &mut new_testcase) .expect("Failed adding metadata"); state .solutions_mut() @@ -536,12 +564,13 @@ mod windows_exception_handler { }, }, corpus::{Corpus, Testcase}, - events::{Event, EventManager}, + events::{Event, EventFirer, EventRestarter}, executors::ExitKind, feedbacks::Feedback, + fuzzer::HasObjective, inputs::{HasTargetBytes, Input}, observers::ObserversTuple, - state::{HasObjective, HasSolutions}, + state::HasSolutions, }; /// Signal handling on unix systems needs some nasty unsafe. @@ -550,6 +579,8 @@ mod windows_exception_handler { state_ptr: ptr::null_mut(), /// The event manager ptr for signal handling event_mgr_ptr: ptr::null_mut(), + /// The fuzzer ptr for signal handling + fuzzer_ptr: ptr::null_mut(), /// The observers ptr for signal handling observers_ptr: ptr::null(), /// The current input for signal handling @@ -563,6 +594,7 @@ mod windows_exception_handler { pub struct InProcessExecutorHandlerData { pub state_ptr: *mut c_void, pub event_mgr_ptr: *mut c_void, + pub fuzzer_ptr: *mut c_void, pub observers_ptr: *const c_void, pub current_input_ptr: *const c_void, pub crash_handler: unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut Self), @@ -592,23 +624,25 @@ mod windows_exception_handler { } } - pub unsafe fn inproc_crash_handler( + pub unsafe fn inproc_crash_handler( code: ExceptionCode, exception_pointers: *mut EXCEPTION_POINTERS, data: &mut InProcessExecutorHandlerData, ) where - EM: EventManager, + EM: EventFirer + EventRestarter, OT: ObserversTuple, OC: Corpus, - OF: Feedback, - S: HasObjective + HasSolutions, + OF: Feedback, + S: HasSolutions, I: Input + HasTargetBytes, + Z: HasObjective, { #[cfg(feature = "std")] println!("Crashed with {}", code); if !data.current_input_ptr.is_null() { let state = (data.state_ptr as *mut S).as_mut().unwrap(); let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); + let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); let observers = (data.observers_ptr as *const OT).as_ref().unwrap(); #[cfg(feature = "std")] @@ -620,16 +654,17 @@ mod windows_exception_handler { // Make sure we don't crash in the crash handler forever. data.current_input_ptr = ptr::null(); - let interesting = state + let interesting = fuzzer .objective_mut() - .is_interesting(&input, observers, &ExitKind::Crash) + .is_interesting(state, &input, observers, &ExitKind::Crash) .expect("In crash handler objective failure."); + if interesting { let new_input = input.clone(); let mut new_testcase = Testcase::new(new_input); - state + fuzzer .objective_mut() - .append_metadata(&mut new_testcase) + .append_metadata(state, &mut new_testcase) .expect("Failed adding metadata"); state .solutions_mut() @@ -688,7 +723,6 @@ mod windows_exception_handler { #[cfg(test)] mod tests { - use core::marker::PhantomData; use crate::{ @@ -701,7 +735,7 @@ mod tests { fn test_inmem_exec() { let mut harness = |_buf: &[u8]| ExitKind::Ok; - let mut in_process_executor = InProcessExecutor::<(), _, NopInput, (), ()> { + let mut in_process_executor = InProcessExecutor::<_, NopInput, (), ()> { harness_fn: &mut harness, observers: tuple_list!(), phantom: PhantomData, diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index bff5e9ecf6..1e13c9614e 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -35,52 +35,100 @@ pub enum ExitKind { } /// Pre and post exec hooks -pub trait HasExecHooks { +pub trait HasExecHooks { /// Called right before exexution starts #[inline] - fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { Ok(()) } /// Called right after execution finished. #[inline] - fn post_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { + fn post_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { Ok(()) } } /// A haskell-style tuple of objects that have pre and post exec hooks -pub trait HasExecHooksTuple { +pub trait HasExecHooksTuple { /// This is called right before the next execution. - fn pre_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error>; + fn pre_exec_all( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error>; /// This is called right after the last execution - fn post_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error>; + fn post_exec_all( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error>; } -impl HasExecHooksTuple for () { - fn pre_exec_all(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { +impl HasExecHooksTuple for () { + fn pre_exec_all( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { Ok(()) } - fn post_exec_all(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { + fn post_exec_all( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { Ok(()) } } -impl HasExecHooksTuple for (Head, Tail) +impl HasExecHooksTuple for (Head, Tail) where - Head: HasExecHooks, - Tail: HasExecHooksTuple, + Head: HasExecHooks, + Tail: HasExecHooksTuple, { - fn pre_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { - self.0.pre_exec(state, mgr, input)?; - self.1.pre_exec_all(state, mgr, input) + fn pre_exec_all( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + self.0.pre_exec(fuzzer, state, mgr, input)?; + self.1.pre_exec_all(fuzzer, state, mgr, input) } - fn post_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { - self.0.post_exec(state, mgr, input)?; - self.1.post_exec_all(state, mgr, input) + fn post_exec_all( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + self.0.post_exec(fuzzer, state, mgr, input)?; + self.1.post_exec_all(fuzzer, state, mgr, input) } } @@ -97,20 +145,33 @@ where } /// Execute the exec hooks of the observers if they all implement [`HasExecHooks`]. -pub trait HasObserversHooks: HasObservers +pub trait HasObserversHooks: HasObservers where - OT: ObserversTuple + HasExecHooksTuple, + OT: ObserversTuple + HasExecHooksTuple, { /// Run the pre exec hook for all [`crate::observers::Observer`]`s` linked to this [`Executor`]. #[inline] - fn pre_exec_observers(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { - self.observers_mut().pre_exec_all(state, mgr, input) + fn pre_exec_observers( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + self.observers_mut().pre_exec_all(fuzzer, state, mgr, input) } /// Run the post exec hook for all the [`crate::observers::Observer`]`s` linked to this [`Executor`]. #[inline] - fn post_exec_observers(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { - self.observers_mut().post_exec_all(state, mgr, input) + fn post_exec_observers( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + self.observers_mut() + .post_exec_all(fuzzer, state, mgr, input) } } @@ -142,7 +203,8 @@ where } } -impl HasExecHooks for NopExecutor where I: Input + HasTargetBytes {} +impl HasExecHooks for NopExecutor where I: Input + HasTargetBytes +{} #[cfg(test)] mod test { diff --git a/libafl/src/executors/timeout.rs b/libafl/src/executors/timeout.rs index 4ecf9dc8ff..7a89dc9a2c 100644 --- a/libafl/src/executors/timeout.rs +++ b/libafl/src/executors/timeout.rs @@ -97,21 +97,27 @@ where } } -impl HasObserversHooks for TimeoutExecutor +impl HasObserversHooks for TimeoutExecutor where E: Executor + HasObservers, I: Input, - OT: ObserversTuple + HasExecHooksTuple, + OT: ObserversTuple + HasExecHooksTuple, { } -impl HasExecHooks for TimeoutExecutor +impl HasExecHooks for TimeoutExecutor where - E: Executor + HasExecHooks, + E: Executor + HasExecHooks, I: Input, { #[inline] - fn pre_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { + fn pre_exec( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { #[cfg(unix)] unsafe { let milli_sec = self.exec_tmout.as_millis(); @@ -137,11 +143,17 @@ where // TODO let _ = self.exec_tmout.as_millis(); } - self.executor.pre_exec(state, mgr, input) + self.executor.pre_exec(fuzzer, state, mgr, input) } #[inline] - fn post_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { + fn post_exec( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { #[cfg(unix)] unsafe { let it_value = Timeval { @@ -165,6 +177,6 @@ where { // TODO } - self.executor.post_exec(state, mgr, input) + self.executor.post_exec(fuzzer, state, mgr, input) } } diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index ff6d1acdce..e641d46119 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -12,18 +12,18 @@ use crate::{ bolts::tuples::Named, corpus::Testcase, executors::ExitKind, - feedbacks::Feedback, + feedbacks::{Feedback, FeedbackState, FeedbackStatesTuple}, inputs::Input, observers::{MapObserver, ObserversTuple}, - state::HasMetadata, + state::{HasFeedbackStates, HasMetadata}, utils::AsSlice, Error, }; /// A [`MapFeedback`] that strives to maximize the map contents. -pub type MaxMapFeedback = MapFeedback; +pub type MaxMapFeedback = MapFeedback; /// A [`MapFeedback`] that strives to minimize the map contents. -pub type MinMapFeedback = MapFeedback; +pub type MinMapFeedback = MapFeedback; /// A Reducer function is used to aggregate values for the novelty search pub trait Reducer: Serialize + serde::de::DeserializeOwned + 'static @@ -118,36 +118,104 @@ impl MapNoveltiesMetadata { } } +/// The state of [`MapFeedback`] +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "T: serde::de::DeserializeOwned")] +pub struct MapFeedbackState +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + /// Contains information about untouched entries + pub history_map: Vec, + /// Name identifier of this instance + pub name: String, +} + +impl FeedbackState for MapFeedbackState where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned +{ +} + +impl Named for MapFeedbackState +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl MapFeedbackState +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + /// Create new `MapFeedbackState` + #[must_use] + pub fn new(name: &'static str, map_size: usize) -> Self { + Self { + history_map: vec![T::default(); map_size], + name: name.to_string(), + } + } + + /// Create new `MapFeedbackState` for the observer type. + pub fn with_observer(map_observer: &O) -> Self + where + O: MapObserver, + { + Self { + history_map: vec![T::default(); map_observer.map().len()], + name: map_observer.name().to_string(), + } + } + + /// Create new `MapFeedbackState` using a name and a map. + /// The map can be shared. + #[must_use] + pub fn with_history_map(name: &'static str, history_map: Vec) -> Self { + Self { + history_map, + name: name.to_string(), + } + } +} + /// The most common AFL-like feedback type #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(bound = "T: serde::de::DeserializeOwned")] -pub struct MapFeedback +pub struct MapFeedback where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, R: Reducer, O: MapObserver, + S: HasFeedbackStates, + FT: FeedbackStatesTuple, { - /// Contains information about untouched entries - history_map: Vec, /// Indexes used in the last observation indexes: Option>, /// New indexes observed in the last observation novelties: Option>, /// Name identifier of this instance name: String, + /// Name identifier of the observer + observer_name: String, /// Phantom Data of Reducer - phantom: PhantomData<(R, O)>, + phantom: PhantomData<(FT, S, R, O, T)>, } -impl Feedback for MapFeedback +impl Feedback for MapFeedback where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, R: Reducer, O: MapObserver, I: Input, + S: HasFeedbackStates, + FT: FeedbackStatesTuple, { fn is_interesting( &mut self, + state: &mut S, _input: &I, observers: &OT, _exit_kind: &ExitKind, @@ -157,24 +225,29 @@ where { let mut interesting = false; // TODO Replace with match_name_type when stable - let observer = observers.match_name::(&self.name).unwrap(); + let observer = observers.match_name::(&self.observer_name).unwrap(); let size = observer.usable_count(); let initial = observer.initial(); + let map_state = state + .feedback_states_mut() + .match_name_mut::>(&self.name) + .unwrap(); + if self.indexes.is_none() && self.novelties.is_none() { for i in 0..size { - let history = self.history_map[i]; + let history = map_state.history_map[i]; let item = observer.map()[i]; let reduced = R::reduce(history, item); if history != reduced { - self.history_map[i] = reduced; + map_state.history_map[i] = reduced; interesting = true; } } } else if self.indexes.is_some() && self.novelties.is_none() { for i in 0..size { - let history = self.history_map[i]; + let history = map_state.history_map[i]; let item = observer.map()[i]; if item != initial { self.indexes.as_mut().unwrap().push(i); @@ -182,25 +255,25 @@ where let reduced = R::reduce(history, item); if history != reduced { - self.history_map[i] = reduced; + map_state.history_map[i] = reduced; interesting = true; } } } else if self.indexes.is_none() && self.novelties.is_some() { for i in 0..size { - let history = self.history_map[i]; + let history = map_state.history_map[i]; let item = observer.map()[i]; let reduced = R::reduce(history, item); if history != reduced { - self.history_map[i] = reduced; + map_state.history_map[i] = reduced; interesting = true; self.novelties.as_mut().unwrap().push(i); } } } else { for i in 0..size { - let history = self.history_map[i]; + let history = map_state.history_map[i]; let item = observer.map()[i]; if item != initial { self.indexes.as_mut().unwrap().push(i); @@ -208,7 +281,7 @@ where let reduced = R::reduce(history, item); if history != reduced { - self.history_map[i] = reduced; + map_state.history_map[i] = reduced; interesting = true; self.novelties.as_mut().unwrap().push(i); } @@ -218,7 +291,7 @@ where Ok(interesting) } - fn append_metadata(&mut self, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { if let Some(v) = self.indexes.as_mut() { let meta = MapIndexesMetadata::new(core::mem::take(v)); testcase.add_metadata(meta); @@ -231,7 +304,7 @@ where } /// Discard the stored metadata in case that the testcase is not added to the corpus - fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { if let Some(v) = self.indexes.as_mut() { v.clear(); } @@ -242,11 +315,13 @@ where } } -impl Named for MapFeedback +impl Named for MapFeedback where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, R: Reducer, O: MapObserver, + S: HasFeedbackStates, + FT: FeedbackStatesTuple, { #[inline] fn name(&self) -> &str { @@ -254,83 +329,68 @@ where } } -impl MapFeedback +impl MapFeedback where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, R: Reducer, O: MapObserver, + S: HasFeedbackStates, + FT: FeedbackStatesTuple, { /// Create new `MapFeedback` #[must_use] - pub fn new(name: &'static str, map_size: usize) -> Self { + pub fn new(feedback_state: &MapFeedbackState, map_observer: &O) -> Self { Self { - history_map: vec![T::default(); map_size], - phantom: PhantomData, indexes: None, novelties: None, - name: name.to_string(), + name: feedback_state.name().to_string(), + observer_name: map_observer.name().to_string(), + phantom: PhantomData, } } - /// Create new `MapFeedback` for the observer type. - pub fn new_with_observer(map_observer: &O) -> Self { - Self { - history_map: vec![T::default(); map_observer.map().len()], - phantom: PhantomData, - indexes: None, - novelties: None, - name: map_observer.name().to_string(), - } - } - - /// Create new `MapFeedback` specifying if it must track indexes of novelties + /// Create new `MapFeedback` specifying if it must track indexes of used entries and/or novelties #[must_use] pub fn new_tracking( - name: &'static str, - map_size: usize, - track_indexes: bool, - track_novelties: bool, - ) -> Self { - Self { - history_map: vec![T::default(); map_size], - phantom: PhantomData, - indexes: if track_indexes { Some(vec![]) } else { None }, - novelties: if track_novelties { Some(vec![]) } else { None }, - name: name.to_string(), - } - } - - /// Create new `MapFeedback` for the observer type if it must track indexes of novelties - pub fn new_tracking_with_observer( + feedback_state: &MapFeedbackState, map_observer: &O, track_indexes: bool, track_novelties: bool, ) -> Self { Self { - history_map: vec![T::default(); map_observer.map().len()], - phantom: PhantomData, indexes: if track_indexes { Some(vec![]) } else { None }, novelties: if track_novelties { Some(vec![]) } else { None }, - name: map_observer.name().to_string(), + name: feedback_state.name().to_string(), + observer_name: map_observer.name().to_string(), + phantom: PhantomData, } } -} -impl MapFeedback -where - T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, - R: Reducer, - O: MapObserver, -{ - /// Create new `MapFeedback` using a map observer, and a map. - /// The map can be shared. + /// Create new `MapFeedback` #[must_use] - pub fn with_history_map(name: &'static str, history_map: Vec) -> Self { + pub fn with_names(name: &'static str, observer_name: &'static str) -> Self { Self { - history_map, - name: name.to_string(), indexes: None, novelties: None, + name: name.to_string(), + observer_name: observer_name.to_string(), + phantom: PhantomData, + } + } + + /// Create new `MapFeedback` specifying if it must track indexes of used entries and/or novelties + #[must_use] + pub fn with_names_tracking( + name: &'static str, + observer_name: &'static str, + track_indexes: bool, + track_novelties: bool, + ) -> Self { + Self { + indexes: if track_indexes { Some(vec![]) } else { None }, + novelties: if track_novelties { Some(vec![]) } else { None }, + observer_name: observer_name.to_string(), + name: name.to_string(), phantom: PhantomData, } } @@ -350,7 +410,7 @@ where { /// Creates a new [`ReachabilityFeedback`] for a [`MapObserver`]. #[must_use] - pub fn new_with_observer(map_observer: &O) -> Self { + pub fn new(map_observer: &O) -> Self { Self { name: map_observer.name().to_string(), target_idx: vec![], @@ -360,7 +420,7 @@ where /// Creates a new [`ReachabilityFeedback`] for a [`MapObserver`] with the given `name`. #[must_use] - pub fn new(name: &'static str) -> Self { + pub fn with_name(name: &'static str) -> Self { Self { name: name.to_string(), target_idx: vec![], @@ -369,17 +429,21 @@ where } } -impl Feedback for ReachabilityFeedback +impl Feedback for ReachabilityFeedback where I: Input, O: MapObserver, { - fn is_interesting( + fn is_interesting( &mut self, + _state: &mut S, _input: &I, observers: &OT, _exit_kind: &ExitKind, - ) -> Result { + ) -> Result + where + OT: ObserversTuple, + { // TODO Replace with match_name_type when stable let observer = observers.match_name::(&self.name).unwrap(); let size = observer.usable_count(); @@ -398,7 +462,7 @@ where } } - fn append_metadata(&mut self, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { if !self.target_idx.is_empty() { let meta = MapIndexesMetadata::new(core::mem::take(self.target_idx.as_mut())); testcase.add_metadata(meta); @@ -406,7 +470,7 @@ where Ok(()) } - fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { self.target_idx.clear(); Ok(()) } diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 41633436ae..bb69b4c27c 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -8,7 +8,7 @@ use alloc::string::{String, ToString}; use serde::{Deserialize, Serialize}; use crate::{ - bolts::tuples::Named, + bolts::tuples::{MatchName, Named}, corpus::Testcase, executors::ExitKind, inputs::Input, @@ -24,13 +24,14 @@ use core::{marker::PhantomData, time::Duration}; /// Feedbacks evaluate the observers. /// Basically, they reduce the information provided by an observer to a value, /// indicating the "interestingness" of the last run. -pub trait Feedback: Named + serde::Serialize + serde::de::DeserializeOwned +pub trait Feedback: Named where I: Input, { - /// `is_interesting ` should return the "Interestingness" from 0 to 255 (percent times 2.55) + /// `is_interesting ` return if an input is worth the addition to the corpus fn is_interesting( &mut self, + state: &mut S, input: &I, observers: &OT, exit_kind: &ExitKind, @@ -41,6 +42,7 @@ where #[cfg(feature = "introspection")] fn is_interesting_with_perf( &mut self, + state: &mut S, input: &I, observers: &OT, exit_kind: &ExitKind, @@ -54,7 +56,7 @@ where let start_time = crate::cpu::read_time_counter(); // Execute this feedback - let ret = self.is_interesting(input, observers, &exit_kind); + let ret = self.is_interesting(state, input, observers, &exit_kind); // Get the elapsed time for checking this feedback let elapsed = crate::cpu::read_time_counter() - start_time; @@ -69,41 +71,62 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata( + &mut self, + _state: &mut S, + _testcase: &mut Testcase, + ) -> Result<(), Error> { Ok(()) } /// Discard the stored metadata in case that the testcase is not added to the corpus #[inline] - fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { Ok(()) } } -/// Compose [`Feedback`]`s` with an `AND` operation -#[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(bound = "I: serde::de::DeserializeOwned")] -pub struct AndFeedback +/// [`FeedbackState`] is the data associated with a [`Feedback`] that must persist as part +/// of the fuzzer State +pub trait FeedbackState: Named + serde::Serialize + serde::de::DeserializeOwned {} + +/// A haskell-style tuple of feedback states +pub trait FeedbackStatesTuple: MatchName + serde::Serialize + serde::de::DeserializeOwned {} + +impl FeedbackStatesTuple for () {} + +impl FeedbackStatesTuple for (Head, Tail) where - A: Feedback, - B: Feedback, + Head: FeedbackState, + Tail: FeedbackStatesTuple, +{ +} + +/// Compose [`Feedback`]`s` with an `AND` operation +pub struct AndFeedback +where + A: Feedback, + B: Feedback, I: Input, { /// The first [`Feedback`] to `AND`. pub first: A, /// The second [`Feedback`] to `AND`. pub second: B, - phantom: PhantomData, + /// The name + name: String, + phantom: PhantomData<(I, S)>, } -impl Feedback for AndFeedback +impl Feedback for AndFeedback where - A: Feedback, - B: Feedback, + A: Feedback, + B: Feedback, I: Input, { fn is_interesting( &mut self, + state: &mut S, input: &I, observers: &OT, exit_kind: &ExitKind, @@ -111,14 +134,19 @@ where where OT: ObserversTuple, { - let a = self.first.is_interesting(input, observers, exit_kind)?; - let b = self.second.is_interesting(input, observers, exit_kind)?; + let a = self + .first + .is_interesting(state, input, observers, exit_kind)?; + let b = self + .second + .is_interesting(state, input, observers, exit_kind)?; Ok(a && b) } #[cfg(feature = "introspection")] fn is_interesting_with_perf( &mut self, + state: &mut S, input: &I, observers: &OT, exit_kind: &ExitKind, @@ -130,6 +158,7 @@ where { // Execute this feedback let a = self.first.is_interesting_with_perf( + state, input, observers, &exit_kind, @@ -137,6 +166,7 @@ where feedback_index, )?; let b = self.second.is_interesting_with_perf( + state, input, observers, &exit_kind, @@ -147,71 +177,73 @@ where } #[inline] - fn append_metadata(&mut self, testcase: &mut Testcase) -> Result<(), Error> { - self.first.append_metadata(testcase)?; - self.second.append_metadata(testcase) + fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + self.first.append_metadata(state, testcase)?; + self.second.append_metadata(state, testcase) } #[inline] - fn discard_metadata(&mut self, input: &I) -> Result<(), Error> { - self.first.discard_metadata(input)?; - self.second.discard_metadata(input) + fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> { + self.first.discard_metadata(state, input)?; + self.second.discard_metadata(state, input) } } -impl Named for AndFeedback +impl Named for AndFeedback where - A: Feedback, - B: Feedback, + A: Feedback, + B: Feedback, I: Input, { #[inline] fn name(&self) -> &str { - //format!("And({}, {})", self.first.name(), self.second.name()) - "AndFeedback" + &self.name } } -impl AndFeedback +impl AndFeedback where - A: Feedback, - B: Feedback, + A: Feedback, + B: Feedback, I: Input, { /// Creates a new [`AndFeedback`], resulting in the `AND` of two feedbacks. pub fn new(first: A, second: B) -> Self { + let name = format!("And({}, {})", first.name(), second.name()); Self { first, second, + name, phantom: PhantomData, } } } /// Compose feedbacks with an OR operation -#[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(bound = "I: serde::de::DeserializeOwned")] -pub struct OrFeedback +pub struct OrFeedback where - A: Feedback, - B: Feedback, + A: Feedback, + B: Feedback, I: Input, { /// The first [`Feedback`] pub first: A, /// The second [`Feedback`], `OR`ed with the first. pub second: B, - phantom: PhantomData, + /// The name + name: String, + phantom: PhantomData<(I, S)>, } -impl Feedback for OrFeedback +impl Feedback for OrFeedback where - A: Feedback, - B: Feedback, + A: Feedback, + B: Feedback, I: Input, { fn is_interesting( &mut self, + state: &mut S, input: &I, observers: &OT, exit_kind: &ExitKind, @@ -219,14 +251,19 @@ where where OT: ObserversTuple, { - let a = self.first.is_interesting(input, observers, exit_kind)?; - let b = self.second.is_interesting(input, observers, exit_kind)?; + let a = self + .first + .is_interesting(state, input, observers, exit_kind)?; + let b = self + .second + .is_interesting(state, input, observers, exit_kind)?; Ok(a || b) } #[cfg(feature = "introspection")] fn is_interesting_with_perf( &mut self, + state: &mut S, input: &I, observers: &OT, exit_kind: &ExitKind, @@ -238,6 +275,7 @@ where { // Execute this feedback let a = self.first.is_interesting_with_perf( + state, input, observers, &exit_kind, @@ -245,6 +283,7 @@ where feedback_index, )?; let b = self.second.is_interesting_with_perf( + state, input, observers, &exit_kind, @@ -255,67 +294,69 @@ where } #[inline] - fn append_metadata(&mut self, testcase: &mut Testcase) -> Result<(), Error> { - self.first.append_metadata(testcase)?; - self.second.append_metadata(testcase) + fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + self.first.append_metadata(state, testcase)?; + self.second.append_metadata(state, testcase) } #[inline] - fn discard_metadata(&mut self, input: &I) -> Result<(), Error> { - self.first.discard_metadata(input)?; - self.second.discard_metadata(input) + fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> { + self.first.discard_metadata(state, input)?; + self.second.discard_metadata(state, input) } } -impl Named for OrFeedback +impl Named for OrFeedback where - A: Feedback, - B: Feedback, + A: Feedback, + B: Feedback, I: Input, { #[inline] fn name(&self) -> &str { - //format!("Or({}, {})", self.first.name(), self.second.name()) - "OrFeedback" + &self.name } } -impl OrFeedback +impl OrFeedback where - A: Feedback, - B: Feedback, + A: Feedback, + B: Feedback, I: Input, { /// Creates a new [`OrFeedback`] for two feedbacks. pub fn new(first: A, second: B) -> Self { + let name = format!("Or({}, {})", first.name(), second.name()); Self { first, second, + name, phantom: PhantomData, } } } /// Compose feedbacks with an OR operation -#[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(bound = "I: serde::de::DeserializeOwned")] -pub struct NotFeedback +pub struct NotFeedback where - A: Feedback, + A: Feedback, I: Input, { /// The feedback to invert pub first: A, - phantom: PhantomData, + /// The name + name: String, + phantom: PhantomData<(I, S)>, } -impl Feedback for NotFeedback +impl Feedback for NotFeedback where - A: Feedback, + A: Feedback, I: Input, { fn is_interesting( &mut self, + state: &mut S, input: &I, observers: &OT, exit_kind: &ExitKind, @@ -323,41 +364,44 @@ where where OT: ObserversTuple, { - Ok(!self.first.is_interesting(input, observers, exit_kind)?) + Ok(!self + .first + .is_interesting(state, input, observers, exit_kind)?) } #[inline] - fn append_metadata(&mut self, testcase: &mut Testcase) -> Result<(), Error> { - self.first.append_metadata(testcase) + fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + self.first.append_metadata(state, testcase) } #[inline] - fn discard_metadata(&mut self, input: &I) -> Result<(), Error> { - self.first.discard_metadata(input) + fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> { + self.first.discard_metadata(state, input) } } -impl Named for NotFeedback +impl Named for NotFeedback where - A: Feedback, + A: Feedback, I: Input, { #[inline] fn name(&self) -> &str { - //format!("Not({})", self.first.name()) - "NotFeedback" + &self.name } } -impl NotFeedback +impl NotFeedback where - A: Feedback, + A: Feedback, I: Input, { /// Creates a new [`NotFeedback`]. pub fn new(first: A) -> Self { + let name = format!("Not({})", first.name()); Self { first, + name, phantom: PhantomData, } } @@ -394,12 +438,13 @@ macro_rules! feedback_not { } /// Hack to use () as empty Feedback -impl Feedback for () +impl Feedback for () where I: Input, { fn is_interesting( &mut self, + _state: &mut S, _input: &I, _observers: &OT, _exit_kind: &ExitKind, @@ -422,12 +467,13 @@ impl Named for () { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct CrashFeedback {} -impl Feedback for CrashFeedback +impl Feedback for CrashFeedback where I: Input, { fn is_interesting( &mut self, + _state: &mut S, _input: &I, _observers: &OT, exit_kind: &ExitKind, @@ -468,12 +514,13 @@ impl Default for CrashFeedback { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct TimeoutFeedback {} -impl Feedback for TimeoutFeedback +impl Feedback for TimeoutFeedback where I: Input, { fn is_interesting( &mut self, + _state: &mut S, _input: &I, _observers: &OT, exit_kind: &ExitKind, @@ -519,12 +566,13 @@ pub struct TimeFeedback { name: String, } -impl Feedback for TimeFeedback +impl Feedback for TimeFeedback where I: Input, { fn is_interesting( &mut self, + _state: &mut S, _input: &I, observers: &OT, _exit_kind: &ExitKind, @@ -540,7 +588,7 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { *testcase.exec_time_mut() = self.exec_time; self.exec_time = None; Ok(()) @@ -548,7 +596,7 @@ where /// Discard the stored metadata in case that the testcase is not added to the corpus #[inline] - fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { self.exec_time = None; Ok(()) } diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index 22224df57c..cf9af86022 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -1,39 +1,31 @@ //! The `Fuzzer` is the main struct for a fuzz campaign. use crate::{ - corpus::CorpusScheduler, + corpus::{Corpus, CorpusScheduler, Testcase}, events::{Event, EventManager}, - executors::{Executor, HasObservers}, + executors::{ + Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, + }, + feedbacks::Feedback, inputs::Input, + mark_feature_time, observers::ObserversTuple, stages::StagesTuple, - state::{HasClientPerfStats, HasExecutions}, + start_timer, + state::{HasClientPerfStats, HasCorpus, HasExecutions, HasSolutions}, utils::current_time, Error, }; +#[cfg(feature = "introspection")] +use crate::stats::PerfFeature; + use alloc::string::ToString; use core::{marker::PhantomData, time::Duration}; /// Send a stats update all 3 (or more) seconds const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(3 * 1000); -/// Holds a set of stages -pub trait HasStages -where - ST: StagesTuple, - E: Executor, - EM: EventManager, - I: Input, - Self: Sized, -{ - /// The stages - fn stages(&self) -> &ST; - - /// The stages (mut) - fn stages_mut(&mut self) -> &mut ST; -} - /// Holds a scheduler pub trait HasCorpusScheduler where @@ -47,8 +39,73 @@ where fn scheduler_mut(&mut self) -> &mut CS; } +/// Holds an feedback +pub trait HasFeedback +where + F: Feedback, + I: Input, +{ + /// The feedback + fn feedback(&self) -> &F; + + /// The feedback (mut) + fn feedback_mut(&mut self) -> &mut F; +} + +/// Holds an objective feedback +pub trait HasObjective +where + OF: Feedback, + I: Input, +{ + /// The objective feedback + fn objective(&self) -> &OF; + + /// The objective feedback (mut) + fn objective_mut(&mut self) -> &mut OF; +} + +/// Evaluate if an input is interesting using the feedback +pub trait IsInteresting +where + OT: ObserversTuple, +{ + /// Evaluate if a set of observation channels has an interesting state + fn is_interesting( + &mut self, + state: &mut S, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result; +} + +/// Add to the state if interesting +pub trait IfInteresting { + /// Adds this input to the corpus, if it's intersting, and return the index + fn add_if_interesting( + &mut self, + state: &mut S, + input: &I, + is_interesting: bool, + ) -> Result, Error>; +} + +/// Evaluate an input modyfing the state of the fuzzer +pub trait Evaluator { + /// Runs the input and triggers observers and feedback, + /// returns if is interesting an (option) the index of the new testcase in the corpus + fn evaluate_input( + &mut self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + input: I, + ) -> Result<(bool, Option), Error>; +} + /// The main fuzzer trait. -pub trait Fuzzer { +pub trait Fuzzer { /// Fuzz for a single iteration /// Returns the index of the last fuzzed corpus item /// @@ -57,24 +114,24 @@ pub trait Fuzzer { /// This way, the state will be available in the next, respawned, iteration. fn fuzz_one( &mut self, - state: &mut S, + stages: &mut ST, executor: &mut E, + state: &mut S, manager: &mut EM, - scheduler: &CS, ) -> Result; /// Fuzz forever (or until stopped) fn fuzz_loop( &mut self, - state: &mut S, + stages: &mut ST, executor: &mut E, + state: &mut S, manager: &mut EM, - scheduler: &CS, ) -> Result { let mut last = current_time(); let stats_timeout = STATS_TIMEOUT_DEFAULT; loop { - self.fuzz_one(state, executor, manager, scheduler)?; + self.fuzz_one(stages, executor, state, manager)?; last = Self::maybe_report_stats(state, manager, last, stats_timeout)?; } } @@ -85,18 +142,14 @@ pub trait Fuzzer { /// If you use this fn in a restarting scenario to only run for `n` iterations, /// before exiting, make sure you call `event_mgr.on_restart(&mut state)?;`. /// This way, the state will be available in the next, respawned, iteration. - fn fuzz_loop_for( + fn fuzz_loop_for( &mut self, + stages: &mut ST, state: &mut S, executor: &mut E, manager: &mut EM, - scheduler: &CS, iters: u64, - ) -> Result - where - EM: EventManager, - I: Input, - { + ) -> Result { if iters == 0 { return Err(Error::IllegalArgument( "Cannot fuzz for 0 iterations!".to_string(), @@ -108,7 +161,7 @@ pub trait Fuzzer { let stats_timeout = STATS_TIMEOUT_DEFAULT; for _ in 0..iters { - ret = self.fuzz_one(state, executor, manager, scheduler)?; + ret = self.fuzz_one(stages, executor, state, manager)?; last = Self::maybe_report_stats(state, manager, last, stats_timeout)?; } @@ -132,44 +185,27 @@ pub trait Fuzzer { } /// Your default fuzzer instance, for everyday use. -#[derive(Clone, Debug)] -pub struct StdFuzzer +#[derive(Debug)] +pub struct StdFuzzer where CS: CorpusScheduler, - ST: StagesTuple, - E: Executor, - EM: EventManager, + F: Feedback, I: Input, + OF: Feedback, { - stages: ST, - phantom: PhantomData<(CS, E, EM, I, OT, S)>, + scheduler: CS, + feedback: F, + objective: OF, + phantom: PhantomData<(C, I, OT, S, SC)>, } -impl HasStages for StdFuzzer +impl HasCorpusScheduler + for StdFuzzer where CS: CorpusScheduler, - ST: StagesTuple, - E: Executor, - EM: EventManager, - 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, - E: Executor, - EM: EventManager, + F: Feedback, I: Input, + OF: Feedback, { fn scheduler(&self) -> &CS { &self.scheduler @@ -179,17 +215,164 @@ where &mut self.scheduler } } -*/ -impl Fuzzer for StdFuzzer +impl HasFeedback for StdFuzzer where CS: CorpusScheduler, - S: HasExecutions + HasClientPerfStats, - ST: StagesTuple, - EM: EventManager, - E: Executor + HasObservers, - OT: ObserversTuple, + F: Feedback, I: Input, + OF: Feedback, +{ + fn feedback(&self) -> &F { + &self.feedback + } + + fn feedback_mut(&mut self) -> &mut F { + &mut self.feedback + } +} + +impl HasObjective for StdFuzzer +where + CS: CorpusScheduler, + F: Feedback, + I: Input, + OF: Feedback, +{ + fn objective(&self) -> &OF { + &self.objective + } + + fn objective_mut(&mut self) -> &mut OF { + &mut self.objective + } +} + +impl IsInteresting for StdFuzzer +where + C: Corpus, + CS: CorpusScheduler, + F: Feedback, + I: Input, + OF: Feedback, + OT: ObserversTuple, + S: HasCorpus, +{ + /// Evaluate if a set of observation channels has an interesting state + fn is_interesting( + &mut self, + state: &mut S, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result + where + OT: ObserversTuple, + { + self.feedback_mut() + .is_interesting(state, input, observers, exit_kind) + } +} + +impl IfInteresting for StdFuzzer +where + C: Corpus, + CS: CorpusScheduler, + F: Feedback, + I: Input, + OF: Feedback, + OT: ObserversTuple, + S: HasCorpus, +{ + /// Adds this input to the corpus, if it's intersting, and return the index + #[inline] + fn add_if_interesting( + &mut self, + state: &mut S, + input: &I, + is_interesting: bool, + ) -> Result, Error> { + if is_interesting { + let mut testcase = Testcase::new(input.clone()); + self.feedback_mut().append_metadata(state, &mut testcase)?; + let idx = state.corpus_mut().add(testcase)?; + self.scheduler_mut().on_add(state, idx)?; + Ok(Some(idx)) + } else { + self.feedback_mut().discard_metadata(state, input)?; + Ok(None) + } + } +} + +impl Evaluator + for StdFuzzer +where + C: Corpus, + CS: CorpusScheduler, + E: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, + EM: EventManager, + F: Feedback, + I: Input, + OF: Feedback, + S: HasExecutions + HasCorpus + HasSolutions + HasClientPerfStats, + SC: Corpus, +{ + /// Process one input, adding to the respective corpuses if needed and firing the right events + #[inline] + fn evaluate_input( + &mut self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + input: I, + ) -> Result<(bool, Option), Error> { + let (is_interesting, is_solution) = self.execute_input(state, executor, manager, &input)?; + let observers = executor.observers(); + + if is_solution { + // If the input is a solution, add it to the respective corpus + let mut testcase = Testcase::new(input.clone()); + self.objective_mut().append_metadata(state, &mut testcase)?; + state.solutions_mut().add(testcase)?; + } else { + self.objective_mut().discard_metadata(state, &input)?; + } + + let corpus_idx = self.add_if_interesting(state, &input, is_interesting)?; + if corpus_idx.is_some() { + let observers_buf = manager.serialize_observers(observers)?; + manager.fire( + state, + Event::NewTestcase { + input, + observers_buf, + corpus_size: state.corpus().count(), + client_config: "TODO".into(), + time: crate::utils::current_time(), + executions: *state.executions(), + }, + )?; + } + + Ok((is_interesting, corpus_idx)) + } +} + +impl Fuzzer + for StdFuzzer +where + CS: CorpusScheduler, + EM: EventManager, + F: Feedback, + I: Input, + S: HasExecutions + HasClientPerfStats, + OF: Feedback, + ST: StagesTuple, { #[inline] fn maybe_report_stats( @@ -240,17 +423,17 @@ where fn fuzz_one( &mut self, - state: &mut S, + stages: &mut ST, executor: &mut E, + state: &mut S, manager: &mut EM, - scheduler: &CS, ) -> Result { // Init timer for scheduler #[cfg(feature = "introspection")] state.introspection_stats_mut().start_timer(); // Get the next index from the scheduler - let idx = scheduler.next(state)?; + let idx = self.scheduler.next(state)?; // Mark the elapsed time for the scheduler #[cfg(feature = "introspection")] @@ -261,15 +444,14 @@ where state.introspection_stats_mut().reset_stage_index(); // Execute all stages - self.stages_mut() - .perform_all(state, executor, manager, scheduler, idx)?; + stages.perform_all(self, executor, state, manager, idx)?; // Init timer for manager #[cfg(feature = "introspection")] state.introspection_stats_mut().start_timer(); // Execute the manager - manager.process(state, executor, scheduler)?; + manager.process(self, state, executor)?; // Mark the elapsed time for the manager #[cfg(feature = "introspection")] @@ -279,19 +461,101 @@ where } } -impl StdFuzzer +impl StdFuzzer where CS: CorpusScheduler, - ST: StagesTuple, - E: Executor, - EM: EventManager, + F: Feedback, I: Input, + OF: Feedback, + S: HasExecutions + HasClientPerfStats, { /// Create a new `StdFuzzer` with standard behavior. - pub fn new(stages: ST) -> Self { + pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self { Self { - stages, + scheduler, + feedback, + objective, phantom: PhantomData, } } + + /// Runs the input and triggers observers and feedback + pub fn execute_input( + &mut self, + state: &mut S, + executor: &mut E, + event_mgr: &mut EM, + input: &I, + ) -> Result<(bool, bool), Error> + where + E: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, + EM: EventManager, + { + start_timer!(state); + executor.pre_exec_observers(self, state, event_mgr, input)?; + mark_feature_time!(state, PerfFeature::PreExecObservers); + + start_timer!(state); + executor.pre_exec(self, state, event_mgr, input)?; + mark_feature_time!(state, PerfFeature::PreExec); + + start_timer!(state); + let exit_kind = executor.run_target(input)?; + mark_feature_time!(state, PerfFeature::TargetExecution); + + start_timer!(state); + executor.post_exec(self, state, event_mgr, input)?; + mark_feature_time!(state, PerfFeature::PostExec); + + *state.executions_mut() += 1; + + start_timer!(state); + executor.post_exec_observers(self, state, event_mgr, input)?; + mark_feature_time!(state, PerfFeature::PostExecObservers); + + let observers = executor.observers(); + #[cfg(not(feature = "introspection"))] + let is_interesting = self + .feedback_mut() + .is_interesting(state, &input, observers, &exit_kind)?; + + #[cfg(feature = "introspection")] + let is_interesting = { + // Init temporary feedback stats here. We can't use the typical pattern above + // since we need a `mut self` for `feedbacks_mut`, so we can't also hand a + // new `mut self` to `is_interesting_with_perf`. We use this stack + // variable to get the stats and then update the feedbacks directly + let mut feedback_stats = [0_u64; crate::stats::NUM_FEEDBACKS]; + let feedback_index = 0; + let is_interesting = self.feedback_mut().is_interesting_with_perf( + state, + &input, + observers, + &exit_kind, + &mut feedback_stats, + feedback_index, + )?; + + // Update the feedback stats + state + .introspection_stats_mut() + .update_feedbacks(feedback_stats); + + // Return the total fitness + is_interesting + }; + + start_timer!(state); + let is_solution = self + .objective_mut() + .is_interesting(state, &input, observers, &exit_kind)?; + + mark_feature_time!(state, PerfFeature::GetObjectivesInterestingAll); + + Ok((is_interesting, is_solution)) + } } diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index 72a90befe8..c74f9fea24 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -156,7 +156,6 @@ impl From for Error { #[cfg(feature = "std")] #[cfg(test)] mod tests { - use crate::{ bolts::tuples::tuple_list, corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase}, @@ -164,7 +163,7 @@ mod tests { inputs::BytesInput, mutators::{mutations::BitFlipMutator, StdScheduledMutator}, stages::StdMutationalStage, - state::{HasCorpus, State}, + state::{HasCorpus, StdState}, stats::SimpleStats, utils::StdRand, Fuzzer, StdFuzzer, @@ -182,10 +181,9 @@ mod tests { let testcase = Testcase::new(vec![0; 4]); corpus.add(testcase).unwrap(); - let mut state = State::new( + let mut state = StdState::new( rand, corpus, - tuple_list!(), InMemoryCorpus::::new(), tuple_list!(), ); @@ -195,33 +193,33 @@ mod tests { }); let mut event_manager = SimpleEventManager::new(stats); + let scheduler = RandCorpusScheduler::new(); + let mut fuzzer = StdFuzzer::new(scheduler, (), ()); + let mut harness = |_buf: &[u8]| ExitKind::Ok; let mut executor = InProcessExecutor::new( &mut harness, tuple_list!(), - //Box::new(|_, _, _, _, _| ()), + &mut fuzzer, &mut state, &mut event_manager, ) .unwrap(); let mutator = StdScheduledMutator::new(tuple_list!(BitFlipMutator::new())); - let stage = StdMutationalStage::new(mutator); - let scheduler = RandCorpusScheduler::new(); - let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); for i in 0..1000 { fuzzer - .fuzz_one(&mut state, &mut executor, &mut event_manager, &scheduler) + .fuzz_one(&mut stages, &mut executor, &mut state, &mut event_manager) .unwrap_or_else(|_| panic!("Error in iter {}", i)); } let state_serialized = postcard::to_allocvec(&state).unwrap(); - let state_deserialized: State< + let state_deserialized: StdState< InMemoryCorpus, (), BytesInput, - (), StdRand, InMemoryCorpus, > = postcard::from_bytes(state_serialized.as_slice()).unwrap(); diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index 9710472682..67d61c35da 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -1837,7 +1837,7 @@ mod tests { corpus::{Corpus, InMemoryCorpus}, inputs::BytesInput, mutators::MutatorsTuple, - state::{HasMetadata, State}, + state::{HasMetadata, StdState}, utils::StdRand, }; @@ -1895,7 +1895,7 @@ mod tests { .add(BytesInput::new(vec![0x42; 0x1337]).into()) .unwrap(); - let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ()); + let mut state = StdState::new(rand, corpus, InMemoryCorpus::new(), ()); let mut mutations = test_mutations(); for _ in 0..2 { diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index 170462737b..96737b34b8 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -404,7 +404,7 @@ mod tests { scheduled::{havoc_mutations, StdScheduledMutator}, Mutator, }, - state::State, + state::StdState, utils::{Rand, StdRand, XkcdRand}, }; @@ -419,7 +419,7 @@ mod tests { let testcase = corpus.get(0).expect("Corpus did not contain entries"); let mut input = testcase.borrow_mut().load_input().unwrap().clone(); - let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ()); + let mut state = StdState::new(rand, corpus, InMemoryCorpus::new(), ()); rand.set_seed(5); @@ -446,7 +446,7 @@ mod tests { let mut input = testcase.borrow_mut().load_input().unwrap().clone(); let input_prior = input.clone(); - let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ()); + let mut state = StdState::new(rand, corpus, InMemoryCorpus::new(), ()); let mut havoc = StdScheduledMutator::new(havoc_mutations()); diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index b6dbeaa63b..843e736df9 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -75,13 +75,19 @@ impl<'a, T> Observer for StdMapObserver<'a, T> where { } -impl<'a, EM, I, S, T> HasExecHooks for StdMapObserver<'a, T> +impl<'a, EM, I, S, T, Z> HasExecHooks for StdMapObserver<'a, T> where T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, Self: MapObserver, { #[inline] - fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { self.reset_map() } } @@ -185,12 +191,18 @@ impl<'a, T> Observer for VariableMapObserver<'a, T> where { } -impl<'a, EM, I, S, T> HasExecHooks for VariableMapObserver<'a, T> +impl<'a, EM, I, S, T, Z> HasExecHooks for VariableMapObserver<'a, T> where T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, { #[inline] - fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { self.reset_map() } } @@ -302,21 +314,33 @@ static COUNT_CLASS_LOOKUP: [u8; 256] = [ impl Observer for HitcountsMapObserver where M: MapObserver {} -impl HasExecHooks for HitcountsMapObserver +impl HasExecHooks for HitcountsMapObserver where - M: MapObserver + HasExecHooks, + M: MapObserver + HasExecHooks, { #[inline] - fn pre_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { - self.base.pre_exec(state, mgr, input) + fn pre_exec( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + self.base.pre_exec(fuzzer, state, mgr, input) } #[inline] - fn post_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { + fn post_exec( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { for x in self.map_mut().iter_mut() { *x = COUNT_CLASS_LOOKUP[*x as usize]; } - self.base.post_exec(state, mgr, input) + self.base.post_exec(fuzzer, state, mgr, input) } } diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 2a4a1a726f..d0b2d08406 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -65,14 +65,26 @@ impl TimeObserver { impl Observer for TimeObserver {} -impl HasExecHooks for TimeObserver { - fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { +impl HasExecHooks for TimeObserver { + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { self.last_runtime = None; self.start_time = current_time(); Ok(()) } - fn post_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { + fn post_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { self.last_runtime = Some(current_time() - self.start_time); Ok(()) } diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index e7a19378df..f7f6b049d6 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -11,90 +11,67 @@ pub use mutational::{MutationalStage, StdMutationalStage}; //pub mod power; //pub use power::PowerMutationalStage; - -use crate::{ - bolts::tuples::TupleList, events::EventManager, executors::Executor, inputs::Input, - state::HasClientPerfStats, Error, -}; +use crate::Error; /// A stage is one step in the fuzzing process. /// Multiple stages will be scheduled one by one for each input. -pub trait Stage -where - EM: EventManager, - E: Executor, - I: Input, -{ +pub trait Stage { /// Run the stage fn perform( &mut self, - state: &mut S, + fuzzer: &mut Z, executor: &mut E, + state: &mut S, manager: &mut EM, - scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error>; } /// A tuple holding all `Stages` used for fuzzing. -pub trait StagesTuple -where - EM: EventManager, - E: Executor, - I: Input, -{ +pub trait StagesTuple { /// Performs all `Stages` in this tuple fn perform_all( &mut self, - state: &mut S, + fuzzer: &mut Z, executor: &mut E, + state: &mut S, manager: &mut EM, - scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error>; } -impl StagesTuple for () -where - EM: EventManager, - E: Executor, - I: Input, -{ +impl StagesTuple for () { fn perform_all( &mut self, - _: &mut S, + _: &mut Z, _: &mut E, + _: &mut S, _: &mut EM, - _: &CS, _: usize, ) -> Result<(), Error> { Ok(()) } } -impl StagesTuple for (Head, Tail) +impl StagesTuple for (Head, Tail) where - Head: Stage, - Tail: StagesTuple + TupleList, - EM: EventManager, - E: Executor, - I: Input, - S: HasClientPerfStats, + Head: Stage, + Tail: StagesTuple, { fn perform_all( &mut self, - state: &mut S, + fuzzer: &mut Z, executor: &mut E, + state: &mut S, manager: &mut EM, - scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error> { // Perform the current stage self.0 - .perform(state, executor, manager, scheduler, corpus_idx)?; + .perform(fuzzer, executor, state, manager, corpus_idx)?; // Execute the remaining stages self.1 - .perform_all(state, executor, manager, scheduler, corpus_idx) + .perform_all(fuzzer, executor, state, manager, corpus_idx) } } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 66029821dc..ec7d976010 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -1,16 +1,14 @@ use core::marker::PhantomData; use crate::{ - corpus::{Corpus, CorpusScheduler}, - events::EventManager, - executors::{Executor, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks}, + corpus::Corpus, + fuzzer::Evaluator, inputs::Input, mark_feature_time, mutators::Mutator, - observers::ObserversTuple, stages::Stage, start_timer, - state::{Evaluator, HasClientPerfStats, HasCorpus, HasRand}, + state::{HasClientPerfStats, HasCorpus, HasRand}, utils::Rand, Error, }; @@ -23,16 +21,13 @@ use crate::stats::PerfFeature; /// 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 + C: Corpus, M: Mutator, I: Input, - S: HasCorpus + Evaluator + HasClientPerfStats, - C: Corpus, - EM: EventManager, - E: Executor + HasObservers + HasExecHooks + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - CS: CorpusScheduler, + S: HasClientPerfStats + HasCorpus, + Z: Evaluator, { /// The mutator registered for this stage fn mutator(&self) -> &M; @@ -47,17 +42,17 @@ where #[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely... fn perform_mutational( &mut self, - state: &mut S, + fuzzer: &mut Z, executor: &mut E, + state: &mut S, manager: &mut EM, - scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error> { let num = self.iterations(state); for i in 0..num { start_timer!(state); - let mut input_mut = state + let mut input = state .corpus() .get(corpus_idx)? .borrow_mut() @@ -66,11 +61,11 @@ where mark_feature_time!(state, PerfFeature::GetInputFromCorpus); start_timer!(state); - self.mutator_mut().mutate(state, &mut input_mut, i as i32)?; + self.mutator_mut().mutate(state, &mut input, i as i32)?; mark_feature_time!(state, PerfFeature::Mutate); // Time is measured directly the `evaluate_input` function - let (_, corpus_idx) = state.evaluate_input(input_mut, executor, manager, scheduler)?; + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, input)?; start_timer!(state); self.mutator_mut().post_exec(state, i as i32, corpus_idx)?; @@ -86,35 +81,29 @@ pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128; /// The default mutational stage #[derive(Clone, Debug)] -pub struct StdMutationalStage +pub struct StdMutationalStage where + C: Corpus, M: Mutator, I: Input, - S: HasCorpus + Evaluator + HasRand, - C: Corpus, - EM: EventManager, - E: Executor + HasObservers + HasExecHooks + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - CS: CorpusScheduler, R: Rand, + S: HasClientPerfStats + HasCorpus + HasRand, + Z: Evaluator, { mutator: M, #[allow(clippy::type_complexity)] - phantom: PhantomData<(C, CS, E, EM, I, OT, R, S)>, + phantom: PhantomData<(C, E, EM, I, R, S, Z)>, } -impl MutationalStage - for StdMutationalStage +impl MutationalStage + for StdMutationalStage where + C: Corpus, M: Mutator, I: Input, - S: HasCorpus + Evaluator + HasRand + HasClientPerfStats, - C: Corpus, - EM: EventManager, - E: Executor + HasObservers + HasExecHooks + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - CS: CorpusScheduler, R: Rand, + S: HasClientPerfStats + HasCorpus + HasRand, + Z: Evaluator, { /// The mutator, added to this stage #[inline] @@ -134,43 +123,36 @@ where } } -impl Stage - for StdMutationalStage +impl Stage for StdMutationalStage where + C: Corpus, M: Mutator, I: Input, - S: HasCorpus + Evaluator + HasRand + HasClientPerfStats, - C: Corpus, - EM: EventManager, - E: Executor + HasObservers + HasExecHooks + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - CS: CorpusScheduler, R: Rand, + S: HasClientPerfStats + HasCorpus + HasRand, + Z: Evaluator, { #[inline] fn perform( &mut self, - state: &mut S, + fuzzer: &mut Z, executor: &mut E, + state: &mut S, manager: &mut EM, - scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error> { - self.perform_mutational(state, executor, manager, scheduler, corpus_idx) + self.perform_mutational(fuzzer, executor, state, manager, corpus_idx) } } -impl StdMutationalStage +impl StdMutationalStage where + C: Corpus, M: Mutator, I: Input, - S: HasCorpus + Evaluator + HasRand, - C: Corpus, - EM: EventManager, - E: Executor + HasObservers + HasExecHooks + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - CS: CorpusScheduler, R: Rand, + S: HasClientPerfStats + HasCorpus + HasRand, + Z: Evaluator, { /// 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 6357f184fe..75167e5e33 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -1,7 +1,7 @@ //! The fuzzer, and state are the core pieces of every good fuzzer use core::{fmt::Debug, marker::PhantomData, time::Duration}; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; #[cfg(feature = "std")] use std::{ fs, @@ -10,31 +10,25 @@ use std::{ use crate::{ bolts::serdeany::{SerdeAny, SerdeAnyMap}, - corpus::{Corpus, CorpusScheduler, Testcase}, + corpus::Corpus, events::{Event, EventManager, LogSeverity}, - executors::{ - Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, - }, - feedbacks::Feedback, + feedbacks::FeedbackStatesTuple, + fuzzer::Evaluator, generators::Generator, inputs::Input, - mark_feature_time, - observers::ObserversTuple, - start_timer, stats::ClientPerfStats, utils::Rand, Error, }; -#[cfg(feature = "introspection")] -use crate::stats::PerfFeature; - #[cfg(feature = "std")] use crate::inputs::bytes::BytesInput; /// The maximum size of a testcase pub const DEFAULT_MAX_SIZE: usize = 1_048_576; +pub trait State: Serialize + DeserializeOwned {} + /// Trait for elements offering a corpus pub trait HasCorpus where @@ -114,29 +108,15 @@ pub trait HasMetadata { } /// Trait for elements offering a feedback -pub trait HasFeedback: Sized +pub trait HasFeedbackStates where - F: Feedback, - I: Input, + FT: FeedbackStatesTuple, { - /// The feedback - fn feedback(&self) -> &F; + /// The feedback states + fn feedback_states(&self) -> &FT; - /// The feedback (mut) - fn feedback_mut(&mut self) -> &mut F; -} - -/// Trait for elements offering an objective feedback tuple -pub trait HasObjective: Sized -where - OF: Feedback, - I: Input, -{ - /// The objective feedback - fn objective(&self) -> &OF; - - /// The objective feedback (mut) - fn objective_mut(&mut self) -> &mut OF; + /// The feedback states (mut) + fn feedback_states_mut(&mut self) -> &mut FT; } /// Trait for the execution counter @@ -157,67 +137,16 @@ pub trait HasStartTime { fn start_time_mut(&mut self) -> &mut Duration; } -/// Add to the state if interesting -pub trait IfInteresting: Sized -where - I: Input, -{ - /// Evaluate if a set of observation channels has an interesting state - fn is_interesting( - &mut self, - input: &I, - observers: &OT, - exit_kind: &ExitKind, - ) -> Result - where - OT: ObserversTuple; - - /// Adds this input to the corpus, if it's intersting, and return the index - fn add_if_interesting( - &mut self, - input: &I, - is_interesting: bool, - scheduler: &CS, - ) -> Result, Error> - where - CS: CorpusScheduler, - Self: Sized; -} - -/// Evaluate an input modyfing the state of the fuzzer -pub trait Evaluator: Sized -where - I: Input, -{ - /// Runs the input and triggers observers and feedback - fn evaluate_input( - &mut self, - input: I, - executor: &mut E, - manager: &mut EM, - scheduler: &CS, - ) -> Result<(bool, Option), Error> - where - E: Executor - + HasObservers - + HasExecHooks - + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - EM: EventManager, - CS: CorpusScheduler; -} - /// The state a fuzz run. #[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(bound = "F: serde::de::DeserializeOwned")] -pub struct State +#[serde(bound = "FT: serde::de::DeserializeOwned")] +pub struct StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { /// RNG instance rand: R, @@ -227,12 +156,10 @@ where start_time: Duration, /// The corpus corpus: C, - /// Feedbacks used to evaluate an input - feedback: F, + /// States of the feedback used to evaluate an input + feedback_states: FT, // Solutions corpus solutions: SC, - /// Objective Feedbacks - objective: OF, /// Metadata stored for this state by one of the components metadata: SerdeAnyMap, /// MaxSize testcase size for mutators that appreciate it @@ -245,14 +172,23 @@ where phantom: PhantomData, } -impl HasRand for State +impl State for StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, + SC: Corpus, +{ +} + +impl HasRand for StdState +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { /// The rand instance #[inline] @@ -267,14 +203,13 @@ where } } -impl HasCorpus for State +impl HasCorpus for StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { /// Returns the corpus #[inline] @@ -289,14 +224,13 @@ where } } -impl HasSolutions for State +impl HasSolutions for StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { /// Returns the solutions corpus #[inline] @@ -311,14 +245,13 @@ where } } -impl HasMetadata for State +impl HasMetadata for StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { /// Get all the metadata into an [`hashbrown::HashMap`] #[inline] @@ -333,58 +266,34 @@ where } } -impl HasFeedback for State +impl HasFeedbackStates for StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { - /// The feedback + /// The feedback states #[inline] - fn feedback(&self) -> &F { - &self.feedback + fn feedback_states(&self) -> &FT { + &self.feedback_states } - /// The feedback (mut) + /// The feedback states (mut) #[inline] - fn feedback_mut(&mut self) -> &mut F { - &mut self.feedback + fn feedback_states_mut(&mut self) -> &mut FT { + &mut self.feedback_states } } -impl HasObjective for State +impl HasExecutions for StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, -{ - /// The objective feedback - #[inline] - fn objective(&self) -> &OF { - &self.objective - } - - /// The objective feedback (mut) - #[inline] - fn objective_mut(&mut self) -> &mut OF { - &mut self.objective - } -} - -impl HasExecutions for State -where - C: Corpus, - I: Input, - R: Rand, - F: Feedback, - SC: Corpus, - OF: Feedback, { /// The executions counter #[inline] @@ -399,14 +308,13 @@ where } } -impl HasMaxSize for State +impl HasMaxSize for StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { fn max_size(&self) -> usize { self.max_size @@ -417,14 +325,13 @@ where } } -impl HasStartTime for State +impl HasStartTime for StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { /// The starting time #[inline] @@ -439,139 +346,24 @@ where } } -impl IfInteresting for State -where - C: Corpus, - I: Input, - R: Rand, - F: Feedback, - SC: Corpus, - OF: Feedback, -{ - /// Evaluate if a set of observation channels has an interesting state - fn is_interesting( - &mut self, - input: &I, - observers: &OT, - exit_kind: &ExitKind, - ) -> Result - where - OT: ObserversTuple, - { - self.feedback_mut() - .is_interesting(input, observers, exit_kind) - } - - /// Adds this input to the corpus, if it's intersting, and return the index - #[inline] - fn add_if_interesting( - &mut self, - input: &I, - is_interesting: bool, - scheduler: &CS, - ) -> Result, Error> - where - CS: CorpusScheduler, - { - if is_interesting { - let mut testcase = Testcase::new(input.clone()); - self.feedback_mut().append_metadata(&mut testcase)?; - let idx = self.corpus.add(testcase)?; - scheduler.on_add(self, idx)?; - Ok(Some(idx)) - } else { - self.feedback_mut().discard_metadata(&input)?; - Ok(None) - } - } -} - -impl Evaluator for State -where - C: Corpus, - I: Input, - R: Rand, - F: Feedback, - SC: Corpus, - OF: Feedback, -{ - /// Process one input, adding to the respective corpuses if needed and firing the right events - #[inline] - fn evaluate_input( - &mut self, - // TODO probably we can take a ref to input and pass a cloned one to add_if_interesting - input: I, - executor: &mut E, - manager: &mut EM, - scheduler: &CS, - ) -> Result<(bool, Option), Error> - where - E: Executor - + HasObservers - + HasExecHooks - + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - C: Corpus, - EM: EventManager, - CS: CorpusScheduler, - { - let (is_interesting, is_solution) = self.execute_input(&input, executor, manager)?; - let observers = executor.observers(); - - if is_solution { - // If the input is a solution, add it to the respective corpus - let mut testcase = Testcase::new(input.clone()); - self.objective_mut().append_metadata(&mut testcase)?; - self.solutions_mut().add(testcase)?; - } else { - self.objective_mut().discard_metadata(&input)?; - } - - let corpus_idx = self.add_if_interesting(&input, is_interesting, scheduler)?; - if corpus_idx.is_some() { - let observers_buf = manager.serialize_observers(observers)?; - manager.fire( - self, - Event::NewTestcase { - input, - observers_buf, - corpus_size: self.corpus().count() + 1, - client_config: "TODO".into(), - time: crate::utils::current_time(), - executions: *self.executions(), - }, - )?; - } - - Ok((is_interesting, corpus_idx)) - } -} - #[cfg(feature = "std")] -impl State +impl StdState where C: Corpus, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { /// loads inputs from a directory - fn load_from_directory( + fn load_from_directory( &mut self, + fuzzer: &mut Z, executor: &mut E, manager: &mut EM, - scheduler: &CS, in_dir: &Path, ) -> Result<(), Error> where - E: Executor - + HasObservers - + HasExecHooks - + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - EM: EventManager, - CS: CorpusScheduler, + Z: Evaluator, { for entry in fs::read_dir(in_dir)? { let entry = entry?; @@ -588,19 +380,12 @@ where println!("Loading file {:?} ...", &path); let bytes = fs::read(&path)?; let input = BytesInput::new(bytes); - let (is_interesting, is_solution) = - self.execute_input(&input, executor, manager)?; - if self - .add_if_interesting(&input, is_interesting, scheduler)? - .is_none() - { + let (is_interesting, _) = fuzzer.evaluate_input(self, executor, manager, input)?; + if !is_interesting { println!("File {:?} was not interesting, skipped.", &path); } - if is_solution { - println!("File {:?} is a solution, however will be not considered as it is an initial testcase.", &path); - } } else if attr.is_dir() { - self.load_from_directory(executor, manager, scheduler, &path)?; + self.load_from_directory(fuzzer, executor, manager, &path)?; } } @@ -608,24 +393,19 @@ where } /// Loads initial inputs from the passed-in `in_dirs`. - pub fn load_initial_inputs( + pub fn load_initial_inputs( &mut self, + fuzzer: &mut Z, executor: &mut E, manager: &mut EM, - scheduler: &CS, in_dirs: &[PathBuf], ) -> Result<(), Error> where - E: Executor - + HasObservers - + HasExecHooks - + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - EM: EventManager, - CS: CorpusScheduler, + Z: Evaluator, + EM: EventManager, { for in_dir in in_dirs { - self.load_from_directory(executor, manager, scheduler, in_dir)?; + self.load_from_directory(fuzzer, executor, manager, in_dir)?; } manager.fire( self, @@ -635,122 +415,37 @@ where phantom: PhantomData, }, )?; - manager.process(self, executor, scheduler)?; + manager.process(fuzzer, self, executor)?; Ok(()) } } -impl State +impl StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { - /// Runs the input and triggers observers and feedback - pub fn execute_input( - &mut self, - input: &I, - executor: &mut E, - event_mgr: &mut EM, - ) -> Result<(bool, bool), Error> - where - E: Executor - + HasObservers - + HasExecHooks - + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - C: Corpus, - EM: EventManager, - { - start_timer!(self); - executor.pre_exec_observers(self, event_mgr, input)?; - mark_feature_time!(self, PerfFeature::PreExecObservers); - - start_timer!(self); - executor.pre_exec(self, event_mgr, input)?; - mark_feature_time!(self, PerfFeature::PreExec); - - start_timer!(self); - let exit_kind = executor.run_target(input)?; - mark_feature_time!(self, PerfFeature::TargetExecution); - - start_timer!(self); - executor.post_exec(self, event_mgr, input)?; - mark_feature_time!(self, PerfFeature::PostExec); - - *self.executions_mut() += 1; - - start_timer!(self); - executor.post_exec_observers(self, event_mgr, input)?; - mark_feature_time!(self, PerfFeature::PostExecObservers); - - let observers = executor.observers(); - #[cfg(not(feature = "introspection"))] - let is_interesting = self - .feedback_mut() - .is_interesting(&input, observers, &exit_kind)?; - - #[cfg(feature = "introspection")] - let is_interesting = { - // Init temporary feedback stats here. We can't use the typical pattern above - // since we need a `mut self` for `feedbacks_mut`, so we can't also hand a - // new `mut self` to `is_interesting_with_perf`. We use this stack - // variable to get the stats and then update the feedbacks directly - let mut feedback_stats = [0_u64; crate::stats::NUM_FEEDBACKS]; - let feedback_index = 0; - let is_interesting = self.feedback_mut().is_interesting_with_perf( - &input, - observers, - &exit_kind, - &mut feedback_stats, - feedback_index, - )?; - - // Update the feedback stats - self.introspection_stats_mut() - .update_feedbacks(feedback_stats); - - // Return the total fitness - is_interesting - }; - - start_timer!(self); - let is_solution = self - .objective_mut() - .is_interesting(&input, observers, &exit_kind)?; - - mark_feature_time!(self, PerfFeature::GetObjectivesInterestingAll); - - Ok((is_interesting, is_solution)) - } - /// Generate `num` initial inputs, using the passed-in generator. - pub fn generate_initial_inputs( + pub fn generate_initial_inputs( &mut self, + fuzzer: &mut Z, executor: &mut E, generator: &mut G, manager: &mut EM, - scheduler: &CS, num: usize, ) -> Result<(), Error> where G: Generator, - C: Corpus, - E: Executor - + HasObservers - + HasExecHooks - + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - EM: EventManager, - CS: CorpusScheduler, + Z: Evaluator, + EM: EventManager, { let mut added = 0; for _ in 0..num { let input = generator.generate(self.rand_mut())?; - let (is_interesting, _) = self.evaluate_input(input, executor, manager, scheduler)?; + let (is_interesting, _) = fuzzer.evaluate_input(self, executor, manager, input)?; if is_interesting { added += 1; } @@ -763,21 +458,20 @@ where phantom: PhantomData, }, )?; - manager.process(self, executor, scheduler)?; + manager.process(fuzzer, self, executor)?; Ok(()) } /// Creates a new `State`, taking ownership of all of the individual components during fuzzing. - pub fn new(rand: R, corpus: C, feedback: F, solutions: SC, objective: OF) -> Self { + pub fn new(rand: R, corpus: C, solutions: SC, feedback_states: FT) -> Self { Self { rand, executions: 0, start_time: Duration::from_millis(0), metadata: SerdeAnyMap::default(), corpus, - feedback, + feedback_states, solutions, - objective, max_size: DEFAULT_MAX_SIZE, #[cfg(feature = "introspection")] introspection_stats: ClientPerfStats::new(), @@ -787,14 +481,13 @@ where } #[cfg(feature = "introspection")] -impl HasClientPerfStats for State +impl HasClientPerfStats for StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { fn introspection_stats(&self) -> &ClientPerfStats { &self.introspection_stats @@ -806,14 +499,13 @@ where } #[cfg(not(feature = "introspection"))] -impl HasClientPerfStats for State +impl HasClientPerfStats for StdState where C: Corpus, I: Input, R: Rand, - F: Feedback, + FT: FeedbackStatesTuple, SC: Corpus, - OF: Feedback, { fn introspection_stats(&self) -> &ClientPerfStats { unimplemented!() diff --git a/libafl_frida/src/asan_rt.rs b/libafl_frida/src/asan_rt.rs index 158acb6d03..ffb2614ba2 100644 --- a/libafl_frida/src/asan_rt.rs +++ b/libafl_frida/src/asan_rt.rs @@ -1707,8 +1707,14 @@ pub struct AsanErrorsObserver { impl Observer for AsanErrorsObserver {} -impl HasExecHooks for AsanErrorsObserver { - fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { +impl HasExecHooks for AsanErrorsObserver { + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { unsafe { if ASAN_ERRORS.is_some() { ASAN_ERRORS.as_mut().unwrap().clear(); @@ -1767,12 +1773,13 @@ pub struct AsanErrorsFeedback { errors: Option, } -impl Feedback for AsanErrorsFeedback +impl Feedback for AsanErrorsFeedback where I: Input + HasTargetBytes, { fn is_interesting( &mut self, + _state: &mut S, _input: &I, observers: &OT, _exit_kind: &ExitKind, @@ -1793,7 +1800,7 @@ where } } - fn append_metadata(&mut self, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { if let Some(errors) = &self.errors { testcase.add_metadata(errors.clone()); } @@ -1801,7 +1808,7 @@ where Ok(()) } - fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { self.errors = None; Ok(()) }