WIP: store memory reads

This commit is contained in:
Alwin Berger 2024-08-12 15:31:56 +02:00
parent 05c17d3159
commit 3db17ceb98
3 changed files with 96 additions and 16 deletions

View File

@ -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<GuestAddr, String>,
isr_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
input_mem: Range<GuestAddr>,
tcb_addr: GuestAddr,
ready_queues: GuestAddr,
delay_queue: GuestAddr,
@ -116,6 +120,7 @@ impl QemuSystemStateHelper {
api_fn_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
isr_addrs: HashMap<GuestAddr, String>,
isr_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
input_mem: Range<GuestAddr>,
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::<QT, S>), false);
}
_hooks.jmps(Hook::Function(gen_jmp_is_syscall::<QT, S>), Hook::Function(trace_jmp::<QT, S>));
_hooks.reads(Hook::Function(gen_read_is_input::<QT, S>), Hook::Empty,Hook::Empty,Hook::Empty,Hook::Empty,Hook::Function(trace_reads::<QT, S>));
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::<i32, u32>(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<QT, S>(
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
_addr: *mut TCGTemp,
_info: MemAccessInfo,
) -> Option<u64>
where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
if let Some(h) = hooks.helpers().match_first_type::<QemuSystemStateHelper>() {
if h.app_range.contains(&pc) {
// println!("gen_read {:x}", pc);
return Some(1);
}
}
return None;
}
static mut INPUT_MEM : Range<GuestAddr> = 0..0;
static mut MEM_READ : Option<HashSet<GuestAddr>> = None;
pub fn trace_reads<QT, S>(
_hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
_size: usize
)
where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
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 {

View File

@ -48,7 +48,8 @@ pub struct RawFreeRTOSSystemState {
dumping_ground: HashMap<u32,freertos::rtos_struct>,
input_counter: u32,
edge: (GuestAddr,GuestAddr),
capture_point: (CaptureEvent,String)
capture_point: (CaptureEvent,String),
mem_reads: HashSet<u32>
}
/// List of system state dumps from QemuHelpers
static mut CURRENT_SYSTEMSTATE_VEC: Vec<RawFreeRTOSSystemState> = vec![];
@ -222,6 +223,7 @@ impl ExecInterval {
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct FreeRTOSSystemStateMetadata {
pub inner: Vec<ReducedFreeRTOSSystemState>,
// TODO: Add abbs and memory reads
trace_length: usize,
indices: Vec<usize>, // 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<GuestAddr>,
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<H: Hasher>(&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
}
}

View File

@ -32,6 +32,7 @@ pub struct QemuSystemStateObserver<I>
pub last_run: Vec<ReducedFreeRTOSSystemState>,
pub last_states: HashMap<u64, ReducedFreeRTOSSystemState>,
pub last_trace: Vec<ExecInterval>,
pub last_reads: Vec<HashSet<u32>>,
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<I> HasLen for QemuSystemStateObserver<I>
impl<I> QemuSystemStateObserver<I>
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<I> Default for QemuSystemStateObserver<I>
@ -137,7 +139,7 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap<u32,rtos_struct>) ->
ret
}
/// Drains a List of raw SystemStates to produce a refined trace
fn refine_system_states(mut input: Vec<RawFreeRTOSSystemState>) -> (Vec<ReducedFreeRTOSSystemState>, Vec<(u64, CaptureEvent, String, (u32, u32))>) {
fn refine_system_states(mut input: Vec<RawFreeRTOSSystemState>) -> (Vec<ReducedFreeRTOSSystemState>, Vec<(u64, CaptureEvent, String, (u32, u32), HashSet<u32>)>) {
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<RawFreeRTOSSystemState>) -> (Vec<ReducedF
delay_list_after: delay_list,
// input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER,
});
ret.1.push((i.qemu_tick, i.capture_point.0, i.capture_point.1.to_string(), i.edge));
ret.1.push((i.qemu_tick, i.capture_point.0, i.capture_point.1.to_string(), i.edge, i.mem_reads));
}
return ret;
}
/// Transform the states and metadata into a list of ExecIntervals
fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, CaptureEvent, String, (u32, u32))>) -> (Vec<ExecInterval>, HashMap<u64, ReducedFreeRTOSSystemState>, 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<ReducedFreeRTOSSystemState>, meta: Vec<(u64, CaptureEvent, String, (u32, u32), HashSet<u32>)>) -> (Vec<ExecInterval>, Vec<HashSet<u32>>, HashMap<u64, ReducedFreeRTOSSystemState>, bool) {
if trace.len() == 0 {return (Vec::new(), Vec::new(), HashMap::new(), true);}
let mut isr_stack : VecDeque<u8> = 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<ExecInterval> = vec![];
let mut reads: Vec<HashSet<u32>> = vec![];
let mut edges: Vec<(u32, u32)> = vec![];
let mut last_hash : u64 = trace[0].get_hash();
let mut table : HashMap<u64, ReducedFreeRTOSSystemState> = HashMap::new();
@ -244,14 +247,16 @@ fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, 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<ExecInterval>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, edges: &Vec<(u32, u32)>) -> bool {
let mut id_count = 0;
let mut ret = true;
let mut task_has_started : HashSet<String> = HashSet::new();
let mut wip_abb_trace : Vec<Rc<RefCell<AtomicBasicBlock>>> = vec![];
@ -273,21 +278,24 @@ fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeR
// 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 isr abb start
CaptureEvent::ISRStart => {
// 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<ExecInterval>, table: &HashMap<u64, ReducedFreeR
if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) {
// assert_eq!(open_abb, None);
ret &= open_abb.is_none();
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: 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: 0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}, instance_id: id_count})));
id_count+=1;
open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i);
task_has_started.insert(curr_name.clone());
} else {
@ -310,7 +319,8 @@ fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeR
// println!("Continued block with no start {} {} {:?} {:?} {:x}-{:x} {} {}", curr_name, trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, edges[i].0, edges[i].1, task_has_started.contains(curr_name),trace[i].level);
// println!("{:x?}", open_abb_at_this_ret_addr_and_task);
ret = false;
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].1, 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].1, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}, instance_id: id_count})));
id_count+=1;
}
}
},