abstract SystemTraceData

This commit is contained in:
Alwin Berger 2024-12-16 16:00:18 +01:00
parent 8d7e32559f
commit a13dca6f39
16 changed files with 2400 additions and 1616 deletions

View File

@ -14,7 +14,7 @@ elf::EasyElf, emu::Emulator, modules::{edges::{self}, FilterList}, GuestAddr, Gu
}; };
use rand::{SeedableRng, StdRng, Rng}; use rand::{SeedableRng, StdRng, Rng};
use crate::{ use crate::{
systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, time::{ systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{
clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, RateLimitedMonitor, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, RateLimitedMonitor, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler}
} }
}; };
@ -123,7 +123,7 @@ macro_rules! do_dump_stg {
if $cli.dump_graph { if $cli.dump_graph {
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"dot"} else {$c}); let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"dot"} else {$c});
println!("Dumping graph to {:?}", &dump_path); println!("Dumping graph to {:?}", &dump_path);
if let Some(md) = $state.named_metadata_map_mut().get_mut::<STGFeedbackState>("stgfeedbackstate") { if let Some(md) = $state.named_metadata_map_mut().get_mut::<STGFeedbackState<FreeRTOSSystem>>("stgfeedbackstate") {
let out = md.graph.map(|_i,x| x.color_print(), |_i,x| x.color_print()); let out = md.graph.map(|_i,x| x.color_print(), |_i,x| x.color_print());
let outs = Dot::with_config(&out, &[]).to_string(); let outs = Dot::with_config(&out, &[]).to_string();
let outs = outs.replace("\\\"","\""); let outs = outs.replace("\\\"","\"");
@ -245,17 +245,17 @@ let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range);
println!("APP functions:"); println!("APP functions:");
let app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone()); let app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone());
let mut isr_ranges : HashMap<String,std::ops::Range<GuestAddr>> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); let mut isr_ranges : HashMap<String,std::ops::Range<GuestAddr>> = systemstate::target_os::freertos::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect();
systemstate::helpers::ISR_SYMBOLS.iter().for_each(|x| {let _ =(app_fn_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone()))).map(|y| isr_ranges.insert(y.0,y.1));}); // add used defined isr systemstate::target_os::freertos::ISR_SYMBOLS.iter().for_each(|x| {let _ =(app_fn_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone()))).map(|y| isr_ranges.insert(y.0,y.1));}); // add used defined isr
let denylist : Vec<_> =isr_ranges.values().map(|x| x.clone()).collect(); let denylist : Vec<_> =isr_ranges.values().map(|x| x.clone()).collect();
let denylist = FilterList::DenyList(denylist); // do not count isr jumps, which are useless let denylist = FilterList::DenyList(denylist); // do not count isr jumps, which are useless
#[cfg(feature = "observe_systemstate")] #[cfg(feature = "observe_systemstate")]
let mut isr_addreses : HashMap<GuestAddr, String> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect(); let mut isr_addreses : HashMap<GuestAddr, String> = systemstate::target_os::freertos::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect();
#[cfg(feature = "observe_systemstate")] #[cfg(feature = "observe_systemstate")]
systemstate::helpers::ISR_SYMBOLS.iter().for_each(|x| {let _ =(app_fn_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone()))).map(|y| isr_addreses.insert(y.1.start, y.0));}); // add used defined isr systemstate::target_os::freertos::ISR_SYMBOLS.iter().for_each(|x| {let _ =(app_fn_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone()))).map(|y| isr_addreses.insert(y.1.start, y.0));}); // add used defined isr
#[cfg(feature = "observe_systemstate")] #[cfg(feature = "observe_systemstate")]
for i in systemstate::helpers::ISR_SYMBOLS { for i in systemstate::target_os::freertos::ISR_SYMBOLS {
if isr_ranges.get(&i.to_string()).is_none() { if isr_ranges.get(&i.to_string()).is_none() {
if let Some(fr) = get_function_range(&elf, i) { if let Some(fr) = get_function_range(&elf, i) {
isr_addreses.insert(fr.start, i.to_string()); isr_addreses.insert(fr.start, i.to_string());
@ -408,13 +408,13 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
)}.track_indices(); )}.track_indices();
#[cfg(feature = "observe_systemstate")] #[cfg(feature = "observe_systemstate")]
let systemstate_observer = QemuSystemStateObserver::new(&cli.select_task); let systemstate_observer = QemuSystemStateObserver::<_,FreeRTOSSystem>::new(&cli.select_task);
// Feedback to rate the interestingness of an input // Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR // This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!( let mut feedback = feedback_or!(
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
ClockTimeFeedback::new_with_observer(&clock_time_observer, &cli.select_task) ClockTimeFeedback::<FreeRTOSSystem>::new_with_observer(&clock_time_observer, &cli.select_task)
); );
#[cfg(feature = "feed_genetic")] #[cfg(feature = "feed_genetic")]
let mut feedback = feedback_or!( let mut feedback = feedback_or!(
@ -437,12 +437,12 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
#[cfg(all(feature = "observe_systemstate"))] #[cfg(all(feature = "observe_systemstate"))]
let mut feedback = feedback_or!( let mut feedback = feedback_or!(
feedback, feedback,
DumpSystraceFeedback::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None}) DumpSystraceFeedback::<FreeRTOSSystem>::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None})
); );
#[cfg(feature = "trace_stg")] #[cfg(feature = "trace_stg")]
let mut feedback = feedback_or!( let mut feedback = feedback_or!(
feedback, feedback,
StgFeedback::new(if cli.dump_graph {cli.dump_name.clone()} else {None}) StgFeedback::<FreeRTOSSystem>::new(if cli.dump_graph {cli.dump_name.clone()} else {None})
); );
#[cfg(feature = "feed_stg_edge")] #[cfg(feature = "feed_stg_edge")]
let mut feedback = feedback_or!( let mut feedback = feedback_or!(
@ -451,7 +451,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
); );
// A feedback to choose if an input is producing an error // A feedback to choose if an input is producing an error
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::new(matches!(cli.command, Commands::Fuzz{..}), Some(10))); let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::<FreeRTOSSystem>::new(matches!(cli.command, Commands::Fuzz{..}), Some(10)));
// 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(|| {
@ -491,7 +491,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
let qhelpers = tuple_list!(); let qhelpers = tuple_list!();
#[cfg(feature = "observe_systemstate")] #[cfg(feature = "observe_systemstate")]
let qhelpers = (QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock,scheduler_running, critical_section,input_counter_ptr,app_range.clone(), job_done_addr), qhelpers); let qhelpers = (FreeRTOSSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock,scheduler_running, critical_section,input_counter_ptr,app_range.clone(), job_done_addr), qhelpers);
#[cfg(feature = "observe_edges")] #[cfg(feature = "observe_edges")]
let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers); let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers);
let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers); let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers);
@ -526,9 +526,9 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
let stages = (systemstate::report::SchedulerStatsStage::default(),()); let stages = (systemstate::report::SchedulerStatsStage::default(),());
let stages = (StdMutationalStage::new(mutator), stages); let stages = (StdMutationalStage::new(mutator), stages);
#[cfg(feature = "mutate_stg")] #[cfg(feature = "mutate_stg")]
let mut stages = (STGSnippetStage::new(input_addr), stages); let mut stages = (STGSnippetStage::<_,_,_,FreeRTOSSystem>::new(input_addr), stages);
#[cfg(feature = "fuzz_int")] #[cfg(feature = "fuzz_int")]
let mut stages = (InterruptShiftStage::new(&interrupt_config), stages); let mut stages = (InterruptShiftStage::<_,_,_,FreeRTOSSystem>::new(&interrupt_config), stages);
if let Commands::Showmap { input } = cli.command.clone() { if let Commands::Showmap { input } = cli.command.clone() {
let s = input.as_os_str(); let s = input.as_os_str();

View File

@ -1,41 +1,48 @@
use libafl::SerdeAny;
use libafl::prelude::UsesInput;
use libafl::common::HasNamedMetadata;
use std::path::PathBuf;
use crate::time::clock::QemuClockObserver; use crate::time::clock::QemuClockObserver;
use libafl::corpus::Testcase;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use std::hash::Hash;
use libafl::events::EventFirer;
use libafl::state::MaybeHasClientPerfMonitor;
use libafl::prelude::State;
use libafl::feedbacks::Feedback;
use libafl_bolts::Named;
use libafl::Error;
use hashbrown::HashMap; use hashbrown::HashMap;
use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata}; use libafl::common::HasNamedMetadata;
use libafl::corpus::Testcase;
use libafl::events::EventFirer;
use libafl::feedbacks::Feedback;
use libafl::prelude::State;
use libafl::prelude::UsesInput;
use libafl::state::MaybeHasClientPerfMonitor;
use libafl::Error;
use libafl::{common::HasMetadata, executors::ExitKind, observers::ObserversTuple};
use libafl_bolts::Named;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
use std::path::PathBuf;
use super::ExecInterval;
use super::ReducedFreeRTOSSystemState;
use super::FreeRTOSSystemStateMetadata;
use super::observers::QemuSystemStateObserver; use super::observers::QemuSystemStateObserver;
use super::target_os::TargetSystem;
use super::ExecInterval;
use libafl_bolts::prelude::SerdeAny;
use std::borrow::Cow; use std::borrow::Cow;
use std::marker::PhantomData;
use crate::systemstate::target_os::*;
use libafl::prelude::StateInitializer; use libafl::prelude::StateInitializer;
//============================= Feedback //============================= Feedback
/// Shared Metadata for a systemstateFeedback /// Shared Metadata for a systemstateFeedback
#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SystemStateFeedbackState pub struct SystemStateFeedbackState<SYS>
where
SYS: TargetSystem,
{ {
name: Cow<'static, str>, name: Cow<'static, str>,
known_traces: HashMap<u64,(u64,u64,usize)>, // encounters,ticks,length known_traces: HashMap<u64, (u64, u64, usize)>, // encounters,ticks,length
longest: Vec<ReducedFreeRTOSSystemState>, longest: Vec<SYS::State>,
} }
impl Named for SystemStateFeedbackState libafl_bolts::impl_serdeany!(SystemStateFeedbackState<SYS: SerdeAny+TargetSystem>);
impl<SYS> Named for SystemStateFeedbackState<SYS>
where
SYS: TargetSystem,
{ {
#[inline] #[inline]
fn name(&self) -> &Cow<'static, str> { fn name(&self) -> &Cow<'static, str> {
@ -43,195 +50,265 @@ impl Named for SystemStateFeedbackState
} }
} }
impl Default for SystemStateFeedbackState impl<SYS> Default for SystemStateFeedbackState<SYS>
where
SYS: TargetSystem,
{ {
fn default() -> Self { fn default() -> Self {
Self {name: Cow::from("systemstate".to_string()), known_traces: HashMap::new(), longest: Vec::new() } Self {
name: Cow::from("systemstate".to_string()),
known_traces: HashMap::new(),
longest: Vec::new(),
}
} }
} }
/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] // /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`]
#[derive(Serialize, Deserialize, Clone, Debug)] // #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NovelSystemStateFeedback // pub struct NovelSystemStateFeedback
// {
// name: Cow<'static, str>,
// last_trace: Option<Vec<ReducedFreeRTOSSystemState>>,
// // known_traces: HashMap<u64,(u64,usize)>,
// }
// impl<S> StateInitializer<S> for NovelSystemStateFeedback {}
// impl<EM, I, OT, S> Feedback<EM, I, OT, S> for NovelSystemStateFeedback
// where
// S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata,
// S::Input: Default,
// EM: EventFirer<State = S>,
// OT: ObserversTuple<I, S>,
// {
// fn is_interesting(
// &mut self,
// state: &mut S,
// _manager: &mut EM,
// _input: &I,
// observers: &OT,
// _exit_kind: &ExitKind,
// ) -> Result<bool, Error>
// where
// {
// let observer : &QemuSystemStateObserver<S::Input> = observers.match_name::<QemuSystemStateObserver<S::Input>>("systemstate")
// .expect("QemuSystemStateObserver not found");
// let clock_observer = observers.match_name::<QemuClockObserver>("clocktime") //TODO not fixed
// .expect("QemuClockObserver not found");
// let feedbackstate = match state
// .named_metadata_map_mut()
// .get_mut::<SystemStateFeedbackState>("systemstate") {
// Some(s) => s,
// Option::None => {
// let n=SystemStateFeedbackState::default();
// state.named_metadata_map_mut().insert("systemstate",n);
// state.named_metadata_map_mut().get_mut::<SystemStateFeedbackState>("systemstate").unwrap()
// }
// };
// #[cfg(feature = "trace_job_response_times")]
// let last_runtime = observer.last_runtime();
// #[cfg(not(feature = "trace_job_response_times"))]
// let last_runtime = clock_observer.last_runtime();
// // let feedbackstate = state
// // .feedback_states_mut()
// // .match_name_mut::<systemstateFeedbackState>("systemstate")
// // .unwrap();
// // Do Stuff
// let mut hasher = DefaultHasher::new();
// observer.last_run.hash(&mut hasher);
// let somehash = hasher.finish();
// let mut is_novel = false;
// let mut takes_longer = false;
// match feedbackstate.known_traces.get_mut(&somehash) {
// Option::None => {
// is_novel = true;
// feedbackstate.known_traces.insert(somehash,(1,last_runtime,observer.last_run.len()));
// }
// Some(s) => {
// s.0+=1;
// if s.1 < last_runtime {
// s.1 = last_runtime;
// takes_longer = true;
// }
// }
// }
// if observer.last_run.len() > feedbackstate.longest.len() {
// feedbackstate.longest=observer.last_run.clone();
// }
// self.last_trace = Some(observer.last_run.clone());
// // if (!is_novel) { println!("not novel") };
// Ok(is_novel | takes_longer)
// }
// /// Append to the testcase the generated metadata in case of a new corpus item
// #[inline]
// fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase<I>) -> Result<(), Error> {
// let a = self.last_trace.take();
// match a {
// Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)),
// Option::None => (),
// }
// 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: &I) -> Result<(), Error> {
// self.last_trace = None;
// Ok(())
// }
// }
// impl Named for NovelSystemStateFeedback
// {
// #[inline]
// fn name(&self) -> &Cow<'static, str> {
// &self.name
// }
// }
// impl Default for NovelSystemStateFeedback
// {
// fn default() -> Self {
// Self {name: Cow::from("NovelSystemStateFeedback".to_string()), last_trace: None }
// }
// }
//=========================== Debugging Feedback
/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSystemStateObserver`]
#[derive(Debug)]
pub struct DumpSystraceFeedback<SYS>
where
SYS: TargetSystem,
{ {
name: Cow<'static, str>, name: Cow<'static, str>,
last_trace: Option<Vec<ReducedFreeRTOSSystemState>>, dumpfile: Option<PathBuf>,
// known_traces: HashMap<u64,(u64,usize)>, dump_metadata: bool,
select_task: Option<String>, // TODO: use some global config for this
phantom: PhantomData<SYS>,
} }
impl<S> StateInitializer<S> for NovelSystemStateFeedback {} impl<S, SYS> StateInitializer<S> for DumpSystraceFeedback<SYS> where SYS: TargetSystem {}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for NovelSystemStateFeedback impl<EM, I, OT, S, SYS> Feedback<EM, I, OT, S> for DumpSystraceFeedback<SYS>
where where
S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata,
S::Input: Default,
EM: EventFirer<State = S>, EM: EventFirer<State = S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
SYS: TargetSystem,
{ {
fn is_interesting( fn is_interesting(
&mut self, &mut self,
state: &mut S, state: &mut S,
_manager: &mut EM, _manager: &mut EM,
_input: &I, _input: &I,
observers: &OT, _observers: &OT,
_exit_kind: &ExitKind, _exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where {
{ if self.dumpfile.is_none() {
let observer : &QemuSystemStateObserver<S::Input> = observers.match_name::<QemuSystemStateObserver<S::Input>>("systemstate") return Ok(false);
.expect("QemuSystemStateObserver not found"); };
let clock_observer = observers.match_name::<QemuClockObserver>("clocktime") //TODO not fixed let trace = state
.expect("QemuClockObserver not found"); .metadata::<SYS::TraceData>()
let feedbackstate = match state .expect("TraceData not found");
.named_metadata_map_mut()
.get_mut::<SystemStateFeedbackState>("systemstate") {
Some(s) => s,
Option::None => {
let n=SystemStateFeedbackState::default();
state.named_metadata_map_mut().insert("systemstate",n);
state.named_metadata_map_mut().get_mut::<SystemStateFeedbackState>("systemstate").unwrap()
}
};
#[cfg(feature = "trace_job_response_times")]
let last_runtime = observer.last_runtime();
#[cfg(not(feature = "trace_job_response_times"))]
let last_runtime = clock_observer.last_runtime();
// let feedbackstate = state
// .feedback_states_mut()
// .match_name_mut::<systemstateFeedbackState>("systemstate")
// .unwrap();
// Do Stuff
let mut hasher = DefaultHasher::new();
observer.last_run.hash(&mut hasher);
let somehash = hasher.finish();
let mut is_novel = false;
let mut takes_longer = false;
match feedbackstate.known_traces.get_mut(&somehash) {
Option::None => {
is_novel = true;
feedbackstate.known_traces.insert(somehash,(1,last_runtime,observer.last_run.len()));
}
Some(s) => {
s.0+=1;
if s.1 < last_runtime {
s.1 = last_runtime;
takes_longer = true;
}
}
}
if observer.last_run.len() > feedbackstate.longest.len() {
feedbackstate.longest=observer.last_run.clone();
}
self.last_trace = Some(observer.last_run.clone());
// if (!is_novel) { println!("not novel") };
Ok(is_novel | takes_longer)
}
/// Append to the testcase the generated metadata in case of a new corpus item let names: Vec<String> = trace
#[inline] .states()
fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase<I>) -> Result<(), Error> { .iter()
let a = self.last_trace.take(); .map(|x| x.current_task().task_name().clone())
match a { .collect();
Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)),
Option::None => (),
}
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: &I) -> Result<(), Error> {
self.last_trace = None;
Ok(())
}
}
impl Named for NovelSystemStateFeedback
{
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl Default for NovelSystemStateFeedback
{
fn default() -> Self {
Self {name: Cow::from("NovelSystemStateFeedback".to_string()), last_trace: None }
}
}
//=========================== Debugging Feedback
/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSystemStateObserver`]
#[derive(Debug)]
pub struct DumpSystraceFeedback
{
name: Cow<'static, str>,
dumpfile: Option<PathBuf>,
dump_metadata: bool,
last_states: Option<HashMap<u64, ReducedFreeRTOSSystemState>>,
last_trace: Option<Vec<ExecInterval>>,
}
impl<S> StateInitializer<S> for DumpSystraceFeedback {}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for DumpSystraceFeedback
where
S: State + UsesInput + MaybeHasClientPerfMonitor,
EM: EventFirer<State = S>,
OT: ObserversTuple<I, S>
{
fn is_interesting(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
{
if self.dumpfile.is_none() {return Ok(false)};
let observer = observers.match_name::<QemuSystemStateObserver<S::Input>>("systemstate")
.expect("QemuSystemStateObserver not found");
let names : Vec<String> = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect();
match &self.dumpfile { match &self.dumpfile {
Some(s) => { Some(s) => {
let per_task_metadata = if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(&x.name) == observer.select_task.as_ref()).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release))) { let per_task_metadata = if let Some(worst_instance) = trace.worst_jobs_per_task_by_time().get(self.select_task.as_ref().unwrap_or(&"".to_string()))
// extract computation time spent in each task and abb {
let t : Vec<_> = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect(); // extract computation time spent in each task and abb
// task_name -> addr -> (count, time) let t: Vec<_> = trace.intervals()
let mut ret : HashMap<String, HashMap<u32, (usize, usize, u64)>> = HashMap::new(); .iter()
let mut t2 = t.clone(); .filter(|x| {
t2.sort_by_key(|x| x.get_task_name_unchecked()); x.start_tick < worst_instance.response
t2.chunk_by_mut(|x,y| x.get_task_name_unchecked() == y.get_task_name_unchecked()).for_each(|x| { && x.end_tick > worst_instance.release
x.sort_by_key(|y| y.abb.as_ref().unwrap().start); })
x.chunk_by(|y,z| y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start).for_each(|y| { .cloned()
match ret.get_mut(&y[0].get_task_name_unchecked()) { .collect();
Option::None => { // task_name -> addr -> (count, time)
ret.insert(y[0].get_task_name_unchecked(), HashMap::from([(y[0].abb.as_ref().unwrap().start, (y.len(), y.iter().filter(|x| x.is_abb_end()).count(), y.iter().map(|z| z.get_exec_time()).sum::<_>()))])); let mut ret: HashMap<String, HashMap<u32, (usize, usize, u64)>> =
} HashMap::new();
Some(x) => { let mut t2 = t.clone();
x.insert(y[0].abb.as_ref().unwrap().start, (y.len(), y.iter().filter(|x| x.is_abb_end()).count(), y.iter().map(|z| z.get_exec_time()).sum())); t2.sort_by_key(|x| x.get_task_name_unchecked());
} t2.chunk_by_mut(|x, y| {
x.get_task_name_unchecked() == y.get_task_name_unchecked()
})
.for_each(|x| {
x.sort_by_key(|y| y.abb.as_ref().unwrap().start);
x.chunk_by(|y, z| {
y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start
})
.for_each(|y| {
match ret.get_mut(&y[0].get_task_name_unchecked()) {
Option::None => {
ret.insert(
y[0].get_task_name_unchecked(),
HashMap::from([(
y[0].abb.as_ref().unwrap().start,
(
y.len(),
y.iter().filter(|x| x.is_abb_end()).count(),
y.iter().map(|z| z.get_exec_time()).sum::<_>(),
),
)]),
);
} }
}); Some(x) => {
x.insert(
y[0].abb.as_ref().unwrap().start,
(
y.len(),
y.iter().filter(|x| x.is_abb_end()).count(),
y.iter().map(|z| z.get_exec_time()).sum(),
),
);
}
}
}); });
// dbg!(&ret); });
ret // dbg!(&ret);
} else {HashMap::new()}; ret
std::fs::write(s,ron::to_string(&(&observer.last_trace,&observer.last_states,&observer.job_instances,per_task_metadata)).expect("Error serializing hashmap")).expect("Can not dump to file"); } else {
self.dumpfile = None HashMap::new()
}, };
Option::None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} std::fs::write(
s,
ron::to_string(&(
&trace,
per_task_metadata,
))
.expect("Error serializing hashmap"),
)
.expect("Can not dump to file");
self.dumpfile = None
}
Option::None => {
if self.dump_metadata {
println!("{:?}\n{:?}", trace, names);
}
}
}; };
// if self.dump_metadata {self.last_trace=Some(observer.last_trace.clone());} // if self.dump_metadata {self.last_trace=Some(observer.last_trace.clone());}
Ok(false) Ok(false)
} }
/// Append to the testcase the generated metadata in case of a new corpus item /// Append to the testcase the generated metadata in case of a new corpus item
#[inline] #[inline]
fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase<I>) -> Result<(), Error> { fn append_metadata(
if !self.dump_metadata {return Ok(());} &mut self,
_state: &mut S,
_manager: &mut EM,
_observers: &OT,
_testcase: &mut Testcase<I>,
) -> Result<(), Error> {
if !self.dump_metadata {
return Ok(());
}
// let a = self.last_trace.take(); // let a = self.last_trace.take();
// match a { // match a {
// Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), // Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)),
@ -243,12 +320,13 @@ where
/// Discard the stored metadata in case that the testcase is not added to the corpus /// Discard the stored metadata in case that the testcase is not added to the corpus
#[inline] #[inline]
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
self.last_trace = None;
Ok(()) Ok(())
} }
} }
impl Named for DumpSystraceFeedback impl<SYS> Named for DumpSystraceFeedback<SYS>
where
SYS: TargetSystem,
{ {
#[inline] #[inline]
fn name(&self) -> &Cow<'static, str> { fn name(&self) -> &Cow<'static, str> {
@ -256,39 +334,62 @@ impl Named for DumpSystraceFeedback
} }
} }
impl DumpSystraceFeedback impl<SYS> DumpSystraceFeedback<SYS>
where
SYS: TargetSystem,
{ {
/// Creates a new [`DumpSystraceFeedback`] /// Creates a new [`DumpSystraceFeedback`]
#[allow(unused)] #[allow(unused)]
pub fn new() -> Self { pub fn new() -> Self {
Self {name: Cow::from("Dumpsystemstate".to_string()), dumpfile: None, dump_metadata: false, last_trace: None, last_states: None } Self {
name: Cow::from("Dumpsystemstate".to_string()),
dumpfile: None,
dump_metadata: false,
phantom: PhantomData,
select_task: None,
}
} }
#[allow(unused)] #[allow(unused)]
pub fn with_dump(dumpfile: Option<PathBuf>) -> Self { pub fn with_dump(dumpfile: Option<PathBuf>) -> Self {
Self {name: Cow::from("Dumpsystemstate".to_string()), dumpfile: dumpfile, dump_metadata: false, last_trace: None, last_states: None} Self {
name: Cow::from("Dumpsystemstate".to_string()),
dumpfile: dumpfile,
dump_metadata: false,
phantom: PhantomData,
select_task: None,
}
} }
#[allow(unused)] #[allow(unused)]
pub fn metadata_only() -> Self { pub fn metadata_only() -> Self {
Self {name: Cow::from("Dumpsystemstate".to_string()), dumpfile: None, dump_metadata: true, last_trace: None, last_states: None} Self {
name: Cow::from("Dumpsystemstate".to_string()),
dumpfile: None,
dump_metadata: true,
phantom: PhantomData,
select_task: None,
}
} }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct SystraceErrorFeedback pub struct SystraceErrorFeedback<SYS>
where
SYS: TargetSystem,
{ {
name: Cow<'static, str>, name: Cow<'static, str>,
dump_case: bool, dump_case: bool,
max_reports: Option<usize>, max_reports: Option<usize>,
phantom: std::marker::PhantomData<SYS>,
} }
impl<S> StateInitializer<S> for SystraceErrorFeedback {} impl<S, SYS> StateInitializer<S> for SystraceErrorFeedback<SYS> where SYS: TargetSystem {}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for SystraceErrorFeedback impl<EM, I, OT, S, SYS> Feedback<EM, I, OT, S> for SystraceErrorFeedback<SYS>
where where
S: State + UsesInput + MaybeHasClientPerfMonitor, S: State + UsesInput + MaybeHasClientPerfMonitor,
EM: EventFirer<State = S>, EM: EventFirer<State = S>,
OT: ObserversTuple<I, S> OT: ObserversTuple<I, S>,
SYS: TargetSystem,
{ {
fn is_interesting( fn is_interesting(
&mut self, &mut self,
@ -298,20 +399,22 @@ where
observers: &OT, observers: &OT,
_exit_kind: &ExitKind, _exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where {
{
#[cfg(feature = "trace_stg")] #[cfg(feature = "trace_stg")]
{ {
let observer = observers.match_name::<QemuSystemStateObserver<S::Input>>("systemstate") let observer = observers
.match_name::<QemuSystemStateObserver<S::Input, SYS>>("systemstate")
.expect("QemuSystemStateObserver not found"); .expect("QemuSystemStateObserver not found");
let is_err = (!observer.success || observer.do_report); let is_err = (!observer.success || observer.do_report);
if let Some(m) = self.max_reports { if let Some(m) = self.max_reports {
if m <= 0 {return Ok(false);} if m <= 0 {
return Ok(false);
}
if is_err { if is_err {
self.max_reports = Some(m-1); self.max_reports = Some(m - 1);
} }
} }
return Ok(self.dump_case&&is_err); return Ok(self.dump_case && is_err);
} }
#[cfg(not(feature = "trace_stg"))] #[cfg(not(feature = "trace_stg"))]
{ {
@ -320,7 +423,13 @@ where
} }
/// Append to the testcase the generated metadata in case of a new corpus item /// Append to the testcase the generated metadata in case of a new corpus item
#[inline] #[inline]
fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase<I>) -> Result<(), Error> { fn append_metadata(
&mut self,
_state: &mut S,
_manager: &mut EM,
_observers: &OT,
_testcase: &mut Testcase<I>,
) -> Result<(), Error> {
Ok(()) Ok(())
} }
@ -331,7 +440,9 @@ where
} }
} }
impl Named for SystraceErrorFeedback impl<SYS> Named for SystraceErrorFeedback<SYS>
where
SYS: TargetSystem,
{ {
#[inline] #[inline]
fn name(&self) -> &Cow<'static, str> { fn name(&self) -> &Cow<'static, str> {
@ -339,10 +450,17 @@ impl Named for SystraceErrorFeedback
} }
} }
impl SystraceErrorFeedback impl<SYS> SystraceErrorFeedback<SYS>
where
SYS: TargetSystem,
{ {
#[must_use] #[must_use]
pub fn new(dump_case: bool, max_reports: Option<usize>) -> Self { pub fn new(dump_case: bool, max_reports: Option<usize>) -> Self {
Self {name: Cow::from(String::from("SystraceErrorFeedback")), dump_case, max_reports} Self {
name: Cow::from(String::from("SystraceErrorFeedback")),
dump_case,
max_reports,
phantom: std::marker::PhantomData,
}
} }
} }

View File

@ -15,14 +15,6 @@ use libafl_qemu::modules::{EmulatorModule, EmulatorModuleTuple};
use libafl_qemu::sys::TCGTemp; use libafl_qemu::sys::TCGTemp;
use libafl_qemu::qemu::MemAccessInfo; use libafl_qemu::qemu::MemAccessInfo;
use crate::systemstate::RawFreeRTOSSystemState;
use crate::systemstate::CURRENT_SYSTEMSTATE_VEC;
use crate::systemstate::NUM_PRIOS;
use super::freertos::void_ptr;
use super::freertos::TCB_t;
use super::freertos::rtos_struct::List_Item_struct;
use super::freertos::rtos_struct::*;
use super::freertos;
use super::CaptureEvent; use super::CaptureEvent;
use libafl_qemu::EmulatorModules; use libafl_qemu::EmulatorModules;
use libafl::prelude::ObserversTuple; use libafl::prelude::ObserversTuple;
@ -31,13 +23,6 @@ use libafl::prelude::ObserversTuple;
//============================= API symbols //============================= API symbols
pub const ISR_SYMBOLS : &'static [&'static str] = &[
// ISRs
"Reset_Handler","Default_Handler","Default_Handler2","Default_Handler3","Default_Handler4","Default_Handler5","Default_Handler6","vPortSVCHandler","xPortPendSVHandler","xPortSysTickHandler","ISR_0_Handler", "ISR_1_Handler", "ISR_2_Handler", "ISR_3_Handler", "ISR_4_Handler", "ISR_5_Handler", "ISR_6_Handler", "ISR_7_Handler", "ISR_8_Handler", "ISR_9_Handler", "ISR_10_Handler", "ISR_11_Handler", "ISR_12_Handler", "ISR_13_Handler"
];
pub const USR_ISR_SYMBOLS : &'static [&'static str] = &[
"ISR_0_Handler", "ISR_1_Handler", "ISR_2_Handler", "ISR_3_Handler", "ISR_4_Handler", "ISR_5_Handler", "ISR_6_Handler", "ISR_7_Handler", "ISR_8_Handler", "ISR_9_Handler", "ISR_10_Handler", "ISR_11_Handler", "ISR_12_Handler", "ISR_13_Handler"
];
/// Read ELF program headers to resolve physical load addresses. /// Read ELF program headers to resolve physical load addresses.
fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
@ -97,454 +82,10 @@ pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option<std::ops::Range
return None; return None;
} }
//============================= Qemu Helper
/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs
#[derive(Debug)]
pub struct QemuSystemStateHelper {
// Address of API functions
api_fn_addrs: HashMap<GuestAddr, String>,
api_fn_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
// Address of interrupt routines
isr_addrs: HashMap<GuestAddr, String>,
isr_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
input_mem: Range<GuestAddr>,
tcb_addr: GuestAddr,
ready_queues: GuestAddr,
delay_queue: GuestAddr,
delay_queue_overflow: GuestAddr,
scheduler_lock_addr: GuestAddr,
scheduler_running_addr: GuestAddr,
critical_addr: GuestAddr,
input_counter: Option<GuestAddr>,
app_range: Range<GuestAddr>,
job_done_addrs: GuestAddr,
}
impl QemuSystemStateHelper {
#[must_use]
pub fn new(
api_fn_addrs: HashMap<GuestAddr, String>,
api_fn_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
isr_addrs: HashMap<GuestAddr, String>,
isr_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
input_mem: Range<GuestAddr>,
tcb_addr: GuestAddr,
ready_queues: GuestAddr,
delay_queue: GuestAddr,
delay_queue_overflow: GuestAddr,
scheduler_lock_addr: GuestAddr,
scheduler_running_addr: GuestAddr,
critical_addr: GuestAddr,
input_counter: Option<GuestAddr>,
app_range: Range<GuestAddr>,
job_done_addrs: GuestAddr,
) -> Self {
QemuSystemStateHelper {
api_fn_addrs,
api_fn_ranges,
isr_addrs,
isr_ranges,
input_mem,
tcb_addr: tcb_addr,
ready_queues: ready_queues,
delay_queue,
delay_queue_overflow,
scheduler_lock_addr,
scheduler_running_addr,
critical_addr,
input_counter: input_counter,
app_range,
job_done_addrs,
}
}
}
impl<S, I> EmulatorModule<S> for QemuSystemStateHelper
where
S: UsesInput<Input = I> + Unpin,
{
fn first_exec<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
where
ET: EmulatorModuleTuple<S>,
{
// for wp in self.api_fn_addrs.keys() {
// _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::<ET, S>), false);
// }
for wp in self.isr_addrs.keys() {
_emulator_modules.instructions(*wp, Hook::Function(exec_isr_hook::<ET, S>), false);
}
_emulator_modules.jmps(Hook::Function(gen_jmp_is_syscall::<ET, S>), Hook::Function(trace_jmp::<ET, S>));
#[cfg(feature = "trace_job_response_times")]
_emulator_modules.instructions(self.job_done_addrs, Hook::Function(job_done_hook::<ET, S>), false);
#[cfg(feature = "trace_reads")]
_emulator_modules.reads(Hook::Function(gen_read_is_input::<ET, S>), Hook::Empty,Hook::Empty,Hook::Empty,Hook::Empty,Hook::Function(trace_reads::<ET, S>));
unsafe { INPUT_MEM = self.input_mem.clone() };
}
// TODO: refactor duplicate code
fn pre_exec<ET>(
&mut self,
_emulator_modules: &mut EmulatorModules<ET, S>,
_state: &mut S,
_input: &S::Input,
) where
ET: EmulatorModuleTuple<S>,
{
unsafe {
CURRENT_SYSTEMSTATE_VEC.clear();
JOBS_DONE.clear();
}
}
fn post_exec<OT, ET>(
&mut self,
_emulator_modules: &mut EmulatorModules<ET, S>,
_state: &mut S,
_input: &S::Input,
_observers: &mut OT,
_exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S::Input, S>,
ET: EmulatorModuleTuple<S>,
{
trigger_collection(&_emulator_modules.qemu(),(0, 0), CaptureEvent::End, self);
unsafe {
let c = _emulator_modules.qemu().cpu_from_index(0);
let pc = c.read_reg::<i32>(15).unwrap();
if CURRENT_SYSTEMSTATE_VEC.len() == 0 {return;}
CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].edge = (pc,0);
CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].capture_point = (CaptureEvent::End,"Breakpoint".to_string());
}
// Find the first ISREnd of vPortSVCHandler and drop anything before
unsafe {
let mut index = 0;
while index < CURRENT_SYSTEMSTATE_VEC.len() {
if CaptureEvent::ISREnd == CURRENT_SYSTEMSTATE_VEC[index].capture_point.0 && CURRENT_SYSTEMSTATE_VEC[index].capture_point.1 == "xPortPendSVHandler" {
break;
}
index += 1;
}
CURRENT_SYSTEMSTATE_VEC.drain(..index);
}
}
type ModuleAddressFilter = NopAddressFilter;
type ModulePageFilter = NopPageFilter;
fn address_filter(&self) -> &Self::ModuleAddressFilter {
todo!()
}
fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter {
todo!()
}
fn page_filter(&self) -> &Self::ModulePageFilter {
todo!()
}
fn page_filter_mut(&mut self) -> &mut Self::ModulePageFilter {
todo!()
}
}
fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &libafl_qemu::Qemu, target: GuestAddr) -> (freertos::List_t, bool) {
let read : freertos::List_t = freertos::emu_lookup::lookup(emulator, target);
let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::<freertos::List_t>()).unwrap();
let mut next_index = read.pxIndex;
for _j in 0..read.uxNumberOfItems {
// always jump over the xListEnd marker
if (target..target+listbytes).contains(&next_index) {
let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index);
let new_next_index=next_item.pxNext;
systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item));
next_index = new_next_index;
}
let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index);
// println!("Item at {}: {:?}",next_index,next_item);
if next_item.pvContainer != target {
// the list is being modified, abort by setting the list empty
eprintln!("Warning: attempted to read a list that is being modified");
let mut read=read;
read.uxNumberOfItems = 0;
return (read, false);
}
// assert_eq!(next_item.pvContainer,target);
let new_next_index=next_item.pxNext;
let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner);
// println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb);
systemstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone()));
systemstate.dumping_ground.insert(next_index,List_Item_struct(next_item));
next_index=new_next_index;
}
// Handle edge case where the end marker was not included yet
if (target..target+listbytes).contains(&next_index) {
let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index);
systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item));
}
return (read, true);
}
#[inline]
fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr), event: CaptureEvent, h: &QemuSystemStateHelper) {
let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::<freertos::List_t>()).unwrap();
let mut systemstate = RawFreeRTOSSystemState::default();
match event {
CaptureEvent::APIStart => {
let s = h.api_fn_addrs.get(&edge.1).unwrap();
systemstate.capture_point=(CaptureEvent::APIStart, s.to_string());
},
CaptureEvent::APIEnd => {
let s = h.api_fn_addrs.get(&edge.0).unwrap();
systemstate.capture_point=(CaptureEvent::APIEnd, s.to_string());
},
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 {
// println!("Not found: {:#x} {:#x}", edge.0.unwrap_or(0), edge.1.unwrap_or(0));
}
systemstate.edge = ((edge.0),(edge.1));
systemstate.qemu_tick = get_icount(emulator);
let mut buf : [u8; 4] = [0,0,0,0];
match h.input_counter {
Some(s) => unsafe { emulator.read_mem(s, &mut buf); },
Option::None => (),
};
systemstate.input_counter = GuestAddr::from_le_bytes(buf);
let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr);
if curr_tcb_addr == 0 {
return;
};
// println!("{:?}",std::str::from_utf8(&current_tcb.pcTaskName));
let critical : void_ptr = freertos::emu_lookup::lookup(emulator, h.critical_addr);
let suspended : void_ptr = freertos::emu_lookup::lookup(emulator, h.scheduler_lock_addr);
let _running : void_ptr = freertos::emu_lookup::lookup(emulator, h.scheduler_running_addr);
systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr);
// During ISRs it is only safe to extract structs if they are not currently being modified
if systemstate.capture_point.0==CaptureEvent::APIStart || systemstate.capture_point.0==CaptureEvent::APIEnd || (critical == 0 && suspended == 0 ) {
// Extract delay list
let mut target : GuestAddr = h.delay_queue;
target = freertos::emu_lookup::lookup(emulator, target);
let _temp = read_freertos_list(&mut systemstate, emulator, target);
systemstate.delay_list = _temp.0;
systemstate.read_invalid |= !_temp.1;
// Extract delay list overflow
let mut target : GuestAddr = h.delay_queue_overflow;
target = freertos::emu_lookup::lookup(emulator, target);
let _temp = read_freertos_list(&mut systemstate, emulator, target);
systemstate.delay_list_overflow = _temp.0;
systemstate.read_invalid |= !_temp.1;
// Extract suspended tasks (infinite wait), seems broken, always appreas to be modified
// let mut target : GuestAddr = h.suspended_queue;
// target = freertos::emu_lookup::lookup(emulator, target);
// systemstate.suspended_list = read_freertos_list(&mut systemstate, emulator, target);
// Extract priority lists
for i in 0..NUM_PRIOS {
let target : GuestAddr = listbytes*GuestAddr::try_from(i).unwrap()+h.ready_queues;
let _temp = read_freertos_list(&mut systemstate, emulator, target);
systemstate.prio_ready_lists[i] = _temp.0;
systemstate.read_invalid |= !_temp.1;
}
} else {
systemstate.read_invalid = true;
}
systemstate.mem_reads = unsafe { MEM_READ.take().unwrap_or_default() };
unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); }
}
//============================= Trace job response times
pub static mut JOBS_DONE : Vec<(u64, String)> = vec![];
pub fn job_done_hook<QT, S>(
hooks: &mut EmulatorModules<QT, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
)
where
S: UsesInput,
QT: EmulatorModuleTuple<S>,
{
let emulator = hooks.qemu();
let h = hooks.modules().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(&emulator, h.tcb_addr);
if curr_tcb_addr == 0 {
return;
};
let current_tcb : TCB_t = freertos::emu_lookup::lookup(&emulator,curr_tcb_addr);
let tmp = unsafe {std::mem::transmute::<[i8; 10],[u8; 10]>(current_tcb.pcTaskName)};
let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::<String>();
unsafe { JOBS_DONE.push((get_icount(&emulator), name)); }
}
//============================= Trace interrupt service routines
pub fn exec_isr_hook<QT, S>(
hooks: &mut EmulatorModules<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
)
where
S: UsesInput,
QT: EmulatorModuleTuple<S>,
{
let emulator = hooks.qemu();
let h = hooks.modules().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
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 syscalls and returns
pub fn gen_jmp_is_syscall<QT, S>(
hooks: &mut EmulatorModules<QT, S>,
_state: Option<&mut S>,
src: GuestAddr,
dest: GuestAddr,
) -> Option<u64>
where
S: UsesInput,
QT: EmulatorModuleTuple<S>,
{
if let Some(h) = hooks.modules().match_first_type::<QemuSystemStateHelper>() {
if h.app_range.contains(&src) && !h.app_range.contains(&dest) && in_any_range(&h.isr_ranges,src).is_none() {
if let Some(_) = in_any_range(&h.api_fn_ranges,dest) {
// println!("New jmp {:x} {:x}", src, dest);
// println!("API Call Edge {:x} {:x}", src, dest);
return Some(1);
// TODO: trigger collection right here
// otherwise there can be a race-condition, where LAST_API_CALL is set before the api starts, if the interrupt handler calls an api function, it will misidentify the callsite of that api call
}
} else if dest == 0 { // !h.app_range.contains(&src) &&
if let Some(_) = in_any_range(&h.api_fn_ranges, src) {
// println!("API Return Edge {:#x}", src);
return Some(2);
}
if let Some(_) = in_any_range(&h.isr_ranges, src) {
// println!("ISR Return Edge {:#x}", src);
return Some(3);
}
}
}
return None;
}
pub fn trace_jmp<QT, S>(
hooks: &mut EmulatorModules<QT, S>,
_state: Option<&mut S>,
src: GuestAddr, mut dest: GuestAddr, id: u64
)
where
S: UsesInput,
QT: EmulatorModuleTuple<S>,
{
let h = hooks.modules().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
let emulator = hooks.qemu();
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));
}
}
//============================= Read Hooks
#[allow(unused)]
pub fn gen_read_is_input<QT, S>(
hooks: &mut EmulatorModules<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
_addr: *mut TCGTemp,
_info: MemAccessInfo,
) -> Option<u64>
where
S: UsesInput,
QT: EmulatorModuleTuple<S>,
{
if let Some(h) = hooks.modules().match_first_type::<QemuSystemStateHelper>() {
if h.app_range.contains(&pc) {
// println!("gen_read {:x}", pc);
return Some(1);
}
}
return None;
}
static mut INPUT_MEM : Range<GuestAddr> = 0..0;
static mut MEM_READ : Option<Vec<(GuestAddr, u8)>> = None;
#[allow(unused)]
pub fn trace_reads<QT, S>(
hooks: &mut EmulatorModules<QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
_size: usize
)
where
S: UsesInput,
QT: EmulatorModuleTuple<S>,
{
if unsafe { INPUT_MEM.contains(&addr) } {
let emulator = hooks.qemu();
let mut buf : [u8; 1] = [0];
unsafe {emulator.read_mem(addr, &mut buf);}
if unsafe { MEM_READ.is_none() } {
unsafe { MEM_READ = Some(Vec::from([(addr, buf[0])])) };
} else {
unsafe { MEM_READ.as_mut().unwrap().push((addr, buf[0])) };
}
// println!("exec_read {:x} {}", addr, size);
}
}
//============================= Utility functions //============================= Utility functions
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
let c = emulator.cpu_from_index(0); let c = emulator.cpu_from_index(0);
@ -556,7 +97,7 @@ fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 {
} }
} }
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 {
unsafe { unsafe {

View File

@ -10,9 +10,6 @@ use hashbrown::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use itertools::Itertools; use itertools::Itertools;
use freertos::TCB_t;
pub mod freertos;
pub mod helpers; pub mod helpers;
pub mod observers; pub mod observers;
pub mod feedbacks; pub mod feedbacks;
@ -20,9 +17,7 @@ pub mod schedulers;
pub mod stg; pub mod stg;
pub mod mutational; pub mod mutational;
pub mod report; pub mod report;
pub mod target_os;
// Constants
const NUM_PRIOS: usize = 15;
//============================= Struct definitions //============================= Struct definitions
@ -43,154 +38,10 @@ pub enum CaptureEvent {
- ReducedFreeRTOSSystemState: Generalized state of the system, without execution context - ReducedFreeRTOSSystemState: Generalized state of the system, without execution context
- ExecInterval: Some interval of execution between instants - ExecInterval: Some interval of execution between instants
- AtomicBasicBlock: A single-entry multiple-exit region between api calls. May be used referenced in multiple intervals. - AtomicBasicBlock: A single-entry multiple-exit region between api calls. May be used referenced in multiple intervals.
- JobInstance: A single execution of a task, records the place and input read - RTOSJob: A single execution of a task, records the place and input read
- TaskJob: Generalized Job instance, records the worst inputs seen so far - RTOSTask: Generalized Job instance, records the worst inputs seen so far
*/ */
// ============================= State info
/// Raw info Dump from Qemu
#[derive(Debug, Default)]
pub struct RawFreeRTOSSystemState {
qemu_tick: u64,
current_tcb: TCB_t,
prio_ready_lists: [freertos::List_t; NUM_PRIOS],
delay_list: freertos::List_t,
delay_list_overflow: freertos::List_t,
dumping_ground: HashMap<u32,freertos::rtos_struct>,
read_invalid: bool,
input_counter: u32,
edge: (GuestAddr,GuestAddr),
capture_point: (CaptureEvent,String),
mem_reads: Vec<(u32, u8)>
}
/// List of system state dumps from EmulatorModules
static mut CURRENT_SYSTEMSTATE_VEC: Vec<RawFreeRTOSSystemState> = vec![];
/// A reduced version of freertos::TCB_t
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct RefinedTCB {
pub task_name: String,
pub priority: u32,
pub base_priority: u32,
mutexes_held: u32,
// notify_value: u32,
notify_state: u8,
}
impl PartialEq for RefinedTCB {
fn eq(&self, other: &Self) -> bool {
let ret = self.task_name == other.task_name &&
self.priority == other.priority &&
self.base_priority == other.base_priority;
#[cfg(feature = "do_hash_notify_state")]
let ret = ret && self.notify_state == other.notify_state;
ret
}
}
impl Hash for RefinedTCB {
fn hash<H: Hasher>(&self, state: &mut H) {
self.task_name.hash(state);
self.priority.hash(state);
self.mutexes_held.hash(state);
#[cfg(feature = "do_hash_notify_state")]
self.notify_state.hash(state);
// self.notify_value.hash(state);
}
}
impl RefinedTCB {
pub fn from_tcb(input: &TCB_t) -> Self {
unsafe {
let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName);
let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::<String>();
Self {
task_name: name,
priority: input.uxPriority,
base_priority: input.uxBasePriority,
mutexes_held: input.uxMutexesHeld,
// notify_value: input.ulNotifiedValue[0],
notify_state: input.ucNotifyState[0],
}
}
}
pub fn from_tcb_owned(input: TCB_t) -> Self {
unsafe {
let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName);
let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::<String>();
Self {
task_name: name,
priority: input.uxPriority,
base_priority: input.uxBasePriority,
mutexes_held: input.uxMutexesHeld,
// notify_value: input.ulNotifiedValue[0],
notify_state: input.ucNotifyState[0],
}
}
}
}
/// Reduced information about a systems state, without any execution context
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct ReducedFreeRTOSSystemState {
// pub tick: u64,
pub current_task: RefinedTCB,
ready_list_after: Vec<RefinedTCB>,
delay_list_after: Vec<RefinedTCB>,
read_invalid: bool,
// edge: (Option<GuestAddr>,Option<GuestAddr>),
// pub capture_point: (CaptureEvent,String),
// input_counter: u32
}
impl PartialEq for ReducedFreeRTOSSystemState {
fn eq(&self, other: &Self) -> bool {
self.current_task == other.current_task && self.ready_list_after == other.ready_list_after &&
self.delay_list_after == other.delay_list_after && self.read_invalid == other.read_invalid
// && self.edge == other.edge
// && self.capture_point == other.capture_point
}
}
impl Hash for ReducedFreeRTOSSystemState {
fn hash<H: Hasher>(&self, state: &mut H) {
self.current_task.hash(state);
self.ready_list_after.hash(state);
self.delay_list_after.hash(state);
self.read_invalid.hash(state);
}
}
impl ReducedFreeRTOSSystemState {
// fn get_tick(&self) -> u64 {
// self.tick
// }
pub fn print_lists(&self) -> String {
let mut ret = String::from("+");
for j in self.ready_list_after.iter() {
ret.push_str(format!(" {}", j.task_name).as_str());
}
ret.push_str("\n-");
for j in self.delay_list_after.iter() {
ret.push_str(format!(" {}", j.task_name).as_str());
}
ret
}
pub fn get_hash(&self) -> u64 {
let mut h = DefaultHasher::new();
self.hash(&mut h);
h.finish()
}
}
impl fmt::Display for ReducedFreeRTOSSystemState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ready = self.ready_list_after.iter().map(|x| x.task_name.clone()).collect::<Vec<_>>().join(" ");
let delay = self.delay_list_after.iter().map(|x| x.task_name.clone()).collect::<Vec<_>>().join(" ");
write!(f, "Valid: {} | Current: {} | Ready: {} | Delay: {}", u32::from(!self.read_invalid), self.current_task.task_name, ready, delay)
}
}
// ============================= Interval info // ============================= Interval info
// #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] // #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
@ -209,13 +60,13 @@ pub struct ExecInterval {
pub start_capture: (CaptureEvent, String), pub start_capture: (CaptureEvent, String),
pub end_capture: (CaptureEvent, String), pub end_capture: (CaptureEvent, String),
pub level: u8, pub level: u8,
tick_spend_preempted: u64, // tick_spend_preempted: u64,
pub abb: Option<AtomicBasicBlock> pub abb: Option<AtomicBasicBlock>
} }
impl ExecInterval { impl ExecInterval {
pub fn get_exec_time(&self) -> u64 { pub fn get_exec_time(&self) -> u64 {
self.end_tick-self.start_tick-self.tick_spend_preempted self.end_tick-self.start_tick//-self.tick_spend_preempted
} }
pub fn is_valid(&self) -> bool { pub fn is_valid(&self) -> bool {
self.start_tick != 0 || self.end_tick != 0 self.start_tick != 0 || self.end_tick != 0
@ -226,18 +77,18 @@ impl ExecInterval {
} }
/// Attach this interval to the later one, keep a record of the time spend preempted /// 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 { // 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() { // if self.end_state!=later_interval.start_state || self.abb!=later_interval.abb || !self.is_valid() || !later_interval.is_valid() {
return false; // return false;
} // }
// assert_eq!(self.end_state, later_interval.start_state); // // assert_eq!(self.end_state, later_interval.start_state);
// assert_eq!(self.abb, later_interval.abb); // // 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.tick_spend_preempted += self.tick_spend_preempted + (later_interval.start_tick-self.end_tick);
later_interval.start_tick = self.start_tick; // later_interval.start_tick = self.start_tick;
later_interval.start_state = self.start_state; // later_interval.start_state = self.start_state;
self.invaildate(); // self.invaildate();
return true; // return true;
} // }
pub fn get_hash_index(&self) -> (u64, u64) { pub fn get_hash_index(&self) -> (u64, u64) {
return (self.start_state, self.abb.as_ref().expect("ABB not set").get_hash()) return (self.start_state, self.abb.as_ref().expect("ABB not set").get_hash())
@ -357,20 +208,13 @@ impl AtomicBasicBlock {
} }
fn get_task_names(trace: &Vec<ReducedFreeRTOSSystemState>) -> HashSet<String> {
let mut ret: HashSet<_, _> = HashSet::new();
for state in trace {
ret.insert(state.current_task.task_name.to_string());
}
ret
}
libafl_bolts::impl_serdeany!(AtomicBasicBlock); libafl_bolts::impl_serdeany!(AtomicBasicBlock);
// ============================= Job instances // ============================= Job instances
#[derive(Debug, Default, Serialize, Deserialize, Clone)] #[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct JobInstance { pub struct RTOSJob {
pub name: String, pub name: String,
pub mem_reads: Vec<(u32, u8)>, pub mem_reads: Vec<(u32, u8)>,
pub release: u64, pub release: u64,
@ -381,18 +225,18 @@ pub struct JobInstance {
hash_cache: u64 hash_cache: u64
} }
impl PartialEq for JobInstance { impl PartialEq for RTOSJob {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.abbs == other.abbs self.abbs == other.abbs
} }
} }
impl Eq for JobInstance {} impl Eq for RTOSJob {}
impl Hash for JobInstance { impl Hash for RTOSJob {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.abbs.hash(state); self.abbs.hash(state);
} }
} }
impl JobInstance { impl RTOSJob {
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();
@ -415,7 +259,7 @@ impl JobInstance {
// ============================= Generalized job instances // ============================= Generalized job instances
#[derive(Debug, Default, Serialize, Deserialize, Clone)] #[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct TaskJob { pub struct RTOSTask {
pub name: String, pub name: String,
pub worst_bytes: Vec<u8>, pub worst_bytes: Vec<u8>,
pub woet_ticks: u64, pub woet_ticks: u64,
@ -424,18 +268,18 @@ pub struct TaskJob {
hash_cache: u64 hash_cache: u64
} }
impl PartialEq for TaskJob { impl PartialEq for RTOSTask {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.abbs == other.abbs self.abbs == other.abbs
} }
} }
impl Eq for TaskJob {} impl Eq for RTOSTask {}
impl Hash for TaskJob { impl Hash for RTOSTask {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.abbs.hash(state); self.abbs.hash(state);
} }
} }
impl TaskJob { impl RTOSTask {
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();
@ -453,7 +297,7 @@ impl TaskJob {
self.hash_cache self.hash_cache
} }
} }
pub fn try_update(&mut self, other: &JobInstance) -> 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;
if other.exec_ticks > self.woet_ticks { if other.exec_ticks > self.woet_ticks {
@ -464,7 +308,7 @@ impl TaskJob {
} }
ret ret
} }
pub fn from_instance(input: &JobInstance) -> Self { pub fn from_instance(input: &RTOSJob) -> Self {
let c = input.get_hash_cached(); let c = input.get_hash_cached();
Self { Self {
name: input.name.clone(), name: input.name.clone(),
@ -475,7 +319,7 @@ impl TaskJob {
hash_cache: c hash_cache: c
} }
} }
pub fn map_bytes_onto(&self, input: &JobInstance, offset: Option<u32>) -> Vec<(u32,u8)> { pub fn map_bytes_onto(&self, input: &RTOSJob, offset: Option<u32>) -> Vec<(u32,u8)> {
if input.mem_reads.len() == 0 {return vec![];} if input.mem_reads.len() == 0 {return vec![];}
let ret = input.mem_reads.iter().take(self.worst_bytes.len()).enumerate().filter_map(|(idx,(addr,oldbyte))| if self.worst_bytes[idx]!=*oldbyte {Some((*addr-offset.unwrap_or_default(), self.worst_bytes[idx]))} else {None}).collect(); let ret = input.mem_reads.iter().take(self.worst_bytes.len()).enumerate().filter_map(|(idx,(addr,oldbyte))| if self.worst_bytes[idx]!=*oldbyte {Some((*addr-offset.unwrap_or_default(), self.worst_bytes[idx]))} else {None}).collect();
// eprintln!("Mapped: {:?}", ret); // eprintln!("Mapped: {:?}", ret);
@ -485,48 +329,3 @@ impl TaskJob {
// ============================= Per testcase metadata // ============================= Per testcase metadata
// Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct FreeRTOSSystemStateMetadata {
pub inner: Vec<ReducedFreeRTOSSystemState>,
// TODO: Add abbs and memory reads
trace_length: usize,
indices: Vec<usize>, // Hashed enumeration of States
tcref: isize,
}
impl FreeRTOSSystemStateMetadata {
pub fn new(inner: Vec<ReducedFreeRTOSSystemState>) -> Self{
let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect();
Self {trace_length: inner.len(), inner: inner, indices: tmp, tcref: 0}
}
}
pub fn compute_hash<T>(obj: T) -> u64
where
T: Hash
{
let mut s = DefaultHasher::new();
obj.hash(&mut s);
s.finish()
}
// impl AsSlice for FreeRTOSSystemStateMetadata {
// /// Convert the slice of system-states to a slice of hashes over enumerated states
// fn as_slice(&self) -> &[usize] {
// self.indices.as_slice()
// }
// type Entry = usize;
// }
impl HasRefCnt for FreeRTOSSystemStateMetadata {
fn refcnt(&self) -> isize {
self.tcref
}
fn refcnt_mut(&mut self) -> &mut isize {
&mut self.tcref
}
}
libafl_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata);

View File

@ -13,13 +13,13 @@ use libafl::{
}; };
use libafl::prelude::State; use libafl::prelude::State;
use petgraph::{graph::NodeIndex, graph::{self, DiGraph}}; use petgraph::{graph::NodeIndex, graph::{self, DiGraph}};
use crate::{time::clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; use crate::{time::clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval}};
use libafl::state::HasCurrentTestcase; use libafl::state::HasCurrentTestcase;
use std::borrow::Cow; use std::borrow::Cow;
use simple_moving_average::SMA; use simple_moving_average::SMA;
use super::{stg::{STGEdge, STGNode}, JobInstance}; use super::{stg::{STGEdge, STGNode}, target_os::TargetSystem, RTOSJob};
// 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
@ -68,21 +68,33 @@ pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec<u8> {
//======================= Custom mutator //======================= Custom mutator
fn is_interrupt_handler(graph: &DiGraph<STGNode, STGEdge>, node: NodeIndex) -> bool { fn is_interrupt_handler<SYS>(graph: &DiGraph<STGNode<SYS>, STGEdge>, node: NodeIndex) -> bool
where
SYS: TargetSystem,
{
graph.edges_directed(node as NodeIndex, petgraph::Direction::Incoming).any(|x| x.weight().event == CaptureEvent::ISRStart) graph.edges_directed(node as NodeIndex, petgraph::Direction::Incoming).any(|x| x.weight().event == CaptureEvent::ISRStart)
} }
fn has_interrupt_handler_non_systick(graph: &DiGraph<STGNode, STGEdge>, node: NodeIndex) -> bool { fn has_interrupt_handler_non_systick<SYS>(graph: &DiGraph<STGNode<SYS>, STGEdge>, node: NodeIndex) -> bool
where
SYS: TargetSystem,
{
graph.edges_directed(node as NodeIndex, petgraph::Direction::Outgoing).any(|x| x.weight().event == CaptureEvent::ISRStart && x.weight().name!="xPortSysTickHandler") graph.edges_directed(node as NodeIndex, petgraph::Direction::Outgoing).any(|x| x.weight().event == CaptureEvent::ISRStart && x.weight().name!="xPortSysTickHandler")
} }
fn is_candidate_for_new_branches(graph: &DiGraph<STGNode, STGEdge>, node: NodeIndex) -> bool { fn is_candidate_for_new_branches<SYS>(graph: &DiGraph<STGNode<SYS>, STGEdge>, node: NodeIndex) -> bool
where
SYS: TargetSystem,
{
!has_interrupt_handler_non_systick(graph, node) && !is_interrupt_handler(graph, node) !has_interrupt_handler_non_systick(graph, node) && !is_interrupt_handler(graph, node)
} }
// TODO: this can be much more efficient, if the graph stored snapshots of the state and input progress was tracked // TODO: this can be much more efficient, if the graph stored snapshots of the state and input progress was tracked
/// Determines if a given node in the state transition graph (STG) is a candidate for introducing new branches. /// Determines if a given node in the state transition graph (STG) is a candidate for introducing new branches.
pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, meta: &STGNodeMetadata, config: (usize, u32)) -> Option<Vec<u32>> { pub fn try_force_new_branches<SYS>(interrupt_ticks : &[u32], fbs: &STGFeedbackState<SYS>, meta: &STGNodeMetadata, config: (usize, u32)) -> Option<Vec<u32>>
where
SYS: TargetSystem,
{
let mut new = false; let mut new = false;
let mut new_interrupt_times = Vec::new(); let mut new_interrupt_times = Vec::new();
for (num,&interrupt_time) in interrupt_ticks.iter().enumerate() { for (num,&interrupt_time) in interrupt_ticks.iter().enumerate() {
@ -112,14 +124,14 @@ pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState,
/// The default mutational stage /// The default mutational stage
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct InterruptShiftStage<E, EM, Z> { pub struct InterruptShiftStage<E, EM, Z, SYS> {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, Z)>, phantom: PhantomData<(E, EM, Z, SYS)>,
interrup_config: Vec<(usize,u32)>, interrup_config: Vec<(usize,u32)>,
success: simple_moving_average::SingleSumSMA<f32, f32, 50> success: simple_moving_average::SingleSumSMA<f32, f32, 50>
} }
impl<E, EM, Z> InterruptShiftStage<E, EM, Z> impl<E, EM, Z, SYS> InterruptShiftStage<E, EM, Z, SYS>
where where
E: UsesState<State = Z::State>, E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>, EM: UsesState<State = Z::State>,
@ -135,7 +147,7 @@ static mut num_stage_execs : u64 = 0;
static mut sum_reruns : u64 = 0; static mut sum_reruns : u64 = 0;
static mut sum_interesting_reruns : u64 = 0; static mut sum_interesting_reruns : u64 = 0;
impl<E, EM, Z, I> InterruptShiftStage<E, EM, Z> impl<E, EM, Z, I, SYS> InterruptShiftStage<E, EM, Z, SYS>
where where
E: UsesState<State = Z::State>, E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>, EM: UsesState<State = Z::State>,
@ -144,9 +156,10 @@ where
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata,
<Z::State as UsesInput>::Input: Input, <Z::State as UsesInput>::Input: Input,
Z::State: UsesInput<Input = MultipartInput<I>>, Z::State: UsesInput<Input = MultipartInput<I>>,
I: HasMutatorBytes + Default I: HasMutatorBytes + Default,
SYS: TargetSystem,
{ {
fn report_stats(&self, state: &mut <InterruptShiftStage<E, EM, Z> as UsesState>::State, manager: &mut EM) { fn report_stats(&self, state: &mut <InterruptShiftStage<E, EM, Z, SYS> as libafl::state::UsesState>::State, manager: &mut EM) {
unsafe { unsafe {
let _ = manager.fire( let _ = manager.fire(
state, state,
@ -163,7 +176,7 @@ where
} }
} }
impl<S, E, EM, Z, I> Stage<E, EM, Z> for InterruptShiftStage<E, EM, Z> impl<S, E, EM, Z, I, SYS> Stage<E, EM, Z> for InterruptShiftStage<E, EM, Z, SYS>
where where
E: UsesState<State = S>, E: UsesState<State = S>,
EM: UsesState<State = S>, EM: UsesState<State = S>,
@ -172,6 +185,7 @@ where
<<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me <<Self as UsesState>::State as HasCorpus>::Corpus: Corpus<Input = Self::Input>, //delete me
EM: EventFirer, EM: EventFirer,
I: Default + Input + HasMutatorBytes, I: Default + Input + HasMutatorBytes,
SYS: TargetSystem,
{ {
fn perform( fn perform(
&mut self, &mut self,
@ -236,7 +250,7 @@ where
else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases
let feedbackstate = match state let feedbackstate = match state
.named_metadata_map() .named_metadata_map()
.get::<STGFeedbackState>("stgfeedbackstate") { .get::<STGFeedbackState<SYS>>("stgfeedbackstate") {
Some(s) => s, Some(s) => s,
Option::None => { Option::None => {
panic!("STGfeedbackstate not visible") panic!("STGfeedbackstate not visible")
@ -439,7 +453,7 @@ where
} }
} }
impl<E, EM, Z> UsesState for InterruptShiftStage<E, EM, Z> impl<E, EM, Z, SYS> UsesState for InterruptShiftStage<E, EM, Z, SYS>
where where
E: UsesState<State = Z::State>, E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>, EM: UsesState<State = Z::State>,
@ -450,7 +464,10 @@ where
} }
pub fn try_worst_snippets(bytes : &[u8], fbs: &STGFeedbackState, meta: &STGNodeMetadata) -> Option<Vec<u8>> { pub fn try_worst_snippets<SYS>(bytes : &[u8], fbs: &STGFeedbackState<SYS>, meta: &STGNodeMetadata) -> Option<Vec<u8>>
where
SYS: TargetSystem,
{
let mut new = false; let mut new = false;
let mut ret = Vec::new(); let mut ret = Vec::new();
for (num,interval) in meta.intervals().iter().enumerate() { for (num,interval) in meta.intervals().iter().enumerate() {
@ -466,25 +483,26 @@ static mut num_snippet_success : u64 = 0;
/// The default mutational stage /// The default mutational stage
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct STGSnippetStage<E, EM, Z> { pub struct STGSnippetStage<E, EM, Z, SYS> {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, Z)>, phantom: PhantomData<(E, EM, Z, SYS)>,
input_addr: u32 input_addr: u32
} }
impl<E, EM, Z> STGSnippetStage<E, EM, Z> impl<E, EM, Z, SYS> STGSnippetStage<E, EM, Z, SYS>
where where
E: UsesState<State = Z::State>, E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>, EM: UsesState<State = Z::State>,
Z: Evaluator<E, EM>, Z: Evaluator<E, EM>,
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand,
SYS: TargetSystem,
{ {
pub fn new(input_addr: u32) -> Self { pub fn new(input_addr: u32) -> Self {
Self { phantom: PhantomData, input_addr } Self { phantom: PhantomData, input_addr }
} }
} }
impl<E, EM, Z, I> STGSnippetStage<E, EM, Z> impl<E, EM, Z, I, SYS> STGSnippetStage<E, EM, Z, SYS>
where where
E: UsesState<State = Z::State>, E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>, EM: UsesState<State = Z::State>,
@ -493,9 +511,10 @@ where
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata,
<Z::State as UsesInput>::Input: Input, <Z::State as UsesInput>::Input: Input,
Z::State: UsesInput<Input = MultipartInput<I>>, Z::State: UsesInput<Input = MultipartInput<I>>,
I: HasMutatorBytes + Default I: HasMutatorBytes + Default,
SYS: TargetSystem,
{ {
fn report_stats(&self, state: &mut <STGSnippetStage<E, EM, Z> as UsesState>::State, manager: &mut EM) { fn report_stats(&self, state: &mut <STGSnippetStage<E, EM, Z, SYS> as UsesState>::State, manager: &mut EM) {
unsafe { unsafe {
let _ = manager.fire( let _ = manager.fire(
state, state,
@ -512,7 +531,7 @@ where
} }
} }
impl<E, EM, Z, I> Stage<E, EM, Z> for STGSnippetStage<E, EM, Z> impl<E, EM, Z, I, SYS> Stage<E, EM, Z> for STGSnippetStage<E, EM, Z, SYS>
where where
E: UsesState<State = Z::State>, E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>, EM: UsesState<State = Z::State>,
@ -523,7 +542,8 @@ where
Z::State: UsesInput<Input = MultipartInput<I>>, Z::State: UsesInput<Input = MultipartInput<I>>,
I: HasMutatorBytes + Default, I: HasMutatorBytes + Default,
Z::State: HasCurrentTestcase+HasCorpus+HasCurrentCorpusId, Z::State: HasCurrentTestcase+HasCorpus+HasCurrentCorpusId,
<Z::State as HasCorpus>::Corpus: Corpus<Input = MultipartInput<I>> <Z::State as HasCorpus>::Corpus: Corpus<Input = MultipartInput<I>>,
SYS: TargetSystem,
{ {
fn perform( fn perform(
&mut self, &mut self,
@ -546,7 +566,7 @@ where
if let Some(meta) = current_case.metadata_map().get::<STGNodeMetadata>() { if let Some(meta) = current_case.metadata_map().get::<STGNodeMetadata>() {
let feedbackstate = match state let feedbackstate = match state
.named_metadata_map() .named_metadata_map()
.get::<STGFeedbackState>("stgfeedbackstate") { .get::<STGFeedbackState<SYS>>("stgfeedbackstate") {
Some(s) => s, Some(s) => s,
Option::None => { Option::None => {
panic!("STGfeedbackstate not visible") panic!("STGfeedbackstate not visible")
@ -590,12 +610,13 @@ where
} }
} }
impl<E, EM, Z> UsesState for STGSnippetStage<E, EM, Z> impl<E, EM, Z, SYS> UsesState for STGSnippetStage<E, EM, Z, SYS>
where where
E: UsesState<State = Z::State>, E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>, EM: UsesState<State = Z::State>,
Z: Evaluator<E, EM>, Z: Evaluator<E, EM>,
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand,
SYS: TargetSystem,
{ {
type State = Z::State; type State = Z::State;
} }

View File

@ -15,18 +15,13 @@ use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::borrow::Cow; use std::borrow::Cow;
use itertools::Itertools;
use super::helpers::USR_ISR_SYMBOLS; use super::target_os::TargetSystem;
use super::JobInstance; use super::RTOSJob;
use super::{ AtomicBasicBlock, ExecInterval}; use super::{ AtomicBasicBlock, ExecInterval};
use super::{ use crate::systemstate::target_os::SystemState;
CURRENT_SYSTEMSTATE_VEC, use crate::systemstate::target_os::*;
RawFreeRTOSSystemState,
RefinedTCB,
ReducedFreeRTOSSystemState,
freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*},
helpers::JOBS_DONE,
};
//============================= Observer //============================= Observer
@ -34,120 +29,76 @@ use super::{
/// that will get updated by the target. /// that will get updated by the target.
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[allow(clippy::unsafe_derive_deserialize)] #[allow(clippy::unsafe_derive_deserialize)]
pub struct QemuSystemStateObserver<I> pub struct QemuSystemStateObserver<I, SYS>
where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
{ {
pub last_run: Vec<ReducedFreeRTOSSystemState>, last_run: Vec<SYS::State>,
pub last_states: HashMap<u64, ReducedFreeRTOSSystemState>, last_states: HashMap<u64, SYS::State>,
pub last_trace: Vec<ExecInterval>, last_trace: Vec<ExecInterval>,
pub last_reads: Vec<Vec<(u32, u8)>>, last_reads: Vec<Vec<(u32, u8)>>,
pub last_input: I, last_input: I,
pub job_instances: Vec<JobInstance>, job_instances: Vec<RTOSJob>,
pub do_report: bool, pub do_report: bool,
pub worst_job_instances: HashMap<String, JobInstance>, worst_job_instances: HashMap<String, RTOSJob>,
pub select_task: Option<String>, pub select_task: Option<String>,
pub success: bool, pub success: bool,
name: Cow<'static, str>, name: Cow<'static, str>,
} }
impl<I, S> Observer<I, S> for QemuSystemStateObserver<I>
impl<I, S, SYS> Observer<I, S> for QemuSystemStateObserver<I, SYS>
where where
S: UsesInput<Input = I> + HasMetadata, S: UsesInput<Input = I> + HasMetadata,
S::Input: Default, S::Input: Default,
I: Clone, I: Clone,
SYS: TargetSystem,
{ {
#[inline] #[inline]
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
unsafe {CURRENT_SYSTEMSTATE_VEC.clear(); }
Ok(()) Ok(())
} }
#[inline] #[inline]
fn post_exec(&mut self, _state: &mut S, _input: &I, _exit_kind: &ExitKind) -> Result<(), Error> { fn post_exec(&mut self, state: &mut S, _input: &I, _exit_kind: &ExitKind) -> Result<(), Error> {
// unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} // let trace =state.metadata::<SYS::TraceData>().expect("TraceData not found");
unsafe {
let temp = refine_system_states(CURRENT_SYSTEMSTATE_VEC.split_off(0)); // copy-paste form clock observer
// fix_broken_trace(&mut temp.1); {
self.last_run = temp.0.clone(); let hist = state.metadata_mut::<IcHist>();
// println!("{:?}",temp); let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
let temp = states2intervals(temp.0, temp.1); match hist {
self.last_trace = temp.0; Err(_) => {
self.last_reads = temp.1; state.add_metadata(IcHist(vec![(self.last_runtime(), timestamp)],
self.last_states = temp.2; (self.last_runtime(), timestamp)));
self.success = temp.3; }
#[cfg(feature="trace_job_response_times")] Ok(v) => {
{ v.0.push((self.last_runtime(), timestamp));
let metadata =_state.metadata_map_mut(); if v.1.0 < self.last_runtime() {
let releases = get_releases(&self.last_trace, &self.last_states); v.1 = (self.last_runtime(), timestamp);
// println!("Releases: {:?}",&releases);
let jobs_done = JOBS_DONE.split_off(0);
let (job_instances, do_report) = get_release_response_pairs(&releases, &jobs_done);
self.do_report = do_report;
let job_instances = job_instances.into_iter().map(|x| {
let intervals = self.last_trace.iter().enumerate().filter(|y| y.1.start_tick <= x.1 && y.1.end_tick >= x.0 && x.2 == y.1.get_task_name_unchecked()).map(|(idx,x)| (x, &self.last_reads[idx])).collect::<Vec<_>>();
let (abbs, rest) : (Vec<_>, Vec<_>) = intervals.chunk_by(|a,b| a.0.abb.as_ref().unwrap().instance_eq(b.0.abb.as_ref().unwrap())).into_iter().map(|intervals| (intervals[0].0.abb.as_ref().unwrap().clone(), (intervals.iter().fold(0, |sum, x| sum+x.0.get_exec_time()), intervals.iter().fold(Vec::new(), |mut sum, x| {sum.extend(x.1.iter()); sum})))).unzip();
let (ticks_per_abb, mem_reads) : (Vec<_>, Vec<_>) = rest.into_iter().unzip();
JobInstance {
name: x.2.clone(),
mem_reads: mem_reads.into_iter().flatten().collect(), // TODO: add read values
release: x.0,
response: x.1,
exec_ticks: ticks_per_abb.iter().sum(),
ticks_per_abb: ticks_per_abb,
abbs: abbs,
hash_cache: 0
}
}).collect::<Vec<_>>();
// println!("Instances: {:?}",&job_instances);
self.job_instances = job_instances;
let observer = &self;
let mut worst_case_per_task : HashMap<String, JobInstance> = HashMap::new();
observer.job_instances.iter().for_each(|x| {
if worst_case_per_task.get(&x.name).is_some() {
let old = worst_case_per_task.get_mut(&x.name).unwrap();
if x.exec_ticks > old.exec_ticks {
old.exec_ticks=x.exec_ticks;
}
} else {
worst_case_per_task.insert(x.name.clone(), x.clone());
}
});
self.worst_job_instances = worst_case_per_task;
// copy-paste form clock observer
{
let hist = metadata.get_mut::<IcHist>();
let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
match hist {
Option::None => {
metadata.insert(IcHist(vec![(self.last_runtime(), timestamp)],
(self.last_runtime(), timestamp)));
}
Some(v) => {
v.0.push((self.last_runtime(), timestamp));
if v.1.0 < self.last_runtime() {
v.1 = (self.last_runtime(), timestamp);
}
}
} }
} }
} }
} }
// 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.clone();
Ok(()) Ok(())
} }
} }
impl<I> Named for QemuSystemStateObserver<I> impl<I, SYS> Named for QemuSystemStateObserver<I, SYS>
where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
{ {
fn name(&self) -> &Cow<'static, str> { fn name(&self) -> &Cow<'static, str> {
&self.name &self.name
} }
} }
impl<I> HasLen for QemuSystemStateObserver<I> impl<I, SYS> HasLen for QemuSystemStateObserver<I, SYS>
where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
{ {
#[inline] #[inline]
fn len(&self) -> usize { fn len(&self) -> usize {
@ -155,8 +106,11 @@ impl<I> HasLen for QemuSystemStateObserver<I>
} }
} }
impl<I> QemuSystemStateObserver<I> impl<I, SYS> QemuSystemStateObserver<I, SYS>
where I: Default { where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
I: Default {
pub fn new(select_task: &Option<String>) -> Self { pub fn new(select_task: &Option<String>) -> Self {
Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), worst_job_instances: HashMap::new(), do_report: false, select_task: select_task.clone(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false, job_instances: vec![]} Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), worst_job_instances: HashMap::new(), do_report: false, select_task: select_task.clone(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false, job_instances: vec![]}
} }
@ -164,491 +118,16 @@ where I: Default {
self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).map(|y| y.response-y.release).unwrap_or(0).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()}) self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).map(|y| y.response-y.release).unwrap_or(0).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()})
} }
} }
impl<I> Default for QemuSystemStateObserver<I> impl<I, SYS> Default for QemuSystemStateObserver<I, SYS>
where I: Default { where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
I: Default {
fn default() -> Self { fn default() -> Self {
Self::new(&None) Self::new(&None)
} }
} }
//============================= Parsing helpers
/// Parse a List_t containing TCB_t into Vec<TCB_t> from cache. Consumes the elements from cache
fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap<u32,rtos_struct>) -> Vec<TCB_t>
{
let mut ret : Vec<TCB_t> = Vec::new();
if list.uxNumberOfItems == 0 {return ret;}
let last_list_item = match dump.remove(&list.pxIndex).expect("List_t entry was not in Hashmap") {
List_Item_struct(li) => li,
List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") {
List_Item_struct(li) => li,
_ => panic!("MiniListItem of a non empty List does not point to ListItem"),
},
_ => panic!("List_t entry was not a ListItem"),
};
let mut next_index = last_list_item.pxNext;
let last_tcb = match dump.remove(&last_list_item.pvOwner).expect("ListItem Owner not in Hashmap") {
TCB_struct(t) => t,
_ => panic!("List content does not equal type"),
};
for _ in 0..list.uxNumberOfItems-1 {
let next_list_item = match dump.remove(&next_index).expect("List_t entry was not in Hashmap") {
List_Item_struct(li) => li,
List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") {
List_Item_struct(li) => li,
_ => panic!("MiniListItem of a non empty List does not point to ListItem"),
},
_ => panic!("List_t entry was not a ListItem"),
};
match dump.remove(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") {
TCB_struct(t) => {ret.push(t)},
_ => panic!("List content does not equal type"),
}
next_index=next_list_item.pxNext;
}
ret.push(last_tcb);
ret
}
/// Drains a List of raw SystemStates to produce a refined trace
/// returns:
/// - a Vec of ReducedFreeRTOSSystemStates
/// - a Vec of metadata tuples (qemu_tick, capture_event, capture_name, edge, mem_reads)
fn refine_system_states(mut input: Vec<RawFreeRTOSSystemState>) -> (Vec<ReducedFreeRTOSSystemState>, Vec<(u64, CaptureEvent, String, (u32, u32), Vec<(u32, u8)>)>) {
let mut ret = (Vec::<_>::new(), Vec::<_>::new());
for mut i in input.drain(..) {
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
let mut collector = Vec::<RefinedTCB>::new();
for j in i.prio_ready_lists.into_iter().rev() {
let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect();
collector.append(&mut tmp);
}
// collect delay list
let mut delay_list : Vec::<RefinedTCB> = tcb_list_to_vec_cached(i.delay_list, &mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect();
let mut delay_list_overflow : Vec::<RefinedTCB> = tcb_list_to_vec_cached(i.delay_list_overflow, &mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect();
delay_list.append(&mut delay_list_overflow);
delay_list.sort_by(|a,b| a.task_name.cmp(&b.task_name));
ret.0.push(ReducedFreeRTOSSystemState {
current_task: cur,
ready_list_after: collector,
delay_list_after: delay_list,
read_invalid: i.read_invalid,
// input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER,
});
ret.1.push((i.qemu_tick, i.capture_point.0, i.capture_point.1.to_string(), i.edge, i.mem_reads));
}
return ret;
}
// Find all task release times.
fn get_releases(trace: &Vec<ExecInterval>, states: &HashMap<u64, ReducedFreeRTOSSystemState>) -> Vec<(u64, String)> {
let mut ret = Vec::new();
let mut initial_released = false;
for (_n, i) in trace.iter().enumerate() {
// The first release starts from xPortPendSVHandler
if !initial_released && i.start_capture.0 == CaptureEvent::ISREnd && i.start_capture.1 == "xPortPendSVHandler" {
let start_state = states.get(&i.start_state).expect("State not found");
initial_released = true;
start_state.ready_list_after.iter().for_each(|x| {
ret.push((i.start_tick, x.task_name.clone()));
});
continue;
}
// A timed release is SysTickHandler isr block that moves a task from the delay list to the ready list.
if i.start_capture.0 == CaptureEvent::ISRStart && ( i.start_capture.1 == "xPortSysTickHandler" || USR_ISR_SYMBOLS.contains(&i.start_capture.1.as_str()) ) {
// detect race-conditions, get start and end state from the nearest valid intervals
if states.get(&i.start_state).map(|x| x.read_invalid).unwrap_or(true) {
let mut start_index=None;
for n in 1.._n {
if let Some(interval_start) = trace.get(_n-n) {
let start_state = states.get(&interval_start.start_state).unwrap();
if !start_state.read_invalid {
start_index = Some(_n-n);
break;
}
} else {break;}
};
let mut end_index=None;
for n in (_n+1)..trace.len() {
if let Some(interval_end) = trace.get(n) {
let end_state = states.get(&interval_end.end_state).unwrap();
if !end_state.read_invalid {
end_index = Some(n);
break;
}
} else {break;}
};
if let Some(Some(start_state)) = start_index.map(|x| states.get(&trace[x].start_state)) {
if let Some(Some(end_state)) = end_index.map(|x| states.get(&trace[x].end_state)) {
end_state.ready_list_after.iter().for_each(|x| {
if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) {
ret.push((i.end_tick, x.task_name.clone()));
}
});
}
}
} else
// canonical case, userspace -> isr -> userspace
if i.end_capture.0 == CaptureEvent::ISREnd {
let start_state = states.get(&i.start_state).expect("State not found");
let end_state = states.get(&i.end_state).expect("State not found");
end_state.ready_list_after.iter().for_each(|x| {
if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) {
ret.push((i.end_tick, x.task_name.clone()));
}
});
// start_state.delay_list_after.iter().for_each(|x| {
// if !end_state.delay_list_after.iter().any(|y| x.task_name == y.task_name) {
// ret.push((i.end_tick, x.task_name.clone()));
// }
// });
} else if i.end_capture.0 == CaptureEvent::ISRStart {
// Nested interrupts. Fast-forward to the end of the original interrupt, or the first valid state thereafter
// TODO: this may cause the same release to be registered multiple times
let mut isr_has_ended = false;
let start_state = states.get(&i.start_state).expect("State not found");
for n in (_n+1)..trace.len() {
if let Some(interval_end) = trace.get(n) {
if interval_end.end_capture.1 == i.start_capture.1 || isr_has_ended {
let end_state = states.get(&interval_end.end_state).unwrap();
isr_has_ended = true;
if !end_state.read_invalid {
end_state.ready_list_after.iter().for_each(|x| {
if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) {
ret.push((i.end_tick, x.task_name.clone()));
}
});
break;
}
}
} else {break;}
};
// if let Some(interval_end) = trace.get(_n+2) {
// if interval_end.start_capture.0 == CaptureEvent::ISREnd && interval_end.end_capture.0 == CaptureEvent::ISREnd && interval_end.end_capture.1 == i.start_capture.1 {
// let start_state = states.get(&i.start_state).expect("State not found");
// let end_state = states.get(&interval_end.end_state).expect("State not found");
// end_state.ready_list_after.iter().for_each(|x| {
// if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) {
// ret.push((i.end_tick, x.task_name.clone()));
// }
// });
// }
// }
}
}
// Release driven by an API call. This produces a lot of false positives, as a job may block multiple times per instance. Despite this, aperiodic jobs not be modeled otherwise. If we assume the first release is the real one, we can filter out the rest.
if i.start_capture.0 == CaptureEvent::APIStart {
let api_start_state = states.get(&i.start_state).expect("State not found");
let api_end_state = {
let mut end_index = _n;
for n in (_n)..trace.len() {
if trace[n].end_capture.0 == CaptureEvent::APIEnd || trace[n].end_capture.0 == CaptureEvent::End {
end_index = n;
break;
} else if n > _n && trace[n].level == 0 { // API Start -> ISR Start+End -> APP Continue
end_index = n-1; // any return to a regular app block is a fair point of comparison for the ready list, because scheduling has been performed
break;
}
};
states.get(&trace[end_index].end_state).expect("State not found")
};
api_end_state.ready_list_after.iter().for_each(|x| {
if x.task_name != api_start_state.current_task.task_name && !api_start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) {
ret.push((i.end_tick, x.task_name.clone()));
// eprintln!("Task {} released by API call at {:.1}ms", x.task_name, crate::time::clock::tick_to_time(i.end_tick).as_micros() as f32/1000.0);
}
});
}
}
ret
}
fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String)>) -> (Vec<(u64, u64, String)>, bool) {
let mut maybe_error = false;
let mut ret = Vec::new();
let mut ready : HashMap<&String, u64> = HashMap::new();
let mut last_response : HashMap<&String, u64> = HashMap::new();
let mut r = rel.iter().peekable();
let mut d = resp.iter().peekable();
loop {
while let Some(peek_rel) = r.peek() {
// Fill releases as soon as possible
if !ready.contains_key(&peek_rel.1) {
ready.insert(&peek_rel.1, peek_rel.0);
r.next();
} else {
if let Some(peek_resp) = d.peek() {
if peek_resp.0 > peek_rel.0 { // multiple releases before response
// It is unclear which release is real
// maybe_error = true;
// eprintln!("Task {} released multiple times before response ({:.1}ms and {:.1}ms)", peek_rel.1, crate::time::clock::tick_to_time(ready[&peek_rel.1]).as_micros()/1000, crate::time::clock::tick_to_time(peek_rel.0).as_micros()/1000);
// ready.insert(&peek_rel.1, peek_rel.0);
r.next();
} else {
// releases have overtaken responses, wait until the ready list clears up a bit
break;
}
} else {
// no more responses
break;
}
}
}
if let Some(next_resp) = d.next() {
if ready.contains_key(&next_resp.1) {
if ready[&next_resp.1] >= next_resp.0 {
if let Some(lr) = last_response.get(&next_resp.1) {
if u128::abs_diff(crate::time::clock::tick_to_time(next_resp.0).as_micros(), crate::time::clock::tick_to_time(*lr).as_micros()) > 500 { // tolerate pending notifications for 500us
maybe_error = true;
// eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0);
}
// Sometimes a task is released immediately after a response. This might not be detected.
// Assume that the release occured with the last response
ret.push((*lr, next_resp.0, next_resp.1.clone()));
last_response.insert(&next_resp.1, next_resp.0);
} else {
maybe_error = true;
// eprintln!("Task {} released after response", next_resp.1);
}
} else {
// assert!(peek_resp.0 >= ready[&peek_resp.1]);
last_response.insert(&next_resp.1, next_resp.0);
ret.push((ready[&next_resp.1], next_resp.0, next_resp.1.clone()));
ready.remove(&next_resp.1);
}
} else {
if let Some(lr) = last_response.get(&next_resp.1) {
if u128::abs_diff(crate::time::clock::tick_to_time(next_resp.0).as_micros(), crate::time::clock::tick_to_time(*lr).as_micros()) > 1000 { // tolerate pending notifications for 1ms
// maybe_error = true;
// eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0);
}
// Sometimes a task is released immediately after a response (e.g. pending notification). This might not be detected.
// Assume that the release occured with the last response
ret.push((*lr, next_resp.0, next_resp.1.clone()));
last_response.insert(&next_resp.1, next_resp.0);
} else {
maybe_error = true;
// eprintln!("Task {} response at {:.1}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0);
}
}
} else {
// TODO: should remaining released tasks be counted as finished?
return (ret,maybe_error);
}
}
}
/// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success
/// returns:
/// - a Vec of ExecIntervals
/// - a Vec of HashSets marking memory reads during these intervals
/// - a HashMap of ReducedFreeRTOSSystemStates by hash
/// - a bool indicating success
fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, CaptureEvent, String, (u32, u32), Vec<(u32, u8)>)>) -> (Vec<ExecInterval>, Vec<Vec<(u32, u8)>>, HashMap<u64, ReducedFreeRTOSSystemState>, bool) {
if trace.len() == 0 {return (Vec::new(), Vec::new(), HashMap::new(), true);}
let mut isr_stack : VecDeque<u8> = VecDeque::from([]); // 2+ = ISR, 1 = systemcall, 0 = APP. Trace starts with an ISREnd and executes the app
let mut level_of_task : HashMap<&str, u8> = HashMap::new();
let mut ret: Vec<ExecInterval> = vec![];
let mut reads: Vec<Vec<(u32, u8)>> = vec![];
let mut edges: Vec<(u32, u32)> = vec![];
let mut last_hash : u64 = trace[0].get_hash();
let mut table : HashMap<u64, ReducedFreeRTOSSystemState> = HashMap::new();
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 mut interval_name = curr_name; // Name of the interval, either the task name or the isr/api funtion name
let level = match meta[i].1 {
CaptureEvent::APIEnd => { // API end always exits towards the app
if !level_of_task.contains_key(curr_name) {
level_of_task.insert(curr_name, 0);
}
*level_of_task.get_mut(curr_name).unwrap()=0;
0
},
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;
// interval_name = &meta[i].2;
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 {
// interval_name = ""; // We can't know which isr is running
isr_stack.pop_back().unwrap();
*isr_stack.back().unwrap()
} else {
isr_stack.pop_back();
// possibly go back to an api call that is still running for this task
if level_of_task.get(curr_name).unwrap() == &1 {
// interval_name = ""; // We can't know which api is running
}
*level_of_task.get(curr_name).unwrap()
}
},
CaptureEvent::ISRStart => {
// special case for isrs which do not capture their end
// if meta[i].2 == "ISR_0_Handler" {
// &2
// } else {
// regular case
// interval_name = &meta[i].2;
if isr_stack.len() > 0 {
let l = *isr_stack.back().unwrap();
isr_stack.push_back(l+1);
l+1
} else {
isr_stack.push_back(2);
2
}
// }
}
_ => 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
});
reads.push(meta[i+1].4.clone());
last_hash = next_hash;
edges.push((meta[i].3.1, meta[i+1].3.0));
}
let t = add_abb_info(&mut ret, &table, &edges);
(ret, reads, table, t)
}
/// Marks which abbs were executed at each interval
fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, edges: &Vec<(u32, u32)>) -> bool {
let mut id_count = 0;
let mut ret = true;
let mut task_has_started : HashSet<String> = HashSet::new();
let mut wip_abb_trace : Vec<Rc<RefCell<AtomicBasicBlock>>> = vec![];
// 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() {
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 {""})).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));
match trace[i].start_capture.0 {
// generic api abb start
CaptureEvent::APIStart => {
// assert_eq!(open_abb, None);
ret &= open_abb.is_none();
open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i);
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}, instance_id: id_count, instance_name: Some(trace[i].start_capture.1.clone())})));
id_count+=1;
},
// generic isr abb start
CaptureEvent::ISRStart => {
// assert_eq!(open_abb, None);
ret &= open_abb.is_none();
open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i);
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}, instance_id: id_count, instance_name: Some(trace[i].start_capture.1.clone())})));
id_count+=1;
},
// generic app abb start
CaptureEvent::APIEnd => {
// assert_eq!(open_abb, None);
ret &= open_abb.is_none();
open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i);
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}, instance_id: id_count, instance_name: if trace[i].level<2 {Some(curr_name.clone())} else {None}})));
id_count+=1;
},
// 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);
ret &= open_abb.is_none();
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: 0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}, instance_id: id_count, instance_name: Some(curr_name.clone())})));
id_count+=1;
open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i);
task_has_started.insert(curr_name.clone());
} else {
if let Some(last) = open_abb_at_this_ret_addr_and_task.get(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""})) {
let last = last.clone(); // required to drop immutable reference
wip_abb_trace.push(wip_abb_trace[last].clone());
// if the abb is interrupted again, it will need to continue at edge[i].1
open_abb_at_this_ret_addr_and_task.remove(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""}));
open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), last); // order matters!
} else {
// panic!();
// println!("Continued block with no start {} {} {:?} {:?} {:x}-{:x} {} {}", curr_name, trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, edges[i].0, edges[i].1, task_has_started.contains(curr_name),trace[i].level);
// println!("{:x?}", open_abb_at_this_ret_addr_and_task);
ret = false;
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].1, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}, instance_id: id_count, instance_name: if trace[i].level<1 {Some(curr_name.clone())} else {None}})));
id_count+=1;
}
}
},
_ => 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);
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);
for i in 0..trace.len() {
trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone());
}
return ret;
}
// /// restore the isr/api begin/end invariant // /// restore the isr/api begin/end invariant

