diff --git a/fuzzers/libfuzzer_stb_image/src/main.rs b/fuzzers/libfuzzer_stb_image/src/main.rs index c718b1ad2e..16eb76ae28 100644 --- a/fuzzers/libfuzzer_stb_image/src/main.rs +++ b/fuzzers/libfuzzer_stb_image/src/main.rs @@ -10,7 +10,7 @@ use libafl::{ QueueCorpusScheduler, }, events::setup_restarting_mgr_std, - executors::{inprocess::InProcessExecutor, ExitKind}, + executors::{inprocess::InProcessExecutor, ExitKind, ShadowExecutor}, feedback_or, feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -18,7 +18,7 @@ use libafl::{ mutators::scheduled::{havoc_mutations, StdScheduledMutator}, mutators::token_mutations::I2SRandReplace, observers::{StdMapObserver, TimeObserver}, - stages::{StdMutationalStage, TracingStage}, + stages::{ShadowTracingStage, StdMutationalStage}, state::{HasCorpus, StdState}, stats::MultiStats, Error, @@ -123,13 +123,16 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re }; // Create the executor for an in-process function with just one observer for edge coverage - let mut executor = InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?; + let mut executor = ShadowExecutor::new( + InProcessExecutor::new( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, + )?, + tuple_list!(cmplog_observer), + ); // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. @@ -151,22 +154,8 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re println!("We imported {} inputs from disk.", state.corpus().count()); } - // Secondary harness due to mut ownership - let mut harness = |input: &BytesInput| { - let target = input.target_bytes(); - let buf = target.as_slice(); - libfuzzer_test_one_input(buf); - ExitKind::Ok - }; - // Setup a tracing stage in which we log comparisons - let tracing = TracingStage::new(InProcessExecutor::new( - &mut harness, - tuple_list!(cmplog_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?); + let tracing = ShadowTracingStage::new(&mut executor); // Setup a randomic Input2State stage let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new()))); diff --git a/libafl/src/executors/combined.rs b/libafl/src/executors/combined.rs index a4ae7b3ccb..9d1b3225e8 100644 --- a/libafl/src/executors/combined.rs +++ b/libafl/src/executors/combined.rs @@ -15,7 +15,12 @@ pub struct CombinedExecutor { impl CombinedExecutor { /// Create a new `CombinedExecutor`, wrapping the given `executor`s. - pub fn new(primary: A, secondary: B) -> Self { + pub fn new(primary: A, secondary: B) -> Self + where + A: Executor, + B: Executor, + I: Input, + { Self { primary, secondary } } diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 25bcaf3c89..4dfa0d4cdc 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -13,6 +13,9 @@ pub use forkserver::{Forkserver, ForkserverExecutor, OutFile, TimeoutForkserverE pub mod combined; pub use combined::CombinedExecutor; +pub mod shadow; +pub use shadow::{HasShadowObserverHooks, ShadowExecutor}; + use crate::{ bolts::serdeany::SerdeAny, inputs::{HasTargetBytes, Input}, diff --git a/libafl/src/executors/shadow.rs b/libafl/src/executors/shadow.rs new file mode 100644 index 0000000000..2f15dc1888 --- /dev/null +++ b/libafl/src/executors/shadow.rs @@ -0,0 +1,186 @@ +//! A `ShadowExecutor` wraps an executor to have shadow observer that will not be considered by the feedbacks and the manager + +use crate::{ + executors::{Executor, ExitKind, HasExecHooksTuple, HasObservers, HasObserversHooks}, + inputs::Input, + observers::ObserversTuple, + Error, +}; + +pub trait HasShadowObserverHooks { + /// Run the pre exec hook for all the shadow [`crate::observers::Observer`]`s` + fn pre_exec_shadow_observers( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error>; + + /// Run the post exec hook for all the shadow [`crate::observers::Observer`]`s` + fn post_exec_shadow_observers( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error>; +} + +/// A [`ShadowExecutor`] wraps an executor and a set of shadow observers +pub struct ShadowExecutor { + executor: E, + shadow_observers: SOT, + // Enable the execution of the shadow observers hooks with the regular observers hooks + shadow_hooks: bool, +} + +impl ShadowExecutor +where + SOT: ObserversTuple, +{ + /// Create a new `ShadowExecutor`, wrapping the given `executor`. + pub fn new(executor: E, shadow_observers: SOT) -> Self { + Self { + executor, + shadow_observers, + shadow_hooks: false, + } + } + + /// Create a new `ShadowExecutor`, wrapping the given `executor`. + pub fn with_shadow_hooks( + executor: E, + shadow_observers: SOT, + shadow_hooks: bool, + ) -> Self { + Self { + executor, + shadow_observers, + shadow_hooks, + } + } + + #[inline] + pub fn shadow_observers(&self) -> &SOT { + &self.shadow_observers + } + + #[inline] + pub fn shadow_observers_mut(&mut self) -> &mut SOT { + &mut self.shadow_observers + } + + pub fn shadow_hooks(&self) -> &bool { + &self.shadow_hooks + } + + pub fn shadow_hooks_mut(&mut self) -> &mut bool { + &mut self.shadow_hooks + } +} + +impl HasShadowObserverHooks for ShadowExecutor +where + I: Input, + SOT: ObserversTuple + HasExecHooksTuple, +{ + #[inline] + fn pre_exec_shadow_observers( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + self.shadow_observers + .pre_exec_all(fuzzer, state, mgr, input) + } + + #[inline] + fn post_exec_shadow_observers( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + self.shadow_observers + .post_exec_all(fuzzer, state, mgr, input) + } +} + +impl Executor for ShadowExecutor +where + E: Executor, + I: Input, + SOT: ObserversTuple, +{ + fn run_target( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result { + self.executor.run_target(fuzzer, state, mgr, input) + } +} + +impl HasObservers for ShadowExecutor +where + E: HasObservers, + OT: ObserversTuple, + SOT: ObserversTuple, +{ + #[inline] + fn observers(&self) -> &OT { + self.executor.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.executor.observers_mut() + } +} + +impl HasObserversHooks for ShadowExecutor +where + E: HasObservers, + I: Input, + OT: ObserversTuple + HasExecHooksTuple, + SOT: ObserversTuple + HasExecHooksTuple, +{ + /// Run the pre exec hook for all [`crate::observers::Observer`]`s` linked to this [`Executor`]. + #[inline] + fn pre_exec_observers( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + if self.shadow_hooks { + self.shadow_observers + .pre_exec_all(fuzzer, state, mgr, input)?; + } + 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, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + if self.shadow_hooks { + self.shadow_observers + .post_exec_all(fuzzer, state, mgr, input)?; + } + self.observers_mut() + .post_exec_all(fuzzer, state, mgr, input) + } +} diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index a24b3fb924..300441cdfa 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -106,6 +106,18 @@ pub trait Evaluator { manager: &mut EM, input: I, ) -> Result<(bool, Option), Error>; + + /// Runs the input and triggers observers and feedback. + /// Adds an input, to the corpus even if it's not considered `interesting` by the `feedback`. + /// Returns the `index` of the new testcase in the corpus. + /// Usually, you want to use [`Evaluator::evaluate_input`], unless you know what you are doing. + fn add_input( + &mut self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + input: I, + ) -> Result; } /// The main fuzzer trait. @@ -391,6 +403,42 @@ where } } } + + /// Adds an input, even if it's not conisered `interesting` by any of the executors + fn add_input( + &mut self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + input: I, + ) -> Result { + let _ = self.execute_input(state, executor, manager, &input)?; + let observers = executor.observers(); + // Always consider this to be "interesting" + + // Not a solution + self.objective_mut().discard_metadata(state, &input)?; + + // Add the input to the main corpus + 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)?; + + 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: current_time(), + executions: *state.executions(), + }, + )?; + Ok(idx) + } } impl Fuzzer diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index a9eebc8316..5042d8e680 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -9,7 +9,7 @@ pub mod mutational; pub use mutational::{MutationalStage, StdMutationalStage}; pub mod tracing; -pub use tracing::TracingStage; +pub use tracing::{ShadowTracingStage, TracingStage}; //pub mod power; //pub use power::PowerMutationalStage; diff --git a/libafl/src/stages/tracing.rs b/libafl/src/stages/tracing.rs index 4e37dce554..1a564c116e 100644 --- a/libafl/src/stages/tracing.rs +++ b/libafl/src/stages/tracing.rs @@ -2,7 +2,7 @@ use core::{marker::PhantomData, mem::drop}; use crate::{ corpus::Corpus, - executors::{Executor, HasExecHooksTuple, HasObservers, HasObserversHooks}, + executors::{Executor, HasExecHooksTuple, HasObservers, HasObserversHooks, ShadowExecutor}, inputs::Input, mark_feature_time, observers::ObserversTuple, @@ -15,7 +15,7 @@ use crate::{ #[cfg(feature = "introspection")] use crate::stats::PerfFeature; -/// The default mutational stage +/// A stage that runs a tracer executor #[derive(Clone, Debug)] pub struct TracingStage where @@ -87,7 +87,7 @@ where OT: ObserversTuple + HasExecHooksTuple, S: HasClientPerfStats + HasExecutions + HasCorpus, { - /// Creates a new default mutational stage + /// Creates a new default stage pub fn new(tracer_executor: TE) -> Self { Self { tracer_executor, @@ -95,3 +95,78 @@ where } } } + +/// A stage that runs the shadow executor using also the shadow observers +#[derive(Clone, Debug)] +pub struct ShadowTracingStage { + #[allow(clippy::type_complexity)] + phantom: PhantomData<(C, E, EM, I, OT, S, SOT, Z)>, +} + +impl Stage, EM, S, Z> + for ShadowTracingStage +where + I: Input, + C: Corpus, + E: Executor + HasObservers + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, + SOT: ObserversTuple + HasExecHooksTuple, + S: HasClientPerfStats + HasExecutions + HasCorpus, +{ + #[inline] + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut ShadowExecutor, + state: &mut S, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + start_timer!(state); + let input = state + .corpus() + .get(corpus_idx)? + .borrow_mut() + .load_input()? + .clone(); + mark_feature_time!(state, PerfFeature::GetInputFromCorpus); + + let prev_shadow_hooks = *executor.shadow_hooks(); + *executor.shadow_hooks_mut() = true; + + start_timer!(state); + executor.pre_exec_observers(fuzzer, state, manager, &input)?; + mark_feature_time!(state, PerfFeature::PreExecObservers); + + start_timer!(state); + drop(executor.run_target(fuzzer, state, manager, &input)?); + mark_feature_time!(state, PerfFeature::TargetExecution); + + *state.executions_mut() += 1; + + start_timer!(state); + executor.post_exec_observers(fuzzer, state, manager, &input)?; + mark_feature_time!(state, PerfFeature::PostExecObservers); + + *executor.shadow_hooks_mut() = prev_shadow_hooks; + + Ok(()) + } +} + +impl ShadowTracingStage +where + I: Input, + C: Corpus, + E: Executor + HasObservers + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, + SOT: ObserversTuple + HasExecHooksTuple, + S: HasClientPerfStats + HasExecutions + HasCorpus, +{ + /// Creates a new default stage + pub fn new(_executor: &mut ShadowExecutor) -> Self { + Self { + phantom: PhantomData, + } + } +} diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 4b7d791df5..5a387fd17f 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -355,12 +355,15 @@ where SC: Corpus, { /// loads inputs from a directory + /// If `forced` is `true`, the value will be loaded, + /// even if it's not considered to be `interesting`. fn load_from_directory( &mut self, fuzzer: &mut Z, executor: &mut E, manager: &mut EM, in_dir: &Path, + forced: bool, ) -> Result<(), Error> where Z: Evaluator, @@ -379,18 +382,69 @@ where if attr.is_file() && attr.len() > 0 { println!("Loading file {:?} ...", &path); let input = I::from_file(&path)?; - let (is_interesting, _) = fuzzer.evaluate_input(self, executor, manager, input)?; - if !is_interesting { - println!("File {:?} was not interesting, skipped.", &path); + if forced { + let _ = fuzzer.add_input(self, executor, manager, input)?; + } else { + let (is_interesting, _) = + fuzzer.evaluate_input(self, executor, manager, input)?; + if !is_interesting { + println!("File {:?} was not interesting, skipped.", &path); + } } } else if attr.is_dir() { - self.load_from_directory(fuzzer, executor, manager, &path)?; + self.load_from_directory(fuzzer, executor, manager, &path, forced)?; } } Ok(()) } + /// Loads initial inputs from the passed-in `in_dirs`. + /// If `forced` is true, will add all testcases, no matter what. + fn load_initial_inputs_internal( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + manager: &mut EM, + in_dirs: &[PathBuf], + forced: bool, + ) -> Result<(), Error> + where + Z: Evaluator, + EM: EventManager, + { + for in_dir in in_dirs { + self.load_from_directory(fuzzer, executor, manager, in_dir, forced)?; + } + manager.fire( + self, + Event::Log { + severity_level: LogSeverity::Debug, + message: format!("Loaded {} initial testcases.", self.corpus().count()), // get corpus count + phantom: PhantomData, + }, + )?; + manager.process(fuzzer, self, executor)?; + Ok(()) + } + + /// Loads all intial inputs, even if they are not consiered `intesting`. + /// This is rarely the right method, use `load_initial_inputs`, + /// and potentially fix your `Feedback`, instead. + pub fn load_initial_inputs_forced( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + manager: &mut EM, + in_dirs: &[PathBuf], + ) -> Result<(), Error> + where + Z: Evaluator, + EM: EventManager, + { + self.load_initial_inputs_internal(fuzzer, executor, manager, in_dirs, true) + } + /// Loads initial inputs from the passed-in `in_dirs`. pub fn load_initial_inputs( &mut self, @@ -403,19 +457,7 @@ where Z: Evaluator, EM: EventManager, { - for in_dir in in_dirs { - self.load_from_directory(fuzzer, executor, manager, in_dir)?; - } - manager.fire( - self, - Event::Log { - severity_level: LogSeverity::Debug, - message: format!("Loaded {} initial testcases.", self.corpus().count()), // get corpus count - phantom: PhantomData, - }, - )?; - manager.process(fuzzer, self, executor)?; - Ok(()) + self.load_initial_inputs_internal(fuzzer, executor, manager, in_dirs, false) } } diff --git a/libafl_frida/src/lib.rs b/libafl_frida/src/lib.rs index 8fcdc4745b..23a8978911 100644 --- a/libafl_frida/src/lib.rs +++ b/libafl_frida/src/lib.rs @@ -109,7 +109,6 @@ impl FridaOptions { } "cmplog" => { options.enable_cmplog = value.parse().unwrap(); - #[cfg(not(target_arch = "aarch64"))] if options.enable_cmplog { panic!(