Skip to content

Commit c44802c

Browse files
R9295tokatokadomenukk
authored
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>
1 parent fef129e commit c44802c

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

libafl/src/stages/sync.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ impl SyncFromDiskMetadata {
5858
}
5959

6060
/// A stage that loads testcases from disk to sync with other fuzzers such as AFL++
61+
/// When syncing, the stage will ignore `Error::InvalidInput` and will skip the file.
6162
#[derive(Debug)]
6263
pub struct SyncFromDiskStage<CB, E, EM, I, S, Z> {
6364
name: Cow<'static, str>,
@@ -125,15 +126,25 @@ where
125126
let to_sync = sync_from_disk_metadata.left_to_sync.clone();
126127
log::debug!("Number of files to sync: {:?}", to_sync.len());
127128
for path in to_sync {
128-
let input = (self.load_callback)(fuzzer, state, &path)?;
129129
// Removing each path from the `left_to_sync` Vec before evaluating
130130
// prevents duplicate processing and ensures that each file is evaluated only once. This approach helps
131-
// avoid potential infinite loops that may occur if a file is an objective.
131+
// avoid potential infinite loops that may occur if a file is an objective or an invalid input.
132132
state
133133
.metadata_mut::<SyncFromDiskMetadata>()
134134
.unwrap()
135135
.left_to_sync
136136
.retain(|p| p != &path);
137+
let input = match (self.load_callback)(fuzzer, state, &path) {
138+
Ok(input) => input,
139+
Err(Error::InvalidInput(reason, _)) => {
140+
log::warn!(
141+
"Invalid input found in {} when syncing; reason {reason}; skipping;",
142+
path.display()
143+
);
144+
continue;
145+
}
146+
Err(e) => return Err(e),
147+
};
137148
log::debug!("Syncing and evaluating {}", path.display());
138149
fuzzer.evaluate_input(state, executor, manager, &input)?;
139150
}
@@ -161,6 +172,7 @@ where
161172

162173
impl<CB, E, EM, I, S, Z> SyncFromDiskStage<CB, E, EM, I, S, Z> {
163174
/// Creates a new [`SyncFromDiskStage`]
175+
/// To skip a file, you can return `Error::invalid_input` in `load_callback`
164176
#[must_use]
165177
pub fn new(sync_dirs: Vec<PathBuf>, load_callback: CB, interval: Duration, name: &str) -> Self {
166178
Self {

libafl_bolts/src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,8 @@ pub enum Error {
341341
InvalidCorpus(String, ErrorBacktrace),
342342
/// Error specific to a runtime like QEMU or Frida
343343
Runtime(String, ErrorBacktrace),
344+
/// The `Input` was invalid.
345+
InvalidInput(String, ErrorBacktrace),
344346
}
345347

346348
impl Error {
@@ -369,6 +371,15 @@ impl Error {
369371
Error::EmptyOptional(arg.into(), ErrorBacktrace::new())
370372
}
371373

374+
/// The `Input` was invalid
375+
#[must_use]
376+
pub fn invalid_input<S>(reason: S) -> Self
377+
where
378+
S: Into<String>,
379+
{
380+
Error::InvalidInput(reason.into(), ErrorBacktrace::new())
381+
}
382+
372383
/// Key not in Map
373384
#[must_use]
374385
pub fn key_not_found<S>(arg: S) -> Self
@@ -580,6 +591,10 @@ impl Display for Error {
580591
write!(f, "Runtime error: {0}", &s)?;
581592
display_error_backtrace(f, b)
582593
}
594+
Self::InvalidInput(s, b) => {
595+
write!(f, "Encountered an invalid input: {0}", &s)?;
596+
display_error_backtrace(f, b)
597+
}
583598
}
584599
}
585600
}

0 commit comments

Comments
 (0)