Multipart Input support (#1617)

* initial commit: multipart

* document + wrap up baby fuzzer

* oops

* core

* add from method, option to iter

* improve example; use minmap; fix initial_mut

* bindings

* clippy, again

* moar clippy

* fmt

* drop rand dep because we don't need it, actually

* docfix

* ok actually fix docs pls
This commit is contained in:
Addison Crump 2024-01-04 01:51:29 +01:00 committed by GitHub
parent 75fcd47044
commit 99fd69acdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 806 additions and 59 deletions

1
fuzzers/baby_fuzzer_multi/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
libpng-*

View File

@ -0,0 +1,24 @@
[package]
name = "baby_fuzzer_multi"
version = "0.10.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>", "Addison Crump <me@addisoncrump.info>"]
edition = "2021"
[features]
default = ["std"]
tui = []
std = []
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
lto = true
codegen-units = 1
opt-level = 3
debug = true
[dependencies]
libafl = { path = "../../libafl/", features = ["multipart_inputs"] }
libafl_bolts = { path = "../../libafl_bolts/" }

View File

@ -0,0 +1,10 @@
# Baby fuzzer multi
This is a minimalistic example about how to create a libafl based fuzzer for targets with multipart inputs.
It 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`, and you can enable the TUI feature by running `cargo run --features tui`.

View File

@ -0,0 +1,164 @@
#[cfg(windows)]
use std::ptr::write_volatile;
use std::{path::PathBuf, ptr::write};
#[cfg(feature = "tui")]
use libafl::monitors::tui::{ui::TuiUI, TuiMonitor};
#[cfg(not(feature = "tui"))]
use libafl::monitors::SimpleMonitor;
use libafl::{
corpus::{InMemoryCorpus, OnDiskCorpus},
events::SimpleEventManager,
executors::{inprocess::InProcessExecutor, ExitKind},
feedback_or_fast,
feedbacks::{CrashFeedback, MaxMapFeedback, MinMapFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes, MultipartInput},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
observers::StdMapObserver,
schedulers::QueueScheduler,
stages::mutational::StdMutationalStage,
state::StdState,
Evaluator,
};
use libafl_bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice};
/// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 128] = [0; 128];
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
/// "Coverage" map for count, just to help things along
static mut LAST_COUNT: [usize; 1] = [usize::MAX];
static mut LAST_COUNT_PTR: *mut usize = unsafe { LAST_COUNT.as_mut_ptr() };
/// Assign a signal to the signals map
fn signals_set(idx: usize) {
unsafe { write(SIGNALS_PTR.add(idx), 1) };
}
/// Assign a count to the count "map"
fn count_set(count: usize) {
unsafe { LAST_COUNT[0] = count };
}
#[allow(clippy::similar_names, clippy::manual_assert)]
pub fn main() {
// The closure that we want to fuzz
let mut harness = |input: &MultipartInput<BytesInput>| {
let mut count = input.parts().len();
for (i, input) in input.parts().iter().enumerate() {
let target = input.target_bytes();
let buf = target.as_slice();
signals_set(i * 8);
if !buf.is_empty() && buf[0] == b'a' {
signals_set(1 + i * 8);
if buf.len() > 1 && buf[1] == b'b' {
signals_set(2 + i * 8);
if buf.len() > 2 && buf[2] == b'c' {
count -= 1;
}
}
}
}
if count == 0 {
#[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);
}
}
// without this, artificial bug is not found
// maybe interesting to try to auto-derive this, researchers! :)
count_set(count);
ExitKind::Ok
};
// Create an observation channel using the signals map
let signals_observer =
unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
let mut count_observer =
unsafe { StdMapObserver::from_mut_ptr("count", LAST_COUNT_PTR, LAST_COUNT.len()) };
*count_observer.initial_mut() = usize::MAX; // we are minimising!
// Feedback to rate the interestingness of an input
let signals_feedback = MaxMapFeedback::new(&signals_observer);
let count_feedback = MinMapFeedback::new(&count_observer);
let mut feedback = feedback_or_fast!(count_feedback, signals_feedback);
// A feedback to choose if an input is a solution or not
let mut objective = CrashFeedback::new();
// create a State from scratch
let mut state = StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
// States of the feedbacks.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
// Same for objective feedbacks
&mut objective,
)
.unwrap();
// The Monitor trait define how the fuzzer stats are displayed to the user
#[cfg(not(feature = "tui"))]
let mon = SimpleMonitor::new(|s| println!("{s}"));
#[cfg(feature = "tui")]
let ui = TuiUI::with_version(String::from("Baby Fuzzer"), String::from("0.0.1"), false);
#[cfg(feature = "tui")]
let mon = TuiMonitor::new(ui);
// The event manager handle the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus
let mut mgr = SimpleEventManager::new(mon);
// A queue policy to get testcasess from the corpus
let scheduler = QueueScheduler::new();
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// Create the executor for an in-process function with just one observer
let mut executor = InProcessExecutor::new(
&mut harness,
tuple_list!(signals_observer, count_observer),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor");
// a generator here is not generalisable
let initial = MultipartInput::from([
("part", BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])),
("part", BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])),
("part", BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])),
("part", BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])),
]);
fuzzer
.evaluate_input(&mut state, &mut executor, &mut mgr, initial)
.unwrap();
// Setup a mutational stage with a basic bytes mutator
let mutator = StdScheduledMutator::new(havoc_mutations());
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
fuzzer
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
.expect("Error in the fuzzing loop");
}

View File

@ -85,6 +85,8 @@ tui_monitor = ["ratatui", "crossterm"]
## Enables `StringClassificationStage` and associated mutators, which allow for mutations which preserve the Unicode property data
unicode = ["libafl_bolts/alloc", "ahash/std", "serde/rc", "bitvec", "reqwest", "zip"]
## Enable multi-part input formats and mutators
multipart_inputs = ["arrayvec", "rand_trait"]
#! ## LibAFL-Bolts Features
@ -186,6 +188,8 @@ libcasr = { version = "2.7", optional = true }
bitvec = { version = "1.0", optional = true, features = ["serde"] } # used for string range storage
arrayvec = { version = "0.7.4", optional = true, default-features = false } # used for fixed-len collects
# optional-dev deps (change when target.'cfg(accessible(::std))'.test-dependencies will be stable)
serial_test = { version = "2", optional = true, default-features = false, features = ["logging"] }

View File

@ -12,8 +12,14 @@ pub use gramatron::*;
pub mod generalized;
pub use generalized::*;
#[cfg(feature = "multipart_inputs")]
pub mod multi;
#[cfg(feature = "multipart_inputs")]
pub use multi::*;
#[cfg(feature = "nautilus")]
pub mod nautilus;
use alloc::{
boxed::Box,
string::{String, ToString},

165
libafl/src/inputs/multi.rs Normal file
View File

@ -0,0 +1,165 @@
//! Definitions for inputs which have multiple distinct subcomponents.
//!
//! Unfortunately, since both [`serde::de::Deserialize`] and [`Clone`] require [`Sized`], it is not
//! possible to dynamically define a single input with dynamic typing. As such, [`MultipartInput`]
//! requires that each subcomponent be the same subtype.
use alloc::{
string::{String, ToString},
vec::Vec,
};
use arrayvec::ArrayVec;
use serde::{Deserialize, Serialize};
use crate::inputs::Input;
/// An input composed of multiple parts. Use in situations where subcomponents are not necessarily
/// related, or represent distinct parts of the input.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MultipartInput<I> {
parts: Vec<I>,
names: Vec<String>,
}
impl<I> Default for MultipartInput<I> {
fn default() -> Self {
Self::new()
}
}
impl<I> MultipartInput<I> {
/// Create a new multipart input.
#[must_use]
pub fn new() -> Self {
Self {
parts: Vec::new(),
names: Vec::new(),
}
}
fn idxs_to_skips(idxs: &mut [usize]) {
for following in (1..idxs.len()).rev() {
let first = idxs[following - 1];
let second = idxs[following];
idxs[following] = second
.checked_sub(first)
.expect("idxs was not sorted")
.checked_sub(1)
.expect("idxs had duplicate elements");
}
}
/// Get the individual parts of this input.
#[must_use]
pub fn parts(&self) -> &[I] {
&self.parts
}
/// Access multiple parts mutably.
///
/// ## Panics
///
/// Panics if idxs is not sorted, has duplicate elements, or any entry is out of bounds.
#[must_use]
pub fn parts_mut<const N: usize>(&mut self, mut idxs: [usize; N]) -> [&mut I; N] {
Self::idxs_to_skips(&mut idxs);
let mut parts = self.parts.iter_mut();
if let Ok(arr) = idxs
.into_iter()
.map(|i| parts.nth(i).expect("idx had an out of bounds entry"))
.collect::<ArrayVec<_, N>>()
.into_inner()
{
arr
} else {
// avoid Debug trait requirement for expect/unwrap
panic!("arrayvec collection failed somehow")
}
}
/// Get a specific part of this input by index.
pub fn part_mut(&mut self, idx: usize) -> Option<&mut I> {
self.parts.get_mut(idx)
}
/// Get the names associated with the subparts of this input. Used to distinguish between the
/// input components in the case where some parts may or may not be present, or in different
/// orders.
#[must_use]
pub fn names(&self) -> &[String] {
&self.names
}
/// Gets a reference to each part with the provided name.
pub fn parts_by_name<'a, 'b>(
&'b self,
name: &'a str,
) -> impl Iterator<Item = (usize, &'b I)> + 'a
where
'b: 'a,
{
self.names()
.iter()
.zip(&self.parts)
.enumerate()
.filter_map(move |(i, (s, item))| (s == name).then_some((i, item)))
}
/// Gets a mutable reference to each part with the provided name.
pub fn parts_by_name_mut<'a, 'b>(
&'b mut self,
name: &'a str,
) -> impl Iterator<Item = (usize, &'b mut I)> + 'a
where
'b: 'a,
{
self.names
.iter()
.zip(&mut self.parts)
.enumerate()
.filter_map(move |(i, (s, item))| (s == name).then_some((i, item)))
}
/// Adds a part to this input, potentially with the same name as an existing part.
pub fn add_part(&mut self, name: String, part: I) {
self.parts.push(part);
self.names.push(name);
}
/// Iterate over the parts of this input; no order is specified.
pub fn iter(&self) -> impl Iterator<Item = (&str, &I)> {
self.names.iter().map(String::as_ref).zip(self.parts())
}
}
impl<I, It, S> From<It> for MultipartInput<I>
where
It: IntoIterator<Item = (S, I)>,
S: AsRef<str>,
{
fn from(parts: It) -> Self {
let mut input = MultipartInput::new();
for (name, part) in parts {
input.add_part(name.as_ref().to_string(), part);
}
input
}
}
impl<I> Input for MultipartInput<I>
where
I: Input,
{
fn generate_name(&self, idx: usize) -> String {
self.names
.iter()
.cloned()
.zip(self.parts.iter().map(|i| i.generate_name(idx)))
.map(|(name, generated)| format!("{name}-{generated}"))
.collect::<Vec<_>>()
.join(",")
}
}

View File

@ -25,8 +25,14 @@ pub mod string;
#[cfg(feature = "unicode")]
pub use string::*;
#[cfg(feature = "multipart_inputs")]
pub mod multi;
#[cfg(feature = "multipart_inputs")]
pub use multi::*;
#[cfg(feature = "nautilus")]
pub mod nautilus;
use alloc::vec::Vec;
use libafl_bolts::{tuples::HasConstLen, Named};

View File

@ -0,0 +1,330 @@
//! Mutator definitions for [`MultipartInput`]s. See [`crate::inputs::multi`] for details.
use core::cmp::{min, Ordering};
use libafl_bolts::{rands::Rand, Error};
use crate::{
corpus::{Corpus, CorpusId},
impl_default_multipart,
inputs::{multi::MultipartInput, HasBytesVec, Input},
mutators::{
mutations::{
rand_range, BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator,
ByteIncMutator, ByteInterestingMutator, ByteNegMutator, ByteRandMutator,
BytesCopyMutator, BytesDeleteMutator, BytesExpandMutator, BytesInsertCopyMutator,
BytesInsertMutator, BytesRandInsertMutator, BytesRandSetMutator, BytesSetMutator,
BytesSwapMutator, CrossoverInsertMutator, CrossoverReplaceMutator, DwordAddMutator,
DwordInterestingMutator, QwordAddMutator, WordAddMutator, WordInterestingMutator,
},
token_mutations::{I2SRandReplace, TokenInsert, TokenReplace},
MutationResult, Mutator,
},
random_corpus_id,
state::{HasCorpus, HasMaxSize, HasRand},
};
/// Marker trait for if the default multipart input mutator implementation is appropriate.
///
/// You should implement this type for your mutator if you just want a random part of the input to
/// be selected and mutated. Use [`impl_default_multipart`] to implement this marker trait for many
/// at once.
pub trait DefaultMultipartMutator {}
impl<I, M, S> Mutator<MultipartInput<I>, S> for M
where
M: DefaultMultipartMutator + Mutator<I, S>,
S: HasRand,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut MultipartInput<I>,
stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.parts().is_empty() {
Ok(MutationResult::Skipped)
} else {
let selected = state.rand_mut().below(input.parts().len() as u64) as usize;
let mutated = input.part_mut(selected).unwrap();
self.mutate(state, mutated, stage_idx)
}
}
fn post_exec(
&mut self,
state: &mut S,
stage_idx: i32,
corpus_idx: Option<CorpusId>,
) -> Result<(), Error> {
M::post_exec(self, state, stage_idx, corpus_idx)
}
}
mod macros {
/// Implements the marker trait [`super::DefaultMultipartMutator`] for one to many types, e.g.:
///
/// ```rs
/// impl_default_multipart!(
/// // --- havoc ---
/// BitFlipMutator,
/// ByteAddMutator,
/// ByteDecMutator,
/// ByteFlipMutator,
/// ByteIncMutator,
/// ...
/// );
/// ```
#[macro_export]
macro_rules! impl_default_multipart {
($mutator: ty, $($mutators: ty),+$(,)?) => {
impl $crate::mutators::multi::DefaultMultipartMutator for $mutator {}
impl_default_multipart!($($mutators),+);
};
($mutator: ty) => {
impl $crate::mutators::multi::DefaultMultipartMutator for $mutator {}
};
}
}
impl_default_multipart!(
// --- havoc ---
BitFlipMutator,
ByteAddMutator,
ByteDecMutator,
ByteFlipMutator,
ByteIncMutator,
ByteInterestingMutator,
ByteNegMutator,
ByteRandMutator,
BytesCopyMutator,
BytesDeleteMutator,
BytesExpandMutator,
BytesInsertCopyMutator,
BytesInsertMutator,
BytesRandInsertMutator,
BytesRandSetMutator,
BytesSetMutator,
BytesSwapMutator,
// crossover has a custom implementation below
DwordAddMutator,
DwordInterestingMutator,
QwordAddMutator,
WordAddMutator,
WordInterestingMutator,
// --- token ---
TokenInsert,
TokenReplace,
// --- i2s ---
I2SRandReplace,
);
impl<I, S> Mutator<MultipartInput<I>, S> for CrossoverInsertMutator<I>
where
S: HasCorpus<Input = MultipartInput<I>> + HasMaxSize + HasRand,
I: Input + HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut MultipartInput<I>,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
// we can eat the slight bias; number of parts will be small
let name_choice = state.rand_mut().next() as usize;
let part_choice = state.rand_mut().next() as usize;
// We special-case crossover with self
let idx = random_corpus_id!(state.corpus(), state.rand_mut());
if let Some(cur) = state.corpus().current() {
if idx == *cur {
let choice = name_choice % input.names().len();
let name = input.names()[choice].clone();
let other_size = input.parts()[choice].bytes().len();
if other_size < 2 {
return Ok(MutationResult::Skipped);
}
let parts = input.parts_by_name(&name).count() - 1;
if parts == 0 {
return Ok(MutationResult::Skipped);
}
let maybe_size = input
.parts_by_name(&name)
.filter(|&(p, _)| p != choice)
.nth(part_choice % parts)
.map(|(idx, part)| (idx, part.bytes().len()));
if let Some((part_idx, size)) = maybe_size {
let target = state.rand_mut().below(size as u64) as usize;
let range = rand_range(state, other_size, min(other_size, size - target));
let [part, chosen] = match part_idx.cmp(&choice) {
Ordering::Less => input.parts_mut([part_idx, choice]),
Ordering::Equal => {
unreachable!("choice should never equal the part idx!")
}
Ordering::Greater => {
let [chosen, part] = input.parts_mut([choice, part_idx]);
[part, chosen]
}
};
return Ok(Self::crossover_insert(part, size, target, range, chosen));
}
return Ok(MutationResult::Skipped);
}
}
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input(state.corpus())?;
let choice = name_choice % other.names().len();
let name = &other.names()[choice];
let other_size = other.parts()[choice].bytes().len();
if other_size < 2 {
return Ok(MutationResult::Skipped);
}
let parts = input.parts_by_name(name).count();
if parts > 0 {
let (_, part) = input
.parts_by_name_mut(name)
.nth(part_choice % parts)
.unwrap();
drop(other_testcase);
let size = part.bytes().len();
let target = state.rand_mut().below(size as u64) as usize;
let range = rand_range(state, other_size, min(other_size, size - target));
let other_testcase = state.corpus().get(idx)?.borrow_mut();
// No need to load the input again, it'll still be cached.
let other = other_testcase.input().as_ref().unwrap();
Ok(Self::crossover_insert(
part,
size,
target,
range,
&other.parts()[choice],
))
} else {
// just add it!
input.add_part(name.clone(), other.parts()[choice].clone());
Ok(MutationResult::Mutated)
}
}
}
impl<I, S> Mutator<MultipartInput<I>, S> for CrossoverReplaceMutator<I>
where
S: HasCorpus<Input = MultipartInput<I>> + HasMaxSize + HasRand,
I: Input + HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut MultipartInput<I>,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
// we can eat the slight bias; number of parts will be small
let name_choice = state.rand_mut().next() as usize;
let part_choice = state.rand_mut().next() as usize;
// We special-case crossover with self
let idx = random_corpus_id!(state.corpus(), state.rand_mut());
if let Some(cur) = state.corpus().current() {
if idx == *cur {
let choice = name_choice % input.names().len();
let name = input.names()[choice].clone();
let other_size = input.parts()[choice].bytes().len();
if other_size < 2 {
return Ok(MutationResult::Skipped);
}
let parts = input.parts_by_name(&name).count() - 1;
if parts == 0 {
return Ok(MutationResult::Skipped);
}
let maybe_size = input
.parts_by_name(&name)
.filter(|&(p, _)| p != choice)
.nth(part_choice % parts)
.map(|(idx, part)| (idx, part.bytes().len()));
if let Some((part_idx, size)) = maybe_size {
let target = state.rand_mut().below(size as u64) as usize;
let range = rand_range(state, other_size, min(other_size, size - target));
let [part, chosen] = match part_idx.cmp(&choice) {
Ordering::Less => input.parts_mut([part_idx, choice]),
Ordering::Equal => {
unreachable!("choice should never equal the part idx!")
}
Ordering::Greater => {
let [chosen, part] = input.parts_mut([choice, part_idx]);
[part, chosen]
}
};
return Ok(Self::crossover_replace(part, target, range, chosen));
}
return Ok(MutationResult::Skipped);
}
}
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input(state.corpus())?;
let choice = name_choice % other.names().len();
let name = &other.names()[choice];
let other_size = other.parts()[choice].bytes().len();
if other_size < 2 {
return Ok(MutationResult::Skipped);
}
let parts = input.parts_by_name(name).count();
if parts > 0 {
let (_, part) = input
.parts_by_name_mut(name)
.nth(part_choice % parts)
.unwrap();
drop(other_testcase);
let size = part.bytes().len();
let target = state.rand_mut().below(size as u64) as usize;
let range = rand_range(state, other_size, min(other_size, size - target));
let other_testcase = state.corpus().get(idx)?.borrow_mut();
// No need to load the input again, it'll still be cached.
let other = other_testcase.input().as_ref().unwrap();
Ok(Self::crossover_replace(
part,
target,
range,
&other.parts()[choice],
))
} else {
// just add it!
input.add_part(name.clone(), other.parts()[choice].clone());
Ok(MutationResult::Mutated)
}
}
}

