diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 2138a51407..1a4e0bf652 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -1,7 +1,7 @@ //! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! use core::time::Duration; -use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::min, mem::transmute_copy, collections::btree_map::Range}; +use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range}; use libafl::{ bolts::{ @@ -22,10 +22,8 @@ use libafl::{ fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, - mutators::scheduled::{havoc_mutations, StdScheduledMutator}, observers::{VariableMapObserver}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, - stages::StdMutationalStage, state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata}, Error, prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata}, Evaluator, @@ -39,8 +37,11 @@ use rand::{SeedableRng, StdRng, Rng}; use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, qemustate::QemuStateRestoreHelper, - systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, + systemstate::{mutators::{MINIMUM_INTER_ARRIVAL_TIME, InterruptShifterMutator}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, + systemstate::mutation::scheduled::{havoc_mutations, StdScheduledMutator}, }; +use crate::mutational::StdMutationalStage; + pub static mut RNG_SEED: u64 = 1; pub const MAX_NUM_INTERRUPT: usize = 32; @@ -189,13 +190,19 @@ pub fn fuzz() { unsafe { #[cfg(feature = "fuzz_int")] { + let mut start_tick : u32 = 0; for i in 0..DO_NUM_INTERRUPT { let mut t : [u8; 4] = [0,0,0,0]; if len > (i+1)*4 { for j in 0 as usize..4 as usize { t[j]=buf[i*4+j]; } - libafl_interrupt_offsets[i] = u32::from_le_bytes(t); + if i == 0 { + start_tick = u32::from_le_bytes(t); + } else { + start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); + } + libafl_interrupt_offsets[i] = start_tick; libafl_num_interrupts = i+1; } } @@ -348,6 +355,8 @@ pub fn fuzz() { let mut executor = TimeoutExecutor::new(executor, timeout); let mutations = havoc_mutations(); + #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] + let mutations = (InterruptShifterMutator::new(),havoc_mutations()); // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(mutations); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); diff --git a/fuzzers/FRET/src/lib.rs b/fuzzers/FRET/src/lib.rs index 36557a3ab5..4f300be90b 100644 --- a/fuzzers/FRET/src/lib.rs +++ b/fuzzers/FRET/src/lib.rs @@ -8,4 +8,6 @@ mod qemustate; #[cfg(target_os = "linux")] pub mod systemstate; #[cfg(target_os = "linux")] +mod mutational; +#[cfg(target_os = "linux")] mod worst; \ No newline at end of file diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index 57798ef3d3..c907864a84 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -10,6 +10,8 @@ mod qemustate; mod systemstate; #[cfg(target_os = "linux")] mod worst; +#[cfg(target_os = "linux")] +mod mutational; #[cfg(target_os = "linux")] pub fn main() { diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs new file mode 100644 index 0000000000..a876979299 --- /dev/null +++ b/fuzzers/FRET/src/mutational.rs @@ -0,0 +1,164 @@ +//| The [`MutationalStage`] is the default stage used during fuzzing. +//! For the current input, it will perform a range of random mutations, and then run them in the executor. + +use core::marker::PhantomData; + +use libafl::{ + bolts::rands::Rand, + corpus::Corpus, + fuzzer::Evaluator, + mark_feature_time, + stages::{Stage}, + start_timer, + state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, + Error, +}; +use crate::systemstate::mutation::Mutator; + +// TODO multi mutators stage + +/// A Mutational stage is the stage in a fuzzing run that mutates inputs. +/// Mutational stages will usually have a range of mutations that are +/// being applied to the input one by one, between executions. +pub trait MutationalStage: Stage +where + E: UsesState, + M: Mutator, + EM: UsesState, + Z: Evaluator, + Self::State: HasClientPerfMonitor + HasCorpus, +{ + /// The mutator registered for this stage + fn mutator(&self) -> &M; + + /// The mutator registered for this stage (mutable) + fn mutator_mut(&mut self) -> &mut M; + + /// Gets the number of iterations this mutator should run for. + fn iterations(&self, state: &mut Z::State, corpus_idx: usize) -> Result; + + /// Runs this (mutational) stage for the given testcase + #[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely... + fn perform_mutational( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut Z::State, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + let num = self.iterations(state, corpus_idx)?; + + for i in 0..num { + start_timer!(state); + let mut input = state + .corpus() + .get(corpus_idx)? + .borrow_mut() + .clone(); + mark_feature_time!(state, PerfFeature::GetInputFromCorpus); + + start_timer!(state); + self.mutator_mut().mutate(state, &mut input, i as i32)?; + mark_feature_time!(state, PerfFeature::Mutate); + + // Time is measured directly the `evaluate_input` function + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, input.load_input()?.clone())?; + + start_timer!(state); + self.mutator_mut().post_exec(state, i as i32, corpus_idx)?; + mark_feature_time!(state, PerfFeature::MutatePostExec); + } + Ok(()) + } +} + +/// Default value, how many iterations each stage gets, as an upper bound. +/// It may randomly continue earlier. +pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128; + +/// The default mutational stage +#[derive(Clone, Debug)] +pub struct StdMutationalStage { + mutator: M, + #[allow(clippy::type_complexity)] + phantom: PhantomData<(E, EM, Z)>, +} + +impl MutationalStage for StdMutationalStage +where + E: UsesState, + EM: UsesState, + M: Mutator, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + /// The mutator, added to this stage + #[inline] + fn mutator(&self) -> &M { + &self.mutator + } + + /// The list of mutators, added to this stage (as mutable ref) + #[inline] + fn mutator_mut(&mut self) -> &mut M { + &mut self.mutator + } + + /// Gets the number of iterations as a random number + fn iterations(&self, state: &mut Z::State, _corpus_idx: usize) -> Result { + Ok(1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS)) + } +} + +impl UsesState for StdMutationalStage +where + E: UsesState, + EM: UsesState, + M: Mutator, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + type State = Z::State; +} + +impl Stage for StdMutationalStage +where + E: UsesState, + EM: UsesState, + M: Mutator, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + #[inline] + #[allow(clippy::let_and_return)] + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut Z::State, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + let ret = self.perform_mutational(fuzzer, executor, state, manager, corpus_idx); + ret + } +} + +impl StdMutationalStage +where + E: UsesState, + EM: UsesState, + M: Mutator, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + /// Creates a new default mutational stage + pub fn new(mutator: M) -> Self { + Self { + mutator, + phantom: PhantomData, + } + } +} + diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 76ecf60bc6..693e17a8eb 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -15,13 +15,14 @@ pub mod observers; pub mod feedbacks; pub mod graph; pub mod schedulers; -// pub mod mutators; +pub mod mutation; +pub mod mutators; -#[cfg(feature = "fuzz_interrupt")] -pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes -#[cfg(not(feature = "fuzz_interrupt"))] -pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes -pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start +// #[cfg(feature = "fuzz_interrupt")] +// pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes +// #[cfg(not(feature = "fuzz_interrupt"))] +// pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes +// pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start // Constants const NUM_PRIOS: usize = 5; diff --git a/fuzzers/FRET/src/systemstate/mutation/mod.rs b/fuzzers/FRET/src/systemstate/mutation/mod.rs new file mode 100644 index 0000000000..0b8b6741d2 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/mutation/mod.rs @@ -0,0 +1,185 @@ +//! Mutators mutate input during fuzzing. + +pub mod scheduled; +pub use scheduled::*; +pub mod mutations; +pub use mutations::*; + +use libafl::{ + bolts::tuples::{HasConstLen, Named}, + inputs::UsesInput, + Error, prelude::Testcase, + mutators::{MutationResult}, +}; + +// TODO mutator stats method that produces something that can be sent with the NewTestcase event +// We can use it to report which mutations generated the testcase in the broker logs + + +/// A mutator takes input, and mutates it. +/// Simple as that. +pub trait Mutator +where + S: UsesInput, +{ + /// Mutate a given input + fn mutate( + &mut self, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result; + + /// Post-process given the outcome of the execution + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option, + ) -> Result<(), Error> { + Ok(()) + } +} + +/// A `Tuple` of `Mutators` that can execute multiple `Mutators` in a row. +pub trait MutatorsTuple: HasConstLen +where + S: UsesInput, +{ + /// Runs the `mutate` function on all `Mutators` in this `Tuple`. + fn mutate_all( + &mut self, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result; + + /// Runs the `post_exec` function on all `Mutators` in this `Tuple`. + fn post_exec_all( + &mut self, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error>; + + /// Gets the [`Mutator`] at the given index and runs the `mutate` function on it. + fn get_and_mutate( + &mut self, + index: usize, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result; + + /// Gets the [`Mutator`] at the given index and runs the `post_exec` function on it. + fn get_and_post_exec( + &mut self, + index: usize, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error>; +} + +impl MutatorsTuple for () +where + S: UsesInput, +{ + fn mutate_all( + &mut self, + _state: &mut S, + _input: &mut Testcase, + _stage_idx: i32, + ) -> Result { + Ok(MutationResult::Skipped) + } + + fn post_exec_all( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option, + ) -> Result<(), Error> { + Ok(()) + } + + fn get_and_mutate( + &mut self, + _index: usize, + _state: &mut S, + _input: &mut Testcase, + _stage_idx: i32, + ) -> Result { + Ok(MutationResult::Skipped) + } + + fn get_and_post_exec( + &mut self, + _index: usize, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option, + ) -> Result<(), Error> { + Ok(()) + } +} + +impl MutatorsTuple for (Head, Tail) +where + Head: Mutator + Named, + Tail: MutatorsTuple, + S: UsesInput, +{ + fn mutate_all( + &mut self, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result { + let r = self.0.mutate(state, input, stage_idx)?; + if self.1.mutate_all(state, input, stage_idx)? == MutationResult::Mutated { + Ok(MutationResult::Mutated) + } else { + Ok(r) + } + } + + fn post_exec_all( + &mut self, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + self.0.post_exec(state, stage_idx, corpus_idx)?; + self.1.post_exec_all(state, stage_idx, corpus_idx) + } + + fn get_and_mutate( + &mut self, + index: usize, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result { + if index == 0 { + self.0.mutate(state, input, stage_idx) + } else { + self.1.get_and_mutate(index - 1, state, input, stage_idx) + } + } + + fn get_and_post_exec( + &mut self, + index: usize, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + if index == 0 { + self.0.post_exec(state, stage_idx, corpus_idx) + } else { + self.1 + .get_and_post_exec(index - 1, state, stage_idx, corpus_idx) + } + } +} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/mutation/mutations.rs b/fuzzers/FRET/src/systemstate/mutation/mutations.rs new file mode 100644 index 0000000000..fd28548caa --- /dev/null +++ b/fuzzers/FRET/src/systemstate/mutation/mutations.rs @@ -0,0 +1,1189 @@ +//! A wide variety of mutations used during fuzzing. + +extern crate alloc; +use alloc::{borrow::ToOwned, vec::Vec}; +use core::{ + cmp::{max, min}, + mem::size_of, +}; + +use libafl::{ + bolts::{rands::Rand, tuples::Named}, + corpus::Corpus, + inputs::{HasBytesVec, UsesInput}, + // mutators::{MutationResult, Mutator}, + state::{HasCorpus, HasMaxSize, HasRand}, + Error, prelude::Testcase, +}; + +use libafl::mutators::{MutationResult}; + +use crate::systemstate::mutation::Mutator; + +/// Mem move in the own vec +#[inline] +pub fn buffer_self_copy(data: &mut [T], from: usize, to: usize, len: usize) { + debug_assert!(!data.is_empty()); + debug_assert!(from + len <= data.len()); + debug_assert!(to + len <= data.len()); + if len != 0 && from != to { + let ptr = data.as_mut_ptr(); + unsafe { + core::ptr::copy(ptr.add(from), ptr.add(to), len); + } + } +} + +/// Mem move between vecs +#[inline] +pub fn buffer_copy(dst: &mut [T], src: &[T], from: usize, to: usize, len: usize) { + debug_assert!(!dst.is_empty()); + debug_assert!(!src.is_empty()); + debug_assert!(from + len <= src.len()); + debug_assert!(to + len <= dst.len()); + let dst_ptr = dst.as_mut_ptr(); + let src_ptr = src.as_ptr(); + if len != 0 { + unsafe { + core::ptr::copy(src_ptr.add(from), dst_ptr.add(to), len); + } + } +} + +/// A simple way to set buffer contents. +/// The compiler does the heavy lifting. +/// see +#[inline] +pub fn buffer_set(data: &mut [T], from: usize, len: usize, val: T) { + debug_assert!(from + len <= data.len()); + for p in &mut data[from..(from + len)] { + *p = val.clone(); + } +} + +/// The max value that will be added or subtracted during add mutations +pub const ARITH_MAX: u64 = 35; + +/// Interesting 8-bit values from AFL +pub const INTERESTING_8: [i8; 9] = [-128, -1, 0, 1, 16, 32, 64, 100, 127]; +/// Interesting 16-bit values from AFL +pub const INTERESTING_16: [i16; 19] = [ + -128, -1, 0, 1, 16, 32, 64, 100, 127, -32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767, +]; +/// Interesting 32-bit values from AFL +pub const INTERESTING_32: [i32; 27] = [ + -128, + -1, + 0, + 1, + 16, + 32, + 64, + 100, + 127, + -32768, + -129, + 128, + 255, + 256, + 512, + 1000, + 1024, + 4096, + 32767, + -2147483648, + -100663046, + -32769, + 32768, + 65535, + 65536, + 100663045, + 2147483647, +]; + +/// Bitflip mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BitFlipMutator; + +impl Mutator for BitFlipMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let bit = 1 << state.rand_mut().choose(0..8); + let byte = state.rand_mut().choose(input.bytes_mut()); + *byte ^= bit; + Ok(MutationResult::Mutated) + } + } +} + +impl Named for BitFlipMutator { + fn name(&self) -> &str { + "BitFlipMutator" + } +} + +impl BitFlipMutator { + /// Creates a new [`BitFlipMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Byteflip mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct ByteFlipMutator; + +impl Mutator for ByteFlipMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + *state.rand_mut().choose(input.bytes_mut()) ^= 0xff; + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteFlipMutator { + fn name(&self) -> &str { + "ByteFlipMutator" + } +} + +impl ByteFlipMutator { + /// Creates a new [`ByteFlipMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Byte increment mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct ByteIncMutator; + +impl Mutator for ByteIncMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let byte = state.rand_mut().choose(input.bytes_mut()); + *byte = byte.wrapping_add(1); + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteIncMutator { + fn name(&self) -> &str { + "ByteIncMutator" + } +} + +impl ByteIncMutator { + /// Creates a new [`ByteIncMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Byte decrement mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct ByteDecMutator; + +impl Mutator for ByteDecMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let byte = state.rand_mut().choose(input.bytes_mut()); + *byte = byte.wrapping_sub(1); + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteDecMutator { + fn name(&self) -> &str { + "ByteDecMutator" + } +} + +impl ByteDecMutator { + /// Creates a a new [`ByteDecMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Byte negate mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct ByteNegMutator; + +impl Mutator for ByteNegMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let byte = state.rand_mut().choose(input.bytes_mut()); + *byte = (!(*byte)).wrapping_add(1); + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteNegMutator { + fn name(&self) -> &str { + "ByteNegMutator" + } +} + +impl ByteNegMutator { + /// Creates a new [`ByteNegMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Byte random mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct ByteRandMutator; + +impl Mutator for ByteRandMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let byte = state.rand_mut().choose(input.bytes_mut()); + *byte = state.rand_mut().next() as u8; + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteRandMutator { + fn name(&self) -> &str { + "ByteRandMutator" + } +} + +impl ByteRandMutator { + /// Creates a new [`ByteRandMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +// Helper macro that defines the arithmetic addition/subtraction mutations where random slices +// within the input are treated as u8, u16, u32, or u64, then mutated in place. +macro_rules! add_mutator_impl { + ($name: ident, $size: ty) => { + /// Adds or subtracts a random value up to `ARITH_MAX` to a [`<$size>`] at a random place in the [`Vec`], in random byte order. + #[derive(Default, Debug)] + pub struct $name; + + #[allow(trivial_numeric_casts)] + impl Mutator for $name + where + S: UsesInput + HasRand, + S::Input: HasBytesVec, + { + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().len() < size_of::<$size>() { + Ok(MutationResult::Skipped) + } else { + // choose a random window of bytes (windows overlap) and convert to $size + let (index, bytes) = state + .rand_mut() + .choose(input.bytes().windows(size_of::<$size>()).enumerate()); + let val = <$size>::from_ne_bytes(bytes.try_into().unwrap()); + + // mutate + let num = 1 + state.rand_mut().below(ARITH_MAX) as $size; + let new_val = match state.rand_mut().below(4) { + 0 => val.wrapping_add(num), + 1 => val.wrapping_sub(num), + 2 => val.swap_bytes().wrapping_add(num).swap_bytes(), + _ => val.swap_bytes().wrapping_sub(num).swap_bytes(), + }; + + // set bytes to mutated value + let new_bytes = &mut input.bytes_mut()[index..index + size_of::<$size>()]; + new_bytes.copy_from_slice(&new_val.to_ne_bytes()); + Ok(MutationResult::Mutated) + } + } + } + + impl Named for $name { + fn name(&self) -> &str { + stringify!($name) + } + } + + impl $name { + /// Creates a new [`$name`]. + #[must_use] + pub fn new() -> Self { + Self + } + } + }; +} + +add_mutator_impl!(ByteAddMutator, u8); +add_mutator_impl!(WordAddMutator, u16); +add_mutator_impl!(DwordAddMutator, u32); +add_mutator_impl!(QwordAddMutator, u64); + +/////////////////////////// + +macro_rules! interesting_mutator_impl { + ($name: ident, $size: ty, $interesting: ident) => { + /// Inserts an interesting value at a random place in the input vector + #[derive(Default, Debug)] + pub struct $name; + + impl Mutator for $name + where + S: UsesInput + HasRand, + S::Input: HasBytesVec, + { + #[allow(clippy::cast_sign_loss)] + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().len() < size_of::<$size>() { + Ok(MutationResult::Skipped) + } else { + let bytes = input.bytes_mut(); + let upper_bound = (bytes.len() + 1 - size_of::<$size>()) as u64; + let idx = state.rand_mut().below(upper_bound) as usize; + let val = *state.rand_mut().choose(&$interesting) as $size; + let new_bytes = match state.rand_mut().choose(&[0, 1]) { + 0 => val.to_be_bytes(), + _ => val.to_le_bytes(), + }; + bytes[idx..idx + size_of::<$size>()].copy_from_slice(&new_bytes); + Ok(MutationResult::Mutated) + } + } + } + + impl Named for $name { + fn name(&self) -> &str { + stringify!($name) + } + } + + impl $name { + /// Creates a new [`$name`]. + #[must_use] + pub fn new() -> Self { + Self + } + } + }; +} + +interesting_mutator_impl!(ByteInterestingMutator, u8, INTERESTING_8); +interesting_mutator_impl!(WordInterestingMutator, u16, INTERESTING_16); +interesting_mutator_impl!(DwordInterestingMutator, u32, INTERESTING_32); + +/// Bytes delete mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesDeleteMutator; + +impl Mutator for BytesDeleteMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size <= 2 { + return Ok(MutationResult::Skipped); + } + + let off = state.rand_mut().below(size as u64) as usize; + let len = state.rand_mut().below((size - off) as u64) as usize; + input.bytes_mut().drain(off..off + len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesDeleteMutator { + fn name(&self) -> &str { + "BytesDeleteMutator" + } +} + +impl BytesDeleteMutator { + /// Creates a new [`BytesDeleteMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes expand mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesExpandMutator; + +impl Mutator for BytesExpandMutator +where + S: UsesInput + HasRand + HasMaxSize, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let max_size = state.max_size(); + let size = input.bytes().len(); + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesExpandMutator { + fn name(&self) -> &str { + "BytesExpandMutator" + } +} + +impl BytesExpandMutator { + /// Creates a new [`BytesExpandMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes insert mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesInsertMutator; + +impl Mutator for BytesInsertMutator +where + S: UsesInput + HasRand + HasMaxSize, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let max_size = state.max_size(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesInsertMutator { + fn name(&self) -> &str { + "BytesInsertMutator" + } +} + +impl BytesInsertMutator { + /// Creates a new [`BytesInsertMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes random insert mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesRandInsertMutator; + +impl Mutator for BytesRandInsertMutator +where + S: UsesInput + HasRand + HasMaxSize, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let max_size = state.max_size(); + let size = input.bytes().len(); + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + let val = state.rand_mut().next() as u8; + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesRandInsertMutator { + fn name(&self) -> &str { + "BytesRandInsertMutator" + } +} + +impl BytesRandInsertMutator { + /// Create a new [`BytesRandInsertMutator`] + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes set mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesSetMutator; + +impl Mutator for BytesSetMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below(size as u64) as usize; + let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; + + let val = *state.rand_mut().choose(input.bytes()); + + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesSetMutator { + fn name(&self) -> &str { + "BytesSetMutator" + } +} + +impl BytesSetMutator { + /// Creates a new [`BytesSetMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes random set mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesRandSetMutator; + +impl Mutator for BytesRandSetMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below(size as u64) as usize; + let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; + + let val = state.rand_mut().next() as u8; + + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesRandSetMutator { + fn name(&self) -> &str { + "BytesRandSetMutator" + } +} + +impl BytesRandSetMutator { + /// Creates a new [`BytesRandSetMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes copy mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesCopyMutator; + +impl Mutator for BytesCopyMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size <= 1 { + return Ok(MutationResult::Skipped); + } + + let from = state.rand_mut().below(input.bytes().len() as u64) as usize; + let to = state.rand_mut().below(input.bytes().len() as u64) as usize; + let len = 1 + state.rand_mut().below((size - max(from, to)) as u64) as usize; + + buffer_self_copy(input.bytes_mut(), from, to, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesCopyMutator { + fn name(&self) -> &str { + "BytesCopyMutator" + } +} + +impl BytesCopyMutator { + /// Creates a new [`BytesCopyMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes insert and self copy mutation for inputs with a bytes vector +#[derive(Debug, Default)] +pub struct BytesInsertCopyMutator { + tmp_buf: Vec, +} + +impl Mutator for BytesInsertCopyMutator +where + S: UsesInput + HasRand + HasMaxSize, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let max_size = state.max_size(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(min(16, size as u64)) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + let from = if size == len { + 0 + } else { + state.rand_mut().below((size - len) as u64) as usize + }; + + input.bytes_mut().resize(size + len, 0); + self.tmp_buf.resize(len, 0); + buffer_copy(&mut self.tmp_buf, input.bytes(), from, 0, len); + + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_copy(input.bytes_mut(), &self.tmp_buf, 0, off, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesInsertCopyMutator { + fn name(&self) -> &str { + "BytesInsertCopyMutator" + } +} + +impl BytesInsertCopyMutator { + /// Creates a new [`BytesInsertCopyMutator`]. + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + +/// Bytes swap mutation for inputs with a bytes vector +#[derive(Debug, Default)] +pub struct BytesSwapMutator; + +impl Mutator for BytesSwapMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size <= 1 { + return Ok(MutationResult::Skipped); + } + + let first = state.rand_mut().below(input.bytes().len() as u64) as usize; + let second = state.rand_mut().below(input.bytes().len() as u64) as usize; + let len = 1 + state.rand_mut().below((size - max(first, second)) as u64) as usize; + + let tmp = input.bytes()[first..(first + len)].to_vec(); + buffer_self_copy(input.bytes_mut(), second, first, len); + buffer_copy(input.bytes_mut(), &tmp, 0, second, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesSwapMutator { + fn name(&self) -> &str { + "BytesSwapMutator" + } +} + +impl BytesSwapMutator { + /// Creates a new [`BytesSwapMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Crossover insert mutation for inputs with a bytes vector +#[derive(Debug, Default)] +pub struct CrossoverInsertMutator; + +impl Mutator for CrossoverInsertMutator +where + S: HasCorpus + HasRand + HasMaxSize, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { + return Ok(MutationResult::Skipped); + } + } + + let other_size = state + .corpus() + .get(idx)? + .borrow_mut() + .load_input()? + .bytes() + .len(); + if other_size < 2 { + return Ok(MutationResult::Skipped); + } + + let max_size = state.max_size(); + let from = state.rand_mut().below(other_size as u64) as usize; + let to = state.rand_mut().below(size as u64) as usize; + let mut len = 1 + state.rand_mut().below((other_size - from) as u64) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), to, to + len, size - to); + buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for CrossoverInsertMutator { + fn name(&self) -> &str { + "CrossoverInsertMutator" + } +} + +impl CrossoverInsertMutator { + /// Creates a new [`CrossoverInsertMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Crossover replace mutation for inputs with a bytes vector +#[derive(Debug, Default)] +pub struct CrossoverReplaceMutator; + +impl Mutator for CrossoverReplaceMutator +where + S: HasCorpus + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { + return Ok(MutationResult::Skipped); + } + } + + let other_size = state + .corpus() + .get(idx)? + .borrow_mut() + .load_input()? + .bytes() + .len(); + if other_size < 2 { + return Ok(MutationResult::Skipped); + } + + let from = state.rand_mut().below(other_size as u64) as usize; + let len = state.rand_mut().below(min(other_size - from, size) as u64) as usize; + let to = state.rand_mut().below((size - len) as u64) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + + buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for CrossoverReplaceMutator { + fn name(&self) -> &str { + "CrossoverReplaceMutator" + } +} + +impl CrossoverReplaceMutator { + /// Creates a new [`CrossoverReplaceMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Returns the first and last diff position between the given vectors, stopping at the min len +fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) { + let mut first_diff: i64 = -1; + let mut last_diff: i64 = -1; + for (i, (this_el, other_el)) in this.iter().zip(other.iter()).enumerate() { + if this_el != other_el { + if first_diff < 0 { + first_diff = i as i64; + } + last_diff = i as i64; + } + } + + (first_diff, last_diff) +} + +/// Splice mutation for inputs with a bytes vector +#[derive(Debug, Default)] +pub struct SpliceMutator; + +impl Mutator for SpliceMutator +where + S: HasCorpus + HasRand, + S::Input: HasBytesVec, +{ + #[allow(clippy::cast_sign_loss)] + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { + return Ok(MutationResult::Skipped); + } + } + + let (first_diff, last_diff) = { + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + + let mut counter: u32 = 0; + loop { + let (f, l) = locate_diffs(input.bytes(), other.bytes()); + + if f != l && f >= 0 && l >= 2 { + break (f as u64, l as u64); + } + if counter == 3 { + return Ok(MutationResult::Skipped); + } + counter += 1; + } + }; + + let split_at = state.rand_mut().between(first_diff, last_diff) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + input + .bytes_mut() + .splice(split_at.., other.bytes()[split_at..].iter().copied()); + + Ok(MutationResult::Mutated) + } +} + +impl Named for SpliceMutator { + fn name(&self) -> &str { + "SpliceMutator" + } +} + +impl SpliceMutator { + /// Creates a new [`SpliceMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +// Converts a hex u8 to its u8 value: 'A' -> 10 etc. +fn from_hex(hex: u8) -> Result { + match hex { + 48..=57 => Ok(hex - 48), + 65..=70 => Ok(hex - 55), + 97..=102 => Ok(hex - 87), + _ => Err(Error::illegal_argument("Invalid hex character".to_owned())), + } +} + +/// Decodes a dictionary token: 'foo\x41\\and\"bar' -> 'fooA\and"bar' +pub fn str_decode(item: &str) -> Result, Error> { + let mut token: Vec = Vec::new(); + let item: Vec = item.as_bytes().to_vec(); + let backslash: u8 = 92; // '\\' + let mut take_next: bool = false; + let mut take_next_two: u32 = 0; + let mut decoded: u8 = 0; + + for c in item { + if take_next_two == 1 { + decoded = from_hex(c)? << 4; + take_next_two = 2; + } else if take_next_two == 2 { + decoded += from_hex(c)?; + token.push(decoded); + take_next_two = 0; + } else if c != backslash || take_next { + if take_next && (c == 120 || c == 88) { + take_next_two = 1; + } else { + token.push(c); + } + take_next = false; + } else { + take_next = true; + } + } + + Ok(token) +} diff --git a/fuzzers/FRET/src/systemstate/mutation/scheduled.rs b/fuzzers/FRET/src/systemstate/mutation/scheduled.rs new file mode 100644 index 0000000000..04c5da3216 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/mutation/scheduled.rs @@ -0,0 +1,421 @@ +//! The `ScheduledMutator` schedules multiple mutations internally. + +extern crate alloc; +use alloc::{string::String, vec::Vec}; +use core::{ + fmt::{self, Debug}, + marker::PhantomData, +}; + +use serde::{Deserialize, Serialize}; + +pub use crate::systemstate::mutation::{ + mutations::*, + Mutator,MutatorsTuple, +}; +use libafl::{ + bolts::{ + rands::Rand, + tuples::{tuple_list, tuple_list_type, NamedTuple}, + AsMutSlice, AsSlice, + }, + corpus::Corpus, + inputs::UsesInput, + mutators::{MutationResult}, + state::{HasCorpus, HasMetadata, HasRand, State}, + Error, prelude::{TokenInsert, TokenReplace, Testcase}, +}; + +/// The metadata placed in a [`crate::corpus::Testcase`] by a [`LoggerScheduledMutator`]. +#[derive(Debug, Serialize, Deserialize)] +pub struct LogMutationMetadata { + /// A list of logs + pub list: Vec, +} + +libafl::impl_serdeany!(LogMutationMetadata); + +impl AsSlice for LogMutationMetadata { + type Entry = String; + #[must_use] + fn as_slice(&self) -> &[String] { + self.list.as_slice() + } +} +impl AsMutSlice for LogMutationMetadata { + type Entry = String; + #[must_use] + fn as_mut_slice(&mut self) -> &mut [String] { + self.list.as_mut_slice() + } +} + +impl LogMutationMetadata { + /// Creates new [`struct@LogMutationMetadata`]. + #[must_use] + pub fn new(list: Vec) -> Self { + Self { list } + } +} + +/// A [`Mutator`] that composes multiple mutations into one. +pub trait ComposedByMutations +where + MT: MutatorsTuple, + S: UsesInput, +{ + /// Get the mutations + fn mutations(&self) -> &MT; + + /// Get the mutations (mutable) + fn mutations_mut(&mut self) -> &mut MT; +} + +/// A [`Mutator`] scheduling multiple [`Mutator`]s for an input. +pub trait ScheduledMutator: ComposedByMutations + Mutator +where + MT: MutatorsTuple, + S: UsesInput, +{ + /// Compute the number of iterations used to apply stacked mutations + fn iterations(&self, state: &mut S, input: &Testcase) -> u64; + + /// Get the next mutation to apply + fn schedule(&self, state: &mut S, input: &Testcase) -> usize; + + /// New default implementation for mutate. + /// Implementations must forward mutate() to this method + fn scheduled_mutate( + &mut self, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result { + let mut r = MutationResult::Skipped; + let num = self.iterations(state, input); + for _ in 0..num { + let idx = self.schedule(state, input); + let outcome = self + .mutations_mut() + .get_and_mutate(idx, state, input, stage_idx)?; + if outcome == MutationResult::Mutated { + r = MutationResult::Mutated; + } + } + Ok(r) + } +} + +/// A [`Mutator`] that schedules one of the embedded mutations on each call. +pub struct StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + mutations: MT, + max_stack_pow: u64, + phantom: PhantomData, +} + +impl Debug for StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "StdScheduledMutator with {} mutations for Input type {}", + self.mutations.len(), + core::any::type_name::() + ) + } +} + +impl Mutator for StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + #[inline] + fn mutate( + &mut self, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result { + self.scheduled_mutate(state, input, stage_idx) + } +} + +impl ComposedByMutations for StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + /// Get the mutations + #[inline] + fn mutations(&self) -> &MT { + &self.mutations + } + + // Get the mutations (mutable) + #[inline] + fn mutations_mut(&mut self) -> &mut MT { + &mut self.mutations + } +} + +impl ScheduledMutator for StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + /// Compute the number of iterations used to apply stacked mutations + fn iterations(&self, state: &mut S, _: &Testcase) -> u64 { + 1 << (1 + state.rand_mut().below(self.max_stack_pow)) + } + + /// Get the next mutation to apply + fn schedule(&self, state: &mut S, _: &Testcase) -> usize { + debug_assert!(!self.mutations().is_empty()); + state.rand_mut().below(self.mutations().len() as u64) as usize + } +} + +impl StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + /// Create a new [`StdScheduledMutator`] instance specifying mutations + pub fn new(mutations: MT) -> Self { + StdScheduledMutator { + mutations, + max_stack_pow: 7, + phantom: PhantomData, + } + } + + /// Create a new [`StdScheduledMutator`] instance specifying mutations and the maximun number of iterations + pub fn with_max_stack_pow(mutations: MT, max_stack_pow: u64) -> Self { + StdScheduledMutator { + mutations, + max_stack_pow, + phantom: PhantomData, + } + } +} + +/// Tuple type of the mutations that compose the Havoc mutator +pub type HavocMutationsType = tuple_list_type!( + BitFlipMutator, + ByteFlipMutator, + ByteIncMutator, + ByteDecMutator, + ByteNegMutator, + ByteRandMutator, + ByteAddMutator, + WordAddMutator, + DwordAddMutator, + QwordAddMutator, + ByteInterestingMutator, + WordInterestingMutator, + DwordInterestingMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesExpandMutator, + BytesInsertMutator, + BytesRandInsertMutator, + BytesSetMutator, + BytesRandSetMutator, + BytesCopyMutator, + BytesInsertCopyMutator, + BytesSwapMutator, + CrossoverInsertMutator, + CrossoverReplaceMutator, +); + +/// Get the mutations that compose the Havoc mutator +#[must_use] +pub fn havoc_mutations() -> HavocMutationsType { + tuple_list!( + BitFlipMutator::new(), + ByteFlipMutator::new(), + ByteIncMutator::new(), + ByteDecMutator::new(), + ByteNegMutator::new(), + ByteRandMutator::new(), + ByteAddMutator::new(), + WordAddMutator::new(), + DwordAddMutator::new(), + QwordAddMutator::new(), + ByteInterestingMutator::new(), + WordInterestingMutator::new(), + DwordInterestingMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesExpandMutator::new(), + BytesInsertMutator::new(), + BytesRandInsertMutator::new(), + BytesSetMutator::new(), + BytesRandSetMutator::new(), + BytesCopyMutator::new(), + BytesInsertCopyMutator::new(), + BytesSwapMutator::new(), + CrossoverInsertMutator::new(), + CrossoverReplaceMutator::new(), + ) +} + +/// 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(),) +} + +/// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`]. +pub struct LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: UsesInput + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + scheduled: SM, + mutation_log: Vec, + phantom: PhantomData<(MT, S)>, +} + +impl Debug for LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: UsesInput + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "LoggerScheduledMutator with {} mutations for Input type {}", + self.scheduled.mutations().len(), + core::any::type_name::<::Input>() + ) + } +} + +impl Mutator for LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: State + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut Testcase<::Input>, + stage_idx: i32, + ) -> Result { + self.scheduled_mutate(state, input, stage_idx) + } + + fn post_exec( + &mut self, + state: &mut S, + _stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + if let Some(idx) = corpus_idx { + let mut testcase = (*state.corpus_mut().get(idx)?).borrow_mut(); + let mut log = Vec::::new(); + while let Some(idx) = self.mutation_log.pop() { + let name = String::from(self.scheduled.mutations().name(idx).unwrap()); // TODO maybe return an Error on None + log.push(name); + } + let meta = LogMutationMetadata::new(log); + testcase.add_metadata(meta); + }; + // Always reset the log for each run + self.mutation_log.clear(); + Ok(()) + } +} + +impl ComposedByMutations for LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: State + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + #[inline] + fn mutations(&self) -> &MT { + self.scheduled.mutations() + } + + #[inline] + fn mutations_mut(&mut self) -> &mut MT { + self.scheduled.mutations_mut() + } +} + +impl ScheduledMutator for LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: State + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + /// Compute the number of iterations used to apply stacked mutations + fn iterations(&self, state: &mut S, _: &Testcase<::Input>) -> u64 { + 1 << (1 + state.rand_mut().below(6)) + } + + /// Get the next mutation to apply + fn schedule(&self, state: &mut S, _: &Testcase<::Input>) -> usize { + debug_assert!(!self.scheduled.mutations().is_empty()); + state + .rand_mut() + .below(self.scheduled.mutations().len() as u64) as usize + } + + fn scheduled_mutate( + &mut self, + state: &mut S, + input: &mut Testcase<::Input>, + stage_idx: i32, + ) -> Result { + let mut r = MutationResult::Skipped; + let num = self.iterations(state, input); + self.mutation_log.clear(); + for _ in 0..num { + let idx = self.schedule(state, input); + self.mutation_log.push(idx); + let outcome = self + .mutations_mut() + .get_and_mutate(idx, state, input, stage_idx)?; + if outcome == MutationResult::Mutated { + r = MutationResult::Mutated; + } + } + Ok(r) + } +} + +impl LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: State + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + /// Create a new [`StdScheduledMutator`] instance without mutations and corpus + pub fn new(scheduled: SM) -> Self { + Self { + scheduled, + mutation_log: vec![], + phantom: PhantomData, + } + } +} + + diff --git a/fuzzers/FRET/src/systemstate/mutators.rs b/fuzzers/FRET/src/systemstate/mutators.rs index 14bc7e40fa..f081ab1b06 100644 --- a/fuzzers/FRET/src/systemstate/mutators.rs +++ b/fuzzers/FRET/src/systemstate/mutators.rs @@ -1,15 +1,19 @@ +use crate::fuzzer::DO_NUM_INTERRUPT; +use crate::systemstate::mutation::Mutator; use crate::systemstate::graph::SysGraphMetadata; use crate::systemstate::graph::SysGraphNode; -use crate::systemstate::IRQ_INPUT_OFFSET; -use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; +// use crate::systemstate::IRQ_INPUT_OFFSET; +// use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; use crate::systemstate::graph::SysGraphFeedbackState; use libafl::inputs::HasBytesVec; use libafl::bolts::rands::RandomSeed; use libafl::bolts::rands::StdRand; -use libafl::mutators::Mutator; +use libafl::prelude::Testcase; use libafl::mutators::MutationResult; +use libafl::prelude::Corpus; use libafl::prelude::UsesInput; use core::marker::PhantomData; +use std::cmp::max; use libafl::state::HasCorpus; use libafl::state::HasSolutions; use libafl::state::HasRand; @@ -20,9 +24,11 @@ use libafl::Error; use libafl::{inputs::Input, state::HasMetadata}; use super::FreeRTOSSystemStateMetadata; +use super::RefinedFreeRTOSSystemState; use libafl::bolts::rands::Rand; +pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4); //=============================== Interrupt /// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata @@ -42,61 +48,136 @@ where } impl Mutator for InterruptShifterMutator where - S: UsesInput, + S: UsesInput + HasRand + HasMetadata + HasCorpus, + S::Input: HasBytesVec, { fn mutate( &mut self, state: &mut S, - input: &mut S::Input, + _input: &mut Testcase, _stage_idx: i32 ) -> Result { // need our own random generator, because borrowing rules let mut myrand = StdRand::new(); - let tmp = &mut state.rand_mut(); - myrand.set_seed(tmp.next()); - drop(tmp); + let mut target_bytes : Vec = vec![]; + { + let input = _input.input_mut().as_ref().unwrap(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + target_bytes = input.bytes().to_vec(); + } - let target_bytes = input.bytes_mut(); let mut target_tick = 0; - #[cfg(feature = "sched_state")] + // produce a slice of absolute interrupt times + let mut interrupt_offsets : [u32; 32] = [0u32; 32]; + let mut num_interrupts : usize = 0; { - let tmp = state.metadata().get::(); - if tmp.is_none() { // if there are no metadata it was probably not interesting anyways - return Ok(MutationResult::Skipped); + let mut start_tick : u32 = 0; + for i in 0..DO_NUM_INTERRUPT { + let mut t : [u8; 4] = [0,0,0,0]; + if target_bytes.len() > (i+1)*4 { + for j in 0 as usize..4 as usize { + t[j]=target_bytes[i*4+j]; + } + if i == 0 { + start_tick = u32::from_le_bytes(t); + } else { + start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); + } + interrupt_offsets[i] = start_tick; + num_interrupts = i+1; + } } - let trace =tmp.expect("FreeRTOSSystemStateMetadata not found"); - let target_block = myrand.choose(trace.inner.iter()); - target_tick = myrand.between(target_block.start_tick,target_block.end_tick)-IRQ_INPUT_OFFSET as u64; } - #[cfg(feature = "sched_state")] - { - let feedbackstate = state - .feedback_states() - .match_name::("SysMap") - .unwrap(); - let g = &feedbackstate.graph; - let tmp = state.metadata().get::(); - if tmp.is_none() { // if there are no metadata it was probably not interesting anyways - return Ok(MutationResult::Skipped); - } - let trace = tmp.expect("SysGraphMetadata not found"); - let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())]; - target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) { - Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64, - None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64, - }; - } - if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 { - for i in 0..IRQ_INPUT_BYTES_NUMBER as usize { - target_bytes[i] = u64::to_le_bytes(target_tick)[i]; + let mut prefix : Vec<[u8; 4]> = vec![]; + let mut suffix : Vec = vec![]; + // #[cfg(feature = "feed_systemtrace")] + { + let tmp = _input.metadata().get::(); + if tmp.is_none() { + return Ok(MutationResult::Skipped); } - return Ok(MutationResult::Mutated); - } else { - return Ok(MutationResult::Skipped); + let trace = tmp.expect("FreeRTOSSystemStateMetadata not found"); + + // calculate hits and identify snippets + let mut last_m = false; + let mut marks : Vec<(&RefinedFreeRTOSSystemState, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler + for i in 0..trace.inner.len() { + let curr = &trace.inner[i]; + let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); + if m { + marks.push((curr, i, 1)); + // println!("1: {}",curr.current_task.task_name); + } else if last_m { + marks.push((curr, i, 2)); + // println!("2: {}",curr.current_task.task_name); + } else { + marks.push((curr, i, 0)); + } + last_m = m; + } + let untouched : Vec = marks.iter().filter_map( + |x| if x.2 == 0 { + Some(x.0.start_tick.try_into().expect("ticks > u32")) + } else { + None + } + ).collect(); + let mut numbers : Vec = vec![]; + for i in 0..num_interrupts { + numbers.push(myrand.choose(untouched.clone().into_iter()).try_into().expect("ticks > u32")); + } + numbers.sort(); + let mut start : u32 = 0; + for i in 0..numbers.len() { + let tmp = numbers[i]; + numbers[i] = numbers[i]-start; + start = tmp; + } + for i in 0..numbers.len() { + prefix.push(u32::to_le_bytes(numbers[i])); + } + suffix = target_bytes[4*num_interrupts..].to_vec(); } + // #[cfg(feature = "sched_state")] + // { + // let feedbackstate = state + // .feedback_states() + // .match_name::("SysMap") + // .unwrap(); + // let g = &feedbackstate.graph; + // let tmp = state.metadata().get::(); + // if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + // return Ok(MutationResult::Skipped); + // } + // let trace = tmp.expect("SysGraphMetadata not found"); + // let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())]; + // target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) { + // Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64, + // None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64, + // }; + + // } + + // calculate ranges, alternative hits + // move snippets + + let mut n = [prefix.concat(), suffix].concat(); + let input = _input.input_mut().as_mut().unwrap(); + input.bytes_mut().clear(); + input.bytes_mut().append(&mut n); + return Ok(MutationResult::Mutated); + // if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 { + // for i in 0..IRQ_INPUT_BYTES_NUMBER as usize { + // target_bytes[i] = u64::to_le_bytes(target_tick)[i]; + // } + // return Ok(MutationResult::Mutated); + // } else { + // return Ok(MutationResult::Skipped); + // } } fn post_exec( diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 4941f45b5b..200f22e0a1 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -1,4 +1,4 @@ -use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; +// use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; use libafl::prelude::{ExitKind, AsSlice}; use libafl::{inputs::HasTargetBytes, prelude::UsesInput}; use libafl::bolts::HasLen; @@ -124,7 +124,7 @@ for mut i in input.drain(..) { start_tick: start_tick, end_tick: i.qemu_tick, ready_list_after: collector, - input_counter: i.input_counter+IRQ_INPUT_BYTES_NUMBER, + input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, last_pc: i.last_pc, }); start_tick=i.qemu_tick;