From f5bf5605f1e9eb1c6eac979361f58e2d7e8ec5a7 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 22 Feb 2022 16:49:02 +0100 Subject: [PATCH] split system_state module, add tracedump --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 9 +- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 30 +- fuzzers/wcet_qemu_sys/src/lib.rs | 6 +- .../wcet_qemu_sys/src/sysstate/feedbacks.rs | 191 +++++++ .../src/{ => sysstate}/freertos.rs | 2 +- fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs | 138 +++++ fuzzers/wcet_qemu_sys/src/sysstate/mod.rs | 108 ++++ .../wcet_qemu_sys/src/sysstate/observers.rs | 131 +++++ fuzzers/wcet_qemu_sys/src/system_trace.rs | 502 ------------------ 9 files changed, 597 insertions(+), 520 deletions(-) create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs rename fuzzers/wcet_qemu_sys/src/{ => sysstate}/freertos.rs (98%) create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/mod.rs create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/observers.rs delete mode 100644 fuzzers/wcet_qemu_sys/src/system_trace.rs diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 8762b992eb..251c82e324 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,11 +1,10 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. use libafl_qemu::QemuInstrumentationFilter; -use wcet_qemu_sys::system_trace::QemuSystemStateHelper; -use wcet_qemu_sys::system_trace::QemuSysStateObserver; -use wcet_qemu_sys::system_trace::FreeRTOSSystemStateMetadata; -use wcet_qemu_sys::system_trace::SysStateFeedbackState; -use wcet_qemu_sys::system_trace::NovelSysStateFeedback; +use wcet_qemu_sys::sysstate::helpers::QemuSystemStateHelper; +use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; +use wcet_qemu_sys::sysstate::feedbacks::SysStateFeedbackState; +use wcet_qemu_sys::sysstate::feedbacks::NovelSysStateFeedback; use wcet_qemu_sys::worst::QemuHashMapObserver; use hashbrown::HashMap; use clap::{App, Arg}; diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index d005e471b0..772a117104 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -1,11 +1,12 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. use std::io::Read; -use wcet_qemu_sys::system_trace::QemuSysStateObserver; +use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; +use wcet_qemu_sys::sysstate::feedbacks::DumpSystraceFeedback; use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::{ worst::{DumpMapFeedback,DummyFeedback}, - system_trace::QemuSystemStateHelper, + sysstate::helpers::QemuSystemStateHelper, }; use clap::{App, Arg}; use std::{ @@ -23,7 +24,7 @@ use libafl::{ corpus::{Corpus,InMemoryCorpus,QueueCorpusScheduler}, executors::{ExitKind}, fuzzer::{StdFuzzer}, - inputs::{Input,BytesInput, HasTargetBytes}, + inputs::{BytesInput, HasTargetBytes}, observers::{VariableMapObserver}, state::{HasCorpus,StdState}, Error, @@ -33,6 +34,7 @@ use libafl::{ stages::StdMutationalStage, mutators::BitFlipMutator, Fuzzer, + feedback_or, }; use libafl_qemu::{ edges, @@ -95,6 +97,11 @@ pub fn main() { .long("libafl-edges") .takes_value(true), ) + .arg( + Arg::new("traces") + .long("libafl-traces") + .takes_value(true), + ) .arg( Arg::new("snapshot") .help("The qcow2 file used for snapshots") @@ -165,9 +172,14 @@ pub fn main() { None => None }; + let traces = match res.value_of("traces") { + Some(st) => Some(PathBuf::from(st.to_string())), + None => None + }; + let snapshot = PathBuf::from(res.value_of("snapshot").unwrap().to_string()); - fuzz(seed, kernel, edges, snapshot) + fuzz(seed, kernel, edges, traces, snapshot) .expect("An error occurred while fuzzing"); } @@ -189,6 +201,7 @@ fn fuzz( seed: Either,PathBuf>, kernel: PathBuf, dump_edges: Option, + dump_traces: Option, snapshot: PathBuf, ) -> Result<(), Error> { //=========== Setup emulator @@ -278,7 +291,7 @@ fn fuzz( let clock_observer = QemuClockObserver::default(); //========= Feedback-Function evaluate the Maps. Need to dump it for debugging and check if it reaches targets. - let feedback = DumpMapFeedback::with_dump(dump_edges, &edges_observer); + let feedback = feedback_or!(DumpMapFeedback::with_dump(dump_edges, &edges_observer),DumpSystraceFeedback::with_dump(dump_traces)); // A feedback to choose if an input is a solution or not let objective = DummyFeedback::new(false); @@ -353,10 +366,9 @@ fn fuzz( }); println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer - .fuzz_one(&mut tuple_list!(StdMutationalStage::new(BitFlipMutator::new())), &mut executor, &mut state, &mut mgr) - .expect("Error in the fuzzing loop"); - + // fuzzer + // .fuzz_one(&mut tuple_list!(StdMutationalStage::new(BitFlipMutator::new())), &mut executor, &mut state, &mut mgr) + // .expect("Error in the fuzzing loop"); }, Left(s) => { fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(s)).expect("Evaluation failed"); diff --git a/fuzzers/wcet_qemu_sys/src/lib.rs b/fuzzers/wcet_qemu_sys/src/lib.rs index ab85cf7d2e..34b31773e6 100644 --- a/fuzzers/wcet_qemu_sys/src/lib.rs +++ b/fuzzers/wcet_qemu_sys/src/lib.rs @@ -1,6 +1,6 @@ #![feature(iter_advance_by)] #![feature(is_sorted)] #[cfg(target_os = "linux")] -pub mod worst; -pub mod freertos; -pub mod system_trace; +pub mod sysstate; + +pub mod worst; \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs new file mode 100644 index 0000000000..cfbe0af0fe --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs @@ -0,0 +1,191 @@ +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::MiniFreeRTOSSystemState; +use super::FreeRTOSSystemStateMetadata; +use super::observers::QemuSysStateObserver; + +//============================= Feedback + +/// Shared Metadata for a SysStateFeedback +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysStateFeedbackState +{ + known_traces: HashMap, // encounters,ticks,length + longest: Vec, +} +impl Named for SysStateFeedbackState +{ + #[inline] + fn name(&self) -> &str { + "sysstate" + } +} +impl FeedbackState for SysStateFeedbackState +{ + fn reset(&mut self) -> Result<(), Error> { + self.longest.clear(); + self.known_traces.clear(); + Ok(()) + } +} + +/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct NovelSysStateFeedback +{ + last_trace: Option>, + // known_traces: HashMap, +} + +impl Feedback for NovelSysStateFeedback +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 clock_observer = observers.match_name::("clock") //TODO not fixed + .expect("QemuSysStateObserver not found"); + let feedbackstate = state + .feedback_states_mut() + .match_name_mut::("sysstate") + .unwrap(); + // Do Stuff + let mut hasher = DefaultHasher::new(); + observer.last_run.hash(&mut hasher); + let somehash = hasher.finish(); + let mut is_novel = false; + let mut takes_longer = false; + match feedbackstate.known_traces.get_mut(&somehash) { + None => { + is_novel = true; + feedbackstate.known_traces.insert(somehash,(1,clock_observer.last_runtime(),observer.last_run.len())); + } + Some(s) => { + s.0+=1; + if s.1 < clock_observer.last_runtime() { + s.1 = clock_observer.last_runtime(); + takes_longer = true; + } + } + } + if observer.last_run.len() > feedbackstate.longest.len() { + feedbackstate.longest=observer.last_run.clone(); + } + self.last_trace = Some(observer.last_run.clone()); + // if (!is_novel) { println!("not novel") }; + Ok(is_novel | takes_longer) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + let a = self.last_trace.take(); + match a { + Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)), + None => (), + } + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.last_trace = None; + Ok(()) + } +} + +impl Named for NovelSysStateFeedback +{ + #[inline] + fn name(&self) -> &str { + "sysstate" + } +} + +//=========================== Debugging Feedback +/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSysStateObserver`] +#[derive(Debug)] +pub struct DumpSystraceFeedback +{ + dumpfile: Option, +} + +impl Feedback for DumpSystraceFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + 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"); + 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"); + self.dumpfile = None + }, + None => println!("{:?}",observer.last_run), + }; + Ok(true) + } +} + +impl Named for DumpSystraceFeedback +{ + #[inline] + fn name(&self) -> &str { + "DumpSysState" + } +} + +impl DumpSystraceFeedback +{ + /// Creates a new [`DumpSystraceFeedback`] + #[must_use] + pub fn new() -> Self { + Self {dumpfile: None} + } + pub fn with_dump(dumpfile: Option) -> Self { + Self {dumpfile: dumpfile} + } +} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/sysstate/freertos.rs similarity index 98% rename from fuzzers/wcet_qemu_sys/src/freertos.rs rename to fuzzers/wcet_qemu_sys/src/sysstate/freertos.rs index 2b22eb6cb0..31bdbcbeec 100644 --- a/fuzzers/wcet_qemu_sys/src/freertos.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/freertos.rs @@ -103,7 +103,7 @@ pub enum rtos_struct { #[macro_export] macro_rules! impl_emu_lookup { ($struct_name:ident) => { - impl $crate::freertos::emu_lookup for $struct_name { + impl $crate::sysstate::freertos::emu_lookup for $struct_name { fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> $struct_name { let mut tmp : [u8; std::mem::size_of::<$struct_name>()] = [0u8; std::mem::size_of::<$struct_name>()]; unsafe { diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs new file mode 100644 index 0000000000..8513a778b3 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs @@ -0,0 +1,138 @@ +use crate::sysstate::FreeRTOSSystemStateRaw; +use crate::sysstate::CURRENT_SYSSTATE_VEC; +use crate::sysstate::NUM_PRIOS; +use super::freertos::TCB_t; +use super::freertos::rtos_struct::List_Item_struct; +use super::freertos::rtos_struct::*; +use super::freertos; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; + +use libafl_qemu::{ + emu::Emulator, + executor::QemuExecutor, + helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, +}; + +//============================= Struct definitions + + +//============================= Qemu Helper + +/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur +#[derive(Debug)] +pub struct QemuSystemStateHelper { + filter: QemuInstrumentationFilter, + tcb_addr: u32, + ready_queues: u32, +} + +impl QemuSystemStateHelper { + #[must_use] + pub fn new(tcb_addr: u32, ready_queues: u32) -> Self { + Self { + filter: QemuInstrumentationFilter::None, + tcb_addr: tcb_addr, + ready_queues: ready_queues, + } + } + + #[must_use] + pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter, tcb_addr: u32, ready_queues: u32) -> Self { + Self { filter, tcb_addr, ready_queues} + } + + #[must_use] + pub fn must_instrument(&self, addr: u64) -> bool { + self.filter.allowed(addr) + } +} + +impl QemuHelper for QemuSystemStateHelper +where + I: Input, + S: HasMetadata, +{ + fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) + where + H: FnMut(&I) -> ExitKind, + OT: ObserversTuple, + QT: QemuHelperTuple, + { + // emu::Emulator{_private: ()}.set_gen_block_hook(test_gen_hook); + executor.hook_block_generation(gen_not_exec_block_hook::); + executor.hook_block_execution(exec_syscall_hook::); + } +} + +pub fn exec_syscall_hook( + emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + pc: u64, +) +where + S: HasMetadata, + I: Input, + QT: QemuHelperTuple, +{ + let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + if !h.must_instrument(pc) { + return; + } + let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); + let mut sysstate = FreeRTOSSystemStateRaw::default(); + sysstate.qemu_tick = emulator.get_ticks(); + + let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); + sysstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); + // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); + + for i in 0..NUM_PRIOS { + let target : u32 = listbytes*u32::try_from(i).unwrap()+h.ready_queues; + sysstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target); + // println!("List at {}: {:?}",target, sysstate.prio_ready_lists[i]); + let mut next_index = sysstate.prio_ready_lists[i].pxIndex; + for _j in 0..sysstate.prio_ready_lists[i].uxNumberOfItems { + // always jump over the xListEnd marker + if (target..target+listbytes).contains(&next_index) { + let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + let new_next_index=next_item.pxNext; + sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); + next_index = new_next_index; + } + 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); + 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); + sysstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone())); + sysstate.dumping_ground.insert(next_index,List_Item_struct(next_item)); + next_index=new_next_index; + } + // Handle edge case where the end marker was not included yet + if (target..target+listbytes).contains(&next_index) { + let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); + } + } + + unsafe { CURRENT_SYSSTATE_VEC.push(sysstate); } +} + +pub fn gen_not_exec_block_hook( + _emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + pc: u64, +) +-> Option where + S: HasMetadata, + I: Input, + QT: QemuHelperTuple, +{ + let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + if !h.must_instrument(pc) { + None + } else {Some(1)} +} diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs new file mode 100644 index 0000000000..9a0c2c2208 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -0,0 +1,108 @@ +//! Sysstate referes to the State of a FreeRTOS fuzzing target +use std::hash::Hasher; +use std::hash::Hash; +use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; + +use freertos::TCB_t; + +pub mod freertos; +pub mod helpers; +pub mod observers; +pub mod feedbacks; + +// Constants +const NUM_PRIOS: usize = 5; + +//============================= Struct definitions +/// Raw info Dump from Qemu +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct FreeRTOSSystemStateRaw { + qemu_tick: u64, + current_tcb: TCB_t, + prio_ready_lists: [freertos::List_t; NUM_PRIOS], + dumping_ground: HashMap, +} +/// List of system state dumps from QemuHelpers +static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; + +/// A reduced version of freertos::TCB_t +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct MiniTCB { + task_name: String, + priority: u32, + base_priority: u32, + mutexes_held: u32, + notify_value: u32, + notify_state: u8, +} + +impl Hash for MiniTCB { + fn hash(&self, state: &mut H) { + self.task_name.hash(state); + // self.priority.hash(state); + // self.mutexes_held.hash(state); + // self.notify_state.hash(state); + // self.notify_value.hash(state); + } +} + +impl MiniTCB { + pub fn from_tcb(input: &TCB_t) -> Self { + unsafe { + let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); + let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + Self { + task_name: name, + priority: input.uxPriority, + base_priority: input.uxBasePriority, + mutexes_held: input.uxMutexesHeld, + notify_value: input.ulNotifiedValue[0], + notify_state: input.ucNotifyState[0], + } + } + } + pub fn from_tcb_owned(input: TCB_t) -> Self { + unsafe { + let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); + let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + Self { + task_name: name, + priority: input.uxPriority, + base_priority: input.uxBasePriority, + mutexes_held: input.uxMutexesHeld, + notify_value: input.ulNotifiedValue[0], + notify_state: input.ucNotifyState[0], + } + } + } +} + +/// Refined information about the states an execution transitioned between +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct MiniFreeRTOSSystemState { + start_tick: u64, + end_tick: u64, + current_task: MiniTCB, + ready_list_after: Vec, +} + +impl Hash for MiniFreeRTOSSystemState { + fn hash(&self, state: &mut H) { + self.current_task.hash(state); + // self.ready_list_after.hash(state); + } +} + +// Wrapper around Vec to attach as Metadata +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct FreeRTOSSystemStateMetadata { + inner: Vec, +} +impl FreeRTOSSystemStateMetadata { + pub fn new(inner: Vec) -> Self{ + Self {inner: inner} + } +} + +libafl::impl_serdeany!(FreeRTOSSystemStateMetadata); diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs new file mode 100644 index 0000000000..30bbd5b7a7 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -0,0 +1,131 @@ +use libafl::bolts::HasLen; +use libafl::bolts::tuples::Named; +use libafl::Error; +use libafl::observers::Observer; +use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; + +use super::{ + CURRENT_SYSSTATE_VEC, + FreeRTOSSystemStateRaw, + MiniTCB, + MiniFreeRTOSSystemState, + freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*}, +}; + +//============================= Observer + +/// The QemuSysState Observer retrieves the SysState +/// that will get updated by the target. +#[derive(Serialize, Deserialize, Debug, Default)] +#[allow(clippy::unsafe_derive_deserialize)] +pub struct QemuSysStateObserver +{ + pub last_run: Vec, + name: String, +} + +impl Observer for QemuSysStateObserver +{ + #[inline] + fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + unsafe {CURRENT_SYSSTATE_VEC.clear(); } + Ok(()) + } + + #[inline] + fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + unsafe {self.last_run = refine_system_states(&mut CURRENT_SYSSTATE_VEC);} + // let mut hasher = DefaultHasher::new(); + // let mut a = self.parse_last(); + // a[0].start_tick=21355; + // a[0].end_tick=2131; + // a.hash(&mut hasher); + // let somehash = hasher.finish(); + // println!("HashValue: {}",somehash); + // println!("{:#?}",self.parse_last()); + Ok(()) + } +} + +impl Named for QemuSysStateObserver +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl HasLen for QemuSysStateObserver +{ + #[inline] + fn len(&self) -> usize { + self.last_run.len() + } +} + +impl QemuSysStateObserver { + pub fn new() -> Self { + Self{last_run: vec![], name: "sysstate".to_string()} + } + +} + +//============================= Parsing helpers + +/// Parse a List_t containing TCB_t into Vec from cache. Consumes the elements from cache +fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> Vec +{ + let mut ret : Vec = Vec::new(); + if list.uxNumberOfItems == 0 {return ret;} + let last_list_item = match dump.remove(&list.pxIndex).expect("List_t entry was not in Hashmap") { + List_Item_struct(li) => li, + List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { + List_Item_struct(li) => li, + _ => panic!("MiniListItem of a non empty List does not point to ListItem"), + }, + _ => panic!("List_t entry was not a ListItem"), + }; + let mut next_index = last_list_item.pxNext; + let last_tcb = match dump.remove(&last_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { + TCB_struct(t) => t, + _ => panic!("List content does not equal type"), + }; + for _ in 0..list.uxNumberOfItems-1 { + let next_list_item = match dump.remove(&next_index).expect("List_t entry was not in Hashmap") { + List_Item_struct(li) => li, + List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { + List_Item_struct(li) => li, + _ => panic!("MiniListItem of a non empty List does not point to ListItem"), + }, + _ => panic!("List_t entry was not a ListItem"), + }; + match dump.remove(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { + TCB_struct(t) => {ret.push(t)}, + _ => panic!("List content does not equal type"), + } + next_index=next_list_item.pxNext; + } + ret.push(last_tcb); + 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(); +let mut start_tick : u64 = 0; +for i in input.into_iter() { + 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(); + collector.append(&mut tmp); + } + ret.push(MiniFreeRTOSSystemState { + current_task: MiniTCB::from_tcb_owned(i.current_tcb), + start_tick: start_tick, + end_tick: i.qemu_tick, + ready_list_after: collector, + }); + start_tick=i.qemu_tick; +} +return ret; +} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs deleted file mode 100644 index c511dc99cc..0000000000 --- a/fuzzers/wcet_qemu_sys/src/system_trace.rs +++ /dev/null @@ -1,502 +0,0 @@ -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 crate::freertos::emu_lookup; -use crate::freertos::rtos_struct; -use crate::freertos::List_t; -use crate::freertos::TCB_t; -use crate::freertos::rtos_struct::List_Item_struct; -use libafl::events::EventFirer; -use libafl::state::HasClientPerfMonitor; -use libafl::feedbacks::Feedback; -use libafl::bolts::HasLen; -use libafl::bolts::tuples::Named; -use libafl::Error; -use libafl::observers::Observer; -use crate::freertos::rtos_struct::*; -use crate::freertos; -use hashbrown::HashMap; -use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; -use serde::{Deserialize, Serialize}; - -use libafl_qemu::{ - emu, - emu::Emulator, - executor::QemuExecutor, - helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, -}; - -const NUM_PRIOS: usize = 5; - -//============================= Datatypes - -/// Info Dump from Qemu -// pub type SysState = (u64,freertos::TCB_t,HashMap); -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct FreeRTOSSystemStateRaw { - qemu_tick: u64, - current_tcb: TCB_t, - prio_ready_lists: [freertos::List_t; NUM_PRIOS], - dumping_ground: HashMap, -} - -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct MiniTCB { - task_name: String, - priority: u32, - base_priority: u32, - mutexes_held: u32, - notify_value: u32, - notify_state: u8, -} - -impl Hash for MiniTCB { - fn hash(&self, state: &mut H) { - self.task_name.hash(state); - // self.priority.hash(state); - // self.mutexes_held.hash(state); - // self.notify_state.hash(state); - // self.notify_value.hash(state); - } -} - -impl MiniTCB { - pub fn from_tcb(input: &TCB_t) -> Self { - unsafe { - let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); - let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); - Self { - task_name: name, - priority: input.uxPriority, - base_priority: input.uxBasePriority, - mutexes_held: input.uxMutexesHeld, - notify_value: input.ulNotifiedValue[0], - notify_state: input.ucNotifyState[0], - } - } - } - pub fn from_tcb_owned(input: TCB_t) -> Self { - unsafe { - let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); - let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); - Self { - task_name: name, - priority: input.uxPriority, - base_priority: input.uxBasePriority, - mutexes_held: input.uxMutexesHeld, - notify_value: input.ulNotifiedValue[0], - notify_state: input.ucNotifyState[0], - } - } - } -} - -/// Refined information about the states an execution transitioned between -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct MiniFreeRTOSSystemState { - start_tick: u64, - end_tick: u64, - current_task: MiniTCB, - ready_list_after: Vec, -} - -impl Hash for MiniFreeRTOSSystemState { - fn hash(&self, state: &mut H) { - self.current_task.hash(state); - // self.ready_list_after.hash(state); - } -} - -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct FreeRTOSSystemStateMetadata { - inner: Vec, -} -impl FreeRTOSSystemStateMetadata { - pub fn new(inner: Vec) -> Self{ - Self {inner: inner} - } -} - -libafl::impl_serdeany!(FreeRTOSSystemStateMetadata); - - -//============================= Qemu Helper -static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; - -/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur -#[derive(Debug)] -pub struct QemuSystemStateHelper { - filter: QemuInstrumentationFilter, - tcb_addr: u32, - ready_queues: u32, -} - -impl QemuSystemStateHelper { - #[must_use] - pub fn new(tcb_addr: u32, ready_queues: u32) -> Self { - Self { - filter: QemuInstrumentationFilter::None, - tcb_addr: tcb_addr, - ready_queues: ready_queues, - } - } - - #[must_use] - pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter, tcb_addr: u32, ready_queues: u32) -> Self { - Self { filter, tcb_addr, ready_queues} - } - - #[must_use] - pub fn must_instrument(&self, addr: u64) -> bool { - self.filter.allowed(addr) - } -} - -impl QemuHelper for QemuSystemStateHelper -where - I: Input, - S: HasMetadata, -{ - fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) - where - H: FnMut(&I) -> ExitKind, - OT: ObserversTuple, - QT: QemuHelperTuple, - { - // emu::Emulator{_private: ()}.set_gen_block_hook(test_gen_hook); - executor.hook_block_generation(gen_not_exec_block_hook::); - executor.hook_block_execution(exec_syscall_hook::); - } -} - -pub fn exec_syscall_hook( - emulator: &Emulator, - helpers: &mut QT, - state: &mut S, - pc: u64, -) -where - S: HasMetadata, - I: Input, - QT: QemuHelperTuple, -{ - let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - if !h.must_instrument(pc) { - return; - } - let LISTBYTES : u32 = u32::try_from(std::mem::size_of::()).unwrap(); - let mut sysstate = FreeRTOSSystemStateRaw::default(); - sysstate.qemu_tick = emulator.get_ticks(); - - let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); - sysstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); - // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); - - for i in 0..NUM_PRIOS { - let target : u32 = LISTBYTES*u32::try_from(i).unwrap()+h.ready_queues; - sysstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target); - // println!("List at {}: {:?}",target, sysstate.prio_ready_lists[i]); - let mut next_index = sysstate.prio_ready_lists[i].pxIndex; - for _j in 0..sysstate.prio_ready_lists[i].uxNumberOfItems { - // always jump over the xListEnd marker - if (target..target+LISTBYTES).contains(&next_index) { - let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); - let new_next_index=next_item.pxNext; - sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); - next_index = new_next_index; - } - 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); - 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); - sysstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone())); - sysstate.dumping_ground.insert(next_index,List_Item_struct(next_item)); - next_index=new_next_index; - } - // Handle edge case where the end marker was not included yet - if (target..target+LISTBYTES).contains(&next_index) { - let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); - sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); - } - } - - unsafe { CURRENT_SYSSTATE_VEC.push(sysstate); } -} - -pub fn gen_not_exec_block_hook( - _emulator: &Emulator, - helpers: &mut QT, - _state: &mut S, - pc: u64, -) --> Option where - S: HasMetadata, - I: Input, - QT: QemuHelperTuple, -{ - let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - if !h.must_instrument(pc) { - None - } else {Some(1)} -} - -//============================= Observer - -/// The QemuSysState Observer retrieves the SysState -/// that will get updated by the target. -#[derive(Serialize, Deserialize, Debug, Default)] -#[allow(clippy::unsafe_derive_deserialize)] -pub struct QemuSysStateObserver -{ - last_run: Vec, - name: String, -} - -impl Observer for QemuSysStateObserver -{ - #[inline] - fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - unsafe {CURRENT_SYSSTATE_VEC.clear(); } - Ok(()) - } - - #[inline] - fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - unsafe {self.last_run = parse_last(&mut CURRENT_SYSSTATE_VEC);} - // let mut hasher = DefaultHasher::new(); - // let mut a = self.parse_last(); - // a[0].start_tick=21355; - // a[0].end_tick=2131; - // a.hash(&mut hasher); - // let somehash = hasher.finish(); - // println!("HashValue: {}",somehash); - // println!("{:#?}",self.parse_last()); - Ok(()) - } -} - -impl Named for QemuSysStateObserver -{ - #[inline] - fn name(&self) -> &str { - self.name.as_str() - } -} - -impl HasLen for QemuSysStateObserver -{ - #[inline] - fn len(&self) -> usize { - self.last_run.len() - } -} -pub fn parse_last(input: &mut Vec) -> Vec { -let mut ret = Vec::::new(); -let mut start_tick : u64 = 0; -for i in input.into_iter() { - let mut collector = Vec::::new(); - for j in i.prio_ready_lists.into_iter().rev() { - let mut tmp = list_to_tcb_vec_owned(j,&mut i.dumping_ground).iter().map(|x| MiniTCB::from_tcb(x)).collect(); - collector.append(&mut tmp); - } - ret.push(MiniFreeRTOSSystemState { - current_task: MiniTCB::from_tcb_owned(i.current_tcb), - start_tick: start_tick, - end_tick: i.qemu_tick, - ready_list_after: collector, - }); - start_tick=i.qemu_tick; -} -return ret; -} - -impl QemuSysStateObserver { - pub fn new() -> Self { - Self{last_run: vec![], name: "sysstate".to_string()} - } - -} - -pub fn list_to_tcb_vec(list: &List_t, dump: &HashMap) -> Vec -{ - let mut ret : Vec = Vec::new(); - if list.uxNumberOfItems == 0 {return ret;} - let first_list_index = match dump.get(&list.pxIndex).expect("List_t entry was not in Hashmap") { - List_Item_struct(li) => li.pxNext, - List_MiniItem_struct(mli) => match dump.get(&mli.pxNext).expect("MiniListItem pointer invaild") { - List_Item_struct(li) => li.pxNext, - _ => panic!("MiniListItem of a non empty List does not point to ListItem"), - }, - _ => panic!("List_t entry was not a ListItem"), - }; - let mut next_index = first_list_index; - for _ in 0..list.uxNumberOfItems { - let next_list_item = match dump.get(&next_index).expect("List_t entry was not in Hashmap") { - List_Item_struct(li) => li, - List_MiniItem_struct(mli) => match dump.get(&mli.pxNext).expect("MiniListItem pointer invaild") { - List_Item_struct(li) => li, - _ => panic!("MiniListItem of a non empty List does not point to ListItem"), - }, - _ => panic!("List_t entry was not a ListItem"), - }; - match dump.get(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { - TCB_struct(t) => {ret.push(*t)}, - _ => panic!("List content does not equal type"), - } - next_index=next_list_item.pxNext; - } - ret -} -pub fn list_to_tcb_vec_owned(list: List_t, dump: &mut HashMap) -> Vec -{ - let mut ret : Vec = Vec::new(); - if list.uxNumberOfItems == 0 {return ret;} - let last_list_item = match dump.remove(&list.pxIndex).expect("List_t entry was not in Hashmap") { - List_Item_struct(li) => li, - List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { - List_Item_struct(li) => li, - _ => panic!("MiniListItem of a non empty List does not point to ListItem"), - }, - _ => panic!("List_t entry was not a ListItem"), - }; - let mut next_index = last_list_item.pxNext; - let last_tcb = match dump.remove(&last_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { - TCB_struct(t) => t, - _ => panic!("List content does not equal type"), - }; - for _ in 0..list.uxNumberOfItems-1 { - let next_list_item = match dump.remove(&next_index).expect("List_t entry was not in Hashmap") { - List_Item_struct(li) => li, - List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { - List_Item_struct(li) => li, - _ => panic!("MiniListItem of a non empty List does not point to ListItem"), - }, - _ => panic!("List_t entry was not a ListItem"), - }; - match dump.remove(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { - TCB_struct(t) => {ret.push(t)}, - _ => panic!("List content does not equal type"), - } - next_index=next_list_item.pxNext; - } - ret.push(last_tcb); - ret -} -//============================= Feedback - -/// Shared Metadata for a SysStateFeedback -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct SysStateFeedbackState -{ - known_traces: HashMap, // encounters,ticks,length - longest: Vec, -} -impl Named for SysStateFeedbackState -{ - #[inline] - fn name(&self) -> &str { - "sysstate" - } -} -impl FeedbackState for SysStateFeedbackState -{ - fn reset(&mut self) -> Result<(), Error> { - self.longest.clear(); - self.known_traces.clear(); - Ok(()) - } -} - -/// A Feedback reporting novel System-State Transitions -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct NovelSysStateFeedback -{ - last_trace: Option>, - // known_traces: HashMap, -} - -impl Feedback for NovelSysStateFeedback -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 clock_observer = observers.match_name::("clock") //TODO not fixed - .expect("QemuSysStateObserver not found"); - let feedbackstate = state - .feedback_states_mut() - .match_name_mut::("sysstate") - .unwrap(); - // Do Stuff - let mut hasher = DefaultHasher::new(); - observer.last_run.hash(&mut hasher); - let somehash = hasher.finish(); - let mut is_novel = false; - let mut takes_longer = false; - match feedbackstate.known_traces.get_mut(&somehash) { - None => { - is_novel = true; - feedbackstate.known_traces.insert(somehash,(1,clock_observer.last_runtime(),observer.last_run.len())); - } - Some(s) => { - s.0+=1; - if s.1 < clock_observer.last_runtime() { - s.1 = clock_observer.last_runtime(); - takes_longer = true; - } - } - } - if observer.last_run.len() > feedbackstate.longest.len() { - feedbackstate.longest=observer.last_run.clone(); - } - self.last_trace = Some(observer.last_run.clone()); - // if (!is_novel) { println!("not novel") }; - Ok(is_novel | takes_longer) - } - - /// Append to the testcase the generated metadata in case of a new corpus item - #[inline] - fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { - let a = self.last_trace.take(); - match a { - Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)), - None => (), - } - Ok(()) - } - - /// Discard the stored metadata in case that the testcase is not added to the corpus - #[inline] - fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - self.last_trace = None; - Ok(()) - } -} - -impl Named for NovelSysStateFeedback -{ - #[inline] - fn name(&self) -> &str { - "sysstate" - } -} \ No newline at end of file