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..557a51d1ba 100644 --- a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs +++ b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs @@ -1,18 +1,15 @@ use core::num::NonZeroUsize; -use std::{ - borrow::Cow, - hash::{DefaultHasher, Hash, Hasher}, -}; +use std::{borrow::Cow, hash::Hash}; use libafl::{ corpus::CorpusId, generators::{Generator, RandBytesGenerator}, - inputs::{BytesInput, HasTargetBytes, Input, MutVecInput}, + inputs::{value::MutI16Input, BytesInput, HasTargetBytes, Input, MutVecInput}, mutators::{MutationResult, Mutator}, state::HasRand, Error, SerdeAny, }; -use libafl_bolts::{rands::Rand, Named}; +use libafl_bolts::{generic_hash_std, rands::Rand, Named}; use serde::{Deserialize, Serialize}; /// The custom [`Input`] type used in this example, consisting of a byte array part, a byte array that is not always present, and a boolean @@ -20,20 +17,20 @@ use serde::{Deserialize, Serialize}; /// Imagine these could be used to model command line arguments for a bash command, where /// - `byte_array` is binary data that is always needed like what is passed to stdin, /// - `optional_byte_array` is binary data passed as a command line arg, and it is only passed if it is not `None` in the input, +/// - `num` is an arbitrary number (`i16` in this case) /// - `boolean` models the presence or absence of a command line flag that does not require additional data #[derive(Serialize, Deserialize, Clone, Debug, Hash, SerdeAny)] 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}", generic_hash_std(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) -> MutI16Input<'_> { + (&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/bytes.rs b/libafl/src/inputs/bytes.rs index 127e672953..55bf30b35c 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -1,61 +1,20 @@ //! The `BytesInput` is the "normal" input, a map of bytes, that can be sent directly to the client //! (As opposed to other, more abstract, inputs, like an Grammar-Based AST Input) -use alloc::{borrow::ToOwned, rc::Rc, string::String, vec::Vec}; -use core::{ - cell::RefCell, - hash::{BuildHasher, Hasher}, +use alloc::{ + borrow::ToOwned, + rc::Rc, + vec::{self, Vec}, }; -#[cfg(feature = "std")] -use std::{fs::File, io::Read, path::Path}; +use core::cell::RefCell; -use ahash::RandomState; -#[cfg(feature = "std")] -use libafl_bolts::{fs::write_file_atomic, Error}; use libafl_bolts::{ownedref::OwnedSlice, HasLen}; -use serde::{Deserialize, Serialize}; -use crate::{ - corpus::CorpusId, - inputs::{HasMutatorBytes, HasTargetBytes, Input}, -}; +use super::ValueInput; +use crate::inputs::{HasMutatorBytes, HasTargetBytes}; /// A bytes input is the basic input -#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash)] -pub struct BytesInput { - /// The raw input bytes - pub(crate) bytes: Vec, -} - -impl Input for BytesInput { - #[cfg(feature = "std")] - /// Write this input to the file - fn to_file

(&self, path: P) -> Result<(), Error> - where - P: AsRef, - { - write_file_atomic(path, &self.bytes) - } - - /// Load the content of this input from a file - #[cfg(feature = "std")] - fn from_file

