diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 75ccd07dc7..6faef9e6fe 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,8 +1,10 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::sysstate::graph::SysMapFeedback; +use wcet_qemu_sys::sysstate::graph::SysGraphFeedbackState; use libafl::stats::SimpleStats; use wcet_qemu_sys::sysstate::feedbacks::HitSysStateFeedback; -use wcet_qemu_sys::sysstate::MiniFreeRTOSSystemState; +use wcet_qemu_sys::sysstate::RefinedFreeRTOSSystemState; use libafl::corpus::QueueCorpusScheduler; use libafl_qemu::QemuInstrumentationFilter; use wcet_qemu_sys::sysstate::helpers::QemuSystemStateHelper; @@ -330,7 +332,8 @@ fn fuzz( let feedback_state = MapFeedbackState::with_observer(&edges_observer); let sysstate_observer = QemuSysStateObserver::new(); - let sysstate_feedback_state = SysStateFeedbackState::default(); + // let sysstate_feedback_state = SysStateFeedbackState::default(); + let sysstate_feedback_state = SysGraphFeedbackState::new(); let target_map : HashMap<(u64,u64),u8> = match dump_edges { None => HashMap::new(), @@ -340,11 +343,11 @@ fn fuzz( hmap }, }; - let target_trace : Option> = match dump_traces { + let target_trace : Option> = match dump_traces { None => None, Some(ref s) => { let raw = fs::read(s).expect("Can not read dumped traces"); - let trace : Vec = ron::from_str(&String::from_utf8_lossy(&raw)).expect("Can not parse traces"); + let trace : Vec = ron::from_str(&String::from_utf8_lossy(&raw)).expect("Can not parse traces"); Some(trace) }, }; @@ -353,14 +356,16 @@ fn fuzz( let feedback = feedback_or!( // New maximization map feedback linked to the edges observer and the feedback state MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), - HitImprovingFeedback::new(target_map.clone(), &edges_observer), - QemuClockIncreaseFeedback::default(), + // HitImprovingFeedback::new(target_map.clone(), &edges_observer), + // QemuClockIncreaseFeedback::default(), ClockFeedback::new_with_observer(&clock_observer), - NovelSysStateFeedback::default() + // NovelSysStateFeedback::default(), + SysMapFeedback::new() ); // A feedback to choose if an input is a solution or not - let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer),HitSysStateFeedback::new(target_trace)); + // let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer),HitSysStateFeedback::new(target_trace)); + let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer)); // let objective = SortedFeedback::new(); // create a State from scratch @@ -392,8 +397,8 @@ fn fuzz( // A minimization+queue policy to get testcasess from the corpus // let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); - let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); - // let scheduler = QueueCorpusScheduler::new(); + // let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); + let scheduler = QueueCorpusScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs index 7561919628..988ba3eb37 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs @@ -18,7 +18,7 @@ use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use serde::{Deserialize, Serialize}; -use super::MiniFreeRTOSSystemState; +use super::RefinedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSysStateObserver; use petgraph::prelude::DiGraph; @@ -26,170 +26,6 @@ use petgraph::graph::NodeIndex; use petgraph::Direction; use std::cmp::Ordering; -//============================= Graph Feedback -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] -struct VariantTuple -{ - start_tick: u64, - end_tick: u64, - input_counter: u32, - input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime -} - -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -struct SysGraphNode -{ - base: MiniFreeRTOSSystemState, - variants: Vec, -} -impl SysGraphNode { - fn from(base: MiniFreeRTOSSystemState, input: Vec) -> Self { - SysGraphNode{variants: vec![VariantTuple{ - start_tick: base.start_tick, - end_tick: base.end_tick, - input_counter: base.input_counter, - input:input}], base:base } - } - /// unites the variants of this value with another, draining the other if the bases are equal - fn unite(mut self, other: &mut SysGraphNode) -> bool { - if &self!=other {return false;} - self.variants.append(&mut other.variants); - self.variants.dedup(); - return true; - } -} -impl PartialEq for SysGraphNode { - fn eq(&self, other: &SysGraphNode) -> bool { - self.base==other.base - } -} - -/// Improved System State Graph -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct SysMapFeedbackState -{ - graph: DiGraph, - entrypoint: NodeIndex, - name: String, -} -impl SysMapFeedbackState -{ - pub fn new() -> Self { - let mut graph = DiGraph::::new(); - let ind = graph.add_node(SysGraphNode::default()); - Self {graph: graph, entrypoint: ind, name: String::from("SysMap")} - } - fn insert(&mut self, list: Vec, input: &Vec) { - let mut current_index = self.entrypoint; - for n in list { - let mut done = false; - for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { - if n == self.graph[i].base { - done = true; - current_index = i; - break; - } - } - if !done { - let j = self.graph.add_node(SysGraphNode::from(n,input.clone())); - self.graph.add_edge(current_index, j, ()); - current_index = j; - } - } - } - fn update(&mut self, list: &Vec, input: &Vec) -> bool { - let mut current_index = self.entrypoint; - let mut novel = false; - for n in list { - let mut matching : Option = None; - for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { - let tmp = &self.graph[i]; - if n == &tmp.base { - matching = Some(i); - current_index = i; - break; - } - } - match matching { - None => { - novel = true; - let j = self.graph.add_node(SysGraphNode::from(n.clone(),input.clone())); - self.graph.add_edge(current_index, j, ()); - current_index = j; - }, - Some(i) => { - if n.get_time() > self.graph[i].base.get_time() { - novel = true; - self.graph[i]=SysGraphNode::from(n.clone(),input.clone()); - } - } - } - } - return novel; - } -} -impl Named for SysMapFeedbackState -{ - #[inline] - fn name(&self) -> &str { - &self.name - } -} -impl FeedbackState for SysMapFeedbackState -{ - fn reset(&mut self) -> Result<(), Error> { - self.graph.clear(); - self.entrypoint = self.graph.add_node(SysGraphNode::default()); - Ok(()) - } -} - -/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`] -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct SysMapFeedback -{ - name: String -} -impl SysMapFeedback { - pub fn new() -> Self { - Self {name: String::from("SysMapFeedback") } - } -} - -impl Feedback for SysMapFeedback -where - I: Input, - S: HasClientPerfMonitor + HasFeedbackStates, -{ - fn is_interesting( - &mut self, - state: &mut S, - _manager: &mut EM, - _input: &I, - observers: &OT, - _exit_kind: &ExitKind, - ) -> Result - where - EM: EventFirer, - OT: ObserversTuple, - { - let observer = observers.match_name::("sysstate") - .expect("QemuSysStateObserver not found"); - let feedbackstate = state - .feedback_states_mut() - .match_name_mut::("SysMap") - .unwrap(); - Ok(feedbackstate.update(&observer.last_run, &observer.last_input)) - } -} -impl Named for SysMapFeedback -{ - #[inline] - fn name(&self) -> &str { - &self.name - } -} - //============================= Feedback /// Shared Metadata for a SysStateFeedback @@ -197,7 +33,7 @@ impl Named for SysMapFeedback pub struct SysStateFeedbackState { known_traces: HashMap, // encounters,ticks,length - longest: Vec, + longest: Vec, } impl Named for SysStateFeedbackState { @@ -219,7 +55,7 @@ impl FeedbackState for SysStateFeedbackState #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct NovelSysStateFeedback { - last_trace: Option>, + last_trace: Option>, // known_traces: HashMap, } @@ -304,7 +140,7 @@ impl Named for NovelSysStateFeedback //============================= -pub fn match_traces(target: &Vec, last: &Vec) -> bool { +pub fn match_traces(target: &Vec, last: &Vec) -> bool { let mut ret = true; if target.len() > last.len() {return false;} for i in 0..target.len() { @@ -312,7 +148,7 @@ pub fn match_traces(target: &Vec, last: &Vec, last: &Vec) -> bool { +pub fn match_traces_name(target: &Vec, last: &Vec) -> bool { let mut ret = true; if target.len() > last.len() {return false;} for i in 0..target.len() { @@ -367,7 +203,7 @@ impl Named for HitSysStateFeedback } impl HitSysStateFeedback { - pub fn new(target: Option>) -> Self { + pub fn new(target: Option>) -> Self { Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())} } } diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs new file mode 100644 index 0000000000..fff4377058 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -0,0 +1,220 @@ + +/// Feedbacks organizing SystemStates as a graph +use libafl::bolts::ownedref::OwnedSlice; +use libafl::inputs::BytesInput; +use std::path::PathBuf; +use libafl_qemu::QemuClockObserver; +use libafl::feedbacks::FeedbackState; +use libafl::corpus::Testcase; +use libafl::state::HasFeedbackStates; +use libafl::bolts::tuples::MatchName; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; +use std::hash::Hash; +use libafl::events::EventFirer; +use libafl::state::HasClientPerfMonitor; +use libafl::feedbacks::Feedback; +use libafl::bolts::tuples::Named; +use libafl::Error; +use hashbrown::HashMap; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; +use serde::{Deserialize, Serialize}; + +use super::RefinedFreeRTOSSystemState; +use super::FreeRTOSSystemStateMetadata; +use super::observers::QemuSysStateObserver; +use petgraph::prelude::DiGraph; +use petgraph::graph::NodeIndex; +use petgraph::Direction; +use std::cmp::Ordering; + +//============================= Data Structures +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] +struct VariantTuple +{ + start_tick: u64, + end_tick: u64, + input_counter: u32, + input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime +} +impl VariantTuple { + fn from(other: &RefinedFreeRTOSSystemState,input: Vec) -> Self { + VariantTuple{ + start_tick: other.start_tick, + end_tick: other.end_tick, + input_counter: other.input_counter, + input: input, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +struct SysGraphNode +{ + base: RefinedFreeRTOSSystemState, + variants: Vec, +} +impl SysGraphNode { + fn from(base: RefinedFreeRTOSSystemState, input: Vec) -> Self { + SysGraphNode{variants: vec![VariantTuple::from(&base, input)], base:base } + } + /// unites the variants of this value with another, draining the other if the bases are equal + fn unite(&mut self, other: &mut SysGraphNode) -> bool { + if self!=other {return false;} + self.variants.append(&mut other.variants); + self.variants.dedup(); + return true; + } + /// add a Varint from a [`RefinedFreeRTOSSystemState`] + fn unite_raw(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec) -> bool { + if &self.base!=other {return false;} + self.variants.push(VariantTuple::from(other, input.clone())); + self.variants.dedup(); + return true; + } + /// add a Varint from a [`RefinedFreeRTOSSystemState`], if it's interesting + fn unite_interesting(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec) -> bool { + if &self.base!=other {return false;} + let interesting = + self.variants.iter().all(|x| x.end_tick-x.start_tickother.end_tick-other.start_tick) || // shortest variant + self.variants.iter().all(|x| x.input_counter>other.input_counter) || // longest input + self.variants.iter().all(|x| x.input_counter bool { + self.base==other.base + } +} + +//============================= Graph Feedback + +/// Improved System State Graph +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysGraphFeedbackState +{ + graph: DiGraph, + entrypoint: NodeIndex, + name: String, +} +impl SysGraphFeedbackState +{ + pub fn new() -> Self { + let mut graph = DiGraph::::new(); + let ind = graph.add_node(SysGraphNode::default()); + Self {graph: graph, entrypoint: ind, name: String::from("SysMap")} + } + fn insert(&mut self, list: Vec, input: &Vec) { + let mut current_index = self.entrypoint; + for n in list { + let mut done = false; + for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { + if n == self.graph[i].base { + done = true; + current_index = i; + break; + } + } + if !done { + let j = self.graph.add_node(SysGraphNode::from(n,input.clone())); + self.graph.add_edge(current_index, j, ()); + current_index = j; + } + } + } + /// Try adding a system state path from a [Vec], return true if the path was interesting + fn update(&mut self, list: &Vec, input: &Vec) -> bool { + let mut current_index = self.entrypoint; + let mut novel = false; + for n in list { + let mut matching : Option = None; + for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { + let tmp = &self.graph[i]; + if n == &tmp.base { + matching = Some(i); + current_index = i; + break; + } + } + match matching { + None => { + novel = true; + let j = self.graph.add_node(SysGraphNode::from(n.clone(),input.clone())); + self.graph.add_edge(current_index, j, ()); + current_index = j; + }, + Some(i) => { + novel |= self.graph[i].unite_interesting(&n, input); + } + } + } + return novel; + } +} +impl Named for SysGraphFeedbackState +{ + #[inline] + fn name(&self) -> &str { + &self.name + } +} +impl FeedbackState for SysGraphFeedbackState +{ + fn reset(&mut self) -> Result<(), Error> { + self.graph.clear(); + self.entrypoint = self.graph.add_node(SysGraphNode::default()); + Ok(()) + } +} + +/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysMapFeedback +{ + name: String +} +impl SysMapFeedback { + pub fn new() -> Self { + Self {name: String::from("SysMapFeedback") } + } +} + +impl Feedback for SysMapFeedback +where + I: Input, + S: HasClientPerfMonitor + HasFeedbackStates, +{ + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("sysstate") + .expect("QemuSysStateObserver not found"); + let feedbackstate = state + .feedback_states_mut() + .match_name_mut::("SysMap") + .unwrap(); + Ok(feedbackstate.update(&observer.last_run, &observer.last_input)) + } +} +impl Named for SysMapFeedback +{ + #[inline] + fn name(&self) -> &str { + &self.name + } +} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs index ab84191108..793081dc6c 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs @@ -1,4 +1,4 @@ -use crate::sysstate::FreeRTOSSystemStateRaw; +use crate::sysstate::RawFreeRTOSSystemState; use crate::sysstate::CURRENT_SYSSTATE_VEC; use crate::sysstate::NUM_PRIOS; use super::freertos::TCB_t; @@ -91,7 +91,7 @@ where return; } let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); - let mut sysstate = FreeRTOSSystemStateRaw::default(); + let mut sysstate = RawFreeRTOSSystemState::default(); sysstate.qemu_tick = emulator.get_ticks(); let mut buf : [u8; 4] = [0,0,0,0]; unsafe { emulator.read_mem(h.input_counter.into(), &mut buf) }; diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs index ea04918788..cd97a928a7 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -13,6 +13,7 @@ pub mod freertos; pub mod helpers; pub mod observers; pub mod feedbacks; +pub mod graph; // Constants const NUM_PRIOS: usize = 5; @@ -20,7 +21,7 @@ const NUM_PRIOS: usize = 5; //============================= Struct definitions /// Raw info Dump from Qemu #[derive(Debug, Default, Serialize, Deserialize)] -pub struct FreeRTOSSystemStateRaw { +pub struct RawFreeRTOSSystemState { qemu_tick: u64, current_tcb: TCB_t, prio_ready_lists: [freertos::List_t; NUM_PRIOS], @@ -28,11 +29,11 @@ pub struct FreeRTOSSystemStateRaw { input_counter: u32, } /// List of system state dumps from QemuHelpers -static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; +static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; /// A reduced version of freertos::TCB_t #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] -pub struct MiniTCB { +pub struct RefinedTCB { task_name: String, priority: u32, base_priority: u32, @@ -41,7 +42,7 @@ pub struct MiniTCB { notify_state: u8, } -impl Hash for MiniTCB { +impl Hash for RefinedTCB { fn hash(&self, state: &mut H) { self.task_name.hash(state); // self.priority.hash(state); @@ -51,7 +52,7 @@ impl Hash for MiniTCB { } } -impl MiniTCB { +impl RefinedTCB { pub fn from_tcb(input: &TCB_t) -> Self { unsafe { let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); @@ -84,40 +85,40 @@ impl MiniTCB { /// Refined information about the states an execution transitioned between #[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct MiniFreeRTOSSystemState { +pub struct RefinedFreeRTOSSystemState { start_tick: u64, end_tick: u64, input_counter: u32, - current_task: MiniTCB, - ready_list_after: Vec, + current_task: RefinedTCB, + ready_list_after: Vec, } -impl PartialEq for MiniFreeRTOSSystemState { +impl PartialEq for RefinedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { self.current_task == other.current_task && self.ready_list_after == other.ready_list_after } } -impl Hash for MiniFreeRTOSSystemState { +impl Hash for RefinedFreeRTOSSystemState { fn hash(&self, state: &mut H) { self.current_task.hash(state); - // self.ready_list_after.hash(state); + self.ready_list_after.hash(state); } } -impl MiniFreeRTOSSystemState { +impl RefinedFreeRTOSSystemState { fn get_time(&self) -> u64 { self.end_tick-self.start_tick } } -// Wrapper around Vec to attach as Metadata +// Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { - inner: Vec, + inner: Vec, indices: Vec, // Hashed enumeration of States tcref: isize, } impl FreeRTOSSystemStateMetadata { - pub fn new(inner: Vec) -> Self{ + pub fn new(inner: Vec) -> Self{ let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect(); Self {inner: inner, indices: tmp, tcref: 0} } diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs index 8e39aced75..fc7ff1c6a2 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -8,9 +8,9 @@ use serde::{Deserialize, Serialize}; use super::{ CURRENT_SYSSTATE_VEC, - FreeRTOSSystemStateRaw, - MiniTCB, - MiniFreeRTOSSystemState, + RawFreeRTOSSystemState, + RefinedTCB, + RefinedFreeRTOSSystemState, freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*}, }; @@ -22,7 +22,7 @@ use super::{ #[allow(clippy::unsafe_derive_deserialize)] pub struct QemuSysStateObserver { - pub last_run: Vec, + pub last_run: Vec, pub last_input: Vec, name: String, } @@ -115,17 +115,17 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> ret } /// Drains a List of raw SystemStates to produce a refined trace -fn refine_system_states(input: &mut Vec) -> Vec { -let mut ret = Vec::::new(); +fn refine_system_states(input: &mut Vec) -> Vec { +let mut ret = Vec::::new(); let mut start_tick : u64 = 0; for mut i in input.drain(..) { - let mut collector = Vec::::new(); + let mut collector = Vec::::new(); for j in i.prio_ready_lists.into_iter().rev() { - let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| MiniTCB::from_tcb(x)).collect(); + let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); collector.append(&mut tmp); } - ret.push(MiniFreeRTOSSystemState { - current_task: MiniTCB::from_tcb_owned(i.current_tcb), + ret.push(RefinedFreeRTOSSystemState { + current_task: RefinedTCB::from_tcb_owned(i.current_tcb), start_tick: start_tick, end_tick: i.qemu_tick, ready_list_after: collector,