From 3db17ceb982031921275d119b4d3ab1aeb71cb06 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 Aug 2024 15:31:56 +0200 Subject: [PATCH] WIP: store memory reads --- fuzzers/FRET/src/systemstate/helpers.rs | 55 +++++++++++++++++++++++ fuzzers/FRET/src/systemstate/mod.rs | 19 +++++++- fuzzers/FRET/src/systemstate/observers.rs | 38 ++++++++++------ 3 files changed, 96 insertions(+), 16 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 13fe53eedb..1e75cc24fa 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -1,5 +1,6 @@ use std::ops::Range; use hashbrown::HashMap; +use hashbrown::HashSet; use libafl::prelude::ExitKind; use libafl::prelude::UsesInput; use libafl_qemu::elf::EasyElf; @@ -9,6 +10,8 @@ use libafl_qemu::GuestPhysAddr; use libafl_qemu::QemuHooks; use libafl_qemu::Hook; use libafl_qemu::helpers::{QemuHelper, QemuHelperTuple}; +use libafl_qemu::sys::TCGTemp; +use libafl_qemu::qemu::MemAccessInfo; use crate::systemstate::RawFreeRTOSSystemState; use crate::systemstate::CURRENT_SYSTEMSTATE_VEC; @@ -98,6 +101,7 @@ pub struct QemuSystemStateHelper { // Address of interrupt routines isr_addrs: HashMap, isr_ranges: Vec<(String, std::ops::Range)>, + input_mem: Range, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -116,6 +120,7 @@ impl QemuSystemStateHelper { api_fn_ranges: Vec<(String, std::ops::Range)>, isr_addrs: HashMap, isr_ranges: Vec<(String, std::ops::Range)>, + input_mem: Range, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -131,6 +136,7 @@ impl QemuSystemStateHelper { api_fn_ranges, isr_addrs, isr_ranges, + input_mem, tcb_addr: tcb_addr, ready_queues: ready_queues, delay_queue, @@ -159,6 +165,8 @@ where _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); } _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_jmp::)); + _hooks.reads(Hook::Function(gen_read_is_input::), Hook::Empty,Hook::Empty,Hook::Empty,Hook::Empty,Hook::Function(trace_reads::)); + unsafe { INPUT_MEM = self.input_mem.clone() }; } // TODO: refactor duplicate code @@ -173,6 +181,7 @@ where unsafe { let c = emulator.cpu_from_index(0); let pc = c.read_reg::(15).unwrap(); + if CURRENT_SYSTEMSTATE_VEC.len() == 0 {return;} CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].edge = (pc,0); CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].capture_point = (CaptureEvent::End,"Breakpoint".to_string()); } @@ -297,6 +306,7 @@ fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr) systemstate.prio_ready_lists[i] = read_freertos_list(&mut systemstate, emulator, target); } } + systemstate.mem_reads = unsafe { MEM_READ.take().unwrap_or_default() }; @@ -393,6 +403,51 @@ where } } +//============================= Read Hooks +pub fn gen_read_is_input( + hooks: &mut QemuHooks, + _state: Option<&mut S>, + pc: GuestAddr, + _addr: *mut TCGTemp, + _info: MemAccessInfo, +) -> Option +where + S: UsesInput, + QT: QemuHelperTuple, +{ + if let Some(h) = hooks.helpers().match_first_type::() { + if h.app_range.contains(&pc) { + // println!("gen_read {:x}", pc); + return Some(1); + } + } + return None; +} + +static mut INPUT_MEM : Range = 0..0; +static mut MEM_READ : Option> = None; + +pub fn trace_reads( + _hooks: &mut QemuHooks, + _state: Option<&mut S>, + _id: u64, + addr: GuestAddr, + _size: usize +) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + if unsafe { INPUT_MEM.contains(&addr) } { + if unsafe { MEM_READ.is_none() } { + unsafe { MEM_READ = Some(HashSet::from([addr])) }; + } else { + unsafe { MEM_READ.as_mut().unwrap().insert(addr) }; + } + // println!("exec_read {:x} {}", addr, size); + } +} + //============================= Utility functions fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 { diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 5928da1d76..b7b2e94a3b 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -48,7 +48,8 @@ pub struct RawFreeRTOSSystemState { dumping_ground: HashMap, input_counter: u32, edge: (GuestAddr,GuestAddr), - capture_point: (CaptureEvent,String) + capture_point: (CaptureEvent,String), + mem_reads: HashSet } /// List of system state dumps from QemuHelpers static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; @@ -222,6 +223,7 @@ impl ExecInterval { #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { pub inner: Vec, + // TODO: Add abbs and memory reads trace_length: usize, indices: Vec, // Hashed enumeration of States tcref: isize, @@ -262,13 +264,22 @@ impl HasRefCnt for FreeRTOSSystemStateMetadata { libafl_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata); -#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[derive(Default, Serialize, Deserialize, Clone)] pub struct AtomicBasicBlock { start: GuestAddr, ends: HashSet, level: u8, + instance_id: usize } +impl PartialEq for AtomicBasicBlock { + fn eq(&self, other: &Self) -> bool { + self.start == other.start && self.ends == other.ends && self.level == other.level + } +} + +impl Eq for AtomicBasicBlock {} + impl Hash for AtomicBasicBlock { fn hash(&self, state: &mut H) { // Use a combination of the start address and the set of ending addresses to compute the hash value @@ -340,6 +351,10 @@ impl AtomicBasicBlock { self.hash(&mut s); s.finish() } + + pub fn instance_eq(&self, other: &Self) -> bool { + self == other && self.instance_id == other.instance_id + } } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index e88309e96a..d16c1c75b0 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -32,6 +32,7 @@ pub struct QemuSystemStateObserver pub last_run: Vec, pub last_states: HashMap, pub last_trace: Vec, + pub last_reads: Vec>, pub last_input: I, pub success: bool, name: Cow<'static, str>, @@ -57,8 +58,9 @@ where // println!("{:?}",temp); let temp = states2intervals(temp.0, temp.1); self.last_trace = temp.0; - self.last_states = temp.1; - self.success = temp.2; + self.last_reads = temp.1; + self.last_states = temp.2; + self.success = temp.3; // println!("{:?}",temp); } // let abbs = extract_abbs_from_trace(&self.last_run); @@ -88,7 +90,7 @@ impl HasLen for QemuSystemStateObserver impl QemuSystemStateObserver where I: Default { pub fn new() -> Self { - Self{last_run: vec![], last_trace: vec![], last_input: I::default(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false } + Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false } } } impl Default for QemuSystemStateObserver @@ -137,7 +139,7 @@ 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(mut input: Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32))>) { +fn refine_system_states(mut input: Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) { let mut ret = (Vec::<_>::new(), Vec::<_>::new()); for mut i in input.drain(..) { let cur = RefinedTCB::from_tcb_owned(i.current_tcb); @@ -160,20 +162,21 @@ fn refine_system_states(mut input: Vec) -> (Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32))>) -> (Vec, HashMap, bool) { - if trace.len() == 0 {return (Vec::new(), HashMap::new(), true);} +/// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success +fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) -> (Vec, Vec>, HashMap, bool) { + if trace.len() == 0 {return (Vec::new(), Vec::new(), HashMap::new(), true);} let mut isr_stack : VecDeque = VecDeque::from([]); // 2+ = ISR, 1 = systemcall, 0 = APP. Trace starts with an ISREnd and executes the app let mut level_of_task : HashMap<&str, u8> = HashMap::new(); let mut ret: Vec = vec![]; + let mut reads: Vec> = vec![]; let mut edges: Vec<(u32, u32)> = vec![]; let mut last_hash : u64 = trace[0].get_hash(); let mut table : HashMap = HashMap::new(); @@ -244,14 +247,16 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt tick_spend_preempted: 0, abb: None }); + reads.push(meta[i].4.clone()); last_hash = next_hash; edges.push((meta[i].3.1, meta[i+1].3.0)); } let t = add_abb_info(&mut ret, &table, &edges); - (ret, table, t) + (ret, reads, table, t) } fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(u32, u32)>) -> bool { + let mut id_count = 0; let mut ret = true; let mut task_has_started : HashSet = HashSet::new(); let mut wip_abb_trace : Vec>> = vec![]; @@ -273,21 +278,24 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap { // assert_eq!(open_abb, None); ret &= open_abb.is_none(); open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i); - wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}, instance_id: id_count}))); + id_count+=1; }, // generic app abb start CaptureEvent::APIEnd => { // assert_eq!(open_abb, None); ret &= open_abb.is_none(); open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i); - wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}, instance_id: id_count}))); + id_count+=1; }, // generic continued blocks CaptureEvent::ISREnd => { @@ -295,7 +303,8 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap, table: &HashMap