diff --git a/fuzzers/fuzzbench_qemu/Makefile.toml b/fuzzers/fuzzbench_qemu/Makefile.toml index 987b46a564..db4a31fa42 100644 --- a/fuzzers/fuzzbench_qemu/Makefile.toml +++ b/fuzzers/fuzzbench_qemu/Makefile.toml @@ -97,4 +97,4 @@ script=''' rm -f ./${FUZZER_NAME} libfuzzer_main.o make -C libpng-1.6.37 clean cargo clean -''' \ No newline at end of file +''' diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index cc883d9ee9..4973c5fb5e 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -22,7 +22,8 @@ use crate::{ inputs::Input, monitors::UserStats, observers::ObserversTuple, - state::{HasClientPerfMonitor, HasExecutions}, + stages::calibrate::UnstableEntriesMetadata, + state::{HasClientPerfMonitor, HasExecutions, HasMetadata}, Error, }; @@ -360,7 +361,7 @@ where monitor_timeout: Duration, ) -> Result where - S: HasExecutions + HasClientPerfMonitor, + S: HasExecutions + HasClientPerfMonitor + HasMetadata, { let executions = *state.executions(); let cur = current_time(); @@ -377,13 +378,15 @@ where }, )?; - if let Some(x) = state.stability() { - let stability = f64::from(*x); + /// Send the stability event to the broker + if let Some(meta) = state.metadata().get::() { + let unstable_entries = meta.unstable_entries().len(); + let map_len = meta.map_len(); self.fire( state, Event::UpdateUserStats { name: "stability".to_string(), - value: UserStats::Float(stability), + value: UserStats::Ratio(unstable_entries as u64, map_len as u64), phantom: PhantomData, }, )?; diff --git a/libafl/src/feedbacks/differential.rs b/libafl/src/feedbacks/differential.rs index 4e385648be..d7a588acfe 100644 --- a/libafl/src/feedbacks/differential.rs +++ b/libafl/src/feedbacks/differential.rs @@ -224,14 +224,6 @@ mod tests { fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor { unimplemented!() } - - fn stability(&self) -> &Option { - unimplemented!() - } - - fn stability_mut(&mut self) -> &mut Option { - unimplemented!() - } } fn test_diff(should_equal: bool) { diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 377af632a7..1c2a5a411c 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -17,7 +17,7 @@ use crate::{ schedulers::Scheduler, stages::StagesTuple, start_timer, - state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasSolutions}, + state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasSolutions}, Error, }; @@ -150,7 +150,7 @@ pub trait Fuzzer where I: Input, EM: ProgressReporter, - S: HasExecutions + HasClientPerfMonitor, + S: HasExecutions + HasClientPerfMonitor + HasMetadata, { /// Fuzz for a single iteration. /// Returns the index of the last fuzzed corpus item. @@ -519,7 +519,7 @@ where EM: EventManager, F: Feedback, I: Input, - S: HasClientPerfMonitor + HasExecutions, + S: HasClientPerfMonitor + HasExecutions + HasMetadata, OF: Feedback, ST: StagesTuple, { diff --git a/libafl/src/stages/calibrate.rs b/libafl/src/stages/calibrate.rs index 62957d0438..b51bf49039 100644 --- a/libafl/src/stages/calibrate.rs +++ b/libafl/src/stages/calibrate.rs @@ -1,8 +1,12 @@ //! The calibration stage. The fuzzer measures the average exec time and the bitmap size. -use alloc::string::{String, ToString}; +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; use core::{fmt::Debug, marker::PhantomData, time::Duration}; +use hashbrown::HashSet; use num_traits::Bounded; use serde::{Deserialize, Serialize}; @@ -24,6 +28,39 @@ use crate::{ Error, }; +crate::impl_serdeany!(UnstableEntriesMetadata); +/// The metadata to keep unstable entries +/// In libafl, the stability is the number of the unstable entries divided by the size of the map +/// This is different from AFL++, which shows the number of the unstable entries divided by the number of filled entries. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct UnstableEntriesMetadata { + unstable_entries: HashSet, + map_len: usize, +} + +impl UnstableEntriesMetadata { + #[must_use] + /// Create a new [`struct@UnstableEntriesMetadata`] + pub fn new(entries: HashSet, map_len: usize) -> Self { + Self { + unstable_entries: entries, + map_len, + } + } + + /// Getter + #[must_use] + pub fn unstable_entries(&self) -> &HashSet { + &self.unstable_entries + } + + /// Getter + #[must_use] + pub fn map_len(&self) -> usize { + self.map_len + } +} + /// The calibration stage will measure the average exec time and the target's stability for this input. #[derive(Clone, Debug)] pub struct CalibrationStage @@ -39,8 +76,8 @@ where phantom: PhantomData<(I, O, OT, S)>, } -const CAL_STAGE_START: usize = 4; -const CAL_STAGE_MAX: usize = 16; +const CAL_STAGE_START: usize = 4; // AFL++'s CAL_CYCLES_FAST + 1 +const CAL_STAGE_MAX: usize = 8; // AFL++'s CAL_CYCLES + 1 impl Stage for CalibrationStage where @@ -109,7 +146,7 @@ where // run is found to be unstable, with CAL_STAGE_MAX total runs. let mut i = 1; let mut has_errors = false; - let mut unstable_entries: usize = 0; + let mut unstable_entries: Vec = vec![]; let map_len: usize = map_first.len(); while i < iter { let input = state @@ -161,24 +198,40 @@ where history_map.resize(map_len, O::Entry::default()); } - for (first, (cur, history)) in - map_first.iter().zip(map.iter().zip(history_map.iter_mut())) + for (idx, (first, (cur, history))) in map_first + .iter() + .zip(map.iter().zip(history_map.iter_mut())) + .enumerate() { if *first != *cur && *history != O::Entry::max_value() { *history = O::Entry::max_value(); - unstable_entries += 1; + unstable_entries.push(idx); }; } + if !unstable_entries.is_empty() && iter < CAL_STAGE_MAX { + iter += 2; + } i += 1; } #[allow(clippy::cast_precision_loss)] - if unstable_entries != 0 { - *state.stability_mut() = Some((map_len - unstable_entries) as f32 / (map_len as f32)); - - if iter < CAL_STAGE_MAX { - iter += 2; + if !unstable_entries.is_empty() { + // If we see new stable entries executing this new corpus entries, then merge with the existing one + if state.has_metadata::() { + let existing = state + .metadata_mut() + .get_mut::() + .unwrap(); + for item in unstable_entries { + existing.unstable_entries.insert(item); // Insert newly found items + } + existing.map_len = map_len; + } else { + state.add_metadata::(UnstableEntriesMetadata::new( + HashSet::from_iter(unstable_entries), + map_len, + )); } }; diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 0a32c006e3..d174bd46c0 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -51,7 +51,7 @@ use crate::{ inputs::Input, observers::ObserversTuple, schedulers::Scheduler, - state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasRand}, + state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasRand}, Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler, }; @@ -179,7 +179,7 @@ where I: Input, OT: ObserversTuple, PS: PushStage, - S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions, + S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions + HasMetadata, Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, { push_stage: PS, @@ -193,7 +193,7 @@ where I: Input, OT: ObserversTuple, PS: PushStage, - S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions, + S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions + HasMetadata, Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, { /// Create a new [`PushStageAdapter`], wrapping the given [`PushStage`] @@ -215,7 +215,7 @@ where I: Input, OT: ObserversTuple, PS: PushStage, - S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions, + S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions + HasMetadata, Z: ExecutesInput + ExecutionProcessor + EvaluatorObservers diff --git a/libafl/src/stages/push/mod.rs b/libafl/src/stages/push/mod.rs index b487e8efff..b53dffb2e6 100644 --- a/libafl/src/stages/push/mod.rs +++ b/libafl/src/stages/push/mod.rs @@ -22,7 +22,7 @@ use crate::{ inputs::Input, observers::ObserversTuple, schedulers::Scheduler, - state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasRand}, + state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasRand}, Error, EvaluatorObservers, ExecutionProcessor, HasScheduler, }; @@ -186,7 +186,7 @@ where EM: EventFirer + EventRestarter + HasEventManagerId + ProgressReporter, I: Input, OT: ObserversTuple, - S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions, + S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions + HasMetadata, Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, { /// Gets the [`PushStageHelper`] diff --git a/libafl/src/stages/push/mutational.rs b/libafl/src/stages/push/mutational.rs index 1efe79c9c2..edcc4192a6 100644 --- a/libafl/src/stages/push/mutational.rs +++ b/libafl/src/stages/push/mutational.rs @@ -18,7 +18,7 @@ use crate::{ observers::ObserversTuple, schedulers::Scheduler, start_timer, - state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasRand}, + state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasRand}, Error, EvaluatorObservers, ExecutionProcessor, HasScheduler, }; @@ -85,7 +85,7 @@ where I: Input, M: Mutator, OT: ObserversTuple, - S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions, + S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions + HasMetadata, Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, { /// Creates a new default mutational stage @@ -196,7 +196,7 @@ where I: Input, M: Mutator, OT: ObserversTuple, - S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions, + S: HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions + HasMetadata, Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, { type Item = Result; diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index b9715e02ff..3a2453a328 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -77,12 +77,6 @@ pub trait HasClientPerfMonitor { /// Mutatable ref to [`ClientPerfMonitor`] fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor; - - /// This node's stability - fn stability(&self) -> &Option; - - /// This node's stability (mutable) - fn stability_mut(&mut self) -> &mut Option; } /// Trait for elements offering metadata @@ -181,9 +175,6 @@ where named_metadata: NamedSerdeAnyMap, /// MaxSize testcase size for mutators that appreciate it max_size: usize, - /// The stability of the current fuzzing process - stability: Option, - /// Performance statistics for this fuzzer #[cfg(feature = "introspection")] introspection_monitor: ClientPerfMonitor, @@ -577,7 +568,6 @@ where let mut state = Self { rand, executions: 0, - stability: None, start_time: Duration::from_millis(0), metadata: SerdeAnyMap::default(), named_metadata: NamedSerdeAnyMap::default(), @@ -609,18 +599,6 @@ where fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor { &mut self.introspection_monitor } - - /// This node's stability - #[inline] - fn stability(&self) -> &Option { - &self.stability - } - - /// This node's stability (mutable) - #[inline] - fn stability_mut(&mut self) -> &mut Option { - &mut self.stability - } } #[cfg(not(feature = "introspection"))] @@ -638,18 +616,6 @@ where fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor { unimplemented!() } - - /// This node's stability - #[inline] - fn stability(&self) -> &Option { - &self.stability - } - - /// This node's stability (mutable) - #[inline] - fn stability_mut(&mut self) -> &mut Option { - &mut self.stability - } } #[cfg(feature = "python")] diff --git a/libafl_targets/src/cmplog.rs b/libafl_targets/src/cmplog.rs index a85369c9bd..381129c257 100644 --- a/libafl_targets/src/cmplog.rs +++ b/libafl_targets/src/cmplog.rs @@ -155,6 +155,7 @@ impl CmpMap for CmpLogMap { /// The global `CmpLog` map for the current `LibAFL` run. #[no_mangle] +#[allow(clippy::large_stack_arrays)] pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap { headers: [CmpLogHeader { hits: 0,