From 2889e9bf61e242bb536de1ab57ae9e7f02a4de0b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 20 Apr 2023 15:50:22 +0200 Subject: [PATCH] WIP: move interrupt mutation to new stage --- fuzzers/FRET/src/fuzzer.rs | 13 +- fuzzers/FRET/src/mutational.rs | 211 +++++++++++++++++++++++++++- fuzzers/FRET/src/systemstate/mod.rs | 2 +- 3 files changed, 216 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 1a4e0bf652..1ac0254f6e 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -37,10 +37,11 @@ use rand::{SeedableRng, StdRng, Rng}; use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, qemustate::QemuStateRestoreHelper, - 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}, + systemstate::{mutators::{MINIMUM_INTER_ARRIVAL_TIME}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, + mutational::MyStateStage, + // systemstate::mutation::scheduled::{havoc_mutations, StdScheduledMutator}, + // mutational::StdMutationalStage }; -use crate::mutational::StdMutationalStage; pub static mut RNG_SEED: u64 = 1; @@ -197,7 +198,7 @@ pub fn fuzz() { for j in 0 as usize..4 as usize { t[j]=buf[i*4+j]; } - if i == 0 { + if i == 0 || true { 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))); @@ -355,11 +356,11 @@ 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)); + #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] + let mut stages = tuple_list!(MyStateStage::new(),stages); if env::var("DO_SHOWMAP").is_ok() { let s = &env::var("DO_SHOWMAP").unwrap(); diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index a876979299..6c09036454 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -2,18 +2,19 @@ //! For the current input, it will perform a range of random mutations, and then run them in the executor. use core::marker::PhantomData; +use std::cmp::max; use libafl::{ bolts::rands::Rand, - corpus::Corpus, + corpus::{Corpus, self}, fuzzer::Evaluator, mark_feature_time, stages::{Stage}, start_timer, state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, - Error, + Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult}, }; -use crate::systemstate::mutation::Mutator; +use crate::{systemstate::{mutation::Mutator, mutators::{InterruptShifterMutator, MINIMUM_INTER_ARRIVAL_TIME}, FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT}; // TODO multi mutators stage @@ -162,3 +163,207 @@ where } } + +//======================= Custom mutator + +/// The default mutational stage +#[derive(Clone, Debug, Default)] +pub struct MyStateStage { + #[allow(clippy::type_complexity)] + phantom: PhantomData<(E, EM, Z)>, +} + +impl MyStateStage +where + E: UsesState, + EM: UsesState, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + pub fn new() -> Self { + Self { phantom: PhantomData } + } +} + +impl Stage for MyStateStage +where + E: UsesState, + EM: UsesState, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata, + ::Input: HasBytesVec +{ + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut Self::State, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + let mut mymut : InterruptShifterMutator = InterruptShifterMutator::new(); + let mut _input = state + .corpus() + .get(corpus_idx)? + .borrow_mut().clone(); + let mut newinput = _input.input_mut().as_mut().unwrap().clone(); + let mut tmpinput = _input.input_mut().as_mut().unwrap().clone(); + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + 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(); + } + + // produce a slice of absolute interrupt times + let mut interrupt_offsets : [u32; 32] = [0u32; 32]; + let mut num_interrupts : usize = 0; + { + 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))); + start_tick = u32::from_le_bytes(t); + } + interrupt_offsets[i] = start_tick; + num_interrupts = i+1; + } + } + } + interrupt_offsets.sort(); + + println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); + // let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT); + let mut suffix = target_bytes.split_off(4 * num_interrupts); + let mut prefix : Vec<[u8; 4]> = vec![]; + // let mut suffix : Vec = vec![]; + // #[cfg(feature = "feed_systemtrace")] + { + let tmp = _input.metadata().get::(); + if tmp.is_some() { + 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; + } + for i in 0..num_interrupts { + // bounds based on minimum inter-arrival time + let mut lb = 0; + let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32"); + if i > 0 { + lb = u32::saturating_add(interrupt_offsets[i-1],MINIMUM_INTER_ARRIVAL_TIME); + } + if i < num_interrupts-1 { + ub = u32::saturating_sub(interrupt_offsets[i+1],MINIMUM_INTER_ARRIVAL_TIME); + } + // get old hit and handler + let old_hit = marks.iter().filter( + |x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick + ).next(); + let old_handler = match old_hit { + Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 { + Some(marks[s.1+1]) + } else {None}, + None => None + }; + // find reachable alternatives + let alternatives : Vec<_> = marks.iter().filter(|x| + ( + x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick + || x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick ) + ).collect(); + // in cases there are no alternatives + if alternatives.len() == 0 { + if old_hit.is_none() { + // choose something random + let untouched : Vec<_> = marks.iter().filter( + |x| x.2 == 0 + ).collect(); + let tmp = interrupt_offsets[i]; + let choice = myrand.choose(untouched); + interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) + .try_into().expect("tick > u32"); + println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); + continue; + } else { + // do nothing + println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); + continue; + } + } + let replacement = myrand.choose(alternatives); + if (old_hit.map_or(false, |x| x == replacement)) { + // use the old value + println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); + continue; + } else { + let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { + // move futher back, respect old_handler + old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick) + } else { 0 }; + let tmp = interrupt_offsets[i]; + interrupt_offsets[i] = (myrand.between(replacement.0.start_tick, + replacement.0.end_tick) + extra).try_into().expect("ticks > u32"); + println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); + } + } + let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); + numbers.sort(); + println!("Mutator: {:?}", numbers); + 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])); + } + } + } + + let mut n : Vec = vec![]; + n = [prefix.concat(), suffix].concat(); + newinput.bytes_mut().clear(); + newinput.bytes_mut().append(&mut n); + } + // InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?; + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, newinput)?; + Ok(()) + } +} + +impl UsesState for MyStateStage +where + E: UsesState, + EM: UsesState, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + type State = Z::State; +} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 693e17a8eb..4e9775d7d6 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -126,7 +126,7 @@ impl RefinedFreeRTOSSystemState { // Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { - inner: Vec, + pub inner: Vec, trace_length: usize, indices: Vec, // Hashed enumeration of States tcref: isize,