(path: P) -> Result - where - P: AsRef, - { - let mut file = File::open(path)?; - let mut bytes: Vec = vec![]; - file.read_to_end(&mut bytes)?; - Ok(BytesInput::new(bytes)) - } - - /// Generate a name for this input - fn generate_name(&self, _id: Option) -> String { - let mut hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher(); - hasher.write(self.bytes()); - format!("{:016x}", hasher.finish()) - } -} +pub type BytesInput = ValueInput>; /// Rc Ref-cell from Input impl From for Rc> { @@ -65,57 +24,48 @@ impl From for Rc> { } impl HasMutatorBytes for BytesInput { - #[inline] fn bytes(&self) -> &[u8] { - &self.bytes + self.as_ref() } - #[inline] fn bytes_mut(&mut self) -> &mut [u8] { - &mut self.bytes + self.as_mut() } fn resize(&mut self, new_len: usize, value: u8) { - self.bytes.resize(new_len, value); + self.as_mut().resize(new_len, value); } fn extend<'a, I: IntoIterator>(&mut self, iter: I) { - Extend::extend(&mut self.bytes, iter); + self.as_mut().extend(iter); } - fn splice(&mut self, range: R, replace_with: I) -> alloc::vec::Splice<'_, I::IntoIter> + fn splice(&mut self, range: R, replace_with: I) -> vec::Splice<'_, I::IntoIter> where R: core::ops::RangeBounds, I: IntoIterator, { - self.bytes.splice(range, replace_with) + self.as_mut().splice(range, replace_with) } - fn drain(&mut self, range: R) -> alloc::vec::Drain<'_, u8> + fn drain(&mut self, range: R) -> vec::Drain<'_, u8> where R: core::ops::RangeBounds, { - self.bytes.drain(range) + self.as_mut().drain(range) } } impl HasTargetBytes for BytesInput { #[inline] fn target_bytes(&self) -> OwnedSlice { - OwnedSlice::from(&self.bytes) + OwnedSlice::from(self.as_ref()) } } impl HasLen for BytesInput { - #[inline] fn len(&self) -> usize { - self.bytes.len() - } -} - -impl From> for BytesInput { - fn from(bytes: Vec) -> Self { - Self::new(bytes) + self.as_ref().len() } } @@ -127,14 +77,6 @@ impl From<&[u8]> for BytesInput { impl From for Vec { fn from(value: BytesInput) -> Vec { - value.bytes - } -} - -impl BytesInput { - /// Creates a new bytes input using the given bytes - #[must_use] - pub const fn new(bytes: Vec) -> Self { - Self { bytes } + value.into_inner() } } diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index 240be96917..dc7a3e5b99 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -3,6 +3,9 @@ pub mod bytes; pub use bytes::BytesInput; +pub mod value; +pub use value::ValueInput; + pub mod encoded; pub use encoded::*; @@ -28,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}; @@ -42,6 +50,7 @@ use libafl_bolts::{ #[cfg(feature = "nautilus")] pub use nautilus::*; use serde::{Deserialize, Serialize}; +use value::ValueMutRefInput; use crate::corpus::CorpusId; @@ -210,36 +219,29 @@ where } /// A wrapper type that allows us to use mutators for Mutators for `&mut `[`Vec`]. -#[derive(Debug)] -pub struct MutVecInput<'a>(&'a mut Vec); - -impl<'a> From<&'a mut Vec> for MutVecInput<'a> { - fn from(value: &'a mut Vec) -> Self { - Self(value) - } -} +pub type MutVecInput<'a> = ValueMutRefInput<'a, Vec>; impl HasLen for MutVecInput<'_> { fn len(&self) -> usize { - self.0.len() + self.deref().len() } } impl HasMutatorBytes for MutVecInput<'_> { fn bytes(&self) -> &[u8] { - self.0 + self } fn bytes_mut(&mut self) -> &mut [u8] { - self.0 + self } fn resize(&mut self, new_len: usize, value: u8) { - self.0.resize(new_len, value); + self.deref_mut().resize(new_len, value); } fn extend<'b, I: IntoIterator>(&mut self, iter: I) { - self.0.extend(iter); + self.deref_mut().extend(iter); } fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter> @@ -247,24 +249,17 @@ impl HasMutatorBytes for MutVecInput<'_> { R: RangeBounds, I: IntoIterator, { - self.0.splice::(range, replace_with) + self.deref_mut().splice::(range, replace_with) } fn drain(&mut self, range: R) -> Drain<'_, u8> where R: RangeBounds, { - self.0.drain(range) + self.deref_mut().drain(range) } } -impl MappedInput for MutVecInput<'_> { - type Type<'b> - = MutVecInput<'b> - where - Self: 'b; -} - /// Defines the input type shared across traits of the type. /// Needed for consistency across HasCorpus/HasSolutions and friends. pub trait UsesInput { diff --git a/libafl/src/inputs/value.rs b/libafl/src/inputs/value.rs new file mode 100644 index 0000000000..06922c5f95 --- /dev/null +++ b/libafl/src/inputs/value.rs @@ -0,0 +1,339 @@ +//! Newtype pattern style wrapper for [`super::Input`]s + +use alloc::{string::String, vec::Vec}; +use core::{ + fmt::Debug, + hash::Hash, + ops::{Deref, DerefMut}, +}; + +use libafl_bolts::{generic_hash_std, rands::Rand}; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] +use { + libafl_bolts::{fs::write_file_atomic, Error}, + std::{fs::File, io::Read, path::Path}, +}; + +use super::{Input, MappedInput}; +use crate::{corpus::CorpusId, mutators::numeric::Numeric}; + +/// Newtype pattern wrapper around an underlying structure to implement inputs +/// +/// This does not blanket implement [`super::Input`], because for certain inputs, writing them to disk does not make sense, because they don't own their data (like [`super::MutVecInput`]) +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)] +pub struct ValueInput(I); + +impl From for ValueInput { + fn from(value: I) -> Self { + Self(value) + } +} + +impl ValueInput { + /// Create a new [`ValueInput`] + pub const fn new(value: I) -> Self { + Self(value) + } + + /// Extract the inner value + pub fn into_inner(self) -> I { + self.0 + } +} + +impl AsRef for ValueInput { + fn as_ref(&self) -> &I { + &self.0 + } +} + +impl AsMut for ValueInput { + fn as_mut(&mut self) -> &mut I { + &mut self.0 + } +} + +impl Copy for ValueInput {} + +// Macro to implement the `Input` trait and create type aliases for `WrappingInput` +macro_rules! impl_input_for_value_input { + ($($t:ty => $name:ident),+ $(,)?) => { + $( + impl Input for ValueInput<$t> { + fn generate_name(&self, _id: Option) -> String { + format!("{:016x}", generic_hash_std(self)) + } + } + + /// Input wrapping a <$t> + pub type $name = ValueInput<$t>; + )* + }; +} + +// Invoke the macro with type-name pairs +impl_input_for_value_input!( + u8 => U8Input, + u16 => U16Input, + u32 => U32Input, + u64 => U64Input, + u128 => U128Input, + usize => UsizeInput, + i8 => I8Input, + i16 => I16Input, + i32 => I32Input, + i64 => I64Input, + i128 => I128Input, + isize => IsizeInput, +); + +/// manually implemented because files can be written more efficiently +impl Input for ValueInput> { + fn generate_name(&self, _id: Option) -> String { + format!("{:016x}", generic_hash_std(self)) + } + + /// Write this input to the file + #[cfg(feature = "std")] + fn to_file

