Skip to content

Commit

Permalink
Add MappedInputs to allow havoc muations for custom (sub-)inputs (AFL…
Browse files Browse the repository at this point in the history
…plusplus#2422)

* introducing MappingMutator

* extending mapping mutators

* adding example fuzzer

* making crossover mutators more flexible.

* moving example fuzzer

* fixing dependency paths

* formatting

* fixing no std error

* fixing broken docs link

* fixing import paths

* fixing imports

* more format fixing

* adding new example fuzzer to CI

* fixing further imports

* fixing formatting

* formatting fixes

* improving docs for the example fuzzer

* adding documentation and tests to mapping mutators

* make extraction function for mapped crossover mutators more general

* adding MutVecFunctionMappingMutator

* Introducing WrapsReference

* code cleanup for mapping mutators

* adding tests and docs to mapping mutators

* reformatting comments

* fixing merging of mutators in example fuzzer

* formatting

* formatting v2

* cleanup according to PR comments

* adding type constraint to MappedInput helper functions to remove the need to specify types

* matching functions passed to mapped_havoc_mutations

* removing unnecessary constraints

* mapping mutators now contain the name of their inner mutator

---------

Co-authored-by: Dominik Maier <[email protected]>
  • Loading branch information
riesentoaster and domenukk authored Sep 18, 2024
1 parent 5b7d307 commit 2c676f0
Show file tree
Hide file tree
Showing 60 changed files with 1,391 additions and 206 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ jobs:
- ./fuzzers/baby/baby_fuzzer_grimoire
- ./fuzzers/baby/baby_fuzzer_gramatron
- ./fuzzers/baby/baby_fuzzer
- ./fuzzers/baby/baby_fuzzer_custom_input
- ./fuzzers/baby/baby_fuzzer_nautilus
# - ./fuzzers/baby/backtrace_baby_fuzzers
- ./fuzzers/baby/baby_fuzzer_unicode
Expand Down
2 changes: 1 addition & 1 deletion fuzzers/baby/baby_fuzzer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use libafl::{
fuzzer::{Fuzzer, StdFuzzer},
generators::RandPrintablesGenerator,
inputs::{BytesInput, HasTargetBytes},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::{havoc_mutations::havoc_mutations, scheduled::StdScheduledMutator},
observers::StdMapObserver,
schedulers::QueueScheduler,
stages::mutational::StdMutationalStage,
Expand Down
24 changes: 24 additions & 0 deletions fuzzers/baby/baby_fuzzer_custom_input/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "baby_fuzzer_custom_input"
version = "0.1.0"
authors = ["Valentin Huber <[email protected]>"]
edition = "2021"

[features]
default = ["simple_interface"]
simple_interface = []

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"
lto = true
codegen-units = 1
opt-level = 3
debug = true

[dependencies]
libafl = { path = "../../../libafl/" }
libafl_bolts = { path = "../../../libafl_bolts/" }
serde = "*"
7 changes: 7 additions & 0 deletions fuzzers/baby/baby_fuzzer_custom_input/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Baby fuzzer

This is a minimalistic fuzzer demonstrating how to employ mapping mutators to use default mutators on custom inputs. Custom inputs are necessary when the input to your program is a combination of parts, especially when those parts have different data types. Check multipart inputs if you have an input consisting of multiple parts of the same datatype and you don't need your mutation scheduler to be able to select which mutation is performed on which part.

The fuzzer runs on a single core until a crash occurs and then exits. The tested program is a simple Rust function without any instrumentation. For real fuzzing, you will want to add some sort to add coverage or other feedback.

You can run this example using `cargo run`.
147 changes: 147 additions & 0 deletions fuzzers/baby/baby_fuzzer_custom_input/src/input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use std::{
borrow::Cow,
hash::{DefaultHasher, Hash, Hasher},
};

use libafl::{
corpus::CorpusId,
generators::Generator,
inputs::{BytesInput, HasTargetBytes, Input, MutVecInput},
mutators::{MutationResult, Mutator},
prelude::RandBytesGenerator,
state::HasRand,
Error, SerdeAny,
};
use libafl_bolts::{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,
/// - `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 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())
}
}

impl CustomInput {
/// Returns a mutable reference to the byte array
pub fn byte_array_mut(&mut self) -> MutVecInput<'_> {
(&mut self.byte_array).into()
}

/// Returns an immutable reference to the byte array wrapped in [`Some`]
pub fn byte_array_optional<'a>(&'a self) -> &'a [u8] {
&self.byte_array
}

/// Returns a mutable reference to the optional byte array
pub fn optional_byte_array_mut(&mut self) -> Option<MutVecInput<'_>> {
self.optional_byte_array.as_mut().map(|e| e.into())
}

/// Returns an immutable reference to the optional byte array
pub fn optional_byte_array_optional<'a>(&'a self) -> Option<&'a [u8]> {
self.optional_byte_array.as_deref()
}
}

/// A generator for [`CustomInput`] used in this example
pub struct CustomInputGenerator {
pub max_len: usize,
}

impl CustomInputGenerator {
/// Creates a new [`CustomInputGenerator`]
pub fn new(max_len: usize) -> Self {
Self { max_len }
}
}

impl<S> Generator<CustomInput, S> for CustomInputGenerator
where
S: HasRand,
{
fn generate(&mut self, state: &mut S) -> Result<CustomInput, Error> {
let mut generator = RandBytesGenerator::new(self.max_len);

let byte_array = generator.generate(state).unwrap().target_bytes().into();
let optional_byte_array = state
.rand_mut()
.coinflip(0.5)
.then(|| generator.generate(state).unwrap().target_bytes().into());
let boolean = state.rand_mut().coinflip(0.5);

Ok(CustomInput {
byte_array,
optional_byte_array,
boolean,
})
}
}

/// [`Mutator`] that toggles the optional byte array of a [`CustomInput`], i.e. sets it to [`None`] if it is not, and to a random byte array if it is [`None`]
pub struct ToggleOptionalByteArrayMutator<G> {
generator: G,
}

impl<S> ToggleOptionalByteArrayMutator<RandBytesGenerator<S>>
where
S: HasRand,
{
/// Creates a new [`ToggleOptionalByteArrayMutator`]
pub fn new(length: usize) -> Self {
Self {
generator: RandBytesGenerator::new(length),
}
}
}

impl<G, S> Mutator<CustomInput, S> for ToggleOptionalByteArrayMutator<G>
where
S: HasRand,
G: Generator<BytesInput, S>,
{
fn mutate(&mut self, state: &mut S, input: &mut CustomInput) -> Result<MutationResult, Error> {
input.optional_byte_array = match input.optional_byte_array {
None => Some(self.generator.generate(state)?.target_bytes().into()),
Some(_) => None,
};
Ok(MutationResult::Mutated)
}
}

impl<G> Named for ToggleOptionalByteArrayMutator<G> {
fn name(&self) -> &Cow<'static, str> {
&Cow::Borrowed("ToggleOptionalByteArrayMutator")
}
}

/// [`Mutator`] that toggles the boolean field in a [`CustomInput`]
pub struct ToggleBooleanMutator;

impl<S> Mutator<CustomInput, S> for ToggleBooleanMutator {
fn mutate(&mut self, _state: &mut S, input: &mut CustomInput) -> Result<MutationResult, Error> {
input.boolean = !input.boolean;
Ok(MutationResult::Mutated)
}
}

impl Named for ToggleBooleanMutator {
fn name(&self) -> &Cow<'static, str> {
&Cow::Borrowed("ToggleBooleanMutator")
}
}
Loading

0 comments on commit 2c676f0

Please sign in to comment.