View File

@ -1,13 +1,13 @@
//! A wide variety of mutations used during fuzzing.
use alloc::{borrow::ToOwned, vec::Vec};
use core::{cmp::min, mem::size_of, ops::Range};
use core::{cmp::min, marker::PhantomData, mem::size_of, ops::Range};
use libafl_bolts::{rands::Rand, Named};
use crate::{
corpus::Corpus,
inputs::HasBytesVec,
inputs::{HasBytesVec, Input},
mutators::{MutationResult, Mutator},
random_corpus_id,
state::{HasCorpus, HasMaxSize, HasRand},
@ -1085,12 +1085,45 @@ impl BytesSwapMutator {
/// Crossover insert mutation for inputs with a bytes vector
#[derive(Debug, Default)]
pub struct CrossoverInsertMutator;
pub struct CrossoverInsertMutator<I> {
phantom: PhantomData<I>,
}
impl<S> Mutator<S::Input, S> for CrossoverInsertMutator
impl<I: HasBytesVec> CrossoverInsertMutator<I> {
pub(crate) fn crossover_insert(
input: &mut I,
size: usize,
target: usize,
range: Range<usize>,
other: &I,
) -> MutationResult {
input.bytes_mut().resize(size + range.len(), 0);
unsafe {
buffer_self_copy(
input.bytes_mut(),
target,
target + range.len(),
size - target,
);
}
unsafe {
buffer_copy(
input.bytes_mut(),
other.bytes(),
range.start,
target,
range.len(),
);
}
MutationResult::Mutated
}
}
impl<I, S> Mutator<I, S> for CrossoverInsertMutator<I>
where
S: HasCorpus + HasRand + HasMaxSize,
S::Input: HasBytesVec,
S: HasCorpus<Input = I> + HasRand + HasMaxSize,
I: Input + HasBytesVec,
{
fn mutate(
&mut self,
@ -1125,20 +1158,43 @@ where
let range = rand_range(state, other_size, min(other_size, max_size - size));
let target = state.rand_mut().below(size as u64) as usize;
input.bytes_mut().resize(size + range.len(), 0);
unsafe {
buffer_self_copy(
input.bytes_mut(),
target,
target + range.len(),
size - target,
);
}
let other_testcase = state.corpus().get(idx)?.borrow_mut();
// No need to load the input again, it'll still be cached.
let other = other_testcase.input().as_ref().unwrap();
Ok(Self::crossover_insert(input, size, target, range, other))
}
}
impl<I> Named for CrossoverInsertMutator<I> {
fn name(&self) -> &str {
"CrossoverInsertMutator"
}
}
impl<I> CrossoverInsertMutator<I> {
/// Creates a new [`CrossoverInsertMutator`].
#[must_use]
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
}
}
/// Crossover replace mutation for inputs with a bytes vector
#[derive(Debug, Default)]
pub struct CrossoverReplaceMutator<I> {
phantom: PhantomData<I>,
}
impl<I: HasBytesVec> CrossoverReplaceMutator<I> {
pub(crate) fn crossover_replace(
input: &mut I,
target: usize,
range: Range<usize>,
other: &I,
) -> MutationResult {
unsafe {
buffer_copy(
input.bytes_mut(),
@ -1148,32 +1204,14 @@ where
range.len(),
);
}
Ok(MutationResult::Mutated)
MutationResult::Mutated
}
}
impl Named for CrossoverInsertMutator {
fn name(&self) -> &str {
"CrossoverInsertMutator"
}
}
impl CrossoverInsertMutator {
/// Creates a new [`CrossoverInsertMutator`].
#[must_use]
pub fn new() -> Self {
Self
}
}
/// Crossover replace mutation for inputs with a bytes vector
#[derive(Debug, Default)]
pub struct CrossoverReplaceMutator;
impl<S> Mutator<S::Input, S> for CrossoverReplaceMutator
impl<I, S> Mutator<I, S> for CrossoverReplaceMutator<I>
where
S: HasCorpus + HasRand,
S::Input: HasBytesVec,
S: HasCorpus<Input = I> + HasRand,
I: Input + HasBytesVec,
{
fn mutate(
&mut self,
@ -1210,30 +1248,23 @@ where
// No need to load the input again, it'll still be cached.
let other = other_testcase.input().as_ref().unwrap();
unsafe {
buffer_copy(
input.bytes_mut(),
other.bytes(),
range.start,
target,
range.len(),
);
}
Ok(MutationResult::Mutated)
Ok(Self::crossover_replace(input, target, range, other))
}
}
impl Named for CrossoverReplaceMutator {
impl<I> Named for CrossoverReplaceMutator<I> {
fn name(&self) -> &str {
"CrossoverReplaceMutator"
}
}
impl CrossoverReplaceMutator {
impl<I> CrossoverReplaceMutator<I> {
/// Creates a new [`CrossoverReplaceMutator`].
#[must_use]
pub fn new() -> Self {
Self
Self {
phantom: PhantomData,
}
}
}

View File

@ -258,10 +258,11 @@ pub type HavocMutationsNoCrossoverType = tuple_list_type!(
);
/// Tuple type of the mutations that compose the Havoc mutator's crossover mutations
pub type HavocCrossoverType = tuple_list_type!(CrossoverInsertMutator, CrossoverReplaceMutator);
pub type HavocCrossoverType<I> =
tuple_list_type!(CrossoverInsertMutator<I>, CrossoverReplaceMutator<I>);
/// Tuple type of the mutations that compose the Havoc mutator
pub type HavocMutationsType = tuple_list_type!(
pub type HavocMutationsType<I> = tuple_list_type!(
BitFlipMutator,
ByteFlipMutator,
ByteIncMutator,
@ -287,8 +288,8 @@ pub type HavocMutationsType = tuple_list_type!(
BytesCopyMutator,
BytesInsertCopyMutator,
BytesSwapMutator,
CrossoverInsertMutator,
CrossoverReplaceMutator,
CrossoverInsertMutator<I>,
CrossoverReplaceMutator<I>,
);
/// Get the mutations that compose the Havoc mutator (only applied to single inputs)
@ -325,7 +326,7 @@ pub fn havoc_mutations_no_crossover() -> HavocMutationsNoCrossoverType {
/// Get the mutations that compose the Havoc mutator's crossover strategy
#[must_use]
pub fn havoc_crossover() -> HavocCrossoverType {
pub fn havoc_crossover<I>() -> HavocCrossoverType<I> {
tuple_list!(
CrossoverInsertMutator::new(),
CrossoverReplaceMutator::new(),
@ -334,14 +335,14 @@ pub fn havoc_crossover() -> HavocCrossoverType {
/// Get the mutations that compose the Havoc mutator
#[must_use]
pub fn havoc_mutations() -> HavocMutationsType {
pub fn havoc_mutations<I>() -> HavocMutationsType<I> {
havoc_mutations_no_crossover().merge(havoc_crossover())
}
/// Get the mutations that uses the Tokens metadata
#[must_use]
pub fn tokens_mutations() -> tuple_list_type!(TokenInsert, TokenReplace) {
tuple_list!(TokenInsert::new(), TokenReplace::new(),)
tuple_list!(TokenInsert::new(), TokenReplace::new())
}
/// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`].
@ -617,7 +618,7 @@ pub mod pybind {
/// Python class for StdHavocMutator
pub struct PythonStdHavocMutator {
/// Rust wrapped StdHavocMutator object
pub inner: StdScheduledMutator<BytesInput, HavocMutationsType, PythonStdState>,
pub inner: StdScheduledMutator<BytesInput, HavocMutationsType<BytesInput>, PythonStdState>,
}
#[pymethods]

View File

@ -542,6 +542,11 @@ where
)
}
/// Gets the initial value for this map, mutably
pub fn initial_mut(&mut self) -> &mut T {
&mut self.initial
}
/// Gets the backing for this map
pub fn map(&self) -> &OwnedMutSlice<'a, T> {
&self.map