diff --git a/fuzzers/baby_fuzzer_multi/.gitignore b/fuzzers/baby_fuzzer_multi/.gitignore new file mode 100644 index 0000000000..a977a2ca5b --- /dev/null +++ b/fuzzers/baby_fuzzer_multi/.gitignore @@ -0,0 +1 @@ +libpng-* \ No newline at end of file diff --git a/fuzzers/baby_fuzzer_multi/Cargo.toml b/fuzzers/baby_fuzzer_multi/Cargo.toml new file mode 100644 index 0000000000..04ab2364b4 --- /dev/null +++ b/fuzzers/baby_fuzzer_multi/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "baby_fuzzer_multi" +version = "0.10.0" +authors = ["Andrea Fioraldi ", "Dominik Maier ", "Addison Crump "] +edition = "2021" + +[features] +default = ["std"] +tui = [] +std = [] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +lto = true +codegen-units = 1 +opt-level = 3 +debug = true + +[dependencies] +libafl = { path = "../../libafl/", features = ["multipart_inputs"] } +libafl_bolts = { path = "../../libafl_bolts/" } diff --git a/fuzzers/baby_fuzzer_multi/README.md b/fuzzers/baby_fuzzer_multi/README.md new file mode 100644 index 0000000000..74e1b50932 --- /dev/null +++ b/fuzzers/baby_fuzzer_multi/README.md @@ -0,0 +1,10 @@ +# Baby fuzzer multi + +This is a minimalistic example about how to create a libafl based fuzzer for targets with multipart inputs. + +It runs on a single core until a crash occurs and then exits. + +The tested program is a simple Rust function without any instrumentation. +For real fuzzing, you will want to add some sort to add coverage or other feedback. + +You can run this example using `cargo run`, and you can enable the TUI feature by running `cargo run --features tui`. \ No newline at end of file diff --git a/fuzzers/baby_fuzzer_multi/src/main.rs b/fuzzers/baby_fuzzer_multi/src/main.rs new file mode 100644 index 0000000000..f462c20b70 --- /dev/null +++ b/fuzzers/baby_fuzzer_multi/src/main.rs @@ -0,0 +1,164 @@ +#[cfg(windows)] +use std::ptr::write_volatile; +use std::{path::PathBuf, ptr::write}; + +#[cfg(feature = "tui")] +use libafl::monitors::tui::{ui::TuiUI, TuiMonitor}; +#[cfg(not(feature = "tui"))] +use libafl::monitors::SimpleMonitor; +use libafl::{ + corpus::{InMemoryCorpus, OnDiskCorpus}, + events::SimpleEventManager, + executors::{inprocess::InProcessExecutor, ExitKind}, + feedback_or_fast, + feedbacks::{CrashFeedback, MaxMapFeedback, MinMapFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + inputs::{BytesInput, HasTargetBytes, MultipartInput}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + observers::StdMapObserver, + schedulers::QueueScheduler, + stages::mutational::StdMutationalStage, + state::StdState, + Evaluator, +}; +use libafl_bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}; + +/// Coverage map with explicit assignments due to the lack of instrumentation +static mut SIGNALS: [u8; 128] = [0; 128]; +static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() }; + +/// "Coverage" map for count, just to help things along +static mut LAST_COUNT: [usize; 1] = [usize::MAX]; +static mut LAST_COUNT_PTR: *mut usize = unsafe { LAST_COUNT.as_mut_ptr() }; + +/// Assign a signal to the signals map +fn signals_set(idx: usize) { + unsafe { write(SIGNALS_PTR.add(idx), 1) }; +} + +/// Assign a count to the count "map" +fn count_set(count: usize) { + unsafe { LAST_COUNT[0] = count }; +} + +#[allow(clippy::similar_names, clippy::manual_assert)] +pub fn main() { + // The closure that we want to fuzz + let mut harness = |input: &MultipartInput| { + let mut count = input.parts().len(); + for (i, input) in input.parts().iter().enumerate() { + let target = input.target_bytes(); + let buf = target.as_slice(); + signals_set(i * 8); + if !buf.is_empty() && buf[0] == b'a' { + signals_set(1 + i * 8); + if buf.len() > 1 && buf[1] == b'b' { + signals_set(2 + i * 8); + if buf.len() > 2 && buf[2] == b'c' { + count -= 1; + } + } + } + } + if count == 0 { + #[cfg(unix)] + panic!("Artificial bug triggered =)"); + + // panic!() raises a STATUS_STACK_BUFFER_OVERRUN exception which cannot be caught by the exception handler. + // Here we make it raise STATUS_ACCESS_VIOLATION instead. + // Extending the windows exception handler is a TODO. Maybe we can refer to what winafl code does. + // https://github.com/googleprojectzero/winafl/blob/ea5f6b85572980bb2cf636910f622f36906940aa/winafl.c#L728 + #[cfg(windows)] + unsafe { + write_volatile(0 as *mut u32, 0); + } + } + + // without this, artificial bug is not found + // maybe interesting to try to auto-derive this, researchers! :) + count_set(count); + + ExitKind::Ok + }; + + // Create an observation channel using the signals map + let signals_observer = + unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) }; + let mut count_observer = + unsafe { StdMapObserver::from_mut_ptr("count", LAST_COUNT_PTR, LAST_COUNT.len()) }; + *count_observer.initial_mut() = usize::MAX; // we are minimising! + + // Feedback to rate the interestingness of an input + let signals_feedback = MaxMapFeedback::new(&signals_observer); + let count_feedback = MinMapFeedback::new(&count_observer); + + let mut feedback = feedback_or_fast!(count_feedback, signals_feedback); + + // A feedback to choose if an input is a solution or not + let mut objective = CrashFeedback::new(); + + // create a State from scratch + let mut state = StdState::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(), + // States of the feedbacks. + // The feedbacks can report the data that should persist in the State. + &mut feedback, + // Same for objective feedbacks + &mut objective, + ) + .unwrap(); + + // The Monitor trait define how the fuzzer stats are displayed to the user + #[cfg(not(feature = "tui"))] + let mon = SimpleMonitor::new(|s| println!("{s}")); + #[cfg(feature = "tui")] + let ui = TuiUI::with_version(String::from("Baby Fuzzer"), String::from("0.0.1"), false); + #[cfg(feature = "tui")] + let mon = TuiMonitor::new(ui); + + // The event manager handle the various events generated during the fuzzing loop + // such as the notification of the addition of a new item to the corpus + let mut mgr = SimpleEventManager::new(mon); + + // A queue policy to get testcasess from the corpus + let scheduler = QueueScheduler::new(); + + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + + // Create the executor for an in-process function with just one observer + let mut executor = InProcessExecutor::new( + &mut harness, + tuple_list!(signals_observer, count_observer), + &mut fuzzer, + &mut state, + &mut mgr, + ) + .expect("Failed to create the Executor"); + + // a generator here is not generalisable + let initial = MultipartInput::from([ + ("part", BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), + ("part", BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), + ("part", BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), + ("part", BytesInput::new(vec![b'h', b'e', b'l', b'l', b'o'])), + ]); + + fuzzer + .evaluate_input(&mut state, &mut executor, &mut mgr, initial) + .unwrap(); + + // Setup a mutational stage with a basic bytes mutator + let mutator = StdScheduledMutator::new(havoc_mutations()); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + + fuzzer + .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + .expect("Error in the fuzzing loop"); +} diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index c313f264da..e0a79e8c5a 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -85,6 +85,8 @@ tui_monitor = ["ratatui", "crossterm"] ## Enables `StringClassificationStage` and associated mutators, which allow for mutations which preserve the Unicode property data unicode = ["libafl_bolts/alloc", "ahash/std", "serde/rc", "bitvec", "reqwest", "zip"] +## Enable multi-part input formats and mutators +multipart_inputs = ["arrayvec", "rand_trait"] #! ## LibAFL-Bolts Features @@ -186,6 +188,8 @@ libcasr = { version = "2.7", optional = true } bitvec = { version = "1.0", optional = true, features = ["serde"] } # used for string range storage +arrayvec = { version = "0.7.4", optional = true, default-features = false } # used for fixed-len collects + # optional-dev deps (change when target.'cfg(accessible(::std))'.test-dependencies will be stable) serial_test = { version = "2", optional = true, default-features = false, features = ["logging"] } diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index 08fa39caea..883537d882 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -12,8 +12,14 @@ pub use gramatron::*; pub mod generalized; pub use generalized::*; +#[cfg(feature = "multipart_inputs")] +pub mod multi; +#[cfg(feature = "multipart_inputs")] +pub use multi::*; + #[cfg(feature = "nautilus")] pub mod nautilus; + use alloc::{ boxed::Box, string::{String, ToString}, diff --git a/libafl/src/inputs/multi.rs b/libafl/src/inputs/multi.rs new file mode 100644 index 0000000000..750495ae64 --- /dev/null +++ b/libafl/src/inputs/multi.rs @@ -0,0 +1,165 @@ +//! Definitions for inputs which have multiple distinct subcomponents. +//! +//! Unfortunately, since both [`serde::de::Deserialize`] and [`Clone`] require [`Sized`], it is not +//! possible to dynamically define a single input with dynamic typing. As such, [`MultipartInput`] +//! requires that each subcomponent be the same subtype. + +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; + +use arrayvec::ArrayVec; +use serde::{Deserialize, Serialize}; + +use crate::inputs::Input; + +/// An input composed of multiple parts. Use in situations where subcomponents are not necessarily +/// related, or represent distinct parts of the input. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct MultipartInput { + parts: Vec, + names: Vec, +} + +impl Default for MultipartInput { + fn default() -> Self { + Self::new() + } +} + +impl MultipartInput { + /// Create a new multipart input. + #[must_use] + pub fn new() -> Self { + Self { + parts: Vec::new(), + names: Vec::new(), + } + } + + fn idxs_to_skips(idxs: &mut [usize]) { + for following in (1..idxs.len()).rev() { + let first = idxs[following - 1]; + let second = idxs[following]; + + idxs[following] = second + .checked_sub(first) + .expect("idxs was not sorted") + .checked_sub(1) + .expect("idxs had duplicate elements"); + } + } + + /// Get the individual parts of this input. + #[must_use] + pub fn parts(&self) -> &[I] { + &self.parts + } + + /// Access multiple parts mutably. + /// + /// ## Panics + /// + /// Panics if idxs is not sorted, has duplicate elements, or any entry is out of bounds. + #[must_use] + pub fn parts_mut(&mut self, mut idxs: [usize; N]) -> [&mut I; N] { + Self::idxs_to_skips(&mut idxs); + + let mut parts = self.parts.iter_mut(); + if let Ok(arr) = idxs + .into_iter() + .map(|i| parts.nth(i).expect("idx had an out of bounds entry")) + .collect::>() + .into_inner() + { + arr + } else { + // avoid Debug trait requirement for expect/unwrap + panic!("arrayvec collection failed somehow") + } + } + + /// Get a specific part of this input by index. + pub fn part_mut(&mut self, idx: usize) -> Option<&mut I> { + self.parts.get_mut(idx) + } + + /// Get the names associated with the subparts of this input. Used to distinguish between the + /// input components in the case where some parts may or may not be present, or in different + /// orders. + #[must_use] + pub fn names(&self) -> &[String] { + &self.names + } + + /// Gets a reference to each part with the provided name. + pub fn parts_by_name<'a, 'b>( + &'b self, + name: &'a str, + ) -> impl Iterator + 'a + where + 'b: 'a, + { + self.names() + .iter() + .zip(&self.parts) + .enumerate() + .filter_map(move |(i, (s, item))| (s == name).then_some((i, item))) + } + + /// Gets a mutable reference to each part with the provided name. + pub fn parts_by_name_mut<'a, 'b>( + &'b mut self, + name: &'a str, + ) -> impl Iterator + 'a + where + 'b: 'a, + { + self.names + .iter() + .zip(&mut self.parts) + .enumerate() + .filter_map(move |(i, (s, item))| (s == name).then_some((i, item))) + } + + /// Adds a part to this input, potentially with the same name as an existing part. + pub fn add_part(&mut self, name: String, part: I) { + self.parts.push(part); + self.names.push(name); + } + + /// Iterate over the parts of this input; no order is specified. + pub fn iter(&self) -> impl Iterator { + self.names.iter().map(String::as_ref).zip(self.parts()) + } +} + +impl From for MultipartInput +where + It: IntoIterator, + S: AsRef, +{ + fn from(parts: It) -> Self { + let mut input = MultipartInput::new(); + for (name, part) in parts { + input.add_part(name.as_ref().to_string(), part); + } + input + } +} + +impl Input for MultipartInput +where + I: Input, +{ + fn generate_name(&self, idx: usize) -> String { + self.names + .iter() + .cloned() + .zip(self.parts.iter().map(|i| i.generate_name(idx))) + .map(|(name, generated)| format!("{name}-{generated}")) + .collect::>() + .join(",") + } +} diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index e3560bd673..7232b0e800 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -25,8 +25,14 @@ pub mod string; #[cfg(feature = "unicode")] pub use string::*; +#[cfg(feature = "multipart_inputs")] +pub mod multi; +#[cfg(feature = "multipart_inputs")] +pub use multi::*; + #[cfg(feature = "nautilus")] pub mod nautilus; + use alloc::vec::Vec; use libafl_bolts::{tuples::HasConstLen, Named}; diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs new file mode 100644 index 0000000000..838703a8f6 --- /dev/null +++ b/libafl/src/mutators/multi.rs @@ -0,0 +1,330 @@ +//! Mutator definitions for [`MultipartInput`]s. See [`crate::inputs::multi`] for details. + +use core::cmp::{min, Ordering}; + +use libafl_bolts::{rands::Rand, Error}; + +use crate::{ + corpus::{Corpus, CorpusId}, + impl_default_multipart, + inputs::{multi::MultipartInput, HasBytesVec, Input}, + mutators::{ + mutations::{ + rand_range, BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, + ByteIncMutator, ByteInterestingMutator, ByteNegMutator, ByteRandMutator, + BytesCopyMutator, BytesDeleteMutator, BytesExpandMutator, BytesInsertCopyMutator, + BytesInsertMutator, BytesRandInsertMutator, BytesRandSetMutator, BytesSetMutator, + BytesSwapMutator, CrossoverInsertMutator, CrossoverReplaceMutator, DwordAddMutator, + DwordInterestingMutator, QwordAddMutator, WordAddMutator, WordInterestingMutator, + }, + token_mutations::{I2SRandReplace, TokenInsert, TokenReplace}, + MutationResult, Mutator, + }, + random_corpus_id, + state::{HasCorpus, HasMaxSize, HasRand}, +}; + +/// Marker trait for if the default multipart input mutator implementation is appropriate. +/// +/// You should implement this type for your mutator if you just want a random part of the input to +/// be selected and mutated. Use [`impl_default_multipart`] to implement this marker trait for many +/// at once. +pub trait DefaultMultipartMutator {} + +impl Mutator, S> for M +where + M: DefaultMultipartMutator + Mutator, + S: HasRand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut MultipartInput, + stage_idx: i32, + ) -> Result { + if input.parts().is_empty() { + Ok(MutationResult::Skipped) + } else { + let selected = state.rand_mut().below(input.parts().len() as u64) as usize; + let mutated = input.part_mut(selected).unwrap(); + self.mutate(state, mutated, stage_idx) + } + } + + fn post_exec( + &mut self, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + M::post_exec(self, state, stage_idx, corpus_idx) + } +} + +mod macros { + /// Implements the marker trait [`super::DefaultMultipartMutator`] for one to many types, e.g.: + /// + /// ```rs + /// impl_default_multipart!( + /// // --- havoc --- + /// BitFlipMutator, + /// ByteAddMutator, + /// ByteDecMutator, + /// ByteFlipMutator, + /// ByteIncMutator, + /// ... + /// ); + /// ``` + #[macro_export] + macro_rules! impl_default_multipart { + ($mutator: ty, $($mutators: ty),+$(,)?) => { + impl $crate::mutators::multi::DefaultMultipartMutator for $mutator {} + impl_default_multipart!($($mutators),+); + }; + + ($mutator: ty) => { + impl $crate::mutators::multi::DefaultMultipartMutator for $mutator {} + }; + } +} + +impl_default_multipart!( + // --- havoc --- + BitFlipMutator, + ByteAddMutator, + ByteDecMutator, + ByteFlipMutator, + ByteIncMutator, + ByteInterestingMutator, + ByteNegMutator, + ByteRandMutator, + BytesCopyMutator, + BytesDeleteMutator, + BytesExpandMutator, + BytesInsertCopyMutator, + BytesInsertMutator, + BytesRandInsertMutator, + BytesRandSetMutator, + BytesSetMutator, + BytesSwapMutator, + // crossover has a custom implementation below + DwordAddMutator, + DwordInterestingMutator, + QwordAddMutator, + WordAddMutator, + WordInterestingMutator, + // --- token --- + TokenInsert, + TokenReplace, + // --- i2s --- + I2SRandReplace, +); + +impl Mutator, S> for CrossoverInsertMutator +where + S: HasCorpus> + HasMaxSize + HasRand, + I: Input + HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut MultipartInput, + _stage_idx: i32, + ) -> Result { + // we can eat the slight bias; number of parts will be small + let name_choice = state.rand_mut().next() as usize; + let part_choice = state.rand_mut().next() as usize; + + // We special-case crossover with self + let idx = random_corpus_id!(state.corpus(), state.rand_mut()); + if let Some(cur) = state.corpus().current() { + if idx == *cur { + let choice = name_choice % input.names().len(); + let name = input.names()[choice].clone(); + + let other_size = input.parts()[choice].bytes().len(); + if other_size < 2 { + return Ok(MutationResult::Skipped); + } + + let parts = input.parts_by_name(&name).count() - 1; + + if parts == 0 { + return Ok(MutationResult::Skipped); + } + + let maybe_size = input + .parts_by_name(&name) + .filter(|&(p, _)| p != choice) + .nth(part_choice % parts) + .map(|(idx, part)| (idx, part.bytes().len())); + + if let Some((part_idx, size)) = maybe_size { + let target = state.rand_mut().below(size as u64) as usize; + let range = rand_range(state, other_size, min(other_size, size - target)); + + let [part, chosen] = match part_idx.cmp(&choice) { + Ordering::Less => input.parts_mut([part_idx, choice]), + Ordering::Equal => { + unreachable!("choice should never equal the part idx!") + } + Ordering::Greater => { + let [chosen, part] = input.parts_mut([choice, part_idx]); + [part, chosen] + } + }; + + return Ok(Self::crossover_insert(part, size, target, range, chosen)); + } + + return Ok(MutationResult::Skipped); + } + } + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input(state.corpus())?; + + let choice = name_choice % other.names().len(); + let name = &other.names()[choice]; + + let other_size = other.parts()[choice].bytes().len(); + if other_size < 2 { + return Ok(MutationResult::Skipped); + } + + let parts = input.parts_by_name(name).count(); + + if parts > 0 { + let (_, part) = input + .parts_by_name_mut(name) + .nth(part_choice % parts) + .unwrap(); + drop(other_testcase); + let size = part.bytes().len(); + + let target = state.rand_mut().below(size as u64) as usize; + let range = rand_range(state, other_size, min(other_size, size - target)); + + let other_testcase = state.corpus().get(idx)?.borrow_mut(); + // No need to load the input again, it'll still be cached. + let other = other_testcase.input().as_ref().unwrap(); + + Ok(Self::crossover_insert( + part, + size, + target, + range, + &other.parts()[choice], + )) + } else { + // just add it! + input.add_part(name.clone(), other.parts()[choice].clone()); + + Ok(MutationResult::Mutated) + } + } +} + +impl Mutator, S> for CrossoverReplaceMutator +where + S: HasCorpus> + HasMaxSize + HasRand, + I: Input + HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut MultipartInput, + _stage_idx: i32, + ) -> Result { + // we can eat the slight bias; number of parts will be small + let name_choice = state.rand_mut().next() as usize; + let part_choice = state.rand_mut().next() as usize; + + // We special-case crossover with self + let idx = random_corpus_id!(state.corpus(), state.rand_mut()); + if let Some(cur) = state.corpus().current() { + if idx == *cur { + let choice = name_choice % input.names().len(); + let name = input.names()[choice].clone(); + + let other_size = input.parts()[choice].bytes().len(); + if other_size < 2 { + return Ok(MutationResult::Skipped); + } + + let parts = input.parts_by_name(&name).count() - 1; + + if parts == 0 { + return Ok(MutationResult::Skipped); + } + + let maybe_size = input + .parts_by_name(&name) + .filter(|&(p, _)| p != choice) + .nth(part_choice % parts) + .map(|(idx, part)| (idx, part.bytes().len())); + + if let Some((part_idx, size)) = maybe_size { + let target = state.rand_mut().below(size as u64) as usize; + let range = rand_range(state, other_size, min(other_size, size - target)); + + let [part, chosen] = match part_idx.cmp(&choice) { + Ordering::Less => input.parts_mut([part_idx, choice]), + Ordering::Equal => { + unreachable!("choice should never equal the part idx!") + } + Ordering::Greater => { + let [chosen, part] = input.parts_mut([choice, part_idx]); + [part, chosen] + } + }; + + return Ok(Self::crossover_replace(part, target, range, chosen)); + } + + return Ok(MutationResult::Skipped); + } + } + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input(state.corpus())?; + + let choice = name_choice % other.names().len(); + let name = &other.names()[choice]; + + let other_size = other.parts()[choice].bytes().len(); + if other_size < 2 { + return Ok(MutationResult::Skipped); + } + + let parts = input.parts_by_name(name).count(); + + if parts > 0 { + let (_, part) = input + .parts_by_name_mut(name) + .nth(part_choice % parts) + .unwrap(); + drop(other_testcase); + let size = part.bytes().len(); + + let target = state.rand_mut().below(size as u64) as usize; + let range = rand_range(state, other_size, min(other_size, size - target)); + + let other_testcase = state.corpus().get(idx)?.borrow_mut(); + // No need to load the input again, it'll still be cached. + let other = other_testcase.input().as_ref().unwrap(); + + Ok(Self::crossover_replace( + part, + target, + range, + &other.parts()[choice], + )) + } else { + // just add it! + input.add_part(name.clone(), other.parts()[choice].clone()); + + Ok(MutationResult::Mutated) + } + } +} diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index 2e19892873..935973f6a5 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -1,13 +1,13 @@ //! A wide variety of mutations used during fuzzing. use alloc::{borrow::ToOwned, vec::Vec}; -use core::{cmp::min, mem::size_of, ops::Range}; +use core::{cmp::min, marker::PhantomData, mem::size_of, ops::Range}; use libafl_bolts::{rands::Rand, Named}; use crate::{ corpus::Corpus, - inputs::HasBytesVec, + inputs::{HasBytesVec, Input}, mutators::{MutationResult, Mutator}, random_corpus_id, state::{HasCorpus, HasMaxSize, HasRand}, @@ -1085,12 +1085,45 @@ impl BytesSwapMutator { /// Crossover insert mutation for inputs with a bytes vector #[derive(Debug, Default)] -pub struct CrossoverInsertMutator; +pub struct CrossoverInsertMutator { + phantom: PhantomData, +} + +impl CrossoverInsertMutator { + pub(crate) fn crossover_insert( + input: &mut I, + size: usize, + target: usize, + range: Range, + other: &I, + ) -> MutationResult { + input.bytes_mut().resize(size + range.len(), 0); + unsafe { + buffer_self_copy( + input.bytes_mut(), + target, + target + range.len(), + size - target, + ); + } + + unsafe { + buffer_copy( + input.bytes_mut(), + other.bytes(), + range.start, + target, + range.len(), + ); + } + MutationResult::Mutated + } +} -impl Mutator for CrossoverInsertMutator +impl Mutator for CrossoverInsertMutator where - S: HasCorpus + HasRand + HasMaxSize, - S::Input: HasBytesVec, + S: HasCorpus + HasRand + HasMaxSize, + I: Input + HasBytesVec, { fn mutate( &mut self, @@ -1125,55 +1158,60 @@ where let range = rand_range(state, other_size, min(other_size, max_size - size)); let target = state.rand_mut().below(size as u64) as usize; - input.bytes_mut().resize(size + range.len(), 0); - unsafe { - buffer_self_copy( - input.bytes_mut(), - target, - target + range.len(), - size - target, - ); - } - let other_testcase = state.corpus().get(idx)?.borrow_mut(); // No need to load the input again, it'll still be cached. let other = other_testcase.input().as_ref().unwrap(); - unsafe { - buffer_copy( - input.bytes_mut(), - other.bytes(), - range.start, - target, - range.len(), - ); - } - Ok(MutationResult::Mutated) + Ok(Self::crossover_insert(input, size, target, range, other)) } } -impl Named for CrossoverInsertMutator { +impl Named for CrossoverInsertMutator { fn name(&self) -> &str { "CrossoverInsertMutator" } } -impl CrossoverInsertMutator { +impl CrossoverInsertMutator { /// Creates a new [`CrossoverInsertMutator`]. #[must_use] pub fn new() -> Self { - Self + Self { + phantom: PhantomData, + } } } /// Crossover replace mutation for inputs with a bytes vector #[derive(Debug, Default)] -pub struct CrossoverReplaceMutator; +pub struct CrossoverReplaceMutator { + phantom: PhantomData, +} -impl Mutator for CrossoverReplaceMutator +impl CrossoverReplaceMutator { + pub(crate) fn crossover_replace( + input: &mut I, + target: usize, + range: Range, + other: &I, + ) -> MutationResult { + unsafe { + buffer_copy( + input.bytes_mut(), + other.bytes(), + range.start, + target, + range.len(), + ); + } + MutationResult::Mutated + } +} + +impl Mutator for CrossoverReplaceMutator where - S: HasCorpus + HasRand, - S::Input: HasBytesVec, + S: HasCorpus + HasRand, + I: Input + HasBytesVec, { fn mutate( &mut self, @@ -1210,30 +1248,23 @@ where // No need to load the input again, it'll still be cached. let other = other_testcase.input().as_ref().unwrap(); - unsafe { - buffer_copy( - input.bytes_mut(), - other.bytes(), - range.start, - target, - range.len(), - ); - } - Ok(MutationResult::Mutated) + Ok(Self::crossover_replace(input, target, range, other)) } } -impl Named for CrossoverReplaceMutator { +impl Named for CrossoverReplaceMutator { fn name(&self) -> &str { "CrossoverReplaceMutator" } } -impl CrossoverReplaceMutator { +impl CrossoverReplaceMutator { /// Creates a new [`CrossoverReplaceMutator`]. #[must_use] pub fn new() -> Self { - Self + Self { + phantom: PhantomData, + } } } diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index 3627d9fee8..ffdab9e92d 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -258,10 +258,11 @@ pub type HavocMutationsNoCrossoverType = tuple_list_type!( ); /// Tuple type of the mutations that compose the Havoc mutator's crossover mutations -pub type HavocCrossoverType = tuple_list_type!(CrossoverInsertMutator, CrossoverReplaceMutator); +pub type HavocCrossoverType = + tuple_list_type!(CrossoverInsertMutator, CrossoverReplaceMutator); /// Tuple type of the mutations that compose the Havoc mutator -pub type HavocMutationsType = tuple_list_type!( +pub type HavocMutationsType = tuple_list_type!( BitFlipMutator, ByteFlipMutator, ByteIncMutator, @@ -287,8 +288,8 @@ pub type HavocMutationsType = tuple_list_type!( BytesCopyMutator, BytesInsertCopyMutator, BytesSwapMutator, - CrossoverInsertMutator, - CrossoverReplaceMutator, + CrossoverInsertMutator, + CrossoverReplaceMutator, ); /// Get the mutations that compose the Havoc mutator (only applied to single inputs) @@ -325,7 +326,7 @@ pub fn havoc_mutations_no_crossover() -> HavocMutationsNoCrossoverType { /// Get the mutations that compose the Havoc mutator's crossover strategy #[must_use] -pub fn havoc_crossover() -> HavocCrossoverType { +pub fn havoc_crossover() -> HavocCrossoverType { tuple_list!( CrossoverInsertMutator::new(), CrossoverReplaceMutator::new(), @@ -334,14 +335,14 @@ pub fn havoc_crossover() -> HavocCrossoverType { /// Get the mutations that compose the Havoc mutator #[must_use] -pub fn havoc_mutations() -> HavocMutationsType { +pub fn havoc_mutations() -> HavocMutationsType { havoc_mutations_no_crossover().merge(havoc_crossover()) } /// Get the mutations that uses the Tokens metadata #[must_use] pub fn tokens_mutations() -> tuple_list_type!(TokenInsert, TokenReplace) { - tuple_list!(TokenInsert::new(), TokenReplace::new(),) + tuple_list!(TokenInsert::new(), TokenReplace::new()) } /// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`]. @@ -617,7 +618,7 @@ pub mod pybind { /// Python class for StdHavocMutator pub struct PythonStdHavocMutator { /// Rust wrapped StdHavocMutator object - pub inner: StdScheduledMutator, + pub inner: StdScheduledMutator, PythonStdState>, } #[pymethods] diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 50becd968b..ed68926378 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -542,6 +542,11 @@ where ) } + /// Gets the initial value for this map, mutably + pub fn initial_mut(&mut self) -> &mut T { + &mut self.initial + } + /// Gets the backing for this map pub fn map(&self) -> &OwnedMutSlice<'a, T> { &self.map