change interrupt injection timing, stg scheduler

This commit is contained in:
Alwin Berger 2024-05-22 13:52:00 +02:00
parent 5342812cf7
commit c533b7e184
5 changed files with 343 additions and 171 deletions

View File

@ -22,6 +22,10 @@ do_hash_notify_state = []
trace_stg = [ "observe_systemstate" ]
# feedbacks
feed_stg = [ "trace_stg", "observe_systemstate" ]
# feed_stg_edge = [ "feed_stg"]
feed_stg_pathhash = [ "feed_stg"]
feed_stg_abbhash = [ "feed_stg"]
feed_stg_aggregatehash = [ "feed_stg"]
feed_longest = [ ]
feed_afl = [ "observe_edges" ]
feed_genetic = []
@ -32,6 +36,10 @@ gensize_100 = [ ]
sched_genetic = []
sched_afl = []
sched_stg = []
# sched_stg_edge = ['sched_stg'] # every edge in the stg
sched_stg_pathhash = ['sched_stg'] # every path in the stg
sched_stg_abbhash = ['sched_stg'] # every path of abbs
sched_stg_aggregatehash = ['sched_stg'] # every aggregated path (order independent)
# overall_configs
config_genetic = ["gensize_100","feed_genetic","sched_genetic"]
config_afl = ["feed_afl","sched_afl","observe_hitcounts"]

View File

@ -2,7 +2,7 @@ import csv
import os
def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,do_hash_notify_state"
remote="timedump_253048_1873f6_all/"
RUNTIME=7600
RUNTIME=1800
TARGET_REPS_A=2
TARGET_REPS_B=2
NUM_NODES=2
@ -244,6 +244,47 @@ rule all_new:
expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2', 'interact'],num=range(0,2)),
expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random_int', 'feedgeneration100_int', 'frafl_int', 'stg_int'], target=['waters_int', 'watersv2_int', 'interact_int'],num=range(0,3))
rule build_stgpath:
output:
directory("bins/target_stgpath_int")
shell:
"cargo build --target-dir {output} {def_flags},config_stg,sched_stg_pathhash,feed_stg_pathhash"
rule build_stgabb:
output:
directory("bins/target_stgabb_int")
shell:
"cargo build --target-dir {output} {def_flags},config_stg,sched_stg_abbhash,feed_stg_abbhash"
rule build_stgaggregate:
output:
directory("bins/target_stgaggregate_int")
shell:
"cargo build --target-dir {output} {def_flags},config_stg,sched_stg_aggregatehash,feed_stg_aggregatehash"
rule build_stgpath_int:
output:
directory("bins/target_stgpath_int")
shell:
"cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_pathhash,feed_stg_pathhash"
rule build_stgabb_int:
output:
directory("bins/target_stgabb_int")
shell:
"cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_abbhash,feed_stg_abbhash"
rule build_stgaggregate_int:
output:
directory("bins/target_stgaggregate_int")
shell:
"cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_aggregatehash,feed_stg_aggregatehash"
rule custom_test:
input:
expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['stgpath', 'stgabb', 'stgaggregate'], target=['waters','watersv2','interact'],num=range(0,2)),
expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['stgpath_int', 'stgabb_int', 'stgaggregate_int'], target=['waters_int','watersv2_int','interact_int'],num=range(0,2)),
rule all_bins:
input:

View File

