Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deriving Mapping Mutators #2585

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use libafl::{
executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::MutVecInput,
monitors::SimpleMonitor,
mutators::scheduled::StdScheduledMutator,
observers::StdMapObserver,
Expand Down Expand Up @@ -138,14 +139,17 @@ pub fn main() {
#[cfg(feature = "simple_interface")]
let (mapped_mutators, optional_mapped_mutators) = {
// Creating mutators that will operate on input.byte_array
let mapped_mutators =
mapped_havoc_mutations(CustomInput::byte_array_mut, CustomInput::byte_array);
let mapped_mutators = mapped_havoc_mutations::<CustomInput, MutVecInput<'_>, &[u8]>(
CustomInput::byte_array_mut,
CustomInput::byte_array,
);

// Creating mutators that will operate on input.optional_byte_array
let optional_mapped_mutators = optional_mapped_havoc_mutations(
CustomInput::optional_byte_array_mut,
CustomInput::optional_byte_array,
);
let optional_mapped_mutators =
optional_mapped_havoc_mutations::<_, Option<MutVecInput<'_>>, Option<&[u8]>>(
CustomInput::optional_byte_array_mut,
CustomInput::optional_byte_array,
);
(mapped_mutators, optional_mapped_mutators)
};

Expand Down
309 changes: 178 additions & 131 deletions libafl/src/mutators/havoc_mutations.rs

Large diffs are not rendered by default.

63 changes: 36 additions & 27 deletions libafl/src/mutators/mapping.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Allowing mixing and matching between [`Mutator`] and [`crate::inputs::Input`] types.
use alloc::borrow::Cow;
use core::marker::PhantomData;

use libafl_bolts::{tuples::MappingFunctor, Named};

