From 6b235472e0f0f2664ac08f6c076c47b02393163d Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 9 Jun 2021 14:51:48 +0200 Subject: [PATCH] Added load_initial_inputs_forced to add all inputs to a corpus (fixes #123) (#158) Co-authored-by: Andrea Fioraldi --- libafl/src/fuzzer.rs | 48 ++++++++++++++++++++++++++ libafl/src/state/mod.rs | 76 ++++++++++++++++++++++++++++++++--------- 2 files changed, 107 insertions(+), 17 deletions(-) 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/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) } }