(&self, path: P) -> Result<(), Error> + where + P: AsRef, + { + write_file_atomic(path, self.as_ref())?; + Ok(()) + } + + /// Load the content of this input from a file + #[cfg(feature = "std")] + fn from_file

(path: P) -> Result + where + P: AsRef, + { + let mut file = File::open(path)?; + let mut data = vec![]; + file.read_to_end(&mut data)?; + Ok(data.into()) + } +} + +impl Numeric for ValueInput +where + I: Numeric, +{ + fn flip_all_bits(&mut self) { + self.as_mut().flip_all_bits(); + } + + fn flip_bit_at(&mut self, rhs: usize) { + self.as_mut().flip_bit_at(rhs); + } + + fn wrapping_inc(&mut self) { + self.as_mut().wrapping_inc(); + } + + fn wrapping_dec(&mut self) { + self.as_mut().wrapping_dec(); + } + + fn twos_complement(&mut self) { + self.as_mut().twos_complement(); + } + + fn randomize(&mut self, rand: &mut R) { + self.as_mut().randomize(rand); + } +} + +/// Input type that holds a mutable reference to an inner value +#[derive(Debug, PartialEq)] +pub struct ValueMutRefInput<'a, I>(&'a mut I); + +// Macro to implement the `Input` trait and create type aliases for `WrappingInput` +macro_rules! impl_input_for_value_mut_ref_input { + ($($t:ty => $name:ident),+ $(,)?) => { + $( /// Input wrapping a <$t> + pub type $name<'a> = ValueMutRefInput<'a, $t>; + )* + }; +} + +// Invoke the macro with type-name pairs +impl_input_for_value_mut_ref_input!( + u8 => MutU8Input, + u16 => MutU16Input, + u32 => MutU32Input, + u64 => MutU64Input, + u128 => MutU128Input, + usize => MutUsizeInput, + i8 => MutI8Input, + i16 => MutI16Input, + i32 => MutI32Input, + i64 => MutI64Input, + i128 => MutI128Input, + isize => MutIsizeInput, +); + +impl Deref for ValueMutRefInput<'_, I> { + type Target = I; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl DerefMut for ValueMutRefInput<'_, I> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +impl<'a, I> From<&'a mut I> for ValueMutRefInput<'a, I> { + fn from(value: &'a mut I) -> Self { + Self(value) + } +} + +impl<'a, I> From<&'a mut ValueInput> for ValueMutRefInput<'a, I> { + fn from(value: &'a mut ValueInput) -> Self { + Self(value.as_mut()) + } +} + +impl MappedInput for ValueMutRefInput<'_, I> { + type Type<'a> + = ValueMutRefInput<'a, I> + where + Self: 'a; +} + +impl Numeric for ValueMutRefInput<'_, I> +where + I: Numeric, +{ + fn flip_all_bits(&mut self) { + self.deref_mut().flip_all_bits(); + } + + fn flip_bit_at(&mut self, rhs: usize) { + self.deref_mut().flip_bit_at(rhs); + } + + fn wrapping_inc(&mut self) { + self.deref_mut().wrapping_inc(); + } + + fn wrapping_dec(&mut self) { + self.deref_mut().wrapping_dec(); + } + + fn twos_complement(&mut self) { + self.deref_mut().twos_complement(); + } + + fn randomize(&mut self, rand: &mut R) { + self.deref_mut().randomize(rand); + } +} + +#[cfg(test)] +mod tests { + #[cfg(feature = "std")] + use { + super::{ValueInput, ValueMutRefInput}, + crate::mutators::numeric::Numeric, + alloc::fmt::Debug, + std::any::type_name, + }; + + #[cfg(feature = "std")] + macro_rules! apply_all_ops { + ($prep:stmt, $value:expr, $type:ty, $check_twos_complement:expr) => {{ + $prep + let mut j = $value; + j.flip_all_bits(); + $prep + assert_ne!(j, $value, "{:?}.flip_all_bits() for {}", j, type_name::<$type>()); + + $prep + let mut j = $value; + j.wrapping_inc(); + $prep + assert_ne!(j, $value, "{:?}.wrapping_inc() for {}", j, type_name::<$type>()); + + $prep + let mut j = $value; + j.wrapping_dec(); + $prep + assert_ne!(j, $value, "{:?}.wrapping_dec() for {}", j, type_name::<$type>()); + + $prep + let mut j = $value; + j.twos_complement(); + if $check_twos_complement { + $prep + assert_ne!(j, $value, "{:?}.twos_complement() for {}", j, type_name::<$type>()); + } + + $prep + let mut j = $value; + j.flip_bit_at(0); + $prep + assert_ne!(j, $value, "{:?}.flip_bit_at(0) for {}", j, type_name::<$type>()); + + $prep + let mut j = $value; + j.flip_bit_at(size_of::() * 8 - 1); + $prep + assert_ne!(j, $value, "{:?}.flip_bit_at({}) for {}", j, size_of::() * 8 - 1, type_name::<$type>()); + }}; + } + + #[cfg(feature = "std")] + fn take_numeric(i: &I, check_twos_complement: bool) { + apply_all_ops!({}, i.clone(), I, check_twos_complement); + apply_all_ops!( + {}, + ValueInput::from(i.clone()), + ValueInput, + check_twos_complement + ); + apply_all_ops!( + let mut i_clone = i.clone(), + ValueMutRefInput::from(&mut i_clone), + ValueMutRefInput<'_, I>, + check_twos_complement + ); + } + + #[test] + #[cfg(feature = "std")] // type detection for better error messages, running with std is sufficient + fn compiles() { + // twos complement doesn't change anything on the min value of numeric types + take_numeric(&u8::MIN, false); + take_numeric(&u16::MIN, false); + take_numeric(&u32::MIN, false); + take_numeric(&u64::MIN, false); + take_numeric(&u128::MIN, false); + take_numeric(&usize::MIN, false); + take_numeric(&i8::MIN, false); + take_numeric(&i16::MIN, false); + take_numeric(&i32::MIN, false); + take_numeric(&i64::MIN, false); + take_numeric(&i128::MIN, false); + take_numeric(&isize::MIN, false); + take_numeric(&u8::MAX, true); + take_numeric(&u16::MAX, true); + take_numeric(&u32::MAX, true); + take_numeric(&u64::MAX, true); + take_numeric(&u128::MAX, true); + take_numeric(&usize::MAX, true); + take_numeric(&i8::MAX, true); + take_numeric(&i16::MAX, true); + take_numeric(&i32::MAX, true); + take_numeric(&i64::MAX, true); + take_numeric(&i128::MAX, true); + take_numeric(&isize::MAX, true); + } +} diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index 73ab635a1d..f3875c7b43 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -13,6 +13,8 @@ use serde::{Deserialize, Serialize}; pub use token_mutations::*; pub mod havoc_mutations; pub use havoc_mutations::*; +pub mod numeric; +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/mutations.rs b/libafl/src/mutators/mutations.rs index 4c014fca9b..c44cabb2ac 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -1749,7 +1749,7 @@ mod tests { } let mut gaps = 0; let mut range = 0..10; - let mut iter = mutated.bytes.iter().copied(); + let mut iter = mutated.as_ref().iter().copied(); while let Some(expected) = range.next() { if let Some(last) = iter.next() { if expected != last { @@ -1768,9 +1768,11 @@ mod tests { } } assert_eq!( - gaps, 1, + gaps, + 1, "{:?} should have exactly one gap, found {}", - mutated.bytes, gaps + mutated.as_ref(), + gaps ); } @@ -1802,20 +1804,20 @@ mod tests { continue; } let mut expansion = 0; - let mut expansion_len = base.bytes.len(); - for (i, value) in mutated.bytes.iter().copied().enumerate() { + let mut expansion_len = base.as_ref().len(); + for (i, value) in mutated.as_ref().iter().copied().enumerate() { if i as u8 != value { expansion = value as usize; expansion_len = i - expansion; break; } } - assert_eq!(mutated.bytes.len(), base.bytes.len() + expansion_len); + assert_eq!(mutated.as_ref().len(), base.as_ref().len() + expansion_len); for (expected, value) in (0..(expansion + expansion_len)) - .chain(expansion..base.bytes.len()) - .zip(mutated.bytes) + .chain(expansion..base.as_ref().len()) + .zip(mutated.as_ref()) { - assert_eq!(expected as u8, value); + assert_eq!(expected as u8, *value); } for i in (expansion..).take(expansion_len) { counts[i] += 1; @@ -1851,19 +1853,19 @@ mod tests { continue; } let mut inserted = 0; - for (i, value) in mutated.bytes.iter().copied().enumerate() { + for (i, value) in mutated.as_ref().iter().copied().enumerate() { if i as u8 != value { inserted = value; break; } } - assert!(mutated.bytes.len() <= base.bytes.len() + 16); + assert!(mutated.as_ref().len() <= base.as_ref().len() + 16); assert_eq!( - bytecount::count(&mutated.bytes, inserted), - mutated.bytes.len() - base.bytes.len() + 1 + bytecount::count(mutated.as_ref(), inserted), + mutated.as_ref().len() - base.as_ref().len() + 1 ); counts[inserted as usize] += 1; - insertions[mutated.bytes.len() - base.bytes.len() - 1] += 1; + insertions[mutated.as_ref().len() - base.as_ref().len() - 1] += 1; } let average = counts.iter().copied().sum::() / counts.len(); @@ -1901,22 +1903,22 @@ mod tests { continue; } let mut inserted = 10; - for (i, value) in mutated.bytes.iter().copied().enumerate() { + for (i, value) in mutated.as_ref().iter().copied().enumerate() { if i as u8 != value { inserted = value; break; } } - assert!(mutated.bytes.len() <= base.bytes.len() + 16); - let offset = usize::from((inserted as usize) < base.bytes.len()); + assert!(mutated.as_ref().len() <= base.as_ref().len() + 16); + let offset = usize::from((inserted as usize) < base.as_ref().len()); assert_eq!( - bytecount::count(&mutated.bytes, inserted), - mutated.bytes.len() - base.bytes.len() + offset, + bytecount::count(mutated.as_ref(), inserted), + mutated.as_ref().len() - base.as_ref().len() + offset, "{:?}", - mutated.bytes + mutated.as_ref() ); counts[inserted as usize] += 1; - insertions[mutated.bytes.len() - base.bytes.len() - 1] += 1; + insertions[mutated.as_ref().len() - base.as_ref().len() - 1] += 1; } let average = counts.iter().copied().sum::() / counts.len(); diff --git a/libafl/src/mutators/numeric.rs b/libafl/src/mutators/numeric.rs new file mode 100644 index 0000000000..d226f2ce4b --- /dev/null +++ b/libafl/src/mutators/numeric.rs @@ -0,0 +1,478 @@ +//! Mutators for integer-style inputs + +use alloc::borrow::Cow; + +use libafl_bolts::{ + rands::Rand, + tuples::{Map as _, Merge}, + Error, Named, +}; +use tuple_list::{tuple_list, tuple_list_type}; + +use super::{ + MappedInputFunctionMappingMutator, MutationResult, Mutator, + ToMappedInputFunctionMappingMutatorMapper, +}; +use crate::{ + corpus::Corpus, + inputs::value::ValueMutRefInput, + random_corpus_id_with_disabled, + state::{HasCorpus, HasRand}, +}; + +/// All mutators for integer-like inputs +pub type IntMutatorsType = tuple_list_type!( + BitFlipMutator, + NegateMutator, + IncMutator, + DecMutator, + 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 without crossover mutations +#[must_use] +pub fn int_mutators_no_crossover() -> IntMutatorsNoCrossoverType { + tuple_list!( + BitFlipMutator, + NegateMutator, + IncMutator, + DecMutator, + TwosComplementMutator, + RandMutator, + ) +} + +/// 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. + /// + /// # Safety + /// + /// Panics if the `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 a u64 can be cast to +macro_rules! impl_numeric_cast_randomize { + ($($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)] + fn randomize(&mut self, rand: &mut R) { + *self = rand.next() as $t; + } + + } + )*) +} + +impl_numeric_cast_randomize!( u8 u16 u32 u64 usize i8 i16 i32 i64 isize ); + +// Macro to implement the Numeric trait for multiple integer types a u64 cannot be cast to +macro_rules! impl_numeric_128_bits_randomize { + ($($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)] + fn randomize(&mut self, rand: &mut R) { + *self = (u128::from(rand.next()) << 64 | u128::from(rand.next())) as $t; + } + + } + )*) +} + +// Apply the macro to all desired integer types +impl_numeric_128_bits_randomize! { u128 i128 } + +/// Bitflip mutation for integer-like inputs +#[derive(Debug)] +pub struct BitFlipMutator; + +impl Mutator for BitFlipMutator +where + S: HasRand, + I: Numeric, +{ + fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { + let offset = state.rand_mut().choose(0..size_of::()).unwrap(); + input.flip_bit_at(offset); + Ok(MutationResult::Mutated) + } +} + +impl Named for BitFlipMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("BitFlipMutator") + } +} + +/// Negate mutation for integer-like inputs, i.e. flip all bits +#[derive(Debug)] +pub struct NegateMutator; + +impl Mutator for NegateMutator +where + I: Numeric, +{ + fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { + input.flip_all_bits(); + Ok(MutationResult::Mutated) + } +} + +impl Named for NegateMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("ByteFlipMutator") + } +} + +/// Increment mutation for integer-like inputs. Wraps on overflows. +#[derive(Debug)] +pub struct IncMutator; + +impl Mutator for IncMutator +where + I: Numeric, +{ + fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { + input.wrapping_inc(); + Ok(MutationResult::Mutated) + } +} + +impl Named for IncMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("IncMutator") + } +} + +/// Decrement mutation for integer-like inputs. Wraps on underflow. +#[derive(Debug)] +pub struct DecMutator; + +impl Mutator for DecMutator +where + I: Numeric, +{ + fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { + input.wrapping_dec(); + Ok(MutationResult::Mutated) + } +} + +impl Named for DecMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("DecMutator") + } +} + +/// Two's complement mutation for integer-like inputs +#[derive(Debug)] +pub struct TwosComplementMutator; + +impl Mutator for TwosComplementMutator +where + I: Numeric, +{ + fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { + input.twos_complement(); + Ok(MutationResult::Mutated) + } +} + +impl Named for TwosComplementMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("NegMutator") + } +} + +/// Randomize mutation for integer-like inputs +#[derive(Debug)] +pub struct RandMutator; + +impl Mutator for RandMutator +where + S: HasRand, + I: Numeric, +{ + 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.randomize(state.rand_mut()); + Ok(MutationResult::Mutated) + } +} + +impl Named for RandMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("RandMutator") + } +} + +/// Crossover mutation for integer-like inputs +#[derive(Debug)] +pub struct CrossoverMutator; + +impl Mutator for CrossoverMutator +where + S: HasRand + HasCorpus, + S::Corpus: Corpus, + I: Copy, +{ + fn mutate(&mut self, state: &mut S, input: &mut 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(); + *input = *other_testcase.input().as_ref().unwrap(); + Ok(MutationResult::Mutated) + } +} + +impl Named for CrossoverMutator { + fn name(&self) -> &Cow<'static, str> { + &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 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::{Rand, XkcdRand}, + tuples::IntoVec as _, + }; + use serde::{Deserialize, Serialize}; + + use super::{int_mutators, Numeric}; + use crate::{ + corpus::{Corpus as _, InMemoryCorpus, Testcase}, + inputs::value::I16Input, + mutators::MutationResult, + state::StdState, + }; + + #[test] + 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(42_i16.into())).unwrap(); + let mut state = StdState::new( + XkcdRand::new(), + corpus, + InMemoryCorpus::new(), + &mut (), + &mut (), + ) + .unwrap(); + + let mutators = int_mutators().into_vec(); + + for mut m in mutators { + let mut input: I16Input = 1_i16.into(); + assert_eq!( + MutationResult::Mutated, + m.mutate(&mut state, &mut input).unwrap(), + "Errored with {}", + m.name() + ); + assert_ne!(1, input.into_inner(), "Errored with {}", m.name()); + } + } +} diff --git a/libafl_bolts/src/lib.rs b/libafl_bolts/src/lib.rs index 0c6af328db..b29139c0c2 100644 --- a/libafl_bolts/src/lib.rs +++ b/libafl_bolts/src/lib.rs @@ -146,7 +146,7 @@ use alloc::{borrow::Cow, vec::Vec}; #[cfg(all(not(feature = "xxh3"), feature = "alloc"))] use core::hash::BuildHasher; #[cfg(any(feature = "xxh3", feature = "alloc"))] -use core::hash::Hasher; +use core::hash::{Hash, Hasher}; #[cfg(feature = "std")] use std::time::{SystemTime, UNIX_EPOCH}; #[cfg(all(unix, feature = "std"))] @@ -265,6 +265,21 @@ pub fn hash_std(input: &[u8]) -> u64 { } } +/// Hashes the input with a given hash +/// +/// Hashes the input with a given hash, depending on features: +/// [`xxh3_64`](https://docs.rs/xxhash-rust/latest/xxhash_rust/xxh3/fn.xxh3_64.html) +/// if the `xxh3` feature is used, /// else [`ahash`](https://docs.rs/ahash/latest/ahash/). +/// +/// If you have access to a `&[u8]` directly, [`hash_std`] may provide better performance +#[cfg(any(feature = "xxh3", feature = "alloc"))] +#[must_use] +pub fn generic_hash_std(input: &I) -> u64 { + let mut hasher = hasher_std(); + input.hash(&mut hasher); + hasher.finish() +} + /// Main error struct for `LibAFL` #[derive(Debug)] pub enum Error {