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:
parent
75fcd47044
commit
99fd69acdc
1
fuzzers/baby_fuzzer_multi/.gitignore
vendored
Normal file
1
fuzzers/baby_fuzzer_multi/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
libpng-*
|
24
fuzzers/baby_fuzzer_multi/Cargo.toml
Normal file
24
fuzzers/baby_fuzzer_multi/Cargo.toml
Normal 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/" }
|
10
fuzzers/baby_fuzzer_multi/README.md
Normal file
10
fuzzers/baby_fuzzer_multi/README.md
Normal 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`.
|
164
fuzzers/baby_fuzzer_multi/src/main.rs
Normal file
164
fuzzers/baby_fuzzer_multi/src/main.rs
Normal 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");
|
||||||
|
}
|
@ -85,6 +85,8 @@ tui_monitor = ["ratatui", "crossterm"]
|
|||||||
## Enables `StringClassificationStage` and associated mutators, which allow for mutations which preserve the Unicode property data
|
## 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"]
|
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
|
#! ## 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
|
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)
|
# 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"] }
|
serial_test = { version = "2", optional = true, default-features = false, features = ["logging"] }
|
||||||
|
|
||||||
|
@ -12,8 +12,14 @@ pub use gramatron::*;
|
|||||||
pub mod generalized;
|
pub mod generalized;
|
||||||
pub use generalized::*;
|
pub use generalized::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "multipart_inputs")]
|
||||||
|
pub mod multi;
|
||||||
|
#[cfg(feature = "multipart_inputs")]
|
||||||
|
pub use multi::*;
|
||||||
|
|
||||||
#[cfg(feature = "nautilus")]
|
#[cfg(feature = "nautilus")]
|
||||||
pub mod nautilus;
|
pub mod nautilus;
|
||||||
|
|
||||||
use alloc::{
|
use alloc::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
|
165
libafl/src/inputs/multi.rs
Normal file
165
libafl/src/inputs/multi.rs
Normal 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(",")
|
||||||
|
}
|
||||||
|
}
|
@ -25,8 +25,14 @@ pub mod string;
|
|||||||
#[cfg(feature = "unicode")]
|
#[cfg(feature = "unicode")]
|
||||||
pub use string::*;
|
pub use string::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "multipart_inputs")]
|
||||||
|
pub mod multi;
|
||||||
|
#[cfg(feature = "multipart_inputs")]
|
||||||
|
pub use multi::*;
|
||||||
|
|
||||||
#[cfg(feature = "nautilus")]
|
#[cfg(feature = "nautilus")]
|
||||||
pub mod nautilus;
|
pub mod nautilus;
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use libafl_bolts::{tuples::HasConstLen, Named};
|
use libafl_bolts::{tuples::HasConstLen, Named};
|
||||||
|
330
libafl/src/mutators/multi.rs
Normal file
330
libafl/src/mutators/multi.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
//! A wide variety of mutations used during fuzzing.
|
//! A wide variety of mutations used during fuzzing.
|
||||||
|
|
||||||
use alloc::{borrow::ToOwned, vec::Vec};
|
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 libafl_bolts::{rands::Rand, Named};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::Corpus,
|
corpus::Corpus,
|
||||||
inputs::HasBytesVec,
|
inputs::{HasBytesVec, Input},
|
||||||
mutators::{MutationResult, Mutator},
|
mutators::{MutationResult, Mutator},
|
||||||
random_corpus_id,
|
random_corpus_id,
|
||||||
state::{HasCorpus, HasMaxSize, HasRand},
|
state::{HasCorpus, HasMaxSize, HasRand},
|
||||||
@ -1085,12 +1085,45 @@ impl BytesSwapMutator {
|
|||||||
|
|
||||||
/// Crossover insert mutation for inputs with a bytes vector
|
/// Crossover insert mutation for inputs with a bytes vector
|
||||||
#[derive(Debug, Default)]
|
#[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
|
where
|
||||||
S: HasCorpus + HasRand + HasMaxSize,
|
S: HasCorpus<Input = I> + HasRand + HasMaxSize,
|
||||||
S::Input: HasBytesVec,
|
I: Input + HasBytesVec,
|
||||||
{
|
{
|
||||||
fn mutate(
|
fn mutate(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -1125,20 +1158,43 @@ where
|
|||||||
let range = rand_range(state, other_size, min(other_size, max_size - size));
|
let range = rand_range(state, other_size, min(other_size, max_size - size));
|
||||||
let target = state.rand_mut().below(size as u64) as usize;
|
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();
|
let other_testcase = state.corpus().get(idx)?.borrow_mut();
|
||||||
// No need to load the input again, it'll still be cached.
|
// No need to load the input again, it'll still be cached.
|
||||||
let other = other_testcase.input().as_ref().unwrap();
|
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 {
|
unsafe {
|
||||||
buffer_copy(
|
buffer_copy(
|
||||||
input.bytes_mut(),
|
input.bytes_mut(),
|
||||||
@ -1148,32 +1204,14 @@ where
|
|||||||
range.len(),
|
range.len(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(MutationResult::Mutated)
|
MutationResult::Mutated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Named for CrossoverInsertMutator {
|
impl<I, S> Mutator<I, S> for CrossoverReplaceMutator<I>
|
||||||
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
|
|
||||||
where
|
where
|
||||||
S: HasCorpus + HasRand,
|
S: HasCorpus<Input = I> + HasRand,
|
||||||
S::Input: HasBytesVec,
|
I: Input + HasBytesVec,
|
||||||
{
|
{
|
||||||
fn mutate(
|
fn mutate(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -1210,30 +1248,23 @@ where
|
|||||||
// No need to load the input again, it'll still be cached.
|
// No need to load the input again, it'll still be cached.
|
||||||
let other = other_testcase.input().as_ref().unwrap();
|
let other = other_testcase.input().as_ref().unwrap();
|
||||||
|
|
||||||
unsafe {
|
Ok(Self::crossover_replace(input, target, range, other))
|
||||||
buffer_copy(
|
|
||||||
input.bytes_mut(),
|
|
||||||
other.bytes(),
|
|
||||||
range.start,
|
|
||||||
target,
|
|
||||||
range.len(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(MutationResult::Mutated)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Named for CrossoverReplaceMutator {
|
impl<I> Named for CrossoverReplaceMutator<I> {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"CrossoverReplaceMutator"
|
"CrossoverReplaceMutator"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CrossoverReplaceMutator {
|
impl<I> CrossoverReplaceMutator<I> {
|
||||||
/// Creates a new [`CrossoverReplaceMutator`].
|
/// Creates a new [`CrossoverReplaceMutator`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self
|
Self {
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,10 +258,11 @@ pub type HavocMutationsNoCrossoverType = tuple_list_type!(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Tuple type of the mutations that compose the Havoc mutator's crossover mutations
|
/// 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
|
/// Tuple type of the mutations that compose the Havoc mutator
|
||||||
pub type HavocMutationsType = tuple_list_type!(
|
pub type HavocMutationsType<I> = tuple_list_type!(
|
||||||
BitFlipMutator,
|
BitFlipMutator,
|
||||||
ByteFlipMutator,
|
ByteFlipMutator,
|
||||||
ByteIncMutator,
|
ByteIncMutator,
|
||||||
@ -287,8 +288,8 @@ pub type HavocMutationsType = tuple_list_type!(
|
|||||||
BytesCopyMutator,
|
BytesCopyMutator,
|
||||||
BytesInsertCopyMutator,
|
BytesInsertCopyMutator,
|
||||||
BytesSwapMutator,
|
BytesSwapMutator,
|
||||||
CrossoverInsertMutator,
|
CrossoverInsertMutator<I>,
|
||||||
CrossoverReplaceMutator,
|
CrossoverReplaceMutator<I>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Get the mutations that compose the Havoc mutator (only applied to single inputs)
|
/// 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
|
/// Get the mutations that compose the Havoc mutator's crossover strategy
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn havoc_crossover() -> HavocCrossoverType {
|
pub fn havoc_crossover<I>() -> HavocCrossoverType<I> {
|
||||||
tuple_list!(
|
tuple_list!(
|
||||||
CrossoverInsertMutator::new(),
|
CrossoverInsertMutator::new(),
|
||||||
CrossoverReplaceMutator::new(),
|
CrossoverReplaceMutator::new(),
|
||||||
@ -334,14 +335,14 @@ pub fn havoc_crossover() -> HavocCrossoverType {
|
|||||||
|
|
||||||
/// Get the mutations that compose the Havoc mutator
|
/// Get the mutations that compose the Havoc mutator
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn havoc_mutations() -> HavocMutationsType {
|
pub fn havoc_mutations<I>() -> HavocMutationsType<I> {
|
||||||
havoc_mutations_no_crossover().merge(havoc_crossover())
|
havoc_mutations_no_crossover().merge(havoc_crossover())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the mutations that uses the Tokens metadata
|
/// Get the mutations that uses the Tokens metadata
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn tokens_mutations() -> tuple_list_type!(TokenInsert, TokenReplace) {
|
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`].
|
/// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`].
|
||||||
@ -617,7 +618,7 @@ pub mod pybind {
|
|||||||
/// Python class for StdHavocMutator
|
/// Python class for StdHavocMutator
|
||||||
pub struct PythonStdHavocMutator {
|
pub struct PythonStdHavocMutator {
|
||||||
/// Rust wrapped StdHavocMutator object
|
/// Rust wrapped StdHavocMutator object
|
||||||
pub inner: StdScheduledMutator<BytesInput, HavocMutationsType, PythonStdState>,
|
pub inner: StdScheduledMutator<BytesInput, HavocMutationsType<BytesInput>, PythonStdState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
|
@ -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
|
/// Gets the backing for this map
|
||||||
pub fn map(&self) -> &OwnedMutSlice<'a, T> {
|
pub fn map(&self) -> &OwnedMutSlice<'a, T> {
|
||||||
&self.map
|
&self.map
|
||||||
|
Loading…
x
Reference in New Issue
Block a user