diff --git a/fuzzers/structure_aware/baby_fuzzer_custom_input/Cargo.toml b/fuzzers/structure_aware/baby_fuzzer_custom_input/Cargo.toml index 8068461b0c..76fd7813e7 100644 --- a/fuzzers/structure_aware/baby_fuzzer_custom_input/Cargo.toml +++ b/fuzzers/structure_aware/baby_fuzzer_custom_input/Cargo.toml @@ -19,6 +19,7 @@ opt-level = 3 debug = true [dependencies] +ahash = "0.8.11" libafl = { path = "../../../libafl" } libafl_bolts = { path = "../../../libafl_bolts" } log = { version = "0.4.22", features = ["release_max_level_info"] } 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 6c45c88939..fadda8e746 100644 --- a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs +++ b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs @@ -1,13 +1,11 @@ use core::num::NonZeroUsize; -use std::{ - borrow::Cow, - hash::{DefaultHasher, Hash, Hasher}, -}; +use std::{borrow::Cow, hash::Hash}; +use ahash::RandomState; use libafl::{ corpus::CorpusId, generators::{Generator, RandBytesGenerator}, - inputs::{BytesInput, HasTargetBytes, Input, MutVecInput}, + inputs::{value::ValueMutRefInput, BytesInput, HasTargetBytes, Input, MutVecInput}, mutators::{MutationResult, Mutator}, state::HasRand, Error, SerdeAny, @@ -25,15 +23,14 @@ use serde::{Deserialize, Serialize}; pub struct CustomInput { pub byte_array: Vec, pub optional_byte_array: Option>, + pub num: i16, pub boolean: bool, } /// Hash-based implementation impl Input for CustomInput { fn generate_name(&self, _id: Option) -> String { - let mut hasher = DefaultHasher::new(); - self.hash(&mut hasher); - format!("{:016x}", hasher.finish()) + format!("{:016x}", RandomState::with_seed(0).hash_one(self)) } } @@ -57,6 +54,16 @@ impl CustomInput { pub fn optional_byte_array(&self) -> Option<&[u8]> { self.optional_byte_array.as_deref() } + + /// Returns a mutable reference to the number + pub fn num_mut(&mut self) -> ValueMutRefInput<'_, i16> { + (&mut self.num).into() + } + + /// Returns an immutable reference to the number + pub fn num(&self) -> &i16 { + &self.num + } } /// A generator for [`CustomInput`] used in this example @@ -86,10 +93,12 @@ where .coinflip(0.5) .then(|| generator.generate(state).unwrap().target_bytes().into()); let boolean = state.rand_mut().coinflip(0.5); + let num = state.rand_mut().next() as i16; Ok(CustomInput { byte_array, optional_byte_array, + 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 873e26dea1..3102ddc0f2 100644 --- a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs +++ b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs @@ -8,7 +8,10 @@ use input::{ CustomInput, CustomInputGenerator, ToggleBooleanMutator, ToggleOptionalByteArrayMutator, }; #[cfg(feature = "simple_interface")] -use libafl::mutators::havoc_mutations::{mapped_havoc_mutations, optional_mapped_havoc_mutations}; +use libafl::mutators::{ + havoc_mutations::{mapped_havoc_mutations, optional_mapped_havoc_mutations}, + numeric::mapped_int_mutators, +}; use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus}, events::SimpleEventManager, @@ -32,6 +35,7 @@ use { libafl::mutators::{ havoc_mutations::{havoc_crossover_with_corpus_mapper, havoc_mutations_no_crossover}, mapping::{ToMappedInputFunctionMappingMutatorMapper, ToOptionMappingMutatorMapper}, + numeric::{int_mutators_no_crossover, mapped_int_mutators_crossover}, }, libafl_bolts::tuples::Map, }; @@ -43,7 +47,7 @@ static mut SIGNALS_PTR: *mut u8 = &raw mut SIGNALS as _; /// Assign a signal to the signals map fn signals_set(idx: usize) { - if idx > 2 { + if idx > 3 { println!("Setting signal: {idx}"); } unsafe { write(SIGNALS_PTR.add(idx), 1) }; @@ -60,17 +64,21 @@ pub fn main() { signals_set(1); if input.optional_byte_array == Some(vec![b'b']) { signals_set(2); - if input.boolean { - #[cfg(unix)] - panic!("Artificial bug triggered =)"); - - // panic!() raises a STATUS_STACK_BUFFER_OVERRUN exception which cannot be caught by the exception handler. - // Here we make it raise STATUS_ACCESS_VIOLATION instead. - // Extending the windows exception handler is a TODO. Maybe we can refer to what winafl code does. - // https://github.com/googleprojectzero/winafl/blob/ea5f6b85572980bb2cf636910f622f36906940aa/winafl.c#L728 - #[cfg(windows)] - unsafe { - write_volatile(0 as *mut u32, 0); + // require input.num to be in the top 1% of possible values + if input.num > i16::MAX - i16::MAX / 50 { + signals_set(3); + if input.boolean { + #[cfg(unix)] + panic!("Artificial bug triggered =)"); + + // panic!() raises a STATUS_STACK_BUFFER_OVERRUN exception which cannot be caught by the exception handler. + // Here we make it raise STATUS_ACCESS_VIOLATION instead. + // Extending the windows exception handler is a TODO. Maybe we can refer to what winafl code does. + // https://github.com/googleprojectzero/winafl/blob/ea5f6b85572980bb2cf636910f622f36906940aa/winafl.c#L728 + #[cfg(windows)] + unsafe { + write_volatile(0 as *mut u32, 0); + } } } } @@ -136,7 +144,7 @@ pub fn main() { .expect("Failed to generate the initial corpus"); #[cfg(feature = "simple_interface")] - let (mapped_mutators, optional_mapped_mutators) = { + let (mapped_mutators, optional_mapped_mutators, int_mutators) = { // Creating mutators that will operate on input.byte_array let mapped_mutators = mapped_havoc_mutations(CustomInput::byte_array_mut, CustomInput::byte_array); @@ -146,11 +154,13 @@ pub fn main() { CustomInput::optional_byte_array_mut, CustomInput::optional_byte_array, ); - (mapped_mutators, optional_mapped_mutators) + + let int_mutators = mapped_int_mutators(CustomInput::num_mut, CustomInput::num); + (mapped_mutators, optional_mapped_mutators, int_mutators) }; #[cfg(not(feature = "simple_interface"))] - let (mapped_mutators, optional_mapped_mutators) = { + let (mapped_mutators, optional_mapped_mutators, 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)) @@ -168,7 +178,13 @@ pub fn main() { CustomInput::optional_byte_array_mut, )); - (mapped_mutators, optional_mapped_mutators) + // Creating mutators that will operate on input.num + let int_mutators = int_mutators_no_crossover() + .merge(mapped_int_mutators_crossover(CustomInput::num)) + .map(ToMappedInputFunctionMappingMutatorMapper::new( + CustomInput::num_mut, + )); + (mapped_mutators, optional_mapped_mutators, int_mutators) }; // Merging multiple lists of mutators that mutate a sub-part of the custom input @@ -178,6 +194,8 @@ 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 number + .merge(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 diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index b38b18856f..dc7a3e5b99 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -31,7 +31,12 @@ use alloc::{ string::{String, ToString}, vec::{Drain, Splice, Vec}, }; -use core::{clone::Clone, fmt::Debug, marker::PhantomData, ops::RangeBounds}; +use core::{ + clone::Clone, + fmt::Debug, + marker::PhantomData, + ops::{Deref, DerefMut, RangeBounds}, +}; #[cfg(feature = "std")] use std::{fs::File, hash::Hash, io::Read, path::Path}; @@ -45,6 +50,7 @@ use libafl_bolts::{ #[cfg(feature = "nautilus")] pub use nautilus::*; use serde::{Deserialize, Serialize}; +use value::ValueMutRefInput; use crate::corpus::CorpusId; @@ -213,29 +219,29 @@ where } /// A wrapper type that allows us to use mutators for Mutators for `&mut `[`Vec`]. -pub type MutVecInput<'a> = ValueInput<&'a mut Vec>; +pub type MutVecInput<'a> = ValueMutRefInput<'a, Vec>; impl HasLen for MutVecInput<'_> { fn len(&self) -> usize { - self.as_ref().len() + self.deref().len() } } impl HasMutatorBytes for MutVecInput<'_> { fn bytes(&self) -> &[u8] { - self.as_ref() + self } fn bytes_mut(&mut self) -> &mut [u8] { - self.as_mut() + self } fn resize(&mut self, new_len: usize, value: u8) { - self.as_mut().resize(new_len, value); + self.deref_mut().resize(new_len, value); } fn extend<'b, I: IntoIterator>(&mut self, iter: I) { - self.as_mut().extend(iter); + self.deref_mut().extend(iter); } fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter> @@ -243,14 +249,14 @@ impl HasMutatorBytes for MutVecInput<'_> { R: RangeBounds, I: IntoIterator, { - self.as_mut().splice::(range, replace_with) + self.deref_mut().splice::(range, replace_with) } fn drain(&mut self, range: R) -> Drain<'_, u8> where R: RangeBounds, { - self.as_mut().drain(range) + self.deref_mut().drain(range) } } diff --git a/libafl/src/inputs/value.rs b/libafl/src/inputs/value.rs index b1af47414d..98635fdc09 100644 --- a/libafl/src/inputs/value.rs +++ b/libafl/src/inputs/value.rs @@ -3,11 +3,11 @@ use alloc::{string::String, vec::Vec}; use core::{ fmt::Debug, - ops::{Add, BitOrAssign, BitXorAssign, Mul, Not, Shl, Sub}, + ops::{Deref, DerefMut}, }; use ahash::RandomState; -use num_traits::{One, WrappingAdd, WrappingSub}; +use libafl_bolts::rands::Rand; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use { @@ -16,7 +16,7 @@ use { }; use super::{Input, MappedInput}; -use crate::corpus::CorpusId; +use crate::{corpus::CorpusId, mutators::numeric::Numeric}; /// Newtype pattern wrapper around an underlying structure to implement inputs /// @@ -54,12 +54,7 @@ impl AsMut for ValueInput { } } -impl MappedInput for ValueInput<&mut I> { - type Type<'a> - = ValueInput<&'a mut I> - where - Self: 'a; -} +impl Copy for ValueInput {} // Macro to implement the `Input` trait and create type aliases for `WrappingInput` macro_rules! impl_input_for_value_input { @@ -128,212 +123,155 @@ impl Input for ValueInput> { } } -impl Shl for ValueInput +impl Numeric for ValueInput where - I: Shl, - I::Output: Into, + I: Numeric, { - type Output = Self; - - fn shl(self, rhs: R) -> Self::Output { - self.inner().shl(rhs).into() + fn flip_all_bits(&mut self) { + self.as_mut().flip_all_bits(); } -} -impl BitXorAssign for ValueInput -where - I: BitXorAssign, -{ - fn bitxor_assign(&mut self, rhs: Self) { - self.as_mut().bitxor_assign(rhs.inner()); + fn flip_bit_at(&mut self, rhs: usize) { + self.as_mut().flip_bit_at(rhs); } -} -impl BitOrAssign for ValueInput -where - I: BitOrAssign, -{ - fn bitor_assign(&mut self, rhs: Self) { - self.as_mut().bitor_assign(rhs.inner()); + + fn wrapping_inc(&mut self) { + self.as_mut().wrapping_inc(); } -} -impl One for ValueInput -where - I: One + Mul, -{ - fn one() -> Self { - I::one().into() + fn wrapping_dec(&mut self) { + self.as_mut().wrapping_dec(); } -} -impl Mul for ValueInput -where - I: Mul, - I::Output: Into, -{ - type Output = Self; + fn twos_complement(&mut self) { + self.as_mut().twos_complement(); + } - fn mul(self, rhs: Self) -> Self::Output { - self.inner().mul(rhs.inner()).into() + fn randomize(&mut self, rand: &mut R) { + self.as_mut().randomize(rand); } } -// impl Zero for ValueInput -// where -// I: Zero + Into, -// { -// fn zero() -> Self { -// I::zero().into() -// } +/// Input type that holds a mutable reference to an inner value +#[derive(Debug)] +pub struct ValueMutRefInput<'a, I>(&'a mut I); -// fn is_zero(&self) -> bool { -// self.as_ref().is_zero() -// } -// } +impl<'a, I> Deref for ValueMutRefInput<'a, I> { + type Target = I; -impl WrappingAdd for ValueInput -where - I: WrappingAdd, - I::Output: Into, -{ - fn wrapping_add(&self, v: &Self) -> Self { - self.as_ref().wrapping_add(v.as_ref()).into() + fn deref(&self) -> &Self::Target { + self.0 } } -impl Add for ValueInput -where - I: Add, - I::Output: Into, -{ - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - self.inner().add(rhs.inner()).into() - } -} -impl WrappingSub for ValueInput -where - I: WrappingSub, - I::Output: Into, -{ - fn wrapping_sub(&self, v: &Self) -> Self { - self.as_ref().wrapping_sub(v.as_ref()).into() +impl<'a, I> DerefMut for ValueMutRefInput<'a, I> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 } } -impl Sub for ValueInput -where - I: Sub, - I::Output: Into, -{ - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - self.inner().sub(rhs.inner()).into() +impl<'a, I> From<&'a mut I> for ValueMutRefInput<'a, I> { + fn from(value: &'a mut I) -> Self { + Self(value) } } -impl Not for ValueInput -where - I: Not, - I::Output: Into, -{ - type Output = Self; - - fn not(self) -> Self::Output { - self.inner().not().into() +impl<'a, I> From<&'a mut ValueInput> for ValueMutRefInput<'a, I> { + fn from(value: &'a mut ValueInput) -> Self { + Self(value.as_mut()) } } -impl Copy for ValueInput where I: Copy {} - -#[cfg(test)] -mod tests { - use core::ops::{Add as _, Mul as _, Not as _, Sub as _}; - - use num_traits::{One, WrappingAdd as _, WrappingSub as _}; - - use crate::inputs::ValueInput; - - #[test] - fn shl() { - let unwrapped = 0x10_u64; - let wrapped: ValueInput<_> = unwrapped.into(); - let offset = 1_u32; - assert_eq!(unwrapped << offset, *(wrapped << offset).as_ref()); - } - - #[test] - fn bit_xor_assign() { - let mut unwrapped = 0x10_u64; - let mut wrapped: ValueInput<_> = unwrapped.into(); - unwrapped ^= u64::one(); - wrapped ^= ValueInput::one(); - assert_eq!(unwrapped, *wrapped.as_ref()); - } +impl<'b, I> MappedInput for ValueMutRefInput<'b, I> { + type Type<'a> + = ValueMutRefInput<'a, I> + where + Self: 'a; +} - #[test] - fn bit_or_assign() { - let mut unwrapped = 0x10_u64; - let mut wrapped: ValueInput<_> = unwrapped.into(); - unwrapped |= u64::one(); - wrapped |= ValueInput::one(); - assert_eq!(unwrapped, *wrapped.as_ref()); +impl<'a, I> Numeric for ValueMutRefInput<'a, I> +where + I: Numeric, +{ + fn flip_all_bits(&mut self) { + self.deref_mut().flip_all_bits(); } - #[test] - fn one() { - let unwrapped = u64::one(); - let wrapped: ValueInput = ValueInput::one(); - assert_eq!(unwrapped, *wrapped.as_ref()); + fn flip_bit_at(&mut self, rhs: usize) { + self.deref_mut().flip_bit_at(rhs); } - #[test] - fn mul() { - let lhs: ValueInput = 7.into(); - let rhs: ValueInput = 3.into(); - assert_eq!(21, *lhs.mul(rhs).as_ref()); + fn wrapping_inc(&mut self) { + self.deref_mut().wrapping_inc(); } - #[test] - fn add() { - let lhs: ValueInput = 7.into(); - let rhs: ValueInput = 3.into(); - assert_eq!(10, *lhs.add(rhs).as_ref()); + fn wrapping_dec(&mut self) { + self.deref_mut().wrapping_dec(); } - #[test] - fn wrapping_add() { - let lhs: ValueInput = 7.into(); - let rhs: ValueInput = 3.into(); - assert_eq!(10, *lhs.wrapping_add(&rhs).as_ref()); - let lhs: ValueInput = u64::MAX.into(); - let rhs: ValueInput = 1.into(); - assert_eq!(0, *lhs.wrapping_add(&rhs).as_ref()); + fn twos_complement(&mut self) { + self.deref_mut().twos_complement(); } - #[test] - fn sub() { - let lhs: ValueInput = 7.into(); - let rhs: ValueInput = 3.into(); - assert_eq!(4, *lhs.sub(rhs).as_ref()); + fn randomize(&mut self, rand: &mut R) { + self.deref_mut().randomize(rand); } +} - #[test] - fn wrapping_sub() { - let lhs: ValueInput = 7.into(); - let rhs: ValueInput = 3.into(); - assert_eq!(4, *lhs.wrapping_sub(&rhs).as_ref()); - let lhs: ValueInput = u64::MIN.into(); - let rhs: ValueInput = 1.into(); - assert_eq!(u64::MAX, *lhs.wrapping_sub(&rhs).as_ref()); +#[cfg(test)] +mod tests { + use super::{ValueInput, ValueMutRefInput}; + use crate::mutators::numeric::Numeric; + + fn take_numeric(i: I) { + i.clone().flip_all_bits(); + i.clone().flip_bit_at(0); + i.clone().flip_bit_at(size_of::() * 8 - 1); + i.clone().twos_complement(); + i.clone().wrapping_dec(); + i.clone().wrapping_inc(); + + ValueInput::from(i.clone()).flip_all_bits(); + ValueInput::from(i.clone()).flip_bit_at(0); + ValueInput::from(i.clone()).flip_bit_at(size_of::() * 8 - 1); + ValueInput::from(i.clone()).twos_complement(); + ValueInput::from(i.clone()).wrapping_dec(); + ValueInput::from(i.clone()).wrapping_inc(); + + ValueMutRefInput::from(&mut i.clone()).flip_all_bits(); + ValueMutRefInput::from(&mut i.clone()).flip_bit_at(0); + ValueMutRefInput::from(&mut i.clone()).flip_bit_at(size_of::() * 8 - 1); + ValueMutRefInput::from(&mut i.clone()).twos_complement(); + ValueMutRefInput::from(&mut i.clone()).wrapping_dec(); + ValueMutRefInput::from(&mut i.clone()).wrapping_inc(); + drop(i); } #[test] - fn not() { - let unwrapped = 7; - let wrapped: ValueInput = unwrapped.into(); - assert_eq!(unwrapped.not(), *wrapped.not().as_ref()); + fn impls_at_extremes() { + take_numeric(u8::MIN); + take_numeric(u16::MIN); + take_numeric(u32::MIN); + take_numeric(u64::MIN); + take_numeric(u128::MIN); + take_numeric(usize::MIN); + take_numeric(i8::MIN); + take_numeric(i16::MIN); + take_numeric(i32::MIN); + take_numeric(i64::MIN); + take_numeric(i128::MIN); + take_numeric(isize::MIN); + take_numeric(u8::MAX); + take_numeric(u16::MAX); + take_numeric(u32::MAX); + take_numeric(u64::MAX); + take_numeric(u128::MAX); + take_numeric(usize::MAX); + take_numeric(i8::MAX); + take_numeric(i16::MAX); + take_numeric(i32::MAX); + take_numeric(i64::MAX); + take_numeric(i128::MAX); + take_numeric(isize::MAX); } } diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index 4f07b79299..f3875c7b43 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -14,7 +14,7 @@ pub use token_mutations::*; pub mod havoc_mutations; pub use havoc_mutations::*; pub mod numeric; -pub use numeric::int_mutators; +pub use numeric::{int_mutators, mapped_int_mutators}; pub mod encoded_mutations; pub use encoded_mutations::*; pub mod mopt_mutator; diff --git a/libafl/src/mutators/numeric.rs b/libafl/src/mutators/numeric.rs index 9aca85ab51..a55d8714da 100644 --- a/libafl/src/mutators/numeric.rs +++ b/libafl/src/mutators/numeric.rs @@ -1,47 +1,193 @@ //! Mutators for integer-style inputs use alloc::borrow::Cow; -use core::ops::{BitOrAssign, BitXorAssign, Not, Shl}; -use libafl_bolts::{rands::Rand as _, Error, Named}; -use num_traits::{One, WrappingAdd, WrappingSub, Zero}; +use libafl_bolts::{ + rands::Rand, + tuples::{Map as _, Merge}, + Error, Named, +}; +use num_traits::Zero; use tuple_list::{tuple_list, tuple_list_type}; -use super::{MutationResult, Mutator}; +use super::{ + MappedInputFunctionMappingMutator, MutationResult, Mutator, + ToMappedInputFunctionMappingMutatorMapper, +}; use crate::{ corpus::Corpus, - inputs::ValueInput, + inputs::value::ValueMutRefInput, random_corpus_id_with_disabled, state::{HasCorpus, HasRand}, }; -/// All mutators for integer-like inputs, return type of [`int_mutators`] +/// All mutators for integer-like inputs pub type IntMutatorsType = tuple_list_type!( BitFlipMutator, - FlipMutator, + NegateMutator, IncMutator, DecMutator, - NegMutator, + TwosComplementMutator, RandMutator, CrossoverMutator ); +type IntMutatorsCrossoverType = tuple_list_type!(CrossoverMutator); +type MappedIntMutatorsCrossoverType = tuple_list_type!(MappedCrossoverMutator); +type IntMutatorsNoCrossoverType = tuple_list_type!( + BitFlipMutator, + NegateMutator, + IncMutator, + DecMutator, + TwosComplementMutator, + RandMutator, +); -/// Mutators for integer-like inputs -/// -/// Modelled after the applicable mutators from [`super::havoc_mutations::havoc_mutations`] +/// Mutators for integer-like inputs without crossover mutations #[must_use] -pub fn int_mutators() -> IntMutatorsType { +pub fn int_mutators_no_crossover() -> IntMutatorsNoCrossoverType { tuple_list!( BitFlipMutator, - FlipMutator, + NegateMutator, IncMutator, DecMutator, - NegMutator, + TwosComplementMutator, RandMutator, - CrossoverMutator ) } +/// Mutators for integer-like inputs that implement some form of crossover +#[must_use] +pub fn int_mutators_crossover() -> IntMutatorsCrossoverType { + tuple_list!(CrossoverMutator) +} + +/// Mutators for integer-like inputs that implement some form of crossover with a mapper to extract the crossed over information. +#[must_use] +pub fn mapped_int_mutators_crossover(input_mapper: F) -> MappedIntMutatorsCrossoverType { + tuple_list!(MappedCrossoverMutator::new(input_mapper)) +} + +/// Mutators for integer-like inputs +/// +/// Modelled after the applicable mutators from [`super::havoc_mutations::havoc_mutations`] +#[must_use] +pub fn int_mutators() -> IntMutatorsType { + int_mutators_no_crossover().merge(int_mutators_crossover()) +} + +/// Mapped mutators for integer-like inputs +pub type MappedIntMutatorsType = tuple_list_type!( + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator,F1,I> +); + +/// Mapped mutators for integer-like inputs +/// +/// Modelled after the applicable mutators from [`super::havoc_mutations::havoc_mutations`] +pub fn mapped_int_mutators( + current_input_mapper: F1, + input_from_corpus_mapper: F2, +) -> MappedIntMutatorsType +where + F1: Clone + FnMut(IO) -> II, +{ + int_mutators_no_crossover() + .merge(mapped_int_mutators_crossover(input_from_corpus_mapper)) + .map(ToMappedInputFunctionMappingMutatorMapper::new( + current_input_mapper, + )) +} +/// Functionality required for Numeric Mutators (see [`int_mutators`]) +pub trait Numeric { + /// Flip all bits of the number. + fn flip_all_bits(&mut self); + + /// Flip the bit at the specified offset. + /// + /// Has no effect if `offset` is out of bounds for the type. + fn flip_bit_at(&mut self, offset: usize); + + /// Increment the number by one, wrapping around on overflow. + fn wrapping_inc(&mut self); + + /// Decrement the number by one, wrapping around on underflow. + fn wrapping_dec(&mut self); + + /// Compute the two's complement of the number. + fn twos_complement(&mut self); + + /// Randomizes the value using the provided random number generator. + fn randomize(&mut self, rand: &mut R); +} + +// Macro to implement the Numeric trait for multiple integer types +macro_rules! impl_numeric { + ($($t:ty)*) => ($( + impl Numeric for $t { + #[inline] + fn flip_all_bits(&mut self) { + *self = !*self; + } + + #[inline] + fn flip_bit_at(&mut self, offset: usize) { + *self ^= 1 << offset; + } + + #[inline] + fn wrapping_inc(&mut self) { + *self = self.wrapping_add(1); + } + + #[inline] + fn wrapping_dec(&mut self) { + *self = self.wrapping_sub(1); + } + + #[inline] + fn twos_complement(&mut self) { + *self = self.wrapping_neg(); + } + + #[inline] + #[allow(trivial_numeric_casts, clippy::cast_possible_wrap, clippy::cast_lossless)] + fn randomize(&mut self, rand: &mut R) { + // Set the value to zero + self.set_zero(); + + // Number of bytes in the target type + let byte_size = size_of::<$t>(); + // Number of bytes in each random u64 + let bytes_per_rand = 8; // u64 has 8 bytes + + let mut current_rand = 0u64; + + for byte_index in 0..byte_size { + // Fetch a new random u64 every `bytes_per_rand` bytes + if byte_index % bytes_per_rand == 0 { + current_rand = rand.next(); + } + + // Extract the relevant byte from the current random u64 + let rand_byte = ((current_rand >> (8 * (byte_index % bytes_per_rand))) & 0xFF) as u8; + + // Assemble the byte into the target integer + *self |= (rand_byte as $t) << (8 * byte_index); + } + } + + } + )*) +} + +// Apply the macro to all desired integer types +impl_numeric! { u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize } + /// Bitflip mutation for integer-like inputs #[derive(Debug)] pub struct BitFlipMutator; @@ -49,10 +195,11 @@ pub struct BitFlipMutator; impl Mutator for BitFlipMutator where S: HasRand, - I: Shl + BitXorAssign + One, + I: Numeric, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { - *input ^= I::one() << state.rand_mut().choose(0..size_of::()).unwrap(); + let offset = state.rand_mut().choose(0..size_of::()).unwrap(); + input.flip_bit_at(offset); Ok(MutationResult::Mutated) } } @@ -63,21 +210,21 @@ impl Named for BitFlipMutator { } } -/// Flip mutation for integer-like inputs +/// Negate mutation for integer-like inputs, i.e. flip all bits #[derive(Debug)] -pub struct FlipMutator; +pub struct NegateMutator; -impl Mutator for FlipMutator +impl Mutator for NegateMutator where - I: Not + Copy, + I: Numeric, { fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { - *input = !*input; + input.flip_all_bits(); Ok(MutationResult::Mutated) } } -impl Named for FlipMutator { +impl Named for NegateMutator { fn name(&self) -> &Cow<'static, str> { &Cow::Borrowed("ByteFlipMutator") } @@ -89,10 +236,10 @@ pub struct IncMutator; impl Mutator for IncMutator where - I: WrappingAdd + One, + I: Numeric, { fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { - *input = input.wrapping_add(&I::one()); + input.wrapping_inc(); Ok(MutationResult::Mutated) } } @@ -109,10 +256,10 @@ pub struct DecMutator; impl Mutator for DecMutator where - I: WrappingSub + One, + I: Numeric, { fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { - *input = input.wrapping_sub(&I::one()); + input.wrapping_dec(); Ok(MutationResult::Mutated) } } @@ -123,21 +270,21 @@ impl Named for DecMutator { } } -/// Negate mutation for integer-like inputs +/// Two's complement mutation for integer-like inputs #[derive(Debug)] -pub struct NegMutator; +pub struct TwosComplementMutator; -impl Mutator for NegMutator +impl Mutator for TwosComplementMutator where - I: Not + WrappingAdd + One + Copy, + I: Numeric, { fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { - *input = (!(*input)).wrapping_add(&I::one()); + input.twos_complement(); Ok(MutationResult::Mutated) } } -impl Named for NegMutator { +impl Named for TwosComplementMutator { fn name(&self) -> &Cow<'static, str> { &Cow::Borrowed("NegMutator") } @@ -147,26 +294,14 @@ impl Named for NegMutator { #[derive(Debug)] pub struct RandMutator; -impl Mutator, S> for RandMutator +impl Mutator for RandMutator where S: HasRand, - ValueInput: Shl> + BitOrAssign, - I: From + Zero, + I: Numeric, { - fn mutate( - &mut self, - state: &mut S, - input: &mut ValueInput, - ) -> Result { + fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { // set to random data byte-wise since the RNGs don't work for all numeric types - *input = I::zero().into(); - - for offset in 0..(size_of::() % size_of::()) { - let raw = state.rand_mut().next() as u8; - let inner: I = raw.into(); - let mask: ValueInput = inner.into(); - *input |= mask << offset; - } + input.randomize(state.rand_mut()); Ok(MutationResult::Mutated) } } @@ -205,12 +340,59 @@ impl Named for CrossoverMutator { &Cow::Borrowed("CrossoverMutator") } } +/// Crossover mutation for integer-like inputs with custom state extraction function +#[derive(Debug)] +pub struct MappedCrossoverMutator { + input_mapper: F, +} + +impl MappedCrossoverMutator { + /// Create a new [`MappedCrossoverMutator`] + pub fn new(input_mapper: F) -> Self { + Self { input_mapper } + } +} + +impl<'a, I, S, F> Mutator, S> for MappedCrossoverMutator +where + S: HasRand + HasCorpus, + for<'b> F: Fn(&'b ::Input) -> &'b I, + I: Clone, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut ValueMutRefInput<'_, I>, + ) -> Result { + let id = random_corpus_id_with_disabled!(state.corpus(), state.rand_mut()); + + if state.corpus().current().is_some_and(|cur| cur == id) { + return Ok(MutationResult::Skipped); + } + + let other_testcase = state.corpus().get_from_all(id)?.borrow_mut(); + let other_input = other_testcase.input().as_ref().unwrap(); + let mapped_input = (self.input_mapper)(other_input).clone(); + **input = mapped_input; + Ok(MutationResult::Mutated) + } +} + +impl Named for MappedCrossoverMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("MappedCrossoverMutator") + } +} #[cfg(test)] mod tests { - use libafl_bolts::{rands::StdRand, tuples::IntoVec as _}; + use libafl_bolts::{ + rands::{Rand, StdRand}, + tuples::IntoVec as _, + }; + use serde::{Deserialize, Serialize}; - use super::int_mutators; + use super::{int_mutators, Numeric}; use crate::{ corpus::{Corpus as _, InMemoryCorpus, Testcase}, inputs::value::I16Input, @@ -219,7 +401,34 @@ mod tests { }; #[test] - fn all_mutate() { + fn randomized() { + const RAND_NUM: u64 = 0xAAAAAAAAAAAAAAAA; // 0b10101010.. + #[derive(Serialize, Deserialize, Debug)] + struct FixedRand; + impl Rand for FixedRand { + fn set_seed(&mut self, _seed: u64) {} + fn next(&mut self) -> u64 { + RAND_NUM + } + } + + let rand = &mut FixedRand; + + let mut i = 0_u8; + Numeric::randomize(&mut i, rand); + assert_eq!(0xAA, i); + + let mut i = 0_u128; + Numeric::randomize(&mut i, rand); + assert_eq!(((u128::from(RAND_NUM) << 64) | u128::from(RAND_NUM)), i); + + let mut i = 0_i16; + Numeric::randomize(&mut i, rand); + assert_eq!(-0b101010101010110, i); // two's complement + } + + #[test] + fn all_mutate_owned() { let mut corpus = InMemoryCorpus::new(); corpus.add(Testcase::new(1_i16.into())).unwrap(); let mut state = StdState::new( @@ -242,4 +451,29 @@ mod tests { ); } } + + // #[test] + // fn all_mutate_mut_ref() { + // let mut corpus = InMemoryCorpus::new(); + // corpus.add(Testcase::new((&mut 1_i16).into())).unwrap(); + // let mut state = StdState::new( + // StdRand::new(), + // corpus, + // InMemoryCorpus::new(), + // &mut (), + // &mut (), + // ) + // .unwrap(); + + // let mut input: ValueMutRefInput<'_, i16> = (&mut 0_i16).into(); + + // let mutators = int_mutators().into_vec(); + + // for mut m in mutators { + // assert_eq!( + // MutationResult::Mutated, + // m.mutate(&mut state, &mut input).unwrap() + // ); + // } + // } }