From 684b31279e0b903300dd863e7fa5eba14c13bdae Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 22 May 2024 01:50:07 +0200 Subject: [PATCH] Add BytesSubInput to mutate sub-parts of a bytes-backed input (#2220) * Add BytesSubMutator that allows us to mutate sub-parts of a bytes-backed input * no_std * fix string mutator * make build * Fix clippy on macOS * Docs * More docs * Better docs * --amend * Renamed bsi to sub_input. Too much BSI * More more * balance backticks * Make splicing usable with sub_input (not that it makes sense) * More annotations * more input annotations? * Implement HasMutatorBytes for &mut Vec * clippy * Use a wrapper type instead * Add wrapper type for Vec as well * Remove the duplicate BytesInput... lol --- fuzzers/baby_fuzzer_minimizing/src/main.rs | 6 +- libafl/src/corpus/testcase.rs | 4 +- libafl/src/inputs/bytes.rs | 29 +- libafl/src/inputs/bytessub.rs | 426 ++++++++++++++++++ libafl/src/inputs/mod.rs | 98 +++- libafl/src/mutators/mod.rs | 12 +- libafl/src/mutators/multi.rs | 6 +- libafl/src/mutators/mutations.rs | 80 ++-- libafl/src/mutators/scheduled.rs | 2 +- libafl/src/mutators/string.rs | 10 +- libafl/src/mutators/token_mutations.rs | 12 +- libafl/src/stages/colorization.rs | 6 +- libafl/src/stages/concolic.rs | 4 +- libafl/src/stages/generalization.rs | 18 +- libafl/src/stages/string.rs | 2 +- libafl/src/state/mod.rs | 10 + .../libafl_libfuzzer_runtime/src/tmin.rs | 2 +- libafl_qemu/build.rs | 2 + libafl_targets/src/libfuzzer/mutators.rs | 6 +- 19 files changed, 634 insertions(+), 101 deletions(-) create mode 100644 libafl/src/inputs/bytessub.rs diff --git a/fuzzers/baby_fuzzer_minimizing/src/main.rs b/fuzzers/baby_fuzzer_minimizing/src/main.rs index f4b23658ea..0b5a4e9d51 100644 --- a/fuzzers/baby_fuzzer_minimizing/src/main.rs +++ b/fuzzers/baby_fuzzer_minimizing/src/main.rs @@ -94,8 +94,8 @@ pub fn main() -> Result<(), Error> { .expect("Failed to generate the initial corpus"); // Setup a mutational stage with a basic bytes mutator - let mutator = StdScheduledMutator::new(havoc_mutations()); - let minimizer = StdScheduledMutator::new(havoc_mutations()); + let mutator = StdScheduledMutator::new(havoc_mutations::()); + let minimizer = StdScheduledMutator::new(havoc_mutations::()); let mut stages = tuple_list!( StdMutationalStage::new(mutator), StdTMinMutationalStage::new(minimizer, factory, 128) @@ -121,7 +121,7 @@ pub fn main() -> Result<(), Error> { let mut mgr = SimpleEventManager::new(mon); - let minimizer = StdScheduledMutator::new(havoc_mutations()); + let minimizer = StdScheduledMutator::new(havoc_mutations::()); let mut stages = tuple_list!(StdTMinMutationalStage::new( minimizer, CrashFeedback::new(), diff --git a/libafl/src/corpus/testcase.rs b/libafl/src/corpus/testcase.rs index 2296d4f323..6620483dc2 100644 --- a/libafl/src/corpus/testcase.rs +++ b/libafl/src/corpus/testcase.rs @@ -1,4 +1,4 @@ -//! The testcase is a struct embedded in each corpus. +//! The [`Testcase`] is a struct embedded in each [`Corpus`]. //! It will contain a respective input, and metadata. use alloc::string::String; @@ -34,7 +34,7 @@ pub trait HasTestcase: UsesInput { ) -> Result::Input>>, Error>; } -/// An entry in the Testcase Corpus +/// An entry in the [`Testcase`] Corpus #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(bound = "I: serde::de::DeserializeOwned")] pub struct Testcase diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index bcb846bd09..4fa9e0143b 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -15,7 +15,7 @@ use libafl_bolts::{fs::write_file_atomic, Error}; use libafl_bolts::{ownedref::OwnedSlice, HasLen}; use serde::{Deserialize, Serialize}; -use crate::inputs::{HasBytesVec, HasTargetBytes, Input}; +use crate::inputs::{HasMutatorBytes, HasTargetBytes, Input}; /// A bytes input is the basic input #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash)] @@ -61,16 +61,39 @@ impl From for Rc> { } } -impl HasBytesVec for BytesInput { +impl HasMutatorBytes for BytesInput { #[inline] fn bytes(&self) -> &[u8] { &self.bytes } #[inline] - fn bytes_mut(&mut self) -> &mut Vec { + fn bytes_mut(&mut self) -> &mut [u8] { &mut self.bytes } + + fn resize(&mut self, new_len: usize, value: u8) { + self.bytes.resize(new_len, value); + } + + fn extend<'a, I: IntoIterator>(&mut self, iter: I) { + Extend::extend(&mut self.bytes, iter); + } + + fn splice(&mut self, range: R, replace_with: I) -> alloc::vec::Splice<'_, I::IntoIter> + where + R: core::ops::RangeBounds, + I: IntoIterator, + { + self.bytes.splice(range, replace_with) + } + + fn drain(&mut self, range: R) -> alloc::vec::Drain<'_, u8> + where + R: core::ops::RangeBounds, + { + self.bytes.drain(range) + } } impl HasTargetBytes for BytesInput { diff --git a/libafl/src/inputs/bytessub.rs b/libafl/src/inputs/bytessub.rs new file mode 100644 index 0000000000..262e7ff7dc --- /dev/null +++ b/libafl/src/inputs/bytessub.rs @@ -0,0 +1,426 @@ +//! [`BytesSubInput`] is a wrapper input that can be used to mutate parts of a byte slice + +use alloc::vec::Vec; +use core::{ + cmp::{min, Ordering}, + ops::{Bound, Range, RangeBounds}, +}; + +use libafl_bolts::HasLen; + +use super::HasMutatorBytes; + +/// Gets the relevant concrete start index from [`RangeBounds`] (inclusive) +fn start_index(range: &R) -> usize +where + R: RangeBounds, +{ + match range.start_bound() { + Bound::Unbounded => 0, + Bound::Included(start) => *start, + Bound::Excluded(start) => start + 1, + } +} + +/// Gets the relevant concrete end index from [`RangeBounds`] (exclusive) +fn end_index(range: &R, max_len: usize) -> usize +where + R: RangeBounds, +{ + match range.end_bound() { + Bound::Unbounded => max_len, + Bound::Included(end) => end + 1, + Bound::Excluded(end) => *end, + } +} + +/// The [`BytesSubInput`] makes it possible to use [`crate::mutators::Mutator`]`s` that work on +/// inputs implementing the [`HasMutatorBytes`] for a sub-range of this input. +/// For example, we can do the following: +/// ```rust +/// # extern crate alloc; +/// # extern crate libafl; +/// # use libafl::inputs::{BytesInput, HasMutatorBytes}; +/// # use alloc::vec::Vec; +/// # +/// # #[cfg(not(feature = "std"))] +/// # #[no_mangle] +/// # pub extern "C" fn external_current_millis() -> u64 { 0 } +/// +/// let mut bytes_input = BytesInput::new(vec![1,2,3]); +/// let mut sub_input = bytes_input.sub_input(1..); +/// +/// // Run any mutations on the sub input. +/// sub_input.bytes_mut()[0] = 42; +/// +/// // The mutations are applied to the underlying input. +/// assert_eq!(bytes_input.bytes()[1], 42); +/// ``` +/// +/// Growing or shrinking the sub input will grow or shrink the parent input, +/// and keep elements around the current range untouched / move them accordingly. +/// +/// For example: +/// ```rust +/// # extern crate alloc; +/// # extern crate libafl; +/// # use libafl::inputs::{BytesInput, HasMutatorBytes}; +/// # use alloc::vec::Vec; +/// # +/// # #[cfg(not(feature = "std"))] +/// # #[no_mangle] +/// # pub extern "C" fn external_current_millis() -> u64 { 0 } +/// +/// let mut bytes_input = BytesInput::new(vec![1, 2, 3, 4, 5]); +/// +/// // Note that the range ends on an exclusive value this time. +/// let mut sub_input = bytes_input.sub_input(1..=3); +/// +/// assert_eq!(sub_input.bytes(), &[2, 3, 4]); +/// +/// // We extend it with a few values. +/// sub_input.extend(&[42, 42, 42]); +/// +/// // The values outside of the range are moved back and forwards, accordingly. +/// assert_eq!(bytes_input.bytes(), [1, 2, 3, 4, 42, 42, 42, 5]); +/// ``` +/// +/// The input supports all methods in the [`HasMutatorBytes`] trait. +#[derive(Debug)] +pub struct BytesSubInput<'a, I> +where + I: HasMutatorBytes + ?Sized, +{ + /// The (complete) parent input we will work on + pub(crate) parent_input: &'a mut I, + /// The range inside the parent input we will work on + pub(crate) range: Range, +} + +impl<'a, I> BytesSubInput<'a, I> +where + I: HasMutatorBytes + ?Sized + HasLen, +{ + /// Creates a new [`BytesSubInput`] that's a view on an input with mutator bytes. + /// The sub input can then be used to mutate parts of the original input. + pub fn new(parent_input: &'a mut I, range: R) -> Self + where + R: RangeBounds, + { + let parent_len = parent_input.len(); + + BytesSubInput { + parent_input, + range: Range { + start: start_index(&range), + end: end_index(&range, parent_len), + }, + } + } + + /// The inclusive start index in the parent buffer + fn start_index(&self) -> usize { + self.range.start + } + + /// The exclusive end index in the parent buffer + fn end_index(&self) -> usize { + self.range.end + } + + /// Creates a sub range in the current own range + fn sub_range(&self, range: R2) -> (Bound, Bound) + where + R2: RangeBounds, + { + let start = match (self.range.start_bound(), range.start_bound()) { + (Bound::Unbounded, Bound::Unbounded) => Bound::Unbounded, + (Bound::Excluded(bound), Bound::Unbounded) + | (Bound::Unbounded, Bound::Excluded(bound)) => Bound::Excluded(*bound), + (Bound::Included(bound), Bound::Unbounded) + | (Bound::Unbounded, Bound::Included(bound)) => Bound::Included(*bound), + (Bound::Included(own), Bound::Included(other)) => Bound::Included(own + other), + (Bound::Included(own), Bound::Excluded(other)) + | (Bound::Excluded(own), Bound::Included(other)) => Bound::Excluded(own + other), + (Bound::Excluded(own), Bound::Excluded(other)) => Bound::Excluded(own + other + 1), + }; + + let end = match (self.range.end_bound(), range.end_bound()) { + (Bound::Unbounded, Bound::Unbounded) => Bound::Unbounded, + (Bound::Excluded(bound), Bound::Unbounded) => Bound::Excluded(*bound), + (Bound::Unbounded, Bound::Excluded(bound)) => { + Bound::Excluded(self.end_index() - *bound) + } + (Bound::Included(bound), Bound::Unbounded) => Bound::Included(*bound), + (Bound::Unbounded, Bound::Included(bound)) => { + Bound::Included(self.end_index() - *bound) + } + (Bound::Included(own), Bound::Included(other)) => { + Bound::Included(min(*own, self.start_index() + other)) + } + (Bound::Included(own), Bound::Excluded(other)) => { + Bound::Included(min(*own, self.start_index() + other - 1)) + } + (Bound::Excluded(own), Bound::Included(other)) => { + Bound::Included(min(*own - 1, self.start_index() + other)) + } + (Bound::Excluded(own), Bound::Excluded(other)) => { + Bound::Excluded(min(*own, self.start_index() + other)) + } + }; + + (start, end) + } +} + +impl<'a, I> HasMutatorBytes for BytesSubInput<'a, I> +where + I: HasMutatorBytes + HasLen, +{ + #[inline] + fn bytes(&self) -> &[u8] { + &self.parent_input.bytes()[self.range.clone()] + } + + #[inline] + fn bytes_mut(&mut self) -> &mut [u8] { + &mut self.parent_input.bytes_mut()[self.range.clone()] + } + + fn resize(&mut self, new_len: usize, value: u8) { + let start_index = self.start_index(); + let end_index = self.end_index(); + let old_len = end_index - start_index; + + match new_len.cmp(&old_len) { + Ordering::Equal => { + // Nothing to do here. + } + Ordering::Greater => { + // We grow. Resize the underlying buffer, then move the entries past our `end_index` back. + let diff = new_len - old_len; + + let old_parent_len = self.parent_input.len(); + self.parent_input.resize(old_parent_len + diff, value); + + if old_parent_len > end_index { + // the parent has a reminder, move it back. + let parent_bytes = self.parent_input.bytes_mut(); + + // move right + let (_, rest) = parent_bytes.split_at_mut(start_index + old_len); + rest.copy_within(0..rest.len() - diff, diff); + let (new, _rest) = rest.split_at_mut(diff); + + // fill + new.fill(value); + } + + self.range.end += diff; + } + Ordering::Less => { + // We shrink. Remove the values, then remove the underlying buffer. + let diff = old_len - new_len; + + let parent_bytes = self.parent_input.bytes_mut(); + + // move left + let (_, rest) = parent_bytes.split_at_mut(start_index + new_len); + rest.copy_within(diff.., 0); + + // cut off the rest + self.parent_input + .resize(self.parent_input.len() - diff, value); + + self.range.end -= diff; + } + } + } + + fn extend<'b, IT: IntoIterator>(&mut self, iter: IT) { + let old_len = self.end_index() - self.start_index(); + + let new_values: Vec = iter.into_iter().copied().collect(); + self.resize(old_len + new_values.len(), 0); + self.bytes_mut()[old_len..].copy_from_slice(&new_values); + } + + /// Creates a splicing iterator that replaces the specified range in the vector + /// with the given `replace_with` iterator and yields the removed items. + /// `replace_with` does not need to be the same length as range. + /// Refer to the docs of [`Vec::splice`] + fn splice( + &mut self, + range: R2, + replace_with: IT, + ) -> alloc::vec::Splice<'_, IT::IntoIter> + where + R2: RangeBounds, + IT: IntoIterator, + { + let range = self.sub_range(range); + self.parent_input.splice(range, replace_with) + } + + fn drain(&mut self, range: R2) -> alloc::vec::Drain<'_, u8> + where + R2: RangeBounds, + { + let drain = self.parent_input.drain(self.sub_range(range)); + self.range.end -= drain.len(); + drain + } +} + +impl<'a, I> HasLen for BytesSubInput<'a, I> +where + I: HasMutatorBytes + HasLen, +{ + #[inline] + fn len(&self) -> usize { + self.range.end - self.range.start + } +} + +#[cfg(test)] +mod tests { + + use alloc::vec::Vec; + + use libafl_bolts::HasLen; + + use crate::{ + inputs::{BytesInput, HasMutatorBytes, MutVecInput, NopInput}, + mutators::{havoc_mutations_no_crossover, MutatorsTuple}, + state::NopState, + }; + + fn init_bytes_input() -> (BytesInput, usize) { + let bytes_input = BytesInput::new(vec![1, 2, 3, 4, 5, 6, 7]); + let len_orig = bytes_input.len(); + (bytes_input, len_orig) + } + + #[test] + fn test_bytessubinput() { + let (mut bytes_input, len_orig) = init_bytes_input(); + + let mut sub_input = bytes_input.sub_input(0..1); + assert_eq!(sub_input.len(), 1); + sub_input.bytes_mut()[0] = 2; + assert_eq!(bytes_input.bytes()[0], 2); + + let mut sub_input = bytes_input.sub_input(1..=2); + assert_eq!(sub_input.len(), 2); + sub_input.bytes_mut()[0] = 3; + assert_eq!(bytes_input.bytes()[1], 3); + + let mut sub_input = bytes_input.sub_input(..); + assert_eq!(sub_input.len(), len_orig); + sub_input.bytes_mut()[0] = 1; + sub_input.bytes_mut()[1] = 2; + assert_eq!(bytes_input.bytes()[0], 1); + } + + #[test] + fn test_bytessubinput_resize() { + let (mut bytes_input, len_orig) = init_bytes_input(); + let bytes_input_orig = bytes_input.clone(); + + let mut sub_input = bytes_input.sub_input(2..); + assert_eq!(sub_input.len(), len_orig - 2); + sub_input.resize(len_orig, 0); + assert_eq!(sub_input.bytes()[sub_input.len() - 1], 0); + assert_eq!(sub_input.len(), len_orig); + assert_eq!(bytes_input.len(), len_orig + 2); + assert_eq!(bytes_input.bytes()[bytes_input.len() - 1], 0); + + let (mut bytes_input, len_orig) = init_bytes_input(); + + let mut sub_input = bytes_input.sub_input(..2); + assert_eq!(sub_input.len(), 2); + sub_input.resize(3, 0); + assert_eq!(sub_input.len(), 3); + assert_eq!(sub_input.bytes()[sub_input.len() - 1], 0); + assert_eq!(bytes_input.len(), len_orig + 1); + + let mut sub_input = bytes_input.sub_input(..3); + assert_eq!(sub_input.len(), 3); + sub_input.resize(2, 0); + assert_eq!(sub_input.len(), 2); + assert_eq!(bytes_input, bytes_input_orig); + + let mut sub_input = bytes_input.sub_input(2..=2); + sub_input.resize(2, 0); + sub_input.resize(1, 0); + assert_eq!(bytes_input, bytes_input_orig); + + let mut sub_input = bytes_input.sub_input(..); + assert_eq!(sub_input.len(), bytes_input_orig.len()); + sub_input.resize(1, 0); + assert_eq!(sub_input.len(), 1); + sub_input.resize(10, 0); + assert_eq!(sub_input.len(), 10); + assert_eq!(bytes_input.len(), 10); + assert_eq!(bytes_input.bytes()[2], 0); + + let mut sub_input = bytes_input.sub_input(..); + sub_input.resize(1, 0); + assert_eq!(bytes_input.len(), 1); + } + + #[test] + fn test_bytessubinput_drain_extend() { + let (mut bytes_input, len_orig) = init_bytes_input(); + let bytes_input_cloned = bytes_input.clone(); + + let mut sub_input = bytes_input.sub_input(..2); + let drained: Vec<_> = sub_input.drain(..).collect(); + assert_eq!(sub_input.len(), 0); + assert_eq!(bytes_input.len(), len_orig - 2); + + let mut sub_input = bytes_input.sub_input(..0); + assert_eq!(sub_input.len(), 0); + let drained_len = drained.len(); + sub_input.extend(&drained[..]); + assert_eq!(sub_input.len(), drained_len); + assert_eq!(bytes_input, bytes_input_cloned); + } + + #[test] + fn test_bytessubinput_mutator() { + let (mut bytes_input, _len_orig) = init_bytes_input(); + let bytes_input_cloned = bytes_input.clone(); + + let mut sub_input = bytes_input.sub_input(..2); + + // Note that if you want to use NopState in production like this, you should see the rng! :) + let mut state: NopState = NopState::new(); + + let result = havoc_mutations_no_crossover().mutate_all(&mut state, &mut sub_input); + assert!(result.is_ok()); + assert_ne!(bytes_input, bytes_input_cloned); + } + + #[test] + fn test_bytessubinput_use_vec() { + let mut test_vec = vec![0, 1, 2, 3, 4]; + let mut test_vec = MutVecInput::from(&mut test_vec); + let mut sub_vec = test_vec.sub_input(1..2); + drop(sub_vec.drain(..)); + assert_eq!(test_vec.len(), 4); + } + + #[test] + fn test_ranges() { + let mut bytes_input = BytesInput::new(vec![1, 2, 3]); + + assert_eq!(bytes_input.sub_input(..1).start_index(), 0); + assert_eq!(bytes_input.sub_input(1..=1).start_index(), 1); + assert_eq!(bytes_input.sub_input(..1).end_index(), 1); + assert_eq!(bytes_input.sub_input(..=1).end_index(), 2); + assert_eq!(bytes_input.sub_input(1..=1).end_index(), 2); + assert_eq!(bytes_input.sub_input(1..).end_index(), 3); + assert_eq!(bytes_input.sub_input(..3).end_index(), 3); + } +} diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index 883537d882..6269a9b645 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -12,6 +12,9 @@ pub use gramatron::*; pub mod generalized; pub use generalized::*; +pub mod bytessub; +pub use bytessub::BytesSubInput; + #[cfg(feature = "multipart_inputs")] pub mod multi; #[cfg(feature = "multipart_inputs")] @@ -23,15 +26,15 @@ pub mod nautilus; use alloc::{ boxed::Box, string::{String, ToString}, - vec::Vec, + vec::{Drain, Splice, Vec}, }; -use core::{clone::Clone, fmt::Debug, marker::PhantomData}; +use core::{clone::Clone, fmt::Debug, marker::PhantomData, ops::RangeBounds}; #[cfg(feature = "std")] use std::{fs::File, hash::Hash, io::Read, path::Path}; #[cfg(feature = "std")] use libafl_bolts::fs::write_file_atomic; -use libafl_bolts::{ownedref::OwnedSlice, Error}; +use libafl_bolts::{ownedref::OwnedSlice, Error, HasLen}; #[cfg(feature = "nautilus")] pub use nautilus::*; use serde::{Deserialize, Serialize}; @@ -73,7 +76,7 @@ pub trait Input: Clone + Serialize + serde::de::DeserializeOwned + Debug { P: AsRef, { let mut file = File::open(path)?; - let mut bytes: Vec = vec![]; + let mut bytes = vec![]; file.read_to_end(&mut bytes)?; Ok(postcard::from_bytes(&bytes)?) } @@ -127,12 +130,89 @@ pub trait HasTargetBytes { fn target_bytes(&self) -> OwnedSlice; } -/// Contains an internal bytes Vector -pub trait HasBytesVec { - /// The internal bytes map +/// Contains mutateable and resizable bytes +pub trait HasMutatorBytes: HasLen { + /// The bytes fn bytes(&self) -> &[u8]; - /// The internal bytes map (as mutable borrow) - fn bytes_mut(&mut self) -> &mut Vec; + + /// The bytes to mutate + fn bytes_mut(&mut self) -> &mut [u8]; + + /// Resize the mutator bytes to a given new size. + /// Use `value` to fill new slots in case the buffer grows. + /// See [`alloc::vec::Vec::splice`]. + fn resize(&mut self, new_len: usize, value: u8); + + /// Extends the given buffer with an iterator. See [`alloc::vec::Vec::extend`] + fn extend<'a, I: IntoIterator>(&mut self, iter: I); + + /// Splices the given target bytes according to [`alloc::vec::Vec::splice`]'s rules + fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter> + where + R: RangeBounds, + I: IntoIterator; + + /// Drains the given target bytes according to [`alloc::vec::Vec::drain`]'s rules + fn drain(&mut self, range: R) -> Drain<'_, u8> + where + R: RangeBounds; + + /// Creates a [`BytesSubInput`] from this input, that can be used for local mutations. + fn sub_input(&mut self, range: R) -> BytesSubInput + where + R: RangeBounds, + { + BytesSubInput::new(self, range) + } +} + +/// A wrapper type that allows us to use mutators for Mutators for `&mut `[`Vec`]. +#[derive(Debug)] +pub struct MutVecInput<'a>(&'a mut Vec); + +impl<'a> From<&'a mut Vec> for MutVecInput<'a> { + fn from(value: &'a mut Vec) -> Self { + Self(value) + } +} + +impl<'a> HasLen for MutVecInput<'a> { + fn len(&self) -> usize { + self.0.len() + } +} + +impl<'a> HasMutatorBytes for MutVecInput<'a> { + fn bytes(&self) -> &[u8] { + self.0 + } + + fn bytes_mut(&mut self) -> &mut [u8] { + self.0 + } + + fn resize(&mut self, new_len: usize, value: u8) { + self.0.resize(new_len, value); + } + + fn extend<'b, I: IntoIterator>(&mut self, iter: I) { + self.0.extend(iter); + } + + fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter> + where + R: RangeBounds, + I: IntoIterator, + { + self.0.splice::(range, replace_with) + } + + fn drain(&mut self, range: R) -> Drain<'_, u8> + where + R: RangeBounds, + { + self.0.drain(range) + } } /// Defines the input type shared across traits of the type. diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index 5024e67d48..d05c40a9f3 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -1,4 +1,4 @@ -//! Mutators mutate input during fuzzing. +//! [`Mutator`]`s` mutate input during fuzzing. pub mod scheduled; use core::fmt; @@ -87,14 +87,14 @@ pub enum MutationResult { Skipped, } -/// A mutator takes input, and mutates it. +/// A [`Mutator`] takes an input, and mutates it. /// Simple as that. pub trait Mutator: Named { /// Mutate a given input fn mutate(&mut self, state: &mut S, input: &mut I) -> Result; /// Post-process given the outcome of the execution - /// `new_corpus_idx` will be `Some` if a new `Testcase` was created this execution. + /// `new_corpus_idx` will be `Some` if a new [`crate::corpus::Testcase`] was created this execution. #[inline] fn post_exec( &mut self, @@ -129,12 +129,12 @@ pub trait MultiMutator: Named { } } -/// A `Tuple` of `Mutators` that can execute multiple `Mutators` in a row. +/// A `Tuple` of [`Mutator`]`s` that can execute multiple `Mutators` in a row. pub trait MutatorsTuple: HasLen { - /// Runs the `mutate` function on all `Mutators` in this `Tuple`. + /// Runs the [`Mutator::mutate`] function on all [`Mutator`]`s` in this `Tuple`. fn mutate_all(&mut self, state: &mut S, input: &mut I) -> Result; - /// Runs the `post_exec` function on all `Mutators` in this `Tuple`. + /// Runs the [`Mutator::post_exec`] function on all [`Mutator`]`s` in this `Tuple`. /// `new_corpus_idx` will be `Some` if a new `Testcase` was created this execution. fn post_exec_all( &mut self, diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index 25f0643329..591dd50eb7 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -7,7 +7,7 @@ use libafl_bolts::{rands::Rand, Error}; use crate::{ corpus::{Corpus, CorpusId}, impl_default_multipart, - inputs::{multi::MultipartInput, HasBytesVec, Input}, + inputs::{multi::MultipartInput, HasMutatorBytes, Input}, mutators::{ mutations::{ rand_range, BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, @@ -117,7 +117,7 @@ impl_default_multipart!( impl Mutator, S> for CrossoverInsertMutator where S: HasCorpus> + HasMaxSize + HasRand, - I: Input + HasBytesVec, + I: Input + HasMutatorBytes, { fn mutate( &mut self, @@ -221,7 +221,7 @@ where impl Mutator, S> for CrossoverReplaceMutator where S: HasCorpus> + HasMaxSize + HasRand, - I: Input + HasBytesVec, + I: Input + HasMutatorBytes, { fn mutate( &mut self, diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index b3115c62eb..fd39c051db 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -10,7 +10,7 @@ use libafl_bolts::{rands::Rand, Named}; use crate::{ corpus::Corpus, - inputs::{HasBytesVec, Input}, + inputs::HasMutatorBytes, mutators::{MutationResult, Mutator}, random_corpus_id_with_disabled, state::{HasCorpus, HasMaxSize, HasRand}, @@ -123,7 +123,7 @@ pub struct BitFlipMutator; impl Mutator for BitFlipMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { if input.bytes().is_empty() { @@ -159,7 +159,7 @@ pub struct ByteFlipMutator; impl Mutator for ByteFlipMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { if input.bytes().is_empty() { @@ -193,7 +193,7 @@ pub struct ByteIncMutator; impl Mutator for ByteIncMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { if input.bytes().is_empty() { @@ -228,7 +228,7 @@ pub struct ByteDecMutator; impl Mutator for ByteDecMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { if input.bytes().is_empty() { @@ -263,7 +263,7 @@ pub struct ByteNegMutator; impl Mutator for ByteNegMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { if input.bytes().is_empty() { @@ -298,7 +298,7 @@ pub struct ByteRandMutator; impl Mutator for ByteRandMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { if input.bytes().is_empty() { @@ -338,7 +338,7 @@ macro_rules! add_mutator_impl { impl Mutator for $name where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate( &mut self, @@ -405,7 +405,7 @@ macro_rules! interesting_mutator_impl { impl Mutator for $name where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { #[allow(clippy::cast_sign_loss)] fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { @@ -454,7 +454,7 @@ pub struct BytesDeleteMutator; impl Mutator for BytesDeleteMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -464,7 +464,7 @@ where let range = rand_range(state, size, size - 1); - input.bytes_mut().drain(range); + input.drain(range); Ok(MutationResult::Mutated) } @@ -492,7 +492,7 @@ pub struct BytesExpandMutator; impl Mutator for BytesExpandMutator where S: HasRand + HasMaxSize, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let max_size = state.max_size(); @@ -503,7 +503,7 @@ where let range = rand_range(state, size, min(16, max_size - size)); - input.bytes_mut().resize(size + range.len(), 0); + input.resize(size + range.len(), 0); unsafe { buffer_self_copy( input.bytes_mut(), @@ -539,7 +539,7 @@ pub struct BytesInsertMutator; impl Mutator for BytesInsertMutator where S: HasRand + HasMaxSize, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let max_size = state.max_size(); @@ -561,7 +561,7 @@ where let val = input.bytes()[state.rand_mut().below(size)]; - input.bytes_mut().resize(size + amount, 0); + input.resize(size + amount, 0); unsafe { buffer_self_copy(input.bytes_mut(), offset, offset + amount, size - offset); } @@ -593,7 +593,7 @@ pub struct BytesRandInsertMutator; impl Mutator for BytesRandInsertMutator where S: HasRand + HasMaxSize, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let max_size = state.max_size(); @@ -615,7 +615,7 @@ where let val = state.rand_mut().next() as u8; - input.bytes_mut().resize(size + amount, 0); + input.resize(size + amount, 0); unsafe { buffer_self_copy(input.bytes_mut(), offset, offset + amount, size - offset); } @@ -647,7 +647,7 @@ pub struct BytesSetMutator; impl Mutator for BytesSetMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -686,7 +686,7 @@ pub struct BytesRandSetMutator; impl Mutator for BytesRandSetMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -725,7 +725,7 @@ pub struct BytesCopyMutator; impl Mutator for BytesCopyMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -768,7 +768,7 @@ pub struct BytesInsertCopyMutator { impl Mutator for BytesInsertCopyMutator where S: HasRand + HasMaxSize, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -781,7 +781,7 @@ where let max_insert_len = min(size - target, state.max_size() - size); let range = rand_range(state, size, min(16, max_insert_len)); - input.bytes_mut().resize(size + range.len(), 0); + input.resize(size + range.len(), 0); self.tmp_buf.resize(range.len(), 0); unsafe { buffer_copy( @@ -829,7 +829,7 @@ pub struct BytesSwapMutator { impl Mutator for BytesSwapMutator where S: HasRand, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -1029,15 +1029,15 @@ pub struct CrossoverInsertMutator { phantom: PhantomData, } -impl CrossoverInsertMutator { - pub(crate) fn crossover_insert( +impl CrossoverInsertMutator { + pub(crate) fn crossover_insert( input: &mut I, size: usize, target: usize, range: Range, - other: &I, + other: &I2, ) -> MutationResult { - input.bytes_mut().resize(size + range.len(), 0); + input.resize(size + range.len(), 0); unsafe { buffer_self_copy( input.bytes_mut(), @@ -1062,10 +1062,11 @@ impl CrossoverInsertMutator { impl Mutator for CrossoverInsertMutator where - S: HasCorpus + HasRand + HasMaxSize, - I: Input + HasBytesVec, + S: HasCorpus + HasRand + HasMaxSize, + S::Input: HasMutatorBytes, + I: HasMutatorBytes, { - fn mutate(&mut self, state: &mut S, input: &mut S::Input) -> Result { + fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); let max_size = state.max_size(); if size >= max_size { @@ -1124,12 +1125,12 @@ pub struct CrossoverReplaceMutator { phantom: PhantomData, } -impl CrossoverReplaceMutator { - pub(crate) fn crossover_replace( +impl CrossoverReplaceMutator { + pub(crate) fn crossover_replace( input: &mut I, target: usize, range: Range, - other: &I, + other: &I2, ) -> MutationResult { unsafe { buffer_copy( @@ -1146,10 +1147,11 @@ impl CrossoverReplaceMutator { impl Mutator for CrossoverReplaceMutator where - S: HasCorpus + HasRand, - I: Input + HasBytesVec, + S: HasCorpus + HasRand, + S::Input: HasMutatorBytes, + I: HasMutatorBytes, { - fn mutate(&mut self, state: &mut S, input: &mut S::Input) -> Result { + fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); if size == 0 { return Ok(MutationResult::Skipped); @@ -1224,7 +1226,7 @@ pub struct SpliceMutator; impl Mutator for SpliceMutator where S: HasCorpus + HasRand, - S::Input: HasBytesVec, + S::Input: HasMutatorBytes, { #[allow(clippy::cast_sign_loss)] fn mutate(&mut self, state: &mut S, input: &mut S::Input) -> Result { @@ -1255,9 +1257,7 @@ where // Input will already be loaded. let other = other_testcase.input().as_ref().unwrap(); - input - .bytes_mut() - .splice(split_at.., other.bytes()[split_at..].iter().copied()); + input.splice(split_at.., other.bytes()[split_at..].iter().copied()); Ok(MutationResult::Mutated) } diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index a4a5f095e5..57cb5a76d7 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -480,7 +480,7 @@ mod tests { use crate::{ corpus::{Corpus, InMemoryCorpus, Testcase}, feedbacks::ConstFeedback, - inputs::{BytesInput, HasBytesVec}, + inputs::{BytesInput, HasMutatorBytes}, mutators::{ mutations::SpliceMutator, scheduled::{havoc_mutations, StdScheduledMutator}, diff --git a/libafl/src/mutators/string.rs b/libafl/src/mutators/string.rs index 75665f1741..52745f9eed 100644 --- a/libafl/src/mutators/string.rs +++ b/libafl/src/mutators/string.rs @@ -9,7 +9,7 @@ use libafl_bolts::{rands::Rand, Error, HasLen, Named}; use crate::{ corpus::{CorpusId, HasTestcase, Testcase}, - inputs::{BytesInput, HasBytesVec}, + inputs::{BytesInput, HasMutatorBytes}, mutators::{rand_range, MutationResult, Mutator, Tokens}, stages::{ extract_metadata, @@ -259,7 +259,7 @@ fn rand_replace_range char>( } } - input.0.bytes_mut().splice(range, replacement); + input.0.splice(range, replacement); input.1 = extract_metadata(input.0.bytes()); MutationResult::Mutated @@ -423,7 +423,7 @@ where return Ok(MutationResult::Skipped); } - input.0.bytes_mut().splice(range, token.iter().copied()); + input.0.splice(range, token.iter().copied()); input.1 = extract_metadata(input.0.bytes()); return Ok(MutationResult::Mutated); } @@ -483,7 +483,7 @@ where return Ok(MutationResult::Skipped); } - input.0.bytes_mut().splice(range, token.iter().copied()); + input.0.splice(range, token.iter().copied()); input.1 = extract_metadata(input.0.bytes()); return Ok(MutationResult::Mutated); } @@ -498,7 +498,7 @@ mod test { use crate::{ corpus::NopCorpus, - inputs::{BytesInput, HasBytesVec}, + inputs::{BytesInput, HasMutatorBytes}, mutators::{Mutator, StringCategoryRandMutator, StringSubcategoryRandMutator}, stages::extract_metadata, state::StdState, diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 7b219e8e73..5d3b217e97 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -24,7 +24,7 @@ use serde::{Deserialize, Serialize}; use crate::mutators::str_decode; use crate::{ corpus::{CorpusId, HasCurrentCorpusId}, - inputs::{HasBytesVec, UsesInput}, + inputs::{HasMutatorBytes, UsesInput}, mutators::{ buffer_self_copy, mutations::buffer_copy, MultiMutator, MutationResult, Mutator, Named, }, @@ -305,7 +305,7 @@ pub struct TokenInsert; impl Mutator for TokenInsert where S: HasMetadata + HasRand + HasMaxSize, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let max_size = state.max_size(); @@ -335,7 +335,7 @@ where } } - input.bytes_mut().resize(size + len, 0); + input.resize(size + len, 0); unsafe { buffer_self_copy(input.bytes_mut(), off, off + len, size - off); buffer_copy(input.bytes_mut(), token, 0, off, len); @@ -368,7 +368,7 @@ pub struct TokenReplace; impl Mutator for TokenReplace where S: UsesInput + HasMetadata + HasRand + HasMaxSize, - I: HasBytesVec, + I: HasMutatorBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -427,7 +427,7 @@ pub struct I2SRandReplace; impl Mutator for I2SRandReplace where S: UsesInput + HasMetadata + HasRand + HasMaxSize, - I: HasBytesVec, + I: HasMutatorBytes, { #[allow(clippy::too_many_lines)] fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { @@ -1088,7 +1088,7 @@ impl AFLppRedQueen { impl MultiMutator for AFLppRedQueen where S: UsesInput + HasMetadata + HasRand + HasMaxSize + HasCorpus + HasCurrentCorpusId, - I: HasBytesVec + From>, + I: HasMutatorBytes + From>, { #[allow(clippy::needless_range_loop)] #[allow(clippy::too_many_lines)] diff --git a/libafl/src/stages/colorization.rs b/libafl/src/stages/colorization.rs index 04e1d52973..25dd0860a2 100644 --- a/libafl/src/stages/colorization.rs +++ b/libafl/src/stages/colorization.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use crate::{ events::EventFirer, executors::{Executor, HasObservers}, - inputs::HasBytesVec, + inputs::HasMutatorBytes, mutators::mutations::buffer_copy, observers::{MapObserver, ObserversTuple}, stages::{RetryRestartHelper, Stage}, @@ -84,7 +84,7 @@ where EM: UsesState + EventFirer, E: HasObservers + Executor, E::State: HasCorpus + HasMetadata + HasRand + HasNamedMetadata, - E::Input: HasBytesVec, + E::Input: HasMutatorBytes, O: MapObserver, C: AsRef + Named, Z: UsesState, @@ -161,7 +161,7 @@ where C: AsRef + Named, E: HasObservers + Executor, E::State: HasCorpus + HasMetadata + HasRand, - E::Input: HasBytesVec, + E::Input: HasMutatorBytes, Z: UsesState, { #[inline] diff --git a/libafl/src/stages/concolic.rs b/libafl/src/stages/concolic.rs index 118bfc6464..5151b7b2d5 100644 --- a/libafl/src/stages/concolic.rs +++ b/libafl/src/stages/concolic.rs @@ -26,7 +26,7 @@ use crate::{ }; #[cfg(feature = "concolic_mutation")] use crate::{ - inputs::HasBytesVec, + inputs::HasMutatorBytes, mark_feature_time, observers::concolic::{ConcolicMetadata, SymExpr, SymExprRef}, stages::ExecutionCountRestartHelper, @@ -372,7 +372,7 @@ where E: UsesState, EM: UsesState, Z: Evaluator, - Z::Input: HasBytesVec, + Z::Input: HasMutatorBytes, Z::State: State + HasExecutions + HasCorpus + HasMetadata, { #[inline] diff --git a/libafl/src/stages/generalization.rs b/libafl/src/stages/generalization.rs index 0495ca3040..19bae170a8 100644 --- a/libafl/src/stages/generalization.rs +++ b/libafl/src/stages/generalization.rs @@ -12,7 +12,7 @@ use crate::{ corpus::{Corpus, HasCurrentCorpusId}, executors::{Executor, HasObservers}, feedbacks::map::MapNoveltiesMetadata, - inputs::{BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasBytesVec, UsesInput}, + inputs::{BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasMutatorBytes, UsesInput}, mark_feature_time, observers::{CanTrack, MapObserver, ObserversTuple}, require_novelties_tracking, @@ -416,12 +416,8 @@ where end = payload.len(); } let mut candidate = BytesInput::new(vec![]); - candidate - .bytes_mut() - .extend(payload[..start].iter().flatten()); - candidate - .bytes_mut() - .extend(payload[end..].iter().flatten()); + candidate.extend(payload[..start].iter().flatten()); + candidate.extend(payload[end..].iter().flatten()); if self.verify_input(fuzzer, executor, state, manager, novelties, &candidate)? { for item in &mut payload[start..end] { @@ -469,12 +465,8 @@ where if payload[end] == Some(closing_char) { endings += 1; let mut candidate = BytesInput::new(vec![]); - candidate - .bytes_mut() - .extend(payload[..start].iter().flatten()); - candidate - .bytes_mut() - .extend(payload[end..].iter().flatten()); + candidate.extend(payload[..start].iter().flatten()); + candidate.extend(payload[end..].iter().flatten()); if self.verify_input(fuzzer, executor, state, manager, novelties, &candidate)? { for item in &mut payload[start..end] { diff --git a/libafl/src/stages/string.rs b/libafl/src/stages/string.rs index e2e75f30fe..7b94e6a695 100644 --- a/libafl/src/stages/string.rs +++ b/libafl/src/stages/string.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use crate::{ corpus::HasTestcase, - inputs::{BytesInput, HasBytesVec}, + inputs::{BytesInput, HasMutatorBytes}, stages::Stage, state::{HasCorpus, HasCurrentTestcase, State, UsesState}, HasMetadata, diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 3c6268f2e1..eeed0de554 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -1163,6 +1163,16 @@ impl NopState { } } +impl HasMaxSize for NopState { + fn max_size(&self) -> usize { + 16_384 + } + + fn set_max_size(&mut self, _max_size: usize) { + unimplemented!("NopState doesn't allow setting a max size") + } +} + impl UsesInput for NopState where I: Input, diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/tmin.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/tmin.rs index 6dfb4f1d9d..4a2bbae991 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/tmin.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/tmin.rs @@ -8,7 +8,7 @@ use libafl::{ events::SimpleEventManager, executors::{inprocess_fork::InProcessForkExecutor, ExitKind}, feedbacks::{CrashFeedback, TimeoutFeedbackFactory}, - inputs::{BytesInput, HasBytesVec, HasTargetBytes}, + inputs::{BytesInput, HasMutatorBytes, HasTargetBytes}, mutators::{havoc_mutations_no_crossover, Mutator, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdTMinMutationalStage, diff --git a/libafl_qemu/build.rs b/libafl_qemu/build.rs index 5346298ba4..1b8f971220 100644 --- a/libafl_qemu/build.rs +++ b/libafl_qemu/build.rs @@ -4,6 +4,8 @@ mod host_specific { #[cfg(not(target_os = "linux"))] pub fn build() { + // Print a emulation_mode to silence clippy's unexpected cfg on macOS + println!("cargo:rustc-cfg=emulation_mode=\"usermode\""); println!("cargo:warning=libafl_qemu only builds on Linux hosts"); } } diff --git a/libafl_targets/src/libfuzzer/mutators.rs b/libafl_targets/src/libfuzzer/mutators.rs index 44324425fc..74384804ae 100644 --- a/libafl_targets/src/libfuzzer/mutators.rs +++ b/libafl_targets/src/libfuzzer/mutators.rs @@ -11,7 +11,7 @@ use std::{ use libafl::{ corpus::Corpus, - inputs::{BytesInput, HasBytesVec, UsesInput}, + inputs::{BytesInput, HasMutatorBytes, UsesInput}, mutators::{ ComposedByMutations, MutationId, MutationResult, Mutator, MutatorsTuple, ScheduledMutator, }, @@ -351,7 +351,7 @@ where return result.replace(Ok(MutationResult::Skipped)); } bytes.truncate(new_size); - core::mem::swap(input.bytes_mut(), &mut bytes); + input.bytes_mut().copy_from_slice(&bytes); Ok(MutationResult::Mutated) } } @@ -440,7 +440,7 @@ where return result.replace(Ok(MutationResult::Skipped)); } out.truncate(new_size); - core::mem::swap(input.bytes_mut(), &mut out); + input.bytes_mut().copy_from_slice(&out); Ok(MutationResult::Mutated) } }