Added load_initial_inputs_forced to add all inputs to a corpus (fixes #123) (#158)

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
This commit is contained in:
Dominik Maier 2021-06-09 14:51:48 +02:00 committed by GitHub
parent 308e9c7fe9
commit 6b235472e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 107 additions and 17 deletions

View File

@ -106,6 +106,18 @@ pub trait Evaluator<E, EM, I, S> {
manager: &mut EM, manager: &mut EM,
input: I, input: I,
) -> Result<(bool, Option<usize>), Error>; ) -> Result<(bool, Option<usize>), 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<usize, Error>;
} }
/// The main fuzzer trait. /// 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<usize, Error> {
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<C, CS, E, EM, F, I, OF, OT, S, ST, SC> Fuzzer<E, EM, I, S, ST> impl<C, CS, E, EM, F, I, OF, OT, S, ST, SC> Fuzzer<E, EM, I, S, ST>

View File

@ -355,12 +355,15 @@ where
SC: Corpus<I>, SC: Corpus<I>,
{ {
/// loads inputs from a directory /// 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<E, EM, Z>( fn load_from_directory<E, EM, Z>(
&mut self, &mut self,
fuzzer: &mut Z, fuzzer: &mut Z,
executor: &mut E, executor: &mut E,
manager: &mut EM, manager: &mut EM,
in_dir: &Path, in_dir: &Path,
forced: bool,
) -> Result<(), Error> ) -> Result<(), Error>
where where
Z: Evaluator<E, EM, I, Self>, Z: Evaluator<E, EM, I, Self>,
@ -379,18 +382,69 @@ where
if attr.is_file() && attr.len() > 0 { if attr.is_file() && attr.len() > 0 {
println!("Loading file {:?} ...", &path); println!("Loading file {:?} ...", &path);
let input = I::from_file(&path)?; let input = I::from_file(&path)?;
let (is_interesting, _) = fuzzer.evaluate_input(self, executor, manager, input)?; if forced {
if !is_interesting { let _ = fuzzer.add_input(self, executor, manager, input)?;
println!("File {:?} was not interesting, skipped.", &path); } 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() { } else if attr.is_dir() {
self.load_from_directory(fuzzer, executor, manager, &path)?; self.load_from_directory(fuzzer, executor, manager, &path, forced)?;
} }
} }
Ok(()) 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<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
in_dirs: &[PathBuf],
forced: bool,
) -> Result<(), Error>
where
Z: Evaluator<E, EM, I, Self>,
EM: EventManager<E, I, Self, Z>,
{
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<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
Z: Evaluator<E, EM, I, Self>,
EM: EventManager<E, I, Self, Z>,
{
self.load_initial_inputs_internal(fuzzer, executor, manager, in_dirs, true)
}
/// Loads initial inputs from the passed-in `in_dirs`. /// Loads initial inputs from the passed-in `in_dirs`.
pub fn load_initial_inputs<E, EM, Z>( pub fn load_initial_inputs<E, EM, Z>(
&mut self, &mut self,
@ -403,19 +457,7 @@ where
Z: Evaluator<E, EM, I, Self>, Z: Evaluator<E, EM, I, Self>,
EM: EventManager<E, I, Self, Z>, EM: EventManager<E, I, Self, Z>,
{ {
for in_dir in in_dirs { self.load_initial_inputs_internal(fuzzer, executor, manager, in_dirs, false)
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(())
} }
} }