From b9d6f41ac654aad6ed58b0b4fdb16fe5c33eebb5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 20 May 2024 10:54:43 +0200 Subject: [PATCH] WIP: deprecate graph and use STG --- fuzzers/FRET/Cargo.toml | 4 +- fuzzers/FRET/src/clock.rs | 43 ++-- fuzzers/FRET/src/fuzzer.rs | 34 ++-- fuzzers/FRET/src/mutational.rs | 37 ++-- fuzzers/FRET/src/systemstate/feedbacks.rs | 22 +- fuzzers/FRET/src/systemstate/graph.rs | 69 ++++--- fuzzers/FRET/src/systemstate/helpers.rs | 21 +- fuzzers/FRET/src/systemstate/mod.rs | 220 ++------------------ fuzzers/FRET/src/systemstate/observers.rs | 45 ++-- fuzzers/FRET/src/systemstate/stg.rs | 237 ++++++++++++---------- 10 files changed, 312 insertions(+), 420 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index df86886d50..93d9b2d32b 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi ", "Dominik Maier } impl QemuClockObserver { /// Creates a new [`QemuClockObserver`] with the given name. #[must_use] - pub fn new(name: &'static str) -> Self { + pub fn new(name: &'static str, dump_path: Option) -> Self { Self { name: name.to_string(), start_tick: 0, end_tick: 0, + dump_path } } @@ -137,36 +140,33 @@ where fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { unsafe { self.end_tick = emu::icount_get_raw() }; - // println!("clock post {}", self.end_tick); - // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); - let metadata =_state.metadata_map_mut(); - let hist = metadata.get_mut::(); - let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); - match hist { - None => { - metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)], - (self.end_tick - self.start_tick, timestamp))); - } - Some(v) => { - v.0.push((self.end_tick - self.start_tick, timestamp)); - if (v.1.0 < self.end_tick-self.start_tick) { - v.1 = (self.end_tick - self.start_tick, timestamp); + if let Some(td) = &self.dump_path { + // println!("clock post {}", self.end_tick); + // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); + let metadata =_state.metadata_map_mut(); + let hist = metadata.get_mut::(); + let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); + match hist { + None => { + metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)], + (self.end_tick - self.start_tick, timestamp))); } - if v.0.len() >= 100 { - if let Ok(td) = env::var("DUMP_TIMES") { + Some(v) => { + v.0.push((self.end_tick - self.start_tick, timestamp)); + if v.1.0 < self.end_tick-self.start_tick { + v.1 = (self.end_tick - self.start_tick, timestamp); + } + if v.0.len() >= 100 { let mut file = OpenOptions::new() .read(true) .write(true) .create(true) .append(true) .open(td).expect("Could not open timedump"); - let newv : Vec<(u64, u128)> = Vec::with_capacity(100); + let newv : Vec<(u64, u128)> = Vec::with_capacity(110); for i in std::mem::replace(&mut v.0, newv).into_iter() { writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } - } else { - // If we don't write out values we don't need to remember them at all - v.0.clear(); } } } @@ -188,6 +188,7 @@ impl Default for QemuClockObserver { name: String::from("clock"), start_tick: 0, end_tick: 0, + dump_path: None } } } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 46bbc24817..9a09377cb2 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -14,19 +14,23 @@ use libafl_qemu::{ }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; use csv::Reader; -use petgraph::dot::{Dot, Config}; +use petgraph::{dot::{Config, Dot}, Graph}; +use petgraph::graph::EdgeIndex; +use petgraph::graph::NodeIndex; +use petgraph::prelude::DiGraph; use crate::systemstate::stg::STGFeedbackState; // Constants ================================================================================ pub static mut RNG_SEED: u64 = 1; -pub static mut LIMIT : u32 = u32::MAX; +pub static mut LIMIT : u32 = u32::MAX>>1; +pub const FIRST_INT : u32 = 500000; pub const MAX_NUM_INTERRUPT: usize = 32; pub const DO_NUM_INTERRUPT: usize = 32; @@ -98,6 +102,7 @@ pub fn get_all_fn_symbol_ranges(elf: &EasyElf, api_range: std::ops::Range("stgfeedbackstate") { - let out = md.graph.map(|i,x| x.pretty_print(), |_,_| ""); - let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); + let out = md.graph.map(|i,x| x.color_print(), |i,x| x.color_print()); + let outs = Dot::with_config(&out, &[]).to_string(); + let outs = outs.replace("\\\"","\""); let outs = outs.replace(';',"\\n"); fs::write(dump_path,outs).expect("Failed to write graph"); } @@ -302,17 +308,10 @@ pub fn fuzz() { let cli = Cli::parse(); env_from_config(&cli.kernel, &cli.config); unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} - if let Some(n) = &cli.dump_name { - if cli.dump_times { - env::set_var("DUMP_TIMES", n.with_extension("time")); - } - } else if cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph { - cli.dump_name.clone().expect("Dump name not give but dump is requested"); + if cli.dump_name.is_none() && (cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph) { + panic!("Dump name not give but dump is requested"); } let mut starttime = std::time::Instant::now(); - if let Ok(s) = env::var("FUZZ_SIZE") { - str::parse::(&s).expect("FUZZ_SIZE was not a number"); - }; // Hardcoded parameters let timeout = Duration::from_secs(10); let broker_port = 1337; @@ -354,6 +353,7 @@ pub fn fuzz() { let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false); let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false); let scheduler_lock = load_symbol(&elf, "uxSchedulerSuspended", false); + let scheduler_running = load_symbol(&elf, "xSchedulerRunning", false); let critical_section = load_symbol(&elf, "uxCriticalNesting", false); // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); #[cfg(feature = "systemstate")] @@ -472,7 +472,7 @@ pub fn fuzz() { t[j]=buf[i*4+j]; } if i == 0 || true { - unsafe {start_tick = u32::from_le_bytes(t) % LIMIT;} + unsafe {start_tick = u32::from_le_bytes(t) % LIMIT + FIRST_INT;} } else { start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); } @@ -529,7 +529,7 @@ pub fn fuzz() { )}; // Create an observation channel to keep track of the execution time - let clock_time_observer = QemuClockObserver::new("clocktime"); + let clock_time_observer = QemuClockObserver::new("clocktime", if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None} ); let systemstate_observer = QemuSystemStateObserver::new(); @@ -629,7 +629,7 @@ pub fn fuzz() { let qhelpers = tuple_list!( QemuEdgeCoverageHelper::default(), QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock, critical_section,input_counter_ptr,app_range.clone()) + QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock,scheduler_running, critical_section,input_counter_ptr,app_range.clone()) ); let mut hooks = QemuHooks::new(emu.clone(),qhelpers); diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 79910106da..0d0ad79e3a 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -9,16 +9,10 @@ use libafl_bolts::rands::{ Rand }; use libafl::{ - corpus::{Corpus, self}, - fuzzer::Evaluator, - mark_feature_time, - stages::{Stage}, - start_timer, - state::{MaybeHasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, - Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, MutationResult, Mutator, CorpusId}, + corpus::{self, Corpus}, fuzzer::Evaluator, mark_feature_time, prelude::{new_hash_feedback, CorpusId, HasBytesVec, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasMetadata, HasNamedMetadata, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; -use crate::{systemstate::{FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist}; +use crate::{clock::IcHist, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4); @@ -48,7 +42,7 @@ where E: UsesState, EM: UsesState, Z: Evaluator, - Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata, + Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, ::Input: HasBytesVec { fn perform( @@ -89,7 +83,7 @@ where t[j]=target_bytes[i*4+j]; } if i == 0 || true { - start_tick = u32::from_le_bytes(t); + start_tick = u32::from_le_bytes(t)+FIRST_INT; } else { start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); } @@ -105,17 +99,26 @@ where 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")] + // #[cfg(feature = "feed_stg")] { - let tmp = _input.metadata_map().get::(); + 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::(); if tmp.is_some() { - let trace = tmp.expect("FreeRTOSSystemStateMetadata not found"); + let trace = tmp.expect("STGNodeMetadata 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 mut marks : Vec<(&ExecInterval, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler + for i in 0..trace.intervals.len() { + let curr = &trace.intervals[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)); @@ -197,7 +200,7 @@ where let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); numbers.sort(); // println!("Mutator: {:?}", numbers); - let mut start : u32 = 0; + // let mut start : u32 = 0; // for i in 0..numbers.len() { // let tmp = numbers[i]; // numbers[i] = numbers[i]-start; diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index c6eea4770e..bc396d5d62 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -20,7 +20,7 @@ use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use serde::{Deserialize, Serialize}; -use super::RefinedFreeRTOSSystemState; +use super::ReducedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSystemStateObserver; use petgraph::prelude::DiGraph; @@ -35,7 +35,7 @@ use std::cmp::Ordering; pub struct SystemStateFeedbackState { known_traces: HashMap, // encounters,ticks,length - longest: Vec, + longest: Vec, } impl Named for SystemStateFeedbackState { @@ -57,7 +57,7 @@ impl Named for SystemStateFeedbackState #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct NovelSystemStateFeedback { - last_trace: Option>, + last_trace: Option>, // known_traces: HashMap, } @@ -151,19 +151,19 @@ impl Named for NovelSystemStateFeedback //============================= -pub fn match_traces(target: &Vec, last: &Vec) -> bool { +pub fn match_traces(target: &Vec, last: &Vec) -> bool { let mut ret = true; if target.len() > last.len() {return false;} for i in 0..target.len() { - ret &= target[i].current_task.0.task_name==last[i].current_task.0.task_name; + ret &= target[i].current_task.task_name==last[i].current_task.task_name; } ret } -pub fn match_traces_name(target: &Vec, last: &Vec) -> bool { +pub fn match_traces_name(target: &Vec, last: &Vec) -> bool { let mut ret = true; if target.len() > last.len() {return false;} for i in 0..target.len() { - ret &= target[i]==last[i].current_task.0.task_name; + ret &= target[i]==last[i].current_task.task_name; } ret } @@ -213,8 +213,8 @@ impl Named for HitSystemStateFeedback } impl HitSystemStateFeedback { - pub fn new(target: Option>) -> Self { - Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.0.task_name).collect())} + pub fn new(target: Option>) -> Self { + Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())} } } //=========================== Debugging Feedback @@ -224,7 +224,7 @@ pub struct DumpSystraceFeedback { dumpfile: Option, dump_metadata: bool, - last_trace: Option>, + last_trace: Option>, } impl Feedback for DumpSystraceFeedback @@ -245,7 +245,7 @@ where { let observer = observers.match_name::("systemstate") .expect("QemuSystemStateObserver not found"); - let names : Vec = observer.last_run.iter().map(|x| x.current_task.0.task_name.clone()).collect(); + let names : Vec = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect(); match &self.dumpfile { Some(s) => { std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file"); diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index de4599b826..2d59be87c4 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -38,7 +38,8 @@ use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use serde::{Deserialize, Serialize}; -use super::RefinedFreeRTOSSystemState; +use super::ExecInterval; +use super::ReducedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSystemStateObserver; use petgraph::prelude::DiGraph; @@ -58,24 +59,25 @@ pub struct VariantTuple pub input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime } impl VariantTuple { - fn from(other: &RefinedFreeRTOSSystemState,input: Vec) -> Self { - VariantTuple{ - start_tick: other.start_tick, - end_tick: other.end_tick, - input_counter: other.input_counter, - input: input, - } + fn from(other: &ReducedFreeRTOSSystemState,input: Vec) -> Self { + // VariantTuple{ + // start_tick: other.tick, + // end_tick: other.end_tick, + // input_counter: other.input_counter, + // input: input, + // } + todo!() } } #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct SysGraphNode { - base: RefinedFreeRTOSSystemState, + base: ReducedFreeRTOSSystemState, pub variants: Vec, } impl SysGraphNode { - fn from(base: RefinedFreeRTOSSystemState, input: Vec) -> Self { + fn from(base: ReducedFreeRTOSSystemState, input: Vec) -> Self { SysGraphNode{variants: vec![VariantTuple::from(&base, input)], base:base } } /// unites the variants of this value with another, draining the other if the bases are equal @@ -86,42 +88,43 @@ impl SysGraphNode { return true; } /// add a Varint from a [`RefinedFreeRTOSSystemState`] - fn unite_raw(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec) -> bool { + fn unite_raw(&mut self, other: &ReducedFreeRTOSSystemState, input: &Vec) -> bool { if &self.base!=other {return false;} self.variants.push(VariantTuple::from(other, input.clone())); self.variants.dedup(); return true; } /// add a Varint from a [`RefinedFreeRTOSSystemState`], if it's interesting - fn unite_interesting(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec) -> bool { - if &self.base!=other {return false;} - let interesting = - self.variants.iter().all(|x| x.end_tick-x.start_tickother.end_tick-other.start_tick) || // shortest variant - self.variants.iter().all(|x| x.input_counter>other.input_counter) || // longest input - self.variants.iter().all(|x| x.input_counter) -> bool { + // if &self.base!=other {return false;} + // let interesting = + // self.variants.iter().all(|x| x.end_tick-x.start_tickother.end_tick-other.tick) || // shortest variant + // self.variants.iter().all(|x| x.input_counter>other.input_counter) || // longest input + // self.variants.iter().all(|x| x.input_counter &str { - &self.base.current_task.0.task_name + &self.base.current_task.task_name } pub fn get_input_counts(&self) -> Vec { self.variants.iter().map(|x| x.input_counter).collect() } pub fn pretty_print(&self) -> String { let mut ret = String::new(); - ret.push_str(&format!("{}#{}",&self.base.current_task.0.task_name,&self.base.current_task.1)); + ret.push_str(&format!("{}",&self.base.current_task.task_name)); ret.push_str(";Rl:"); for i in &self.base.ready_list_after { - ret.push_str(&format!(";{}#{}",i.0.task_name,i.1)); + ret.push_str(&format!(";{}",i.task_name)); } ret.push_str(";Dl:"); for i in &self.base.delay_list_after { - ret.push_str(&format!(";{}#{}",i.0.task_name,i.1)); + ret.push_str(&format!(";{}",i.task_name)); } // println!("{}",ret); ret @@ -185,14 +188,14 @@ impl SysGraphFeedbackState pub fn new() -> Self { let mut graph = DiGraph::::new(); let mut entry = SysGraphNode::default(); - entry.base.current_task.0.task_name="Start".to_string(); + entry.base.current_task.task_name="Start".to_string(); let mut exit = SysGraphNode::default(); - exit.base.current_task.0.task_name="End".to_string(); + exit.base.current_task.task_name="End".to_string(); let entry = graph.add_node(entry); let exit = graph.add_node(exit); Self {graph: graph, entrypoint: entry, exit: exit, name: String::from("SysMap")} } - fn insert(&mut self, list: Vec, input: &Vec) { + fn insert(&mut self, list: Vec, input: &Vec) { let mut current_index = self.entrypoint; for n in list { let mut done = false; @@ -211,7 +214,7 @@ impl SysGraphFeedbackState } } /// Try adding a system state path from a [Vec], return true if the path was interesting - fn update(&mut self, list: &Vec, input: &Vec) -> (bool, Vec) { + fn update(&mut self, list: &Vec, input: &Vec) -> (bool, Vec) { let mut current_index = self.entrypoint; let mut novel = false; let mut trace : Vec = vec![current_index]; @@ -262,9 +265,9 @@ impl SysGraphFeedbackState fn reset(&mut self) -> Result<(), Error> { self.graph.clear(); let mut entry = SysGraphNode::default(); - entry.base.current_task.0.task_name="Start".to_string(); + entry.base.current_task.task_name="Start".to_string(); let mut exit = SysGraphNode::default(); - exit.base.current_task.0.task_name="End".to_string(); + exit.base.current_task.task_name="End".to_string(); self.entrypoint = self.graph.add_node(entry); self.exit = self.graph.add_node(exit); Ok(()) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 4d440fee87..06b88e743c 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -13,7 +13,7 @@ use libafl_qemu::edges::QemuEdgesMapMetadata; use libafl_qemu::emu; use libafl_qemu::hooks; use libafl_qemu::Hook; -use crate::systemstate::extract_abbs_from_trace; +// use crate::systemstate::extract_abbs_from_trace; use crate::systemstate::RawFreeRTOSSystemState; use crate::systemstate::CURRENT_SYSTEMSTATE_VEC; use crate::systemstate::NUM_PRIOS; @@ -41,7 +41,7 @@ pub static mut NEXT_INPUT : Vec = Vec::new(); pub const ISR_SYMBOLS : &'static [&'static str] = &[ // ISRs -"Reset_Handler","Default_Handler","Default_Handler2","Default_Handler3","Default_Handler4","Default_Handler5","Default_Handler6","vPortSVCHandler","xPortPendSVHandler","xPortSysTickHandler","isr_starter" +"Reset_Handler","Default_Handler","Default_Handler2","Default_Handler3","Default_Handler4","Default_Handler5","Default_Handler6","vPortSVCHandler","xPortPendSVHandler","xPortSysTickHandler","isr_starter"//,"vTaskGenericNotifyGiveFromISR" ]; //============================= Qemu Helper @@ -60,6 +60,7 @@ pub struct QemuSystemStateHelper { delay_queue: GuestAddr, delay_queue_overflow: GuestAddr, scheduler_lock_addr: GuestAddr, + scheduler_running_addr: GuestAddr, critical_addr: GuestAddr, input_counter: Option, app_range: Range, @@ -77,6 +78,7 @@ impl QemuSystemStateHelper { delay_queue: GuestAddr, delay_queue_overflow: GuestAddr, scheduler_lock_addr: GuestAddr, + scheduler_running_addr: GuestAddr, critical_addr: GuestAddr, input_counter: Option, app_range: Range, @@ -91,6 +93,7 @@ impl QemuSystemStateHelper { delay_queue, delay_queue_overflow, scheduler_lock_addr, + scheduler_running_addr, critical_addr, input_counter: input_counter, app_range, @@ -162,7 +165,14 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul } let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index); // println!("Item at {}: {:?}",next_index,next_item); - assert_eq!(next_item.pvContainer,target); + if next_item.pvContainer != target { + // the list is being modified, abort by setting the list empty + eprintln!("Warning: attempted to read a list that is being modified"); + let mut read=read; + read.uxNumberOfItems = 0; + return read; + } + // assert_eq!(next_item.pvContainer,target); let new_next_index=next_item.pxNext; let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner); // println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb); @@ -246,10 +256,11 @@ fn trigger_collection(emulator: &Emulator, edge: (Option,Option + pub abb: Option } impl ExecInterval { @@ -219,6 +219,10 @@ impl ExecInterval { self.invaildate(); return true; } + + pub fn get_hash_index(&self) -> (u64, u64) { + return (self.start_state, self.abb.as_ref().expect("ABB not set").get_hash()) + } } // Wrapper around Vec to attach as Metadata @@ -337,6 +341,14 @@ impl Ord for AtomicBasicBlock { } } +impl AtomicBasicBlock { + fn get_hash(&self) -> u64 { + let mut s = DefaultHasher::new(); + self.hash(&mut s); + s.finish() + } +} + fn get_task_names(trace: &Vec) -> HashSet { let mut ret: HashSet<_, _> = HashSet::new(); @@ -346,194 +358,4 @@ fn get_task_names(trace: &Vec) -> HashSet { ret } -// fn extract_abbs_from_trace(trace: &Vec) -> HashMap>> { -// let mut abbs_of_task : HashMap>> = HashMap::new(); -// let mut last_abb_of_task : HashMap = HashMap::new(); -// // iterate over all states and extract atomic basic blocks -// // an atomic base block has a single entry and multiple exits -// // the cuts between blocks are api calls -// // when capture_point is APIEnd, the destination of the edge is the start of an atomic block -// // so the next APIStart with the same current_tcb.pcTaskName is the end of the atomic block -// for i in 0..trace.len() { -// let curr_name = trace[i].current_task.0.task_name.clone(); -// let last : Option<&usize> = last_abb_of_task.get(&curr_name); -// match trace[i].capture_point.0 { -// CaptureEvent::APIStart => { -// // end the last atomic block -// if let Some(&l) = last { -// let start = trace[l].edge.1.unwrap(); -// let end = trace[i].edge.0.unwrap(); -// match abbs_of_task.get_mut(&curr_name) { -// Some(v) => { -// match v.iter_mut().find(|x| x.start==start) { -// Some(abb) => { -// Rc::get_mut(abb).unwrap().ends.insert(end); -// } -// None => { -// let mut t = HashSet::new(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); -// } -// }; -// }, -// None => { -// let mut v = Vec::new(); -// let mut t = HashSet::new(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); -// abbs_of_task.insert(curr_name, v); -// } -// } -// } else { -// // first API call of this task -// let mut v = Vec::new(); -// let mut t = HashSet::new(); -// let end = trace[i].edge.0.unwrap(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); -// abbs_of_task.insert(curr_name, v); -// } -// }, -// CaptureEvent::APIEnd => { -// match last { -// Some(&l) => { -// //assert!(trace[l].capture_point.0 == CaptureEvent::APIStart); -// }, -// None => (), -// } -// last_abb_of_task.insert(curr_name, i); -// }, -// CaptureEvent::ISRStart => { -// }, -// CaptureEvent::ISREnd => { -// }, -// CaptureEvent::End => { -// // end the last atomic block -// if let Some(&l) = last { -// let start = trace[l].edge.1.unwrap(); -// let end = trace[i].edge.0.unwrap(); -// match abbs_of_task.get_mut(&curr_name) { -// Some(v) => { -// match v.iter_mut().find(|x| x.start==start) { -// Some(abb) => { -// Rc::get_mut(abb).unwrap().ends.insert(end); -// } -// None => { -// let mut t = HashSet::new(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); -// } -// }; -// }, -// None => { -// let mut v = Vec::new(); -// let mut t = HashSet::new(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); -// abbs_of_task.insert(curr_name, v); -// } -// } -// } else { -// // first API call of this task -// let mut v = Vec::new(); -// let mut t = HashSet::new(); -// let end = trace[i].edge.0.unwrap(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); -// abbs_of_task.insert(curr_name, v); -// } -// }, -// CaptureEvent::Undefined => { -// }, -// } -// } -// abbs_of_task -// } - -/// returns (name, abb, index, ticks, Option) -// fn trace_to_state_abb(trace: &Vec) -> (Vec<(String, Rc, usize, u64)>, Vec<(AtomicBasicBlock, u64)>) { -// let mut abbs_in_exec_order : Vec<(usize,AtomicBasicBlock,u64)> = vec![]; // indices in trace where an abb ends, along with it's time -// let mut has_started : HashSet = HashSet::new(); -// let mut abb_begin_end : HashMap = HashMap::new(); -// let mut last_abb_of_task : HashMap = HashMap::new(); -// for i in 0..trace.len() { -// let curr_name = trace[i].current_task.0.task_name.clone(); -// let last : Option<&usize> = last_abb_of_task.get(&curr_name); -// match trace[i].capture_point.0 { -// CaptureEvent::APIStart => { -// // end the last atomic block, which began with APIEnd or initial PendSV End -// if let Some(&l) = last { -// // let start = trace[l].edge.1.unwrap(); -// // let end = trace[i].edge.0.unwrap(); -// abb_begin_end.insert(l, i); -// } else { -// panic!("Start an API call with no ABB to terminate"); -// } -// last_abb_of_task.remove(&curr_name); -// }, -// CaptureEvent::APIEnd => { -// // APIEnd means a new ABB begins -// match last { -// Some(&l) => { -// panic!("End an API call with open ABB"); -// }, -// None => (), -// } -// last_abb_of_task.insert(curr_name, i); -// }, -// CaptureEvent::ISRStart => { -// }, -// CaptureEvent::ISREnd => { -// if last.is_none() && trace[i].capture_point.1=="xPortPendSVHandler" && !has_started.contains(&curr_name) { -// // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled -// last_abb_of_task.insert(curr_name.clone(), i); -// has_started.insert(curr_name); -// } -// }, -// CaptureEvent::End => { -// // end the last atomic block -// if let Some(&l) = last { -// abb_begin_end.insert(l, i); -// } else { -// panic!("End without running ABB"); -// } -// last_abb_of_task.remove(&curr_name); -// }, -// CaptureEvent::Undefined => { -// }, -// } -// } -// let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); -// abb_intervals.sort_by_key(|(x,_)| *x); -// let mut chunks = Vec::new(); // (name, abb, index, ticks) -// for &(s,e) in abb_intervals.iter() { -// let curr_name = trace[s].current_task.0.task_name.clone(); -// let start = match trace[s].capture_point.0 { -// CaptureEvent::ISREnd => 0, -// CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), -// _ => panic!(), -// }; -// let end = trace[e].edge.0.unwrap(); -// let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); -// // find intervalls where the abb is actually running, not preempted -// // count up exec time -// let mut sum_of_pieces = 0; -// for i in s..e { -// if trace[i].current_task.0.task_name == curr_name { -// match trace[i].capture_point.0 { -// CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, -// CaptureEvent::ISRStart => (), -// CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, -// _ => panic!(), -// } -// } -// } -// abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); -// } -// abbs_in_exec_order.sort_by_key(|x| x.0); -// let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); -// chunks.sort_by_key(|x| x.2); -// (chunks, abbs_in_exec_order) -// } - libafl_bolts::impl_serdeany!(AtomicBasicBlock); \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 95025334fb..d90fd4c509 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -53,6 +53,7 @@ where // unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} unsafe { let mut temp = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC); + fix_broken_trace(&mut temp.1); self.last_run = temp.0.clone(); // println!("{:?}",temp); let mut temp = states2intervals(temp.0, temp.1); @@ -174,6 +175,9 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt let curr_name = trace[i].current_task.task_name.as_str(); let level = match meta[i].1 { CaptureEvent::APIEnd => { // API end always exits towards the app + if !level_of_task.contains_key(curr_name) { + level_of_task.insert(curr_name, 0); + } *level_of_task.get_mut(curr_name).unwrap()=0; &0 }, @@ -197,14 +201,19 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt } }, CaptureEvent::ISRStart => { + // special case for isrs which do not capture their end + if meta[i].2 == "isr_starter" { + &2 + } else { + // regular case if isr_stack.len() > 0 { let l = isr_stack.back().unwrap(); - isr_stack.push_back(l+1); + isr_stack.push_back(*l); isr_stack.back().unwrap() } else { isr_stack.push_back(2); &2 - } + }} } _ => &100 }; @@ -240,7 +249,8 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap = last_abb_start_of_task.get(&curr_name); - let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); // apps/apis are differentiated by task name, isrs by nested level + let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level + match (&trace[i].start_capture.0, &trace[i].end_capture.0) { // case with abb to block correspondence @@ -265,13 +275,13 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap { assert_eq!(open_abb, None); - open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); }, // generic app abb start CaptureEvent::APIEnd => { assert_eq!(open_abb, None); - open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); }, // generic continued blocks @@ -280,11 +290,11 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap, table: &HashMap { let _t = &wip_abb_trace[i]; RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); }, // generic api abb end CaptureEvent::APIEnd => { RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); }, // generic isr abb end CaptureEvent::ISREnd => { RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); }, // end anything CaptureEvent::End => { RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); }, CaptureEvent::ISRStart => (), _ => panic!("Undefined block end") } } } - // println!("{} {:x}-{:x} {:x} {} {} {:?} {:?}",curr_name, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, &table[&trace[i].end_state].current_task.task_name, trace[i].level, trace[i].start_capture, trace[i].end_capture); + // println!("{} {} {:x}-{:x} {:x}-{:X} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick); } + drop(open_abb_at_this_task_or_level); for i in 0..trace.len() { trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); @@ -412,6 +423,16 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap, Option))>) { + for i in meta.iter_mut() { + if i.1 == CaptureEvent::APIStart && i.2 == "vTaskGenericNotifyGiveFromISR" { + i.1 = CaptureEvent::ISREnd; + i.2 = "isr_starter".to_string(); + } + } +} + /// invalidate subsequent intervals of equal states where an ISREnd follows an ISRStart. If the interrupt had no effect on the system we, are not interested. fn invalidate_ineffective_isr(trace: &mut Vec) { let mut i = 0; diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 409a2c49e7..6037a72d58 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -70,7 +70,24 @@ impl STGNode { pub fn pretty_print(&self) -> String { format!("{}\nl{} {:x}-{:x}\n{}", self.base.current_task.task_name, self.abb.level, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.print_lists()) } - fn calculate_hash(&self) -> u64 { + pub fn color_print(&self) -> String { + let color = match self.abb.level { + 1 => "\", shape=box, style=filled, fillcolor=\"lightblue", + 2 => "\", shape=box, style=filled, fillcolor=\"yellow", + 0 => "\", shape=box, style=filled, fillcolor=\"white", + _ => "\", style=filled, fillcolor=\"lightgray", + }; + let message = match self.abb.level { + 1 => format!("API Call"), + 2 => format!("ISR"), + 0 => format!("Task: {}",self.base.current_task.task_name), + _ => format!(""), + }; + let mut label = format!("{}\nABB: {:x}-{:x}\n{}", message, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.print_lists()); + label.push_str(color); + label + } + fn get_hash(&self) -> u64 { let mut s = DefaultHasher::new(); self.base.hash(&mut s); self.abb.hash(&mut s); @@ -104,6 +121,18 @@ impl STGEdge { short.push_str(&self.name); short } + pub fn color_print(&self) -> String { + let mut short = self.name.clone(); + short.push_str(match self.event { + CaptureEvent::APIStart => "\", color=\"blue", + CaptureEvent::APIEnd => "\", color=\"black", + CaptureEvent::ISRStart => "\", color=red, style=\"dashed", + CaptureEvent::ISREnd => "\", color=red, style=\"solid", + CaptureEvent::End => "", + CaptureEvent::Undefined => "", + }); + short + } } /// Shared Metadata for a systemstateFeedback @@ -113,9 +142,10 @@ pub struct STGFeedbackState // aggregated traces as a graph pub graph: DiGraph, systemstate_index: HashMap, + state_abb_hash_index: HashMap<(u64, u64), NodeIndex>, stgnode_index: HashMap, entrypoint: NodeIndex, - exit: NodeIndex, + exitpoint: NodeIndex, // Metadata about aggregated traces. aggegated meaning, order has been removed worst_observed_per_aggegated_path: HashMap,u64> } @@ -130,21 +160,24 @@ impl Default for STGFeedbackState { let systemstate_index = HashMap::from([(entry.base.get_hash(), entry.base.clone()), (exit.base.get_hash(), exit.base.clone())]); - let h_entry = entry.calculate_hash(); - let h_exit = exit.calculate_hash(); + let h_entry = entry.get_hash(); + let h_exit = exit.get_hash(); - let entrypoint = graph.add_node(entry); - let exit = graph.add_node(exit); + let entrypoint = graph.add_node(entry.clone()); + let exitpoint = graph.add_node(exit.clone()); - let index = HashMap::from([(h_entry, entrypoint), (h_exit, exit)]); + let state_abb_hash_index = HashMap::from([((entry.base.get_hash(), entry.abb.get_hash()), entrypoint), ((exit.base.get_hash(), exit.abb.get_hash()), exitpoint)]); + + let index = HashMap::from([(h_entry, entrypoint), (h_exit, exitpoint)]); STGFeedbackState { graph, stgnode_index: index, entrypoint, - exit, + exitpoint, worst_observed_per_aggegated_path: HashMap::new(), systemstate_index, + state_abb_hash_index } } } @@ -157,6 +190,43 @@ impl Named for STGFeedbackState } } +// Wrapper around Vec to attach as Metadata +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct STGNodeMetadata { + pub inner: Vec, + pub intervals: Vec, + indices: Vec, + tcref: isize, +} +impl STGNodeMetadata { + pub fn new(inner: Vec, intervals: Vec) -> Self{ + Self {indices: inner.iter().map(|x| x.index()).collect(), intervals, inner: inner, tcref: 0} + } +} +impl AsSlice for STGNodeMetadata { + /// Convert the slice of system-states to a slice of hashes over enumerated states + fn as_slice(&self) -> &[usize] { + self.indices.as_slice() + } + + type Entry = usize; +} + +impl HasRefCnt for STGNodeMetadata { + fn refcnt(&self) -> isize { + self.tcref + } + + fn refcnt_mut(&mut self) -> &mut isize { + &mut self.tcref + } +} + +libafl_bolts::impl_serdeany!(STGNodeMetadata); + +pub type GraphMaximizerCorpusScheduler = + MinimizerScheduler::State>,STGNodeMetadata>; + //============================= Graph Feedback pub static mut STG_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; @@ -171,19 +241,20 @@ pub struct StgFeedback { name: String, last_trace: Option>, + last_intervals: Option>, } #[cfg(feature = "feed_stg")] -const INTEREST_EDGE : bool = false; -#[cfg(feature = "feed_stg")] -const INTEREST_NODE : bool = false; -#[cfg(feature = "feed_stg")] -const INTEREST_AGGREGATE : bool = false; -#[cfg(not(feature = "feed_stg"))] const INTEREST_EDGE : bool = true; -#[cfg(not(feature = "feed_stg"))] +#[cfg(feature = "feed_stg")] const INTEREST_NODE : bool = true; -#[cfg(not(feature = "feed_stg"))] +#[cfg(feature = "feed_stg")] const INTEREST_AGGREGATE : bool = true; +#[cfg(not(feature = "feed_stg"))] +const INTEREST_EDGE : bool = false; +#[cfg(not(feature = "feed_stg"))] +const INTEREST_NODE : bool = false; +#[cfg(not(feature = "feed_stg"))] +const INTEREST_AGGREGATE : bool = false; fn set_observer_map(trace : &Vec) { unsafe { for i in 0..MAX_STG_NUM { @@ -199,74 +270,18 @@ fn set_observer_map(trace : &Vec) { } impl StgFeedback { pub fn new() -> Self { - Self {name: String::from("SysMapFeedback"), last_trace: None } + Self {name: String::from("STGFeedback"), last_trace: None, last_intervals: None } } - /// params: - /// tarce of system states - /// list of all atomic basic blocks with asociated state index and ticks - /// produces: - /// tarce of node indexes representing the path trough the graph - /// newly discovered node? - /// side effect: - /// the graph gets new nodes - // fn update_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { - // let mut return_node_trace = vec![fbs.entrypoint]; - // let mut return_edge_trace = vec![]; - // let mut interesting = false; - // // add all missing state+abb combinations to the graph - // for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact - // if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { // - // let node = STGNode {base: state.clone(), abb: (*marker.1).clone()}; - // let h_node = node.calculate_hash(); - // let next_idx = if let Some(idx) = fbs.index.get(&h_node) { - // // alredy present - // *idx - // } else { - // // not present - // let idx = fbs.graph.add_node(node); - // fbs.index.insert(h_node, idx); - // interesting |= INTEREST_NODE; - // idx - // }; - // // connect in graph if edge not present - // let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx); - // if let Some(e_) = e { - // return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_)); - // } else { - // let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, ()); - // return_edge_trace.push(e_); - // interesting |= INTEREST_EDGE; - // } - // return_node_trace.push(next_idx); - // /* - // Ideas: - // Mark edges triggered by interrupts - // Specify path with edges instead of nodes? - // Form a coverage map over edges? - // Sum up execution time per ABB - // */ - // } - // } - // // every path terminates at the end - // if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { - // let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, ()); - // return_edge_trace.push(e_); - // interesting |= INTEREST_EDGE; - // } - // return_node_trace.push(fbs.exit); - // #[cfg(feature = "feed_stg")] - // set_observer_map(&return_edge_trace); - // (return_node_trace, return_edge_trace, interesting) - // } /// params: - /// tarce of system states - /// list of all atomic basic blocks with asociated state index and ticks + /// tarce of intervals + /// hashtable of states + /// feedbackstate /// produces: /// tarce of node indexes representing the path trough the graph /// newly discovered node? /// side effect: - /// the graph gets new nodes + /// the graph gets new nodes and edge fn update_stg_interval(trace: &Vec, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { let mut return_node_trace = vec![fbs.entrypoint]; let mut return_edge_trace = vec![]; @@ -274,14 +289,16 @@ impl StgFeedback { // add all missing state+abb combinations to the graph for (i,interval) in trace.iter().enumerate() { // Iterate intervals let node = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()}; - let h_node = node.calculate_hash(); + let h_node = node.get_hash(); let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) { // alredy present *idx } else { // not present + let h = (node.base.get_hash(), node.abb.get_hash()); let idx = fbs.graph.add_node(node); fbs.stgnode_index.insert(h_node, idx); + fbs.state_abb_hash_index.insert(h, idx); interesting |= INTEREST_NODE; idx }; @@ -304,16 +321,27 @@ impl StgFeedback { */ } // every path terminates at the end - if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { - let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, STGEdge { event: CaptureEvent::End, name: String::from("End") }); + if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exitpoint) { + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exitpoint, STGEdge { event: CaptureEvent::End, name: String::from("End") }); return_edge_trace.push(e_); interesting |= INTEREST_EDGE; } - return_node_trace.push(fbs.exit); + return_node_trace.push(fbs.exitpoint); #[cfg(feature = "feed_stg")] set_observer_map(&return_edge_trace); (return_node_trace, return_edge_trace, interesting) } + + fn abbs_in_exec_order(trace: &Vec) -> Vec { + let mut ret = Vec::new(); + for i in 0..trace.len() { + if trace[i].abb != None && + (trace[i].end_capture.0 == CaptureEvent::APIStart || trace[i].end_capture.0 == CaptureEvent::APIEnd || trace[i].end_capture.0 == CaptureEvent::End || trace[i].end_capture.0 == CaptureEvent::ISREnd) { + ret.push(trace[i].abb.as_ref().unwrap().clone()); + } + } + ret + } } impl Feedback for StgFeedback @@ -349,39 +377,42 @@ where } }; - let (_trace, _, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate); - // let (abbs, ordered) = trace_to_state_abb(&observer.last_run); - // // println!("{:?}",abbs); - // let (_trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); - // if INTEREST_AGGREGATE { - // let (it1, _it2) : (Vec<_>, Vec<_>) = ordered.into_iter().unzip(); - // // aggegation by sorting, order of states is not relevant - // let mut tmp : Vec<_> = it1; - // tmp.sort(); - // if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { - // let t = clock_observer.last_runtime(); - // if t > *x { - // *x = t; - // interesting |= INTEREST_AGGREGATE; - // } - // } else { - // feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); - // interesting |= INTEREST_AGGREGATE; - // } - // } + let (nodetrace, _edgetrace, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate); + + if INTEREST_AGGREGATE { + let mut tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); + // aggegation by sorting, order of states is not relevant + tmp.sort(); + if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { + let t = clock_observer.last_runtime(); + if t > *x { + *x = t; + interesting |= INTEREST_AGGREGATE; + } + } else { + feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); + interesting |= INTEREST_AGGREGATE; + } + } // let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| ""); // let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); // let outs = outs.replace(';',"\\n"); // fs::write("./mystg.dot",outs).expect("Failed to write graph"); + self.last_trace = Some(nodetrace); + self.last_intervals = Some(observer.last_trace.clone()); - // Ok(interesting) Ok(interesting) } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + let a = self.last_trace.take(); + match a { + Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, self.last_intervals.take().unwrap())), + None => (), + } Ok(()) }