View File

@ -14,7 +14,7 @@ use libafl::{
use crate::time::worst::MaxTimeFavFactor; use crate::time::worst::MaxTimeFavFactor;
use super::{stg::STGNodeMetadata, FreeRTOSSystemStateMetadata}; use super::{stg::STGNodeMetadata, target_os::*};
/// A state metadata holding a map of favoreds testcases for each map entry /// A state metadata holding a map of favoreds testcases for each map entry
#[derive(Debug, Serialize, Deserialize, SerdeAny, Default)] #[derive(Debug, Serialize, Deserialize, SerdeAny, Default)]
@ -33,22 +33,24 @@ impl LongestTracesMetadata {
/// corpus that exercise all the requested features (e.g. all the coverage seen so far) /// corpus that exercise all the requested features (e.g. all the coverage seen so far)
/// prioritizing [`Testcase`]`s` using [`TestcaseScore`] /// prioritizing [`Testcase`]`s` using [`TestcaseScore`]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LongestTraceScheduler<CS> { pub struct LongestTraceScheduler<CS, SYS> {
base: CS, base: CS,
skip_non_favored_prob: f64, skip_non_favored_prob: f64,
phantom: PhantomData<SYS>,
} }
impl<CS> UsesState for LongestTraceScheduler<CS> impl<CS, SYS> UsesState for LongestTraceScheduler<CS, SYS>
where where
CS: UsesState, CS: UsesState,
{ {
type State = CS::State; type State = CS::State;
} }
impl<CS> Scheduler<CS::Input, CS::State> for LongestTraceScheduler<CS> impl<CS, SYS> Scheduler<CS::Input, CS::State> for LongestTraceScheduler<CS, SYS>
where where
CS: UsesState + Scheduler<CS::Input, CS::State>, CS: UsesState + Scheduler<CS::Input, CS::State>,
CS::State: HasCorpus + HasMetadata + HasRand, CS::State: HasCorpus + HasMetadata + HasRand,
SYS: TargetSystem,
{ {
/// Add an entry to the corpus and return its index /// Add an entry to the corpus and return its index
fn on_add(&mut self, state: &mut CS::State, idx: CorpusId) -> Result<(), Error> { fn on_add(&mut self, state: &mut CS::State, idx: CorpusId) -> Result<(), Error> {
@ -56,7 +58,7 @@ where
.get(idx)? .get(idx)?
.borrow() .borrow()
.metadata_map() .metadata_map()
.get::<FreeRTOSSystemStateMetadata>().map_or(0, |x| x.trace_length); .get::<SYS::TraceData>().map_or(0, |x| x.trace_length());
self.get_update_trace_length(state,l); self.get_update_trace_length(state,l);
self.base.on_add(state, idx) self.base.on_add(state, idx)
} }
@ -115,10 +117,11 @@ where
} }
} }
impl<CS> LongestTraceScheduler<CS> impl<CS, SYS> LongestTraceScheduler<CS, SYS>
where where
CS: UsesState + Scheduler<CS::Input, CS::State>, CS: UsesState + Scheduler<CS::Input, CS::State>,
CS::State: HasCorpus + HasMetadata + HasRand, CS::State: HasCorpus + HasMetadata + HasRand,
SYS: TargetSystem,
{ {
pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 { pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 {
// Create a new top rated meta if not existing // Create a new top rated meta if not existing
@ -136,6 +139,7 @@ where
Self { Self {
base, base,
skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB, skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB,
phantom: PhantomData,
} }
} }
} }

