No Use* from stages (#2745)

* no from stage

* fixer

* doc fix

* how was this working????

* more fixes

* delete more

* rq

* cargo-fuzz

* m

* aa
This commit is contained in:
Dongjia "toka" Zhang 2024-12-12 16:50:17 +01:00 committed by GitHub
parent c176fee1e0
commit 3446ad974c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 1316 additions and 1775 deletions

View File

@ -132,7 +132,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
// Setup a lain mutator with a mutational stage
let mutator = LainMutator::new();
let power: StdPowerMutationalStage<_, _, PacketData, _, _> =
let power: StdPowerMutationalStage<_, _, PacketData, _, _, _> =
StdPowerMutationalStage::new(mutator);
let mut stages = tuple_list!(calibration, power);

View File

@ -314,7 +314,7 @@ fn fuzz(
5,
)?;
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
// A minimization+queue policy to get testcasess from the corpus

View File

@ -317,7 +317,7 @@ fn fuzz(
5,
)?;
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
// A minimization+queue policy to get testcasess from the corpus

View File

@ -1,5 +1,5 @@
use core::fmt::Debug;
use std::{fs, marker::PhantomData, ops::Range, process, time::Duration};
use std::{fs, marker::PhantomData, ops::Range, path::PathBuf, process};
#[cfg(feature = "simplemgr")]
use libafl::events::SimpleEventManager;
@ -23,8 +23,8 @@ use libafl::{
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler,
},
stages::{
calibrate::CalibrationStage, power::StdPowerMutationalStage, IfStage, ShadowTracingStage,
StagesTuple, StatsStage, StdMutationalStage,
calibrate::CalibrationStage, power::StdPowerMutationalStage, AflStatsStage, IfStage,
ShadowTracingStage, StagesTuple, StdMutationalStage,
},
state::{HasCorpus, StdState, UsesState},
Error, HasMetadata, NopFuzzer,
@ -137,7 +137,10 @@ impl<M: Monitor> Instance<'_, M> {
let stats_stage = IfStage::new(
|_, _, _, _| Ok(self.options.tui),
tuple_list!(StatsStage::new(Duration::from_secs(5))),
tuple_list!(AflStatsStage::builder()
.map_observer(&edges_observer)
.stats_file(PathBuf::from("stats.txt"))
.build()?),
);
// Feedback to rate the interestingness of an input
@ -274,7 +277,7 @@ impl<M: Monitor> Instance<'_, M> {
5,
)?;
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
// The order of the stages matter!

View File

@ -298,7 +298,7 @@ fn fuzz(
5,
)?;
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
// A minimization+queue policy to get testcasess from the corpus

View File

