Add Input Types and Mutators for Numeric Types (#2760)
* fixing empty multipart name * fixing clippy * New rules for the contributing (#2752) * Rules * more * aa * Improve Flexibility of DumpToDiskStage (#2753) * fixing empty multipart name * fixing clippy * improve flexibility of DumpToDiskStage * adding note to MIGRATION.md * Introduce WrappingMutator * introducing mutators for int types * fixing no_std * random fixes * Add hash derivation for WrappingInput * Revert fixes that broke things * Derive Default on WrappingInput * Add unit tests * Fixes according to code review * introduce mappable ValueInputs * remove unnecessary comments * Elide more lifetimes * remove dead code * simplify hashing * improve docs * improve randomization * rename method to align with standard library * add typedefs for int types for ValueMutRefInput * rename test * add safety notice to trait function * improve randomize performance for i128/u128 * rename macro * improve comment * actually check return values in test * make 128 bit int randomize even more efficient * shifting signed values --------- Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com> Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
65e544a417
commit
61e3f0b3a4
@ -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<u8>,
|
||||
pub optional_byte_array: Option<Vec<u8>>,
|
||||
pub num: i16,
|
||||
pub boolean: bool,
|
||||
}
|
||||
|
||||
/// Hash-based implementation
|
||||
impl Input for CustomInput {
|
||||
fn generate_name(&self, _id: Option<CorpusId>) -> 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,
|
||||
})
|
||||
}
|
||||
|
@ -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 =)");
|
||||
// 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);
|
||||
// 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
|
||||
|
@ -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<u8>,
|
||||
}
|
||||
|
||||
impl Input for BytesInput {
|
||||
#[cfg(feature = "std")]
|
||||
/// Write this input to the file
|
||||
fn to_file<P>(&self, path: P) -> Result<(), Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
write_file_atomic(path, &self.bytes)
|
||||
}
|
||||
|
||||
/// Load the content of this input from a file
|
||||
#[cfg(feature = "std")]
|
||||
fn from_file<P>(path: P) -> Result<Self, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut file = File::open(path)?;
|
||||
let mut bytes: Vec<u8> = vec![];
|
||||
file.read_to_end(&mut bytes)?;
|
||||
Ok(BytesInput::new(bytes))
|
||||
}
|
||||
|
||||
/// Generate a name for this input
|
||||
fn generate_name(&self, _id: Option<CorpusId>) -> 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<Vec<u8>>;
|
||||
|
||||
/// Rc Ref-cell from Input
|
||||
impl From<BytesInput> for Rc<RefCell<BytesInput>> {
|
||||
@ -65,57 +24,48 @@ impl From<BytesInput> for Rc<RefCell<BytesInput>> {
|
||||
}
|
||||
|
||||
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<Item = &'a u8>>(&mut self, iter: I) {
|
||||
Extend::extend(&mut self.bytes, iter);
|
||||
self.as_mut().extend(iter);
|
||||
}
|
||||
|
||||
fn splice<R, I>(&mut self, range: R, replace_with: I) -> alloc::vec::Splice<'_, I::IntoIter>
|
||||
fn splice<R, I>(&mut self, range: R, replace_with: I) -> vec::Splice<'_, I::IntoIter>
|
||||
where
|
||||
R: core::ops::RangeBounds<usize>,
|
||||
I: IntoIterator<Item = u8>,
|
||||
{
|
||||
self.bytes.splice(range, replace_with)
|
||||
self.as_mut().splice(range, replace_with)
|
||||
}
|
||||
|
||||
fn drain<R>(&mut self, range: R) -> alloc::vec::Drain<'_, u8>
|
||||
fn drain<R>(&mut self, range: R) -> vec::Drain<'_, u8>
|
||||
where
|
||||
R: core::ops::RangeBounds<usize>,
|
||||
{
|
||||
self.bytes.drain(range)
|
||||
self.as_mut().drain(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasTargetBytes for BytesInput {
|
||||
#[inline]
|
||||
fn target_bytes(&self) -> OwnedSlice<u8> {
|
||||
OwnedSlice::from(&self.bytes)
|
||||
OwnedSlice::from(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasLen for BytesInput {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.bytes.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for BytesInput {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self::new(bytes)
|
||||
self.as_ref().len()
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,14 +77,6 @@ impl From<&[u8]> for BytesInput {
|
||||
|
||||
impl From<BytesInput> for Vec<u8> {
|
||||
fn from(value: BytesInput) -> Vec<u8> {
|
||||
value.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl BytesInput {
|
||||
/// Creates a new bytes input using the given bytes
|
||||
#[must_use]
|
||||
pub const fn new(bytes: Vec<u8>) -> Self {
|
||||
Self { bytes }
|
||||
value.into_inner()
|
||||
}
|
||||
}
|
||||
|
@ -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<u8>);
|
||||
|
||||
impl<'a> From<&'a mut Vec<u8>> for MutVecInput<'a> {
|
||||
fn from(value: &'a mut Vec<u8>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
pub type MutVecInput<'a> = ValueMutRefInput<'a, Vec<u8>>;
|
||||
|
||||
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<Item = &'b u8>>(&mut self, iter: I) {
|
||||
self.0.extend(iter);
|
||||
self.deref_mut().extend(iter);
|
||||
}
|
||||
|
||||
fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter>
|
||||
@ -247,24 +249,17 @@ impl HasMutatorBytes for MutVecInput<'_> {
|
||||
R: RangeBounds<usize>,
|
||||
I: IntoIterator<Item = u8>,
|
||||
{
|
||||
self.0.splice::<R, I>(range, replace_with)
|
||||
self.deref_mut().splice::<R, I>(range, replace_with)
|
||||
}
|
||||
|
||||
fn drain<R>(&mut self, range: R) -> Drain<'_, u8>
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
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 {
|
||||
|
339
libafl/src/inputs/value.rs
Normal file
339
libafl/src/inputs/value.rs
Normal file
@ -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>(I);
|
||||
|
||||
impl<I> From<I> for ValueInput<I> {
|
||||
fn from(value: I) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> ValueInput<I> {
|
||||
/// 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<I> AsRef<I> for ValueInput<I> {
|
||||
fn as_ref(&self) -> &I {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> AsMut<I> for ValueInput<I> {
|
||||
fn as_mut(&mut self) -> &mut I {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Copy> Copy for ValueInput<I> {}
|
||||
|
||||
// Macro to implement the `Input` trait and create type aliases for `WrappingInput<T>`
|
||||
macro_rules! impl_input_for_value_input {
|
||||
($($t:ty => $name:ident),+ $(,)?) => {
|
||||
$(
|
||||
impl Input for ValueInput<$t> {
|
||||
fn generate_name(&self, _id: Option<CorpusId>) -> 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<Vec<u8>> {
|
||||
fn generate_name(&self, _id: Option<CorpusId>) -> String {
|
||||
format!("{:016x}", generic_hash_std(self))
|
||||
}
|
||||
|
||||
/// Write this input to the file
|
||||
#[cfg(feature = "std")]
|
||||
fn to_file<P>(&self, path: P) -> Result<(), Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
write_file_atomic(path, self.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load the content of this input from a file
|
||||
#[cfg(feature = "std")]
|
||||
fn from_file<P>(path: P) -> Result<Self, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut file = File::open(path)?;
|
||||
let mut data = vec![];
|
||||
file.read_to_end(&mut data)?;
|
||||
Ok(data.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Numeric for ValueInput<I>
|
||||
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<R: Rand>(&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<T>`
|
||||
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<I> Deref for ValueMutRefInput<'_, I> {
|
||||
type Target = I;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> 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<I>> for ValueMutRefInput<'a, I> {
|
||||
fn from(value: &'a mut ValueInput<I>) -> Self {
|
||||
Self(value.as_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> MappedInput for ValueMutRefInput<'_, I> {
|
||||
type Type<'a>
|
||||
= ValueMutRefInput<'a, I>
|
||||
where
|
||||
Self: 'a;
|
||||
}
|
||||
|
||||
impl<I> 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<R: Rand>(&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::<I>() * 8 - 1);
|
||||
$prep
|
||||
assert_ne!(j, $value, "{:?}.flip_bit_at({}) for {}", j, size_of::<I>() * 8 - 1, type_name::<$type>());
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn take_numeric<I: Numeric + Clone + PartialEq + Debug>(i: &I, check_twos_complement: bool) {
|
||||
apply_all_ops!({}, i.clone(), I, check_twos_complement);
|
||||
apply_all_ops!(
|
||||
{},
|
||||
ValueInput::from(i.clone()),
|
||||
ValueInput<I>,
|
||||
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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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::<usize>() / 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::<usize>() / counts.len();
|
||||
|
478
libafl/src/mutators/numeric.rs
Normal file
478
libafl/src/mutators/numeric.rs
Normal file
@ -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<F> = tuple_list_type!(MappedCrossoverMutator<F>);
|
||||
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<F>(input_mapper: F) -> MappedIntMutatorsCrossoverType<F> {
|
||||
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<F1, F2, I> = tuple_list_type!(
|
||||
MappedInputFunctionMappingMutator<BitFlipMutator,F1,I>,
|
||||
MappedInputFunctionMappingMutator<NegateMutator,F1,I>,
|
||||
MappedInputFunctionMappingMutator<IncMutator,F1,I>,
|
||||
MappedInputFunctionMappingMutator<DecMutator,F1,I>,
|
||||
MappedInputFunctionMappingMutator<TwosComplementMutator,F1,I>,
|
||||
MappedInputFunctionMappingMutator<RandMutator,F1,I>,
|
||||
MappedInputFunctionMappingMutator<MappedCrossoverMutator<F2>,F1,I>
|
||||
);
|
||||
|
||||
/// Mapped mutators for integer-like inputs
|
||||
///
|
||||
/// Modelled after the applicable mutators from [`super::havoc_mutations::havoc_mutations`]
|
||||
pub fn mapped_int_mutators<F1, F2, IO, II>(
|
||||
current_input_mapper: F1,
|
||||
input_from_corpus_mapper: F2,
|
||||
) -> MappedIntMutatorsType<F1, F2, II>
|
||||
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<R: Rand>(&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<R: Rand>(&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<R: Rand>(&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<I, S> Mutator<I, S> for BitFlipMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: Numeric,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
let offset = state.rand_mut().choose(0..size_of::<I>()).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<I, S> Mutator<I, S> for NegateMutator
|
||||
where
|
||||
I: Numeric,
|
||||
{
|
||||
fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<I, S> Mutator<I, S> for IncMutator
|
||||
where
|
||||
I: Numeric,
|
||||
{
|
||||
fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<I, S> Mutator<I, S> for DecMutator
|
||||
where
|
||||
I: Numeric,
|
||||
{
|
||||
fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<I, S> Mutator<I, S> for TwosComplementMutator
|
||||
where
|
||||
I: Numeric,
|
||||
{
|
||||
fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<I, S> Mutator<I, S> for RandMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: Numeric,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
// 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<I, S> Mutator<I, S> for CrossoverMutator
|
||||
where
|
||||
S: HasRand + HasCorpus,
|
||||
S::Corpus: Corpus<Input = I>,
|
||||
I: Copy,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<F> {
|
||||
input_mapper: F,
|
||||
}
|
||||
|
||||
impl<F> MappedCrossoverMutator<F> {
|
||||
/// Create a new [`MappedCrossoverMutator`]
|
||||
pub fn new(input_mapper: F) -> Self {
|
||||
Self { input_mapper }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S, F> Mutator<ValueMutRefInput<'_, I>, S> for MappedCrossoverMutator<F>
|
||||
where
|
||||
S: HasRand + HasCorpus,
|
||||
for<'b> F: Fn(&'b <S::Corpus as Corpus>::Input) -> &'b I,
|
||||
I: Clone,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut ValueMutRefInput<'_, I>,
|
||||
) -> Result<MutationResult, Error> {
|
||||
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<F> Named for MappedCrossoverMutator<F> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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<I: Hash>(input: &I) -> u64 {
|
||||
let mut hasher = hasher_std();
|
||||
input.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
/// Main error struct for `LibAFL`
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
|
Loading…
x
Reference in New Issue
Block a user