From db321e9d723db5bc64fdcd7a475adf24e8d1bf2e Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Wed, 12 Feb 2025 15:48:50 +0100 Subject: [PATCH 01/16] Introduce ListInput --- libafl/src/inputs/list.rs | 280 ++++++++++++++++++++++++++++++++++++ libafl/src/inputs/mod.rs | 3 + libafl/src/mutators/list.rs | 41 ++++++ libafl/src/mutators/mod.rs | 2 + 4 files changed, 326 insertions(+) create mode 100644 libafl/src/inputs/list.rs create mode 100644 libafl/src/mutators/list.rs diff --git a/libafl/src/inputs/list.rs b/libafl/src/inputs/list.rs new file mode 100644 index 0000000000..4c03f294c6 --- /dev/null +++ b/libafl/src/inputs/list.rs @@ -0,0 +1,280 @@ +//! Module for list-based inputs and their mutators. +//! Provides functionality for fuzzing sequences of inputs. + +use alloc::{borrow::Cow, string::String, vec::Vec}; +use core::{ + hash::{Hash, Hasher}, + num::NonZero, +}; + +use libafl_bolts::{ + generic_hash_std, + rands::Rand as _, + tuples::{Map, MappingFunctor}, + HasLen, Named, +}; +use serde::{Deserialize, Serialize}; + +use crate::{ + corpus::CorpusId, + inputs::Input, + mutators::{MutationResult, Mutator}, + state::HasRand, + Error, +}; + +/// Input consisting of a list of variable length of an arbitrary input. +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct ListInput { + parts: Vec, +} + +impl Input for ListInput { + fn generate_name(&self, _id: Option) -> String { + format!( + "ListInput<{},{}>", + generic_hash_std(&self.parts), + self.parts.len() + ) + } +} + +impl Hash for ListInput { + fn hash(&self, state: &mut H) { + self.parts.hash(state); + } +} + +impl From> for ListInput { + fn from(value: Vec) -> Self { + Self::new(value) + } +} + +impl ListInput { + /// Create a new [`ListInput`]. + #[must_use] + #[inline] + pub fn new(parts: Vec) -> Self { + Self { parts } + } + + /// Create a new empty [`ListInput`]. + #[must_use] + pub fn empty() -> Self { + Self::new(Vec::new()) + } + + /// 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(ToLastEntryListMutator) + } + + /// 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(ToRandomEntryListMutator) + } + + /// Get a slice of the parts of the [`ListInput`]. + #[must_use] + #[inline] + pub fn parts(&self) -> &[I] { + &self.parts + } + + /// Get a mutable slice of the parts of the [`ListInput`]. + #[must_use] + #[inline] + pub fn parts_mut(&mut self) -> &mut Vec { + &mut self.parts + } + + /// Get the parts of the [`ListInput`]. + #[must_use] + #[inline] + pub fn parts_owned(self) -> Vec { + self.parts + } +} + +impl HasLen for ListInput { + fn len(&self) -> usize { + self.parts.len() + } +} + +/// Mutator that applies mutations to the last element of a [`ListInput`]. +/// +/// If the input is empty, [`MutationResult::Skipped`] is returned. +#[derive(Debug)] +pub struct LastEntryListMutator { + inner: M, + name: Cow<'static, str>, +} + +impl LastEntryListMutator { + /// Create a new [`LastEntryListMutator`]. + #[must_use] + pub fn new(inner: M) -> Self { + let name = Cow::Owned(format!("LastEntryListMutator<{}>", inner.name())); + Self { inner, name } + } +} + +impl Mutator, S> for LastEntryListMutator +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]), + } + } +} + +/// Mapping functor to convert mutators to [`LastEntryListMutator`]. +#[derive(Debug)] +pub struct ToLastEntryListMutator; + +impl MappingFunctor for ToLastEntryListMutator { + type Output = LastEntryListMutator; + + fn apply(&mut self, from: M) -> Self::Output { + LastEntryListMutator::new(from) + } +} + +impl Named for LastEntryListMutator { + 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 RandomEntryListMutator { + inner: M, + name: Cow<'static, str>, +} + +impl RandomEntryListMutator { + /// Create a new [`RandomEntryListMutator`]. + #[must_use] + pub fn new(inner: M) -> Self { + let name = Cow::Owned(format!("RandomEntryListMutator<{}>", inner.name())); + Self { inner, name } + } +} + +impl Mutator, S> for RandomEntryListMutator +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]) + } + } + } +} + +/// Mapping functor to convert mutators to [`RandomEntryListMutator`]. +#[derive(Debug)] +pub struct ToRandomEntryListMutator; + +impl MappingFunctor for ToRandomEntryListMutator { + type Output = RandomEntryListMutator; + + fn apply(&mut self, from: M) -> Self::Output { + RandomEntryListMutator::new(from) + } +} + +impl Named for RandomEntryListMutator { + 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::new(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::>::empty(); + 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::new(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::>::empty(); + 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..26d156e8b7 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -18,6 +18,9 @@ pub use generalized::*; pub mod bytessub; pub use bytessub::BytesSubInput; +pub mod list; +pub use list::ListInput; + #[cfg(feature = "multipart_inputs")] pub mod multi; #[cfg(feature = "multipart_inputs")] diff --git a/libafl/src/mutators/list.rs b/libafl/src/mutators/list.rs new file mode 100644 index 0000000000..3f073d916a --- /dev/null +++ b/libafl/src/mutators/list.rs @@ -0,0 +1,41 @@ +//! Mutators for [`ListInput`] +use alloc::borrow::Cow; + +use libafl_bolts::{Error, Named}; + +use crate::{ + generators::Generator, + inputs::ListInput, + mutators::{MutationResult, Mutator}, +}; + +/// 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, +{ + fn mutate(&mut self, state: &mut S, input: &mut ListInput) -> Result { + let generated = self.generator.generate(state)?; + input.parts_mut().push(generated); + Ok(MutationResult::Mutated) + } +} + +impl Named for GenerateToAppendMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("GenerateToAppendMutator") + } +} diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index 114e39dc4c..f8043e475d 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -8,6 +8,8 @@ use core::fmt; pub use scheduled::*; pub mod mutations; pub use mutations::*; +pub mod list; +pub use list::*; pub mod token_mutations; use serde::{Deserialize, Serialize}; pub use token_mutations::*; From 238ea8b4e4185841d1683e9c06f82b9f70f2b733 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Wed, 12 Feb 2025 16:02:30 +0100 Subject: [PATCH 02/16] Add remove mutators for ListInput --- libafl/src/mutators/list.rs | 61 ++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/libafl/src/mutators/list.rs b/libafl/src/mutators/list.rs index 3f073d916a..6fa1ca8e38 100644 --- a/libafl/src/mutators/list.rs +++ b/libafl/src/mutators/list.rs @@ -1,12 +1,14 @@ //! Mutators for [`ListInput`] use alloc::borrow::Cow; +use core::num::NonZero; -use libafl_bolts::{Error, Named}; +use libafl_bolts::{rands::Rand as _, Error, Named}; use crate::{ generators::Generator, inputs::ListInput, mutators::{MutationResult, Mutator}, + state::HasRand, }; /// Mutator that generates a new input and appends it to the list. @@ -39,3 +41,60 @@ impl Named for GenerateToAppendMutator { &Cow::Borrowed("GenerateToAppendMutator") } } + +/// Mutator that removes the last entry from a [`ListInput`]. +/// +/// Returns [`MutationResult::Skipped`] if the input is empty. +#[derive(Debug)] +pub struct RemoveLastEntryMutator; + +impl Mutator, S> for RemoveLastEntryMutator { + fn mutate( + &mut self, + _state: &mut S, + input: &mut ListInput, + ) -> Result { + match input.parts_mut().pop() { + 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 [`ListInput`]. +/// +/// 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 ListInput) -> Result { + let parts = input.parts_mut(); + match parts.len() { + 0 => Ok(MutationResult::Skipped), + len => { + // Safety: null checks are done above + let index = state + .rand_mut() + .below(unsafe { NonZero::new_unchecked(len) }); + parts.remove(index); + Ok(MutationResult::Mutated) + } + } + } +} + +impl Named for RemoveRandomEntryMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("RemoveRandomEntryMutator") + } +} From 2f9393ab9e9f5314a794b5bba5f74886ea420099 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Thu, 13 Feb 2025 15:44:12 +0100 Subject: [PATCH 03/16] Merge ListInput and MultipartInput --- .../baby_fuzzer_multi/src/main.rs | 18 +- libafl/src/inputs/list.rs | 280 ------------ libafl/src/inputs/mod.rs | 3 - libafl/src/inputs/multi.rs | 419 +++++++++++++++--- libafl/src/mutators/list.rs | 100 ----- libafl/src/mutators/mod.rs | 4 - libafl/src/mutators/multi.rs | 288 ++++++++++-- 7 files changed, 614 insertions(+), 498 deletions(-) delete mode 100644 libafl/src/inputs/list.rs delete mode 100644 libafl/src/mutators/list.rs diff --git a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs index 0dd16eaa4c..52edb90852 100644 --- a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs +++ b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs @@ -43,10 +43,10 @@ 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 target = input.target_bytes(); + let mut harness = |input: &MultipartInput| { + let mut count = input.len(); + for (i, input) in input.iter().enumerate() { + let target = input.1.target_bytes(); let buf = target.as_slice(); signals_set(i * 8); if !buf.is_empty() && buf[0] == b'a' { @@ -143,11 +143,11 @@ 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::with_default_names([ + BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o']), + BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o']), + BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o']), + BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o']), ]); fuzzer diff --git a/libafl/src/inputs/list.rs b/libafl/src/inputs/list.rs deleted file mode 100644 index 4c03f294c6..0000000000 --- a/libafl/src/inputs/list.rs +++ /dev/null @@ -1,280 +0,0 @@ -//! Module for list-based inputs and their mutators. -//! Provides functionality for fuzzing sequences of inputs. - -use alloc::{borrow::Cow, string::String, vec::Vec}; -use core::{ - hash::{Hash, Hasher}, - num::NonZero, -}; - -use libafl_bolts::{ - generic_hash_std, - rands::Rand as _, - tuples::{Map, MappingFunctor}, - HasLen, Named, -}; -use serde::{Deserialize, Serialize}; - -use crate::{ - corpus::CorpusId, - inputs::Input, - mutators::{MutationResult, Mutator}, - state::HasRand, - Error, -}; - -/// Input consisting of a list of variable length of an arbitrary input. -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ListInput { - parts: Vec, -} - -impl Input for ListInput { - fn generate_name(&self, _id: Option) -> String { - format!( - "ListInput<{},{}>", - generic_hash_std(&self.parts), - self.parts.len() - ) - } -} - -impl Hash for ListInput { - fn hash(&self, state: &mut H) { - self.parts.hash(state); - } -} - -impl From> for ListInput { - fn from(value: Vec) -> Self { - Self::new(value) - } -} - -impl ListInput { - /// Create a new [`ListInput`]. - #[must_use] - #[inline] - pub fn new(parts: Vec) -> Self { - Self { parts } - } - - /// Create a new empty [`ListInput`]. - #[must_use] - pub fn empty() -> Self { - Self::new(Vec::new()) - } - - /// 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(ToLastEntryListMutator) - } - - /// 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(ToRandomEntryListMutator) - } - - /// Get a slice of the parts of the [`ListInput`]. - #[must_use] - #[inline] - pub fn parts(&self) -> &[I] { - &self.parts - } - - /// Get a mutable slice of the parts of the [`ListInput`]. - #[must_use] - #[inline] - pub fn parts_mut(&mut self) -> &mut Vec { - &mut self.parts - } - - /// Get the parts of the [`ListInput`]. - #[must_use] - #[inline] - pub fn parts_owned(self) -> Vec { - self.parts - } -} - -impl HasLen for ListInput { - fn len(&self) -> usize { - self.parts.len() - } -} - -/// Mutator that applies mutations to the last element of a [`ListInput`]. -/// -/// If the input is empty, [`MutationResult::Skipped`] is returned. -#[derive(Debug)] -pub struct LastEntryListMutator { - inner: M, - name: Cow<'static, str>, -} - -impl LastEntryListMutator { - /// Create a new [`LastEntryListMutator`]. - #[must_use] - pub fn new(inner: M) -> Self { - let name = Cow::Owned(format!("LastEntryListMutator<{}>", inner.name())); - Self { inner, name } - } -} - -impl Mutator, S> for LastEntryListMutator -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]), - } - } -} - -/// Mapping functor to convert mutators to [`LastEntryListMutator`]. -#[derive(Debug)] -pub struct ToLastEntryListMutator; - -impl MappingFunctor for ToLastEntryListMutator { - type Output = LastEntryListMutator; - - fn apply(&mut self, from: M) -> Self::Output { - LastEntryListMutator::new(from) - } -} - -impl Named for LastEntryListMutator { - 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 RandomEntryListMutator { - inner: M, - name: Cow<'static, str>, -} - -impl RandomEntryListMutator { - /// Create a new [`RandomEntryListMutator`]. - #[must_use] - pub fn new(inner: M) -> Self { - let name = Cow::Owned(format!("RandomEntryListMutator<{}>", inner.name())); - Self { inner, name } - } -} - -impl Mutator, S> for RandomEntryListMutator -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]) - } - } - } -} - -/// Mapping functor to convert mutators to [`RandomEntryListMutator`]. -#[derive(Debug)] -pub struct ToRandomEntryListMutator; - -impl MappingFunctor for ToRandomEntryListMutator { - type Output = RandomEntryListMutator; - - fn apply(&mut self, from: M) -> Self::Output { - RandomEntryListMutator::new(from) - } -} - -impl Named for RandomEntryListMutator { - 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::new(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::>::empty(); - 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::new(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::>::empty(); - 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 26d156e8b7..e572124d63 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -18,9 +18,6 @@ pub use generalized::*; pub mod bytessub; pub use bytessub::BytesSubInput; -pub mod list; -pub use list::ListInput; - #[cfg(feature = "multipart_inputs")] pub mod multi; #[cfg(feature = "multipart_inputs")] diff --git a/libafl/src/inputs/multi.rs b/libafl/src/inputs/multi.rs index 9583a40a12..20bf00df97 100644 --- a/libafl/src/inputs/multi.rs +++ b/libafl/src/inputs/multi.rs @@ -5,37 +5,47 @@ //! requires that each subcomponent be the same subtype. use alloc::{ + borrow::Cow, + fmt::Debug, string::{String, ToString}, vec::Vec, }; +use core::{hash::Hash, num::NonZero}; use arrayvec::ArrayVec; -use serde::{Deserialize, Serialize}; +use libafl_bolts::{ + rands::Rand as _, + tuples::{Map, MappingFunctor}, + Error, Named, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use crate::{corpus::CorpusId, inputs::Input}; +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 MultipartInput { - parts: Vec, - names: Vec, +pub struct MultipartInput { + parts: Vec<(N, I)>, } -impl Default for MultipartInput { +impl Default for MultipartInput { fn default() -> Self { Self::new() } } -impl MultipartInput { +impl MultipartInput { /// Create a new multipart input. #[must_use] + #[inline] pub fn new() -> Self { - Self { - parts: Vec::new(), - names: Vec::new(), - } + Self { parts: Vec::new() } } fn idxs_to_skips(idxs: &mut [usize]) { @@ -53,24 +63,51 @@ impl MultipartInput { /// Get the individual parts of this input. #[must_use] - pub fn parts(&self) -> &[I] { + #[inline] + pub fn parts_and_names(&self) -> &[(N, I)] { &self.parts } + /// Get the individual parts of this input. + #[must_use] + #[inline] + pub fn parts_and_names_mut(&mut self) -> &mut [(N, I)] { + &mut self.parts + } + + /// Get a specific part of this input by index. + #[must_use] + #[inline] + pub fn part_by_idx(&self, idx: usize) -> Option<&I> { + self.parts.get(idx).map(|(_, i)| i) + } + + /// Get a specific part of this input by index. + #[must_use] + #[inline] + pub fn part_by_idx_mut(&mut self, idx: usize) -> Option<&mut I> { + self.parts.get_mut(idx).map(|(_, i)| i) + } + /// Access multiple parts mutably. /// /// ## Panics /// /// Panics if idxs is not sorted, has duplicate elements, or any entry is out of bounds. #[must_use] - pub fn parts_mut(&mut self, mut idxs: [usize; N]) -> [&mut I; N] { + pub fn parts_by_idxs_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::>() + .map(|i| { + parts + .nth(i) + .map(|(_, i)| i) + .expect("idx had an out of bounds entry") + }) + .collect::>() .into_inner() { arr @@ -80,90 +117,358 @@ impl MultipartInput { } } - /// 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. + #[inline] + pub fn names(&self) -> impl Iterator { + self.parts.iter().map(|(n, _)| n) + } + + /// Adds a part to this input, potentially with the same name as an existing part. + #[inline] + pub fn append_part(&mut self, name: N, part: I) { + self.parts.push((name, part)); + } + + /// Inserts a part to this input at the given index, potentially with the same name as an existing part. + #[inline] + pub fn insert_part(&mut self, idx: usize, name: N, part: I) { + self.parts.insert(idx, (name, 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_idx(&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<(N, I)> { + self.parts.pop() + } + + /// Iterate over the parts of this input; no order is specified. + #[inline] + pub fn iter(&self) -> impl Iterator { + self.parts.iter() + } + + /// Iterate over the parts of this input; no order is specified. + #[inline] + pub fn iter_mut(&mut self) -> impl Iterator { + self.parts.iter_mut() + } + + /// 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 [`MultipartInput`]'s inner type to a tuple of mutators able to work on the entire [`MultipartInput`], + /// 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 [`MultipartInput`]'s inner type to a tuple of mutators able to work on the entire [`MultipartInput`], + /// by mutating on a random part. If the input is empty, [`MutationResult::Skipped`] is returned. #[must_use] - pub fn names(&self) -> &[String] { - &self.names + #[inline] + pub fn map_to_mutate_on_random_part>( + inner: M, + ) -> >::MapResult { + inner.map(ToRandomEntryMutator) } +} +impl MultipartInput +where + N: PartialEq, +{ /// Gets a reference to each part with the provided name. - pub fn parts_by_name<'a, 'b>( + pub fn parts_with_name<'a, 'b>( &'b self, - name: &'a str, + name: &'a N, ) -> impl Iterator + 'a where 'b: 'a, { - 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, (n, input))| (name == n).then_some((i, input))) } /// Gets a mutable reference to each part with the provided name. - pub fn parts_by_name_mut<'a, 'b>( + pub fn parts_with_name_mut<'a, 'b>( &'b mut self, - name: &'a str, + name: &'a N, ) -> impl Iterator + 'a where 'b: 'a, { - self.names - .iter() - .zip(&mut self.parts) + self.parts + .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); + .filter_map(move |(i, (n, input))| (name == n).then_some((i, input))) } +} - /// 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, +{ + fn from(parts: It) -> Self { + let vec = parts.into_iter().collect(); + Self { parts: vec } } } -impl From for MultipartInput +impl MultipartInput where - It: IntoIterator, - S: AsRef, + N: Default, { - 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 + /// Create a new multipart input with default names. + #[must_use] + pub fn with_default_names>(parts: It) -> Self { + let vec = parts.into_iter().map(|i| (N::default(), i)).collect(); + Self { parts: vec } + } + + /// Append a part to this input with a default name. + #[inline] + pub fn append_part_with_default_name(&mut self, part: I) { + self.parts.push((N::default(), part)); } } -impl Input for MultipartInput +impl Input for MultipartInput where I: Input, + N: Debug + Hash + Serialize + DeserializeOwned + Clone, { fn generate_name(&self, id: Option) -> String { - if self.names().is_empty() { + if self.parts.is_empty() { "empty_multipart_input".to_string() // empty strings cause issues with OnDiskCorpus } else { - self.names + self.parts .iter() - .cloned() - .zip(self.parts.iter().map(|i| i.generate_name(id))) - .map(|(name, generated)| format!("{name}-{generated}")) + .map(|(name, input)| format!("{name:?}-{}", input.generate_name(id))) .collect::>() .join(",") } } } + +/// Mutator that applies mutations to the last element of a [`MultipartInput`]. +/// +/// 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 MultipartInput, + ) -> Result { + match input.parts.len() { + 0 => Ok(MutationResult::Skipped), + len => self.inner.mutate(state, &mut input.parts[len - 1].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 [`MultipartInput`]. +/// +/// 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 MultipartInput, + ) -> 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].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 [`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 alloc::vec::Vec; + + use tuple_list::tuple_list; + + use super::MultipartInput; + 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 = + MultipartInput::, ()>::map_to_mutate_on_last_part(mutator); + let mut input: MultipartInput, ()> = + MultipartInput::with_default_names(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.iter().map(|((), i)| *i).collect::>(), + 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 = + MultipartInput::<(), ValueInput>::map_to_mutate_on_last_part(mutator); + let mut input = MultipartInput::, ()>::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 = + MultipartInput::, ()>::map_to_mutate_on_random_part(mutator); + let initial_input = vec![ValueInput::new(1_u8), ValueInput::new(2)]; + let mut input = + MultipartInput::, ()>::with_default_names(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 + .iter() + .zip(initial_input.iter()) + .filter(|&(a, b)| a.1 != *b) + .count() + ); + } + + #[test] + fn map_to_mutate_on_random_part_empty() { + let mutator = tuple_list!(IncMutator); + let mut mapped_mutator = + MultipartInput::, ()>::map_to_mutate_on_random_part(mutator); + let mut input = MultipartInput::, ()>::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/mutators/list.rs b/libafl/src/mutators/list.rs deleted file mode 100644 index 6fa1ca8e38..0000000000 --- a/libafl/src/mutators/list.rs +++ /dev/null @@ -1,100 +0,0 @@ -//! Mutators for [`ListInput`] -use alloc::borrow::Cow; -use core::num::NonZero; - -use libafl_bolts::{rands::Rand as _, Error, Named}; - -use crate::{ - generators::Generator, - inputs::ListInput, - mutators::{MutationResult, Mutator}, - state::HasRand, -}; - -/// 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, -{ - fn mutate(&mut self, state: &mut S, input: &mut ListInput) -> Result { - let generated = self.generator.generate(state)?; - input.parts_mut().push(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 [`ListInput`]. -/// -/// Returns [`MutationResult::Skipped`] if the input is empty. -#[derive(Debug)] -pub struct RemoveLastEntryMutator; - -impl Mutator, S> for RemoveLastEntryMutator { - fn mutate( - &mut self, - _state: &mut S, - input: &mut ListInput, - ) -> Result { - match input.parts_mut().pop() { - 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 [`ListInput`]. -/// -/// 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 ListInput) -> Result { - let parts = input.parts_mut(); - match parts.len() { - 0 => Ok(MutationResult::Skipped), - len => { - // Safety: null checks are done above - let index = state - .rand_mut() - .below(unsafe { NonZero::new_unchecked(len) }); - parts.remove(index); - Ok(MutationResult::Mutated) - } - } - } -} - -impl Named for RemoveRandomEntryMutator { - fn name(&self) -> &Cow<'static, str> { - &Cow::Borrowed("RemoveRandomEntryMutator") - } -} diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index f8043e475d..507309bd66 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -8,8 +8,6 @@ use core::fmt; pub use scheduled::*; pub mod mutations; pub use mutations::*; -pub mod list; -pub use list::*; pub mod token_mutations; use serde::{Deserialize, Serialize}; pub use token_mutations::*; @@ -42,8 +40,6 @@ pub use unicode::*; #[cfg(feature = "multipart_inputs")] pub mod multi; -#[cfg(feature = "multipart_inputs")] -pub use multi::*; #[cfg(feature = "nautilus")] pub mod nautilus; diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index 1d7d1eae46..9ac5ceb83e 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -1,14 +1,16 @@ //! Mutator definitions for [`MultipartInput`]s. See [`crate::inputs::multi`] for details. +use alloc::borrow::Cow; use core::{ cmp::{min, Ordering}, num::NonZero, }; -use libafl_bolts::{rands::Rand, Error}; +use libafl_bolts::{rands::Rand, Error, Named}; use crate::{ corpus::{Corpus, CorpusId}, + generators::Generator, impl_default_multipart, inputs::{multi::MultipartInput, HasMutatorBytes, Input, ResizableMutator}, mutators::{ @@ -17,7 +19,8 @@ use crate::{ 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 +37,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,13 +45,13 @@ 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 { + let Some(parts_len) = NonZero::new(input.len()) else { return Ok(MutationResult::Skipped); }; let selected = state.rand_mut().below(parts_len); - let mutated = input.part_mut(selected).unwrap(); + let mutated = input.part_by_idx_mut(selected).unwrap(); self.mutate(state, mutated) } @@ -116,15 +119,16 @@ 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, + N: 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; @@ -134,25 +138,26 @@ where 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 = name_choice % len; + let name = input.names().nth(choice).unwrap(); - let other_size = input.parts()[choice].mutator_bytes().len(); + let other_size = input.part_by_idx(choice).unwrap().mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } - let parts = input.parts_by_name(&name).count() - 1; + let parts = input.parts_with_name(name).count() - 1; if parts == 0 { return Ok(MutationResult::Skipped); } let maybe_size = input - .parts_by_name(&name) + .parts_with_name(name) .filter(|&(p, _)| p != choice) .nth(part_choice % parts) .map(|(id, part)| (id, part.mutator_bytes().len())); @@ -171,12 +176,12 @@ where }); let [part, chosen] = match part_idx.cmp(&choice) { - Ordering::Less => input.parts_mut([part_idx, choice]), + Ordering::Less => input.parts_by_idxs_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_by_idxs_mut([choice, part_idx]); [part, chosen] } }; @@ -196,24 +201,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 = name_choice % other_len; + let name = other.names().nth(choice).unwrap(); - let other_size = other.parts()[choice].mutator_bytes().len(); + let other_size = other.part_by_idx(choice).unwrap().mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } - let parts = input.parts_by_name(name).count(); + let parts = input.parts_with_name(name).count(); if parts > 0 { let (_, part) = input - .parts_by_name_mut(name) + .parts_with_name_mut(name) .nth(part_choice % parts) .unwrap(); drop(other_testcase); @@ -240,26 +245,27 @@ where size, target, range, - other.parts()[choice].mutator_bytes(), + other.part_by_idx(choice).unwrap().mutator_bytes(), )) } else { // just add it! - input.add_part(name.clone(), other.parts()[choice].clone()); + input.append_part(name.clone(), other.part_by_idx(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, + N: 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; @@ -269,25 +275,26 @@ where 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.iter().count(); + if len == 0 { return Ok(MutationResult::Skipped); } - let choice = name_choice % input.names().len(); - let name = input.names()[choice].clone(); + let choice = name_choice % len; + let name = input.names().nth(choice).unwrap(); - let other_size = input.parts()[choice].mutator_bytes().len(); + let other_size = input.part_by_idx(choice).unwrap().mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } - let parts = input.parts_by_name(&name).count() - 1; + let parts = input.parts_with_name(name).count() - 1; if parts == 0 { return Ok(MutationResult::Skipped); } let maybe_size = input - .parts_by_name(&name) + .parts_with_name(name) .filter(|&(p, _)| p != choice) .nth(part_choice % parts) .map(|(id, part)| (id, part.mutator_bytes().len())); @@ -306,12 +313,12 @@ where }); let [part, chosen] = match part_idx.cmp(&choice) { - Ordering::Less => input.parts_mut([part_idx, choice]), + Ordering::Less => input.parts_by_idxs_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_by_idxs_mut([choice, part_idx]); [part, chosen] } }; @@ -331,23 +338,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.iter().count(); + if other_len == 0 { return Ok(MutationResult::Skipped); } - let choice = name_choice % other.names().len(); - let name = &other.names()[choice]; + let choice = name_choice % other_len; + let name = other.names().nth(choice).unwrap(); - let other_size = other.parts()[choice].mutator_bytes().len(); + let other_size = other.part_by_idx(choice).unwrap().mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } - let parts = input.parts_by_name(name).count(); + let parts = input.parts_with_name(name).count(); if parts > 0 { let (_, part) = input - .parts_by_name_mut(name) + .parts_with_name_mut(name) .nth(part_choice % parts) .unwrap(); drop(other_testcase); @@ -372,13 +380,203 @@ where part, target, range, - other.parts()[choice].mutator_bytes(), + other.part_by_idx(choice).unwrap().mutator_bytes(), )) } else { // just add it! - input.add_part(name.clone(), other.parts()[choice].clone()); + input.append_part(name.clone(), other.part_by_idx(choice).unwrap().clone()); Ok(MutationResult::Mutated) } } } + +/// 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, + N: Default, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut MultipartInput, + ) -> Result { + let generated = self.generator.generate(state)?; + input.append_part_with_default_name(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 + N: 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_idx(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, + N: 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 (name, part) = match other_len { + 0 => return Ok(MutationResult::Skipped), + len => other.parts_and_names()[other_idx_raw % len].clone(), + }; + + input.insert_part(current_idx, name, 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, + N: 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 (name, part) = match other_len { + 0 => return Ok(MutationResult::Skipped), + len => other.parts_and_names()[other_idx_raw % len].clone(), + }; + + input.remove_part_at_idx(current_idx); + input.insert_part(current_idx, name, part); + Ok(MutationResult::Mutated) + } +} + +impl Named for CrossoverReplaceMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("CrossoverReplaceMutator") + } +} From d9ce17f3d23aafdd8652f5a691cc5d0049e0883e Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Fri, 14 Feb 2025 14:22:29 +0100 Subject: [PATCH 04/16] Reimplement MultipartInput as a special case of ListInput --- libafl/Cargo.toml | 1 + libafl/src/inputs/multi.rs | 242 ++++++++++++++++------------------- libafl/src/mutators/multi.rs | 51 ++++---- 3 files changed, 139 insertions(+), 155 deletions(-) diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 728ef52edd..bfab12b7ed 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -40,6 +40,7 @@ default = [ "serdeany_autoreg", "libafl_bolts/xxh3", "tui_monitor", + "multipart_inputs", ] document-features = ["dep:document-features"] diff --git a/libafl/src/inputs/multi.rs b/libafl/src/inputs/multi.rs index 20bf00df97..9707915c78 100644 --- a/libafl/src/inputs/multi.rs +++ b/libafl/src/inputs/multi.rs @@ -18,7 +18,7 @@ use libafl_bolts::{ tuples::{Map, MappingFunctor}, Error, Named, }; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use crate::{ corpus::CorpusId, @@ -30,18 +30,18 @@ use crate::{ /// 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<(N, I)>, +pub struct ListInput { + parts: Vec, } -impl Default for MultipartInput { +impl Default for ListInput { fn default() -> Self { Self::new() } } -impl MultipartInput { - /// Create a new multipart input. +impl ListInput { + /// Create a new [`ListInput`]. #[must_use] #[inline] pub fn new() -> Self { @@ -64,14 +64,14 @@ impl MultipartInput { /// Get the individual parts of this input. #[must_use] #[inline] - pub fn parts_and_names(&self) -> &[(N, I)] { + pub fn parts(&self) -> &[I] { &self.parts } /// Get the individual parts of this input. #[must_use] #[inline] - pub fn parts_and_names_mut(&mut self) -> &mut [(N, I)] { + pub fn parts_mut(&mut self) -> &mut [I] { &mut self.parts } @@ -79,14 +79,14 @@ impl MultipartInput { #[must_use] #[inline] pub fn part_by_idx(&self, idx: usize) -> Option<&I> { - self.parts.get(idx).map(|(_, i)| i) + self.parts.get(idx) } /// Get a specific part of this input by index. #[must_use] #[inline] pub fn part_by_idx_mut(&mut self, idx: usize) -> Option<&mut I> { - self.parts.get_mut(idx).map(|(_, i)| i) + self.parts.get_mut(idx) } /// Access multiple parts mutably. @@ -101,12 +101,7 @@ impl MultipartInput { let mut parts = self.parts.iter_mut(); if let Ok(arr) = idxs .into_iter() - .map(|i| { - parts - .nth(i) - .map(|(_, i)| i) - .expect("idx had an out of bounds entry") - }) + .map(|i| parts.nth(i).expect("idx had an out of bounds entry")) .collect::>() .into_inner() { @@ -117,24 +112,16 @@ impl MultipartInput { } } - /// 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. + /// Adds a part to this input #[inline] - pub fn names(&self) -> impl Iterator { - self.parts.iter().map(|(n, _)| n) + pub fn append_part(&mut self, part: I) { + self.parts.push(part); } - /// Adds a part to this input, potentially with the same name as an existing part. + /// Inserts a part to this input at the given index #[inline] - pub fn append_part(&mut self, name: N, part: I) { - self.parts.push((name, part)); - } - - /// Inserts a part to this input at the given index, potentially with the same name as an existing part. - #[inline] - pub fn insert_part(&mut self, idx: usize, name: N, part: I) { - self.parts.insert(idx, (name, part)); + 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. @@ -151,19 +138,19 @@ impl MultipartInput { /// /// Returns [`None`] if the input is empty. #[inline] - pub fn pop_part(&mut self) -> Option<(N, I)> { + pub fn pop_part(&mut self) -> Option { self.parts.pop() } /// Iterate over the parts of this input; no order is specified. #[inline] - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.parts.iter() } /// Iterate over the parts of this input; no order is specified. #[inline] - pub fn iter_mut(&mut self) -> impl Iterator { + pub fn iter_mut(&mut self) -> impl Iterator { self.parts.iter_mut() } @@ -181,7 +168,7 @@ impl MultipartInput { self.parts.is_empty() } - /// Map a tuple of mutators targeting [`MultipartInput`]'s inner type to a tuple of mutators able to work on the entire [`MultipartInput`], + /// 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] @@ -191,7 +178,7 @@ impl MultipartInput { inner.map(ToLastEntryMutator) } - /// Map a tuple of mutators targeting [`MultipartInput`]'s inner type to a tuple of mutators able to work on the entire [`MultipartInput`], + /// 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] @@ -202,42 +189,9 @@ impl MultipartInput { } } -impl MultipartInput -where - N: PartialEq, -{ - /// Gets a reference to each part with the provided name. - pub fn parts_with_name<'a, 'b>( - &'b self, - name: &'a N, - ) -> impl Iterator + 'a - where - 'b: 'a, - { - self.parts - .iter() - .enumerate() - .filter_map(move |(i, (n, input))| (name == n).then_some((i, input))) - } - - /// Gets a mutable reference to each part with the provided name. - pub fn parts_with_name_mut<'a, 'b>( - &'b mut self, - name: &'a N, - ) -> impl Iterator + 'a - where - 'b: 'a, - { - self.parts - .iter_mut() - .enumerate() - .filter_map(move |(i, (n, input))| (name == n).then_some((i, input))) - } -} - -impl From for MultipartInput +impl From for ListInput where - It: IntoIterator, + It: IntoIterator, { fn from(parts: It) -> Self { let vec = parts.into_iter().collect(); @@ -245,43 +199,24 @@ where } } -impl MultipartInput -where - N: Default, -{ - /// Create a new multipart input with default names. - #[must_use] - pub fn with_default_names>(parts: It) -> Self { - let vec = parts.into_iter().map(|i| (N::default(), i)).collect(); - Self { parts: vec } - } - - /// Append a part to this input with a default name. - #[inline] - pub fn append_part_with_default_name(&mut self, part: I) { - self.parts.push((N::default(), part)); - } -} - -impl Input for MultipartInput +impl Input for ListInput where I: Input, - N: Debug + Hash + Serialize + DeserializeOwned + Clone, { fn generate_name(&self, id: Option) -> String { if self.parts.is_empty() { - "empty_multipart_input".to_string() // empty strings cause issues with OnDiskCorpus + "empty_list_input".to_string() // empty strings cause issues with OnDiskCorpus } else { self.parts .iter() - .map(|(name, input)| format!("{name:?}-{}", input.generate_name(id))) + .map(|input| input.generate_name(id)) .collect::>() .join(",") } } } -/// Mutator that applies mutations to the last element of a [`MultipartInput`]. +/// Mutator that applies mutations to the last element of a [`ListInput`]. /// /// If the input is empty, [`MutationResult::Skipped`] is returned. #[derive(Debug)] @@ -299,18 +234,14 @@ impl LastEntryMutator { } } -impl Mutator, S> for LastEntryMutator +impl Mutator, S> for LastEntryMutator where M: Mutator, { - fn mutate( - &mut self, - state: &mut S, - input: &mut MultipartInput, - ) -> Result { + 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].1), + len => self.inner.mutate(state, &mut input.parts[len - 1]), } } @@ -337,7 +268,7 @@ impl Named for LastEntryMutator { } } -/// Mutator that applies mutations to a random element of a [`MultipartInput`]. +/// Mutator that applies mutations to a random element of a [`ListInput`]. /// /// If the input is empty, [`MutationResult::Skipped`] is returned. #[derive(Debug)] @@ -355,22 +286,18 @@ impl RandomEntryMutator { } } -impl Mutator, S> for RandomEntryMutator +impl Mutator, S> for RandomEntryMutator where M: Mutator, S: HasRand, { - fn mutate( - &mut self, - state: &mut S, - input: &mut MultipartInput, - ) -> Result { + 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].1) + self.inner.mutate(state, &mut input.parts[index]) } } } @@ -398,13 +325,75 @@ impl Named for RandomEntryMutator { } } +/// An input composed of multiple named parts. +/// +/// It relies on a list to store the names and parts. +pub type MultipartInput = ListInput<(N, I)>; + +/// Trait for inputs composed of multiple named parts. +pub trait NamedMultipartInput { + /// Get the names of the parts of this input. + fn names<'a>(&'a self) -> impl Iterator + where + N: 'a; + /// Get a reference to each part with the provided name. + fn parts_with_name<'a, 'b>(&'b self, name: &'a N) -> impl Iterator + 'a + where + 'b: 'a, + I: 'b; + + /// Gets a mutable reference to each part with the provided name. + fn parts_with_name_mut<'a, 'b>( + &'b mut self, + name: &'a N, + ) -> impl Iterator + 'a + where + 'b: 'a, + I: 'b; +} + +impl NamedMultipartInput for MultipartInput +where + N: PartialEq, +{ + fn names<'a>(&'a self) -> impl Iterator + where + N: 'a, + { + self.iter().map(|(n, _)| n) + } + + fn parts_with_name<'a, 'b>(&'b self, name: &'a N) -> impl Iterator + 'a + where + 'b: 'a, + I: 'b, + { + self.iter() + .enumerate() + .filter_map(move |(i, (n, input))| (name == n).then_some((i, input))) + } + + fn parts_with_name_mut<'a, 'b>( + &'b mut self, + name: &'a N, + ) -> impl Iterator + 'a + where + 'b: 'a, + I: 'b, + { + self.iter_mut() + .enumerate() + .filter_map(move |(i, (n, input))| (name == n).then_some((i, input))) + } +} + #[cfg(test)] mod tests { use alloc::vec::Vec; use tuple_list::tuple_list; - use super::MultipartInput; + use super::ListInput; use crate::{ inputs::ValueInput, mutators::{numeric::IncMutator, MutationResult, MutatorsTuple as _}, @@ -414,15 +403,14 @@ mod tests { #[test] fn map_to_mutate_on_last_part() { let mutator = tuple_list!(IncMutator); - let mut mapped_mutator = - MultipartInput::, ()>::map_to_mutate_on_last_part(mutator); - let mut input: MultipartInput, ()> = - MultipartInput::with_default_names(vec![ValueInput::new(1_u8), ValueInput::new(2)]); - let mut state = NopState::, ()>>::new(); + 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.iter().map(|((), i)| *i).collect::>(), + input.iter().copied().collect::>(), vec![ValueInput::new(1), ValueInput::new(3)] ); } @@ -430,10 +418,9 @@ mod tests { #[test] fn map_to_mutate_on_last_part_empty() { let mutator = tuple_list!(IncMutator); - let mut mapped_mutator = - MultipartInput::<(), ValueInput>::map_to_mutate_on_last_part(mutator); - let mut input = MultipartInput::, ()>::default(); - let mut state = NopState::, ()>>::new(); + 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![]); @@ -442,12 +429,10 @@ mod tests { #[test] fn map_to_mutate_on_random_part() { let mutator = tuple_list!(IncMutator); - let mut mapped_mutator = - MultipartInput::, ()>::map_to_mutate_on_random_part(mutator); + 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 = - MultipartInput::, ()>::with_default_names(initial_input.clone()); - let mut state = NopState::, ()>>::new(); + 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!( @@ -455,7 +440,7 @@ mod tests { input .iter() .zip(initial_input.iter()) - .filter(|&(a, b)| a.1 != *b) + .filter(|&(a, b)| a != b) .count() ); } @@ -463,10 +448,9 @@ mod tests { #[test] fn map_to_mutate_on_random_part_empty() { let mutator = tuple_list!(IncMutator); - let mut mapped_mutator = - MultipartInput::, ()>::map_to_mutate_on_random_part(mutator); - let mut input = MultipartInput::, ()>::default(); - let mut state = NopState::, ()>>::new(); + 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/mutators/multi.rs b/libafl/src/mutators/multi.rs index 9ac5ceb83e..3e8203b87e 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -12,7 +12,10 @@ use crate::{ corpus::{Corpus, CorpusId}, generators::Generator, impl_default_multipart, - inputs::{multi::MultipartInput, HasMutatorBytes, Input, ResizableMutator}, + inputs::{ + multi::MultipartInput, HasMutatorBytes, Input, ListInput, NamedMultipartInput as _, + ResizableMutator, + }, mutators::{ mutations::{ rand_range, BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, @@ -52,7 +55,7 @@ where }; let selected = state.rand_mut().below(parts_len); let mutated = input.part_by_idx_mut(selected).unwrap(); - self.mutate(state, mutated) + self.mutate(state, &mut mutated.1) } fn post_exec(&mut self, state: &mut S, new_corpus_id: Option) -> Result<(), Error> { @@ -145,7 +148,7 @@ where let choice = name_choice % len; let name = input.names().nth(choice).unwrap(); - let other_size = input.part_by_idx(choice).unwrap().mutator_bytes().len(); + let other_size = input.part_by_idx(choice).unwrap().1.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } @@ -187,11 +190,11 @@ where }; return Ok(Self::crossover_insert( - part, + &mut part.1, size, target, range, - chosen.mutator_bytes(), + chosen.1.mutator_bytes(), )); } @@ -209,7 +212,7 @@ where let choice = name_choice % other_len; let name = other.names().nth(choice).unwrap(); - let other_size = other.part_by_idx(choice).unwrap().mutator_bytes().len(); + let other_size = other.part_by_idx(choice).unwrap().1.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } @@ -245,11 +248,11 @@ where size, target, range, - other.part_by_idx(choice).unwrap().mutator_bytes(), + other.part_by_idx(choice).unwrap().1.mutator_bytes(), )) } else { // just add it! - input.append_part(name.clone(), other.part_by_idx(choice).unwrap().clone()); + input.append_part(other.part_by_idx(choice).unwrap().clone()); Ok(MutationResult::Mutated) } @@ -282,7 +285,7 @@ where let choice = name_choice % len; let name = input.names().nth(choice).unwrap(); - let other_size = input.part_by_idx(choice).unwrap().mutator_bytes().len(); + let other_size = input.part_by_idx(choice).unwrap().1.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } @@ -324,10 +327,10 @@ where }; return Ok(Self::crossover_replace( - part, + &mut part.1, target, range, - chosen.mutator_bytes(), + chosen.1.mutator_bytes(), )); } @@ -346,7 +349,7 @@ where let choice = name_choice % other_len; let name = other.names().nth(choice).unwrap(); - let other_size = other.part_by_idx(choice).unwrap().mutator_bytes().len(); + let other_size = other.part_by_idx(choice).unwrap().1.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } @@ -380,11 +383,11 @@ where part, target, range, - other.part_by_idx(choice).unwrap().mutator_bytes(), + other.part_by_idx(choice).unwrap().1.mutator_bytes(), )) } else { // just add it! - input.append_part(name.clone(), other.part_by_idx(choice).unwrap().clone()); + input.append_part(other.part_by_idx(choice).unwrap().clone()); Ok(MutationResult::Mutated) } @@ -405,18 +408,14 @@ impl GenerateToAppendMutator { } } -impl Mutator, S> for GenerateToAppendMutator +impl Mutator, S> for GenerateToAppendMutator where G: Generator, - N: Default, + I: Input, { - fn mutate( - &mut self, - state: &mut S, - input: &mut MultipartInput, - ) -> Result { + fn mutate(&mut self, state: &mut S, input: &mut ListInput) -> Result { let generated = self.generator.generate(state)?; - input.append_part_with_default_name(generated); + input.append_part(generated); Ok(MutationResult::Mutated) } } @@ -521,10 +520,10 @@ where let (name, part) = match other_len { 0 => return Ok(MutationResult::Skipped), - len => other.parts_and_names()[other_idx_raw % len].clone(), + len => other.parts()[other_idx_raw % len].clone(), }; - input.insert_part(current_idx, name, part); + input.insert_part(current_idx, (name, part)); Ok(MutationResult::Mutated) } } @@ -566,11 +565,11 @@ where let (name, part) = match other_len { 0 => return Ok(MutationResult::Skipped), - len => other.parts_and_names()[other_idx_raw % len].clone(), + len => other.parts()[other_idx_raw % len].clone(), }; input.remove_part_at_idx(current_idx); - input.insert_part(current_idx, name, part); + input.insert_part(current_idx, (name, part)); Ok(MutationResult::Mutated) } } From 1cf988c5de9f63b7412064e584d980ff13aae87a Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Fri, 14 Feb 2025 14:22:53 +0100 Subject: [PATCH 05/16] Revert changes to Cargo.toml --- libafl/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index bfab12b7ed..728ef52edd 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -40,7 +40,6 @@ default = [ "serdeany_autoreg", "libafl_bolts/xxh3", "tui_monitor", - "multipart_inputs", ] document-features = ["dep:document-features"] From 2688cb8f5234c048e8616127b3571288a8080f13 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Fri, 14 Feb 2025 14:28:16 +0100 Subject: [PATCH 06/16] Add collection of generic listinput mutators --- libafl/src/mutators/multi.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index 3e8203b87e..095c72814a 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -7,6 +7,7 @@ use core::{ }; use libafl_bolts::{rands::Rand, Error, Named}; +use tuple_list::{tuple_list, tuple_list_type}; use crate::{ corpus::{Corpus, CorpusId}, @@ -33,6 +34,24 @@ use crate::{ 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`]. +#[must_use] +pub fn generic_list_input_mutators() -> GenericListInputMutators { + tuple_list!( + RemoveLastEntryMutator, + RemoveRandomEntryMutator, + CrossoverInsertMutator, + CrossoverReplaceMutator + ) +} /// Marker trait for if the default multipart input mutator implementation is appropriate. /// /// You should implement this type for your mutator if you just want a random part of the input to From 50e6a841e03fe6effea23a1e95e4161bf1f771e2 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Fri, 14 Feb 2025 16:29:00 +0100 Subject: [PATCH 07/16] Fix example --- .../baby_fuzzer_multi/src/main.rs | 10 ++--- libafl/src/inputs/multi.rs | 16 +++++++- libafl/src/mutators/multi.rs | 41 +++++++++++-------- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs index 52edb90852..751024eeb8 100644 --- a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs +++ b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs @@ -143,11 +143,11 @@ pub fn main() { .expect("Failed to create the Executor"); // a generator here is not generalisable - let initial = MultipartInput::with_default_names([ - BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o']), - BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o']), - BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o']), - BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o']), + let initial = MultipartInput::from([ + ((), BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), + ((), BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), + ((), BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), + ((), BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), ]); fuzzer diff --git a/libafl/src/inputs/multi.rs b/libafl/src/inputs/multi.rs index 9707915c78..c4e4253299 100644 --- a/libafl/src/inputs/multi.rs +++ b/libafl/src/inputs/multi.rs @@ -18,7 +18,7 @@ use libafl_bolts::{ tuples::{Map, MappingFunctor}, Error, Named, }; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ corpus::CorpusId, @@ -330,6 +330,20 @@ impl Named for RandomEntryMutator { /// It relies on a list to store the names and parts. pub type MultipartInput = ListInput<(N, I)>; +impl Input for MultipartInput +where + I: Input, + N: PartialEq + Debug + Serialize + DeserializeOwned + Clone + Hash, +{ + fn generate_name(&self, id: Option) -> String { + self.parts + .iter() + .map(|(_n, i)| i.generate_name(id)) + .collect::>() + .join(",") + } +} + /// Trait for inputs composed of multiple named parts. pub trait NamedMultipartInput { /// Get the names of the parts of this input. diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index 095c72814a..ba7411605d 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -69,12 +69,14 @@ where state: &mut S, input: &mut MultipartInput, ) -> Result { - let Some(parts_len) = NonZero::new(input.len()) else { - return Ok(MutationResult::Skipped); - }; - let selected = state.rand_mut().below(parts_len); - let mutated = input.part_by_idx_mut(selected).unwrap(); - self.mutate(state, &mut mutated.1) + match NonZero::new(input.len()) { + None => Ok(MutationResult::Skipped), + Some(len) => { + let idx = state.rand_mut().below(len); + let (_name, part) = &mut input.parts_mut()[idx]; + self.mutate(state, part) + } + } } fn post_exec(&mut self, state: &mut S, new_corpus_id: Option) -> Result<(), Error> { @@ -165,9 +167,11 @@ where return Ok(MutationResult::Skipped); } let choice = name_choice % len; - let name = input.names().nth(choice).unwrap(); + // Safety: len is checked above + let (name, part) = &input.parts()[choice]; + + let other_size = part.mutator_bytes().len(); - let other_size = input.part_by_idx(choice).unwrap().1.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } @@ -229,9 +233,10 @@ where } let choice = name_choice % other_len; - let name = other.names().nth(choice).unwrap(); + // Safety: choice is checked above + let (name, part) = &other.parts()[choice]; - let other_size = other.part_by_idx(choice).unwrap().1.mutator_bytes().len(); + let other_size = part.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } @@ -255,7 +260,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(); @@ -302,9 +307,10 @@ where return Ok(MutationResult::Skipped); } let choice = name_choice % len; - let name = input.names().nth(choice).unwrap(); + // Safety: len is checked above + let (name, part) = &input.parts()[choice]; - let other_size = input.part_by_idx(choice).unwrap().1.mutator_bytes().len(); + let other_size = part.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } @@ -331,7 +337,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 [part, chosen] = match part_idx.cmp(&choice) { @@ -366,9 +372,10 @@ where } let choice = name_choice % other_len; - let name = other.names().nth(choice).unwrap(); + // Safety: choice is checked above + let (name, part) = &other.parts()[choice]; - let other_size = other.part_by_idx(choice).unwrap().1.mutator_bytes().len(); + let other_size = part.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } @@ -391,7 +398,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(); From e62eafb6ad7d48538c4a78bd214c106aac56d9b9 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Fri, 14 Feb 2025 16:44:53 +0100 Subject: [PATCH 08/16] Add note to MIGRATION --- MIGRATION.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index 1a10f84e25..764c263a10 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 a special case of `ListInput`. The interface slightly changed, all functionality is maintained. + - Names are now generic. + - If you don't need the names, consider using `ListInput` directly. ## 0.14.1 -> 0.15.0 From 098c2727563087f87eb97a9c0eae80c6d51efce5 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 18 Feb 2025 14:25:13 +0100 Subject: [PATCH 09/16] Split list and multi into separate modules --- libafl/src/inputs/list.rs | 395 ++++++++++++++++++++++++++++++++++ libafl/src/inputs/mod.rs | 4 + libafl/src/inputs/multi.rs | 397 +---------------------------------- libafl/src/mutators/list.rs | 223 ++++++++++++++++++++ libafl/src/mutators/mod.rs | 2 + libafl/src/mutators/multi.rs | 228 +------------------- 6 files changed, 640 insertions(+), 609 deletions(-) create mode 100644 libafl/src/inputs/list.rs create mode 100644 libafl/src/mutators/list.rs diff --git a/libafl/src/inputs/list.rs b/libafl/src/inputs/list.rs new file mode 100644 index 0000000000..fb8aa0b053 --- /dev/null +++ b/libafl/src/inputs/list.rs @@ -0,0 +1,395 @@ +//! Definitions for inputs which have multiple distinct subcomponents. +//! +//! Unfortunately, since both [`serde::de::Deserialize`] and [`Clone`] require [`Sized`], it is not +//! possible to dynamically define a single input with dynamic typing. As such, [`MultipartInput`] +//! requires that each subcomponent be the same subtype. + +use alloc::{ + 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::new() + } +} + +impl ListInput { + /// Create a new [`ListInput`]. + #[must_use] + #[inline] + pub fn new() -> Self { + Self { parts: 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] + #[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() + } + + /// Iterate over the parts of this input; no order is specified. + #[inline] + pub fn iter(&self) -> impl Iterator { + self.parts.iter() + } + + /// Iterate over the parts of this input; no order is specified. + #[inline] + pub fn iter_mut(&mut self) -> impl Iterator { + self.parts.iter_mut() + } + + /// 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 alloc::vec::Vec; + + 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.iter().copied().collect::>(), + 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 + .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 c4e4253299..9fcde81637 100644 --- a/libafl/src/inputs/multi.rs +++ b/libafl/src/inputs/multi.rs @@ -1,330 +1,15 @@ -//! 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 named parts. -use alloc::{ - borrow::Cow, - fmt::Debug, - string::{String, ToString}, - vec::Vec, -}; -use core::{hash::Hash, num::NonZero}; +use alloc::{fmt::Debug, string::String, vec::Vec}; +use core::hash::Hash; -use arrayvec::ArrayVec; -use libafl_bolts::{ - rands::Rand as _, - tuples::{Map, MappingFunctor}, - Error, Named, -}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Serialize}; use crate::{ corpus::CorpusId, - inputs::Input, - mutators::{MutationResult, Mutator}, - state::HasRand, + 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 ListInput { - parts: Vec, -} - -impl Default for ListInput { - fn default() -> Self { - Self::new() - } -} - -impl ListInput { - /// Create a new [`ListInput`]. - #[must_use] - #[inline] - pub fn new() -> Self { - Self { parts: 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] - #[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_by_idx(&self, idx: usize) -> Option<&I> { - self.parts.get(idx) - } - - /// Get a specific part of this input by index. - #[must_use] - #[inline] - pub fn part_by_idx_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_by_idxs_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_idx(&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() - } - - /// Iterate over the parts of this input; no order is specified. - #[inline] - pub fn iter(&self) -> impl Iterator { - self.parts.iter() - } - - /// Iterate over the parts of this input; no order is specified. - #[inline] - pub fn iter_mut(&mut self) -> impl Iterator { - self.parts.iter_mut() - } - - /// 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 - } -} - /// An input composed of multiple named parts. /// /// It relies on a list to store the names and parts. @@ -336,7 +21,7 @@ where N: PartialEq + Debug + Serialize + DeserializeOwned + Clone + Hash, { fn generate_name(&self, id: Option) -> String { - self.parts + self.parts() .iter() .map(|(_n, i)| i.generate_name(id)) .collect::>() @@ -400,73 +85,3 @@ where .filter_map(move |(i, (n, input))| (name == n).then_some((i, input))) } } - -#[cfg(test)] -mod tests { - use alloc::vec::Vec; - - 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.iter().copied().collect::>(), - 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 - .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/mutators/list.rs b/libafl/src/mutators/list.rs new file mode 100644 index 0000000000..afe5448c5b --- /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 + N: 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, + N: 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 (name, part) = match other_len { + 0 => return Ok(MutationResult::Skipped), + len => other.parts()[other_idx_raw % len].clone(), + }; + + input.insert_part(current_idx, (name, 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, + N: 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 (name, 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, (name, 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 507309bd66..01d972aa90 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -38,6 +38,8 @@ pub mod unicode; #[cfg(feature = "unicode")] pub use unicode::*; +#[cfg(feature = "multipart_inputs")] +pub mod list; #[cfg(feature = "multipart_inputs")] pub mod multi; diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index ba7411605d..cb52bce740 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -1,21 +1,17 @@ //! Mutator definitions for [`MultipartInput`]s. See [`crate::inputs::multi`] for details. -use alloc::borrow::Cow; use core::{ cmp::{min, Ordering}, num::NonZero, }; -use libafl_bolts::{rands::Rand, Error, Named}; -use tuple_list::{tuple_list, tuple_list_type}; +use libafl_bolts::{rands::Rand, Error}; use crate::{ corpus::{Corpus, CorpusId}, - generators::Generator, impl_default_multipart, inputs::{ - multi::MultipartInput, HasMutatorBytes, Input, ListInput, NamedMultipartInput as _, - ResizableMutator, + multi::MultipartInput, HasMutatorBytes, Input, NamedMultipartInput as _, ResizableMutator, }, mutators::{ mutations::{ @@ -34,24 +30,6 @@ use crate::{ 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`]. -#[must_use] -pub fn generic_list_input_mutators() -> GenericListInputMutators { - tuple_list!( - RemoveLastEntryMutator, - RemoveRandomEntryMutator, - CrossoverInsertMutator, - CrossoverReplaceMutator - ) -} /// Marker trait for if the default multipart input mutator implementation is appropriate. /// /// You should implement this type for your mutator if you just want a random part of the input to @@ -202,12 +180,12 @@ where }); let [part, chosen] = match part_idx.cmp(&choice) { - Ordering::Less => input.parts_by_idxs_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_by_idxs_mut([choice, part_idx]); + let [chosen, part] = input.parts_at_indices_mut([choice, part_idx]); [part, chosen] } }; @@ -272,11 +250,11 @@ where size, target, range, - other.part_by_idx(choice).unwrap().1.mutator_bytes(), + other.part_at_index(choice).unwrap().1.mutator_bytes(), )) } else { // just add it! - input.append_part(other.part_by_idx(choice).unwrap().clone()); + input.append_part(other.part_at_index(choice).unwrap().clone()); Ok(MutationResult::Mutated) } @@ -341,12 +319,12 @@ where }); let [part, chosen] = match part_idx.cmp(&choice) { - Ordering::Less => input.parts_by_idxs_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_by_idxs_mut([choice, part_idx]); + let [chosen, part] = input.parts_at_indices_mut([choice, part_idx]); [part, chosen] } }; @@ -409,199 +387,13 @@ where part, target, range, - other.part_by_idx(choice).unwrap().1.mutator_bytes(), + other.part_at_index(choice).unwrap().1.mutator_bytes(), )) } else { // just add it! - input.append_part(other.part_by_idx(choice).unwrap().clone()); + input.append_part(other.part_at_index(choice).unwrap().clone()); Ok(MutationResult::Mutated) } } } - -/// 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 - N: 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_idx(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, - N: 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 (name, part) = match other_len { - 0 => return Ok(MutationResult::Skipped), - len => other.parts()[other_idx_raw % len].clone(), - }; - - input.insert_part(current_idx, (name, 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, - N: 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 (name, part) = match other_len { - 0 => return Ok(MutationResult::Skipped), - len => other.parts()[other_idx_raw % len].clone(), - }; - - input.remove_part_at_idx(current_idx); - input.insert_part(current_idx, (name, part)); - Ok(MutationResult::Mutated) - } -} - -impl Named for CrossoverReplaceMutator { - fn name(&self) -> &Cow<'static, str> { - &Cow::Borrowed("CrossoverReplaceMutator") - } -} From 3f543e1bc689fe084e369199251e99b43728aed5 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 18 Feb 2025 14:38:10 +0100 Subject: [PATCH 10/16] Fix docs --- libafl/src/inputs/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl/src/inputs/list.rs b/libafl/src/inputs/list.rs index fb8aa0b053..cd6d20022f 100644 --- a/libafl/src/inputs/list.rs +++ b/libafl/src/inputs/list.rs @@ -1,7 +1,7 @@ //! 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`] +//! possible to dynamically define a single input with dynamic typing. As such, [`ListInput`] //! requires that each subcomponent be the same subtype. use alloc::{ From e7f327e3ad75dbc548d2641b0a33c213e848e250 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Wed, 19 Feb 2025 18:45:44 +0100 Subject: [PATCH 11/16] Using string names again in the multi example fuzzer --- .../baby_fuzzer_multi/src/main.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs index 751024eeb8..d1a8add121 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,7 +43,7 @@ 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 harness = |input: &MultipartInput| { let mut count = input.len(); for (i, input) in input.iter().enumerate() { let target = input.1.target_bytes(); @@ -143,12 +143,13 @@ pub fn main() { .expect("Failed to create the Executor"); // a generator here is not generalisable - let initial = MultipartInput::from([ - ((), BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), - ((), BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), - ((), BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), - ((), BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), - ]); + let initial = MultipartInput::from( + iter::repeat(( + "part".to_string(), + BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o']), + )) + .take(4), + ); fuzzer .evaluate_input(&mut state, &mut executor, &mut mgr, &initial) From be2c7d4f33b0c9249216c3dc7ad7693538ff51a7 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Wed, 19 Feb 2025 21:45:42 +0100 Subject: [PATCH 12/16] Remove unnecessary code --- libafl/src/inputs/list.rs | 33 ++++++++++++--------------------- libafl/src/inputs/multi.rs | 8 +++++--- libafl/src/mutators/multi.rs | 4 ++-- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/libafl/src/inputs/list.rs b/libafl/src/inputs/list.rs index cd6d20022f..38a51996c7 100644 --- a/libafl/src/inputs/list.rs +++ b/libafl/src/inputs/list.rs @@ -35,18 +35,25 @@ pub struct ListInput { impl Default for ListInput { fn default() -> Self { - Self::new() + Self::empty() } } impl ListInput { - /// Create a new [`ListInput`]. + /// Create a new empty [`ListInput`]. #[must_use] #[inline] - pub fn new() -> Self { + 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]; @@ -141,18 +148,6 @@ impl ListInput { self.parts.pop() } - /// Iterate over the parts of this input; no order is specified. - #[inline] - pub fn iter(&self) -> impl Iterator { - self.parts.iter() - } - - /// Iterate over the parts of this input; no order is specified. - #[inline] - pub fn iter_mut(&mut self) -> impl Iterator { - self.parts.iter_mut() - } - /// Get the number of parts in this input. #[must_use] #[inline] @@ -326,8 +321,6 @@ impl Named for RandomEntryMutator { #[cfg(test)] mod tests { - use alloc::vec::Vec; - use tuple_list::tuple_list; use super::ListInput; @@ -346,10 +339,7 @@ mod tests { let mut state = NopState::>>::new(); let res = mapped_mutator.mutate_all(&mut state, &mut input); assert_eq!(res.unwrap(), MutationResult::Mutated); - assert_eq!( - input.iter().copied().collect::>(), - vec![ValueInput::new(1), ValueInput::new(3)] - ); + assert_eq!(input.parts, vec![ValueInput::new(1), ValueInput::new(3)]); } #[test] @@ -375,6 +365,7 @@ mod tests { assert_eq!( 1, input + .parts .iter() .zip(initial_input.iter()) .filter(|&(a, b)| a != b) diff --git a/libafl/src/inputs/multi.rs b/libafl/src/inputs/multi.rs index 9fcde81637..451547c6b0 100644 --- a/libafl/src/inputs/multi.rs +++ b/libafl/src/inputs/multi.rs @@ -59,7 +59,7 @@ where where N: 'a, { - self.iter().map(|(n, _)| n) + self.parts().iter().map(|(n, _)| n) } fn parts_with_name<'a, 'b>(&'b self, name: &'a N) -> impl Iterator + 'a @@ -67,7 +67,8 @@ where 'b: 'a, I: 'b, { - self.iter() + self.parts() + .iter() .enumerate() .filter_map(move |(i, (n, input))| (name == n).then_some((i, input))) } @@ -80,7 +81,8 @@ where 'b: 'a, I: 'b, { - self.iter_mut() + self.parts_mut() + .iter_mut() .enumerate() .filter_map(move |(i, (n, input))| (name == n).then_some((i, input))) } diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index cb52bce740..ff0671f005 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -280,7 +280,7 @@ where let id = random_corpus_id!(state.corpus(), state.rand_mut()); if let Some(cur) = state.corpus().current() { if id == *cur { - let len = input.iter().count(); + let len = input.len(); if len == 0 { return Ok(MutationResult::Skipped); } @@ -344,7 +344,7 @@ where let mut other_testcase = state.corpus().get(id)?.borrow_mut(); let other = other_testcase.load_input(state.corpus())?; - let other_len = other.iter().count(); + let other_len = other.len(); if other_len == 0 { return Ok(MutationResult::Skipped); } From c65b15f922f3dd840155afb68e17e5d9e0b2c4aa Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Wed, 19 Feb 2025 22:27:50 +0100 Subject: [PATCH 13/16] Fix fuzzer --- fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs index d1a8add121..b342e2e6ee 100644 --- a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs +++ b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs @@ -45,8 +45,8 @@ pub fn main() { // The closure that we want to fuzz let mut harness = |input: &MultipartInput| { let mut count = input.len(); - for (i, input) in input.iter().enumerate() { - let target = input.1.target_bytes(); + for (i, (_name, input)) in input.parts().iter().enumerate() { + let target = input.target_bytes(); let buf = target.as_slice(); signals_set(i * 8); if !buf.is_empty() && buf[0] == b'a' { From 8d3f39f96e8f3a589df51e0a0a5922d71f84cde2 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Thu, 20 Feb 2025 11:54:50 +0100 Subject: [PATCH 14/16] Use key instead of name for MultipartInput key --- MIGRATION.md | 6 ++-- libafl/src/inputs/multi.rs | 63 +++++++++++++++++---------------- libafl/src/mutators/list.rs | 34 +++++++++--------- libafl/src/mutators/multi.rs | 68 ++++++++++++++++-------------------- 4 files changed, 83 insertions(+), 88 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 764c263a10..6de6720870 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -13,9 +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 a special case of `ListInput`. The interface slightly changed, all functionality is maintained. - - Names are now generic. - - If you don't need the names, consider using `ListInput` directly. +- `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/libafl/src/inputs/multi.rs b/libafl/src/inputs/multi.rs index 451547c6b0..89ce8efbd5 100644 --- a/libafl/src/inputs/multi.rs +++ b/libafl/src/inputs/multi.rs @@ -1,4 +1,4 @@ -//! An input composed of multiple named parts. +//! An input composed of multiple parts identified by a key. use alloc::{fmt::Debug, string::String, vec::Vec}; use core::hash::Hash; @@ -10,59 +10,62 @@ use crate::{ inputs::{Input, ListInput}, }; -/// An input composed of multiple named parts. +/// An input composed of multiple parts, each identified by a key. /// -/// It relies on a list to store the names and parts. -pub type MultipartInput = ListInput<(N, I)>; +/// It relies on a list to store the keys and parts. Keys may appear multiple times. +pub type MultipartInput = ListInput<(K, I)>; -impl Input for MultipartInput +impl Input for MultipartInput where I: Input, - N: PartialEq + Debug + Serialize + DeserializeOwned + Clone + Hash, + K: PartialEq + Debug + Serialize + DeserializeOwned + Clone + Hash, { fn generate_name(&self, id: Option) -> String { self.parts() .iter() - .map(|(_n, i)| i.generate_name(id)) + .map(|(_k, i)| i.generate_name(id)) .collect::>() .join(",") } } -/// Trait for inputs composed of multiple named parts. -pub trait NamedMultipartInput { - /// Get the names of the parts of this input. - fn names<'a>(&'a self) -> impl Iterator +/// Trait for types that provide a way to access parts by key. +pub trait Keyed { + /// Get the keys of the parts of this input. + /// + /// Keys may appear multiple times if they are used multiple times in the input. + fn keys<'a>(&'a self) -> impl Iterator where - N: 'a; - /// Get a reference to each part with the provided name. - fn parts_with_name<'a, 'b>(&'b self, name: &'a N) -> impl Iterator + 'a + K: '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 + 'a where 'b: 'a, - I: 'b; + V: 'b; - /// Gets a mutable reference to each part with the provided name. - fn parts_with_name_mut<'a, '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, - name: &'a N, - ) -> impl Iterator + 'a + key: &'a K, + ) -> impl Iterator + 'a where 'b: 'a, - I: 'b; + V: 'b; } -impl NamedMultipartInput for MultipartInput +impl Keyed for MultipartInput where - N: PartialEq, + K: PartialEq, { - fn names<'a>(&'a self) -> impl Iterator + fn keys<'a>(&'a self) -> impl Iterator where - N: 'a, + K: 'a, { - self.parts().iter().map(|(n, _)| n) + self.parts().iter().map(|(k, _)| k) } - fn parts_with_name<'a, 'b>(&'b self, name: &'a N) -> impl Iterator + 'a + fn with_key<'a, 'b>(&'b self, key: &'a K) -> impl Iterator + 'a where 'b: 'a, I: 'b, @@ -70,12 +73,12 @@ where self.parts() .iter() .enumerate() - .filter_map(move |(i, (n, input))| (name == n).then_some((i, input))) + .filter_map(move |(i, (k, input))| (key == k).then_some((i, input))) } - fn parts_with_name_mut<'a, 'b>( + fn with_key_mut<'a, 'b>( &'b mut self, - name: &'a N, + key: &'a K, ) -> impl Iterator + 'a where 'b: 'a, @@ -84,6 +87,6 @@ where self.parts_mut() .iter_mut() .enumerate() - .filter_map(move |(i, (n, input))| (name == n).then_some((i, input))) + .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 index afe5448c5b..4c1e122846 100644 --- a/libafl/src/mutators/list.rs +++ b/libafl/src/mutators/list.rs @@ -74,14 +74,14 @@ impl Named for GenerateToAppendMutator { #[derive(Debug)] pub struct RemoveLastEntryMutator; -impl Mutator, S> for RemoveLastEntryMutator +impl Mutator, S> for RemoveLastEntryMutator where - N: Default, + K: Default, { fn mutate( &mut self, _state: &mut S, - input: &mut MultipartInput, + input: &mut MultipartInput, ) -> Result { match input.pop_part() { Some(_) => Ok(MutationResult::Mutated), @@ -102,14 +102,14 @@ impl Named for RemoveLastEntryMutator { #[derive(Debug)] pub struct RemoveRandomEntryMutator; -impl Mutator, S> for RemoveRandomEntryMutator +impl Mutator, S> for RemoveRandomEntryMutator where S: HasRand, { fn mutate( &mut self, state: &mut S, - input: &mut MultipartInput, + input: &mut MultipartInput, ) -> Result { match MultipartInput::len(input) { 0 => Ok(MutationResult::Skipped), @@ -135,16 +135,16 @@ impl Named for RemoveRandomEntryMutator { #[derive(Debug)] pub struct CrossoverInsertMutator; -impl Mutator, S> for CrossoverInsertMutator +impl Mutator, S> for CrossoverInsertMutator where - S: HasCorpus> + HasMaxSize + HasRand, + S: HasCorpus> + HasMaxSize + HasRand, I: Clone, - N: Clone, + K: Clone, { fn mutate( &mut self, state: &mut S, - input: &mut MultipartInput, + input: &mut MultipartInput, ) -> Result { let current_idx = match input.len() { 0 => return Ok(MutationResult::Skipped), @@ -160,12 +160,12 @@ where let other_len = other.len(); - let (name, part) = match 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, (name, part)); + input.insert_part(current_idx, (key, part)); Ok(MutationResult::Mutated) } } @@ -180,16 +180,16 @@ impl Named for CrossoverInsertMutator { #[derive(Debug)] pub struct CrossoverReplaceMutator; -impl Mutator, S> for CrossoverReplaceMutator +impl Mutator, S> for CrossoverReplaceMutator where - S: HasCorpus> + HasMaxSize + HasRand, + S: HasCorpus> + HasMaxSize + HasRand, I: Clone, - N: Clone, + K: Clone, { fn mutate( &mut self, state: &mut S, - input: &mut MultipartInput, + input: &mut MultipartInput, ) -> Result { let current_idx = match input.len() { 0 => return Ok(MutationResult::Skipped), @@ -205,13 +205,13 @@ where let other_len = other.len(); - let (name, part) = match 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, (name, part)); + input.insert_part(current_idx, (key, part)); Ok(MutationResult::Mutated) } } diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index ff0671f005..f17c64bb61 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -10,9 +10,7 @@ use libafl_bolts::{rands::Rand, Error}; use crate::{ corpus::{Corpus, CorpusId}, impl_default_multipart, - inputs::{ - multi::MultipartInput, HasMutatorBytes, Input, NamedMultipartInput as _, ResizableMutator, - }, + inputs::{multi::MultipartInput, HasMutatorBytes, Input, Keyed as _, ResizableMutator}, mutators::{ mutations::{ rand_range, BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, @@ -37,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, @@ -45,13 +43,13 @@ where fn mutate( &mut self, state: &mut S, - input: &mut MultipartInput, + input: &mut MultipartInput, ) -> Result { match NonZero::new(input.len()) { None => Ok(MutationResult::Skipped), Some(len) => { let idx = state.rand_mut().below(len); - let (_name, part) = &mut input.parts_mut()[idx]; + let (_key, part) = &mut input.parts_mut()[idx]; self.mutate(state, part) } } @@ -121,19 +119,19 @@ impl_default_multipart!( I2SRandReplace, ); -impl Mutator, S> for BytesInputCrossoverInsertMutator +impl Mutator, S> for BytesInputCrossoverInsertMutator where - S: HasCorpus> + HasMaxSize + HasRand, + S: HasCorpus> + HasMaxSize + HasRand, I: Input + ResizableMutator + HasMutatorBytes, - N: Clone + PartialEq, + 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 @@ -144,9 +142,9 @@ where if len == 0 { return Ok(MutationResult::Skipped); } - let choice = name_choice % len; + let choice = key_choice % len; // Safety: len is checked above - let (name, part) = &input.parts()[choice]; + let (key, part) = &input.parts()[choice]; let other_size = part.mutator_bytes().len(); @@ -154,14 +152,14 @@ where return Ok(MutationResult::Skipped); } - let parts = input.parts_with_name(name).count() - 1; + let parts = input.with_key(key).count() - 1; if parts == 0 { return Ok(MutationResult::Skipped); } let maybe_size = input - .parts_with_name(name) + .with_key(key) .filter(|&(p, _)| p != choice) .nth(part_choice % parts) .map(|(id, part)| (id, part.mutator_bytes().len())); @@ -210,22 +208,19 @@ where return Ok(MutationResult::Skipped); } - let choice = name_choice % other_len; + let choice = key_choice % other_len; // Safety: choice is checked above - let (name, part) = &other.parts()[choice]; + let (key, part) = &other.parts()[choice]; let other_size = part.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } - let parts = input.parts_with_name(name).count(); + let parts = input.with_key(key).count(); if parts > 0 { - let (_, part) = input - .parts_with_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 { @@ -261,19 +256,19 @@ where } } -impl Mutator, S> for BytesInputCrossoverReplaceMutator +impl Mutator, S> for BytesInputCrossoverReplaceMutator where - S: HasCorpus> + HasMaxSize + HasRand, + S: HasCorpus> + HasMaxSize + HasRand, I: Input + ResizableMutator + HasMutatorBytes, - N: Clone + PartialEq, + 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 @@ -284,23 +279,23 @@ where if len == 0 { return Ok(MutationResult::Skipped); } - let choice = name_choice % len; + let choice = key_choice % len; // Safety: len is checked above - let (name, part) = &input.parts()[choice]; + let (key, part) = &input.parts()[choice]; let other_size = part.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } - let parts = input.parts_with_name(name).count() - 1; + let parts = input.with_key(key).count() - 1; if parts == 0 { return Ok(MutationResult::Skipped); } let maybe_size = input - .parts_with_name(name) + .with_key(key) .filter(|&(p, _)| p != choice) .nth(part_choice % parts) .map(|(id, part)| (id, part.mutator_bytes().len())); @@ -349,22 +344,19 @@ where return Ok(MutationResult::Skipped); } - let choice = name_choice % other_len; + let choice = key_choice % other_len; // Safety: choice is checked above - let (name, part) = &other.parts()[choice]; + let (key, part) = &other.parts()[choice]; let other_size = part.mutator_bytes().len(); if other_size < 2 { return Ok(MutationResult::Skipped); } - let parts = input.parts_with_name(name).count(); + let parts = input.with_key(key).count(); if parts > 0 { - let (_, part) = input - .parts_with_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 { From 73cc8926adca497a41425d92bc36d3c259e3d344 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Thu, 20 Feb 2025 11:58:32 +0100 Subject: [PATCH 15/16] Prettier code in example fuzzer --- fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs index b342e2e6ee..9c491aa90d 100644 --- a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs +++ b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs @@ -144,11 +144,7 @@ pub fn main() { // a generator here is not generalisable let initial = MultipartInput::from( - iter::repeat(( - "part".to_string(), - BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o']), - )) - .take(4), + iter::repeat(("part".to_string(), BytesInput::new(b"hello".to_vec()))).take(4), ); fuzzer From 33bb0f22c71ca86cac51680e7544df466a4779c0 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Thu, 20 Feb 2025 13:38:19 +0100 Subject: [PATCH 16/16] Do not convert slice to vec manually --- fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs index 9c491aa90d..7ced193195 100644 --- a/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs +++ b/fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs @@ -144,7 +144,7 @@ pub fn main() { // a generator here is not generalisable let initial = MultipartInput::from( - iter::repeat(("part".to_string(), BytesInput::new(b"hello".to_vec()))).take(4), + iter::repeat(("part".to_string(), BytesInput::from(&b"hello"[..]))).take(4), ); fuzzer