diff --git a/crates/libafl/src/mutators/havoc_mutations.rs b/crates/libafl/src/mutators/havoc_mutations.rs index f03b5e903b..3cfba01582 100644 --- a/crates/libafl/src/mutators/havoc_mutations.rs +++ b/crates/libafl/src/mutators/havoc_mutations.rs @@ -6,7 +6,7 @@ use libafl_bolts::{ }; use crate::mutators::{ - mapping::{ToMappingMutator, ToOptionalMutator}, + mapping::{ToMappingMutator, ToOptionalMutator, ToStateAwareMappingMutator}, mutations::{ BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, ByteIncMutator, ByteInterestingMutator, ByteNegMutator, ByteRandMutator, BytesCopyMutator, @@ -66,6 +66,12 @@ pub type MappedHavocMutationsType = map_tuple_list_type!( ToMappingMutator ); +/// Tuple type of the mutations that compose the Havoc mutator for state-aware-mapped input types +pub type StateAwareMappedHavocMutationsType = map_tuple_list_type!( + merge_tuple_list_type!(HavocMutationsNoCrossoverType, MappedHavocCrossoverType), + ToStateAwareMappingMutator +); + /// Tuple type of the mutations that compose the Havoc mutator for mapped input types, for optional byte array input parts pub type OptionMappedHavocMutationsType = map_tuple_list_type!( map_tuple_list_type!( @@ -165,6 +171,27 @@ where .map(ToMappingMutator::new(current_input_mapper)) } +/// Get the mutations that compose the Havoc mutator for state-aware-mapped input types +/// +/// Check the example fuzzer for details on how to use this. +/// Check the docs of [`crate::mutators::mapping::StateAwareMappingMutator`] for how mapping works internally. +#[must_use] +pub fn state_aware_mapped_havoc_mutations<'a, F1, F2, IO1, IO2, II, O, S>( + current_input_mapper: F1, + input_from_corpus_mapper: F2, +) -> StateAwareMappedHavocMutationsType +where + F1: Clone + FnMut(&'a mut IO1, &'a mut S) -> (Option<&'a mut II>, &'a mut S), + F2: Clone + Fn(&IO2) -> &O, + II: 'a, + IO1: 'a, + S: 'a, +{ + havoc_mutations_no_crossover() + .merge(havoc_crossover_with_corpus_mapper(input_from_corpus_mapper)) + .map(ToStateAwareMappingMutator::new(current_input_mapper)) +} + /// Get the mutations that compose the Havoc mutator for mapped input types, for optional input parts /// /// Check the example fuzzer for details on how to use this. diff --git a/crates/libafl/src/mutators/mapping.rs b/crates/libafl/src/mutators/mapping.rs index bdc064348d..ead08d0a49 100644 --- a/crates/libafl/src/mutators/mapping.rs +++ b/crates/libafl/src/mutators/mapping.rs @@ -5,6 +5,7 @@ use libafl_bolts::{Named, tuples::MappingFunctor}; use crate::{ Error, + corpus::CorpusId, mutators::{MutationResult, Mutator}, }; @@ -12,6 +13,8 @@ use crate::{ /// /// Allows using [`Mutator`]s for a certain type on (parts of) other input types that can be mapped to this type. /// +/// For a more flexible alternative, which allows access to `state`, see [`StateAwareMappingMutator`]. +/// /// # Example #[cfg_attr(feature = "std", doc = " ```")] #[cfg_attr(not(feature = "std"), doc = " ```ignore")] @@ -74,11 +77,7 @@ where self.inner.mutate(state, (self.mapper)(input)) } #[inline] - fn post_exec( - &mut self, - state: &mut S, - new_corpus_id: Option, - ) -> Result<(), Error> { + fn post_exec(&mut self, state: &mut S, new_corpus_id: Option) -> Result<(), Error> { self.inner.post_exec(state, new_corpus_id) } } @@ -89,7 +88,7 @@ impl Named for MappingMutator { } } -/// Mapper to use to map a [`tuple_list`] of [`Mutator`]s using [`ToMappingMutator`]s. +/// Mapper to use to map a [`tuple_list`] of [`Mutator`]s using [`MappingMutator`]s. /// /// See the explanation of [`MappingMutator`] for details. /// @@ -211,11 +210,7 @@ where } } #[inline] - fn post_exec( - &mut self, - state: &mut S, - new_corpus_id: Option, - ) -> Result<(), Error> { + fn post_exec(&mut self, state: &mut S, new_corpus_id: Option) -> Result<(), Error> { self.inner.post_exec(state, new_corpus_id) } } @@ -272,3 +267,183 @@ where OptionalMutator::new(from) } } + +/// Mapping [`Mutator`] using a function returning a reference. +/// +/// Allows using [`Mutator`]s for a certain type on (parts of) other input types that can be mapped to this type. +/// +/// Provides access to the state. If [`Option::None`] is returned from the mapping function, the mutator is returning [`MutationResult::Skipped`]. +/// +/// # Example +#[cfg_attr(feature = "std", doc = " ```")] +#[cfg_attr(not(feature = "std"), doc = " ```ignore")] +/// use std::vec::Vec; +/// +/// use libafl::{ +/// mutators::{ByteIncMutator, MutationResult, Mutator, StateAwareMappingMutator}, +/// state::{HasRand, NopState}, +/// }; +/// use libafl_bolts::rands::Rand as _; +/// +/// #[derive(Debug, PartialEq)] +/// struct CustomInput(Vec); +/// +/// impl CustomInput { +/// pub fn possibly_vec<'a, S: HasRand>( +/// &'a mut self, +/// state: &'a mut S, +/// ) -> (Option<&'a mut Vec>, &'a mut S) { +/// // we have access to the state +/// if state.rand_mut().coinflip(0.5) { +/// // If this input cannot be mutated with the outer mutator, return None +/// // e.g. because the input doesn't contain the field this mutator is supposed to mutate +/// (None, state) +/// } else { +/// // else, return the type that the outer mutator can mutate +/// (Some(&mut self.0), state) +/// } +/// } +/// } +/// +/// // construct a mutator that works on &mut Vec (since it impls `HasMutatorBytes`) +/// let inner = ByteIncMutator::new(); +/// // construct a mutator that works on &mut CustomInput +/// let mut outer = StateAwareMappingMutator::new(CustomInput::possibly_vec, inner); +/// +/// let mut input = CustomInput(vec![1]); +/// +/// let mut state: NopState = NopState::new(); +/// let res = outer.mutate(&mut state, &mut input).unwrap(); +/// if res == MutationResult::Mutated { +/// assert_eq!(input, CustomInput(vec![2],)); +/// } else { +/// assert_eq!(input, CustomInput(vec![1],)); +/// } +/// ``` +#[derive(Debug)] +pub struct StateAwareMappingMutator { + mapper: F, + inner: M, + name: Cow<'static, str>, +} + +impl StateAwareMappingMutator { + /// Creates a new [`StateAwareMappingMutator`] + pub fn new(mapper: F, inner: M) -> Self + where + M: Named, + { + let name = Cow::Owned(format!("StateAwareMappingMutator<{}>", inner.name())); + Self { + mapper, + inner, + name, + } + } +} + +impl Mutator for StateAwareMappingMutator +where + F: for<'a> FnMut(&'a mut IO, &'a mut S) -> (Option<&'a mut II>, &'a mut S), + M: Mutator, +{ + fn mutate(&mut self, state: &mut S, input: &mut IO) -> Result { + let (mapped, state) = (self.mapper)(input, state); + match mapped { + Some(mapped) => self.inner.mutate(state, mapped), + None => Ok(MutationResult::Skipped), + } + } + #[inline] + fn post_exec(&mut self, state: &mut S, new_corpus_id: Option) -> Result<(), Error> { + self.inner.post_exec(state, new_corpus_id) + } +} + +impl Named for StateAwareMappingMutator { + fn name(&self) -> &Cow<'static, str> { + &self.name + } +} + +/// Mapper to use to map a [`tuple_list`] of [`Mutator`]s using [`StateAwareMappingMutator`]s. +/// +/// See the explanation of [`StateAwareMappingMutator`] for details. +/// +/// # Example +#[cfg_attr(feature = "std", doc = " ```")] +#[cfg_attr(not(feature = "std"), doc = " ```ignore")] +/// use std::vec::Vec; +/// +/// use libafl::{ +/// mutators::{ +/// ByteIncMutator, MutationResult, MutatorsTuple, ToStateAwareMappingMutator, +/// }, +/// state::{HasRand, NopState}, +/// }; +/// +/// use libafl_bolts::{ +/// tuples::{tuple_list, Map}, +/// rands::Rand as _, +/// }; +/// +/// #[derive(Debug, PartialEq)] +/// struct CustomInput(Vec); +/// +/// impl CustomInput { +/// pub fn possibly_vec<'a, S: HasRand>( +/// &'a mut self, +/// state: &'a mut S, +/// ) -> (Option<&'a mut Vec>, &'a mut S) { +/// // we have access to the state +/// if state.rand_mut().coinflip(0.5) { +/// // If this input cannot be mutated with the outer mutator, return None +/// // e.g. because the input doesn't contain the field this mutator is supposed to mutate +/// (None, state) +/// } else { +/// // else, return the type that the outer mutator can mutate +/// (Some(&mut self.0), state) +/// } +/// } +/// } +/// +/// // construct a mutator that works on &mut Vec (since it impls `HasMutatorBytes`) +/// let mutators = tuple_list!(ByteIncMutator::new(), ByteIncMutator::new()); +/// // construct a mutator that works on &mut CustomInput +/// let mut mapped_mutators = +/// mutators.map(ToStateAwareMappingMutator::new(CustomInput::possibly_vec)); +/// +/// let mut input = CustomInput(vec![1]); +/// +/// let mut state: NopState = NopState::new(); +/// let res = mapped_mutators.mutate_all(&mut state, &mut input).unwrap(); +/// if res == MutationResult::Mutated { +/// // no way of knowing if either or both mutated +/// assert!(input.0 == vec![2] || input.0 == vec![3]); +/// } else { +/// assert_eq!(input, CustomInput(vec![1],)); +/// } +/// ``` +#[derive(Debug)] +pub struct ToStateAwareMappingMutator { + mapper: F, +} + +impl ToStateAwareMappingMutator { + /// Creates a new [`ToStateAwareMappingMutator`] + pub fn new(mapper: F) -> Self { + Self { mapper } + } +} + +impl MappingFunctor for ToStateAwareMappingMutator +where + F: Clone, + M: Named, +{ + type Output = StateAwareMappingMutator; + + fn apply(&mut self, from: M) -> Self::Output { + StateAwareMappingMutator::new(self.mapper.clone(), from) + } +} diff --git a/crates/libafl/src/mutators/numeric.rs b/crates/libafl/src/mutators/numeric.rs index f5ac9ba00f..6665aac8a0 100644 --- a/crates/libafl/src/mutators/numeric.rs +++ b/crates/libafl/src/mutators/numeric.rs @@ -4,13 +4,13 @@ use alloc::borrow::Cow; use core::marker::PhantomData; use libafl_bolts::{ - Error, Named, + Error, Named, map_tuple_list_type, merge_tuple_list_type, rands::Rand, tuples::{Map as _, Merge}, }; use tuple_list::{tuple_list, tuple_list_type}; -use super::{MappingMutator, MutationResult, Mutator, ToMappingMutator}; +use super::{MutationResult, Mutator, ToMappingMutator, ToStateAwareMappingMutator}; use crate::{ corpus::Corpus, random_corpus_id_with_disabled, @@ -80,14 +80,15 @@ pub fn int_mutators() -> IntMutatorsType { } /// Mapped mutators for integer-like inputs -pub type MappedIntMutatorsType = tuple_list_type!( - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator,F1> +pub type MappedIntMutatorsType = map_tuple_list_type!( + merge_tuple_list_type!(IntMutatorsNoCrossoverType, MappedIntMutatorsCrossoverType), + ToMappingMutator +); + +/// State-aware Mapped mutators for integer-like inputs +pub type StateAwareMappedIntMutatorsType = map_tuple_list_type!( + merge_tuple_list_type!(IntMutatorsNoCrossoverType, MappedIntMutatorsCrossoverType), + ToStateAwareMappingMutator ); /// Mapped mutators for integer-like inputs @@ -104,6 +105,25 @@ where .merge(mapped_int_mutators_crossover(input_from_corpus_mapper)) .map(ToMappingMutator::new(current_input_mapper)) } + +/// State-awareMapped mutators for integer-like inputs +/// +/// Modelled after the applicable mutators from [`super::havoc_mutations::havoc_mutations`] +pub fn state_aware_mapped_int_mutators<'a, F1, F2, IO, II, S>( + current_input_mapper: F1, + input_from_corpus_mapper: F2, +) -> StateAwareMappedIntMutatorsType +where + F1: Clone + FnMut(&'a mut IO, &'a mut S) -> (Option<&'a mut II>, &'a mut S), + IO: 'a, + II: 'a, + S: 'a, +{ + int_mutators_no_crossover() + .merge(mapped_int_mutators_crossover(input_from_corpus_mapper)) + .map(ToStateAwareMappingMutator::new(current_input_mapper)) +} + /// Functionality required for Numeric Mutators (see [`int_mutators`]) pub trait Numeric { /// Flip all bits of the number. diff --git a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs index 74ffe3f90f..5c3bcccdbf 100644 --- a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs +++ b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs @@ -23,7 +23,9 @@ use serde::{Deserialize, Serialize}; pub struct CustomInput { pub byte_array: Vec, pub optional_byte_array: Option>, + pub sometimes_hidden_byte_array: Vec, pub num: i16, + pub sometimes_hidden_num: i16, pub boolean: bool, } @@ -51,6 +53,23 @@ impl CustomInput { &self.optional_byte_array } + /// Sometimes returns a mutable reference to the sometimes-hidden byte array with logic depending on state + pub fn sometimes_hidden_byte_array_mut<'a, S: HasRand>( + &'a mut self, + state: &'a mut S, + ) -> (Option<&'a mut Vec>, &'a mut S) { + if state.rand_mut().coinflip(0.5) { + (Some(&mut self.sometimes_hidden_byte_array), state) + } else { + (None, state) + } + } + + /// Returns an immutable reference to the sometimes hidden byte array + pub fn sometimes_hidden_byte_array(&self) -> &Vec { + &self.sometimes_hidden_byte_array + } + /// Returns a mutable reference to the number pub fn num_mut(&mut self) -> &mut i16 { &mut self.num @@ -60,6 +79,23 @@ impl CustomInput { pub fn num(&self) -> &i16 { &self.num } + + /// Sometimes returns a mutable reference to the sometimes-hidden number with logic depending on state + pub fn sometimes_hidden_num_mut<'a, S: HasRand>( + &'a mut self, + state: &'a mut S, + ) -> (Option<&'a mut i16>, &'a mut S) { + if state.rand_mut().coinflip(0.5) { + (Some(&mut self.sometimes_hidden_num), state) + } else { + (None, state) + } + } + + /// Returns an immutable reference to the sometimes hidden number + pub fn sometimes_hidden_num(&self) -> &i16 { + &self.sometimes_hidden_num + } } /// A generator for [`CustomInput`] used in this example @@ -88,13 +124,17 @@ where .rand_mut() .coinflip(0.5) .then(|| generator.generate(state).unwrap().target_bytes().into()); + let sometimes_hidden_byte_array = generator.generate(state).unwrap().target_bytes().into(); let boolean = state.rand_mut().coinflip(0.5); let num = state.rand_mut().next() as i16; + let sometimes_hidden_num = state.rand_mut().next() as i16; Ok(CustomInput { byte_array, optional_byte_array, + sometimes_hidden_byte_array, num, + sometimes_hidden_num, boolean, }) } diff --git a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs index 04d4c416a8..bca74cfab7 100644 --- a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs +++ b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs @@ -34,7 +34,7 @@ use libafl_bolts::{ use { libafl::mutators::{ havoc_mutations::{havoc_crossover_with_corpus_mapper, havoc_mutations_no_crossover}, - mapping::{ToMappingMutator, ToOptionalMutator}, + mapping::{ToMappingMutator, ToOptionalMutator, ToStateAwareMappingMutator}, numeric::{int_mutators_no_crossover, mapped_int_mutators_crossover}, }, libafl_bolts::tuples::Map, @@ -145,8 +145,18 @@ pub fn main() { .expect("Failed to generate the initial corpus"); #[cfg(feature = "simple_interface")] - let (mapped_mutators, optional_mapped_mutators, int_mutators) = { + let ( + mapped_mutators, + optional_mapped_mutators, + sometimes_hidden_mapped_mutators, + int_mutators, + sometimes_hidden_int_mutators, + ) = { // Creating mutators that will operate on input.byte_array + + use libafl::mutators::{ + numeric::state_aware_mapped_int_mutators, state_aware_mapped_havoc_mutations, + }; let mapped_mutators = mapped_havoc_mutations(CustomInput::byte_array_mut, CustomInput::byte_array); @@ -156,12 +166,38 @@ pub fn main() { CustomInput::optional_byte_array, ); + // Creating mutators that will operate on input.sometimes_hidden_byte_array + let sometimes_hidden_mapped_mutators = state_aware_mapped_havoc_mutations( + CustomInput::sometimes_hidden_byte_array_mut, + CustomInput::sometimes_hidden_byte_array, + ); + + // Creating mutators that will operate on input.num let int_mutators = mapped_int_mutators(CustomInput::num_mut, CustomInput::num); - (mapped_mutators, optional_mapped_mutators, int_mutators) + + // Creating mutators that will operate on input.sometimes_hidden_num + let sometimes_hidden_int_mutators = state_aware_mapped_int_mutators( + CustomInput::sometimes_hidden_num_mut, + CustomInput::sometimes_hidden_num, + ); + + ( + mapped_mutators, + optional_mapped_mutators, + sometimes_hidden_mapped_mutators, + int_mutators, + sometimes_hidden_int_mutators, + ) }; #[cfg(not(feature = "simple_interface"))] - let (mapped_mutators, optional_mapped_mutators, int_mutators) = { + let ( + mapped_mutators, + optional_mapped_mutators, + sometimes_hidden_mapped_mutators, + int_mutators, + sometimes_hidden_int_mutators, + ) = { // Creating mutators that will operate on input.byte_array let mapped_mutators = havoc_mutations_no_crossover() .merge(havoc_crossover_with_corpus_mapper(CustomInput::byte_array)) @@ -175,11 +211,36 @@ pub fn main() { .map(ToOptionalMutator) .map(ToMappingMutator::new(CustomInput::optional_byte_array_mut)); + // Creating mutators that will operate on input.sometimes_hidden_byte_array + let sometimes_hidden_mapped_mutators = havoc_mutations_no_crossover() + .merge(havoc_crossover_with_corpus_mapper( + CustomInput::sometimes_hidden_byte_array, + )) + .map(ToStateAwareMappingMutator::new( + CustomInput::sometimes_hidden_byte_array_mut, + )); + // Creating mutators that will operate on input.num let int_mutators = int_mutators_no_crossover() .merge(mapped_int_mutators_crossover(CustomInput::num)) .map(ToMappingMutator::new(CustomInput::num_mut)); - (mapped_mutators, optional_mapped_mutators, int_mutators) + + // Creating mutators that will operate on input.sometimes_hidden_num + let sometimes_hidden_int_mutators = int_mutators_no_crossover() + .merge(mapped_int_mutators_crossover( + CustomInput::sometimes_hidden_num, + )) + .map(ToStateAwareMappingMutator::new( + CustomInput::sometimes_hidden_num_mut, + )); + + ( + mapped_mutators, + optional_mapped_mutators, + sometimes_hidden_mapped_mutators, + int_mutators, + sometimes_hidden_int_mutators, + ) }; // Merging multiple lists of mutators that mutate a sub-part of the custom input @@ -189,8 +250,12 @@ pub fn main() { .merge(mapped_mutators) // Then, mutators for the optional byte array, these return MutationResult::Skipped if the part is not present .merge(optional_mapped_mutators) + // Then, mutators for the sometimes hidden byte array + .merge(sometimes_hidden_mapped_mutators) // Then, mutators for the number .merge(int_mutators) + // Then, mutators for the sometimes hidden number + .merge(sometimes_hidden_int_mutators) // A custom mutator that sets the optional byte array to None if present, and generates a random byte array of length 1 if it is not .prepend(ToggleOptionalByteArrayMutator::new(nonzero!(1))) // Finally, a custom mutator that toggles the boolean part of the input