Make input loading fallible in SyncFromDiskStage (#3195)
* Make input loading fallible in SyncFromDiskStage * fmt * Add InvalidInput in Error enum and skip the Input in SyncFromDiskStage if it is encountered * sync: remove file if error on loading in SyncFromDiskStage * add reason to Error::InvalidInput * sync make failure log a warning. clippy, fmt * typo * fmt * fmt --------- Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com> Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
fef129e23c
commit
c44802cf02
@ -58,6 +58,7 @@ impl SyncFromDiskMetadata {
|
||||
}
|
||||
|
||||
/// A stage that loads testcases from disk to sync with other fuzzers such as AFL++
|
||||
/// When syncing, the stage will ignore `Error::InvalidInput` and will skip the file.
|
||||
#[derive(Debug)]
|
||||
pub struct SyncFromDiskStage<CB, E, EM, I, S, Z> {
|
||||
name: Cow<'static, str>,
|
||||
@ -125,15 +126,25 @@ where
|
||||
let to_sync = sync_from_disk_metadata.left_to_sync.clone();
|
||||
log::debug!("Number of files to sync: {:?}", to_sync.len());
|
||||
for path in to_sync {
|
||||
let input = (self.load_callback)(fuzzer, state, &path)?;
|
||||
// Removing each path from the `left_to_sync` Vec before evaluating
|
||||
// prevents duplicate processing and ensures that each file is evaluated only once. This approach helps
|
||||
// avoid potential infinite loops that may occur if a file is an objective.
|
||||
// avoid potential infinite loops that may occur if a file is an objective or an invalid input.
|
||||
state
|
||||
.metadata_mut::<SyncFromDiskMetadata>()
|
||||
.unwrap()
|
||||
.left_to_sync
|
||||
.retain(|p| p != &path);
|
||||
let input = match (self.load_callback)(fuzzer, state, &path) {
|
||||
Ok(input) => input,
|
||||
Err(Error::InvalidInput(reason, _)) => {
|
||||
log::warn!(
|
||||
"Invalid input found in {} when syncing; reason {reason}; skipping;",
|
||||
path.display()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
log::debug!("Syncing and evaluating {}", path.display());
|
||||
fuzzer.evaluate_input(state, executor, manager, &input)?;
|
||||
}
|
||||
@ -161,6 +172,7 @@ where
|
||||
|
||||
impl<CB, E, EM, I, S, Z> SyncFromDiskStage<CB, E, EM, I, S, Z> {
|
||||
/// Creates a new [`SyncFromDiskStage`]
|
||||
/// To skip a file, you can return `Error::invalid_input` in `load_callback`
|
||||
#[must_use]
|
||||
pub fn new(sync_dirs: Vec<PathBuf>, load_callback: CB, interval: Duration, name: &str) -> Self {
|
||||
Self {
|
||||
|
@ -341,6 +341,8 @@ pub enum Error {
|
||||
InvalidCorpus(String, ErrorBacktrace),
|
||||
/// Error specific to a runtime like QEMU or Frida
|
||||
Runtime(String, ErrorBacktrace),
|
||||
/// The `Input` was invalid.
|
||||
InvalidInput(String, ErrorBacktrace),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
@ -369,6 +371,15 @@ impl Error {
|
||||
Error::EmptyOptional(arg.into(), ErrorBacktrace::new())
|
||||
}
|
||||
|
||||
/// The `Input` was invalid
|
||||
#[must_use]
|
||||
pub fn invalid_input<S>(reason: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Error::InvalidInput(reason.into(), ErrorBacktrace::new())
|
||||
}
|
||||
|
||||
/// Key not in Map
|
||||
#[must_use]
|
||||
pub fn key_not_found<S>(arg: S) -> Self
|
||||
@ -580,6 +591,10 @@ impl Display for Error {
|
||||
write!(f, "Runtime error: {0}", &s)?;
|
||||
display_error_backtrace(f, b)
|
||||
}
|
||||
Self::InvalidInput(s, b) => {
|
||||
write!(f, "Encountered an invalid input: {0}", &s)?;
|
||||
display_error_backtrace(f, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user