stg try_force_new_branches

This commit is contained in:
Alwin Berger 2024-07-02 09:34:35 +02:00
parent 810ec36115
commit 47724ad1c3

View File

@ -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 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 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 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 libafl::state::HasCurrentTestcase;
use super::stg::{STGEdge, STGNode};
pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC;
// one isn per 2**4 ns // one isn per 2**4 ns
// virtual insn/sec 62500000 = 1/16 GHz // virtual insn/sec 62500000 = 1/16 GHz
@ -53,6 +56,43 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8]) -> Vec<u32> {
//======================= Custom mutator //======================= Custom mutator
fn is_interrupt_handler(graph: &DiGraph<STGNode, STGEdge>, 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<STGNode, STGEdge>, 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<STGNode, STGEdge>, 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<Vec<u32>> {
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 /// The default mutational stage
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct InterruptShiftStage<E, EM, Z> { pub struct InterruptShiftStage<E, EM, Z> {
@ -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"))); prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick as usize, u32::MAX as usize)).try_into().expect("ticks > u32")));
} }
} else { } else {
// let choice = myrand.between(1,100); let choice = myrand.between(1,100);
// if choice <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts if choice <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts
// do_rerun = true; do_rerun = true;
// // let metadata = state.metadata_map(); // let metadata = state.metadata_map();
// let hist = metadata.get::<IcHist>().unwrap(); let hist = metadata.get::<IcHist>().unwrap();
// let maxtick : u64 = hist.1.0; let maxtick : u64 = hist.1.0;
// // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap();
// let mut numbers : Vec<u32> = vec![]; let mut numbers : Vec<u32> = vec![];
// for i in 0..num_interrupts { 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"))); 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 else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases
// let feedbackstate = match state let feedbackstate = match state
// .named_metadata_map_mut() .named_metadata_map()
// .get_mut::<STGFeedbackState>("stgfeedbackstate") { .get::<STGFeedbackState>("stgfeedbackstate") {
// Some(s) => s, Some(s) => s,
// None => { None => {
// panic!("STGfeedbackstate not visible") panic!("STGfeedbackstate not visible")
// } }
// }; };
// let tmp = _input.metadata_map().get::<STGNodeMetadata>(); if let Some(meta) = current_case.metadata_map().get::<STGNodeMetadata>() {
if let Some(t) = try_force_new_branches(&interrupt_offsets, feedbackstate, meta) {
do_rerun = true;
for i in 0..t.len() {
if i<num_interrupts {
interrupt_offsets[i]=t[i];
} else {break;}
}
}
}
// let tmp = current_case.metadata_map().get::<STGNodeMetadata>();
// if tmp.is_some() { // if tmp.is_some() {
// let trace = tmp.expect("STGNodeMetadata not found"); // let trace = tmp.expect("STGNodeMetadata not found");
// let mut node_indices = vec![]; // let mut node_indices = vec![];
// for i in (0..trace.intervals.len()).into_iter() { // for i in (0..trace.intervals.len()).into_iter() {
// if let Some(abb) = &trace.intervals[i].abb { // 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)); // node_indices.push(Some(idx));
// continue; // continue;
// } // }
@ -190,18 +241,17 @@ where
// let not_yet_hit : Vec<_> = alternatives.iter().filter( // 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(); // |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 { // if not_yet_hit.len() > 0 {
// let replacement = &trace.intervals[*myrand.choose(not_yet_hit)]; // let replacement = &trace.intervals[*myrand.choose(not_yet_hit).unwrap()];
// interrupt_offsets[i] = (myrand.between(replacement.start_tick, // interrupt_offsets[i] = (myrand.between(replacement.start_tick as usize,
// replacement.end_tick)).try_into().expect("ticks > u32"); // replacement.end_tick as usize)).try_into().expect("ticks > u32");
// // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); // // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]);
// do_rerun = true; // do_rerun = true;
// break; // break;
// } // }
// } // }
// } // }
// } }
// else { // old version of the alternative search else { // old version of the alternative search
{
let tmp = current_case.metadata_map().get::<STGNodeMetadata>(); let tmp = current_case.metadata_map().get::<STGNodeMetadata>();
if tmp.is_some() { if tmp.is_some() {
let trace = tmp.expect("STGNodeMetadata not found"); let trace = tmp.expect("STGNodeMetadata not found");