WIP: complet rework of STG

This commit is contained in:
Alwin Berger 2024-05-17 15:57:44 +02:00
parent ba3850cf4d
commit 8f652f754c
3 changed files with 780 additions and 348 deletions

View File

@ -32,13 +32,13 @@ const NUM_PRIOS: usize = 5;
//============================= Struct definitions //============================= Struct definitions
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] #[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CaptureEvent { pub enum CaptureEvent {
APIStart, APIStart, /// src,dst
APIEnd, APIEnd, /// src,dst
ISRStart, ISRStart, /// _,dst
ISREnd, ISREnd, /// src,_
End, End, /// src,_
#[default] #[default]
Undefined, Undefined,
} }
@ -67,16 +67,18 @@ pub struct RefinedTCB {
pub priority: u32, pub priority: u32,
pub base_priority: u32, pub base_priority: u32,
mutexes_held: u32, mutexes_held: u32,
notify_value: u32, // notify_value: u32,
notify_state: u8, notify_state: u8,
} }
impl PartialEq for RefinedTCB { impl PartialEq for RefinedTCB {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.task_name == other.task_name && let ret = self.task_name == other.task_name &&
self.priority == other.priority && self.priority == other.priority &&
self.base_priority == other.base_priority self.base_priority == other.base_priority;
// && self.notify_state == other.notify_state #[cfg(feature = "do_hash_notify_state")]
let ret = ret && self.notify_state == other.notify_state;
ret
} }
} }
@ -85,7 +87,7 @@ impl Hash for RefinedTCB {
self.task_name.hash(state); self.task_name.hash(state);
self.priority.hash(state); self.priority.hash(state);
self.mutexes_held.hash(state); self.mutexes_held.hash(state);
#[cfg(not(feature = "no_hash_state"))] #[cfg(feature = "do_hash_notify_state")]
self.notify_state.hash(state); self.notify_state.hash(state);
// self.notify_value.hash(state); // self.notify_value.hash(state);
} }
@ -101,7 +103,7 @@ impl RefinedTCB {
priority: input.uxPriority, priority: input.uxPriority,
base_priority: input.uxBasePriority, base_priority: input.uxBasePriority,
mutexes_held: input.uxMutexesHeld, mutexes_held: input.uxMutexesHeld,
notify_value: input.ulNotifiedValue[0], // notify_value: input.ulNotifiedValue[0],
notify_state: input.ucNotifyState[0], notify_state: input.ucNotifyState[0],
} }
} }
@ -115,27 +117,25 @@ impl RefinedTCB {
priority: input.uxPriority, priority: input.uxPriority,
base_priority: input.uxBasePriority, base_priority: input.uxBasePriority,
mutexes_held: input.uxMutexesHeld, mutexes_held: input.uxMutexesHeld,
notify_value: input.ulNotifiedValue[0], // notify_value: input.ulNotifiedValue[0],
notify_state: input.ucNotifyState[0], notify_state: input.ucNotifyState[0],
} }
} }
} }
} }
/// Refined information about the states an execution transitioned between /// Reduced information about a systems state, without any execution context
#[derive(Debug, Default, Serialize, Deserialize, Clone)] #[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct RefinedFreeRTOSSystemState { pub struct ReducedFreeRTOSSystemState {
pub start_tick: u64, // pub tick: u64,
pub end_tick: u64, pub current_task: RefinedTCB,
edge: (Option<GuestAddr>,Option<GuestAddr>), ready_list_after: Vec<RefinedTCB>,
input_counter: u32, delay_list_after: Vec<RefinedTCB>,
pub current_task: (RefinedTCB, u32), // edge: (Option<GuestAddr>,Option<GuestAddr>),
ready_list_after: Vec<(RefinedTCB, u32)>, // pub capture_point: (CaptureEvent,String),
delay_list_after: Vec<(RefinedTCB, u32)>, // input_counter: u32
// pub capture_point: String
pub capture_point: (CaptureEvent,String)
} }
impl PartialEq for RefinedFreeRTOSSystemState { impl PartialEq for ReducedFreeRTOSSystemState {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.current_task == other.current_task && self.ready_list_after == other.ready_list_after && self.current_task == other.current_task && self.ready_list_after == other.ready_list_after &&
self.delay_list_after == other.delay_list_after self.delay_list_after == other.delay_list_after
@ -144,42 +144,93 @@ impl PartialEq for RefinedFreeRTOSSystemState {
} }
} }
impl Hash for RefinedFreeRTOSSystemState { impl Hash for ReducedFreeRTOSSystemState {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.current_task.hash(state); self.current_task.hash(state);
self.ready_list_after.hash(state); self.ready_list_after.hash(state);
self.delay_list_after.hash(state); self.delay_list_after.hash(state);
// self.last_pc.hash(state);
} }
} }
impl RefinedFreeRTOSSystemState { impl ReducedFreeRTOSSystemState {
fn get_time(&self) -> u64 { // fn get_tick(&self) -> u64 {
self.end_tick-self.start_tick // self.tick
} // }
pub fn print_lists(&self) -> String { pub fn print_lists(&self) -> String {
let mut ret = String::from("+"); let mut ret = String::from("+");
for j in self.ready_list_after.iter() { for j in self.ready_list_after.iter() {
ret.push_str(format!(" {}#{}", j.0.task_name, j.1).as_str()); ret.push_str(format!(" {}", j.task_name).as_str());
} }
ret.push_str("\n-"); ret.push_str("\n-");
for j in self.delay_list_after.iter() { for j in self.delay_list_after.iter() {
ret.push_str(format!(" {}#{}", j.0.task_name, j.1).as_str()); ret.push_str(format!(" {}", j.task_name).as_str());
} }
ret ret
} }
pub fn get_hash(&self) -> u64 {
let mut h = DefaultHasher::new();
self.hash(&mut h);
h.finish()
}
}
// #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
// pub enum ExecLevel {
// APP = 0,
// API = 1,
// ISR = 2,
// }
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
struct ExecInterval {
start_tick: u64,
end_tick: u64,
start_state: u64,
end_state: u64,
start_capture: (CaptureEvent, String),
end_capture: (CaptureEvent, String),
level: u8,
tick_spend_preempted: u64,
abb: Option<AtomicBasicBlock>
}
impl ExecInterval {
pub fn get_exec_time(&self) -> u64 {
self.end_tick-self.start_tick-self.tick_spend_preempted
}
pub fn is_valid(&self) -> bool {
self.start_tick != 0 || self.end_tick != 0
}
pub fn invaildate(&mut self) {
self.start_tick = 0;
self.end_tick = 0;
}
/// Attach this interval to the later one, keep a record of the time spend preempted
pub fn try_unite_with_later_interval(&mut self, later_interval : &mut Self) -> bool {
if self.end_state!=later_interval.start_state || self.abb!=later_interval.abb || !self.is_valid() || !later_interval.is_valid() {
return false;
}
// assert_eq!(self.end_state, later_interval.start_state);
// assert_eq!(self.abb, later_interval.abb);
later_interval.tick_spend_preempted += self.tick_spend_preempted + (later_interval.start_tick-self.end_tick);
later_interval.start_tick = self.start_tick;
later_interval.start_state = self.start_state;
self.invaildate();
return true;
}
} }
// Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata // Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata
#[derive(Debug, Default, Serialize, Deserialize, Clone)] #[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct FreeRTOSSystemStateMetadata { pub struct FreeRTOSSystemStateMetadata {
pub inner: Vec<RefinedFreeRTOSSystemState>, pub inner: Vec<ReducedFreeRTOSSystemState>,
trace_length: usize, trace_length: usize,
indices: Vec<usize>, // Hashed enumeration of States indices: Vec<usize>, // Hashed enumeration of States
tcref: isize, tcref: isize,
} }
impl FreeRTOSSystemStateMetadata { impl FreeRTOSSystemStateMetadata {
pub fn new(inner: Vec<RefinedFreeRTOSSystemState>) -> Self{ pub fn new(inner: Vec<ReducedFreeRTOSSystemState>) -> Self{
let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect(); let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect();
Self {trace_length: inner.len(), inner: inner, indices: tmp, tcref: 0} Self {trace_length: inner.len(), inner: inner, indices: tmp, tcref: 0}
} }
@ -218,6 +269,7 @@ libafl_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata);
pub struct AtomicBasicBlock { pub struct AtomicBasicBlock {
start: GuestAddr, start: GuestAddr,
ends: HashSet<GuestAddr>, ends: HashSet<GuestAddr>,
level: u8,
} }
impl Hash for AtomicBasicBlock { impl Hash for AtomicBasicBlock {
@ -226,6 +278,7 @@ impl Hash for AtomicBasicBlock {
self.start.hash(state); self.start.hash(state);
let mut keys : Vec<_> = self.ends.iter().collect(); let mut keys : Vec<_> = self.ends.iter().collect();
keys.sort(); keys.sort();
self.level.hash(state);
keys.hash(state); keys.hash(state);
} }
} }
@ -236,7 +289,7 @@ impl fmt::Display for AtomicBasicBlock {
for end in &self.ends { for end in &self.ends {
ends_str.push_str(&format!("0x{:#x}, ", end)); ends_str.push_str(&format!("0x{:#x}, ", end));
} }
write!(f, "ABB {{ start: 0x{:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) write!(f, "ABB {{ level: {}, start: 0x{:#x}, ends: [{}]}}", self.level, self.start, ends_str.trim().trim_matches(','))
} }
} }
impl fmt::Debug for AtomicBasicBlock { impl fmt::Debug for AtomicBasicBlock {
@ -245,7 +298,7 @@ impl fmt::Debug for AtomicBasicBlock {
for end in &self.ends { for end in &self.ends {
ends_str.push_str(&format!("{:#x}, ", end)); ends_str.push_str(&format!("{:#x}, ", end));
} }
write!(f, "ABB {{ start: {:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) write!(f, "ABB {{ level: {}, start: {:#x}, ends: [{}]}}", self.level, self.start, ends_str.trim().trim_matches(','))
} }
} }
@ -258,6 +311,9 @@ impl PartialOrd for AtomicBasicBlock {
impl Ord for AtomicBasicBlock { impl Ord for AtomicBasicBlock {
fn cmp(&self, other: &AtomicBasicBlock) -> std::cmp::Ordering { fn cmp(&self, other: &AtomicBasicBlock) -> std::cmp::Ordering {
if self.start.cmp(&other.start) == std::cmp::Ordering::Equal { if self.start.cmp(&other.start) == std::cmp::Ordering::Equal {
if self.level.cmp(&other.level) != std::cmp::Ordering::Equal {
return self.level.cmp(&other.level);
}
// If the start addresses are equal, compare by 'ends' // If the start addresses are equal, compare by 'ends'
let end1 = if self.ends.len() == 1 { *self.ends.iter().next().unwrap() as u64 } else { let end1 = if self.ends.len() == 1 { *self.ends.iter().next().unwrap() as u64 } else {
let mut temp = self.ends.iter().collect::<Vec<_>>().into_iter().collect::<Vec<&GuestAddr>>(); let mut temp = self.ends.iter().collect::<Vec<_>>().into_iter().collect::<Vec<&GuestAddr>>();
@ -282,202 +338,202 @@ impl Ord for AtomicBasicBlock {
} }
fn get_task_names(trace: &Vec<RefinedFreeRTOSSystemState>) -> HashSet<String> { fn get_task_names(trace: &Vec<ReducedFreeRTOSSystemState>) -> HashSet<String> {
let mut ret: HashSet<_, _> = HashSet::new(); let mut ret: HashSet<_, _> = HashSet::new();
for state in trace { for state in trace {
ret.insert(state.current_task.0.task_name.to_string()); ret.insert(state.current_task.task_name.to_string());
} }
ret ret
} }
fn extract_abbs_from_trace(trace: &Vec<RefinedFreeRTOSSystemState>) -> HashMap<String,Vec<Rc<AtomicBasicBlock>>> { // fn extract_abbs_from_trace(trace: &Vec<ReducedFreeRTOSSystemState>) -> HashMap<String,Vec<Rc<AtomicBasicBlock>>> {
let mut abbs_of_task : HashMap<String, Vec<Rc<AtomicBasicBlock>>> = HashMap::new(); // let mut abbs_of_task : HashMap<String, Vec<Rc<AtomicBasicBlock>>> = HashMap::new();
let mut last_abb_of_task : HashMap<String, usize> = HashMap::new(); // let mut last_abb_of_task : HashMap<String, usize> = HashMap::new();
// iterate over all states and extract atomic basic blocks // // iterate over all states and extract atomic basic blocks
// an atomic base block has a single entry and multiple exits // // an atomic base block has a single entry and multiple exits
// the cuts between blocks are api calls // // the cuts between blocks are api calls
// when capture_point is APIEnd, the destination of the edge is the start of an atomic block // // when capture_point is APIEnd, the destination of the edge is the start of an atomic block
// so the next APIStart with the same current_tcb.pcTaskName is the end of the atomic block // // so the next APIStart with the same current_tcb.pcTaskName is the end of the atomic block
for i in 0..trace.len() { // for i in 0..trace.len() {
let curr_name = trace[i].current_task.0.task_name.clone(); // let curr_name = trace[i].current_task.0.task_name.clone();
let last : Option<&usize> = last_abb_of_task.get(&curr_name); // let last : Option<&usize> = last_abb_of_task.get(&curr_name);
match trace[i].capture_point.0 { // match trace[i].capture_point.0 {
CaptureEvent::APIStart => { // CaptureEvent::APIStart => {
// end the last atomic block // // end the last atomic block
if let Some(&l) = last { // if let Some(&l) = last {
let start = trace[l].edge.1.unwrap();
let end = trace[i].edge.0.unwrap();
match abbs_of_task.get_mut(&curr_name) {
Some(v) => {
match v.iter_mut().find(|x| x.start==start) {
Some(abb) => {
Rc::get_mut(abb).unwrap().ends.insert(end);
}
None => {
let mut t = HashSet::new();
t.insert(end);
v.push(Rc::new(AtomicBasicBlock {start, ends: t}));
}
};
},
None => {
let mut v = Vec::new();
let mut t = HashSet::new();
t.insert(end);
v.push(Rc::new(AtomicBasicBlock {start, ends: t}));
abbs_of_task.insert(curr_name, v);
}
}
} else {
// first API call of this task
let mut v = Vec::new();
let mut t = HashSet::new();
let end = trace[i].edge.0.unwrap();
t.insert(end);
v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t}));
abbs_of_task.insert(curr_name, v);
}
},
CaptureEvent::APIEnd => {
match last {
Some(&l) => {
//assert!(trace[l].capture_point.0 == CaptureEvent::APIStart);
},
None => (),
}
last_abb_of_task.insert(curr_name, i);
},
CaptureEvent::ISRStart => {
},
CaptureEvent::ISREnd => {
},
CaptureEvent::End => {
// end the last atomic block
if let Some(&l) = last {
let start = trace[l].edge.1.unwrap();
let end = trace[i].edge.0.unwrap();
match abbs_of_task.get_mut(&curr_name) {
Some(v) => {
match v.iter_mut().find(|x| x.start==start) {
Some(abb) => {
Rc::get_mut(abb).unwrap().ends.insert(end);
}
None => {
let mut t = HashSet::new();
t.insert(end);
v.push(Rc::new(AtomicBasicBlock {start, ends: t}));
}
};
},
None => {
let mut v = Vec::new();
let mut t = HashSet::new();
t.insert(end);
v.push(Rc::new(AtomicBasicBlock {start, ends: t}));
abbs_of_task.insert(curr_name, v);
}
}
} else {
// first API call of this task
let mut v = Vec::new();
let mut t = HashSet::new();
let end = trace[i].edge.0.unwrap();
t.insert(end);
v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t}));
abbs_of_task.insert(curr_name, v);
}
},
CaptureEvent::Undefined => {
},
}
}
abbs_of_task
}
/// returns (name, abb, index, ticks, Option<total abb ticks iff abb termiates here>)
fn trace_to_state_abb(trace: &Vec<RefinedFreeRTOSSystemState>) -> (Vec<(String, Rc<AtomicBasicBlock>, usize, u64)>, Vec<(AtomicBasicBlock, u64)>) {
let mut abbs_in_exec_order : Vec<(usize,AtomicBasicBlock,u64)> = vec![]; // indices in trace where an abb ends, along with it's time
let mut has_started : HashSet<String> = HashSet::new();
let mut abb_begin_end : HashMap<usize,usize> = HashMap::new();
let mut last_abb_of_task : HashMap<String, usize> = HashMap::new();
for i in 0..trace.len() {
let curr_name = trace[i].current_task.0.task_name.clone();
let last : Option<&usize> = last_abb_of_task.get(&curr_name);
match trace[i].capture_point.0 {
CaptureEvent::APIStart => {
// end the last atomic block, which began with APIEnd or initial PendSV End
if let Some(&l) = last {
// let start = trace[l].edge.1.unwrap(); // let start = trace[l].edge.1.unwrap();
// let end = trace[i].edge.0.unwrap(); // let end = trace[i].edge.0.unwrap();
abb_begin_end.insert(l, i); // match abbs_of_task.get_mut(&curr_name) {
} else { // Some(v) => {
panic!("Start an API call with no ABB to terminate"); // match v.iter_mut().find(|x| x.start==start) {
} // Some(abb) => {
last_abb_of_task.remove(&curr_name); // Rc::get_mut(abb).unwrap().ends.insert(end);
}, // }
CaptureEvent::APIEnd => { // None => {
// APIEnd means a new ABB begins // let mut t = HashSet::new();
match last { // t.insert(end);
Some(&l) => { // v.push(Rc::new(AtomicBasicBlock {start, ends: t}));
panic!("End an API call with open ABB"); // }
}, // };
None => (), // },
} // None => {
last_abb_of_task.insert(curr_name, i); // let mut v = Vec::new();
}, // let mut t = HashSet::new();
CaptureEvent::ISRStart => { // t.insert(end);
}, // v.push(Rc::new(AtomicBasicBlock {start, ends: t}));
CaptureEvent::ISREnd => { // abbs_of_task.insert(curr_name, v);
if last.is_none() && trace[i].capture_point.1=="xPortPendSVHandler" && !has_started.contains(&curr_name) { // }
// The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled // }
last_abb_of_task.insert(curr_name.clone(), i); // } else {
has_started.insert(curr_name); // // first API call of this task
} // let mut v = Vec::new();
}, // let mut t = HashSet::new();
CaptureEvent::End => { // let end = trace[i].edge.0.unwrap();
// end the last atomic block // t.insert(end);
if let Some(&l) = last { // v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t}));
abb_begin_end.insert(l, i); // abbs_of_task.insert(curr_name, v);
} else { // }
panic!("End without running ABB"); // },
} // CaptureEvent::APIEnd => {
last_abb_of_task.remove(&curr_name); // match last {
}, // Some(&l) => {
CaptureEvent::Undefined => { // //assert!(trace[l].capture_point.0 == CaptureEvent::APIStart);
}, // },
} // None => (),
} // }
let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); // last_abb_of_task.insert(curr_name, i);
abb_intervals.sort_by_key(|(x,_)| *x); // },
let mut chunks = Vec::new(); // (name, abb, index, ticks) // CaptureEvent::ISRStart => {
for &(s,e) in abb_intervals.iter() { // },
let curr_name = trace[s].current_task.0.task_name.clone(); // CaptureEvent::ISREnd => {
let start = match trace[s].capture_point.0 { // },
CaptureEvent::ISREnd => 0, // CaptureEvent::End => {
CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), // // end the last atomic block
_ => panic!(), // if let Some(&l) = last {
}; // let start = trace[l].edge.1.unwrap();
let end = trace[e].edge.0.unwrap(); // let end = trace[i].edge.0.unwrap();
let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); // match abbs_of_task.get_mut(&curr_name) {
// find intervalls where the abb is actually running, not preempted // Some(v) => {
// count up exec time // match v.iter_mut().find(|x| x.start==start) {
let mut sum_of_pieces = 0; // Some(abb) => {
for i in s..e { // Rc::get_mut(abb).unwrap().ends.insert(end);
if trace[i].current_task.0.task_name == curr_name { // }
match trace[i].capture_point.0 { // None => {
CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, // let mut t = HashSet::new();
CaptureEvent::ISRStart => (), // t.insert(end);
CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, // v.push(Rc::new(AtomicBasicBlock {start, ends: t}));
_ => panic!(), // }
} // };
} // },
} // None => {
abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); // let mut v = Vec::new();
} // let mut t = HashSet::new();
abbs_in_exec_order.sort_by_key(|x| x.0); // t.insert(end);
let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); // v.push(Rc::new(AtomicBasicBlock {start, ends: t}));
chunks.sort_by_key(|x| x.2); // abbs_of_task.insert(curr_name, v);
(chunks, abbs_in_exec_order) // }
} // }
// } else {
// // first API call of this task
// let mut v = Vec::new();
// let mut t = HashSet::new();
// let end = trace[i].edge.0.unwrap();
// t.insert(end);
// v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t}));
// abbs_of_task.insert(curr_name, v);
// }
// },
// CaptureEvent::Undefined => {
// },
// }
// }
// abbs_of_task
// }
/// returns (name, abb, index, ticks, Option<total abb ticks iff abb termiates here>)
// fn trace_to_state_abb(trace: &Vec<ReducedFreeRTOSSystemState>) -> (Vec<(String, Rc<AtomicBasicBlock>, usize, u64)>, Vec<(AtomicBasicBlock, u64)>) {
// let mut abbs_in_exec_order : Vec<(usize,AtomicBasicBlock,u64)> = vec![]; // indices in trace where an abb ends, along with it's time
// let mut has_started : HashSet<String> = HashSet::new();
// let mut abb_begin_end : HashMap<usize,usize> = HashMap::new();
// let mut last_abb_of_task : HashMap<String, usize> = HashMap::new();
// for i in 0..trace.len() {
// let curr_name = trace[i].current_task.0.task_name.clone();
// let last : Option<&usize> = last_abb_of_task.get(&curr_name);
// match trace[i].capture_point.0 {
// CaptureEvent::APIStart => {
// // end the last atomic block, which began with APIEnd or initial PendSV End
// if let Some(&l) = last {
// // let start = trace[l].edge.1.unwrap();
// // let end = trace[i].edge.0.unwrap();
// abb_begin_end.insert(l, i);
// } else {
// panic!("Start an API call with no ABB to terminate");
// }
// last_abb_of_task.remove(&curr_name);
// },
// CaptureEvent::APIEnd => {
// // APIEnd means a new ABB begins
// match last {
// Some(&l) => {
// panic!("End an API call with open ABB");
// },
// None => (),
// }
// last_abb_of_task.insert(curr_name, i);
// },
// CaptureEvent::ISRStart => {
// },
// CaptureEvent::ISREnd => {
// if last.is_none() && trace[i].capture_point.1=="xPortPendSVHandler" && !has_started.contains(&curr_name) {
// // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled
// last_abb_of_task.insert(curr_name.clone(), i);
// has_started.insert(curr_name);
// }
// },
// CaptureEvent::End => {
// // end the last atomic block
// if let Some(&l) = last {
// abb_begin_end.insert(l, i);
// } else {
// panic!("End without running ABB");
// }
// last_abb_of_task.remove(&curr_name);
// },
// CaptureEvent::Undefined => {
// },
// }
// }
// let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect();
// abb_intervals.sort_by_key(|(x,_)| *x);
// let mut chunks = Vec::new(); // (name, abb, index, ticks)
// for &(s,e) in abb_intervals.iter() {
// let curr_name = trace[s].current_task.0.task_name.clone();
// let start = match trace[s].capture_point.0 {
// CaptureEvent::ISREnd => 0,
// CaptureEvent::APIEnd => trace[s].edge.1.unwrap(),
// _ => panic!(),
// };
// let end = trace[e].edge.0.unwrap();
// let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])});
// // find intervalls where the abb is actually running, not preempted
// // count up exec time
// let mut sum_of_pieces = 0;
// for i in s..e {
// if trace[i].current_task.0.task_name == curr_name {
// match trace[i].capture_point.0 {
// CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;},
// CaptureEvent::ISRStart => (),
// CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;},
// _ => panic!(),
// }
// }
// }
// abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces));
// }
// abbs_in_exec_order.sort_by_key(|x| x.0);
// let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect();
// chunks.sort_by_key(|x| x.2);
// (chunks, abbs_in_exec_order)
// }
libafl_bolts::impl_serdeany!(AtomicBasicBlock); libafl_bolts::impl_serdeany!(AtomicBasicBlock);

View File

@ -7,14 +7,18 @@ use libafl_bolts::AsSlice;
use libafl::Error; use libafl::Error;
use libafl::observers::Observer; use libafl::observers::Observer;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use hashbrown::HashMap; use hashbrown::{HashMap, HashSet};
use crate::systemstate::CaptureEvent; use crate::systemstate::CaptureEvent;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::VecDeque;
use super::{ AtomicBasicBlock, ExecInterval};
use super::{ use super::{
CURRENT_SYSTEMSTATE_VEC, CURRENT_SYSTEMSTATE_VEC,
RawFreeRTOSSystemState, RawFreeRTOSSystemState,
RefinedTCB, RefinedTCB,
RefinedFreeRTOSSystemState, ReducedFreeRTOSSystemState,
freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*}, freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*},
}; };
@ -26,7 +30,9 @@ use super::{
#[allow(clippy::unsafe_derive_deserialize)] #[allow(clippy::unsafe_derive_deserialize)]
pub struct QemuSystemStateObserver pub struct QemuSystemStateObserver
{ {
pub last_run: Vec<RefinedFreeRTOSSystemState>, pub last_run: Vec<ReducedFreeRTOSSystemState>,
pub last_states: HashMap<u64, ReducedFreeRTOSSystemState>,
pub last_trace: Vec<ExecInterval>,
pub last_input: Vec<u8>, pub last_input: Vec<u8>,
name: String, name: String,
} }
@ -44,7 +50,20 @@ where
#[inline] #[inline]
fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> {
unsafe {self.last_run = remove_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} // unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));}
unsafe {
let mut temp = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC);
self.last_run = temp.0.clone();
// println!("{:?}",temp);
let mut temp = states2intervals(temp.0, temp.1);
self.last_trace = temp.0;
self.last_states = temp.1;
// println!("{:?}",temp);
}
// let abbs = extract_abbs_from_trace(&self.last_run);
// println!("{:?}",abbs);
// let abbs = trace_to_state_abb(&self.last_run);
// println!("{:?}",abbs);
self.last_input=_input.target_bytes().as_slice().to_owned(); self.last_input=_input.target_bytes().as_slice().to_owned();
Ok(()) Ok(())
} }
@ -68,7 +87,7 @@ impl HasLen for QemuSystemStateObserver
impl QemuSystemStateObserver { impl QemuSystemStateObserver {
pub fn new() -> Self { pub fn new() -> Self {
Self{last_run: vec![], last_input: vec![], name: "systemstate".to_string()} Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: "systemstate".to_string(), last_states: HashMap::new() }
} }
} }
@ -112,10 +131,8 @@ 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<RefinedFreeRTOSSystemState> { fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> (Vec<ReducedFreeRTOSSystemState>, Vec<(u64, CaptureEvent, String, (Option<u32>, Option<u32>))>) {
let mut iteration_counts : HashMap<String, u32>= HashMap::new(); let mut ret = (Vec::<_>::new(), Vec::<_>::new());
let mut ret = Vec::<RefinedFreeRTOSSystemState>::new();
let mut start_tick : u64 = 0;
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);
// collect ready list // collect ready list
@ -130,66 +147,339 @@ fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> Vec<RefinedF
delay_list.append(&mut delay_list_overflow); delay_list.append(&mut delay_list_overflow);
delay_list.sort_by(|a,b| a.task_name.cmp(&b.task_name)); delay_list.sort_by(|a,b| a.task_name.cmp(&b.task_name));
// keep counts for all tasks ret.0.push(ReducedFreeRTOSSystemState {
let _ = iteration_counts.try_insert(cur.task_name.clone(), 1); current_task: cur,
for j in collector.iter() {let _ = iteration_counts.try_insert(j.task_name.clone(), 1);}
for j in delay_list.iter() {let _ = iteration_counts.try_insert(j.task_name.clone(), 0);}
// Ensure that async events appear in the delay list, so we the counts in all states
if let Some(lst) = ret.last() {
for a in lst.delay_list_after.iter().filter(|x| x.0.task_name.contains("async")) {
if delay_list.iter().find(|x| (*x).task_name==a.0.task_name).is_none() && cur.task_name != a.0.task_name {
delay_list.push(a.0.clone());
delay_list.sort_by(|a,b| a.task_name.cmp(&b.task_name));
}
}
}
// increase when:
// current and delayed afterwards
if let Some(_) = delay_list.iter().find(|x| (*x).task_name==cur.task_name) {
*iteration_counts.get_mut(&cur.task_name).unwrap()+=1;
}
let collector = collector.into_iter().map(|x| {let t = *iteration_counts.get(&x.task_name).unwrap_or(&1); (x, t)}).collect();
// let filter_delay = delay_list.into_iter().filter(|x| !x.task_name.contains("async"));
let delay_list : Vec<(RefinedTCB, u32)> = delay_list.into_iter().map(|x| {let t = *iteration_counts.get(&x.task_name).unwrap_or(&0); (x, t)}).collect();
let t = *iteration_counts.get(&cur.task_name).unwrap_or(&1);
// We don't care about the order
ret.push(RefinedFreeRTOSSystemState {
current_task: (cur, t),
start_tick: start_tick,
end_tick: i.qemu_tick,
ready_list_after: collector, ready_list_after: collector,
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,
edge: i.edge,
capture_point: (i.capture_point.0,i.capture_point.1.to_string()),
}); });
start_tick=i.qemu_tick; ret.1.push((i.qemu_tick, i.capture_point.0, i.capture_point.1.to_string(), i.edge));
} }
return ret; return ret;
} }
fn remove_ineffective_isr(mut trace: Vec<RefinedFreeRTOSSystemState>) -> Vec<RefinedFreeRTOSSystemState> { /// Transform the states and metadata into a list of ExecIntervals
// remove subsequent pairs of equal states where an ISREnd follows an ISRStart. If the interrupt had no effect on the system we, are not interested. fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, CaptureEvent, String, (Option<u32>, Option<u32>))>) -> (Vec<ExecInterval>, HashMap<u64, ReducedFreeRTOSSystemState>) {
let mut ret : Vec<RefinedFreeRTOSSystemState> = Vec::new(); let mut isr_stack : VecDeque<u8> = VecDeque::from([]); // 2+ = ISR, 1 = systemcall, 0 = APP. Trace starts with an ISREnd and executes the app
ret.push(trace[0].clone());
let mut i = 1;
while i < trace.len() - 1 {
if trace[i] == trace[i + 1] &&
matches!(trace[i].capture_point.0, CaptureEvent::ISRStart) &&
matches!(trace[i + 1].capture_point.0, CaptureEvent::ISREnd) &&
trace[i].capture_point.1 == trace[i + 1].capture_point.1
{
// extend the end of the last ABB until the end of the next one
ret.last_mut().unwrap().end_tick = trace[i+1].end_tick;
i+=2;
let mut level_of_task : HashMap<&str, u8> = HashMap::new();
let mut ret: Vec<ExecInterval> = vec![];
let mut edges: Vec<(Option<u32>, Option<u32>)> = vec![];
let mut last_hash : u64 = trace[0].get_hash();
let mut table : HashMap<u64, ReducedFreeRTOSSystemState> = HashMap::new();
table.insert(last_hash, trace[0].clone());
for i in 0..trace.len()-1 {
let curr_name = trace[i].current_task.task_name.as_str();
let level = match meta[i].1 {
CaptureEvent::APIEnd => { // API end always exits towards the app
*level_of_task.get_mut(curr_name).unwrap()=0;
&0
},
CaptureEvent::APIStart => { // API start can only be called in the app
*level_of_task.get_mut(curr_name).unwrap()=1;
&1
},
CaptureEvent::ISREnd => {
// special case where the next block is an app start
if !level_of_task.contains_key(curr_name) {
level_of_task.insert(curr_name, 0);
}
// nested isr, TODO: Test level > 2
if isr_stack.len() > 1 {
isr_stack.pop_back();
isr_stack.back().unwrap()
} else { } else {
ret.push(trace[i].clone()); isr_stack.pop_back();
i+=1; // possibly go back to an api call that is still running for this task
level_of_task.get(curr_name).unwrap()
}
},
CaptureEvent::ISRStart => {
if isr_stack.len() > 0 {
let l = isr_stack.back().unwrap();
isr_stack.push_back(l+1);
isr_stack.back().unwrap()
} else {
isr_stack.push_back(2);
&2
} }
} }
ret _ => &100
};
// if trace[i].2 == CaptureEvent::End {break;}
let next_hash=trace[i+1].get_hash();
if !table.contains_key(&next_hash) {
table.insert(next_hash, trace[i+1].clone());
} }
ret.push(ExecInterval{
start_tick: meta[i].0,
end_tick: meta[i+1].0,
start_state: last_hash,
end_state: next_hash,
start_capture: (meta[i].1, meta[i].2.clone()),
end_capture: (meta[i+1].1, meta[i+1].2.clone()),
level: *level,
tick_spend_preempted: 0,
abb: None
});
last_hash = next_hash;
edges.push((meta[i].3.1, meta[i+1].3.0));
}
add_abb_info(&mut ret, &table, &edges);
(ret, table)
}
fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, edges: &Vec<(Option<u32>, Option<u32>)>) {
let mut task_has_started : HashSet<String> = HashSet::new();
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();
for i in 0..trace.len() {
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 open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); // apps/apis are differentiated by task name, isrs by nested level
match (&trace[i].start_capture.0, &trace[i].end_capture.0) {
// case with abb to block correspondence
// APP ABB
(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}})))},
(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}})))},
// API ABB
(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}})))},
(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
(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::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}})))},
//
(_, _) => {
match trace[i].start_capture.0 {
// generic api abb start
CaptureEvent::APIStart => {
assert_eq!(open_abb, None);
open_abb_at_this_task_or_level.insert((trace[i].level, 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}})));
},
// generic isr abb start
CaptureEvent::ISRStart => {
assert_eq!(open_abb, None);
open_abb_at_this_task_or_level.insert((trace[i].level, 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}})));
},
// generic app abb start
CaptureEvent::APIEnd => {
assert_eq!(open_abb, None);
open_abb_at_this_task_or_level.insert((trace[i].level, 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}})));
},
// generic continued blocks
CaptureEvent::ISREnd => {
// special case app abb start
if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) {
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);
task_has_started.insert(curr_name.clone());
} else {
// generic case, continue a preempted block
let last = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).expect(&format!("Continued block with no start {} {} {}", trace[i].start_tick, task_has_started.contains(curr_name),trace[i].level));
wip_abb_trace.push(wip_abb_trace[*last].clone());
}
},
_ => panic!("Undefined block start")
}
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.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} {} {} {:?} {:?}",curr_name, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, &table[&trace[i].end_state].current_task.task_name, trace[i].level, trace[i].start_capture, trace[i].end_capture);
}
for i in 0..trace.len() {
trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone());
}
// let last : Option<&usize> = last_abb_start_of_task.get(&curr_name);
// // genric start of an ABB that gets preeempted
// match trace[i].start_capture.0 {
// CaptureEvent::APIEnd => {
// if last.is_some() {
// panic!("End an API call with open ABB");
// }
// last_abb_start_of_task.insert(curr_name, i);
// }
// CaptureEvent::ISREnd => {
// if last.is_none() && trace[i].end_capture.1=="xPortPendSVHandler" && !task_has_started.contains(&curr_name) {
// // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled
// last_abb_start_of_task.insert(curr_name.clone(), i);
// task_has_started.insert(curr_name);
// }
// },
// _ => (),
// }
// // genric end of an ABB that got preeempted
// match trace[i].end_capture.0 {
// CaptureEvent::APIStart => {
// if let Some(&l) = last {
// // let start = trace[l].edge.1.unwrap();
// // let end = trace[i].edge.0.unwrap();
// abb_begin_end.insert(l, i);
// } else {
// panic!("Start an API call with no ABB to terminate");
// }
// last_abb_start_of_task.remove(&curr_name);
// }
// CaptureEvent::End => {
// if let Some(&l) = last {
// // let start = trace[l].edge.1.unwrap();
// // let end = trace[i].edge.0.unwrap();
// abb_begin_end.insert(l, i);
// last_abb_start_of_task.remove(&curr_name);
// } else {
// eprintln!("End with no ABB to terminate"); // could happen if the run ends in a kernel panic
// }
// }
// _ => (),
// }
// }
// }
// for (b,e) in abb_begin_end.into_iter() {
// let curr_name = table[&trace[i].start_state].current_task.task_name.clone();
// let abb = Some(AtomicBasicBlock{start: edges[b].0.unwrap(), ends: HashSet::from([edges[e].1.unwrap()])});
// for j in b..(e+1) {
// if trace[j].current_task.0.task_name == curr_name {
// trace[j].abb=abb.clone();
// }
// }
// }
// let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect();
// abb_intervals.sort_by_key(|(x,_)| *x);
// let mut chunks = Vec::new(); // (name, abb, index, ticks)
// for &(s,e) in abb_intervals.iter() {
// let curr_name = trace[s].current_task.0.task_name.clone();
// let start = match trace[s].capture_point.0 {
// CaptureEvent::ISREnd => 0,
// CaptureEvent::APIEnd => trace[s].edge.1.unwrap(),
// _ => panic!(),
// };
// let end = trace[e].edge.0.unwrap();
// let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])});
// // find intervalls where the abb is actually running, not preempted
// // count up exec time
// let mut sum_of_pieces = 0;
// for i in s..e {
// if trace[i].current_task.0.task_name == curr_name {
// match trace[i].capture_point.0 {
// CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;},
// CaptureEvent::ISRStart => (),
// CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;},
// _ => panic!(),
// }
// }
// }
// abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces));
// }
// abbs_in_exec_order.sort_by_key(|x| x.0);
// let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect();
// chunks.sort_by_key(|x| x.2);
// (chunks, abbs_in_exec_order)
}
/// invalidate subsequent intervals of equal states where an ISREnd follows an ISRStart. If the interrupt had no effect on the system we, are not interested.
fn invalidate_ineffective_isr(trace: &mut Vec<ExecInterval>) {
let mut i = 0;
while i < trace.len() - 1 {
if trace[i].is_valid() &&
matches!(trace[i].start_capture.0, CaptureEvent::ISRStart) && matches!(trace[i].end_capture.0, CaptureEvent::ISREnd) &&
trace[i].start_capture.1 == trace[i].end_capture.1 && trace[i].start_state == trace[i].end_state
{
trace[i].invaildate();
}
}
}
/// merge a sequence of intervals of the same state+abb. jump over all invalid blocks.
fn merge_subsequent_abbs(trace: &mut Vec<ExecInterval>) {
let mut i = 1;
let mut lst_valid=0;
while i < trace.len() - 1 {
if trace[i].is_valid() {
let mut temp = trace[i].clone();
trace[lst_valid].try_unite_with_later_interval(&mut temp);
trace[i] = temp;
lst_valid = i;
}
}
}
//============================= ABB Observer
// The Qemusystemstate Observer retrieves the systemstate
// that will get updated by the target.
// #[derive(Serialize, Deserialize, Debug, Default)]
// #[allow(clippy::unsafe_derive_deserialize)]
// pub struct ABBObserver
// {
// pub last_run: HashMap<String,Vec<AtomicBasicBlock>>,
// name: String,
// }
// impl<S> Observer<S> for ABBObserver
// where
// S: UsesInput,
// S::Input : HasTargetBytes,
// {
// #[inline]
// fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
// self.last_run.clear();
// Ok(())
// }
// #[inline]
// fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> {
// unsafe {self.last_run=extract_abbs_from_trace(&mut CURRENT_SYSTEMSTATE_VEC);}
// println!("{:?}", self.last_run);
// Ok(())
// }
// }
// impl Named for ABBObserver
// {
// #[inline]
// fn name(&self) -> &str {
// self.name.as_str()
// }
// }
// impl ABBObserver {
// pub fn new() -> Self {
// Self{last_run: HashMap::new(), name: "abbs".to_string()}
// }
// }

