Skip to content

Commit

Permalink
Add macros to libafl_bolts tuples for mapping and merging types (#2788)
Browse files Browse the repository at this point in the history
* Add macros

* Use the macros for havoc_mutations

* Fix docs

* improve merge_tuple_list_type to accept n items
  • Loading branch information
riesentoaster authored Dec 23, 2024
1 parent 03f7fc9 commit 54202c3
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 101 deletions.
105 changes: 16 additions & 89 deletions libafl/src/mutators/havoc_mutations.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//! [`crate::mutators::Mutator`] collection equivalent to AFL++'s havoc mutations
use libafl_bolts::tuples::{Map, Merge};
use tuple_list::{tuple_list, tuple_list_type};
use libafl_bolts::{
map_tuple_list_type, merge_tuple_list_type,
tuples::{tuple_list, tuple_list_type, Map, Merge},
};

use super::{MappingMutator, ToMappingMutator};
use crate::mutators::{
mapping::{OptionalMutator, ToOptionalMutator},
mapping::{ToMappingMutator, ToOptionalMutator},
mutations::{
BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, ByteIncMutator,
ByteInterestingMutator, ByteNegMutator, ByteRandMutator, BytesCopyMutator,
Expand Down Expand Up @@ -56,96 +57,22 @@ pub type MappedHavocCrossoverType<F, O> = tuple_list_type!(
);

/// Tuple type of the mutations that compose the Havoc mutator
pub type HavocMutationsType = tuple_list_type!(
BitFlipMutator,
ByteFlipMutator,
ByteIncMutator,
ByteDecMutator,
ByteNegMutator,
ByteRandMutator,
ByteAddMutator,
WordAddMutator,
DwordAddMutator,
QwordAddMutator,
ByteInterestingMutator,
WordInterestingMutator,
DwordInterestingMutator,
BytesDeleteMutator,
BytesDeleteMutator,
BytesDeleteMutator,
BytesDeleteMutator,
BytesExpandMutator,
BytesInsertMutator,
BytesRandInsertMutator,
BytesSetMutator,
BytesRandSetMutator,
BytesCopyMutator,
BytesInsertCopyMutator,
BytesSwapMutator,
CrossoverInsertMutator,
CrossoverReplaceMutator,
);
pub type HavocMutationsType =
merge_tuple_list_type!(HavocMutationsNoCrossoverType, HavocCrossoverType);

/// Tuple type of the mutations that compose the Havoc mutator for mapped input types
pub type MappedHavocMutationsType<F1, F2, O> = tuple_list_type!(
MappingMutator<BitFlipMutator, F1>,
MappingMutator<ByteFlipMutator, F1>,
MappingMutator<ByteIncMutator, F1>,
MappingMutator<ByteDecMutator, F1>,
MappingMutator<ByteNegMutator, F1>,
MappingMutator<ByteRandMutator, F1>,
MappingMutator<ByteAddMutator, F1>,
MappingMutator<WordAddMutator, F1>,
MappingMutator<DwordAddMutator, F1>,
MappingMutator<QwordAddMutator, F1>,
MappingMutator<ByteInterestingMutator, F1>,
MappingMutator<WordInterestingMutator, F1>,
MappingMutator<DwordInterestingMutator, F1>,
MappingMutator<BytesDeleteMutator, F1>,
MappingMutator<BytesDeleteMutator, F1>,
MappingMutator<BytesDeleteMutator, F1>,
MappingMutator<BytesDeleteMutator, F1>,
MappingMutator<BytesExpandMutator, F1>,
MappingMutator<BytesInsertMutator, F1>,
MappingMutator<BytesRandInsertMutator, F1>,
MappingMutator<BytesSetMutator, F1>,
MappingMutator<BytesRandSetMutator, F1>,
MappingMutator<BytesCopyMutator, F1>,
MappingMutator<BytesInsertCopyMutator, F1>,
MappingMutator<BytesSwapMutator, F1>,
MappingMutator<MappedCrossoverInsertMutator<F2, O>, F1>,
MappingMutator<MappedCrossoverReplaceMutator<F2, O>, F1>,
pub type MappedHavocMutationsType<F1, F2, O> = map_tuple_list_type!(
merge_tuple_list_type!(HavocMutationsNoCrossoverType, MappedHavocCrossoverType<F2,O>),
ToMappingMutator<F1>
);

/// Tuple type of the mutations that compose the Havoc mutator for mapped input types, for optional byte array input parts
pub type OptionMappedHavocMutationsType<F1, F2, O> = tuple_list_type!(
MappingMutator<OptionalMutator<BitFlipMutator>, F1>,
MappingMutator<OptionalMutator<ByteFlipMutator>, F1>,
MappingMutator<OptionalMutator<ByteIncMutator>, F1>,
MappingMutator<OptionalMutator<ByteDecMutator>, F1>,
MappingMutator<OptionalMutator<ByteNegMutator>, F1>,
MappingMutator<OptionalMutator<ByteRandMutator>, F1>,
MappingMutator<OptionalMutator<ByteAddMutator>, F1>,
MappingMutator<OptionalMutator<WordAddMutator>, F1>,
MappingMutator<OptionalMutator<DwordAddMutator>, F1>,
MappingMutator<OptionalMutator<QwordAddMutator>, F1>,
MappingMutator<OptionalMutator<ByteInterestingMutator>, F1>,
MappingMutator<OptionalMutator<WordInterestingMutator>, F1>,
MappingMutator<OptionalMutator<DwordInterestingMutator>, F1>,
MappingMutator<OptionalMutator<BytesDeleteMutator>, F1>,
MappingMutator<OptionalMutator<BytesDeleteMutator>, F1>,
MappingMutator<OptionalMutator<BytesDeleteMutator>, F1>,
MappingMutator<OptionalMutator<BytesDeleteMutator>, F1>,
MappingMutator<OptionalMutator<BytesExpandMutator>, F1>,
MappingMutator<OptionalMutator<BytesInsertMutator>, F1>,
MappingMutator<OptionalMutator<BytesRandInsertMutator>, F1>,
MappingMutator<OptionalMutator<BytesSetMutator>, F1>,
MappingMutator<OptionalMutator<BytesRandSetMutator>, F1>,
MappingMutator<OptionalMutator<BytesCopyMutator>, F1>,
MappingMutator<OptionalMutator<BytesInsertCopyMutator>, F1>,
MappingMutator<OptionalMutator<BytesSwapMutator>, F1>,
MappingMutator<OptionalMutator<MappedCrossoverInsertMutator<F2, O>>, F1>,
MappingMutator<OptionalMutator<MappedCrossoverReplaceMutator<F2, O>>, F1>,
pub type OptionMappedHavocMutationsType<F1, F2, O> = map_tuple_list_type!(
map_tuple_list_type!(
merge_tuple_list_type!(HavocMutationsNoCrossoverType, MappedHavocCrossoverType<F2,O>),
ToOptionalMutator
),
ToMappingMutator<F1>
);

/// Get the mutations that compose the Havoc mutator (only applied to single inputs)
Expand Down
9 changes: 5 additions & 4 deletions libafl_bolts/src/shmem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,12 +624,12 @@ where
/// Is needed on top.
#[cfg(all(unix, feature = "std", not(target_os = "haiku")))]
pub mod unix_shmem {
/// Mmap [`ShMem`] for Unix
#[cfg(not(target_os = "android"))]
pub use default::MmapShMem;
/// Mmap [`ShMemProvider`] for Unix
#[cfg(not(target_os = "android"))]
pub use default::MmapShMemProvider;
/// Mmap [`ShMem`] for Unix
#[cfg(not(target_os = "android"))]
pub use default::{MmapShMem, MAX_MMAP_FILENAME_LEN};

#[cfg(doc)]
use crate::shmem::{ShMem, ShMemProvider};
Expand Down Expand Up @@ -669,7 +669,8 @@ pub mod unix_shmem {
Error,
};

const MAX_MMAP_FILENAME_LEN: usize = 20;
/// The size of the buffer of the filename of mmap mapped memory regions
pub const MAX_MMAP_FILENAME_LEN: usize = 20;

/// Mmap-based The sharedmap impl for unix using [`shm_open`] and [`mmap`].
/// Default on `MacOS` and `iOS`, where we need a central point to unmap
Expand Down
123 changes: 115 additions & 8 deletions libafl_bolts/src/tuples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,81 @@ macro_rules! tuple_for_each_mut {
};
}

/// Maps the types of a mapping with a [`MappingFunctor`]
///
/// ```rust
/// use libafl_bolts::{
/// map_tuple_list_type,
/// tuples::{MappingFunctor, Map, tuple_list, tuple_list_type}
/// };
///
/// struct Wrapper<T>(T);
/// struct MyMapper;
///
/// impl<T> MappingFunctor<T> for MyMapper {
/// type Output = Wrapper<T>;
///
/// fn apply(&mut self, from: T) -> <Self as MappingFunctor<T>>::Output {
/// Wrapper(from)
/// }
/// }
///
/// struct A;
/// struct B;
/// struct C;
///
/// type OrigType = tuple_list_type!(A, B, C);
/// type MappedType = map_tuple_list_type!(OrigType, MyMapper);
/// let orig: OrigType = tuple_list!(A, B, C);
/// let _mapped: MappedType = orig.map(MyMapper);
/// ```
#[macro_export]
macro_rules! map_tuple_list_type {
($Tuple:ty, $Mapper:ty) => {
<$Tuple as $crate::tuples::Map<$Mapper>>::MapResult
};
}

/// Merges the types of two merged [`tuple_list!`]s
///
/// ```rust
/// use libafl_bolts::{merge_tuple_list_type, tuples::{Merge, tuple_list, tuple_list_type}};
///
/// struct A;
/// struct B;
/// struct C;
/// struct D;
/// struct E;
///
/// type Lhs = tuple_list_type!(A, B, C);
/// type Rhs = tuple_list_type!(D, E);
/// type Merged = merge_tuple_list_type!(Lhs, Rhs);
///
/// let lhs: Lhs = tuple_list!(A, B, C);
/// let rhs: Rhs = tuple_list!(D, E);
/// let _merged: Merged = lhs.merge(rhs);
/// ```
#[macro_export]
macro_rules! merge_tuple_list_type {
// Base case: when only two types are provided, apply the Merge trait directly
($Type1:ty) => {
$Type1
};

// Base case: when only two types are provided, apply the Merge trait directly
($Type1:ty, $Type2:ty) => {
<$Type1 as $crate::tuples::Merge<$Type2>>::MergeResult
};

// Recursive case: when more than two types are provided
($Type1:ty, $Type2:ty, $( $rest:ty ),+) => {
merge_tuple_list_type!(
<$Type1 as $crate::tuples::Merge<$Type2>>::MergeResult,
$( $rest ),+
)
};
}

/*
// Define trait and implement it for several primitive types.
Expand Down Expand Up @@ -864,11 +939,13 @@ impl<Head, Tail> PlusOne for (Head, Tail) where

#[cfg(test)]
mod test {
use core::marker::PhantomData;

use tuple_list::{tuple_list, tuple_list_type};

#[cfg(feature = "alloc")]
use crate::ownedref::OwnedMutSlice;
use crate::tuples::{type_eq, Map, MappingFunctor};
use crate::tuples::{type_eq, Map, MappingFunctor, Merge};

#[test]
// for type name tests
Expand Down Expand Up @@ -916,9 +993,11 @@ mod test {
#[test]
fn test_mapper() {
struct W<T>(T);
struct MyMapper;

impl<T> MappingFunctor<T> for MyMapper {
// PhantomData shows how to deal with mappers that have generics
struct ExampleMapper<P>(PhantomData<P>);

impl<T, P> MappingFunctor<T> for ExampleMapper<P> {
type Output = W<T>;

fn apply(&mut self, from: T) -> Self::Output {
Expand All @@ -930,12 +1009,40 @@ mod test {
struct B;
struct C;

let orig = tuple_list!(A, B, C);
let mapped = orig.map(MyMapper);
type OrigType = tuple_list_type!(A, B, C);
type MappedType = map_tuple_list_type!(OrigType, ExampleMapper<usize>);
let orig: OrigType = tuple_list!(A, B, C);
let _mapped: MappedType = orig.map(ExampleMapper(PhantomData::<usize>));
}

// this won't compile if the mapped type is not correct
#[expect(clippy::no_effect_underscore_binding)]
let _type_assert: tuple_list_type!(W<A>, W<B>, W<C>) = mapped;
#[test]
fn test_merge() {
struct A;
struct B;
struct C;
struct D;
struct E;

type Lhs = tuple_list_type!(A, B, C);
type Rhs = tuple_list_type!(D, E);
type Merged = merge_tuple_list_type!(Lhs, Rhs);
type IndividuallyMergedPre = merge_tuple_list_type!(
tuple_list_type!(A),
tuple_list_type!(B),
tuple_list_type!(C),
Rhs
);
type IndividuallyMergedPost =
merge_tuple_list_type!(Lhs, tuple_list_type!(D), tuple_list_type!(E));
type MergedCloned = merge_tuple_list_type!(Merged);

let lhs: Lhs = tuple_list!(A, B, C);
let rhs: Rhs = tuple_list!(D, E);
let merged: Merged = lhs.merge(rhs);
let merged: IndividuallyMergedPre = merged;
let merged: IndividuallyMergedPost = merged;
#[allow(clippy::no_effect_underscore_binding)]
let _merged: MergedCloned = merged;
}

/// Function that tests the tuple macros
Expand Down

0 comments on commit 54202c3

Please sign in to comment.