parent
4e302a2115
commit
902571c0c7
@ -79,9 +79,6 @@ pub mod tuneable;
|
|||||||
#[cfg(feature = "unicode")]
|
#[cfg(feature = "unicode")]
|
||||||
pub mod unicode;
|
pub mod unicode;
|
||||||
|
|
||||||
pub mod pruning;
|
|
||||||
pub use pruning::*;
|
|
||||||
|
|
||||||
/// A stage is one step in the fuzzing process.
|
/// A stage is one step in the fuzzing process.
|
||||||
/// Multiple stages will be scheduled one by one for each input.
|
/// Multiple stages will be scheduled one by one for each input.
|
||||||
pub trait Stage<E, EM, Z>: UsesState
|
pub trait Stage<E, EM, Z>: UsesState
|
||||||
|
@ -1,195 +0,0 @@
|
|||||||
//! Corpus pruning stage
|
|
||||||
|
|
||||||
use alloc::string::ToString;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
use libafl_bolts::{rands::Rand, Error};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
corpus::{Corpus, HasCurrentCorpusId},
|
|
||||||
schedulers::{RemovableScheduler, Scheduler},
|
|
||||||
stages::Stage,
|
|
||||||
state::{HasCorpus, HasRand, UsesState},
|
|
||||||
HasScheduler,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use crate::{events::EventRestarter, state::Stoppable};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// The stage to probabilistically disable a corpus entry.
|
|
||||||
///
|
|
||||||
/// This stage should be wrapped in a if stage and run only when the fuzzer perform restarting
|
|
||||||
/// The idea comes from `https://mschloegel.me/paper/schiller2023fuzzerrestarts.pdf`
|
|
||||||
pub struct CorpusPruning<EM> {
|
|
||||||
/// The chance of retaining this corpus
|
|
||||||
prob: f64,
|
|
||||||
phantom: PhantomData<EM>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<EM> CorpusPruning<EM> {
|
|
||||||
fn new(prob: f64) -> Self {
|
|
||||||
Self {
|
|
||||||
prob,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<EM> Default for CorpusPruning<EM> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new(0.05)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<EM> UsesState for CorpusPruning<EM>
|
|
||||||
where
|
|
||||||
EM: UsesState,
|
|
||||||
{
|
|
||||||
type State = EM::State;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, EM, Z> Stage<E, EM, Z> for CorpusPruning<EM>
|
|
||||||
where
|
|
||||||
EM: UsesState,
|
|
||||||
E: UsesState<State = Self::State>,
|
|
||||||
Z: UsesState<State = Self::State> + HasScheduler,
|
|
||||||
<Z as HasScheduler>::Scheduler: RemovableScheduler,
|
|
||||||
Self::State: HasCorpus + HasRand,
|
|
||||||
{
|
|
||||||
#[allow(clippy::cast_precision_loss)]
|
|
||||||
fn perform(
|
|
||||||
&mut self,
|
|
||||||
fuzzer: &mut Z,
|
|
||||||
_executor: &mut E,
|
|
||||||
state: &mut Self::State,
|
|
||||||
_manager: &mut EM,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
// Iterate over every corpus entr
|
|
||||||
let n_all = state.corpus().count_all();
|
|
||||||
let n_enabled = state.corpus().count();
|
|
||||||
|
|
||||||
let Some(currently_fuzzed_idx) = state.current_corpus_id()? else {
|
|
||||||
return Err(Error::illegal_state("Not fuzzing any testcase".to_string()));
|
|
||||||
};
|
|
||||||
|
|
||||||
// eprintln!("Currently fuzzing {:#?}", currently_fuzzed_idx);
|
|
||||||
|
|
||||||
let mut disabled_to_enabled = vec![];
|
|
||||||
let mut enabled_to_disabled = vec![];
|
|
||||||
// do it backwards so that the index won't change even after remove
|
|
||||||
for i in (0..n_all).rev() {
|
|
||||||
let r = state.rand_mut().below(100) as f64;
|
|
||||||
if self.prob * 100_f64 < r {
|
|
||||||
let idx = state.corpus().nth_from_all(i);
|
|
||||||
|
|
||||||
// skip the currently fuzzed id; don't remove it
|
|
||||||
// because else after restart we can't call currrent.next() to find the next testcase
|
|
||||||
if idx == currently_fuzzed_idx {
|
|
||||||
// eprintln!("skipping {:#?}", idx);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let removed = state.corpus_mut().remove(idx)?;
|
|
||||||
fuzzer
|
|
||||||
.scheduler_mut()
|
|
||||||
.on_remove(state, idx, &Some(removed.clone()))?;
|
|
||||||
// because [n_enabled, n_all) is disabled testcases
|
|
||||||
// and [0, n_enabled) is enabled testcases
|
|
||||||
if i >= n_enabled {
|
|
||||||
// we are moving disabled to enabled now
|
|
||||||
disabled_to_enabled.push((idx, removed));
|
|
||||||
} else {
|
|
||||||
// we are moving enabled to disabled now
|
|
||||||
enabled_to_disabled.push((idx, removed));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actually move them
|
|
||||||
for (idx, testcase) in disabled_to_enabled {
|
|
||||||
state.corpus_mut().add(testcase)?;
|
|
||||||
fuzzer.scheduler_mut().on_add(state, idx)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (idx, testcase) in enabled_to_disabled {
|
|
||||||
state.corpus_mut().add_disabled(testcase)?;
|
|
||||||
fuzzer.scheduler_mut().on_add(state, idx)?;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
eprintln!(
|
|
||||||
"There was {}, and we retained {} corpura",
|
|
||||||
n_all,
|
|
||||||
state.corpus().count()
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_restart(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
|
|
||||||
// Not executing the target, so restart safety is not needed
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
|
|
||||||
// Not executing the target, so restart safety is not needed
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A stage for conditional restart
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub struct RestartStage<E, EM, Z> {
|
|
||||||
phantom: PhantomData<(E, EM, Z)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl<E, EM, Z> UsesState for RestartStage<E, EM, Z>
|
|
||||||
where
|
|
||||||
E: UsesState,
|
|
||||||
{
|
|
||||||
type State = E::State;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl<E, EM, Z> Stage<E, EM, Z> for RestartStage<E, EM, Z>
|
|
||||||
where
|
|
||||||
E: UsesState,
|
|
||||||
EM: UsesState<State = Self::State> + EventRestarter,
|
|
||||||
Z: UsesState<State = Self::State>,
|
|
||||||
{
|
|
||||||
#[allow(unreachable_code)]
|
|
||||||
fn perform(
|
|
||||||
&mut self,
|
|
||||||
_fuzzer: &mut Z,
|
|
||||||
_executor: &mut E,
|
|
||||||
state: &mut Self::State,
|
|
||||||
manager: &mut EM,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
manager.on_restart(state).unwrap();
|
|
||||||
state.request_stop();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_restart(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl<E, EM, Z> RestartStage<E, EM, Z>
|
|
||||||
where
|
|
||||||
E: UsesState,
|
|
||||||
{
|
|
||||||
/// Constructor for this conditionally enabled stage.
|
|
||||||
/// If the closure returns true, the wrapped stage will be executed, else it will be skipped.
|
|
||||||
#[must_use]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user