Named Mutators and MultiMutator API change (#1387)

* Mutators need names (alternative to #1379)

* Signature of MultiMutator shouldn't be the same as the normal mutator

* Named for python, remove mutator for multi_mutator

* fmt

* clippy edition warning

* clippy

* mac_count doc fix, return cleanup
This commit is contained in:
Dominik Maier 2023-08-01 16:58:40 +02:00 committed by GitHub
parent 90e9f3c786
commit 006dcac00c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 166 additions and 67 deletions

View File

@ -1,4 +1,5 @@
[workspace]
resolver = "2"
members = [
"libafl",
"libafl_derive",

View File

@ -26,7 +26,7 @@ use libafl::{
monitors::SimpleMonitor,
mutators::{
scheduled::havoc_mutations, token_mutations::AFLppRedQueen, tokens_mutations,
MutationResult, StdMOptMutator, Tokens,
StdMOptMutator, Tokens,
},
observers::{
AFLppCmpMap, AFLppForkserverCmpObserver, HitcountsMapObserver, StdMapObserver, TimeObserver,
@ -35,7 +35,7 @@ use libafl::{
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
},
stages::{
calibrate::CalibrationStage, mutational::MultipleMutationalStage,
calibrate::CalibrationStage, mutational::MultiMutationalStage,
power::StdPowerMutationalStage, tracing::AFLppCmplogTracingStage, ColorizationStage,
IfStage,
},
@ -368,7 +368,7 @@ fn fuzz(
let tracing = AFLppCmplogTracingStage::with_cmplog_observer_name(cmplog_executor, "cmplog");
// Setup a randomic Input2State stage
let rq = MultipleMutationalStage::new(AFLppRedQueen::with_cmplog_options(true, true));
let rq = MultiMutationalStage::new(AFLppRedQueen::with_cmplog_options(true, true));
let cb = |_fuzzer: &mut _,
_executor: &mut _,

View File

@ -80,7 +80,7 @@ pub enum MutationResult {
/// A mutator takes input, and mutates it.
/// Simple as that.
pub trait Mutator<I, S> {
pub trait Mutator<I, S>: Named {
/// Mutate a given input
fn mutate(
&mut self,
@ -90,6 +90,7 @@ pub trait Mutator<I, S> {
) -> Result<MutationResult, Error>;
/// Post-process given the outcome of the execution
#[inline]
fn post_exec(
&mut self,
_state: &mut S,
@ -102,18 +103,20 @@ pub trait Mutator<I, S> {
/// A mutator that takes input, and returns a vector of mutated inputs.
/// Simple as that.
pub trait MultipleMutator<I, S> {
/// Mutate a given input
fn mutate(
pub trait MultiMutator<I, S>: Named {
/// Mutate a given input up to `max_count` times,
/// or as many times as appropriate, if no `max_count` is given
fn multi_mutate(
&mut self,
state: &mut S,
input: &I,
vec: &mut Vec<I>,
stage_idx: i32,
) -> Result<MutationResult, Error>;
max_count: Option<usize>,
) -> Result<Vec<I>, Error>;
/// Post-process given the outcome of the execution
fn post_exec(
#[inline]
fn multi_post_exec(
&mut self,
_state: &mut S,
_stage_idx: i32,
@ -158,9 +161,13 @@ pub trait MutatorsTuple<I, S>: HasConstLen {
stage_idx: i32,
corpus_idx: Option<CorpusId>,
) -> Result<(), Error>;
/// Gets all names of the wrapped [`Mutator`]`s`.
fn names(&self) -> Vec<&str>;
}
impl<I, S> MutatorsTuple<I, S> for () {
#[inline]
fn mutate_all(
&mut self,
_state: &mut S,
@ -170,6 +177,7 @@ impl<I, S> MutatorsTuple<I, S> for () {
Ok(MutationResult::Skipped)
}
#[inline]
fn post_exec_all(
&mut self,
_state: &mut S,
@ -179,6 +187,7 @@ impl<I, S> MutatorsTuple<I, S> for () {
Ok(())
}
#[inline]
fn get_and_mutate(
&mut self,
_index: MutationId,
@ -189,6 +198,7 @@ impl<I, S> MutatorsTuple<I, S> for () {
Ok(MutationResult::Skipped)
}
#[inline]
fn get_and_post_exec(
&mut self,
_index: usize,
@ -198,11 +208,16 @@ impl<I, S> MutatorsTuple<I, S> for () {
) -> Result<(), Error> {
Ok(())
}
#[inline]
fn names(&self) -> Vec<&str> {
Vec::new()
}
}
impl<Head, Tail, I, S> MutatorsTuple<I, S> for (Head, Tail)
where
Head: Mutator<I, S> + Named,
Head: Mutator<I, S>,
Tail: MutatorsTuple<I, S>,
{
fn mutate_all(
@ -258,16 +273,25 @@ where
.get_and_post_exec(index - 1, state, stage_idx, corpus_idx)
}
}
fn names(&self) -> Vec<&str> {
let mut ret = self.1.names();
ret.insert(0, self.0.name());
ret
}
}
/// `Mutator` Python bindings
#[cfg(feature = "python")]
#[allow(missing_docs)]
pub mod pybind {
use pyo3::prelude::*;
use core::ffi::CStr;
use pyo3::{prelude::*, AsPyPointer};
use super::{MutationResult, Mutator};
use crate::{
bolts::tuples::Named,
corpus::CorpusId,
inputs::{BytesInput, HasBytesVec},
mutators::scheduled::pybind::PythonStdHavocMutator,
@ -287,6 +311,14 @@ pub mod pybind {
}
}
impl Named for PyObjectMutator {
fn name(&self) -> &str {
unsafe { CStr::from_ptr((*(*self.inner.as_ptr()).ob_type).tp_name) }
.to_str()
.unwrap()
}
}
impl Mutator<BytesInput, PythonStdState> for PyObjectMutator {
fn mutate(
&mut self,
@ -386,6 +418,15 @@ pub mod pybind {
}
}
impl Named for PythonMutator {
fn name(&self) -> &str {
match &self.wrapper {
PythonMutatorWrapper::Python(pyo) => pyo.name(),
PythonMutatorWrapper::StdHavoc(_) => "StdHavocPythonMutator",
}
}
}
impl Mutator<BytesInput, PythonStdState> for PythonMutator {
fn mutate(
&mut self,

View File

@ -1,5 +1,8 @@
//! The `MOpt` mutator scheduler, see <https://github.com/puppet-meteor/MOpt-AFL> and <https://www.usenix.org/conference/usenixsecurity19/presentation/lyu>
use alloc::{string::ToString, vec::Vec};
use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::{
fmt::{self, Debug},
marker::PhantomData,
@ -9,7 +12,10 @@ use serde::{Deserialize, Serialize};
use super::MutationId;
use crate::{
bolts::rands::{Rand, StdRand},
bolts::{
rands::{Rand, StdRand},
tuples::Named,
},
corpus::{Corpus, CorpusId},
mutators::{ComposedByMutations, MutationResult, Mutator, MutatorsTuple, ScheduledMutator},
state::{HasCorpus, HasMetadata, HasRand, HasSolutions},
@ -365,6 +371,7 @@ where
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
name: String,
mode: MOptMode,
finds_before: usize,
mutations: MT,
@ -539,6 +546,7 @@ where
state.add_metadata::<MOpt>(MOpt::new(mutations.len(), swarm_num, rand_seed)?);
}
Ok(Self {
name: format!("StdMOptMutator[{}]", mutations.names().join(",")),
mode: MOptMode::Pilotfuzzing,
finds_before: 0,
mutations,
@ -632,6 +640,16 @@ where
}
}
impl<I, MT, S> Named for StdMOptMutator<I, MT, S>
where
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
fn name(&self) -> &str {
&self.name
}
}
impl<I, MT, S> ScheduledMutator<I, MT, S> for StdMOptMutator<I, MT, S>
where
MT: MutatorsTuple<I, S>,

View File

@ -13,7 +13,7 @@ pub use crate::mutators::{mutations::*, token_mutations::*};
use crate::{
bolts::{
rands::Rand,
tuples::{tuple_list, tuple_list_type, NamedTuple},
tuples::{tuple_list, tuple_list_type, Named, NamedTuple},
AsMutSlice, AsSlice,
},
corpus::{Corpus, CorpusId},
@ -106,6 +106,7 @@ where
MT: MutatorsTuple<I, S>,
S: HasRand,
{
name: String,
mutations: MT,
max_stack_pow: u64,
phantom: PhantomData<(I, S)>,
@ -126,6 +127,16 @@ where
}
}
impl<I, MT, S> Named for StdScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<I, S>,
S: HasRand,
{
fn name(&self) -> &str {
&self.name
}
}
impl<I, MT, S> Mutator<I, S> for StdScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<I, S>,
@ -185,6 +196,7 @@ where
/// Create a new [`StdScheduledMutator`] instance specifying mutations
pub fn new(mutations: MT) -> Self {
StdScheduledMutator {
name: format!("StdScheduledMutator[{}]", mutations.names().join(", ")),
mutations,
max_stack_pow: 7,
phantom: PhantomData,
@ -194,6 +206,7 @@ where
/// Create a new [`StdScheduledMutator`] instance specifying mutations and the maximun number of iterations
pub fn with_max_stack_pow(mutations: MT, max_stack_pow: u64) -> Self {
StdScheduledMutator {
name: format!("StdScheduledMutator[{}]", mutations.names().join(", ")),
mutations,
max_stack_pow,
phantom: PhantomData,
@ -279,6 +292,7 @@ where
S: HasRand + HasCorpus,
SM: ScheduledMutator<I, MT, S>,
{
name: String,
scheduled: SM,
mutation_log: Vec<MutationId>,
phantom: PhantomData<(I, MT, S)>,
@ -300,6 +314,17 @@ where
}
}
impl<I, MT, S, SM> Named for LoggerScheduledMutator<I, MT, S, SM>
where
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus,
SM: ScheduledMutator<I, MT, S>,
{
fn name(&self) -> &str {
&self.name
}
}
impl<I, MT, S, SM> Mutator<I, S> for LoggerScheduledMutator<I, MT, S, SM>
where
MT: MutatorsTuple<I, S> + NamedTuple,
@ -403,9 +428,11 @@ where
S: HasRand + HasCorpus,
SM: ScheduledMutator<I, MT, S>,
{
/// Create a new [`StdScheduledMutator`] instance without mutations and corpus
/// Create a new [`LoggerScheduledMutator`] instance without mutations and corpus
/// This mutator logs all mutators.
pub fn new(scheduled: SM) -> Self {
Self {
name: format!("LoggerScheduledMutator[{}]", scheduled.name()),
scheduled,
mutation_log: vec![],
phantom: PhantomData,

View File

@ -25,7 +25,7 @@ use crate::{
bolts::{rands::Rand, AsSlice},
inputs::{HasBytesVec, UsesInput},
mutators::{
buffer_self_copy, mutations::buffer_copy, MultipleMutator, MutationResult, Mutator, Named,
buffer_self_copy, mutations::buffer_copy, MultiMutator, MutationResult, Mutator, Named,
},
observers::cmp::{AFLppCmpValuesMetadata, CmpValues, CmpValuesMetadata},
stages::TaintMetadata,
@ -1090,37 +1090,37 @@ impl AFLppRedQueen {
}
}
impl<I, S> MultipleMutator<I, S> for AFLppRedQueen
impl<I, S> MultiMutator<I, S> for AFLppRedQueen
where
S: UsesInput + HasMetadata + HasRand + HasMaxSize + HasCorpus,
I: HasBytesVec + From<Vec<u8>>,
{
#[allow(clippy::needless_range_loop)]
#[allow(clippy::too_many_lines)]
fn mutate(
fn multi_mutate(
&mut self,
state: &mut S,
input: &I,
ret: &mut Vec<I>,
stage_idx: i32,
) -> Result<MutationResult, Error> {
max_count: Option<usize>,
) -> Result<Vec<I>, Error> {
// TODO
// handle 128-bits logs
let size = input.bytes().len();
if size == 0 {
return Ok(MutationResult::Skipped);
return Ok(vec![]);
}
let (cmp_len, cmp_meta, taint_meta) = {
let cmp_meta = state.metadata_map().get::<AFLppCmpValuesMetadata>();
let taint_meta = state.metadata_map().get::<TaintMetadata>();
if cmp_meta.is_none() || taint_meta.is_none() {
return Ok(MutationResult::Skipped);
return Ok(vec![]);
}
let cmp_len = cmp_meta.unwrap().headers().len();
if cmp_len == 0 {
return Ok(MutationResult::Skipped);
return Ok(vec![]);
}
(cmp_len, cmp_meta.unwrap(), taint_meta.unwrap())
};
@ -1135,7 +1135,7 @@ where
let orig_bytes = input.bytes();
let taint = taint_meta.ranges();
let mut vec = vec![];
let mut ret = max_count.map_or_else(Vec::new, Vec::with_capacity);
let mut gathered_tokens = Tokens::new();
// println!("orig: {:#?} new: {:#?}", orig_cmpvals, new_cmpvals);
@ -1172,6 +1172,13 @@ where
}
for cmp_buf_idx in 0..input_len {
if let Some(max_count) = max_count {
if ret.len() >= max_count {
// TODO: does this bias towards earlier mutations?
break;
}
}
let taint_len = match taint.get(taint_idx) {
Some(t) => {
if cmp_buf_idx < t.start {
@ -1213,7 +1220,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
// Swapped
@ -1229,7 +1236,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
}
@ -1247,7 +1254,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
// Swapped
@ -1263,7 +1270,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
}
*/
@ -1301,7 +1308,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
// Swapped
@ -1318,7 +1325,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
}
@ -1336,7 +1343,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
// Swapped
@ -1352,7 +1359,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
}
@ -1390,7 +1397,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
// swapped
@ -1407,7 +1414,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
}
@ -1425,7 +1432,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
// Swapped
@ -1442,7 +1449,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
}
@ -1483,7 +1490,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
// Swapped
@ -1500,7 +1507,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
}
@ -1518,7 +1525,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
// Swapped
@ -1535,7 +1542,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
}
@ -1574,7 +1581,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
// Compare v1 against v0
@ -1589,7 +1596,7 @@ where
taint_len,
input_len,
hshape,
&mut vec,
&mut ret,
);
let is_ascii_or_utf8 = self.text_type.is_ascii_or_utf8();
@ -1663,16 +1670,10 @@ where
}
}
let mut mutated = false;
for item in vec {
ret.push(I::from(item));
mutated = true;
}
if mutated {
Ok(MutationResult::Mutated)
if let Some(max_count) = max_count {
Ok(ret.into_iter().take(max_count).map(I::from).collect())
} else {
Ok(MutationResult::Skipped)
Ok(ret.into_iter().map(I::from).collect())
}
}
}

View File

@ -2,7 +2,7 @@
//! Instead of a random mutator for a random amount of iterations, we can run
//! a specific mutator for a specified amount of iterations
use alloc::vec::Vec;
use alloc::{string::String, vec::Vec};
use core::{
fmt::{self, Debug},
marker::PhantomData,
@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
pub use crate::mutators::{mutations::*, token_mutations::*};
use crate::{
bolts::{calculate_cumulative_sum_in_place, rands::Rand},
bolts::{calculate_cumulative_sum_in_place, rands::Rand, tuples::Named},
impl_serdeany,
mutators::{
ComposedByMutations, MutationId, MutationResult, Mutator, MutatorsTuple, ScheduledMutator,
@ -80,6 +80,7 @@ where
MT: MutatorsTuple<I, S>,
S: HasRand,
{
name: String,
mutations: MT,
max_stack_pow: u64,
phantom: PhantomData<(I, S)>,
@ -134,6 +135,16 @@ where
}
}
impl<I, MT, S> Named for TuneableScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<I, S>,
S: HasRand,
{
fn name(&self) -> &str {
&self.name
}
}
impl<I, MT, S> ScheduledMutator<I, MT, S> for TuneableScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<I, S>,
@ -215,6 +226,7 @@ where
state.add_metadata(TuneableScheduledMutatorMetadata::default());
}
TuneableScheduledMutator {
name: format!("TuneableMutator[{}]", mutations.names().join(", ")),
mutations,
max_stack_pow: 7,
phantom: PhantomData,

View File

@ -11,7 +11,7 @@ use crate::{
fuzzer::Evaluator,
inputs::Input,
mark_feature_time,
mutators::{MultipleMutator, MutationResult, Mutator},
mutators::{MultiMutator, MutationResult, Mutator},
stages::Stage,
start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState},
@ -260,28 +260,28 @@ where
/// The default mutational stage
#[derive(Clone, Debug)]
pub struct MultipleMutationalStage<E, EM, I, M, Z> {
pub struct MultiMutationalStage<E, EM, I, M, Z> {
mutator: M,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, I, Z)>,
}
impl<E, EM, I, M, Z> UsesState for MultipleMutationalStage<E, EM, I, M, Z>
impl<E, EM, I, M, Z> UsesState for MultiMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: MultipleMutator<I, Z::State>,
M: MultiMutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
type State = Z::State;
}
impl<E, EM, I, M, Z> Stage<E, EM, Z> for MultipleMutationalStage<E, EM, I, M, Z>
impl<E, EM, I, M, Z> Stage<E, EM, Z> for MultiMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: MultipleMutator<I, Z::State>,
M: MultiMutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
I: MutatedTransform<Self::Input, Self::State> + Clone,
@ -303,14 +303,13 @@ where
};
drop(testcase);
let mut generated = vec![];
let _ = self.mutator.mutate(state, &input, &mut generated, 0)?;
let generated = self.mutator.multi_mutate(state, &input, 0, None)?;
// println!("Generated {}", generated.len());
for (i, new_input) in generated.into_iter().enumerate() {
// Time is measured directly the `evaluate_input` function
let (untransformed, post) = new_input.try_transform_into(state)?;
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, untransformed)?;
self.mutator.post_exec(state, i as i32, corpus_idx)?;
self.mutator.multi_post_exec(state, i as i32, corpus_idx)?;
post.post_exec(state, i as i32, corpus_idx)?;
}
// println!("Found {}", found);
@ -319,11 +318,11 @@ where
}
}
impl<E, EM, M, Z> MultipleMutationalStage<E, EM, Z::Input, M, Z>
impl<E, EM, M, Z> MultiMutationalStage<E, EM, Z::Input, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: MultipleMutator<Z::Input, Z::State>,
M: MultiMutator<Z::Input, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
@ -333,11 +332,11 @@ where
}
}
impl<E, EM, I, M, Z> MultipleMutationalStage<E, EM, I, M, Z>
impl<E, EM, I, M, Z> MultiMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: MultipleMutator<I, Z::State>,
M: MultiMutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{