From 47724ad1c37060cd809851f472a9f9ada5b52a5c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 2 Jul 2024 09:34:35 +0200 Subject: [PATCH] stg try_force_new_branches --- fuzzers/FRET/src/systemstate/mutational.rs | 108 +++++++++++++++------ 1 file changed, 79 insertions(+), 29 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 930d5f9aa2..3909ada36c 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -13,9 +13,12 @@ use libafl::{ common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, CorpusId, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; +use petgraph::{graph::NodeIndex, graph::{self, DiGraph}}; use crate::{time::clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; use libafl::state::HasCurrentTestcase; +use super::stg::{STGEdge, STGNode}; + pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; // one isn per 2**4 ns // virtual insn/sec 62500000 = 1/16 GHz @@ -53,6 +56,43 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8]) -> Vec { //======================= Custom mutator +fn is_interrupt_handler(graph: &DiGraph, node: NodeIndex) -> bool { + graph.edges_directed(node as NodeIndex, petgraph::Direction::Incoming).any(|x| x.weight().event == CaptureEvent::ISRStart) +} + +fn has_interrupt_handler_non_systick(graph: &DiGraph, node: NodeIndex) -> bool { + graph.edges_directed(node as NodeIndex, petgraph::Direction::Outgoing).any(|x| x.weight().event == CaptureEvent::ISRStart && x.weight().name!="xPortSysTickHandler") +} + +fn is_candidate_for_new_branches(graph: &DiGraph, node: NodeIndex) -> bool { + !has_interrupt_handler_non_systick(graph, node) && !is_interrupt_handler(graph, node) +} + +// TODO: thic can be much more efficient, if the graph stored snapshots of the state and input progress was tracked +/// Determines if a given node in the state transition graph (STG) is a candidate for introducing new branches. +pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, meta: &STGNodeMetadata) -> Option> { + let mut new = false; + let mut new_interrupt_times = Vec::new(); + for (num,&interrupt_time) in interrupt_ticks.iter().enumerate() { + let lower_bound = interrupt_time + unsafe{MINIMUM_INTER_ARRIVAL_TIME}; + let next = if interrupt_ticks.len()>num {interrupt_ticks[num+1]} else {u32::MAX}; + for exec_interval in meta.intervals.iter().filter(|x| x.start_tick >= lower_bound as u64 && x.start_tick < next as u64) { + if !(exec_interval.start_capture.0==CaptureEvent::ISRStart) { // shortcut to skip interrupt handers without node lookup + let node_index = fbs.state_abb_hash_index.get(&exec_interval.get_hash_index()).unwrap(); + if !has_interrupt_handler_non_systick(&fbs.graph, node_index.clone()) { + new_interrupt_times.push(lower_bound); + new_interrupt_times.append(interrupt_ticks[num+2..].to_vec().as_mut()); + new=true; + break; + } + } + } + if new {break;} + new_interrupt_times.push(interrupt_time); + } + if new {Some(new_interrupt_times)} else {None} +} + /// The default mutational stage #[derive(Clone, Debug, Default)] pub struct InterruptShiftStage { @@ -130,34 +170,45 @@ where prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick as usize, u32::MAX as usize)).try_into().expect("ticks > u32"))); } } else { - // let choice = myrand.between(1,100); - // if choice <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts - // do_rerun = true; - // // let metadata = state.metadata_map(); - // let hist = metadata.get::().unwrap(); - // let maxtick : u64 = hist.1.0; - // // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); - // let mut numbers : Vec = vec![]; - // for i in 0..num_interrupts { - // prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); - // } - // } - // else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases - // let feedbackstate = match state - // .named_metadata_map_mut() - // .get_mut::("stgfeedbackstate") { - // Some(s) => s, - // None => { - // panic!("STGfeedbackstate not visible") - // } - // }; - // let tmp = _input.metadata_map().get::(); + let choice = myrand.between(1,100); + if choice <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts + do_rerun = true; + // let metadata = state.metadata_map(); + let hist = metadata.get::().unwrap(); + let maxtick : u64 = hist.1.0; + // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); + let mut numbers : Vec = vec![]; + for i in 0..num_interrupts { + prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64) as usize).try_into().expect("ticks > u32"))); + } + } + else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases + let feedbackstate = match state + .named_metadata_map() + .get::("stgfeedbackstate") { + Some(s) => s, + None => { + panic!("STGfeedbackstate not visible") + } + }; + if let Some(meta) = current_case.metadata_map().get::() { + if let Some(t) = try_force_new_branches(&interrupt_offsets, feedbackstate, meta) { + do_rerun = true; + for i in 0..t.len() { + if i(); // if tmp.is_some() { // let trace = tmp.expect("STGNodeMetadata not found"); // let mut node_indices = vec![]; // for i in (0..trace.intervals.len()).into_iter() { // if let Some(abb) = &trace.intervals[i].abb { - // if let Some(idx) = feedbackstate.state_abb_hash_index.get(&(abb.get_hash(), trace.intervals[i].start_state)) { + // if let Some(idx) = feedbackstate.state_abb_hash_index.get(&(trace.intervals[i].start_state,abb.get_hash())) { // node_indices.push(Some(idx)); // continue; // } @@ -190,18 +241,17 @@ where // let not_yet_hit : Vec<_> = alternatives.iter().filter( // |x| feedbackstate.graph.edges_directed(*node_indices[**x].unwrap(), petgraph::Direction::Outgoing).any(|y| y.weight().event != CaptureEvent::ISRStart)).collect(); // if not_yet_hit.len() > 0 { - // let replacement = &trace.intervals[*myrand.choose(not_yet_hit)]; - // interrupt_offsets[i] = (myrand.between(replacement.start_tick, - // replacement.end_tick)).try_into().expect("ticks > u32"); + // let replacement = &trace.intervals[*myrand.choose(not_yet_hit).unwrap()]; + // interrupt_offsets[i] = (myrand.between(replacement.start_tick as usize, + // replacement.end_tick as usize)).try_into().expect("ticks > u32"); // // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); // do_rerun = true; // break; // } // } // } - // } - // else { // old version of the alternative search - { + } + else { // old version of the alternative search let tmp = current_case.metadata_map().get::(); if tmp.is_some() { let trace = tmp.expect("STGNodeMetadata not found");