Skip to content

Commit

Permalink
Introduce AFL_EXIT_ON_SEED_ISSUES (AFLplusplus#2085)
Browse files Browse the repository at this point in the history
* introduce load_initial_inputs_disallow_solution to exit if a solution is found during seed loading

* fmt

* rename CorpusError to Corpus

* add LoadConfig to simplify configuration for loading initial inputs

* Rename Error::Corpus to Error::InvalidCorpus
Add documentation to LoadConfig struct
fix nostd for LoadConfig

---------

Co-authored-by: aarnav <[email protected]>
  • Loading branch information
R9295 and R9295 authored Apr 23, 2024
1 parent d349651 commit 76a95bc
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 24 deletions.
113 changes: 89 additions & 24 deletions libafl/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,24 @@ pub trait HasLastReportTime {
fn last_report_time_mut(&mut self) -> &mut Option<Duration>;
}

/// Struct that holds the options for input loading
#[cfg(feature = "std")]
pub struct LoadConfig<'a, I, S, Z> {
/// Load Input even if it was deemed "uninteresting" by the fuzzer
forced: bool,
/// Function to load input from a Path
loader: &'a mut dyn FnMut(&mut Z, &mut S, &Path) -> Result<I, Error>,
/// Error if Input leads to a Solution.
exit_on_solution: bool,
}

#[cfg(feature = "std")]
impl<'a, I, S, Z> Debug for LoadConfig<'a, I, S, Z> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "LoadConfig {{}}")
}
}

/// The state a fuzz run.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "
Expand Down Expand Up @@ -647,8 +665,7 @@ where
executor: &mut E,
manager: &mut EM,
file_list: &[PathBuf],
forced: bool,
loader: &mut dyn FnMut(&mut Z, &mut Self, &Path) -> Result<I, Error>,
load_config: LoadConfig<I, Self, Z>,
) -> Result<(), Error>
where
E: UsesState<State = Self>,
Expand All @@ -664,45 +681,45 @@ where
self.remaining_initial_files = Some(file_list.to_vec());
}

self.continue_loading_initial_inputs_custom(fuzzer, executor, manager, forced, loader)
self.continue_loading_initial_inputs_custom(fuzzer, executor, manager, load_config)
}

fn load_file<E, EM, Z>(
&mut self,
path: &PathBuf,
manager: &mut EM,
fuzzer: &mut Z,
executor: &mut E,
forced: bool,
loader: &mut dyn FnMut(&mut Z, &mut Self, &Path) -> Result<I, Error>,
) -> Result<(), Error>
config: &mut LoadConfig<I, Self, Z>,
) -> Result<ExecuteInputResult, Error>
where
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
log::info!("Loading file {:?} ...", &path);
let input = loader(fuzzer, self, path)?;
if forced {
let input = (config.loader)(fuzzer, self, path)?;
if config.forced {
let _: CorpusId = fuzzer.add_input(self, executor, manager, input)?;
Ok(ExecuteInputResult::Corpus)
} else {
let (res, _) = fuzzer.evaluate_input(self, executor, manager, input.clone())?;
if res == ExecuteInputResult::None {
fuzzer.add_disabled_input(self, input)?;
log::warn!("input {:?} was not interesting, adding as disabled.", &path);
}
Ok(res)
}
Ok(())
}
/// Loads initial inputs from the passed-in `in_dirs`.
/// If `forced` is true, will add all testcases, no matter what.
/// This method takes a list of files.
/// This method takes a list of files and a `LoadConfig`
/// which specifies the special handling of initial inputs
fn continue_loading_initial_inputs_custom<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
forced: bool,
loader: &mut dyn FnMut(&mut Z, &mut Self, &Path) -> Result<I, Error>,
mut config: LoadConfig<I, Self, Z>,
) -> Result<(), Error>
where
E: UsesState<State = Self>,
Expand All @@ -712,7 +729,13 @@ where
loop {
match self.next_file() {
Ok(path) => {
self.load_file(&path, manager, fuzzer, executor, forced, loader)?;
let res = self.load_file(&path, manager, fuzzer, executor, &mut config)?;
if config.exit_on_solution && matches!(res, ExecuteInputResult::Solution) {
return Err(Error::invalid_corpus(format!(
"Input {} resulted in a solution.",
path.display()
)));
}
}
Err(Error::IteratorEnd(_, _)) => break,
Err(e) => return Err(e),
Expand Down Expand Up @@ -751,8 +774,11 @@ where
executor,
manager,
file_list,
false,
&mut |_, _, path| I::from_file(path),
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: false,
exit_on_solution: false,
},
)
}

Expand All @@ -776,8 +802,11 @@ where
fuzzer,
executor,
manager,
true,
&mut |_, _, path| I::from_file(path),
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: true,
exit_on_solution: false,
},
)
}
/// Loads initial inputs from the passed-in `in_dirs`.
Expand All @@ -800,8 +829,11 @@ where
executor,
manager,
file_list,
true,
&mut |_, _, path| I::from_file(path),
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: true,
exit_on_solution: false,
},
)
}

Expand All @@ -823,8 +855,38 @@ where
fuzzer,
executor,
manager,
false,
&mut |_, _, path| I::from_file(path),
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: false,
exit_on_solution: false,
},
)
}

/// Loads initial inputs from the passed-in `in_dirs`.
/// Will return a `CorpusError` if a solution is found
pub fn load_initial_inputs_disallow_solution<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
self.canonicalize_input_dirs(in_dirs)?;
self.continue_loading_initial_inputs_custom(
fuzzer,
executor,
manager,
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: false,
exit_on_solution: true,
},
)
}

Expand Down Expand Up @@ -862,8 +924,11 @@ where
fuzzer,
executor,
manager,
false,
&mut |_, _, path| I::from_file(path),
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: false,
exit_on_solution: false,
},
)?;
} else {
self.canonicalize_input_dirs(in_dirs)?;
Expand Down
14 changes: 14 additions & 0 deletions libafl_bolts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ pub enum Error {
OsError(io::Error, String, ErrorBacktrace),
/// Something else happened
Unknown(String, ErrorBacktrace),
/// Error with the corpora
InvalidCorpus(String, ErrorBacktrace),
}

impl Error {
Expand Down Expand Up @@ -438,6 +440,14 @@ impl Error {
{
Error::Unknown(arg.into(), ErrorBacktrace::new())
}
/// Error with corpora
#[must_use]
pub fn invalid_corpus<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::InvalidCorpus(arg.into(), ErrorBacktrace::new())
}
}

impl Display for Error {
Expand Down Expand Up @@ -498,6 +508,10 @@ impl Display for Error {
write!(f, "Unknown error: {0}", &s)?;
display_error_backtrace(f, b)
}
Self::InvalidCorpus(s, b) => {
write!(f, "Invalid corpus: {0}", &s)?;
display_error_backtrace(f, b)
}
}
}
}
Expand Down

0 comments on commit 76a95bc

Please sign in to comment.