-
-
Notifications
You must be signed in to change notification settings - Fork 327
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add MappedInputs to allow havoc muations for custom (sub-)inputs (#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
1 parent
5b7d307
commit 2c676f0
Showing
60 changed files
with
1,391 additions
and
206 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = "*" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} | ||
} |
Oops, something went wrong.