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:
Valentin Huber 2025-02-20 13:41:53 +01:00 committed by GitHub
parent 98ef505a0e
commit 1eef4ffb58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 766 additions and 223 deletions

View File

@ -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

View File

@ -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
View 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![]);
}
}

View File

@ -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;

View File

@ -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
View 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")
}
}

View File

@ -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;

View File

@ -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)
}