scheduler, mutator changes
This commit is contained in:
parent
c533b7e184
commit
bde16f8297
@ -5,7 +5,7 @@ authors = ["Alwin Berger <alwin.berger@tu-dortmund.de>"]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["std", "snapshot_restore", "singlecore", "restarting", "do_hash_notify_state", "config_stg" ]
|
||||
default = ["std", "snapshot_restore", "singlecore", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int" ]
|
||||
std = []
|
||||
# Exec environemnt basics
|
||||
snapshot_restore = []
|
||||
@ -26,6 +26,7 @@ feed_stg = [ "trace_stg", "observe_systemstate" ]
|
||||
feed_stg_pathhash = [ "feed_stg"]
|
||||
feed_stg_abbhash = [ "feed_stg"]
|
||||
feed_stg_aggregatehash = [ "feed_stg"]
|
||||
mutate_stg = [ "observe_systemstate" ]
|
||||
feed_longest = [ ]
|
||||
feed_afl = [ "observe_edges" ]
|
||||
feed_genetic = []
|
||||
@ -44,7 +45,7 @@ sched_stg_aggregatehash = ['sched_stg'] # every aggregated path (order independe
|
||||
config_genetic = ["gensize_100","feed_genetic","sched_genetic"]
|
||||
config_afl = ["feed_afl","sched_afl","observe_hitcounts"]
|
||||
config_frafl = ["feed_afl","sched_afl","feed_longest"]
|
||||
config_stg = ["feed_stg","sched_stg"]
|
||||
config_stg = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
@ -4,6 +4,7 @@
|
||||
use core::marker::PhantomData;
|
||||
use std::cmp::{max, min};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use libafl_bolts::rands::{
|
||||
StdRand, RandomSeed,
|
||||
Rand
|
||||
@ -12,7 +13,7 @@ use libafl::{
|
||||
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::{clock::IcHist, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}};
|
||||
use crate::{clock::IcHist, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}};
|
||||
|
||||
pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*ms*/ * 62500;
|
||||
// one isn per 2**4 ns
|
||||
@ -89,9 +90,8 @@ where
|
||||
.get(corpus_idx)?
|
||||
.borrow_mut().clone();
|
||||
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
|
||||
// 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();
|
||||
@ -120,19 +120,31 @@ 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 = "trace_stg")]
|
||||
#[cfg(feature = "mutate_stg")]
|
||||
{
|
||||
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;
|
||||
drop(hist);
|
||||
if interrupt_offsets[0] as u64 > maxtick {
|
||||
do_rerun = true;
|
||||
for _ in 0..num_interrupts {
|
||||
prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32")));
|
||||
}
|
||||
} else {
|
||||
let choice = myrand.between(1,100);
|
||||
if choice <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts
|
||||
do_rerun = true;
|
||||
// let metadata = state.metadata_map();
|
||||
let hist = metadata.get::<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")));
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases
|
||||
let feedbackstate = match state
|
||||
.named_metadata_map_mut()
|
||||
.get_mut::<STGFeedbackState>("stgfeedbackstate") {
|
||||
@ -141,7 +153,55 @@ where
|
||||
panic!("STGfeedbackstate not visible")
|
||||
}
|
||||
};
|
||||
|
||||
let tmp = _input.metadata_map().get::<STGNodeMetadata>();
|
||||
if tmp.is_some() {
|
||||
let trace = tmp.expect("STGNodeMetadata not found");
|
||||
let mut node_indices = vec![];
|
||||
for i in (0..trace.intervals.len()).into_iter() {
|
||||
if let Some(abb) = &trace.intervals[i].abb {
|
||||
if let Some(idx) = feedbackstate.state_abb_hash_index.get(&(abb.get_hash(), trace.intervals[i].start_state)) {
|
||||
node_indices.push(Some(idx));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
node_indices.push(None);
|
||||
}
|
||||
// let mut marks : HashMap<u32, usize>= HashMap::new(); // interrupt -> block hit
|
||||
// for i in 0..trace.intervals.len() {
|
||||
// let curr = &trace.intervals[i];
|
||||
// let m = interrupt_offsets[0..num_interrupts].iter().filter(|x| (curr.start_tick..curr.end_tick).contains(&((**x) as u64)));
|
||||
// for k in m {
|
||||
// marks.insert(*k,i);
|
||||
// }
|
||||
// }
|
||||
// walk backwards trough the trace and try moving the interrupt to a block that does not have an outgoing interrupt edge or ist already hit by a predecessor
|
||||
for i in (0..num_interrupts).rev() {
|
||||
let mut lb = FIRST_INT;
|
||||
let mut ub : u32 = trace.intervals[trace.intervals.len()-1].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});
|
||||
}
|
||||
let alternatives : Vec<_> = (0..trace.intervals.len()).filter(|x|
|
||||
trace.intervals[*x].start_tick < (lb as u64) && (lb as u64) < trace.intervals[*x].end_tick
|
||||
|| trace.intervals[*x].start_tick > (lb as u64) && trace.intervals[*x].start_tick < (ub as u64)
|
||||
).collect();
|
||||
let not_yet_hit : Vec<_> = alternatives.iter().filter(
|
||||
|x| feedbackstate.graph.edges_directed(*node_indices[**x].unwrap(), petgraph::Direction::Outgoing).any(|y| y.weight().event != CaptureEvent::ISRStart)).collect();
|
||||
if not_yet_hit.len() > 0 {
|
||||
let replacement = &trace.intervals[*myrand.choose(not_yet_hit)];
|
||||
interrupt_offsets[i] = (myrand.between(replacement.start_tick,
|
||||
replacement.end_tick)).try_into().expect("ticks > u32");
|
||||
// println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]);
|
||||
do_rerun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // old version of the alternative search
|
||||
let tmp = _input.metadata_map().get::<STGNodeMetadata>();
|
||||
if tmp.is_some() {
|
||||
let trace = tmp.expect("STGNodeMetadata not found");
|
||||
@ -165,7 +225,7 @@ where
|
||||
}
|
||||
for i in 0..num_interrupts {
|
||||
// bounds based on minimum inter-arrival time
|
||||
let mut lb = 0;
|
||||
let mut lb = FIRST_INT;
|
||||
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});
|
||||
@ -188,7 +248,7 @@ where
|
||||
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 )
|
||||
|| x.0.start_tick > (lb as u64) && x.0.start_tick < (ub as u64))
|
||||
).collect();
|
||||
// in cases there are no alternatives
|
||||
if alternatives.len() == 0 {
|
||||
@ -244,6 +304,7 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "trace_stg"))]
|
||||
{
|
||||
if myrand.between(1,100) <= 25 { // we have no hint if interrupt times will change anything
|
||||
|
@ -336,7 +336,7 @@ impl Ord for AtomicBasicBlock {
|
||||
}
|
||||
|
||||
impl AtomicBasicBlock {
|
||||
fn get_hash(&self) -> u64 {
|
||||
pub fn get_hash(&self) -> u64 {
|
||||
let mut s = DefaultHasher::new();
|
||||
self.hash(&mut s);
|
||||
s.finish()
|
||||
|
@ -344,7 +344,7 @@ 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" {
|
||||
if i.1 == CaptureEvent::APIStart && i.2.ends_with("FromISR") {
|
||||
i.1 = CaptureEvent::ISREnd;
|
||||
i.2 = "isr_starter".to_string();
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use libafl::SerdeAny;
|
||||
/// Feedbacks organizing SystemStates as a graph
|
||||
use libafl::inputs::HasBytesVec;
|
||||
@ -108,8 +109,8 @@ impl PartialEq for STGNode {
|
||||
pub struct STGEdge
|
||||
{
|
||||
// is_interrupt: bool,
|
||||
event: CaptureEvent,
|
||||
name: String
|
||||
pub event: CaptureEvent,
|
||||
pub name: String
|
||||
}
|
||||
|
||||
impl STGEdge {
|
||||
@ -146,14 +147,15 @@ 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>,
|
||||
pub state_abb_hash_index: HashMap<(u64, u64), NodeIndex>,
|
||||
stgnode_index: HashMap<u64, NodeIndex>,
|
||||
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_abb_path: HashMap<u64,u64>,
|
||||
worst_observed_per_stg_path: HashMap<u64,u64>
|
||||
worst_observed_per_stg_path: HashMap<u64,u64>,
|
||||
worst_abb_exec_count: HashMap<AtomicBasicBlock, usize>
|
||||
}
|
||||
|
||||
impl Default for STGFeedbackState {
|
||||
@ -184,6 +186,7 @@ impl Default for STGFeedbackState {
|
||||
worst_observed_per_aggegated_path: HashMap::new(),
|
||||
worst_observed_per_abb_path: HashMap::new(),
|
||||
worst_observed_per_stg_path: HashMap::new(),
|
||||
worst_abb_exec_count: HashMap::new(),
|
||||
systemstate_index,
|
||||
state_abb_hash_index
|
||||
}
|
||||
@ -203,13 +206,15 @@ impl Named for STGFeedbackState
|
||||
pub struct STGNodeMetadata {
|
||||
pub nodes: Vec<NodeIndex>,
|
||||
pub edges: Vec<EdgeIndex>,
|
||||
pub abbs: Vec<AtomicBasicBlock>,
|
||||
pub abbs: u64,
|
||||
pub aggregate: u64,
|
||||
pub top_abb_counts: Vec<u64>,
|
||||
pub intervals: Vec<ExecInterval>,
|
||||
indices: Vec<usize>,
|
||||
tcref: isize,
|
||||
}
|
||||
impl STGNodeMetadata {
|
||||
pub fn new(nodes: Vec<NodeIndex>, edges: Vec<EdgeIndex>, abbs: Vec<AtomicBasicBlock>, intervals: Vec<ExecInterval>) -> Self{
|
||||
pub fn new(nodes: Vec<NodeIndex>, edges: Vec<EdgeIndex>, abbs: u64, aggregate: u64, top_abb_counts: Vec<u64>, 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"))))]
|
||||
{
|
||||
@ -223,15 +228,14 @@ impl STGNodeMetadata {
|
||||
}
|
||||
#[cfg(feature = "sched_stg_abbhash")]
|
||||
{
|
||||
indices.push(get_generic_hash(&abbs) as usize);
|
||||
indices.push(abbs as usize);
|
||||
}
|
||||
#[cfg(feature = "sched_stg_aggregatehash")]
|
||||
{
|
||||
let mut _abbs = abbs.clone();
|
||||
_abbs.sort_unstable();
|
||||
indices.push(get_generic_hash(&_abbs) as usize);
|
||||
// indices.push(aggregate as usize);
|
||||
indices = top_abb_counts.iter().map(|x| (*x) as usize).collect();
|
||||
}
|
||||
Self {indices, intervals, nodes, abbs, edges, tcref: 0}
|
||||
Self {indices, intervals, nodes, abbs, aggregate, top_abb_counts, edges, tcref: 0}
|
||||
}
|
||||
}
|
||||
impl AsSlice for STGNodeMetadata {
|
||||
@ -258,6 +262,36 @@ libafl_bolts::impl_serdeany!(STGNodeMetadata);
|
||||
pub type GraphMaximizerCorpusScheduler<CS> =
|
||||
MinimizerScheduler<CS, MaxTimeFavFactor<<CS as UsesState>::State>,STGNodeMetadata>;
|
||||
|
||||
// AI generated, human verified
|
||||
fn count_occurrences<T>(vec: &Vec<T>) -> HashMap<&T, usize>
|
||||
where
|
||||
T: PartialEq + Eq + Hash + Clone,
|
||||
{
|
||||
let mut counts = HashMap::new();
|
||||
|
||||
if vec.is_empty() {
|
||||
return counts;
|
||||
}
|
||||
|
||||
let mut current_obj = &vec[0];
|
||||
let mut current_count = 1;
|
||||
|
||||
for obj in vec.iter().skip(1) {
|
||||
if obj == current_obj {
|
||||
current_count += 1;
|
||||
} else {
|
||||
counts.insert(current_obj, current_count);
|
||||
current_obj = obj;
|
||||
current_count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the count of the last object
|
||||
counts.insert(current_obj, current_count);
|
||||
|
||||
counts
|
||||
}
|
||||
|
||||
//============================= Graph Feedback
|
||||
|
||||
pub static mut STG_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE];
|
||||
@ -274,7 +308,9 @@ 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>>,
|
||||
last_abbs_hash: Option<u64>, // only set, if it was interesting
|
||||
last_aggregate_hash: Option<u64>, // only set, if it was interesting
|
||||
last_top_abb_hashes: Option<Vec<u64>>, // only set, if it was interesting
|
||||
dump_path: Option<PathBuf>
|
||||
}
|
||||
#[cfg(feature = "feed_stg")]
|
||||
@ -458,6 +494,7 @@ where
|
||||
if INTEREST_AGGREGATE || INTEREST_ABBPATH {
|
||||
if INTEREST_ABBPATH {
|
||||
let h = get_generic_hash(&tmp);
|
||||
self.last_abbs_hash = Some(h);
|
||||
// order of execution is relevant
|
||||
if let Some(x) = feedbackstate.worst_observed_per_abb_path.get_mut(&h) {
|
||||
let t = clock_observer.last_runtime();
|
||||
@ -474,6 +511,22 @@ where
|
||||
// aggegation by sorting, order of states is not relevant
|
||||
let mut _tmp = tmp.clone();
|
||||
_tmp.sort();
|
||||
let counts = count_occurrences(&_tmp);
|
||||
let mut top_indices = Vec::new();
|
||||
for (k,c) in counts {
|
||||
if let Some(reference) = feedbackstate.worst_abb_exec_count.get_mut(k) {
|
||||
if *reference < c {
|
||||
*reference = c;
|
||||
top_indices.push(get_generic_hash(k));
|
||||
}
|
||||
} else {
|
||||
top_indices.push(get_generic_hash(k));
|
||||
feedbackstate.worst_abb_exec_count.insert(k.clone(), c);
|
||||
}
|
||||
}
|
||||
self.last_top_abb_hashes = Some(top_indices);
|
||||
|
||||
self.last_aggregate_hash = Some(get_generic_hash(&_tmp));
|
||||
if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&_tmp) {
|
||||
let t = clock_observer.last_runtime();
|
||||
if t > *x {
|
||||
@ -494,7 +547,6 @@ 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 {
|
||||
@ -516,9 +568,8 @@ where
|
||||
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(), abbs.unwrap(), self.last_intervals.take().unwrap())),
|
||||
Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, edges.unwrap(), self.last_abbs_hash.take().unwrap_or_default(), self.last_aggregate_hash.take().unwrap_or_default(), self.last_top_abb_hashes.take().unwrap_or_default(), self.last_intervals.take().unwrap())),
|
||||
None => (),
|
||||
}
|
||||
Ok(())
|
||||
|
@ -58,6 +58,15 @@ impl TopRatedsMetadata {
|
||||
pub fn map(&self) -> &HashMap<usize, CorpusId> {
|
||||
&self.map
|
||||
}
|
||||
|
||||
/// Retruns the number of inices that are considered interesting
|
||||
pub fn get_number(&self) -> usize {
|
||||
let mut tmp = HashSet::new();
|
||||
for i in self.map.values() {
|
||||
tmp.insert(*i);
|
||||
}
|
||||
tmp.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TopRatedsMetadata {
|
||||
@ -325,6 +334,7 @@ where
|
||||
.map
|
||||
.insert(elem, idx);
|
||||
}
|
||||
println!("Number of interesting corpus elements: {}", state.metadata_map_mut().get::<TopRatedsMetadata>().unwrap().get_number());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user