Add MutatedTransform to the input type in TMinMutationalStage (#1251) (#1971)

* Support `MutatedTransform` in `TMinMutationalStage`.

* Run `MutatedTransformPost` for the replaced testcase.

* Add clone trait bound for `MutatedTransformPost`.

* Return an error instead of using unwrap.
This commit is contained in:
am009 2024-03-28 01:59:45 +08:00 committed by GitHub
parent f0ee6e0587
commit c221108916
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,7 +1,7 @@
//! The [`TMinMutationalStage`] is a stage which will attempt to minimize corpus entries. //! The [`TMinMutationalStage`] is a stage which will attempt to minimize corpus entries.
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use core::{borrow::BorrowMut, fmt::Debug, hash::Hash, marker::PhantomData};
use ahash::RandomState; use ahash::RandomState;
use libafl_bolts::{HasLen, Named}; use libafl_bolts::{HasLen, Named};
@ -16,7 +16,10 @@ use crate::{
mutators::{MutationResult, Mutator}, mutators::{MutationResult, Mutator},
observers::{MapObserver, ObserversTuple}, observers::{MapObserver, ObserversTuple},
schedulers::{RemovableScheduler, Scheduler}, schedulers::{RemovableScheduler, Scheduler},
stages::{ExecutionCountRestartHelper, Stage}, stages::{
mutational::{MutatedTransform, MutatedTransformPost},
ExecutionCountRestartHelper, Stage,
},
start_timer, start_timer,
state::{ state::{
HasCorpus, HasCurrentTestcase, HasExecutions, HasMaxSize, HasMetadata, HasSolutions, State, HasCorpus, HasCurrentTestcase, HasExecutions, HasMaxSize, HasMetadata, HasSolutions, State,
@ -30,7 +33,7 @@ use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
/// Mutational stage which minimizes corpus entries. /// Mutational stage which minimizes corpus entries.
/// ///
/// You must provide at least one mutator that actually reduces size. /// You must provide at least one mutator that actually reduces size.
pub trait TMinMutationalStage<CS, E, EM, F1, F2, M, OT, Z>: pub trait TMinMutationalStage<CS, E, EM, F1, F2, I, IP, M, OT, Z>:
Stage<E, EM, Z> + FeedbackFactory<F2, CS::State, OT> Stage<E, EM, Z> + FeedbackFactory<F2, CS::State, OT>
where where
Self::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize, Self::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize,
@ -40,12 +43,14 @@ where
EM: EventFirer<State = Self::State>, EM: EventFirer<State = Self::State>,
F1: Feedback<Self::State>, F1: Feedback<Self::State>,
F2: Feedback<Self::State>, F2: Feedback<Self::State>,
M: Mutator<Self::Input, Self::State>, M: Mutator<I, Self::State>,
OT: ObserversTuple<CS::State>, OT: ObserversTuple<CS::State>,
Z: ExecutionProcessor<OT, State = Self::State> Z: ExecutionProcessor<OT, State = Self::State>
+ ExecutesInput<E, EM> + ExecutesInput<E, EM>
+ HasFeedback<Feedback = F1> + HasFeedback<Feedback = F1>
+ HasScheduler<Scheduler = CS>, + HasScheduler<Scheduler = CS>,
IP: MutatedTransformPost<Self::State> + Clone,
I: MutatedTransform<Self::Input, Self::State, Post = IP> + Clone,
{ {
/// The mutator registered for this stage /// The mutator registered for this stage
fn mutator(&self) -> &M; fn mutator(&self) -> &M;
@ -77,7 +82,10 @@ where
- usize::try_from(self.execs_since_progress_start(state)?).unwrap(); - usize::try_from(self.execs_since_progress_start(state)?).unwrap();
start_timer!(state); start_timer!(state);
let transformed = I::try_transform_from(state.current_testcase_mut()?.borrow_mut(), state)?;
let mut base = state.current_input_cloned()?; let mut base = state.current_input_cloned()?;
// potential post operation if base is replaced by a shorter input
let mut base_post = None;
let base_hash = RandomState::with_seeds(0, 0, 0, 0).hash_one(&base); let base_hash = RandomState::with_seeds(0, 0, 0, 0).hash_one(&base);
mark_feature_time!(state, PerfFeature::GetInputFromCorpus); mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
@ -93,20 +101,21 @@ where
} }
let mut next_i = i + 1; let mut next_i = i + 1;
let mut input = base.clone(); let mut input_transformed = transformed.clone();
let before_len = input.len(); let before_len = base.len();
state.set_max_size(before_len); state.set_max_size(before_len);
start_timer!(state); start_timer!(state);
let mutated = self.mutator_mut().mutate(state, &mut input)?; let mutated = self.mutator_mut().mutate(state, &mut input_transformed)?;
mark_feature_time!(state, PerfFeature::Mutate); mark_feature_time!(state, PerfFeature::Mutate);
if mutated == MutationResult::Skipped { if mutated == MutationResult::Skipped {
continue; continue;
} }
let (input, post) = input_transformed.try_transform_into(state)?;
let corpus_idx = if input.len() < before_len { let corpus_idx = if input.len() < before_len {
// run the input // run the input
let exit_kind = fuzzer.execute_input(state, executor, manager, &input)?; let exit_kind = fuzzer.execute_input(state, executor, manager, &input)?;
@ -134,6 +143,7 @@ where
if feedback.is_interesting(state, manager, &input, observers, &exit_kind)? { if feedback.is_interesting(state, manager, &input, observers, &exit_kind)? {
// we found a reduced corpus entry! use the smaller base // we found a reduced corpus entry! use the smaller base
base = input; base = input;
base_post = Some(post.clone());
// do more runs! maybe we can minify further // do more runs! maybe we can minify further
next_i = 0; next_i = 0;
@ -149,6 +159,7 @@ where
start_timer!(state); start_timer!(state);
self.mutator_mut().post_exec(state, corpus_idx)?; self.mutator_mut().post_exec(state, corpus_idx)?;
post.post_exec(state, corpus_idx)?;
mark_feature_time!(state, PerfFeature::MutatePostExec); mark_feature_time!(state, PerfFeature::MutatePostExec);
i = next_i; i = next_i;
@ -171,6 +182,11 @@ where
fuzzer fuzzer
.scheduler_mut() .scheduler_mut()
.on_replace(state, base_corpus_idx, &prev)?; .on_replace(state, base_corpus_idx, &prev)?;
// perform the post operation for the new testcase, e.g. to update metadata.
// base_post should be updated along with the base (and is no longer None)
base_post
.ok_or_else(|| Error::empty_optional("Failed to get the MutatedTransformPost"))?
.post_exec(state, Some(base_corpus_idx))?;
} }
state.set_max_size(orig_max_size); state.set_max_size(orig_max_size);
@ -184,7 +200,7 @@ where
/// The default corpus entry minimising mutational stage /// The default corpus entry minimising mutational stage
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> { pub struct StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z> {
/// The mutator(s) this stage uses /// The mutator(s) this stage uses
mutator: M, mutator: M,
/// The factory /// The factory
@ -194,22 +210,24 @@ pub struct StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> {
/// The progress helper for this stage, keeping track of resumes after timeouts/crashes /// The progress helper for this stage, keeping track of resumes after timeouts/crashes
restart_helper: ExecutionCountRestartHelper, restart_helper: ExecutionCountRestartHelper,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
phantom: PhantomData<(CS, E, EM, F1, F2, OT, Z)>, phantom: PhantomData<(CS, E, EM, F1, F2, I, IP, OT, Z)>,
} }
impl<CS, E, EM, F1, F2, FF, M, OT, Z> UsesState impl<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z> UsesState
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z>
where where
CS: Scheduler, CS: Scheduler,
M: Mutator<CS::Input, CS::State>, M: Mutator<I, CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>, Z: ExecutionProcessor<OT, State = CS::State>,
CS::State: HasCorpus, CS::State: HasCorpus,
IP: MutatedTransformPost<CS::State> + Clone,
I: MutatedTransform<CS::Input, CS::State, Post = IP> + Clone,
{ {
type State = CS::State; type State = CS::State;
} }
impl<CS, E, EM, F1, F2, FF, M, OT, Z> Stage<E, EM, Z> impl<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z> Stage<E, EM, Z>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z>
where where
CS: Scheduler + RemovableScheduler, CS: Scheduler + RemovableScheduler,
CS::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize + HasCorpus + HasMetadata, CS::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize + HasCorpus + HasMetadata,
@ -219,12 +237,14 @@ where
F1: Feedback<CS::State>, F1: Feedback<CS::State>,
F2: Feedback<CS::State>, F2: Feedback<CS::State>,
FF: FeedbackFactory<F2, CS::State, OT>, FF: FeedbackFactory<F2, CS::State, OT>,
M: Mutator<CS::Input, CS::State>, M: Mutator<I, CS::State>,
OT: ObserversTuple<CS::State>, OT: ObserversTuple<CS::State>,
Z: ExecutionProcessor<OT, State = CS::State> Z: ExecutionProcessor<OT, State = CS::State>
+ ExecutesInput<E, EM> + ExecutesInput<E, EM>
+ HasFeedback<Feedback = F1> + HasFeedback<Feedback = F1>
+ HasScheduler<Scheduler = CS>, + HasScheduler<Scheduler = CS>,
IP: MutatedTransformPost<CS::State> + Clone,
I: MutatedTransform<CS::Input, CS::State, Post = IP> + Clone,
{ {
fn perform( fn perform(
&mut self, &mut self,
@ -250,8 +270,8 @@ where
} }
} }
impl<CS, E, EM, F1, F2, FF, M, OT, Z> FeedbackFactory<F2, Z::State, OT> impl<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z> FeedbackFactory<F2, Z::State, OT>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z>
where where
F2: Feedback<Z::State>, F2: Feedback<Z::State>,
FF: FeedbackFactory<F2, Z::State, OT>, FF: FeedbackFactory<F2, Z::State, OT>,
@ -262,8 +282,8 @@ where
} }
} }
impl<CS, E, EM, F1, F2, FF, M, OT, Z> TMinMutationalStage<CS, E, EM, F1, F2, M, OT, Z> impl<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z> TMinMutationalStage<CS, E, EM, F1, F2, I, IP, M, OT, Z>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z>
where where
CS: Scheduler + RemovableScheduler, CS: Scheduler + RemovableScheduler,
E: HasObservers<Observers = OT, State = CS::State> + Executor<EM, Z>, E: HasObservers<Observers = OT, State = CS::State> + Executor<EM, Z>,
@ -272,13 +292,15 @@ where
F2: Feedback<CS::State>, F2: Feedback<CS::State>,
FF: FeedbackFactory<F2, CS::State, OT>, FF: FeedbackFactory<F2, CS::State, OT>,
<CS::State as UsesInput>::Input: HasLen + Hash, <CS::State as UsesInput>::Input: HasLen + Hash,
M: Mutator<CS::Input, CS::State>, M: Mutator<I, CS::State>,
OT: ObserversTuple<CS::State>, OT: ObserversTuple<CS::State>,
CS::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize + HasMetadata, CS::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize + HasMetadata,
Z: ExecutionProcessor<OT, State = CS::State> Z: ExecutionProcessor<OT, State = CS::State>
+ ExecutesInput<E, EM> + ExecutesInput<E, EM>
+ HasFeedback<Feedback = F1> + HasFeedback<Feedback = F1>
+ HasScheduler<Scheduler = CS>, + HasScheduler<Scheduler = CS>,
IP: MutatedTransformPost<CS::State> + Clone,
I: MutatedTransform<CS::Input, CS::State, Post = IP> + Clone,
{ {
/// The mutator, added to this stage /// The mutator, added to this stage
#[inline] #[inline]
@ -302,12 +324,15 @@ where
} }
} }
impl<CS, E, EM, F1, F2, FF, M, OT, Z> StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> impl<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z>
StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, IP, M, OT, Z>
where where
CS: Scheduler, CS: Scheduler,
M: Mutator<CS::Input, CS::State>, M: Mutator<I, CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>, Z: ExecutionProcessor<OT, State = CS::State>,
CS::State: HasCorpus, CS::State: HasCorpus,
IP: MutatedTransformPost<CS::State> + Clone,
I: MutatedTransform<CS::Input, CS::State, Post = IP> + Clone,
{ {
/// Creates a new minimizing mutational stage that will minimize provided corpus entries /// Creates a new minimizing mutational stage that will minimize provided corpus entries
pub fn new(mutator: M, factory: FF, runs: usize) -> Self { pub fn new(mutator: M, factory: FF, runs: usize) -> Self {