diff --git a/MIGRATION.md b/MIGRATION.md index 1a10f84e25..6de6720870 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -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 diff --git a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs index 0dd16eaa4c..7ced193195 100644 --- a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs +++ b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs @@ -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| { - let mut count = input.parts().len(); - for (i, input) in input.parts().iter().enumerate() { + let mut harness = |input: &MultipartInput| { + 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) diff --git a/libafl/src/inputs/list.rs b/libafl/src/inputs/list.rs new file mode 100644 index 0000000000..38a51996c7 --- /dev/null +++ b/libafl/src/inputs/list.rs @@ -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 { + parts: Vec, +} + +impl Default for ListInput { + fn default() -> Self { + Self::empty() + } +} + +impl ListInput { + /// 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) -> 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(&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::>() + .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 { + 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>( + inner: M, + ) -> >::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>( + inner: M, + ) -> >::MapResult { + inner.map(ToRandomEntryMutator) + } +} + +impl From for ListInput +where + It: IntoIterator, +{ + fn from(parts: It) -> Self { + let vec = parts.into_iter().collect(); + Self { parts: vec } + } +} + +impl Input for ListInput +where + I: Input, +{ + fn generate_name(&self, id: Option) -> 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::>() + .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 { + inner: M, + name: Cow<'static, str>, +} + +impl LastEntryMutator { + /// Create a new [`LastEntryMutator`]. + #[must_use] + pub fn new(inner: M) -> Self { + let name = Cow::Owned(format!("LastEntryMutator<{}>", inner.name())); + Self { inner, name } + } +} + +impl Mutator, S> for LastEntryMutator +where + M: Mutator, +{ + fn mutate(&mut self, state: &mut S, input: &mut ListInput) -> Result { + 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) -> Result<(), Error> { + self.inner.post_exec(state, new_corpus_id) + } +} + +/// Mapping functor to convert mutators to [`LastEntryMutator`]. +#[derive(Debug)] +pub struct ToLastEntryMutator; + +impl MappingFunctor for ToLastEntryMutator { + type Output = LastEntryMutator; + + fn apply(&mut self, from: M) -> Self::Output { + LastEntryMutator::new(from) + } +} + +impl Named for LastEntryMutator { + 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 { + inner: M, + name: Cow<'static, str>, +} + +impl RandomEntryMutator { + /// Create a new [`RandomEntryMutator`]. + #[must_use] + pub fn new(inner: M) -> Self { + let name = Cow::Owned(format!("RandomEntryMutator<{}>", inner.name())); + Self { inner, name } + } +} + +impl Mutator, S> for RandomEntryMutator +where + M: Mutator, + S: HasRand, +{ + fn mutate(&mut self, state: &mut S, input: &mut ListInput) -> Result { + 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) -> Result<(), Error> { + self.inner.post_exec(state, new_corpus_id) + } +} + +/// Mapping functor to convert mutators to [`RandomEntryMutator`]. +#[derive(Debug)] +pub struct ToRandomEntryMutator; + +impl MappingFunctor for ToRandomEntryMutator { + type Output = RandomEntryMutator; + + fn apply(&mut self, from: M) -> Self::Output { + RandomEntryMutator::new(from) + } +} + +impl Named for RandomEntryMutator { + 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::>::map_to_mutate_on_last_part(mutator); + let mut input: ListInput> = + ListInput::from(vec![ValueInput::new(1_u8), ValueInput::new(2)]); + let mut state = NopState::>>::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::>::map_to_mutate_on_last_part(mutator); + let mut input = ListInput::>::default(); + let mut state = NopState::>>::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::>::map_to_mutate_on_random_part(mutator); + let initial_input = vec![ValueInput::new(1_u8), ValueInput::new(2)]; + let mut input = ListInput::>::from(initial_input.clone()); + let mut state = NopState::>>::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::>::map_to_mutate_on_random_part(mutator); + let mut input = ListInput::>::default(); + let mut state = NopState::>>::new(); + let res = mapped_mutator.mutate_all(&mut state, &mut input); + assert_eq!(res.unwrap(), MutationResult::Skipped); + assert_eq!(input.parts, vec![]); + } +} diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index e572124d63..a349649b74 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -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; diff --git a/libafl/src/inputs/multi.rs b/libafl/src/inputs/multi.rs index 9583a40a12..89ce8efbd5 100644 --- a/libafl/src/inputs/multi.rs +++ b/libafl/src/inputs/multi.rs @@ -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 arrayvec::ArrayVec; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Serialize}; -use crate::{corpus::CorpusId, inputs::Input}; +use crate::{ + corpus::CorpusId, + inputs::{Input, ListInput}, +}; -/// 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 { - parts: Vec, - names: Vec, -} +/// 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 = ListInput<(K, I)>; -impl Default for MultipartInput { - fn default() -> Self { - Self::new() +impl Input for MultipartInput +where + I: Input, + K: PartialEq + Debug + Serialize + DeserializeOwned + Clone + Hash, +{ + fn generate_name(&self, id: Option) -> String { + self.parts() + .iter() + .map(|(_k, i)| i.generate_name(id)) + .collect::>() + .join(",") } } -impl MultipartInput { - /// 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 { + /// 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(&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 + 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::>() - .into_inner() - { - arr - } else { - // avoid Debug trait requirement for expect/unwrap - panic!("arrayvec collection failed somehow") - } - } + /// 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 + 'a + where + 'b: 'a, + V: 'b; - /// 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) - } + /// 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 + 'a + where + 'b: 'a, + V: 'b; +} - /// 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 +impl Keyed for MultipartInput +where + K: PartialEq, +{ + fn keys<'a>(&'a self) -> impl Iterator + where + K: 'a, + { + self.parts().iter().map(|(k, _)| k) } - /// Gets a reference to each part with the provided name. - pub fn parts_by_name<'a, 'b>( - &'b self, - name: &'a str, - ) -> impl Iterator + 'a + fn with_key<'a, 'b>(&'b self, key: &'a K) -> impl Iterator + 'a where 'b: 'a, + I: 'b, { - self.names() + self.parts() .iter() - .zip(&self.parts) .enumerate() - .filter_map(move |(i, (s, item))| (s == name).then_some((i, item))) + .filter_map(move |(i, (k, input))| (key == k).then_some((i, input))) } - /// Gets a mutable reference to each part with the provided name. - pub fn parts_by_name_mut<'a, 'b>( + fn with_key_mut<'a, 'b>( &'b mut self, - name: &'a str, + key: &'a K, ) -> impl Iterator + '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 { - self.names.iter().map(String::as_ref).zip(self.parts()) - } -} - -impl From for MultipartInput -where - It: IntoIterator, - S: AsRef, -{ - 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 Input for MultipartInput -where - I: Input, -{ - fn generate_name(&self, id: Option) -> 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::>() - .join(",") - } + .filter_map(move |(i, (k, input))| (key == k).then_some((i, input))) } } diff --git a/libafl/src/mutators/list.rs b/libafl/src/mutators/list.rs new file mode 100644 index 0000000000..4c1e122846 --- /dev/null +++ b/libafl/src/mutators/list.rs @@ -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 { + generator: G, +} + +impl GenerateToAppendMutator { + /// Create a new `GenerateToAppendMutator`. + #[must_use] + pub fn new(generator: G) -> Self { + Self { generator } + } +} + +impl Mutator, S> for GenerateToAppendMutator +where + G: Generator, + I: Input, +{ + fn mutate(&mut self, state: &mut S, input: &mut ListInput) -> Result { + let generated = self.generator.generate(state)?; + input.append_part(generated); + Ok(MutationResult::Mutated) + } +} + +impl Named for GenerateToAppendMutator { + 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 Mutator, S> for RemoveLastEntryMutator +where + K: Default, +{ + fn mutate( + &mut self, + _state: &mut S, + input: &mut MultipartInput, + ) -> Result { + 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 Mutator, S> for RemoveRandomEntryMutator +where + S: HasRand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut MultipartInput, + ) -> Result { + 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 Mutator, S> for CrossoverInsertMutator +where + S: HasCorpus> + HasMaxSize + HasRand, + I: Clone, + K: Clone, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut MultipartInput, + ) -> Result { + 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 Mutator, S> for CrossoverReplaceMutator +where + S: HasCorpus> + HasMaxSize + HasRand, + I: Clone, + K: Clone, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut MultipartInput, + ) -> Result { + 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") + } +} diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index 114e39dc4c..01d972aa90 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -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; diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index 1d7d1eae46..f17c64bb61 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -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 Mutator, S> for M +impl Mutator, S> for M where M: DefaultMultipartMutator + Mutator, S: HasRand, @@ -42,14 +43,16 @@ where fn mutate( &mut self, state: &mut S, - input: &mut MultipartInput, + input: &mut MultipartInput, ) -> Result { - 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) -> Result<(), Error> { @@ -116,43 +119,47 @@ impl_default_multipart!( I2SRandReplace, ); -impl Mutator, S> for CrossoverInsertMutator +impl Mutator, S> for BytesInputCrossoverInsertMutator where - S: HasCorpus> + HasMaxSize + HasRand, + S: HasCorpus> + HasMaxSize + HasRand, I: Input + ResizableMutator + HasMutatorBytes, + K: Clone + PartialEq, { fn mutate( &mut self, state: &mut S, - input: &mut MultipartInput, + input: &mut MultipartInput, ) -> Result { // 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 Mutator, S> for CrossoverReplaceMutator +impl Mutator, S> for BytesInputCrossoverReplaceMutator where - S: HasCorpus> + HasMaxSize + HasRand, + S: HasCorpus> + HasMaxSize + HasRand, I: Input + ResizableMutator + HasMutatorBytes, + K: Clone + PartialEq, { fn mutate( &mut self, state: &mut S, - input: &mut MultipartInput, + input: &mut MultipartInput, ) -> Result { // 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) }