Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Input Types and Mutators for Numeric Types #2760

Merged
merged 35 commits into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
aefb8e3
fixing empty multipart name
riesentoaster Dec 6, 2024
a98c981
fixing clippy
riesentoaster Dec 6, 2024
7acf5a3
Merge branch 'main' into main
riesentoaster Dec 6, 2024
2da6dc5
New rules for the contributing (#2752)
tokatoka Dec 6, 2024
1e571a0
Improve Flexibility of DumpToDiskStage (#2753)
riesentoaster Dec 8, 2024
b4ff6b6
Introduce WrappingMutator
riesentoaster Dec 11, 2024
8af6280
introducing mutators for int types
riesentoaster Dec 11, 2024
9806f3f
Merge branch 'main' into numeric-mutators
riesentoaster Dec 11, 2024
21f9177
fixing no_std
riesentoaster Dec 11, 2024
93b79a6
random fixes
riesentoaster Dec 11, 2024
ac648c4
Add hash derivation for WrappingInput
riesentoaster Dec 11, 2024
aa207ec
Revert fixes that broke things
riesentoaster Dec 11, 2024
39ae88c
Derive Default on WrappingInput
riesentoaster Dec 11, 2024
d54a7d3
Add unit tests
riesentoaster Dec 11, 2024
2ea8132
Fixes according to code review
riesentoaster Dec 12, 2024
0b093e4
introduce mappable ValueInputs
riesentoaster Dec 12, 2024
11981ea
remove unnecessary comments
riesentoaster Dec 12, 2024
0fa453a
Merge branch 'main' into numeric-mutators
riesentoaster Dec 12, 2024
cd5e834
Elide more lifetimes
riesentoaster Dec 12, 2024
90932d1
remove dead code
riesentoaster Dec 13, 2024
4158166
simplify hashing
riesentoaster Dec 13, 2024
829e365
improve docs
riesentoaster Dec 13, 2024
ab97abd
improve randomization
riesentoaster Dec 13, 2024
22a26e0
rename method to align with standard library
riesentoaster Dec 13, 2024
6ba97db
add typedefs for int types for ValueMutRefInput
riesentoaster Dec 13, 2024
25f034d
rename test
riesentoaster Dec 13, 2024
faf127c
add safety notice to trait function
riesentoaster Dec 13, 2024
58cf5d0
improve randomize performance for i128/u128
riesentoaster Dec 15, 2024
d81ee93
rename macro
riesentoaster Dec 15, 2024
56c947a
improve comment
riesentoaster Dec 15, 2024
ed97a73
Merge branch 'main' into numeric-mutators
riesentoaster Dec 15, 2024
ea9e4a1
actually check return values in test
riesentoaster Dec 15, 2024
8b87945
make 128 bit int randomize even more efficient
riesentoaster Dec 15, 2024
7f513d4
shifting signed values
riesentoaster Dec 15, 2024
e77d7a5
Merge branch 'main' into numeric-mutators
domenukk Dec 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
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
///
/// 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))
}
}

Expand All @@ -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
Expand Down Expand Up @@ -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,
})
}
Expand Down
52 changes: 35 additions & 17 deletions fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
};
Expand All @@ -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) };
Expand All @@ -60,17 +64,21 @@ pub fn main() {
signals_set(1);
if input.optional_byte_array == Some(vec![b'b']) {
signals_set(2);
if input.boolean {
#[cfg(unix)]
panic!("Artificial bug triggered =)");

// panic!() raises a STATUS_STACK_BUFFER_OVERRUN exception which cannot be caught by the exception handler.
// Here we make it raise STATUS_ACCESS_VIOLATION instead.
// Extending the windows exception handler is a TODO. Maybe we can refer to what winafl code does.
// https://github.com/googleprojectzero/winafl/blob/ea5f6b85572980bb2cf636910f622f36906940aa/winafl.c#L728
#[cfg(windows)]
unsafe {
write_volatile(0 as *mut u32, 0);
// require input.num to be in the top 1% of possible values
if input.num > i16::MAX - i16::MAX / 50 {
signals_set(3);
if input.boolean {
#[cfg(unix)]
panic!("Artificial bug triggered =)");

// panic!() raises a STATUS_STACK_BUFFER_OVERRUN exception which cannot be caught by the exception handler.
// Here we make it raise STATUS_ACCESS_VIOLATION instead.
// Extending the windows exception handler is a TODO. Maybe we can refer to what winafl code does.
// https://github.com/googleprojectzero/winafl/blob/ea5f6b85572980bb2cf636910f622f36906940aa/winafl.c#L728
#[cfg(windows)]
unsafe {
write_volatile(0 as *mut u32, 0);
}
}
}
}
Expand Down Expand Up @@ -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);
Expand All @@ -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))
Expand All @@ -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
Expand All @@ -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
Expand Down
96 changes: 19 additions & 77 deletions libafl/src/inputs/bytes.rs
Original file line number Diff line number Diff line change
@@ -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>> {
Expand All @@ -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()
}
}

Expand All @@ -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()
}
}
Loading
Loading