use alloc::string::{String, ToString};
use core::{
fmt::Debug,
hash::{BuildHasher, Hash, Hasher},
marker::PhantomData,
};
use ahash::RandomState;
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
bolts::{tuples::Named, HasLen},
corpus::{Corpus, CorpusId, Testcase},
events::EventFirer,
executors::{Executor, ExitKind, HasObservers},
feedbacks::{Feedback, FeedbackFactory, HasObserverName},
inputs::UsesInput,
mark_feature_time,
mutators::{MutationResult, Mutator},
observers::{MapObserver, ObserversTuple},
schedulers::{RemovableScheduler, Scheduler},
stages::Stage,
start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMaxSize, HasSolutions, UsesState},
Error, ExecutesInput, ExecutionProcessor, HasFeedback, HasScheduler,
};
pub trait TMinMutationalStage<CS, E, EM, F1, F2, M, OT, Z>:
Stage<E, EM, Z> + FeedbackFactory<F2, CS::State, OT>
where
Self::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize + HasClientPerfMonitor,
<Self::State as UsesInput>::Input: HasLen + Hash,
CS: Scheduler<State = Self::State> + RemovableScheduler,
E: Executor<EM, Z> + HasObservers<Observers = OT, State = Self::State>,
EM: EventFirer<State = Self::State>,
F1: Feedback<Self::State>,
F2: Feedback<Self::State>,
M: Mutator<Self::Input, Self::State>,
OT: ObserversTuple<CS::State>,
Z: ExecutionProcessor<OT, State = Self::State>
+ ExecutesInput<E, EM>
+ HasFeedback<Feedback = F1>
+ HasScheduler<Scheduler = CS>,
{
fn mutator(&self) -> &M;
fn mutator_mut(&mut self) -> &mut M;
fn iterations(&self, state: &mut CS::State, corpus_idx: CorpusId) -> Result<usize, Error>;
#[allow(clippy::cast_possible_wrap)] fn perform_minification(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut CS::State,
manager: &mut EM,
base_corpus_idx: CorpusId,
) -> Result<(), Error> {
let orig_max_size = state.max_size();
let num = self.iterations(state, base_corpus_idx)?;
start_timer!(state);
let mut base = state.corpus().cloned_input_for_id(base_corpus_idx)?;
let mut hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher();
base.hash(&mut hasher);
let base_hash = hasher.finish();
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
fuzzer.execute_input(state, executor, manager, &base)?;
let observers = executor.observers();
let mut feedback = self.create_feedback(observers);
let mut i = 0;
loop {
if i >= num {
break;
}
let mut next_i = i + 1;
let mut input = base.clone();
let before_len = input.len();
state.set_max_size(before_len);
start_timer!(state);
let mutated = self.mutator_mut().mutate(state, &mut input, i as i32)?;
mark_feature_time!(state, PerfFeature::Mutate);
if mutated == MutationResult::Skipped {
continue;
}
let corpus_idx = if input.len() < before_len {
let exit_kind = fuzzer.execute_input(state, executor, manager, &input)?;
let observers = executor.observers();
let solution_count = state.solutions().count();
let corpus_count = state.corpus().count();
*state.executions_mut() += 1;
let (_, corpus_idx) = fuzzer.process_execution(
state,
manager,
input.clone(),
observers,
&exit_kind,
false,
)?;
if state.corpus().count() == corpus_count
&& state.solutions().count() == solution_count
{
if feedback.is_interesting(state, manager, &input, observers, &exit_kind)? {
base = input;
next_i = 0;
}
}
corpus_idx
} else {
None
};
start_timer!(state);
self.mutator_mut().post_exec(state, i as i32, corpus_idx)?;
mark_feature_time!(state, PerfFeature::MutatePostExec);
i = next_i;
}
let mut hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher();
base.hash(&mut hasher);
let new_hash = hasher.finish();
if base_hash != new_hash {
let exit_kind = fuzzer.execute_input(state, executor, manager, &base)?;
let observers = executor.observers();
*state.executions_mut() += 1;
fuzzer
.feedback_mut()
.is_interesting(state, manager, &base, observers, &exit_kind)?;
let mut testcase = Testcase::with_executions(base, *state.executions());
fuzzer
.feedback_mut()
.append_metadata(state, observers, &mut testcase)?;
let prev = state.corpus_mut().replace(base_corpus_idx, testcase)?;
fuzzer
.scheduler_mut()
.on_replace(state, base_corpus_idx, &prev)?;
}
state.set_max_size(orig_max_size);
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> {
mutator: M,
factory: FF,
runs: usize,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(CS, E, EM, F1, F2, OT, Z)>,
}
impl<CS, E, EM, F1, F2, FF, M, OT, Z> UsesState
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
CS: Scheduler,
M: Mutator<CS::Input, CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>,
CS::State: HasCorpus,
{
type State = CS::State;
}
impl<CS, E, EM, F1, F2, FF, M, OT, Z> Stage<E, EM, Z>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
CS: Scheduler + RemovableScheduler,
CS::State:
HasCorpus + HasSolutions + HasExecutions + HasMaxSize + HasClientPerfMonitor + HasCorpus,
<CS::State as UsesInput>::Input: HasLen + Hash,
E: Executor<EM, Z> + HasObservers<Observers = OT, State = CS::State>,
EM: EventFirer<State = CS::State>,
F1: Feedback<CS::State>,
F2: Feedback<CS::State>,
FF: FeedbackFactory<F2, CS::State, OT>,
M: Mutator<CS::Input, CS::State>,
OT: ObserversTuple<CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>
+ ExecutesInput<E, EM>
+ HasFeedback<Feedback = F1>
+ HasScheduler<Scheduler = CS>,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut CS::State,
manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> {
self.perform_minification(fuzzer, executor, state, manager, corpus_idx)?;
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().finish_stage();
Ok(())
}
}
impl<CS, E, EM, F1, F2, FF, M, OT, Z> FeedbackFactory<F2, Z::State, OT>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
F2: Feedback<Z::State>,
FF: FeedbackFactory<F2, Z::State, OT>,
Z: UsesState,
Z::State: HasClientPerfMonitor,
{
fn create_feedback(&self, ctx: &OT) -> F2 {
self.factory.create_feedback(ctx)
}
}
impl<CS, E, EM, F1, F2, FF, M, OT, Z> TMinMutationalStage<CS, E, EM, F1, F2, M, OT, Z>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
CS: Scheduler + RemovableScheduler,
E: HasObservers<Observers = OT, State = CS::State> + Executor<EM, Z>,
EM: EventFirer<State = CS::State>,
F1: Feedback<CS::State>,
F2: Feedback<CS::State>,
FF: FeedbackFactory<F2, CS::State, OT>,
<CS::State as UsesInput>::Input: HasLen + Hash,
M: Mutator<CS::Input, CS::State>,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasCorpus + HasSolutions + HasExecutions + HasMaxSize,
Z: ExecutionProcessor<OT, State = CS::State>
+ ExecutesInput<E, EM>
+ HasFeedback<Feedback = F1>
+ HasScheduler<Scheduler = CS>,
{
#[inline]
fn mutator(&self) -> &M {
&self.mutator
}
#[inline]
fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator
}
fn iterations(&self, _state: &mut CS::State, _corpus_idx: CorpusId) -> Result<usize, Error> {
Ok(self.runs)
}
}
impl<CS, E, EM, F1, F2, FF, M, OT, Z> StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
CS: Scheduler,
M: Mutator<CS::Input, CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>,
CS::State: HasCorpus,
{
pub fn new(mutator: M, factory: FF, runs: usize) -> Self {
Self {
mutator,
factory,
runs,
phantom: PhantomData,
}
}
}
#[derive(Clone, Debug)]
pub struct MapEqualityFeedback<M, S> {
name: String,
obs_name: String,
orig_hash: u64,
phantom: PhantomData<(M, S)>,
}
impl<M, S> MapEqualityFeedback<M, S> {
#[must_use]
pub fn new(name: &str, obs_name: &str, orig_hash: u64) -> Self {
MapEqualityFeedback {
name: name.to_string(),
obs_name: obs_name.to_string(),
orig_hash,
phantom: PhantomData,
}
}
}
impl<M, S> Named for MapEqualityFeedback<M, S> {
fn name(&self) -> &str {
&self.name
}
}
impl<M, S> HasObserverName for MapEqualityFeedback<M, S> {
fn observer_name(&self) -> &str {
&self.obs_name
}
}
impl<M, S> Feedback<S> for MapEqualityFeedback<M, S>
where
M: MapObserver + Debug,
S: UsesInput + HasClientPerfMonitor + Debug,
{
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &S::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
let obs = observers
.match_name::<M>(self.observer_name())
.expect("Should have been provided valid observer name.");
Ok(obs.hash() == self.orig_hash)
}
}
#[derive(Debug, Clone)]
pub struct MapEqualityFactory<M, S> {
obs_name: String,
phantom: PhantomData<(M, S)>,
}
impl<M, S> MapEqualityFactory<M, S>
where
M: MapObserver,
{
pub fn with_observer(obs: &M) -> Self {
Self {
obs_name: obs.name().to_string(),
phantom: PhantomData,
}
}
}
impl<M, S> HasObserverName for MapEqualityFactory<M, S> {
fn observer_name(&self) -> &str {
&self.obs_name
}
}
impl<M, OT, S> FeedbackFactory<MapEqualityFeedback<M, S>, S, OT> for MapEqualityFactory<M, S>
where
M: MapObserver,
OT: ObserversTuple<S>,
S: UsesInput + HasClientPerfMonitor + Debug,
{
fn create_feedback(&self, observers: &OT) -> MapEqualityFeedback<M, S> {
let obs = observers
.match_name::<M>(self.observer_name())
.expect("Should have been provided valid observer name.");
MapEqualityFeedback {
name: "MapEq".to_string(),
obs_name: self.obs_name.clone(),
orig_hash: obs.hash(),
phantom: PhantomData,
}
}
}