Expand Down Expand Up @@ -58,6 +57,7 @@ pub struct FunctionMappingMutator<M, F> {

impl<M, F> FunctionMappingMutator<M, F> {
/// Creates a new [`FunctionMappingMutator`]
#[must_use]
pub fn new(mapper: F, inner: M) -> Self
where
M: Named,
Expand Down Expand Up @@ -137,6 +137,7 @@ pub struct ToFunctionMappingMutatorMapper<F> {

impl<F> ToFunctionMappingMutatorMapper<F> {
/// Creates a new [`ToFunctionMappingMutatorMapper`]
#[must_use]
pub fn new(mapper: F) -> Self {
Self { mapper }
}
Expand Down Expand Up @@ -188,16 +189,22 @@ where
/// assert_eq!(input, (vec![2],));
/// ```
#[derive(Debug)]
pub struct MappedInputFunctionMappingMutator<M, F, II> {
mapper: F,
pub struct MappedInputFunctionMappingMutator<M, IO, II>
where
II: MappedInput,
{
mapper: for<'a> fn(&'a mut IO) -> II::Type<'a>,
inner: M,
name: Cow<'static, str>,
phantom: PhantomData<II>,
}

impl<M, F, II> MappedInputFunctionMappingMutator<M, F, II> {
impl<M, IO, II> MappedInputFunctionMappingMutator<M, IO, II>
where
II: MappedInput,
{
/// Creates a new [`MappedInputFunctionMappingMutator`]
pub fn new(mapper: F, inner: M) -> Self
#[must_use]
pub fn new(mapper: for<'a> fn(&'a mut IO) -> II::Type<'a>, inner: M) -> Self
where
M: Named,
{
Expand All @@ -210,24 +217,25 @@ impl<M, F, II> MappedInputFunctionMappingMutator<M, F, II> {
mapper,
inner,
name,
phantom: PhantomData,
}
}
}

impl<M, S, F, IO, II> Mutator<IO, S> for MappedInputFunctionMappingMutator<M, F, II>
impl<M, S, IO, II> Mutator<IO, S> for MappedInputFunctionMappingMutator<M, IO, II>
where
for<'a> M: Mutator<II::Type<'a>, S>,
for<'a> II: MappedInput + 'a,
for<'a> F: FnMut(&'a mut IO) -> II::Type<'a>,
II: MappedInput,
{
fn mutate(&mut self, state: &mut S, input: &mut IO) -> Result<MutationResult, Error> {
let mapped = &mut (self.mapper)(input);
self.inner.mutate(state, mapped)
}
}

impl<M, F, II> Named for MappedInputFunctionMappingMutator<M, F, II> {
impl<M, IO, II> Named for MappedInputFunctionMappingMutator<M, IO, II>
where
II: MappedInput,
{
fn name(&self) -> &Cow<'static, str> {
&self.name
}
Expand Down Expand Up @@ -271,33 +279,33 @@ impl<M, F, II> Named for MappedInputFunctionMappingMutator<M, F, II> {
/// assert_eq!(input, (vec![2],));
/// ```
#[derive(Debug)]
pub struct ToMappedInputFunctionMappingMutatorMapper<F, II> {
mapper: F,
phantom: PhantomData<II>,
pub struct ToMappedInputFunctionMappingMutatorMapper<IO, II>
where
II: MappedInput,
{
mapper: for<'a> fn(&'a mut IO) -> II::Type<'a>,
}

impl<F, II> ToMappedInputFunctionMappingMutatorMapper<F, II> {
impl<IO, II> ToMappedInputFunctionMappingMutatorMapper<IO, II>
where
II: MappedInput,
{
/// Creates a new [`ToMappedInputFunctionMappingMutatorMapper`]
pub fn new<IO>(mapper: F) -> Self
where
F: FnMut(IO) -> II,
{
Self {
mapper,
phantom: PhantomData,
}
#[must_use]
pub fn new(mapper: for<'a> fn(&'a mut IO) -> II::Type<'a>) -> Self {
Self { mapper }
}
}

impl<M, F, II> MappingFunctor<M> for ToMappedInputFunctionMappingMutatorMapper<F, II>
impl<M, II, IO> MappingFunctor<M> for ToMappedInputFunctionMappingMutatorMapper<IO, II>
where
F: Clone,
M: Named,
II: MappedInput,
{
type Output = MappedInputFunctionMappingMutator<M, F, II>;
type Output = MappedInputFunctionMappingMutator<M, IO, II>;

fn apply(&mut self, from: M) -> Self::Output {
MappedInputFunctionMappingMutator::new(self.mapper.clone(), from)
MappedInputFunctionMappingMutator::new(self.mapper, from)
}
}

Expand Down Expand Up @@ -339,6 +347,7 @@ pub struct OptionMappingMutator<M> {

impl<M> OptionMappingMutator<M> {
/// Creates a new [`OptionMappingMutator`]
#[must_use]
pub fn new(inner: M) -> Self
where
M: Named,
Expand Down
9 changes: 9 additions & 0 deletions libafl/src/mutators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,12 @@ impl Named for NopMutator {
&Cow::Borrowed("NopMutator")
}
}

/// Extensions of [`crate::inputs::Input`]s that have default mutators
pub trait DefaultMutators {
/// The resulting mutator list type
type Type;
/// Get the default mutators for this type
#[must_use]
fn default_mutators() -> Self::Type;
}
76 changes: 46 additions & 30 deletions libafl/src/mutators/mutations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use alloc::{
};
use core::{
cmp::min,
marker::PhantomData,
mem::size_of,
num::{NonZero, NonZeroUsize},
ops::Range,
Expand Down Expand Up @@ -1279,9 +1278,12 @@ impl CrossoverReplaceMutator {
}
}

trait IntoOptionBytes {
/// Anything that can be mapped to `Option<&[u8]>`, ensuring that the lifetime of the input and output are the same
pub trait IntoOptionBytes {
/// Should be Self, ensure that the lifetimes match
type Type<'b>;

/// Run the mapping
fn into_option_bytes<'a>(self) -> Option<&'a [u8]>
where
Self: 'a;
Expand Down Expand Up @@ -1311,28 +1313,32 @@ impl IntoOptionBytes for Option<&[u8]> {

/// Crossover insert mutation for inputs mapped to a bytes vector
#[derive(Debug)]
pub struct MappedCrossoverInsertMutator<F, O> {
input_mapper: F,
phantom: PhantomData<O>,
pub struct MappedCrossoverInsertMutator<IO, II>
where
II: IntoOptionBytes,
{
input_mapper: for<'a> fn(&'a IO) -> II::Type<'a>,
}

impl<F, O> MappedCrossoverInsertMutator<F, O> {
impl<IO, II> MappedCrossoverInsertMutator<IO, II>
where
II: IntoOptionBytes,
{
/// Creates a new [`MappedCrossoverInsertMutator`]
pub fn new(input_mapper: F) -> Self {
Self {
input_mapper,
phantom: PhantomData,
}
#[must_use]
pub fn new(input_mapper: fn(&IO) -> II::Type<'_>) -> Self {
Self { input_mapper }
}
}

impl<S, F, I, O> Mutator<I, S> for MappedCrossoverInsertMutator<F, O>
impl<S, I, IO, II> Mutator<I, S> for MappedCrossoverInsertMutator<IO, II>
where
II: IntoOptionBytes,
S: HasCorpus + HasMaxSize + HasRand,
S::Corpus: Corpus<Input = IO>,
I: HasMutatorBytes,
for<'a> O: IntoOptionBytes,
for<'a> O::Type<'a>: IntoOptionBytes,
for<'a> F: Fn(&'a <S::Corpus as Corpus>::Input) -> <O as IntoOptionBytes>::Type<'a>,
for<'a> II: IntoOptionBytes,
for<'a> II::Type<'a>: IntoOptionBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let size = input.bytes().len();
Expand Down Expand Up @@ -1392,7 +1398,10 @@ where
}
}

impl<F, O> Named for MappedCrossoverInsertMutator<F, O> {
impl<IO, II> Named for MappedCrossoverInsertMutator<IO, II>
where
II: IntoOptionBytes,
{
fn name(&self) -> &Cow<'static, str> {
static NAME: Cow<'static, str> = Cow::Borrowed("MappedCrossoverInsertMutator");
&NAME
Expand All @@ -1401,28 +1410,32 @@ impl<F, O> Named for MappedCrossoverInsertMutator<F, O> {

/// Crossover replace mutation for inputs mapped to a bytes vector
#[derive(Debug)]
pub struct MappedCrossoverReplaceMutator<F, O> {
input_mapper: F,
phantom: PhantomData<O>,
pub struct MappedCrossoverReplaceMutator<IO, II>
where
II: IntoOptionBytes,
{
input_mapper: for<'a> fn(&'a IO) -> II::Type<'a>,
}

impl<F, O> MappedCrossoverReplaceMutator<F, O> {
impl<IO, II> MappedCrossoverReplaceMutator<IO, II>
where
II: IntoOptionBytes,
{
/// Creates a new [`MappedCrossoverReplaceMutator`]
pub fn new(input_mapper: F) -> Self {
Self {
input_mapper,
phantom: PhantomData,
}
#[must_use]
pub fn new(input_mapper: fn(&IO) -> II::Type<'_>) -> Self {
Self { input_mapper }
}
}

impl<S, F, I, O> Mutator<I, S> for MappedCrossoverReplaceMutator<F, O>
impl<S, I, IO, II> Mutator<I, S> for MappedCrossoverReplaceMutator<IO, II>
where
II: IntoOptionBytes,
S: HasCorpus + HasMaxSize + HasRand,
S::Corpus: Corpus<Input = IO>,
I: HasMutatorBytes,
O: IntoOptionBytes,
for<'a> O::Type<'a>: IntoOptionBytes,
for<'a> F: Fn(&'a <S::Corpus as Corpus>::Input) -> <O as IntoOptionBytes>::Type<'a>,
for<'a> II: IntoOptionBytes,
for<'a> II::Type<'a>: IntoOptionBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let size = input.bytes().len();
Expand Down Expand Up @@ -1479,7 +1492,10 @@ where
}
}

impl<F, O> Named for MappedCrossoverReplaceMutator<F, O> {
impl<IO, II> Named for MappedCrossoverReplaceMutator<IO, II>
where
II: IntoOptionBytes,
{
fn name(&self) -> &Cow<'static, str> {
static NAME: Cow<'static, str> = Cow::Borrowed("MappedCrossoverReplaceMutator");
&NAME
Expand Down
31 changes: 31 additions & 0 deletions libafl_bolts/src/tuples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,37 @@ impl<Head, Tail> PlusOne for (Head, Tail) where

*/

/// Merge two lists of types created by the [`tuple_list_type`] macro
///
/// ```rust
/// use libafl_bolts::{merge_tuple_list_type, tuples::{Merge, tuple_list, tuple_list_type}};
/// #[derive(PartialEq, Debug)]
/// struct T1;
/// #[derive(PartialEq, Debug)]
/// struct T2;
/// #[derive(PartialEq, Debug)]
/// struct T3;
/// #[derive(PartialEq, Debug)]
/// struct T4;
/// #[derive(PartialEq, Debug)]
/// struct T5;
///
/// type List1 = tuple_list_type!(T1, T2);
/// let list1: List1 = tuple_list!(T1, T2);
/// type List2 = tuple_list_type!(T3, T4, T5);
/// let list2: List2 = tuple_list!(T3, T4, T5);
/// type Combined = merge_tuple_list_type!(List1, List2);
/// let combined: Combined = list1.merge(list2);
/// let manual: Combined = tuple_list!(T1, T2, T3, T4, T5);
/// assert_eq!(combined, manual);
/// ```
#[macro_export]
macro_rules! merge_tuple_list_type {
($Type1:ty, $Type2:ty) => {
<$Type1 as $crate::tuples::Merge<$Type2>>::MergeResult
};
}

#[cfg(test)]
mod test {
use tuple_list::{tuple_list, tuple_list_type};
Expand Down
1 change: 1 addition & 0 deletions libafl_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ proc-macro = true
syn = { version = "2.0.77", features = ["full", "extra-traits"] }
quote = "1.0.37"
proc-macro2 = "1.0.86"
proc-macro-crate = "3.2"

[lints]
workspace = true
Loading
Loading