Skip to content

Commit

Permalink
Add Input Types and Mutators for Numeric Types (AFLplusplus#2760)
Browse files Browse the repository at this point in the history
* fixing empty multipart name

* fixing clippy

* New rules for the contributing (AFLplusplus#2752)

* Rules

* more

* aa

* Improve Flexibility of DumpToDiskStage (AFLplusplus#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 <[email protected]>
Co-authored-by: Dominik Maier <[email protected]>
  • Loading branch information
3 people committed Dec 15, 2024
1 parent 294d2f1 commit c170986
Show file tree
Hide file tree
Showing 9 changed files with 948 additions and 148 deletions.
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

0 comments on commit c170986

Please sign in to comment.