two-way isr edges, graceful parsing error handling

This commit is contained in:
Alwin Berger 2024-06-14 13:56:36 +02:00
parent 69d0c6f9bb
commit 1146c2c1e5
6 changed files with 242 additions and 217 deletions

View File

@ -14,7 +14,7 @@ edges::{self, edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM}, elf::
}; };
use rand::{SeedableRng, StdRng, Rng}; use rand::{SeedableRng, StdRng, Rng};
use crate::{ use crate::{
clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback, SystraceErrorFeedback}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler}
}; };
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
@ -549,7 +549,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| {
); );
// A feedback to choose if an input is a solution or not // A feedback to choose if an input is a solution or not
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new()); let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::new(cli.dump_cases));
// If not restarting, create a State from scratch // If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| { let mut state = state.unwrap_or_else(|| {

View File

@ -13,7 +13,7 @@ use libafl::{
corpus::{self, Corpus}, fuzzer::Evaluator, mark_feature_time, prelude::{new_hash_feedback, CorpusId, HasBytesVec, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasMetadata, HasNamedMetadata, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error corpus::{self, Corpus}, fuzzer::Evaluator, mark_feature_time, prelude::{new_hash_feedback, CorpusId, HasBytesVec, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasMetadata, HasNamedMetadata, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error
}; };
use libafl::prelude::State; use libafl::prelude::State;
use crate::{clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; use crate::{clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}};
pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC;
// one isn per 2**4 ns // one isn per 2**4 ns
@ -106,7 +106,7 @@ where
} }
// produce a slice of absolute interrupt times // produce a slice of absolute interrupt times
let mut interrupt_offsets : [u32; 32] = [u32::MAX; 32]; let mut interrupt_offsets : [u32; MAX_NUM_INTERRUPT] = [u32::MAX; MAX_NUM_INTERRUPT];
let mut num_interrupts : usize = 0; let mut num_interrupts : usize = 0;
{ {
let t = input_bytes_to_interrupt_times(&target_bytes); let t = input_bytes_to_interrupt_times(&target_bytes);

View File

@ -224,4 +224,60 @@ impl DumpSystraceFeedback
pub fn metadata_only() -> Self { pub fn metadata_only() -> Self {
Self {dumpfile: None, dump_metadata: true, last_trace: None, last_states: None} Self {dumpfile: None, dump_metadata: true, last_trace: None, last_states: None}
} }
}
#[derive(Debug, Default)]
pub struct SystraceErrorFeedback
{
dump_case: bool
}
impl<S> Feedback<S> for SystraceErrorFeedback
where
S: State + UsesInput + MaybeHasClientPerfMonitor,
{
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &S::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>
{
let observer = observers.match_name::<QemuSystemStateObserver>("systemstate")
.expect("QemuSystemStateObserver not found");
Ok(self.dump_case&&!observer.success)
}
/// Append to the testcase the generated metadata in case of a new corpus item
#[inline]
fn append_metadata<OT>(&mut self, _state: &mut S, _observers: &OT, _testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
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: &S::Input) -> Result<(), Error> {
Ok(())
}
}
impl Named for SystraceErrorFeedback
{
#[inline]
fn name(&self) -> &str {
"SystraceErrorFeedback"
}
}
impl SystraceErrorFeedback
{
#[must_use]
pub fn new(dump_case: bool) -> Self {
Self {dump_case}
}
} }

View File

