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++
|
/// 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)]
|
#[derive(Debug)]
|
||||||
pub struct SyncFromDiskStage<CB, E, EM, I, S, Z> {
|
pub struct SyncFromDiskStage<CB, E, EM, I, S, Z> {
|
||||||
name: Cow<'static, str>,
|
name: Cow<'static, str>,
|
||||||
@ -125,15 +126,25 @@ where
|
|||||||
let to_sync = sync_from_disk_metadata.left_to_sync.clone();
|
let to_sync = sync_from_disk_metadata.left_to_sync.clone();
|
||||||
log::debug!("Number of files to sync: {:?}", to_sync.len());
|
log::debug!("Number of files to sync: {:?}", to_sync.len());
|
||||||
for path in to_sync {
|
for path in to_sync {
|
||||||
let input = (self.load_callback)(fuzzer, state, &path)?;
|
|
||||||
// Removing each path from the `left_to_sync` Vec before evaluating
|
// 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
|
// 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
|
state
|
||||||
.metadata_mut::<SyncFromDiskMetadata>()
|
.metadata_mut::<SyncFromDiskMetadata>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.left_to_sync
|
.left_to_sync
|
||||||
.retain(|p| p != &path);
|
.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());
|
log::debug!("Syncing and evaluating {}", path.display());
|
||||||
fuzzer.evaluate_input(state, executor, manager, &input)?;
|
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> {
|
impl<CB, E, EM, I, S, Z> SyncFromDiskStage<CB, E, EM, I, S, Z> {
|
||||||
/// Creates a new [`SyncFromDiskStage`]
|
/// Creates a new [`SyncFromDiskStage`]
|
||||||
|
/// To skip a file, you can return `Error::invalid_input` in `load_callback`
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(sync_dirs: Vec<PathBuf>, load_callback: CB, interval: Duration, name: &str) -> Self {
|
pub fn new(sync_dirs: Vec<PathBuf>, load_callback: CB, interval: Duration, name: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -341,6 +341,8 @@ pub enum Error {
|
|||||||
InvalidCorpus(String, ErrorBacktrace),
|
InvalidCorpus(String, ErrorBacktrace),
|
||||||
/// Error specific to a runtime like QEMU or Frida
|
/// Error specific to a runtime like QEMU or Frida
|
||||||
Runtime(String, ErrorBacktrace),
|
Runtime(String, ErrorBacktrace),
|
||||||
|
/// The `Input` was invalid.
|
||||||
|
InvalidInput(String, ErrorBacktrace),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
@ -369,6 +371,15 @@ impl Error {
|
|||||||
Error::EmptyOptional(arg.into(), ErrorBacktrace::new())
|
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
|
/// Key not in Map
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn key_not_found<S>(arg: S) -> Self
|
pub fn key_not_found<S>(arg: S) -> Self
|
||||||
@ -580,6 +591,10 @@ impl Display for Error {
|
|||||||
write!(f, "Runtime error: {0}", &s)?;
|
write!(f, "Runtime error: {0}", &s)?;
|
||||||
display_error_backtrace(f, b)
|
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