Stability improve (#773)

* initial

* add

* fmt & fix

* dbg remove

* clp

* clp

* more

* clippy

* del

* fix

* remove unused

* fix

* doc
This commit is contained in:
Dongjia "toka" Zhang 2022-09-12 18:08:07 +02:00 committed by GitHub
parent b863142829
commit d17269d3d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 87 additions and 72 deletions

View File

@ -22,7 +22,8 @@ use crate::{
inputs::Input, inputs::Input,
monitors::UserStats, monitors::UserStats,
observers::ObserversTuple, observers::ObserversTuple,
state::{HasClientPerfMonitor, HasExecutions}, stages::calibrate::UnstableEntriesMetadata,
state::{HasClientPerfMonitor, HasExecutions, HasMetadata},
Error, Error,
}; };
@ -360,7 +361,7 @@ where
monitor_timeout: Duration, monitor_timeout: Duration,
) -> Result<Duration, Error> ) -> Result<Duration, Error>
where where
S: HasExecutions + HasClientPerfMonitor, S: HasExecutions + HasClientPerfMonitor + HasMetadata,
{ {
let executions = *state.executions(); let executions = *state.executions();
let cur = current_time(); let cur = current_time();
@ -377,13 +378,15 @@ where
}, },
)?; )?;
if let Some(x) = state.stability() { /// Send the stability event to the broker
let stability = f64::from(*x); if let Some(meta) = state.metadata().get::<UnstableEntriesMetadata>() {
let unstable_entries = meta.unstable_entries().len();
let map_len = meta.map_len();
self.fire( self.fire(
state, state,
Event::UpdateUserStats { Event::UpdateUserStats {
name: "stability".to_string(), name: "stability".to_string(),
value: UserStats::Float(stability), value: UserStats::Ratio(unstable_entries as u64, map_len as u64),
phantom: PhantomData, phantom: PhantomData,
}, },
)?; )?;

View File

@ -224,14 +224,6 @@ mod tests {
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor { fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor {
unimplemented!() unimplemented!()
} }
fn stability(&self) -> &Option<f32> {
unimplemented!()
}
fn stability_mut(&mut self) -> &mut Option<f32> {
unimplemented!()
}
} }
fn test_diff(should_equal: bool) { fn test_diff(should_equal: bool) {

View File

@ -17,7 +17,7 @@ use crate::{
schedulers::Scheduler, schedulers::Scheduler,
stages::StagesTuple, stages::StagesTuple,
start_timer, start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasSolutions}, state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasSolutions},
Error, Error,
}; };
@ -150,7 +150,7 @@ pub trait Fuzzer<E, EM, I, S, ST>
where where
I: Input, I: Input,
EM: ProgressReporter<I>, EM: ProgressReporter<I>,
S: HasExecutions + HasClientPerfMonitor, S: HasExecutions + HasClientPerfMonitor + HasMetadata,
{ {
/// Fuzz for a single iteration. /// Fuzz for a single iteration.
/// Returns the index of the last fuzzed corpus item. /// Returns the index of the last fuzzed corpus item.
@ -519,7 +519,7 @@ where
EM: EventManager<E, I, S, Self>, EM: EventManager<E, I, S, Self>,
F: Feedback<I, S>, F: Feedback<I, S>,
I: Input, I: Input,
S: HasClientPerfMonitor + HasExecutions, S: HasClientPerfMonitor + HasExecutions + HasMetadata,
OF: Feedback<I, S>, OF: Feedback<I, S>,
ST: StagesTuple<E, EM, S, Self>, ST: StagesTuple<E, EM, S, Self>,
{ {

View File

@ -1,8 +1,12 @@
//! The calibration stage. The fuzzer measures the average exec time and the bitmap size. //! 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 core::{fmt::Debug, marker::PhantomData, time::Duration};
use hashbrown::HashSet;
use num_traits::Bounded; use num_traits::Bounded;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -24,6 +28,39 @@ use crate::{
Error, 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<usize>,
map_len: usize,
}
impl UnstableEntriesMetadata {
#[must_use]
/// Create a new [`struct@UnstableEntriesMetadata`]
pub fn new(entries: HashSet<usize>, map_len: usize) -> Self {
Self {
unstable_entries: entries,
map_len,
}
}
/// Getter
#[must_use]
pub fn unstable_entries(&self) -> &HashSet<usize> {
&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. /// The calibration stage will measure the average exec time and the target's stability for this input.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CalibrationStage<I, O, OT, S> pub struct CalibrationStage<I, O, OT, S>
@ -39,8 +76,8 @@ where
phantom: PhantomData<(I, O, OT, S)>, phantom: PhantomData<(I, O, OT, S)>,
} }
const CAL_STAGE_START: usize = 4; const CAL_STAGE_START: usize = 4; // AFL++'s CAL_CYCLES_FAST + 1
const CAL_STAGE_MAX: usize = 16; const CAL_STAGE_MAX: usize = 8; // AFL++'s CAL_CYCLES + 1
impl<E, EM, I, O, OT, S, Z> Stage<E, EM, S, Z> for CalibrationStage<I, O, OT, S> impl<E, EM, I, O, OT, S, Z> Stage<E, EM, S, Z> for CalibrationStage<I, O, OT, S>
where where
@ -109,7 +146,7 @@ where
// run is found to be unstable, with CAL_STAGE_MAX total runs. // run is found to be unstable, with CAL_STAGE_MAX total runs.
let mut i = 1; let mut i = 1;
let mut has_errors = false; let mut has_errors = false;
let mut unstable_entries: usize = 0; let mut unstable_entries: Vec<usize> = vec![];
let map_len: usize = map_first.len(); let map_len: usize = map_first.len();
while i < iter { while i < iter {
let input = state let input = state
@ -161,24 +198,40 @@ where
history_map.resize(map_len, O::Entry::default()); history_map.resize(map_len, O::Entry::default());
} }
for (first, (cur, history)) in for (idx, (first, (cur, history))) in map_first
map_first.iter().zip(map.iter().zip(history_map.iter_mut())) .iter()
.zip(map.iter().zip(history_map.iter_mut()))
.enumerate()
{ {
if *first != *cur && *history != O::Entry::max_value() { if *first != *cur && *history != O::Entry::max_value() {
*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; i += 1;
} }
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
if unstable_entries != 0 { if !unstable_entries.is_empty() {
*state.stability_mut() = Some((map_len - unstable_entries) as f32 / (map_len as f32)); // If we see new stable entries executing this new corpus entries, then merge with the existing one
if state.has_metadata::<UnstableEntriesMetadata>() {
if iter < CAL_STAGE_MAX { let existing = state
iter += 2; .metadata_mut()
.get_mut::<UnstableEntriesMetadata>()
.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>(UnstableEntriesMetadata::new(
HashSet::from_iter(unstable_entries),
map_len,
));
} }
}; };

View File

@ -51,7 +51,7 @@ use crate::{
inputs::Input, inputs::Input,
observers::ObserversTuple, observers::ObserversTuple,
schedulers::Scheduler, schedulers::Scheduler,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasRand}, state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasRand},
Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler, Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler,
}; };
@ -179,7 +179,7 @@ where
I: Input, I: Input,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
PS: PushStage<CS, EM, I, OT, S, Z>, PS: PushStage<CS, EM, I, OT, S, Z>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions, S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>, Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
{ {
push_stage: PS, push_stage: PS,
@ -193,7 +193,7 @@ where
I: Input, I: Input,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
PS: PushStage<CS, EM, I, OT, S, Z>, PS: PushStage<CS, EM, I, OT, S, Z>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions, S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>, Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
{ {
/// Create a new [`PushStageAdapter`], wrapping the given [`PushStage`] /// Create a new [`PushStageAdapter`], wrapping the given [`PushStage`]
@ -215,7 +215,7 @@ where
I: Input, I: Input,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
PS: PushStage<CS, EM, I, OT, S, Z>, PS: PushStage<CS, EM, I, OT, S, Z>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions, S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutesInput<I, OT, S, Z> Z: ExecutesInput<I, OT, S, Z>
+ ExecutionProcessor<I, OT, S> + ExecutionProcessor<I, OT, S>
+ EvaluatorObservers<I, OT, S> + EvaluatorObservers<I, OT, S>

View File

@ -22,7 +22,7 @@ use crate::{
inputs::Input, inputs::Input,
observers::ObserversTuple, observers::ObserversTuple,
schedulers::Scheduler, schedulers::Scheduler,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasRand}, state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasRand},
Error, EvaluatorObservers, ExecutionProcessor, HasScheduler, Error, EvaluatorObservers, ExecutionProcessor, HasScheduler,
}; };
@ -186,7 +186,7 @@ where
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId + ProgressReporter<I>, EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId + ProgressReporter<I>,
I: Input, I: Input,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions, S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>, Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
{ {
/// Gets the [`PushStageHelper`] /// Gets the [`PushStageHelper`]

View File

@ -18,7 +18,7 @@ use crate::{
observers::ObserversTuple, observers::ObserversTuple,
schedulers::Scheduler, schedulers::Scheduler,
start_timer, start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasRand}, state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasRand},
Error, EvaluatorObservers, ExecutionProcessor, HasScheduler, Error, EvaluatorObservers, ExecutionProcessor, HasScheduler,
}; };
@ -85,7 +85,7 @@ where
I: Input, I: Input,
M: Mutator<I, S>, M: Mutator<I, S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions, S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>, Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
{ {
/// Creates a new default mutational stage /// Creates a new default mutational stage
@ -196,7 +196,7 @@ where
I: Input, I: Input,
M: Mutator<I, S>, M: Mutator<I, S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions, S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>, Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
{ {
type Item = Result<I, Error>; type Item = Result<I, Error>;

View File

@ -77,12 +77,6 @@ pub trait HasClientPerfMonitor {
/// Mutatable ref to [`ClientPerfMonitor`] /// Mutatable ref to [`ClientPerfMonitor`]
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor; fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor;
/// This node's stability
fn stability(&self) -> &Option<f32>;
/// This node's stability (mutable)
fn stability_mut(&mut self) -> &mut Option<f32>;
} }
/// Trait for elements offering metadata /// Trait for elements offering metadata
@ -181,9 +175,6 @@ where
named_metadata: NamedSerdeAnyMap, named_metadata: NamedSerdeAnyMap,
/// MaxSize testcase size for mutators that appreciate it /// MaxSize testcase size for mutators that appreciate it
max_size: usize, max_size: usize,
/// The stability of the current fuzzing process
stability: Option<f32>,
/// Performance statistics for this fuzzer /// Performance statistics for this fuzzer
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
introspection_monitor: ClientPerfMonitor, introspection_monitor: ClientPerfMonitor,
@ -577,7 +568,6 @@ where
let mut state = Self { let mut state = Self {
rand, rand,
executions: 0, executions: 0,
stability: None,
start_time: Duration::from_millis(0), start_time: Duration::from_millis(0),
metadata: SerdeAnyMap::default(), metadata: SerdeAnyMap::default(),
named_metadata: NamedSerdeAnyMap::default(), named_metadata: NamedSerdeAnyMap::default(),
@ -609,18 +599,6 @@ where
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor { fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor {
&mut self.introspection_monitor &mut self.introspection_monitor
} }
/// This node's stability
#[inline]
fn stability(&self) -> &Option<f32> {
&self.stability
}
/// This node's stability (mutable)
#[inline]
fn stability_mut(&mut self) -> &mut Option<f32> {
&mut self.stability
}
} }
#[cfg(not(feature = "introspection"))] #[cfg(not(feature = "introspection"))]
@ -638,18 +616,6 @@ where
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor { fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor {
unimplemented!() unimplemented!()
} }
/// This node's stability
#[inline]
fn stability(&self) -> &Option<f32> {
&self.stability
}
/// This node's stability (mutable)
#[inline]
fn stability_mut(&mut self) -> &mut Option<f32> {
&mut self.stability
}
} }
#[cfg(feature = "python")] #[cfg(feature = "python")]

View File

@ -155,6 +155,7 @@ impl CmpMap for CmpLogMap {
/// The global `CmpLog` map for the current `LibAFL` run. /// The global `CmpLog` map for the current `LibAFL` run.
#[no_mangle] #[no_mangle]
#[allow(clippy::large_stack_arrays)]
pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap { pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap {
headers: [CmpLogHeader { headers: [CmpLogHeader {
hits: 0, hits: 0,