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 core::num::NonZeroUsize;
|
||||||
use std::{
|
use std::{borrow::Cow, hash::Hash};
|
||||||
borrow::Cow,
|
|
||||||
hash::{DefaultHasher, Hash, Hasher},
|
|
||||||
};
|
|
||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::CorpusId,
|
corpus::CorpusId,
|
||||||
generators::{Generator, RandBytesGenerator},
|
generators::{Generator, RandBytesGenerator},
|
||||||
inputs::{BytesInput, HasTargetBytes, Input, MutVecInput},
|
inputs::{value::MutI16Input, BytesInput, HasTargetBytes, Input, MutVecInput},
|
||||||
mutators::{MutationResult, Mutator},
|
mutators::{MutationResult, Mutator},
|
||||||
state::HasRand,
|
state::HasRand,
|
||||||
Error, SerdeAny,
|
Error, SerdeAny,
|
||||||
};
|
};
|
||||||
use libafl_bolts::{rands::Rand, Named};
|
use libafl_bolts::{generic_hash_std, rands::Rand, Named};
|
||||||
use serde::{Deserialize, Serialize};
|
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
|
/// 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
|
/// 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,
|
/// - `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,
|
/// - `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
|
/// - `boolean` models the presence or absence of a command line flag that does not require additional data
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Hash, SerdeAny)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Hash, SerdeAny)]
|
||||||
pub struct CustomInput {
|
pub struct CustomInput {
|
||||||
pub byte_array: Vec<u8>,
|
pub byte_array: Vec<u8>,
|
||||||
pub optional_byte_array: Option<Vec<u8>>,
|
pub optional_byte_array: Option<Vec<u8>>,
|
||||||
|
pub num: i16,
|
||||||
pub boolean: bool,
|
pub boolean: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hash-based implementation
|
/// Hash-based implementation
|
||||||
impl Input for CustomInput {
|
impl Input for CustomInput {
|
||||||
fn generate_name(&self, _id: Option<CorpusId>) -> String {
|
fn generate_name(&self, _id: Option<CorpusId>) -> String {
|
||||||
let mut hasher = DefaultHasher::new();
|
format!("{:016x}", generic_hash_std(self))
|
||||||
self.hash(&mut hasher);
|
|
||||||
format!("{:016x}", hasher.finish())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +54,16 @@ impl CustomInput {
|
|||||||
pub fn optional_byte_array(&self) -> Option<&[u8]> {
|
pub fn optional_byte_array(&self) -> Option<&[u8]> {
|
||||||
self.optional_byte_array.as_deref()
|
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
|
/// A generator for [`CustomInput`] used in this example
|
||||||
@ -86,10 +93,12 @@ where
|
|||||||
.coinflip(0.5)
|
.coinflip(0.5)
|
||||||
.then(|| generator.generate(state).unwrap().target_bytes().into());
|
.then(|| generator.generate(state).unwrap().target_bytes().into());
|
||||||
let boolean = state.rand_mut().coinflip(0.5);
|
let boolean = state.rand_mut().coinflip(0.5);
|
||||||
|
let num = state.rand_mut().next() as i16;
|
||||||
|
|
||||||
Ok(CustomInput {
|
Ok(CustomInput {
|
||||||
byte_array,
|
byte_array,
|
||||||
optional_byte_array,
|
optional_byte_array,
|
||||||
|
num,
|
||||||
boolean,
|
boolean,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,10 @@ use input::{
|
|||||||
CustomInput, CustomInputGenerator, ToggleBooleanMutator, ToggleOptionalByteArrayMutator,
|
CustomInput, CustomInputGenerator, ToggleBooleanMutator, ToggleOptionalByteArrayMutator,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "simple_interface")]
|
#[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::{
|
use libafl::{
|
||||||
corpus::{InMemoryCorpus, OnDiskCorpus},
|
corpus::{InMemoryCorpus, OnDiskCorpus},
|
||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
@ -32,6 +35,7 @@ use {
|
|||||||
libafl::mutators::{
|
libafl::mutators::{
|
||||||
havoc_mutations::{havoc_crossover_with_corpus_mapper, havoc_mutations_no_crossover},
|
havoc_mutations::{havoc_crossover_with_corpus_mapper, havoc_mutations_no_crossover},
|
||||||
mapping::{ToMappedInputFunctionMappingMutatorMapper, ToOptionMappingMutatorMapper},
|
mapping::{ToMappedInputFunctionMappingMutatorMapper, ToOptionMappingMutatorMapper},
|
||||||
|
numeric::{int_mutators_no_crossover, mapped_int_mutators_crossover},
|
||||||
},
|
},
|
||||||
libafl_bolts::tuples::Map,
|
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
|
/// Assign a signal to the signals map
|
||||||
fn signals_set(idx: usize) {
|
fn signals_set(idx: usize) {
|
||||||
if idx > 2 {
|
if idx > 3 {
|
||||||
println!("Setting signal: {idx}");
|
println!("Setting signal: {idx}");
|
||||||
}
|
}
|
||||||
unsafe { write(SIGNALS_PTR.add(idx), 1) };
|
unsafe { write(SIGNALS_PTR.add(idx), 1) };
|
||||||
@ -60,17 +64,21 @@ pub fn main() {
|
|||||||
signals_set(1);
|
signals_set(1);
|
||||||
if input.optional_byte_array == Some(vec![b'b']) {
|
if input.optional_byte_array == Some(vec![b'b']) {
|
||||||
signals_set(2);
|
signals_set(2);
|
||||||
if input.boolean {
|
// require input.num to be in the top 1% of possible values
|
||||||
#[cfg(unix)]
|
if input.num > i16::MAX - i16::MAX / 50 {
|
||||||
panic!("Artificial bug triggered =)");
|
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.
|
// 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.
|
// 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.
|
// 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
|
// https://github.com/googleprojectzero/winafl/blob/ea5f6b85572980bb2cf636910f622f36906940aa/winafl.c#L728
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
unsafe {
|
unsafe {
|
||||||
write_volatile(0 as *mut u32, 0);
|
write_volatile(0 as *mut u32, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,7 +144,7 @@ pub fn main() {
|
|||||||
.expect("Failed to generate the initial corpus");
|
.expect("Failed to generate the initial corpus");
|
||||||
|
|
||||||
#[cfg(feature = "simple_interface")]
|
#[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
|
// Creating mutators that will operate on input.byte_array
|
||||||
let mapped_mutators =
|
let mapped_mutators =
|
||||||
mapped_havoc_mutations(CustomInput::byte_array_mut, CustomInput::byte_array);
|
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_mut,
|
||||||
CustomInput::optional_byte_array,
|
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"))]
|
#[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
|
// Creating mutators that will operate on input.byte_array
|
||||||
let mapped_mutators = havoc_mutations_no_crossover()
|
let mapped_mutators = havoc_mutations_no_crossover()
|
||||||
.merge(havoc_crossover_with_corpus_mapper(CustomInput::byte_array))
|
.merge(havoc_crossover_with_corpus_mapper(CustomInput::byte_array))
|
||||||
@ -168,7 +178,13 @@ pub fn main() {
|
|||||||
CustomInput::optional_byte_array_mut,
|
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
|
// Merging multiple lists of mutators that mutate a sub-part of the custom input
|
||||||
@ -178,6 +194,8 @@ pub fn main() {
|
|||||||
.merge(mapped_mutators)
|
.merge(mapped_mutators)
|
||||||
// Then, mutators for the optional byte array, these return MutationResult::Skipped if the part is not present
|
// Then, mutators for the optional byte array, these return MutationResult::Skipped if the part is not present
|
||||||
.merge(optional_mapped_mutators)
|
.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
|
// 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)))
|
.prepend(ToggleOptionalByteArrayMutator::new(nonzero!(1)))
|
||||||
// Finally, a custom mutator that toggles the boolean part of the input
|
// 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
|
//! 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)
|
//! (As opposed to other, more abstract, inputs, like an Grammar-Based AST Input)
|
||||||
|
|
||||||
use alloc::{borrow::ToOwned, rc::Rc, string::String, vec::Vec};
|
use alloc::{
|
||||||
use core::{
|
borrow::ToOwned,
|
||||||
cell::RefCell,
|
rc::Rc,
|
||||||
hash::{BuildHasher, Hasher},
|
vec::{self, Vec},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
use core::cell::RefCell;
|
||||||
use std::{fs::File, io::Read, path::Path};
|
|
||||||
|
|
||||||
use ahash::RandomState;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use libafl_bolts::{fs::write_file_atomic, Error};
|
|
||||||
use libafl_bolts::{ownedref::OwnedSlice, HasLen};
|
use libafl_bolts::{ownedref::OwnedSlice, HasLen};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
use super::ValueInput;
|
||||||
corpus::CorpusId,
|
use crate::inputs::{HasMutatorBytes, HasTargetBytes};
|
||||||
inputs::{HasMutatorBytes, HasTargetBytes, Input},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A bytes input is the basic input
|
/// A bytes input is the basic input
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash)]
|
pub type BytesInput = ValueInput<Vec<u8>>;
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rc Ref-cell from Input
|
/// Rc Ref-cell from Input
|
||||||
impl From<BytesInput> for Rc<RefCell<BytesInput>> {
|
impl From<BytesInput> for Rc<RefCell<BytesInput>> {
|
||||||
@ -65,57 +24,48 @@ impl From<BytesInput> for Rc<RefCell<BytesInput>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HasMutatorBytes for BytesInput {
|
impl HasMutatorBytes for BytesInput {
|
||||||
#[inline]
|
|
||||||
fn bytes(&self) -> &[u8] {
|
fn bytes(&self) -> &[u8] {
|
||||||
&self.bytes
|
self.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn bytes_mut(&mut self) -> &mut [u8] {
|
fn bytes_mut(&mut self) -> &mut [u8] {
|
||||||
&mut self.bytes
|
self.as_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize(&mut self, new_len: usize, value: u8) {
|
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) {
|
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
|
where
|
||||||
R: core::ops::RangeBounds<usize>,
|
R: core::ops::RangeBounds<usize>,
|
||||||
I: IntoIterator<Item = u8>,
|
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
|
where
|
||||||
R: core::ops::RangeBounds<usize>,
|
R: core::ops::RangeBounds<usize>,
|
||||||
{
|
{
|
||||||
self.bytes.drain(range)
|
self.as_mut().drain(range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasTargetBytes for BytesInput {
|
impl HasTargetBytes for BytesInput {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn target_bytes(&self) -> OwnedSlice<u8> {
|
fn target_bytes(&self) -> OwnedSlice<u8> {
|
||||||
OwnedSlice::from(&self.bytes)
|
OwnedSlice::from(self.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasLen for BytesInput {
|
impl HasLen for BytesInput {
|
||||||
#[inline]
|
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.bytes.len()
|
self.as_ref().len()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<u8>> for BytesInput {
|
|
||||||
fn from(bytes: Vec<u8>) -> Self {
|
|
||||||
Self::new(bytes)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,14 +77,6 @@ impl From<&[u8]> for BytesInput {
|
|||||||
|
|
||||||
impl From<BytesInput> for Vec<u8> {
|
impl From<BytesInput> for Vec<u8> {
|
||||||
fn from(value: BytesInput) -> Vec<u8> {
|
fn from(value: BytesInput) -> Vec<u8> {
|
||||||
value.bytes
|
value.into_inner()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BytesInput {
|
|
||||||
/// Creates a new bytes input using the given bytes
|
|
||||||
#[must_use]
|
|
||||||
pub const fn new(bytes: Vec<u8>) -> Self {
|
|
||||||
Self { bytes }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
pub mod bytes;
|
pub mod bytes;
|
||||||
pub use bytes::BytesInput;
|
pub use bytes::BytesInput;
|
||||||
|
|
||||||
|
pub mod value;
|
||||||
|
pub use value::ValueInput;
|
||||||
|
|
||||||
pub mod encoded;
|
pub mod encoded;
|
||||||
pub use encoded::*;
|
pub use encoded::*;
|
||||||
|
|
||||||
@ -28,7 +31,12 @@ use alloc::{
|
|||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::{Drain, Splice, Vec},
|
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")]
|
#[cfg(feature = "std")]
|
||||||
use std::{fs::File, hash::Hash, io::Read, path::Path};
|
use std::{fs::File, hash::Hash, io::Read, path::Path};
|
||||||
|
|
||||||
@ -42,6 +50,7 @@ use libafl_bolts::{
|
|||||||
#[cfg(feature = "nautilus")]
|
#[cfg(feature = "nautilus")]
|
||||||
pub use nautilus::*;
|
pub use nautilus::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use value::ValueMutRefInput;
|
||||||
|
|
||||||
use crate::corpus::CorpusId;
|
use crate::corpus::CorpusId;
|
||||||
|
|
||||||
@ -210,36 +219,29 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper type that allows us to use mutators for Mutators for `&mut `[`Vec`].
|
/// A wrapper type that allows us to use mutators for Mutators for `&mut `[`Vec`].
|
||||||
#[derive(Debug)]
|
pub type MutVecInput<'a> = ValueMutRefInput<'a, Vec<u8>>;
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasLen for MutVecInput<'_> {
|
impl HasLen for MutVecInput<'_> {
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.deref().len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasMutatorBytes for MutVecInput<'_> {
|
impl HasMutatorBytes for MutVecInput<'_> {
|
||||||
fn bytes(&self) -> &[u8] {
|
fn bytes(&self) -> &[u8] {
|
||||||
self.0
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bytes_mut(&mut self) -> &mut [u8] {
|
fn bytes_mut(&mut self) -> &mut [u8] {
|
||||||
self.0
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize(&mut self, new_len: usize, value: u8) {
|
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) {
|
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>
|
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>,
|
R: RangeBounds<usize>,
|
||||||
I: IntoIterator<Item = u8>,
|
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>
|
fn drain<R>(&mut self, range: R) -> Drain<'_, u8>
|
||||||
where
|
where
|
||||||
R: RangeBounds<usize>,
|
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.
|
/// Defines the input type shared across traits of the type.
|
||||||
/// Needed for consistency across HasCorpus/HasSolutions and friends.
|
/// Needed for consistency across HasCorpus/HasSolutions and friends.
|
||||||
pub trait UsesInput {
|
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 use token_mutations::*;
|
||||||
pub mod havoc_mutations;
|
pub mod havoc_mutations;
|
||||||
pub use havoc_mutations::*;
|
pub use havoc_mutations::*;
|
||||||
|
pub mod numeric;
|
||||||
|
pub use numeric::{int_mutators, mapped_int_mutators};
|
||||||
pub mod encoded_mutations;
|
pub mod encoded_mutations;
|
||||||
pub use encoded_mutations::*;
|
pub use encoded_mutations::*;
|
||||||
pub mod mopt_mutator;
|
pub mod mopt_mutator;
|
||||||
|
@ -1749,7 +1749,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
let mut gaps = 0;
|
let mut gaps = 0;
|
||||||
let mut range = 0..10;
|
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() {
|
while let Some(expected) = range.next() {
|
||||||
if let Some(last) = iter.next() {
|
if let Some(last) = iter.next() {
|
||||||
if expected != last {
|
if expected != last {
|
||||||
@ -1768,9 +1768,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
gaps, 1,
|
gaps,
|
||||||
|
1,
|
||||||
"{:?} should have exactly one gap, found {}",
|
"{:?} should have exactly one gap, found {}",
|
||||||
mutated.bytes, gaps
|
mutated.as_ref(),
|
||||||
|
gaps
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1802,20 +1804,20 @@ mod tests {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut expansion = 0;
|
let mut expansion = 0;
|
||||||
let mut expansion_len = base.bytes.len();
|
let mut expansion_len = base.as_ref().len();
|
||||||
for (i, value) in mutated.bytes.iter().copied().enumerate() {
|
for (i, value) in mutated.as_ref().iter().copied().enumerate() {
|
||||||
if i as u8 != value {
|
if i as u8 != value {
|
||||||
expansion = value as usize;
|
expansion = value as usize;
|
||||||
expansion_len = i - expansion;
|
expansion_len = i - expansion;
|
||||||
break;
|
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))
|
for (expected, value) in (0..(expansion + expansion_len))
|
||||||
.chain(expansion..base.bytes.len())
|
.chain(expansion..base.as_ref().len())
|
||||||
.zip(mutated.bytes)
|
.zip(mutated.as_ref())
|
||||||
{
|
{
|
||||||
assert_eq!(expected as u8, value);
|
assert_eq!(expected as u8, *value);
|
||||||
}
|
}
|
||||||
for i in (expansion..).take(expansion_len) {
|
for i in (expansion..).take(expansion_len) {
|
||||||
counts[i] += 1;
|
counts[i] += 1;
|
||||||
@ -1851,19 +1853,19 @@ mod tests {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut inserted = 0;
|
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 {
|
if i as u8 != value {
|
||||||
inserted = value;
|
inserted = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert!(mutated.bytes.len() <= base.bytes.len() + 16);
|
assert!(mutated.as_ref().len() <= base.as_ref().len() + 16);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bytecount::count(&mutated.bytes, inserted),
|
bytecount::count(mutated.as_ref(), inserted),
|
||||||
mutated.bytes.len() - base.bytes.len() + 1
|
mutated.as_ref().len() - base.as_ref().len() + 1
|
||||||
);
|
);
|
||||||
counts[inserted as usize] += 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();
|
let average = counts.iter().copied().sum::<usize>() / counts.len();
|
||||||
@ -1901,22 +1903,22 @@ mod tests {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut inserted = 10;
|
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 {
|
if i as u8 != value {
|
||||||
inserted = value;
|
inserted = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert!(mutated.bytes.len() <= base.bytes.len() + 16);
|
assert!(mutated.as_ref().len() <= base.as_ref().len() + 16);
|
||||||
let offset = usize::from((inserted as usize) < base.bytes.len());
|
let offset = usize::from((inserted as usize) < base.as_ref().len());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bytecount::count(&mutated.bytes, inserted),
|
bytecount::count(mutated.as_ref(), inserted),
|
||||||
mutated.bytes.len() - base.bytes.len() + offset,
|
mutated.as_ref().len() - base.as_ref().len() + offset,
|
||||||
"{:?}",
|
"{:?}",
|
||||||
mutated.bytes
|
mutated.as_ref()
|
||||||
);
|
);
|
||||||
counts[inserted as usize] += 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();
|
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"))]
|
#[cfg(all(not(feature = "xxh3"), feature = "alloc"))]
|
||||||
use core::hash::BuildHasher;
|
use core::hash::BuildHasher;
|
||||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
||||||
use core::hash::Hasher;
|
use core::hash::{Hash, Hasher};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
#[cfg(all(unix, feature = "std"))]
|
#[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`
|
/// Main error struct for `LibAFL`
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user