View File

@ -2,7 +2,7 @@
use hashbrown::HashSet; use hashbrown::HashSet;
use libafl::inputs::Input; use libafl::inputs::Input;
/// Feedbacks organizing SystemStates as a graph /// Feedbacks organizing SystemStates as a graph
use libafl::SerdeAny; use libafl_bolts::prelude::SerdeAny;
use libafl_bolts::ownedref::OwnedMutSlice; use libafl_bolts::ownedref::OwnedMutSlice;
use petgraph::graph::EdgeIndex; use petgraph::graph::EdgeIndex;
use libafl::prelude::UsesInput; use libafl::prelude::UsesInput;
@ -11,6 +11,7 @@ use libafl::state::UsesState;
use libafl::prelude::State; use libafl::prelude::State;
use libafl::schedulers::MinimizerScheduler; use libafl::schedulers::MinimizerScheduler;
use libafl_bolts::HasRefCnt; use libafl_bolts::HasRefCnt;
use serde::de::DeserializeOwned;
use std::path::PathBuf; use std::path::PathBuf;
use libafl::corpus::Testcase; use libafl::corpus::Testcase;
use std::collections::hash_map::DefaultHasher; use std::collections::hash_map::DefaultHasher;
@ -24,14 +25,15 @@ use libafl::Error;
use hashbrown::HashMap; use hashbrown::HashMap;
use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata}; use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use super::target_os::SystemState;
use super::AtomicBasicBlock; use super::AtomicBasicBlock;
use super::CaptureEvent; use super::CaptureEvent;
use super::ExecInterval; use super::ExecInterval;
use super::JobInstance; use super::RTOSJob;
use super::ReducedFreeRTOSSystemState;
use super::observers::QemuSystemStateObserver; use super::observers::QemuSystemStateObserver;
use super::TaskJob; use super::RTOSTask;
use petgraph::prelude::DiGraph; use petgraph::prelude::DiGraph;
use petgraph::graph::NodeIndex; use petgraph::graph::NodeIndex;
use petgraph::Direction; use petgraph::Direction;
@ -46,19 +48,25 @@ use std::ops::Deref;
use std::ops::DerefMut; use std::ops::DerefMut;
use std::rc::Rc; use std::rc::Rc;
use petgraph::visit::EdgeRef; use petgraph::visit::EdgeRef;
use crate::systemstate::target_os::*;
use libafl::prelude::StateInitializer; use libafl::prelude::StateInitializer;
//============================= Data Structures //============================= Data Structures
#[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)] #[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)]
pub struct STGNode #[serde(bound = "SYS: Serialize, for<'de2> SYS: Deserialize<'de2>")]
pub struct STGNode<SYS>
where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
{ {
base: ReducedFreeRTOSSystemState, base: SYS::State,
abb: AtomicBasicBlock, abb: AtomicBasicBlock,
} }
impl STGNode { impl<SYS> STGNode<SYS>
where SYS: TargetSystem {
pub fn _pretty_print(&self) -> String { pub fn _pretty_print(&self) -> String {
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()) 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())
} }
pub fn color_print(&self) -> String { pub fn color_print(&self) -> String {
let color = match self.abb.level { let color = match self.abb.level {
@ -70,10 +78,10 @@ impl STGNode {
let message = match self.abb.level { let message = match self.abb.level {
1 => format!("API Call"), 1 => format!("API Call"),
2 => format!("ISR"), 2 => format!("ISR"),
0 => format!("Task: {}",self.base.current_task.task_name), 0 => format!("Task: {}",self.base.current_task().task_name()),
_ => format!(""), _ => format!(""),
}; };
let mut label = format!("{}\nABB: {:x}-{:x}\nHash:{:X}\n{}", message, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.get_hash()>>48, self.base.print_lists()); let mut label = format!("{}\nABB: {:x}-{:x}\nHash:{:X}\n{}", message, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), compute_hash(&self.base)>>48, self.base.print_lists());
label.push_str(color); label.push_str(color);
label label
} }
@ -84,8 +92,11 @@ impl STGNode {
s.finish() s.finish()
} }
} }
impl PartialEq for STGNode { impl<SYS> PartialEq for STGNode<SYS>
fn eq(&self, other: &STGNode) -> bool { where
SYS: TargetSystem,
{
fn eq(&self, other: &STGNode<SYS>) -> bool {
self.base==other.base self.base==other.base
} }
} }
@ -139,13 +150,17 @@ impl Hash for STGEdge {
} }
/// Shared Metadata for a systemstateFeedback /// Shared Metadata for a systemstateFeedback
#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct STGFeedbackState #[serde(bound = "SYS: Serialize, for<'de2> SYS: Deserialize<'de2>")]
pub struct STGFeedbackState<SYS>
where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
{ {
name: Cow<'static, str>, name: Cow<'static, str>,
// aggregated traces as a graph // aggregated traces as a graph
pub graph: DiGraph<STGNode, STGEdge>, pub graph: DiGraph<STGNode<SYS>, STGEdge>,
systemstate_index: HashMap<u64, ReducedFreeRTOSSystemState>, systemstate_index: HashMap<u64, SYS::State>,
pub state_abb_hash_index: HashMap<(u64, u64), NodeIndex>, pub state_abb_hash_index: HashMap<(u64, u64), NodeIndex>,
stgnode_index: HashMap<u64, NodeIndex>, stgnode_index: HashMap<u64, NodeIndex>,
entrypoint: NodeIndex, entrypoint: NodeIndex,
@ -156,18 +171,24 @@ pub struct STGFeedbackState
worst_observed_per_stg_path: HashMap<u64,u64>, worst_observed_per_stg_path: HashMap<u64,u64>,
worst_abb_exec_count: HashMap<AtomicBasicBlock, usize>, worst_abb_exec_count: HashMap<AtomicBasicBlock, usize>,
// Metadata about job instances // Metadata about job instances
pub worst_task_jobs: HashMap<u64, TaskJob>, pub worst_task_jobs: HashMap<u64, RTOSTask>,
} }
impl Default for STGFeedbackState { libafl_bolts::impl_serdeany!(STGFeedbackState<SYS: SerdeAny+TargetSystem>);
fn default() -> STGFeedbackState {
let mut graph = DiGraph::new();
let mut entry = STGNode::default();
entry.base.current_task.task_name="Start".to_string();
let mut exit = STGNode::default();
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())]); impl<SYS> Default for STGFeedbackState<SYS>
where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
{
fn default() -> STGFeedbackState<SYS> {
let mut graph = DiGraph::new();
let mut entry : STGNode<SYS> = STGNode::default();
*(entry.base.current_task_mut().task_name_mut())="Start".to_string();
let mut exit : STGNode<SYS> = STGNode::default();
*(exit.base.current_task_mut().task_name_mut())="End".to_string();
let systemstate_index = HashMap::from([(compute_hash(&entry.base), entry.base.clone()), (compute_hash(&exit.base), exit.base.clone())]);
let h_entry = entry.get_hash(); let h_entry = entry.get_hash();
let h_exit = exit.get_hash(); let h_exit = exit.get_hash();
@ -175,7 +196,7 @@ impl Default for STGFeedbackState {
let entrypoint = graph.add_node(entry.clone()); let entrypoint = graph.add_node(entry.clone());
let exitpoint = graph.add_node(exit.clone()); let exitpoint = graph.add_node(exit.clone());
let state_abb_hash_index = HashMap::from([((entry.base.get_hash(), entry.abb.get_hash()), entrypoint), ((exit.base.get_hash(), exit.abb.get_hash()), exitpoint)]); let state_abb_hash_index = HashMap::from([((compute_hash(&entry.base), entry.abb.get_hash()), entrypoint), ((compute_hash(&exit.base), exit.abb.get_hash()), exitpoint)]);
let index = HashMap::from([(h_entry, entrypoint), (h_exit, exitpoint)]); let index = HashMap::from([(h_entry, entrypoint), (h_exit, exitpoint)]);
@ -196,7 +217,9 @@ impl Default for STGFeedbackState {
} }
} }
impl Named for STGFeedbackState impl<SYS> Named for STGFeedbackState<SYS>
where
SYS: TargetSystem,
{ {
#[inline] #[inline]
fn name(&self) -> &Cow<'static, str> { fn name(&self) -> &Cow<'static, str> {
@ -213,12 +236,12 @@ pub struct STGNodeMetadata {
aggregate: u64, aggregate: u64,
top_abb_counts: Vec<u64>, top_abb_counts: Vec<u64>,
intervals: Vec<ExecInterval>, intervals: Vec<ExecInterval>,
jobs: Vec<JobInstance>, jobs: Vec<RTOSJob>,
indices: Vec<usize>, indices: Vec<usize>,
tcref: isize, tcref: isize,
} }
impl STGNodeMetadata { impl STGNodeMetadata {
pub fn new(nodes: Vec<NodeIndex>, edges: Vec<EdgeIndex>, abb_trace: Vec<AtomicBasicBlock>, abbs_pathhash: u64, aggregate: u64, top_abb_counts: Vec<u64>, intervals: Vec<ExecInterval>, jobs: Vec<JobInstance>) -> Self { pub fn new(nodes: Vec<NodeIndex>, edges: Vec<EdgeIndex>, abb_trace: Vec<AtomicBasicBlock>, abbs_pathhash: u64, aggregate: u64, top_abb_counts: Vec<u64>, intervals: Vec<ExecInterval>, jobs: Vec<RTOSJob>) -> Self {
#[allow(unused)] #[allow(unused)]
let mut indices : Vec<_> = vec![]; let mut indices : Vec<_> = vec![];
#[cfg(feature = "sched_stg_edge")] #[cfg(feature = "sched_stg_edge")]
@ -267,7 +290,7 @@ impl STGNodeMetadata {
&self.intervals &self.intervals
} }
pub fn jobs(&self) -> &Vec<JobInstance> { pub fn jobs(&self) -> &Vec<RTOSJob> {
&self.jobs &self.jobs
} }
} }
@ -344,7 +367,11 @@ pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u16> {
/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`]
#[derive(Serialize, Deserialize, Clone, Debug, Default)] #[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct StgFeedback #[serde(bound = "SYS: Serialize, for<'de2> SYS: Deserialize<'de2>")]
pub struct StgFeedback<SYS>
where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
{ {
name: Cow<'static, str>, name: Cow<'static, str>,
last_node_trace: Option<Vec<NodeIndex>>, last_node_trace: Option<Vec<NodeIndex>>,
@ -354,8 +381,9 @@ pub struct StgFeedback
last_abbs_hash: Option<u64>, // only set, if it was interesting last_abbs_hash: Option<u64>, // only set, if it was interesting
last_aggregate_hash: Option<u64>, // only set, if it was interesting last_aggregate_hash: Option<u64>, // only set, if it was interesting
last_top_abb_hashes: Option<Vec<u64>>, // only set, if it was interesting last_top_abb_hashes: Option<Vec<u64>>, // only set, if it was interesting
last_job_trace: Option<Vec<JobInstance>>, // only set, if it was interesting last_job_trace: Option<Vec<RTOSJob>>, // only set, if it was interesting
dump_path: Option<PathBuf> dump_path: Option<PathBuf>,
_phantom_data: PhantomData<SYS>,
} }
#[cfg(feature = "feed_stg")] #[cfg(feature = "feed_stg")]
const INTEREST_EDGE : bool = true; const INTEREST_EDGE : bool = true;
@ -425,7 +453,10 @@ fn execinterval_to_abb_instances(trace: &Vec<ExecInterval>, read_trace: &Vec<Vec
return instance_time; return instance_time;
} }
impl StgFeedback { impl<SYS> StgFeedback<SYS>
where
SYS: TargetSystem,
{
pub fn new(dump_name: Option<PathBuf>) -> Self { pub fn new(dump_name: Option<PathBuf>) -> Self {
// Self {name: String::from("STGFeedback"), last_node_trace: None, last_edge_trace: None, last_intervals: None } // Self {name: String::from("STGFeedback"), last_node_trace: None, last_edge_trace: None, last_intervals: None }
let mut s = Self::default(); let mut s = Self::default();
@ -442,7 +473,7 @@ impl StgFeedback {
/// newly discovered node? /// newly discovered node?
/// side effect: /// side effect:
/// the graph gets new nodes and edge /// the graph gets new nodes and edge
fn update_stg_interval(trace: &Vec<ExecInterval>, read_trace: &Vec<Vec<(u32, u8)>>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, fbs: &mut STGFeedbackState) -> (Vec<(NodeIndex, u64)>, Vec<(EdgeIndex, u64)>, bool, bool) { fn update_stg_interval(trace: &Vec<ExecInterval>, read_trace: &Vec<Vec<(u32, u8)>>, table: &HashMap<u64, SYS::State>, fbs: &mut STGFeedbackState<SYS>) -> (Vec<(NodeIndex, u64)>, Vec<(EdgeIndex, u64)>, bool, bool) {
let mut return_node_trace = vec![(fbs.entrypoint, 0)]; // Assuming entrypoint timestamp is 0 let mut return_node_trace = vec![(fbs.entrypoint, 0)]; // Assuming entrypoint timestamp is 0
let mut return_edge_trace = vec![]; let mut return_edge_trace = vec![];
let mut interesting = false; let mut interesting = false;
@ -453,14 +484,14 @@ impl StgFeedback {
let mut instance_time = execinterval_to_abb_instances(trace, read_trace); let mut instance_time = execinterval_to_abb_instances(trace, read_trace);
// add all missing state+abb combinations to the graph // add all missing state+abb combinations to the graph
for (_i,interval) in trace.iter().enumerate() { // Iterate intervals for (_i,interval) in trace.iter().enumerate() { // Iterate intervals
let node = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()}; let node : STGNode<SYS> = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()};
let h_node = node.get_hash(); let h_node = node.get_hash();
let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) { let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) {
// already present // already present
*idx *idx
} else { } else {
// not present // not present
let h = (node.base.get_hash(), node.abb.get_hash()); let h = (compute_hash(&node.base), node.abb.get_hash());
let idx = fbs.graph.add_node(node); let idx = fbs.graph.add_node(node);
fbs.stgnode_index.insert(h_node, idx); fbs.stgnode_index.insert(h_node, idx);
fbs.state_abb_hash_index.insert(h, idx); fbs.state_abb_hash_index.insert(h, idx);
@ -524,14 +555,18 @@ impl StgFeedback {
} }
} }
impl<S> StateInitializer<S> for StgFeedback {} impl<S, SYS> StateInitializer<S> for StgFeedback<SYS>
where
SYS: TargetSystem,
{}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for StgFeedback impl<EM, I, OT, S, SYS> Feedback<EM, I, OT, S> for StgFeedback<SYS>
where where
S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata + HasMetadata,
S::Input: Default, S::Input: Default,
EM: EventFirer<State = S>, EM: EventFirer<State = S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
SYS: TargetSystem,
{ {
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
fn is_interesting( fn is_interesting(
@ -545,7 +580,9 @@ where
where where
<S as UsesInput>::Input: Default, <S as UsesInput>::Input: Default,
{ {
let observer = observers.match_name::<QemuSystemStateObserver<<S as UsesInput>::Input>>("systemstate") // TODO: don't remove metadata. work around ownership issues
let trace = state.remove_metadata::<SYS::TraceData>().expect("TraceData not found");
let observer = observers.match_name::<QemuSystemStateObserver<<S as UsesInput>::Input, SYS>>("systemstate")
.expect("QemuSystemStateObserver not found"); .expect("QemuSystemStateObserver not found");
let clock_observer = observers.match_name::<QemuClockObserver>("clocktime") let clock_observer = observers.match_name::<QemuClockObserver>("clocktime")
.expect("QemuClockObserver not found"); .expect("QemuClockObserver not found");
@ -555,21 +592,22 @@ where
let last_runtime = clock_observer.last_runtime(); let last_runtime = clock_observer.last_runtime();
let feedbackstate = match state let feedbackstate = match state
.named_metadata_map_mut() .named_metadata_map_mut()
.get_mut::<STGFeedbackState>("stgfeedbackstate") { .get_mut::<STGFeedbackState<SYS>>("stgfeedbackstate") {
Some(s) => s, Some(s) => s,
Option::None => { Option::None => {
let n=STGFeedbackState::default(); let n=STGFeedbackState::<SYS>::default();
state.named_metadata_map_mut().insert("stgfeedbackstate",n); state.named_metadata_map_mut().insert("stgfeedbackstate",n);
state.named_metadata_map_mut().get_mut::<STGFeedbackState>("stgfeedbackstate").unwrap() state.named_metadata_map_mut().get_mut::<STGFeedbackState<SYS>>("stgfeedbackstate").unwrap()
} }
}; };
// --------------------------------- Update STG // --------------------------------- Update STG
let (mut nodetrace, mut edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_reads, &observer.last_states, feedbackstate); let (mut nodetrace, mut edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(trace.intervals(), &trace.mem_reads(), trace.states_map(), feedbackstate);
let worst_jobs = trace.worst_jobs_per_task_by_time();
#[cfg(feature = "trace_job_response_times")] #[cfg(feature = "trace_job_response_times")]
let worst_target_instance = observer.job_instances.iter().filter(|x| Some(x.name.clone()) == observer.select_task).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release))); let worst_target_instance = worst_jobs.get(observer.select_task.as_ref().unwrap_or(&String::new()));
#[cfg(feature = "trace_job_response_times")] #[cfg(feature = "trace_job_response_times")]
if let Some(worst_instance) = worst_target_instance { if let Some(worst_instance) = worst_target_instance {
@ -586,17 +624,17 @@ where
set_observer_map(&edgetrace.iter().map(|x| x.0).collect::<Vec<_>>()); set_observer_map(&edgetrace.iter().map(|x| x.0).collect::<Vec<_>>());
// --------------------------------- Update job instances // --------------------------------- Update job instances
for i in observer.worst_job_instances.iter() { for i in trace.worst_jobs_per_task_by_time().iter() {
interesting |= INTEREST_JOB_INSTANCE && if let Some(x) = feedbackstate.worst_task_jobs.get_mut(&i.1.get_hash_cached()) { interesting |= INTEREST_JOB_INSTANCE && if let Some(x) = feedbackstate.worst_task_jobs.get_mut(&i.1.get_hash_cached()) {
// eprintln!("Job instance already present"); // eprintln!("Job instance already present");
x.try_update(i.1) x.try_update(i.1)
} else { } else {
// eprintln!("New Job instance"); // eprintln!("New Job instance");
feedbackstate.worst_task_jobs.insert(i.1.get_hash_cached(), TaskJob::from_instance(&i.1)); feedbackstate.worst_task_jobs.insert(i.1.get_hash_cached(), RTOSTask::from_instance(&i.1));
true true
} }
}; };
self.last_job_trace = Some(observer.job_instances.clone()); self.last_job_trace = Some(trace.jobs().clone());
// dbg!(&observer.job_instances); // dbg!(&observer.job_instances);
{ {
@ -619,11 +657,11 @@ where
#[cfg(feature = "trace_job_response_times")] #[cfg(feature = "trace_job_response_times")]
let tmp = { let tmp = {
if let Some(worst_instance) = worst_target_instance { if let Some(worst_instance) = worst_target_instance {
let t = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect(); let t = trace.intervals().iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect();
StgFeedback::abbs_in_exec_order(&t) StgFeedback::<SYS>::abbs_in_exec_order(&t)
} else { } else {
if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here
StgFeedback::abbs_in_exec_order(&observer.last_trace) StgFeedback::<SYS>::abbs_in_exec_order(trace.intervals())
} else { } else {
Vec::new() Vec::new()
} }
@ -684,7 +722,7 @@ where
// fs::write("./mystg.dot",outs).expect("Failed to write graph"); // fs::write("./mystg.dot",outs).expect("Failed to write graph");
self.last_node_trace = Some(nodetrace.into_iter().map(|x| x.0).collect::<Vec<_>>()); self.last_node_trace = Some(nodetrace.into_iter().map(|x| x.0).collect::<Vec<_>>());
self.last_edge_trace = Some(edgetrace.into_iter().map(|x| x.0).collect::<Vec<_>>()); self.last_edge_trace = Some(edgetrace.into_iter().map(|x| x.0).collect::<Vec<_>>());
self.last_intervals = Some(observer.last_trace.clone()); self.last_intervals = Some(trace.intervals().clone());
self.last_abb_trace = Some(tmp); self.last_abb_trace = Some(tmp);
if let Some(dp) = &self.dump_path { if let Some(dp) = &self.dump_path {
@ -716,7 +754,9 @@ where
Ok(()) Ok(())
} }
} }
impl Named for StgFeedback impl<SYS> Named for StgFeedback<SYS>
where
SYS: TargetSystem,
{ {
#[inline] #[inline]
fn name(&self) -> &Cow<'static, str> { fn name(&self) -> &Cow<'static, str> {

View File

@ -85,38 +85,4 @@ pub struct tskTaskControlBlock {
} }
pub type tskTCB = tskTaskControlBlock; pub type tskTCB = tskTaskControlBlock;
pub type TCB_t = tskTCB; pub type TCB_t = tskTCB;
/*========== End of generated Code =============*/ /*========== End of generated Code =============*/
pub trait emu_lookup {
fn lookup(emu: &Qemu, addr: ::std::os::raw::c_uint) -> Self;
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum rtos_struct {
TCB_struct(TCB_t),
List_struct(List_t),
List_Item_struct(ListItem_t),
List_MiniItem_struct(MiniListItem_t),
}
#[macro_export]
macro_rules! impl_emu_lookup {
($struct_name:ident) => {
impl $crate::systemstate::freertos::emu_lookup for $struct_name {
fn lookup(emu: &Qemu, addr: ::std::os::raw::c_uint) -> $struct_name {
let mut tmp : [u8; std::mem::size_of::<$struct_name>()] = [0u8; std::mem::size_of::<$struct_name>()];
unsafe {
emu.read_mem(addr.into(), &mut tmp);
std::mem::transmute::<[u8; std::mem::size_of::<$struct_name>()], $struct_name>(tmp)
}
}
}
};
}
impl_emu_lookup!(TCB_t);
impl_emu_lookup!(List_t);
impl_emu_lookup!(ListItem_t);
impl_emu_lookup!(MiniListItem_t);
impl_emu_lookup!(void_ptr);
impl_emu_lookup!(TaskStatus_t);

View File

@ -0,0 +1,548 @@
use libafl_qemu::GuestAddr;
use qemu_module::{FreeRTOSSystemStateHelper, MEM_READ};
use serde::{Deserialize, Serialize};
use crate::{
impl_emu_lookup,
systemstate::{helpers::get_icount, CaptureEvent},
};
pub mod bindings;
pub mod qemu_module;
use bindings::*;
use super::QemuLookup;
use crate::systemstate::target_os::*;
// Constants
const NUM_PRIOS: usize = 15;
//============================================================================= Outside interface
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FreeRTOSSystem {
pub raw_trace: Vec<RawFreeRTOSSystemState>,
}
impl TargetSystem for FreeRTOSSystem {
type State = FreeRTOSSystemState;
type TCB = RefinedTCB;
type TraceData = FreeRTOSTraceMetadata;
}
impl TaskControlBlock for RefinedTCB {
fn task_name(&self) -> &String {
&self.task_name
}
fn task_name_mut(&mut self) -> &mut String {
&mut self.task_name
}
}
impl SystemState for FreeRTOSSystemState {
type TCB = RefinedTCB;
fn current_task(&self) -> &Self::TCB {
&self.current_task
}
fn get_ready_lists(&self) -> &Vec<Self::TCB> {
&self.ready_list_after
}
fn get_delay_list(&self) -> &Vec<Self::TCB> {
&self.delay_list_after
}
fn print_lists(&self) -> String {
self.print_lists()
}
fn current_task_mut(&mut self) -> &mut Self::TCB {
&mut self.current_task
}
// fn get_edge(&self) -> (GuestAddr, GuestAddr) {
// (self.edge.0, self.edge.1)
// }
// fn get_capture_point(&self) -> (CaptureEvent, String) {
// self.capture_point.clone()
// }
// fn get_mem_reads(&self) -> &Vec<(u32, u8)> {
// &self.mem_reads
// }
}
//============================================================================= Data structures
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum FreeRTOSStruct {
TCB_struct(TCB_t),
List_struct(List_t),
List_Item_struct(ListItem_t),
List_MiniItem_struct(MiniListItem_t),
}
impl_emu_lookup!(TCB_t);
impl_emu_lookup!(List_t);
impl_emu_lookup!(ListItem_t);
impl_emu_lookup!(MiniListItem_t);
impl_emu_lookup!(void_ptr);
impl_emu_lookup!(TaskStatus_t);
pub const ISR_SYMBOLS: &'static [&'static str] = &[
// ISRs
"Reset_Handler",
"Default_Handler",
"Default_Handler2",
"Default_Handler3",
"Default_Handler4",
"Default_Handler5",
"Default_Handler6",
"vPortSVCHandler",
"xPortPendSVHandler",
"xPortSysTickHandler",
"ISR_0_Handler",
"ISR_1_Handler",
"ISR_2_Handler",
"ISR_3_Handler",
"ISR_4_Handler",
"ISR_5_Handler",
"ISR_6_Handler",
"ISR_7_Handler",
"ISR_8_Handler",
"ISR_9_Handler",
"ISR_10_Handler",
"ISR_11_Handler",
"ISR_12_Handler",
"ISR_13_Handler",
];
pub const USR_ISR_SYMBOLS: &'static [&'static str] = &[
"ISR_0_Handler",
"ISR_1_Handler",
"ISR_2_Handler",
"ISR_3_Handler",
"ISR_4_Handler",
"ISR_5_Handler",
"ISR_6_Handler",
"ISR_7_Handler",
"ISR_8_Handler",
"ISR_9_Handler",
"ISR_10_Handler",
"ISR_11_Handler",
"ISR_12_Handler",
"ISR_13_Handler",
];
//============================================================================= Helper functions
fn read_freertos_list(
systemstate: &mut RawFreeRTOSSystemState,
emulator: &libafl_qemu::Qemu,
target: GuestAddr,
) -> (List_t, bool) {
let read: List_t = QemuLookup::lookup(emulator, target);
let listbytes: GuestAddr = GuestAddr::try_from(std::mem::size_of::<List_t>()).unwrap();
let mut next_index = read.pxIndex;
for _j in 0..read.uxNumberOfItems {
// always jump over the xListEnd marker
if (target..target + listbytes).contains(&next_index) {
let next_item: MiniListItem_t = QemuLookup::lookup(emulator, next_index);
let new_next_index = next_item.pxNext;
systemstate
.dumping_ground
.insert(next_index, FreeRTOSStruct::List_MiniItem_struct(next_item));
next_index = new_next_index;
}
let next_item: ListItem_t = QemuLookup::lookup(emulator, next_index);
// println!("Item at {}: {:?}",next_index,next_item);
if next_item.pvContainer != target {
// the list is being modified, abort by setting the list empty
eprintln!("Warning: attempted to read a list that is being modified");
let mut read = read;
read.uxNumberOfItems = 0;
return (read, false);
}
// assert_eq!(next_item.pvContainer,target);
let new_next_index = next_item.pxNext;
let next_tcb: TCB_t = QemuLookup::lookup(emulator, next_item.pvOwner);
// println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb);
systemstate.dumping_ground.insert(
next_item.pvOwner,
FreeRTOSStruct::TCB_struct(next_tcb.clone()),
);
systemstate
.dumping_ground
.insert(next_index, FreeRTOSStruct::List_Item_struct(next_item));
next_index = new_next_index;
}
// Handle edge case where the end marker was not included yet
if (target..target + listbytes).contains(&next_index) {
let next_item: freertos::MiniListItem_t = QemuLookup::lookup(emulator, next_index);
systemstate
.dumping_ground
.insert(next_index, FreeRTOSStruct::List_MiniItem_struct(next_item));
}
return (read, true);
}
#[inline]
fn trigger_collection(
emulator: &libafl_qemu::Qemu,
edge: (GuestAddr, GuestAddr),
event: CaptureEvent,
h: &FreeRTOSSystemStateHelper,
) {
let listbytes: GuestAddr =
GuestAddr::try_from(std::mem::size_of::<freertos::List_t>()).unwrap();
let mut systemstate = RawFreeRTOSSystemState::default();
match event {
CaptureEvent::APIStart => {
let s = h.api_fn_addrs.get(&edge.1).unwrap();
systemstate.capture_point = (CaptureEvent::APIStart, s.to_string());
}
CaptureEvent::APIEnd => {
let s = h.api_fn_addrs.get(&edge.0).unwrap();
systemstate.capture_point = (CaptureEvent::APIEnd, s.to_string());
}
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 {
// println!("Not found: {:#x} {:#x}", edge.0.unwrap_or(0), edge.1.unwrap_or(0));
}
systemstate.edge = ((edge.0), (edge.1));
systemstate.qemu_tick = get_icount(emulator);
let mut buf: [u8; 4] = [0, 0, 0, 0];
match h.input_counter {
Some(s) => unsafe {
emulator.read_mem(s, &mut buf);
},
Option::None => (),
};
systemstate.input_counter = GuestAddr::from_le_bytes(buf);
let curr_tcb_addr: freertos::void_ptr = QemuLookup::lookup(emulator, h.tcb_addr);
if curr_tcb_addr == 0 {
return;
};
// println!("{:?}",std::str::from_utf8(&current_tcb.pcTaskName));
let critical: void_ptr = QemuLookup::lookup(emulator, h.critical_addr);
let suspended: void_ptr = QemuLookup::lookup(emulator, h.scheduler_lock_addr);
let _running: void_ptr = QemuLookup::lookup(emulator, h.scheduler_running_addr);
systemstate.current_tcb = QemuLookup::lookup(emulator, curr_tcb_addr);
// During ISRs it is only safe to extract structs if they are not currently being modified
if systemstate.capture_point.0 == CaptureEvent::APIStart
|| systemstate.capture_point.0 == CaptureEvent::APIEnd
|| (critical == 0 && suspended == 0)
{
// Extract delay list
let mut target: GuestAddr = h.delay_queue;
target = QemuLookup::lookup(emulator, target);
let _temp = read_freertos_list(&mut systemstate, emulator, target);
systemstate.delay_list = _temp.0;
systemstate.read_invalid |= !_temp.1;
// Extract delay list overflow
let mut target: GuestAddr = h.delay_queue_overflow;
target = QemuLookup::lookup(emulator, target);
let _temp = read_freertos_list(&mut systemstate, emulator, target);
systemstate.delay_list_overflow = _temp.0;
systemstate.read_invalid |= !_temp.1;
// Extract suspended tasks (infinite wait), seems broken, always appreas to be modified
// let mut target : GuestAddr = h.suspended_queue;
// target = QemuLookup::lookup(emulator, target);
// systemstate.suspended_list = read_freertos_list(&mut systemstate, emulator, target);
// Extract priority lists
for i in 0..NUM_PRIOS {
let target: GuestAddr = listbytes * GuestAddr::try_from(i).unwrap() + h.ready_queues;
let _temp = read_freertos_list(&mut systemstate, emulator, target);
systemstate.prio_ready_lists[i] = _temp.0;
systemstate.read_invalid |= !_temp.1;
}
} else {
systemstate.read_invalid = true;
}
systemstate.mem_reads = unsafe { MEM_READ.take().unwrap_or_default() };
unsafe {
CURRENT_SYSTEMSTATE_VEC.push(systemstate);
}
}
/// Raw info Dump from Qemu
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RawFreeRTOSSystemState {
qemu_tick: u64,
current_tcb: TCB_t,
prio_ready_lists: [freertos::List_t; NUM_PRIOS],
delay_list: freertos::List_t,
delay_list_overflow: freertos::List_t,
dumping_ground: HashMap<u32, freertos::FreeRTOSStruct>,
read_invalid: bool,
input_counter: u32,
edge: (GuestAddr, GuestAddr),
capture_point: (CaptureEvent, String),
mem_reads: Vec<(u32, u8)>,
}
/// List of system state dumps from EmulatorModules
static mut CURRENT_SYSTEMSTATE_VEC: Vec<RawFreeRTOSSystemState> = vec![];
/// A reduced version of freertos::TCB_t
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct RefinedTCB {
pub task_name: String,
pub priority: u32,
pub base_priority: u32,
mutexes_held: u32,
// notify_value: u32,
notify_state: u8,
}
impl PartialEq for RefinedTCB {
fn eq(&self, other: &Self) -> bool {
let ret = self.task_name == other.task_name
&& self.priority == other.priority
&& self.base_priority == other.base_priority;
#[cfg(feature = "do_hash_notify_state")]
let ret = ret && self.notify_state == other.notify_state;
ret
}
}
impl Hash for RefinedTCB {
fn hash<H: Hasher>(&self, state: &mut H) {
self.task_name.hash(state);
self.priority.hash(state);
self.mutexes_held.hash(state);
#[cfg(feature = "do_hash_notify_state")]
self.notify_state.hash(state);
// self.notify_value.hash(state);
}
}
impl RefinedTCB {
pub fn from_tcb(input: &TCB_t) -> Self {
unsafe {
let tmp = std::mem::transmute::<[i8; 10], [u8; 10]>(input.pcTaskName);
let name: String = std::str::from_utf8(&tmp)
.expect("TCB name was not utf8")
.chars()
.filter(|x| *x != '\0')
.collect::<String>();
Self {
task_name: name,
priority: input.uxPriority,
base_priority: input.uxBasePriority,
mutexes_held: input.uxMutexesHeld,
// notify_value: input.ulNotifiedValue[0],
notify_state: input.ucNotifyState[0],
}
}
}
pub fn from_tcb_owned(input: TCB_t) -> Self {
unsafe {
let tmp = std::mem::transmute::<[i8; 10], [u8; 10]>(input.pcTaskName);
let name: String = std::str::from_utf8(&tmp)
.expect("TCB name was not utf8")
.chars()
.filter(|x| *x != '\0')
.collect::<String>();
Self {
task_name: name,
priority: input.uxPriority,
base_priority: input.uxBasePriority,
mutexes_held: input.uxMutexesHeld,
// notify_value: input.ulNotifiedValue[0],
notify_state: input.ucNotifyState[0],
}
}
}
}
/// Reduced information about a systems state, without any execution context
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct FreeRTOSSystemState {
current_task: RefinedTCB,
ready_list_after: Vec<RefinedTCB>,
delay_list_after: Vec<RefinedTCB>,
read_invalid: bool,
}
impl PartialEq for FreeRTOSSystemState {
fn eq(&self, other: &Self) -> bool {
self.current_task == other.current_task
&& self.ready_list_after == other.ready_list_after
&& self.delay_list_after == other.delay_list_after
&& self.read_invalid == other.read_invalid
}
}
impl Hash for FreeRTOSSystemState {
fn hash<H: Hasher>(&self, state: &mut H) {
self.current_task.hash(state);
self.ready_list_after.hash(state);
self.delay_list_after.hash(state);
self.read_invalid.hash(state);
}
}
impl FreeRTOSSystemState {
// fn get_tick(&self) -> u64 {
// self.tick
// }
pub fn print_lists(&self) -> String {
let mut ret = String::from("+");
for j in self.ready_list_after.iter() {
ret.push_str(format!(" {}", j.task_name).as_str());
}
ret.push_str("\n-");
for j in self.delay_list_after.iter() {
ret.push_str(format!(" {}", j.task_name).as_str());
}
ret
}
pub fn get_hash(&self) -> u64 {
let mut h = DefaultHasher::new();
self.hash(&mut h);
h.finish()
}
}
impl fmt::Display for FreeRTOSSystemState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ready = self
.ready_list_after
.iter()
.map(|x| x.task_name.clone())
.collect::<Vec<_>>()
.join(" ");
let delay = self
.delay_list_after
.iter()
.map(|x| x.task_name.clone())
.collect::<Vec<_>>()
.join(" ");
write!(
f,
"Valid: {} | Current: {} | Ready: {} | Delay: {}",
u32::from(!self.read_invalid),
self.current_task.task_name,
ready,
delay
)
}
}
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub(crate)struct FreeRTOSSystemStateContext {
pub qemu_tick: u64,
pub capture_point: (CaptureEvent, String),
pub edge: (GuestAddr, GuestAddr),
pub mem_reads: Vec<(u32, u8)>,
}
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct FreeRTOSTraceMetadata
{
trace_map: HashMap<u64, <FreeRTOSTraceMetadata as SystemTraceData>::State>,
intervals: Vec<ExecInterval>,
mem_reads: Vec<Vec<(u32, u8)>>,
jobs: Vec<RTOSJob>,
trace_length: usize,
indices: Vec<usize>, // Hashed enumeration of States
tcref: isize,
}
impl FreeRTOSTraceMetadata
{
pub fn new(trace: Vec<<FreeRTOSTraceMetadata as SystemTraceData>::State>, intervals: Vec<ExecInterval>, mem_reads: Vec<Vec<(u32, u8)>>, jobs: Vec<RTOSJob>) -> Self {
let hashes : Vec<_> = trace
.iter()
.map(|x| compute_hash(&x) as usize)
.collect();
let trace_map = HashMap::from_iter(trace.into_iter().zip(hashes.iter()).map(|(x, y)| (*y as u64, x)));
Self {
trace_length: hashes.len(), // TODO make this configurable
trace_map: trace_map,
intervals: intervals,
mem_reads: mem_reads,
jobs: jobs,
indices: hashes,
tcref: 0,
}
}
}
impl HasRefCnt for FreeRTOSTraceMetadata
{
fn refcnt(&self) -> isize {
self.tcref
}
fn refcnt_mut(&mut self) -> &mut isize {
&mut self.tcref
}
}
impl SystemTraceData for FreeRTOSTraceMetadata
{
type State = FreeRTOSSystemState;
fn states(&self) -> Vec<&Self::State> {
self.indices.iter().map(|x| self.trace_map.get(&(*x as u64)).unwrap()).collect()
}
fn intervals(&self) -> &Vec<ExecInterval> {
&self.intervals
}
fn jobs(&self) -> &Vec<RTOSJob> {
&self.jobs
}
fn trace_length(&self) -> usize {
self.trace_length
}
fn mem_reads(&self) -> &Vec<Vec<(u32, u8)>> {
&self.mem_reads
}
fn states_map(&self) -> &HashMap<u64, Self::State> {
&self.trace_map
}
}
libafl_bolts::impl_serdeany!(FreeRTOSTraceMetadata);
libafl_bolts::impl_serdeany!(RefinedTCB);
libafl_bolts::impl_serdeany!(FreeRTOSSystemState);
libafl_bolts::impl_serdeany!(FreeRTOSSystem);
fn get_task_names(trace: &Vec<FreeRTOSSystemState>) -> HashSet<String> {
let mut ret: HashSet<_, _> = HashSet::new();
for state in trace {
ret.insert(state.current_task.task_name.to_string());
}
ret
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,110 @@
use std::collections::hash_map::DefaultHasher;
use std::fmt;
use hashbrown::HashSet;
use libafl_bolts::prelude::SerdeAny;
use libafl_bolts::HasRefCnt;
use libafl_qemu::GuestAddr;
use libafl_qemu::Qemu;
use serde::de::DeserializeOwned;
use std::hash::Hasher;
use std::hash::Hash;
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use itertools::Itertools;
use std::fmt::Debug;
use super::CaptureEvent;
use super::ExecInterval;
use super::RTOSJob;
pub mod freertos;
// Constants
const NUM_PRIOS: usize = 15;
//============================= Trait definitions
pub trait TargetSystem: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Clone + SerdeAny {
type State: SystemState<TCB = Self::TCB>;
type TCB: TaskControlBlock;
type TraceData: SystemTraceData<State = Self::State>;
}
pub trait SystemState: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Hash + PartialEq + Clone + SerdeAny {
type TCB: TaskControlBlock;
fn current_task(&self) -> &Self::TCB;
fn current_task_mut(&mut self) -> &mut Self::TCB;
fn get_ready_lists(&self) -> &Vec<Self::TCB>;
fn get_delay_list(&self) -> &Vec<Self::TCB>;
fn print_lists(&self) -> String;
}
pub trait SystemTraceData: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Clone + SerdeAny + HasRefCnt {
type State: SystemState;
fn states(&self) -> Vec<&Self::State>;
fn states_map(&self) -> &HashMap<u64, Self::State>;
fn intervals(&self) -> &Vec<ExecInterval>;
fn mem_reads(&self) -> &Vec<Vec<(u32, u8)>>;
fn jobs(&self) -> &Vec<RTOSJob>;
fn trace_length(&self) -> usize;
#[inline]
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| {
match acc.get_mut(&next.name) {
Some(old) => {
if pred(old,next) {
*old=next.clone();
}
},
Option::None => {
acc.insert(next.name.clone(), next.clone());
}
}
acc
})
}
#[inline]
fn worst_jobs_per_task_by_time(&self) -> HashMap<String, RTOSJob> {
self.worst_jobs_per_task_by(&|old, x| x.exec_ticks > old.exec_ticks)
}
}
pub trait TaskControlBlock: Serialize + for<'a> Deserialize<'a> + Default + Debug + Hash + PartialEq + Clone + SerdeAny {
fn task_name(&self) -> &String;
fn task_name_mut(&mut self) -> &mut String;
// Define methods common to TCBs across different systems
}
//=============================
pub trait QemuLookup {
fn lookup(emu: &Qemu, addr: ::std::os::raw::c_uint) -> Self;
}
#[macro_export]
macro_rules! impl_emu_lookup {
($struct_name:ident) => {
impl $crate::systemstate::target_os::QemuLookup for $struct_name {
fn lookup(emu: &Qemu, addr: ::std::os::raw::c_uint) -> $struct_name {
let mut tmp : [u8; std::mem::size_of::<$struct_name>()] = [0u8; std::mem::size_of::<$struct_name>()];
unsafe {
emu.read_mem(addr.into(), &mut tmp);
std::mem::transmute::<[u8; std::mem::size_of::<$struct_name>()], $struct_name>(tmp)
}
}
}
};
}
pub fn compute_hash<T>(obj: &T) -> u64
where
T: Hash,
{
let mut s = DefaultHasher::new();
obj.hash(&mut s);
s.finish()
}

View File

@ -23,6 +23,7 @@ use std::path::PathBuf;
use std::borrow::Cow; use std::borrow::Cow;
use crate::systemstate::observers::QemuSystemStateObserver; use crate::systemstate::observers::QemuSystemStateObserver;
use crate::systemstate::target_os::TargetSystem;
pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH; pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH;
@ -212,20 +213,22 @@ impl Default for QemuClockObserver {
/// for this Feedback, the testcase is never interesting (use with an OR). /// for this Feedback, the testcase is never interesting (use with an OR).
/// It decides, if the given [`QemuClockObserver`] value of a run is interesting. /// It decides, if the given [`QemuClockObserver`] value of a run is interesting.
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ClockTimeFeedback { pub struct ClockTimeFeedback<SYS> {
exec_time: Option<Duration>, exec_time: Option<Duration>,
select_task: Option<String>, select_task: Option<String>,
name: Cow<'static, str>, name: Cow<'static, str>,
phantom: std::marker::PhantomData<SYS>,
} }
impl<S> StateInitializer<S> for ClockTimeFeedback {} impl<S, SYS> StateInitializer<S> for ClockTimeFeedback<SYS> where SYS: TargetSystem {}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for ClockTimeFeedback impl<EM, I, OT, S, SYS> Feedback<EM, I, OT, S> for ClockTimeFeedback<SYS>
where where
S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata, S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata,
<S as UsesInput>::Input: Default, <S as UsesInput>::Input: Default,
EM: EventFirer<State = S>, EM: EventFirer<State = S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
SYS: TargetSystem,
{ {
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
fn is_interesting( fn is_interesting(
@ -241,7 +244,7 @@ where
#[cfg(feature="trace_job_response_times")] #[cfg(feature="trace_job_response_times")]
{ {
if self.select_task.is_some() { if self.select_task.is_some() {
let observer = observers.match_name::<QemuSystemStateObserver<S::Input>>("systemstate").unwrap(); let observer = observers.match_name::<QemuSystemStateObserver<S::Input, SYS>>("systemstate").unwrap();
self.exec_time = Some(Duration::from_nanos(observer.last_runtime())); self.exec_time = Some(Duration::from_nanos(observer.last_runtime()));
return Ok(false) return Ok(false)
} }
@ -274,14 +277,14 @@ where
} }
} }
impl Named for ClockTimeFeedback { impl<SYS> Named for ClockTimeFeedback<SYS> {
#[inline] #[inline]
fn name(&self) -> &Cow<'static, str> { fn name(&self) -> &Cow<'static, str> {
&self.name &self.name
} }
} }
impl ClockTimeFeedback { impl<SYS> ClockTimeFeedback<SYS> {
/// Creates a new [`ClockFeedback`], deciding if the value of a [`QemuClockObserver`] with the given `name` of a run is interesting. /// Creates a new [`ClockFeedback`], deciding if the value of a [`QemuClockObserver`] with the given `name` of a run is interesting.
#[must_use] #[must_use]
pub fn new(name: &'static str, select_task: Option<String>) -> Self { pub fn new(name: &'static str, select_task: Option<String>) -> Self {
@ -289,6 +292,7 @@ impl ClockTimeFeedback {
exec_time: None, exec_time: None,
select_task: select_task, select_task: select_task,
name: Cow::from(name.to_string()), name: Cow::from(name.to_string()),
phantom: std::marker::PhantomData,
} }
} }
@ -299,6 +303,7 @@ impl ClockTimeFeedback {
exec_time: None, exec_time: None,
select_task: select_task.clone(), select_task: select_task.clone(),
name: observer.name().clone(), name: observer.name().clone(),
phantom: std::marker::PhantomData,
} }
} }
} }

View File

@ -36,8 +36,8 @@ use libafl::{
Error, Error,
}; };
use crate::systemstate::target_os::TargetSystem;
use crate::time::clock::QemuClockObserver; use crate::time::clock::QemuClockObserver;
use crate::systemstate::FreeRTOSSystemStateMetadata;
use libafl::prelude::StateInitializer; use libafl::prelude::StateInitializer;
@ -72,8 +72,8 @@ where
pub type LenTimeMaximizerCorpusScheduler<CS, O> = pub type LenTimeMaximizerCorpusScheduler<CS, O> =
MinimizerScheduler<CS, MaxExecsLenFavFactor<<CS as UsesState>::State>, MapIndexesMetadata, O>; MinimizerScheduler<CS, MaxExecsLenFavFactor<<CS as UsesState>::State>, MapIndexesMetadata, O>;
pub type TimeStateMaximizerCorpusScheduler<CS, O> = pub type TimeStateMaximizerCorpusScheduler<CS, O, SYS> =
MinimizerScheduler<CS, MaxTimeFavFactor<<CS as UsesState>::State>, FreeRTOSSystemStateMetadata, O>; MinimizerScheduler<CS, MaxTimeFavFactor<<CS as UsesState>::State>, <SYS as TargetSystem>::TraceData, O>;
/// Multiply the testcase size with the execution time. /// Multiply the testcase size with the execution time.
/// This favors small and quick testcases. /// This favors small and quick testcases.

View File

@ -313,7 +313,6 @@ where
} }
let mut ret = None; let mut ret = None;
let mut last = current_time();
let monitor_timeout = STATS_TIMEOUT_DEFAULT; let monitor_timeout = STATS_TIMEOUT_DEFAULT;
let starttime = std::time::Instant::now(); let starttime = std::time::Instant::now();
@ -349,7 +348,6 @@ where
time: std::time::Instant time: std::time::Instant
) -> Result<CorpusId, Error> { ) -> Result<CorpusId, Error> {
let mut ret = None; let mut ret = None;
let mut last = current_time();
let monitor_timeout = STATS_TIMEOUT_DEFAULT; let monitor_timeout = STATS_TIMEOUT_DEFAULT;
while std::time::Instant::now() < time { while std::time::Instant::now() < time {
@ -364,7 +362,7 @@ where
if ret.is_none() { if ret.is_none() {
eprintln!("Warning: fuzzing loop ended with no last element"); eprintln!("Warning: fuzzing loop ended with no last element");
ret = Some(crate::corpus::CorpusId(0)); ret = Some(CorpusId(0));
} }
Ok(ret.unwrap()) Ok(ret.unwrap())
} }

View File

@ -181,6 +181,7 @@ pub fn generate(
.allowlist_function("vm_start") .allowlist_function("vm_start")
.allowlist_function("qemu_main_loop") .allowlist_function("qemu_main_loop")
.allowlist_function("qemu_cleanup") .allowlist_function("qemu_cleanup")
.allowlist_function("icount_get_raw")
.blocklist_function("main_loop_wait") // bindgen issue #1313 .blocklist_function("main_loop_wait") // bindgen issue #1313
.blocklist_type("siginfo_t") .blocklist_type("siginfo_t")
.raw_line("use libc::siginfo_t;") .raw_line("use libc::siginfo_t;")