diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 0fc7560507..edc1afd8e7 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -78,6 +78,37 @@ pub fn try_load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> } else {ret} } +pub fn get_function_range(elf: &EasyElf, symbol: &'static str) -> Option> { + let gob = elf.goblin(); + + let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect(); + funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); + + for sym in &gob.syms { + if let Some(sym_name) = gob.strtab.get_at(sym.st_name) { + if sym_name == symbol { + if sym.st_value == 0 { + return None; + } else { + //#[cfg(cpu_target = "arm")] + // Required because of arm interworking addresses aka bit(0) for thumb mode + let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr); + //#[cfg(not(cpu_target = "arm"))] + //let addr = sym.st_value as GuestAddr; + // look for first function after addr + let sym_end = funcs.iter().find(|x| x.st_value > sym.st_value); + if let Some(sym_end) = sym_end { + println!("{} {:#x}..{} {:#x}", gob.strtab.get_at(sym.st_name).unwrap_or(""),addr, gob.strtab.get_at(sym_end.st_name).unwrap_or(""),sym_end.st_value & !0x1); + return Some(addr..((sym_end.st_value & !0x1) as GuestAddr)); + } + return None; + }; + } + } + } + return None; +} + extern "C" { static mut libafl_interrupt_offsets : [u32; 32]; static mut libafl_num_interrupts : usize; @@ -350,6 +381,19 @@ pub fn fuzz() { api_addreses.insert(sb,s); } } + #[cfg(feature = "systemstate")] + let mut isr_addreses : HashMap = HashMap::new(); + #[cfg(feature = "systemstate")] + for s in systemstate::helpers::ISR_SYMBOLS { + if let Some(sb) = try_load_symbol(&elf, &s, false) { + isr_addreses.insert(sb & !1,s); + } + } + + #[cfg(feature = "systemstate")] + let mut api_ranges : Vec<(&'static str,std::ops::Range)> = systemstate::helpers::API_SYMBOLS.iter().filter_map(|x| get_function_range(&elf, x).map(|y| (*x,y))).collect(); + #[cfg(feature = "systemstate")] + let mut isr_ranges : Vec<(&'static str,std::ops::Range)> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| get_function_range(&elf, x).map(|y| (*x,y))).collect(); // Client setup ================================================================================ @@ -545,7 +589,7 @@ pub fn fuzz() { let qhelpers = tuple_list!( QemuEdgeCoverageHelper::default(), QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(api_addreses,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,input_counter_ptr,app_range.clone()) + QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,input_counter_ptr,app_range.clone()) ); let mut hooks = QemuHooks::new(emu.clone(),qhelpers); diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 7342715e84..08a80d07d4 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -19,6 +19,7 @@ use super::freertos::TCB_t; use super::freertos::rtos_struct::List_Item_struct; use super::freertos::rtos_struct::*; use super::freertos; +use super::CaptureEvent; use libafl_qemu::{ helper::{QemuHelper, QemuHelperTuple}, @@ -35,7 +36,7 @@ pub static mut NEXT_INPUT : Vec = Vec::new(); //============================= API symbols -pub static API_SYMBOLS : &'static [&str] = &[ +pub const API_SYMBOLS : &'static [&'static str] = &[ // Task Creation "xTaskCreate", "xTaskCreateStatic", "vTaskDelete", "xTaskGetStaticBuffers", // Task Control @@ -65,7 +66,13 @@ pub static API_SYMBOLS : &'static [&str] = &[ // Co-routines "xCoRoutineCreate","crDELAY","crQUEUE_SEND","crQUEUE_RECEIVE","crQUEUE_SEND_FROM_ISR","crQUEUE_RECEIVE_FROM_ISR","vCoRoutineSchedule", // Custom resolved macros -"vPortEnterCritical","vPortExitCritical","xTaskGenericNotify","xTaskGenericNotifyFromISR","xQueueGenericSend","xQueueGenericReset" +"vPortEnterCritical","vPortExitCritical","xTaskGenericNotify","xTaskGenericNotifyFromISR","xQueueGenericSend","xQueueGenericReset","ulTaskGenericNotifyTake","xTimerCreateTimerTask", +"pvPortMalloc","prvAddNewTaskToReadyList","prvUnlockQueue","prvCheckForValidListAndQueue","prvProcessTimerOrBlockTask","prvInitialiseNewQueue","prvSampleTimeNow" +]; + +pub const ISR_SYMBOLS : &'static [&'static str] = &[ +// ISRs +"Reset_Handler","Default_Handler","Default_Handler2","Default_Handler3","Default_Handler4","Default_Handler5","Default_Handler6","vPortSVCHandler","xPortPendSVHandler","xPortSysTickHandler","isr_starter" ]; //============================= Qemu Helper @@ -73,7 +80,12 @@ pub static API_SYMBOLS : &'static [&str] = &[ /// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs #[derive(Debug)] pub struct QemuSystemStateHelper { - watchaddr: HashMap, + // Address of API functions + api_fn_addrs: HashMap, + api_fn_ranges: Vec<(&'static str, std::ops::Range)>, + // Address of interrupt routines + isr_addrs: HashMap, + isr_ranges: Vec<(&'static str, std::ops::Range)>, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -85,7 +97,10 @@ pub struct QemuSystemStateHelper { impl QemuSystemStateHelper { #[must_use] pub fn new( - watchaddr: HashMap, + api_fn_addrs: HashMap, + api_fn_ranges: Vec<(&'static str, std::ops::Range)>, + isr_addrs: HashMap, + isr_ranges: Vec<(&'static str, std::ops::Range)>, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -94,7 +109,10 @@ impl QemuSystemStateHelper { app_range: Range, ) -> Self { QemuSystemStateHelper { - watchaddr: watchaddr, + api_fn_addrs, + api_fn_ranges, + isr_addrs, + isr_ranges, tcb_addr: tcb_addr, ready_queues: ready_queues, delay_queue, @@ -113,10 +131,13 @@ where where QT: QemuHelperTuple, { - for wp in self.watchaddr.keys() { + for wp in self.api_fn_addrs.keys() { _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::), false); } - #[cfg(feature = "trace_abbs")] + for wp in self.isr_addrs.keys() { + _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); + } + //#[cfg(feature = "trace_abbs")] _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_api_call::)); } @@ -130,7 +151,7 @@ where } fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { - trigger_collection(emulator,0, self); + trigger_collection(emulator,(None, None), self); } } @@ -166,7 +187,7 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul } #[inline] -fn trigger_collection(emulator: &Emulator, pc: GuestAddr, h: &QemuSystemStateHelper) { +fn trigger_collection(emulator: &Emulator, edge: (Option,Option), h: &QemuSystemStateHelper) { let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); let mut systemstate = RawFreeRTOSSystemState::default(); unsafe { @@ -190,16 +211,6 @@ fn trigger_collection(emulator: &Emulator, pc: GuestAddr, h: &QemuSystemStateHel }; systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); - unsafe { - LAST_API_CALL.with(|x| - match *x.get() { - Some(s) => { - systemstate.last_pc = Some(s.0 as u64); - }, - None => (), - } - ); - } // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); // Extract delay list @@ -218,11 +229,63 @@ fn trigger_collection(emulator: &Emulator, pc: GuestAddr, h: &QemuSystemStateHel systemstate.prio_ready_lists[i] = read_freertos_list(&mut systemstate, emulator, target); } - systemstate.capture_point = h.watchaddr.get(&pc).unwrap_or(&"unknown"); + // Note type of capture + match edge.1 { + None => { // No destination set, must be ISR Return + // ISR End + if let Some(src) = edge.0 { + if let Some(s) = h.isr_addrs.get(&src) { + systemstate.capture_point=(CaptureEvent::ISREnd, s); + } else { + println!("ISR Ret Not found: {:#x}", src); + systemstate.capture_point=(CaptureEvent::ISREnd, ""); + } + } + }, + Some(dest) => { + if let Some(src) = edge.0 { // Bot set, can be API Call/Ret + if let Some(s) = h.api_fn_addrs.get(&src) { // API End + systemstate.capture_point=(CaptureEvent::APIEnd, s); + } else if let Some(s) = h.api_fn_addrs.get(&dest) { // API Call + systemstate.capture_point=(CaptureEvent::APIStart, s); + } else { + println!("API Not found: {:#x}", src); + } + } else { // No source, must be ISR + if let Some(s) = h.isr_addrs.get(&dest) { // ISR Start + systemstate.capture_point=(CaptureEvent::ISRStart, s); + } else { + println!("ISR call Not found: {:#x}", dest); + } + } + }, + } + if systemstate.capture_point.0 == CaptureEvent::Undefined { + println!("Not found: {:#x} {:#x}", edge.0.unwrap_or(0), edge.1.unwrap_or(0)); + } + systemstate.edge = edge; unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); } } +//============================= Trace interrupt service routines + +pub fn exec_isr_hook( + hooks: &mut QemuHooks, + _state: Option<&mut S>, + pc: GuestAddr, +) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + let emulator = hooks.emulator(); + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + trigger_collection(emulator, (None,Some(pc)), h); +} + +//============================= Trace syscall execution + pub fn exec_syscall_hook( hooks: &mut QemuHooks, _state: Option<&mut S>, @@ -234,9 +297,25 @@ where { let emulator = hooks.emulator(); let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - trigger_collection(emulator, pc, h); + + let mut edge = (None, Some(pc)); + unsafe { + LAST_API_CALL.with(|x| { + match *x.get() { + Some(s) => { + edge.0=Some(s.0); + trigger_collection(emulator, edge, h); + }, + None => (), + } + *x.get()=None; + }); + } + } +//============================= Trace jumps to syscalls + thread_local!(static LAST_API_CALL : UnsafeCell> = UnsafeCell::new(None)); pub fn gen_jmp_is_syscall( @@ -251,15 +330,27 @@ where { if let Some(h) = hooks.helpers().match_first_type::() { if h.app_range.contains(&src) && !h.app_range.contains(&dest) { - // println!("New jmp {:x} {:x}", src, dest); - return Some(1); + if let Some(_) = in_any_range(&h.api_fn_ranges,dest) { + // println!("New jmp {:x} {:x}", src, dest); + // println!("API Call Edge"); + return Some(1); + } + } else if !h.app_range.contains(&src) && dest == 0 { + if let Some(_) = in_any_range(&h.api_fn_ranges, src) { + // println!("API Return Edge {:#x}", src); + return Some(2); + } + if let Some(_) = in_any_range(&h.isr_ranges, src) { + // println!("ISR Return Edge {:#x}", src); + return Some(3); + } } } return None; } pub fn trace_api_call( - _hooks: &mut QemuHooks, + hooks: &mut QemuHooks, _state: Option<&mut S>, src: GuestAddr, dest: GuestAddr, id: u64 ) @@ -267,9 +358,39 @@ where S: UsesInput, QT: QemuHelperTuple, { - unsafe { - let p = LAST_API_CALL.with(|x| x.get()); - *p = Some((src,dest)); - // print!("*"); + if id == 1 { // API call + unsafe { + let p = LAST_API_CALL.with(|x| x.get()); + *p = Some((src,dest)); + // println!("Jump {:#x} {:#x}", src, dest); + } + } else if id == 2 { // API return + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + if in_any_range(&h.api_fn_ranges, dest).is_none() { + let emulator = hooks.emulator(); + + let mut edge = (None, None); + edge.0=Some(in_any_range(&h.api_fn_ranges, src).unwrap().start); + edge.1=Some(dest); + + trigger_collection(emulator, edge, h); + // println!("Exec API Return Edge {:#x} {:#x}", src, dest); + } + } else if id == 3 { // ISR return + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + let emulator = hooks.emulator(); + + let mut edge = (None, None); + edge.0=Some(in_any_range(&h.isr_ranges, src).unwrap().start); + + trigger_collection(emulator, edge, h); + // println!("Exec ISR Return Edge {:#x} {:#x}", src, dest); } +} + +pub fn in_any_range<'a>(ranges: &'a Vec<(&str, Range)>, addr : GuestAddr) -> Option<&'a std::ops::Range> { + for (_,r) in ranges { + if r.contains(&addr) {return Some(r);} + } + return None; } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 3a46aff80b..7de704a19b 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -27,8 +27,21 @@ pub mod schedulers; const NUM_PRIOS: usize = 5; //============================= Struct definitions + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] +pub enum CaptureEvent { + APIStart, + APIEnd, + ISRStart, + ISREnd, + End, + #[default] + Undefined, +} + + /// Raw info Dump from Qemu -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Default)] pub struct RawFreeRTOSSystemState { qemu_tick: u64, current_tcb: TCB_t, @@ -37,8 +50,8 @@ pub struct RawFreeRTOSSystemState { delay_list_overflow: freertos::List_t, dumping_ground: HashMap, input_counter: u32, - last_pc: Option, - capture_point: &'static str + edge: (Option,Option), + capture_point: (CaptureEvent,&'static str) } /// List of system state dumps from QemuHelpers static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; @@ -110,12 +123,13 @@ impl RefinedTCB { pub struct RefinedFreeRTOSSystemState { pub start_tick: u64, pub end_tick: u64, - last_pc: Option, + edge: (Option,Option), input_counter: u32, pub current_task: (RefinedTCB, u32), ready_list_after: Vec<(RefinedTCB, u32)>, delay_list_after: Vec<(RefinedTCB, u32)>, - pub capture_point: String + // pub capture_point: String + pub capture_point: (CaptureEvent,String) } impl PartialEq for RefinedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 963f12758b..3797ae090d 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -162,8 +162,8 @@ fn refine_system_states(input: &mut Vec) -> Vec