@ -14,7 +14,7 @@ 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}, stg::{GraphMaximizerCorpusScheduler}, 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}
clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, 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};
@ -29,7 +29,7 @@ use crate::systemstate::stg::STGFeedbackState;
pub static mut RNG_SEED: u64 = 1;
pub static mut LIMIT : u32 = u32::MAX>>1;
pub static mut LIMIT : u32 = u32::MAX;
pub const FIRST_INT : u32 = 500000;
pub const MAX_NUM_INTERRUPT: usize = 32;
@ -439,22 +439,12 @@ pub fn fuzz() {
unsafe {
#[cfg(feature = "fuzz_int")]
{
let mut start_tick : u32 = 0;
for i in 0..DO_NUM_INTERRUPT {
let mut t : [u8; 4] = [0,0,0,0];
if len > (i+1)*4 {
for j in 0 as usize..4 as usize {
t[j]=buf[i*4+j];
}
if i == 0 || true {
unsafe {start_tick = u32::from_le_bytes(t) % LIMIT + FIRST_INT;}
} else {
start_tick = u32::saturating_add(start_tick,max(unsafe{MINIMUM_INTER_ARRIVAL_TIME},u32::from_le_bytes(t)));
}
libafl_interrupt_offsets[i] = start_tick;
libafl_num_interrupts = i+1;
}
let times = input_bytes_to_interrupt_times(buf);
for (i,t) in times.iter().enumerate() {
libafl_interrupt_offsets[i] = *t;
}
libafl_num_interrupts = times.len();
if buf.len() > libafl_num_interrupts*4 {
buf = &buf[libafl_num_interrupts*4..];
len = buf.len();
@ -500,7 +490,7 @@ pub fn fuzz() {
#[cfg(feature = "observer_hitcounts")]
let edges_observer = HitcountsMapObserver::new(edges_observer);
#[cfg(feature = "feed_stg")]
#[cfg(feature = "observe_systemstate")]
let stg_coverage_observer = unsafe { VariableMapObserver::from_mut_slice(
"stg",
stg_map_mut_slice(),
@ -542,9 +532,9 @@ pub fn fuzz() {
#[cfg(feature = "trace_stg")]
let mut feedback = feedback_or!(
feedback,
StgFeedback::default()
StgFeedback::new(if cli.dump_graph {cli.dump_name.clone()} else {None})
);
#[cfg(feature = "feed_stg")]
#[cfg(feature = "feed_stg_edge")]
let mut feedback = feedback_or!(
feedback,
MaxMapFeedback::tracking(&stg_coverage_observer, true, true)
@ -642,7 +632,7 @@ pub fn fuzz() {
if let Some(se) = seed {
unsafe {
let mut rng = StdRng::seed_from_u64(se);
for i in 0..20 {
for i in 0..1000 {
let inp = BytesInput::new(vec![rng.gen::<u8>(); MAX_INPUT_SIZE]);
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap();
}

View File

@ -20,6 +20,33 @@ pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*ms*/ * 62500;
// 1ms = 62500 insn
// 1us = 62.5 insn
pub fn input_bytes_to_interrupt_times(buf: &[u8]) -> Vec<u32> {
let len = buf.len();
let mut start_tick : u32 = 0;
let mut ret = Vec::with_capacity(DO_NUM_INTERRUPT);
for i in 0..DO_NUM_INTERRUPT {
let mut t : [u8; 4] = [0,0,0,0];
if len > (i+1)*4 {
for j in 0usize..4usize {
t[j]=buf[i*4+j];
}
unsafe {start_tick = max(u32::from_le_bytes(t), FIRST_INT);}
ret.push(start_tick);
} else {break;}
}
ret.sort_unstable();
// obey the minimum inter arrival time while maintaining the sort
for i in 0..ret.len()-1 {
for j in i+1..ret.len()-1 {
if ret[j]-ret[i] < unsafe{MINIMUM_INTER_ARRIVAL_TIME} {
ret[j] = u32::saturating_add(ret[i],unsafe{MINIMUM_INTER_ARRIVAL_TIME});
} else {break;}
}
}
ret
}
//======================= Custom mutator
/// The default mutational stage
@ -64,39 +91,29 @@ where
let mut newinput = _input.input_mut().as_mut().unwrap().clone();
// let mut tmpinput = _input.input_mut().as_mut().unwrap().clone();
let mut do_rerun = false;
if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time
{
// need our own random generator, because borrowing rules
let mut myrand = StdRand::new();
let mut target_bytes : Vec<u8> = vec![];
{
let input = _input.input_mut().as_ref().unwrap();
target_bytes = input.bytes().to_vec();
let tmp = &mut state.rand_mut();
myrand.set_seed(tmp.next());
target_bytes = input.bytes().to_vec();
}
// produce a slice of absolute interrupt times
let mut interrupt_offsets : [u32; 32] = [0u32; 32];
let mut interrupt_offsets : [u32; 32] = [u32::MAX; 32];
let mut num_interrupts : usize = 0;
{
let mut start_tick : u32 = 0;
for i in 0..DO_NUM_INTERRUPT {
let mut t : [u8; 4] = [0,0,0,0];
if target_bytes.len() > (i+1)*4 {
for j in 0 as usize..4 as usize {
t[j]=target_bytes[i*4+j];
}
if i == 0 || true {
start_tick = u32::saturating_add(u32::from_le_bytes(t),FIRST_INT);
} else {
start_tick = u32::saturating_add(start_tick,max(unsafe{MINIMUM_INTER_ARRIVAL_TIME},u32::from_le_bytes(t)));
}
interrupt_offsets[i] = start_tick;
num_interrupts = i+1;
}
let times = input_bytes_to_interrupt_times(&target_bytes);
for (i,t) in times.iter().enumerate() {
interrupt_offsets[i] = *t;
}
num_interrupts = times.len();
}
interrupt_offsets.sort();
interrupt_offsets.sort_unstable();
// println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec());
// let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT);
@ -105,125 +122,140 @@ where
// let mut suffix : Vec<u8> = vec![];
#[cfg(feature = "trace_stg")]
{
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("STGNodeMetadata not found");
// calculate hits and identify snippets
let mut last_m = false;
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));
// println!("1: {}",curr.current_task.0.task_name);
} else if last_m {
marks.push((curr, i, 2));
// println!("2: {}",curr.current_task.0.task_name);
} else {
marks.push((curr, i, 0));
if myrand.between(1,100) <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts
do_rerun = true;
let metadata = state.metadata_map();
let hist = metadata.get::<IcHist>().unwrap();
let maxtick : u64 = hist.1.0;
// let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap();
let mut numbers : Vec<u32> = vec![];
for i in 0..num_interrupts {
prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32")));
}
last_m = m;
}
for i in 0..num_interrupts {
// bounds based on minimum inter-arrival time
let mut lb = 0;
let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32");
if i > 0 {
lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME});
}
if i < num_interrupts-1 {
ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME});
}
// get old hit and handler
let old_hit = marks.iter().filter(
|x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick
).next();
let old_handler = match old_hit {
Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 {
Some(marks[s.1+1])
} else {None},
None => None
};
// find reachable alternatives
let alternatives : Vec<_> = marks.iter().filter(|x|
x.2 != 2 &&
(
x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick
|| x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick )
).collect();
// in cases there are no alternatives
if alternatives.len() == 0 {
if old_hit.is_none() {
// choose something random
let untouched : Vec<_> = marks.iter().filter(
|x| x.2 == 0
).collect();
if untouched.len() > 0 {
let tmp = interrupt_offsets[i];
let choice = myrand.choose(untouched);
interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick)
.try_into().expect("tick > u32");
do_rerun = true;
} else {
let feedbackstate = match state
.named_metadata_map_mut()
.get_mut::<STGFeedbackState>("stgfeedbackstate") {
Some(s) => s,
None => {
panic!("STGfeedbackstate not visible")
}
// println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]);
};
let tmp = _input.metadata_map().get::<STGNodeMetadata>();
if tmp.is_some() {
let trace = tmp.expect("STGNodeMetadata not found");
// calculate hits and identify snippets
let mut last_m = false;
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));
// println!("1: {}",curr.current_task.0.task_name);
} else if last_m {
marks.push((curr, i, 2));
// println!("2: {}",curr.current_task.0.task_name);
} else {
marks.push((curr, i, 0));
}
last_m = m;
}
for i in 0..num_interrupts {
// bounds based on minimum inter-arrival time
let mut lb = 0;
let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32");
if i > 0 {
lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME});
}
if i < num_interrupts-1 {
ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME});
}
// get old hit and handler
let old_hit = marks.iter().filter(
|x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick
).next();
let old_handler = match old_hit {
Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 {
Some(marks[s.1+1])
} else {None},
None => None
};
// find reachable alternatives
let alternatives : Vec<_> = marks.iter().filter(|x|
x.2 != 2 &&
(
x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick
|| x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick )
).collect();
// in cases there are no alternatives
if alternatives.len() == 0 {
if old_hit.is_none() {
// choose something random
let untouched : Vec<_> = marks.iter().filter(
|x| x.2 == 0
).collect();
if untouched.len() > 0 {
let tmp = interrupt_offsets[i];
let choice = myrand.choose(untouched);
interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick)
.try_into().expect("tick > u32");
do_rerun = true;
}
// println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]);
continue;
} else {
// do nothing
// println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]);
continue;
}
}
let replacement = myrand.choose(alternatives);
if (old_hit.map_or(false, |x| x == replacement)) {
// use the old value
// println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]);
continue;
} else {
// do nothing
// println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]);
continue;
let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) {
// move futher back, respect old_handler
old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick)
} else { 0 };
let tmp = interrupt_offsets[i];
interrupt_offsets[i] = (myrand.between(replacement.0.start_tick,
replacement.0.end_tick) + extra).try_into().expect("ticks > u32");
// println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]);
do_rerun = true;
}
}
let replacement = myrand.choose(alternatives);
if (old_hit.map_or(false, |x| x == replacement)) {
// use the old value
// println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]);
continue;
} else {
let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) {
// move futher back, respect old_handler
old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick)
} else { 0 };
let tmp = interrupt_offsets[i];
interrupt_offsets[i] = (myrand.between(replacement.0.start_tick,
replacement.0.end_tick) + extra).try_into().expect("ticks > u32");
// println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]);
do_rerun = true;
let mut numbers : Vec<u32> = interrupt_offsets[0..num_interrupts].to_vec();
numbers.sort();
// println!("Mutator: {:?}", numbers);
// let mut start : u32 = 0;
// for i in 0..numbers.len() {
// let tmp = numbers[i];
// numbers[i] = numbers[i]-start;
// start = tmp;
// }
for i in 0..numbers.len() {
prefix.push(u32::to_le_bytes(numbers[i]));
}
}
}
let mut numbers : Vec<u32> = interrupt_offsets[0..num_interrupts].to_vec();
numbers.sort();
// println!("Mutator: {:?}", numbers);
// let mut start : u32 = 0;
// for i in 0..numbers.len() {
// let tmp = numbers[i];
// numbers[i] = numbers[i]-start;
// start = tmp;
// }
for i in 0..numbers.len() {
prefix.push(u32::to_le_bytes(numbers[i]));
}
}
}
#[cfg(not(feature = "trace_stg"))]
{
let metadata = state.metadata_map();
let hist = metadata.get::<IcHist>().unwrap();
let maxtick : u64 = hist.1.0;
// let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap();
let mut numbers : Vec<u32> = vec![];
for i in 0..num_interrupts {
prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32")));
if myrand.between(1,100) <= 25 { // we have no hint if interrupt times will change anything
do_rerun = true;
let metadata = state.metadata_map();
let hist = metadata.get::<IcHist>().unwrap();
let maxtick : u64 = hist.1.0;
// let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap();
let mut numbers : Vec<u32> = vec![];
for i in 0..num_interrupts {
prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32")));
}
}
}

View File

@ -59,6 +59,10 @@ use std::rc::Rc;
use libafl_bolts::rands::Rand;
use crate::clock::FUZZ_START_TIMESTAMP;
use std::time::SystemTime;
use std::{fs::OpenOptions, io::Write};
//============================= Data Structures
#[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)]
pub struct STGNode
@ -147,7 +151,9 @@ pub struct STGFeedbackState
entrypoint: NodeIndex,
exitpoint: NodeIndex,
// Metadata about aggregated traces. aggegated meaning, order has been removed
worst_observed_per_aggegated_path: HashMap<Vec<AtomicBasicBlock>,u64>
worst_observed_per_aggegated_path: HashMap<Vec<AtomicBasicBlock>,u64>,
worst_observed_per_abb_path: HashMap<u64,u64>,
worst_observed_per_stg_path: HashMap<u64,u64>
}
impl Default for STGFeedbackState {
@ -176,6 +182,8 @@ impl Default for STGFeedbackState {
entrypoint,
exitpoint,
worst_observed_per_aggegated_path: HashMap::new(),
worst_observed_per_abb_path: HashMap::new(),
worst_observed_per_stg_path: HashMap::new(),
systemstate_index,
state_abb_hash_index
}
@ -195,16 +203,35 @@ impl Named for STGFeedbackState
pub struct STGNodeMetadata {
pub nodes: Vec<NodeIndex>,
pub edges: Vec<EdgeIndex>,
pub abbs: Vec<AtomicBasicBlock>,
pub intervals: Vec<ExecInterval>,
indices: Vec<usize>,
tcref: isize,
}
impl STGNodeMetadata {
pub fn new(nodes: Vec<NodeIndex>, edges: Vec<EdgeIndex>, intervals: Vec<ExecInterval>) -> Self{
let mut indices : Vec<_> = edges.iter().map(|x| x.index()).collect();
indices.sort_unstable();
indices.dedup();
Self {indices, intervals, nodes, edges, tcref: 0}
pub fn new(nodes: Vec<NodeIndex>, edges: Vec<EdgeIndex>, abbs: Vec<AtomicBasicBlock>, intervals: Vec<ExecInterval>) -> Self{
let mut indices : Vec<_> = vec![];
#[cfg(all(feature = "sched_stg",not(any(feature = "sched_stg_pathhash",feature = "sched_stg_abbhash",feature = "sched_stg_aggregatehash"))))]
{
indices = edges.iter().map(|x| x.index()).collect();
indices.sort_unstable();
indices.dedup();
}
#[cfg(feature = "sched_stg_pathhash")]
{
indices.push(get_generic_hash(&edges) as usize);
}
#[cfg(feature = "sched_stg_abbhash")]
{
indices.push(get_generic_hash(&abbs) as usize);
}
#[cfg(feature = "sched_stg_aggregatehash")]
{
let mut _abbs = abbs.clone();
_abbs.sort_unstable();
indices.push(get_generic_hash(&_abbs) as usize);
}
Self {indices, intervals, nodes, abbs, edges, tcref: 0}
}
}
impl AsSlice for STGNodeMetadata {
@ -247,18 +274,29 @@ pub struct StgFeedback
last_node_trace: Option<Vec<NodeIndex>>,
last_edge_trace: Option<Vec<EdgeIndex>>,
last_intervals: Option<Vec<ExecInterval>>,
last_abbs: Option<Vec<AtomicBasicBlock>>,
dump_path: Option<PathBuf>
}
#[cfg(feature = "feed_stg")]
const INTEREST_EDGE : bool = true;
#[cfg(feature = "feed_stg")]
const INTEREST_NODE : bool = true;
#[cfg(feature = "feed_stg")]
#[cfg(feature = "feed_stg_pathhash")]
const INTEREST_PATH : bool = true;
#[cfg(feature = "feed_stg_abbhash")]
const INTEREST_ABBPATH : bool = true;
#[cfg(feature = "feed_stg_aggregatehash")]
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"))]
#[cfg(not(feature = "feed_stg_pathhash"))]
const INTEREST_PATH : bool = false;
#[cfg(not(feature = "feed_stg_abbhash"))]
const INTEREST_ABBPATH : bool = false;
#[cfg(not(feature = "feed_stg_aggregatehash"))]
const INTEREST_AGGREGATE : bool = false;
fn set_observer_map(trace : &Vec<EdgeIndex>) {
unsafe {
@ -273,9 +311,22 @@ fn set_observer_map(trace : &Vec<EdgeIndex>) {
}
}
}
fn get_generic_hash<H>(input: &H) -> u64
where
H: Hash,
{
let mut s = DefaultHasher::new();
input.hash(&mut s);
s.finish()
}
impl StgFeedback {
pub fn new() -> Self {
Self {name: String::from("STGFeedback"), last_node_trace: None, last_edge_trace: None, last_intervals: None }
pub fn new(dump_name: Option<PathBuf>) -> Self {
// Self {name: String::from("STGFeedback"), last_node_trace: None, last_edge_trace: None, last_intervals: None }
let mut s = Self::default();
s.dump_path = dump_name.map(|x| x.with_extension("stgsize"));
s
}
/// params:
@ -287,10 +338,11 @@ impl StgFeedback {
/// newly discovered node?
/// side effect:
/// 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) {
fn update_stg_interval(trace: &Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, fbs: &mut STGFeedbackState) -> (Vec<NodeIndex>, Vec<EdgeIndex>, bool, bool) {
let mut return_node_trace = vec![fbs.entrypoint];
let mut return_edge_trace = vec![];
let mut interesting = false;
let mut updated = false;
// 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()};
@ -305,6 +357,7 @@ impl StgFeedback {
fbs.stgnode_index.insert(h_node, idx);
fbs.state_abb_hash_index.insert(h, idx);
interesting |= INTEREST_NODE;
updated = true;
idx
};
// connect in graph if edge not present
@ -315,6 +368,7 @@ impl StgFeedback {
let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, STGEdge{event: interval.start_capture.0, name: interval.start_capture.1.clone()});
return_edge_trace.push(e_);
interesting |= INTEREST_EDGE;
updated = true;
}
return_node_trace.push(next_idx);
/*
@ -330,11 +384,12 @@ impl StgFeedback {
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;
updated = true;
}
return_node_trace.push(fbs.exitpoint);
#[cfg(feature = "feed_stg")]
set_observer_map(&return_edge_trace);
(return_node_trace, return_edge_trace, interesting)
(return_node_trace, return_edge_trace, interesting, updated)
}
fn abbs_in_exec_order(trace: &Vec<ExecInterval>) -> Vec<AtomicBasicBlock> {
@ -382,21 +437,53 @@ where
}
};
let (nodetrace, edgetrace, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate);
let (nodetrace, edgetrace, mut interesting, mut updated) = 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 h = get_generic_hash(&edgetrace);
if let Some(x) = feedbackstate.worst_observed_per_stg_path.get_mut(&h) {
let t = clock_observer.last_runtime();
if t > *x {
*x = t;
interesting |= INTEREST_AGGREGATE;
interesting |= INTEREST_PATH;
}
} else {
feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime());
interesting |= INTEREST_AGGREGATE;
feedbackstate.worst_observed_per_stg_path.insert(h, clock_observer.last_runtime());
updated = true;
interesting |= INTEREST_PATH;
}
}
let mut tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace);
if INTEREST_AGGREGATE || INTEREST_ABBPATH {
if INTEREST_ABBPATH {
let h = get_generic_hash(&tmp);
// order of execution is relevant
if let Some(x) = feedbackstate.worst_observed_per_abb_path.get_mut(&h) {
let t = clock_observer.last_runtime();
if t > *x {
*x = t;
interesting |= INTEREST_ABBPATH;
}
} else {
feedbackstate.worst_observed_per_abb_path.insert(h, clock_observer.last_runtime());
interesting |= INTEREST_ABBPATH;
}
}
if INTEREST_AGGREGATE {
// aggegation by sorting, order of states is not relevant
let mut _tmp = tmp.clone();
_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;
}
}
}
@ -407,17 +494,31 @@ where
self.last_node_trace = Some(nodetrace);
self.last_edge_trace = Some(edgetrace);
self.last_intervals = Some(observer.last_trace.clone());
self.last_abbs = Some(tmp);
if let Some(dp) = &self.dump_path {
if updated {
let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.append(true)
.open(dp).expect("Could not open stgsize");
writeln!(file, "{},{},{},{},{}", feedbackstate.graph.edge_count(), feedbackstate.graph.node_count(), feedbackstate.worst_observed_per_aggegated_path.len(),feedbackstate.worst_observed_per_stg_path.len(), timestamp).expect("Write to dump failed");
}
}
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 nodes = self.last_node_trace.take();
let edges = self.last_edge_trace.take();
let abbs = self.last_abbs.take();
match nodes {
Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, edges.unwrap(), self.last_intervals.take().unwrap())),
Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, edges.unwrap(), abbs.unwrap(), self.last_intervals.take().unwrap())),
None => (),
}
Ok(())