Introduce ListInput (#2972)
* Introduce ListInput * Add remove mutators for ListInput * Merge ListInput and MultipartInput * Reimplement MultipartInput as a special case of ListInput * Revert changes to Cargo.toml * Add collection of generic listinput mutators * Fix example * Add note to MIGRATION * Split list and multi into separate modules * Fix docs * Using string names again in the multi example fuzzer * Remove unnecessary code * Fix fuzzer * Use key instead of name for MultipartInput key * Prettier code in example fuzzer * Do not convert slice to vec manually
This commit is contained in:
parent
98ef505a0e
commit
1eef4ffb58
@ -13,6 +13,9 @@
|
||||
- There is a `ClientStatsManager` to manage client statistics, and is owned by `EventManager`. Most of previous `Monitor`'s trait methods have been moved to the `ClientStatsManager`.
|
||||
- `user_monitor` has been renamed to `user_stats`, `introspection_monitor` has been renamed to `introspection_stats`, perf-related structure definitions have been renamed, and all were moved to the `stats` module.
|
||||
- `OnDiskTomlMonitor`, `OnDiskJsonMonitor`, `OnDiskJsonAggregateMonitor` are now no longer takes a base monitor to wrap. If you want to use multiple monitors together, simply use a `tuple_list`.
|
||||
- `MultipartInput` is now implemented as key-value tuples in a `ListInput`. The interface slightly changed, all functionality is maintained.
|
||||
- Instead of names, `MultipartInput` uses generic `key`s (function names were changed accordingly).
|
||||
- If you don't need the keys to identify individual parts, consider using `ListInput` directly.
|
||||
|
||||
## 0.14.1 -> 0.15.0
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
#[cfg(windows)]
|
||||
use std::ptr::write_volatile;
|
||||
use std::{iter, path::PathBuf};
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
use libafl::monitors::tui::TuiMonitor;
|
||||
@ -43,9 +43,9 @@ fn count_set(count: usize) {
|
||||
#[expect(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 mut harness = |input: &MultipartInput<BytesInput, String>| {
|
||||
let mut count = input.len();
|
||||
for (i, (_name, input)) in input.parts().iter().enumerate() {
|
||||
let target = input.target_bytes();
|
||||
let buf = target.as_slice();
|
||||
signals_set(i * 8);
|
||||
@ -143,12 +143,9 @@ pub fn main() {
|
||||
.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'])),
|
||||
]);
|
||||
let initial = MultipartInput::from(
|
||||
iter::repeat(("part".to_string(), BytesInput::from(&b"hello"[..]))).take(4),
|
||||
);
|
||||
|
||||
fuzzer
|
||||
.evaluate_input(&mut state, &mut executor, &mut mgr, &initial)
|
||||
|
386
libafl/src/inputs/list.rs
Normal file
386
libafl/src/inputs/list.rs
Normal file
@ -0,0 +1,386 @@
|
||||
//! 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, [`ListInput`]
|
||||
//! requires that each subcomponent be the same subtype.
|
||||
|
||||
use alloc::{
|
||||
borrow::Cow,
|
||||
string::{String, ToString as _},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::num::NonZero;
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use libafl_bolts::{
|
||||
rands::Rand as _,
|
||||
tuples::{Map, MappingFunctor},
|
||||
Error, Named,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
corpus::CorpusId,
|
||||
inputs::Input,
|
||||
mutators::{MutationResult, Mutator},
|
||||
state::HasRand,
|
||||
};
|
||||
|
||||
/// 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, Hash)]
|
||||
pub struct ListInput<I> {
|
||||
parts: Vec<I>,
|
||||
}
|
||||
|
||||
impl<I> Default for ListInput<I> {
|
||||
fn default() -> Self {
|
||||
Self::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> ListInput<I> {
|
||||
/// Create a new empty [`ListInput`].
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn empty() -> Self {
|
||||
Self { parts: Vec::new() }
|
||||
}
|
||||
|
||||
/// Create a new [`ListInput`] with the given parts.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn new(parts: Vec<I>) -> Self {
|
||||
Self { parts }
|
||||
}
|
||||
|
||||
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]
|
||||
#[inline]
|
||||
pub fn parts(&self) -> &[I] {
|
||||
&self.parts
|
||||
}
|
||||
|
||||
/// Get the individual parts of this input.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn parts_mut(&mut self) -> &mut [I] {
|
||||
&mut self.parts
|
||||
}
|
||||
|
||||
/// Get a specific part of this input by index.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn part_at_index(&self, idx: usize) -> Option<&I> {
|
||||
self.parts.get(idx)
|
||||
}
|
||||
|
||||
/// Get a specific part of this input by index.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn part_at_index_mut(&mut self, idx: usize) -> Option<&mut I> {
|
||||
self.parts.get_mut(idx)
|
||||
}
|
||||
|
||||
/// 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_at_indices_mut<const C: usize>(&mut self, mut idxs: [usize; C]) -> [&mut I; C] {
|
||||
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<_, C>>()
|
||||
.into_inner()
|
||||
{
|
||||
arr
|
||||
} else {
|
||||
// avoid Debug trait requirement for expect/unwrap
|
||||
panic!("arrayvec collection failed somehow")
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a part to this input
|
||||
#[inline]
|
||||
pub fn append_part(&mut self, part: I) {
|
||||
self.parts.push(part);
|
||||
}
|
||||
|
||||
/// Inserts a part to this input at the given index
|
||||
#[inline]
|
||||
pub fn insert_part(&mut self, idx: usize, part: I) {
|
||||
self.parts.insert(idx, part);
|
||||
}
|
||||
|
||||
/// Removes a part from this input at the given index.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Panics if the index is out of bounds.
|
||||
#[inline]
|
||||
pub fn remove_part_at_index(&mut self, idx: usize) {
|
||||
self.parts.remove(idx);
|
||||
}
|
||||
|
||||
/// Removes the last part from this input.
|
||||
///
|
||||
/// Returns [`None`] if the input is empty.
|
||||
#[inline]
|
||||
pub fn pop_part(&mut self) -> Option<I> {
|
||||
self.parts.pop()
|
||||
}
|
||||
|
||||
/// Get the number of parts in this input.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.parts.len()
|
||||
}
|
||||
|
||||
/// Check if this input has no parts.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.parts.is_empty()
|
||||
}
|
||||
|
||||
/// Map a tuple of mutators targeting [`ListInput`]'s inner type to a tuple of mutators able to work on the entire [`ListInput`],
|
||||
/// by mutating on the last part. If the input is empty, [`MutationResult::Skipped`] is returned.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn map_to_mutate_on_last_part<M: Map<ToLastEntryMutator>>(
|
||||
inner: M,
|
||||
) -> <M as Map<ToLastEntryMutator>>::MapResult {
|
||||
inner.map(ToLastEntryMutator)
|
||||
}
|
||||
|
||||
/// Map a tuple of mutators targeting [`ListInput`]'s inner type to a tuple of mutators able to work on the entire [`ListInput`],
|
||||
/// by mutating on a random part. If the input is empty, [`MutationResult::Skipped`] is returned.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn map_to_mutate_on_random_part<M: Map<ToRandomEntryMutator>>(
|
||||
inner: M,
|
||||
) -> <M as Map<ToRandomEntryMutator>>::MapResult {
|
||||
inner.map(ToRandomEntryMutator)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, It> From<It> for ListInput<I>
|
||||
where
|
||||
It: IntoIterator<Item = I>,
|
||||
{
|
||||
fn from(parts: It) -> Self {
|
||||
let vec = parts.into_iter().collect();
|
||||
Self { parts: vec }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Input for ListInput<I>
|
||||
where
|
||||
I: Input,
|
||||
{
|
||||
fn generate_name(&self, id: Option<CorpusId>) -> String {
|
||||
if self.parts.is_empty() {
|
||||
"empty_list_input".to_string() // empty strings cause issues with OnDiskCorpus
|
||||
} else {
|
||||
self.parts
|
||||
.iter()
|
||||
.map(|input| input.generate_name(id))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutator that applies mutations to the last element of a [`ListInput`].
|
||||
///
|
||||
/// If the input is empty, [`MutationResult::Skipped`] is returned.
|
||||
#[derive(Debug)]
|
||||
pub struct LastEntryMutator<M> {
|
||||
inner: M,
|
||||
name: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl<M: Named> LastEntryMutator<M> {
|
||||
/// Create a new [`LastEntryMutator`].
|
||||
#[must_use]
|
||||
pub fn new(inner: M) -> Self {
|
||||
let name = Cow::Owned(format!("LastEntryMutator<{}>", inner.name()));
|
||||
Self { inner, name }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, M, S> Mutator<ListInput<I>, S> for LastEntryMutator<M>
|
||||
where
|
||||
M: Mutator<I, S>,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut ListInput<I>) -> Result<MutationResult, Error> {
|
||||
match input.parts.len() {
|
||||
0 => Ok(MutationResult::Skipped),
|
||||
len => self.inner.mutate(state, &mut input.parts[len - 1]),
|
||||
}
|
||||
}
|
||||
|
||||
fn post_exec(&mut self, state: &mut S, new_corpus_id: Option<CorpusId>) -> Result<(), Error> {
|
||||
self.inner.post_exec(state, new_corpus_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mapping functor to convert mutators to [`LastEntryMutator`].
|
||||
#[derive(Debug)]
|
||||
pub struct ToLastEntryMutator;
|
||||
|
||||
impl<M: Named> MappingFunctor<M> for ToLastEntryMutator {
|
||||
type Output = LastEntryMutator<M>;
|
||||
|
||||
fn apply(&mut self, from: M) -> Self::Output {
|
||||
LastEntryMutator::new(from)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> Named for LastEntryMutator<M> {
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutator that applies mutations to a random element of a [`ListInput`].
|
||||
///
|
||||
/// If the input is empty, [`MutationResult::Skipped`] is returned.
|
||||
#[derive(Debug)]
|
||||
pub struct RandomEntryMutator<M> {
|
||||
inner: M,
|
||||
name: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl<M: Named> RandomEntryMutator<M> {
|
||||
/// Create a new [`RandomEntryMutator`].
|
||||
#[must_use]
|
||||
pub fn new(inner: M) -> Self {
|
||||
let name = Cow::Owned(format!("RandomEntryMutator<{}>", inner.name()));
|
||||
Self { inner, name }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, M, S> Mutator<ListInput<I>, S> for RandomEntryMutator<M>
|
||||
where
|
||||
M: Mutator<I, S>,
|
||||
S: HasRand,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut ListInput<I>) -> Result<MutationResult, Error> {
|
||||
let rand = state.rand_mut();
|
||||
match input.parts.len() {
|
||||
0 => Ok(MutationResult::Skipped),
|
||||
len => {
|
||||
let index = rand.below(unsafe { NonZero::new_unchecked(len) });
|
||||
self.inner.mutate(state, &mut input.parts[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn post_exec(&mut self, state: &mut S, new_corpus_id: Option<CorpusId>) -> Result<(), Error> {
|
||||
self.inner.post_exec(state, new_corpus_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mapping functor to convert mutators to [`RandomEntryMutator`].
|
||||
#[derive(Debug)]
|
||||
pub struct ToRandomEntryMutator;
|
||||
|
||||
impl<M: Named> MappingFunctor<M> for ToRandomEntryMutator {
|
||||
type Output = RandomEntryMutator<M>;
|
||||
|
||||
fn apply(&mut self, from: M) -> Self::Output {
|
||||
RandomEntryMutator::new(from)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> Named for RandomEntryMutator<M> {
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tuple_list::tuple_list;
|
||||
|
||||
use super::ListInput;
|
||||
use crate::{
|
||||
inputs::ValueInput,
|
||||
mutators::{numeric::IncMutator, MutationResult, MutatorsTuple as _},
|
||||
state::NopState,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn map_to_mutate_on_last_part() {
|
||||
let mutator = tuple_list!(IncMutator);
|
||||
let mut mapped_mutator = ListInput::<ValueInput<u8>>::map_to_mutate_on_last_part(mutator);
|
||||
let mut input: ListInput<ValueInput<u8>> =
|
||||
ListInput::from(vec![ValueInput::new(1_u8), ValueInput::new(2)]);
|
||||
let mut state = NopState::<ListInput<ValueInput<u8>>>::new();
|
||||
let res = mapped_mutator.mutate_all(&mut state, &mut input);
|
||||
assert_eq!(res.unwrap(), MutationResult::Mutated);
|
||||
assert_eq!(input.parts, vec![ValueInput::new(1), ValueInput::new(3)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_to_mutate_on_last_part_empty() {
|
||||
let mutator = tuple_list!(IncMutator);
|
||||
let mut mapped_mutator = ListInput::<ValueInput<u8>>::map_to_mutate_on_last_part(mutator);
|
||||
let mut input = ListInput::<ValueInput<u8>>::default();
|
||||
let mut state = NopState::<ListInput<ValueInput<u8>>>::new();
|
||||
let res = mapped_mutator.mutate_all(&mut state, &mut input);
|
||||
assert_eq!(res.unwrap(), MutationResult::Skipped);
|
||||
assert_eq!(input.parts, vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_to_mutate_on_random_part() {
|
||||
let mutator = tuple_list!(IncMutator);
|
||||
let mut mapped_mutator = ListInput::<ValueInput<u8>>::map_to_mutate_on_random_part(mutator);
|
||||
let initial_input = vec![ValueInput::new(1_u8), ValueInput::new(2)];
|
||||
let mut input = ListInput::<ValueInput<u8>>::from(initial_input.clone());
|
||||
let mut state = NopState::<ListInput<ValueInput<u8>>>::new();
|
||||
let res = mapped_mutator.mutate_all(&mut state, &mut input);
|
||||
assert_eq!(res.unwrap(), MutationResult::Mutated);
|
||||
assert_eq!(
|
||||
1,
|
||||
input
|
||||
.parts
|
||||
.iter()
|
||||
.zip(initial_input.iter())
|
||||
.filter(|&(a, b)| a != b)
|
||||
.count()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_to_mutate_on_random_part_empty() {
|
||||
let mutator = tuple_list!(IncMutator);
|
||||
let mut mapped_mutator = ListInput::<ValueInput<u8>>::map_to_mutate_on_random_part(mutator);
|
||||
let mut input = ListInput::<ValueInput<u8>>::default();
|
||||
let mut state = NopState::<ListInput<ValueInput<u8>>>::new();
|
||||
let res = mapped_mutator.mutate_all(&mut state, &mut input);
|
||||
assert_eq!(res.unwrap(), MutationResult::Skipped);
|
||||
assert_eq!(input.parts, vec![]);
|
||||
}
|
||||
}
|
@ -22,6 +22,10 @@ pub use bytessub::BytesSubInput;
|
||||
pub mod multi;
|
||||
#[cfg(feature = "multipart_inputs")]
|
||||
pub use multi::*;
|
||||
#[cfg(feature = "multipart_inputs")]
|
||||
pub mod list;
|
||||
#[cfg(feature = "multipart_inputs")]
|
||||
pub use list::*;
|
||||
|
||||
#[cfg(feature = "nautilus")]
|
||||
pub mod nautilus;
|
||||
|
@ -1,169 +1,92 @@
|
||||
//! 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.
|
||||
//! An input composed of multiple parts identified by a key.
|
||||
|
||||
use alloc::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
use alloc::{fmt::Debug, string::String, vec::Vec};
|
||||
use core::hash::Hash;
|
||||
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
use crate::{
|
||||
corpus::CorpusId,
|
||||
inputs::{Input, ListInput},
|
||||
};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use serde::{Deserialize, Serialize};
|
||||
/// An input composed of multiple parts, each identified by a key.
|
||||
///
|
||||
/// It relies on a list to store the keys and parts. Keys may appear multiple times.
|
||||
pub type MultipartInput<I, K> = ListInput<(K, I)>;
|
||||
|
||||
use crate::{corpus::CorpusId, 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, Hash)]
|
||||
pub struct MultipartInput<I> {
|
||||
parts: Vec<I>,
|
||||
names: Vec<String>,
|
||||
}
|
||||
|
||||
impl<I> Default for MultipartInput<I> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
impl<I, K> Input for MultipartInput<I, K>
|
||||
where
|
||||
I: Input,
|
||||
K: PartialEq + Debug + Serialize + DeserializeOwned + Clone + Hash,
|
||||
{
|
||||
fn generate_name(&self, id: Option<CorpusId>) -> String {
|
||||
self.parts()
|
||||
.iter()
|
||||
.map(|(_k, i)| i.generate_name(id))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
/// Trait for types that provide a way to access parts by key.
|
||||
pub trait Keyed<K, V> {
|
||||
/// Get the keys of the parts of this input.
|
||||
///
|
||||
/// ## 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);
|
||||
/// Keys may appear multiple times if they are used multiple times in the input.
|
||||
fn keys<'a>(&'a self) -> impl Iterator<Item = &'a K>
|
||||
where
|
||||
K: 'a;
|
||||
|
||||
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
|
||||
/// Get a reference to each part with the provided key along with its index.
|
||||
fn with_key<'a, 'b>(&'b self, key: &'a K) -> impl Iterator<Item = (usize, &'b V)> + 'a
|
||||
where
|
||||
'b: 'a,
|
||||
V: 'b;
|
||||
|
||||
/// Gets a mutable reference to each part with the provided key along with its index.
|
||||
fn with_key_mut<'a, 'b>(
|
||||
&'b mut self,
|
||||
key: &'a K,
|
||||
) -> impl Iterator<Item = (usize, &'b mut V)> + 'a
|
||||
where
|
||||
'b: 'a,
|
||||
V: 'b;
|
||||
}
|
||||
|
||||
impl<I, K> Keyed<K, I> for MultipartInput<I, K>
|
||||
where
|
||||
K: PartialEq,
|
||||
{
|
||||
fn keys<'a>(&'a self) -> impl Iterator<Item = &'a K>
|
||||
where
|
||||
K: 'a,
|
||||
{
|
||||
self.names()
|
||||
.iter()
|
||||
.zip(&self.parts)
|
||||
.enumerate()
|
||||
.filter_map(move |(i, (s, item))| (s == name).then_some((i, item)))
|
||||
self.parts().iter().map(|(k, _)| k)
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to each part with the provided name.
|
||||
pub fn parts_by_name_mut<'a, 'b>(
|
||||
fn with_key<'a, 'b>(&'b self, key: &'a K) -> impl Iterator<Item = (usize, &'b I)> + 'a
|
||||
where
|
||||
'b: 'a,
|
||||
I: 'b,
|
||||
{
|
||||
self.parts()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(move |(i, (k, input))| (key == k).then_some((i, input)))
|
||||
}
|
||||
|
||||
fn with_key_mut<'a, 'b>(
|
||||
&'b mut self,
|
||||
name: &'a str,
|
||||
key: &'a K,
|
||||
) -> impl Iterator<Item = (usize, &'b mut I)> + 'a
|
||||
where
|
||||
'b: 'a,
|
||||
I: 'b,
|
||||
{
|
||||
self.names
|
||||
.iter()
|
||||
.zip(&mut self.parts)
|
||||
self.parts_mut()
|
||||
.iter_mut()
|
||||
.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, id: Option<CorpusId>) -> String {
|
||||
if self.names().is_empty() {
|
||||
"empty_multipart_input".to_string() // empty strings cause issues with OnDiskCorpus
|
||||
} else {
|
||||
self.names
|
||||
.iter()
|
||||
.cloned()
|
||||
.zip(self.parts.iter().map(|i| i.generate_name(id)))
|
||||
.map(|(name, generated)| format!("{name}-{generated}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
}
|
||||
.filter_map(move |(i, (k, input))| (key == k).then_some((i, input)))
|
||||
}
|
||||
}
|
||||
|
223
libafl/src/mutators/list.rs
Normal file
223
libafl/src/mutators/list.rs
Normal file
@ -0,0 +1,223 @@
|
||||
//! Mutator definitions for [`ListInput`]s. See [`crate::inputs::list`] for details.
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use core::num::NonZero;
|
||||
|
||||
use libafl_bolts::{rands::Rand, Error, Named};
|
||||
use tuple_list::{tuple_list, tuple_list_type};
|
||||
|
||||
use crate::{
|
||||
corpus::Corpus,
|
||||
generators::Generator,
|
||||
inputs::{multi::MultipartInput, Input, ListInput},
|
||||
mutators::{MutationResult, Mutator},
|
||||
random_corpus_id,
|
||||
state::{HasCorpus, HasMaxSize, HasRand},
|
||||
};
|
||||
|
||||
/// A list of mutators that can be used on a [`ListInput`].
|
||||
pub type GenericListInputMutators = tuple_list_type!(
|
||||
RemoveLastEntryMutator,
|
||||
RemoveRandomEntryMutator,
|
||||
CrossoverInsertMutator,
|
||||
CrossoverReplaceMutator
|
||||
);
|
||||
|
||||
/// Create a list of mutators that can be used on a [`ListInput`].
|
||||
///
|
||||
/// You may also want to use [`GenerateToAppendMutator`]
|
||||
#[must_use]
|
||||
pub fn generic_list_input_mutators() -> GenericListInputMutators {
|
||||
tuple_list!(
|
||||
RemoveLastEntryMutator,
|
||||
RemoveRandomEntryMutator,
|
||||
CrossoverInsertMutator,
|
||||
CrossoverReplaceMutator
|
||||
)
|
||||
}
|
||||
|
||||
/// Mutator that generates a new input and appends it to the list.
|
||||
#[derive(Debug)]
|
||||
pub struct GenerateToAppendMutator<G> {
|
||||
generator: G,
|
||||
}
|
||||
|
||||
impl<G> GenerateToAppendMutator<G> {
|
||||
/// Create a new `GenerateToAppendMutator`.
|
||||
#[must_use]
|
||||
pub fn new(generator: G) -> Self {
|
||||
Self { generator }
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, I, S> Mutator<ListInput<I>, S> for GenerateToAppendMutator<G>
|
||||
where
|
||||
G: Generator<I, S>,
|
||||
I: Input,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut ListInput<I>) -> Result<MutationResult, Error> {
|
||||
let generated = self.generator.generate(state)?;
|
||||
input.append_part(generated);
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> Named for GenerateToAppendMutator<G> {
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
&Cow::Borrowed("GenerateToAppendMutator")
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutator that removes the last entry from a [`MultipartInput`].
|
||||
///
|
||||
/// Returns [`MutationResult::Skipped`] if the input is empty.
|
||||
#[derive(Debug)]
|
||||
pub struct RemoveLastEntryMutator;
|
||||
|
||||
impl<I, K, S> Mutator<MultipartInput<I, K>, S> for RemoveLastEntryMutator
|
||||
where
|
||||
K: Default,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
input: &mut MultipartInput<I, K>,
|
||||
) -> Result<MutationResult, Error> {
|
||||
match input.pop_part() {
|
||||
Some(_) => Ok(MutationResult::Mutated),
|
||||
None => Ok(MutationResult::Skipped),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for RemoveLastEntryMutator {
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
&Cow::Borrowed("RemoveLastEntryMutator")
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutator that removes a random entry from a [`MultipartInput`].
|
||||
///
|
||||
/// Returns [`MutationResult::Skipped`] if the input is empty.
|
||||
#[derive(Debug)]
|
||||
pub struct RemoveRandomEntryMutator;
|
||||
|
||||
impl<I, K, S> Mutator<MultipartInput<I, K>, S> for RemoveRandomEntryMutator
|
||||
where
|
||||
S: HasRand,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut MultipartInput<I, K>,
|
||||
) -> Result<MutationResult, Error> {
|
||||
match MultipartInput::len(input) {
|
||||
0 => Ok(MutationResult::Skipped),
|
||||
len => {
|
||||
// Safety: null checks are done above
|
||||
let index = state
|
||||
.rand_mut()
|
||||
.below(unsafe { NonZero::new_unchecked(len) });
|
||||
input.remove_part_at_index(index);
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for RemoveRandomEntryMutator {
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
&Cow::Borrowed("RemoveRandomEntryMutator")
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutator that inserts a random part from another [`MultipartInput`] into the current input.
|
||||
#[derive(Debug)]
|
||||
pub struct CrossoverInsertMutator;
|
||||
|
||||
impl<I, K, S> Mutator<MultipartInput<I, K>, S> for CrossoverInsertMutator
|
||||
where
|
||||
S: HasCorpus<MultipartInput<I, K>> + HasMaxSize + HasRand,
|
||||
I: Clone,
|
||||
K: Clone,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut MultipartInput<I, K>,
|
||||
) -> Result<MutationResult, Error> {
|
||||
let current_idx = match input.len() {
|
||||
0 => return Ok(MutationResult::Skipped),
|
||||
len => state
|
||||
.rand_mut()
|
||||
.below(unsafe { NonZero::new_unchecked(len) }),
|
||||
};
|
||||
let other_idx_raw = state.rand_mut().next() as usize;
|
||||
|
||||
let id = random_corpus_id!(state.corpus(), state.rand_mut());
|
||||
let mut testcase = state.corpus().get(id)?.borrow_mut();
|
||||
let other = testcase.load_input(state.corpus())?;
|
||||
|
||||
let other_len = other.len();
|
||||
|
||||
let (key, part) = match other_len {
|
||||
0 => return Ok(MutationResult::Skipped),
|
||||
len => other.parts()[other_idx_raw % len].clone(),
|
||||
};
|
||||
|
||||
input.insert_part(current_idx, (key, part));
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for CrossoverInsertMutator {
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
&Cow::Borrowed("CrossoverInsertMutator")
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutator that replaces a random part from the current [`MultipartInput`] with a random part from another input.
|
||||
#[derive(Debug)]
|
||||
pub struct CrossoverReplaceMutator;
|
||||
|
||||
impl<I, K, S> Mutator<MultipartInput<I, K>, S> for CrossoverReplaceMutator
|
||||
where
|
||||
S: HasCorpus<MultipartInput<I, K>> + HasMaxSize + HasRand,
|
||||
I: Clone,
|
||||
K: Clone,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut MultipartInput<I, K>,
|
||||
) -> Result<MutationResult, Error> {
|
||||
let current_idx = match input.len() {
|
||||
0 => return Ok(MutationResult::Skipped),
|
||||
len => state
|
||||
.rand_mut()
|
||||
.below(unsafe { NonZero::new_unchecked(len) }),
|
||||
};
|
||||
let other_idx_raw = state.rand_mut().next() as usize;
|
||||
|
||||
let id = random_corpus_id!(state.corpus(), state.rand_mut());
|
||||
let mut testcase = state.corpus().get(id)?.borrow_mut();
|
||||
let other = testcase.load_input(state.corpus())?;
|
||||
|
||||
let other_len = other.len();
|
||||
|
||||
let (key, part) = match other_len {
|
||||
0 => return Ok(MutationResult::Skipped),
|
||||
len => other.parts()[other_idx_raw % len].clone(),
|
||||
};
|
||||
|
||||
input.remove_part_at_index(current_idx);
|
||||
input.insert_part(current_idx, (key, part));
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for CrossoverReplaceMutator {
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
&Cow::Borrowed("CrossoverReplaceMutator")
|
||||
}
|
||||
}
|
@ -39,9 +39,9 @@ pub mod unicode;
|
||||
pub use unicode::*;
|
||||
|
||||
#[cfg(feature = "multipart_inputs")]
|
||||
pub mod multi;
|
||||
pub mod list;
|
||||
#[cfg(feature = "multipart_inputs")]
|
||||
pub use multi::*;
|
||||
pub mod multi;
|
||||
|
||||
#[cfg(feature = "nautilus")]
|
||||
pub mod nautilus;
|
||||
|
@ -10,14 +10,15 @@ use libafl_bolts::{rands::Rand, Error};
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId},
|
||||
impl_default_multipart,
|
||||
inputs::{multi::MultipartInput, HasMutatorBytes, Input, ResizableMutator},
|
||||
inputs::{multi::MultipartInput, HasMutatorBytes, Input, Keyed as _, ResizableMutator},
|
||||
mutators::{
|
||||
mutations::{
|
||||
rand_range, BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator,
|
||||
ByteIncMutator, ByteInterestingMutator, ByteNegMutator, ByteRandMutator,
|
||||
BytesCopyMutator, BytesDeleteMutator, BytesExpandMutator, BytesInsertCopyMutator,
|
||||
BytesInsertMutator, BytesRandInsertMutator, BytesRandSetMutator, BytesSetMutator,
|
||||
BytesSwapMutator, CrossoverInsertMutator, CrossoverReplaceMutator, DwordAddMutator,
|
||||
BytesSwapMutator, CrossoverInsertMutator as BytesInputCrossoverInsertMutator,
|
||||
CrossoverReplaceMutator as BytesInputCrossoverReplaceMutator, DwordAddMutator,
|
||||
DwordInterestingMutator, QwordAddMutator, WordAddMutator, WordInterestingMutator,
|
||||
},
|
||||
token_mutations::{I2SRandReplace, TokenInsert, TokenReplace},
|
||||
@ -34,7 +35,7 @@ use crate::{
|
||||
/// at once.
|
||||
pub trait DefaultMultipartMutator {}
|
||||
|
||||
impl<I, M, S> Mutator<MultipartInput<I>, S> for M
|
||||
impl<I, K, M, S> Mutator<MultipartInput<I, K>, S> for M
|
||||
where
|
||||
M: DefaultMultipartMutator + Mutator<I, S>,
|
||||
S: HasRand,
|
||||
@ -42,14 +43,16 @@ where
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut MultipartInput<I>,
|
||||
input: &mut MultipartInput<I, K>,
|
||||
) -> Result<MutationResult, Error> {
|
||||
let Some(parts_len) = NonZero::new(input.parts().len()) else {
|
||||
return Ok(MutationResult::Skipped);
|
||||
};
|
||||
let selected = state.rand_mut().below(parts_len);
|
||||
let mutated = input.part_mut(selected).unwrap();
|
||||
self.mutate(state, mutated)
|
||||
match NonZero::new(input.len()) {
|
||||
None => Ok(MutationResult::Skipped),
|
||||
Some(len) => {
|
||||
let idx = state.rand_mut().below(len);
|
||||
let (_key, part) = &mut input.parts_mut()[idx];
|
||||
self.mutate(state, part)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn post_exec(&mut self, state: &mut S, new_corpus_id: Option<CorpusId>) -> Result<(), Error> {
|
||||
@ -116,43 +119,47 @@ impl_default_multipart!(
|
||||
I2SRandReplace,
|
||||
);
|
||||
|
||||
impl<I, S> Mutator<MultipartInput<I>, S> for CrossoverInsertMutator
|
||||
impl<I, K, S> Mutator<MultipartInput<I, K>, S> for BytesInputCrossoverInsertMutator
|
||||
where
|
||||
S: HasCorpus<MultipartInput<I>> + HasMaxSize + HasRand,
|
||||
S: HasCorpus<MultipartInput<I, K>> + HasMaxSize + HasRand,
|
||||
I: Input + ResizableMutator<u8> + HasMutatorBytes,
|
||||
K: Clone + PartialEq,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut MultipartInput<I>,
|
||||
input: &mut MultipartInput<I, K>,
|
||||
) -> 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 key_choice = state.rand_mut().next() as usize;
|
||||
let part_choice = state.rand_mut().next() as usize;
|
||||
|
||||
// We special-case crossover with self
|
||||
let id = random_corpus_id!(state.corpus(), state.rand_mut());
|
||||
if let Some(cur) = state.corpus().current() {
|
||||
if id == *cur {
|
||||
if input.names().is_empty() {
|
||||
let len = input.len();
|
||||
if len == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
let choice = name_choice % input.names().len();
|
||||
let name = input.names()[choice].clone();
|
||||
let choice = key_choice % len;
|
||||
// Safety: len is checked above
|
||||
let (key, part) = &input.parts()[choice];
|
||||
|
||||
let other_size = part.mutator_bytes().len();
|
||||
|
||||
let other_size = input.parts()[choice].mutator_bytes().len();
|
||||
if other_size < 2 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let parts = input.parts_by_name(&name).count() - 1;
|
||||
let parts = input.with_key(key).count() - 1;
|
||||
|
||||
if parts == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let maybe_size = input
|
||||
.parts_by_name(&name)
|
||||
.with_key(key)
|
||||
.filter(|&(p, _)| p != choice)
|
||||
.nth(part_choice % parts)
|
||||
.map(|(id, part)| (id, part.mutator_bytes().len()));
|
||||
@ -171,22 +178,22 @@ where
|
||||
});
|
||||
|
||||
let [part, chosen] = match part_idx.cmp(&choice) {
|
||||
Ordering::Less => input.parts_mut([part_idx, choice]),
|
||||
Ordering::Less => input.parts_at_indices_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]);
|
||||
let [chosen, part] = input.parts_at_indices_mut([choice, part_idx]);
|
||||
[part, chosen]
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(Self::crossover_insert(
|
||||
part,
|
||||
&mut part.1,
|
||||
size,
|
||||
target,
|
||||
range,
|
||||
chosen.mutator_bytes(),
|
||||
chosen.1.mutator_bytes(),
|
||||
));
|
||||
}
|
||||
|
||||
@ -196,26 +203,24 @@ where
|
||||
|
||||
let mut other_testcase = state.corpus().get(id)?.borrow_mut();
|
||||
let other = other_testcase.load_input(state.corpus())?;
|
||||
|
||||
if other.names().is_empty() {
|
||||
let other_len = other.len();
|
||||
if other_len == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let choice = name_choice % other.names().len();
|
||||
let name = &other.names()[choice];
|
||||
let choice = key_choice % other_len;
|
||||
// Safety: choice is checked above
|
||||
let (key, part) = &other.parts()[choice];
|
||||
|
||||
let other_size = other.parts()[choice].mutator_bytes().len();
|
||||
let other_size = part.mutator_bytes().len();
|
||||
if other_size < 2 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let parts = input.parts_by_name(name).count();
|
||||
let parts = input.with_key(key).count();
|
||||
|
||||
if parts > 0 {
|
||||
let (_, part) = input
|
||||
.parts_by_name_mut(name)
|
||||
.nth(part_choice % parts)
|
||||
.unwrap();
|
||||
let (_, part) = input.with_key_mut(key).nth(part_choice % parts).unwrap();
|
||||
drop(other_testcase);
|
||||
let size = part.mutator_bytes().len();
|
||||
let Some(nz) = NonZero::new(size) else {
|
||||
@ -228,7 +233,7 @@ where
|
||||
// size is larger than 0.
|
||||
// target is smaller than size -> the subtraction is larger than 0.
|
||||
let range = rand_range(state, other_size, unsafe {
|
||||
NonZero::new(min(other_size, size - target)).unwrap_unchecked()
|
||||
NonZero::new_unchecked(min(other_size, size - target))
|
||||
});
|
||||
|
||||
let other_testcase = state.corpus().get(id)?.borrow_mut();
|
||||
@ -240,54 +245,57 @@ where
|
||||
size,
|
||||
target,
|
||||
range,
|
||||
other.parts()[choice].mutator_bytes(),
|
||||
other.part_at_index(choice).unwrap().1.mutator_bytes(),
|
||||
))
|
||||
} else {
|
||||
// just add it!
|
||||
input.add_part(name.clone(), other.parts()[choice].clone());
|
||||
input.append_part(other.part_at_index(choice).unwrap().clone());
|
||||
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Mutator<MultipartInput<I>, S> for CrossoverReplaceMutator
|
||||
impl<I, K, S> Mutator<MultipartInput<I, K>, S> for BytesInputCrossoverReplaceMutator
|
||||
where
|
||||
S: HasCorpus<MultipartInput<I>> + HasMaxSize + HasRand,
|
||||
S: HasCorpus<MultipartInput<I, K>> + HasMaxSize + HasRand,
|
||||
I: Input + ResizableMutator<u8> + HasMutatorBytes,
|
||||
K: Clone + PartialEq,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut MultipartInput<I>,
|
||||
input: &mut MultipartInput<I, K>,
|
||||
) -> 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 key_choice = state.rand_mut().next() as usize;
|
||||
let part_choice = state.rand_mut().next() as usize;
|
||||
|
||||
// We special-case crossover with self
|
||||
let id = random_corpus_id!(state.corpus(), state.rand_mut());
|
||||
if let Some(cur) = state.corpus().current() {
|
||||
if id == *cur {
|
||||
if input.names().is_empty() {
|
||||
let len = input.len();
|
||||
if len == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
let choice = name_choice % input.names().len();
|
||||
let name = input.names()[choice].clone();
|
||||
let choice = key_choice % len;
|
||||
// Safety: len is checked above
|
||||
let (key, part) = &input.parts()[choice];
|
||||
|
||||
let other_size = input.parts()[choice].mutator_bytes().len();
|
||||
let other_size = part.mutator_bytes().len();
|
||||
if other_size < 2 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let parts = input.parts_by_name(&name).count() - 1;
|
||||
let parts = input.with_key(key).count() - 1;
|
||||
|
||||
if parts == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let maybe_size = input
|
||||
.parts_by_name(&name)
|
||||
.with_key(key)
|
||||
.filter(|&(p, _)| p != choice)
|
||||
.nth(part_choice % parts)
|
||||
.map(|(id, part)| (id, part.mutator_bytes().len()));
|
||||
@ -302,25 +310,25 @@ where
|
||||
// other_size is checked above.
|
||||
// size is larger than than target and larger than 1. The subtraction result will always be positive.
|
||||
let range = rand_range(state, other_size, unsafe {
|
||||
NonZero::new(min(other_size, size - target)).unwrap_unchecked()
|
||||
NonZero::new_unchecked(min(other_size, size - target))
|
||||
});
|
||||
|
||||
let [part, chosen] = match part_idx.cmp(&choice) {
|
||||
Ordering::Less => input.parts_mut([part_idx, choice]),
|
||||
Ordering::Less => input.parts_at_indices_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]);
|
||||
let [chosen, part] = input.parts_at_indices_mut([choice, part_idx]);
|
||||
[part, chosen]
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(Self::crossover_replace(
|
||||
part,
|
||||
&mut part.1,
|
||||
target,
|
||||
range,
|
||||
chosen.mutator_bytes(),
|
||||
chosen.1.mutator_bytes(),
|
||||
));
|
||||
}
|
||||
|
||||
@ -331,25 +339,24 @@ where
|
||||
let mut other_testcase = state.corpus().get(id)?.borrow_mut();
|
||||
let other = other_testcase.load_input(state.corpus())?;
|
||||
|
||||
if other.names().is_empty() {
|
||||
let other_len = other.len();
|
||||
if other_len == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let choice = name_choice % other.names().len();
|
||||
let name = &other.names()[choice];
|
||||
let choice = key_choice % other_len;
|
||||
// Safety: choice is checked above
|
||||
let (key, part) = &other.parts()[choice];
|
||||
|
||||
let other_size = other.parts()[choice].mutator_bytes().len();
|
||||
let other_size = part.mutator_bytes().len();
|
||||
if other_size < 2 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let parts = input.parts_by_name(name).count();
|
||||
let parts = input.with_key(key).count();
|
||||
|
||||
if parts > 0 {
|
||||
let (_, part) = input
|
||||
.parts_by_name_mut(name)
|
||||
.nth(part_choice % parts)
|
||||
.unwrap();
|
||||
let (_, part) = input.with_key_mut(key).nth(part_choice % parts).unwrap();
|
||||
drop(other_testcase);
|
||||
let size = part.mutator_bytes().len();
|
||||
let Some(nz) = NonZero::new(size) else {
|
||||
@ -361,7 +368,7 @@ where
|
||||
// other_size is checked above.
|
||||
// size is larger than than target and larger than 1. The subtraction result will always be positive.
|
||||
let range = rand_range(state, other_size, unsafe {
|
||||
NonZero::new(min(other_size, size - target)).unwrap_unchecked()
|
||||
NonZero::new_unchecked(min(other_size, size - target))
|
||||
});
|
||||
|
||||
let other_testcase = state.corpus().get(id)?.borrow_mut();
|
||||
@ -372,11 +379,11 @@ where
|
||||
part,
|
||||
target,
|
||||
range,
|
||||
other.parts()[choice].mutator_bytes(),
|
||||
other.part_at_index(choice).unwrap().1.mutator_bytes(),
|
||||
))
|
||||
} else {
|
||||
// just add it!
|
||||
input.add_part(name.clone(), other.parts()[choice].clone());
|
||||
input.append_part(other.part_at_index(choice).unwrap().clone());
|
||||
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user