use alloc::{
borrow::{Cow, ToOwned},
string::ToString,
};
use core::{marker::PhantomData, num::NonZeroUsize};
use libafl_bolts::{Named, rands::Rand};
#[cfg(feature = "introspection")]
use crate::monitors::stats::PerfFeature;
use crate::{
Error, HasMetadata, HasNamedMetadata,
corpus::{Corpus, CorpusId, HasCurrentCorpusId, Testcase},
fuzzer::Evaluator,
inputs::Input,
mark_feature_time,
mutators::{MultiMutator, MutationResult, Mutator},
nonzero,
stages::{Restartable, RetryCountRestartHelper, Stage},
start_timer,
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, MaybeHasClientPerfMonitor},
};
pub trait MutatedTransformPost<S>: Sized {
#[inline]
fn post_exec(self, _state: &mut S, _new_corpus_id: Option<CorpusId>) -> Result<(), Error> {
Ok(())
}
}
impl<S> MutatedTransformPost<S> for () {}
pub trait MutatedTransform<I, S>: Sized {
type Post: MutatedTransformPost<S>;
fn try_transform_from(base: &mut Testcase<I>, state: &S) -> Result<Self, Error>;
fn try_transform_into(self, state: &S) -> Result<(I, Self::Post), Error>;
}
impl<I, S> MutatedTransform<I, S> for I
where
I: Clone,
S: HasCorpus<I>,
{
type Post = ();
#[inline]
fn try_transform_from(base: &mut Testcase<I>, state: &S) -> Result<Self, Error> {
state.corpus().load_input_into(base)?;
Ok(base.input().as_ref().unwrap().clone())
}
#[inline]
fn try_transform_into(self, _state: &S) -> Result<(I, Self::Post), Error> {
Ok((self, ()))
}
}
pub trait MutationalStage<S> {
type Mutator;
fn mutator(&self) -> &Self::Mutator;
fn mutator_mut(&mut self) -> &mut Self::Mutator;
fn iterations(&self, state: &mut S) -> Result<usize, Error>;
}
pub const DEFAULT_MUTATIONAL_MAX_ITERATIONS: usize = 128;
#[derive(Debug, Clone)]
pub struct StdMutationalStage<E, EM, I1, I2, M, S, Z> {
name: Cow<'static, str>,
mutator: M,
max_iterations: NonZeroUsize,
phantom: PhantomData<(E, EM, I1, I2, S, Z)>,
}
impl<E, EM, I1, I2, M, S, Z> MutationalStage<S> for StdMutationalStage<E, EM, I1, I2, M, S, Z>
where
S: HasRand,
{
type Mutator = M;
#[inline]
fn mutator(&self) -> &Self::Mutator {
&self.mutator
}
#[inline]
fn mutator_mut(&mut self) -> &mut Self::Mutator {
&mut self.mutator
}
fn iterations(&self, state: &mut S) -> Result<usize, Error> {
Ok(1 + state.rand_mut().below(self.max_iterations))
}
}
static mut MUTATIONAL_STAGE_ID: usize = 0;
pub static MUTATIONAL_STAGE_NAME: &str = "mutational";
impl<E, EM, I1, I2, M, S, Z> Named for StdMutationalStage<E, EM, I1, I2, M, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, EM, I1, I2, M, S, Z> Stage<E, EM, S, Z> for StdMutationalStage<E, EM, I1, I2, M, S, Z>
where
I1: Clone + MutatedTransform<I2, S>,
I2: Input,
M: Mutator<I1, S>,
S: HasRand
+ HasCorpus<I2>
+ HasMetadata
+ HasExecutions
+ HasNamedMetadata
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
Z: Evaluator<E, EM, I2, S>,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
self.perform_mutational(fuzzer, executor, state, manager)
}
}
impl<E, EM, I1, I2, M, S, Z> Restartable<S> for StdMutationalStage<E, EM, I1, I2, M, S, Z>
where
S: HasMetadata + HasNamedMetadata + HasCurrentCorpusId,
{
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, I, M, S, Z> StdMutationalStage<E, EM, I, I, M, S, Z>
where
M: Mutator<I, S>,
I: MutatedTransform<I, S> + Input + Clone,
S: HasCorpus<I> + HasRand + HasCurrentCorpusId + MaybeHasClientPerfMonitor,
Z: Evaluator<E, EM, I, S>,
{
pub fn new(mutator: M) -> Self {
Self::transforming_with_max_iterations(mutator, nonzero!(DEFAULT_MUTATIONAL_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, I1, I2, M, S, Z> StdMutationalStage<E, EM, I1, I2, M, S, Z>
where
I1: MutatedTransform<I2, S> + Clone,
I2: Input,
M: Mutator<I1, S>,
S: HasCorpus<I2> + HasRand + HasCurrentCorpusId + MaybeHasClientPerfMonitor,
Z: Evaluator<E, EM, I2, S>,
{
pub fn transforming(mutator: M) -> Self {
Self::transforming_with_max_iterations(mutator, nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS))
}
#[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,
}
}
}
impl<E, EM, I1, I2, M, S, Z> StdMutationalStage<E, EM, I1, I2, M, S, Z>
where
I1: MutatedTransform<I2, S> + Clone,
I2: Input,
M: Mutator<I1, S>,
S: HasRand + HasCurrentTestcase<I2> + MaybeHasClientPerfMonitor,
Z: Evaluator<E, EM, I2, S>,
{
fn perform_mutational(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
start_timer!(state);
let num = self.iterations(state)?;
let mut testcase = state.current_testcase_mut()?;
let Ok(input) = I1::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;
}
let (untransformed, post) = input.try_transform_into(state)?;
let (_, corpus_id) =
fuzzer.evaluate_filtered(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(())
}
}
#[derive(Debug, Clone)]
pub struct MultiMutationalStage<E, EM, I, M, S, Z> {
name: Cow<'static, str>,
mutator: M,
phantom: PhantomData<(E, EM, I, S, Z)>,
}
static mut MULTI_MUTATIONAL_STAGE_ID: usize = 0;
pub static MULTI_MUTATIONAL_STAGE_NAME: &str = "multimutational";
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, S, Z> Stage<E, EM, S, Z> for MultiMutationalStage<E, EM, I, M, S, Z>
where
I: Clone + MutatedTransform<I, S>,
M: MultiMutator<I, S>,
S: HasRand + HasNamedMetadata + HasCurrentTestcase<I> + HasCurrentCorpusId,
Z: Evaluator<E, EM, I, S>,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
let mut testcase = state.current_testcase_mut()?;
let Ok(input) = I::try_transform_from(&mut testcase, state) else {
return Ok(());
};
drop(testcase);
let generated = self.mutator.multi_mutate(state, &input, None)?;
for new_input in generated {
let (untransformed, post) = new_input.try_transform_into(state)?;
let (_, corpus_id) =
fuzzer.evaluate_filtered(state, executor, manager, &untransformed)?;
self.mutator.multi_post_exec(state, corpus_id)?;
post.post_exec(state, corpus_id)?;
}
Ok(())
}
}
impl<E, EM, I, M, S, Z> Restartable<S> for MultiMutationalStage<E, EM, I, M, S, Z>
where
S: HasMetadata + HasNamedMetadata + HasCurrentCorpusId,
{
#[inline]
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
RetryCountRestartHelper::should_restart(state, &self.name, 3)
}
#[inline]
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<E, EM, I, M, S, Z> MultiMutationalStage<E, EM, I, M, S, Z> {
pub fn new(mutator: M) -> Self {
Self::transforming(mutator)
}
}
impl<E, EM, I, M, S, Z> MultiMutationalStage<E, EM, I, M, S, Z> {
pub fn transforming(mutator: M) -> Self {
let stage_id = unsafe {
let ret = MULTI_MUTATIONAL_STAGE_ID;
MULTI_MUTATIONAL_STAGE_ID += 1;
ret
};
Self {
name: Cow::Owned(
MULTI_MUTATIONAL_STAGE_NAME.to_owned() + ":" + stage_id.to_string().as_str(),
),
mutator,
phantom: PhantomData,
}
}
}