View File

@ -45,9 +45,10 @@ use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, stat
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::feedbacks::SystemStateFeedbackState; use super::feedbacks::SystemStateFeedbackState;
use super::trace_to_state_abb;
use super::AtomicBasicBlock; use super::AtomicBasicBlock;
use super::RefinedFreeRTOSSystemState; use super::CaptureEvent;
use super::ExecInterval;
use super::ReducedFreeRTOSSystemState;
use super::FreeRTOSSystemStateMetadata; use super::FreeRTOSSystemStateMetadata;
use super::observers::QemuSystemStateObserver; use super::observers::QemuSystemStateObserver;
use petgraph::prelude::DiGraph; use petgraph::prelude::DiGraph;
@ -62,12 +63,12 @@ use libafl_bolts::rands::Rand;
#[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)] #[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)]
pub struct STGNode pub struct STGNode
{ {
base: RefinedFreeRTOSSystemState, base: ReducedFreeRTOSSystemState,
abb: AtomicBasicBlock, abb: AtomicBasicBlock,
} }
impl STGNode { impl STGNode {
pub fn pretty_print(&self) -> String { pub fn pretty_print(&self) -> String {
format!("{}\n{:x}-{:x}\n{}", self.base.current_task.0.task_name, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0), self.base.print_lists()) format!("{}\nl{} {:x}-{:x}\n{}", self.base.current_task.task_name, self.abb.level, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.print_lists())
} }
fn calculate_hash(&self) -> u64 { fn calculate_hash(&self) -> u64 {
let mut s = DefaultHasher::new(); let mut s = DefaultHasher::new();
@ -82,13 +83,37 @@ impl PartialEq for STGNode {
} }
} }
#[derive(Serialize, Deserialize, Clone, Debug, Default, Hash, PartialEq, Eq)]
pub struct STGEdge
{
// is_interrupt: bool,
event: CaptureEvent,
name: String
}
impl STGEdge {
pub fn pretty_print(&self) -> String {
let mut short = match self.event {
CaptureEvent::APIStart => "Call: ",
CaptureEvent::APIEnd => "Ret: ",
CaptureEvent::ISRStart => "Int: ",
CaptureEvent::ISREnd => "IRet: ",
CaptureEvent::End => "End: ",
CaptureEvent::Undefined => "",
}.to_string();
short.push_str(&self.name);
short
}
}
/// Shared Metadata for a systemstateFeedback /// Shared Metadata for a systemstateFeedback
#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] #[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)]
pub struct STGFeedbackState pub struct STGFeedbackState
{ {
// aggregated traces as a graph // aggregated traces as a graph
pub graph: DiGraph<STGNode, ()>, pub graph: DiGraph<STGNode, STGEdge>,
index: HashMap<u64, NodeIndex>, systemstate_index: HashMap<u64, ReducedFreeRTOSSystemState>,
stgnode_index: HashMap<u64, NodeIndex>,
entrypoint: NodeIndex, entrypoint: NodeIndex,
exit: NodeIndex, exit: NodeIndex,
// Metadata about aggregated traces. aggegated meaning, order has been removed // Metadata about aggregated traces. aggegated meaning, order has been removed
@ -99,9 +124,11 @@ impl Default for STGFeedbackState {
fn default() -> STGFeedbackState { fn default() -> STGFeedbackState {
let mut graph = DiGraph::new(); let mut graph = DiGraph::new();
let mut entry = STGNode::default(); let mut entry = STGNode::default();
entry.base.current_task.0.task_name="Start".to_string(); entry.base.current_task.task_name="Start".to_string();
let mut exit = STGNode::default(); let mut exit = STGNode::default();
exit.base.current_task.0.task_name="End".to_string(); exit.base.current_task.task_name="End".to_string();
let systemstate_index = HashMap::from([(entry.base.get_hash(), entry.base.clone()), (exit.base.get_hash(), exit.base.clone())]);
let h_entry = entry.calculate_hash(); let h_entry = entry.calculate_hash();
let h_exit = exit.calculate_hash(); let h_exit = exit.calculate_hash();
@ -113,10 +140,11 @@ impl Default for STGFeedbackState {
STGFeedbackState { STGFeedbackState {
graph, graph,
index, stgnode_index: index,
entrypoint, entrypoint,
exit, exit,
worst_observed_per_aggegated_path: HashMap::new(), worst_observed_per_aggegated_path: HashMap::new(),
systemstate_index,
} }
} }
} }
@ -181,22 +209,79 @@ impl StgFeedback {
/// newly discovered node? /// newly discovered node?
/// side effect: /// side effect:
/// the graph gets new nodes /// the graph gets new nodes
fn update_stg(trace: &Vec<RefinedFreeRTOSSystemState>, map: Vec<(String, Rc<AtomicBasicBlock>, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec<NodeIndex>, Vec<EdgeIndex>, bool) { // fn update_stg(trace: &Vec<ReducedFreeRTOSSystemState>, map: Vec<(String, Rc<AtomicBasicBlock>, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec<NodeIndex>, Vec<EdgeIndex>, bool) {
// let mut return_node_trace = vec![fbs.entrypoint];
// let mut return_edge_trace = vec![];
// let mut interesting = false;
// // add all missing state+abb combinations to the graph
// for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact
// if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { //
// let node = STGNode {base: state.clone(), abb: (*marker.1).clone()};
// let h_node = node.calculate_hash();
// let next_idx = if let Some(idx) = fbs.index.get(&h_node) {
// // alredy present
// *idx
// } else {
// // not present
// let idx = fbs.graph.add_node(node);
// fbs.index.insert(h_node, idx);
// interesting |= INTEREST_NODE;
// idx
// };
// // connect in graph if edge not present
// let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx);
// if let Some(e_) = e {
// return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_));
// } else {
// let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, ());
// return_edge_trace.push(e_);
// interesting |= INTEREST_EDGE;
// }
// return_node_trace.push(next_idx);
// /*
// Ideas:
// Mark edges triggered by interrupts
// Specify path with edges instead of nodes?
// Form a coverage map over edges?
// Sum up execution time per ABB
// */
// }
// }
// // every path terminates at the end
// if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) {
// let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, ());
// return_edge_trace.push(e_);
// interesting |= INTEREST_EDGE;
// }
// return_node_trace.push(fbs.exit);
// #[cfg(feature = "feed_stg")]
// set_observer_map(&return_edge_trace);
// (return_node_trace, return_edge_trace, interesting)
// }
/// params:
/// tarce of system states
/// list of all atomic basic blocks with asociated state index and ticks
/// produces:
/// tarce of node indexes representing the path trough the graph
/// newly discovered node?
/// side effect:
/// the graph gets new nodes
fn update_stg_interval(trace: &Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, fbs: &mut STGFeedbackState) -> (Vec<NodeIndex>, Vec<EdgeIndex>, bool) {
let mut return_node_trace = vec![fbs.entrypoint]; let mut return_node_trace = vec![fbs.entrypoint];
let mut return_edge_trace = vec![]; let mut return_edge_trace = vec![];
let mut interesting = false; let mut interesting = false;
// add all missing state+abb combinations to the graph // add all missing state+abb combinations to the graph
for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact for (i,interval) in trace.iter().enumerate() { // Iterate intervals
if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { // let node = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()};
let node = STGNode {base: state.clone(), abb: (*marker.1).clone()};
let h_node = node.calculate_hash(); let h_node = node.calculate_hash();
let next_idx = if let Some(idx) = fbs.index.get(&h_node) { let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) {
// alredy present // alredy present
*idx *idx
} else { } else {
// not present // not present
let idx = fbs.graph.add_node(node); let idx = fbs.graph.add_node(node);
fbs.index.insert(h_node, idx); fbs.stgnode_index.insert(h_node, idx);
interesting |= INTEREST_NODE; interesting |= INTEREST_NODE;
idx idx
}; };
@ -205,7 +290,7 @@ impl StgFeedback {
if let Some(e_) = e { if let Some(e_) = e {
return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_)); return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_));
} else { } else {
let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, ()); let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, STGEdge{event: interval.start_capture.0, name: interval.start_capture.1.clone()});
return_edge_trace.push(e_); return_edge_trace.push(e_);
interesting |= INTEREST_EDGE; interesting |= INTEREST_EDGE;
} }
@ -218,10 +303,9 @@ impl StgFeedback {
Sum up execution time per ABB Sum up execution time per ABB
*/ */
} }
}
// every path terminates at the end // every path terminates at the end
if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) {
let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, ()); let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, STGEdge { event: CaptureEvent::End, name: String::from("End") });
return_edge_trace.push(e_); return_edge_trace.push(e_);
interesting |= INTEREST_EDGE; interesting |= INTEREST_EDGE;
} }
@ -265,31 +349,33 @@ where
} }
}; };
let (abbs, ordered) = trace_to_state_abb(&observer.last_run); let (_trace, _, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate);
// println!("{:?}",abbs); // let (abbs, ordered) = trace_to_state_abb(&observer.last_run);
let (_trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); // // println!("{:?}",abbs);
if INTEREST_AGGREGATE { // let (_trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate);
let (it1, _it2) : (Vec<_>, Vec<_>) = ordered.into_iter().unzip(); // if INTEREST_AGGREGATE {
// aggegation by sorting, order of states is not relevant // let (it1, _it2) : (Vec<_>, Vec<_>) = ordered.into_iter().unzip();
let mut tmp : Vec<_> = it1; // // aggegation by sorting, order of states is not relevant
tmp.sort(); // let mut tmp : Vec<_> = it1;
if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { // tmp.sort();
let t = clock_observer.last_runtime(); // if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) {
if t > *x { // let t = clock_observer.last_runtime();
*x = t; // if t > *x {
interesting |= true; // *x = t;
} // interesting |= INTEREST_AGGREGATE;
} else { // }
feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); // } else {
interesting |= true; // feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime());
} // interesting |= INTEREST_AGGREGATE;
} // }
// }
// let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| ""); // let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| "");
// let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); // let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string();
// let outs = outs.replace(';',"\\n"); // let outs = outs.replace(';',"\\n");
// fs::write("./mystg.dot",outs).expect("Failed to write graph"); // fs::write("./mystg.dot",outs).expect("Failed to write graph");
// Ok(interesting)
Ok(interesting) Ok(interesting)
} }