From 15aa498d5e562927b977fbef9710900fd38d8e18 Mon Sep 17 00:00:00 2001 From: Railroad6230 Date: Thu, 16 Jan 2025 17:06:19 +0100 Subject: [PATCH] Fix #2853: split `HasMutatorBytes` trait into two traits. (#2856) (See #2853.) This commit attempts to improve the [`HasMutatorBytes`] trait to allow it to be used along with input types that cannot be resizable. [`HasMutatorBytes`] is split into two traits: - [`HasMutatorBytes`]: requires `bytes` and `bytes_mut`. - `HasMutatorResizableBytes`: requires [`HasMutatorBytes`], and also requires a few other methods for resizing / shrinking the underlying input type. N.B.: I believe that if merged, this would introduce a breaking change. [`HasMutatorBytes`]: https://github.com/AFLplusplus/LibAFL/blob/198cd5dbc5200bfba3df48cf2a14376e0f0e3000/libafl/src/inputs/bytes.rs#L26 --- libafl/src/inputs/bytes.rs | 4 +- libafl/src/inputs/bytessub.rs | 14 +++-- libafl/src/inputs/mod.rs | 73 +++++++++++++----------- libafl/src/mutators/multi.rs | 6 +- libafl/src/mutators/mutations.rs | 22 +++---- libafl/src/mutators/token_mutations.rs | 12 ++-- libafl/src/mutators/unicode/mod.rs | 2 +- libafl/src/stages/colorization.rs | 6 +- libafl/src/stages/generalization.rs | 5 +- libafl_targets/src/libfuzzer/mutators.rs | 2 +- 10 files changed, 82 insertions(+), 64 deletions(-) diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index 3cfe0ec45b..128ecc14c6 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -11,7 +11,7 @@ use core::cell::RefCell; use libafl_bolts::{ownedref::OwnedSlice, HasLen}; use super::ValueInput; -use crate::inputs::{HasMutatorBytes, HasTargetBytes}; +use crate::inputs::{HasMutatorBytes, HasMutatorResizableBytes, HasTargetBytes}; /// A bytes input is the basic input pub type BytesInput = ValueInput>; @@ -31,7 +31,9 @@ impl HasMutatorBytes for BytesInput { fn bytes_mut(&mut self) -> &mut [u8] { self.as_mut() } +} +impl HasMutatorResizableBytes for BytesInput { fn resize(&mut self, new_len: usize, value: u8) { self.as_mut().resize(new_len, value); } diff --git a/libafl/src/inputs/bytessub.rs b/libafl/src/inputs/bytessub.rs index c0280a9ec8..e96c96d376 100644 --- a/libafl/src/inputs/bytessub.rs +++ b/libafl/src/inputs/bytessub.rs @@ -11,7 +11,7 @@ use libafl_bolts::{ HasLen, }; -use crate::inputs::HasMutatorBytes; +use crate::inputs::{HasMutatorBytes, HasMutatorResizableBytes}; /// 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. @@ -37,14 +37,15 @@ use crate::inputs::HasMutatorBytes; /// assert_eq!(bytes_input.bytes()[1], 42); /// ``` /// -/// Growing or shrinking the sub input will grow or shrink the parent input, +/// If inputs implement the [`HasMutatorResizableBytes`] trait, 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 libafl::inputs::{BytesInput, HasMutatorBytes, HasMutatorResizableBytes}; /// # use alloc::vec::Vec; /// # /// # #[cfg(not(feature = "std"))] @@ -109,7 +110,12 @@ where fn bytes_mut(&mut self) -> &mut [u8] { &mut self.parent_input.bytes_mut()[self.range.clone()] } +} +impl HasMutatorResizableBytes for BytesSubInput<'_, I> +where + I: HasMutatorResizableBytes, +{ fn resize(&mut self, new_len: usize, value: u8) { let start_index = self.range.start; let end_index = self.range.end; @@ -208,7 +214,7 @@ mod tests { use libafl_bolts::HasLen; use crate::{ - inputs::{BytesInput, HasMutatorBytes, NopInput}, + inputs::{BytesInput, HasMutatorBytes, HasMutatorResizableBytes, NopInput}, mutators::{havoc_mutations_no_crossover, MutatorsTuple}, state::NopState, }; diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index 432cdef35a..348aa0609c 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -155,7 +155,7 @@ pub trait HasTargetBytes { fn target_bytes(&self) -> OwnedSlice; } -/// Contains mutable and resizable bytes +/// Contains mutable bytes pub trait HasMutatorBytes: HasLen { /// The bytes fn bytes(&self) -> &[u8]; @@ -163,25 +163,6 @@ pub trait HasMutatorBytes: HasLen { /// 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 [`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 [`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 [`Vec::drain`]'s rules - fn drain(&mut self, range: R) -> Drain<'_, u8> - where - R: RangeBounds; - /// Creates a [`SubRangeSlice`] from this input, that can be used to slice a byte array. fn sub_bytes(&self, range: R) -> SubRangeSlice where @@ -215,7 +196,45 @@ impl HasMutatorBytes for Vec { fn bytes_mut(&mut self) -> &mut [u8] { self.as_mut() } +} +/// A wrapper type that allows us to use mutators for Mutators for `&mut `[`Vec`]. +#[deprecated(since = "0.15.0", note = "Use &mut Vec directly")] +pub type MutVecInput<'a> = &'a mut Vec; + +impl HasMutatorBytes for &'_ mut Vec { + fn bytes(&self) -> &[u8] { + self + } + + fn bytes_mut(&mut self) -> &mut [u8] { + self + } +} + +/// Contains mutable and resizable bytes +pub trait HasMutatorResizableBytes: HasMutatorBytes { + /// Resize the mutator bytes to a given new size. + /// Use `value` to fill new slots in case the buffer grows. + /// See [`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 [`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 [`Vec::drain`]'s rules + fn drain(&mut self, range: R) -> Drain<'_, u8> + where + R: RangeBounds; +} + +impl HasMutatorResizableBytes for Vec { fn resize(&mut self, new_len: usize, value: u8) { >::resize(self, new_len, value); } @@ -240,19 +259,7 @@ impl HasMutatorBytes for Vec { } } -/// A wrapper type that allows us to use mutators for Mutators for `&mut `[`Vec`]. -#[deprecated(since = "0.15.0", note = "Use &mut Vec directly")] -pub type MutVecInput<'a> = &'a mut Vec; - -impl HasMutatorBytes for &mut Vec { - fn bytes(&self) -> &[u8] { - self - } - - fn bytes_mut(&mut self) -> &mut [u8] { - self - } - +impl HasMutatorResizableBytes for &mut Vec { fn resize(&mut self, new_len: usize, value: u8) { self.deref_mut().resize(new_len, value); } diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index 66dd1ab672..f7c353e866 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -10,7 +10,7 @@ use libafl_bolts::{rands::Rand, Error}; use crate::{ corpus::{Corpus, CorpusId}, impl_default_multipart, - inputs::{multi::MultipartInput, HasMutatorBytes, Input}, + inputs::{multi::MultipartInput, HasMutatorResizableBytes, Input}, mutators::{ mutations::{ rand_range, BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, @@ -119,7 +119,7 @@ impl_default_multipart!( impl Mutator, S> for CrossoverInsertMutator where S: HasCorpus + HasMaxSize + HasRand, - I: Input + HasMutatorBytes, + I: Input + HasMutatorResizableBytes, S::Corpus: Corpus>, { fn mutate( @@ -255,7 +255,7 @@ where impl Mutator, S> for CrossoverReplaceMutator where S: HasCorpus + HasMaxSize + HasRand, - I: Input + HasMutatorBytes, + I: Input + HasMutatorResizableBytes, S::Corpus: Corpus>, { fn mutate( diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index e9f729a0d9..dce3ffb730 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -16,7 +16,7 @@ use libafl_bolts::{rands::Rand, Named}; use crate::{ corpus::Corpus, - inputs::HasMutatorBytes, + inputs::{HasMutatorBytes, HasMutatorResizableBytes}, mutators::{MutationResult, Mutator}, nonzero, random_corpus_id_with_disabled, state::{HasCorpus, HasMaxSize, HasRand}, @@ -464,7 +464,7 @@ pub struct BytesDeleteMutator; impl Mutator for BytesDeleteMutator where S: HasRand, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -506,7 +506,7 @@ pub struct BytesExpandMutator; impl Mutator for BytesExpandMutator where S: HasRand + HasMaxSize, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let max_size = state.max_size(); @@ -557,7 +557,7 @@ pub struct BytesInsertMutator; impl Mutator for BytesInsertMutator where S: HasRand + HasMaxSize, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let max_size = state.max_size(); @@ -620,7 +620,7 @@ pub struct BytesRandInsertMutator; impl Mutator for BytesRandInsertMutator where S: HasRand + HasMaxSize, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let max_size = state.max_size(); @@ -815,7 +815,7 @@ pub struct BytesInsertCopyMutator { impl Mutator for BytesInsertCopyMutator where S: HasRand + HasMaxSize, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -886,7 +886,7 @@ pub struct BytesSwapMutator { impl Mutator for BytesSwapMutator where S: HasRand, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -1104,7 +1104,7 @@ impl CrossoverInsertMutator { other: &[u8], ) -> MutationResult where - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { input.resize(size + range.len(), 0); unsafe { @@ -1127,7 +1127,7 @@ impl Mutator for CrossoverInsertMutator where S: HasCorpus + HasRand + HasMaxSize, ::Input: HasMutatorBytes, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -1315,7 +1315,7 @@ impl MappedCrossoverInsertMutator { impl Mutator for MappedCrossoverInsertMutator where S: HasCorpus + HasMaxSize + HasRand, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, O: IntoOptionBytes, F: Fn(&::Input) -> &O, { @@ -1494,7 +1494,7 @@ impl Mutator for SpliceMutator where S: HasCorpus + HasRand, ::Input: HasMutatorBytes, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { #[expect(clippy::cast_sign_loss)] fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 23664f3d53..e56b118f4a 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -25,7 +25,7 @@ use serde::{Deserialize, Serialize}; use crate::mutators::str_decode; use crate::{ corpus::{CorpusId, HasCurrentCorpusId}, - inputs::HasMutatorBytes, + inputs::HasMutatorResizableBytes, mutators::{ buffer_self_copy, mutations::buffer_copy, MultiMutator, MutationResult, Mutator, Named, }, @@ -306,7 +306,7 @@ pub struct TokenInsert; impl Mutator for TokenInsert where S: HasMetadata + HasRand + HasMaxSize, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let max_size = state.max_size(); @@ -375,7 +375,7 @@ pub struct TokenReplace; impl Mutator for TokenReplace where S: HasMetadata + HasRand + HasMaxSize, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { let size = input.bytes().len(); @@ -435,7 +435,7 @@ pub struct I2SRandReplace; impl Mutator for I2SRandReplace where S: HasMetadata + HasRand + HasMaxSize, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { #[expect(clippy::too_many_lines)] fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { @@ -636,7 +636,7 @@ where impl Mutator for I2SRandReplaceBinonly where S: HasMetadata + HasRand + HasMaxSize, - I: HasMutatorBytes, + I: HasMutatorResizableBytes, { #[expect(clippy::too_many_lines)] fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { @@ -1306,7 +1306,7 @@ impl AFLppRedQueen { impl MultiMutator for AFLppRedQueen where S: HasMetadata + HasRand + HasMaxSize + HasCorpus + HasCurrentCorpusId, - I: HasMutatorBytes + From>, + I: HasMutatorResizableBytes + From>, { #[expect(clippy::needless_range_loop, clippy::too_many_lines)] fn multi_mutate( diff --git a/libafl/src/mutators/unicode/mod.rs b/libafl/src/mutators/unicode/mod.rs index c719377736..ef7dd90edb 100644 --- a/libafl/src/mutators/unicode/mod.rs +++ b/libafl/src/mutators/unicode/mod.rs @@ -11,7 +11,7 @@ use libafl_bolts::{rands::Rand, Error, HasLen, Named}; use crate::{ corpus::{Corpus, CorpusId, HasTestcase, Testcase}, - inputs::{BytesInput, HasMutatorBytes}, + inputs::{BytesInput, HasMutatorBytes, HasMutatorResizableBytes}, mutators::{rand_range, MutationResult, Mutator, Tokens}, nonzero, stages::{ diff --git a/libafl/src/stages/colorization.rs b/libafl/src/stages/colorization.rs index 87109a3820..6c06e10dce 100644 --- a/libafl/src/stages/colorization.rs +++ b/libafl/src/stages/colorization.rs @@ -17,7 +17,7 @@ use crate::{ corpus::{Corpus, HasCurrentCorpusId}, events::EventFirer, executors::{Executor, HasObservers}, - inputs::HasMutatorBytes, + inputs::{HasMutatorBytes, HasMutatorResizableBytes}, mutators::mutations::buffer_copy, nonzero, observers::{MapObserver, ObserversTuple}, @@ -80,7 +80,7 @@ where E: HasObservers + Executor::Input, S, Z>, S: HasCorpus + HasMetadata + HasRand + HasNamedMetadata + HasCurrentCorpusId, E::Observers: ObserversTuple<::Input, S>, - ::Input: HasMutatorBytes + Clone, + ::Input: HasMutatorResizableBytes + Clone, O: MapObserver, C: AsRef + Named, { @@ -157,7 +157,7 @@ where E: HasObservers + Executor::Input, S, Z>, E::Observers: ObserversTuple<::Input, S>, S: HasCorpus + HasMetadata + HasRand + HasCurrentCorpusId + HasCurrentTestcase, - ::Input: HasMutatorBytes + Clone, + ::Input: HasMutatorResizableBytes + Clone, { #[inline] fn colorize( diff --git a/libafl/src/stages/generalization.rs b/libafl/src/stages/generalization.rs index 432514f09b..88d14c7f5a 100644 --- a/libafl/src/stages/generalization.rs +++ b/libafl/src/stages/generalization.rs @@ -17,7 +17,10 @@ use crate::{ corpus::{Corpus, HasCurrentCorpusId}, executors::{Executor, HasObservers}, feedbacks::map::MapNoveltiesMetadata, - inputs::{BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasMutatorBytes}, + inputs::{ + BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasMutatorBytes, + HasMutatorResizableBytes, + }, mark_feature_time, observers::{CanTrack, MapObserver, ObserversTuple}, require_novelties_tracking, diff --git a/libafl_targets/src/libfuzzer/mutators.rs b/libafl_targets/src/libfuzzer/mutators.rs index 34abfb72a3..ed47616c5f 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, HasMutatorBytes}, + inputs::{BytesInput, HasMutatorBytes, HasMutatorResizableBytes}, mutators::{ ComposedByMutations, MutationId, MutationResult, Mutator, MutatorsTuple, ScheduledMutator, },