From b73a971c51a78a9aae4e52c04c61bbc6e4ac37c5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 17 Feb 2022 19:47:18 +0100 Subject: [PATCH] add sysstate feedback --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 35 ++- fuzzers/wcet_qemu_sys/src/system_trace.rs | 266 +++++++++++++++++----- 2 files changed, 236 insertions(+), 65 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index a5bf2c827c..8762b992eb 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,11 @@ //! 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::worst::QemuHashMapObserver; use hashbrown::HashMap; use clap::{App, Arg}; @@ -238,6 +244,21 @@ fn fuzz( let check_breakpoint = virt2phys(check_breakpoint,&elf.goblin()); println!("Breakpoint at {:#x}", check_breakpoint); + let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers + .resolve_symbol("pxCurrentTCB", 0) + .expect("Symbol pxCurrentTCBC not found"); + // let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf.goblin()); + println!("TCB pointer at {:#x}", curr_tcb_pointer); + let task_queue_addr = elf + .resolve_symbol("pxReadyTasksLists", 0) + .expect("Symbol pxReadyTasksLists not found"); + // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); + println!("Task Queue at {:#x}", task_queue_addr); + let svh = elf + .resolve_symbol("xPortPendSVHandler", 0) + .expect("Symbol xPortPendSVHandler not found"); + let svh = virt2phys(svh,&elf.goblin()); + //=========== Prepare Emulator Args let args: Vec = vec![ "qemu-system-arm", @@ -288,6 +309,9 @@ fn fuzz( // The state of the edges feedback. let feedback_state = MapFeedbackState::with_observer(&edges_observer); + + let sysstate_observer = QemuSysStateObserver::new(); + let sysstate_feedback_state = SysStateFeedbackState::default(); // Feedback to rate the interestingness of an input // This one is composed by two Feedbacks in OR @@ -304,7 +328,8 @@ fn fuzz( MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), HitImprovingFeedback::new(target_map.clone(), &edges_observer), QemuClockIncreaseFeedback::default(), - ClockFeedback::new_with_observer(&clock_observer) + ClockFeedback::new_with_observer(&clock_observer), + NovelSysStateFeedback::default() ); // A feedback to choose if an input is a solution or not @@ -323,7 +348,7 @@ fn fuzz( OnDiskCorpus::new(&objective_dir).unwrap(), // States of the feedbacks. // They are the data related to the feedbacks that you want to persist in the State. - tuple_list!(feedback_state,clock::MaxIcountMetadata::default()), + tuple_list!(feedback_state,clock::MaxIcountMetadata::default(),sysstate_feedback_state), ) }); @@ -365,6 +390,7 @@ fn fuzz( ExitKind::Ok }; + let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1]); let executor = QemuExecutor::new( &mut harness, &emu, @@ -372,9 +398,10 @@ fn fuzz( QemuEdgeCoverageHelper::new(), QemuCmpLogHelper::new(), //QemuAsanHelper::new(), - QemuSysSnapshotHelper::new() + QemuSysSnapshotHelper::new(), + QemuSystemStateHelper::with_instrumentation_filter(system_state_filter,curr_tcb_pointer.try_into().unwrap(),task_queue_addr.try_into().unwrap()) ), - tuple_list!(edges_observer, clock_observer), + tuple_list!(edges_observer, clock_observer,sysstate_observer), &mut fuzzer, &mut state, &mut mgr, diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs index 041ed78208..ee955099ba 100644 --- a/fuzzers/wcet_qemu_sys/src/system_trace.rs +++ b/fuzzers/wcet_qemu_sys/src/system_trace.rs @@ -1,3 +1,10 @@ +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; @@ -37,7 +44,7 @@ pub struct FreeRTOSSystemStateRaw { dumping_ground: HashMap, } -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct MiniTCB { task_name: String, priority: u32, @@ -47,6 +54,16 @@ pub struct MiniTCB { 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 { @@ -62,18 +79,49 @@ impl MiniTCB { } } } + 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)] -pub struct QemuSystemStateMetadata { +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct MiniFreeRTOSSystemState { start_tick: u64, end_tick: u64, current_task: MiniTCB, ready_list_after: Vec, } -libafl::impl_serdeany!(QemuSystemStateMetadata); +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 @@ -206,7 +254,7 @@ pub fn gen_not_exec_block_hook( #[allow(clippy::unsafe_derive_deserialize)] pub struct QemuSysStateObserver { - last_run: Vec, + last_run: Vec, name: String, } @@ -220,8 +268,15 @@ impl Observer for QemuSysStateObserver #[inline] fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - unsafe {self.last_run.append(&mut CURRENT_SYSSTATE_VEC);} - println!("{:#?}",self.parse_last()); + 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(()) } } @@ -241,31 +296,31 @@ impl HasLen for QemuSysStateObserver 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 parse_last(&self) -> Vec { - let mut ret = Vec::::new(); - let mut start_tick : u64 = 0; - for i in 0..self.last_run.len() { - let mut collector = Vec::::new(); - for j in self.last_run[i].prio_ready_lists.iter().rev() { - let mut tmp = list_to_tcb_vec(j,&self.last_run[i].dumping_ground).iter().map(|x| MiniTCB::from_tcb(x)).collect(); - collector.append(&mut tmp); - } - ret.push(QemuSystemStateMetadata { - current_task: MiniTCB::from_tcb(&self.last_run[i].current_tcb), - start_tick: start_tick, - end_tick: self.last_run[i].qemu_tick, - ready_list_after: collector, - }); - start_tick=self.last_run[i].qemu_tick; - } - return ret; - } } pub fn list_to_tcb_vec(list: &List_t, dump: &HashMap) -> Vec @@ -298,51 +353,140 @@ pub fn list_to_tcb_vec(list: &List_t, dump: &HashMap) -> Vec) -> 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 -/// A Feedback reporting interesting System-State Transitions -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct SysStateFeedback +/// Shared Metadata for a SysStateFeedback +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysStateFeedbackState { + known_traces: HashMap, + longest: Vec, } - -impl Feedback for SysStateFeedback -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"); - // Do Stuff - Ok(false) - } -} - -impl Named for SysStateFeedback +impl Named for SysStateFeedbackState { #[inline] fn name(&self) -> &str { "sysstate" } } - -impl SysStateFeedback +impl FeedbackState for SysStateFeedbackState { - /// Creates a new [`SysStateFeedback`] - #[must_use] - pub fn new() -> Self { - Self {} + 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 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; + match feedbackstate.known_traces.get_mut(&somehash) { + None => { + is_novel = true; + feedbackstate.known_traces.insert(somehash,(1,observer.last_run.len())); + } + Some(s) => s.0+=1, + } + 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) + } + + /// 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