@ -4,6 +4,7 @@ use std::ops::Range;
use hashbrown::HashMap; use hashbrown::HashMap;
use libafl::prelude::ExitKind; use libafl::prelude::ExitKind;
use libafl::prelude::UsesInput; use libafl::prelude::UsesInput;
use libafl_qemu::read_user_reg_unchecked;
use libafl_qemu::Emulator; use libafl_qemu::Emulator;
use libafl_qemu::GuestAddr; use libafl_qemu::GuestAddr;
use libafl_qemu::GuestPhysAddr; use libafl_qemu::GuestPhysAddr;
@ -115,24 +116,22 @@ where
for wp in self.isr_addrs.keys() { for wp in self.isr_addrs.keys() {
_hooks.instruction(*wp, Hook::Function(exec_isr_hook::<QT, S>), false); _hooks.instruction(*wp, Hook::Function(exec_isr_hook::<QT, S>), false);
} }
_hooks.jmps(Hook::Function(gen_jmp_is_syscall::<QT, S>), Hook::Function(trace_api_call::<QT, S>)); _hooks.jmps(Hook::Function(gen_jmp_is_syscall::<QT, S>), Hook::Function(trace_jmp::<QT, S>));
} }
// TODO: refactor duplicate code // TODO: refactor duplicate code
fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) { fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) {
unsafe { unsafe {
CURRENT_SYSTEMSTATE_VEC.clear(); CURRENT_SYSTEMSTATE_VEC.clear();
let p = LAST_API_CALL.with(|x| x.get());
*p = None;
} }
} }
fn post_exec<OT>(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { fn post_exec<OT>(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) {
trigger_collection(emulator,(None, None), self); trigger_collection(emulator,(0, 0), CaptureEvent::End, self);
unsafe { unsafe {
let c = emulator.cpu_from_index(0); let c = emulator.cpu_from_index(0);
let pc = c.read_reg::<i32, u32>(15).unwrap(); let pc = c.read_reg::<i32, u32>(15).unwrap();
CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].edge = (Some(pc),None); 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()); CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].capture_point = (CaptureEvent::End,"Breakpoint".to_string());
} }
// Find the first ISREnd of vPortSVCHandler and drop anything before // Find the first ISREnd of vPortSVCHandler and drop anything before
@ -188,62 +187,38 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul
} }
#[inline] #[inline]
fn trigger_collection(emulator: &Emulator, edge: (Option<GuestAddr>,Option<GuestAddr>), h: &QemuSystemStateHelper) { fn trigger_collection(emulator: &Emulator, edge: (GuestAddr, GuestAddr), event: CaptureEvent, h: &QemuSystemStateHelper) {
let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::<freertos::List_t>()).unwrap(); let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::<freertos::List_t>()).unwrap();
let mut systemstate = RawFreeRTOSSystemState::default(); let mut systemstate = RawFreeRTOSSystemState::default();
// Note type of capture match event {
match edge.1 { CaptureEvent::APIStart => {
None => { // No destination set, must be ISR Return let s = h.api_fn_addrs.get(&edge.1).unwrap();
// ISR End systemstate.capture_point=(CaptureEvent::APIStart, s.to_string());
if let Some(src) = edge.0 {
if let Some(s) = h.isr_addrs.get(&src) {
systemstate.capture_point=(CaptureEvent::ISREnd, s.to_string());
} else {
println!("ISR Ret Not found: {:#x}", src);
systemstate.capture_point=(CaptureEvent::ISREnd, "".to_string());
}
}
}, },
Some(dest) => { CaptureEvent::APIEnd => {
if let Some(src) = edge.0 { // Both set, can be API Call/Ret let s = h.api_fn_addrs.get(&edge.0).unwrap();
if let Some(s) = h.api_fn_addrs.get(&src) { // API End systemstate.capture_point=(CaptureEvent::APIEnd, s.to_string());
systemstate.capture_point=(CaptureEvent::APIEnd, s.to_string());
if !h.app_range.contains(&dest) {
println!("API Not found: {:#x} {:#x}", src, dest);
}
} else if let Some(s) = h.api_fn_addrs.get(&dest) { // API Call
// if let None = in_any_range(&h.isr_ranges, src) {
systemstate.capture_point=(CaptureEvent::APIStart, s.to_string());
// } else {
// return;
// }
} 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.to_string());
} else {
println!("ISR call Not found: {:#x}", dest);
}
}
}, },
CaptureEvent::ISRStart => {
let s = h.isr_addrs.get(&edge.1).unwrap();
systemstate.capture_point=(CaptureEvent::ISRStart, s.to_string());
},
CaptureEvent::ISREnd => {
let s = h.isr_addrs.get(&edge.0).unwrap();
systemstate.capture_point=(CaptureEvent::ISREnd, s.to_string());
},
CaptureEvent::End => {systemstate.capture_point=(CaptureEvent::End, "".to_string());},
CaptureEvent::Undefined => (),
} }
if systemstate.capture_point.0 == CaptureEvent::Undefined { if systemstate.capture_point.0 == CaptureEvent::Undefined {
// println!("Not found: {:#x} {:#x}", edge.0.unwrap_or(0), edge.1.unwrap_or(0)); // println!("Not found: {:#x} {:#x}", edge.0.unwrap_or(0), edge.1.unwrap_or(0));
} }
systemstate.edge = edge; systemstate.edge = ((edge.0),(edge.1));
systemstate.qemu_tick = get_icount(emulator);
unsafe {
// TODO: investigate why can_do_io is not set sometimes, as this is just a workaround
let c = emulator.cpu_from_index(0);
let can_do_io = (*c.raw_ptr()).neg.can_do_io;
(*c.raw_ptr()).neg.can_do_io = true;
systemstate.qemu_tick = emu::icount_get_raw();
(*c.raw_ptr()).neg.can_do_io = can_do_io;
}
let mut buf : [u8; 4] = [0,0,0,0]; let mut buf : [u8; 4] = [0,0,0,0];
match h.input_counter { match h.input_counter {
Some(s) => unsafe { emulator.read_mem(s, &mut buf); }, Some(s) => unsafe { emulator.read_mem(s, &mut buf); },
@ -299,41 +274,12 @@ where
{ {
let emulator = hooks.emulator(); let emulator = hooks.emulator();
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel"); let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
trigger_collection(emulator, (None,Some(pc)), h); let src = read_rec_return_stackframe(emulator, 0xfffffffc);
trigger_collection(emulator, (src, pc), CaptureEvent::ISRStart, h);
// println!("Exec ISR Call {:#x} {:#x} {}", src, pc, get_icount(emulator));
} }
//============================= Trace syscall execution //============================= Trace syscalls and returns
pub fn exec_syscall_hook<QT, S>(
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
)
where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator();
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
let mut edge = (None, Some(pc));
unsafe {
LAST_API_CALL.with(|x| {
match (*x.get()).take() {
Some(s) => {
edge.0=Some(s.0);
trigger_collection(emulator, edge, h);
},
None => (),
}
});
}
}
//============================= Trace jumps to syscalls
thread_local!(static LAST_API_CALL : UnsafeCell<Option<(GuestAddr,GuestAddr)>> = UnsafeCell::new(None));
pub fn gen_jmp_is_syscall<QT, S>( pub fn gen_jmp_is_syscall<QT, S>(
hooks: &mut QemuHooks<QT, S>, hooks: &mut QemuHooks<QT, S>,
@ -368,6 +314,45 @@ where
return None; return None;
} }
pub fn trace_jmp<QT, S>(
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
src: GuestAddr, mut dest: GuestAddr, id: u64
)
where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
let emulator = hooks.emulator();
if id == 1 { // API call
trigger_collection(emulator, (src, dest), CaptureEvent::APIStart, h);
// println!("Exec API Call {:#x} {:#x} {}", src, dest, get_icount(emulator));
} else if id == 2 { // API return
// Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space.
if in_any_range(&h.api_fn_ranges, dest).is_none() && in_any_range(&h.isr_ranges, dest).is_none() {
let mut edge = (0, 0);
edge.0=in_any_range(&h.api_fn_ranges, src).unwrap().start;
edge.1=dest;
trigger_collection(emulator, edge, CaptureEvent::APIEnd, h);
// println!("Exec API Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator));
}
} else if id == 3 { // ISR return
dest = read_rec_return_stackframe(emulator, dest);
let mut edge = (0, 0);
edge.0=in_any_range(&h.isr_ranges, src).unwrap().start;
edge.1=dest;
trigger_collection(emulator, edge, CaptureEvent::ISREnd, h);
// println!("Exec ISR Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator));
}
}
//============================= Utility functions
fn get_icount(emulator : &Emulator) -> u64 { fn get_icount(emulator : &Emulator) -> u64 {
unsafe { unsafe {
// TODO: investigate why can_do_io is not set sometimes, as this is just a workaround // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround
@ -380,43 +365,24 @@ fn get_icount(emulator : &Emulator) -> u64 {
} }
} }
pub fn trace_api_call<QT, S>( fn read_rec_return_stackframe(emu : &Emulator, lr : GuestAddr) -> GuestAddr {
hooks: &mut QemuHooks<QT, S>, let lr_ = lr & u32::MAX-1;
_state: Option<&mut S>, if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF8 || lr_ == 0xFFFFFFF0 {
src: GuestAddr, dest: GuestAddr, id: u64 unsafe {
) // if 0xFFFFFFF0/1 0xFFFFFFF8/9 -> "main stack" MSP
where let mut buf = [0u8; 4];
S: UsesInput, let sp : GuestAddr = if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF0 { // PSP
QT: QemuHelperTuple<S>, read_user_reg_unchecked(emu) as u32
{ } else {
if id == 1 { // API call emu.read_reg(13).unwrap()
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel"); };
let emulator = hooks.emulator(); let ret_pc = sp+0x18; // https://developer.arm.com/documentation/dui0552/a/the-cortex-m3-processor/exception-model/exception-entry-and-return
trigger_collection(emulator, (Some(src), Some(dest)), h); emu.read_mem(ret_pc, buf.as_mut_slice());
// println!("Exec API Call {:#x} {:#x} {}", src, dest, get_icount(emulator)); return u32::from_le_bytes(buf);
} else if id == 2 { // API return // elseif 0xfffffffc/d
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel"); }} else {
// Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space. return lr;
if in_any_range(&h.api_fn_ranges, dest).is_none() && in_any_range(&h.isr_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, get_icount(emulator));
}
} else if id == 3 { // ISR return
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().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, get_icount(emulator));
}
} }
pub fn in_any_range<'a>(ranges: &'a Vec<(String, Range<u32>)>, addr : GuestAddr) -> Option<&'a std::ops::Range<GuestAddr>> { pub fn in_any_range<'a>(ranges: &'a Vec<(String, Range<u32>)>, addr : GuestAddr) -> Option<&'a std::ops::Range<GuestAddr>> {

View File

@ -48,7 +48,7 @@ pub struct RawFreeRTOSSystemState {
delay_list_overflow: freertos::List_t, delay_list_overflow: freertos::List_t,
dumping_ground: HashMap<u32,freertos::rtos_struct>, dumping_ground: HashMap<u32,freertos::rtos_struct>,
input_counter: u32, input_counter: u32,
edge: (Option<GuestAddr>,Option<GuestAddr>), edge: (GuestAddr,GuestAddr),
capture_point: (CaptureEvent,String) capture_point: (CaptureEvent,String)
} }
/// List of system state dumps from QemuHelpers /// List of system state dumps from QemuHelpers

View File

@ -34,6 +34,7 @@ pub struct QemuSystemStateObserver
pub last_states: HashMap<u64, ReducedFreeRTOSSystemState>, pub last_states: HashMap<u64, ReducedFreeRTOSSystemState>,
pub last_trace: Vec<ExecInterval>, pub last_trace: Vec<ExecInterval>,
pub last_input: Vec<u8>, pub last_input: Vec<u8>,
pub success: bool,
name: String, name: String,
} }
@ -59,6 +60,7 @@ where
let mut temp = states2intervals(temp.0, temp.1); let mut temp = states2intervals(temp.0, temp.1);
self.last_trace = temp.0; self.last_trace = temp.0;
self.last_states = temp.1; self.last_states = temp.1;
self.success = temp.2;
// println!("{:?}",temp); // println!("{:?}",temp);
} }
// let abbs = extract_abbs_from_trace(&self.last_run); // let abbs = extract_abbs_from_trace(&self.last_run);
@ -88,7 +90,7 @@ impl HasLen for QemuSystemStateObserver
impl QemuSystemStateObserver { impl QemuSystemStateObserver {
pub fn new() -> Self { pub fn new() -> Self {
Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: "systemstate".to_string(), last_states: HashMap::new() } Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: "systemstate".to_string(), last_states: HashMap::new(), success: false }
} }
} }
@ -132,10 +134,11 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap<u32,rtos_struct>) ->
ret ret
} }
/// Drains a List of raw SystemStates to produce a refined trace /// Drains a List of raw SystemStates to produce a refined trace
fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> (Vec<ReducedFreeRTOSSystemState>, Vec<(u64, CaptureEvent, String, (Option<u32>, Option<u32>))>) { fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> (Vec<ReducedFreeRTOSSystemState>, Vec<(u64, CaptureEvent, String, (u32, u32))>) {
let mut ret = (Vec::<_>::new(), Vec::<_>::new()); let mut ret = (Vec::<_>::new(), Vec::<_>::new());
for mut i in input.drain(..) { for mut i in input.drain(..) {
let cur = RefinedTCB::from_tcb_owned(i.current_tcb); let cur = RefinedTCB::from_tcb_owned(i.current_tcb);
// println!("Refine: {} {:?} {:?} {:x}-{:x}", cur.task_name, i.capture_point.0, i.capture_point.1.to_string(), i.edge.0, i.edge.1);
// collect ready list // collect ready list
let mut collector = Vec::<RefinedTCB>::new(); let mut collector = Vec::<RefinedTCB>::new();
for j in i.prio_ready_lists.into_iter().rev() { for j in i.prio_ready_lists.into_iter().rev() {
@ -154,22 +157,21 @@ fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> (Vec<Reduced
delay_list_after: delay_list, delay_list_after: delay_list,
// input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, // input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER,
}); });
// println!("Refine: {:?} {:?} {:x}-{:x}",i.capture_point.0, i.capture_point.1.to_string(), i.edge.0.unwrap_or(0), i.edge.1.unwrap_or(0));
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));
} }
return ret; return ret;
} }
/// Transform the states and metadata into a list of ExecIntervals /// Transform the states and metadata into a list of ExecIntervals
fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, CaptureEvent, String, (Option<u32>, Option<u32>))>) -> (Vec<ExecInterval>, HashMap<u64, ReducedFreeRTOSSystemState>) { 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());} if trace.len() == 0 {return (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 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 level_of_task : HashMap<&str, u8> = HashMap::new();
let mut ret: Vec<ExecInterval> = vec![]; let mut ret: Vec<ExecInterval> = vec![];
let mut edges: Vec<(Option<u32>, Option<u32>)> = vec![]; let mut edges: Vec<(u32, u32)> = vec![];
let mut last_hash : u64 = trace[0].get_hash(); let mut last_hash : u64 = trace[0].get_hash();
let mut table : HashMap<u64, ReducedFreeRTOSSystemState> = HashMap::new(); let mut table : HashMap<u64, ReducedFreeRTOSSystemState> = HashMap::new();
table.insert(last_hash, trace[0].clone()); table.insert(last_hash, trace[0].clone());
@ -184,6 +186,9 @@ fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, Capt
0 0
}, },
CaptureEvent::APIStart => { // API start can only be called in the app CaptureEvent::APIStart => { // API start can only be called in the app
if !level_of_task.contains_key(curr_name) { // Should not happen, apps start from an ISR End. Some input exibited this behavior for unknown reasons
level_of_task.insert(curr_name, 0);
}
*level_of_task.get_mut(curr_name).unwrap()=1; *level_of_task.get_mut(curr_name).unwrap()=1;
1 1
}, },
@ -239,111 +244,109 @@ fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, Capt
last_hash = next_hash; last_hash = next_hash;
edges.push((meta[i].3.1, meta[i+1].3.0)); edges.push((meta[i].3.1, meta[i+1].3.0));
} }
add_abb_info(&mut ret, &table, &edges); let t = add_abb_info(&mut ret, &table, &edges);
(ret, table) (ret, table, t)
} }
fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, edges: &Vec<(Option<u32>, Option<u32>)>) { fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, edges: &Vec<(u32, u32)>) -> bool {
let mut ret = true;
let mut task_has_started : HashSet<String> = HashSet::new(); let mut task_has_started : HashSet<String> = HashSet::new();
let mut wip_abb_trace : Vec<Rc<RefCell<AtomicBasicBlock>>> = vec![]; let mut wip_abb_trace : Vec<Rc<RefCell<AtomicBasicBlock>>> = vec![];
let mut open_abb_at_this_task_or_level : HashMap<(u8,&str),usize> = HashMap::new(); // let mut open_abb_at_this_task_or_level : HashMap<(u8,&str),usize> = HashMap::new();
let mut open_abb_at_this_ret_addr_and_task : HashMap<(u32,&str),usize> = HashMap::new();
for i in 0..trace.len() { for i in 0..trace.len() {
let curr_name = &table[&trace[i].start_state].current_task.task_name; let curr_name = &table[&trace[i].start_state].current_task.task_name;
// let last : Option<&usize> = last_abb_start_of_task.get(&curr_name); // let last : Option<&usize> = last_abb_start_of_task.get(&curr_name);
let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level // let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level
let open_abb = open_abb_at_this_ret_addr_and_task.get(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level
// println!("Edge {:x}-{:x}", edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff)); // println!("Edge {:x}-{:x}", edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff));
match (&trace[i].start_capture.0, &trace[i].end_capture.0) { match trace[i].start_capture.0 {
// case with abb to block correspondence // generic api abb start
// APP ABB CaptureEvent::APIStart => {
// (CaptureEvent::APIEnd , CaptureEvent::APIStart ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, // assert_eq!(open_abb, None);
// (CaptureEvent::APIEnd , CaptureEvent::End ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, ret &= open_abb.is_none();
// // API ABB open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i);
// (CaptureEvent::APIStart, CaptureEvent::APIEnd ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), 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}})));
// (CaptureEvent::APIStart , CaptureEvent::End ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, },
// // ISR ABB // generic isr abb start
// (CaptureEvent::ISRStart, CaptureEvent::ISREnd ) => {/*assert_eq!(open_abb, None);*/ wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, CaptureEvent::ISRStart => {
// (CaptureEvent::ISRStart , CaptureEvent::End ) => {assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, // 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);
match trace[i].start_capture.0 { 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}})));
// generic api abb start },
CaptureEvent::APIStart => { // generic app abb start
assert_eq!(open_abb, None); CaptureEvent::APIEnd => {
open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); // assert_eq!(open_abb, None);
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); 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);
// generic isr abb start 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}})));
CaptureEvent::ISRStart => { },
assert_eq!(open_abb, None); // generic continued blocks
open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i); CaptureEvent::ISREnd => {
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); // special case app abb start
}, if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) {
// generic app abb start // assert_eq!(open_abb, None);
CaptureEvent::APIEnd => { ret &= open_abb.is_none();
assert_eq!(open_abb, 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}})));
open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i); 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.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); task_has_started.insert(curr_name.clone());
}, } else {
// generic continued blocks if let Some(last) = open_abb_at_this_ret_addr_and_task.get(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""})) {
CaptureEvent::ISREnd => { let last = last.clone(); // required to drop immutable reference
// special case app abb start wip_abb_trace.push(wip_abb_trace[last].clone());
if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) { // if the abb is interrupted again, it will need to continue at edge[i].1
assert_eq!(open_abb, None); open_abb_at_this_ret_addr_and_task.remove(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""}));
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}}))); open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), last); // order matters!
open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i); } else {
task_has_started.insert(curr_name.clone()); // panic!();
} else { // 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);
// assert_ne!(open_abb,None); // println!("{:x?}", open_abb_at_this_ret_addr_and_task);
if let Some(last) = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})) { ret = false;
wip_abb_trace.push(wip_abb_trace[*last].clone()); 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}})))
} else { }
// panic!();
eprintln!("Continued block with no start {} {:?} {:?} {:x}-{:x} {} {}", trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff),task_has_started.contains(curr_name),trace[i].level);
panic!();
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].1.unwrap_or(0), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}})))
}
}
},
_ => panic!("Undefined block start")
} }
match trace[i].end_capture.0 { },
// generic app abb end _ => panic!("Undefined block start")
CaptureEvent::APIStart => {
let _t = &wip_abb_trace[i];
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap());
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
},
// generic api abb end
CaptureEvent::APIEnd => {
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap());
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
},
// generic isr abb end
CaptureEvent::ISREnd => {
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap());
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
},
// end anything
CaptureEvent::End => {
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap());
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
},
CaptureEvent::ISRStart => (),
_ => panic!("Undefined block end")
}
}
} }
// println!("{} {} {:x}-{:x} {:x}-{:x} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick); match trace[i].end_capture.0 {
// generic app abb end
CaptureEvent::APIStart => {
let _t = &wip_abb_trace[i];
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1);
open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, if trace[i].level<2 {&curr_name} else {""}));
},
// generic api abb end
CaptureEvent::APIEnd => {
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1);
open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, if trace[i].level<2 {&curr_name} else {""}));
},
// generic isr abb end
CaptureEvent::ISREnd => {
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1);
open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, if trace[i].level<2 {&curr_name} else {""}));
},
// end anything
CaptureEvent::End => {
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1);
open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, if trace[i].level<2 {&curr_name} else {""}));
},
CaptureEvent::ISRStart => (),
_ => panic!("Undefined block end")
}
// println!("{} {} {:x}-{:x} {:x}-{:x} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0, edges[i].1, ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick);
// println!("{:x?}", open_abb_at_this_ret_addr_and_task);
} }
drop(open_abb_at_this_task_or_level); // drop(open_abb_at_this_task_or_level);
for i in 0..trace.len() { for i in 0..trace.len() {
trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone());
} }
return ret;
} }