@ -300,7 +300,7 @@ fn fuzz(
5,
)?;
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
// A minimization+queue policy to get testcasess from the corpus
@ -371,7 +371,8 @@ fn fuzz(
let tracing = AFLppCmplogTracingStage::new(cmplog_executor, cmplog_ref);
// Setup a randomic Input2State stage
let rq = MultiMutationalStage::new(AFLppRedQueen::with_cmplog_options(true, true));
let rq: MultiMutationalStage<_, _, BytesInput, _, _, _> =
MultiMutationalStage::new(AFLppRedQueen::with_cmplog_options(true, true));
let cb = |_fuzzer: &mut _,
_executor: &mut _,

View File

@ -266,7 +266,7 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, {
SupportedMutationalStages::StdMutational(StdMutationalStage::new(mutation), PhantomData)
} else {
SupportedMutationalStages::PowerMutational(
StdPowerMutationalStage::new(mutation),
StdPowerMutationalStage::<_, _, BytesInput, _, _, _>::new(mutation),
PhantomData,
)
};
@ -487,7 +487,9 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, {
let tracing = AFLppCmplogTracingStage::new(cmplog_executor, cmplog_ref);
// Create a randomic Input2State stage
let rq = MultiMutationalStage::new(AFLppRedQueen::with_cmplog_options(true, true));
let rq = MultiMutationalStage::<_, _, BytesInput, _, _, _>::new(
AFLppRedQueen::with_cmplog_options(true, true),
);
// Create an IfStage and wrap the CmpLog stages in it.
// We run cmplog on the second fuzz run of the testcase.

View File

@ -1,37 +1,25 @@
use std::{borrow::Cow, marker::PhantomData};
use libafl::{
corpus::Corpus,
inputs::Input,
mutators::Mutator,
stages::{mutational::MutatedTransform, MutationalStage, Stage},
state::{HasCorpus, HasRand, State, UsesState},
Error, Evaluator, HasNamedMetadata,
stages::{MutationalStage, Stage},
Error,
};
use libafl_bolts::Named;
#[derive(Debug)]
pub enum SupportedMutationalStages<S, SM, P, E, EM, M, I, Z> {
StdMutational(SM, PhantomData<(S, I, M, EM, Z, E)>),
PowerMutational(P, PhantomData<(S, I, M, EM, Z, E)>),
pub enum SupportedMutationalStages<SM, P> {
StdMutational(SM, PhantomData<P>),
PowerMutational(P, PhantomData<SM>),
}
impl<S, SM, P, E, EM, M, I, Z> MutationalStage<E, EM, I, M, Z>
for SupportedMutationalStages<S, SM, P, E, EM, M, I, Z>
impl<S, SM, P> MutationalStage<S> for SupportedMutationalStages<SM, P>
where
E: UsesState<State = S>,
EM: UsesState<State = S>,
M: Mutator<I, S>,
Z: Evaluator<E, EM, State = S>,
I: MutatedTransform<S::Input, S> + Clone + Input,
SM: MutationalStage<E, EM, I, M, Z, State = S>,
P: MutationalStage<E, EM, I, M, Z, State = S>,
S: State<Input = I> + HasRand + HasCorpus + HasNamedMetadata,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
SM: MutationalStage<S>,
P: MutationalStage<S, Mutator = SM::Mutator>,
{
type Mutator = SM::Mutator;
/// The mutator, added to this stage
#[inline]
fn mutator(&self) -> &M {
fn mutator(&self) -> &Self::Mutator {
match self {
Self::StdMutational(m, _) => m.mutator(),
Self::PowerMutational(p, _) => p.mutator(),
@ -40,7 +28,7 @@ where
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut M {
fn mutator_mut(&mut self) -> &mut Self::Mutator {
match self {
Self::StdMutational(m, _) => m.mutator_mut(),
Self::PowerMutational(p, _) => p.mutator_mut(),
@ -56,14 +44,7 @@ where
}
}
impl<S, SM, P, E, EM, M, I, Z> UsesState for SupportedMutationalStages<S, SM, P, E, EM, M, I, Z>
where
S: State + HasRand,
{
type State = S;
}
impl<S, SM, P, E, EM, M, I, Z> Named for SupportedMutationalStages<S, SM, P, E, EM, M, I, Z>
impl<SM, P> Named for SupportedMutationalStages<SM, P>
where
SM: Named,
P: Named,
@ -76,18 +57,10 @@ where
}
}
impl<S, SM, P, E, EM, M, I, Z> Stage<E, EM, Z>
for SupportedMutationalStages<S, SM, P, E, EM, M, I, Z>
impl<E, EM, S, SM, P, Z> Stage<E, EM, S, Z> for SupportedMutationalStages<SM, P>
where
E: UsesState<State = S>,
EM: UsesState<State = S>,
M: Mutator<I, S>,
Z: Evaluator<E, EM, State = S>,
I: MutatedTransform<S::Input, S> + Clone + Input,
SM: MutationalStage<E, EM, I, M, Z, State = S>,
P: MutationalStage<E, EM, I, M, Z, State = S>,
S: State<Input = I> + HasRand + HasCorpus + HasNamedMetadata,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
SM: Stage<E, EM, S, Z>,
P: Stage<E, EM, S, Z>,
{
#[inline]
#[allow(clippy::let_and_return)]

View File

@ -314,7 +314,7 @@ fn fuzz(
5,
)?;
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
// A minimization+queue policy to get testcasess from the corpus

View File

@ -307,7 +307,7 @@ fn fuzz(
5,
)?;
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
// A minimization+queue policy to get testcasess from the corpus

View File

@ -317,7 +317,7 @@ fn fuzz(
5,
)?;
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
// A minimization+queue policy to get testcasess from the corpus

View File

@ -374,7 +374,7 @@ fn fuzz_binary(
5,
)?;
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
// A minimization+queue policy to get testcasess from the corpus
@ -589,7 +589,7 @@ fn fuzz_text(
5,
)?;
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
let grimoire_mutator = StdScheduledMutator::with_max_stack_pow(

View File

@ -143,7 +143,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
let mut stages = tuple_list!(calibration, power);

View File

@ -140,7 +140,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
let mut stages = tuple_list!(calibration, power);

View File

@ -141,7 +141,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
let mut stages = tuple_list!(calibration, power);

View File

@ -110,7 +110,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let power: StdPowerMutationalStage<_, _, BytesInput, _, _> =
let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> =
StdPowerMutationalStage::new(mutator);
let mut stages = tuple_list!(calibration, power);

View File

@ -28,6 +28,7 @@ rustc-args = ["--cfg", "docsrs"]
[features]
default = [
"introspection",
"std",
"derive",
"llmp_compression",

View File

@ -27,7 +27,7 @@ use crate::{
};
/// Send a monitor update all 15 (or more) seconds
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15);
pub(crate) const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15);
/// Holds a scheduler
pub trait HasScheduler: UsesState

View File

@ -26,11 +26,12 @@ use crate::{
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},
state::{HasCorpus, HasExecutions, HasImported, HasStartTime, Stoppable},
std::string::ToString,
Error, HasMetadata, HasNamedMetadata, HasScheduler,
};
@ -73,7 +74,7 @@ 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)]
pub struct AflStatsStage<C, E, EM, O, Z> {
pub struct AflStatsStage<C, E, EM, O, S, Z> {
map_observer_handle: Handle<C>,
stats_file_path: PathBuf,
plot_file_path: Option<PathBuf>,
@ -112,7 +113,7 @@ pub struct AflStatsStage<C, E, EM, O, Z> {
autotokens_enabled: bool,
/// The core we are bound to
core_id: CoreId,
phantom_data: PhantomData<(O, E, EM, Z)>,
phantom_data: PhantomData<(O, E, EM, S, Z)>,
}
/// AFL++'s `fuzzer_stats`
@ -234,39 +235,31 @@ pub struct AFLPlotData<'a> {
edges_found: &'a u64,
}
impl<C, E, EM, O, Z> UsesState for AflStatsStage<C, E, EM, O, Z>
impl<C, E, EM, O, S, Z> Stage<E, EM, S, Z> for AflStatsStage<C, E, EM, O, S, Z>
where
E: UsesState,
EM: EventFirer<State = E::State>,
Z: UsesState<State = E::State>,
{
type State = E::State;
}
impl<C, E, EM, O, Z> Stage<E, EM, Z> for AflStatsStage<C, E, EM, O, Z>
where
E: UsesState + HasObservers,
EM: EventFirer<State = E::State>,
Z: UsesState<State = E::State> + HasScheduler,
E::State: HasImported
E: HasObservers,
EM: EventFirer,
Z: HasScheduler<State = S>,
S: HasImported
+ HasCorpus
+ HasMetadata
+ HasStartTime
+ HasExecutions
+ HasNamedMetadata
+ Stoppable,
+ Stoppable
+ HasCurrentCorpusId
+ UsesInput,
E::Observers: MatchNameRef,
O: MapObserver,
C: AsRef<O> + Named,
<Z as HasScheduler>::Scheduler: HasQueueCycles,
<<E as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = E::Input>,
{
#[allow(clippy::too_many_lines)]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut E::State,
state: &mut S,
_manager: &mut EM,
) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_id()? else {
@ -413,27 +406,26 @@ where
Ok(())
}
fn should_restart(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, _state: &mut S) -> Result<bool, Error> {
Ok(true)
}
fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> {
Ok(())
}
}
impl<C, E, EM, O, Z> AflStatsStage<C, E, EM, O, Z>
impl<C, E, EM, O, S, Z> AflStatsStage<C, E, EM, O, S, Z>
where
E: UsesState + HasObservers,
EM: EventFirer<State = E::State>,
Z: UsesState<State = E::State>,
E::State: HasImported + HasCorpus + HasMetadata + HasExecutions,
E: HasObservers,
EM: EventFirer,
S: HasImported + HasCorpus + HasMetadata + HasExecutions,
C: AsRef<O> + Named,
O: MapObserver,
{
/// Builder for `AflStatsStage`
#[must_use]
pub fn builder() -> AflStatsStageBuilder<C, E, EM, O, Z> {
pub fn builder() -> AflStatsStageBuilder<C, E, EM, O, S, Z> {
AflStatsStageBuilder::new()
}
@ -459,13 +451,13 @@ where
Ok(())
}
fn maybe_update_is_favored_size(&mut self, testcase: &Testcase<E::Input>) {
fn maybe_update_is_favored_size(&mut self, testcase: &Testcase<<S::Corpus as Corpus>::Input>) {
if testcase.has_metadata::<IsFavoredMetadata>() {
self.is_favored_size += 1;
}
}
fn maybe_update_slowest_exec(&mut self, testcase: &Testcase<E::Input>) {
fn maybe_update_slowest_exec(&mut self, testcase: &Testcase<<S::Corpus as Corpus>::Input>) {
if let Some(exec_time) = testcase.exec_time() {
if exec_time > &self.slowest_exec {
self.slowest_exec = *exec_time;
@ -477,7 +469,7 @@ where
self.has_fuzzed_size += 1;
}
fn maybe_update_max_depth(&mut self, testcase: &Testcase<E::Input>) {
fn maybe_update_max_depth(&mut self, testcase: &Testcase<<S::Corpus as Corpus>::Input>) {
if let Ok(metadata) = testcase.metadata::<SchedulerTestcaseMetadata>() {
if metadata.depth() > self.max_depth {
self.max_depth = metadata.depth();
@ -490,7 +482,11 @@ where
}
#[cfg(feature = "track_hit_feedbacks")]
fn maybe_update_last_crash(&mut self, testcase: &Testcase<E::Input>, state: &E::State) {
fn maybe_update_last_crash(
&mut self,
testcase: &Testcase<<S::Corpus as Corpus>::Input>,
state: &S,
) {
#[cfg(feature = "track_hit_feedbacks")]
if testcase
.hit_objectives()
@ -502,7 +498,11 @@ where
}
#[cfg(feature = "track_hit_feedbacks")]
fn maybe_update_last_hang(&mut self, testcase: &Testcase<E::Input>, state: &E::State) {
fn maybe_update_last_hang(
&mut self,
testcase: &Testcase<<S::Corpus as Corpus>::Input>,
state: &S,
) {
if testcase
.hit_objectives()
.contains(&Cow::Borrowed(TIMEOUT_FEEDBACK_NAME))
@ -624,7 +624,7 @@ pub fn get_run_cmdline() -> Cow<'static, str> {
/// The Builder for `AflStatsStage`
#[derive(Debug)]
pub struct AflStatsStageBuilder<C, E, EM, O, Z> {
pub struct AflStatsStageBuilder<C, E, EM, O, S, Z> {
stats_file_path: Option<PathBuf>,
plot_file_path: Option<PathBuf>,
core_id: Option<CoreId>,
@ -636,15 +636,14 @@ pub struct AflStatsStageBuilder<C, E, EM, O, Z> {
banner: String,
version: String,
target_mode: String,
phantom_data: PhantomData<(O, E, EM, Z)>,
phantom_data: PhantomData<(O, E, EM, S, Z)>,
}
impl<C, E, EM, O, Z> AflStatsStageBuilder<C, E, EM, O, Z>
impl<C, E, EM, O, S, Z> AflStatsStageBuilder<C, E, EM, O, S, Z>
where
E: UsesState + HasObservers,
EM: EventFirer<State = E::State>,
Z: UsesState<State = E::State>,
E::State: HasImported + HasCorpus + HasMetadata + HasExecutions,
E: HasObservers,
EM: EventFirer,
S: HasImported + HasCorpus + HasMetadata + HasExecutions,
C: AsRef<O> + Named,
O: MapObserver,
{
@ -758,7 +757,7 @@ where
/// Cannot create the plot file (if provided)
/// No `MapObserver` supplied to the builder
/// No `stats_file_path` provieded
pub fn build(self) -> Result<AflStatsStage<C, E, EM, O, Z>, Error> {
pub fn build(self) -> Result<AflStatsStage<C, E, EM, O, S, Z>, Error> {
if self.stats_file_path.is_none() {
return Err(Error::illegal_argument("Must set `stats_file_path`"));
}

View File

@ -13,17 +13,17 @@ use num_traits::Bounded;
use serde::{Deserialize, Serialize};
use crate::{
corpus::{Corpus, SchedulerTestcaseMetadata},
corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata},
events::{Event, EventFirer, LogSeverity},
executors::{Executor, ExitKind, HasObservers},
feedbacks::{map::MapFeedbackMetadata, HasObserverHandle},
fuzzer::Evaluator,
inputs::UsesInput,
inputs::{Input, UsesInput},
monitors::{AggregatorOps, UserStats, UserStatsValue},
observers::{MapObserver, ObserversTuple},
schedulers::powersched::SchedulerMetadata,
stages::{RetryCountRestartHelper, Stage},
state::{HasCorpus, HasCurrentTestcase, HasExecutions, UsesState},
state::{HasCorpus, HasCurrentTestcase, HasExecutions},
Error, HasMetadata, HasNamedMetadata,
};
@ -73,38 +73,37 @@ impl Default for UnstableEntriesMetadata {
pub const CALIBRATION_STAGE_NAME: &str = "calibration";
/// The calibration stage will measure the average exec time and the target's stability for this input.
#[derive(Clone, Debug)]
pub struct CalibrationStage<C, E, O, OT> {
pub struct CalibrationStage<C, E, O, OT, S> {
map_observer_handle: Handle<C>,
map_name: Cow<'static, str>,
name: Cow<'static, str>,
stage_max: usize,
/// If we should track stability
track_stability: bool,
phantom: PhantomData<(E, O, OT)>,
phantom: PhantomData<(E, O, OT, S)>,
}
const CAL_STAGE_START: usize = 4; // AFL++'s CAL_CYCLES_FAST + 1
const CAL_STAGE_MAX: usize = 8; // AFL++'s CAL_CYCLES + 1
impl<C, E, O, OT> UsesState for CalibrationStage<C, E, O, OT>
impl<C, E, EM, O, OT, S, Z> Stage<E, EM, S, Z> for CalibrationStage<C, E, O, OT, S>
where
E: UsesState,
{
type State = E::State;
}
impl<C, E, EM, O, OT, Z> Stage<E, EM, Z> for CalibrationStage<C, E, O, OT>
where
E: Executor<EM, Z> + HasObservers<Observers = OT>,
EM: EventFirer<State = Self::State>,
E: Executor<EM, Z, State = S> + HasObservers<Observers = OT>,
EM: EventFirer<State = S>,
O: MapObserver,
C: AsRef<O>,
for<'de> <O as MapObserver>::Entry:
Serialize + Deserialize<'de> + 'static + Default + Debug + Bounded,
OT: ObserversTuple<Self::Input, Self::State>,
E::State: HasCorpus + HasMetadata + HasNamedMetadata + HasExecutions + HasCurrentTestcase,
Z: Evaluator<E, EM, State = Self::State>,
<<E as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus
+ HasMetadata
+ HasNamedMetadata
+ HasExecutions
+ HasCurrentTestcase
+ HasCurrentCorpusId
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
Z: Evaluator<E, EM, State = S>,
<S::Corpus as Corpus>::Input: Input,
{
#[inline]
#[allow(
@ -116,7 +115,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
mgr: &mut EM,
) -> Result<(), Error> {
// Run this stage only once for each corpus entry and only if we haven't already inspected it
@ -368,7 +367,7 @@ where
Ok(())
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// Calibration stage disallow restarts
// If a testcase that causes crash/timeout in the queue, we need to remove it from the queue immediately.
RetryCountRestartHelper::no_retry(state, &self.name)
@ -377,19 +376,19 @@ where
// remove this guy from corpus queue
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
// TODO: Make sure this is the correct way / there may be a better way?
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<C, E, O, OT> CalibrationStage<C, E, O, OT>
impl<C, E, O, OT, S> CalibrationStage<C, E, O, OT, S>
where
O: MapObserver,
for<'it> O: AsIter<'it, Item = O::Entry>,
C: AsRef<O>,
OT: ObserversTuple<<Self as UsesInput>::Input, <Self as UsesState>::State>,
E: UsesState,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus,
{
/// Create a new [`CalibrationStage`].
#[must_use]
@ -422,7 +421,7 @@ where
}
}
impl<C, E, O, OT> Named for CalibrationStage<C, E, O, OT> {
impl<C, E, O, OT, S> Named for CalibrationStage<C, E, O, OT, S> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}

View File

@ -14,7 +14,7 @@ use libafl_bolts::{
use serde::{Deserialize, Serialize};
use crate::{
corpus::Corpus,
corpus::{Corpus, HasCurrentCorpusId},
events::EventFirer,
executors::{Executor, HasObservers},
inputs::{HasMutatorBytes, UsesInput},
@ -62,21 +62,14 @@ impl Ord for Earlier {
pub const COLORIZATION_STAGE_NAME: &str = "colorization";
/// The mutational stage using power schedules
#[derive(Clone, Debug)]
pub struct ColorizationStage<C, E, EM, O, Z> {
pub struct ColorizationStage<C, E, EM, O, S, Z> {
map_observer_handle: Handle<C>,
name: Cow<'static, str>,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, O, E, Z)>,
phantom: PhantomData<(E, EM, O, E, S, Z)>,
}
impl<C, E, EM, O, Z> UsesState for ColorizationStage<C, E, EM, O, Z>
where
E: UsesState,
{
type State = E::State;
}
impl<C, E, EM, O, Z> Named for ColorizationStage<C, E, EM, O, Z>
impl<C, E, EM, O, S, Z> Named for ColorizationStage<C, E, EM, O, S, Z>
where
E: UsesState,
{
@ -85,17 +78,21 @@ where
}
}
impl<C, E, EM, O, Z> Stage<E, EM, Z> for ColorizationStage<C, E, EM, O, Z>
impl<C, E, EM, O, S, Z> Stage<E, EM, S, Z> for ColorizationStage<C, E, EM, O, S, Z>
where
EM: UsesState<State = Self::State> + EventFirer,
E: HasObservers + Executor<EM, Z>,
E::State: HasCorpus + HasMetadata + HasRand + HasNamedMetadata,
E::Observers: ObserversTuple<<Self as UsesInput>::Input, <Self as UsesState>::State>,
E::Input: HasMutatorBytes,
EM: EventFirer<State = S>,
E: HasObservers + Executor<EM, Z, State = S>,
S: HasCorpus
+ HasMetadata
+ HasRand
+ HasNamedMetadata
+ HasCurrentCorpusId
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: HasMutatorBytes + Clone,
O: MapObserver,
C: AsRef<O> + Named,
Z: UsesState<State = Self::State>,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = E::Input>, //delete me
Z: UsesState<State = S>,
{
#[inline]
#[allow(clippy::let_and_return)]
@ -103,7 +100,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E, // don't need the *main* executor for tracing
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
// Run with the mutated input
@ -112,14 +109,14 @@ where
Ok(())
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// This is a deterministic stage
// Once it failed, then don't retry,
// It will just fail again
RetryCountRestartHelper::no_retry(state, &self.name)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
@ -163,27 +160,31 @@ impl TaintMetadata {
libafl_bolts::impl_serdeany!(TaintMetadata);
impl<C, E, EM, O, Z> ColorizationStage<C, E, EM, O, Z>
impl<C, E, EM, O, S, Z> ColorizationStage<C, E, EM, O, S, Z>
where
EM: UsesState<State = <Self as UsesState>::State> + EventFirer,
EM: EventFirer<State = S>,
O: MapObserver,
C: AsRef<O> + Named,
E: HasObservers + Executor<EM, Z>,
E::Observers: ObserversTuple<<Self as UsesInput>::Input, <Self as UsesState>::State>,
<E as UsesState>::State: HasCorpus + HasMetadata + HasRand,
E::Input: HasMutatorBytes,
Z: UsesState<State = <Self as UsesState>::State>,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = E::Input>, //delete me
E: HasObservers + Executor<EM, Z, State = S>,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus
+ HasMetadata
+ HasRand
+ HasCurrentCorpusId
+ HasCurrentTestcase
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
<S::Corpus as Corpus>::Input: HasMutatorBytes + Clone,
Z: UsesState<State = S>,
{
#[inline]
#[allow(clippy::let_and_return)]
fn colorize(
fuzzer: &mut Z,
executor: &mut E,
state: &mut <Self as UsesState>::State,
state: &mut S,
manager: &mut EM,
observer_handle: &Handle<C>,
) -> Result<E::Input, Error> {
) -> Result<<S::Corpus as Corpus>::Input, Error> {
let mut input = state.current_input_cloned()?;
// The backup of the input
let backup = input.clone();
@ -322,9 +323,9 @@ where
fn get_raw_map_hash_run(
fuzzer: &mut Z,
executor: &mut E,
state: &mut <Self as UsesState>::State,
state: &mut S,
manager: &mut EM,
input: &E::Input,
input: &<S::Corpus as Corpus>::Input,
observer_handle: &Handle<C>,
) -> Result<usize, Error> {
executor.observers_mut().pre_exec_all(state, input)?;
@ -348,7 +349,7 @@ where
/// Replace bytes with random values but following certain rules
#[allow(clippy::needless_range_loop)]
fn type_replace(bytes: &mut [u8], state: &mut <Self as UsesState>::State) {
fn type_replace(bytes: &mut [u8], state: &mut S) {
let len = bytes.len();
for idx in 0..len {
let c = match bytes[idx] {

View File

@ -14,14 +14,13 @@ use libafl_bolts::{
#[cfg(all(feature = "concolic_mutation", feature = "introspection"))]
use crate::monitors::PerfFeature;
#[cfg(all(feature = "introspection", feature = "concolic_mutation"))]
use crate::state::HasClientPerfMonitor;
use crate::{
corpus::Corpus,
corpus::{Corpus, HasCurrentCorpusId},
executors::{Executor, HasObservers},
inputs::UsesInput,
observers::{concolic::ConcolicObserver, ObserversTuple},
stages::{RetryCountRestartHelper, Stage, TracingStage},
state::{HasCorpus, HasCurrentTestcase, HasExecutions, UsesState},
state::{HasCorpus, HasCurrentTestcase, HasExecutions, MaybeHasClientPerfMonitor, UsesState},
Error, HasMetadata, HasNamedMetadata,
};
#[cfg(feature = "concolic_mutation")]
@ -29,51 +28,46 @@ use crate::{
inputs::HasMutatorBytes,
mark_feature_time,
observers::concolic::{ConcolicMetadata, SymExpr, SymExprRef},
start_timer,
state::State,
Evaluator,
start_timer, Evaluator,
};
/// Wraps a [`TracingStage`] to add concolic observing.
#[derive(Clone, Debug)]
pub struct ConcolicTracingStage<'a, EM, TE, Z> {
pub struct ConcolicTracingStage<'a, EM, TE, S, Z> {
name: Cow<'static, str>,
inner: TracingStage<EM, TE, Z>,
inner: TracingStage<EM, TE, S, Z>,
observer_handle: Handle<ConcolicObserver<'a>>,
}
impl<EM, TE, Z> UsesState for ConcolicTracingStage<'_, EM, TE, Z>
where
TE: UsesState,
{
type State = TE::State;
}
/// The name for concolic tracer
pub const CONCOLIC_TRACING_STAGE_NAME: &str = "concolictracing";
impl<EM, TE, Z> Named for ConcolicTracingStage<'_, EM, TE, Z> {
impl<EM, TE, S, Z> Named for ConcolicTracingStage<'_, EM, TE, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, EM, TE, Z> Stage<E, EM, Z> for ConcolicTracingStage<'_, EM, TE, Z>
impl<E, EM, TE, S, Z> Stage<E, EM, S, Z> for ConcolicTracingStage<'_, EM, TE, S, Z>
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
TE: Executor<EM, Z> + HasObservers,
TE::Observers: ObserversTuple<TE::Input, <Self as UsesState>::State>,
TE::State: HasExecutions + HasCorpus + HasNamedMetadata + HasCurrentTestcase,
Z: UsesState<State = Self::State>,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
TE: Executor<EM, Z, State = S> + HasObservers,
TE::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions
+ HasCorpus
+ HasNamedMetadata
+ HasCurrentTestcase
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
_executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
self.inner.trace(fuzzer, state, manager)?;
@ -87,23 +81,23 @@ where
Ok(())
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// This is a deterministic stage
// Once it failed, then don't retry,
// It will just fail again
RetryCountRestartHelper::no_retry(state, &self.name)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<'a, EM, TE, Z> ConcolicTracingStage<'a, EM, TE, Z> {
impl<'a, EM, TE, S, Z> ConcolicTracingStage<'a, EM, TE, S, Z> {
/// Creates a new default tracing stage using the given [`Executor`], observing traces from a
/// [`ConcolicObserver`] with the given name.
pub fn new(
inner: TracingStage<EM, TE, Z>,
inner: TracingStage<EM, TE, S, Z>,
observer_handle: Handle<ConcolicObserver<'a>>,
) -> Self {
let observer_name = observer_handle.name().clone();
@ -392,22 +386,25 @@ impl<Z> Named for SimpleConcolicMutationalStage<Z> {
}
#[cfg(feature = "concolic_mutation")]
impl<E, EM, Z> Stage<E, EM, Z> for SimpleConcolicMutationalStage<Z>
impl<E, EM, S, Z> Stage<E, EM, S, Z> for SimpleConcolicMutationalStage<Z>
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
Z: Evaluator<E, EM>,
Z::Input: HasMutatorBytes,
Z::State:
State + HasExecutions + HasCorpus + HasMetadata + HasNamedMetadata + HasCurrentTestcase,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Z::Input>, //delete me
Z: Evaluator<E, EM, State = S>,
<S::Corpus as Corpus>::Input: HasMutatorBytes + Clone,
S: HasExecutions
+ HasCorpus
+ HasMetadata
+ HasNamedMetadata
+ HasCurrentTestcase
+ MaybeHasClientPerfMonitor
+ HasCurrentCorpusId
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
{
@ -437,7 +434,7 @@ where
}
#[inline]
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// This is a deterministic stage
// Once it failed, then don't retry,
// It will just fail again
@ -445,7 +442,7 @@ where
}
#[inline]
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}

View File

@ -16,7 +16,7 @@ use crate::{
corpus::{Corpus, CorpusId, Testcase},
inputs::Input,
stages::Stage,
state::{HasCorpus, HasRand, HasSolutions, UsesState},
state::{HasCorpus, HasRand, HasSolutions},
Error, HasMetadata,
};
@ -35,65 +35,53 @@ impl_serdeany!(DumpToDiskMetadata);
/// The [`DumpToDiskStage`] is a stage that dumps the corpus and the solutions to disk
#[derive(Debug)]
pub struct DumpToDiskStage<CB1, CB2, EM, Z> {
pub struct DumpToDiskStage<CB1, CB2, EM, S, Z> {
solutions_dir: PathBuf,
corpus_dir: PathBuf,
to_bytes: CB1,
generate_filename: CB2,
phantom: PhantomData<(EM, Z)>,
phantom: PhantomData<(EM, S, Z)>,
}
impl<CB1, CB2, EM, Z> UsesState for DumpToDiskStage<CB1, CB2, EM, Z>
impl<CB1, CB2, E, EM, S, P, Z> Stage<E, EM, S, Z> for DumpToDiskStage<CB1, CB2, EM, S, Z>
where
EM: UsesState,
{
type State = EM::State;
}
impl<CB1, CB2, P, E, EM, Z> Stage<E, EM, Z> for DumpToDiskStage<CB1, CB2, EM, Z>
where
CB1: FnMut(&Testcase<Self::Input>, &Self::State) -> Vec<u8>,
CB2: FnMut(&Testcase<Self::Input>, &CorpusId) -> P,
CB1: FnMut(&Testcase<<S::Corpus as Corpus>::Input>, &S) -> Vec<u8>,
CB2: FnMut(&Testcase<<S::Corpus as Corpus>::Input>, &CorpusId) -> P,
S: HasCorpus + HasSolutions + HasRand + HasMetadata,
S::Solutions: Corpus<Input = <S::Corpus as Corpus>::Input>,
P: AsRef<Path>,
EM: UsesState,
E: UsesState<State = Self::State>,
Z: UsesState<State = Self::State>,
EM::State: HasCorpus + HasSolutions + HasRand + HasMetadata,
<<EM as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
<<EM as UsesState>::State as HasSolutions>::Solutions: Corpus<Input = Self::Input>, //delete me
{
#[inline]
fn perform(
&mut self,
_fuzzer: &mut Z,
_executor: &mut E,
state: &mut Self::State,
state: &mut S,
_manager: &mut EM,
) -> Result<(), Error> {
self.dump_state_to_disk(state)
}
#[inline]
fn should_restart(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, _state: &mut S) -> Result<bool, Error> {
// Not executing the target, so restart safety is not needed
Ok(true)
}
#[inline]
fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> {
// Not executing the target, so restart safety is not needed
Ok(())
}
}
/// Implementation for `DumpToDiskStage` with a default `generate_filename` function.
impl<CB1, EM, Z> DumpToDiskStage<CB1, fn(&Testcase<EM::Input>, &CorpusId) -> String, EM, Z>
impl<CB1, EM, S, Z>
DumpToDiskStage<CB1, fn(&Testcase<<S::Corpus as Corpus>::Input>, &CorpusId) -> String, EM, S, Z>
where
EM: UsesState,
Z: UsesState,
<EM as UsesState>::State: HasCorpus + HasSolutions + HasRand + HasMetadata,
<<EM as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = EM::Input>,
<<EM as UsesState>::State as HasSolutions>::Solutions: Corpus<Input = EM::Input>,
S: HasCorpus + HasSolutions + HasRand + HasMetadata,
S::Solutions: Corpus<Input = <S::Corpus as Corpus>::Input>,
<S::Corpus as Corpus>::Input: Input,
{
/// Create a new [`DumpToDiskStage`] with a default `generate_filename` function.
pub fn new<A, B>(to_bytes: CB1, corpus_dir: A, solutions_dir: B) -> Result<Self, Error>
@ -111,7 +99,10 @@ where
/// Default `generate_filename` function.
#[allow(clippy::trivially_copy_pass_by_ref)]
fn generate_filename(testcase: &Testcase<EM::Input>, id: &CorpusId) -> String {
fn generate_filename(
testcase: &Testcase<<S::Corpus as Corpus>::Input>,
id: &CorpusId,
) -> String {
[
Some(id.0.to_string()),
testcase.filename().clone(),
@ -128,13 +119,10 @@ where
}
}
impl<CB1, CB2, EM, Z> DumpToDiskStage<CB1, CB2, EM, Z>
impl<CB1, CB2, EM, S, Z> DumpToDiskStage<CB1, CB2, EM, S, Z>
where
EM: UsesState,
Z: UsesState,
<EM as UsesState>::State: HasCorpus + HasSolutions + HasRand + HasMetadata,
<<EM as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = EM::Input>,
<<EM as UsesState>::State as HasSolutions>::Solutions: Corpus<Input = EM::Input>,
S: HasCorpus + HasMetadata + HasSolutions,
S::Solutions: Corpus<Input = <S::Corpus as Corpus>::Input>,
{
/// Create a new [`DumpToDiskStage`] with a custom `generate_filename` function.
pub fn new_with_custom_filenames<A, B>(
@ -175,19 +163,10 @@ where
}
#[inline]
fn dump_state_to_disk<P: AsRef<Path>>(
&mut self,
state: &mut <Self as UsesState>::State,
) -> Result<(), Error>
fn dump_state_to_disk<P: AsRef<Path>>(&mut self, state: &mut S) -> Result<(), Error>
where
CB1: FnMut(
&Testcase<<<<EM as UsesState>::State as HasCorpus>::Corpus as Corpus>::Input>,
&<EM as UsesState>::State,
) -> Vec<u8>,
CB2: FnMut(
&Testcase<<<<EM as UsesState>::State as HasCorpus>::Corpus as Corpus>::Input>,
&CorpusId,
) -> P,
CB1: FnMut(&Testcase<<S::Corpus as Corpus>::Input>, &S) -> Vec<u8>,
CB2: FnMut(&Testcase<<S::Corpus as Corpus>::Input>, &CorpusId) -> P,
{
let (mut corpus_id, mut solutions_id) =
if let Some(meta) = state.metadata_map().get::<DumpToDiskMetadata>() {

View File

@ -11,6 +11,8 @@ use libafl_bolts::{
AsSlice, Named,
};
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
corpus::{Corpus, HasCurrentCorpusId},
executors::{Executor, HasObservers},
@ -21,11 +23,9 @@ use crate::{
require_novelties_tracking,
stages::{RetryCountRestartHelper, Stage},
start_timer,
state::{HasCorpus, HasExecutions, UsesState},
state::{HasCorpus, HasExecutions, MaybeHasClientPerfMonitor, UsesState},
Error, HasMetadata, HasNamedMetadata,
};
#[cfg(feature = "introspection")]
use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
const MAX_GENERALIZED_LEN: usize = 8192;
@ -48,37 +48,35 @@ pub static GENERALIZATION_STAGE_NAME: &str = "generalization";
/// A stage that runs a tracer executor
#[derive(Clone, Debug)]
pub struct GeneralizationStage<C, EM, O, OT, Z> {
pub struct GeneralizationStage<C, EM, O, OT, S, Z> {
name: Cow<'static, str>,
map_observer_handle: Handle<C>,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(EM, O, OT, Z)>,
phantom: PhantomData<(EM, O, OT, S, Z)>,
}
impl<C, EM, O, OT, Z> Named for GeneralizationStage<C, EM, O, OT, Z> {
impl<C, EM, O, OT, S, Z> Named for GeneralizationStage<C, EM, O, OT, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<C, EM, O, OT, Z> UsesState for GeneralizationStage<C, EM, O, OT, Z>
where
EM: UsesState,
{
type State = EM::State;
}
impl<C, E, EM, O, Z> Stage<E, EM, Z> for GeneralizationStage<C, EM, O, E::Observers, Z>
impl<C, E, EM, O, S, Z> Stage<E, EM, S, Z> for GeneralizationStage<C, EM, O, E::Observers, S, Z>
where
O: MapObserver,
C: CanTrack + AsRef<O> + Named,
E: Executor<EM, Z, State = Self::State> + HasObservers,
E::Observers: ObserversTuple<BytesInput, <Self as UsesState>::State>,
EM::State:
UsesInput<Input = BytesInput> + HasExecutions + HasMetadata + HasCorpus + HasNamedMetadata,
EM: UsesState,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = BytesInput>, //delete me
Z: UsesState<State = Self::State>,
E: Executor<EM, Z, State = S> + HasObservers,
E::Observers: ObserversTuple<BytesInput, S>,
S: HasExecutions
+ HasMetadata
+ HasCorpus
+ HasNamedMetadata
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = BytesInput>,
S::Corpus: Corpus<Input = BytesInput>,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
#[inline]
#[allow(clippy::too_many_lines)]
@ -86,7 +84,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let Some(corpus_id) = state.current_corpus_id()? else {
@ -331,26 +329,30 @@ where
}
#[inline]
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// TODO: We need to be able to resume better if something crashes or times out
RetryCountRestartHelper::should_restart(state, &self.name, 3)
}
#[inline]
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
// TODO: We need to be able to resume better if something crashes or times out
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<C, EM, O, OT, Z> GeneralizationStage<C, EM, O, OT, Z>
impl<C, EM, O, OT, S, Z> GeneralizationStage<C, EM, O, OT, S, Z>
where
EM: UsesState,
O: MapObserver,
C: CanTrack + AsRef<O> + Named,
<Self as UsesState>::State:
UsesInput<Input = BytesInput> + HasExecutions + HasMetadata + HasCorpus,
OT: ObserversTuple<BytesInput, <EM as UsesState>::State>,
S: HasExecutions
+ HasMetadata
+ HasCorpus
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = BytesInput>,
OT: ObserversTuple<BytesInput, S>,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
/// Create a new [`GeneralizationStage`].
#[must_use]
@ -370,15 +372,14 @@ where
&self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut <Self as UsesState>::State,
state: &mut S,
manager: &mut EM,
novelties: &[usize],
input: &BytesInput,
) -> Result<bool, Error>
where
E: Executor<EM, Z, State = <Self as UsesState>::State> + HasObservers,
E::Observers: ObserversTuple<BytesInput, <Self as UsesState>::State>,
Z: UsesState<State = EM::State>,
E: Executor<EM, Z, State = S> + HasObservers,
E::Observers: ObserversTuple<BytesInput, S>,
{
start_timer!(state);
executor.observers_mut().pre_exec_all(state, input)?;
@ -411,7 +412,7 @@ where
&self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut <Self as UsesState>::State,
state: &mut S,
manager: &mut EM,
payload: &mut Vec<Option<u8>>,
novelties: &[usize],
@ -419,8 +420,7 @@ where
split_char: u8,
) -> Result<(), Error>
where
E: Executor<EM, Z, State = <Self as UsesState>::State> + HasObservers<Observers = OT>,
Z: UsesState<State = EM::State>,
E: Executor<EM, Z, State = S> + HasObservers<Observers = OT>,
{
let mut start = 0;
while start < payload.len() {
@ -450,7 +450,7 @@ where
&self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut <Self as UsesState>::State,
state: &mut S,
manager: &mut EM,
payload: &mut Vec<Option<u8>>,
novelties: &[usize],
@ -458,8 +458,7 @@ where
closing_char: u8,
) -> Result<(), Error>
where
E: Executor<EM, Z, State = <Self as UsesState>::State> + HasObservers<Observers = OT>,
Z: UsesState<State = EM::State>,
E: Executor<EM, Z, State = S> + HasObservers<Observers = OT>,
{
let mut index = 0;
while index < payload.len() {

View File

@ -7,10 +7,11 @@
use core::marker::PhantomData;
use crate::{
corpus::Corpus,
generators::Generator,
inputs::UsesInput,
stages::Stage,
state::{HasCorpus, HasRand, UsesState},
state::{HasCorpus, HasRand},
Error, Evaluator,
};
@ -19,36 +20,27 @@ use crate::{
///
/// This stage can be used to construct black-box (e.g., grammar-based) fuzzers.
#[derive(Debug)]
pub struct GenStage<G, Z>(G, PhantomData<Z>);
pub struct GenStage<G, S, Z>(G, PhantomData<(S, Z)>);
impl<G, Z> GenStage<G, Z> {
impl<G, S, Z> GenStage<G, S, Z> {
/// Create a new [`GenStage`].
pub fn new(g: G) -> Self {
Self(g, PhantomData)
}
}
impl<G, Z> UsesState for GenStage<G, Z>
impl<E, EM, G, S, Z> Stage<E, EM, S, Z> for GenStage<G, S, Z>
where
Z: UsesState,
{
type State = Z::State;
}
impl<E, EM, Z, G> Stage<E, EM, Z> for GenStage<G, Z>
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
Z: Evaluator<E, EM>,
Self::State: HasCorpus + HasRand,
G: Generator<<<Self as UsesState>::State as UsesInput>::Input, Self::State>,
Z: Evaluator<E, EM, State = S>,
S: HasCorpus + HasRand + UsesInput<Input = <S::Corpus as Corpus>::Input>,
G: Generator<<S::Corpus as Corpus>::Input, S>,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let input = self.0.generate(state)?;
@ -56,13 +48,13 @@ where
Ok(())
}
fn should_restart(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, _state: &mut S) -> Result<bool, Error> {
// It's a random generation stage
// so you can restart for whatever times you want
Ok(true)
}
fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> {
Ok(())
}
}

View File

@ -3,8 +3,7 @@
use core::marker::PhantomData;
use crate::{
stages::{HasCurrentStageId, HasNestedStageStatus, Stage, StageId, StagesTuple},
state::UsesState,
stages::{HasNestedStageStatus, Stage, StageId, StagesTuple},
Error,
};
@ -32,33 +31,23 @@ impl NestedStageRetryCountRestartHelper {
#[derive(Debug)]
/// Perform the stage while the closure evaluates to true
pub struct WhileStage<CB, E, EM, ST, Z> {
pub struct WhileStage<CB, E, EM, ST, S, Z> {
closure: CB,
stages: ST,
phantom: PhantomData<(E, EM, Z)>,
phantom: PhantomData<(E, EM, S, Z)>,
}
impl<CB, E, EM, ST, Z> UsesState for WhileStage<CB, E, EM, ST, Z>
impl<CB, E, EM, ST, S, Z> Stage<E, EM, S, Z> for WhileStage<CB, E, EM, ST, S, Z>
where
E: UsesState,
{
type State = E::State;
}
impl<CB, E, EM, ST, Z> Stage<E, EM, Z> for WhileStage<CB, E, EM, ST, Z>
where
CB: FnMut(&mut Z, &mut E, &mut Self::State, &mut EM) -> Result<bool, Error>,
E: UsesState,
EM: UsesState<State = E::State>,
ST: StagesTuple<E, EM, Self::State, Z>,
Z: UsesState<State = Self::State>,
Self::State: HasNestedStageStatus,
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result<bool, Error>,
ST: StagesTuple<E, EM, S, Z>,
S: HasNestedStageStatus,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
while state.current_stage_id()?.is_some()
@ -70,19 +59,18 @@ where
Ok(())
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
NestedStageRetryCountRestartHelper::should_restart(state, self)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
NestedStageRetryCountRestartHelper::clear_progress(state, self)
}
}
impl<CB, E, EM, ST, Z> WhileStage<CB, E, EM, ST, Z>
impl<CB, E, EM, ST, S, Z> WhileStage<CB, E, EM, ST, S, Z>
where
CB: FnMut(&mut Z, &mut E, &mut <Self as UsesState>::State, &mut EM) -> Result<bool, Error>,
E: UsesState,
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result<bool, Error>,
{
/// Constructor
pub fn new(closure: CB, stages: ST) -> Self {
@ -97,33 +85,23 @@ where
/// A conditionally enabled stage.
/// If the closure returns true, the wrapped stage will be executed, else it will be skipped.
#[derive(Debug)]
pub struct IfStage<CB, E, EM, ST, Z> {
pub struct IfStage<CB, E, EM, ST, S, Z> {
closure: CB,
if_stages: ST,
phantom: PhantomData<(E, EM, Z)>,
phantom: PhantomData<(E, EM, S, Z)>,
}
impl<CB, E, EM, ST, Z> UsesState for IfStage<CB, E, EM, ST, Z>
impl<CB, E, EM, ST, S, Z> Stage<E, EM, S, Z> for IfStage<CB, E, EM, ST, S, Z>
where
E: UsesState,
{
type State = E::State;
}
impl<CB, E, EM, ST, Z> Stage<E, EM, Z> for IfStage<CB, E, EM, ST, Z>
where
CB: FnMut(&mut Z, &mut E, &mut Self::State, &mut EM) -> Result<bool, Error>,
E: UsesState,
EM: UsesState<State = Self::State>,
ST: StagesTuple<E, EM, Self::State, Z>,
Z: UsesState<State = Self::State>,
Self::State: HasNestedStageStatus,
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result<bool, Error>,
ST: StagesTuple<E, EM, S, Z>,
S: HasNestedStageStatus,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
if state.current_stage_id()?.is_some() || (self.closure)(fuzzer, executor, state, manager)?
@ -134,19 +112,18 @@ where
Ok(())
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
NestedStageRetryCountRestartHelper::should_restart(state, self)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
NestedStageRetryCountRestartHelper::clear_progress(state, self)
}
}
impl<CB, E, EM, ST, Z> IfStage<CB, E, EM, ST, Z>
impl<CB, E, EM, ST, S, Z> IfStage<CB, E, EM, ST, S, Z>
where
CB: FnMut(&mut Z, &mut E, &mut <Self as UsesState>::State, &mut EM) -> Result<bool, Error>,
E: UsesState,
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result<bool, Error>,
{
/// Constructor for this conditionally enabled stage.
/// If the closure returns true, the wrapped stage will be executed, else it will be skipped.
@ -161,35 +138,25 @@ where
/// Perform the stage if closure evaluates to true
#[derive(Debug)]
pub struct IfElseStage<CB, E, EM, ST1, ST2, Z> {
pub struct IfElseStage<CB, E, EM, ST1, ST2, S, Z> {
closure: CB,
if_stages: ST1,
else_stages: ST2,
phantom: PhantomData<(E, EM, Z)>,
phantom: PhantomData<(E, EM, S, Z)>,
}
impl<CB, E, EM, ST1, ST2, Z> UsesState for IfElseStage<CB, E, EM, ST1, ST2, Z>
impl<CB, E, EM, ST1, ST2, S, Z> Stage<E, EM, S, Z> for IfElseStage<CB, E, EM, ST1, ST2, S, Z>
where
E: UsesState,
{
type State = E::State;
}
impl<CB, E, EM, ST1, ST2, Z> Stage<E, EM, Z> for IfElseStage<CB, E, EM, ST1, ST2, Z>
where
CB: FnMut(&mut Z, &mut E, &mut Self::State, &mut EM) -> Result<bool, Error>,
E: UsesState,
EM: UsesState<State = Self::State>,
ST1: StagesTuple<E, EM, Self::State, Z>,
ST2: StagesTuple<E, EM, Self::State, Z>,
Z: UsesState<State = Self::State>,
Self::State: HasNestedStageStatus,
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result<bool, Error>,
ST1: StagesTuple<E, EM, S, Z>,
ST2: StagesTuple<E, EM, S, Z>,
S: HasNestedStageStatus,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let current = state.current_stage_id()?;
@ -219,19 +186,18 @@ where
Ok(())
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
NestedStageRetryCountRestartHelper::should_restart(state, self)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
NestedStageRetryCountRestartHelper::clear_progress(state, self)
}
}
impl<CB, E, EM, ST1, ST2, Z> IfElseStage<CB, E, EM, ST1, ST2, Z>
impl<CB, E, EM, ST1, ST2, S, Z> IfElseStage<CB, E, EM, ST1, ST2, S, Z>
where
CB: FnMut(&mut Z, &mut E, &mut <Self as UsesState>::State, &mut EM) -> Result<bool, Error>,
E: UsesState,
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result<bool, Error>,
{
/// Constructor
pub fn new(closure: CB, if_stages: ST1, else_stages: ST2) -> Self {
@ -246,31 +212,21 @@ where
/// A stage wrapper where the stages do not need to be initialized, but can be [`None`].
#[derive(Debug)]
pub struct OptionalStage<E, EM, ST, Z> {
pub struct OptionalStage<E, EM, ST, S, Z> {
stages: Option<ST>,
phantom: PhantomData<(E, EM, Z)>,
phantom: PhantomData<(E, EM, S, Z)>,
}
impl<E, EM, ST, Z> UsesState for OptionalStage<E, EM, ST, Z>
impl<E, EM, ST, S, Z> Stage<E, EM, S, Z> for OptionalStage<E, EM, ST, S, Z>
where
E: UsesState,
{
type State = E::State;
}
impl<E, EM, ST, Z> Stage<E, EM, Z> for OptionalStage<E, EM, ST, Z>
where
E: UsesState,
EM: UsesState<State = Self::State>,
ST: StagesTuple<E, EM, Self::State, Z>,
Z: UsesState<State = Self::State>,
Self::State: HasNestedStageStatus,
ST: StagesTuple<E, EM, S, Z>,
S: HasNestedStageStatus,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
if let Some(stages) = &mut self.stages {
@ -280,16 +236,16 @@ where
}
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
NestedStageRetryCountRestartHelper::should_restart(state, self)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
NestedStageRetryCountRestartHelper::clear_progress(state, self)
}
}
impl<E, EM, ST, Z> OptionalStage<E, EM, ST, Z> {
impl<E, EM, ST, S, Z> OptionalStage<E, EM, ST, S, Z> {
/// Constructor for this conditionally enabled stage.
#[must_use]
pub fn new(stages: Option<ST>) -> Self {

View File

@ -33,14 +33,11 @@ pub use logics::*;
pub use mutational::{MutationalStage, StdMutationalStage};
pub use power::{PowerMutationalStage, StdPowerMutationalStage};
use serde::{Deserialize, Serialize};
pub use stats::StatsStage;
#[cfg(feature = "std")]
pub use sync::*;
#[cfg(feature = "std")]
pub use time_tracker::TimeTrackingStageWrapper;
pub use tmin::{
MapEqualityFactory, MapEqualityFeedback, StdTMinMutationalStage, TMinMutationalStage,
};
pub use tmin::{MapEqualityFactory, MapEqualityFeedback, StdTMinMutationalStage};
pub use tracing::{ShadowTracingStage, TracingStage};
pub use tuneable::*;
use tuple_list::NonEmptyTuple;
@ -51,15 +48,9 @@ pub use verify_timeouts::{TimeoutsToVerify, VerifyTimeoutsStage};
use crate::{
corpus::{CorpusId, HasCurrentCorpusId},
events::{EventFirer, EventProcessor, EventRestarter, HasEventManagerId, ProgressReporter},
executors::{Executor, HasObservers},
inputs::UsesInput,
observers::ObserversTuple,
schedulers::Scheduler,
stages::push::PushStage,
state::{HasCorpus, HasExecutions, HasLastReportTime, HasRand, State, Stoppable, UsesState},
Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasMetadata, HasNamedMetadata,
HasScheduler,
events::EventProcessor,
state::{HasExecutions, State, Stoppable},
Error, HasNamedMetadata,
};
/// Mutational stage is the normal fuzzing stage.
@ -79,7 +70,6 @@ pub mod generalization;
pub mod generation;
pub mod logics;
pub mod power;
pub mod stats;
#[cfg(feature = "std")]
pub mod sync;
#[cfg(feature = "std")]
@ -93,21 +83,16 @@ pub mod verify_timeouts;
/// A stage is one step in the fuzzing process.
/// Multiple stages will be scheduled one by one for each input.
pub trait Stage<E, EM, Z>: UsesState
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
Z: UsesState<State = Self::State>,
{
pub trait Stage<E, EM, S, Z> {
/// This method will be called before every call to [`Stage::perform`].
/// Initialize the restart tracking for this stage, _if it is not yet initialized_.
/// On restart, this will be called again.
/// As long as [`Stage::clear_progress`], all subsequent calls happen on restart.
/// Returns `true`, if the stage's [`Stage::perform`] method should run, else `false`.
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error>;
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error>;
/// Clear the current status tracking of the associated stage
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error>;
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error>;
/// Run the stage.
///
@ -119,7 +104,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error>;
@ -128,7 +113,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
if self.should_restart(state)? {
@ -139,13 +124,7 @@ where
}
/// A tuple holding all `Stages` used for fuzzing.
pub trait StagesTuple<E, EM, S, Z>
where
E: UsesState<State = S>,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
S: UsesInput + HasCurrentStageId,
{
pub trait StagesTuple<E, EM, S, Z> {
/// Performs all `Stages` in this tuple.
fn perform_all(
&mut self,
@ -158,10 +137,7 @@ where
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for ()
where
E: UsesState<State = S>,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
S: UsesInput + HasCurrentStageId,
S: HasCurrentStageId,
{
fn perform_all(
&mut self,
@ -180,14 +156,12 @@ where
}
}
impl<Head, Tail, E, EM, Z> StagesTuple<E, EM, Head::State, Z> for (Head, Tail)
impl<Head, Tail, E, EM, S, Z> StagesTuple<E, EM, S, Z> for (Head, Tail)
where
Head: Stage<E, EM, Z>,
Tail: StagesTuple<E, EM, Head::State, Z> + HasConstLen,
E: UsesState<State = Head::State>,
EM: UsesState<State = Head::State> + EventProcessor<E, Z>,
Z: UsesState<State = Head::State>,
Head::State: HasCurrentStageId,
Head: Stage<E, EM, S, Z>,
Tail: StagesTuple<E, EM, S, Z> + HasConstLen,
S: HasCurrentStageId + Stoppable,
EM: EventProcessor<E, Z>,
{
/// Performs all stages in the tuple,
/// Checks after every stage if state wants to stop
@ -196,7 +170,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Head::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
match state.current_stage_id()? {
@ -239,66 +213,45 @@ where
}
}
impl<Head, Tail, E, EM, Z>
IntoVec<Box<dyn Stage<E, EM, Z, State = Head::State, Input = Head::Input>>> for (Head, Tail)
impl<Head, Tail, E, EM, S, Z> IntoVec<Box<dyn Stage<E, EM, S, Z>>> for (Head, Tail)
where
Head: Stage<E, EM, Z> + 'static,
Tail: StagesTuple<E, EM, Head::State, Z>
+ HasConstLen
+ IntoVec<Box<dyn Stage<E, EM, Z, State = Head::State, Input = Head::Input>>>,
E: UsesState<State = Head::State>,
EM: UsesState<State = Head::State>,
Z: UsesState<State = Head::State>,
Head::State: HasCurrentStageId,
Head: Stage<E, EM, S, Z> + 'static,
Tail: StagesTuple<E, EM, S, Z> + HasConstLen + IntoVec<Box<dyn Stage<E, EM, S, Z>>>,
S: HasCurrentStageId,
{
fn into_vec_reversed(
self,
) -> Vec<Box<dyn Stage<E, EM, Z, State = Head::State, Input = Head::Input>>> {
fn into_vec_reversed(self) -> Vec<Box<dyn Stage<E, EM, S, Z>>> {
let (head, tail) = self.uncons();
let mut ret = tail.0.into_vec_reversed();
ret.push(Box::new(head));
ret
}
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, Z, State = Head::State, Input = Head::Input>>> {
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, S, Z>>> {
let mut ret = self.into_vec_reversed();
ret.reverse();
ret
}
}
impl<Tail, E, EM, Z> IntoVec<Box<dyn Stage<E, EM, Z, State = Tail::State, Input = Tail::Input>>>
for (Tail,)
impl<Tail, E, EM, S, Z> IntoVec<Box<dyn Stage<E, EM, S, Z>>> for (Tail,)
where
Tail: UsesState + IntoVec<Box<dyn Stage<E, EM, Z, State = Tail::State, Input = Tail::Input>>>,
Z: UsesState<State = Tail::State>,
EM: UsesState<State = Tail::State>,
E: UsesState<State = Tail::State>,
Tail: IntoVec<Box<dyn Stage<E, EM, S, Z>>>,
{
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, Z, State = Tail::State, Input = Tail::Input>>> {
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, S, Z>>> {
self.0.into_vec()
}
}
impl<E, EM, Z> IntoVec<Box<dyn Stage<E, EM, Z, State = Z::State, Input = Z::Input>>>
for Vec<Box<dyn Stage<E, EM, Z, State = Z::State, Input = Z::Input>>>
where
Z: UsesState,
EM: UsesState<State = Z::State>,
E: UsesState<State = Z::State>,
{
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, Z, State = Z::State, Input = Z::Input>>> {
impl<E, EM, S, Z> IntoVec<Box<dyn Stage<E, EM, S, Z>>> for Vec<Box<dyn Stage<E, EM, S, Z>>> {
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, S, Z>>> {
self
}
}
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z>
for Vec<Box<dyn Stage<E, EM, Z, State = S, Input = S::Input>>>
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for Vec<Box<dyn Stage<E, EM, S, Z>>>
where
E: UsesState<State = S>,
EM: UsesState<State = S> + EventProcessor<E, Z>,
Z: UsesState<State = S>,
S: UsesInput + HasCurrentStageId + State,
EM: EventProcessor<E, Z>,
S: HasCurrentStageId + State,
{
/// Performs all stages in the `Vec`
/// Checks after every stage if state wants to stop
@ -333,46 +286,36 @@ pub struct ClosureStage<CB, E, EM, Z> {
phantom: PhantomData<(E, EM, Z)>,
}
impl<CB, E, EM, Z> UsesState for ClosureStage<CB, E, EM, Z>
where
E: UsesState,
{
type State = E::State;
}
impl<CB, E, EM, Z> Named for ClosureStage<CB, E, EM, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<CB, E, EM, Z> Stage<E, EM, Z> for ClosureStage<CB, E, EM, Z>
impl<CB, E, EM, S, Z> Stage<E, EM, S, Z> for ClosureStage<CB, E, EM, Z>
where
CB: FnMut(&mut Z, &mut E, &mut Self::State, &mut EM) -> Result<(), Error>,
E: UsesState,
EM: UsesState<State = Self::State>,
Z: UsesState<State = Self::State>,
Self::State: HasNamedMetadata,
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result<(), Error>,
S: HasNamedMetadata + HasCurrentCorpusId,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut E::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
(self.closure)(fuzzer, executor, state, manager)
}
#[inline]
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// There's no restart safety in the content of the closure.
// don't restart
RetryCountRestartHelper::no_retry(state, &self.name)
}
#[inline]
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
@ -396,132 +339,6 @@ impl<CB, E, EM, Z> ClosureStage<CB, E, EM, Z> {
}
}
/// Allows us to use a [`push::PushStage`] as a normal [`Stage`]
#[allow(clippy::type_complexity)]
#[derive(Debug)]
pub struct PushStageAdapter<CS, EM, OT, PS, Z> {
name: Cow<'static, str>,
push_stage: PS,
phantom: PhantomData<(CS, EM, OT, Z)>,
}
impl<CS, EM, OT, PS, Z> PushStageAdapter<CS, EM, OT, PS, Z> {
/// Create a new [`PushStageAdapter`], wrapping the given [`PushStage`]
/// to be used as a normal [`Stage`]
#[must_use]
pub fn new(push_stage: PS) -> Self {
// unsafe but impossible that you create two threads both instantiating this instance
let stage_id = unsafe {
let ret = PUSH_STAGE_ADAPTER_ID;
PUSH_STAGE_ADAPTER_ID += 1;
ret
};
Self {
name: Cow::Owned(
PUSH_STAGE_ADAPTER_NAME.to_owned() + ":" + stage_id.to_string().as_str(),
),
push_stage,
phantom: PhantomData,
}
}
}
/// The unique counter for this stage
static mut PUSH_STAGE_ADAPTER_ID: usize = 0;
/// The name for push stage adapter
pub static PUSH_STAGE_ADAPTER_NAME: &str = "pushstageadapter";
impl<CS, EM, OT, PS, Z> UsesState for PushStageAdapter<CS, EM, OT, PS, Z>
where
Z: UsesState,
{
type State = Z::State;
}
impl<CS, EM, OT, PS, Z> Named for PushStageAdapter<CS, EM, OT, PS, Z> {
#[must_use]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<CS, E, EM, OT, PS, Z> Stage<E, EM, Z> for PushStageAdapter<CS, EM, OT, PS, Z>
where
CS: Scheduler<Z::Input, Z::State>,
Self::State: HasExecutions
+ HasRand
+ HasCorpus
+ HasLastReportTime
+ HasCurrentCorpusId
+ HasNamedMetadata
+ HasMetadata,
E: Executor<EM, Z, State = <Self as UsesState>::State> + HasObservers<Observers = OT>,
EM: EventFirer<State = Self::State>
+ EventRestarter
+ HasEventManagerId
+ ProgressReporter<State = Self::State>,
OT: ObserversTuple<Self::Input, Self::State>,
PS: PushStage<CS, EM, OT, Z>,
Z: ExecutesInput<E, EM>
+ ExecutionProcessor<EM, OT>
+ EvaluatorObservers<EM, OT>
+ HasScheduler<Scheduler = CS>,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Z::State,
event_mgr: &mut EM,
) -> Result<(), Error> {
let push_stage = &mut self.push_stage;
let Some(corpus_id) = state.current_corpus_id()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
push_stage.set_current_corpus_id(corpus_id);
push_stage.init(fuzzer, state, event_mgr, &mut *executor.observers_mut())?;
loop {
let input =
match push_stage.pre_exec(fuzzer, state, event_mgr, &mut *executor.observers_mut())
{
Some(Ok(next_input)) => next_input,
Some(Err(err)) => return Err(err),
None => break,
};
let exit_kind = fuzzer.execute_input(state, executor, event_mgr, &input)?;
push_stage.post_exec(
fuzzer,
state,
event_mgr,
&mut *executor.observers_mut(),
input,
exit_kind,
)?;
}
self.push_stage
.deinit(fuzzer, state, event_mgr, &mut *executor.observers_mut())
}
#[inline]
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
// TODO: Proper restart handling - call post_exec at the right time, etc...
RetryCountRestartHelper::no_retry(state, &self.name)
}
#[inline]
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
/// Progress which permits a fixed amount of resumes per round of fuzzing. If this amount is ever
/// exceeded, the input will no longer be executed by this stage.
#[derive(Clone, Deserialize, Serialize, Debug)]
@ -722,7 +539,7 @@ mod test {
corpus::{Corpus, HasCurrentCorpusId, Testcase},
inputs::NopInput,
stages::{RetryCountRestartHelper, Stage},
state::{HasCorpus, State, StdState, UsesState},
state::{HasCorpus, StdState},
HasMetadata,
};
@ -771,35 +588,25 @@ mod test {
}
}
impl<S> UsesState for ResumeSucceededStage<S>
impl<E, EM, S, Z> Stage<E, EM, S, Z> for ResumeSucceededStage<S>
where
S: State,
{
type State = S;
}
impl<E, EM, Z> Stage<E, EM, Z> for ResumeSucceededStage<Z::State>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: UsesState,
Z::State: HasMetadata,
S: HasMetadata,
{
fn perform(
&mut self,
_fuzzer: &mut Z,
_executor: &mut E,
_state: &mut Self::State,
_state: &mut S,
_manager: &mut EM,
) -> Result<(), Error> {
Ok(())
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
TestProgress::should_restart(state, self)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
TestProgress::clear_progress(state, self)
}
}

View File

@ -9,20 +9,20 @@ use core::{marker::PhantomData, num::NonZeroUsize};
use libafl_bolts::{rands::Rand, Named};
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
corpus::{Corpus, CorpusId, Testcase},
corpus::{Corpus, CorpusId, HasCurrentCorpusId, Testcase},
fuzzer::Evaluator,
inputs::Input,
inputs::{Input, UsesInput},
mark_feature_time,
mutators::{MultiMutator, MutationResult, Mutator},
nonzero,
stages::{RetryCountRestartHelper, Stage},
start_timer,
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, UsesState},
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, MaybeHasClientPerfMonitor},
Error, HasMetadata, HasNamedMetadata,
};
#[cfg(feature = "introspection")]
use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
// TODO multi mutators stage
@ -81,24 +81,170 @@ where
/// A Mutational stage is the stage in a fuzzing run that mutates inputs.
/// Mutational stages will usually have a range of mutations that are
/// being applied to the input one by one, between executions.
pub trait MutationalStage<E, EM, I, M, Z>: Stage<E, EM, Z>
where
E: UsesState<State = Self::State>,
M: Mutator<I, Self::State>,
EM: UsesState<State = Self::State>,
Z: Evaluator<E, EM, State = Self::State>,
Self::State: HasCorpus + HasCurrentTestcase,
I: MutatedTransform<Self::Input, Self::State> + Clone,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>,
{
pub trait MutationalStage<S> {
/// The mutator of this stage
type Mutator;
/// The mutator registered for this stage
fn mutator(&self) -> &M;
fn mutator(&self) -> &Self::Mutator;
/// The mutator registered for this stage (mutable)
fn mutator_mut(&mut self) -> &mut M;
fn mutator_mut(&mut self) -> &mut Self::Mutator;
/// Gets the number of iterations this mutator should run for.
fn iterations(&self, state: &mut Self::State) -> Result<usize, Error>;
fn iterations(&self, state: &mut S) -> Result<usize, Error>;
}
/// Default value, how many iterations each stage gets, as an upper bound.
/// It may randomly continue earlier.
pub const DEFAULT_MUTATIONAL_MAX_ITERATIONS: usize = 128;
/// The default mutational stage
#[derive(Clone, Debug)]
pub struct StdMutationalStage<E, EM, I, M, S, Z> {
/// The name
name: Cow<'static, str>,
/// The mutator(s) to use
mutator: M,
/// The maximum amount of iterations we should do each round
max_iterations: NonZeroUsize,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, I, S, Z)>,
}
impl<E, EM, I, M, S, Z> MutationalStage<S> for StdMutationalStage<E, EM, I, M, S, Z>
where
S: HasRand,
{
type Mutator = M;
/// The mutator, added to this stage
#[inline]
fn mutator(&self) -> &Self::Mutator {
&self.mutator
}
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut Self::Mutator {
&mut self.mutator
}
/// Gets the number of iterations as a random number
fn iterations(&self, state: &mut S) -> Result<usize, Error> {
Ok(1 + state.rand_mut().below(self.max_iterations))
}
}
/// The unique id for mutational stage
static mut MUTATIONAL_STAGE_ID: usize = 0;
/// The name for mutational stage
pub static MUTATIONAL_STAGE_NAME: &str = "mutational";
impl<E, EM, I, M, S, Z> Named for StdMutationalStage<E, EM, I, M, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, EM, I, M, S, Z> Stage<E, EM, S, Z> for StdMutationalStage<E, EM, I, M, S, Z>
where
M: Mutator<I, S>,
Z: Evaluator<E, EM, State = S>,
S: HasCorpus
+ HasRand
+ HasMetadata
+ HasExecutions
+ HasNamedMetadata
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor
+ UsesInput,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone,
<S::Corpus as Corpus>::Input: Input,
S::Corpus: Corpus<Input = S::Input>,
{
#[inline]
#[allow(clippy::let_and_return)]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let ret = self.perform_mutational(fuzzer, executor, state, manager);
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().finish_stage();
ret
}
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
RetryCountRestartHelper::should_restart(state, &self.name, 3)
}
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<E, EM, M, S, Z> StdMutationalStage<E, EM, <S::Corpus as Corpus>::Input, M, S, Z>
where
M: Mutator<<S::Corpus as Corpus>::Input, S>,
Z: Evaluator<E, EM, State = S>,
S: HasCorpus + HasRand + HasCurrentCorpusId + UsesInput + MaybeHasClientPerfMonitor,
<S::Corpus as Corpus>::Input: Input + Clone,
S::Corpus: Corpus<Input = S::Input>,
{
/// Creates a new default mutational stage
pub fn new(mutator: M) -> Self {
// Safe to unwrap: DEFAULT_MUTATIONAL_MAX_ITERATIONS is never 0.
Self::transforming_with_max_iterations(mutator, nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS))
}
/// Creates a new mutational stage with the given max iterations
#[inline]
pub fn with_max_iterations(mutator: M, max_iterations: NonZeroUsize) -> Self {
Self::transforming_with_max_iterations(mutator, max_iterations)
}
}
impl<E, EM, I, M, S, Z> StdMutationalStage<E, EM, I, M, S, Z>
where
M: Mutator<I, S>,
Z: Evaluator<E, EM, State = S>,
S: HasCorpus + HasRand + HasCurrentTestcase + MaybeHasClientPerfMonitor + UsesInput,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone,
<S::Corpus as Corpus>::Input: Input,
S::Corpus: Corpus<Input = S::Input>,
{
/// Creates a new transforming mutational stage with the default max iterations
pub fn transforming(mutator: M) -> Self {
// Safe to unwrap: DEFAULT_MUTATIONAL_MAX_ITERATIONS is never 0.
Self::transforming_with_max_iterations(mutator, nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS))
}
/// Creates a new transforming mutational stage with the given max iterations
///
/// # Errors
/// Will return [`Error::IllegalArgument`] for `max_iterations` of 0.
#[inline]
pub fn transforming_with_max_iterations(mutator: M, max_iterations: NonZeroUsize) -> Self {
let stage_id = unsafe {
let ret = MUTATIONAL_STAGE_ID;
MUTATIONAL_STAGE_ID += 1;
ret
};
let name =
Cow::Owned(MUTATIONAL_STAGE_NAME.to_owned() + ":" + stage_id.to_string().as_str());
Self {
name,
mutator,
max_iterations,
phantom: PhantomData,
}
}
/// Runs this (mutational) stage for the given testcase
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
@ -106,7 +252,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
start_timer!(state);
@ -150,170 +296,13 @@ where
Ok(())
}
}
/// Default value, how many iterations each stage gets, as an upper bound.
/// It may randomly continue earlier.
pub const DEFAULT_MUTATIONAL_MAX_ITERATIONS: usize = 128;
/// The default mutational stage
#[derive(Clone, Debug)]
pub struct StdMutationalStage<E, EM, I, M, Z> {
/// The name
name: Cow<'static, str>,
/// The mutator(s) to use
mutator: M,
/// The maximum amount of iterations we should do each round
max_iterations: NonZeroUsize,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, I, Z)>,
}
impl<E, EM, I, M, Z> MutationalStage<E, EM, I, M, Z> for StdMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
M: Mutator<I, Self::State>,
Z: Evaluator<E, EM>,
Z::State: HasCorpus + HasRand + HasExecutions + HasMetadata + HasNamedMetadata,
I: MutatedTransform<Self::Input, Self::State> + Clone,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
{
/// The mutator, added to this stage
#[inline]
fn mutator(&self) -> &M {
&self.mutator
}
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator
}
/// Gets the number of iterations as a random number
fn iterations(&self, state: &mut Self::State) -> Result<usize, Error> {
Ok(1 + state.rand_mut().below(self.max_iterations))
}
}
/// The unique id for mutational stage
static mut MUTATIONAL_STAGE_ID: usize = 0;
/// The name for mutational stage
pub static MUTATIONAL_STAGE_NAME: &str = "mutational";
impl<E, EM, I, M, Z> UsesState for StdMutationalStage<E, EM, I, M, Z>
where
Z: UsesState,
{
type State = Z::State;
}
impl<E, EM, I, M, Z> Named for StdMutationalStage<E, EM, I, M, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, EM, I, M, Z> Stage<E, EM, Z> for StdMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
M: Mutator<I, Self::State>,
Z: Evaluator<E, EM>,
Z::State: HasCorpus + HasRand + HasMetadata + HasExecutions + HasNamedMetadata,
I: MutatedTransform<Self::Input, Self::State> + Clone,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
{
#[inline]
#[allow(clippy::let_and_return)]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
manager: &mut EM,
) -> Result<(), Error> {
let ret = self.perform_mutational(fuzzer, executor, state, manager);
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().finish_stage();
ret
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
RetryCountRestartHelper::should_restart(state, &self.name, 3)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<E, EM, M, Z> StdMutationalStage<E, EM, Z::Input, M, Z>
where
E: UsesState<State = <Self as UsesState>::State>,
EM: UsesState<State = <Self as UsesState>::State>,
M: Mutator<Z::Input, <Self as UsesState>::State>,
Z: Evaluator<E, EM>,
<Self as UsesState>::State: HasCorpus + HasRand,
{
/// Creates a new default mutational stage
pub fn new(mutator: M) -> Self {
// Safe to unwrap: DEFAULT_MUTATIONAL_MAX_ITERATIONS is never 0.
Self::transforming_with_max_iterations(mutator, nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS))
}
/// Creates a new mutational stage with the given max iterations
#[inline]
pub fn with_max_iterations(mutator: M, max_iterations: NonZeroUsize) -> Self {
Self::transforming_with_max_iterations(mutator, max_iterations)
}
}
impl<E, EM, I, M, Z> StdMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = <Self as UsesState>::State>,
EM: UsesState<State = <Self as UsesState>::State>,
M: Mutator<I, <Self as UsesState>::State>,
Z: Evaluator<E, EM>,
<Self as UsesState>::State: HasCorpus + HasRand,
{
/// Creates a new transforming mutational stage with the default max iterations
pub fn transforming(mutator: M) -> Self {
// Safe to unwrap: DEFAULT_MUTATIONAL_MAX_ITERATIONS is never 0.
Self::transforming_with_max_iterations(mutator, nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS))
}
/// Creates a new transforming mutational stage with the given max iterations
///
/// # Errors
/// Will return [`Error::IllegalArgument`] for `max_iterations` of 0.
#[inline]
pub fn transforming_with_max_iterations(mutator: M, max_iterations: NonZeroUsize) -> Self {
let stage_id = unsafe {
let ret = MUTATIONAL_STAGE_ID;
MUTATIONAL_STAGE_ID += 1;
ret
};
let name =
Cow::Owned(MUTATIONAL_STAGE_NAME.to_owned() + ":" + stage_id.to_string().as_str());
Self {
name,
mutator,
max_iterations,
phantom: PhantomData,
}
}
}
/// A mutational stage that operates on multiple inputs, as returned by [`MultiMutator::multi_mutate`].
#[derive(Clone, Debug)]
pub struct MultiMutationalStage<E, EM, I, M, Z> {
pub struct MultiMutationalStage<E, EM, I, M, S, Z> {
name: Cow<'static, str>,
mutator: M,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, I, Z)>,
phantom: PhantomData<(E, EM, I, S, Z)>,
}
/// The unique id for multi mutational stage
@ -321,37 +310,29 @@ static mut MULTI_MUTATIONAL_STAGE_ID: usize = 0;
/// The name for multi mutational stage
pub static MULTI_MUTATIONAL_STAGE_NAME: &str = "multimutational";
impl<E, EM, I, M, Z> UsesState for MultiMutationalStage<E, EM, I, M, Z>
where
Z: UsesState,
{
type State = Z::State;
}
impl<E, EM, I, M, Z> Named for MultiMutationalStage<E, EM, I, M, Z> {
impl<E, EM, I, M, S, Z> Named for MultiMutationalStage<E, EM, I, M, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, EM, I, M, Z> Stage<E, EM, Z> for MultiMutationalStage<E, EM, I, M, Z>
impl<E, EM, I, M, S, Z> Stage<E, EM, S, Z> for MultiMutationalStage<E, EM, I, M, S, Z>
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
M: MultiMutator<I, Self::State>,
Z: Evaluator<E, EM>,
Z::State: HasCorpus + HasRand + HasNamedMetadata + HasCurrentTestcase,
I: MutatedTransform<Self::Input, Self::State> + Clone,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
M: MultiMutator<I, S>,
Z: Evaluator<E, EM, State = S>,
S: HasCorpus + HasRand + HasNamedMetadata + HasCurrentTestcase + HasCurrentCorpusId + UsesInput,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone,
<S::Corpus as Corpus>::Input: Input,
S::Corpus: Corpus<Input = S::Input>,
{
#[inline]
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// Make sure we don't get stuck crashing on a single testcase
RetryCountRestartHelper::should_restart(state, &self.name, 3)
}
#[inline]
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
@ -362,7 +343,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let mut testcase = state.current_testcase_mut()?;
@ -386,17 +367,14 @@ where
}
}
impl<E, EM, M, Z> MultiMutationalStage<E, EM, Z::Input, M, Z>
where
Z: UsesState,
{
impl<E, EM, I, M, S, Z> MultiMutationalStage<E, EM, I, M, S, Z> {
/// Creates a new [`MultiMutationalStage`]
pub fn new(mutator: M) -> Self {
Self::transforming(mutator)
}
}
impl<E, EM, I, M, Z> MultiMutationalStage<E, EM, I, M, Z> {
impl<E, EM, I, M, S, Z> MultiMutationalStage<E, EM, I, M, S, Z> {
/// Creates a new transforming mutational stage
pub fn transforming(mutator: M) -> Self {
// unsafe but impossible that you create two threads both instantiating this instance

View File

@ -8,15 +8,24 @@ use core::{fmt::Debug, marker::PhantomData};
use libafl_bolts::Named;
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
corpus::Corpus,
corpus::{Corpus, HasCurrentCorpusId},
executors::{Executor, HasObservers},
fuzzer::Evaluator,
inputs::Input,
mutators::Mutator,
inputs::{Input, UsesInput},
mark_feature_time,
mutators::{MutationResult, Mutator},
schedulers::{testcase_score::CorpusPowerTestcaseScore, TestcaseScore},
stages::{mutational::MutatedTransform, MutationalStage, RetryCountRestartHelper, Stage},
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, UsesState},
stages::{
mutational::{MutatedTransform, MutatedTransformPost},
MutationalStage, RetryCountRestartHelper, Stage,
},
start_timer,
state::{
HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, MaybeHasClientPerfMonitor, UsesState,
},
Error, HasMetadata, HasNamedMetadata,
};
@ -26,55 +35,41 @@ static mut POWER_MUTATIONAL_STAGE_ID: usize = 0;
pub const POWER_MUTATIONAL_STAGE_NAME: &str = "power";
/// The mutational stage using power schedules
#[derive(Clone, Debug)]
pub struct PowerMutationalStage<E, F, EM, I, M, Z> {
pub struct PowerMutationalStage<E, F, EM, I, M, S, Z> {
name: Cow<'static, str>,
/// The mutators we use
mutator: M,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, F, EM, I, Z)>,
phantom: PhantomData<(E, F, EM, I, S, Z)>,
}
impl<E, F, EM, I, M, Z> UsesState for PowerMutationalStage<E, F, EM, I, M, Z>
where
E: UsesState,
{
type State = E::State;
}
impl<E, F, EM, I, M, Z> Named for PowerMutationalStage<E, F, EM, I, M, Z> {
impl<E, F, EM, I, M, S, Z> Named for PowerMutationalStage<E, F, EM, I, M, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, F, EM, I, M, Z> MutationalStage<E, EM, I, M, Z> for PowerMutationalStage<E, F, EM, I, M, Z>
impl<E, F, EM, I, M, S, Z> MutationalStage<S> for PowerMutationalStage<E, F, EM, I, M, S, Z>
where
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = Self::State>,
F: TestcaseScore<Self::State>,
I: Input,
M: Mutator<I, Self::State>,
E::State:
HasCorpus + HasMetadata + HasRand + HasExecutions + HasNamedMetadata + HasCurrentTestcase,
Z: Evaluator<E, EM, State = Self::State>,
I: MutatedTransform<E::Input, Self::State> + Clone,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
S: HasCurrentTestcase,
F: TestcaseScore<S>,
{
type Mutator = M;
/// The mutator, added to this stage
#[inline]
fn mutator(&self) -> &M {
fn mutator(&self) -> &Self::Mutator {
&self.mutator
}
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut M {
fn mutator_mut(&mut self) -> &mut Self::Mutator {
&mut self.mutator
}
/// Gets the number of iterations as a random number
#[allow(clippy::cast_sign_loss)]
fn iterations(&self, state: &mut Self::State) -> Result<usize, Error> {
fn iterations(&self, state: &mut S) -> Result<usize, Error> {
// Update handicap
let mut testcase = state.current_testcase_mut()?;
let score = F::compute(state, &mut testcase)? as usize;
@ -83,17 +78,24 @@ where
}
}
impl<E, F, EM, I, M, Z> Stage<E, EM, Z> for PowerMutationalStage<E, F, EM, I, M, Z>
impl<E, F, EM, I, M, S, Z> Stage<E, EM, S, Z> for PowerMutationalStage<E, F, EM, I, M, S, Z>
where
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = Self::State>,
F: TestcaseScore<Self::State>,
M: Mutator<I, Self::State>,
E::State:
HasCorpus + HasMetadata + HasRand + HasExecutions + HasNamedMetadata + HasCurrentTestcase,
Z: Evaluator<E, EM, State = Self::State>,
I: MutatedTransform<Self::Input, Self::State> + Clone + Input,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
E: Executor<EM, Z, State = S> + HasObservers,
EM: UsesState<State = S>,
F: TestcaseScore<S>,
M: Mutator<I, S>,
S: HasCorpus
+ HasMetadata
+ HasRand
+ HasExecutions
+ HasNamedMetadata
+ HasCurrentTestcase
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
Z: Evaluator<E, EM, State = S>,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone + Input,
<S::Corpus as Corpus>::Input: Input,
{
#[inline]
#[allow(clippy::let_and_return)]
@ -101,32 +103,39 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let ret = self.perform_mutational(fuzzer, executor, state, manager);
ret
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// Make sure we don't get stuck crashing on a single testcase
RetryCountRestartHelper::should_restart(state, &self.name, 3)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<E, F, EM, I, M, Z> PowerMutationalStage<E, F, EM, I, M, Z>
impl<E, F, EM, I, M, S, Z> PowerMutationalStage<E, F, EM, I, M, S, Z>
where
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = <Self as UsesState>::State>,
F: TestcaseScore<<Self as UsesState>::State>,
E: Executor<EM, Z, State = S> + HasObservers,
EM: UsesState<State = S>,
F: TestcaseScore<S>,
I: Input,
M: Mutator<I, <Self as UsesState>::State>,
<Self as UsesState>::State: HasCorpus + HasMetadata + HasRand,
Z: Evaluator<E, EM, State = <Self as UsesState>::State>,
M: Mutator<I, S>,
S: HasCorpus
+ HasMetadata
+ HasRand
+ HasCurrentTestcase
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone + Input,
Z: Evaluator<E, EM, State = S>,
<S::Corpus as Corpus>::Input: Input,
{
/// Creates a new [`PowerMutationalStage`]
pub fn new(mutator: M) -> Self {
@ -144,8 +153,58 @@ where
phantom: PhantomData,
}
}
/// Runs this (mutational) stage for the given testcase
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
fn perform_mutational(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
start_timer!(state);
// Here saturating_sub is needed as self.iterations() might be actually smaller than the previous value before reset.
/*
let num = self
.iterations(state)?
.saturating_sub(self.execs_since_progress_start(state)?);
*/
let num = self.iterations(state)?;
let mut testcase = state.current_testcase_mut()?;
let Ok(input) = I::try_transform_from(&mut testcase, state) else {
return Ok(());
};
drop(testcase);
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
for _ in 0..num {
let mut input = input.clone();
start_timer!(state);
let mutated = self.mutator_mut().mutate(state, &mut input)?;
mark_feature_time!(state, PerfFeature::Mutate);
if mutated == MutationResult::Skipped {
continue;
}
// Time is measured directly the `evaluate_input` function
let (untransformed, post) = input.try_transform_into(state)?;
let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?;
start_timer!(state);
self.mutator_mut().post_exec(state, corpus_id)?;
post.post_exec(state, corpus_id)?;
mark_feature_time!(state, PerfFeature::MutatePostExec);
}
Ok(())
}
}
/// The standard powerscheduling stage
pub type StdPowerMutationalStage<E, EM, I, M, Z> =
PowerMutationalStage<E, CorpusPowerTestcaseScore, EM, I, M, Z>;
pub type StdPowerMutationalStage<E, EM, I, M, S, Z> =
PowerMutationalStage<E, CorpusPowerTestcaseScore, EM, I, M, S, Z>;

View File

@ -8,62 +8,51 @@
/// Mutational stage is the normal fuzzing stage.
pub mod mutational;
use alloc::rc::Rc;
use alloc::{
borrow::{Cow, ToOwned},
rc::Rc,
string::ToString,
};
use core::{
cell::{Cell, RefCell},
marker::PhantomData,
time::Duration,
};
use libafl_bolts::Named;
pub use mutational::StdMutationalPushStage;
use crate::{
corpus::CorpusId,
common::HasNamedMetadata,
corpus::{Corpus, CorpusId, HasCurrentCorpusId},
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
executors::ExitKind,
executors::{Executor, ExitKind, HasObservers},
inputs::UsesInput,
observers::ObserversTuple,
schedulers::Scheduler,
stages::{RetryCountRestartHelper, Stage},
state::{HasCorpus, HasExecutions, HasLastReportTime, HasRand},
Error, EvaluatorObservers, ExecutionProcessor, HasMetadata, HasScheduler,
Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasMetadata, HasScheduler,
};
/// Send a monitor update all 15 (or more) seconds
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15);
// The shared state for all [`PushStage`]s
/// Should be stored inside a `[Rc<RefCell<_>>`]
#[derive(Clone, Debug)]
pub struct PushStageSharedState<CS, EM, OT, Z>
where
CS: Scheduler<Z::Input, Z::State>,
EM: EventFirer<State = Z::State> + EventRestarter + HasEventManagerId,
OT: ObserversTuple<Z::Input, Z::State>,
Z::State: HasRand + HasCorpus,
Z: ExecutionProcessor<EM, OT> + EvaluatorObservers<EM, OT> + HasScheduler<Scheduler = CS>,
{
pub struct PushStageSharedState<EM, I, OT, S, Z> {
/// The [`crate::state::State`]
pub state: Z::State,
pub state: S,
/// The [`crate::fuzzer::Fuzzer`] instance
pub fuzzer: Z,
/// The [`crate::events::EventManager`]
pub event_mgr: EM,
/// The [`crate::observers::ObserversTuple`]
pub observers: OT,
phantom: PhantomData<(CS, Z)>,
phantom: PhantomData<I>,
}
impl<CS, EM, OT, Z> PushStageSharedState<CS, EM, OT, Z>
where
CS: Scheduler<Z::Input, Z::State>,
EM: EventFirer<State = Z::State> + EventRestarter + HasEventManagerId,
OT: ObserversTuple<Z::Input, Z::State>,
Z::State: HasRand + HasCorpus,
Z: ExecutionProcessor<EM, OT> + EvaluatorObservers<EM, OT> + HasScheduler<Scheduler = CS>,
{
impl<EM, I, OT, S, Z> PushStageSharedState<EM, I, OT, S, Z> {
/// Create a new `PushStageSharedState` that can be used by all [`PushStage`]s
#[must_use]
pub fn new(fuzzer: Z, state: Z::State, observers: OT, event_mgr: EM) -> Self {
pub fn new(fuzzer: Z, state: S, observers: OT, event_mgr: EM) -> Self {
Self {
state,
fuzzer,
@ -76,20 +65,13 @@ where
/// Helper class for the [`PushStage`] trait, taking care of borrowing the shared state
#[derive(Clone, Debug)]
pub struct PushStageHelper<CS, EM, OT, Z>
where
CS: Scheduler<Z::Input, Z::State>,
EM: EventFirer<State = Z::State> + EventRestarter + HasEventManagerId,
OT: ObserversTuple<Z::Input, Z::State>,
Z::State: HasRand + HasCorpus,
Z: ExecutionProcessor<EM, OT> + EvaluatorObservers<EM, OT> + HasScheduler<Scheduler = CS>,
{
pub struct PushStageHelper<EM, I, OT, S, Z> {
/// If this stage has already been initalized.
/// This gets reset to `false` after one iteration of the stage is done.
pub initialized: bool,
/// The shared state, keeping track of the corpus and the fuzzer
#[allow(clippy::type_complexity)]
pub shared_state: Rc<RefCell<Option<PushStageSharedState<CS, EM, OT, Z>>>>,
pub shared_state: Rc<RefCell<Option<PushStageSharedState<EM, I, OT, S, Z>>>>,
/// If the last iteration failed
pub errored: bool,
@ -97,32 +79,22 @@ where
pub current_corpus_id: Option<CorpusId>,
/// The input we just ran
pub current_input: Option<<Z::State as UsesInput>::Input>, // Todo: Get rid of copy
pub current_input: Option<I>, // Todo: Get rid of copy
#[allow(clippy::type_complexity)]
phantom: PhantomData<(CS, EM, OT, Z)>,
exit_kind: Rc<Cell<Option<ExitKind>>>,
}
impl<CS, EM, OT, Z> PushStageHelper<CS, EM, OT, Z>
where
CS: Scheduler<Z::Input, Z::State>,
EM: EventFirer<State = Z::State> + EventRestarter + HasEventManagerId,
OT: ObserversTuple<Z::Input, Z::State>,
Z::State: HasRand + HasCorpus,
Z: ExecutionProcessor<EM, OT> + EvaluatorObservers<EM, OT> + HasScheduler<Scheduler = CS>,
{
impl<EM, I, OT, S, Z> PushStageHelper<EM, I, OT, S, Z> {
/// Create a new [`PushStageHelper`]
#[must_use]
#[allow(clippy::type_complexity)]
pub fn new(
shared_state: Rc<RefCell<Option<PushStageSharedState<CS, EM, OT, Z>>>>,
shared_state: Rc<RefCell<Option<PushStageSharedState<EM, I, OT, S, Z>>>>,
exit_kind_ref: Rc<Cell<Option<ExitKind>>>,
) -> Self {
Self {
shared_state,
initialized: false,
phantom: PhantomData,
exit_kind: exit_kind_ref,
errored: false,
current_input: None,
@ -132,14 +104,14 @@ where
/// Sets the shared state for this helper (and all other helpers owning the same [`RefCell`])
#[inline]
pub fn set_shared_state(&mut self, shared_state: PushStageSharedState<CS, EM, OT, Z>) {
pub fn set_shared_state(&mut self, shared_state: PushStageSharedState<EM, I, OT, S, Z>) {
(*self.shared_state.borrow_mut()).replace(shared_state);
}
/// Takes the shared state from this helper, replacing it with `None`
#[inline]
#[allow(clippy::type_complexity)]
pub fn take_shared_state(&mut self) -> Option<PushStageSharedState<CS, EM, OT, Z>> {
pub fn take_shared_state(&mut self) -> Option<PushStageSharedState<EM, I, OT, S, Z>> {
let shared_state_ref = &mut (*self.shared_state).borrow_mut();
shared_state_ref.take()
}
@ -158,7 +130,7 @@ where
}
/// Resets this state after a full stage iter.
fn end_of_iter(&mut self, shared_state: PushStageSharedState<CS, EM, OT, Z>, errored: bool) {
fn end_of_iter(&mut self, shared_state: PushStageSharedState<EM, I, OT, S, Z>, errored: bool) {
self.set_shared_state(shared_state);
self.errored = errored;
self.current_corpus_id = None;
@ -171,18 +143,11 @@ where
/// A push stage is a generator that returns a single testcase for each call.
/// It's an iterator so we can chain it.
/// After it has finished once, we will call it agan for the next fuzzer round.
pub trait PushStage<CS, EM, OT, Z>: Iterator
where
CS: Scheduler<Z::Input, Z::State>,
Z::State: HasRand + HasExecutions + HasMetadata + HasCorpus + HasLastReportTime,
EM: EventFirer<State = Z::State> + EventRestarter + HasEventManagerId + ProgressReporter,
OT: ObserversTuple<Z::Input, Z::State>,
Z: ExecutionProcessor<EM, OT> + EvaluatorObservers<EM, OT> + HasScheduler<Scheduler = CS>,
{
pub trait PushStage<EM, I, OT, S, Z> {
/// Gets the [`PushStageHelper`]
fn push_stage_helper(&self) -> &PushStageHelper<CS, EM, OT, Z>;
fn push_stage_helper(&self) -> &PushStageHelper<EM, I, OT, S, Z>;
/// Gets the [`PushStageHelper`] (mutable)
fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper<CS, EM, OT, Z>;
fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper<EM, I, OT, S, Z>;
/// Set the current corpus index this stage works on
fn set_current_corpus_id(&mut self, corpus_id: CorpusId) {
@ -196,7 +161,7 @@ where
fn init(
&mut self,
_fuzzer: &mut Z,
_state: &mut Z::State,
_state: &mut S,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Result<(), Error> {
@ -209,20 +174,20 @@ where
fn pre_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut Z::State,
_state: &mut S,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Option<Result<<Z::State as UsesInput>::Input, Error>>;
) -> Option<Result<I, Error>>;
/// Called after the execution of a testcase finished.
#[inline]
fn post_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut Z::State,
_state: &mut S,
_event_mgr: &mut EM,
_observers: &mut OT,
_input: <Z::State as UsesInput>::Input,
_input: I,
_exit_kind: ExitKind,
) -> Result<(), Error> {
Ok(())
@ -233,80 +198,127 @@ where
fn deinit(
&mut self,
_fuzzer: &mut Z,
_state: &mut Z::State,
_state: &mut S,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Result<(), Error> {
Ok(())
}
}
/// This is the default implementation for `next` for this stage
fn next_std(&mut self) -> Option<Result<<Z::State as UsesInput>::Input, Error>> {
let mut shared_state = {
let shared_state_ref = &mut (*self.push_stage_helper_mut().shared_state).borrow_mut();
shared_state_ref.take().unwrap()
/// Allows us to use a [`PushStage`] as a normal [`Stage`]
#[allow(clippy::type_complexity)]
#[derive(Debug)]
pub struct PushStageAdapter<CS, EM, OT, PS, Z> {
name: Cow<'static, str>,
push_stage: PS,
phantom: PhantomData<(CS, EM, OT, Z)>,
}
impl<CS, EM, OT, PS, Z> PushStageAdapter<CS, EM, OT, PS, Z> {
/// Create a new [`PushStageAdapter`], wrapping the given [`PushStage`]
/// to be used as a normal [`Stage`]
#[must_use]
pub fn new(push_stage: PS) -> Self {
// unsafe but impossible that you create two threads both instantiating this instance
let stage_id = unsafe {
let ret = PUSH_STAGE_ADAPTER_ID;
PUSH_STAGE_ADAPTER_ID += 1;
ret
};
let step_success = if self.push_stage_helper().initialized {
// We already ran once
let last_input = self.push_stage_helper_mut().current_input.take().unwrap();
self.post_exec(
&mut shared_state.fuzzer,
&mut shared_state.state,
&mut shared_state.event_mgr,
&mut shared_state.observers,
last_input,
self.push_stage_helper().exit_kind().unwrap(),
)
} else {
self.init(
&mut shared_state.fuzzer,
&mut shared_state.state,
&mut shared_state.event_mgr,
&mut shared_state.observers,
)
};
if let Err(err) = step_success {
self.push_stage_helper_mut().end_of_iter(shared_state, true);
return Some(Err(err));
Self {
name: Cow::Owned(
PUSH_STAGE_ADAPTER_NAME.to_owned() + ":" + stage_id.to_string().as_str(),
),
push_stage,
phantom: PhantomData,
}
//for i in 0..num {
let ret = self.pre_exec(
&mut shared_state.fuzzer,
&mut shared_state.state,
&mut shared_state.event_mgr,
&mut shared_state.observers,
);
if ret.is_none() {
// We're done.
drop(self.push_stage_helper_mut().current_input.take());
self.push_stage_helper_mut().initialized = false;
if let Err(err) = self.deinit(
&mut shared_state.fuzzer,
&mut shared_state.state,
&mut shared_state.event_mgr,
&mut shared_state.observers,
) {
self.push_stage_helper_mut().end_of_iter(shared_state, true);
return Some(Err(err));
};
if let Err(err) = shared_state
.event_mgr
.maybe_report_progress(&mut shared_state.state, STATS_TIMEOUT_DEFAULT)
{
self.push_stage_helper_mut().end_of_iter(shared_state, true);
return Some(Err(err));
};
} else {
self.push_stage_helper_mut().reset_exit_kind();
}
self.push_stage_helper_mut()
.end_of_iter(shared_state, false);
ret
}
}
/// The unique counter for this stage
static mut PUSH_STAGE_ADAPTER_ID: usize = 0;
/// The name for push stage adapter
pub static PUSH_STAGE_ADAPTER_NAME: &str = "pushstageadapter";
impl<CS, EM, OT, PS, Z> Named for PushStageAdapter<CS, EM, OT, PS, Z> {
#[must_use]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<CS, E, EM, OT, PS, S, Z> Stage<E, EM, S, Z> for PushStageAdapter<CS, EM, OT, PS, Z>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions
+ HasRand
+ HasCorpus
+ HasLastReportTime
+ HasCurrentCorpusId
+ HasNamedMetadata
+ HasMetadata
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
E: Executor<EM, Z, State = S> + HasObservers<Observers = OT>,
EM: EventFirer<State = S> + EventRestarter + HasEventManagerId + ProgressReporter<State = S>,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
PS: PushStage<EM, <S::Corpus as Corpus>::Input, OT, S, Z>,
Z: ExecutesInput<E, EM, State = S>
+ ExecutionProcessor<EM, OT>
+ EvaluatorObservers<EM, OT>
+ HasScheduler<Scheduler = CS>,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
event_mgr: &mut EM,
) -> Result<(), Error> {
let push_stage = &mut self.push_stage;
let Some(corpus_id) = state.current_corpus_id()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
push_stage.set_current_corpus_id(corpus_id);
push_stage.init(fuzzer, state, event_mgr, &mut *executor.observers_mut())?;
loop {
let input =
match push_stage.pre_exec(fuzzer, state, event_mgr, &mut *executor.observers_mut())
{
Some(Ok(next_input)) => next_input,
Some(Err(err)) => return Err(err),
None => break,
};
let exit_kind = fuzzer.execute_input(state, executor, event_mgr, &input)?;
push_stage.post_exec(
fuzzer,
state,
event_mgr,
&mut *executor.observers_mut(),
input,
exit_kind,
)?;
}
self.push_stage
.deinit(fuzzer, state, event_mgr, &mut *executor.observers_mut())
}
#[inline]
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// TODO: Proper restart handling - call post_exec at the right time, etc...
RetryCountRestartHelper::no_retry(state, &self.name)
}
#[inline]
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}

View File

@ -11,22 +11,23 @@ use libafl_bolts::rands::Rand;
use serde::Serialize;
use super::{PushStage, PushStageHelper, PushStageSharedState};
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
corpus::{Corpus, CorpusId},
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
events::{EventFirer, ProgressReporter},
executors::ExitKind,
inputs::UsesInput,
fuzzer::STATS_TIMEOUT_DEFAULT,
inputs::{Input, UsesInput},
mark_feature_time,
mutators::Mutator,
nonzero,
observers::ObserversTuple,
schedulers::Scheduler,
start_timer,
state::{HasCorpus, HasExecutions, HasLastReportTime, HasRand, UsesState},
Error, EvaluatorObservers, ExecutionProcessor, HasMetadata, HasScheduler,
state::{HasCorpus, HasExecutions, HasLastReportTime, HasRand, MaybeHasClientPerfMonitor},
Error, ExecutionProcessor, HasMetadata, HasScheduler,
};
#[cfg(feature = "introspection")]
use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
/// The default maximum number of mutations to perform per input.
pub const DEFAULT_MUTATIONAL_MAX_ITERATIONS: usize = 128;
@ -42,14 +43,10 @@ pub const DEFAULT_MUTATIONAL_MAX_ITERATIONS: usize = 128;
///
/// The default mutational push stage
#[derive(Clone, Debug)]
pub struct StdMutationalPushStage<CS, EM, M, OT, Z>
pub struct StdMutationalPushStage<EM, M, OT, S, Z>
where
CS: Scheduler<Z::Input, Z::State>,
EM: EventFirer<State = Z::State> + EventRestarter + HasEventManagerId,
M: Mutator<Z::Input, Z::State>,
OT: ObserversTuple<Z::Input, Z::State> + Serialize,
Z::State: HasRand + HasCorpus + Clone + Debug,
Z: ExecutionProcessor<EM, OT> + EvaluatorObservers<EM, OT> + HasScheduler<Scheduler = CS>,
S: HasCorpus,
<S::Corpus as Corpus>::Input: Clone + Debug,
{
current_corpus_id: Option<CorpusId>,
testcases_to_do: usize,
@ -57,21 +54,17 @@ where
mutator: M,
psh: PushStageHelper<CS, EM, OT, Z>,
psh: PushStageHelper<EM, <S::Corpus as Corpus>::Input, OT, S, Z>,
}
impl<CS, EM, M, OT, Z> StdMutationalPushStage<CS, EM, M, OT, Z>
impl<EM, M, OT, S, Z> StdMutationalPushStage<EM, M, OT, S, Z>
where
CS: Scheduler<Z::Input, Z::State>,
EM: EventFirer<State = Z::State> + EventRestarter + HasEventManagerId,
M: Mutator<Z::Input, Z::State>,
OT: ObserversTuple<Z::Input, Z::State> + Serialize,
Z::State: HasCorpus + HasRand + Clone + Debug,
Z: ExecutionProcessor<EM, OT> + EvaluatorObservers<EM, OT> + HasScheduler<Scheduler = CS>,
S: HasCorpus + HasRand,
<S::Corpus as Corpus>::Input: Clone + Debug,
{
/// Gets the number of iterations as a random number
#[allow(clippy::unused_self, clippy::unnecessary_wraps)] // TODO: we should put this function into a trait later
fn iterations(&self, state: &mut Z::State, _corpus_id: CorpusId) -> Result<usize, Error> {
fn iterations(&self, state: &mut S, _corpus_id: CorpusId) -> Result<usize, Error> {
Ok(1 + state
.rand_mut()
.below(nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS)))
@ -83,23 +76,28 @@ where
}
}
impl<CS, EM, M, OT, Z> PushStage<CS, EM, OT, Z> for StdMutationalPushStage<CS, EM, M, OT, Z>
impl<EM, M, OT, S, Z> PushStage<EM, <S::Corpus as Corpus>::Input, OT, S, Z>
for StdMutationalPushStage<EM, M, OT, S, Z>
where
CS: Scheduler<Z::Input, Z::State>,
EM: EventFirer<State = Z::State> + EventRestarter + HasEventManagerId + ProgressReporter,
M: Mutator<Z::Input, Z::State>,
OT: ObserversTuple<Z::Input, Z::State> + Serialize,
Z::State: HasCorpus + HasRand + HasExecutions + HasLastReportTime + HasMetadata + Clone + Debug,
Z: ExecutionProcessor<EM, OT> + EvaluatorObservers<EM, OT> + HasScheduler<Scheduler = CS>,
<<Z as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Z::Input>, //delete me
EM: EventFirer<State = S>,
Z: HasScheduler<State = S> + ExecutionProcessor<EM, OT>,
S: HasCorpus
+ UsesInput<Input = <S::Corpus as Corpus>::Input>
+ HasRand
+ MaybeHasClientPerfMonitor,
M: Mutator<<S::Corpus as Corpus>::Input, S>,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
<S::Corpus as Corpus>::Input: Input + Clone,
{
#[inline]
fn push_stage_helper(&self) -> &PushStageHelper<CS, EM, OT, Z> {
fn push_stage_helper(&self) -> &PushStageHelper<EM, <S::Corpus as Corpus>::Input, OT, S, Z> {
&self.psh
}
#[inline]
fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper<CS, EM, OT, Z> {
fn push_stage_helper_mut(
&mut self,
) -> &mut PushStageHelper<EM, <S::Corpus as Corpus>::Input, OT, S, Z> {
&mut self.psh
}
@ -107,7 +105,7 @@ where
fn init(
&mut self,
fuzzer: &mut Z,
state: &mut Z::State,
state: &mut S,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Result<(), Error> {
@ -126,10 +124,10 @@ where
fn pre_exec(
&mut self,
_fuzzer: &mut Z,
state: &mut Z::State,
state: &mut S,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Option<Result<<Z::State as UsesInput>::Input, Error>> {
) -> Option<Result<<S::Corpus as Corpus>::Input, Error>> {
if self.testcases_done >= self.testcases_to_do {
// finished with this cicle.
return None;
@ -161,10 +159,10 @@ where
fn post_exec(
&mut self,
fuzzer: &mut Z,
state: &mut Z::State,
state: &mut S,
event_mgr: &mut EM,
observers: &mut OT,
last_input: <Z::State as UsesInput>::Input,
last_input: <S::Corpus as Corpus>::Input,
exit_kind: ExitKind,
) -> Result<(), Error> {
// todo: is_interesting, etc.
@ -183,7 +181,7 @@ where
fn deinit(
&mut self,
_fuzzer: &mut Z,
_state: &mut Z::State,
_state: &mut S,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Result<(), Error> {
@ -192,38 +190,51 @@ where
}
}
impl<CS, EM, M, OT, Z> Iterator for StdMutationalPushStage<CS, EM, M, OT, Z>
impl<EM, M, OT, S, Z> Iterator for StdMutationalPushStage<EM, M, OT, S, Z>
where
CS: Scheduler<Z::Input, Z::State>,
EM: EventFirer + EventRestarter + HasEventManagerId + ProgressReporter<State = Z::State>,
M: Mutator<Z::Input, Z::State>,
OT: ObserversTuple<Z::Input, Z::State> + Serialize,
Z::State: HasCorpus + HasRand + HasExecutions + HasMetadata + HasLastReportTime + Clone + Debug,
Z: ExecutionProcessor<EM, OT> + EvaluatorObservers<EM, OT> + HasScheduler<Scheduler = CS>,
<<Z as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Z::Input>, //delete me
EM: ProgressReporter<State = S>,
S: HasCorpus
+ HasMetadata
+ HasExecutions
+ HasLastReportTime
+ HasRand
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
M: Mutator<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: Clone + Debug + Input,
Z: HasScheduler<State = S> + ExecutionProcessor<EM, OT>,
{
type Item = Result<<Z::State as UsesInput>::Input, Error>;
type Item = Result<<S::Corpus as Corpus>::Input, Error>;
fn next(&mut self) -> Option<Result<<Z::State as UsesInput>::Input, Error>> {
fn next(&mut self) -> Option<Result<<S::Corpus as Corpus>::Input, Error>> {
self.next_std()
}
}
impl<CS, EM, M, OT, Z> StdMutationalPushStage<CS, EM, M, OT, Z>
impl<EM, M, OT, S, Z> StdMutationalPushStage<EM, M, OT, S, Z>
where
CS: Scheduler<Z::Input, Z::State>,
EM: EventFirer<State = Z::State> + EventRestarter + HasEventManagerId,
M: Mutator<Z::Input, Z::State>,
OT: ObserversTuple<Z::Input, Z::State> + Serialize,
Z::State: HasCorpus + HasRand + Clone + Debug,
Z: ExecutionProcessor<EM, OT> + EvaluatorObservers<EM, OT> + HasScheduler<Scheduler = CS>,
EM: ProgressReporter<State = S>,
S: HasCorpus
+ HasMetadata
+ HasExecutions
+ HasLastReportTime
+ HasRand
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
M: Mutator<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: Clone + Debug + Input,
Z: HasScheduler<State = S> + ExecutionProcessor<EM, OT>,
{
/// Creates a new default mutational stage
#[must_use]
#[allow(clippy::type_complexity)]
pub fn new(
mutator: M,
shared_state: Rc<RefCell<Option<PushStageSharedState<CS, EM, OT, Z>>>>,
shared_state: Rc<
RefCell<Option<PushStageSharedState<EM, <S::Corpus as Corpus>::Input, OT, S, Z>>>,
>,
exit_kind: Rc<Cell<Option<ExitKind>>>,
) -> Self {
Self {
@ -234,4 +245,74 @@ where
testcases_done: 0,
}
}
/// This is the implementation for `next` for this stage
pub fn next_std(&mut self) -> Option<Result<<S::Corpus as Corpus>::Input, Error>> {
let mut shared_state = {
let shared_state_ref = &mut (*self.push_stage_helper_mut().shared_state).borrow_mut();
shared_state_ref.take().unwrap()
};
let step_success = if self.push_stage_helper().initialized {
// We already ran once
let last_input = self.push_stage_helper_mut().current_input.take().unwrap();
self.post_exec(
&mut shared_state.fuzzer,
&mut shared_state.state,
&mut shared_state.event_mgr,
&mut shared_state.observers,
last_input,
self.push_stage_helper().exit_kind().unwrap(),
)
} else {
self.init(
&mut shared_state.fuzzer,
&mut shared_state.state,
&mut shared_state.event_mgr,
&mut shared_state.observers,
)
};
if let Err(err) = step_success {
self.push_stage_helper_mut().end_of_iter(shared_state, true);
return Some(Err(err));
}
//for i in 0..num {
let ret = self.pre_exec(
&mut shared_state.fuzzer,
&mut shared_state.state,
&mut shared_state.event_mgr,
&mut shared_state.observers,
);
if ret.is_none() {
// We're done.
drop(self.push_stage_helper_mut().current_input.take());
self.push_stage_helper_mut().initialized = false;
if let Err(err) = self.deinit(
&mut shared_state.fuzzer,
&mut shared_state.state,
&mut shared_state.event_mgr,
&mut shared_state.observers,
) {
self.push_stage_helper_mut().end_of_iter(shared_state, true);
return Some(Err(err));
};
if let Err(err) = shared_state
.event_mgr
.maybe_report_progress(&mut shared_state.state, STATS_TIMEOUT_DEFAULT)
{
self.push_stage_helper_mut().end_of_iter(shared_state, true);
return Some(Err(err));
};
} else {
self.push_stage_helper_mut().reset_exit_kind();
}
self.push_stage_helper_mut()
.end_of_iter(shared_state, false);
ret
}
}

View File

@ -1,181 +0,0 @@
//! Stage to compute/report minimal AFL-like stats
#[cfg(feature = "std")]
use alloc::{borrow::Cow, string::ToString};
use core::{marker::PhantomData, time::Duration};
use libafl_bolts::current_time;
#[cfg(feature = "std")]
use serde_json::json;
use crate::{
corpus::{Corpus, HasCurrentCorpusId},
events::EventFirer,
schedulers::minimizer::IsFavoredMetadata,
stages::Stage,
state::{HasCorpus, HasImported, UsesState},
Error, HasMetadata,
};
#[cfg(feature = "std")]
use crate::{
events::Event,
monitors::{AggregatorOps, UserStats, UserStatsValue},
};
/// The [`StatsStage`] is a simple stage that computes and reports some stats.
#[derive(Debug, Clone)]
pub struct StatsStage<E, EM, Z> {
// 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
stats_report_interval: Duration,
phantom: PhantomData<(E, EM, Z)>,
}
impl<E, EM, Z> UsesState for StatsStage<E, EM, Z>
where
E: UsesState,
{
type State = E::State;
}
impl<E, EM, Z> Stage<E, EM, Z> for StatsStage<E, EM, Z>
where
E: UsesState,
EM: EventFirer<State = Self::State>,
Z: UsesState<State = Self::State>,
Self::State: HasImported + HasCorpus + HasMetadata,
{
fn perform(
&mut self,
_fuzzer: &mut Z,
_executor: &mut E,
state: &mut Self::State,
_manager: &mut EM,
) -> Result<(), Error> {
self.update_and_report_afl_stats(state, _manager)
}
#[inline]
fn should_restart(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
// 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<E, EM, Z> StatsStage<E, EM, Z> {
fn update_and_report_afl_stats(
&mut self,
state: &mut <Self as UsesState>::State,
_manager: &mut EM,
) -> Result<(), Error>
where
E: UsesState,
EM: EventFirer<State = E::State>,
<Self as UsesState>::State: HasCorpus + HasImported,
{
let Some(corpus_id) = state.current_corpus_id()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
// 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::<IsFavoredMetadata>() {
self.is_favored_size += 1;
}
} else {
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 cur = current_time();
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(())
}
}
impl<E, EM, Z> StatsStage<E, EM, Z> {
/// create a new instance of the [`StatsStage`]
#[must_use]
pub fn new(interval: Duration) -> Self {
Self {
stats_report_interval: interval,
..Default::default()
}
}
}
impl<E, EM, Z> Default for StatsStage<E, EM, Z> {
/// the default instance of the [`StatsStage`]
#[must_use]
fn default() -> Self {
Self {
has_fuzzed_size: 0,
is_favored_size: 0,
own_finds_size: 0,
imported_size: 0,
last_report_time: current_time(),
stats_report_interval: Duration::from_secs(15),
phantom: PhantomData,
}
}
}

View File

@ -10,16 +10,14 @@ use std::path::{Path, PathBuf};
use libafl_bolts::{current_time, fs::find_new_files_rec, shmem::ShMemProvider, Named};
use serde::{Deserialize, Serialize};
#[cfg(feature = "introspection")]
use crate::state::HasClientPerfMonitor;
use crate::{
corpus::{Corpus, CorpusId},
corpus::{Corpus, CorpusId, HasCurrentCorpusId},
events::{llmp::LlmpEventConverter, Event, EventConfig, EventFirer},
executors::{Executor, ExitKind, HasObservers},
fuzzer::{Evaluator, EvaluatorObservers, ExecutionProcessor},
inputs::{Input, InputConverter, UsesInput},
stages::{RetryCountRestartHelper, Stage},
state::{HasCorpus, HasExecutions, HasRand, State, UsesState},
state::{HasCorpus, HasExecutions, HasRand, MaybeHasClientPerfMonitor, State, Stoppable},
Error, HasMetadata, HasNamedMetadata,
};
@ -54,41 +52,38 @@ impl SyncFromDiskMetadata {
/// A stage that loads testcases from disk to sync with other fuzzers such as AFL++
#[derive(Debug)]
pub struct SyncFromDiskStage<CB, E, EM, Z> {
pub struct SyncFromDiskStage<CB, E, EM, S, Z> {
name: Cow<'static, str>,
sync_dirs: Vec<PathBuf>,
load_callback: CB,
interval: Duration,
phantom: PhantomData<(E, EM, Z)>,
phantom: PhantomData<(E, EM, S, Z)>,
}
impl<CB, E, EM, Z> UsesState for SyncFromDiskStage<CB, E, EM, Z>
where
Z: UsesState,
{
type State = Z::State;
}
impl<CB, E, EM, Z> Named for SyncFromDiskStage<CB, E, EM, Z> {
impl<CB, E, EM, S, Z> Named for SyncFromDiskStage<CB, E, EM, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<CB, E, EM, Z> Stage<E, EM, Z> for SyncFromDiskStage<CB, E, EM, Z>
impl<CB, E, EM, S, Z> Stage<E, EM, S, Z> for SyncFromDiskStage<CB, E, EM, S, Z>
where
CB: FnMut(&mut Z, &mut Self::State, &Path) -> Result<<Self::State as UsesInput>::Input, Error>,
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
Z: Evaluator<E, EM>,
Self::State: HasCorpus + HasRand + HasMetadata + HasNamedMetadata,
CB: FnMut(&mut Z, &mut S, &Path) -> Result<<S::Corpus as Corpus>::Input, Error>,
Z: Evaluator<E, EM, State = S>,
S: HasCorpus
+ HasRand
+ HasMetadata
+ HasNamedMetadata
+ UsesInput<Input = <S::Corpus as Corpus>::Input>
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let last = state
@ -144,19 +139,19 @@ where
}
#[inline]
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// TODO: Needs proper crash handling for when an imported testcase crashes
// For now, Make sure we don't get stuck crashing on this testcase
RetryCountRestartHelper::no_retry(state, &self.name)
}
#[inline]
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<CB, E, EM, Z> SyncFromDiskStage<CB, E, EM, Z> {
impl<CB, E, EM, S, Z> SyncFromDiskStage<CB, E, EM, S, Z> {
/// Creates a new [`SyncFromDiskStage`]
#[must_use]
pub fn new(sync_dirs: Vec<PathBuf>, load_callback: CB, interval: Duration, name: &str) -> Self {
@ -174,10 +169,8 @@ impl<CB, E, EM, Z> SyncFromDiskStage<CB, E, EM, Z> {
pub type SyncFromDiskFunction<S, Z> =
fn(&mut Z, &mut S, &Path) -> Result<<S as UsesInput>::Input, Error>;
impl<E, EM, Z> SyncFromDiskStage<SyncFromDiskFunction<Z::State, Z>, E, EM, Z>
impl<E, EM, S, Z> SyncFromDiskStage<SyncFromDiskFunction<Z::State, Z>, E, EM, S, Z>
where
E: UsesState<State = <Self as UsesState>::State>,
EM: UsesState<State = <Self as UsesState>::State>,
Z: Evaluator<E, EM>,
{
/// Creates a new [`SyncFromDiskStage`] invoking `Input::from_file` to load inputs
@ -234,38 +227,31 @@ where
client: LlmpEventConverter<DI, IC, ICB, S, SP>,
}
impl<DI, IC, ICB, S, SP> UsesState for SyncFromBrokerStage<DI, IC, ICB, S, SP>
impl<E, EM, IC, ICB, DI, S, SP, Z> Stage<E, EM, S, Z> for SyncFromBrokerStage<DI, IC, ICB, S, SP>
where
SP: ShMemProvider + 'static,
S: State,
IC: InputConverter<From = S::Input, To = DI>,
ICB: InputConverter<From = DI, To = S::Input>,
DI: Input,
{
type State = S;
}
impl<E, EM, IC, ICB, DI, S, SP, Z> Stage<E, EM, Z> for SyncFromBrokerStage<DI, IC, ICB, S, SP>
where
EM: UsesState<State = S> + EventFirer,
S: State + HasExecutions + HasCorpus + HasRand + HasMetadata,
EM: EventFirer<State = S>,
S: HasExecutions
+ HasCorpus
+ HasRand
+ HasMetadata
+ Stoppable
+ UsesInput<Input = <S::Corpus as Corpus>::Input>
+ State,
SP: ShMemProvider,
E: HasObservers + Executor<EM, Z, State = S>,
for<'a> E::Observers: Deserialize<'a>,
Z: EvaluatorObservers<EM, E::Observers, State = S>
+ ExecutionProcessor<EM, E::Observers, State = S>,
IC: InputConverter<From = S::Input, To = DI>,
ICB: InputConverter<From = DI, To = S::Input>,
Z: EvaluatorObservers<EM, E::Observers> + ExecutionProcessor<EM, E::Observers, State = S>,
IC: InputConverter<From = <S::Corpus as Corpus>::Input, To = DI>,
ICB: InputConverter<From = DI, To = <S::Corpus as Corpus>::Input>,
DI: Input,
<<S as HasCorpus>::Corpus as Corpus>::Input: Clone,
S::Corpus: Corpus<Input = S::Input>, // delete me
<<S as HasCorpus>::Corpus as Corpus>::Input: Input + Clone,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
if self.client.can_convert() {
@ -319,13 +305,13 @@ where
}
#[inline]
fn should_restart(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, _state: &mut S) -> Result<bool, Error> {
// No restart handling needed - does not execute the target.
Ok(true)
}
#[inline]
fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> {
// Not needed - does not execute the target.
Ok(())
}

View File

@ -3,12 +3,7 @@ use std::{marker::PhantomData, time::Duration};
use libafl_bolts::{current_time, Error};
use crate::{
inputs::UsesInput,
stages::Stage,
state::{State, UsesState},
HasMetadata,
};
use crate::{stages::Stage, HasMetadata};
/// Track an inner Stage's execution time
#[derive(Debug)]
pub struct TimeTrackingStageWrapper<T, S, ST> {
@ -28,27 +23,17 @@ impl<T, S, ST> TimeTrackingStageWrapper<T, S, ST> {
}
}
impl<T, S, ST> UsesState for TimeTrackingStageWrapper<T, S, ST>
impl<T, E, M, Z, S, ST> Stage<E, M, S, Z> for TimeTrackingStageWrapper<T, S, ST>
where
S: State + HasMetadata,
{
type State = S;
}
impl<T, E, M, Z, S, ST> Stage<E, M, Z> for TimeTrackingStageWrapper<T, S, ST>
where
S: UsesInput + State + HasMetadata,
ST: Stage<E, M, Z, State = S>,
M: UsesState<State = S>,
Z: UsesState<State = S>,
E: UsesState<State = S>,
S: HasMetadata,
ST: Stage<E, M, S, Z>,
T: libafl_bolts::serdeany::SerdeAny + From<Duration>,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut M,
) -> Result<(), Error> {
let before_run = current_time();
@ -59,11 +44,11 @@ where
Ok(())
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
self.inner.should_restart(state)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
self.inner.clear_progress(state)
}
@ -71,7 +56,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut M,
) -> Result<(), Error> {
self.inner

View File

@ -1,4 +1,4 @@
//! The [`TMinMutationalStage`] is a stage which will attempt to minimize corpus entries.
//! The [`StdTMinMutationalStage`] is a stage which will attempt to minimize corpus entries.
use alloc::{
borrow::{Cow, ToOwned},
@ -15,12 +15,14 @@ use serde::Serialize;
#[cfg(feature = "track_hit_feedbacks")]
use crate::feedbacks::premature_last_result_err;
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
corpus::{Corpus, HasCurrentCorpusId, Testcase},
events::EventFirer,
executors::{ExitKind, HasObservers},
feedbacks::{Feedback, FeedbackFactory, HasObserverHandle, StateInitializer},
inputs::UsesInput,
inputs::{Input, UsesInput},
mark_feature_time,
mutators::{MutationResult, Mutator},
observers::{MapObserver, ObserversTuple},
@ -31,44 +33,137 @@ use crate::{
},
start_timer,
state::{
HasCorpus, HasCurrentTestcase, HasExecutions, HasMaxSize, HasSolutions, State, UsesState,
HasCorpus, HasCurrentTestcase, HasExecutions, HasMaxSize, HasSolutions,
MaybeHasClientPerfMonitor, State, UsesState,
},
Error, ExecutesInput, ExecutionProcessor, HasFeedback, HasMetadata, HasNamedMetadata,
HasScheduler,
};
#[cfg(feature = "introspection")]
use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
/// Mutational stage which minimizes corpus entries.
///
/// You must provide at least one mutator that actually reduces size.
pub trait TMinMutationalStage<E, EM, F, IP, M, Z>:
Stage<E, EM, Z> + FeedbackFactory<F, E::Observers>
/// The default corpus entry minimising mutational stage
#[derive(Clone, Debug)]
pub struct StdTMinMutationalStage<E, EM, F, FF, M, S, Z> {
/// The name
name: Cow<'static, str>,
/// The mutator(s) this stage uses
mutator: M,
/// The factory
factory: FF,
/// The runs (=iterations) we are supposed to do
runs: usize,
/// The progress helper for this stage, keeping track of resumes after timeouts/crashes
restart_helper: ExecutionCountRestartHelper,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, F, S, Z)>,
}
impl<E, EM, F, FF, M, S, Z> Stage<E, EM, S, Z> for StdTMinMutationalStage<E, EM, F, FF, M, S, Z>
where
E: UsesState<State = Self::State> + HasObservers,
E::Observers: ObserversTuple<Self::Input, Self::State> + Serialize,
EM: UsesState<State = Self::State> + EventFirer,
F: Feedback<EM, Self::Input, E::Observers, Self::State>,
Self::State: HasMaxSize + HasCorpus + HasSolutions + HasExecutions + HasCurrentTestcase,
Self::Input: MutatedTransform<Self::Input, Self::State, Post = IP> + Clone + Hash + HasLen,
IP: Clone + MutatedTransformPost<Self::State>,
M: Mutator<Self::Input, Self::State>,
Z: UsesState<State = Self::State>
+ HasScheduler
+ HasFeedback
Z: HasScheduler<State = S>
+ ExecutionProcessor<EM, E::Observers>
+ ExecutesInput<E, EM>
+ ExecutionProcessor<EM, E::Observers>,
Z::Feedback: Feedback<EM, Self::Input, E::Observers, Self::State>,
Z::Scheduler: RemovableScheduler<Self::Input, Self::State>,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>,
+ HasFeedback,
Z::Scheduler: RemovableScheduler<<S::Corpus as Corpus>::Input, S>,
E: HasObservers + UsesState<State = S>,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
EM: EventFirer<State = S>,
FF: FeedbackFactory<F, E::Observers>,
F: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
S: HasMetadata
+ HasExecutions
+ HasSolutions
+ HasCorpus
+ HasMaxSize
+ HasNamedMetadata
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
Z::Feedback: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
M: Mutator<<S::Corpus as Corpus>::Input, S>,
<<S as HasCorpus>::Corpus as Corpus>::Input: Input + Hash + HasLen,
{
/// The mutator registered for this stage
fn mutator(&self) -> &M;
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
self.restart_helper.should_restart(state, &self.name)
}
/// The mutator registered for this stage (mutable)
fn mutator_mut(&mut self) -> &mut M;
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
self.restart_helper.clear_progress(state, &self.name)
}
/// Gets the number of iterations this mutator should run for.
fn iterations(&self, state: &mut Self::State) -> Result<usize, Error>;
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Z::State,
manager: &mut EM,
) -> Result<(), Error> {
self.perform_minification(fuzzer, executor, state, manager)?;
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().finish_stage();
Ok(())
}
}
impl<E, EM, F, FF, M, S, Z> FeedbackFactory<F, E::Observers>
for StdTMinMutationalStage<E, EM, F, FF, M, S, Z>
where
E: HasObservers,
FF: FeedbackFactory<F, E::Observers>,
{
fn create_feedback(&self, ctx: &E::Observers) -> F {
self.factory.create_feedback(ctx)
}
}
impl<E, EM, F, FF, M, S, Z> Named for StdTMinMutationalStage<E, EM, F, FF, M, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
/// The counter for giving this stage unique id
static mut TMIN_STAGE_ID: usize = 0;
/// The name for tmin stage
pub static TMIN_STAGE_NAME: &str = "tmin";
impl<E, EM, F, FF, M, S, Z> StdTMinMutationalStage<E, EM, F, FF, M, S, Z>
where
Z: HasScheduler<State = S>
+ ExecutionProcessor<EM, E::Observers>
+ ExecutesInput<E, EM>
+ HasFeedback,
Z::Scheduler: RemovableScheduler<<S::Corpus as Corpus>::Input, S>,
E: HasObservers + UsesState<State = Z::State>,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
EM: EventFirer<State = Z::State>,
FF: FeedbackFactory<F, E::Observers>,
F: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
S: HasMetadata
+ HasExecutions
+ HasSolutions
+ HasCorpus
+ HasMaxSize
+ HasNamedMetadata
+ HasCurrentTestcase
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
Z::Feedback: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
M: Mutator<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: Hash + HasLen + Input,
{
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator
}
/// Gets the number of iterations from a fixed number of runs
fn iterations(&self, _state: &mut S) -> usize {
self.runs
}
/// Runs this (mutational) stage for new objectives
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
@ -76,7 +171,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let Some(base_corpus_id) = state.current_corpus_id()? else {
@ -88,7 +183,7 @@ where
let orig_max_size = state.max_size();
// basically copy-pasted from mutational.rs
let num = self
.iterations(state)?
.iterations(state)
.saturating_sub(usize::try_from(self.execs_since_progress_start(state)?)?);
// If num is negative, then quit.
@ -97,8 +192,10 @@ where
}
start_timer!(state);
let transformed =
Self::Input::try_transform_from(state.current_testcase_mut()?.borrow_mut(), state)?;
let transformed = <S::Corpus as Corpus>::Input::try_transform_from(
state.current_testcase_mut()?.borrow_mut(),
state,
)?;
let mut base = state.current_input_cloned()?;
// potential post operation if base is replaced by a shorter input
let mut base_post = None;
@ -159,7 +256,7 @@ where
if feedback.is_interesting(state, manager, &input, &*observers, &exit_kind)? {
// we found a reduced corpus entry! use the smaller base
base = input;
base_post = Some(post.clone());
base_post = Some(post);
// do more runs! maybe we can minify further
next_i = 0;
@ -210,144 +307,13 @@ where
Ok(())
}
/// Gets the number of executions this mutator already did since it got first called in this fuzz round.
fn execs_since_progress_start(&mut self, state: &mut Self::State) -> Result<u64, Error>;
}
/// The default corpus entry minimising mutational stage
#[derive(Clone, Debug)]
pub struct StdTMinMutationalStage<E, EM, F, FF, IP, M, Z> {
/// The name
name: Cow<'static, str>,
/// The mutator(s) this stage uses
mutator: M,
/// The factory
factory: FF,
/// The runs (=iterations) we are supposed to do
runs: usize,
/// The progress helper for this stage, keeping track of resumes after timeouts/crashes
restart_helper: ExecutionCountRestartHelper,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, F, IP, Z)>,
}
impl<E, EM, F, FF, IP, M, Z> UsesState for StdTMinMutationalStage<E, EM, F, FF, IP, M, Z>
where
Z: UsesState,
{
type State = Z::State;
}
impl<E, EM, F, FF, IP, M, Z> Stage<E, EM, Z> for StdTMinMutationalStage<E, EM, F, FF, IP, M, Z>
where
Z: HasScheduler + ExecutionProcessor<EM, E::Observers> + ExecutesInput<E, EM> + HasFeedback,
Z::Scheduler: RemovableScheduler<Self::Input, Self::State>,
E: HasObservers + UsesState<State = Z::State>,
E::Observers: ObserversTuple<Self::Input, Self::State> + Serialize,
EM: EventFirer<State = Self::State>,
FF: FeedbackFactory<F, E::Observers>,
F: Feedback<EM, Self::Input, E::Observers, Self::State>,
Self::Input: MutatedTransform<Self::Input, Self::State, Post = IP> + Clone + HasLen + Hash,
Z::State:
HasMetadata + HasExecutions + HasSolutions + HasCorpus + HasMaxSize + HasNamedMetadata,
Z::Feedback: Feedback<EM, Self::Input, E::Observers, Self::State>,
M: Mutator<Self::Input, Self::State>,
IP: MutatedTransformPost<Self::State> + Clone,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, // delete me
{
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
self.restart_helper.should_restart(state, &self.name)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
self.restart_helper.clear_progress(state, &self.name)
}
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Z::State,
manager: &mut EM,
) -> Result<(), Error> {
self.perform_minification(fuzzer, executor, state, manager)?;
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().finish_stage();
Ok(())
}
}
impl<E, EM, F, FF, IP, M, Z> FeedbackFactory<F, E::Observers>
for StdTMinMutationalStage<E, EM, F, FF, IP, M, Z>
where
E: HasObservers,
FF: FeedbackFactory<F, E::Observers>,
{
fn create_feedback(&self, ctx: &E::Observers) -> F {
self.factory.create_feedback(ctx)
}
}
impl<E, EM, F, FF, IP, M, Z> Named for StdTMinMutationalStage<E, EM, F, FF, IP, M, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
/// The counter for giving this stage unique id
static mut TMIN_STAGE_ID: usize = 0;
/// The name for tmin stage
pub static TMIN_STAGE_NAME: &str = "tmin";
impl<E, EM, F, FF, IP, M, Z> TMinMutationalStage<E, EM, F, IP, M, Z>
for StdTMinMutationalStage<E, EM, F, FF, IP, M, Z>
where
Z: HasScheduler + ExecutionProcessor<EM, E::Observers> + ExecutesInput<E, EM> + HasFeedback,
Z::Scheduler: RemovableScheduler<Self::Input, Self::State>,
E: HasObservers + UsesState<State = Z::State>,
E::Observers: ObserversTuple<Self::Input, Self::State> + Serialize,
EM: EventFirer<State = Self::State>,
FF: FeedbackFactory<F, E::Observers>,
F: Feedback<EM, Self::Input, E::Observers, Self::State>,
Self::Input: MutatedTransform<Self::Input, Self::State, Post = IP> + Clone + HasLen + Hash,
Z::State: HasMetadata
+ HasExecutions
+ HasSolutions
+ HasCorpus
+ HasMaxSize
+ HasNamedMetadata
+ HasCurrentTestcase,
Z::Feedback: Feedback<EM, Self::Input, E::Observers, Self::State>,
M: Mutator<Self::Input, Self::State>,
IP: MutatedTransformPost<Self::State> + Clone,
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, // delete me
{
/// The mutator, added to this stage
#[inline]
fn mutator(&self) -> &M {
&self.mutator
}
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator
}
/// Gets the number of iterations from a fixed number of runs
fn iterations(&self, _state: &mut Self::State) -> Result<usize, Error> {
Ok(self.runs)
}
fn execs_since_progress_start(&mut self, state: &mut Self::State) -> Result<u64, Error> {
fn execs_since_progress_start(&mut self, state: &mut S) -> Result<u64, Error> {
self.restart_helper
.execs_since_progress_start(state, &self.name)
}
}
impl<E, EM, F, FF, IP, M, Z> StdTMinMutationalStage<E, EM, F, FF, IP, M, Z> {
impl<E, EM, F, FF, M, S, Z> StdTMinMutationalStage<E, EM, F, FF, M, S, Z> {
/// Creates a new minimizing mutational stage that will minimize provided corpus entries
pub fn new(mutator: M, factory: FF, runs: usize) -> Self {
// unsafe but impossible that you create two threads both instantiating this instance

View File

@ -8,54 +8,47 @@ use core::{fmt::Debug, marker::PhantomData};
use libafl_bolts::Named;
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
corpus::Corpus,
corpus::{Corpus, HasCurrentCorpusId},
executors::{Executor, HasObservers, ShadowExecutor},
inputs::{Input, UsesInput},
mark_feature_time,
observers::ObserversTuple,
stages::{RetryCountRestartHelper, Stage},
start_timer,
state::{HasCorpus, HasCurrentTestcase, HasExecutions, State, UsesState},
state::{HasCorpus, HasCurrentTestcase, HasExecutions, MaybeHasClientPerfMonitor, UsesState},
Error, HasNamedMetadata,
};
#[cfg(feature = "introspection")]
use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
/// A stage that runs a tracer executor
#[derive(Clone, Debug)]
pub struct TracingStage<EM, TE, Z> {
pub struct TracingStage<EM, TE, S, Z> {
name: Cow<'static, str>,
tracer_executor: TE,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(EM, TE, Z)>,
phantom: PhantomData<(EM, TE, S, Z)>,
}
impl<EM, TE, Z> UsesState for TracingStage<EM, TE, Z>
impl<EM, TE, S, Z> TracingStage<EM, TE, S, Z>
where
TE: UsesState,
{
type State = TE::State;
}
impl<EM, TE, Z> TracingStage<EM, TE, Z>
where
TE: Executor<EM, Z> + HasObservers,
TE::Observers: ObserversTuple<TE::Input, <Self as UsesState>::State>,
<TE as UsesState>::State: HasExecutions + HasCorpus + HasNamedMetadata + HasCurrentTestcase,
EM: UsesState<State = <Self as UsesState>::State>,
Z: UsesState<State = <Self as UsesState>::State>,
<<TE as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = TE::Input>, // delete me
TE: Executor<EM, Z, State = S> + HasObservers,
TE::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions
+ HasCorpus
+ HasNamedMetadata
+ HasCurrentTestcase
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
EM: UsesState<State = S>, //delete me
Z: UsesState<State = S>, //delete me
{
#[allow(rustdoc::broken_intra_doc_links)]
/// Perform tracing on the given `CorpusId`. Useful for if wrapping [`TracingStage`] with your
/// own stage and you need to manage [`super::NestedStageRetryCountRestartHelper`] differently
/// see [`super::ConcolicTracingStage`]'s implementation as an example of usage.
pub fn trace(
&mut self,
fuzzer: &mut Z,
state: &mut <Self as UsesState>::State,
manager: &mut EM,
) -> Result<(), Error> {
pub fn trace(&mut self, fuzzer: &mut Z, state: &mut S, manager: &mut EM) -> Result<(), Error> {
start_timer!(state);
let input = state.current_input_cloned()?;
@ -83,37 +76,41 @@ where
}
}
impl<E, EM, TE, Z> Stage<E, EM, Z> for TracingStage<EM, TE, Z>
impl<E, EM, TE, S, Z> Stage<E, EM, S, Z> for TracingStage<EM, TE, S, Z>
where
E: UsesState<State = <Self as UsesState>::State>,
TE: Executor<EM, Z> + HasObservers,
TE::Observers: ObserversTuple<TE::Input, <Self as UsesState>::State>,
<TE as UsesState>::State: HasExecutions + HasCorpus + HasNamedMetadata,
EM: UsesState<State = <Self as UsesState>::State>,
Z: UsesState<State = <Self as UsesState>::State>,
<<TE as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = TE::Input>, // delete me
TE: Executor<EM, Z, State = S> + HasObservers,
TE::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions
+ HasCorpus
+ HasNamedMetadata
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
<S::Corpus as Corpus>::Input: Input,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
_executor: &mut E,
state: &mut <Self as UsesState>::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
self.trace(fuzzer, state, manager)
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
RetryCountRestartHelper::no_retry(state, &self.name)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<EM, TE, Z> Named for TracingStage<EM, TE, Z> {
impl<EM, TE, S, Z> Named for TracingStage<EM, TE, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
@ -124,7 +121,7 @@ static mut TRACING_STAGE_ID: usize = 0;
/// The name for tracing stage
pub static TRACING_STAGE_NAME: &str = "tracing";
impl<EM, TE, Z> TracingStage<EM, TE, Z> {
impl<EM, TE, S, Z> TracingStage<EM, TE, S, Z> {
/// Creates a new default stage
pub fn new(tracer_executor: TE) -> Self {
// unsafe but impossible that you create two threads both instantiating this instance
@ -154,49 +151,46 @@ impl<EM, TE, Z> TracingStage<EM, TE, Z> {
/// A stage that runs the shadow executor using also the shadow observers
#[derive(Clone, Debug)]
pub struct ShadowTracingStage<E, EM, SOT, Z> {
pub struct ShadowTracingStage<E, EM, SOT, S, Z> {
name: Cow<'static, str>,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, SOT, Z)>,
phantom: PhantomData<(E, EM, SOT, S, Z)>,
}
impl<E, EM, SOT, Z> UsesState for ShadowTracingStage<E, EM, SOT, Z>
where
E: UsesState,
{
type State = E::State;
}
/// The counter for giving this stage unique id
static mut SHADOW_TRACING_STAGE_ID: usize = 0;
/// Name for shadow tracing stage
pub static SHADOW_TRACING_STAGE_NAME: &str = "shadow";
impl<E, EM, SOT, Z> Named for ShadowTracingStage<E, EM, SOT, Z>
where
E: UsesState,
{
impl<E, EM, SOT, S, Z> Named for ShadowTracingStage<E, EM, SOT, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, EM, SOT, Z> Stage<ShadowExecutor<E, SOT>, EM, Z> for ShadowTracingStage<E, EM, SOT, Z>
impl<E, EM, SOT, S, Z> Stage<ShadowExecutor<E, SOT>, EM, S, Z>
for ShadowTracingStage<E, EM, SOT, S, Z>
where
E: Executor<EM, Z> + HasObservers,
E::Observers: ObserversTuple<E::Input, E::State>,
EM: UsesState<State = <Self as UsesState>::State>,
SOT: ObserversTuple<E::Input, E::State>,
Z: UsesState<State = <Self as UsesState>::State>,
<E as UsesState>::State:
State + HasExecutions + HasCorpus + HasNamedMetadata + Debug + HasCurrentTestcase,
<<E as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = E::Input>, // delete me
E: Executor<EM, Z, State = S> + HasObservers,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
SOT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions
+ HasCorpus
+ HasNamedMetadata
+ Debug
+ HasCurrentTestcase
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut ShadowExecutor<E, SOT>,
state: &mut <Self as UsesState>::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
start_timer!(state);
@ -227,22 +221,22 @@ where
Ok(())
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
RetryCountRestartHelper::no_retry(state, &self.name)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<E, EM, SOT, Z> ShadowTracingStage<E, EM, SOT, Z>
impl<E, EM, SOT, S, Z> ShadowTracingStage<E, EM, SOT, S, Z>
where
E: Executor<EM, Z> + HasObservers,
<Self as UsesState>::State: State + HasExecutions + HasCorpus,
EM: UsesState<State = <Self as UsesState>::State>,
SOT: ObserversTuple<E::Input, E::State>,
Z: UsesState<State = <Self as UsesState>::State>,
E: Executor<EM, Z, State = Z::State> + HasObservers,
S: HasExecutions + HasCorpus,
SOT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
EM: UsesState<State = Z::State>,
Z: UsesState,
{
/// Creates a new default stage
pub fn new(_executor: &mut ShadowExecutor<E, SOT>) -> Self {

View File

@ -6,8 +6,11 @@ use core::{marker::PhantomData, time::Duration};
use libafl_bolts::{current_time, impl_serdeany, rands::Rand};
use serde::{Deserialize, Serialize};
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
corpus::Corpus,
inputs::{Input, UsesInput},
mark_feature_time,
mutators::{MutationResult, Mutator},
nonzero,
@ -16,11 +19,9 @@ use crate::{
ExecutionCountRestartHelper, MutationalStage, Stage,
},
start_timer,
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, UsesState},
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, MaybeHasClientPerfMonitor},
Error, Evaluator, HasMetadata, HasNamedMetadata,
};
#[cfg(feature = "introspection")]
use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
#[cfg_attr(
any(not(feature = "serdeany_autoreg"), miri),
@ -150,26 +151,103 @@ where
/// A [`crate::stages::MutationalStage`] where the mutator iteration can be tuned at runtime
#[derive(Clone, Debug)]
pub struct TuneableMutationalStage<E, EM, I, M, Z> {
pub struct TuneableMutationalStage<E, EM, I, M, S, Z> {
/// The mutator we use
mutator: M,
/// The name of this stage
name: String,
/// The progress helper we use to keep track of progress across restarts
restart_helper: ExecutionCountRestartHelper,
phantom: PhantomData<(E, EM, I, Z)>,
phantom: PhantomData<(E, EM, I, S, Z)>,
}
impl<E, EM, I, M, Z> MutationalStage<E, EM, I, M, Z> for TuneableMutationalStage<E, EM, I, M, Z>
impl<E, EM, I, M, S, Z> MutationalStage<S> for TuneableMutationalStage<E, EM, I, M, S, Z>
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
M: Mutator<I, Self::State>,
M: Mutator<I, S>,
Z: Evaluator<E, EM>,
Z::State:
HasCorpus + HasRand + HasNamedMetadata + HasMetadata + HasExecutions + HasCurrentTestcase,
I: MutatedTransform<Z::Input, Self::State> + Clone,
<<Z as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Z::Input>, // delete me
S: HasCorpus + HasRand + HasNamedMetadata + HasMetadata + HasExecutions + HasCurrentTestcase,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone,
<S::Corpus as Corpus>::Input: Input,
{
type Mutator = M;
/// The mutator, added to this stage
#[inline]
fn mutator(&self) -> &Self::Mutator {
&self.mutator
}
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut Self::Mutator {
&mut self.mutator
}
/// Gets the number of iterations as a random number
fn iterations(&self, state: &mut S) -> Result<usize, Error> {
Ok(
// fall back to random
1 + state
.rand_mut()
.below(nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS)),
)
}
}
impl<E, EM, I, M, S, Z> Stage<E, EM, S, Z> for TuneableMutationalStage<E, EM, I, M, S, Z>
where
M: Mutator<I, S>,
Z: Evaluator<E, EM, State = S>,
S: HasCorpus
+ HasRand
+ HasNamedMetadata
+ HasMetadata
+ HasExecutions
+ HasCurrentTestcase
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone,
<S::Corpus as Corpus>::Input: Input,
{
#[inline]
#[allow(clippy::let_and_return)]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let ret = self.perform_mutational(fuzzer, executor, state, manager);
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().finish_stage();
ret
}
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
self.restart_helper.should_restart(state, &self.name)
}
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
self.restart_helper.clear_progress(state, &self.name)
}
}
impl<E, EM, I, M, S, Z> TuneableMutationalStage<E, EM, I, M, S, Z>
where
M: Mutator<I, S>,
Z: Evaluator<E, EM, State = S>,
S: HasCorpus
+ HasRand
+ HasNamedMetadata
+ HasExecutions
+ HasMetadata
+ HasCurrentTestcase
+ MaybeHasClientPerfMonitor
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone,
<S::Corpus as Corpus>::Input: Input,
{
/// Runs this (mutational) stage for the given `testcase`
/// Exactly the same functionality as [`MutationalStage::perform_mutational`], but with added timeout support.
@ -177,7 +255,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let fuzz_time = self.seed_fuzz_time(state)?;
@ -233,105 +311,24 @@ where
Ok(())
}
/// The mutator, added to this stage
#[inline]
fn mutator(&self) -> &M {
&self.mutator
}
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator
}
/// Gets the number of iterations as a random number
fn iterations(&self, state: &mut Self::State) -> Result<usize, Error> {
Ok(
// fall back to random
1 + state
.rand_mut()
.below(nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS)),
)
}
}
impl<E, EM, I, M, Z> UsesState for TuneableMutationalStage<E, EM, I, M, Z>
where
Z: Evaluator<E, EM>,
{
type State = Z::State;
}
impl<E, EM, I, M, Z> Stage<E, EM, Z> for TuneableMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
M: Mutator<I, Self::State>,
Z: Evaluator<E, EM>,
Z::State:
HasCorpus + HasRand + HasNamedMetadata + HasMetadata + HasExecutions + HasCurrentTestcase,
I: MutatedTransform<Self::Input, Self::State> + Clone,
<<Z as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Z::Input>, // delete me
{
#[inline]
#[allow(clippy::let_and_return)]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
manager: &mut EM,
) -> Result<(), Error> {
let ret = self.perform_mutational(fuzzer, executor, state, manager);
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().finish_stage();
ret
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
self.restart_helper.should_restart(state, &self.name)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
self.restart_helper.clear_progress(state, &self.name)
}
}
impl<E, EM, I, M, Z> TuneableMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = <Self as UsesState>::State>,
EM: UsesState<State = <Self as UsesState>::State>,
M: Mutator<I, <Self as UsesState>::State>,
Z: Evaluator<E, EM>,
<Z as UsesState>::State:
HasCorpus + HasRand + HasNamedMetadata + HasExecutions + HasMetadata + HasCurrentTestcase,
I: MutatedTransform<Z::Input, <Self as UsesState>::State> + Clone,
<<Z as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Z::Input>, // delete me
{
fn execs_since_progress_start(
&mut self,
state: &mut <Self as UsesState>::State,
) -> Result<u64, Error> {
fn execs_since_progress_start(&mut self, state: &mut S) -> Result<u64, Error> {
self.restart_helper
.execs_since_progress_start(state, &self.name)
}
/// Creates a new default tuneable mutational stage
#[must_use]
pub fn new(state: &mut <Self as UsesState>::State, mutator: M) -> Self {
pub fn new(state: &mut S, mutator: M) -> Self {
Self::transforming(state, mutator, STD_TUNEABLE_MUTATIONAL_STAGE_NAME)
}
/// Crates a new tuneable mutational stage with the given name
pub fn with_name(state: &mut <Self as UsesState>::State, mutator: M, name: &str) -> Self {
pub fn with_name(state: &mut S, mutator: M, name: &str) -> Self {
Self::transforming(state, mutator, name)
}
/// Set the number of iterations to be used by this [`TuneableMutationalStage`]
pub fn set_iters<S>(&self, state: &mut S, iters: u64) -> Result<(), Error>
pub fn set_iters(&self, state: &mut S, iters: u64) -> Result<(), Error>
where
S: HasNamedMetadata,
{
@ -339,12 +336,12 @@ where
}
/// Set the number of iterations to be used by the std [`TuneableMutationalStage`]
pub fn set_iters_std(state: &mut <Self as UsesState>::State, iters: u64) -> Result<(), Error> {
pub fn set_iters_std(state: &mut S, iters: u64) -> Result<(), Error> {
set_iters_by_name(state, iters, STD_TUNEABLE_MUTATIONAL_STAGE_NAME)
}
/// Set the number of iterations to be used by the [`TuneableMutationalStage`] with the given name
pub fn set_iters_by_name<S>(state: &mut S, iters: u64, name: &str) -> Result<(), Error>
pub fn set_iters_by_name(state: &mut S, iters: u64, name: &str) -> Result<(), Error>
where
S: HasNamedMetadata,
{
@ -352,7 +349,7 @@ where
}
/// Get the set iterations for this [`TuneableMutationalStage`], if any
pub fn fixed_iters<S>(&self, state: &S) -> Result<Option<u64>, Error>
pub fn fixed_iters(&self, state: &S) -> Result<Option<u64>, Error>
where
S: HasNamedMetadata,
{
@ -360,12 +357,12 @@ where
}
/// Get the set iterations for the std [`TuneableMutationalStage`], if any
pub fn iters_std(state: &<Self as UsesState>::State) -> Result<Option<u64>, Error> {
pub fn iters_std(state: &S) -> Result<Option<u64>, Error> {
get_iters_by_name(state, STD_TUNEABLE_MUTATIONAL_STAGE_NAME)
}
/// Get the set iterations for the [`TuneableMutationalStage`] with the given name, if any
pub fn iters_by_name<S>(state: &S, name: &str) -> Result<Option<u64>, Error>
pub fn iters_by_name(state: &S, name: &str) -> Result<Option<u64>, Error>
where
S: HasNamedMetadata,
{
@ -373,7 +370,7 @@ where
}
/// Set the time to mutate a single input in this [`TuneableMutationalStage`]
pub fn set_seed_fuzz_time<S>(&self, state: &mut S, fuzz_time: Duration) -> Result<(), Error>
pub fn set_seed_fuzz_time(&self, state: &mut S, fuzz_time: Duration) -> Result<(), Error>
where
S: HasNamedMetadata,
{
@ -381,15 +378,12 @@ where
}
/// Set the time to mutate a single input in the std [`TuneableMutationalStage`]
pub fn set_seed_fuzz_time_std(
state: &mut <Self as UsesState>::State,
fuzz_time: Duration,
) -> Result<(), Error> {
pub fn set_seed_fuzz_time_std(state: &mut S, fuzz_time: Duration) -> Result<(), Error> {
set_seed_fuzz_time_by_name(state, fuzz_time, STD_TUNEABLE_MUTATIONAL_STAGE_NAME)
}
/// Set the time to mutate a single input in the [`TuneableMutationalStage`] with the given name
pub fn set_seed_fuzz_time_by_name<S>(
pub fn set_seed_fuzz_time_by_name(
state: &mut S,
fuzz_time: Duration,
name: &str,
@ -401,7 +395,7 @@ where
}
/// Set the time to mutate a single input in this [`TuneableMutationalStage`]
pub fn seed_fuzz_time<S>(&self, state: &S) -> Result<Option<Duration>, Error>
pub fn seed_fuzz_time(&self, state: &S) -> Result<Option<Duration>, Error>
where
S: HasNamedMetadata,
{
@ -409,19 +403,12 @@ where
}
/// Set the time to mutate a single input for the std [`TuneableMutationalStage`]
pub fn seed_fuzz_time_std(
&self,
state: &<Self as UsesState>::State,
) -> Result<Option<Duration>, Error> {
pub fn seed_fuzz_time_std(&self, state: &S) -> Result<Option<Duration>, Error> {
get_seed_fuzz_time_by_name(state, STD_TUNEABLE_MUTATIONAL_STAGE_NAME)
}
/// Set the time to mutate a single input for the [`TuneableMutationalStage`] with a given name
pub fn seed_fuzz_time_by_name<S>(
&self,
state: &S,
name: &str,
) -> Result<Option<Duration>, Error>
pub fn seed_fuzz_time_by_name(&self, state: &S, name: &str) -> Result<Option<Duration>, Error>
where
S: HasNamedMetadata,
{
@ -429,7 +416,7 @@ where
}
/// Reset this to a normal, randomized, stage with
pub fn reset<S>(&self, state: &mut S) -> Result<(), Error>
pub fn reset(&self, state: &mut S) -> Result<(), Error>
where
S: HasNamedMetadata,
{
@ -437,12 +424,12 @@ where
}
/// Reset the std stage to a normal, randomized, stage
pub fn reset_std(state: &mut <Self as UsesState>::State) -> Result<(), Error> {
pub fn reset_std(state: &mut S) -> Result<(), Error> {
reset_by_name(state, STD_TUNEABLE_MUTATIONAL_STAGE_NAME)
}
/// Reset this to a normal, randomized, stage by name
pub fn reset_by_name<S>(state: &mut S, name: &str) -> Result<(), Error>
pub fn reset_by_name(state: &mut S, name: &str) -> Result<(), Error>
where
S: HasNamedMetadata,
{
@ -453,7 +440,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut <Self as UsesState>::State,
state: &mut S,
manager: &mut EM,
input: &I,
) -> Result<(), Error> {
@ -480,17 +467,15 @@ where
}
}
impl<E, EM, I, M, Z> TuneableMutationalStage<E, EM, I, M, Z>
impl<E, EM, I, M, S, Z> TuneableMutationalStage<E, EM, I, M, S, Z>
where
E: UsesState<State = <Self as UsesState>::State>,
EM: UsesState<State = <Self as UsesState>::State>,
M: Mutator<I, Z::State>,
Z: Evaluator<E, EM>,
<Self as UsesState>::State: HasCorpus + HasRand + HasNamedMetadata,
S: HasCorpus + HasRand + HasNamedMetadata,
{
/// Creates a new transforming mutational stage
#[must_use]
pub fn transforming(state: &mut <Self as UsesState>::State, mutator: M, name: &str) -> Self {
pub fn transforming(state: &mut S, mutator: M, name: &str) -> Self {
let _ = state.named_metadata_or_insert_with(name, TuneableMutationalStageMetadata::default);
Self {
mutator,

View File

@ -11,7 +11,7 @@ use crate::{
corpus::Corpus,
inputs::{BytesInput, HasTargetBytes},
stages::Stage,
state::{HasCorpus, HasCurrentTestcase, State, UsesState},
state::{HasCorpus, HasCurrentTestcase},
HasMetadata,
};
@ -109,39 +109,29 @@ impl<S> UnicodeIdentificationStage<S> {
}
}
impl<S> UsesState for UnicodeIdentificationStage<S>
impl<E, EM, S, Z> Stage<E, EM, S, Z> for UnicodeIdentificationStage<S>
where
S: State,
{
type State = S;
}
impl<S, E, EM, Z> Stage<E, EM, Z> for UnicodeIdentificationStage<S>
where
S: HasCorpus + State + HasCurrentTestcase,
S: HasCorpus + HasCurrentTestcase,
S::Corpus: Corpus<Input = BytesInput>,
E: UsesState<State = S>,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
fn perform(
&mut self,
_fuzzer: &mut Z,
_executor: &mut E,
state: &mut Self::State,
state: &mut S,
_manager: &mut EM,
) -> Result<(), Error> {
UnicodeIdentificationStage::identify_unicode_in_current_testcase(state)
}
#[inline]
fn should_restart(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, _state: &mut S) -> Result<bool, Error> {
// Stage does not run the target. No reset helper needed.
Ok(true)
}
#[inline]
fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> {
// Stage does not run the target. No reset helper needed.
Ok(())
}

View File

@ -15,7 +15,7 @@ use crate::{
inputs::{BytesInput, UsesInput},
observers::ObserversTuple,
stages::Stage,
state::{HasCorpus, State, UsesState},
state::{HasCorpus, UsesState},
Evaluator, HasMetadata,
};
@ -43,13 +43,6 @@ impl<E, S> VerifyTimeoutsStage<E, S> {
}
}
impl<E, S> UsesState for VerifyTimeoutsStage<E, S>
where
S: State,
{
type State = S;
}
/// Timeouts that `VerifyTimeoutsStage` will read from
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: for<'a> Deserialize<'a> + Serialize")]
@ -88,21 +81,20 @@ impl<I> TimeoutsToVerify<I> {
}
}
impl<E, EM, Z, S> Stage<E, EM, Z> for VerifyTimeoutsStage<E, S>
impl<E, EM, S, Z> Stage<E, EM, S, Z> for VerifyTimeoutsStage<E, S>
where
E::Observers: ObserversTuple<<Self as UsesInput>::Input, <Self as UsesState>::State>,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
E: Executor<EM, Z, State = S> + HasObservers + HasTimeout,
EM: UsesState<State = S>,
Z: UsesState<State = S> + Evaluator<E, EM>,
S: HasCorpus + State + HasMetadata,
Self::Input: Debug + Serialize + DeserializeOwned + Default + 'static + Clone,
<<E as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
Z: Evaluator<E, EM, State = S>,
S: HasCorpus + HasMetadata + UsesInput<Input = <S::Corpus as Corpus>::Input>,
<S::Corpus as Corpus>::Input: Debug + Serialize + DeserializeOwned + Default + 'static + Clone,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let mut timeouts = state
@ -118,15 +110,17 @@ where
}
executor.set_timeout(self.original_timeout);
*self.capture_timeouts.borrow_mut() = true;
let res = state.metadata_mut::<TimeoutsToVerify<E::Input>>().unwrap();
*res = TimeoutsToVerify::<E::Input>::new();
let res = state
.metadata_mut::<TimeoutsToVerify<<S::Corpus as Corpus>::Input>>()
.unwrap();
*res = TimeoutsToVerify::<<S::Corpus as Corpus>::Input>::new();
Ok(())
}
fn should_restart(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, _state: &mut S) -> Result<bool, Error> {
Ok(true)
}
fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> {
Ok(())
}
}

View File

@ -356,7 +356,7 @@ macro_rules! fuzz_with {
// TODO configure with mutation stacking options from libfuzzer
let std_mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let std_power: StdPowerMutationalStage<_, _, BytesInput, _, _> = StdPowerMutationalStage::new(std_mutator);
let std_power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(std_mutator);
let std_power = IfStage::new(|_, _, _, _| Ok(mutator_status.std_mutational.into()), (std_power, ()));
// for custom mutator and crossover, each have access to the LLVMFuzzerMutate -- but it appears
@ -378,7 +378,7 @@ macro_rules! fuzz_with {
// Safe to unwrap: stack pow is not 0.
let std_mutator_no_mutate = StdScheduledMutator::with_max_stack_pow(havoc_crossover(),3);
let cm_power: StdPowerMutationalStage<_, _, BytesInput, _, _> = StdPowerMutationalStage::new(custom_mutator);
let cm_power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(custom_mutator);
let cm_power = IfStage::new(|_, _, _, _| Ok(mutator_status.custom_mutation.into()), (cm_power, ()));
let cm_std_power = StdMutationalStage::new(std_mutator_no_mutate);
let cm_std_power =
@ -398,7 +398,7 @@ macro_rules! fuzz_with {
let cc_power = StdMutationalStage::new(custom_crossover);
let cc_power = IfStage::new(|_, _, _, _| Ok(mutator_status.custom_crossover.into()), (cc_power, ()));
let cc_std_power: StdPowerMutationalStage<_, _, BytesInput, _, _> = StdPowerMutationalStage::new(std_mutator_no_crossover);
let cc_std_power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(std_mutator_no_crossover);
let cc_std_power =
IfStage::new(|_, _, _, _| Ok(mutator_status.std_no_crossover.into()), (cc_std_power, ()));

View File

@ -2,12 +2,12 @@ use alloc::borrow::{Cow, ToOwned};
use core::marker::PhantomData;
use libafl::{
corpus::Corpus,
corpus::{Corpus, HasCurrentCorpusId},
executors::{Executor, HasObservers},
inputs::{BytesInput, UsesInput},
observers::ObserversTuple,
stages::{colorization::TaintMetadata, RetryCountRestartHelper, Stage},
state::{HasCorpus, HasCurrentTestcase, HasExecutions, UsesState},
state::{HasCorpus, HasCurrentTestcase, UsesState},
Error, HasMetadata, HasNamedMetadata,
};
use libafl_bolts::{
@ -19,56 +19,42 @@ use crate::cmps::observers::AFLppCmpLogObserver;
/// Trace with tainted input
#[derive(Clone, Debug)]
pub struct AFLppCmplogTracingStage<'a, EM, TE, Z>
where
TE: UsesState,
{
pub struct AFLppCmplogTracingStage<'a, EM, TE, S, Z> {
name: Cow<'static, str>,
tracer_executor: TE,
cmplog_observer_handle: Handle<AFLppCmpLogObserver<'a>>,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(EM, TE, Z)>,
phantom: PhantomData<(EM, TE, S, Z)>,
}
/// The name for aflpp tracing stage
pub static AFLPP_CMPLOG_TRACING_STAGE_NAME: &str = "aflpptracing";
impl<EM, TE, Z> UsesState for AFLppCmplogTracingStage<'_, EM, TE, Z>
where
TE: UsesState,
{
type State = TE::State;
}
impl<EM, TE, Z> Named for AFLppCmplogTracingStage<'_, EM, TE, Z>
where
TE: UsesState,
{
impl<EM, TE, S, Z> Named for AFLppCmplogTracingStage<'_, EM, TE, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, EM, TE, Z> Stage<E, EM, Z> for AFLppCmplogTracingStage<'_, EM, TE, Z>
impl<E, EM, TE, S, Z> Stage<E, EM, S, Z> for AFLppCmplogTracingStage<'_, EM, TE, S, Z>
where
E: UsesState<State = Self::State>,
TE: Executor<EM, Z> + HasObservers,
TE::State: HasExecutions
+ HasCorpus
+ HasMetadata
EM: UsesState<State = S>,
Z: UsesState<State = S>,
TE: HasObservers + Executor<EM, Z, State = S>,
TE::Observers: MatchNameRef + ObserversTuple<BytesInput, S>,
S: HasCorpus
+ HasCurrentTestcase
+ UsesInput<Input = BytesInput>
+ HasMetadata
+ HasNamedMetadata
+ HasCurrentTestcase,
TE::Observers: MatchNameRef + ObserversTuple<BytesInput, TE::State>,
EM: UsesState<State = Self::State>,
Z: UsesState<State = Self::State>,
<Self::State as HasCorpus>::Corpus: Corpus<Input = BytesInput>, //delete me
+ HasCurrentCorpusId,
S::Corpus: Corpus<Input = BytesInput>,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
_executor: &mut E,
state: &mut TE::State,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
// First run with the un-mutated input
@ -131,22 +117,19 @@ where
Ok(())
}
fn should_restart(&mut self, state: &mut Self::State) -> Result<bool, Error> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// Tracing stage is always deterministic
// don't restart
RetryCountRestartHelper::no_retry(state, &self.name)
}
fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
// TODO: this may need better resumption? (Or is it always used with a forkserver?)
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<'a, EM, TE, Z> AFLppCmplogTracingStage<'a, EM, TE, Z>
where
TE: UsesState,
{
impl<'a, EM, TE, S, Z> AFLppCmplogTracingStage<'a, EM, TE, S, Z> {
/// With cmplog observer
pub fn new(tracer_executor: TE, observer_handle: Handle<AFLppCmpLogObserver<'a>>) -> Self {
let observer_name = observer_handle.name().clone();