From 085db55f1967d7f815a9a38c1b53dd021d02f2c5 Mon Sep 17 00:00:00 2001 From: Aarnav Date: Mon, 23 Sep 2024 18:55:01 +0200 Subject: [PATCH] libafl-fuzz: fix not loading seeds recursively from directories (#2532) * libafl-fuzz: fix not loading seeds recursively from directories * add walk_initial_inputs to State * libafl-fuzz: add afl++ style seed filename when copying initial files * typo --- fuzzers/others/libafl-fuzz/src/corpus.rs | 30 ++------------------ fuzzers/others/libafl-fuzz/src/fuzzer.rs | 35 +++++++++++++++++++++++- fuzzers/others/libafl-fuzz/src/main.rs | 4 +-- libafl/src/state/mod.rs | 24 +++++++++++++++- 4 files changed, 61 insertions(+), 32 deletions(-) diff --git a/fuzzers/others/libafl-fuzz/src/corpus.rs b/fuzzers/others/libafl-fuzz/src/corpus.rs index ed2a9f82d2..0499f6d764 100644 --- a/fuzzers/others/libafl-fuzz/src/corpus.rs +++ b/fuzzers/others/libafl-fuzz/src/corpus.rs @@ -82,11 +82,7 @@ fn parse_time_line(line: &str) -> Result { .map_err(|_| Error::illegal_state("invalid stats file")) } -pub fn check_autoresume( - fuzzer_dir: &Path, - intial_inputs: &PathBuf, - auto_resume: bool, -) -> Result, Error> { +pub fn check_autoresume(fuzzer_dir: &Path, auto_resume: bool) -> Result, Error> { if !fuzzer_dir.exists() { std::fs::create_dir(fuzzer_dir)?; } @@ -129,11 +125,7 @@ pub fn check_autoresume( return Err(Error::illegal_state("The job output directory already exists and contains results! use AFL_AUTORESUME=true or provide \"-\" for -i ")); } } - if auto_resume { - // TODO: once the queue stuff is implemented finish the rest of the function - // see afl-fuzz-init.c line 1898 onwards. Gotta copy and delete shit - // No usable test cases in './output/default/_resume' - } else { + if !auto_resume { let queue_dir = fuzzer_dir.join("queue"); let hangs_dir = fuzzer_dir.join("hangs"); let crashes_dir = fuzzer_dir.join("crashes"); @@ -141,24 +133,6 @@ pub fn check_autoresume( create_dir_if_not_exists(&crashes_dir).expect("should be able to create crashes dir"); create_dir_if_not_exists(&hangs_dir).expect("should be able to create hangs dir"); create_dir_if_not_exists(&queue_dir).expect("should be able to create queue dir"); - // Copy all our seeds to queue - for file in std::fs::read_dir(intial_inputs)? { - let path = file?.path(); - let cpy_res = std::fs::copy( - &path, - queue_dir.join(path.file_name().ok_or(Error::illegal_state(format!( - "file {} in input directory does not have a filename", - path.display() - )))?), - ); - match cpy_res { - Err(e) if e.kind() == io::ErrorKind::InvalidInput => { - println!("skipping {} since it is not a regular file", path.display()); - } - Err(e) => return Err(e.into()), - Ok(_) => {} - } - } } Ok(file) } diff --git a/fuzzers/others/libafl-fuzz/src/fuzzer.rs b/fuzzers/others/libafl-fuzz/src/fuzzer.rs index 516ca4af11..0bacd76af5 100644 --- a/fuzzers/others/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/others/libafl-fuzz/src/fuzzer.rs @@ -250,6 +250,39 @@ where .build(tuple_list!(time_observer, edges_observer)) .unwrap(); + let queue_dir = fuzzer_dir.join("queue"); + if opt.auto_resume { + // TODO - see afl-fuzz-init.c line 1898 onwards + } else { + // If we aren't auto resuming, copy all the files to our queue directory. + let mut id = 0; + state.walk_initial_inputs(&[opt.input_dir.clone()], |path: &PathBuf| { + let mut filename = path + .file_name() + .ok_or(Error::illegal_state(format!( + "file {} in input directory does not have a filename", + path.display() + )))? + .to_str() + .ok_or(Error::illegal_state(format!( + "file {} in input directory does not have a legal filename", + path.display() + )))? + .to_string(); + filename = format!("id:{id:0>6},time:0,execs:0,orig:{filename}"); + let cpy_res = std::fs::copy(&path, queue_dir.join(filename)); + match cpy_res { + Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => { + println!("skipping {} since it is not a regular file", path.display()); + } + Err(e) => return Err(e.into()), + Ok(_) => { + id += 1; + } + } + Ok(()) + })?; + } // Load our seeds. if state.must_load_initial_inputs() { state @@ -257,7 +290,7 @@ where &mut fuzzer, &mut executor, &mut restarting_mgr, - &[fuzzer_dir.join("queue")], + &[queue_dir], &core_id, opt.cores.as_ref().expect("invariant; should never occur"), ) diff --git a/fuzzers/others/libafl-fuzz/src/main.rs b/fuzzers/others/libafl-fuzz/src/main.rs index a093c3062f..ab1a2deb56 100644 --- a/fuzzers/others/libafl-fuzz/src/main.rs +++ b/fuzzers/others/libafl-fuzz/src/main.rs @@ -77,7 +77,7 @@ fn main() { .main_run_client(|state: Option<_>, mgr: _, core_id: CoreId| { println!("run primary client on core {}", core_id.0); let fuzzer_dir = opt.output_dir.join("fuzzer_main"); - check_autoresume(&fuzzer_dir, &opt.input_dir, opt.auto_resume).unwrap(); + let _ = check_autoresume(&fuzzer_dir, opt.auto_resume).unwrap(); let res = run_client(state, mgr, &fuzzer_dir, core_id, &opt, true); let _ = remove_main_node_file(&fuzzer_dir); res @@ -87,7 +87,7 @@ fn main() { let fuzzer_dir = opt .output_dir .join(format!("fuzzer_secondary_{}", core_id.0)); - check_autoresume(&fuzzer_dir, &opt.input_dir, opt.auto_resume).unwrap(); + let _ = check_autoresume(&fuzzer_dir, opt.auto_resume).unwrap(); run_client(state, mgr, &fuzzer_dir, core_id, &opt, false) }) .cores(&opt.cores.clone().expect("invariant; should never occur")) diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index ce046b770d..97c3297134 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -260,7 +260,7 @@ pub struct StdState { /// Remaining initial inputs to load, if any remaining_initial_files: Option>, #[cfg(feature = "std")] - /// Remaining initial inputs to load, if any + /// symlinks we have already traversed when loading `remaining_initial_files` dont_reenter: Option>, #[cfg(feature = "std")] /// If inputs have been processed for multicore loading @@ -798,6 +798,28 @@ where Ok(()) } + /// Recursively walk supplied corpus directories + pub fn walk_initial_inputs( + &mut self, + in_dirs: &[PathBuf], + mut closure: F, + ) -> Result<(), Error> + where + F: FnMut(&PathBuf) -> Result<(), Error>, + { + self.canonicalize_input_dirs(in_dirs)?; + loop { + match self.next_file() { + Ok(path) => { + closure(&path)?; + } + Err(Error::IteratorEnd(_, _)) => break, + Err(e) => return Err(e), + } + } + self.reset_initial_files_state(); + Ok(()) + } /// Loads all intial inputs, even if they are not considered `interesting`. /// This is rarely the right method, use `load_initial_inputs`, /// and potentially fix your `Feedback`, instead.