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`]: 198cd5dbc5/libafl/src/inputs/bytes.rs (L26)
This commit is contained in:
Railroad6230 2025-01-16 17:06:19 +01:00 committed by GitHub
parent 198cd5dbc5
commit 15aa498d5e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 82 additions and 64 deletions

View File

@ -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<Vec<u8>>;
@ -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);
}

View File

@ -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<I> 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,
};

View File

@ -155,7 +155,7 @@ pub trait HasTargetBytes {
fn target_bytes(&self) -> OwnedSlice<u8>;
}
/// 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<Item = &'a u8>>(&mut self, iter: I);
/// Splices the given target bytes according to [`Vec::splice`]'s rules
fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter>
where
R: RangeBounds<usize>,
I: IntoIterator<Item = u8>;
/// Drains the given target bytes according to [`Vec::drain`]'s rules
fn drain<R>(&mut self, range: R) -> Drain<'_, u8>
where
R: RangeBounds<usize>;
/// Creates a [`SubRangeSlice`] from this input, that can be used to slice a byte array.
fn sub_bytes<R>(&self, range: R) -> SubRangeSlice<u8>
where
@ -215,7 +196,45 @@ impl HasMutatorBytes for Vec<u8> {
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<u8> directly")]
pub type MutVecInput<'a> = &'a mut Vec<u8>;
impl HasMutatorBytes for &'_ mut Vec<u8> {
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<Item = &'a u8>>(&mut self, iter: I);
/// Splices the given target bytes according to [`Vec::splice`]'s rules
fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter>
where
R: RangeBounds<usize>,
I: IntoIterator<Item = u8>;
/// Drains the given target bytes according to [`Vec::drain`]'s rules
fn drain<R>(&mut self, range: R) -> Drain<'_, u8>
where
R: RangeBounds<usize>;
}
impl HasMutatorResizableBytes for Vec<u8> {
fn resize(&mut self, new_len: usize, value: u8) {
<Vec<u8>>::resize(self, new_len, value);
}
@ -240,19 +259,7 @@ impl HasMutatorBytes for Vec<u8> {
}
}
/// A wrapper type that allows us to use mutators for Mutators for `&mut `[`Vec`].
#[deprecated(since = "0.15.0", note = "Use &mut Vec<u8> directly")]
pub type MutVecInput<'a> = &'a mut Vec<u8>;
impl HasMutatorBytes for &mut Vec<u8> {
fn bytes(&self) -> &[u8] {
self
}
fn bytes_mut(&mut self) -> &mut [u8] {
self
}
impl HasMutatorResizableBytes for &mut Vec<u8> {
fn resize(&mut self, new_len: usize, value: u8) {
self.deref_mut().resize(new_len, value);
}

View File

@ -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<I, S> Mutator<MultipartInput<I>, S> for CrossoverInsertMutator
where
S: HasCorpus + HasMaxSize + HasRand,
I: Input + HasMutatorBytes,
I: Input + HasMutatorResizableBytes,
S::Corpus: Corpus<Input = MultipartInput<I>>,
{
fn mutate(
@ -255,7 +255,7 @@ where
impl<I, S> Mutator<MultipartInput<I>, S> for CrossoverReplaceMutator
where
S: HasCorpus + HasMaxSize + HasRand,
I: Input + HasMutatorBytes,
I: Input + HasMutatorResizableBytes,
S::Corpus: Corpus<Input = MultipartInput<I>>,
{
fn mutate(

View File

@ -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<I, S> Mutator<I, S> for BytesDeleteMutator
where
S: HasRand,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -506,7 +506,7 @@ pub struct BytesExpandMutator;
impl<I, S> Mutator<I, S> for BytesExpandMutator
where
S: HasRand + HasMaxSize,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -557,7 +557,7 @@ pub struct BytesInsertMutator;
impl<I, S> Mutator<I, S> for BytesInsertMutator
where
S: HasRand + HasMaxSize,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -620,7 +620,7 @@ pub struct BytesRandInsertMutator;
impl<I, S> Mutator<I, S> for BytesRandInsertMutator
where
S: HasRand + HasMaxSize,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -815,7 +815,7 @@ pub struct BytesInsertCopyMutator {
impl<I, S> Mutator<I, S> for BytesInsertCopyMutator
where
S: HasRand + HasMaxSize,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -886,7 +886,7 @@ pub struct BytesSwapMutator {
impl<I, S> Mutator<I, S> for BytesSwapMutator
where
S: HasRand,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
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<I, S> Mutator<I, S> for CrossoverInsertMutator
where
S: HasCorpus + HasRand + HasMaxSize,
<S::Corpus as Corpus>::Input: HasMutatorBytes,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -1315,7 +1315,7 @@ impl<F, O> MappedCrossoverInsertMutator<F, O> {
impl<S, F, I, O> Mutator<I, S> for MappedCrossoverInsertMutator<F, O>
where
S: HasCorpus + HasMaxSize + HasRand,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
O: IntoOptionBytes,
F: Fn(&<S::Corpus as Corpus>::Input) -> &O,
{
@ -1494,7 +1494,7 @@ impl<I, S> Mutator<I, S> for SpliceMutator
where
S: HasCorpus + HasRand,
<S::Corpus as Corpus>::Input: HasMutatorBytes,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
{
#[expect(clippy::cast_sign_loss)]
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {

View File

@ -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<I, S> Mutator<I, S> for TokenInsert
where
S: HasMetadata + HasRand + HasMaxSize,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -375,7 +375,7 @@ pub struct TokenReplace;
impl<I, S> Mutator<I, S> for TokenReplace
where
S: HasMetadata + HasRand + HasMaxSize,
I: HasMutatorBytes,
I: HasMutatorResizableBytes,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -435,7 +435,7 @@ pub struct I2SRandReplace;
impl<I, S> Mutator<I, S> 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<MutationResult, Error> {
@ -636,7 +636,7 @@ where
impl<I, S> Mutator<I, S> 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<MutationResult, Error> {
@ -1306,7 +1306,7 @@ impl AFLppRedQueen {
impl<I, S> MultiMutator<I, S> for AFLppRedQueen
where
S: HasMetadata + HasRand + HasMaxSize + HasCorpus + HasCurrentCorpusId,
I: HasMutatorBytes + From<Vec<u8>>,
I: HasMutatorResizableBytes + From<Vec<u8>>,
{
#[expect(clippy::needless_range_loop, clippy::too_many_lines)]
fn multi_mutate(

View File

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

View File

@ -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<EM, <S::Corpus as Corpus>::Input, S, Z>,
S: HasCorpus + HasMetadata + HasRand + HasNamedMetadata + HasCurrentCorpusId,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: HasMutatorBytes + Clone,
<S::Corpus as Corpus>::Input: HasMutatorResizableBytes + Clone,
O: MapObserver,
C: AsRef<O> + Named,
{
@ -157,7 +157,7 @@ where
E: HasObservers + Executor<EM, <S::Corpus as Corpus>::Input, S, Z>,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus + HasMetadata + HasRand + HasCurrentCorpusId + HasCurrentTestcase,
<S::Corpus as Corpus>::Input: HasMutatorBytes + Clone,
<S::Corpus as Corpus>::Input: HasMutatorResizableBytes + Clone,
{
#[inline]
fn colorize(

View File

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

View File

@ -11,7 +11,7 @@ use std::{
use libafl::{
corpus::Corpus,
inputs::{BytesInput, HasMutatorBytes},
inputs::{BytesInput, HasMutatorBytes, HasMutatorResizableBytes},
mutators::{
ComposedByMutations, MutationId, MutationResult, Mutator, MutatorsTuple, ScheduledMutator,
},