From 0bd362cea9c9c0e53d97967c0c2c74d770d15e75 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 8 Oct 2024 14:12:23 +0200 Subject: [PATCH 01/67] libafl-fuzz: separate frida build --- fuzzers/forkserver/libafl-fuzz/Makefile.toml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/Makefile.toml b/fuzzers/forkserver/libafl-fuzz/Makefile.toml index 1c7401072d..92dc99372f 100644 --- a/fuzzers/forkserver/libafl-fuzz/Makefile.toml +++ b/fuzzers/forkserver/libafl-fuzz/Makefile.toml @@ -25,12 +25,16 @@ if [ ! -d "$AFL_DIR" ]; then cd ${AFL_DIR} git checkout ${AFL_VERSION} LLVM_CONFIG=${LLVM_CONFIG} make +fi +''' +[tasks.build_frida_mode] +script_runner = '@shell' +script = ''' + cd ${AFL_DIR} cd frida_mode LLVM_CONFIG=${LLVM_CONFIG} make cd ../.. -fi ''' - [tasks.build_qemuafl] script_runner = "@shell" script = ''' @@ -162,7 +166,7 @@ test -n "$RUNTIME" -a -n "$RUNTIME_PERSISTENT" && { unset AFL_FRIDA_PERSISTENT_ADDR ''' -dependencies = ["build_afl", "build_libafl_fuzz"] +dependencies = ["build_afl", "build_frida_mode", "build_libafl_fuzz"] [tasks.test_qemu] script_runner = "@shell" From a8cfc8461390eb362d000bdf95f8893fee6910e8 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 8 Oct 2024 14:14:46 +0200 Subject: [PATCH 02/67] cmplog debug --- fuzzers/forkserver/libafl-fuzz/Makefile.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fuzzers/forkserver/libafl-fuzz/Makefile.toml b/fuzzers/forkserver/libafl-fuzz/Makefile.toml index 92dc99372f..726d37b00d 100644 --- a/fuzzers/forkserver/libafl-fuzz/Makefile.toml +++ b/fuzzers/forkserver/libafl-fuzz/Makefile.toml @@ -114,6 +114,8 @@ script = ''' # cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog AFL_CORES=1 timeout 5 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true +ls -l ./output-cmplog/fuzzer_main/hangs +ls -l ./output-cmplog/fuzzer_main/crashes test -n "$( ls ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/hangs/id:0000* ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/crashes/id:0000*)" || { echo "No crashes found" exit 1 From 66245dcc88a99521f5eaae9244f4415c1f861f0f Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 8 Oct 2024 14:33:55 +0200 Subject: [PATCH 03/67] update --- fuzzers/forkserver/libafl-fuzz/Makefile.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/Makefile.toml b/fuzzers/forkserver/libafl-fuzz/Makefile.toml index 726d37b00d..1022f3d6ae 100644 --- a/fuzzers/forkserver/libafl-fuzz/Makefile.toml +++ b/fuzzers/forkserver/libafl-fuzz/Makefile.toml @@ -114,8 +114,8 @@ script = ''' # cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog AFL_CORES=1 timeout 5 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true -ls -l ./output-cmplog/fuzzer_main/hangs -ls -l ./output-cmplog/fuzzer_main/crashes +ls -l ./test/output-cmplog/fuzzer_main/hangs +ls -l ./test/output-cmplog/fuzzer_main/crashes test -n "$( ls ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/hangs/id:0000* ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/crashes/id:0000*)" || { echo "No crashes found" exit 1 From 8d3f5f69ca02712f7169003b1f61ba6f2b182e2c Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 8 Oct 2024 16:38:03 +0200 Subject: [PATCH 04/67] merge AflStatsStage move time_tracker stage to LibAFL --- fuzzers/forkserver/libafl-fuzz/.gitignore | 4 - .../forkserver/libafl-fuzz/src/afl_stats.rs | 665 -------------- .../forkserver/libafl-fuzz/src/env_parser.rs | 4 +- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 34 +- fuzzers/forkserver/libafl-fuzz/src/main.rs | 1 - libafl/src/stages/mod.rs | 4 +- libafl/src/stages/stats.rs | 849 +++++++++++++++--- .../src/stages/time_tracker.rs | 29 +- 8 files changed, 794 insertions(+), 796 deletions(-) delete mode 100644 fuzzers/forkserver/libafl-fuzz/.gitignore delete mode 100644 fuzzers/forkserver/libafl-fuzz/src/afl_stats.rs rename {fuzzers/forkserver/libafl-fuzz => libafl}/src/stages/time_tracker.rs (73%) diff --git a/fuzzers/forkserver/libafl-fuzz/.gitignore b/fuzzers/forkserver/libafl-fuzz/.gitignore deleted file mode 100644 index 0a452b5af1..0000000000 --- a/fuzzers/forkserver/libafl-fuzz/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -test/out-cmplog -test/out-instr -test/output-cmplog/ -test/output/ diff --git a/fuzzers/forkserver/libafl-fuzz/src/afl_stats.rs b/fuzzers/forkserver/libafl-fuzz/src/afl_stats.rs deleted file mode 100644 index 3001915d88..0000000000 --- a/fuzzers/forkserver/libafl-fuzz/src/afl_stats.rs +++ /dev/null @@ -1,665 +0,0 @@ -use core::{marker::PhantomData, time::Duration}; -use std::{ - borrow::Cow, - fmt::Display, - fs::{File, OpenOptions}, - io::{BufRead, BufReader, Write}, - path::{Path, PathBuf}, - process, -}; - -use libafl::{ - corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase}, - events::EventFirer, - executors::HasObservers, - inputs::UsesInput, - mutators::Tokens, - observers::MapObserver, - schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles}, - stages::{calibrate::UnstableEntriesMetadata, Stage}, - state::{HasCorpus, HasExecutions, HasImported, HasStartTime, Stoppable, UsesState}, - Error, HasMetadata, HasNamedMetadata, HasScheduler, SerdeAny, -}; -use libafl_bolts::{ - core_affinity::CoreId, - current_time, - os::peak_rss_mb_child_processes, - tuples::{Handle, Handled, MatchNameRef}, - Named, -}; -use serde::{Deserialize, Serialize}; - -use crate::{fuzzer::fuzzer_target_mode, Opt}; - -#[derive(Debug, SerdeAny, Serialize, Deserialize)] -pub struct CalibrationTime(pub Duration); -impl From for CalibrationTime { - fn from(value: Duration) -> Self { - Self(value) - } -} -#[derive(Debug, SerdeAny, Serialize, Deserialize)] -pub struct SyncTime(pub Duration); -impl From for SyncTime { - fn from(value: Duration) -> Self { - Self(value) - } -} - -#[derive(Debug, SerdeAny, Serialize, Deserialize)] -pub struct FuzzTime(pub Duration); -impl From for FuzzTime { - fn from(value: Duration) -> Self { - Self(value) - } -} - -/// The [`AflStatsStage`] is a Stage that calculates and writes -/// AFL++'s `fuzzer_stats` and `plot_data` information. -#[derive(Debug, Clone)] -pub struct AflStatsStage { - map_observer_handle: Handle, - fuzzer_dir: PathBuf, - start_time: u64, - // the number of testcases that have been fuzzed - has_fuzzed_size: usize, - // the number of "favored" testcases - is_favored_size: usize, - // the last time that we report all stats - last_report_time: Duration, - // the interval at which we report all stats - stats_report_interval: Duration, - pid: u32, - slowest_exec: Duration, - max_depth: u64, - cycles_done: u64, - saved_crashes: u64, - saved_hangs: u64, - last_find: Duration, - last_hang: Duration, - last_crash: Duration, - exec_timeout: u64, - execs_at_last_objective: u64, - cycles_wo_finds: u64, - /// banner text (e.g., the target name) - afl_banner: Cow<'static, str>, - /// the version of libafl-fuzz used - afl_version: Cow<'static, str>, - /// default, persistent, qemu, unicorn, non-instrumented - target_mode: Cow<'static, str>, - /// full command line used for the fuzzing session - command_line: Cow<'static, str>, - /// Amount of tokens provided by the user. Used to determine autotokens count. - provided_tokens: usize, - /// autotokens are enabled - autotokens_enabled: bool, - /// The core we are bound to - core_id: CoreId, - phantom: PhantomData<(C, O, E, EM, Z)>, -} - -#[derive(Debug, Clone)] -pub struct AFLFuzzerStats<'a> { - /// unix time indicating the start time of afl-fuzz - start_time: u64, - /// unix time corresponding to the last interval - last_update: u64, - /// run time in seconds to the last update of this file - run_time: u64, - /// process id of the fuzzer process - fuzzer_pid: u32, - /// queue cycles completed so far - cycles_done: u64, - /// number of queue cycles without any new paths found - cycles_wo_find: u64, - /// longest time in seconds no new path was found - time_wo_finds: u64, - /// Time spent fuzzing - fuzz_time: u64, - /// Time spent calibrating inputs - calibration_time: u64, - /// Time spent syncing with foreign fuzzers - /// NOTE: Syncing between our own instances is not counted. - sync_time: u64, - /// TODO - trim_time: u64, - /// number of fuzzer executions attempted (what does attempted mean here?) - execs_done: u64, - /// overall number of execs per second - execs_per_sec: u64, - /// TODO - execs_ps_last_min: u64, - /// total number of entries in the queue - corpus_count: usize, - /// number of queue entries that are favored - corpus_favored: usize, - /// number of entries discovered through local fuzzing - corpus_found: usize, - /// number of entries imported from other instances - corpus_imported: usize, - /// number of levels in the generated data set - max_depth: u64, - /// currently processed entry number - cur_item: usize, - /// number of favored entries still waiting to be fuzzed - pending_favs: usize, - /// number of all entries waiting to be fuzzed - pending_total: usize, - /// number of test cases showing variable behavior - corpus_variable: u64, - /// percentage of bitmap bytes that behave consistently - stability: f64, - /// percentage of edge coverage found in the map so far, - bitmap_cvg: f64, - /// number of unique crashes recorded - saved_crashes: u64, - /// number of unique hangs encountered - saved_hangs: u64, - /// seconds since the last find was found - last_find: Duration, - /// seconds since the last crash was found - last_crash: Duration, - /// seconds since the last hang was found - last_hang: Duration, - /// execs since the last crash was found - execs_since_crash: u64, - /// the -t command line value - exec_timeout: u64, - /// real time of the slowest execution in ms - slowest_exec_ms: u128, - /// max rss usage reached during fuzzing in MB - peak_rss_mb: i64, - /// TODO - cpu_affinity: usize, - /// how many edges have been found - edges_found: u64, - /// Size of our edges map - total_edges: u64, - /// how many edges are non-deterministic - var_byte_count: usize, - /// TODO: - havoc_expansion: usize, - /// Amount of automatic dict entries found - auto_dict_entries: usize, - /// TODO: - testcache_size: usize, - /// TODO: - testcache_count: usize, - /// TODO: - testcache_evict: usize, - /// banner text (e.g., the target name) - afl_banner: &'a Cow<'static, str>, - /// the version of AFL++ used - afl_version: &'a Cow<'static, str>, - /// default, persistent, qemu, unicorn, non-instrumented - target_mode: &'a Cow<'static, str>, - /// full command line used for the fuzzing session - command_line: &'a str, -} - -#[derive(Debug, Clone)] -pub struct AFLPlotData<'a> { - relative_time: &'a u64, - cycles_done: &'a u64, - cur_item: &'a usize, - corpus_count: &'a usize, - pending_total: &'a usize, - pending_favs: &'a usize, - /// Note: renamed `map_size` -> `total_edges` for consistency with `fuzzer_stats` - total_edges: &'a u64, - saved_crashes: &'a u64, - saved_hangs: &'a u64, - max_depth: &'a u64, - execs_per_sec: &'a u64, - /// Note: renamed `total_execs` -> `execs_done` for consistency with `fuzzer_stats` - execs_done: &'a u64, - edges_found: &'a u64, -} - -impl UsesState for AflStatsStage -where - E: UsesState, - EM: EventFirer, - Z: UsesState, -{ - type State = E::State; -} - -impl Stage for AflStatsStage -where - E: UsesState + HasObservers, - EM: EventFirer, - Z: UsesState + HasScheduler, - E::State: HasImported - + HasCorpus - + HasMetadata - + HasStartTime - + HasExecutions - + HasNamedMetadata - + Stoppable, - E::Observers: MatchNameRef, - O: MapObserver, - C: AsRef + Named, - ::Scheduler: HasQueueCycles, - <::State as HasCorpus>::Corpus: Corpus, -{ - fn perform( - &mut self, - fuzzer: &mut Z, - executor: &mut E, - state: &mut E::State, - _manager: &mut EM, - ) -> Result<(), Error> { - let Some(corpus_idx) = state.current_corpus_id()? else { - return Err(Error::illegal_state( - "state is not currently processing a corpus index", - )); - }; - let testcase = state.corpus().get(corpus_idx)?.borrow(); - // NOTE: scheduled_count represents the amount of fuzzing iterations a - // testcase has had. Since this stage is kept at the very end of stage list, - // the entry would have been fuzzed already (and should contain IsFavoredMetadata) but would have a scheduled count of zero - // since the scheduled count is incremented after all stages have been run. - if testcase.scheduled_count() == 0 { - // New testcase! - self.cycles_wo_finds = 0; - self.update_last_find(); - self.maybe_update_last_crash(&testcase, state); - self.maybe_update_last_hang(&testcase, state); - self.update_has_fuzzed_size(); - self.maybe_update_is_favored_size(&testcase); - } - self.maybe_update_slowest_exec(&testcase); - self.maybe_update_max_depth(&testcase)?; - - // See if we actually need to run the stage, if not, avoid dynamic value computation. - if !self.check_interval() { - return Ok(()); - } - - let corpus_size = state.corpus().count(); - let total_executions = *state.executions(); - - let scheduler = fuzzer.scheduler(); - let queue_cycles = scheduler.queue_cycles(); - self.maybe_update_cycles(queue_cycles); - self.maybe_update_cycles_wo_finds(queue_cycles); - - let observers = executor.observers(); - let map_observer = observers - .get(&self.map_observer_handle) - .ok_or_else(|| Error::key_not_found("invariant: MapObserver not found".to_string()))? - .as_ref(); - let filled_entries_in_map = map_observer.count_bytes(); - let map_size = map_observer.usable_count(); - // Since we do not calibrate when using `QueueScheduler`; we cannot calculate unstable entries. - let unstable_entries_in_map = state - .metadata_map() - .get::() - .map_or(0, |m| m.unstable_entries().len()); - - let auto_dict_entries = if self.autotokens_enabled { - state - .metadata::()? - .len() - .saturating_sub(self.provided_tokens) - } else { - 0 - }; - let stats = AFLFuzzerStats { - start_time: self.start_time, - last_update: self.last_report_time.as_secs(), - run_time: self.last_report_time.as_secs() - self.start_time, - fuzzer_pid: self.pid, - cycles_done: queue_cycles, - cycles_wo_find: self.cycles_wo_finds, - fuzz_time: state - .metadata::() - .map_or(Duration::from_secs(0), |d| d.0) - .as_secs(), - calibration_time: state - .metadata::() - .map_or(Duration::from_secs(0), |d| d.0) - .as_secs(), - sync_time: state - .metadata::() - .map_or(Duration::from_secs(0), |d| d.0) - .as_secs(), - trim_time: 0, // TODO - execs_done: total_executions, - execs_per_sec: *state.executions(), // TODO - execs_ps_last_min: *state.executions(), // TODO - max_depth: self.max_depth, - corpus_count: corpus_size, - corpus_favored: corpus_size - self.is_favored_size, - corpus_found: corpus_size - state.imported(), - corpus_imported: *state.imported(), - cur_item: corpus_idx.into(), - pending_total: corpus_size - self.has_fuzzed_size, - pending_favs: 0, // TODO - time_wo_finds: (current_time() - self.last_find).as_secs(), - corpus_variable: 0, - stability: self.calculate_stability(unstable_entries_in_map, filled_entries_in_map), - #[allow(clippy::cast_precision_loss)] - bitmap_cvg: (filled_entries_in_map as f64 / map_size as f64) * 100.0, - saved_crashes: self.saved_crashes, - saved_hangs: self.saved_hangs, - last_find: self.last_find, - last_hang: self.last_hang, - last_crash: self.last_crash, - execs_since_crash: total_executions - self.execs_at_last_objective, - exec_timeout: self.exec_timeout, - slowest_exec_ms: self.slowest_exec.as_millis(), - peak_rss_mb: peak_rss_mb_child_processes()?, - cpu_affinity: self.core_id.0, - total_edges: map_size as u64, - edges_found: filled_entries_in_map, - var_byte_count: unstable_entries_in_map, - havoc_expansion: 0, // TODO - auto_dict_entries, - testcache_size: 0, - testcache_count: 0, - testcache_evict: 0, - afl_banner: &self.afl_banner, - afl_version: &self.afl_version, - target_mode: &self.target_mode, - command_line: &self.command_line, - }; - let plot_data = AFLPlotData { - corpus_count: &stats.corpus_count, - cur_item: &stats.cur_item, - cycles_done: &stats.cycles_done, - edges_found: &stats.edges_found, - total_edges: &stats.total_edges, - execs_per_sec: &stats.execs_per_sec, - pending_total: &stats.pending_total, - pending_favs: &stats.pending_favs, - max_depth: &stats.max_depth, - relative_time: &stats.run_time, - saved_hangs: &stats.saved_hangs, - saved_crashes: &stats.saved_crashes, - execs_done: &stats.execs_done, - }; - self.write_fuzzer_stats(&stats)?; - self.write_plot_data(&plot_data)?; - Ok(()) - } - fn should_restart(&mut self, _state: &mut Self::State) -> Result { - Ok(true) - } - fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { - Ok(()) - } -} - -impl AflStatsStage -where - E: UsesState + HasObservers, - EM: EventFirer, - Z: UsesState, - E::State: HasImported + HasCorpus + HasMetadata + HasExecutions, - C: AsRef + Named, - O: MapObserver, -{ - /// create a new instance of the [`AflStatsStage`] - #[allow(clippy::too_many_arguments)] - #[must_use] - pub fn new( - opt: &Opt, - fuzzer_dir: PathBuf, - map_observer: &C, - provided_tokens: usize, - autotokens_enabled: bool, - core_id: CoreId, - ) -> Self { - Self::create_plot_data_file(&fuzzer_dir).unwrap(); - Self::create_fuzzer_stats_file(&fuzzer_dir).unwrap(); - Self { - map_observer_handle: map_observer.handle(), - start_time: current_time().as_secs(), - stats_report_interval: Duration::from_secs(opt.stats_interval), - has_fuzzed_size: 0, - is_favored_size: 0, - cycles_done: 0, - cycles_wo_finds: 0, - execs_at_last_objective: 0, - last_crash: current_time(), - last_find: current_time(), - last_hang: current_time(), - max_depth: 0, - saved_hangs: 0, - saved_crashes: 0, - slowest_exec: Duration::from_secs(0), - last_report_time: current_time(), - pid: process::id(), - exec_timeout: opt.hang_timeout, - target_mode: fuzzer_target_mode(opt), - afl_banner: Cow::Owned(opt.executable.display().to_string()), - afl_version: Cow::Borrowed("libafl-fuzz-0.0.1"), - command_line: get_run_cmdline(), - fuzzer_dir, - provided_tokens, - core_id, - autotokens_enabled, - phantom: PhantomData, - } - } - - fn create_plot_data_file(fuzzer_dir: &Path) -> Result<(), Error> { - let path = fuzzer_dir.join("plot_data"); - if path.exists() { - // check if it contains any data - let file = File::open(path)?; - if BufReader::new(file).lines().next().is_none() { - std::fs::write(fuzzer_dir.join("plot_data"), AFLPlotData::get_header())?; - } - } else { - std::fs::write(fuzzer_dir.join("plot_data"), AFLPlotData::get_header())?; - } - Ok(()) - } - - fn create_fuzzer_stats_file(fuzzer_dir: &Path) -> Result<(), Error> { - let path = fuzzer_dir.join("fuzzer_stats"); - if !path.exists() { - _ = OpenOptions::new().append(true).create(true).open(path)?; - } - Ok(()) - } - - fn write_fuzzer_stats(&self, stats: &AFLFuzzerStats) -> Result<(), Error> { - let tmp_file = self.fuzzer_dir.join(".fuzzer_stats_tmp"); - let stats_file = self.fuzzer_dir.join("fuzzer_stats"); - std::fs::write(&tmp_file, stats.to_string())?; - _ = std::fs::copy(&tmp_file, &stats_file)?; - std::fs::remove_file(tmp_file)?; - Ok(()) - } - - fn write_plot_data(&self, plot_data: &AFLPlotData) -> Result<(), Error> { - let plot_file = self.fuzzer_dir.join("plot_data"); - let mut file = OpenOptions::new().append(true).open(&plot_file)?; - writeln!(file, "{plot_data}")?; - Ok(()) - } - - fn maybe_update_is_favored_size( - &mut self, - testcase: &Testcase<<::State as UsesInput>::Input>, - ) { - if testcase.has_metadata::() { - self.is_favored_size += 1; - } - } - - fn maybe_update_slowest_exec( - &mut self, - testcase: &Testcase<<::State as UsesInput>::Input>, - ) { - if let Some(exec_time) = testcase.exec_time() { - if exec_time > &self.slowest_exec { - self.slowest_exec = *exec_time; - } - } - } - - fn update_has_fuzzed_size(&mut self) { - self.has_fuzzed_size += 1; - } - - fn maybe_update_max_depth( - &mut self, - testcase: &Testcase<<::State as UsesInput>::Input>, - ) -> Result<(), Error> { - if let Ok(metadata) = testcase.metadata::() { - if metadata.depth() > self.max_depth { - self.max_depth = metadata.depth(); - } - } else { - return Err(Error::illegal_state( - "testcase must have scheduler metdata?", - )); - } - Ok(()) - } - - fn update_last_find(&mut self) { - self.last_find = current_time(); - } - - fn maybe_update_last_crash( - &mut self, - testcase: &Testcase<<::State as UsesInput>::Input>, - state: &E::State, - ) { - if testcase - .hit_objectives() - .contains(&Cow::Borrowed("CrashFeedback")) - { - self.last_crash = current_time(); - self.execs_at_last_objective = *state.executions(); - } - } - - fn maybe_update_last_hang( - &mut self, - testcase: &Testcase<<::State as UsesInput>::Input>, - state: &E::State, - ) { - if testcase - .hit_objectives() - .contains(&Cow::Borrowed("TimeoutFeedback")) - { - self.last_hang = current_time(); - self.execs_at_last_objective = *state.executions(); - } - } - - fn check_interval(&mut self) -> bool { - let cur = current_time(); - if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval { - self.last_report_time = cur; - return true; - } - false - } - fn maybe_update_cycles(&mut self, queue_cycles: u64) { - if queue_cycles > self.cycles_done { - self.cycles_done += 1; - } - } - - fn maybe_update_cycles_wo_finds(&mut self, queue_cycles: u64) { - if queue_cycles > self.cycles_done && self.last_find < current_time() { - self.cycles_wo_finds += 1; - } - } - - #[allow(clippy::cast_precision_loss)] - #[allow(clippy::unused_self)] - fn calculate_stability(&self, unstable_entries: usize, filled_entries: u64) -> f64 { - ((filled_entries as f64 - unstable_entries as f64) / filled_entries as f64) * 100.0 - } -} - -impl Display for AFLPlotData<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{},", self.relative_time)?; - write!(f, "{},", self.cycles_done)?; - write!(f, "{},", self.cur_item)?; - write!(f, "{},", self.corpus_count)?; - write!(f, "{},", self.pending_total)?; - write!(f, "{},", self.pending_favs)?; - write!(f, "{},", self.total_edges)?; - write!(f, "{},", self.saved_crashes)?; - write!(f, "{},", self.saved_hangs)?; - write!(f, "{},", self.max_depth)?; - write!(f, "{},", self.execs_per_sec)?; - write!(f, "{},", self.execs_done)?; - write!(f, "{}", self.edges_found)?; - Ok(()) - } -} -impl AFLPlotData<'_> { - fn get_header() -> String { - "# relative_time, cycles_done, cur_item, corpus_count, pending_total, pending_favs, total_edges, saved_crashes, saved_hangs, max_depth, execs_per_sec, execs_done, edges_found".to_string() - } -} -impl Display for AFLFuzzerStats<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "start_time : {}", &self.start_time)?; - writeln!(f, "start_time : {}", &self.start_time)?; - writeln!(f, "last_update : {}", &self.last_update)?; - writeln!(f, "run_time : {}", &self.run_time)?; - writeln!(f, "fuzzer_pid : {}", &self.fuzzer_pid)?; - writeln!(f, "cycles_done : {}", &self.cycles_done)?; - writeln!(f, "cycles_wo_find : {}", &self.cycles_wo_find)?; - writeln!(f, "time_wo_finds : {}", &self.time_wo_finds)?; - writeln!(f, "fuzz_time : {}", &self.fuzz_time)?; - writeln!(f, "calibration_time : {}", &self.calibration_time)?; - writeln!(f, "sync_time : {}", &self.sync_time)?; - writeln!(f, "trim_time : {}", &self.trim_time)?; - writeln!(f, "execs_done : {}", &self.execs_done)?; - writeln!(f, "execs_per_sec : {}", &self.execs_per_sec)?; - writeln!(f, "execs_ps_last_min : {}", &self.execs_ps_last_min)?; - writeln!(f, "corpus_count : {}", &self.corpus_count)?; - writeln!(f, "corpus_favored : {}", &self.corpus_favored)?; - writeln!(f, "corpus_found : {}", &self.corpus_found)?; - writeln!(f, "corpus_imported : {}", &self.corpus_imported)?; - writeln!(f, "max_depth : {}", &self.max_depth)?; - writeln!(f, "cur_item : {}", &self.cur_item)?; - writeln!(f, "pending_favs : {}", &self.pending_favs)?; - writeln!(f, "pending_total : {}", &self.pending_total)?; - writeln!(f, "corpus_variable : {}", &self.corpus_variable)?; - writeln!(f, "stability : {:.2}%", &self.stability)?; - writeln!(f, "bitmap_cvg : {:.2}%", &self.bitmap_cvg)?; - writeln!(f, "saved_crashes : {}", &self.saved_crashes)?; - writeln!(f, "saved_hangs : {}", &self.saved_hangs)?; - writeln!(f, "last_find : {}", &self.last_find.as_secs())?; - writeln!(f, "last_crash : {}", &self.last_crash.as_secs())?; - writeln!(f, "last_hang : {}", &self.last_hang.as_secs())?; - writeln!(f, "execs_since_crash : {}", &self.execs_since_crash)?; - writeln!(f, "exec_timeout : {}", &self.exec_timeout)?; - writeln!(f, "slowest_exec_ms : {}", &self.slowest_exec_ms)?; - writeln!(f, "peak_rss_mb : {}", &self.peak_rss_mb)?; - writeln!(f, "cpu_affinity : {}", &self.cpu_affinity)?; - writeln!(f, "edges_found : {}", &self.edges_found)?; - writeln!(f, "total_edges : {}", &self.total_edges)?; - writeln!(f, "var_byte_count : {}", &self.var_byte_count)?; - writeln!(f, "havoc_expansion : {}", &self.havoc_expansion)?; - writeln!(f, "auto_dict_entries : {}", &self.auto_dict_entries)?; - writeln!(f, "testcache_size : {}", &self.testcache_size)?; - writeln!(f, "testcache_count : {}", &self.testcache_count)?; - writeln!(f, "testcache_evict : {}", &self.testcache_evict)?; - writeln!(f, "afl_banner : {}", self.afl_banner)?; - writeln!(f, "afl_version : {}", self.afl_version)?; - writeln!(f, "target_mode : {}", self.target_mode)?; - writeln!(f, "command_line : {}", self.command_line)?; - Ok(()) - } -} -/// Get the command used to invoke libafl-fuzz -pub fn get_run_cmdline() -> Cow<'static, str> { - let args: Vec = std::env::args().collect(); - Cow::Owned(args.join(" ")) -} diff --git a/fuzzers/forkserver/libafl-fuzz/src/env_parser.rs b/fuzzers/forkserver/libafl-fuzz/src/env_parser.rs index d1b2def508..8046835ad6 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/env_parser.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/env_parser.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, path::PathBuf, time::Duration}; -use libafl::Error; +use libafl::{stages::stats::AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS, Error}; use libafl_bolts::core_affinity::Cores; use crate::Opt; @@ -73,6 +73,8 @@ pub fn parse_envs(opt: &mut Opt) -> Result<(), Error> { } if let Ok(res) = std::env::var("AFL_FUZZER_STATS_UPDATE_INTERVAL") { opt.stats_interval = res.parse()?; + } else { + opt.stats_interval = AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS; } if let Ok(res) = std::env::var("AFL_BROKER_PORT") { opt.broker_port = Some(res.parse()?); diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 4fbf277480..4caa1d73c5 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -23,8 +23,11 @@ use libafl::{ IndexesLenTimeMinimizerScheduler, QueueScheduler, StdWeightedScheduler, }, stages::{ - mutational::MultiMutationalStage, CalibrationStage, ColorizationStage, IfStage, - StagesTuple, StdMutationalStage, StdPowerMutationalStage, SyncFromDiskStage, + time_tracker::TimeTrackingStageWrapper, + mutational::MultiMutationalStage, + stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}, + CalibrationStage, ColorizationStage, IfStage, StagesTuple, StdMutationalStage, + StdPowerMutationalStage, SyncFromDiskStage, }, state::{ HasCorpus, HasCurrentTestcase, HasExecutions, HasLastReportTime, HasStartTime, StdState, @@ -46,7 +49,6 @@ use libafl_targets::{cmps::AFLppCmpLogMap, AFLppCmpLogObserver, AFLppCmplogTraci use serde::{Deserialize, Serialize}; use crate::{ - afl_stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}, corpus::{set_corpus_filepath, set_solution_filepath}, env_parser::AFL_DEFAULT_MAP_SIZE, executor::find_afl_binary, @@ -55,7 +57,7 @@ use crate::{ seed::SeedFeedback, }, scheduler::SupportedSchedulers, - stages::{mutational_stage::SupportedMutationalStages, time_tracker::TimeTrackingStageWrapper}, + stages::{mutational_stage::SupportedMutationalStages}, Opt, AFL_DEFAULT_INPUT_LEN_MAX, AFL_DEFAULT_INPUT_LEN_MIN, AFL_HARNESS_FILE_INPUT, SHMEM_ENV_VAR, }; @@ -109,17 +111,21 @@ where let mut tokens = Tokens::new(); tokens = tokens.add_from_files(&opt.dicts)?; - let user_token_count = tokens.len(); - // Create a AFLStatsStage; - let afl_stats_stage = AflStatsStage::new( - opt, - fuzzer_dir.to_path_buf(), - &edges_observer, - user_token_count, - !opt.no_autodict, - core_id, - ); + let afl_stats_stage = AflStatsStage::builder() + .stats_file(fuzzer_dir.join("fuzzer_stats")) + .plot_file(fuzzer_dir.join("plot_data")) + .core_id(core_id) + .report_interval(Duration::from_secs(opt.stats_interval)) + .map_observer(&edges_observer) + .uses_autotokens(!opt.no_autodict) + .tokens(&tokens) + .banner(opt.executable.display().to_string()) + .version("0.13.2".to_string()) + .exec_timeout(opt.hang_timeout) + .target_mode(fuzzer_target_mode(opt).to_string()) + .build() + .expect("invariant; should never occur"); // Create an observation channel to keep track of the execution time. let time_observer = TimeObserver::new("time"); diff --git a/fuzzers/forkserver/libafl-fuzz/src/main.rs b/fuzzers/forkserver/libafl-fuzz/src/main.rs index 01b4b8c967..41e2c1326d 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/main.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/main.rs @@ -66,7 +66,6 @@ )] use std::{collections::HashMap, path::PathBuf, time::Duration}; -mod afl_stats; mod env_parser; mod feedback; mod scheduler; diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 3909528332..397301c710 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -29,9 +29,10 @@ use libafl_bolts::{ }; pub use logics::*; pub use mutational::{MutationalStage, StdMutationalStage}; +pub use stats::{AflStatsStage, SyncTime, FuzzTime, CalibrationTime}; pub use power::{PowerMutationalStage, StdPowerMutationalStage}; +pub use time_tracker::TimeTrackingStageWrapper; use serde::{Deserialize, Serialize}; -pub use stats::AflStatsStage; #[cfg(feature = "std")] pub use sync::*; pub use tmin::{ @@ -60,6 +61,7 @@ use crate::{ pub mod mutational; pub mod push; pub mod tmin; +pub mod time_tracker; pub mod calibrate; pub mod colorization; diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index fa3db5692d..af8f6fd6d3 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -1,166 +1,809 @@ -//! Stage to compute/report AFL stats - -#[cfg(feature = "std")] -use alloc::{borrow::Cow, string::ToString}; +//! Stage to compute and report AFL++ stats +use alloc::{string::String, vec::Vec}; use core::{marker::PhantomData, time::Duration}; +use std::{ + borrow::Cow, + fmt::Display, + fs::{File, OpenOptions}, + io::{BufRead, BufReader, Write}, + path::{Path, PathBuf}, + process, +}; -use libafl_bolts::current_time; -#[cfg(feature = "std")] -use serde_json::json; +use libafl_bolts::{ + core_affinity::CoreId, + current_time, + os::peak_rss_mb_child_processes, + tuples::{Handle, Handled, MatchNameRef}, + Named, +}; +use serde::{Deserialize, Serialize}; use crate::{ - corpus::{Corpus, HasCurrentCorpusId}, + corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase}, events::EventFirer, - schedulers::minimizer::IsFavoredMetadata, - stages::Stage, - state::{HasCorpus, HasImported, UsesState}, - Error, HasMetadata, -}; -#[cfg(feature = "std")] -use crate::{ - events::Event, - monitors::{AggregatorOps, UserStats, UserStatsValue}, + executors::HasObservers, + inputs::UsesInput, + mutators::Tokens, + observers::MapObserver, + schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles}, + stages::{calibrate::UnstableEntriesMetadata, Stage}, + state::{HasCorpus, HasExecutions, HasImported, HasStartTime, Stoppable, UsesState}, + std::string::ToString, + Error, HasMetadata, HasNamedMetadata, HasScheduler, SerdeAny, }; -/// The [`AflStatsStage`] is a simple stage that computes and reports some stats. +/// AFL++'s default stats update interval +pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60; + +/// CalibrationTime - Use in conjunction with `TimeTrackingFeedback` +#[derive(Debug, SerdeAny, Serialize, Deserialize)] +pub struct CalibrationTime(pub Duration); +impl From for CalibrationTime { + fn from(value: Duration) -> Self { + Self(value) + } +} + +/// SyncTime - Use in conjunction with `TimeTrackingFeedback` +#[derive(Debug, SerdeAny, Serialize, Deserialize)] +pub struct SyncTime(pub Duration); +impl From for SyncTime { + fn from(value: Duration) -> Self { + Self(value) + } +} + +/// FuzzTime - Use in conjunction with `TimeTrackingFeedback` +#[derive(Debug, SerdeAny, Serialize, Deserialize)] +pub struct FuzzTime(pub Duration); +impl From for FuzzTime { + fn from(value: Duration) -> Self { + Self(value) + } +} + +/// The [`AflStatsStage`] is a Stage that calculates and writes +/// AFL++'s `fuzzer_stats` and `plot_data` information. #[derive(Debug, Clone)] -pub struct AflStatsStage { +pub struct AflStatsStage { + map_observer_handle: Handle, + stats_file_path: PathBuf, + plot_file_path: Option, + start_time: u64, // the number of testcases that have been fuzzed has_fuzzed_size: usize, // the number of "favored" testcases is_favored_size: usize, - // the number of testcases found by itself - own_finds_size: usize, - // the number of testcases imported by other fuzzers - imported_size: usize, // the last time that we report all stats last_report_time: Duration, - // the interval that we report all stats + // the interval at which we report all stats stats_report_interval: Duration, + pid: u32, + slowest_exec: Duration, + max_depth: u64, + cycles_done: u64, + saved_crashes: u64, + saved_hangs: u64, + last_find: Duration, + last_hang: Duration, + last_crash: Duration, + exec_timeout: u64, + execs_at_last_objective: u64, + cycles_wo_finds: u64, + /// banner text (e.g., the target name) + afl_banner: Cow<'static, str>, + /// the version of libafl-fuzz used + afl_version: Cow<'static, str>, + /// default, persistent, qemu, unicorn, non-instrumented + target_mode: Cow<'static, str>, + /// full command line used for the fuzzing session + command_line: Cow<'static, str>, + /// Amount of tokens provided by the user. Used to determine autotokens count. + dict_count: usize, + /// autotokens are enabled + autotokens_enabled: bool, + /// The core we are bound to + core_id: CoreId, + phantom_data: PhantomData<(O, E, EM, Z)>, +} - phantom: PhantomData<(E, EM, Z)>, +/// AFL++'s fuzzer_stats +#[derive(Debug, Clone)] +pub struct AFLFuzzerStats<'a> { + /// unix time indicating the start time of afl-fuzz + start_time: u64, + /// unix time corresponding to the last interval + last_update: u64, + /// run time in seconds to the last update of this file + run_time: u64, + /// process id of the fuzzer process + fuzzer_pid: u32, + /// queue cycles completed so far + cycles_done: u64, + /// number of queue cycles without any new paths found + cycles_wo_find: u64, + /// longest time in seconds no new path was found + time_wo_finds: u64, + /// Time spent fuzzing + fuzz_time: u64, + /// Time spent calibrating inputs + calibration_time: u64, + /// Time spent syncing with foreign fuzzers + /// NOTE: Syncing between our own instances is not counted. + sync_time: u64, + /// TODO + trim_time: u64, + /// number of fuzzer executions attempted (what does attempted mean here?) + execs_done: u64, + /// overall number of execs per second + execs_per_sec: u64, + /// TODO + execs_ps_last_min: u64, + /// total number of entries in the queue + corpus_count: usize, + /// number of queue entries that are favored + corpus_favored: usize, + /// number of entries discovered through local fuzzing + corpus_found: usize, + /// number of entries imported from other instances + corpus_imported: usize, + /// number of levels in the generated data set + max_depth: u64, + /// currently processed entry number + cur_item: usize, + /// number of favored entries still waiting to be fuzzed + pending_favs: usize, + /// number of all entries waiting to be fuzzed + pending_total: usize, + /// number of test cases showing variable behavior + corpus_variable: u64, + /// percentage of bitmap bytes that behave consistently + stability: f64, + /// percentage of edge coverage found in the map so far, + bitmap_cvg: f64, + /// number of unique crashes recorded + saved_crashes: u64, + /// number of unique hangs encountered + saved_hangs: u64, + /// seconds since the last find was found + last_find: Duration, + /// seconds since the last crash was found + last_crash: Duration, + /// seconds since the last hang was found + last_hang: Duration, + /// execs since the last crash was found + execs_since_crash: u64, + /// the -t command line value + exec_timeout: u64, + /// real time of the slowest execution in ms + slowest_exec_ms: u128, + /// max rss usage reached during fuzzing in MB + peak_rss_mb: i64, + /// TODO + cpu_affinity: usize, + /// how many edges have been found + edges_found: u64, + /// Size of our edges map + total_edges: u64, + /// how many edges are non-deterministic + var_byte_count: usize, + /// TODO: + havoc_expansion: usize, + /// Amount of automatic dict entries found + auto_dict_entries: usize, + /// TODO: + testcache_size: usize, + /// TODO: + testcache_count: usize, + /// TODO: + testcache_evict: usize, + /// banner text (e.g., the target name) + afl_banner: &'a Cow<'static, str>, + /// the version of AFL++ used + afl_version: &'a Cow<'static, str>, + /// default, persistent, qemu, unicorn, non-instrumented + target_mode: &'a Cow<'static, str>, + /// full command line used for the fuzzing session + command_line: &'a str, +} +/// AFL++'s plot_data +#[derive(Debug, Clone)] +pub struct AFLPlotData<'a> { + relative_time: &'a u64, + cycles_done: &'a u64, + cur_item: &'a usize, + corpus_count: &'a usize, + pending_total: &'a usize, + pending_favs: &'a usize, + /// Note: renamed `map_size` -> `total_edges` for consistency with `fuzzer_stats` + total_edges: &'a u64, + saved_crashes: &'a u64, + saved_hangs: &'a u64, + max_depth: &'a u64, + execs_per_sec: &'a u64, + /// Note: renamed `total_execs` -> `execs_done` for consistency with `fuzzer_stats` + execs_done: &'a u64, + edges_found: &'a u64, } -impl UsesState for AflStatsStage +impl UsesState for AflStatsStage where E: UsesState, + EM: EventFirer, + Z: UsesState, { type State = E::State; } -impl Stage for AflStatsStage +impl Stage for AflStatsStage where - E: UsesState, - EM: EventFirer, - Z: UsesState, - Self::State: HasImported + HasCorpus + HasMetadata, + E: UsesState + HasObservers, + EM: EventFirer, + Z: UsesState + HasScheduler, + E::State: HasImported + + HasCorpus + + HasMetadata + + HasStartTime + + HasExecutions + + HasNamedMetadata + + Stoppable, + E::Observers: MatchNameRef, + O: MapObserver, + C: AsRef + Named, + ::Scheduler: HasQueueCycles, + <::State as HasCorpus>::Corpus: Corpus, { fn perform( &mut self, - _fuzzer: &mut Z, - _executor: &mut E, - state: &mut Self::State, + fuzzer: &mut Z, + executor: &mut E, + state: &mut E::State, _manager: &mut EM, ) -> Result<(), Error> { - let Some(corpus_id) = state.current_corpus_id()? else { + let Some(corpus_idx) = state.current_corpus_id()? else { return Err(Error::illegal_state( "state is not currently processing a corpus index", )); }; + let testcase = state.corpus().get(corpus_idx)?.borrow(); + // NOTE: scheduled_count represents the amount of fuzz runs a + // testcase has had. Since this stage is kept at the very end of stage list, + // the entry would have been fuzzed already (and should contain IsFavoredMetadata) but would have a scheduled count of zero + // since the scheduled count is incremented after all stages have been run. + if testcase.scheduled_count() == 0 { + // New testcase! + self.cycles_wo_finds = 0; + self.update_last_find(); + self.maybe_update_last_crash(&testcase, state); + self.maybe_update_last_hang(&testcase, state); + self.update_has_fuzzed_size(); + self.maybe_update_is_favored_size(&testcase); + } + self.maybe_update_slowest_exec(&testcase); + self.maybe_update_max_depth(&testcase)?; - // Report your stats every `STATS_REPORT_INTERVAL` - // compute pending, pending_favored, imported, own_finds - { - let testcase = state.corpus().get(corpus_id)?.borrow(); - if testcase.scheduled_count() == 0 { - self.has_fuzzed_size += 1; - if testcase.has_metadata::() { - self.is_favored_size += 1; - } - } else { - return Ok(()); - } + // See if we actually need to run the stage, if not, avoid dynamic value computation. + if !self.check_interval() { + return Ok(()); } let corpus_size = state.corpus().count(); - let pending_size = corpus_size - self.has_fuzzed_size; - let pend_favored_size = corpus_size - self.is_favored_size; - self.imported_size = *state.imported(); - self.own_finds_size = corpus_size - self.imported_size; + let total_executions = *state.executions(); - let cur = current_time(); + let scheduler = fuzzer.scheduler(); + let queue_cycles = scheduler.queue_cycles(); + self.maybe_update_cycles(queue_cycles); + self.maybe_update_cycles_wo_finds(queue_cycles); - if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval { - #[cfg(feature = "std")] - { - let json = json!({ - "pending":pending_size, - "pend_fav":pend_favored_size, - "own_finds":self.own_finds_size, - "imported":self.imported_size, - }); - _manager.fire( - state, - Event::UpdateUserStats { - name: Cow::from("AflStats"), - value: UserStats::new( - UserStatsValue::String(Cow::from(json.to_string())), - AggregatorOps::None, - ), - phantom: PhantomData, - }, - )?; - } - #[cfg(not(feature = "std"))] - log::info!( - "pending: {}, pend_favored: {}, own_finds: {}, imported: {}", - pending_size, - pend_favored_size, - self.own_finds_size, - self.imported_size - ); - self.last_report_time = cur; - } + let observers = executor.observers(); + let map_observer = observers + .get(&self.map_observer_handle) + .ok_or_else(|| Error::key_not_found("invariant: MapObserver not found".to_string()))? + .as_ref(); + let filled_entries_in_map = map_observer.count_bytes(); + let map_size = map_observer.usable_count(); + // Since we do not calibrate when using `QueueScheduler`; we cannot calculate unstable entries. + let unstable_entries_in_map = state + .metadata_map() + .get::() + .map_or(0, |m| m.unstable_entries().len()); + let auto_dict_entries = if self.autotokens_enabled { + state + .metadata::()? + .len() + .saturating_sub(self.dict_count) + } else { + 0 + }; + let stats = AFLFuzzerStats { + start_time: self.start_time, + last_update: self.last_report_time.as_secs(), + run_time: self.last_report_time.as_secs() - self.start_time, + fuzzer_pid: self.pid, + cycles_done: queue_cycles, + cycles_wo_find: self.cycles_wo_finds, + fuzz_time: state + .metadata::() + .map_or(Duration::from_secs(0), |d| d.0) + .as_secs(), + calibration_time: state + .metadata::() + .map_or(Duration::from_secs(0), |d| d.0) + .as_secs(), + sync_time: state + .metadata::() + .map_or(Duration::from_secs(0), |d| d.0) + .as_secs(), + trim_time: 0, // TODO + execs_done: total_executions, + execs_per_sec: *state.executions(), // TODO + execs_ps_last_min: *state.executions(), // TODO + max_depth: self.max_depth, + corpus_count: corpus_size, + corpus_favored: corpus_size - self.is_favored_size, + corpus_found: corpus_size - state.imported(), + corpus_imported: *state.imported(), + cur_item: corpus_idx.into(), + pending_total: corpus_size - self.has_fuzzed_size, + pending_favs: 0, // TODO + time_wo_finds: (current_time() - self.last_find).as_secs(), + corpus_variable: 0, + stability: self.calculate_stability(unstable_entries_in_map, filled_entries_in_map), + #[allow(clippy::cast_precision_loss)] + bitmap_cvg: (filled_entries_in_map as f64 / map_size as f64) * 100.0, + saved_crashes: self.saved_crashes, + saved_hangs: self.saved_hangs, + last_find: self.last_find, + last_hang: self.last_hang, + last_crash: self.last_crash, + execs_since_crash: total_executions - self.execs_at_last_objective, + exec_timeout: self.exec_timeout, + slowest_exec_ms: self.slowest_exec.as_millis(), + peak_rss_mb: peak_rss_mb_child_processes()?, + cpu_affinity: self.core_id.0, + total_edges: map_size as u64, + edges_found: filled_entries_in_map, + var_byte_count: unstable_entries_in_map, + havoc_expansion: 0, // TODO + auto_dict_entries, + testcache_size: 0, + testcache_count: 0, + testcache_evict: 0, + afl_banner: &self.afl_banner, + afl_version: &self.afl_version, + target_mode: &self.target_mode, + command_line: &self.command_line, + }; + let plot_data = AFLPlotData { + corpus_count: &stats.corpus_count, + cur_item: &stats.cur_item, + cycles_done: &stats.cycles_done, + edges_found: &stats.edges_found, + total_edges: &stats.total_edges, + execs_per_sec: &stats.execs_per_sec, + pending_total: &stats.pending_total, + pending_favs: &stats.pending_favs, + max_depth: &stats.max_depth, + relative_time: &stats.run_time, + saved_hangs: &stats.saved_hangs, + saved_crashes: &stats.saved_crashes, + execs_done: &stats.execs_done, + }; + println!("writing"); + self.write_fuzzer_stats(&stats)?; + if self.plot_file_path.is_some() { + self.write_plot_data(&plot_data)?; + } Ok(()) } - #[inline] fn should_restart(&mut self, _state: &mut Self::State) -> Result { - // Not running the target so we wont't crash/timeout and, hence, don't need to restore anything Ok(true) } - #[inline] fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { - // Not running the target so we wont't crash/timeout and, hence, don't need to restore anything Ok(()) } } -impl AflStatsStage { - /// create a new instance of the [`AflStatsStage`] - #[must_use] - pub fn new(interval: Duration) -> Self { - Self { - stats_report_interval: interval, - ..Default::default() +impl AflStatsStage +where + E: UsesState + HasObservers, + EM: EventFirer, + Z: UsesState, + E::State: HasImported + HasCorpus + HasMetadata + HasExecutions, + C: AsRef + Named, + O: MapObserver, +{ + /// Builder for `AflStatsStage` + pub fn builder() -> AflStatsStageBuilder { + AflStatsStageBuilder::new() + } + + fn write_fuzzer_stats(&self, stats: &AFLFuzzerStats) -> Result<(), Error> { + let tmp_file = self + .stats_file_path + .parent() + .expect("fuzzer_stats file must have a parent!") + .join(".fuzzer_stats_tmp"); + std::fs::write(&tmp_file, stats.to_string())?; + _ = std::fs::copy(&tmp_file, &self.stats_file_path)?; + std::fs::remove_file(tmp_file)?; + Ok(()) + } + + fn write_plot_data(&self, plot_data: &AFLPlotData) -> Result<(), Error> { + let mut file = OpenOptions::new().append(true).open( + &self + .plot_file_path + .as_ref() + .expect("invariant; should never occur"), + )?; + writeln!(file, "{plot_data}")?; + Ok(()) + } + + fn maybe_update_is_favored_size( + &mut self, + testcase: &Testcase<<::State as UsesInput>::Input>, + ) { + if testcase.has_metadata::() { + self.is_favored_size += 1; } } + + fn maybe_update_slowest_exec( + &mut self, + testcase: &Testcase<<::State as UsesInput>::Input>, + ) { + if let Some(exec_time) = testcase.exec_time() { + if exec_time > &self.slowest_exec { + self.slowest_exec = *exec_time; + } + } + } + + fn update_has_fuzzed_size(&mut self) { + self.has_fuzzed_size += 1; + } + + fn maybe_update_max_depth( + &mut self, + testcase: &Testcase<<::State as UsesInput>::Input>, + ) -> Result<(), Error> { + if let Ok(metadata) = testcase.metadata::() { + if metadata.depth() > self.max_depth { + self.max_depth = metadata.depth(); + } + } else { + return Err(Error::illegal_state( + "testcase must have scheduler metdata?", + )); + } + Ok(()) + } + + fn update_last_find(&mut self) { + self.last_find = current_time(); + } + + fn maybe_update_last_crash( + &mut self, + testcase: &Testcase<<::State as UsesInput>::Input>, + state: &E::State, + ) { + if testcase + .hit_objectives() + .contains(&Cow::Borrowed("CrashFeedback")) + { + self.last_crash = current_time(); + self.execs_at_last_objective = *state.executions(); + } + } + + fn maybe_update_last_hang( + &mut self, + testcase: &Testcase<<::State as UsesInput>::Input>, + state: &E::State, + ) { + if testcase + .hit_objectives() + .contains(&Cow::Borrowed("TimeoutFeedback")) + { + self.last_hang = current_time(); + self.execs_at_last_objective = *state.executions(); + } + } + + fn check_interval(&mut self) -> bool { + let cur = current_time(); + if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval { + self.last_report_time = cur; + return true; + } + false + } + fn maybe_update_cycles(&mut self, queue_cycles: u64) { + if queue_cycles > self.cycles_done { + self.cycles_done += 1; + } + } + + fn maybe_update_cycles_wo_finds(&mut self, queue_cycles: u64) { + if queue_cycles > self.cycles_done && self.last_find < current_time() { + self.cycles_wo_finds += 1; + } + } + + #[allow(clippy::cast_precision_loss)] + #[allow(clippy::unused_self)] + fn calculate_stability(&self, unstable_entries: usize, filled_entries: u64) -> f64 { + ((filled_entries as f64 - unstable_entries as f64) / filled_entries as f64) * 100.0 + } } -impl Default for AflStatsStage { - /// the default instance of the [`AflStatsStage`] - #[must_use] - fn default() -> Self { +impl Display for AFLPlotData<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{},", self.relative_time)?; + write!(f, "{},", self.cycles_done)?; + write!(f, "{},", self.cur_item)?; + write!(f, "{},", self.corpus_count)?; + write!(f, "{},", self.pending_total)?; + write!(f, "{},", self.pending_favs)?; + write!(f, "{},", self.total_edges)?; + write!(f, "{},", self.saved_crashes)?; + write!(f, "{},", self.saved_hangs)?; + write!(f, "{},", self.max_depth)?; + write!(f, "{},", self.execs_per_sec)?; + write!(f, "{},", self.execs_done)?; + write!(f, "{}", self.edges_found)?; + Ok(()) + } +} +impl AFLPlotData<'_> { + fn get_header() -> String { + "# relative_time, cycles_done, cur_item, corpus_count, pending_total, pending_favs, total_edges, saved_crashes, saved_hangs, max_depth, execs_per_sec, execs_done, edges_found".to_string() + } +} +impl Display for AFLFuzzerStats<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "start_time : {}", &self.start_time)?; + writeln!(f, "start_time : {}", &self.start_time)?; + writeln!(f, "last_update : {}", &self.last_update)?; + writeln!(f, "run_time : {}", &self.run_time)?; + writeln!(f, "fuzzer_pid : {}", &self.fuzzer_pid)?; + writeln!(f, "cycles_done : {}", &self.cycles_done)?; + writeln!(f, "cycles_wo_find : {}", &self.cycles_wo_find)?; + writeln!(f, "time_wo_finds : {}", &self.time_wo_finds)?; + writeln!(f, "fuzz_time : {}", &self.fuzz_time)?; + writeln!(f, "calibration_time : {}", &self.calibration_time)?; + writeln!(f, "sync_time : {}", &self.sync_time)?; + writeln!(f, "trim_time : {}", &self.trim_time)?; + writeln!(f, "execs_done : {}", &self.execs_done)?; + writeln!(f, "execs_per_sec : {}", &self.execs_per_sec)?; + writeln!(f, "execs_ps_last_min : {}", &self.execs_ps_last_min)?; + writeln!(f, "corpus_count : {}", &self.corpus_count)?; + writeln!(f, "corpus_favored : {}", &self.corpus_favored)?; + writeln!(f, "corpus_found : {}", &self.corpus_found)?; + writeln!(f, "corpus_imported : {}", &self.corpus_imported)?; + writeln!(f, "max_depth : {}", &self.max_depth)?; + writeln!(f, "cur_item : {}", &self.cur_item)?; + writeln!(f, "pending_favs : {}", &self.pending_favs)?; + writeln!(f, "pending_total : {}", &self.pending_total)?; + writeln!(f, "corpus_variable : {}", &self.corpus_variable)?; + writeln!(f, "stability : {:.2}%", &self.stability)?; + writeln!(f, "bitmap_cvg : {:.2}%", &self.bitmap_cvg)?; + writeln!(f, "saved_crashes : {}", &self.saved_crashes)?; + writeln!(f, "saved_hangs : {}", &self.saved_hangs)?; + writeln!(f, "last_find : {}", &self.last_find.as_secs())?; + writeln!(f, "last_crash : {}", &self.last_crash.as_secs())?; + writeln!(f, "last_hang : {}", &self.last_hang.as_secs())?; + writeln!(f, "execs_since_crash : {}", &self.execs_since_crash)?; + writeln!(f, "exec_timeout : {}", &self.exec_timeout)?; + writeln!(f, "slowest_exec_ms : {}", &self.slowest_exec_ms)?; + writeln!(f, "peak_rss_mb : {}", &self.peak_rss_mb)?; + writeln!(f, "cpu_affinity : {}", &self.cpu_affinity)?; + writeln!(f, "edges_found : {}", &self.edges_found)?; + writeln!(f, "total_edges : {}", &self.total_edges)?; + writeln!(f, "var_byte_count : {}", &self.var_byte_count)?; + writeln!(f, "havoc_expansion : {}", &self.havoc_expansion)?; + writeln!(f, "auto_dict_entries : {}", &self.auto_dict_entries)?; + writeln!(f, "testcache_size : {}", &self.testcache_size)?; + writeln!(f, "testcache_count : {}", &self.testcache_count)?; + writeln!(f, "testcache_evict : {}", &self.testcache_evict)?; + writeln!(f, "afl_banner : {}", self.afl_banner)?; + writeln!(f, "afl_version : {}", self.afl_version)?; + writeln!(f, "target_mode : {}", self.target_mode)?; + writeln!(f, "command_line : {}", self.command_line)?; + Ok(()) + } +} +/// Get the command used to invoke libafl-fuzz +pub fn get_run_cmdline() -> Cow<'static, str> { + let args: Vec = std::env::args().collect(); + Cow::Owned(args.join(" ")) +} + +/// The Builder for `AflStatsStage` +#[derive(Debug)] +pub struct AflStatsStageBuilder { + stats_file_path: Option, + plot_file_path: Option, + core_id: Option, + map_observer_handle: Option>, + uses_autotokens: bool, + report_interval: Duration, + dict_count: usize, + exec_timeout: u64, + banner: String, + version: String, + target_mode: String, + phantom_data: PhantomData<(O, E, EM, Z)>, +} + +impl AflStatsStageBuilder +where + E: UsesState + HasObservers, + EM: EventFirer, + Z: UsesState, + E::State: HasImported + HasCorpus + HasMetadata + HasExecutions, + C: AsRef + Named, + O: MapObserver, +{ + fn new() -> Self { Self { + report_interval: Duration::from_secs(AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS), + stats_file_path: None, + plot_file_path: None, + core_id: None, + map_observer_handle: None, + uses_autotokens: false, + dict_count: 0, + exec_timeout: 0, + banner: String::default(), + version: String::default(), + target_mode: String::default(), + phantom_data: PhantomData, + } + } + + /// The file path to which we will write the fuzzer stats + #[must_use] + pub fn stats_file(mut self, path: PathBuf) -> Self { + self.stats_file_path = Some(path); + self + } + /// The file path to which we will write the plot data + #[must_use] + pub fn plot_file(mut self, path: PathBuf) -> Self { + self.plot_file_path = Some(path); + self + } + /// The core we are bound to + #[must_use] + pub fn core_id(mut self, core_id: CoreId) -> Self { + self.core_id = Some(core_id); + self + } + /// The interval with which we report stats + #[must_use] + pub fn report_interval(mut self, interval: Duration) -> Self { + self.report_interval = interval; + self + } + /// Our `MapObserver` + #[must_use] + pub fn map_observer(mut self, map_observer: &C) -> Self { + self.map_observer_handle = Some(map_observer.handle()); + self + } + /// If we use autotokens provided by the target + #[must_use] + pub fn uses_autotokens(mut self, uses: bool) -> Self { + self.uses_autotokens = uses; + self + } + /// The tokens utilized by the fuzzer + #[must_use] + pub fn tokens(mut self, tokens: &Tokens) -> Self { + self.dict_count = tokens.len(); + self + } + /// AFL++ Banner (typically the target) + #[must_use] + pub fn banner(mut self, banner: String) -> Self { + self.banner = banner; + self + } + /// Version of the fuzzer + #[must_use] + pub fn version(mut self, version: String) -> Self { + self.version = version; + self + } + /// The "timeout" value used in `TimeoutFeedback` + #[must_use] + pub fn exec_timeout(mut self, timeout: u64) -> Self { + self.exec_timeout = timeout; + self + } + + /// + #[must_use] + pub fn target_mode(mut self, target_mode: String) -> Self { + self.target_mode = target_mode; + self + } + + fn create_plot_data_file(path: &Path) -> Result<(), Error> { + if path.exists() { + // check if it contains any data + let file = File::open(path)?; + if BufReader::new(file).lines().next().is_none() { + std::fs::write(path, AFLPlotData::get_header())?; + } + } else { + std::fs::write(path, AFLPlotData::get_header())?; + } + Ok(()) + } + + fn create_fuzzer_stats_file(path: &Path) -> Result<(), Error> { + if !path.exists() { + _ = OpenOptions::new().append(true).create(true).open(path)?; + } + Ok(()) + } + /// Build [`AflStatsStage`] + /// Will error if: + /// Cannot create the stats file + /// Cannot create the plot file (if provided) + /// No MapObserver supplied to the builder + /// No stats_file_path provieded + #[must_use] + pub fn build(self) -> Result, Error> { + if !self.stats_file_path.is_some() { + return Err(Error::illegal_argument("Must set `stats_file_path`")); + } + let stats_file_path = self.stats_file_path.unwrap(); + if !self.map_observer_handle.is_some() { + return Err(Error::illegal_argument("Must set `map_observer`")); + } + if let Some(ref plot_file) = self.plot_file_path { + Self::create_plot_data_file(&plot_file)?; + } + Self::create_fuzzer_stats_file(&stats_file_path)?; + Ok(AflStatsStage { + stats_file_path, + plot_file_path: self.plot_file_path, + map_observer_handle: self.map_observer_handle.unwrap(), + start_time: current_time().as_secs(), + stats_report_interval: self.report_interval, has_fuzzed_size: 0, is_favored_size: 0, - own_finds_size: 0, - imported_size: 0, + cycles_done: 0, + cycles_wo_finds: 0, + execs_at_last_objective: 0, + last_crash: current_time(), + last_find: current_time(), + last_hang: current_time(), + max_depth: 0, + saved_hangs: 0, + saved_crashes: 0, + slowest_exec: Duration::from_secs(0), last_report_time: current_time(), - stats_report_interval: Duration::from_secs(15), - phantom: PhantomData, - } + pid: process::id(), + exec_timeout: self.exec_timeout, + target_mode: Cow::Owned(self.target_mode), + afl_banner: Cow::Owned(self.banner), + afl_version: Cow::Owned(self.version), + command_line: get_run_cmdline(), + dict_count: self.dict_count, + core_id: self.core_id.unwrap_or(CoreId(0)), + autotokens_enabled: self.uses_autotokens, + phantom_data: PhantomData, + }) } } diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/time_tracker.rs b/libafl/src/stages/time_tracker.rs similarity index 73% rename from fuzzers/forkserver/libafl-fuzz/src/stages/time_tracker.rs rename to libafl/src/stages/time_tracker.rs index b053b30979..79389923c5 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/time_tracker.rs +++ b/libafl/src/stages/time_tracker.rs @@ -1,13 +1,27 @@ +//! Stage that wraps another stage and tracks it's execution time in `State` use std::{marker::PhantomData, time::Duration}; -use libafl::{ +use libafl_bolts::{current_time, Error}; + +use crate::{ inputs::UsesInput, stages::Stage, state::{State, UsesState}, HasMetadata, }; -use libafl_bolts::current_time; - +/// Track an inner Stage's execution time +/// ``` +///#[derive(Debug, SerdeAny, Serialize, Deserialize)] +///pub struct FuzzTime(pub Duration); +///impl From for FuzzTime { +/// fn from(value: Duration) -> Self { +/// Self(value) +/// } +///} +/// TimeTrackingStageWrapper::::new(my_fuzz_stage); +/// state.metadata::(); +/// ``` +#[derive(Debug)] pub struct TimeTrackingStageWrapper { inner: ST, count: Duration, @@ -15,6 +29,7 @@ pub struct TimeTrackingStageWrapper { } impl TimeTrackingStageWrapper { + /// Create a `TimeTrackingStageWrapper` pub fn new(inner: ST) -> Self { Self { inner, @@ -46,7 +61,7 @@ where executor: &mut E, state: &mut Self::State, manager: &mut M, - ) -> Result<(), libafl::prelude::Error> { + ) -> Result<(), Error> { let before_run = current_time(); self.inner.perform(fuzzer, executor, state, manager)?; let after_run = current_time(); @@ -55,11 +70,11 @@ where Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut Self::State) -> Result { self.inner.should_restart(state) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), libafl::prelude::Error> { + fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { self.inner.clear_progress(state) } @@ -69,7 +84,7 @@ where executor: &mut E, state: &mut Self::State, manager: &mut M, - ) -> Result<(), libafl::prelude::Error> { + ) -> Result<(), Error> { self.inner .perform_restartable(fuzzer, executor, state, manager) } From 9d1a98de7312506b468587be6a536630f8f7e893 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 8 Oct 2024 16:46:59 +0200 Subject: [PATCH 05/67] mandate track_hit_feedbacks feature for AflStatsStage --- libafl/src/stages/mod.rs | 6 ++++-- libafl/src/stages/stats.rs | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 397301c710..b089561ab5 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -29,7 +29,6 @@ use libafl_bolts::{ }; pub use logics::*; pub use mutational::{MutationalStage, StdMutationalStage}; -pub use stats::{AflStatsStage, SyncTime, FuzzTime, CalibrationTime}; pub use power::{PowerMutationalStage, StdPowerMutationalStage}; pub use time_tracker::TimeTrackingStageWrapper; use serde::{Deserialize, Serialize}; @@ -73,13 +72,16 @@ pub mod generalization; pub mod generation; pub mod logics; pub mod power; -pub mod stats; #[cfg(feature = "std")] pub mod sync; pub mod tracing; pub mod tuneable; #[cfg(feature = "unicode")] pub mod unicode; +#[cfg(all(feature = "std", feature ="track_hit_feedbacks"))] +pub use stats::{AflStatsStage, SyncTime, FuzzTime, CalibrationTime}; +#[cfg(all(feature = "std", feature ="track_hit_feedbacks"))] +pub mod stats; /// A stage is one step in the fuzzing process. /// Multiple stages will be scheduled one by one for each input. diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index af8f6fd6d3..6d9001e8b0 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -361,7 +361,10 @@ where execs_since_crash: total_executions - self.execs_at_last_objective, exec_timeout: self.exec_timeout, slowest_exec_ms: self.slowest_exec.as_millis(), + #[cfg(unix)] peak_rss_mb: peak_rss_mb_child_processes()?, + #[cfg(not(unix))] + peak_rss_mb: 0, // TODO for Windows cpu_affinity: self.core_id.0, total_edges: map_size as u64, edges_found: filled_entries_in_map, From 012ab10f89431168e122bd78ce529051bb24625e Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 8 Oct 2024 16:52:35 +0200 Subject: [PATCH 06/67] afl_stats do not hardcode TimeoutFeedback and CrashFeedback names --- libafl/src/feedbacks/mod.rs | 9 ++++++--- libafl/src/stages/stats.rs | 5 +++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 4659948cff..57347e6df8 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -776,25 +776,28 @@ pub trait ExitKindLogic { /// Check whether the provided [`ExitKind`] is actually interesting fn check_exit_kind(kind: &ExitKind) -> Result; } - +/// Name used by `CrashFeedback` +pub const CRASH_FEEDBACK_NAME: &str = "CrashFeedback"; /// Logic which finds all [`ExitKind::Crash`] exits interesting #[derive(Debug, Copy, Clone)] pub struct CrashLogic; impl ExitKindLogic for CrashLogic { - const NAME: Cow<'static, str> = Cow::Borrowed("CrashFeedback"); + const NAME: Cow<'static, str> = Cow::Borrowed(CRASH_FEEDBACK_NAME); fn check_exit_kind(kind: &ExitKind) -> Result { Ok(matches!(kind, ExitKind::Crash)) } } +/// Name used by `TimeoutFeedback` +pub const TIMEOUT_FEEDBACK_NAME: &str = "TimeoutFeedback"; /// Logic which finds all [`ExitKind::Timeout`] exits interesting #[derive(Debug, Copy, Clone)] pub struct TimeoutLogic; impl ExitKindLogic for TimeoutLogic { - const NAME: Cow<'static, str> = Cow::Borrowed("TimeoutFeedback"); + const NAME: Cow<'static, str> = Cow::Borrowed(TIMEOUT_FEEDBACK_NAME); fn check_exit_kind(kind: &ExitKind) -> Result { Ok(matches!(kind, ExitKind::Timeout)) diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index 6d9001e8b0..0d6f8aad0e 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -25,6 +25,7 @@ use crate::{ executors::HasObservers, inputs::UsesInput, mutators::Tokens, + feedbacks::{TIMEOUT_FEEDBACK_NAME, CRASH_FEEDBACK_NAME}, observers::MapObserver, schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles}, stages::{calibrate::UnstableEntriesMetadata, Stage}, @@ -499,7 +500,7 @@ where ) { if testcase .hit_objectives() - .contains(&Cow::Borrowed("CrashFeedback")) + .contains(&Cow::Borrowed(CRASH_FEEDBACK_NAME)) { self.last_crash = current_time(); self.execs_at_last_objective = *state.executions(); @@ -513,7 +514,7 @@ where ) { if testcase .hit_objectives() - .contains(&Cow::Borrowed("TimeoutFeedback")) + .contains(&Cow::Borrowed(TIMEOUT_FEEDBACK_NAME)) { self.last_hang = current_time(); self.execs_at_last_objective = *state.executions(); From 6534c94953bd518dd1acc5ec703877b80b6daddd Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 8 Oct 2024 16:58:17 +0200 Subject: [PATCH 07/67] typo --- fuzzers/forkserver/libafl-fuzz/src/stages/mod.rs | 1 - libafl/src/stages/mod.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/mod.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/mod.rs index be980f514a..ee139c48c8 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/mod.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/mod.rs @@ -1,2 +1 @@ pub mod mutational_stage; -pub mod time_tracker; diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index b089561ab5..09b86e7bca 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -30,7 +30,6 @@ use libafl_bolts::{ pub use logics::*; pub use mutational::{MutationalStage, StdMutationalStage}; pub use power::{PowerMutationalStage, StdPowerMutationalStage}; -pub use time_tracker::TimeTrackingStageWrapper; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] pub use sync::*; @@ -39,6 +38,7 @@ pub use tmin::{ }; pub use tracing::{ShadowTracingStage, TracingStage}; pub use tuneable::*; +pub use time_tracker::TimeTrackingStageWrapper; use tuple_list::NonEmptyTuple; #[cfg(feature = "unicode")] pub use unicode::*; From 18f1feddd82bb9f8bd2c32ee551a9d76c667a1b0 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 8 Oct 2024 17:09:32 +0200 Subject: [PATCH 08/67] typo --- libafl/src/stages/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 09b86e7bca..3bbc3ca375 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -27,6 +27,7 @@ use libafl_bolts::{ tuples::{HasConstLen, IntoVec}, Named, }; +pub use time_tracker::TimeTrackingStageWrapper; pub use logics::*; pub use mutational::{MutationalStage, StdMutationalStage}; pub use power::{PowerMutationalStage, StdPowerMutationalStage}; @@ -38,7 +39,6 @@ pub use tmin::{ }; pub use tracing::{ShadowTracingStage, TracingStage}; pub use tuneable::*; -pub use time_tracker::TimeTrackingStageWrapper; use tuple_list::NonEmptyTuple; #[cfg(feature = "unicode")] pub use unicode::*; @@ -60,13 +60,13 @@ use crate::{ pub mod mutational; pub mod push; pub mod tmin; -pub mod time_tracker; pub mod calibrate; pub mod colorization; #[cfg(all(feature = "std", unix))] pub mod concolic; #[cfg(feature = "std")] +pub mod time_tracker; pub mod dump; pub mod generalization; pub mod generation; From 02f85d9da2d7fc55e504cb77e1c0249b92dba97a Mon Sep 17 00:00:00 2001 From: aarnav Date: Wed, 9 Oct 2024 10:04:53 +0200 Subject: [PATCH 09/67] fix generics order --- libafl/src/stages/stats.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index 0d6f8aad0e..d934d2f100 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -67,7 +67,7 @@ impl From for FuzzTime { /// The [`AflStatsStage`] is a Stage that calculates and writes /// AFL++'s `fuzzer_stats` and `plot_data` information. #[derive(Debug, Clone)] -pub struct AflStatsStage { +pub struct AflStatsStage { map_observer_handle: Handle, stats_file_path: PathBuf, plot_file_path: Option, @@ -228,7 +228,7 @@ pub struct AFLPlotData<'a> { edges_found: &'a u64, } -impl UsesState for AflStatsStage +impl UsesState for AflStatsStage where E: UsesState, EM: EventFirer, @@ -237,7 +237,7 @@ where type State = E::State; } -impl Stage for AflStatsStage +impl Stage for AflStatsStage where E: UsesState + HasObservers, EM: EventFirer, @@ -412,7 +412,7 @@ where } } -impl AflStatsStage +impl AflStatsStage where E: UsesState + HasObservers, EM: EventFirer, @@ -422,7 +422,7 @@ where O: MapObserver, { /// Builder for `AflStatsStage` - pub fn builder() -> AflStatsStageBuilder { + pub fn builder() -> AflStatsStageBuilder { AflStatsStageBuilder::new() } @@ -632,7 +632,7 @@ pub fn get_run_cmdline() -> Cow<'static, str> { /// The Builder for `AflStatsStage` #[derive(Debug)] -pub struct AflStatsStageBuilder { +pub struct AflStatsStageBuilder { stats_file_path: Option, plot_file_path: Option, core_id: Option, @@ -647,7 +647,7 @@ pub struct AflStatsStageBuilder { phantom_data: PhantomData<(O, E, EM, Z)>, } -impl AflStatsStageBuilder +impl AflStatsStageBuilder where E: UsesState + HasObservers, EM: EventFirer, @@ -767,7 +767,7 @@ where /// No MapObserver supplied to the builder /// No stats_file_path provieded #[must_use] - pub fn build(self) -> Result, Error> { + pub fn build(self) -> Result, Error> { if !self.stats_file_path.is_some() { return Err(Error::illegal_argument("Must set `stats_file_path`")); } From 6a358e8da0bcbc9203c07695ff67df603d94ccd7 Mon Sep 17 00:00:00 2001 From: aarnav Date: Wed, 9 Oct 2024 16:18:47 +0200 Subject: [PATCH 10/67] add verify timeouts stage --- .../src/feedback/capture_timeout.rs | 71 +++++++++++ .../libafl-fuzz/src/feedback/mod.rs | 1 + fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 27 +++-- .../forkserver/libafl-fuzz/src/stages/mod.rs | 1 + .../libafl-fuzz/src/stages/verify_timeouts.rs | 113 ++++++++++++++++++ libafl/src/inputs/bytes.rs | 2 +- libafl/src/stages/stats.rs | 36 ++---- 7 files changed, 216 insertions(+), 35 deletions(-) create mode 100644 fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs create mode 100644 fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs new file mode 100644 index 0000000000..c1940dfa5b --- /dev/null +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs @@ -0,0 +1,71 @@ +use std::{borrow::Cow, fmt::Debug}; + +use libafl::{ + corpus::Testcase, + executors::ExitKind, + feedbacks::{Feedback, StateInitializer}, + state::HasCorpus, + HasMetadata, +}; +use libafl_bolts::{Error, Named}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +use crate::stages::verify_timeouts::TimeoutsToVerify; + +#[derive(Serialize, Deserialize)] +pub struct CaptureTimeoutFeedback {} + +impl CaptureTimeoutFeedback { + /// Create a new [`CaptureTimeoutFeedback`]. + pub fn new() -> Self { + Self {} + } +} + +impl Named for CaptureTimeoutFeedback { + fn name(&self) -> &Cow<'static, str> { + static NAME: Cow<'static, str> = Cow::Borrowed("CaptureTimeoutFeedback"); + &NAME + } +} + +impl StateInitializer for CaptureTimeoutFeedback {} + +impl Feedback for CaptureTimeoutFeedback +where + S: HasCorpus + HasMetadata, + I: Debug + Serialize + DeserializeOwned + Default + 'static + Clone, +{ + #[allow(clippy::wrong_self_convention)] + #[inline] + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + input: &I, + _observers: &OT, + exit_kind: &ExitKind, + ) -> Result { + if matches!(exit_kind, ExitKind::Timeout) { + let timeouts = state.metadata_or_insert_with(|| TimeoutsToVerify::::new()); + timeouts.push(input.clone()); + } + Ok(false) + } + + fn append_metadata( + &mut self, + _state: &mut S, + _manager: &mut EM, + _observers: &OT, + _testcase: &mut Testcase, + ) -> Result<(), Error> { + Ok(()) + } + + #[cfg(feature = "track_hit_feedbacks")] + #[inline] + fn last_result(&self) -> Result { + Ok(false) + } +} diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/mod.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/mod.rs index 7b824a9269..75ba5951b0 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/feedback/mod.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/mod.rs @@ -1,3 +1,4 @@ +pub mod capture_timeout; pub mod filepath; pub mod persistent_record; pub mod seed; diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 4caa1d73c5..f947b8d941 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -13,7 +13,7 @@ use libafl::{ }, executors::forkserver::{ForkserverExecutor, ForkserverExecutorBuilder}, feedback_and, feedback_or, feedback_or_fast, - feedbacks::{ConstFeedback, CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, + feedbacks::{ConstFeedback, CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::StdFuzzer, inputs::BytesInput, mutators::{havoc_mutations, tokens_mutations, AFLppRedQueen, StdScheduledMutator, Tokens}, @@ -23,9 +23,9 @@ use libafl::{ IndexesLenTimeMinimizerScheduler, QueueScheduler, StdWeightedScheduler, }, stages::{ - time_tracker::TimeTrackingStageWrapper, mutational::MultiMutationalStage, stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}, + time_tracker::TimeTrackingStageWrapper, CalibrationStage, ColorizationStage, IfStage, StagesTuple, StdMutationalStage, StdPowerMutationalStage, SyncFromDiskStage, }, @@ -53,11 +53,11 @@ use crate::{ env_parser::AFL_DEFAULT_MAP_SIZE, executor::find_afl_binary, feedback::{ - filepath::CustomFilepathToTestcaseFeedback, persistent_record::PersitentRecordFeedback, - seed::SeedFeedback, + capture_timeout::CaptureTimeoutFeedback, filepath::CustomFilepathToTestcaseFeedback, + persistent_record::PersitentRecordFeedback, seed::SeedFeedback, }, scheduler::SupportedSchedulers, - stages::{mutational_stage::SupportedMutationalStages}, + stages::{mutational_stage::SupportedMutationalStages, verify_timeouts::VerifyTimeoutsStage}, Opt, AFL_DEFAULT_INPUT_LEN_MAX, AFL_DEFAULT_INPUT_LEN_MIN, AFL_HARNESS_FILE_INPUT, SHMEM_ENV_VAR, }; @@ -159,7 +159,7 @@ where CrashFeedback::new(), feedback_and!( ConstFeedback::new(!opt.ignore_timeouts), - TimeoutFeedback::new() + CaptureTimeoutFeedback::new() ) ), MaxMapFeedback::with_name("edges_objective", &edges_observer) @@ -221,6 +221,12 @@ where // Create our Fuzzer let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + // Like AFL++, we re-run timeouts to make sure that they aren't false positives + let timeout_verify_stage = IfStage::new( + |_, _, _, _| Ok(!opt.ignore_timeouts), + tuple_list!(VerifyTimeoutsStage::new(opt.hang_timeout, &time_observer)), + ); + // Set LD_PRELOAD (Linux) && DYLD_INSERT_LIBRARIES (OSX) for target. if let Some(preload_env) = &opt.afl_preload { std::env::set_var("LD_PRELOAD", preload_env); @@ -402,6 +408,7 @@ where calibration, cmplog, mutational_stage, + timeout_verify_stage, afl_stats_stage, sync_stage ); @@ -417,7 +424,13 @@ where )?; } else { // The order of the stages matter! - let mut stages = tuple_list!(calibration, mutational_stage, afl_stats_stage, sync_stage); + let mut stages = tuple_list!( + calibration, + mutational_stage, + timeout_verify_stage, + afl_stats_stage, + sync_stage + ); // Run our fuzzer; NO CmpLog run_fuzzer_with_stages( diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/mod.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/mod.rs index ee139c48c8..77952b0f92 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/mod.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/mod.rs @@ -1 +1,2 @@ pub mod mutational_stage; +pub mod verify_timeouts; diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs new file mode 100644 index 0000000000..389b53c986 --- /dev/null +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -0,0 +1,113 @@ +//! Stage that re-runs inputs deemed as timeouts with double the timeout to assert that they are +//! not false positives. AFL++ style +//! WARNING: DO NOT USE WITH INPROCESS EXECUTORS! +use std::{collections::VecDeque, fmt::Debug, marker::PhantomData}; + +use libafl::{ + inputs::UsesInput, + prelude::{BytesInput, Executor, ExitKind, HasObservers, ObserversTuple, TimeObserver}, + stages::Stage, + state::{HasCorpus, UsesState}, + HasMetadata, +}; +use libafl_bolts::{ + tuples::{Handle, Handled}, + Error, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +#[derive(Debug)] +pub struct VerifyTimeoutsStage { + time_observer_handle: Handle, + // Timeouts in milliseconds multiplied by two + doubled_timeout: u64, + // The handle to our time observer + phantom: PhantomData, +} + +impl VerifyTimeoutsStage { + /// Create a `VerifyTimeoutsStage` + pub fn new(configured_timeout: u64, time_observer: &TimeObserver) -> Self { + Self { + time_observer_handle: time_observer.handle(), + doubled_timeout: configured_timeout * 2, + phantom: PhantomData, + } + } +} + +impl UsesState for VerifyTimeoutsStage +where + E: UsesState, + ::State: HasMetadata + HasCorpus, +{ + type State = E::State; +} + +#[derive(Default, Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "I: for<'a> Deserialize<'a> + Serialize")] +pub struct TimeoutsToVerify { + inputs: VecDeque, +} + +libafl_bolts::impl_serdeany!( + TimeoutsToVerify, + +); + +impl TimeoutsToVerify { + pub fn new() -> Self { + Self { + inputs: VecDeque::new(), + } + } + pub fn push(&mut self, input: I) { + self.inputs.push_back(input); + } + pub fn pop(&mut self) -> Option { + self.inputs.pop_front() + } +} + +impl Stage for VerifyTimeoutsStage +where + E::Observers: ObserversTuple<::Input, ::State>, + E: Executor + HasObservers, + EM: UsesState, + Z: UsesState, + ::State: HasMetadata + HasCorpus, + E::Input: Debug + Serialize + DeserializeOwned + Default + 'static + Clone, +{ + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut Self::State, + manager: &mut EM, + ) -> Result<(), Error> { + let mut timeouts = state + .metadata_or_insert_with(TimeoutsToVerify::::new) + .clone(); + while let Some(input) = timeouts.pop() { + executor.observers_mut().pre_exec_all(state, &input)?; + let exit_kind = executor.run_target(fuzzer, state, manager, &input)?; + let observers = executor.observers(); + let observer = &observers[&self.time_observer_handle]; + let last_runtime = observer + .last_runtime() + .expect("invariant; we just ran an input - and thus we must have runtime"); + // u128 -> u64 truncation but we should be fine. + if matches!(exit_kind, ExitKind::Timeout) + && last_runtime.as_millis() > self.doubled_timeout.into() + {} + } + *state.metadata_mut::>().unwrap() = timeouts; + Ok(()) + } + fn should_restart(&mut self, _state: &mut Self::State) -> Result { + Ok(true) + } + + fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { + Ok(()) + } +} diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index 127e672953..d850389065 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -21,7 +21,7 @@ use crate::{ }; /// A bytes input is the basic input -#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(SerdeAny, Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash)] pub struct BytesInput { /// The raw input bytes pub(crate) bytes: Vec, diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index d934d2f100..304eb120ec 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -23,9 +23,8 @@ use crate::{ corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase}, events::EventFirer, executors::HasObservers, - inputs::UsesInput, + feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME}, mutators::Tokens, - feedbacks::{TIMEOUT_FEEDBACK_NAME, CRASH_FEEDBACK_NAME}, observers::MapObserver, schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles}, stages::{calibrate::UnstableEntriesMetadata, Stage}, @@ -228,7 +227,7 @@ pub struct AFLPlotData<'a> { edges_found: &'a u64, } -impl UsesState for AflStatsStage +impl UsesState for AflStatsStage where E: UsesState, EM: EventFirer, @@ -237,7 +236,7 @@ where type State = E::State; } -impl Stage for AflStatsStage +impl Stage for AflStatsStage where E: UsesState + HasObservers, EM: EventFirer, @@ -412,7 +411,7 @@ where } } -impl AflStatsStage +impl AflStatsStage where E: UsesState + HasObservers, EM: EventFirer, @@ -449,19 +448,13 @@ where Ok(()) } - fn maybe_update_is_favored_size( - &mut self, - testcase: &Testcase<<::State as UsesInput>::Input>, - ) { + fn maybe_update_is_favored_size(&mut self, testcase: &Testcase) { if testcase.has_metadata::() { self.is_favored_size += 1; } } - fn maybe_update_slowest_exec( - &mut self, - testcase: &Testcase<<::State as UsesInput>::Input>, - ) { + fn maybe_update_slowest_exec(&mut self, testcase: &Testcase) { if let Some(exec_time) = testcase.exec_time() { if exec_time > &self.slowest_exec { self.slowest_exec = *exec_time; @@ -473,10 +466,7 @@ where self.has_fuzzed_size += 1; } - fn maybe_update_max_depth( - &mut self, - testcase: &Testcase<<::State as UsesInput>::Input>, - ) -> Result<(), Error> { + fn maybe_update_max_depth(&mut self, testcase: &Testcase) -> Result<(), Error> { if let Ok(metadata) = testcase.metadata::() { if metadata.depth() > self.max_depth { self.max_depth = metadata.depth(); @@ -493,11 +483,7 @@ where self.last_find = current_time(); } - fn maybe_update_last_crash( - &mut self, - testcase: &Testcase<<::State as UsesInput>::Input>, - state: &E::State, - ) { + fn maybe_update_last_crash(&mut self, testcase: &Testcase, state: &E::State) { if testcase .hit_objectives() .contains(&Cow::Borrowed(CRASH_FEEDBACK_NAME)) @@ -507,11 +493,7 @@ where } } - fn maybe_update_last_hang( - &mut self, - testcase: &Testcase<<::State as UsesInput>::Input>, - state: &E::State, - ) { + fn maybe_update_last_hang(&mut self, testcase: &Testcase, state: &E::State) { if testcase .hit_objectives() .contains(&Cow::Borrowed(TIMEOUT_FEEDBACK_NAME)) From def8485af450ef5de96834a8ce2f6a52062aabed Mon Sep 17 00:00:00 2001 From: aarnav Date: Wed, 9 Oct 2024 17:55:32 +0200 Subject: [PATCH 11/67] libafl: introduce set_timeout func to dynamically set timeouts for executor libafl-fuzz: add verify_timeout stage --- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 7 +- .../libafl-fuzz/src/stages/verify_timeouts.rs | 18 +++-- libafl/src/executors/combined.rs | 6 +- libafl/src/executors/command.rs | 8 ++ libafl/src/executors/differential.rs | 4 + libafl/src/executors/forkserver.rs | 4 + libafl/src/executors/inprocess/inner.rs | 10 +++ libafl/src/executors/inprocess/mod.rs | 4 + libafl/src/executors/inprocess/stateful.rs | 3 + libafl/src/executors/inprocess_fork/inner.rs | 81 ++++++++++++------- libafl/src/executors/inprocess_fork/mod.rs | 3 + .../src/executors/inprocess_fork/stateful.rs | 3 + libafl/src/executors/mod.rs | 4 +- libafl/src/executors/shadow.rs | 6 +- libafl/src/executors/with_observers.rs | 5 +- 15 files changed, 124 insertions(+), 42 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index f947b8d941..33d6855bf7 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -221,10 +221,13 @@ where // Create our Fuzzer let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - // Like AFL++, we re-run timeouts to make sure that they aren't false positives + // Like AFL++ we re-run all timeouts with double the timeout to assert that they are not false positives let timeout_verify_stage = IfStage::new( |_, _, _, _| Ok(!opt.ignore_timeouts), - tuple_list!(VerifyTimeoutsStage::new(opt.hang_timeout, &time_observer)), + tuple_list!(VerifyTimeoutsStage::new( + Duration::from_millis(opt.hang_timeout), + &time_observer + )), ); // Set LD_PRELOAD (Linux) && DYLD_INSERT_LIBRARIES (OSX) for target. diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index 389b53c986..383dc7e43b 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -1,6 +1,6 @@ //! Stage that re-runs inputs deemed as timeouts with double the timeout to assert that they are //! not false positives. AFL++ style -//! WARNING: DO NOT USE WITH INPROCESS EXECUTORS! +use core::time::Duration; use std::{collections::VecDeque, fmt::Debug, marker::PhantomData}; use libafl::{ @@ -18,18 +18,19 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; #[derive(Debug)] pub struct VerifyTimeoutsStage { time_observer_handle: Handle, - // Timeouts in milliseconds multiplied by two - doubled_timeout: u64, + doubled_timeout: Duration, + original_timeout: Duration, // The handle to our time observer phantom: PhantomData, } impl VerifyTimeoutsStage { /// Create a `VerifyTimeoutsStage` - pub fn new(configured_timeout: u64, time_observer: &TimeObserver) -> Self { + pub fn new(configured_timeout: Duration, time_observer: &TimeObserver) -> Self { Self { time_observer_handle: time_observer.handle(), doubled_timeout: configured_timeout * 2, + original_timeout: configured_timeout, phantom: PhantomData, } } @@ -87,6 +88,7 @@ where let mut timeouts = state .metadata_or_insert_with(TimeoutsToVerify::::new) .clone(); + executor.set_timeout(self.doubled_timeout); while let Some(input) = timeouts.pop() { executor.observers_mut().pre_exec_all(state, &input)?; let exit_kind = executor.run_target(fuzzer, state, manager, &input)?; @@ -96,11 +98,11 @@ where .last_runtime() .expect("invariant; we just ran an input - and thus we must have runtime"); // u128 -> u64 truncation but we should be fine. - if matches!(exit_kind, ExitKind::Timeout) - && last_runtime.as_millis() > self.doubled_timeout.into() - {} + if matches!(exit_kind, ExitKind::Timeout) {} } - *state.metadata_mut::>().unwrap() = timeouts; + executor.set_timeout(self.original_timeout); + let res = state.metadata_mut::>().unwrap(); + *res = TimeoutsToVerify::::new(); Ok(()) } fn should_restart(&mut self, _state: &mut Self::State) -> Result { diff --git a/libafl/src/executors/combined.rs b/libafl/src/executors/combined.rs index 1468de1218..5446ce15d8 100644 --- a/libafl/src/executors/combined.rs +++ b/libafl/src/executors/combined.rs @@ -1,7 +1,7 @@ //! A `CombinedExecutor` wraps a primary executor and a secondary one //! In comparison to the [`crate::executors::DiffExecutor`] it does not run the secondary executor in `run_target`. -use core::fmt::Debug; +use core::{fmt::Debug, time::Duration}; use libafl_bolts::tuples::RefIndexable; @@ -58,6 +58,10 @@ where ) -> Result { self.primary.run_target(fuzzer, state, mgr, input) } + fn set_timeout(&mut self, timeout: Duration) { + self.primary.set_timeout(timeout); + self.secondary.set_timeout(timeout); + } } impl UsesState for CombinedExecutor diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 3529dfc43c..5f20d22069 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -151,6 +151,9 @@ where fn exec_timeout(&self) -> Duration { self.timeout } + fn exec_timeout_mut(&mut self) -> &mut Duration { + &mut self.timeout + } } /// A `CommandExecutor` is a wrapper around [`std::process::Command`] to execute a target as a child process. @@ -281,6 +284,9 @@ where } res } + fn set_timeout(&mut self, timeout: Duration) { + *self.configurer.exec_timeout_mut() = timeout + } } impl UsesState for CommandExecutor @@ -592,6 +598,8 @@ pub trait CommandConfigurator: Sized { /// Provides timeout duration for execution of the child process. fn exec_timeout(&self) -> Duration; + /// Set the timeout duration for execution of the child process. + fn exec_timeout_mut(&mut self) -> &mut Duration; /// Create an `Executor` from this `CommandConfigurator`. fn into_executor(self, observers: OT) -> CommandExecutor diff --git a/libafl/src/executors/differential.rs b/libafl/src/executors/differential.rs index c7c53ec47e..b3555d0fdf 100644 --- a/libafl/src/executors/differential.rs +++ b/libafl/src/executors/differential.rs @@ -118,6 +118,10 @@ where }) } } + fn set_timeout(&mut self, timeout: core::time::Duration) { + self.primary.set_timeout(timeout); + self.secondary.set_timeout(timeout); + } } /// Proxy the observers of the inner executors diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 645a3bacc0..43e2eb00b6 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -1466,6 +1466,10 @@ where Ok(exit_kind) } + + fn set_timeout(&mut self, timeout: Duration) { + self.timeout = TimeSpec::from_duration(timeout); + } } impl UsesState for ForkserverExecutor diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index a4f4202721..c080637328 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -95,6 +95,16 @@ where OT: ObserversTuple, S: State, { + pub fn set_timeout(&mut self, duration: Duration) { + #[cfg(all(feature = "std", windows))] + { + *self.hooks.0.millis_sec_mut() = timeout.as_millis() as i64; + } + #[cfg(not(windows))] { + self.hooks.0.timer_mut().exec_tmout = duration; + } + + } /// This function marks the boundary between the fuzzer and the target /// /// # Safety diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index 15e14486fa..6865f8163f 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -131,6 +131,10 @@ where self.inner.leave_target(fuzzer, state, mgr, input); Ok(ret) } + + fn set_timeout(&mut self, timeout: Duration) { + self.inner.set_timeout(timeout) + } } impl HasObservers for GenericInProcessExecutor diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs index 27bd145836..d72e567d30 100644 --- a/libafl/src/executors/inprocess/stateful.rs +++ b/libafl/src/executors/inprocess/stateful.rs @@ -121,6 +121,9 @@ where self.inner.leave_target(fuzzer, state, mgr, input); Ok(ret) } + fn set_timeout(&mut self, timeout: Duration) { + self.inner.set_timeout(timeout) + } } impl HasObservers for StatefulGenericInProcessExecutor diff --git a/libafl/src/executors/inprocess_fork/inner.rs b/libafl/src/executors/inprocess_fork/inner.rs index ec45b17132..7692533828 100644 --- a/libafl/src/executors/inprocess_fork/inner.rs +++ b/libafl/src/executors/inprocess_fork/inner.rs @@ -105,6 +105,17 @@ where EM: EventFirer + EventRestarter, Z: UsesState, { + pub fn set_timeout(&mut self, timeout: Duration) { + #[cfg(target_os = "linux")] + { + self.itimerspec = Self::parse_itimerspec(timeout); + } + #[cfg(not(target_os = "linux"))] + { + self.itimerval = Self::parse_itimerval(timeout); + } + } + pub(super) unsafe fn pre_run_target_child( &mut self, fuzzer: &mut Z, @@ -238,22 +249,8 @@ where // do nothing } - /// Creates a new [`GenericInProcessForkExecutorInner`] with custom hooks #[cfg(target_os = "linux")] - #[allow(clippy::too_many_arguments)] - pub fn with_hooks( - userhooks: HT, - observers: OT, - _fuzzer: &mut Z, - state: &mut S, - _event_mgr: &mut EM, - timeout: Duration, - shmem_provider: SP, - ) -> Result { - let default_hooks = InChildProcessHooks::new::()?; - let mut hooks = tuple_list!(default_hooks).merge(userhooks); - hooks.init_all::(state); - + fn parse_itimerspec(timeout: Duration) -> libc::itimerspec { let milli_sec = timeout.as_millis(); let it_value = libc::timespec { tv_sec: (milli_sec / 1000) as _, @@ -267,7 +264,42 @@ where it_interval, it_value, }; + itimerspec + } + #[cfg(not(target_os = "linux"))] + fn parse_itimerval(timeout: Duration) -> Itimerval { + let milli_sec = timeout.as_millis(); + let it_value = Timeval { + tv_sec: (milli_sec / 1000) as i64, + tv_usec: (milli_sec % 1000) as i64, + }; + let it_interval = Timeval { + tv_sec: 0, + tv_usec: 0, + }; + let itimerval = Itimerval { + it_interval, + it_value, + }; + } + + /// Creates a new [`GenericInProcessForkExecutorInner`] with custom hooks + #[cfg(target_os = "linux")] + #[allow(clippy::too_many_arguments)] + pub fn with_hooks( + userhooks: HT, + observers: OT, + _fuzzer: &mut Z, + state: &mut S, + _event_mgr: &mut EM, + timeout: Duration, + shmem_provider: SP, + ) -> Result { + let default_hooks = InChildProcessHooks::new::()?; + let mut hooks = tuple_list!(default_hooks).merge(userhooks); + hooks.init_all::(state); + let itimerspec = Self::parse_itimerspec(timeout); Ok(Self { shmem_provider, observers, @@ -277,6 +309,11 @@ where }) } + #[cfg(not(target_os = "linux"))] + pub fn set_timeout(&mut self, timeout: Duration) { + self.itimerspec = Self::parse_itimerspec(timeout); + } + /// Creates a new [`GenericInProcessForkExecutorInner`], non linux #[cfg(not(target_os = "linux"))] #[allow(clippy::too_many_arguments)] @@ -293,19 +330,7 @@ where let mut hooks = tuple_list!(default_hooks).merge(userhooks); hooks.init_all::(state); - let milli_sec = timeout.as_millis(); - let it_value = Timeval { - tv_sec: (milli_sec / 1000) as i64, - tv_usec: (milli_sec % 1000) as i64, - }; - let it_interval = Timeval { - tv_sec: 0, - tv_usec: 0, - }; - let itimerval = Itimerval { - it_interval, - it_value, - }; + let itimerval = Self::parse_itimmerval(timeout); Ok(Self { shmem_provider, diff --git a/libafl/src/executors/inprocess_fork/mod.rs b/libafl/src/executors/inprocess_fork/mod.rs index 77c561db21..dd34e3aae3 100644 --- a/libafl/src/executors/inprocess_fork/mod.rs +++ b/libafl/src/executors/inprocess_fork/mod.rs @@ -180,6 +180,9 @@ where } } } + fn set_timeout(&mut self, timeout: Duration) { + self.inner.set_timeout(timeout) + } } impl<'a, H, HT, OT, S, SP, EM, Z, OF> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> diff --git a/libafl/src/executors/inprocess_fork/stateful.rs b/libafl/src/executors/inprocess_fork/stateful.rs index 2bff436020..46f152512e 100644 --- a/libafl/src/executors/inprocess_fork/stateful.rs +++ b/libafl/src/executors/inprocess_fork/stateful.rs @@ -168,6 +168,9 @@ where } } } + fn set_timeout(&mut self, timeout: Duration) { + self.inner.set_timeout(timeout) + } } impl<'a, H, HT, OT, S, SP, ES, EM, Z, OF> diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 01366253d7..de7b150cd8 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -2,7 +2,7 @@ #[cfg(unix)] use alloc::vec::Vec; -use core::fmt::Debug; +use core::{fmt::Debug, time::Duration}; pub use combined::CombinedExecutor; #[cfg(all(feature = "std", any(unix, doc)))] @@ -142,6 +142,8 @@ where { WithObservers::new(self, observers) } + + fn set_timeout(&mut self, timeout: Duration); } /// The common signals we want to handle diff --git a/libafl/src/executors/shadow.rs b/libafl/src/executors/shadow.rs index 9982374362..e2715e686d 100644 --- a/libafl/src/executors/shadow.rs +++ b/libafl/src/executors/shadow.rs @@ -1,6 +1,6 @@ //! A `ShadowExecutor` wraps an executor to have shadow observer that will not be considered by the feedbacks and the manager -use core::fmt::{self, Debug, Formatter}; +use core::{fmt::{self, Debug, Formatter}, time::Duration}; use libafl_bolts::tuples::RefIndexable; @@ -75,6 +75,10 @@ where ) -> Result { self.executor.run_target(fuzzer, state, mgr, input) } + + fn set_timeout(&mut self, timeout: Duration) { + self.executor.set_timeout(timeout) + } } impl UsesState for ShadowExecutor diff --git a/libafl/src/executors/with_observers.rs b/libafl/src/executors/with_observers.rs index 7b161cbb97..b5e49e2e78 100644 --- a/libafl/src/executors/with_observers.rs +++ b/libafl/src/executors/with_observers.rs @@ -1,6 +1,6 @@ //! A wrapper for any [`Executor`] to make it implement [`HasObservers`] using a given [`ObserversTuple`]. -use core::fmt::Debug; +use core::{fmt::Debug, time::Duration}; use libafl_bolts::tuples::RefIndexable; @@ -34,6 +34,9 @@ where ) -> Result { self.executor.run_target(fuzzer, state, mgr, input) } + fn set_timeout(&mut self, timeout: Duration) { + self.executor.set_timeout(timeout) + } } impl UsesState for WithObservers From a3f203e18c75c764205d07c60ec93aef458c637e Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 10 Oct 2024 10:36:42 +0200 Subject: [PATCH 12/67] add missing set_timeout implementations --- libafl/src/executors/command.rs | 2 +- libafl/src/executors/inprocess/inner.rs | 5 ++-- libafl/src/executors/inprocess/mod.rs | 4 +-- libafl/src/executors/inprocess/stateful.rs | 2 +- libafl/src/executors/inprocess_fork/inner.rs | 10 +++---- libafl/src/executors/inprocess_fork/mod.rs | 2 +- .../src/executors/inprocess_fork/stateful.rs | 2 +- libafl/src/executors/mod.rs | 2 ++ libafl/src/executors/shadow.rs | 7 +++-- libafl/src/executors/with_observers.rs | 2 +- libafl/src/stages/mod.rs | 12 ++++---- libafl/src/stages/stats.rs | 30 +++++++++---------- libafl/src/stages/time_tracker.rs | 2 +- libafl_frida/src/executor.rs | 4 +++ libafl_nyx/src/executor.rs | 10 +++++++ libafl_qemu/src/executor.rs | 15 ++++++++++ libafl_targets/src/coverage.rs | 4 ++- libafl_tinyinst/src/executor.rs | 4 +++ 18 files changed, 79 insertions(+), 40 deletions(-) diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 5f20d22069..e4d0d98115 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -285,7 +285,7 @@ where res } fn set_timeout(&mut self, timeout: Duration) { - *self.configurer.exec_timeout_mut() = timeout + *self.configurer.exec_timeout_mut() = timeout; } } diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index c080637328..b06e4f0bfa 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -95,15 +95,16 @@ where OT: ObserversTuple, S: State, { + /// Set the threshold for timeout pub fn set_timeout(&mut self, duration: Duration) { #[cfg(all(feature = "std", windows))] { *self.hooks.0.millis_sec_mut() = timeout.as_millis() as i64; } - #[cfg(not(windows))] { + #[cfg(not(windows))] + { self.hooks.0.timer_mut().exec_tmout = duration; } - } /// This function marks the boundary between the fuzzer and the target /// diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index 6865f8163f..d74233b52b 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -132,9 +132,7 @@ where Ok(ret) } - fn set_timeout(&mut self, timeout: Duration) { - self.inner.set_timeout(timeout) - } + fn set_timeout(&mut self, _timeout: Duration) {} } impl HasObservers for GenericInProcessExecutor diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs index d72e567d30..556913c617 100644 --- a/libafl/src/executors/inprocess/stateful.rs +++ b/libafl/src/executors/inprocess/stateful.rs @@ -122,7 +122,7 @@ where Ok(ret) } fn set_timeout(&mut self, timeout: Duration) { - self.inner.set_timeout(timeout) + self.inner.set_timeout(timeout); } } diff --git a/libafl/src/executors/inprocess_fork/inner.rs b/libafl/src/executors/inprocess_fork/inner.rs index 7692533828..5788d0d019 100644 --- a/libafl/src/executors/inprocess_fork/inner.rs +++ b/libafl/src/executors/inprocess_fork/inner.rs @@ -105,6 +105,7 @@ where EM: EventFirer + EventRestarter, Z: UsesState, { + /// Set the thresold for timeout pub fn set_timeout(&mut self, timeout: Duration) { #[cfg(target_os = "linux")] { @@ -260,11 +261,10 @@ where tv_sec: 0, tv_nsec: 0, }; - let itimerspec = libc::itimerspec { + libc::itimerspec { it_interval, it_value, - }; - itimerspec + } } #[cfg(not(target_os = "linux"))] @@ -278,10 +278,10 @@ where tv_sec: 0, tv_usec: 0, }; - let itimerval = Itimerval { + Itimerval { it_interval, it_value, - }; + } } /// Creates a new [`GenericInProcessForkExecutorInner`] with custom hooks diff --git a/libafl/src/executors/inprocess_fork/mod.rs b/libafl/src/executors/inprocess_fork/mod.rs index dd34e3aae3..cac9473b3c 100644 --- a/libafl/src/executors/inprocess_fork/mod.rs +++ b/libafl/src/executors/inprocess_fork/mod.rs @@ -181,7 +181,7 @@ where } } fn set_timeout(&mut self, timeout: Duration) { - self.inner.set_timeout(timeout) + self.inner.set_timeout(timeout); } } diff --git a/libafl/src/executors/inprocess_fork/stateful.rs b/libafl/src/executors/inprocess_fork/stateful.rs index 46f152512e..5296c4f117 100644 --- a/libafl/src/executors/inprocess_fork/stateful.rs +++ b/libafl/src/executors/inprocess_fork/stateful.rs @@ -169,7 +169,7 @@ where } } fn set_timeout(&mut self, timeout: Duration) { - self.inner.set_timeout(timeout) + self.inner.set_timeout(timeout); } } diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index de7b150cd8..3527ee72d7 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -143,6 +143,7 @@ where WithObservers::new(self, observers) } + /// Set the threshold for timeout fn set_timeout(&mut self, timeout: Duration); } @@ -231,6 +232,7 @@ mod test { Ok(ExitKind::Ok) } } + fn set_timeout(&mut self, _timeout: core::time::Duration) {} } #[test] diff --git a/libafl/src/executors/shadow.rs b/libafl/src/executors/shadow.rs index e2715e686d..98ab654dbe 100644 --- a/libafl/src/executors/shadow.rs +++ b/libafl/src/executors/shadow.rs @@ -1,6 +1,9 @@ //! A `ShadowExecutor` wraps an executor to have shadow observer that will not be considered by the feedbacks and the manager -use core::{fmt::{self, Debug, Formatter}, time::Duration}; +use core::{ + fmt::{self, Debug, Formatter}, + time::Duration, +}; use libafl_bolts::tuples::RefIndexable; @@ -77,7 +80,7 @@ where } fn set_timeout(&mut self, timeout: Duration) { - self.executor.set_timeout(timeout) + self.executor.set_timeout(timeout); } } diff --git a/libafl/src/executors/with_observers.rs b/libafl/src/executors/with_observers.rs index b5e49e2e78..e115fa90fa 100644 --- a/libafl/src/executors/with_observers.rs +++ b/libafl/src/executors/with_observers.rs @@ -35,7 +35,7 @@ where self.executor.run_target(fuzzer, state, mgr, input) } fn set_timeout(&mut self, timeout: Duration) { - self.executor.set_timeout(timeout) + self.executor.set_timeout(timeout); } } diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 3bbc3ca375..4f43310a24 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -27,13 +27,13 @@ use libafl_bolts::{ tuples::{HasConstLen, IntoVec}, Named, }; -pub use time_tracker::TimeTrackingStageWrapper; pub use logics::*; pub use mutational::{MutationalStage, StdMutationalStage}; pub use power::{PowerMutationalStage, StdPowerMutationalStage}; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] pub use sync::*; +pub use time_tracker::TimeTrackingStageWrapper; pub use tmin::{ MapEqualityFactory, MapEqualityFeedback, StdTMinMutationalStage, TMinMutationalStage, }; @@ -65,8 +65,6 @@ pub mod calibrate; pub mod colorization; #[cfg(all(feature = "std", unix))] pub mod concolic; -#[cfg(feature = "std")] -pub mod time_tracker; pub mod dump; pub mod generalization; pub mod generation; @@ -74,13 +72,15 @@ pub mod logics; pub mod power; #[cfg(feature = "std")] pub mod sync; +#[cfg(feature = "std")] +pub mod time_tracker; pub mod tracing; pub mod tuneable; #[cfg(feature = "unicode")] pub mod unicode; -#[cfg(all(feature = "std", feature ="track_hit_feedbacks"))] -pub use stats::{AflStatsStage, SyncTime, FuzzTime, CalibrationTime}; -#[cfg(all(feature = "std", feature ="track_hit_feedbacks"))] +#[cfg(all(feature = "std", feature = "track_hit_feedbacks"))] +pub use stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}; +#[cfg(all(feature = "std", feature = "track_hit_feedbacks"))] pub mod stats; /// A stage is one step in the fuzzing process. diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index 304eb120ec..195fa4421b 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -36,7 +36,7 @@ use crate::{ /// AFL++'s default stats update interval pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60; -/// CalibrationTime - Use in conjunction with `TimeTrackingFeedback` +/// `CalibrationTime` - Use in conjunction with `TimeTrackingFeedback` #[derive(Debug, SerdeAny, Serialize, Deserialize)] pub struct CalibrationTime(pub Duration); impl From for CalibrationTime { @@ -45,7 +45,7 @@ impl From for CalibrationTime { } } -/// SyncTime - Use in conjunction with `TimeTrackingFeedback` +/// `SyncTime` - Use in conjunction with `TimeTrackingFeedback` #[derive(Debug, SerdeAny, Serialize, Deserialize)] pub struct SyncTime(pub Duration); impl From for SyncTime { @@ -54,7 +54,7 @@ impl From for SyncTime { } } -/// FuzzTime - Use in conjunction with `TimeTrackingFeedback` +/// `FuzzTime` - Use in conjunction with `TimeTrackingFeedback` #[derive(Debug, SerdeAny, Serialize, Deserialize)] pub struct FuzzTime(pub Duration); impl From for FuzzTime { @@ -108,7 +108,7 @@ pub struct AflStatsStage { phantom_data: PhantomData<(O, E, EM, Z)>, } -/// AFL++'s fuzzer_stats +/// AFL++'s `fuzzer_stats` #[derive(Debug, Clone)] pub struct AFLFuzzerStats<'a> { /// unix time indicating the start time of afl-fuzz @@ -207,7 +207,7 @@ pub struct AFLFuzzerStats<'a> { /// full command line used for the fuzzing session command_line: &'a str, } -/// AFL++'s plot_data +/// AFL++'s `plot_data` #[derive(Debug, Clone)] pub struct AFLPlotData<'a> { relative_time: &'a u64, @@ -421,6 +421,7 @@ where O: MapObserver, { /// Builder for `AflStatsStage` + #[must_use] pub fn builder() -> AflStatsStageBuilder { AflStatsStageBuilder::new() } @@ -439,8 +440,7 @@ where fn write_plot_data(&self, plot_data: &AFLPlotData) -> Result<(), Error> { let mut file = OpenOptions::new().append(true).open( - &self - .plot_file_path + self.plot_file_path .as_ref() .expect("invariant; should never occur"), )?; @@ -607,6 +607,7 @@ impl Display for AFLFuzzerStats<'_> { } } /// Get the command used to invoke libafl-fuzz +#[must_use] pub fn get_run_cmdline() -> Cow<'static, str> { let args: Vec = std::env::args().collect(); Cow::Owned(args.join(" ")) @@ -715,8 +716,8 @@ where self.exec_timeout = timeout; self } - - /// + /// Used in the UI (optional) + /// default, persistent, qemu, unicorn, non-instrumented etc #[must_use] pub fn target_mode(mut self, target_mode: String) -> Self { self.target_mode = target_mode; @@ -746,19 +747,18 @@ where /// Will error if: /// Cannot create the stats file /// Cannot create the plot file (if provided) - /// No MapObserver supplied to the builder - /// No stats_file_path provieded - #[must_use] + /// No `MapObserver` supplied to the builder + /// No `stats_file_path` provieded pub fn build(self) -> Result, Error> { - if !self.stats_file_path.is_some() { + if self.stats_file_path.is_none() { return Err(Error::illegal_argument("Must set `stats_file_path`")); } let stats_file_path = self.stats_file_path.unwrap(); - if !self.map_observer_handle.is_some() { + if self.map_observer_handle.is_none() { return Err(Error::illegal_argument("Must set `map_observer`")); } if let Some(ref plot_file) = self.plot_file_path { - Self::create_plot_data_file(&plot_file)?; + Self::create_plot_data_file(plot_file)?; } Self::create_fuzzer_stats_file(&stats_file_path)?; Ok(AflStatsStage { diff --git a/libafl/src/stages/time_tracker.rs b/libafl/src/stages/time_tracker.rs index 79389923c5..6b54ce7a5b 100644 --- a/libafl/src/stages/time_tracker.rs +++ b/libafl/src/stages/time_tracker.rs @@ -9,7 +9,7 @@ use crate::{ state::{State, UsesState}, HasMetadata, }; -/// Track an inner Stage's execution time +/// Track an inner Stage's execution time /// ``` ///#[derive(Debug, SerdeAny, Serialize, Deserialize)] ///pub struct FuzzTime(pub Duration); diff --git a/libafl_frida/src/executor.rs b/libafl_frida/src/executor.rs index 474d641e8b..00815e366f 100644 --- a/libafl_frida/src/executor.rs +++ b/libafl_frida/src/executor.rs @@ -118,6 +118,10 @@ where self.helper.post_exec(input)?; res } + + fn set_timeout(&mut self, timeout: std::time::Duration) { + as Executor>::set_timeout(&mut self.base, timeout); + } } impl UsesState for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S> diff --git a/libafl_nyx/src/executor.rs b/libafl_nyx/src/executor.rs index b3834068a2..0f843f57eb 100644 --- a/libafl_nyx/src/executor.rs +++ b/libafl_nyx/src/executor.rs @@ -127,6 +127,16 @@ where Ok(exit_kind) } + + fn set_timeout(&mut self, duration: std::time::Duration) { + assert!( + duration.as_secs() <= 255, + "cannot set a timeout of seconds greater than 255 (u8::MAX)" + ); + let timeout_secs = duration.as_secs() as u8; + let timeout_microsecs = duration.subsec_micros(); + self.helper.set_timeout(timeout_secs, timeout_microsecs); + } } impl NyxExecutor { diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 3eb7adb5ed..3dbd8a4824 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -245,6 +245,17 @@ where Ok(exit_kind) } + + fn set_timeout(&mut self, timeout: Duration) { + , + > as libafl::executors::Executor>::set_timeout(&mut self.inner, timeout); + } } impl UsesState for QemuExecutor<'_, CM, ED, ET, H, OT, S, SM> @@ -397,6 +408,10 @@ where ) -> Result { self.inner.run_target(fuzzer, state, mgr, input) } + + fn set_timeout(&mut self, timeout: Duration) { + self.inner.set_timeout(timeout); + } } #[cfg(feature = "fork")] diff --git a/libafl_targets/src/coverage.rs b/libafl_targets/src/coverage.rs index 464a684cea..beeb964e17 100644 --- a/libafl_targets/src/coverage.rs +++ b/libafl_targets/src/coverage.rs @@ -286,7 +286,9 @@ mod swap { impl Observer for DifferentialAFLMapSwapObserver<'_, '_> {} - impl DifferentialObserver for DifferentialAFLMapSwapObserver<'_, '_> { + impl DifferentialObserver + for DifferentialAFLMapSwapObserver<'_, '_> + { fn pre_observe_first(&mut self, _: &mut OTA) -> Result<(), Error> { let slice = self.first_map.as_slice_mut(); unsafe { diff --git a/libafl_tinyinst/src/executor.rs b/libafl_tinyinst/src/executor.rs index 6dca42c5ae..d94e721b68 100644 --- a/libafl_tinyinst/src/executor.rs +++ b/libafl_tinyinst/src/executor.rs @@ -100,6 +100,10 @@ where _ => Err(Error::unknown("Tinyinst RunResult is unknown".to_string())), } } + + fn set_timeout(&mut self, timeout: Duration) { + self.timeout = timeout; + } } /// Builder for `TinyInstExecutor` From eec550509932f7ff8e53b8735eb05536bbf56b35 Mon Sep 17 00:00:00 2001 From: aarnav Date: Fri, 11 Oct 2024 12:13:41 +0200 Subject: [PATCH 13/67] libafl-fuzz: move set_timeout and timeout from Executor to HasTimeout --- .../libafl-fuzz/src/stages/verify_timeouts.rs | 8 +- libafl/src/executors/combined.rs | 16 +++ libafl/src/executors/command.rs | 12 ++ libafl/src/executors/differential.rs | 16 +++ libafl/src/executors/forkserver.rs | 10 ++ libafl/src/executors/inprocess/inner.rs | 43 ++++++-- libafl/src/executors/inprocess/mod.rs | 18 ++- libafl/src/executors/inprocess/stateful.rs | 16 ++- libafl/src/executors/inprocess_fork/inner.rs | 104 ++++++++++-------- libafl/src/executors/inprocess_fork/mod.rs | 18 ++- .../src/executors/inprocess_fork/stateful.rs | 19 +++- libafl/src/executors/mod.rs | 8 +- libafl/src/executors/shadow.rs | 10 ++ libafl/src/executors/with_observers.rs | 5 +- libafl_frida/src/executor.rs | 15 ++- libafl_nyx/src/executor.rs | 19 ++-- libafl_nyx/src/helper.rs | 24 +++- libafl_qemu/src/executor.rs | 40 +++++-- libafl_tinyinst/src/executor.rs | 10 ++ 19 files changed, 318 insertions(+), 93 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index 383dc7e43b..17a1dfde63 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -5,7 +5,7 @@ use std::{collections::VecDeque, fmt::Debug, marker::PhantomData}; use libafl::{ inputs::UsesInput, - prelude::{BytesInput, Executor, ExitKind, HasObservers, ObserversTuple, TimeObserver}, + prelude::{BytesInput, HasTimeout, Executor, ExitKind, HasObservers, ObserversTuple, TimeObserver}, stages::Stage, state::{HasCorpus, UsesState}, HasMetadata, @@ -72,7 +72,7 @@ impl TimeoutsToVerify { impl Stage for VerifyTimeoutsStage where E::Observers: ObserversTuple<::Input, ::State>, - E: Executor + HasObservers, + E: Executor + HasObservers + HasTimeout, EM: UsesState, Z: UsesState, ::State: HasMetadata + HasCorpus, @@ -94,10 +94,6 @@ where let exit_kind = executor.run_target(fuzzer, state, manager, &input)?; let observers = executor.observers(); let observer = &observers[&self.time_observer_handle]; - let last_runtime = observer - .last_runtime() - .expect("invariant; we just ran an input - and thus we must have runtime"); - // u128 -> u64 truncation but we should be fine. if matches!(exit_kind, ExitKind::Timeout) {} } executor.set_timeout(self.original_timeout); diff --git a/libafl/src/executors/combined.rs b/libafl/src/executors/combined.rs index 5446ce15d8..e5e624e2fa 100644 --- a/libafl/src/executors/combined.rs +++ b/libafl/src/executors/combined.rs @@ -5,6 +5,7 @@ use core::{fmt::Debug, time::Duration}; use libafl_bolts::tuples::RefIndexable; +use super::HasTimeout; use crate::{ executors::{Executor, ExitKind, HasObservers}, state::{HasExecutions, UsesState}, @@ -58,10 +59,25 @@ where ) -> Result { self.primary.run_target(fuzzer, state, mgr, input) } +} + +impl HasTimeout for CombinedExecutor +where + A: HasTimeout, + B: HasTimeout, +{ fn set_timeout(&mut self, timeout: Duration) { self.primary.set_timeout(timeout); self.secondary.set_timeout(timeout); } + + fn timeout(&self) -> Duration { + assert!( + self.primary.timeout() == self.secondary.timeout(), + "Primary and Secondary Executors have different timeouts!" + ); + self.primary.timeout() + } } impl UsesState for CombinedExecutor diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index e4d0d98115..172a80f51f 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -23,6 +23,7 @@ use libafl_bolts::{ AsSlice, }; +use super::HasTimeout; #[cfg(all(feature = "std", unix))] use crate::executors::{Executor, ExitKind}; use crate::{ @@ -284,9 +285,20 @@ where } res } +} + +impl HasTimeout for CommandExecutor +where + S: State, + T: CommandConfigurator, +{ fn set_timeout(&mut self, timeout: Duration) { *self.configurer.exec_timeout_mut() = timeout; } + + fn timeout(&self) -> Duration { + self.configurer.exec_timeout() + } } impl UsesState for CommandExecutor diff --git a/libafl/src/executors/differential.rs b/libafl/src/executors/differential.rs index b3555d0fdf..db07cee62c 100644 --- a/libafl/src/executors/differential.rs +++ b/libafl/src/executors/differential.rs @@ -16,6 +16,7 @@ use libafl_bolts::{ }; use serde::{Deserialize, Serialize}; +use super::HasTimeout; use crate::{ corpus::Corpus, executors::{Executor, ExitKind, HasObservers}, @@ -118,10 +119,25 @@ where }) } } +} + +impl HasTimeout for DiffExecutor +where + A: HasTimeout, + B: HasTimeout, +{ fn set_timeout(&mut self, timeout: core::time::Duration) { self.primary.set_timeout(timeout); self.secondary.set_timeout(timeout); } + + fn timeout(&self) -> core::time::Duration { + assert!( + self.primary.timeout() == self.secondary.timeout(), + "Primary and Secondary Executors have different timeouts!" + ); + self.primary.timeout() + } } /// Proxy the observers of the inner executors diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 43e2eb00b6..983f36970e 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -36,6 +36,7 @@ use nix::{ unistd::Pid, }; +use super::HasTimeout; #[cfg(feature = "regex")] use crate::observers::{get_asan_runtime_flags_with_log_path, AsanBacktraceObserver}; use crate::{ @@ -1466,10 +1467,19 @@ where Ok(exit_kind) } +} +impl HasTimeout for ForkserverExecutor +where + SP: ShMemProvider, +{ fn set_timeout(&mut self, timeout: Duration) { self.timeout = TimeSpec::from_duration(timeout); } + + fn timeout(&self) -> Duration { + self.timeout.into() + } } impl UsesState for ForkserverExecutor diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index b06e4f0bfa..660c96e2fc 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -24,7 +24,7 @@ use crate::{ ExecutorHooksTuple, }, inprocess::HasInProcessHooks, - Executor, HasObservers, + Executor, HasObservers, HasTimeout as HasExecutorTimeout, }, feedbacks::Feedback, fuzzer::HasObjective, @@ -95,17 +95,6 @@ where OT: ObserversTuple, S: State, { - /// Set the threshold for timeout - pub fn set_timeout(&mut self, duration: Duration) { - #[cfg(all(feature = "std", windows))] - { - *self.hooks.0.millis_sec_mut() = timeout.as_millis() as i64; - } - #[cfg(not(windows))] - { - self.hooks.0.timer_mut().exec_tmout = duration; - } - } /// This function marks the boundary between the fuzzer and the target /// /// # Safety @@ -163,6 +152,36 @@ where } } +impl HasExecutorTimeout for GenericInProcessExecutorInner +where + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + /// Set the threshold for timeout + fn set_timeout(&mut self, duration: Duration) { + #[cfg(all(feature = "std", windows))] + { + *self.hooks.0.millis_sec_mut() = timeout.as_millis() as i64; + } + #[cfg(not(windows))] + { + self.hooks.0.timer_mut().exec_tmout = duration; + } + } + + fn timeout(&self) -> Duration { + #[cfg(all(feature = "std", windows))] + { + Duration::from_millis(self.hooks.0.millis_sec()) + } + #[cfg(not(windows))] + { + self.hooks.0.timer().exec_tmout + } + } +} + impl GenericInProcessExecutorInner where HT: ExecutorHooksTuple, diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index d74233b52b..6424141121 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -18,6 +18,7 @@ use core::{ use libafl_bolts::tuples::{tuple_list, RefIndexable}; +use super::HasTimeout; #[cfg(any(unix, feature = "std"))] use crate::executors::hooks::inprocess::GLOBAL_STATE; use crate::{ @@ -131,8 +132,23 @@ where self.inner.leave_target(fuzzer, state, mgr, input); Ok(ret) } +} - fn set_timeout(&mut self, _timeout: Duration) {} +impl HasTimeout for GenericInProcessExecutor +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + fn set_timeout(&mut self, timeout: Duration) { + self.inner.set_timeout(timeout); + } + + fn timeout(&self) -> Duration { + self.inner.timeout() + } } impl HasObservers for GenericInProcessExecutor diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs index 556913c617..3262b917a2 100644 --- a/libafl/src/executors/inprocess/stateful.rs +++ b/libafl/src/executors/inprocess/stateful.rs @@ -16,7 +16,7 @@ use crate::{ executors::{ hooks::{inprocess::InProcessHooks, ExecutorHooksTuple}, inprocess::{GenericInProcessExecutorInner, HasInProcessHooks}, - Executor, ExitKind, HasObservers, + Executor, ExitKind, HasObservers, HasTimeout, }, feedbacks::Feedback, fuzzer::HasObjective, @@ -121,9 +121,23 @@ where self.inner.leave_target(fuzzer, state, mgr, input); Ok(ret) } +} + +impl HasTimeout for StatefulGenericInProcessExecutor +where + H: FnMut(&mut ES, &mut S, &S::Input) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ fn set_timeout(&mut self, timeout: Duration) { self.inner.set_timeout(timeout); } + + fn timeout(&self) -> Duration { + self.inner.timeout() + } } impl HasObservers for StatefulGenericInProcessExecutor diff --git a/libafl/src/executors/inprocess_fork/inner.rs b/libafl/src/executors/inprocess_fork/inner.rs index 5788d0d019..5b4451f856 100644 --- a/libafl/src/executors/inprocess_fork/inner.rs +++ b/libafl/src/executors/inprocess_fork/inner.rs @@ -26,7 +26,7 @@ use crate::{ inprocess_fork::{InChildProcessHooks, FORK_EXECUTOR_GLOBAL_DATA}, ExecutorHooksTuple, }, - ExitKind, HasObservers, + ExitKind, HasObservers, HasTimeout, }, inputs::UsesInput, observers::ObserversTuple, @@ -96,27 +96,75 @@ where type State = S; } -impl GenericInProcessForkExecutorInner +impl HasTimeout for GenericInProcessForkExecutorInner where - OT: ObserversTuple + Debug, - S: State + UsesInput, + OT: ObserversTuple, + S: UsesInput, SP: ShMemProvider, HT: ExecutorHooksTuple, - EM: EventFirer + EventRestarter, + EM: UsesState, Z: UsesState, { /// Set the thresold for timeout - pub fn set_timeout(&mut self, timeout: Duration) { + fn set_timeout(&mut self, timeout: Duration) { #[cfg(target_os = "linux")] { - self.itimerspec = Self::parse_itimerspec(timeout); + self.itimerspec = parse_itimerspec(timeout); } #[cfg(not(target_os = "linux"))] { - self.itimerval = Self::parse_itimerval(timeout); + self.itimerval = parse_itimerval(timeout); } } + fn timeout(&self) -> Duration { + todo!() + } +} + +#[cfg(target_os = "linux")] +fn parse_itimerspec(timeout: Duration) -> libc::itimerspec { + let milli_sec = timeout.as_millis(); + let it_value = libc::timespec { + tv_sec: (milli_sec / 1000) as _, + tv_nsec: ((milli_sec % 1000) * 1000 * 1000) as _, + }; + let it_interval = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + libc::itimerspec { + it_interval, + it_value, + } +} + +#[cfg(not(target_os = "linux"))] +fn parse_itimerval(timeout: Duration) -> Itimerval { + let milli_sec = timeout.as_millis(); + let it_value = Timeval { + tv_sec: (milli_sec / 1000) as i64, + tv_usec: (milli_sec % 1000) as i64, + }; + let it_interval = Timeval { + tv_sec: 0, + tv_usec: 0, + }; + Itimerval { + it_interval, + it_value, + } +} + +impl GenericInProcessForkExecutorInner +where + OT: ObserversTuple + Debug, + S: State + UsesInput, + SP: ShMemProvider, + HT: ExecutorHooksTuple, + EM: EventFirer + EventRestarter, + Z: UsesState, +{ pub(super) unsafe fn pre_run_target_child( &mut self, fuzzer: &mut Z, @@ -250,40 +298,6 @@ where // do nothing } - #[cfg(target_os = "linux")] - fn parse_itimerspec(timeout: Duration) -> libc::itimerspec { - let milli_sec = timeout.as_millis(); - let it_value = libc::timespec { - tv_sec: (milli_sec / 1000) as _, - tv_nsec: ((milli_sec % 1000) * 1000 * 1000) as _, - }; - let it_interval = libc::timespec { - tv_sec: 0, - tv_nsec: 0, - }; - libc::itimerspec { - it_interval, - it_value, - } - } - - #[cfg(not(target_os = "linux"))] - fn parse_itimerval(timeout: Duration) -> Itimerval { - let milli_sec = timeout.as_millis(); - let it_value = Timeval { - tv_sec: (milli_sec / 1000) as i64, - tv_usec: (milli_sec % 1000) as i64, - }; - let it_interval = Timeval { - tv_sec: 0, - tv_usec: 0, - }; - Itimerval { - it_interval, - it_value, - } - } - /// Creates a new [`GenericInProcessForkExecutorInner`] with custom hooks #[cfg(target_os = "linux")] #[allow(clippy::too_many_arguments)] @@ -299,7 +313,7 @@ where let default_hooks = InChildProcessHooks::new::()?; let mut hooks = tuple_list!(default_hooks).merge(userhooks); hooks.init_all::(state); - let itimerspec = Self::parse_itimerspec(timeout); + let itimerspec = parse_itimerspec(timeout); Ok(Self { shmem_provider, observers, @@ -311,7 +325,7 @@ where #[cfg(not(target_os = "linux"))] pub fn set_timeout(&mut self, timeout: Duration) { - self.itimerspec = Self::parse_itimerspec(timeout); + self.itimerspec = parse_itimerspec(timeout); } /// Creates a new [`GenericInProcessForkExecutorInner`], non linux @@ -330,7 +344,7 @@ where let mut hooks = tuple_list!(default_hooks).merge(userhooks); hooks.init_all::(state); - let itimerval = Self::parse_itimmerval(timeout); + let itimerval = parse_itimmerval(timeout); Ok(Self { shmem_provider, diff --git a/libafl/src/executors/inprocess_fork/mod.rs b/libafl/src/executors/inprocess_fork/mod.rs index cac9473b3c..309a784253 100644 --- a/libafl/src/executors/inprocess_fork/mod.rs +++ b/libafl/src/executors/inprocess_fork/mod.rs @@ -12,7 +12,7 @@ use libafl_bolts::{ use libc::siginfo_t; use nix::unistd::{fork, ForkResult}; -use super::hooks::ExecutorHooksTuple; +use super::{hooks::ExecutorHooksTuple, HasTimeout}; use crate::{ events::{EventFirer, EventRestarter}, executors::{ @@ -180,9 +180,25 @@ where } } } +} + +impl<'a, H, HT, OT, S, SP, EM, Z> HasTimeout + for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + OT: ObserversTuple, + S: UsesInput, + SP: ShMemProvider, + HT: ExecutorHooksTuple, + EM: UsesState, + Z: UsesState, +{ fn set_timeout(&mut self, timeout: Duration) { self.inner.set_timeout(timeout); } + fn timeout(&self) -> Duration { + self.inner.timeout() + } } impl<'a, H, HT, OT, S, SP, EM, Z, OF> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> diff --git a/libafl/src/executors/inprocess_fork/stateful.rs b/libafl/src/executors/inprocess_fork/stateful.rs index 5296c4f117..701168ac14 100644 --- a/libafl/src/executors/inprocess_fork/stateful.rs +++ b/libafl/src/executors/inprocess_fork/stateful.rs @@ -18,7 +18,7 @@ use crate::{ events::{EventFirer, EventRestarter}, executors::{ hooks::ExecutorHooksTuple, inprocess_fork::GenericInProcessForkExecutorInner, Executor, - ExitKind, HasObservers, + ExitKind, HasObservers, HasTimeout, }, feedbacks::Feedback, fuzzer::HasObjective, @@ -168,9 +168,26 @@ where } } } +} + +impl<'a, H, HT, OT, S, SP, ES, EM, Z> HasTimeout + for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + OT: ObserversTuple, + S: State, + SP: ShMemProvider, + HT: ExecutorHooksTuple, + EM: UsesState, + Z: UsesState, +{ fn set_timeout(&mut self, timeout: Duration) { self.inner.set_timeout(timeout); } + + fn timeout(&self) -> Duration { + self.inner.timeout() + } } impl<'a, H, HT, OT, S, SP, ES, EM, Z, OF> diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 3527ee72d7..feb79dc3f3 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -142,8 +142,13 @@ where { WithObservers::new(self, observers) } +} - /// Set the threshold for timeout +/// A trait that allows to get/set an `Executor`'s timeout thresold +pub trait HasTimeout { + /// Get an timeout + fn timeout(&self) -> Duration; + /// Set timeout fn set_timeout(&mut self, timeout: Duration); } @@ -232,7 +237,6 @@ mod test { Ok(ExitKind::Ok) } } - fn set_timeout(&mut self, _timeout: core::time::Duration) {} } #[test] diff --git a/libafl/src/executors/shadow.rs b/libafl/src/executors/shadow.rs index 98ab654dbe..94bf4baadc 100644 --- a/libafl/src/executors/shadow.rs +++ b/libafl/src/executors/shadow.rs @@ -7,6 +7,7 @@ use core::{ use libafl_bolts::tuples::RefIndexable; +use super::HasTimeout; use crate::{ executors::{Executor, ExitKind, HasObservers}, inputs::UsesInput, @@ -78,10 +79,19 @@ where ) -> Result { self.executor.run_target(fuzzer, state, mgr, input) } +} +impl HasTimeout for ShadowExecutor +where + E: HasTimeout, +{ fn set_timeout(&mut self, timeout: Duration) { self.executor.set_timeout(timeout); } + + fn timeout(&self) -> Duration { + self.executor.timeout() + } } impl UsesState for ShadowExecutor diff --git a/libafl/src/executors/with_observers.rs b/libafl/src/executors/with_observers.rs index e115fa90fa..7b161cbb97 100644 --- a/libafl/src/executors/with_observers.rs +++ b/libafl/src/executors/with_observers.rs @@ -1,6 +1,6 @@ //! A wrapper for any [`Executor`] to make it implement [`HasObservers`] using a given [`ObserversTuple`]. -use core::{fmt::Debug, time::Duration}; +use core::fmt::Debug; use libafl_bolts::tuples::RefIndexable; @@ -34,9 +34,6 @@ where ) -> Result { self.executor.run_target(fuzzer, state, mgr, input) } - fn set_timeout(&mut self, timeout: Duration) { - self.executor.set_timeout(timeout); - } } impl UsesState for WithObservers diff --git a/libafl_frida/src/executor.rs b/libafl_frida/src/executor.rs index 00815e366f..1d16d22901 100644 --- a/libafl_frida/src/executor.rs +++ b/libafl_frida/src/executor.rs @@ -17,6 +17,7 @@ use libafl::{ executors::{Executor, ExitKind, HasObservers, InProcessExecutor}, inputs::HasTargetBytes, observers::ObserversTuple, + prelude::HasTimeout, state::{HasExecutions, State, UsesState}, Error, }; @@ -118,9 +119,21 @@ where self.helper.post_exec(input)?; res } +} +impl HasTimeout for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S> +where + H: FnMut(&S::Input) -> ExitKind, + S: State, + S::Input: HasTargetBytes, + OT: ObserversTuple + Debug, +{ fn set_timeout(&mut self, timeout: std::time::Duration) { - as Executor>::set_timeout(&mut self.base, timeout); + self.base.set_timeout(timeout); + } + + fn timeout(&self) -> std::time::Duration { + self.base.timeout() } } diff --git a/libafl_nyx/src/executor.rs b/libafl_nyx/src/executor.rs index 0f843f57eb..14f8727e23 100644 --- a/libafl_nyx/src/executor.rs +++ b/libafl_nyx/src/executor.rs @@ -1,13 +1,14 @@ use std::{ io::{Read, Seek}, marker::PhantomData, - os::fd::AsRawFd, + os::fd::AsRawFd, time::Duration, }; use libafl::{ executors::{Executor, ExitKind, HasObservers}, inputs::HasTargetBytes, observers::{ObserversTuple, StdOutObserver}, + prelude::HasTimeout, state::{HasExecutions, State, UsesState}, Error, }; @@ -26,6 +27,7 @@ pub struct NyxExecutor { // stderr: Option, /// observers observers: OT, + timeout: Duration, /// phantom data to keep generic type phantom: PhantomData, } @@ -127,15 +129,15 @@ where Ok(exit_kind) } +} +impl HasTimeout for NyxExecutor { fn set_timeout(&mut self, duration: std::time::Duration) { - assert!( - duration.as_secs() <= 255, - "cannot set a timeout of seconds greater than 255 (u8::MAX)" - ); - let timeout_secs = duration.as_secs() as u8; - let timeout_microsecs = duration.subsec_micros(); - self.helper.set_timeout(timeout_secs, timeout_microsecs); + self.helper.set_timeout(duration); + } + + fn timeout(&self) -> std::time::Duration { + self.timeout } } @@ -185,6 +187,7 @@ impl NyxExecutorBuilder { pub fn build(&self, helper: NyxHelper, observers: OT) -> NyxExecutor { NyxExecutor { + timeout: helper.timeout(), helper, stdout: self.stdout.clone(), // stderr: self.stderr.clone(), diff --git a/libafl_nyx/src/helper.rs b/libafl_nyx/src/helper.rs index fc7b2163de..9ea0a6452a 100644 --- a/libafl_nyx/src/helper.rs +++ b/libafl_nyx/src/helper.rs @@ -1,5 +1,5 @@ /// [`NyxHelper`] is used to wrap `NyxProcess` -use std::{fmt::Debug, fs::File, path::Path}; +use std::{fmt::Debug, fs::File, path::Path, time::Duration}; use libafl::Error; use libnyx::{NyxConfig, NyxProcess, NyxProcessRole}; @@ -7,6 +7,7 @@ use libnyx::{NyxConfig, NyxProcess, NyxProcessRole}; use crate::settings::NyxSettings; pub struct NyxHelper { + pub timeout: Duration, pub nyx_process: NyxProcess, pub nyx_stdout: File, @@ -67,6 +68,9 @@ impl NyxHelper { let bitmap_buffer = nyx_process.bitmap_buffer_mut().as_mut_ptr(); Ok(Self { + timeout: Duration::from_micros( + u64::from(settings.timeout_secs) * 100_000 + u64::from(settings.timeout_micro_secs), + ), nyx_process, nyx_stdout, bitmap_size, @@ -75,8 +79,22 @@ impl NyxHelper { } /// Set a timeout for Nyx. - pub fn set_timeout(&mut self, secs: u8, micro_secs: u32) { - self.nyx_process.option_set_timeout(secs, micro_secs); + #[allow(clippy::implicit_saturating_sub)] + pub fn set_timeout(&mut self, duration: Duration) { + self.timeout = duration; + // Since nyx takes seconds as a u8, all seconds above `u8::MAX` + // are given as micro seconds + let secs = duration.as_secs(); + let u8_max = u64::from(u8::MAX); + let secs_above_u8max = if secs > u8_max { secs - u8_max } else { 0 }; + let micro_secs = duration.subsec_micros() + (secs_above_u8max * 1_000_000) as u32; + // legal truncation + self.nyx_process.option_set_timeout(secs as u8, micro_secs); self.nyx_process.option_apply(); } + + #[must_use] + pub fn timeout(&self) -> Duration { + self.timeout + } } diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 3dbd8a4824..6543406fd7 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -21,6 +21,7 @@ use libafl::{ fuzzer::HasObjective, inputs::UsesInput, observers::ObserversTuple, + prelude::HasTimeout, state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState}, Error, ExecutionProcessor, HasScheduler, }; @@ -245,16 +246,22 @@ where Ok(exit_kind) } +} +impl HasTimeout for QemuExecutor<'_, CM, ED, ET, H, OT, S, SM> +where + CM: CommandManager, + ET: EmulatorModuleTuple, + H: FnMut(&mut Emulator, &mut S, &S::Input) -> ExitKind, + OT: ObserversTuple, + S: State, +{ fn set_timeout(&mut self, timeout: Duration) { - , - > as libafl::executors::Executor>::set_timeout(&mut self.inner, timeout); + self.inner.set_timeout(timeout); + } + + fn timeout(&self) -> Duration { + self.inner.timeout() } } @@ -408,10 +415,27 @@ where ) -> Result { self.inner.run_target(fuzzer, state, mgr, input) } +} +#[cfg(feature = "fork")] +impl HasTimeout + for QemuForkExecutor<'_, CM, ED, EM, ET, H, OT, S, SM, SP, Z> +where + CM: CommandManager, + EM: UsesState, + ET: EmulatorModuleTuple, + H: FnMut(&S::Input) -> ExitKind + ?Sized, + OT: ObserversTuple, + S: UsesInput, + SP: ShMemProvider, + Z: UsesState, +{ fn set_timeout(&mut self, timeout: Duration) { self.inner.set_timeout(timeout); } + fn timeout(&self) -> Duration { + self.inner.timeout() + } } #[cfg(feature = "fork")] diff --git a/libafl_tinyinst/src/executor.rs b/libafl_tinyinst/src/executor.rs index d94e721b68..03ea8b898b 100644 --- a/libafl_tinyinst/src/executor.rs +++ b/libafl_tinyinst/src/executor.rs @@ -4,6 +4,7 @@ use libafl::{ executors::{Executor, ExitKind, HasObservers}, inputs::HasTargetBytes, observers::ObserversTuple, + prelude::HasTimeout, state::{HasExecutions, State, UsesState}, Error, }; @@ -100,10 +101,19 @@ where _ => Err(Error::unknown("Tinyinst RunResult is unknown".to_string())), } } +} +impl HasTimeout for TinyInstExecutor +where + SP: ShMemProvider, +{ fn set_timeout(&mut self, timeout: Duration) { self.timeout = timeout; } + + fn timeout(&self) -> Duration { + self.timeout + } } /// Builder for `TinyInstExecutor` From d17f50a51bf2c0e6c52edffdae621dd326f4e32c Mon Sep 17 00:00:00 2001 From: aarnav Date: Fri, 11 Oct 2024 12:15:37 +0200 Subject: [PATCH 14/67] libafl-fuzz: add removed gitignore --- fuzzers/others/libafl-fuzz/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 fuzzers/others/libafl-fuzz/.gitignore diff --git a/fuzzers/others/libafl-fuzz/.gitignore b/fuzzers/others/libafl-fuzz/.gitignore new file mode 100644 index 0000000000..0a452b5af1 --- /dev/null +++ b/fuzzers/others/libafl-fuzz/.gitignore @@ -0,0 +1,4 @@ +test/out-cmplog +test/out-instr +test/output-cmplog/ +test/output/ From 2a35f550d12d07c57470f8f10d4f37c1040ebe5a Mon Sep 17 00:00:00 2001 From: aarnav Date: Fri, 11 Oct 2024 12:16:52 +0200 Subject: [PATCH 15/67] remove timeout from libafl_nyx::Executor and move it to NyxHelper --- libafl_nyx/src/executor.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libafl_nyx/src/executor.rs b/libafl_nyx/src/executor.rs index 14f8727e23..04a8b6153a 100644 --- a/libafl_nyx/src/executor.rs +++ b/libafl_nyx/src/executor.rs @@ -1,7 +1,8 @@ use std::{ io::{Read, Seek}, marker::PhantomData, - os::fd::AsRawFd, time::Duration, + os::fd::AsRawFd, + time::Duration, }; use libafl::{ @@ -27,7 +28,6 @@ pub struct NyxExecutor { // stderr: Option, /// observers observers: OT, - timeout: Duration, /// phantom data to keep generic type phantom: PhantomData, } @@ -137,7 +137,7 @@ impl HasTimeout for NyxExecutor { } fn timeout(&self) -> std::time::Duration { - self.timeout + self.helper.timeout } } @@ -187,7 +187,6 @@ impl NyxExecutorBuilder { pub fn build(&self, helper: NyxHelper, observers: OT) -> NyxExecutor { NyxExecutor { - timeout: helper.timeout(), helper, stdout: self.stdout.clone(), // stderr: self.stderr.clone(), From 74e41c70dfecc976caaa54d6e395809a07af027c Mon Sep 17 00:00:00 2001 From: aarnav Date: Fri, 11 Oct 2024 12:17:25 +0200 Subject: [PATCH 16/67] clippy --- libafl_nyx/src/executor.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/libafl_nyx/src/executor.rs b/libafl_nyx/src/executor.rs index 04a8b6153a..188b696f94 100644 --- a/libafl_nyx/src/executor.rs +++ b/libafl_nyx/src/executor.rs @@ -2,7 +2,6 @@ use std::{ io::{Read, Seek}, marker::PhantomData, os::fd::AsRawFd, - time::Duration, }; use libafl::{ From 282514b9c4ca1480901d25f5f815d00f94462b1e Mon Sep 17 00:00:00 2001 From: aarnav Date: Fri, 11 Oct 2024 12:47:28 +0200 Subject: [PATCH 17/67] fix HasTimeout for QemuExecutor --- libafl/src/executors/inprocess_fork/inner.rs | 10 +--------- libafl/src/executors/inprocess_fork/stateful.rs | 10 ++-------- libafl_qemu/src/executor.rs | 7 +++---- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/libafl/src/executors/inprocess_fork/inner.rs b/libafl/src/executors/inprocess_fork/inner.rs index 9c6177b864..464e35985f 100644 --- a/libafl/src/executors/inprocess_fork/inner.rs +++ b/libafl/src/executors/inprocess_fork/inner.rs @@ -80,15 +80,7 @@ where type State = S; } -impl HasTimeout for GenericInProcessForkExecutorInner -where - OT: ObserversTuple, - S: UsesInput, - SP: ShMemProvider, - HT: ExecutorHooksTuple, - EM: UsesState, - Z: UsesState, -{ +impl HasTimeout for GenericInProcessForkExecutorInner { /// Set the thresold for timeout fn set_timeout(&mut self, timeout: Duration) { #[cfg(target_os = "linux")] diff --git a/libafl/src/executors/inprocess_fork/stateful.rs b/libafl/src/executors/inprocess_fork/stateful.rs index 5bd0c1050e..706e3286a4 100644 --- a/libafl/src/executors/inprocess_fork/stateful.rs +++ b/libafl/src/executors/inprocess_fork/stateful.rs @@ -160,13 +160,8 @@ where impl<'a, H, HT, OT, S, SP, ES, EM, Z> HasTimeout for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> where - H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, - OT: ObserversTuple, - S: State, - SP: ShMemProvider, - HT: ExecutorHooksTuple, - EM: UsesState, - Z: UsesState, + H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized, + S: UsesInput, { fn set_timeout(&mut self, timeout: Duration) { self.inner.set_timeout(timeout); @@ -176,7 +171,6 @@ where self.inner.timeout() } } - impl<'a, H, HT, OT, S, SP, ES, EM, Z> StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> where diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index c9d08c3ded..46cc9be4e4 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -432,13 +432,12 @@ where } #[cfg(feature = "fork")] -impl HasTimeout - for QemuForkExecutor<'_, CM, ED, EM, ET, H, OT, S, SM, SP, Z> +impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> HasTimeout + for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> where CM: CommandManager, - EM: UsesState, ET: EmulatorModuleTuple, - H: FnMut(&S::Input) -> ExitKind + ?Sized, + H: FnMut(&mut Emulator, &S::Input) -> ExitKind + ?Sized, OT: ObserversTuple, S: UsesInput, SP: ShMemProvider, From 5b24eaceed6fac3e23a7c64d6c5fdd8f72f1674d Mon Sep 17 00:00:00 2001 From: aarnav Date: Fri, 11 Oct 2024 17:15:34 +0200 Subject: [PATCH 18/67] libafl-fuzz: remove observer handle usage in verify_timeouts misc: remove prelude imports --- .../src/feedback/persistent_record.rs | 3 +-- .../forkserver/libafl-fuzz/src/feedback/seed.rs | 2 +- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 1 - .../libafl-fuzz/src/stages/verify_timeouts.rs | 17 +++++------------ fuzzers/others/libafl-fuzz/.gitignore | 4 ---- libafl_frida/src/executor.rs | 3 +-- libafl_nyx/src/executor.rs | 3 +-- libafl_tinyinst/src/executor.rs | 3 +-- 8 files changed, 10 insertions(+), 26 deletions(-) delete mode 100644 fuzzers/others/libafl-fuzz/.gitignore diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/persistent_record.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/persistent_record.rs index 2943c469b2..7ab04c67dc 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/feedback/persistent_record.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/persistent_record.rs @@ -3,9 +3,8 @@ use std::{borrow::Cow, collections::VecDeque}; use libafl::{ corpus::{Corpus, Testcase}, executors::ExitKind, - feedbacks::{Feedback, FeedbackFactory}, + feedbacks::{Feedback, StateInitializer, FeedbackFactory}, inputs::Input, - prelude::StateInitializer, state::HasCorpus, }; use libafl_bolts::{Error, Named}; diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/seed.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/seed.rs index 8e2c399a19..daa265d3b8 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/feedback/seed.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/seed.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, marker::PhantomData}; use libafl::{ - corpus::Testcase, executors::ExitKind, feedbacks::Feedback, prelude::StateInitializer, Error, + corpus::Testcase, executors::ExitKind, feedbacks::{StateInitializer, Feedback}, Error, }; use libafl_bolts::Named; diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 33d6855bf7..5400713a91 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -226,7 +226,6 @@ where |_, _, _, _| Ok(!opt.ignore_timeouts), tuple_list!(VerifyTimeoutsStage::new( Duration::from_millis(opt.hang_timeout), - &time_observer )), ); diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index 17a1dfde63..599b8b59c1 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -4,20 +4,17 @@ use core::time::Duration; use std::{collections::VecDeque, fmt::Debug, marker::PhantomData}; use libafl::{ - inputs::UsesInput, - prelude::{BytesInput, HasTimeout, Executor, ExitKind, HasObservers, ObserversTuple, TimeObserver}, + executors::{Executor, ExitKind, HasObservers, HasTimeout}, + inputs::{BytesInput, UsesInput}, + observers::ObserversTuple, stages::Stage, state::{HasCorpus, UsesState}, HasMetadata, }; -use libafl_bolts::{ - tuples::{Handle, Handled}, - Error, -}; +use libafl_bolts::Error; use serde::{de::DeserializeOwned, Deserialize, Serialize}; #[derive(Debug)] pub struct VerifyTimeoutsStage { - time_observer_handle: Handle, doubled_timeout: Duration, original_timeout: Duration, // The handle to our time observer @@ -26,9 +23,8 @@ pub struct VerifyTimeoutsStage { impl VerifyTimeoutsStage { /// Create a `VerifyTimeoutsStage` - pub fn new(configured_timeout: Duration, time_observer: &TimeObserver) -> Self { + pub fn new(configured_timeout: Duration) -> Self { Self { - time_observer_handle: time_observer.handle(), doubled_timeout: configured_timeout * 2, original_timeout: configured_timeout, phantom: PhantomData, @@ -90,10 +86,7 @@ where .clone(); executor.set_timeout(self.doubled_timeout); while let Some(input) = timeouts.pop() { - executor.observers_mut().pre_exec_all(state, &input)?; let exit_kind = executor.run_target(fuzzer, state, manager, &input)?; - let observers = executor.observers(); - let observer = &observers[&self.time_observer_handle]; if matches!(exit_kind, ExitKind::Timeout) {} } executor.set_timeout(self.original_timeout); diff --git a/fuzzers/others/libafl-fuzz/.gitignore b/fuzzers/others/libafl-fuzz/.gitignore deleted file mode 100644 index 0a452b5af1..0000000000 --- a/fuzzers/others/libafl-fuzz/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -test/out-cmplog -test/out-instr -test/output-cmplog/ -test/output/ diff --git a/libafl_frida/src/executor.rs b/libafl_frida/src/executor.rs index 8de34a5430..25e7b5b6f4 100644 --- a/libafl_frida/src/executor.rs +++ b/libafl_frida/src/executor.rs @@ -14,10 +14,9 @@ use libafl::{ state::{HasCorpus, HasSolutions}, }; use libafl::{ - executors::{Executor, ExitKind, HasObservers, InProcessExecutor}, + executors::{Executor, ExitKind, HasObservers, HasTimeout, InProcessExecutor}, inputs::HasTargetBytes, observers::ObserversTuple, - prelude::HasTimeout, state::{HasExecutions, State, UsesState}, Error, }; diff --git a/libafl_nyx/src/executor.rs b/libafl_nyx/src/executor.rs index 188b696f94..3ab6e47dc0 100644 --- a/libafl_nyx/src/executor.rs +++ b/libafl_nyx/src/executor.rs @@ -5,10 +5,9 @@ use std::{ }; use libafl::{ - executors::{Executor, ExitKind, HasObservers}, + executors::{Executor, ExitKind, HasObservers, HasTimeout}, inputs::HasTargetBytes, observers::{ObserversTuple, StdOutObserver}, - prelude::HasTimeout, state::{HasExecutions, State, UsesState}, Error, }; diff --git a/libafl_tinyinst/src/executor.rs b/libafl_tinyinst/src/executor.rs index 03ea8b898b..a1a7f06506 100644 --- a/libafl_tinyinst/src/executor.rs +++ b/libafl_tinyinst/src/executor.rs @@ -1,10 +1,9 @@ use core::{marker::PhantomData, ptr, time::Duration}; use libafl::{ - executors::{Executor, ExitKind, HasObservers}, + executors::{Executor, ExitKind, HasObservers, HasTimeout}, inputs::HasTargetBytes, observers::ObserversTuple, - prelude::HasTimeout, state::{HasExecutions, State, UsesState}, Error, }; From 2eab13c438163bbd576e008c20ac5bda0416eaaf Mon Sep 17 00:00:00 2001 From: aarnav Date: Fri, 11 Oct 2024 17:18:22 +0200 Subject: [PATCH 19/67] libafl-fuzz: fix foreign_sync_dirs option --- fuzzers/forkserver/libafl-fuzz/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/main.rs b/fuzzers/forkserver/libafl-fuzz/src/main.rs index 41e2c1326d..49bee26ab1 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/main.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/main.rs @@ -187,7 +187,7 @@ struct Opt { #[arg(short = 'c')] cmplog: Option, /// sync to a foreign fuzzer queue directory (requires -M, can be specified up to 32 times) - #[arg(short = 'F', num_args = 32)] + #[arg(short = 'F')] foreign_sync_dirs: Vec, /// fuzzer dictionary (see README.md) #[arg(short = 'x')] From 1c5a5ebeef8cab6572a18ca75b9755533ec3fe6a Mon Sep 17 00:00:00 2001 From: aarnav Date: Mon, 14 Oct 2024 17:54:53 +0200 Subject: [PATCH 20/67] fmt && clippy --- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 6 +++--- libafl/src/executors/inprocess_fork/mod.rs | 4 ++-- libafl/src/executors/inprocess_fork/stateful.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 5400713a91..cbad55b4a2 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -224,9 +224,9 @@ where // Like AFL++ we re-run all timeouts with double the timeout to assert that they are not false positives let timeout_verify_stage = IfStage::new( |_, _, _, _| Ok(!opt.ignore_timeouts), - tuple_list!(VerifyTimeoutsStage::new( - Duration::from_millis(opt.hang_timeout), - )), + tuple_list!(VerifyTimeoutsStage::new(Duration::from_millis( + opt.hang_timeout + ),)), ); // Set LD_PRELOAD (Linux) && DYLD_INSERT_LIBRARIES (OSX) for target. diff --git a/libafl/src/executors/inprocess_fork/mod.rs b/libafl/src/executors/inprocess_fork/mod.rs index 309a784253..ad3090f7a4 100644 --- a/libafl/src/executors/inprocess_fork/mod.rs +++ b/libafl/src/executors/inprocess_fork/mod.rs @@ -182,8 +182,8 @@ where } } -impl<'a, H, HT, OT, S, SP, EM, Z> HasTimeout - for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> +impl HasTimeout + for GenericInProcessForkExecutor<'_, H, HT, OT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind + ?Sized, OT: ObserversTuple, diff --git a/libafl/src/executors/inprocess_fork/stateful.rs b/libafl/src/executors/inprocess_fork/stateful.rs index 706e3286a4..ec6ac05eab 100644 --- a/libafl/src/executors/inprocess_fork/stateful.rs +++ b/libafl/src/executors/inprocess_fork/stateful.rs @@ -157,8 +157,8 @@ where } } -impl<'a, H, HT, OT, S, SP, ES, EM, Z> HasTimeout - for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> +impl HasTimeout + for StatefulGenericInProcessForkExecutor<'_, H, HT, OT, S, SP, ES, EM, Z> where H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized, S: UsesInput, From 5ee021636196b0d42d3ae473ba4c1f0da4aea1aa Mon Sep 17 00:00:00 2001 From: aarnav Date: Mon, 14 Oct 2024 18:02:09 +0200 Subject: [PATCH 21/67] clippy && fmt --- libafl/src/stages/mod.rs | 7 ++----- libafl/src/stages/stats.rs | 13 +++++++++---- libafl/src/stages/time_tracker.rs | 13 +------------ libafl_qemu/src/executor.rs | 3 +-- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 4f43310a24..3060349e6d 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -32,6 +32,7 @@ pub use mutational::{MutationalStage, StdMutationalStage}; pub use power::{PowerMutationalStage, StdPowerMutationalStage}; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] +pub use stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}; pub use sync::*; pub use time_tracker::TimeTrackingStageWrapper; pub use tmin::{ @@ -71,17 +72,13 @@ pub mod generation; pub mod logics; pub mod power; #[cfg(feature = "std")] +pub mod stats; pub mod sync; -#[cfg(feature = "std")] pub mod time_tracker; pub mod tracing; pub mod tuneable; #[cfg(feature = "unicode")] pub mod unicode; -#[cfg(all(feature = "std", feature = "track_hit_feedbacks"))] -pub use stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}; -#[cfg(all(feature = "std", feature = "track_hit_feedbacks"))] -pub mod stats; /// A stage is one step in the fuzzing process. /// Multiple stages will be scheduled one by one for each input. diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index 195fa4421b..5a3db264eb 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -23,7 +23,6 @@ use crate::{ corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase}, events::EventFirer, executors::HasObservers, - feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME}, mutators::Tokens, observers::MapObserver, schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles}, @@ -32,7 +31,8 @@ use crate::{ std::string::ToString, Error, HasMetadata, HasNamedMetadata, HasScheduler, SerdeAny, }; - +#[cfg(feature = "track_hit_feedbacks")] +use crate::feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME}; /// AFL++'s default stats update interval pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60; @@ -275,8 +275,10 @@ where // New testcase! self.cycles_wo_finds = 0; self.update_last_find(); - self.maybe_update_last_crash(&testcase, state); - self.maybe_update_last_hang(&testcase, state); + #[cfg(feature = "track_hit_feedbacks")] { + self.maybe_update_last_crash(&testcase, state); + self.maybe_update_last_hang(&testcase, state); + } self.update_has_fuzzed_size(); self.maybe_update_is_favored_size(&testcase); } @@ -483,7 +485,9 @@ where self.last_find = current_time(); } + #[cfg(feature = "track_hit_feedbacks")] fn maybe_update_last_crash(&mut self, testcase: &Testcase, state: &E::State) { + #[cfg(feature = "track_hit_feedbacks")] if testcase .hit_objectives() .contains(&Cow::Borrowed(CRASH_FEEDBACK_NAME)) @@ -493,6 +497,7 @@ where } } + #[cfg(feature = "track_hit_feedbacks")] fn maybe_update_last_hang(&mut self, testcase: &Testcase, state: &E::State) { if testcase .hit_objectives() diff --git a/libafl/src/stages/time_tracker.rs b/libafl/src/stages/time_tracker.rs index 6b54ce7a5b..1d2609f3a0 100644 --- a/libafl/src/stages/time_tracker.rs +++ b/libafl/src/stages/time_tracker.rs @@ -9,18 +9,7 @@ use crate::{ state::{State, UsesState}, HasMetadata, }; -/// Track an inner Stage's execution time -/// ``` -///#[derive(Debug, SerdeAny, Serialize, Deserialize)] -///pub struct FuzzTime(pub Duration); -///impl From for FuzzTime { -/// fn from(value: Duration) -> Self { -/// Self(value) -/// } -///} -/// TimeTrackingStageWrapper::::new(my_fuzz_stage); -/// state.metadata::(); -/// ``` + #[derive(Debug)] pub struct TimeTrackingStageWrapper { inner: ST, diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 46cc9be4e4..f9bcf446b6 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -16,13 +16,12 @@ use libafl::{ hooks::inprocess::InProcessExecutorHandlerData, inprocess::{stateful::StatefulInProcessExecutor, HasInProcessHooks}, inprocess_fork::stateful::StatefulInProcessForkExecutor, - Executor, ExitKind, HasObservers, + Executor, ExitKind, HasObservers, HasTimeout, }, feedbacks::Feedback, fuzzer::HasObjective, inputs::UsesInput, observers::ObserversTuple, - prelude::HasTimeout, state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState}, Error, ExecutionProcessor, HasScheduler, }; From c04a7791f016160a8eddf549b43b7a388255d2e8 Mon Sep 17 00:00:00 2001 From: aarnav Date: Mon, 14 Oct 2024 18:04:24 +0200 Subject: [PATCH 22/67] missing doc --- libafl/src/stages/time_tracker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl/src/stages/time_tracker.rs b/libafl/src/stages/time_tracker.rs index 1d2609f3a0..ee08d0dec8 100644 --- a/libafl/src/stages/time_tracker.rs +++ b/libafl/src/stages/time_tracker.rs @@ -9,7 +9,7 @@ use crate::{ state::{State, UsesState}, HasMetadata, }; - +/// Track an inner Stage's execution time #[derive(Debug)] pub struct TimeTrackingStageWrapper { inner: ST, From 866cbce89c87a4bc04b706469a37ac4b0a8e0c40 Mon Sep 17 00:00:00 2001 From: aarnav Date: Mon, 14 Oct 2024 18:05:03 +0200 Subject: [PATCH 23/67] clippy --- libafl/src/stages/stats.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index 5a3db264eb..77de13254f 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -396,7 +396,6 @@ where saved_crashes: &stats.saved_crashes, execs_done: &stats.execs_done, }; - println!("writing"); self.write_fuzzer_stats(&stats)?; if self.plot_file_path.is_some() { self.write_plot_data(&plot_data)?; @@ -473,10 +472,6 @@ where if metadata.depth() > self.max_depth { self.max_depth = metadata.depth(); } - } else { - return Err(Error::illegal_state( - "testcase must have scheduler metdata?", - )); } Ok(()) } From 03cae63f96946068f1aef1607af171f617e00cb1 Mon Sep 17 00:00:00 2001 From: aarnav Date: Mon, 14 Oct 2024 18:11:04 +0200 Subject: [PATCH 24/67] bruh --- libafl/src/stages/stats.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index 77de13254f..8f3b4c55aa 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -19,6 +19,8 @@ use libafl_bolts::{ }; use serde::{Deserialize, Serialize}; +#[cfg(feature = "track_hit_feedbacks")] +use crate::feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME}; use crate::{ corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase}, events::EventFirer, @@ -31,8 +33,6 @@ use crate::{ std::string::ToString, Error, HasMetadata, HasNamedMetadata, HasScheduler, SerdeAny, }; -#[cfg(feature = "track_hit_feedbacks")] -use crate::feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME}; /// AFL++'s default stats update interval pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60; @@ -275,7 +275,8 @@ where // New testcase! self.cycles_wo_finds = 0; self.update_last_find(); - #[cfg(feature = "track_hit_feedbacks")] { + #[cfg(feature = "track_hit_feedbacks")] + { self.maybe_update_last_crash(&testcase, state); self.maybe_update_last_hang(&testcase, state); } @@ -283,7 +284,7 @@ where self.maybe_update_is_favored_size(&testcase); } self.maybe_update_slowest_exec(&testcase); - self.maybe_update_max_depth(&testcase)?; + self.maybe_update_max_depth(&testcase); // See if we actually need to run the stage, if not, avoid dynamic value computation. if !self.check_interval() { @@ -467,13 +468,12 @@ where self.has_fuzzed_size += 1; } - fn maybe_update_max_depth(&mut self, testcase: &Testcase) -> Result<(), Error> { + fn maybe_update_max_depth(&mut self, testcase: &Testcase) { if let Ok(metadata) = testcase.metadata::() { if metadata.depth() > self.max_depth { self.max_depth = metadata.depth(); } } - Ok(()) } fn update_last_find(&mut self) { @@ -606,7 +606,7 @@ impl Display for AFLFuzzerStats<'_> { Ok(()) } } -/// Get the command used to invoke libafl-fuzz +/// Get the command used to invoke the fuzzer #[must_use] pub fn get_run_cmdline() -> Cow<'static, str> { let args: Vec = std::env::args().collect(); From 0ffb0a7fc58056962354103ae52c798cd18b436a Mon Sep 17 00:00:00 2001 From: aarnav Date: Mon, 14 Oct 2024 18:18:43 +0200 Subject: [PATCH 25/67] damned doc build --- .../libfuzzer_stb_image_concolic/fuzzer/src/main.rs | 4 ++++ libafl/src/executors/command.rs | 3 +++ 2 files changed, 7 insertions(+) diff --git a/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs b/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs index a88b71ca7b..d480e1d821 100644 --- a/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs +++ b/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs @@ -256,4 +256,8 @@ impl CommandConfigurator for MyCommandConfigurator { fn exec_timeout(&self) -> Duration { Duration::from_secs(5) } + + fn exec_timeout_mut(&mut self, duration: Duration) { + todo!() + } } diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 172a80f51f..d5aacb73fa 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -583,6 +583,9 @@ impl CommandExecutorBuilder { /// fn exec_timeout(&self) -> Duration { /// Duration::from_secs(5) /// } +/// fn exec_timeout_mut(&mut self, duration: Duration) { +/// todo!() +/// } /// } /// /// fn make_executor() -> impl Executor From 5f79ecd2cdb5ef59a364803cc6d8b2081972f1a4 Mon Sep 17 00:00:00 2001 From: aarnav Date: Mon, 14 Oct 2024 18:23:42 +0200 Subject: [PATCH 26/67] trait fix --- .../libfuzzer_stb_image_concolic/fuzzer/src/main.rs | 2 +- libafl/src/executors/command.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs b/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs index d480e1d821..f7703e40a9 100644 --- a/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs +++ b/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs @@ -257,7 +257,7 @@ impl CommandConfigurator for MyCommandConfigurator { Duration::from_secs(5) } - fn exec_timeout_mut(&mut self, duration: Duration) { + fn exec_timeout_mut(&mut self) -> &mut Duration { todo!() } } diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index d5aacb73fa..2c508d0d30 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -583,7 +583,7 @@ impl CommandExecutorBuilder { /// fn exec_timeout(&self) -> Duration { /// Duration::from_secs(5) /// } -/// fn exec_timeout_mut(&mut self, duration: Duration) { +/// fn exec_timeout_mut(&mut self) -> &mut Duration { /// todo!() /// } /// } From 22db281557428e4b6f3609877c8d9c6c78b52a02 Mon Sep 17 00:00:00 2001 From: aarnav Date: Mon, 14 Oct 2024 18:57:09 +0200 Subject: [PATCH 27/67] impl HasTimeout for InProcessExecutor only if std --- libafl/src/executors/hooks/inprocess.rs | 6 +++--- libafl/src/executors/inprocess/inner.rs | 10 ++++++---- libafl/src/executors/inprocess/mod.rs | 2 ++ libafl/src/executors/inprocess/stateful.rs | 6 ++++-- libafl/src/stages/mod.rs | 5 +++++ 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/libafl/src/executors/hooks/inprocess.rs b/libafl/src/executors/hooks/inprocess.rs index ab926794bb..7220de976b 100644 --- a/libafl/src/executors/hooks/inprocess.rs +++ b/libafl/src/executors/hooks/inprocess.rs @@ -27,14 +27,14 @@ use crate::executors::hooks::timer::TimerStruct; use crate::executors::hooks::unix::unix_signal_handler; #[cfg(windows)] use crate::state::State; +#[cfg(feature = "std")] +use crate::{corpus::Corpus, observers::ObserversTuple, state::UsesState}; use crate::{ - corpus::Corpus, events::{EventFirer, EventRestarter}, executors::{hooks::ExecutorHook, inprocess::HasInProcessHooks, Executor, HasObservers}, feedbacks::Feedback, inputs::UsesInput, - observers::ObserversTuple, - state::{HasCorpus, HasExecutions, HasSolutions, UsesState}, + state::{HasCorpus, HasExecutions, HasSolutions}, Error, HasObjective, }; diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index 3da86ca4cb..c4fbb295bd 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -15,6 +15,8 @@ use windows::Win32::System::Threading::SetThreadStackGuarantee; use crate::executors::hooks::inprocess::HasTimeout; #[cfg(all(windows, feature = "std"))] use crate::executors::hooks::inprocess::HasTimeout; +#[cfg(feature = "std")] +use crate::executors::HasTimeout as HasExecutorTimeout; use crate::{ corpus::Corpus, events::{EventFirer, EventRestarter}, @@ -24,7 +26,7 @@ use crate::{ ExecutorHooksTuple, }, inprocess::HasInProcessHooks, - Executor, HasObservers, HasTimeout as HasExecutorTimeout, + Executor, HasObservers, }, feedbacks::Feedback, fuzzer::HasObjective, @@ -140,7 +142,7 @@ where } } } - +#[cfg(feature = "std")] impl HasExecutorTimeout for GenericInProcessExecutorInner where HT: ExecutorHooksTuple, @@ -149,7 +151,7 @@ where { /// Set the threshold for timeout fn set_timeout(&mut self, duration: Duration) { - #[cfg(all(feature = "std", windows))] + #[cfg(windows)] { *self.hooks.0.millis_sec_mut() = timeout.as_millis() as i64; } @@ -160,7 +162,7 @@ where } fn timeout(&self) -> Duration { - #[cfg(all(feature = "std", windows))] + #[cfg(windows)] { Duration::from_millis(self.hooks.0.millis_sec()) } diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index 6424141121..5c3b571f84 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -18,6 +18,7 @@ use core::{ use libafl_bolts::tuples::{tuple_list, RefIndexable}; +#[cfg(feature = "std")] use super::HasTimeout; #[cfg(any(unix, feature = "std"))] use crate::executors::hooks::inprocess::GLOBAL_STATE; @@ -134,6 +135,7 @@ where } } +#[cfg(feature = "std")] impl HasTimeout for GenericInProcessExecutor where H: FnMut(&S::Input) -> ExitKind + ?Sized, diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs index 3262b917a2..dd1e6c372c 100644 --- a/libafl/src/executors/inprocess/stateful.rs +++ b/libafl/src/executors/inprocess/stateful.rs @@ -10,13 +10,15 @@ use core::{ use libafl_bolts::tuples::{tuple_list, RefIndexable}; +#[cfg(feature = "std")] +use crate::executors::HasTimeout; use crate::{ corpus::Corpus, events::{EventFirer, EventRestarter}, executors::{ hooks::{inprocess::InProcessHooks, ExecutorHooksTuple}, inprocess::{GenericInProcessExecutorInner, HasInProcessHooks}, - Executor, ExitKind, HasObservers, HasTimeout, + Executor, ExitKind, HasObservers, }, feedbacks::Feedback, fuzzer::HasObjective, @@ -122,7 +124,7 @@ where Ok(ret) } } - +#[cfg(feature = "std")] impl HasTimeout for StatefulGenericInProcessExecutor where H: FnMut(&mut ES, &mut S, &S::Input) -> ExitKind + ?Sized, diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 3060349e6d..8972cc2006 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -33,7 +33,9 @@ pub use power::{PowerMutationalStage, StdPowerMutationalStage}; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] pub use stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}; +#[cfg(feature = "std")] pub use sync::*; +#[cfg(feature = "std")] pub use time_tracker::TimeTrackingStageWrapper; pub use tmin::{ MapEqualityFactory, MapEqualityFeedback, StdTMinMutationalStage, TMinMutationalStage, @@ -66,6 +68,7 @@ pub mod calibrate; pub mod colorization; #[cfg(all(feature = "std", unix))] pub mod concolic; +#[cfg(feature = "std")] pub mod dump; pub mod generalization; pub mod generation; @@ -73,7 +76,9 @@ pub mod logics; pub mod power; #[cfg(feature = "std")] pub mod stats; +#[cfg(feature = "std")] pub mod sync; +#[cfg(feature = "std")] pub mod time_tracker; pub mod tracing; pub mod tuneable; From c5edce42113cd389497df694013801f8adb9180e Mon Sep 17 00:00:00 2001 From: aarnav Date: Mon, 14 Oct 2024 19:13:30 +0200 Subject: [PATCH 28/67] clippy --- libafl_qemu/src/executor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index f9bcf446b6..98a8372bdd 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -431,8 +431,8 @@ where } #[cfg(feature = "fork")] -impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> HasTimeout - for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> +impl HasTimeout + for QemuForkExecutor<'_, CM, ED, EM, ET, H, OT, S, SM, SP, Z> where CM: CommandManager, ET: EmulatorModuleTuple, From dcb1774475bac9ac039be43c02b0577f8c8a0106 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 15 Oct 2024 17:02:50 +0200 Subject: [PATCH 29/67] fix typo --- libafl/src/inputs/bytes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index d850389065..127e672953 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -21,7 +21,7 @@ use crate::{ }; /// A bytes input is the basic input -#[derive(SerdeAny, Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash)] pub struct BytesInput { /// The raw input bytes pub(crate) bytes: Vec, From c4ce299155958dccbd9a85c9e67e0b329ea284ec Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 15 Oct 2024 17:06:07 +0200 Subject: [PATCH 30/67] fix nostd build --- libafl/src/executors/hooks/inprocess.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libafl/src/executors/hooks/inprocess.rs b/libafl/src/executors/hooks/inprocess.rs index 7220de976b..ab926794bb 100644 --- a/libafl/src/executors/hooks/inprocess.rs +++ b/libafl/src/executors/hooks/inprocess.rs @@ -27,14 +27,14 @@ use crate::executors::hooks::timer::TimerStruct; use crate::executors::hooks::unix::unix_signal_handler; #[cfg(windows)] use crate::state::State; -#[cfg(feature = "std")] -use crate::{corpus::Corpus, observers::ObserversTuple, state::UsesState}; use crate::{ + corpus::Corpus, events::{EventFirer, EventRestarter}, executors::{hooks::ExecutorHook, inprocess::HasInProcessHooks, Executor, HasObservers}, feedbacks::Feedback, inputs::UsesInput, - state::{HasCorpus, HasExecutions, HasSolutions}, + observers::ObserversTuple, + state::{HasCorpus, HasExecutions, HasSolutions, UsesState}, Error, HasObjective, }; From 97b679262b161c294b8d3f9277ba7eab4031a685 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 15 Oct 2024 17:24:59 +0200 Subject: [PATCH 31/67] clippy --- libafl/src/executors/inprocess/inner.rs | 4 +--- libafl/src/executors/inprocess_fork/inner.rs | 5 ----- libafl/src/stages/stats.rs | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index c4fbb295bd..66b1731ffe 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -11,9 +11,7 @@ use libafl_bolts::tuples::{tuple_list, Merge, RefIndexable}; #[cfg(windows)] use windows::Win32::System::Threading::SetThreadStackGuarantee; -#[cfg(all(feature = "std", target_os = "linux"))] -use crate::executors::hooks::inprocess::HasTimeout; -#[cfg(all(windows, feature = "std"))] +#[cfg(feature = "std")] use crate::executors::hooks::inprocess::HasTimeout; #[cfg(feature = "std")] use crate::executors::HasTimeout as HasExecutorTimeout; diff --git a/libafl/src/executors/inprocess_fork/inner.rs b/libafl/src/executors/inprocess_fork/inner.rs index 464e35985f..d46972852d 100644 --- a/libafl/src/executors/inprocess_fork/inner.rs +++ b/libafl/src/executors/inprocess_fork/inner.rs @@ -296,11 +296,6 @@ where }) } - #[cfg(not(target_os = "linux"))] - pub fn set_timeout(&mut self, timeout: Duration) { - self.itimerspec = parse_itimerspec(timeout); - } - /// Creates a new [`GenericInProcessForkExecutorInner`], non linux #[cfg(not(target_os = "linux"))] #[allow(clippy::too_many_arguments)] diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index 8f3b4c55aa..f4dcd87452 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -31,7 +31,7 @@ use crate::{ stages::{calibrate::UnstableEntriesMetadata, Stage}, state::{HasCorpus, HasExecutions, HasImported, HasStartTime, Stoppable, UsesState}, std::string::ToString, - Error, HasMetadata, HasNamedMetadata, HasScheduler, SerdeAny, + Error, HasMetadata, HasNamedMetadata, HasScheduler, }; /// AFL++'s default stats update interval pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60; From 8662e7b0a1bedcd5327824f47af4da8be09cba5e Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 15 Oct 2024 17:49:12 +0200 Subject: [PATCH 32/67] remove most HasTimeout implementations for now --- libafl/src/executors/inprocess/inner.rs | 32 --------------- libafl/src/executors/inprocess/mod.rs | 20 ---------- libafl/src/executors/inprocess/stateful.rs | 19 --------- libafl/src/executors/inprocess_fork/inner.rs | 20 +--------- libafl/src/executors/inprocess_fork/mod.rs | 21 +--------- .../src/executors/inprocess_fork/stateful.rs | 16 +------- libafl_frida/src/executor.rs | 18 +-------- libafl_nyx/src/executor.rs | 12 +----- libafl_nyx/src/helper.rs | 24 ++---------- libafl_qemu/src/executor.rs | 39 +------------------ libafl_tinyinst/src/executor.rs | 15 +------ 11 files changed, 10 insertions(+), 226 deletions(-) diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index 66b1731ffe..a2827dd332 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -13,8 +13,6 @@ use windows::Win32::System::Threading::SetThreadStackGuarantee; #[cfg(feature = "std")] use crate::executors::hooks::inprocess::HasTimeout; -#[cfg(feature = "std")] -use crate::executors::HasTimeout as HasExecutorTimeout; use crate::{ corpus::Corpus, events::{EventFirer, EventRestarter}, @@ -140,36 +138,6 @@ where } } } -#[cfg(feature = "std")] -impl HasExecutorTimeout for GenericInProcessExecutorInner -where - HT: ExecutorHooksTuple, - OT: ObserversTuple, - S: State, -{ - /// Set the threshold for timeout - fn set_timeout(&mut self, duration: Duration) { - #[cfg(windows)] - { - *self.hooks.0.millis_sec_mut() = timeout.as_millis() as i64; - } - #[cfg(not(windows))] - { - self.hooks.0.timer_mut().exec_tmout = duration; - } - } - - fn timeout(&self) -> Duration { - #[cfg(windows)] - { - Duration::from_millis(self.hooks.0.millis_sec()) - } - #[cfg(not(windows))] - { - self.hooks.0.timer().exec_tmout - } - } -} impl GenericInProcessExecutorInner where diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index 5c3b571f84..15e14486fa 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -18,8 +18,6 @@ use core::{ use libafl_bolts::tuples::{tuple_list, RefIndexable}; -#[cfg(feature = "std")] -use super::HasTimeout; #[cfg(any(unix, feature = "std"))] use crate::executors::hooks::inprocess::GLOBAL_STATE; use crate::{ @@ -135,24 +133,6 @@ where } } -#[cfg(feature = "std")] -impl HasTimeout for GenericInProcessExecutor -where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - HB: BorrowMut, - HT: ExecutorHooksTuple, - OT: ObserversTuple, - S: State, -{ - fn set_timeout(&mut self, timeout: Duration) { - self.inner.set_timeout(timeout); - } - - fn timeout(&self) -> Duration { - self.inner.timeout() - } -} - impl HasObservers for GenericInProcessExecutor where H: FnMut(&S::Input) -> ExitKind + ?Sized, diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs index dd1e6c372c..27bd145836 100644 --- a/libafl/src/executors/inprocess/stateful.rs +++ b/libafl/src/executors/inprocess/stateful.rs @@ -10,8 +10,6 @@ use core::{ use libafl_bolts::tuples::{tuple_list, RefIndexable}; -#[cfg(feature = "std")] -use crate::executors::HasTimeout; use crate::{ corpus::Corpus, events::{EventFirer, EventRestarter}, @@ -124,23 +122,6 @@ where Ok(ret) } } -#[cfg(feature = "std")] -impl HasTimeout for StatefulGenericInProcessExecutor -where - H: FnMut(&mut ES, &mut S, &S::Input) -> ExitKind + ?Sized, - HB: BorrowMut, - HT: ExecutorHooksTuple, - OT: ObserversTuple, - S: State, -{ - fn set_timeout(&mut self, timeout: Duration) { - self.inner.set_timeout(timeout); - } - - fn timeout(&self) -> Duration { - self.inner.timeout() - } -} impl HasObservers for StatefulGenericInProcessExecutor where diff --git a/libafl/src/executors/inprocess_fork/inner.rs b/libafl/src/executors/inprocess_fork/inner.rs index d46972852d..24332791ac 100644 --- a/libafl/src/executors/inprocess_fork/inner.rs +++ b/libafl/src/executors/inprocess_fork/inner.rs @@ -26,7 +26,7 @@ use crate::{ inprocess_fork::{InChildProcessHooks, FORK_EXECUTOR_GLOBAL_DATA}, ExecutorHooksTuple, }, - ExitKind, HasObservers, HasTimeout, + ExitKind, HasObservers, }, inputs::UsesInput, observers::ObserversTuple, @@ -80,24 +80,6 @@ where type State = S; } -impl HasTimeout for GenericInProcessForkExecutorInner { - /// Set the thresold for timeout - fn set_timeout(&mut self, timeout: Duration) { - #[cfg(target_os = "linux")] - { - self.itimerspec = parse_itimerspec(timeout); - } - #[cfg(not(target_os = "linux"))] - { - self.itimerval = parse_itimerval(timeout); - } - } - - fn timeout(&self) -> Duration { - todo!() - } -} - #[cfg(target_os = "linux")] fn parse_itimerspec(timeout: Duration) -> libc::itimerspec { let milli_sec = timeout.as_millis(); diff --git a/libafl/src/executors/inprocess_fork/mod.rs b/libafl/src/executors/inprocess_fork/mod.rs index ad3090f7a4..77c561db21 100644 --- a/libafl/src/executors/inprocess_fork/mod.rs +++ b/libafl/src/executors/inprocess_fork/mod.rs @@ -12,7 +12,7 @@ use libafl_bolts::{ use libc::siginfo_t; use nix::unistd::{fork, ForkResult}; -use super::{hooks::ExecutorHooksTuple, HasTimeout}; +use super::hooks::ExecutorHooksTuple; use crate::{ events::{EventFirer, EventRestarter}, executors::{ @@ -182,25 +182,6 @@ where } } -impl HasTimeout - for GenericInProcessForkExecutor<'_, H, HT, OT, S, SP, EM, Z> -where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - OT: ObserversTuple, - S: UsesInput, - SP: ShMemProvider, - HT: ExecutorHooksTuple, - EM: UsesState, - Z: UsesState, -{ - fn set_timeout(&mut self, timeout: Duration) { - self.inner.set_timeout(timeout); - } - fn timeout(&self) -> Duration { - self.inner.timeout() - } -} - impl<'a, H, HT, OT, S, SP, EM, Z, OF> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind + ?Sized, diff --git a/libafl/src/executors/inprocess_fork/stateful.rs b/libafl/src/executors/inprocess_fork/stateful.rs index ec6ac05eab..f5a5d854c2 100644 --- a/libafl/src/executors/inprocess_fork/stateful.rs +++ b/libafl/src/executors/inprocess_fork/stateful.rs @@ -18,7 +18,7 @@ use crate::{ events::{EventFirer, EventRestarter}, executors::{ hooks::ExecutorHooksTuple, inprocess_fork::GenericInProcessForkExecutorInner, Executor, - ExitKind, HasObservers, HasTimeout, + ExitKind, HasObservers, }, feedbacks::Feedback, fuzzer::HasObjective, @@ -157,20 +157,6 @@ where } } -impl HasTimeout - for StatefulGenericInProcessForkExecutor<'_, H, HT, OT, S, SP, ES, EM, Z> -where - H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized, - S: UsesInput, -{ - fn set_timeout(&mut self, timeout: Duration) { - self.inner.set_timeout(timeout); - } - - fn timeout(&self) -> Duration { - self.inner.timeout() - } -} impl<'a, H, HT, OT, S, SP, ES, EM, Z> StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> where diff --git a/libafl_frida/src/executor.rs b/libafl_frida/src/executor.rs index 25e7b5b6f4..2514a99df4 100644 --- a/libafl_frida/src/executor.rs +++ b/libafl_frida/src/executor.rs @@ -14,7 +14,7 @@ use libafl::{ state::{HasCorpus, HasSolutions}, }; use libafl::{ - executors::{Executor, ExitKind, HasObservers, HasTimeout, InProcessExecutor}, + executors::{Executor, ExitKind, HasObservers, InProcessExecutor}, inputs::HasTargetBytes, observers::ObserversTuple, state::{HasExecutions, State, UsesState}, @@ -120,22 +120,6 @@ where } } -impl HasTimeout for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S> -where - H: FnMut(&S::Input) -> ExitKind, - S: State, - S::Input: HasTargetBytes, - OT: ObserversTuple + Debug, -{ - fn set_timeout(&mut self, timeout: std::time::Duration) { - self.base.set_timeout(timeout); - } - - fn timeout(&self) -> std::time::Duration { - self.base.timeout() - } -} - impl UsesState for FridaInProcessExecutor<'_, '_, '_, H, OT, RT, S> where H: FnMut(&S::Input) -> ExitKind, diff --git a/libafl_nyx/src/executor.rs b/libafl_nyx/src/executor.rs index 3ab6e47dc0..b3834068a2 100644 --- a/libafl_nyx/src/executor.rs +++ b/libafl_nyx/src/executor.rs @@ -5,7 +5,7 @@ use std::{ }; use libafl::{ - executors::{Executor, ExitKind, HasObservers, HasTimeout}, + executors::{Executor, ExitKind, HasObservers}, inputs::HasTargetBytes, observers::{ObserversTuple, StdOutObserver}, state::{HasExecutions, State, UsesState}, @@ -129,16 +129,6 @@ where } } -impl HasTimeout for NyxExecutor { - fn set_timeout(&mut self, duration: std::time::Duration) { - self.helper.set_timeout(duration); - } - - fn timeout(&self) -> std::time::Duration { - self.helper.timeout - } -} - impl NyxExecutor { /// Convert `trace_bits` ptr into real trace map /// diff --git a/libafl_nyx/src/helper.rs b/libafl_nyx/src/helper.rs index 9ea0a6452a..fc7b2163de 100644 --- a/libafl_nyx/src/helper.rs +++ b/libafl_nyx/src/helper.rs @@ -1,5 +1,5 @@ /// [`NyxHelper`] is used to wrap `NyxProcess` -use std::{fmt::Debug, fs::File, path::Path, time::Duration}; +use std::{fmt::Debug, fs::File, path::Path}; use libafl::Error; use libnyx::{NyxConfig, NyxProcess, NyxProcessRole}; @@ -7,7 +7,6 @@ use libnyx::{NyxConfig, NyxProcess, NyxProcessRole}; use crate::settings::NyxSettings; pub struct NyxHelper { - pub timeout: Duration, pub nyx_process: NyxProcess, pub nyx_stdout: File, @@ -68,9 +67,6 @@ impl NyxHelper { let bitmap_buffer = nyx_process.bitmap_buffer_mut().as_mut_ptr(); Ok(Self { - timeout: Duration::from_micros( - u64::from(settings.timeout_secs) * 100_000 + u64::from(settings.timeout_micro_secs), - ), nyx_process, nyx_stdout, bitmap_size, @@ -79,22 +75,8 @@ impl NyxHelper { } /// Set a timeout for Nyx. - #[allow(clippy::implicit_saturating_sub)] - pub fn set_timeout(&mut self, duration: Duration) { - self.timeout = duration; - // Since nyx takes seconds as a u8, all seconds above `u8::MAX` - // are given as micro seconds - let secs = duration.as_secs(); - let u8_max = u64::from(u8::MAX); - let secs_above_u8max = if secs > u8_max { secs - u8_max } else { 0 }; - let micro_secs = duration.subsec_micros() + (secs_above_u8max * 1_000_000) as u32; - // legal truncation - self.nyx_process.option_set_timeout(secs as u8, micro_secs); + pub fn set_timeout(&mut self, secs: u8, micro_secs: u32) { + self.nyx_process.option_set_timeout(secs, micro_secs); self.nyx_process.option_apply(); } - - #[must_use] - pub fn timeout(&self) -> Duration { - self.timeout - } } diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 98a8372bdd..0cb5d12b1f 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -16,7 +16,7 @@ use libafl::{ hooks::inprocess::InProcessExecutorHandlerData, inprocess::{stateful::StatefulInProcessExecutor, HasInProcessHooks}, inprocess_fork::stateful::StatefulInProcessForkExecutor, - Executor, ExitKind, HasObservers, HasTimeout, + Executor, ExitKind, HasObservers, }, feedbacks::Feedback, fuzzer::HasObjective, @@ -244,23 +244,6 @@ where } } -impl HasTimeout for QemuExecutor<'_, CM, ED, ET, H, OT, S, SM> -where - CM: CommandManager, - ET: EmulatorModuleTuple, - H: FnMut(&mut Emulator, &mut S, &S::Input) -> ExitKind, - OT: ObserversTuple, - S: State, -{ - fn set_timeout(&mut self, timeout: Duration) { - self.inner.set_timeout(timeout); - } - - fn timeout(&self) -> Duration { - self.inner.timeout() - } -} - impl UsesState for QemuExecutor<'_, CM, ED, ET, H, OT, S, SM> where CM: CommandManager, @@ -430,26 +413,6 @@ where } } -#[cfg(feature = "fork")] -impl HasTimeout - for QemuForkExecutor<'_, CM, ED, EM, ET, H, OT, S, SM, SP, Z> -where - CM: CommandManager, - ET: EmulatorModuleTuple, - H: FnMut(&mut Emulator, &S::Input) -> ExitKind + ?Sized, - OT: ObserversTuple, - S: UsesInput, - SP: ShMemProvider, - Z: UsesState, -{ - fn set_timeout(&mut self, timeout: Duration) { - self.inner.set_timeout(timeout); - } - fn timeout(&self) -> Duration { - self.inner.timeout() - } -} - #[cfg(feature = "fork")] impl UsesState for QemuForkExecutor<'_, CM, ED, EM, ET, H, OT, S, SM, SP, Z> diff --git a/libafl_tinyinst/src/executor.rs b/libafl_tinyinst/src/executor.rs index a1a7f06506..6dca42c5ae 100644 --- a/libafl_tinyinst/src/executor.rs +++ b/libafl_tinyinst/src/executor.rs @@ -1,7 +1,7 @@ use core::{marker::PhantomData, ptr, time::Duration}; use libafl::{ - executors::{Executor, ExitKind, HasObservers, HasTimeout}, + executors::{Executor, ExitKind, HasObservers}, inputs::HasTargetBytes, observers::ObserversTuple, state::{HasExecutions, State, UsesState}, @@ -102,19 +102,6 @@ where } } -impl HasTimeout for TinyInstExecutor -where - SP: ShMemProvider, -{ - fn set_timeout(&mut self, timeout: Duration) { - self.timeout = timeout; - } - - fn timeout(&self) -> Duration { - self.timeout - } -} - /// Builder for `TinyInstExecutor` #[derive(Debug)] pub struct TinyInstExecutorBuilder<'a, SP> { From 1b82afbaf7f9349fd6196bcf67ffe8002691a9bf Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 15 Oct 2024 17:54:03 +0200 Subject: [PATCH 33/67] typo --- libafl/src/executors/inprocess_fork/inner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl/src/executors/inprocess_fork/inner.rs b/libafl/src/executors/inprocess_fork/inner.rs index 24332791ac..1552c042d4 100644 --- a/libafl/src/executors/inprocess_fork/inner.rs +++ b/libafl/src/executors/inprocess_fork/inner.rs @@ -294,7 +294,7 @@ where let mut hooks = tuple_list!(default_hooks).merge(userhooks); hooks.init_all::(state); - let itimerval = parse_itimmerval(timeout); + let itimerval = parse_itimerval(timeout); Ok(Self { shmem_provider, From bd46c24636a527c1af1335ea96e87fc61adbb96b Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 15 Oct 2024 18:02:13 +0200 Subject: [PATCH 34/67] remove redundant import --- libafl/src/executors/inprocess/inner.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index a2827dd332..33936d471d 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -11,8 +11,6 @@ use libafl_bolts::tuples::{tuple_list, Merge, RefIndexable}; #[cfg(windows)] use windows::Win32::System::Threading::SetThreadStackGuarantee; -#[cfg(feature = "std")] -use crate::executors::hooks::inprocess::HasTimeout; use crate::{ corpus::Corpus, events::{EventFirer, EventRestarter}, From 084f1d53ecb47c03298d454c6bb1fcac0b516ebe Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 15 Oct 2024 18:24:04 +0200 Subject: [PATCH 35/67] misc --- libafl/src/executors/inprocess/inner.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index 33936d471d..ac9e7ac928 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -11,6 +11,11 @@ use libafl_bolts::tuples::{tuple_list, Merge, RefIndexable}; #[cfg(windows)] use windows::Win32::System::Threading::SetThreadStackGuarantee; +#[cfg(all(feature = "std", target_os = "linux"))] +use crate::executors::hooks::inprocess::HasTimeout; +#[cfg(all(windows, feature = "std"))] +use crate::executors::hooks::inprocess::HasTimeout; + use crate::{ corpus::Corpus, events::{EventFirer, EventRestarter}, From c83caa828b412faaa7b7249a0d6a66271b84598f Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 15 Oct 2024 19:02:21 +0200 Subject: [PATCH 36/67] fmt --- libafl/src/executors/inprocess/inner.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index ac9e7ac928..52598a574a 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -15,7 +15,6 @@ use windows::Win32::System::Threading::SetThreadStackGuarantee; use crate::executors::hooks::inprocess::HasTimeout; #[cfg(all(windows, feature = "std"))] use crate::executors::hooks::inprocess::HasTimeout; - use crate::{ corpus::Corpus, events::{EventFirer, EventRestarter}, From b94613d1c5bf3cc588304435f159c778a339212a Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 10:39:46 +0200 Subject: [PATCH 37/67] simplify trait bounds --- .../libafl-fuzz/src/stages/verify_timeouts.rs | 31 ++++++++++--------- libafl/src/executors/command.rs | 7 +++-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index 599b8b59c1..864d835ee0 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -4,24 +4,25 @@ use core::time::Duration; use std::{collections::VecDeque, fmt::Debug, marker::PhantomData}; use libafl::{ + corpus::Corpus, executors::{Executor, ExitKind, HasObservers, HasTimeout}, inputs::{BytesInput, UsesInput}, observers::ObserversTuple, stages::Stage, - state::{HasCorpus, UsesState}, + state::{HasCorpus, State, UsesState}, HasMetadata, }; use libafl_bolts::Error; use serde::{de::DeserializeOwned, Deserialize, Serialize}; #[derive(Debug)] -pub struct VerifyTimeoutsStage { +pub struct VerifyTimeoutsStage { doubled_timeout: Duration, original_timeout: Duration, // The handle to our time observer - phantom: PhantomData, + phantom: PhantomData<(E, S)>, } -impl VerifyTimeoutsStage { +impl VerifyTimeoutsStage { /// Create a `VerifyTimeoutsStage` pub fn new(configured_timeout: Duration) -> Self { Self { @@ -32,12 +33,11 @@ impl VerifyTimeoutsStage { } } -impl UsesState for VerifyTimeoutsStage +impl UsesState for VerifyTimeoutsStage where - E: UsesState, - ::State: HasMetadata + HasCorpus, + S: State, { - type State = E::State; + type State = S; } #[derive(Default, Serialize, Deserialize, Clone, Debug)] @@ -65,14 +65,15 @@ impl TimeoutsToVerify { } } -impl Stage for VerifyTimeoutsStage +impl Stage for VerifyTimeoutsStage where E::Observers: ObserversTuple<::Input, ::State>, - E: Executor + HasObservers + HasTimeout, - EM: UsesState, - Z: UsesState, - ::State: HasMetadata + HasCorpus, - E::Input: Debug + Serialize + DeserializeOwned + Default + 'static + Clone, + E: Executor + HasObservers + HasTimeout, + EM: UsesState, + Z: UsesState, + S: HasCorpus + State + HasMetadata, + Self::Input: Debug + Serialize + DeserializeOwned + Default + 'static + Clone, + <::State as HasCorpus>::Corpus: Corpus, //delete me { fn perform( &mut self, @@ -82,7 +83,7 @@ where manager: &mut EM, ) -> Result<(), Error> { let mut timeouts = state - .metadata_or_insert_with(TimeoutsToVerify::::new) + .metadata_or_insert_with(TimeoutsToVerify::<::Input>::new) .clone(); executor.set_timeout(self.doubled_timeout); while let Some(input) = timeouts.pop() { diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 2c508d0d30..dec5c7a5c0 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -27,10 +27,11 @@ use super::HasTimeout; #[cfg(all(feature = "std", unix))] use crate::executors::{Executor, ExitKind}; use crate::{ + corpus::Corpus, executors::HasObservers, inputs::{HasTargetBytes, UsesInput}, observers::{ObserversTuple, StdErrObserver, StdOutObserver}, - state::{HasExecutions, State, UsesState}, + state::{HasCorpus, HasExecutions, State, UsesState}, std::borrow::ToOwned, }; #[cfg(feature = "std")] @@ -289,8 +290,8 @@ where impl HasTimeout for CommandExecutor where - S: State, - T: CommandConfigurator, + S: HasCorpus, + T: CommandConfigurator<::Input>, { fn set_timeout(&mut self, timeout: Duration) { *self.configurer.exec_timeout_mut() = timeout; From 5a4135973a8796635a2e1c5a31cae4b3c5c298e6 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 10:48:13 +0200 Subject: [PATCH 38/67] add old AflStatsStage back and rename it to StatsStage --- .../forkserver/libafl-fuzz/src/env_parser.rs | 2 +- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 2 +- libafl/src/stages/afl_stats.rs | 795 +++++++++++++++++ libafl/src/stages/mod.rs | 8 +- libafl/src/stages/stats.rs | 833 +++--------------- 5 files changed, 904 insertions(+), 736 deletions(-) create mode 100644 libafl/src/stages/afl_stats.rs diff --git a/fuzzers/forkserver/libafl-fuzz/src/env_parser.rs b/fuzzers/forkserver/libafl-fuzz/src/env_parser.rs index 8046835ad6..d909bfbf81 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/env_parser.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/env_parser.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, path::PathBuf, time::Duration}; -use libafl::{stages::stats::AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS, Error}; +use libafl::{stages::afl_stats::AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS, Error}; use libafl_bolts::core_affinity::Cores; use crate::Opt; diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index cbad55b4a2..89a7eb885d 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -24,7 +24,7 @@ use libafl::{ }, stages::{ mutational::MultiMutationalStage, - stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}, + afl_stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}, time_tracker::TimeTrackingStageWrapper, CalibrationStage, ColorizationStage, IfStage, StagesTuple, StdMutationalStage, StdPowerMutationalStage, SyncFromDiskStage, diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs new file mode 100644 index 0000000000..f4dcd87452 --- /dev/null +++ b/libafl/src/stages/afl_stats.rs @@ -0,0 +1,795 @@ +//! Stage to compute and report AFL++ stats +use alloc::{string::String, vec::Vec}; +use core::{marker::PhantomData, time::Duration}; +use std::{ + borrow::Cow, + fmt::Display, + fs::{File, OpenOptions}, + io::{BufRead, BufReader, Write}, + path::{Path, PathBuf}, + process, +}; + +use libafl_bolts::{ + core_affinity::CoreId, + current_time, + os::peak_rss_mb_child_processes, + tuples::{Handle, Handled, MatchNameRef}, + Named, +}; +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "track_hit_feedbacks")] +use crate::feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME}; +use crate::{ + corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase}, + events::EventFirer, + executors::HasObservers, + mutators::Tokens, + observers::MapObserver, + schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles}, + stages::{calibrate::UnstableEntriesMetadata, Stage}, + state::{HasCorpus, HasExecutions, HasImported, HasStartTime, Stoppable, UsesState}, + std::string::ToString, + Error, HasMetadata, HasNamedMetadata, HasScheduler, +}; +/// AFL++'s default stats update interval +pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60; + +/// `CalibrationTime` - Use in conjunction with `TimeTrackingFeedback` +#[derive(Debug, SerdeAny, Serialize, Deserialize)] +pub struct CalibrationTime(pub Duration); +impl From for CalibrationTime { + fn from(value: Duration) -> Self { + Self(value) + } +} + +/// `SyncTime` - Use in conjunction with `TimeTrackingFeedback` +#[derive(Debug, SerdeAny, Serialize, Deserialize)] +pub struct SyncTime(pub Duration); +impl From for SyncTime { + fn from(value: Duration) -> Self { + Self(value) + } +} + +/// `FuzzTime` - Use in conjunction with `TimeTrackingFeedback` +#[derive(Debug, SerdeAny, Serialize, Deserialize)] +pub struct FuzzTime(pub Duration); +impl From for FuzzTime { + fn from(value: Duration) -> Self { + Self(value) + } +} + +/// The [`AflStatsStage`] is a Stage that calculates and writes +/// AFL++'s `fuzzer_stats` and `plot_data` information. +#[derive(Debug, Clone)] +pub struct AflStatsStage { + map_observer_handle: Handle, + stats_file_path: PathBuf, + plot_file_path: Option, + start_time: u64, + // the number of testcases that have been fuzzed + has_fuzzed_size: usize, + // the number of "favored" testcases + is_favored_size: usize, + // the last time that we report all stats + last_report_time: Duration, + // the interval at which we report all stats + stats_report_interval: Duration, + pid: u32, + slowest_exec: Duration, + max_depth: u64, + cycles_done: u64, + saved_crashes: u64, + saved_hangs: u64, + last_find: Duration, + last_hang: Duration, + last_crash: Duration, + exec_timeout: u64, + execs_at_last_objective: u64, + cycles_wo_finds: u64, + /// banner text (e.g., the target name) + afl_banner: Cow<'static, str>, + /// the version of libafl-fuzz used + afl_version: Cow<'static, str>, + /// default, persistent, qemu, unicorn, non-instrumented + target_mode: Cow<'static, str>, + /// full command line used for the fuzzing session + command_line: Cow<'static, str>, + /// Amount of tokens provided by the user. Used to determine autotokens count. + dict_count: usize, + /// autotokens are enabled + autotokens_enabled: bool, + /// The core we are bound to + core_id: CoreId, + phantom_data: PhantomData<(O, E, EM, Z)>, +} + +/// AFL++'s `fuzzer_stats` +#[derive(Debug, Clone)] +pub struct AFLFuzzerStats<'a> { + /// unix time indicating the start time of afl-fuzz + start_time: u64, + /// unix time corresponding to the last interval + last_update: u64, + /// run time in seconds to the last update of this file + run_time: u64, + /// process id of the fuzzer process + fuzzer_pid: u32, + /// queue cycles completed so far + cycles_done: u64, + /// number of queue cycles without any new paths found + cycles_wo_find: u64, + /// longest time in seconds no new path was found + time_wo_finds: u64, + /// Time spent fuzzing + fuzz_time: u64, + /// Time spent calibrating inputs + calibration_time: u64, + /// Time spent syncing with foreign fuzzers + /// NOTE: Syncing between our own instances is not counted. + sync_time: u64, + /// TODO + trim_time: u64, + /// number of fuzzer executions attempted (what does attempted mean here?) + execs_done: u64, + /// overall number of execs per second + execs_per_sec: u64, + /// TODO + execs_ps_last_min: u64, + /// total number of entries in the queue + corpus_count: usize, + /// number of queue entries that are favored + corpus_favored: usize, + /// number of entries discovered through local fuzzing + corpus_found: usize, + /// number of entries imported from other instances + corpus_imported: usize, + /// number of levels in the generated data set + max_depth: u64, + /// currently processed entry number + cur_item: usize, + /// number of favored entries still waiting to be fuzzed + pending_favs: usize, + /// number of all entries waiting to be fuzzed + pending_total: usize, + /// number of test cases showing variable behavior + corpus_variable: u64, + /// percentage of bitmap bytes that behave consistently + stability: f64, + /// percentage of edge coverage found in the map so far, + bitmap_cvg: f64, + /// number of unique crashes recorded + saved_crashes: u64, + /// number of unique hangs encountered + saved_hangs: u64, + /// seconds since the last find was found + last_find: Duration, + /// seconds since the last crash was found + last_crash: Duration, + /// seconds since the last hang was found + last_hang: Duration, + /// execs since the last crash was found + execs_since_crash: u64, + /// the -t command line value + exec_timeout: u64, + /// real time of the slowest execution in ms + slowest_exec_ms: u128, + /// max rss usage reached during fuzzing in MB + peak_rss_mb: i64, + /// TODO + cpu_affinity: usize, + /// how many edges have been found + edges_found: u64, + /// Size of our edges map + total_edges: u64, + /// how many edges are non-deterministic + var_byte_count: usize, + /// TODO: + havoc_expansion: usize, + /// Amount of automatic dict entries found + auto_dict_entries: usize, + /// TODO: + testcache_size: usize, + /// TODO: + testcache_count: usize, + /// TODO: + testcache_evict: usize, + /// banner text (e.g., the target name) + afl_banner: &'a Cow<'static, str>, + /// the version of AFL++ used + afl_version: &'a Cow<'static, str>, + /// default, persistent, qemu, unicorn, non-instrumented + target_mode: &'a Cow<'static, str>, + /// full command line used for the fuzzing session + command_line: &'a str, +} +/// AFL++'s `plot_data` +#[derive(Debug, Clone)] +pub struct AFLPlotData<'a> { + relative_time: &'a u64, + cycles_done: &'a u64, + cur_item: &'a usize, + corpus_count: &'a usize, + pending_total: &'a usize, + pending_favs: &'a usize, + /// Note: renamed `map_size` -> `total_edges` for consistency with `fuzzer_stats` + total_edges: &'a u64, + saved_crashes: &'a u64, + saved_hangs: &'a u64, + max_depth: &'a u64, + execs_per_sec: &'a u64, + /// Note: renamed `total_execs` -> `execs_done` for consistency with `fuzzer_stats` + execs_done: &'a u64, + edges_found: &'a u64, +} + +impl UsesState for AflStatsStage +where + E: UsesState, + EM: EventFirer, + Z: UsesState, +{ + type State = E::State; +} + +impl Stage for AflStatsStage +where + E: UsesState + HasObservers, + EM: EventFirer, + Z: UsesState + HasScheduler, + E::State: HasImported + + HasCorpus + + HasMetadata + + HasStartTime + + HasExecutions + + HasNamedMetadata + + Stoppable, + E::Observers: MatchNameRef, + O: MapObserver, + C: AsRef + Named, + ::Scheduler: HasQueueCycles, + <::State as HasCorpus>::Corpus: Corpus, +{ + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut E::State, + _manager: &mut EM, + ) -> Result<(), Error> { + let Some(corpus_idx) = state.current_corpus_id()? else { + return Err(Error::illegal_state( + "state is not currently processing a corpus index", + )); + }; + let testcase = state.corpus().get(corpus_idx)?.borrow(); + // NOTE: scheduled_count represents the amount of fuzz runs a + // testcase has had. Since this stage is kept at the very end of stage list, + // the entry would have been fuzzed already (and should contain IsFavoredMetadata) but would have a scheduled count of zero + // since the scheduled count is incremented after all stages have been run. + if testcase.scheduled_count() == 0 { + // New testcase! + self.cycles_wo_finds = 0; + self.update_last_find(); + #[cfg(feature = "track_hit_feedbacks")] + { + self.maybe_update_last_crash(&testcase, state); + self.maybe_update_last_hang(&testcase, state); + } + self.update_has_fuzzed_size(); + self.maybe_update_is_favored_size(&testcase); + } + self.maybe_update_slowest_exec(&testcase); + self.maybe_update_max_depth(&testcase); + + // See if we actually need to run the stage, if not, avoid dynamic value computation. + if !self.check_interval() { + return Ok(()); + } + + let corpus_size = state.corpus().count(); + let total_executions = *state.executions(); + + let scheduler = fuzzer.scheduler(); + let queue_cycles = scheduler.queue_cycles(); + self.maybe_update_cycles(queue_cycles); + self.maybe_update_cycles_wo_finds(queue_cycles); + + let observers = executor.observers(); + let map_observer = observers + .get(&self.map_observer_handle) + .ok_or_else(|| Error::key_not_found("invariant: MapObserver not found".to_string()))? + .as_ref(); + let filled_entries_in_map = map_observer.count_bytes(); + let map_size = map_observer.usable_count(); + // Since we do not calibrate when using `QueueScheduler`; we cannot calculate unstable entries. + let unstable_entries_in_map = state + .metadata_map() + .get::() + .map_or(0, |m| m.unstable_entries().len()); + + let auto_dict_entries = if self.autotokens_enabled { + state + .metadata::()? + .len() + .saturating_sub(self.dict_count) + } else { + 0 + }; + let stats = AFLFuzzerStats { + start_time: self.start_time, + last_update: self.last_report_time.as_secs(), + run_time: self.last_report_time.as_secs() - self.start_time, + fuzzer_pid: self.pid, + cycles_done: queue_cycles, + cycles_wo_find: self.cycles_wo_finds, + fuzz_time: state + .metadata::() + .map_or(Duration::from_secs(0), |d| d.0) + .as_secs(), + calibration_time: state + .metadata::() + .map_or(Duration::from_secs(0), |d| d.0) + .as_secs(), + sync_time: state + .metadata::() + .map_or(Duration::from_secs(0), |d| d.0) + .as_secs(), + trim_time: 0, // TODO + execs_done: total_executions, + execs_per_sec: *state.executions(), // TODO + execs_ps_last_min: *state.executions(), // TODO + max_depth: self.max_depth, + corpus_count: corpus_size, + corpus_favored: corpus_size - self.is_favored_size, + corpus_found: corpus_size - state.imported(), + corpus_imported: *state.imported(), + cur_item: corpus_idx.into(), + pending_total: corpus_size - self.has_fuzzed_size, + pending_favs: 0, // TODO + time_wo_finds: (current_time() - self.last_find).as_secs(), + corpus_variable: 0, + stability: self.calculate_stability(unstable_entries_in_map, filled_entries_in_map), + #[allow(clippy::cast_precision_loss)] + bitmap_cvg: (filled_entries_in_map as f64 / map_size as f64) * 100.0, + saved_crashes: self.saved_crashes, + saved_hangs: self.saved_hangs, + last_find: self.last_find, + last_hang: self.last_hang, + last_crash: self.last_crash, + execs_since_crash: total_executions - self.execs_at_last_objective, + exec_timeout: self.exec_timeout, + slowest_exec_ms: self.slowest_exec.as_millis(), + #[cfg(unix)] + peak_rss_mb: peak_rss_mb_child_processes()?, + #[cfg(not(unix))] + peak_rss_mb: 0, // TODO for Windows + cpu_affinity: self.core_id.0, + total_edges: map_size as u64, + edges_found: filled_entries_in_map, + var_byte_count: unstable_entries_in_map, + havoc_expansion: 0, // TODO + auto_dict_entries, + testcache_size: 0, + testcache_count: 0, + testcache_evict: 0, + afl_banner: &self.afl_banner, + afl_version: &self.afl_version, + target_mode: &self.target_mode, + command_line: &self.command_line, + }; + let plot_data = AFLPlotData { + corpus_count: &stats.corpus_count, + cur_item: &stats.cur_item, + cycles_done: &stats.cycles_done, + edges_found: &stats.edges_found, + total_edges: &stats.total_edges, + execs_per_sec: &stats.execs_per_sec, + pending_total: &stats.pending_total, + pending_favs: &stats.pending_favs, + max_depth: &stats.max_depth, + relative_time: &stats.run_time, + saved_hangs: &stats.saved_hangs, + saved_crashes: &stats.saved_crashes, + execs_done: &stats.execs_done, + }; + self.write_fuzzer_stats(&stats)?; + if self.plot_file_path.is_some() { + self.write_plot_data(&plot_data)?; + } + Ok(()) + } + + fn should_restart(&mut self, _state: &mut Self::State) -> Result { + Ok(true) + } + + fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { + Ok(()) + } +} + +impl AflStatsStage +where + E: UsesState + HasObservers, + EM: EventFirer, + Z: UsesState, + E::State: HasImported + HasCorpus + HasMetadata + HasExecutions, + C: AsRef + Named, + O: MapObserver, +{ + /// Builder for `AflStatsStage` + #[must_use] + pub fn builder() -> AflStatsStageBuilder { + AflStatsStageBuilder::new() + } + + fn write_fuzzer_stats(&self, stats: &AFLFuzzerStats) -> Result<(), Error> { + let tmp_file = self + .stats_file_path + .parent() + .expect("fuzzer_stats file must have a parent!") + .join(".fuzzer_stats_tmp"); + std::fs::write(&tmp_file, stats.to_string())?; + _ = std::fs::copy(&tmp_file, &self.stats_file_path)?; + std::fs::remove_file(tmp_file)?; + Ok(()) + } + + fn write_plot_data(&self, plot_data: &AFLPlotData) -> Result<(), Error> { + let mut file = OpenOptions::new().append(true).open( + self.plot_file_path + .as_ref() + .expect("invariant; should never occur"), + )?; + writeln!(file, "{plot_data}")?; + Ok(()) + } + + fn maybe_update_is_favored_size(&mut self, testcase: &Testcase) { + if testcase.has_metadata::() { + self.is_favored_size += 1; + } + } + + fn maybe_update_slowest_exec(&mut self, testcase: &Testcase) { + if let Some(exec_time) = testcase.exec_time() { + if exec_time > &self.slowest_exec { + self.slowest_exec = *exec_time; + } + } + } + + fn update_has_fuzzed_size(&mut self) { + self.has_fuzzed_size += 1; + } + + fn maybe_update_max_depth(&mut self, testcase: &Testcase) { + if let Ok(metadata) = testcase.metadata::() { + if metadata.depth() > self.max_depth { + self.max_depth = metadata.depth(); + } + } + } + + fn update_last_find(&mut self) { + self.last_find = current_time(); + } + + #[cfg(feature = "track_hit_feedbacks")] + fn maybe_update_last_crash(&mut self, testcase: &Testcase, state: &E::State) { + #[cfg(feature = "track_hit_feedbacks")] + if testcase + .hit_objectives() + .contains(&Cow::Borrowed(CRASH_FEEDBACK_NAME)) + { + self.last_crash = current_time(); + self.execs_at_last_objective = *state.executions(); + } + } + + #[cfg(feature = "track_hit_feedbacks")] + fn maybe_update_last_hang(&mut self, testcase: &Testcase, state: &E::State) { + if testcase + .hit_objectives() + .contains(&Cow::Borrowed(TIMEOUT_FEEDBACK_NAME)) + { + self.last_hang = current_time(); + self.execs_at_last_objective = *state.executions(); + } + } + + fn check_interval(&mut self) -> bool { + let cur = current_time(); + if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval { + self.last_report_time = cur; + return true; + } + false + } + fn maybe_update_cycles(&mut self, queue_cycles: u64) { + if queue_cycles > self.cycles_done { + self.cycles_done += 1; + } + } + + fn maybe_update_cycles_wo_finds(&mut self, queue_cycles: u64) { + if queue_cycles > self.cycles_done && self.last_find < current_time() { + self.cycles_wo_finds += 1; + } + } + + #[allow(clippy::cast_precision_loss)] + #[allow(clippy::unused_self)] + fn calculate_stability(&self, unstable_entries: usize, filled_entries: u64) -> f64 { + ((filled_entries as f64 - unstable_entries as f64) / filled_entries as f64) * 100.0 + } +} + +impl Display for AFLPlotData<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{},", self.relative_time)?; + write!(f, "{},", self.cycles_done)?; + write!(f, "{},", self.cur_item)?; + write!(f, "{},", self.corpus_count)?; + write!(f, "{},", self.pending_total)?; + write!(f, "{},", self.pending_favs)?; + write!(f, "{},", self.total_edges)?; + write!(f, "{},", self.saved_crashes)?; + write!(f, "{},", self.saved_hangs)?; + write!(f, "{},", self.max_depth)?; + write!(f, "{},", self.execs_per_sec)?; + write!(f, "{},", self.execs_done)?; + write!(f, "{}", self.edges_found)?; + Ok(()) + } +} +impl AFLPlotData<'_> { + fn get_header() -> String { + "# relative_time, cycles_done, cur_item, corpus_count, pending_total, pending_favs, total_edges, saved_crashes, saved_hangs, max_depth, execs_per_sec, execs_done, edges_found".to_string() + } +} +impl Display for AFLFuzzerStats<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "start_time : {}", &self.start_time)?; + writeln!(f, "start_time : {}", &self.start_time)?; + writeln!(f, "last_update : {}", &self.last_update)?; + writeln!(f, "run_time : {}", &self.run_time)?; + writeln!(f, "fuzzer_pid : {}", &self.fuzzer_pid)?; + writeln!(f, "cycles_done : {}", &self.cycles_done)?; + writeln!(f, "cycles_wo_find : {}", &self.cycles_wo_find)?; + writeln!(f, "time_wo_finds : {}", &self.time_wo_finds)?; + writeln!(f, "fuzz_time : {}", &self.fuzz_time)?; + writeln!(f, "calibration_time : {}", &self.calibration_time)?; + writeln!(f, "sync_time : {}", &self.sync_time)?; + writeln!(f, "trim_time : {}", &self.trim_time)?; + writeln!(f, "execs_done : {}", &self.execs_done)?; + writeln!(f, "execs_per_sec : {}", &self.execs_per_sec)?; + writeln!(f, "execs_ps_last_min : {}", &self.execs_ps_last_min)?; + writeln!(f, "corpus_count : {}", &self.corpus_count)?; + writeln!(f, "corpus_favored : {}", &self.corpus_favored)?; + writeln!(f, "corpus_found : {}", &self.corpus_found)?; + writeln!(f, "corpus_imported : {}", &self.corpus_imported)?; + writeln!(f, "max_depth : {}", &self.max_depth)?; + writeln!(f, "cur_item : {}", &self.cur_item)?; + writeln!(f, "pending_favs : {}", &self.pending_favs)?; + writeln!(f, "pending_total : {}", &self.pending_total)?; + writeln!(f, "corpus_variable : {}", &self.corpus_variable)?; + writeln!(f, "stability : {:.2}%", &self.stability)?; + writeln!(f, "bitmap_cvg : {:.2}%", &self.bitmap_cvg)?; + writeln!(f, "saved_crashes : {}", &self.saved_crashes)?; + writeln!(f, "saved_hangs : {}", &self.saved_hangs)?; + writeln!(f, "last_find : {}", &self.last_find.as_secs())?; + writeln!(f, "last_crash : {}", &self.last_crash.as_secs())?; + writeln!(f, "last_hang : {}", &self.last_hang.as_secs())?; + writeln!(f, "execs_since_crash : {}", &self.execs_since_crash)?; + writeln!(f, "exec_timeout : {}", &self.exec_timeout)?; + writeln!(f, "slowest_exec_ms : {}", &self.slowest_exec_ms)?; + writeln!(f, "peak_rss_mb : {}", &self.peak_rss_mb)?; + writeln!(f, "cpu_affinity : {}", &self.cpu_affinity)?; + writeln!(f, "edges_found : {}", &self.edges_found)?; + writeln!(f, "total_edges : {}", &self.total_edges)?; + writeln!(f, "var_byte_count : {}", &self.var_byte_count)?; + writeln!(f, "havoc_expansion : {}", &self.havoc_expansion)?; + writeln!(f, "auto_dict_entries : {}", &self.auto_dict_entries)?; + writeln!(f, "testcache_size : {}", &self.testcache_size)?; + writeln!(f, "testcache_count : {}", &self.testcache_count)?; + writeln!(f, "testcache_evict : {}", &self.testcache_evict)?; + writeln!(f, "afl_banner : {}", self.afl_banner)?; + writeln!(f, "afl_version : {}", self.afl_version)?; + writeln!(f, "target_mode : {}", self.target_mode)?; + writeln!(f, "command_line : {}", self.command_line)?; + Ok(()) + } +} +/// Get the command used to invoke the fuzzer +#[must_use] +pub fn get_run_cmdline() -> Cow<'static, str> { + let args: Vec = std::env::args().collect(); + Cow::Owned(args.join(" ")) +} + +/// The Builder for `AflStatsStage` +#[derive(Debug)] +pub struct AflStatsStageBuilder { + stats_file_path: Option, + plot_file_path: Option, + core_id: Option, + map_observer_handle: Option>, + uses_autotokens: bool, + report_interval: Duration, + dict_count: usize, + exec_timeout: u64, + banner: String, + version: String, + target_mode: String, + phantom_data: PhantomData<(O, E, EM, Z)>, +} + +impl AflStatsStageBuilder +where + E: UsesState + HasObservers, + EM: EventFirer, + Z: UsesState, + E::State: HasImported + HasCorpus + HasMetadata + HasExecutions, + C: AsRef + Named, + O: MapObserver, +{ + fn new() -> Self { + Self { + report_interval: Duration::from_secs(AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS), + stats_file_path: None, + plot_file_path: None, + core_id: None, + map_observer_handle: None, + uses_autotokens: false, + dict_count: 0, + exec_timeout: 0, + banner: String::default(), + version: String::default(), + target_mode: String::default(), + phantom_data: PhantomData, + } + } + + /// The file path to which we will write the fuzzer stats + #[must_use] + pub fn stats_file(mut self, path: PathBuf) -> Self { + self.stats_file_path = Some(path); + self + } + /// The file path to which we will write the plot data + #[must_use] + pub fn plot_file(mut self, path: PathBuf) -> Self { + self.plot_file_path = Some(path); + self + } + /// The core we are bound to + #[must_use] + pub fn core_id(mut self, core_id: CoreId) -> Self { + self.core_id = Some(core_id); + self + } + /// The interval with which we report stats + #[must_use] + pub fn report_interval(mut self, interval: Duration) -> Self { + self.report_interval = interval; + self + } + /// Our `MapObserver` + #[must_use] + pub fn map_observer(mut self, map_observer: &C) -> Self { + self.map_observer_handle = Some(map_observer.handle()); + self + } + /// If we use autotokens provided by the target + #[must_use] + pub fn uses_autotokens(mut self, uses: bool) -> Self { + self.uses_autotokens = uses; + self + } + /// The tokens utilized by the fuzzer + #[must_use] + pub fn tokens(mut self, tokens: &Tokens) -> Self { + self.dict_count = tokens.len(); + self + } + /// AFL++ Banner (typically the target) + #[must_use] + pub fn banner(mut self, banner: String) -> Self { + self.banner = banner; + self + } + /// Version of the fuzzer + #[must_use] + pub fn version(mut self, version: String) -> Self { + self.version = version; + self + } + /// The "timeout" value used in `TimeoutFeedback` + #[must_use] + pub fn exec_timeout(mut self, timeout: u64) -> Self { + self.exec_timeout = timeout; + self + } + /// Used in the UI (optional) + /// default, persistent, qemu, unicorn, non-instrumented etc + #[must_use] + pub fn target_mode(mut self, target_mode: String) -> Self { + self.target_mode = target_mode; + self + } + + fn create_plot_data_file(path: &Path) -> Result<(), Error> { + if path.exists() { + // check if it contains any data + let file = File::open(path)?; + if BufReader::new(file).lines().next().is_none() { + std::fs::write(path, AFLPlotData::get_header())?; + } + } else { + std::fs::write(path, AFLPlotData::get_header())?; + } + Ok(()) + } + + fn create_fuzzer_stats_file(path: &Path) -> Result<(), Error> { + if !path.exists() { + _ = OpenOptions::new().append(true).create(true).open(path)?; + } + Ok(()) + } + /// Build [`AflStatsStage`] + /// Will error if: + /// Cannot create the stats file + /// Cannot create the plot file (if provided) + /// No `MapObserver` supplied to the builder + /// No `stats_file_path` provieded + pub fn build(self) -> Result, Error> { + if self.stats_file_path.is_none() { + return Err(Error::illegal_argument("Must set `stats_file_path`")); + } + let stats_file_path = self.stats_file_path.unwrap(); + if self.map_observer_handle.is_none() { + return Err(Error::illegal_argument("Must set `map_observer`")); + } + if let Some(ref plot_file) = self.plot_file_path { + Self::create_plot_data_file(plot_file)?; + } + Self::create_fuzzer_stats_file(&stats_file_path)?; + Ok(AflStatsStage { + stats_file_path, + plot_file_path: self.plot_file_path, + map_observer_handle: self.map_observer_handle.unwrap(), + start_time: current_time().as_secs(), + stats_report_interval: self.report_interval, + has_fuzzed_size: 0, + is_favored_size: 0, + cycles_done: 0, + cycles_wo_finds: 0, + execs_at_last_objective: 0, + last_crash: current_time(), + last_find: current_time(), + last_hang: current_time(), + max_depth: 0, + saved_hangs: 0, + saved_crashes: 0, + slowest_exec: Duration::from_secs(0), + last_report_time: current_time(), + pid: process::id(), + exec_timeout: self.exec_timeout, + target_mode: Cow::Owned(self.target_mode), + afl_banner: Cow::Owned(self.banner), + afl_version: Cow::Owned(self.version), + command_line: get_run_cmdline(), + dict_count: self.dict_count, + core_id: self.core_id.unwrap_or(CoreId(0)), + autotokens_enabled: self.uses_autotokens, + phantom_data: PhantomData, + }) + } +} diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 8972cc2006..b8962c45ac 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -12,6 +12,8 @@ use alloc::{ }; use core::{fmt, marker::PhantomData}; +#[cfg(feature = "std")] +pub use afl_stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}; pub use calibrate::CalibrationStage; pub use colorization::*; #[cfg(all(feature = "std", unix))] @@ -31,8 +33,7 @@ pub use logics::*; pub use mutational::{MutationalStage, StdMutationalStage}; pub use power::{PowerMutationalStage, StdPowerMutationalStage}; use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] -pub use stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}; +pub use stats::StatsStage; #[cfg(feature = "std")] pub use sync::*; #[cfg(feature = "std")] @@ -64,6 +65,8 @@ pub mod mutational; pub mod push; pub mod tmin; +#[cfg(feature = "std")] +pub mod afl_stats; pub mod calibrate; pub mod colorization; #[cfg(all(feature = "std", unix))] @@ -74,7 +77,6 @@ pub mod generalization; pub mod generation; pub mod logics; pub mod power; -#[cfg(feature = "std")] pub mod stats; #[cfg(feature = "std")] pub mod sync; diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index f4dcd87452..2bcf4742a2 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -1,795 +1,166 @@ -//! Stage to compute and report AFL++ stats -use alloc::{string::String, vec::Vec}; +//! Stage to compute/report minimal AFL-like stats + +#[cfg(feature = "std")] +use alloc::{borrow::Cow, string::ToString}; use core::{marker::PhantomData, time::Duration}; -use std::{ - borrow::Cow, - fmt::Display, - fs::{File, OpenOptions}, - io::{BufRead, BufReader, Write}, - path::{Path, PathBuf}, - process, -}; -use libafl_bolts::{ - core_affinity::CoreId, - current_time, - os::peak_rss_mb_child_processes, - tuples::{Handle, Handled, MatchNameRef}, - Named, -}; -use serde::{Deserialize, Serialize}; +use libafl_bolts::current_time; +#[cfg(feature = "std")] +use serde_json::json; -#[cfg(feature = "track_hit_feedbacks")] -use crate::feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME}; use crate::{ - corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase}, + corpus::{Corpus, HasCurrentCorpusId}, events::EventFirer, - executors::HasObservers, - mutators::Tokens, - observers::MapObserver, - schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles}, - stages::{calibrate::UnstableEntriesMetadata, Stage}, - state::{HasCorpus, HasExecutions, HasImported, HasStartTime, Stoppable, UsesState}, - std::string::ToString, - Error, HasMetadata, HasNamedMetadata, HasScheduler, + schedulers::minimizer::IsFavoredMetadata, + stages::Stage, + state::{HasCorpus, HasImported, UsesState}, + Error, HasMetadata, +}; +#[cfg(feature = "std")] +use crate::{ + events::Event, + monitors::{AggregatorOps, UserStats, UserStatsValue}, }; -/// AFL++'s default stats update interval -pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60; - -/// `CalibrationTime` - Use in conjunction with `TimeTrackingFeedback` -#[derive(Debug, SerdeAny, Serialize, Deserialize)] -pub struct CalibrationTime(pub Duration); -impl From for CalibrationTime { - fn from(value: Duration) -> Self { - Self(value) - } -} - -/// `SyncTime` - Use in conjunction with `TimeTrackingFeedback` -#[derive(Debug, SerdeAny, Serialize, Deserialize)] -pub struct SyncTime(pub Duration); -impl From for SyncTime { - fn from(value: Duration) -> Self { - Self(value) - } -} - -/// `FuzzTime` - Use in conjunction with `TimeTrackingFeedback` -#[derive(Debug, SerdeAny, Serialize, Deserialize)] -pub struct FuzzTime(pub Duration); -impl From for FuzzTime { - fn from(value: Duration) -> Self { - Self(value) - } -} -/// The [`AflStatsStage`] is a Stage that calculates and writes -/// AFL++'s `fuzzer_stats` and `plot_data` information. +/// The [`StatsStage`] is a simple stage that computes and reports some stats. #[derive(Debug, Clone)] -pub struct AflStatsStage { - map_observer_handle: Handle, - stats_file_path: PathBuf, - plot_file_path: Option, - start_time: u64, +pub struct StatsStage { // the number of testcases that have been fuzzed has_fuzzed_size: usize, // the number of "favored" testcases is_favored_size: usize, + // the number of testcases found by itself + own_finds_size: usize, + // the number of testcases imported by other fuzzers + imported_size: usize, // the last time that we report all stats last_report_time: Duration, - // the interval at which we report all stats + // the interval that we report all stats stats_report_interval: Duration, - pid: u32, - slowest_exec: Duration, - max_depth: u64, - cycles_done: u64, - saved_crashes: u64, - saved_hangs: u64, - last_find: Duration, - last_hang: Duration, - last_crash: Duration, - exec_timeout: u64, - execs_at_last_objective: u64, - cycles_wo_finds: u64, - /// banner text (e.g., the target name) - afl_banner: Cow<'static, str>, - /// the version of libafl-fuzz used - afl_version: Cow<'static, str>, - /// default, persistent, qemu, unicorn, non-instrumented - target_mode: Cow<'static, str>, - /// full command line used for the fuzzing session - command_line: Cow<'static, str>, - /// Amount of tokens provided by the user. Used to determine autotokens count. - dict_count: usize, - /// autotokens are enabled - autotokens_enabled: bool, - /// The core we are bound to - core_id: CoreId, - phantom_data: PhantomData<(O, E, EM, Z)>, -} -/// AFL++'s `fuzzer_stats` -#[derive(Debug, Clone)] -pub struct AFLFuzzerStats<'a> { - /// unix time indicating the start time of afl-fuzz - start_time: u64, - /// unix time corresponding to the last interval - last_update: u64, - /// run time in seconds to the last update of this file - run_time: u64, - /// process id of the fuzzer process - fuzzer_pid: u32, - /// queue cycles completed so far - cycles_done: u64, - /// number of queue cycles without any new paths found - cycles_wo_find: u64, - /// longest time in seconds no new path was found - time_wo_finds: u64, - /// Time spent fuzzing - fuzz_time: u64, - /// Time spent calibrating inputs - calibration_time: u64, - /// Time spent syncing with foreign fuzzers - /// NOTE: Syncing between our own instances is not counted. - sync_time: u64, - /// TODO - trim_time: u64, - /// number of fuzzer executions attempted (what does attempted mean here?) - execs_done: u64, - /// overall number of execs per second - execs_per_sec: u64, - /// TODO - execs_ps_last_min: u64, - /// total number of entries in the queue - corpus_count: usize, - /// number of queue entries that are favored - corpus_favored: usize, - /// number of entries discovered through local fuzzing - corpus_found: usize, - /// number of entries imported from other instances - corpus_imported: usize, - /// number of levels in the generated data set - max_depth: u64, - /// currently processed entry number - cur_item: usize, - /// number of favored entries still waiting to be fuzzed - pending_favs: usize, - /// number of all entries waiting to be fuzzed - pending_total: usize, - /// number of test cases showing variable behavior - corpus_variable: u64, - /// percentage of bitmap bytes that behave consistently - stability: f64, - /// percentage of edge coverage found in the map so far, - bitmap_cvg: f64, - /// number of unique crashes recorded - saved_crashes: u64, - /// number of unique hangs encountered - saved_hangs: u64, - /// seconds since the last find was found - last_find: Duration, - /// seconds since the last crash was found - last_crash: Duration, - /// seconds since the last hang was found - last_hang: Duration, - /// execs since the last crash was found - execs_since_crash: u64, - /// the -t command line value - exec_timeout: u64, - /// real time of the slowest execution in ms - slowest_exec_ms: u128, - /// max rss usage reached during fuzzing in MB - peak_rss_mb: i64, - /// TODO - cpu_affinity: usize, - /// how many edges have been found - edges_found: u64, - /// Size of our edges map - total_edges: u64, - /// how many edges are non-deterministic - var_byte_count: usize, - /// TODO: - havoc_expansion: usize, - /// Amount of automatic dict entries found - auto_dict_entries: usize, - /// TODO: - testcache_size: usize, - /// TODO: - testcache_count: usize, - /// TODO: - testcache_evict: usize, - /// banner text (e.g., the target name) - afl_banner: &'a Cow<'static, str>, - /// the version of AFL++ used - afl_version: &'a Cow<'static, str>, - /// default, persistent, qemu, unicorn, non-instrumented - target_mode: &'a Cow<'static, str>, - /// full command line used for the fuzzing session - command_line: &'a str, -} -/// AFL++'s `plot_data` -#[derive(Debug, Clone)] -pub struct AFLPlotData<'a> { - relative_time: &'a u64, - cycles_done: &'a u64, - cur_item: &'a usize, - corpus_count: &'a usize, - pending_total: &'a usize, - pending_favs: &'a usize, - /// Note: renamed `map_size` -> `total_edges` for consistency with `fuzzer_stats` - total_edges: &'a u64, - saved_crashes: &'a u64, - saved_hangs: &'a u64, - max_depth: &'a u64, - execs_per_sec: &'a u64, - /// Note: renamed `total_execs` -> `execs_done` for consistency with `fuzzer_stats` - execs_done: &'a u64, - edges_found: &'a u64, + phantom: PhantomData<(E, EM, Z)>, } -impl UsesState for AflStatsStage +impl UsesState for StatsStage where E: UsesState, - EM: EventFirer, - Z: UsesState, { type State = E::State; } -impl Stage for AflStatsStage +impl Stage for StatsStage where - E: UsesState + HasObservers, - EM: EventFirer, - Z: UsesState + HasScheduler, - E::State: HasImported - + HasCorpus - + HasMetadata - + HasStartTime - + HasExecutions - + HasNamedMetadata - + Stoppable, - E::Observers: MatchNameRef, - O: MapObserver, - C: AsRef + Named, - ::Scheduler: HasQueueCycles, - <::State as HasCorpus>::Corpus: Corpus, + E: UsesState, + EM: EventFirer, + Z: UsesState, + Self::State: HasImported + HasCorpus + HasMetadata, { fn perform( &mut self, - fuzzer: &mut Z, - executor: &mut E, - state: &mut E::State, + _fuzzer: &mut Z, + _executor: &mut E, + state: &mut Self::State, _manager: &mut EM, ) -> Result<(), Error> { - let Some(corpus_idx) = state.current_corpus_id()? else { + let Some(corpus_id) = state.current_corpus_id()? else { return Err(Error::illegal_state( "state is not currently processing a corpus index", )); }; - let testcase = state.corpus().get(corpus_idx)?.borrow(); - // NOTE: scheduled_count represents the amount of fuzz runs a - // testcase has had. Since this stage is kept at the very end of stage list, - // the entry would have been fuzzed already (and should contain IsFavoredMetadata) but would have a scheduled count of zero - // since the scheduled count is incremented after all stages have been run. - if testcase.scheduled_count() == 0 { - // New testcase! - self.cycles_wo_finds = 0; - self.update_last_find(); - #[cfg(feature = "track_hit_feedbacks")] - { - self.maybe_update_last_crash(&testcase, state); - self.maybe_update_last_hang(&testcase, state); - } - self.update_has_fuzzed_size(); - self.maybe_update_is_favored_size(&testcase); - } - self.maybe_update_slowest_exec(&testcase); - self.maybe_update_max_depth(&testcase); - // See if we actually need to run the stage, if not, avoid dynamic value computation. - if !self.check_interval() { - return Ok(()); + // Report your stats every `STATS_REPORT_INTERVAL` + // compute pending, pending_favored, imported, own_finds + { + let testcase = state.corpus().get(corpus_id)?.borrow(); + if testcase.scheduled_count() == 0 { + self.has_fuzzed_size += 1; + if testcase.has_metadata::() { + self.is_favored_size += 1; + } + } else { + return Ok(()); + } } let corpus_size = state.corpus().count(); - let total_executions = *state.executions(); - - let scheduler = fuzzer.scheduler(); - let queue_cycles = scheduler.queue_cycles(); - self.maybe_update_cycles(queue_cycles); - self.maybe_update_cycles_wo_finds(queue_cycles); + let pending_size = corpus_size - self.has_fuzzed_size; + let pend_favored_size = corpus_size - self.is_favored_size; + self.imported_size = *state.imported(); + self.own_finds_size = corpus_size - self.imported_size; - let observers = executor.observers(); - let map_observer = observers - .get(&self.map_observer_handle) - .ok_or_else(|| Error::key_not_found("invariant: MapObserver not found".to_string()))? - .as_ref(); - let filled_entries_in_map = map_observer.count_bytes(); - let map_size = map_observer.usable_count(); - // Since we do not calibrate when using `QueueScheduler`; we cannot calculate unstable entries. - let unstable_entries_in_map = state - .metadata_map() - .get::() - .map_or(0, |m| m.unstable_entries().len()); + let cur = current_time(); - let auto_dict_entries = if self.autotokens_enabled { - state - .metadata::()? - .len() - .saturating_sub(self.dict_count) - } else { - 0 - }; - let stats = AFLFuzzerStats { - start_time: self.start_time, - last_update: self.last_report_time.as_secs(), - run_time: self.last_report_time.as_secs() - self.start_time, - fuzzer_pid: self.pid, - cycles_done: queue_cycles, - cycles_wo_find: self.cycles_wo_finds, - fuzz_time: state - .metadata::() - .map_or(Duration::from_secs(0), |d| d.0) - .as_secs(), - calibration_time: state - .metadata::() - .map_or(Duration::from_secs(0), |d| d.0) - .as_secs(), - sync_time: state - .metadata::() - .map_or(Duration::from_secs(0), |d| d.0) - .as_secs(), - trim_time: 0, // TODO - execs_done: total_executions, - execs_per_sec: *state.executions(), // TODO - execs_ps_last_min: *state.executions(), // TODO - max_depth: self.max_depth, - corpus_count: corpus_size, - corpus_favored: corpus_size - self.is_favored_size, - corpus_found: corpus_size - state.imported(), - corpus_imported: *state.imported(), - cur_item: corpus_idx.into(), - pending_total: corpus_size - self.has_fuzzed_size, - pending_favs: 0, // TODO - time_wo_finds: (current_time() - self.last_find).as_secs(), - corpus_variable: 0, - stability: self.calculate_stability(unstable_entries_in_map, filled_entries_in_map), - #[allow(clippy::cast_precision_loss)] - bitmap_cvg: (filled_entries_in_map as f64 / map_size as f64) * 100.0, - saved_crashes: self.saved_crashes, - saved_hangs: self.saved_hangs, - last_find: self.last_find, - last_hang: self.last_hang, - last_crash: self.last_crash, - execs_since_crash: total_executions - self.execs_at_last_objective, - exec_timeout: self.exec_timeout, - slowest_exec_ms: self.slowest_exec.as_millis(), - #[cfg(unix)] - peak_rss_mb: peak_rss_mb_child_processes()?, - #[cfg(not(unix))] - peak_rss_mb: 0, // TODO for Windows - cpu_affinity: self.core_id.0, - total_edges: map_size as u64, - edges_found: filled_entries_in_map, - var_byte_count: unstable_entries_in_map, - havoc_expansion: 0, // TODO - auto_dict_entries, - testcache_size: 0, - testcache_count: 0, - testcache_evict: 0, - afl_banner: &self.afl_banner, - afl_version: &self.afl_version, - target_mode: &self.target_mode, - command_line: &self.command_line, - }; - let plot_data = AFLPlotData { - corpus_count: &stats.corpus_count, - cur_item: &stats.cur_item, - cycles_done: &stats.cycles_done, - edges_found: &stats.edges_found, - total_edges: &stats.total_edges, - execs_per_sec: &stats.execs_per_sec, - pending_total: &stats.pending_total, - pending_favs: &stats.pending_favs, - max_depth: &stats.max_depth, - relative_time: &stats.run_time, - saved_hangs: &stats.saved_hangs, - saved_crashes: &stats.saved_crashes, - execs_done: &stats.execs_done, - }; - self.write_fuzzer_stats(&stats)?; - if self.plot_file_path.is_some() { - self.write_plot_data(&plot_data)?; + if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval { + #[cfg(feature = "std")] + { + let json = json!({ + "pending":pending_size, + "pend_fav":pend_favored_size, + "own_finds":self.own_finds_size, + "imported":self.imported_size, + }); + _manager.fire( + state, + Event::UpdateUserStats { + name: Cow::from("Stats"), + value: UserStats::new( + UserStatsValue::String(Cow::from(json.to_string())), + AggregatorOps::None, + ), + phantom: PhantomData, + }, + )?; + } + #[cfg(not(feature = "std"))] + log::info!( + "pending: {}, pend_favored: {}, own_finds: {}, imported: {}", + pending_size, + pend_favored_size, + self.own_finds_size, + self.imported_size + ); + self.last_report_time = cur; } + Ok(()) } + #[inline] fn should_restart(&mut self, _state: &mut Self::State) -> Result { + // Not running the target so we wont't crash/timeout and, hence, don't need to restore anything Ok(true) } + #[inline] fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { + // Not running the target so we wont't crash/timeout and, hence, don't need to restore anything Ok(()) } } -impl AflStatsStage -where - E: UsesState + HasObservers, - EM: EventFirer, - Z: UsesState, - E::State: HasImported + HasCorpus + HasMetadata + HasExecutions, - C: AsRef + Named, - O: MapObserver, -{ - /// Builder for `AflStatsStage` +impl StatsStage { + /// create a new instance of the [`StatsStage`] #[must_use] - pub fn builder() -> AflStatsStageBuilder { - AflStatsStageBuilder::new() - } - - fn write_fuzzer_stats(&self, stats: &AFLFuzzerStats) -> Result<(), Error> { - let tmp_file = self - .stats_file_path - .parent() - .expect("fuzzer_stats file must have a parent!") - .join(".fuzzer_stats_tmp"); - std::fs::write(&tmp_file, stats.to_string())?; - _ = std::fs::copy(&tmp_file, &self.stats_file_path)?; - std::fs::remove_file(tmp_file)?; - Ok(()) - } - - fn write_plot_data(&self, plot_data: &AFLPlotData) -> Result<(), Error> { - let mut file = OpenOptions::new().append(true).open( - self.plot_file_path - .as_ref() - .expect("invariant; should never occur"), - )?; - writeln!(file, "{plot_data}")?; - Ok(()) - } - - fn maybe_update_is_favored_size(&mut self, testcase: &Testcase) { - if testcase.has_metadata::() { - self.is_favored_size += 1; - } - } - - fn maybe_update_slowest_exec(&mut self, testcase: &Testcase) { - if let Some(exec_time) = testcase.exec_time() { - if exec_time > &self.slowest_exec { - self.slowest_exec = *exec_time; - } - } - } - - fn update_has_fuzzed_size(&mut self) { - self.has_fuzzed_size += 1; - } - - fn maybe_update_max_depth(&mut self, testcase: &Testcase) { - if let Ok(metadata) = testcase.metadata::() { - if metadata.depth() > self.max_depth { - self.max_depth = metadata.depth(); - } - } - } - - fn update_last_find(&mut self) { - self.last_find = current_time(); - } - - #[cfg(feature = "track_hit_feedbacks")] - fn maybe_update_last_crash(&mut self, testcase: &Testcase, state: &E::State) { - #[cfg(feature = "track_hit_feedbacks")] - if testcase - .hit_objectives() - .contains(&Cow::Borrowed(CRASH_FEEDBACK_NAME)) - { - self.last_crash = current_time(); - self.execs_at_last_objective = *state.executions(); - } - } - - #[cfg(feature = "track_hit_feedbacks")] - fn maybe_update_last_hang(&mut self, testcase: &Testcase, state: &E::State) { - if testcase - .hit_objectives() - .contains(&Cow::Borrowed(TIMEOUT_FEEDBACK_NAME)) - { - self.last_hang = current_time(); - self.execs_at_last_objective = *state.executions(); - } - } - - fn check_interval(&mut self) -> bool { - let cur = current_time(); - if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval { - self.last_report_time = cur; - return true; - } - false - } - fn maybe_update_cycles(&mut self, queue_cycles: u64) { - if queue_cycles > self.cycles_done { - self.cycles_done += 1; - } - } - - fn maybe_update_cycles_wo_finds(&mut self, queue_cycles: u64) { - if queue_cycles > self.cycles_done && self.last_find < current_time() { - self.cycles_wo_finds += 1; - } - } - - #[allow(clippy::cast_precision_loss)] - #[allow(clippy::unused_self)] - fn calculate_stability(&self, unstable_entries: usize, filled_entries: u64) -> f64 { - ((filled_entries as f64 - unstable_entries as f64) / filled_entries as f64) * 100.0 - } -} - -impl Display for AFLPlotData<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{},", self.relative_time)?; - write!(f, "{},", self.cycles_done)?; - write!(f, "{},", self.cur_item)?; - write!(f, "{},", self.corpus_count)?; - write!(f, "{},", self.pending_total)?; - write!(f, "{},", self.pending_favs)?; - write!(f, "{},", self.total_edges)?; - write!(f, "{},", self.saved_crashes)?; - write!(f, "{},", self.saved_hangs)?; - write!(f, "{},", self.max_depth)?; - write!(f, "{},", self.execs_per_sec)?; - write!(f, "{},", self.execs_done)?; - write!(f, "{}", self.edges_found)?; - Ok(()) - } -} -impl AFLPlotData<'_> { - fn get_header() -> String { - "# relative_time, cycles_done, cur_item, corpus_count, pending_total, pending_favs, total_edges, saved_crashes, saved_hangs, max_depth, execs_per_sec, execs_done, edges_found".to_string() - } -} -impl Display for AFLFuzzerStats<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "start_time : {}", &self.start_time)?; - writeln!(f, "start_time : {}", &self.start_time)?; - writeln!(f, "last_update : {}", &self.last_update)?; - writeln!(f, "run_time : {}", &self.run_time)?; - writeln!(f, "fuzzer_pid : {}", &self.fuzzer_pid)?; - writeln!(f, "cycles_done : {}", &self.cycles_done)?; - writeln!(f, "cycles_wo_find : {}", &self.cycles_wo_find)?; - writeln!(f, "time_wo_finds : {}", &self.time_wo_finds)?; - writeln!(f, "fuzz_time : {}", &self.fuzz_time)?; - writeln!(f, "calibration_time : {}", &self.calibration_time)?; - writeln!(f, "sync_time : {}", &self.sync_time)?; - writeln!(f, "trim_time : {}", &self.trim_time)?; - writeln!(f, "execs_done : {}", &self.execs_done)?; - writeln!(f, "execs_per_sec : {}", &self.execs_per_sec)?; - writeln!(f, "execs_ps_last_min : {}", &self.execs_ps_last_min)?; - writeln!(f, "corpus_count : {}", &self.corpus_count)?; - writeln!(f, "corpus_favored : {}", &self.corpus_favored)?; - writeln!(f, "corpus_found : {}", &self.corpus_found)?; - writeln!(f, "corpus_imported : {}", &self.corpus_imported)?; - writeln!(f, "max_depth : {}", &self.max_depth)?; - writeln!(f, "cur_item : {}", &self.cur_item)?; - writeln!(f, "pending_favs : {}", &self.pending_favs)?; - writeln!(f, "pending_total : {}", &self.pending_total)?; - writeln!(f, "corpus_variable : {}", &self.corpus_variable)?; - writeln!(f, "stability : {:.2}%", &self.stability)?; - writeln!(f, "bitmap_cvg : {:.2}%", &self.bitmap_cvg)?; - writeln!(f, "saved_crashes : {}", &self.saved_crashes)?; - writeln!(f, "saved_hangs : {}", &self.saved_hangs)?; - writeln!(f, "last_find : {}", &self.last_find.as_secs())?; - writeln!(f, "last_crash : {}", &self.last_crash.as_secs())?; - writeln!(f, "last_hang : {}", &self.last_hang.as_secs())?; - writeln!(f, "execs_since_crash : {}", &self.execs_since_crash)?; - writeln!(f, "exec_timeout : {}", &self.exec_timeout)?; - writeln!(f, "slowest_exec_ms : {}", &self.slowest_exec_ms)?; - writeln!(f, "peak_rss_mb : {}", &self.peak_rss_mb)?; - writeln!(f, "cpu_affinity : {}", &self.cpu_affinity)?; - writeln!(f, "edges_found : {}", &self.edges_found)?; - writeln!(f, "total_edges : {}", &self.total_edges)?; - writeln!(f, "var_byte_count : {}", &self.var_byte_count)?; - writeln!(f, "havoc_expansion : {}", &self.havoc_expansion)?; - writeln!(f, "auto_dict_entries : {}", &self.auto_dict_entries)?; - writeln!(f, "testcache_size : {}", &self.testcache_size)?; - writeln!(f, "testcache_count : {}", &self.testcache_count)?; - writeln!(f, "testcache_evict : {}", &self.testcache_evict)?; - writeln!(f, "afl_banner : {}", self.afl_banner)?; - writeln!(f, "afl_version : {}", self.afl_version)?; - writeln!(f, "target_mode : {}", self.target_mode)?; - writeln!(f, "command_line : {}", self.command_line)?; - Ok(()) - } -} -/// Get the command used to invoke the fuzzer -#[must_use] -pub fn get_run_cmdline() -> Cow<'static, str> { - let args: Vec = std::env::args().collect(); - Cow::Owned(args.join(" ")) -} - -/// The Builder for `AflStatsStage` -#[derive(Debug)] -pub struct AflStatsStageBuilder { - stats_file_path: Option, - plot_file_path: Option, - core_id: Option, - map_observer_handle: Option>, - uses_autotokens: bool, - report_interval: Duration, - dict_count: usize, - exec_timeout: u64, - banner: String, - version: String, - target_mode: String, - phantom_data: PhantomData<(O, E, EM, Z)>, -} - -impl AflStatsStageBuilder -where - E: UsesState + HasObservers, - EM: EventFirer, - Z: UsesState, - E::State: HasImported + HasCorpus + HasMetadata + HasExecutions, - C: AsRef + Named, - O: MapObserver, -{ - fn new() -> Self { + pub fn new(interval: Duration) -> Self { Self { - report_interval: Duration::from_secs(AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS), - stats_file_path: None, - plot_file_path: None, - core_id: None, - map_observer_handle: None, - uses_autotokens: false, - dict_count: 0, - exec_timeout: 0, - banner: String::default(), - version: String::default(), - target_mode: String::default(), - phantom_data: PhantomData, + stats_report_interval: interval, + ..Default::default() } } +} - /// The file path to which we will write the fuzzer stats - #[must_use] - pub fn stats_file(mut self, path: PathBuf) -> Self { - self.stats_file_path = Some(path); - self - } - /// The file path to which we will write the plot data +impl Default for StatsStage { + /// the default instance of the [`StatsStage`] #[must_use] - pub fn plot_file(mut self, path: PathBuf) -> Self { - self.plot_file_path = Some(path); - self - } - /// The core we are bound to - #[must_use] - pub fn core_id(mut self, core_id: CoreId) -> Self { - self.core_id = Some(core_id); - self - } - /// The interval with which we report stats - #[must_use] - pub fn report_interval(mut self, interval: Duration) -> Self { - self.report_interval = interval; - self - } - /// Our `MapObserver` - #[must_use] - pub fn map_observer(mut self, map_observer: &C) -> Self { - self.map_observer_handle = Some(map_observer.handle()); - self - } - /// If we use autotokens provided by the target - #[must_use] - pub fn uses_autotokens(mut self, uses: bool) -> Self { - self.uses_autotokens = uses; - self - } - /// The tokens utilized by the fuzzer - #[must_use] - pub fn tokens(mut self, tokens: &Tokens) -> Self { - self.dict_count = tokens.len(); - self - } - /// AFL++ Banner (typically the target) - #[must_use] - pub fn banner(mut self, banner: String) -> Self { - self.banner = banner; - self - } - /// Version of the fuzzer - #[must_use] - pub fn version(mut self, version: String) -> Self { - self.version = version; - self - } - /// The "timeout" value used in `TimeoutFeedback` - #[must_use] - pub fn exec_timeout(mut self, timeout: u64) -> Self { - self.exec_timeout = timeout; - self - } - /// Used in the UI (optional) - /// default, persistent, qemu, unicorn, non-instrumented etc - #[must_use] - pub fn target_mode(mut self, target_mode: String) -> Self { - self.target_mode = target_mode; - self - } - - fn create_plot_data_file(path: &Path) -> Result<(), Error> { - if path.exists() { - // check if it contains any data - let file = File::open(path)?; - if BufReader::new(file).lines().next().is_none() { - std::fs::write(path, AFLPlotData::get_header())?; - } - } else { - std::fs::write(path, AFLPlotData::get_header())?; - } - Ok(()) - } - - fn create_fuzzer_stats_file(path: &Path) -> Result<(), Error> { - if !path.exists() { - _ = OpenOptions::new().append(true).create(true).open(path)?; - } - Ok(()) - } - /// Build [`AflStatsStage`] - /// Will error if: - /// Cannot create the stats file - /// Cannot create the plot file (if provided) - /// No `MapObserver` supplied to the builder - /// No `stats_file_path` provieded - pub fn build(self) -> Result, Error> { - if self.stats_file_path.is_none() { - return Err(Error::illegal_argument("Must set `stats_file_path`")); - } - let stats_file_path = self.stats_file_path.unwrap(); - if self.map_observer_handle.is_none() { - return Err(Error::illegal_argument("Must set `map_observer`")); - } - if let Some(ref plot_file) = self.plot_file_path { - Self::create_plot_data_file(plot_file)?; - } - Self::create_fuzzer_stats_file(&stats_file_path)?; - Ok(AflStatsStage { - stats_file_path, - plot_file_path: self.plot_file_path, - map_observer_handle: self.map_observer_handle.unwrap(), - start_time: current_time().as_secs(), - stats_report_interval: self.report_interval, + fn default() -> Self { + Self { has_fuzzed_size: 0, is_favored_size: 0, - cycles_done: 0, - cycles_wo_finds: 0, - execs_at_last_objective: 0, - last_crash: current_time(), - last_find: current_time(), - last_hang: current_time(), - max_depth: 0, - saved_hangs: 0, - saved_crashes: 0, - slowest_exec: Duration::from_secs(0), + own_finds_size: 0, + imported_size: 0, last_report_time: current_time(), - pid: process::id(), - exec_timeout: self.exec_timeout, - target_mode: Cow::Owned(self.target_mode), - afl_banner: Cow::Owned(self.banner), - afl_version: Cow::Owned(self.version), - command_line: get_run_cmdline(), - dict_count: self.dict_count, - core_id: self.core_id.unwrap_or(CoreId(0)), - autotokens_enabled: self.uses_autotokens, - phantom_data: PhantomData, - }) + stats_report_interval: Duration::from_secs(15), + phantom: PhantomData, + } } } From 5eb82b54f639e409fd5346bdc7065b1ea8798889 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 10:57:50 +0200 Subject: [PATCH 39/67] fix ci --- .../command_executor/src/main.rs | 10 ++++++++-- fuzzers/binary_only/qemu_launcher/src/instance.rs | 4 ++-- libafl/src/stages/afl_stats.rs | 5 +++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/fuzzers/baby/backtrace_baby_fuzzers/command_executor/src/main.rs b/fuzzers/baby/backtrace_baby_fuzzers/command_executor/src/main.rs index 6498751e3b..7705633058 100644 --- a/fuzzers/baby/backtrace_baby_fuzzers/command_executor/src/main.rs +++ b/fuzzers/baby/backtrace_baby_fuzzers/command_executor/src/main.rs @@ -83,6 +83,7 @@ pub fn main() { #[derive(Debug)] struct MyExecutor { shmem_id: ShMemId, + timeout: Duration, } impl CommandConfigurator for MyExecutor { @@ -105,11 +106,16 @@ pub fn main() { } fn exec_timeout(&self) -> Duration { - Duration::from_secs(5) + self.timeout + } + fn exec_timeout_mut(&mut self) -> &mut Duration { + &mut self.timeout } } - let mut executor = MyExecutor { shmem_id }.into_executor(tuple_list!(observer, bt_observer)); + let timeout = Duration::from_secs(5); + let mut executor = + MyExecutor { shmem_id, timeout }.into_executor(tuple_list!(observer, bt_observer)); // Generator of printable bytearrays of max size 32 let mut generator = RandPrintablesGenerator::new(32).unwrap(); diff --git a/fuzzers/binary_only/qemu_launcher/src/instance.rs b/fuzzers/binary_only/qemu_launcher/src/instance.rs index 1a0fd277e9..f967d6e421 100644 --- a/fuzzers/binary_only/qemu_launcher/src/instance.rs +++ b/fuzzers/binary_only/qemu_launcher/src/instance.rs @@ -23,7 +23,7 @@ use libafl::{ powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler, }, stages::{ - calibrate::CalibrationStage, power::StdPowerMutationalStage, AflStatsStage, IfStage, + calibrate::CalibrationStage, power::StdPowerMutationalStage, StatsStage, IfStage, ShadowTracingStage, StagesTuple, StdMutationalStage, }, state::{HasCorpus, StdState, UsesState}, @@ -138,7 +138,7 @@ impl Instance<'_, M> { let stats_stage = IfStage::new( |_, _, _, _| Ok(self.options.tui), - tuple_list!(AflStatsStage::new(Duration::from_secs(5))), + tuple_list!(StatsStage::new(Duration::from_secs(5))), ); // Feedback to rate the interestingness of an input diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs index f4dcd87452..a341280bf8 100644 --- a/libafl/src/stages/afl_stats.rs +++ b/libafl/src/stages/afl_stats.rs @@ -10,12 +10,13 @@ use std::{ process, }; +#[cfg(unix)] +use libafl_bolts::os::peak_rss_mb_child_processes; use libafl_bolts::{ core_affinity::CoreId, current_time, - os::peak_rss_mb_child_processes, tuples::{Handle, Handled, MatchNameRef}, - Named, + Named, SerdeAny, }; use serde::{Deserialize, Serialize}; From 049a940c9991529072c22058d0fe48bd08081d51 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 11:01:02 +0200 Subject: [PATCH 40/67] make set_timeout and timeout of HasTimeout inline --- libafl/src/executors/combined.rs | 2 ++ libafl/src/executors/command.rs | 2 ++ libafl/src/executors/differential.rs | 2 ++ libafl/src/executors/forkserver.rs | 2 ++ libafl/src/executors/mod.rs | 3 ++- libafl/src/executors/shadow.rs | 3 ++- 6 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libafl/src/executors/combined.rs b/libafl/src/executors/combined.rs index e5e624e2fa..639f1f1528 100644 --- a/libafl/src/executors/combined.rs +++ b/libafl/src/executors/combined.rs @@ -66,11 +66,13 @@ where A: HasTimeout, B: HasTimeout, { + #[inline] fn set_timeout(&mut self, timeout: Duration) { self.primary.set_timeout(timeout); self.secondary.set_timeout(timeout); } + #[inline] fn timeout(&self) -> Duration { assert!( self.primary.timeout() == self.secondary.timeout(), diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index dec5c7a5c0..6ed35dc993 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -293,10 +293,12 @@ where S: HasCorpus, T: CommandConfigurator<::Input>, { + #[inline] fn set_timeout(&mut self, timeout: Duration) { *self.configurer.exec_timeout_mut() = timeout; } + #[inline] fn timeout(&self) -> Duration { self.configurer.exec_timeout() } diff --git a/libafl/src/executors/differential.rs b/libafl/src/executors/differential.rs index db07cee62c..87505531ea 100644 --- a/libafl/src/executors/differential.rs +++ b/libafl/src/executors/differential.rs @@ -126,11 +126,13 @@ where A: HasTimeout, B: HasTimeout, { + #[inline] fn set_timeout(&mut self, timeout: core::time::Duration) { self.primary.set_timeout(timeout); self.secondary.set_timeout(timeout); } + #[inline] fn timeout(&self) -> core::time::Duration { assert!( self.primary.timeout() == self.secondary.timeout(), diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index d18efe5ae2..d5b00857ae 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -1486,10 +1486,12 @@ impl HasTimeout for ForkserverExecutor where SP: ShMemProvider, { + #[inline] fn set_timeout(&mut self, timeout: Duration) { self.timeout = TimeSpec::from_duration(timeout); } + #[inline] fn timeout(&self) -> Duration { self.timeout.into() } diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index feb79dc3f3..993a85fe2e 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -146,8 +146,9 @@ where /// A trait that allows to get/set an `Executor`'s timeout thresold pub trait HasTimeout { - /// Get an timeout + /// Get a timeout fn timeout(&self) -> Duration; + /// Set timeout fn set_timeout(&mut self, timeout: Duration); } diff --git a/libafl/src/executors/shadow.rs b/libafl/src/executors/shadow.rs index 94bf4baadc..0ec225b1ca 100644 --- a/libafl/src/executors/shadow.rs +++ b/libafl/src/executors/shadow.rs @@ -85,10 +85,11 @@ impl HasTimeout for ShadowExecutor where E: HasTimeout, { + #[inline] fn set_timeout(&mut self, timeout: Duration) { self.executor.set_timeout(timeout); } - + #[inline] fn timeout(&self) -> Duration { self.executor.timeout() } From fbad34685b6dbeadf663a6862ac2a730648f1060 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 11:01:53 +0200 Subject: [PATCH 41/67] fmt --- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 89a7eb885d..d898c0498c 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -23,8 +23,8 @@ use libafl::{ IndexesLenTimeMinimizerScheduler, QueueScheduler, StdWeightedScheduler, }, stages::{ - mutational::MultiMutationalStage, afl_stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime}, + mutational::MultiMutationalStage, time_tracker::TimeTrackingStageWrapper, CalibrationStage, ColorizationStage, IfStage, StagesTuple, StdMutationalStage, StdPowerMutationalStage, SyncFromDiskStage, From 55851c9f6138980b8fe321743df22deff53c9f1b Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 11:04:10 +0200 Subject: [PATCH 42/67] add gitignore --- fuzzers/binary_only/qemu_launcher/src/instance.rs | 4 ++-- fuzzers/forkserver/libafl-fuzz/.gitignore | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 fuzzers/forkserver/libafl-fuzz/.gitignore diff --git a/fuzzers/binary_only/qemu_launcher/src/instance.rs b/fuzzers/binary_only/qemu_launcher/src/instance.rs index f967d6e421..979fe6bd22 100644 --- a/fuzzers/binary_only/qemu_launcher/src/instance.rs +++ b/fuzzers/binary_only/qemu_launcher/src/instance.rs @@ -23,8 +23,8 @@ use libafl::{ powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler, }, stages::{ - calibrate::CalibrationStage, power::StdPowerMutationalStage, StatsStage, IfStage, - ShadowTracingStage, StagesTuple, StdMutationalStage, + calibrate::CalibrationStage, power::StdPowerMutationalStage, IfStage, ShadowTracingStage, + StagesTuple, StatsStage, StdMutationalStage, }, state::{HasCorpus, StdState, UsesState}, Error, HasMetadata, NopFuzzer, diff --git a/fuzzers/forkserver/libafl-fuzz/.gitignore b/fuzzers/forkserver/libafl-fuzz/.gitignore new file mode 100644 index 0000000000..0a452b5af1 --- /dev/null +++ b/fuzzers/forkserver/libafl-fuzz/.gitignore @@ -0,0 +1,4 @@ +test/out-cmplog +test/out-instr +test/output-cmplog/ +test/output/ From 3ce8a08b6cead493a94ec09a9a6aed2493398a78 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 11:18:49 +0200 Subject: [PATCH 43/67] serde_any fix --- libafl/src/stages/afl_stats.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs index a341280bf8..01bebdb695 100644 --- a/libafl/src/stages/afl_stats.rs +++ b/libafl/src/stages/afl_stats.rs @@ -16,7 +16,7 @@ use libafl_bolts::{ core_affinity::CoreId, current_time, tuples::{Handle, Handled, MatchNameRef}, - Named, SerdeAny, + Named, }; use serde::{Deserialize, Serialize}; @@ -38,7 +38,7 @@ use crate::{ pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60; /// `CalibrationTime` - Use in conjunction with `TimeTrackingFeedback` -#[derive(Debug, SerdeAny, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct CalibrationTime(pub Duration); impl From for CalibrationTime { fn from(value: Duration) -> Self { @@ -46,8 +46,10 @@ impl From for CalibrationTime { } } +libafl_bolts::impl_serdeany!(CalibrationTime); + /// `SyncTime` - Use in conjunction with `TimeTrackingFeedback` -#[derive(Debug, SerdeAny, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct SyncTime(pub Duration); impl From for SyncTime { fn from(value: Duration) -> Self { @@ -55,8 +57,10 @@ impl From for SyncTime { } } +libafl_bolts::impl_serdeany!(SyncTime); + /// `FuzzTime` - Use in conjunction with `TimeTrackingFeedback` -#[derive(Debug, SerdeAny, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct FuzzTime(pub Duration); impl From for FuzzTime { fn from(value: Duration) -> Self { @@ -64,6 +68,8 @@ impl From for FuzzTime { } } +libafl_bolts::impl_serdeany!(FuzzTime); + /// The [`AflStatsStage`] is a Stage that calculates and writes /// AFL++'s `fuzzer_stats` and `plot_data` information. #[derive(Debug, Clone)] From 00e5babc40d1cbbe69e0810785abc0470d6d3423 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 11:35:18 +0200 Subject: [PATCH 44/67] tmate --- .github/workflows/build_and_test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5ffc40d3bd..b3a300a14f 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -304,6 +304,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/fuzzer-tester-prepare + - uses: mxschmitt/action-tmate@v3 - name: Build and run example fuzzers (Linux) if: runner.os == 'Linux' shell: bash @@ -518,4 +519,4 @@ jobs: #- name: Try if clang works # run: clang -v #- name: Windows Test - # run: C:\Rust\.cargo\bin\cargo.exe test --verbose \ No newline at end of file + # run: C:\Rust\.cargo\bin\cargo.exe test --verbose From 9e3f66d2ecebae63305b8692760311e2e7bddfd0 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 15:57:49 +0200 Subject: [PATCH 45/67] misc --- fuzzers/forkserver/libafl-fuzz/Makefile.toml | 12 ++++++------ fuzzers/forkserver/libafl-fuzz/src/corpus.rs | 2 +- fuzzers/forkserver/libafl-fuzz/src/feedback/seed.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/Makefile.toml b/fuzzers/forkserver/libafl-fuzz/Makefile.toml index 1022f3d6ae..59f71efa52 100644 --- a/fuzzers/forkserver/libafl-fuzz/Makefile.toml +++ b/fuzzers/forkserver/libafl-fuzz/Makefile.toml @@ -12,7 +12,7 @@ FUZZER = '${CARGO_TARGET_DIR}/${PROFILE_DIR}/${FUZZER_NAME}' LLVM_CONFIG = { value = "llvm-config-18", condition = { env_not_set = [ "LLVM_CONFIG", ] } } -AFL_VERSION = "8b35dd49be5f846e945f6d6a9414623d195a99cb" +AFL_VERSION = "78b7e14c73baacf1d88b3c03955e78f5080d17ba" AFL_DIR = { value = "${PROJECT_DIR}/AFLplusplus" } AFL_CC_PATH = { value = "${AFL_DIR}/afl-clang-fast" } CC = { value = "clang" } @@ -81,7 +81,7 @@ script = ''' AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-instr.c -o ./test/out-instr export LIBAFL_DEBUG_OUTPUT=1 -export AFL_CORES=1 +export AFL_CORES=0 export AFL_STATS_INTERVAL=1 timeout 5 ${FUZZER} -i ./test/seeds -o ./test/output ./test/out-instr || true @@ -113,7 +113,7 @@ script_runner = "@shell" script = ''' # cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog -AFL_CORES=1 timeout 5 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true +AFL_CORES=0 timeout 5 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true ls -l ./test/output-cmplog/fuzzer_main/hangs ls -l ./test/output-cmplog/fuzzer_main/crashes test -n "$( ls ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/hangs/id:0000* ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/crashes/id:0000*)" || { @@ -129,7 +129,7 @@ script = ''' ${CC} -no-pie ./test/test-instr.c -o ./test/out-frida export AFL_PATH=${AFL_DIR} -export AFL_CORES=1 +export AFL_CORES=0 export AFL_STATS_INTERVAL=1 timeout 5 ${FUZZER} -m 0 -O -i ./test/seeds_frida -o ./test/output-frida -- ./test/out-frida || true @@ -177,7 +177,7 @@ ${CC} -pie -fPIE ./test/test-instr.c -o ./test/out-qemu ${CC} -o ./test/out-qemu-cmpcov ./test/test-cmpcov.c export AFL_PATH=${AFL_DIR} -export AFL_CORES=1 +export AFL_CORES=0 export AFL_STATS_INTERVAL=1 timeout 5 ${FUZZER} -m 0 -Q -i ./test/seeds_qemu -o ./test/output-qemu -- ./test/out-qemu || true @@ -208,7 +208,7 @@ dependencies = ["build_afl", "build_qemuafl", "build_libafl_fuzz"] script_runner = "@shell" script = ''' export AFL_PATH=${AFL_DIR} -export AFL_CORES=1 +export AFL_CORES=0 export AFL_STATS_INTERVAL=1 # TODO: test unicorn persistent mode once it's fixed on AFL++ diff --git a/fuzzers/forkserver/libafl-fuzz/src/corpus.rs b/fuzzers/forkserver/libafl-fuzz/src/corpus.rs index 66fa80552f..031a3f361a 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/corpus.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/corpus.rs @@ -128,7 +128,7 @@ pub fn check_autoresume(fuzzer_dir: &Path, auto_resume: bool) -> Result OUTPUT_GRACE * 60 { - return Err(Error::illegal_state("The job output directory already exists and contains results! use AFL_AUTORESUME=true or provide \"-\" for -i ")); + return Err(Error::illegal_state("The job output directory already exists and contains results! use AFL_AUTORESUME=1 or provide \"-\" for -i ")); } } if !auto_resume { diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/seed.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/seed.rs index 3626ae6940..f1eb674feb 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/feedback/seed.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/seed.rs @@ -65,7 +65,7 @@ where if !self.ignore_timeouts { if !self.ignore_seed_issues || self.exit_on_seed_issues { return Err(Error::invalid_corpus( - "input led to a timeout; use AFL_IGNORE_SEED_ISSUES=true", + "input led to a timeout; use AFL_IGNORE_SEED_ISSUES=1", )); } return Ok(false); From dcb2f8114e65ba4ec179f7d027034fbec8a00fbe Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 15:59:00 +0200 Subject: [PATCH 46/67] remove tmate --- .github/workflows/build_and_test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index b3a300a14f..6f8fadbc64 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -304,7 +304,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/fuzzer-tester-prepare - - uses: mxschmitt/action-tmate@v3 - name: Build and run example fuzzers (Linux) if: runner.os == 'Linux' shell: bash From 4344350d3c19f8efff597cfd43ff8b13cdea5b80 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 16:37:20 +0200 Subject: [PATCH 47/67] test --- .../src/feedback/capture_timeout.rs | 23 +++++++++++++--- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 21 +++++++++------ .../libafl-fuzz/src/stages/verify_timeouts.rs | 26 ++++++++++++++----- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs index c1940dfa5b..f62b17c019 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs @@ -12,13 +12,15 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::stages::verify_timeouts::TimeoutsToVerify; -#[derive(Serialize, Deserialize)] -pub struct CaptureTimeoutFeedback {} +#[derive(Debug, Serialize, Deserialize)] +pub struct CaptureTimeoutFeedback { + enabled: bool, +} impl CaptureTimeoutFeedback { /// Create a new [`CaptureTimeoutFeedback`]. pub fn new() -> Self { - Self {} + Self { enabled: true } } } @@ -46,7 +48,7 @@ where _observers: &OT, exit_kind: &ExitKind, ) -> Result { - if matches!(exit_kind, ExitKind::Timeout) { + if self.enabled && matches!(exit_kind, ExitKind::Timeout) { let timeouts = state.metadata_or_insert_with(|| TimeoutsToVerify::::new()); timeouts.push(input.clone()); } @@ -69,3 +71,16 @@ where Ok(false) } } + +impl CaptureTimeoutFeedback { + /// Enable capturing of timeouts for re-running. + /// WARN: when re-running the timeouts, this feedback must be disabled + /// else it will keep capturing timeouts + pub fn enable(&mut self) { + self.enabled = true; + } + /// Disable capturing of timeouts. + pub fn disable(&mut self) { + self.enabled = false; + } +} diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index d898c0498c..373b296490 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -146,6 +146,18 @@ where opt, ); + let mut capture_timeout_feedback = CaptureTimeoutFeedback::new(); + + // Like AFL++ we re-run all timeouts with double the timeout to assert that they are not false positives + let timeout_verify_stage = IfStage::new( + |_, _, _, _| Ok(!opt.ignore_timeouts), + tuple_list!(VerifyTimeoutsStage::new( + &mut capture_timeout_feedback, + Duration::from_millis( + opt.hang_timeout + ),)), + ); + /* * Feedback to decide if the Input is "solution worthy". * We check if it's a crash or a timeout (if we are configured to consider timeouts) @@ -159,7 +171,7 @@ where CrashFeedback::new(), feedback_and!( ConstFeedback::new(!opt.ignore_timeouts), - CaptureTimeoutFeedback::new() + capture_timeout_feedback, ) ), MaxMapFeedback::with_name("edges_objective", &edges_observer) @@ -221,13 +233,6 @@ where // Create our Fuzzer let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - // Like AFL++ we re-run all timeouts with double the timeout to assert that they are not false positives - let timeout_verify_stage = IfStage::new( - |_, _, _, _| Ok(!opt.ignore_timeouts), - tuple_list!(VerifyTimeoutsStage::new(Duration::from_millis( - opt.hang_timeout - ),)), - ); // Set LD_PRELOAD (Linux) && DYLD_INSERT_LIBRARIES (OSX) for target. if let Some(preload_env) = &opt.afl_preload { diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index 864d835ee0..729dad9890 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -14,18 +14,24 @@ use libafl::{ }; use libafl_bolts::Error; use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +use crate::feedback::capture_timeout::CaptureTimeoutFeedback; #[derive(Debug)] -pub struct VerifyTimeoutsStage { +pub struct VerifyTimeoutsStage<'a, E, S> { doubled_timeout: Duration, original_timeout: Duration, - // The handle to our time observer + capture_feedback: &'a mut CaptureTimeoutFeedback, phantom: PhantomData<(E, S)>, } -impl VerifyTimeoutsStage { +impl<'a, E, S> VerifyTimeoutsStage<'a, E, S> { /// Create a `VerifyTimeoutsStage` - pub fn new(configured_timeout: Duration) -> Self { + pub fn new( + capture_feedback: &'a mut CaptureTimeoutFeedback, + configured_timeout: Duration, + ) -> Self { Self { + capture_feedback, doubled_timeout: configured_timeout * 2, original_timeout: configured_timeout, phantom: PhantomData, @@ -33,7 +39,7 @@ impl VerifyTimeoutsStage { } } -impl UsesState for VerifyTimeoutsStage +impl UsesState for VerifyTimeoutsStage<'_, E, S> where S: State, { @@ -63,9 +69,12 @@ impl TimeoutsToVerify { pub fn pop(&mut self) -> Option { self.inputs.pop_front() } + pub fn count(&self) -> usize { + self.inputs.len() + } } -impl Stage for VerifyTimeoutsStage +impl Stage for VerifyTimeoutsStage<'_, E, S> where E::Observers: ObserversTuple<::Input, ::State>, E: Executor + HasObservers + HasTimeout, @@ -85,12 +94,17 @@ where let mut timeouts = state .metadata_or_insert_with(TimeoutsToVerify::<::Input>::new) .clone(); + if timeouts.count() == 0 { + return Ok(()); + } executor.set_timeout(self.doubled_timeout); + self.capture_feedback.disable(); while let Some(input) = timeouts.pop() { let exit_kind = executor.run_target(fuzzer, state, manager, &input)?; if matches!(exit_kind, ExitKind::Timeout) {} } executor.set_timeout(self.original_timeout); + self.capture_feedback.enable(); let res = state.metadata_mut::>().unwrap(); *res = TimeoutsToVerify::::new(); Ok(()) From 08602355b92fa6e7080d4b142ddb7a7540bfd8cc Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 17:02:40 +0200 Subject: [PATCH 48/67] coordinate between capture_timeout and verify_timeout --- fuzzers/forkserver/libafl-fuzz/Makefile.toml | 2 +- .../src/feedback/capture_timeout.rs | 28 ++++++------------- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 21 ++++++++------ .../libafl-fuzz/src/stages/verify_timeouts.rs | 23 +++++++-------- 4 files changed, 31 insertions(+), 43 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/Makefile.toml b/fuzzers/forkserver/libafl-fuzz/Makefile.toml index 59f71efa52..8a732a8d67 100644 --- a/fuzzers/forkserver/libafl-fuzz/Makefile.toml +++ b/fuzzers/forkserver/libafl-fuzz/Makefile.toml @@ -113,7 +113,7 @@ script_runner = "@shell" script = ''' # cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog -AFL_CORES=0 timeout 5 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true +LIBAFL_DEBUG_OUTPUT=1 AFL_CORES=0 timeout 10 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true ls -l ./test/output-cmplog/fuzzer_main/hangs ls -l ./test/output-cmplog/fuzzer_main/crashes test -n "$( ls ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/hangs/id:0000* ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/crashes/id:0000*)" || { diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs index f62b17c019..985e4565c9 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fmt::Debug}; +use std::{borrow::Cow, cell::RefCell, fmt::Debug, rc::Rc}; use libafl::{ corpus::Testcase, @@ -8,19 +8,19 @@ use libafl::{ HasMetadata, }; use libafl_bolts::{Error, Named}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Serialize}; use crate::stages::verify_timeouts::TimeoutsToVerify; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug)] pub struct CaptureTimeoutFeedback { - enabled: bool, + enabled: Rc>, } impl CaptureTimeoutFeedback { /// Create a new [`CaptureTimeoutFeedback`]. - pub fn new() -> Self { - Self { enabled: true } + pub fn new(enabled: Rc>) -> Self { + Self { enabled } } } @@ -48,7 +48,8 @@ where _observers: &OT, exit_kind: &ExitKind, ) -> Result { - if self.enabled && matches!(exit_kind, ExitKind::Timeout) { + if *self.enabled.borrow() && matches!(exit_kind, ExitKind::Timeout) { + println!("captured!"); let timeouts = state.metadata_or_insert_with(|| TimeoutsToVerify::::new()); timeouts.push(input.clone()); } @@ -71,16 +72,3 @@ where Ok(false) } } - -impl CaptureTimeoutFeedback { - /// Enable capturing of timeouts for re-running. - /// WARN: when re-running the timeouts, this feedback must be disabled - /// else it will keep capturing timeouts - pub fn enable(&mut self) { - self.enabled = true; - } - /// Disable capturing of timeouts. - pub fn disable(&mut self) { - self.enabled = false; - } -} diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 373b296490..453d40cfba 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -1,7 +1,9 @@ use std::{ - borrow::Cow, + borrow::{BorrowMut, Cow}, + cell::RefCell, marker::PhantomData, path::{Path, PathBuf}, + rc::Rc, time::Duration, }; @@ -146,18 +148,20 @@ where opt, ); - let mut capture_timeout_feedback = CaptureTimeoutFeedback::new(); - + // We need to share this reference as [`VerifyTimeoutsStage`] will toggle this + // value before re-running the alleged timeouts so we don't keep capturing timeouts infinitely. + let enable_capture_timeouts = Rc::new(RefCell::new(false)); + let capture_timeout_feedback = CaptureTimeoutFeedback::new(Rc::clone(&enable_capture_timeouts)); + // Like AFL++ we re-run all timeouts with double the timeout to assert that they are not false positives let timeout_verify_stage = IfStage::new( |_, _, _, _| Ok(!opt.ignore_timeouts), tuple_list!(VerifyTimeoutsStage::new( - &mut capture_timeout_feedback, - Duration::from_millis( - opt.hang_timeout - ),)), + enable_capture_timeouts, + Duration::from_millis(opt.hang_timeout), + )), ); - + /* * Feedback to decide if the Input is "solution worthy". * We check if it's a crash or a timeout (if we are configured to consider timeouts) @@ -233,7 +237,6 @@ where // Create our Fuzzer let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - // Set LD_PRELOAD (Linux) && DYLD_INSERT_LIBRARIES (OSX) for target. if let Some(preload_env) = &opt.afl_preload { std::env::set_var("LD_PRELOAD", preload_env); diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index 729dad9890..533281ce98 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -1,7 +1,7 @@ //! Stage that re-runs inputs deemed as timeouts with double the timeout to assert that they are //! not false positives. AFL++ style use core::time::Duration; -use std::{collections::VecDeque, fmt::Debug, marker::PhantomData}; +use std::{cell::RefCell, collections::VecDeque, fmt::Debug, marker::PhantomData, rc::Rc}; use libafl::{ corpus::Corpus, @@ -15,21 +15,17 @@ use libafl::{ use libafl_bolts::Error; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use crate::feedback::capture_timeout::CaptureTimeoutFeedback; #[derive(Debug)] -pub struct VerifyTimeoutsStage<'a, E, S> { +pub struct VerifyTimeoutsStage { doubled_timeout: Duration, original_timeout: Duration, - capture_feedback: &'a mut CaptureTimeoutFeedback, + capture_feedback: Rc>, phantom: PhantomData<(E, S)>, } -impl<'a, E, S> VerifyTimeoutsStage<'a, E, S> { +impl VerifyTimeoutsStage { /// Create a `VerifyTimeoutsStage` - pub fn new( - capture_feedback: &'a mut CaptureTimeoutFeedback, - configured_timeout: Duration, - ) -> Self { + pub fn new(capture_feedback: Rc>, configured_timeout: Duration) -> Self { Self { capture_feedback, doubled_timeout: configured_timeout * 2, @@ -39,7 +35,7 @@ impl<'a, E, S> VerifyTimeoutsStage<'a, E, S> { } } -impl UsesState for VerifyTimeoutsStage<'_, E, S> +impl UsesState for VerifyTimeoutsStage where S: State, { @@ -74,7 +70,7 @@ impl TimeoutsToVerify { } } -impl Stage for VerifyTimeoutsStage<'_, E, S> +impl Stage for VerifyTimeoutsStage where E::Observers: ObserversTuple<::Input, ::State>, E: Executor + HasObservers + HasTimeout, @@ -98,13 +94,14 @@ where return Ok(()); } executor.set_timeout(self.doubled_timeout); - self.capture_feedback.disable(); + *self.capture_feedback.borrow_mut() = false; while let Some(input) = timeouts.pop() { + println!("verifying!"); let exit_kind = executor.run_target(fuzzer, state, manager, &input)?; if matches!(exit_kind, ExitKind::Timeout) {} } executor.set_timeout(self.original_timeout); - self.capture_feedback.enable(); + *self.capture_feedback.borrow_mut() = true; let res = state.metadata_mut::>().unwrap(); *res = TimeoutsToVerify::::new(); Ok(()) From f43f37b22dbc3bce7ef6d099c30d922ee08eb9d9 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 17:11:05 +0200 Subject: [PATCH 49/67] makefile --- fuzzers/forkserver/libafl-fuzz/Makefile.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/Makefile.toml b/fuzzers/forkserver/libafl-fuzz/Makefile.toml index 8a732a8d67..2b483400e0 100644 --- a/fuzzers/forkserver/libafl-fuzz/Makefile.toml +++ b/fuzzers/forkserver/libafl-fuzz/Makefile.toml @@ -113,9 +113,7 @@ script_runner = "@shell" script = ''' # cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog -LIBAFL_DEBUG_OUTPUT=1 AFL_CORES=0 timeout 10 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true -ls -l ./test/output-cmplog/fuzzer_main/hangs -ls -l ./test/output-cmplog/fuzzer_main/crashes +AFL_DEBUG=1 LIBAFL_DEBUG_OUTPUT=1 AFL_CORES=0 timeout 15 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true test -n "$( ls ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/hangs/id:0000* ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/crashes/id:0000*)" || { echo "No crashes found" exit 1 From 6de03327bf8c27daa9fcbdea0491784a4a755a5f Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 17:23:01 +0200 Subject: [PATCH 50/67] fix --- .../libafl-fuzz/src/feedback/capture_timeout.rs | 1 - fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 2 +- .../libafl-fuzz/src/stages/verify_timeouts.rs | 10 ++++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs index 985e4565c9..745a1a8a53 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs @@ -49,7 +49,6 @@ where exit_kind: &ExitKind, ) -> Result { if *self.enabled.borrow() && matches!(exit_kind, ExitKind::Timeout) { - println!("captured!"); let timeouts = state.metadata_or_insert_with(|| TimeoutsToVerify::::new()); timeouts.push(input.clone()); } diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 453d40cfba..5172fd69dc 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -1,5 +1,5 @@ use std::{ - borrow::{BorrowMut, Cow}, + borrow::Cow, cell::RefCell, marker::PhantomData, path::{Path, PathBuf}, diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index 533281ce98..4161887c16 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -5,12 +5,12 @@ use std::{cell::RefCell, collections::VecDeque, fmt::Debug, marker::PhantomData, use libafl::{ corpus::Corpus, - executors::{Executor, ExitKind, HasObservers, HasTimeout}, + executors::{Executor, HasObservers, HasTimeout}, inputs::{BytesInput, UsesInput}, observers::ObserversTuple, stages::Stage, state::{HasCorpus, State, UsesState}, - HasMetadata, + HasMetadata, Evaluator, }; use libafl_bolts::Error; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -75,7 +75,7 @@ where E::Observers: ObserversTuple<::Input, ::State>, E: Executor + HasObservers + HasTimeout, EM: UsesState, - Z: UsesState, + Z: UsesState + Evaluator, S: HasCorpus + State + HasMetadata, Self::Input: Debug + Serialize + DeserializeOwned + Default + 'static + Clone, <::State as HasCorpus>::Corpus: Corpus, //delete me @@ -96,9 +96,7 @@ where executor.set_timeout(self.doubled_timeout); *self.capture_feedback.borrow_mut() = false; while let Some(input) = timeouts.pop() { - println!("verifying!"); - let exit_kind = executor.run_target(fuzzer, state, manager, &input)?; - if matches!(exit_kind, ExitKind::Timeout) {} + fuzzer.evaluate_input(state, executor, manager, input)?; } executor.set_timeout(self.original_timeout); *self.capture_feedback.borrow_mut() = true; From be31f5e6ce5f2a040e0d537d08760754a205b8ff Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 17:23:19 +0200 Subject: [PATCH 51/67] fix --- fuzzers/forkserver/libafl-fuzz/Makefile.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/forkserver/libafl-fuzz/Makefile.toml b/fuzzers/forkserver/libafl-fuzz/Makefile.toml index 2b483400e0..7539efc7d0 100644 --- a/fuzzers/forkserver/libafl-fuzz/Makefile.toml +++ b/fuzzers/forkserver/libafl-fuzz/Makefile.toml @@ -113,7 +113,7 @@ script_runner = "@shell" script = ''' # cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog -AFL_DEBUG=1 LIBAFL_DEBUG_OUTPUT=1 AFL_CORES=0 timeout 15 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true +AFL_CORES=0 timeout 5 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true test -n "$( ls ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/hangs/id:0000* ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/crashes/id:0000*)" || { echo "No crashes found" exit 1 From 1f0e96e1736e37f4e7ae69faeefd5207672d5bda Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 17:29:12 +0200 Subject: [PATCH 52/67] fmt --- fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index 4161887c16..9a03b813aa 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -10,7 +10,7 @@ use libafl::{ observers::ObserversTuple, stages::Stage, state::{HasCorpus, State, UsesState}, - HasMetadata, Evaluator, + Evaluator, HasMetadata, }; use libafl_bolts::Error; use serde::{de::DeserializeOwned, Deserialize, Serialize}; From 8c19f6e84fd1a75c2b6166727fcbc538440c0da5 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 17:31:12 +0200 Subject: [PATCH 53/67] increase cmplog timeout --- fuzzers/forkserver/libafl-fuzz/Makefile.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/forkserver/libafl-fuzz/Makefile.toml b/fuzzers/forkserver/libafl-fuzz/Makefile.toml index 7539efc7d0..085db1d6f1 100644 --- a/fuzzers/forkserver/libafl-fuzz/Makefile.toml +++ b/fuzzers/forkserver/libafl-fuzz/Makefile.toml @@ -113,7 +113,7 @@ script_runner = "@shell" script = ''' # cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog -AFL_CORES=0 timeout 5 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true +AFL_CORES=0 timeout 15 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true test -n "$( ls ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/hangs/id:0000* ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/crashes/id:0000*)" || { echo "No crashes found" exit 1 From 59bddefc285fffab20bb545218d135c5228c8251 Mon Sep 17 00:00:00 2001 From: aarnav Date: Tue, 22 Oct 2024 17:31:56 +0200 Subject: [PATCH 54/67] semantic --- .../libafl-fuzz/src/stages/verify_timeouts.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index 9a03b813aa..e7bd0ea5f4 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -19,15 +19,15 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; pub struct VerifyTimeoutsStage { doubled_timeout: Duration, original_timeout: Duration, - capture_feedback: Rc>, + capture_timeouts: Rc>, phantom: PhantomData<(E, S)>, } impl VerifyTimeoutsStage { /// Create a `VerifyTimeoutsStage` - pub fn new(capture_feedback: Rc>, configured_timeout: Duration) -> Self { + pub fn new(capture_timeouts: Rc>, configured_timeout: Duration) -> Self { Self { - capture_feedback, + capture_timeouts, doubled_timeout: configured_timeout * 2, original_timeout: configured_timeout, phantom: PhantomData, @@ -94,12 +94,12 @@ where return Ok(()); } executor.set_timeout(self.doubled_timeout); - *self.capture_feedback.borrow_mut() = false; + *self.capture_timeouts.borrow_mut() = false; while let Some(input) = timeouts.pop() { fuzzer.evaluate_input(state, executor, manager, input)?; } executor.set_timeout(self.original_timeout); - *self.capture_feedback.borrow_mut() = true; + *self.capture_timeouts.borrow_mut() = true; let res = state.metadata_mut::>().unwrap(); *res = TimeoutsToVerify::::new(); Ok(()) From f25747e53e006fb712481723db456194dd1c8f45 Mon Sep 17 00:00:00 2001 From: aarnav Date: Wed, 23 Oct 2024 10:51:19 +0200 Subject: [PATCH 55/67] debug --- fuzzers/forkserver/libafl-fuzz/Makefile.toml | 2 +- fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fuzzers/forkserver/libafl-fuzz/Makefile.toml b/fuzzers/forkserver/libafl-fuzz/Makefile.toml index 085db1d6f1..95e71e3ad9 100644 --- a/fuzzers/forkserver/libafl-fuzz/Makefile.toml +++ b/fuzzers/forkserver/libafl-fuzz/Makefile.toml @@ -113,7 +113,7 @@ script_runner = "@shell" script = ''' # cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog -AFL_CORES=0 timeout 15 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true +LIBAFL_DEBUG_OUTPUT=1 AFL_CORES=0 timeout 15 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true test -n "$( ls ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/hangs/id:0000* ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/crashes/id:0000*)" || { echo "No crashes found" exit 1 diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index e7bd0ea5f4..ffe8793e83 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -96,6 +96,7 @@ where executor.set_timeout(self.doubled_timeout); *self.capture_timeouts.borrow_mut() = false; while let Some(input) = timeouts.pop() { + println!("verifying"); fuzzer.evaluate_input(state, executor, manager, input)?; } executor.set_timeout(self.original_timeout); From 46ae0823e65fa9cc7b3b54a364791c3796efd376 Mon Sep 17 00:00:00 2001 From: aarnav Date: Wed, 23 Oct 2024 11:36:38 +0200 Subject: [PATCH 56/67] debug --- fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs index 745a1a8a53..8c49fcf469 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs @@ -49,6 +49,7 @@ where exit_kind: &ExitKind, ) -> Result { if *self.enabled.borrow() && matches!(exit_kind, ExitKind::Timeout) { + println!("borrowing"); let timeouts = state.metadata_or_insert_with(|| TimeoutsToVerify::::new()); timeouts.push(input.clone()); } From 527e31071c1aeb659859aa13ffeb8a0abf3a61fe Mon Sep 17 00:00:00 2001 From: aarnav Date: Wed, 23 Oct 2024 15:03:09 +0200 Subject: [PATCH 57/67] remove dbeug --- fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs | 1 - fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs index 8c49fcf469..745a1a8a53 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs @@ -49,7 +49,6 @@ where exit_kind: &ExitKind, ) -> Result { if *self.enabled.borrow() && matches!(exit_kind, ExitKind::Timeout) { - println!("borrowing"); let timeouts = state.metadata_or_insert_with(|| TimeoutsToVerify::::new()); timeouts.push(input.clone()); } diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs index ffe8793e83..e7bd0ea5f4 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/verify_timeouts.rs @@ -96,7 +96,6 @@ where executor.set_timeout(self.doubled_timeout); *self.capture_timeouts.borrow_mut() = false; while let Some(input) = timeouts.pop() { - println!("verifying"); fuzzer.evaluate_input(state, executor, manager, input)?; } executor.set_timeout(self.original_timeout); From a1e7e77620e11a912ad23b649392cb9a4ec5e2d6 Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 24 Oct 2024 16:09:03 +0200 Subject: [PATCH 58/67] only test libafl-fuzz on CI for now --- .github/workflows/build_and_test.yml | 68 ---------------------------- 1 file changed, 68 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 6f8fadbc64..2fd87a1ab2 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -230,75 +230,7 @@ jobs: matrix: os: [ ubuntu-24.04 ] fuzzer: - # Baby - - ./fuzzers/baby/baby_fuzzer_swap_differential - - ./fuzzers/baby/tutorial - - ./fuzzers/baby/baby_fuzzer - # - ./fuzzers/baby/backtrace_baby_fuzzers - - ./fuzzers/baby/baby_fuzzer_unicode - - ./fuzzers/baby/baby_fuzzer_minimizing - - ./fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor - - ./fuzzers/baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor - - ./fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_fork_executor - - ./fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_inprocess_executor - - ./fuzzers/baby/backtrace_baby_fuzzers/command_executor - - ./fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor - - # Binary-only - - ./fuzzers/binary_only/fuzzbench_fork_qemu - - ./fuzzers/binary_only/frida_executable_libpng - - ./fuzzers/binary_only/frida_windows_gdiplus - - ./fuzzers/binary_only/frida_libpng - - ./fuzzers/binary_only/fuzzbench_qemu - - ./fuzzers/binary_only/tinyinst_simple - - # Forkserver - - ./fuzzers/forkserver/forkserver_simple - - ./fuzzers/forkserver/forkserver_libafl_cc - - ./fuzzers/forkserver/fuzzbench_forkserver - - ./fuzzers/forkserver/fuzzbench_forkserver_cmplog - ./fuzzers/forkserver/libafl-fuzz - - ./fuzzers/forkserver/baby_fuzzer_with_forkexecutor - - # Full-system - - ./fuzzers/full_system/nyx_libxml2_standalone - - ./fuzzers/full_system/nyx_libxml2_parallel - - # Structure-aware - - ./fuzzers/structure_aware/nautilus_sync - - ./fuzzers/structure_aware/baby_fuzzer_grimoire - - ./fuzzers/structure_aware/baby_fuzzer_gramatron - - ./fuzzers/structure_aware/baby_fuzzer_tokens - - ./fuzzers/structure_aware/baby_fuzzer_multi - - ./fuzzers/structure_aware/baby_fuzzer_custom_input - - ./fuzzers/structure_aware/baby_fuzzer_nautilus - - # In-process - - ./fuzzers/fuzz_anything/cargo_fuzz - # - ./fuzzers/inprocess/dynamic_analysis - - ./fuzzers/inprocess/fuzzbench - - ./fuzzers/inprocess/fuzzbench_text - - ./fuzzers/inprocess/fuzzbench_ctx - - ./fuzzers/inprocess/libfuzzer_libmozjpeg - - ./fuzzers/inprocess/libfuzzer_libpng - - ./fuzzers/inprocess/libfuzzer_libpng_launcher - - ./fuzzers/inprocess/libfuzzer_libpng_accounting - - ./fuzzers/inprocess/libfuzzer_libpng_centralized - - ./fuzzers/inprocess/libfuzzer_libpng_cmin - - ./fuzzers/inprocess/libfuzzer_libpng_norestart - # - ./fuzzers/inprocess/libfuzzer_libpng_tcp_manager - - ./fuzzers/inprocess/libfuzzer_stb_image_sugar - - ./fuzzers/inprocess/libfuzzer_stb_image - # - ./fuzzers/structure_aware/libfuzzer_stb_image_concolic - # - ./fuzzers/inprocess/libfuzzer_windows_asan - # - ./fuzzers/inprocess/sqlite_centralized_multi_machine - - # Fuzz Anything - - ./fuzzers/fuzz_anything/push_harness - - ./fuzzers/fuzz_anything/push_stage_harness - - ./fuzzers/fuzz_anything/libafl_atheris - - ./fuzzers/fuzz_anything/baby_no_std - - ./fuzzers/fuzz_anything/baby_fuzzer_wasm runs-on: ${{ matrix.os }} steps: From d50ea3741dfa6e99bab1c0e0f586fe522c18d165 Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 24 Oct 2024 16:13:09 +0200 Subject: [PATCH 59/67] better seed for cmplog? --- fuzzers/forkserver/libafl-fuzz/test/seeds_cmplog/init | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/forkserver/libafl-fuzz/test/seeds_cmplog/init b/fuzzers/forkserver/libafl-fuzz/test/seeds_cmplog/init index a965e715f7..df08510a83 100644 --- a/fuzzers/forkserver/libafl-fuzz/test/seeds_cmplog/init +++ b/fuzzers/forkserver/libafl-fuzz/test/seeds_cmplog/init @@ -1 +1 @@ -00000000000000000000000000000000 +öööu¼C¼¼‹à \ No newline at end of file From e722eb0c97a638c20df2717751649b888bd3d643 Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 24 Oct 2024 16:23:29 +0200 Subject: [PATCH 60/67] remove preflight check for now --- .github/workflows/build_and_test.yml | 2 -- libafl/src/executors/forkserver.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 0eae3b60da..6e383c708d 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -223,8 +223,6 @@ jobs: run: ./scripts/check_tested_fuzzers.sh fuzzers: - needs: - - fuzzers-preflight strategy: fail-fast: true matrix: diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 58d3702cc8..4d6b768d99 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -1423,7 +1423,7 @@ impl<'a, TC, SP> ForkserverExecutorBuilder<'a, TC, SP> { timeout: self.timeout, asan_obs: self.asan_obs, crash_exitcode: self.crash_exitcode, - target_bytes_converter: target_bytes_converter, + target_bytes_converter, } } } From 2b1451209890a7366233b9948acc298d71f912a8 Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 24 Oct 2024 16:31:25 +0200 Subject: [PATCH 61/67] set Input type in forkserver --- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 5172fd69dc..4f246b2b04 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -17,7 +17,7 @@ use libafl::{ feedback_and, feedback_or, feedback_or_fast, feedbacks::{ConstFeedback, CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::StdFuzzer, - inputs::BytesInput, + inputs::{BytesInput, NopTargetBytesConverter}, mutators::{havoc_mutations, tokens_mutations, AFLppRedQueen, StdScheduledMutator, Tokens}, observers::{CanTrack, HitcountsMapObserver, StdMapObserver, TimeObserver}, schedulers::{ @@ -460,7 +460,7 @@ fn base_executor_builder<'a>( opt: &'a Opt, shmem_provider: &'a mut UnixShMemProvider, fuzzer_dir: &Path, -) -> ForkserverExecutorBuilder<'a, UnixShMemProvider> { +) -> ForkserverExecutorBuilder<'a, NopTargetBytesConverter, UnixShMemProvider> { let mut executor = ForkserverExecutor::builder() .program(opt.executable.clone()) .coverage_map_size(opt.map_size.unwrap_or(AFL_DEFAULT_MAP_SIZE)) From 4cb37153b61abc33586c18d31b2cdd90279e1929 Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 24 Oct 2024 16:46:00 +0200 Subject: [PATCH 62/67] debug --- libafl/src/executors/forkserver.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 4d6b768d99..ad67119658 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -1521,6 +1521,7 @@ where false }; if libc::WIFSIGNALED(self.forkserver().status()) || exitcode_is_crash { + println!("CRASH"); exit_kind = ExitKind::Crash; #[cfg(feature = "regex")] if let Some(asan_observer) = self.observers.get_mut(&self.asan_obs) { @@ -1537,6 +1538,7 @@ where "Could not kill timed-out child: {err:?}" ))); } + println!("TIMEOUT"); exit_kind = ExitKind::Timeout; } From 2b3cbbbeddb89db2b6f03306afd10767d0ce0284 Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 24 Oct 2024 16:55:53 +0200 Subject: [PATCH 63/67] tmate --- .github/workflows/build_and_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 6e383c708d..d0ddaa3157 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -234,6 +234,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/fuzzer-tester-prepare + - uses: mxschmitt/action-tmate@v3 - name: Build and run example fuzzers (Linux) if: runner.os == 'Linux' shell: bash From 99ffca3af8a25f267f97aadb5e0f04d89951881d Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 24 Oct 2024 17:25:37 +0200 Subject: [PATCH 64/67] fix capture_timeout --- fuzzers/forkserver/libafl-fuzz/Makefile.toml | 2 +- fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs | 3 ++- libafl/src/executors/forkserver.rs | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/fuzzers/forkserver/libafl-fuzz/Makefile.toml b/fuzzers/forkserver/libafl-fuzz/Makefile.toml index 95e71e3ad9..21829a21e2 100644 --- a/fuzzers/forkserver/libafl-fuzz/Makefile.toml +++ b/fuzzers/forkserver/libafl-fuzz/Makefile.toml @@ -113,7 +113,7 @@ script_runner = "@shell" script = ''' # cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog -LIBAFL_DEBUG_OUTPUT=1 AFL_CORES=0 timeout 15 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true +LIBAFL_DEBUG_OUTPUT=1 AFL_CORES=0 timeout 10 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/output-cmplog -c 0 ./test/out-cmplog || true test -n "$( ls ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/hangs/id:0000* ${PROJECT_DIR}/test/output-cmplog/fuzzer_main/crashes/id:0000*)" || { echo "No crashes found" exit 1 diff --git a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs index 745a1a8a53..f3908bc01a 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/feedback/capture_timeout.rs @@ -51,8 +51,9 @@ where if *self.enabled.borrow() && matches!(exit_kind, ExitKind::Timeout) { let timeouts = state.metadata_or_insert_with(|| TimeoutsToVerify::::new()); timeouts.push(input.clone()); + return Ok(false); } - Ok(false) + Ok(matches!(exit_kind, ExitKind::Timeout)) } fn append_metadata( diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index ad67119658..4d6b768d99 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -1521,7 +1521,6 @@ where false }; if libc::WIFSIGNALED(self.forkserver().status()) || exitcode_is_crash { - println!("CRASH"); exit_kind = ExitKind::Crash; #[cfg(feature = "regex")] if let Some(asan_observer) = self.observers.get_mut(&self.asan_obs) { @@ -1538,7 +1537,6 @@ where "Could not kill timed-out child: {err:?}" ))); } - println!("TIMEOUT"); exit_kind = ExitKind::Timeout; } From 4c0b38800a968d4322eee2a995ba14ac623b341e Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 24 Oct 2024 17:26:51 +0200 Subject: [PATCH 65/67] revert workflow --- .github/workflows/build_and_test.yml | 72 +++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index d0ddaa3157..f4d3e1d3ea 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -223,18 +223,88 @@ jobs: run: ./scripts/check_tested_fuzzers.sh fuzzers: + needs: + - fuzzers-preflight strategy: fail-fast: true matrix: os: [ ubuntu-24.04 ] fuzzer: + # Baby + - ./fuzzers/baby/baby_fuzzer_swap_differential + - ./fuzzers/baby/tutorial + - ./fuzzers/baby/baby_fuzzer + # - ./fuzzers/baby/backtrace_baby_fuzzers + - ./fuzzers/baby/baby_fuzzer_unicode + - ./fuzzers/baby/baby_fuzzer_minimizing + - ./fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor + - ./fuzzers/baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor + - ./fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_fork_executor + - ./fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_inprocess_executor + - ./fuzzers/baby/backtrace_baby_fuzzers/command_executor + - ./fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor + + # Binary-only + - ./fuzzers/binary_only/fuzzbench_fork_qemu + - ./fuzzers/binary_only/frida_executable_libpng + - ./fuzzers/binary_only/frida_windows_gdiplus + - ./fuzzers/binary_only/frida_libpng + - ./fuzzers/binary_only/fuzzbench_qemu + - ./fuzzers/binary_only/tinyinst_simple + + # Forkserver + - ./fuzzers/forkserver/forkserver_simple + - ./fuzzers/forkserver/forkserver_libafl_cc + - ./fuzzers/forkserver/fuzzbench_forkserver + - ./fuzzers/forkserver/fuzzbench_forkserver_cmplog - ./fuzzers/forkserver/libafl-fuzz + - ./fuzzers/forkserver/baby_fuzzer_with_forkexecutor + + # Full-system + - ./fuzzers/full_system/nyx_libxml2_standalone + - ./fuzzers/full_system/nyx_libxml2_parallel + + # Structure-aware + - ./fuzzers/structure_aware/nautilus_sync + - ./fuzzers/structure_aware/baby_fuzzer_grimoire + - ./fuzzers/structure_aware/baby_fuzzer_gramatron + - ./fuzzers/structure_aware/baby_fuzzer_tokens + - ./fuzzers/structure_aware/baby_fuzzer_multi + - ./fuzzers/structure_aware/baby_fuzzer_custom_input + - ./fuzzers/structure_aware/baby_fuzzer_nautilus + - ./fuzzers/structure_aware/forkserver_simple_nautilus + + # In-process + - ./fuzzers/fuzz_anything/cargo_fuzz + # - ./fuzzers/inprocess/dynamic_analysis + - ./fuzzers/inprocess/fuzzbench + - ./fuzzers/inprocess/fuzzbench_text + - ./fuzzers/inprocess/fuzzbench_ctx + - ./fuzzers/inprocess/libfuzzer_libmozjpeg + - ./fuzzers/inprocess/libfuzzer_libpng + - ./fuzzers/inprocess/libfuzzer_libpng_launcher + - ./fuzzers/inprocess/libfuzzer_libpng_accounting + - ./fuzzers/inprocess/libfuzzer_libpng_centralized + - ./fuzzers/inprocess/libfuzzer_libpng_cmin + - ./fuzzers/inprocess/libfuzzer_libpng_norestart + # - ./fuzzers/inprocess/libfuzzer_libpng_tcp_manager + - ./fuzzers/inprocess/libfuzzer_stb_image_sugar + - ./fuzzers/inprocess/libfuzzer_stb_image + # - ./fuzzers/structure_aware/libfuzzer_stb_image_concolic + # - ./fuzzers/inprocess/libfuzzer_windows_asan + # - ./fuzzers/inprocess/sqlite_centralized_multi_machine + + # Fuzz Anything + - ./fuzzers/fuzz_anything/push_harness + - ./fuzzers/fuzz_anything/push_stage_harness + - ./fuzzers/fuzz_anything/libafl_atheris + - ./fuzzers/fuzz_anything/baby_no_std + - ./fuzzers/fuzz_anything/baby_fuzzer_wasm runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/fuzzer-tester-prepare - - uses: mxschmitt/action-tmate@v3 - name: Build and run example fuzzers (Linux) if: runner.os == 'Linux' shell: bash From 8ad53426d10e4a888d3a31232679a3531c51e685 Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 24 Oct 2024 17:45:59 +0200 Subject: [PATCH 66/67] run only libafl-fuzz --- .github/workflows/build_and_test.yml | 68 ---------------------------- 1 file changed, 68 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index f4d3e1d3ea..0bb7df38c5 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -231,75 +231,7 @@ jobs: os: [ ubuntu-24.04 ] fuzzer: # Baby - - ./fuzzers/baby/baby_fuzzer_swap_differential - - ./fuzzers/baby/tutorial - - ./fuzzers/baby/baby_fuzzer - # - ./fuzzers/baby/backtrace_baby_fuzzers - - ./fuzzers/baby/baby_fuzzer_unicode - - ./fuzzers/baby/baby_fuzzer_minimizing - - ./fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor - - ./fuzzers/baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor - - ./fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_fork_executor - - ./fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_inprocess_executor - - ./fuzzers/baby/backtrace_baby_fuzzers/command_executor - - ./fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor - - # Binary-only - - ./fuzzers/binary_only/fuzzbench_fork_qemu - - ./fuzzers/binary_only/frida_executable_libpng - - ./fuzzers/binary_only/frida_windows_gdiplus - - ./fuzzers/binary_only/frida_libpng - - ./fuzzers/binary_only/fuzzbench_qemu - - ./fuzzers/binary_only/tinyinst_simple - - # Forkserver - - ./fuzzers/forkserver/forkserver_simple - - ./fuzzers/forkserver/forkserver_libafl_cc - - ./fuzzers/forkserver/fuzzbench_forkserver - - ./fuzzers/forkserver/fuzzbench_forkserver_cmplog - ./fuzzers/forkserver/libafl-fuzz - - ./fuzzers/forkserver/baby_fuzzer_with_forkexecutor - - # Full-system - - ./fuzzers/full_system/nyx_libxml2_standalone - - ./fuzzers/full_system/nyx_libxml2_parallel - - # Structure-aware - - ./fuzzers/structure_aware/nautilus_sync - - ./fuzzers/structure_aware/baby_fuzzer_grimoire - - ./fuzzers/structure_aware/baby_fuzzer_gramatron - - ./fuzzers/structure_aware/baby_fuzzer_tokens - - ./fuzzers/structure_aware/baby_fuzzer_multi - - ./fuzzers/structure_aware/baby_fuzzer_custom_input - - ./fuzzers/structure_aware/baby_fuzzer_nautilus - - ./fuzzers/structure_aware/forkserver_simple_nautilus - - # In-process - - ./fuzzers/fuzz_anything/cargo_fuzz - # - ./fuzzers/inprocess/dynamic_analysis - - ./fuzzers/inprocess/fuzzbench - - ./fuzzers/inprocess/fuzzbench_text - - ./fuzzers/inprocess/fuzzbench_ctx - - ./fuzzers/inprocess/libfuzzer_libmozjpeg - - ./fuzzers/inprocess/libfuzzer_libpng - - ./fuzzers/inprocess/libfuzzer_libpng_launcher - - ./fuzzers/inprocess/libfuzzer_libpng_accounting - - ./fuzzers/inprocess/libfuzzer_libpng_centralized - - ./fuzzers/inprocess/libfuzzer_libpng_cmin - - ./fuzzers/inprocess/libfuzzer_libpng_norestart - # - ./fuzzers/inprocess/libfuzzer_libpng_tcp_manager - - ./fuzzers/inprocess/libfuzzer_stb_image_sugar - - ./fuzzers/inprocess/libfuzzer_stb_image - # - ./fuzzers/structure_aware/libfuzzer_stb_image_concolic - # - ./fuzzers/inprocess/libfuzzer_windows_asan - # - ./fuzzers/inprocess/sqlite_centralized_multi_machine - - # Fuzz Anything - - ./fuzzers/fuzz_anything/push_harness - - ./fuzzers/fuzz_anything/push_stage_harness - - ./fuzzers/fuzz_anything/libafl_atheris - - ./fuzzers/fuzz_anything/baby_no_std - - ./fuzzers/fuzz_anything/baby_fuzzer_wasm runs-on: ${{ matrix.os }} steps: From ac0d38b9ac1ee08321be07db1bbed701378c4251 Mon Sep 17 00:00:00 2001 From: aarnav Date: Thu, 24 Oct 2024 17:46:25 +0200 Subject: [PATCH 67/67] remove pre-flight --- .github/workflows/build_and_test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 0bb7df38c5..705c80b1fc 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -223,8 +223,6 @@ jobs: run: ./scripts/check_tested_fuzzers.sh fuzzers: - needs: - - fuzzers-preflight strategy: fail-fast: true matrix: