WIP: deprecate graph and use STG

This commit is contained in:
Alwin Berger 2024-05-20 10:54:43 +02:00
parent 8f652f754c
commit b9d6f41ac6
10 changed files with 312 additions and 420 deletions

View File

@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenuk
edition = "2021"
[features]
default = ["std", "snapshot_restore", "singlecore", "restarting", "feed_systemtrace", "fuzz_int", "no_hash_state" ]
default = ["std", "snapshot_restore", "singlecore", "restarting", "feed_systemtrace", "fuzz_int", "do_hash_notify_state" ]
std = []
snapshot_restore = []
snapshot_fast = [ "snapshot_restore" ]
@ -24,7 +24,7 @@ gensize_1 = [ ]
gensize_10 = [ ]
gensize_100 = [ ]
observer_hitcounts = []
no_hash_state = []
do_hash_notify_state = []
count_iterations = []
run_until_saturation = []

View File

@ -36,6 +36,7 @@ use core::{fmt::Debug, time::Duration};
// use libafl::feedbacks::FeedbackState;
// use libafl::state::HasFeedbackStates;
use std::time::{SystemTime, UNIX_EPOCH};
use std::path::PathBuf;
pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH;
@ -98,16 +99,18 @@ pub struct QemuClockObserver {
name: String,
start_tick: u64,
end_tick: u64,
dump_path: Option<PathBuf>
}
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<PathBuf>) -> 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::<IcHist>();
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::<IcHist>();
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
}
}
}

View File

@ -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<GuestA
for sym in &funcs {
let sym_name = gob.strtab.get_at(sym.st_name);
if let Some(sym_name) = sym_name {
// if ISR_SYMBOLS.contains(&sym_name) {continue;}; // skip select symbols, which correspond to ISR-safe system calls
if let Some(r) = get_function_range(elf, sym_name) {
api_addreses.insert(sym_name.to_string(), r);
}
@ -241,8 +246,9 @@ macro_rules! do_dump_stg {
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c});
println!("Dumping graph to {:?}", &dump_path);
if let Some(md) = $state.named_metadata_map_mut().get_mut::<STGFeedbackState>("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::<usize>(&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);

View File

@ -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<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: Evaluator<E, EM>,
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata,
<Z::State as UsesInput>::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<u8> = vec![];
#[cfg(feature = "feed_systemtrace")]
// #[cfg(feature = "feed_stg")]
{
let tmp = _input.metadata_map().get::<FreeRTOSSystemStateMetadata>();
let feedbackstate = match state
.named_metadata_map_mut()
.get_mut::<STGFeedbackState>("stgfeedbackstate") {
Some(s) => s,
None => {
panic!("STGfeedbackstate not visible")
}
};
let tmp = _input.metadata_map().get::<STGNodeMetadata>();
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<u32> = 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;

View File

@ -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<u64,(u64,u64,usize)>, // encounters,ticks,length
longest: Vec<RefinedFreeRTOSSystemState>,
longest: Vec<ReducedFreeRTOSSystemState>,
}
impl Named for SystemStateFeedbackState
{
@ -57,7 +57,7 @@ impl Named for SystemStateFeedbackState
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct NovelSystemStateFeedback
{
last_trace: Option<Vec<RefinedFreeRTOSSystemState>>,
last_trace: Option<Vec<ReducedFreeRTOSSystemState>>,
// known_traces: HashMap<u64,(u64,usize)>,
}
@ -151,19 +151,19 @@ impl Named for NovelSystemStateFeedback
//=============================
pub fn match_traces(target: &Vec<RefinedFreeRTOSSystemState>, last: &Vec<RefinedFreeRTOSSystemState>) -> bool {
pub fn match_traces(target: &Vec<ReducedFreeRTOSSystemState>, last: &Vec<ReducedFreeRTOSSystemState>) -> 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<String>, last: &Vec<RefinedFreeRTOSSystemState>) -> bool {
pub fn match_traces_name(target: &Vec<String>, last: &Vec<ReducedFreeRTOSSystemState>) -> 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<Vec<RefinedFreeRTOSSystemState>>) -> Self {
Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.0.task_name).collect())}
pub fn new(target: Option<Vec<ReducedFreeRTOSSystemState>>) -> 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<PathBuf>,
dump_metadata: bool,
last_trace: Option<Vec<RefinedFreeRTOSSystemState>>,
last_trace: Option<Vec<ReducedFreeRTOSSystemState>>,
}
impl<S> Feedback<S> for DumpSystraceFeedback
@ -245,7 +245,7 @@ where
{
let observer = observers.match_name::<QemuSystemStateObserver>("systemstate")
.expect("QemuSystemStateObserver not found");
let names : Vec<String> = observer.last_run.iter().map(|x| x.current_task.0.task_name.clone()).collect();
let names : Vec<String> = 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");

View File

@ -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<u8>, // in the end any kind of input are bytes, regardless of type and lifetime
}
impl VariantTuple {
fn from(other: &RefinedFreeRTOSSystemState,input: Vec<u8>) -> 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<u8>) -> 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<VariantTuple>,
}
impl SysGraphNode {
fn from(base: RefinedFreeRTOSSystemState, input: Vec<u8>) -> Self {
fn from(base: ReducedFreeRTOSSystemState, input: Vec<u8>) -> 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<u8>) -> bool {
fn unite_raw(&mut self, other: &ReducedFreeRTOSSystemState, input: &Vec<u8>) -> 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<u8>) -> bool {
if &self.base!=other {return false;}
let interesting =
self.variants.iter().all(|x| x.end_tick-x.start_tick<other.end_tick-other.start_tick) || // longest variant
self.variants.iter().all(|x| x.end_tick-x.start_tick>other.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<other.input_counter); // shortest input
if interesting {
let var = VariantTuple::from(other, input.clone());
self.variants.push(var);
}
return interesting;
fn unite_interesting(&mut self, other: &ReducedFreeRTOSSystemState, input: &Vec<u8>) -> bool {
// if &self.base!=other {return false;}
// let interesting =
// self.variants.iter().all(|x| x.end_tick-x.start_tick<other.end_tick-other.tick) || // longest variant
// self.variants.iter().all(|x| x.end_tick-x.start_tick>other.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<other.input_counter); // shortest input
// if interesting {
// let var = VariantTuple::from(other, input.clone());
// self.variants.push(var);
// }
// return interesting;
todo!()
}
pub fn get_taskname(&self) -> &str {
&self.base.current_task.0.task_name
&self.base.current_task.task_name
}
pub fn get_input_counts(&self) -> Vec<u32> {
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::<SysGraphNode, ()>::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<RefinedFreeRTOSSystemState>, input: &Vec<u8>) {
fn insert(&mut self, list: Vec<ReducedFreeRTOSSystemState>, input: &Vec<u8>) {
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<RefinedFreeRTOSSystemState>], return true if the path was interesting
fn update(&mut self, list: &Vec<RefinedFreeRTOSSystemState>, input: &Vec<u8>) -> (bool, Vec<NodeIndex>) {
fn update(&mut self, list: &Vec<ReducedFreeRTOSSystemState>, input: &Vec<u8>) -> (bool, Vec<NodeIndex>) {
let mut current_index = self.entrypoint;
let mut novel = false;
let mut trace : Vec<NodeIndex> = 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(())

View File

@ -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<u8> = 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<GuestAddr>,
app_range: Range<GuestAddr>,
@ -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<GuestAddr>,
app_range: Range<GuestAddr>,
@ -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<GuestAddr>,Option<Guest
// println!("{:?}",std::str::from_utf8(&current_tcb.pcTaskName));
let critical : void_ptr = freertos::emu_lookup::lookup(emulator, h.critical_addr);
let suspended : void_ptr = freertos::emu_lookup::lookup(emulator, h.scheduler_lock_addr);
let running : void_ptr = freertos::emu_lookup::lookup(emulator, h.scheduler_running_addr);
systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr);
// During ISRs it is only safe to extract structs if they are not currently being modified
if (systemstate.capture_point.0==CaptureEvent::APIStart || systemstate.capture_point.0==CaptureEvent::APIEnd) || (critical == 0 && suspended == 0) {
if systemstate.capture_point.0==CaptureEvent::APIStart || systemstate.capture_point.0==CaptureEvent::APIEnd || (critical == 0 && suspended == 0 ) {
// Extract delay list
let mut target : GuestAddr = h.delay_queue;
target = freertos::emu_lookup::lookup(emulator, target);
@ -339,7 +350,7 @@ where
// println!("API Call Edge {:x} {:x}", src, dest);
return Some(1);
}
} else if !h.app_range.contains(&src) && dest == 0 {
} else if dest == 0 { // !h.app_range.contains(&src) &&
if let Some(_) = in_any_range(&h.api_fn_ranges, src) {
// println!("API Return Edge {:#x}", src);
return Some(2);

View File

@ -182,16 +182,16 @@ impl ReducedFreeRTOSSystemState {
// }
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
struct ExecInterval {
start_tick: u64,
end_tick: u64,
start_state: u64,
end_state: u64,
start_capture: (CaptureEvent, String),
end_capture: (CaptureEvent, String),
level: u8,
pub struct ExecInterval {
pub start_tick: u64,
pub end_tick: u64,
pub start_state: u64,
pub end_state: u64,
pub start_capture: (CaptureEvent, String),
pub end_capture: (CaptureEvent, String),
pub level: u8,
tick_spend_preempted: u64,
abb: Option<AtomicBasicBlock>
pub abb: Option<AtomicBasicBlock>
}
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<RefinedFreeRTOSSystemState> 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<ReducedFreeRTOSSystemState>) -> HashSet<String> {
let mut ret: HashSet<_, _> = HashSet::new();
@ -346,194 +358,4 @@ fn get_task_names(trace: &Vec<ReducedFreeRTOSSystemState>) -> HashSet<String> {
ret
}
// fn extract_abbs_from_trace(trace: &Vec<ReducedFreeRTOSSystemState>) -> HashMap<String,Vec<Rc<AtomicBasicBlock>>> {
// let mut abbs_of_task : HashMap<String, Vec<Rc<AtomicBasicBlock>>> = HashMap::new();
// let mut last_abb_of_task : HashMap<String, usize> = 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<total abb ticks iff abb termiates here>)
// fn trace_to_state_abb(trace: &Vec<ReducedFreeRTOSSystemState>) -> (Vec<(String, Rc<AtomicBasicBlock>, 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<String> = HashSet::new();
// let mut abb_begin_end : HashMap<usize,usize> = HashMap::new();
// let mut last_abb_of_task : HashMap<String, usize> = 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);

View File

@ -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<ReducedFreeRTOSSystemState>, 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<ReducedFreeRTOSSystemState>, 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<ExecInterval>, table: &HashMap<u64, ReducedFreeR
let curr_name = &table[&trace[i].start_state].current_task.task_name;
// let last : Option<&usize> = 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<ExecInterval>, table: &HashMap<u64, ReducedFreeR
// generic isr abb start
CaptureEvent::ISRStart => {
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<ExecInterval>, table: &HashMap<u64, ReducedFreeR
if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) {
assert_eq!(open_abb, None);
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: 0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}})));
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);
task_has_started.insert(curr_name.clone());
} else {
// generic case, continue a preempted block
let last = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).expect(&format!("Continued block with no start {} {} {}", trace[i].start_tick, task_has_started.contains(curr_name),trace[i].level));
let last = 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()})).expect(&format!("Continued block with no start {} {} {}", trace[i].start_tick, task_has_started.contains(curr_name),trace[i].level));
wip_abb_trace.push(wip_abb_trace[*last].clone());
}
},
@ -295,30 +305,31 @@ fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeR
CaptureEvent::APIStart => {
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<ExecInterval>, table: &HashMap<u64, ReducedFreeR
}
/// restore the isr/api begin/end invariant
fn fix_broken_trace(meta: &mut Vec<(u64, CaptureEvent, String, (Option<u32>, Option<u32>))>) {
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<ExecInterval>) {
let mut i = 0;

View File

@ -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<STGNode, STGEdge>,
systemstate_index: HashMap<u64, ReducedFreeRTOSSystemState>,
state_abb_hash_index: HashMap<(u64, u64), NodeIndex>,
stgnode_index: HashMap<u64, NodeIndex>,
entrypoint: NodeIndex,
exit: NodeIndex,
exitpoint: NodeIndex,
// Metadata about aggregated traces. aggegated meaning, order has been removed
worst_observed_per_aggegated_path: HashMap<Vec<AtomicBasicBlock>,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<RefinedFreeRTOSSystemState> to attach as Metadata
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct STGNodeMetadata {
pub inner: Vec<NodeIndex>,
pub intervals: Vec<ExecInterval>,
indices: Vec<usize>,
tcref: isize,
}
impl STGNodeMetadata {
pub fn new(inner: Vec<NodeIndex>, intervals: Vec<ExecInterval>) -> 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<CS> =
MinimizerScheduler<CS, MaxTimeFavFactor<<CS as UsesState>::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<Vec<NodeIndex>>,
last_intervals: Option<Vec<ExecInterval>>,
}
#[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<EdgeIndex>) {
unsafe {
for i in 0..MAX_STG_NUM {
@ -199,74 +270,18 @@ fn set_observer_map(trace : &Vec<EdgeIndex>) {
}
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<ReducedFreeRTOSSystemState>, map: Vec<(String, Rc<AtomicBasicBlock>, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec<NodeIndex>, Vec<EdgeIndex>, 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<ExecInterval>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, fbs: &mut STGFeedbackState) -> (Vec<NodeIndex>, Vec<EdgeIndex>, 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<ExecInterval>) -> Vec<AtomicBasicBlock> {
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<S> Feedback<S> 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<OT>(&mut self, _state: &mut S, _observers: &OT, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
fn append_metadata<OT>(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase<S::Input>) -> 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(())
}