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
This commit is contained in:
parent
19ef29ed60
commit
684b31279e
@ -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::<BytesInput>());
|
||||
let minimizer = StdScheduledMutator::new(havoc_mutations::<BytesInput>());
|
||||
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::<BytesInput>());
|
||||
let mut stages = tuple_list!(StdTMinMutationalStage::new(
|
||||
minimizer,
|
||||
CrashFeedback::new(),
|
||||
|
@ -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<RefMut<Testcase<<Self as UsesInput>::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<I>
|
||||
|
@ -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<BytesInput> for Rc<RefCell<BytesInput>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasBytesVec for BytesInput {
|
||||
impl HasMutatorBytes for BytesInput {
|
||||
#[inline]
|
||||
fn bytes(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bytes_mut(&mut self) -> &mut Vec<u8> {
|
||||
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<Item = &'a u8>>(&mut self, iter: I) {
|
||||
Extend::extend(&mut self.bytes, iter);
|
||||
}
|
||||
|
||||
fn splice<R, I>(&mut self, range: R, replace_with: I) -> alloc::vec::Splice<'_, I::IntoIter>
|
||||
where
|
||||
R: core::ops::RangeBounds<usize>,
|
||||
I: IntoIterator<Item = u8>,
|
||||
{
|
||||
self.bytes.splice(range, replace_with)
|
||||
}
|
||||
|
||||
fn drain<R>(&mut self, range: R) -> alloc::vec::Drain<'_, u8>
|
||||
where
|
||||
R: core::ops::RangeBounds<usize>,
|
||||
{
|
||||
self.bytes.drain(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasTargetBytes for BytesInput {
|
||||
|
426
libafl/src/inputs/bytessub.rs
Normal file
426
libafl/src/inputs/bytessub.rs
Normal file
@ -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<R>(range: &R) -> usize
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
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<R>(range: &R, max_len: usize) -> usize
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
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<usize>,
|
||||
}
|
||||
|
||||
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<R>(parent_input: &'a mut I, range: R) -> Self
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
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<R2>(&self, range: R2) -> (Bound<usize>, Bound<usize>)
|
||||
where
|
||||
R2: RangeBounds<usize>,
|
||||
{
|
||||
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<Item = &'b u8>>(&mut self, iter: IT) {
|
||||
let old_len = self.end_index() - self.start_index();
|
||||
|
||||
let new_values: Vec<u8> = 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<R2, IT>(
|
||||
&mut self,
|
||||
range: R2,
|
||||
replace_with: IT,
|
||||
) -> alloc::vec::Splice<'_, IT::IntoIter>
|
||||
where
|
||||
R2: RangeBounds<usize>,
|
||||
IT: IntoIterator<Item = u8>,
|
||||
{
|
||||
let range = self.sub_range(range);
|
||||
self.parent_input.splice(range, replace_with)
|
||||
}
|
||||
|
||||
fn drain<R2>(&mut self, range: R2) -> alloc::vec::Drain<'_, u8>
|
||||
where
|
||||
R2: RangeBounds<usize>,
|
||||
{
|
||||
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<NopInput> = 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);
|
||||
}
|
||||
}
|
@ -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<Path>,
|
||||
{
|
||||
let mut file = File::open(path)?;
|
||||
let mut bytes: Vec<u8> = 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<u8>;
|
||||
}
|
||||
|
||||
/// 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<u8>;
|
||||
|
||||
/// 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<Item = &'a u8>>(&mut self, iter: I);
|
||||
|
||||
/// Splices the given target bytes according to [`alloc::vec::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 [`alloc::vec::Vec::drain`]'s rules
|
||||
fn drain<R>(&mut self, range: R) -> Drain<'_, u8>
|
||||
where
|
||||
R: RangeBounds<usize>;
|
||||
|
||||
/// Creates a [`BytesSubInput`] from this input, that can be used for local mutations.
|
||||
fn sub_input<R>(&mut self, range: R) -> BytesSubInput<Self>
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
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<u8>);
|
||||
|
||||
impl<'a> From<&'a mut Vec<u8>> for MutVecInput<'a> {
|
||||
fn from(value: &'a mut Vec<u8>) -> 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<Item = &'b u8>>(&mut self, iter: I) {
|
||||
self.0.extend(iter);
|
||||
}
|
||||
|
||||
fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter>
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
I: IntoIterator<Item = u8>,
|
||||
{
|
||||
self.0.splice::<R, I>(range, replace_with)
|
||||
}
|
||||
|
||||
fn drain<R>(&mut self, range: R) -> Drain<'_, u8>
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
self.0.drain(range)
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines the input type shared across traits of the type.
|
||||
|
@ -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<I, S>: Named {
|
||||
/// Mutate a given input
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error>;
|
||||
|
||||
/// 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<I, S>: 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<I, S>: 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<MutationResult, Error>;
|
||||
|
||||
/// 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,
|
||||
|
@ -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<I, S> Mutator<MultipartInput<I>, S> for CrossoverInsertMutator<I>
|
||||
where
|
||||
S: HasCorpus<Input = MultipartInput<I>> + HasMaxSize + HasRand,
|
||||
I: Input + HasBytesVec,
|
||||
I: Input + HasMutatorBytes,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
@ -221,7 +221,7 @@ where
|
||||
impl<I, S> Mutator<MultipartInput<I>, S> for CrossoverReplaceMutator<I>
|
||||
where
|
||||
S: HasCorpus<Input = MultipartInput<I>> + HasMaxSize + HasRand,
|
||||
I: Input + HasBytesVec,
|
||||
I: Input + HasMutatorBytes,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
|
@ -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<I, S> Mutator<I, S> for BitFlipMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
if input.bytes().is_empty() {
|
||||
@ -159,7 +159,7 @@ pub struct ByteFlipMutator;
|
||||
impl<I, S> Mutator<I, S> for ByteFlipMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
if input.bytes().is_empty() {
|
||||
@ -193,7 +193,7 @@ pub struct ByteIncMutator;
|
||||
impl<I, S> Mutator<I, S> for ByteIncMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
if input.bytes().is_empty() {
|
||||
@ -228,7 +228,7 @@ pub struct ByteDecMutator;
|
||||
impl<I, S> Mutator<I, S> for ByteDecMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
if input.bytes().is_empty() {
|
||||
@ -263,7 +263,7 @@ pub struct ByteNegMutator;
|
||||
impl<I, S> Mutator<I, S> for ByteNegMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
if input.bytes().is_empty() {
|
||||
@ -298,7 +298,7 @@ pub struct ByteRandMutator;
|
||||
impl<I, S> Mutator<I, S> for ByteRandMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
if input.bytes().is_empty() {
|
||||
@ -338,7 +338,7 @@ macro_rules! add_mutator_impl {
|
||||
impl<I, S> Mutator<I, S> for $name
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
@ -405,7 +405,7 @@ macro_rules! interesting_mutator_impl {
|
||||
impl<I, S> Mutator<I, S> 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<MutationResult, Error> {
|
||||
@ -454,7 +454,7 @@ pub struct BytesDeleteMutator;
|
||||
impl<I, S> Mutator<I, S> for BytesDeleteMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<I, S> Mutator<I, S> for BytesExpandMutator
|
||||
where
|
||||
S: HasRand + HasMaxSize,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<I, S> Mutator<I, S> for BytesInsertMutator
|
||||
where
|
||||
S: HasRand + HasMaxSize,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<I, S> Mutator<I, S> for BytesRandInsertMutator
|
||||
where
|
||||
S: HasRand + HasMaxSize,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<I, S> Mutator<I, S> for BytesSetMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
let size = input.bytes().len();
|
||||
@ -686,7 +686,7 @@ pub struct BytesRandSetMutator;
|
||||
impl<I, S> Mutator<I, S> for BytesRandSetMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
let size = input.bytes().len();
|
||||
@ -725,7 +725,7 @@ pub struct BytesCopyMutator;
|
||||
impl<I, S> Mutator<I, S> for BytesCopyMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
let size = input.bytes().len();
|
||||
@ -768,7 +768,7 @@ pub struct BytesInsertCopyMutator {
|
||||
impl<I, S> Mutator<I, S> for BytesInsertCopyMutator
|
||||
where
|
||||
S: HasRand + HasMaxSize,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<I, S> Mutator<I, S> for BytesSwapMutator
|
||||
where
|
||||
S: HasRand,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
let size = input.bytes().len();
|
||||
@ -1029,15 +1029,15 @@ pub struct CrossoverInsertMutator<I> {
|
||||
phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<I: HasBytesVec> CrossoverInsertMutator<I> {
|
||||
pub(crate) fn crossover_insert(
|
||||
impl<I: HasMutatorBytes> CrossoverInsertMutator<I> {
|
||||
pub(crate) fn crossover_insert<I2: HasMutatorBytes>(
|
||||
input: &mut I,
|
||||
size: usize,
|
||||
target: usize,
|
||||
range: Range<usize>,
|
||||
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<I: HasBytesVec> CrossoverInsertMutator<I> {
|
||||
|
||||
impl<I, S> Mutator<I, S> for CrossoverInsertMutator<I>
|
||||
where
|
||||
S: HasCorpus<Input = I> + 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<MutationResult, Error> {
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
let size = input.bytes().len();
|
||||
let max_size = state.max_size();
|
||||
if size >= max_size {
|
||||
@ -1124,12 +1125,12 @@ pub struct CrossoverReplaceMutator<I> {
|
||||
phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<I: HasBytesVec> CrossoverReplaceMutator<I> {
|
||||
pub(crate) fn crossover_replace(
|
||||
impl<I: HasMutatorBytes> CrossoverReplaceMutator<I> {
|
||||
pub(crate) fn crossover_replace<I2: HasMutatorBytes>(
|
||||
input: &mut I,
|
||||
target: usize,
|
||||
range: Range<usize>,
|
||||
other: &I,
|
||||
other: &I2,
|
||||
) -> MutationResult {
|
||||
unsafe {
|
||||
buffer_copy(
|
||||
@ -1146,10 +1147,11 @@ impl<I: HasBytesVec> CrossoverReplaceMutator<I> {
|
||||
|
||||
impl<I, S> Mutator<I, S> for CrossoverReplaceMutator<I>
|
||||
where
|
||||
S: HasCorpus<Input = I> + HasRand,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasCorpus + HasRand,
|
||||
S::Input: HasMutatorBytes,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut S::Input) -> Result<MutationResult, Error> {
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
let size = input.bytes().len();
|
||||
if size == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
@ -1224,7 +1226,7 @@ pub struct SpliceMutator;
|
||||
impl<S> Mutator<S::Input, S> 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<MutationResult, Error> {
|
||||
@ -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)
|
||||
}
|
||||
|
@ -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},
|
||||
|
@ -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<S: HasRand + HasMaxSize, F: Fn(&mut S) -> 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,
|
||||
|
@ -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<I, S> Mutator<I, S> for TokenInsert
|
||||
where
|
||||
S: HasMetadata + HasRand + HasMaxSize,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
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<I, S> Mutator<I, S> for TokenReplace
|
||||
where
|
||||
S: UsesInput + HasMetadata + HasRand + HasMaxSize,
|
||||
I: HasBytesVec,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
|
||||
let size = input.bytes().len();
|
||||
@ -427,7 +427,7 @@ pub struct I2SRandReplace;
|
||||
impl<I, S> Mutator<I, S> 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<MutationResult, Error> {
|
||||
@ -1088,7 +1088,7 @@ impl AFLppRedQueen {
|
||||
impl<I, S> MultiMutator<I, S> for AFLppRedQueen
|
||||
where
|
||||
S: UsesInput + HasMetadata + HasRand + HasMaxSize + HasCorpus + HasCurrentCorpusId,
|
||||
I: HasBytesVec + From<Vec<u8>>,
|
||||
I: HasMutatorBytes + From<Vec<u8>>,
|
||||
{
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
|
@ -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<State = E::State> + EventFirer,
|
||||
E: HasObservers + Executor<EM, Z>,
|
||||
E::State: HasCorpus + HasMetadata + HasRand + HasNamedMetadata,
|
||||
E::Input: HasBytesVec,
|
||||
E::Input: HasMutatorBytes,
|
||||
O: MapObserver,
|
||||
C: AsRef<O> + Named,
|
||||
Z: UsesState<State = E::State>,
|
||||
@ -161,7 +161,7 @@ where
|
||||
C: AsRef<O> + Named,
|
||||
E: HasObservers + Executor<EM, Z>,
|
||||
E::State: HasCorpus + HasMetadata + HasRand,
|
||||
E::Input: HasBytesVec,
|
||||
E::Input: HasMutatorBytes,
|
||||
Z: UsesState<State = E::State>,
|
||||
{
|
||||
#[inline]
|
||||
|
@ -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<State = Z::State>,
|
||||
EM: UsesState<State = Z::State>,
|
||||
Z: Evaluator<E, EM>,
|
||||
Z::Input: HasBytesVec,
|
||||
Z::Input: HasMutatorBytes,
|
||||
Z::State: State + HasExecutions + HasCorpus + HasMetadata,
|
||||
{
|
||||
#[inline]
|
||||
|
@ -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] {
|
||||
|
@ -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,
|
||||
|
@ -1163,6 +1163,16 @@ impl<I> NopState<I> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> HasMaxSize for NopState<I> {
|
||||
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<I> UsesInput for NopState<I>
|
||||
where
|
||||
I: Input,
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user