documentation
This commit is contained in:
parent
a7e00004b2
commit
25d598f30b
@ -12,7 +12,14 @@ use super::ExecInterval;
|
|||||||
|
|
||||||
//============================= API symbols
|
//============================= API symbols
|
||||||
|
|
||||||
/// Read ELF program headers to resolve physical load addresses.
|
/// Resolves a virtual address to a physical address using ELF program headers.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `vaddr` - The virtual address to resolve.
|
||||||
|
/// * `tab` - The ELF file containing program headers.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The corresponding physical address, or the original address if not found.
|
||||||
fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
|
fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
|
||||||
let ret;
|
let ret;
|
||||||
for i in &tab.goblin().program_headers {
|
for i in &tab.goblin().program_headers {
|
||||||
@ -25,12 +32,31 @@ fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
|
|||||||
return vaddr;
|
return vaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lookup a symbol in the ELF file, optionally resolve segment offsets
|
/// Looks up a symbol in the ELF file and returns its address, optionally translating to a physical address.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `elf` - The ELF file to search.
|
||||||
|
/// * `symbol` - The symbol name to look up.
|
||||||
|
/// * `do_translation` - Whether to translate the address to a physical address.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// Panics if the symbol is not found.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The address of the symbol.
|
||||||
pub fn load_symbol(elf: &EasyElf, symbol: &str, do_translation: bool) -> GuestAddr {
|
pub fn load_symbol(elf: &EasyElf, symbol: &str, do_translation: bool) -> GuestAddr {
|
||||||
try_load_symbol(elf, symbol, do_translation).expect(&format!("Symbol {} not found", symbol))
|
try_load_symbol(elf, symbol, do_translation).expect(&format!("Symbol {} not found", symbol))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lookup a symbol in the ELF file, optionally resolve segment offsets
|
/// Looks up a symbol in the ELF file and returns its address, optionally translating to a physical address.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `elf` - The ELF file to search.
|
||||||
|
/// * `symbol` - The symbol name to look up.
|
||||||
|
/// * `do_translation` - Whether to translate the address to a physical address.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Some(address) if found, None otherwise.
|
||||||
pub fn try_load_symbol(elf: &EasyElf, symbol: &str, do_translation: bool) -> Option<GuestAddr> {
|
pub fn try_load_symbol(elf: &EasyElf, symbol: &str, do_translation: bool) -> Option<GuestAddr> {
|
||||||
let ret = elf.resolve_symbol(symbol, 0);
|
let ret = elf.resolve_symbol(symbol, 0);
|
||||||
if do_translation {
|
if do_translation {
|
||||||
@ -42,7 +68,14 @@ pub fn try_load_symbol(elf: &EasyElf, symbol: &str, do_translation: bool) -> Opt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try looking up the address range of a function in the ELF file
|
/// Returns the address range of a function symbol in the ELF file.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `elf` - The ELF file to search.
|
||||||
|
/// * `symbol` - The function symbol name.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Some(range) if found, None otherwise.
|
||||||
pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option<std::ops::Range<GuestAddr>> {
|
pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option<std::ops::Range<GuestAddr>> {
|
||||||
let gob = elf.goblin();
|
let gob = elf.goblin();
|
||||||
|
|
||||||
@ -74,7 +107,14 @@ pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option<std::ops::Range
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if an address is in any of the ranges
|
/// Checks if an address is within any of the provided ranges.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `ranges` - A vector of (name, range) tuples.
|
||||||
|
/// * `addr` - The address to check.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Some(range) if the address is in any range, None otherwise.
|
||||||
pub fn in_any_range<'a>(
|
pub fn in_any_range<'a>(
|
||||||
ranges: &'a Vec<(Cow<'static, str>, Range<u32>)>,
|
ranges: &'a Vec<(Cow<'static, str>, Range<u32>)>,
|
||||||
addr: GuestAddr,
|
addr: GuestAddr,
|
||||||
@ -89,6 +129,13 @@ pub fn in_any_range<'a>(
|
|||||||
|
|
||||||
//============================= QEMU related utility functions
|
//============================= QEMU related utility functions
|
||||||
|
|
||||||
|
/// Retrieves the current QEMU instruction count.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `emulator` - The QEMU emulator instance.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The current instruction count as a u64.
|
||||||
pub fn get_icount(emulator: &libafl_qemu::Qemu) -> u64 {
|
pub fn get_icount(emulator: &libafl_qemu::Qemu) -> 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
|
||||||
@ -101,6 +148,14 @@ pub fn get_icount(emulator: &libafl_qemu::Qemu) -> u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts input bytes to a vector of interrupt times, enforcing minimum inter-arrival time.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `buf` - The input byte buffer.
|
||||||
|
/// * `config` - Tuple of (number of interrupts, minimum inter-arrival time).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A sorted vector of interrupt times.
|
||||||
pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize, u32)) -> Vec<u32> {
|
pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize, u32)) -> Vec<u32> {
|
||||||
let len = buf.len();
|
let len = buf.len();
|
||||||
let mut start_tick;
|
let mut start_tick;
|
||||||
@ -140,6 +195,13 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize, u32)) -> Vec<u
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts interrupt times back to input bytes.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `interrupt_times` - A slice of interrupt times.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A vector of bytes representing the interrupt times.
|
||||||
pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec<u8> {
|
pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec<u8> {
|
||||||
let mut ret = Vec::with_capacity(interrupt_times.len() * 4);
|
let mut ret = Vec::with_capacity(interrupt_times.len() * 4);
|
||||||
for i in interrupt_times {
|
for i in interrupt_times {
|
||||||
@ -148,6 +210,14 @@ pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec<u8> {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads the return address from the stack frame, handling ARM exception return conventions.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `emu` - The QEMU emulator instance.
|
||||||
|
/// * `lr` - The link register value.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The return address from the stack frame.
|
||||||
pub fn read_rec_return_stackframe(emu: &libafl_qemu::Qemu, lr: GuestAddr) -> GuestAddr {
|
pub fn read_rec_return_stackframe(emu: &libafl_qemu::Qemu, lr: GuestAddr) -> GuestAddr {
|
||||||
let lr_ = lr & u32::MAX - 1;
|
let lr_ = lr & u32::MAX - 1;
|
||||||
if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF8 || lr_ == 0xFFFFFFF0 {
|
if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF8 || lr_ == 0xFFFFFFF0 {
|
||||||
@ -171,6 +241,15 @@ pub fn read_rec_return_stackframe(emu: &libafl_qemu::Qemu, lr: GuestAddr) -> Gue
|
|||||||
|
|
||||||
//============================= Tracing related utility functions
|
//============================= Tracing related utility functions
|
||||||
|
|
||||||
|
/// Inserts or updates metadata in a map, returning a mutable reference.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `metadata` - The metadata map.
|
||||||
|
/// * `default` - Function to create a default value if not present.
|
||||||
|
/// * `update` - Function to update the value if present.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A mutable reference to the metadata value.
|
||||||
pub fn metadata_insert_or_update_get<T>(
|
pub fn metadata_insert_or_update_get<T>(
|
||||||
metadata: &mut SerdeAnyMap,
|
metadata: &mut SerdeAnyMap,
|
||||||
default: impl FnOnce() -> T,
|
default: impl FnOnce() -> T,
|
||||||
@ -188,8 +267,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build an ABB-profile from a stretch of intervals
|
/// Builds an ABB (atomic basic block) profile from execution intervals.
|
||||||
/// returns mapping: task_name -> (abb_addr -> (interval_count, exec_count, exec_time, woet))
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `intervals` - A vector of execution intervals.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A mapping from task name to ABB address to (interval count, exec count, exec time, woet).
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn abb_profile(
|
pub fn abb_profile(
|
||||||
mut intervals: Vec<ExecInterval>,
|
mut intervals: Vec<ExecInterval>,
|
||||||
@ -270,6 +354,13 @@ pub fn abb_profile(
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an immutable reference from a mutable one.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `x` - A mutable reference.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An immutable reference to the same value.
|
||||||
pub fn unmut<T>(x: &mut T) -> &T {
|
pub fn unmut<T>(x: &mut T) -> &T {
|
||||||
&(*x)
|
&(*x)
|
||||||
}
|
}
|
||||||
|
@ -55,10 +55,15 @@ pub enum CaptureEvent {
|
|||||||
pub struct ExecInterval {
|
pub struct ExecInterval {
|
||||||
pub start_tick: u64,
|
pub start_tick: u64,
|
||||||
pub end_tick: u64,
|
pub end_tick: u64,
|
||||||
|
/// Hash of the start state
|
||||||
pub start_state: u64,
|
pub start_state: u64,
|
||||||
|
/// Hash of the end state
|
||||||
pub end_state: u64,
|
pub end_state: u64,
|
||||||
|
/// The event that started this interval
|
||||||
pub start_capture: (CaptureEvent, Cow<'static, str>),
|
pub start_capture: (CaptureEvent, Cow<'static, str>),
|
||||||
|
/// The event that ended this interval
|
||||||
pub end_capture: (CaptureEvent, Cow<'static, str>),
|
pub end_capture: (CaptureEvent, Cow<'static, str>),
|
||||||
|
/// Execution level: 0 = APP, 1 = API, 2 = ISR
|
||||||
pub level: u8,
|
pub level: u8,
|
||||||
// tick_spend_preempted: u64,
|
// tick_spend_preempted: u64,
|
||||||
pub abb: Option<AtomicBasicBlock>
|
pub abb: Option<AtomicBasicBlock>
|
||||||
@ -217,6 +222,7 @@ libafl_bolts::impl_serdeany!(AtomicBasicBlock);
|
|||||||
|
|
||||||
// ============================= Job instances
|
// ============================= Job instances
|
||||||
|
|
||||||
|
/// Represents a single execution of a task, recording the place and input read.
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
pub struct RTOSJob {
|
pub struct RTOSJob {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -288,6 +294,7 @@ impl Hash for RTOSTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl RTOSTask {
|
impl RTOSTask {
|
||||||
|
/// Returns the hash value for the task, computing it if not cached.
|
||||||
pub fn get_hash(&mut self) -> u64 {
|
pub fn get_hash(&mut self) -> u64 {
|
||||||
if self.hash_cache == 0 {
|
if self.hash_cache == 0 {
|
||||||
let mut s = DefaultHasher::new();
|
let mut s = DefaultHasher::new();
|
||||||
@ -296,6 +303,7 @@ impl RTOSTask {
|
|||||||
}
|
}
|
||||||
self.hash_cache
|
self.hash_cache
|
||||||
}
|
}
|
||||||
|
/// Returns the cached hash value for the task.
|
||||||
pub fn get_hash_cached(&self) -> u64 {
|
pub fn get_hash_cached(&self) -> u64 {
|
||||||
if self.hash_cache == 0 {
|
if self.hash_cache == 0 {
|
||||||
let mut s = DefaultHasher::new();
|
let mut s = DefaultHasher::new();
|
||||||
@ -305,7 +313,7 @@ impl RTOSTask {
|
|||||||
self.hash_cache
|
self.hash_cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Update woet (time, inputs) and wort (time only) if the new instance is better
|
/// Update WOET (time, inputs) and WORT (time only) if the new instance is better
|
||||||
pub fn try_update(&mut self, other: &RTOSJob) -> bool {
|
pub fn try_update(&mut self, other: &RTOSJob) -> bool {
|
||||||
assert_eq!(self.get_hash(), other.get_hash_cached());
|
assert_eq!(self.get_hash(), other.get_hash_cached());
|
||||||
let mut ret = false;
|
let mut ret = false;
|
||||||
@ -321,6 +329,7 @@ impl RTOSTask {
|
|||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
/// Creates a RTOSTask instance from a given RTOSJob instance.
|
||||||
pub fn from_instance(input: &RTOSJob) -> Self {
|
pub fn from_instance(input: &RTOSJob) -> Self {
|
||||||
let c = input.get_hash_cached();
|
let c = input.get_hash_cached();
|
||||||
Self {
|
Self {
|
||||||
@ -333,9 +342,24 @@ impl RTOSTask {
|
|||||||
hash_cache: c
|
hash_cache: c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn map_bytes_onto(&self, input: &RTOSJob, offset: Option<u32>) -> Vec<(u32,u8)> {
|
/// Maps bytes onto a given RTOSJob instance, returning the differences.
|
||||||
if input.mem_reads.len() == 0 {return vec![];}
|
pub fn map_bytes_onto(&self, input: &RTOSJob, offset: Option<u32>) -> Vec<(u32, u8)> {
|
||||||
let ret = input.mem_reads.iter().take(self.woet_bytes.len()).enumerate().filter_map(|(idx,(addr,oldbyte))| if self.woet_bytes[idx]!=*oldbyte {Some((*addr-offset.unwrap_or_default(), self.woet_bytes[idx]))} else {None}).collect();
|
if input.mem_reads.len() == 0 {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
let ret = input
|
||||||
|
.mem_reads
|
||||||
|
.iter()
|
||||||
|
.take(self.woet_bytes.len())
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(idx, (addr, oldbyte))| {
|
||||||
|
if self.woet_bytes[idx] != *oldbyte {
|
||||||
|
Some((*addr - offset.unwrap_or_default(), self.woet_bytes[idx]))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
// eprintln!("Mapped: {:?}", ret);
|
// eprintln!("Mapped: {:?}", ret);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,15 @@ pub const USR_ISR_SYMBOLS: &'static [&'static str] = &[
|
|||||||
|
|
||||||
//============================================================================= Helper functions
|
//============================================================================= Helper functions
|
||||||
|
|
||||||
|
/// Reads a FreeRTOS list from the target and populates the system state.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `systemstate` - The mutable system state to populate.
|
||||||
|
/// * `emulator` - The QEMU emulator instance.
|
||||||
|
/// * `target` - The address of the list to read.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A tuple containing the read list and a boolean indicating if the read was valid.
|
||||||
fn read_freertos_list(
|
fn read_freertos_list(
|
||||||
systemstate: &mut RawFreeRTOSSystemState,
|
systemstate: &mut RawFreeRTOSSystemState,
|
||||||
emulator: &libafl_qemu::Qemu,
|
emulator: &libafl_qemu::Qemu,
|
||||||
@ -179,6 +188,13 @@ fn read_freertos_list(
|
|||||||
return (read, true);
|
return (read, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Triggers the collection of a FreeRTOS system state snapshot at a given event.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `emulator` - The QEMU emulator instance.
|
||||||
|
/// * `edge` - A tuple of (from, to) addresses representing the edge.
|
||||||
|
/// * `event` - The capture event type.
|
||||||
|
/// * `h` - The FreeRTOS system state helper.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn trigger_collection(
|
fn trigger_collection(
|
||||||
emulator: &libafl_qemu::Qemu,
|
emulator: &libafl_qemu::Qemu,
|
||||||
@ -327,6 +343,13 @@ impl Hash for RefinedTCB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RefinedTCB {
|
impl RefinedTCB {
|
||||||
|
/// Constructs a `RefinedTCB` from a raw FreeRTOS TCB struct reference.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `input` - Reference to a raw TCB_t struct.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A new `RefinedTCB` instance.
|
||||||
pub fn from_tcb(input: &TCB_t) -> Self {
|
pub fn from_tcb(input: &TCB_t) -> Self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let tmp = std::mem::transmute::<[i8; 10], [u8; 10]>(input.pcTaskName);
|
let tmp = std::mem::transmute::<[i8; 10], [u8; 10]>(input.pcTaskName);
|
||||||
@ -345,6 +368,13 @@ impl RefinedTCB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Constructs a `RefinedTCB` from a raw FreeRTOS TCB struct (by value).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `input` - The TCB_t struct.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A new `RefinedTCB` instance.
|
||||||
pub fn from_tcb_owned(input: TCB_t) -> Self {
|
pub fn from_tcb_owned(input: TCB_t) -> Self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let tmp = std::mem::transmute::<[i8; 10], [u8; 10]>(input.pcTaskName);
|
let tmp = std::mem::transmute::<[i8; 10], [u8; 10]>(input.pcTaskName);
|
||||||
@ -391,10 +421,10 @@ impl Hash for FreeRTOSSystemState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl FreeRTOSSystemState {
|
impl FreeRTOSSystemState {
|
||||||
// fn get_tick(&self) -> u64 {
|
/// Prints the ready and delay lists as a formatted string.
|
||||||
// self.tick
|
///
|
||||||
// }
|
/// # Returns
|
||||||
|
/// A string representation of the ready and delay lists.
|
||||||
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() {
|
||||||
@ -406,6 +436,10 @@ impl FreeRTOSSystemState {
|
|||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
/// Computes a hash for the system state.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The hash value as a u64.
|
||||||
pub fn get_hash(&self) -> u64 {
|
pub fn get_hash(&self) -> u64 {
|
||||||
let mut h = DefaultHasher::new();
|
let mut h = DefaultHasher::new();
|
||||||
self.hash(&mut h);
|
self.hash(&mut h);
|
||||||
@ -461,6 +495,17 @@ pub struct FreeRTOSTraceMetadata
|
|||||||
}
|
}
|
||||||
impl FreeRTOSTraceMetadata
|
impl FreeRTOSTraceMetadata
|
||||||
{
|
{
|
||||||
|
/// Constructs a new `FreeRTOSTraceMetadata` from trace data.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `trace` - Vector of system states.
|
||||||
|
/// * `intervals` - Vector of execution intervals.
|
||||||
|
/// * `mem_reads` - Vector of memory reads.
|
||||||
|
/// * `jobs` - Vector of RTOS jobs.
|
||||||
|
/// * `need_to_debug` - Whether the current trace should be dumped for debugging purposes.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A new `FreeRTOSTraceMetadata` instance.
|
||||||
pub fn new(trace: Vec<<FreeRTOSTraceMetadata as SystemTraceData>::State>, intervals: Vec<ExecInterval>, mem_reads: Vec<Vec<(u32, u8)>>, jobs: Vec<RTOSJob>, need_to_debug: bool) -> Self {
|
pub fn new(trace: Vec<<FreeRTOSTraceMetadata as SystemTraceData>::State>, intervals: Vec<ExecInterval>, mem_reads: Vec<Vec<(u32, u8)>>, jobs: Vec<RTOSJob>, need_to_debug: bool) -> Self {
|
||||||
let hashes : Vec<_> = trace
|
let hashes : Vec<_> = trace
|
||||||
.iter()
|
.iter()
|
||||||
@ -529,7 +574,14 @@ libafl_bolts::impl_serdeany!(RefinedTCB);
|
|||||||
libafl_bolts::impl_serdeany!(FreeRTOSSystemState);
|
libafl_bolts::impl_serdeany!(FreeRTOSSystemState);
|
||||||
libafl_bolts::impl_serdeany!(FreeRTOSSystem);
|
libafl_bolts::impl_serdeany!(FreeRTOSSystem);
|
||||||
|
|
||||||
fn get_task_names(trace: &Vec<FreeRTOSSystemState>) -> HashSet<String> {
|
/// Returns a set of all task names present in the given trace.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `trace` - A vector of FreeRTOS system states.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A set of unique task names as strings.
|
||||||
|
pub(crate) fn get_task_names(trace: &Vec<FreeRTOSSystemState>) -> 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.task_name.to_string());
|
ret.insert(state.current_task.task_name.to_string());
|
||||||
|
@ -20,12 +20,16 @@ pub mod freertos;
|
|||||||
|
|
||||||
//============================= Trait definitions
|
//============================= Trait definitions
|
||||||
|
|
||||||
|
/// A trait representing a target system, which includes a system state, task control block, and trace data.
|
||||||
pub trait TargetSystem: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Clone + SerdeAny {
|
pub trait TargetSystem: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Clone + SerdeAny {
|
||||||
type State: SystemState<TCB = Self::TCB>;
|
type State: SystemState<TCB = Self::TCB>;
|
||||||
|
/// The type of a task control block used in the system state.
|
||||||
type TCB: TaskControlBlock;
|
type TCB: TaskControlBlock;
|
||||||
|
/// The type used to store trace data for the system.
|
||||||
type TraceData: SystemTraceData<State = Self::State>;
|
type TraceData: SystemTraceData<State = Self::State>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait representing the system state of a target system, which includes methods to access the current task.
|
||||||
pub trait SystemState: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Hash + PartialEq + Clone + SerdeAny {
|
pub trait SystemState: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Hash + PartialEq + Clone + SerdeAny {
|
||||||
type TCB: TaskControlBlock;
|
type TCB: TaskControlBlock;
|
||||||
|
|
||||||
@ -39,14 +43,20 @@ pub trait SystemState: Serialize + Sized + for<'a> Deserialize<'a> + Default + D
|
|||||||
pub trait SystemTraceData: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Clone + SerdeAny + HasRefCnt {
|
pub trait SystemTraceData: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Clone + SerdeAny + HasRefCnt {
|
||||||
type State: SystemState;
|
type State: SystemState;
|
||||||
|
|
||||||
|
/// Returns a vector of all system states in the trace.
|
||||||
fn states(&self) -> Vec<&Self::State>;
|
fn states(&self) -> Vec<&Self::State>;
|
||||||
|
/// Returns hash map of system states, where the key is the hash value of the state.
|
||||||
fn states_map(&self) -> &HashMap<u64, Self::State>;
|
fn states_map(&self) -> &HashMap<u64, Self::State>;
|
||||||
|
/// Returns a vector of execution intervals in the trace.
|
||||||
fn intervals(&self) -> &Vec<ExecInterval>;
|
fn intervals(&self) -> &Vec<ExecInterval>;
|
||||||
|
/// Returns a vector of memory reads, where each read is represented as a tuple of (address, value).
|
||||||
fn mem_reads(&self) -> &Vec<Vec<(u32, u8)>>;
|
fn mem_reads(&self) -> &Vec<Vec<(u32, u8)>>;
|
||||||
|
/// Returns a vector of RTOS jobs which were executed during the trace.
|
||||||
fn jobs(&self) -> &Vec<RTOSJob>;
|
fn jobs(&self) -> &Vec<RTOSJob>;
|
||||||
fn trace_length(&self) -> usize;
|
fn trace_length(&self) -> usize;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
/// Returns the worst job of each task by a given predicate.
|
||||||
fn worst_jobs_per_task_by(&self, pred: &dyn Fn(&RTOSJob,&RTOSJob) -> bool) -> HashMap<String, RTOSJob> {
|
fn worst_jobs_per_task_by(&self, pred: &dyn Fn(&RTOSJob,&RTOSJob) -> bool) -> HashMap<String, RTOSJob> {
|
||||||
self.jobs().iter().fold(HashMap::new(), |mut acc, next| {
|
self.jobs().iter().fold(HashMap::new(), |mut acc, next| {
|
||||||
match acc.get_mut(&next.name) {
|
match acc.get_mut(&next.name) {
|
||||||
@ -63,10 +73,12 @@ pub trait SystemTraceData: Serialize + Sized + for<'a> Deserialize<'a> + Default
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
|
/// Gives the worst job of each task by execution time.
|
||||||
fn worst_jobs_per_task_by_exec_time(&self) -> HashMap<String, RTOSJob> {
|
fn worst_jobs_per_task_by_exec_time(&self) -> HashMap<String, RTOSJob> {
|
||||||
self.worst_jobs_per_task_by(&|old, x| x.exec_ticks > old.exec_ticks)
|
self.worst_jobs_per_task_by(&|old, x| x.exec_ticks > old.exec_ticks)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
|
/// Gives the worst job of each task by response time.
|
||||||
fn worst_jobs_per_task_by_response_time(&self) -> HashMap<String, RTOSJob> {
|
fn worst_jobs_per_task_by_response_time(&self) -> HashMap<String, RTOSJob> {
|
||||||
self.worst_jobs_per_task_by(&|old, x| x.response_time() > old.response_time())
|
self.worst_jobs_per_task_by(&|old, x| x.response_time() > old.response_time())
|
||||||
}
|
}
|
||||||
@ -118,6 +130,7 @@ pub trait TaskControlBlock: Serialize + for<'a> Deserialize<'a> + Default + Debu
|
|||||||
|
|
||||||
//=============================
|
//=============================
|
||||||
|
|
||||||
|
/// A trait for looking up data in a QEMU emulation environment.
|
||||||
pub trait QemuLookup {
|
pub trait QemuLookup {
|
||||||
fn lookup(emu: &Qemu, addr: ::std::os::raw::c_uint) -> Self;
|
fn lookup(emu: &Qemu, addr: ::std::os::raw::c_uint) -> Self;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user