abstract SystemTraceData
This commit is contained in:
parent
8d7e32559f
commit
a13dca6f39
@ -14,7 +14,7 @@ elf::EasyElf, emu::Emulator, modules::{edges::{self}, FilterList}, GuestAddr, Gu
|
||||
};
|
||||
use rand::{SeedableRng, StdRng, Rng};
|
||||
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}
|
||||
}
|
||||
};
|
||||
@ -123,7 +123,7 @@ macro_rules! do_dump_stg {
|
||||
if $cli.dump_graph {
|
||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"dot"} else {$c});
|
||||
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 outs = Dot::with_config(&out, &[]).to_string();
|
||||
let outs = outs.replace("\\\"","\"");
|
||||
@ -245,17 +245,17 @@ let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range);
|
||||
println!("APP functions:");
|
||||
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();
|
||||
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
|
||||
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::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 = FilterList::DenyList(denylist); // do not count isr jumps, which are useless
|
||||
#[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")]
|
||||
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")]
|
||||
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 let Some(fr) = get_function_range(&elf, i) {
|
||||
isr_addreses.insert(fr.start, i.to_string());
|
||||
@ -408,13 +408,13 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||
)}.track_indices();
|
||||
|
||||
#[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
|
||||
// This one is composed by two Feedbacks in OR
|
||||
let mut feedback = feedback_or!(
|
||||
// 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")]
|
||||
let mut feedback = feedback_or!(
|
||||
@ -437,12 +437,12 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||
#[cfg(all(feature = "observe_systemstate"))]
|
||||
let mut feedback = feedback_or!(
|
||||
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")]
|
||||
let mut feedback = feedback_or!(
|
||||
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")]
|
||||
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
|
||||
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
|
||||
let mut state = state.unwrap_or_else(|| {
|
||||
@ -491,7 +491,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||
|
||||
let qhelpers = tuple_list!();
|
||||
#[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")]
|
||||
let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), 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 = (StdMutationalStage::new(mutator), stages);
|
||||
#[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")]
|
||||
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() {
|
||||
let s = input.as_os_str();
|
||||
|
@ -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 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 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 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::target_os::TargetSystem;
|
||||
use super::ExecInterval;
|
||||
use libafl_bolts::prelude::SerdeAny;
|
||||
use std::borrow::Cow;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::systemstate::target_os::*;
|
||||
use libafl::prelude::StateInitializer;
|
||||
|
||||
//============================= Feedback
|
||||
|
||||
/// Shared Metadata for a systemstateFeedback
|
||||
#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)]
|
||||
pub struct SystemStateFeedbackState
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct SystemStateFeedbackState<SYS>
|
||||
where
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
name: Cow<'static, str>,
|
||||
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]
|
||||
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 {
|
||||
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`]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct NovelSystemStateFeedback
|
||||
// /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`]
|
||||
// #[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
// 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>,
|
||||
last_trace: Option<Vec<ReducedFreeRTOSSystemState>>,
|
||||
// known_traces: HashMap<u64,(u64,usize)>,
|
||||
dumpfile: Option<PathBuf>,
|
||||
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
|
||||
S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata,
|
||||
S::Input: Default,
|
||||
S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata,
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
fn is_interesting(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &I,
|
||||
observers: &OT,
|
||||
_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()
|
||||
}
|
||||
where {
|
||||
if self.dumpfile.is_none() {
|
||||
return Ok(false);
|
||||
};
|
||||
#[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)
|
||||
}
|
||||
let trace = state
|
||||
.metadata::<SYS::TraceData>()
|
||||
.expect("TraceData not found");
|
||||
|
||||
/// 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
|
||||
{
|
||||
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();
|
||||
let names: Vec<String> = trace
|
||||
.states()
|
||||
.iter()
|
||||
.map(|x| x.current_task().task_name().clone())
|
||||
.collect();
|
||||
match &self.dumpfile {
|
||||
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();
|
||||
let t: Vec<_> = trace.intervals()
|
||||
.iter()
|
||||
.filter(|x| {
|
||||
x.start_tick < worst_instance.response
|
||||
&& x.end_tick > worst_instance.release
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
// task_name -> addr -> (count, time)
|
||||
let mut ret : HashMap<String, HashMap<u32, (usize, usize, u64)>> = HashMap::new();
|
||||
let mut ret: HashMap<String, HashMap<u32, (usize, usize, u64)>> =
|
||||
HashMap::new();
|
||||
let mut t2 = t.clone();
|
||||
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| {
|
||||
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| {
|
||||
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::<_>()))]));
|
||||
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()));
|
||||
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
|
||||
} else {HashMap::new()};
|
||||
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 {
|
||||
HashMap::new()
|
||||
};
|
||||
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{:?}",observer.last_run,names);}
|
||||
}
|
||||
Option::None => {
|
||||
if self.dump_metadata {
|
||||
println!("{:?}\n{:?}", trace, names);
|
||||
}
|
||||
}
|
||||
};
|
||||
// if self.dump_metadata {self.last_trace=Some(observer.last_trace.clone());}
|
||||
Ok(false)
|
||||
}
|
||||
/// 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> {
|
||||
if !self.dump_metadata {return Ok(());}
|
||||
fn append_metadata(
|
||||
&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();
|
||||
// match a {
|
||||
// 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
|
||||
#[inline]
|
||||
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
|
||||
self.last_trace = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for DumpSystraceFeedback
|
||||
impl<SYS> Named for DumpSystraceFeedback<SYS>
|
||||
where
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
#[inline]
|
||||
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`]
|
||||
#[allow(unused)]
|
||||
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)]
|
||||
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)]
|
||||
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)]
|
||||
pub struct SystraceErrorFeedback
|
||||
pub struct SystraceErrorFeedback<SYS>
|
||||
where
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
name: Cow<'static, str>,
|
||||
dump_case: bool,
|
||||
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
|
||||
S: State + UsesInput + MaybeHasClientPerfMonitor,
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<I, S>
|
||||
OT: ObserversTuple<I, S>,
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
fn is_interesting(
|
||||
&mut self,
|
||||
@ -298,15 +399,17 @@ where
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
{
|
||||
where {
|
||||
#[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");
|
||||
let is_err = (!observer.success || observer.do_report);
|
||||
if let Some(m) = self.max_reports {
|
||||
if m <= 0 {return Ok(false);}
|
||||
if m <= 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
if is_err {
|
||||
self.max_reports = Some(m - 1);
|
||||
}
|
||||
@ -320,7 +423,13 @@ where
|
||||
}
|
||||
/// 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> {
|
||||
fn append_metadata(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_observers: &OT,
|
||||
_testcase: &mut Testcase<I>,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -331,7 +440,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for SystraceErrorFeedback
|
||||
impl<SYS> Named for SystraceErrorFeedback<SYS>
|
||||
where
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
#[inline]
|
||||
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]
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
@ -15,14 +15,6 @@ use libafl_qemu::modules::{EmulatorModule, EmulatorModuleTuple};
|
||||
use libafl_qemu::sys::TCGTemp;
|
||||
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 libafl_qemu::EmulatorModules;
|
||||
use libafl::prelude::ObserversTuple;
|
||||
@ -31,13 +23,6 @@ use libafl::prelude::ObserversTuple;
|
||||
|
||||
//============================= 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.
|
||||
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;
|
||||
}
|
||||
|
||||
//============================= 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(¤t_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
|
||||
|
||||
fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 {
|
||||
pub fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 {
|
||||
unsafe {
|
||||
// TODO: investigate why can_do_io is not set sometimes, as this is just a workaround
|
||||
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;
|
||||
if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF8 || lr_ == 0xFFFFFFF0 {
|
||||
unsafe {
|
||||
|
@ -10,9 +10,6 @@ use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use itertools::Itertools;
|
||||
|
||||
use freertos::TCB_t;
|
||||
|
||||
pub mod freertos;
|
||||
pub mod helpers;
|
||||
pub mod observers;
|
||||
pub mod feedbacks;
|
||||
@ -20,9 +17,7 @@ pub mod schedulers;
|
||||
pub mod stg;
|
||||
pub mod mutational;
|
||||
pub mod report;
|
||||
|
||||
// Constants
|
||||
const NUM_PRIOS: usize = 15;
|
||||
pub mod target_os;
|
||||
|
||||
//============================= Struct definitions
|
||||
|
||||
@ -43,154 +38,10 @@ pub enum CaptureEvent {
|
||||
- ReducedFreeRTOSSystemState: Generalized state of the system, without execution context
|
||||
- ExecInterval: Some interval of execution between instants
|
||||
- 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
|
||||
- TaskJob: Generalized Job instance, records the worst inputs seen so far
|
||||
- RTOSJob: A single execution of a task, records the place and input read
|
||||
- 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
|
||||
|
||||
// #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
|
||||
@ -209,13 +60,13 @@ pub struct ExecInterval {
|
||||
pub start_capture: (CaptureEvent, String),
|
||||
pub end_capture: (CaptureEvent, String),
|
||||
pub level: u8,
|
||||
tick_spend_preempted: u64,
|
||||
// tick_spend_preempted: u64,
|
||||
pub abb: Option<AtomicBasicBlock>
|
||||
}
|
||||
|
||||
impl ExecInterval {
|
||||
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 {
|
||||
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
|
||||
pub fn try_unite_with_later_interval(&mut self, later_interval : &mut Self) -> bool {
|
||||
if self.end_state!=later_interval.start_state || self.abb!=later_interval.abb || !self.is_valid() || !later_interval.is_valid() {
|
||||
return false;
|
||||
}
|
||||
// assert_eq!(self.end_state, later_interval.start_state);
|
||||
// assert_eq!(self.abb, later_interval.abb);
|
||||
later_interval.tick_spend_preempted += self.tick_spend_preempted + (later_interval.start_tick-self.end_tick);
|
||||
later_interval.start_tick = self.start_tick;
|
||||
later_interval.start_state = self.start_state;
|
||||
self.invaildate();
|
||||
return true;
|
||||
}
|
||||
// pub fn try_unite_with_later_interval(&mut self, later_interval : &mut Self) -> bool {
|
||||
// if self.end_state!=later_interval.start_state || self.abb!=later_interval.abb || !self.is_valid() || !later_interval.is_valid() {
|
||||
// return false;
|
||||
// }
|
||||
// // assert_eq!(self.end_state, later_interval.start_state);
|
||||
// // assert_eq!(self.abb, later_interval.abb);
|
||||
// later_interval.tick_spend_preempted += self.tick_spend_preempted + (later_interval.start_tick-self.end_tick);
|
||||
// later_interval.start_tick = self.start_tick;
|
||||
// later_interval.start_state = self.start_state;
|
||||
// self.invaildate();
|
||||
// return true;
|
||||
// }
|
||||
|
||||
pub fn get_hash_index(&self) -> (u64, u64) {
|
||||
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);
|
||||
|
||||
// ============================= Job instances
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
pub struct JobInstance {
|
||||
pub struct RTOSJob {
|
||||
pub name: String,
|
||||
pub mem_reads: Vec<(u32, u8)>,
|
||||
pub release: u64,
|
||||
@ -381,18 +225,18 @@ pub struct JobInstance {
|
||||
hash_cache: u64
|
||||
}
|
||||
|
||||
impl PartialEq for JobInstance {
|
||||
impl PartialEq for RTOSJob {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.abbs == other.abbs
|
||||
}
|
||||
}
|
||||
impl Eq for JobInstance {}
|
||||
impl Hash for JobInstance {
|
||||
impl Eq for RTOSJob {}
|
||||
impl Hash for RTOSJob {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.abbs.hash(state);
|
||||
}
|
||||
}
|
||||
impl JobInstance {
|
||||
impl RTOSJob {
|
||||
pub fn get_hash(&mut self) -> u64 {
|
||||
if self.hash_cache == 0 {
|
||||
let mut s = DefaultHasher::new();
|
||||
@ -415,7 +259,7 @@ impl JobInstance {
|
||||
// ============================= Generalized job instances
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
pub struct TaskJob {
|
||||
pub struct RTOSTask {
|
||||
pub name: String,
|
||||
pub worst_bytes: Vec<u8>,
|
||||
pub woet_ticks: u64,
|
||||
@ -424,18 +268,18 @@ pub struct TaskJob {
|
||||
hash_cache: u64
|
||||
}
|
||||
|
||||
impl PartialEq for TaskJob {
|
||||
impl PartialEq for RTOSTask {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.abbs == other.abbs
|
||||
}
|
||||
}
|
||||
impl Eq for TaskJob {}
|
||||
impl Hash for TaskJob {
|
||||
impl Eq for RTOSTask {}
|
||||
impl Hash for RTOSTask {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.abbs.hash(state);
|
||||
}
|
||||
}
|
||||
impl TaskJob {
|
||||
impl RTOSTask {
|
||||
pub fn get_hash(&mut self) -> u64 {
|
||||
if self.hash_cache == 0 {
|
||||
let mut s = DefaultHasher::new();
|
||||
@ -453,7 +297,7 @@ impl TaskJob {
|
||||
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());
|
||||
let mut ret = false;
|
||||
if other.exec_ticks > self.woet_ticks {
|
||||
@ -464,7 +308,7 @@ impl TaskJob {
|
||||
}
|
||||
ret
|
||||
}
|
||||
pub fn from_instance(input: &JobInstance) -> Self {
|
||||
pub fn from_instance(input: &RTOSJob) -> Self {
|
||||
let c = input.get_hash_cached();
|
||||
Self {
|
||||
name: input.name.clone(),
|
||||
@ -475,7 +319,7 @@ impl TaskJob {
|
||||
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![];}
|
||||
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);
|
||||
@ -485,48 +329,3 @@ impl TaskJob {
|
||||
|
||||
|
||||
// ============================= 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);
|
||||
|
@ -13,13 +13,13 @@ use libafl::{
|
||||
};
|
||||
use libafl::prelude::State;
|
||||
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 std::borrow::Cow;
|
||||
|
||||
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;
|
||||
// one isn per 2**4 ns
|
||||
@ -68,21 +68,33 @@ pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec<u8> {
|
||||
|
||||
//======================= 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)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
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_interrupt_times = Vec::new();
|
||||
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
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct InterruptShiftStage<E, EM, Z> {
|
||||
pub struct InterruptShiftStage<E, EM, Z, SYS> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
phantom: PhantomData<(E, EM, Z)>,
|
||||
phantom: PhantomData<(E, EM, Z, SYS)>,
|
||||
interrup_config: Vec<(usize,u32)>,
|
||||
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
|
||||
E: 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_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
|
||||
E: UsesState<State = Z::State>,
|
||||
EM: UsesState<State = Z::State>,
|
||||
@ -144,9 +156,10 @@ where
|
||||
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata,
|
||||
<Z::State as UsesInput>::Input: Input,
|
||||
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 {
|
||||
let _ = manager.fire(
|
||||
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
|
||||
E: 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
|
||||
EM: EventFirer,
|
||||
I: Default + Input + HasMutatorBytes,
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -236,7 +250,7 @@ where
|
||||
else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases
|
||||
let feedbackstate = match state
|
||||
.named_metadata_map()
|
||||
.get::<STGFeedbackState>("stgfeedbackstate") {
|
||||
.get::<STGFeedbackState<SYS>>("stgfeedbackstate") {
|
||||
Some(s) => s,
|
||||
Option::None => {
|
||||
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
|
||||
E: 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 ret = Vec::new();
|
||||
for (num,interval) in meta.intervals().iter().enumerate() {
|
||||
@ -466,25 +483,26 @@ static mut num_snippet_success : u64 = 0;
|
||||
|
||||
/// The default mutational stage
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct STGSnippetStage<E, EM, Z> {
|
||||
pub struct STGSnippetStage<E, EM, Z, SYS> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
phantom: PhantomData<(E, EM, Z)>,
|
||||
phantom: PhantomData<(E, EM, Z, SYS)>,
|
||||
input_addr: u32
|
||||
}
|
||||
|
||||
impl<E, EM, Z> STGSnippetStage<E, EM, Z>
|
||||
impl<E, EM, Z, SYS> STGSnippetStage<E, EM, Z, SYS>
|
||||
where
|
||||
E: UsesState<State = Z::State>,
|
||||
EM: UsesState<State = Z::State>,
|
||||
Z: Evaluator<E, EM>,
|
||||
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand,
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
pub fn new(input_addr: u32) -> Self {
|
||||
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
|
||||
E: UsesState<State = Z::State>,
|
||||
EM: UsesState<State = Z::State>,
|
||||
@ -493,9 +511,10 @@ where
|
||||
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata,
|
||||
<Z::State as UsesInput>::Input: Input,
|
||||
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 {
|
||||
let _ = manager.fire(
|
||||
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
|
||||
E: UsesState<State = Z::State>,
|
||||
EM: UsesState<State = Z::State>,
|
||||
@ -523,7 +542,8 @@ where
|
||||
Z::State: UsesInput<Input = MultipartInput<I>>,
|
||||
I: HasMutatorBytes + Default,
|
||||
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(
|
||||
&mut self,
|
||||
@ -546,7 +566,7 @@ where
|
||||
if let Some(meta) = current_case.metadata_map().get::<STGNodeMetadata>() {
|
||||
let feedbackstate = match state
|
||||
.named_metadata_map()
|
||||
.get::<STGFeedbackState>("stgfeedbackstate") {
|
||||
.get::<STGFeedbackState<SYS>>("stgfeedbackstate") {
|
||||
Some(s) => s,
|
||||
Option::None => {
|
||||
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
|
||||
E: UsesState<State = Z::State>,
|
||||
EM: UsesState<State = Z::State>,
|
||||
Z: Evaluator<E, EM>,
|
||||
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand,
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
type State = Z::State;
|
||||
}
|
@ -15,18 +15,13 @@ use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::borrow::Cow;
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::helpers::USR_ISR_SYMBOLS;
|
||||
use super::JobInstance;
|
||||
use super::target_os::TargetSystem;
|
||||
use super::RTOSJob;
|
||||
use super::{ AtomicBasicBlock, ExecInterval};
|
||||
use super::{
|
||||
CURRENT_SYSTEMSTATE_VEC,
|
||||
RawFreeRTOSSystemState,
|
||||
RefinedTCB,
|
||||
ReducedFreeRTOSSystemState,
|
||||
freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*},
|
||||
helpers::JOBS_DONE,
|
||||
};
|
||||
use crate::systemstate::target_os::SystemState;
|
||||
use crate::systemstate::target_os::*;
|
||||
|
||||
//============================= Observer
|
||||
|
||||
@ -34,94 +29,51 @@ use super::{
|
||||
/// that will get updated by the target.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[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>,
|
||||
pub last_states: HashMap<u64, ReducedFreeRTOSSystemState>,
|
||||
pub last_trace: Vec<ExecInterval>,
|
||||
pub last_reads: Vec<Vec<(u32, u8)>>,
|
||||
pub last_input: I,
|
||||
pub job_instances: Vec<JobInstance>,
|
||||
last_run: Vec<SYS::State>,
|
||||
last_states: HashMap<u64, SYS::State>,
|
||||
last_trace: Vec<ExecInterval>,
|
||||
last_reads: Vec<Vec<(u32, u8)>>,
|
||||
last_input: I,
|
||||
job_instances: Vec<RTOSJob>,
|
||||
pub do_report: bool,
|
||||
pub worst_job_instances: HashMap<String, JobInstance>,
|
||||
worst_job_instances: HashMap<String, RTOSJob>,
|
||||
pub select_task: Option<String>,
|
||||
pub success: bool,
|
||||
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
|
||||
S: UsesInput<Input = I> + HasMetadata,
|
||||
S::Input: Default,
|
||||
I: Clone,
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
#[inline]
|
||||
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
|
||||
unsafe {CURRENT_SYSTEMSTATE_VEC.clear(); }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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));}
|
||||
unsafe {
|
||||
let temp = refine_system_states(CURRENT_SYSTEMSTATE_VEC.split_off(0));
|
||||
// fix_broken_trace(&mut temp.1);
|
||||
self.last_run = temp.0.clone();
|
||||
// println!("{:?}",temp);
|
||||
let temp = states2intervals(temp.0, temp.1);
|
||||
self.last_trace = temp.0;
|
||||
self.last_reads = temp.1;
|
||||
self.last_states = temp.2;
|
||||
self.success = temp.3;
|
||||
#[cfg(feature="trace_job_response_times")]
|
||||
{
|
||||
let metadata =_state.metadata_map_mut();
|
||||
let releases = get_releases(&self.last_trace, &self.last_states);
|
||||
// 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;
|
||||
fn post_exec(&mut self, state: &mut S, _input: &I, _exit_kind: &ExitKind) -> Result<(), Error> {
|
||||
// let trace =state.metadata::<SYS::TraceData>().expect("TraceData not found");
|
||||
|
||||
// copy-paste form clock observer
|
||||
{
|
||||
let hist = metadata.get_mut::<IcHist>();
|
||||
let hist = state.metadata_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)],
|
||||
Err(_) => {
|
||||
state.add_metadata(IcHist(vec![(self.last_runtime(), timestamp)],
|
||||
(self.last_runtime(), timestamp)));
|
||||
}
|
||||
Some(v) => {
|
||||
Ok(v) => {
|
||||
v.0.push((self.last_runtime(), timestamp));
|
||||
if v.1.0 < self.last_runtime() {
|
||||
v.1 = (self.last_runtime(), timestamp);
|
||||
@ -129,25 +81,24 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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(())
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
&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]
|
||||
fn len(&self) -> usize {
|
||||
@ -155,8 +106,11 @@ impl<I> HasLen for QemuSystemStateObserver<I>
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> QemuSystemStateObserver<I>
|
||||
where I: Default {
|
||||
impl<I, SYS> QemuSystemStateObserver<I, SYS>
|
||||
where
|
||||
SYS: TargetSystem,
|
||||
for<'de2> SYS: Deserialize<'de2>,
|
||||
I: Default {
|
||||
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![]}
|
||||
}
|
||||
@ -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()})
|
||||
}
|
||||
}
|
||||
impl<I> Default for QemuSystemStateObserver<I>
|
||||
where I: Default {
|
||||
impl<I, SYS> Default for QemuSystemStateObserver<I, SYS>
|
||||
where
|
||||
SYS: TargetSystem,
|
||||
for<'de2> SYS: Deserialize<'de2>,
|
||||
I: Default {
|
||||
fn default() -> Self {
|
||||
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
|
||||
|
@ -14,7 +14,7 @@ use libafl::{
|
||||
|
||||
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
|
||||
#[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)
|
||||
/// prioritizing [`Testcase`]`s` using [`TestcaseScore`]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LongestTraceScheduler<CS> {
|
||||
pub struct LongestTraceScheduler<CS, SYS> {
|
||||
base: CS,
|
||||
skip_non_favored_prob: f64,
|
||||
phantom: PhantomData<SYS>,
|
||||
}
|
||||
|
||||
impl<CS> UsesState for LongestTraceScheduler<CS>
|
||||
impl<CS, SYS> UsesState for LongestTraceScheduler<CS, SYS>
|
||||
where
|
||||
CS: UsesState,
|
||||
{
|
||||
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
|
||||
CS: UsesState + Scheduler<CS::Input, CS::State>,
|
||||
CS::State: HasCorpus + HasMetadata + HasRand,
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
/// Add an entry to the corpus and return its index
|
||||
fn on_add(&mut self, state: &mut CS::State, idx: CorpusId) -> Result<(), Error> {
|
||||
@ -56,7 +58,7 @@ where
|
||||
.get(idx)?
|
||||
.borrow()
|
||||
.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.base.on_add(state, idx)
|
||||
}
|
||||
@ -115,10 +117,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS> LongestTraceScheduler<CS>
|
||||
impl<CS, SYS> LongestTraceScheduler<CS, SYS>
|
||||
where
|
||||
CS: UsesState + Scheduler<CS::Input, CS::State>,
|
||||
CS::State: HasCorpus + HasMetadata + HasRand,
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 {
|
||||
// Create a new top rated meta if not existing
|
||||
@ -136,6 +139,7 @@ where
|
||||
Self {
|
||||
base,
|
||||
skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
use hashbrown::HashSet;
|
||||
use libafl::inputs::Input;
|
||||
/// Feedbacks organizing SystemStates as a graph
|
||||
use libafl::SerdeAny;
|
||||
use libafl_bolts::prelude::SerdeAny;
|
||||
use libafl_bolts::ownedref::OwnedMutSlice;
|
||||
use petgraph::graph::EdgeIndex;
|
||||
use libafl::prelude::UsesInput;
|
||||
@ -11,6 +11,7 @@ use libafl::state::UsesState;
|
||||
use libafl::prelude::State;
|
||||
use libafl::schedulers::MinimizerScheduler;
|
||||
use libafl_bolts::HasRefCnt;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::path::PathBuf;
|
||||
use libafl::corpus::Testcase;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
@ -24,14 +25,15 @@ use libafl::Error;
|
||||
use hashbrown::HashMap;
|
||||
use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::target_os::SystemState;
|
||||
use super::AtomicBasicBlock;
|
||||
use super::CaptureEvent;
|
||||
use super::ExecInterval;
|
||||
use super::JobInstance;
|
||||
use super::ReducedFreeRTOSSystemState;
|
||||
use super::RTOSJob;
|
||||
use super::observers::QemuSystemStateObserver;
|
||||
use super::TaskJob;
|
||||
use super::RTOSTask;
|
||||
use petgraph::prelude::DiGraph;
|
||||
use petgraph::graph::NodeIndex;
|
||||
use petgraph::Direction;
|
||||
@ -46,19 +48,25 @@ use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use std::rc::Rc;
|
||||
use petgraph::visit::EdgeRef;
|
||||
use crate::systemstate::target_os::*;
|
||||
|
||||
use libafl::prelude::StateInitializer;
|
||||
|
||||
//============================= Data Structures
|
||||
#[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,
|
||||
}
|
||||
impl STGNode {
|
||||
impl<SYS> STGNode<SYS>
|
||||
where SYS: TargetSystem {
|
||||
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 {
|
||||
let color = match self.abb.level {
|
||||
@ -70,10 +78,10 @@ impl STGNode {
|
||||
let message = match self.abb.level {
|
||||
1 => format!("API Call"),
|
||||
2 => format!("ISR"),
|
||||
0 => format!("Task: {}",self.base.current_task.task_name),
|
||||
0 => format!("Task: {}",self.base.current_task().task_name()),
|
||||
_ => 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
|
||||
}
|
||||
@ -84,8 +92,11 @@ impl STGNode {
|
||||
s.finish()
|
||||
}
|
||||
}
|
||||
impl PartialEq for STGNode {
|
||||
fn eq(&self, other: &STGNode) -> bool {
|
||||
impl<SYS> PartialEq for STGNode<SYS>
|
||||
where
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
fn eq(&self, other: &STGNode<SYS>) -> bool {
|
||||
self.base==other.base
|
||||
}
|
||||
}
|
||||
@ -139,13 +150,17 @@ impl Hash for STGEdge {
|
||||
}
|
||||
|
||||
/// Shared Metadata for a systemstateFeedback
|
||||
#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)]
|
||||
pub struct STGFeedbackState
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[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>,
|
||||
// aggregated traces as a graph
|
||||
pub graph: DiGraph<STGNode, STGEdge>,
|
||||
systemstate_index: HashMap<u64, ReducedFreeRTOSSystemState>,
|
||||
pub graph: DiGraph<STGNode<SYS>, STGEdge>,
|
||||
systemstate_index: HashMap<u64, SYS::State>,
|
||||
pub state_abb_hash_index: HashMap<(u64, u64), NodeIndex>,
|
||||
stgnode_index: HashMap<u64, NodeIndex>,
|
||||
entrypoint: NodeIndex,
|
||||
@ -156,18 +171,24 @@ pub struct STGFeedbackState
|
||||
worst_observed_per_stg_path: HashMap<u64,u64>,
|
||||
worst_abb_exec_count: HashMap<AtomicBasicBlock, usize>,
|
||||
// Metadata about job instances
|
||||
pub worst_task_jobs: HashMap<u64, TaskJob>,
|
||||
pub worst_task_jobs: HashMap<u64, RTOSTask>,
|
||||
}
|
||||
|
||||
impl Default for STGFeedbackState {
|
||||
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();
|
||||
libafl_bolts::impl_serdeany!(STGFeedbackState<SYS: SerdeAny+TargetSystem>);
|
||||
|
||||
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_exit = exit.get_hash();
|
||||
@ -175,7 +196,7 @@ impl Default for STGFeedbackState {
|
||||
let entrypoint = graph.add_node(entry.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)]);
|
||||
|
||||
@ -196,7 +217,9 @@ impl Default for STGFeedbackState {
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for STGFeedbackState
|
||||
impl<SYS> Named for STGFeedbackState<SYS>
|
||||
where
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
@ -213,12 +236,12 @@ pub struct STGNodeMetadata {
|
||||
aggregate: u64,
|
||||
top_abb_counts: Vec<u64>,
|
||||
intervals: Vec<ExecInterval>,
|
||||
jobs: Vec<JobInstance>,
|
||||
jobs: Vec<RTOSJob>,
|
||||
indices: Vec<usize>,
|
||||
tcref: isize,
|
||||
}
|
||||
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)]
|
||||
let mut indices : Vec<_> = vec![];
|
||||
#[cfg(feature = "sched_stg_edge")]
|
||||
@ -267,7 +290,7 @@ impl STGNodeMetadata {
|
||||
&self.intervals
|
||||
}
|
||||
|
||||
pub fn jobs(&self) -> &Vec<JobInstance> {
|
||||
pub fn jobs(&self) -> &Vec<RTOSJob> {
|
||||
&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`]
|
||||
#[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>,
|
||||
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_aggregate_hash: Option<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
|
||||
dump_path: Option<PathBuf>
|
||||
last_job_trace: Option<Vec<RTOSJob>>, // only set, if it was interesting
|
||||
dump_path: Option<PathBuf>,
|
||||
_phantom_data: PhantomData<SYS>,
|
||||
}
|
||||
#[cfg(feature = "feed_stg")]
|
||||
const INTEREST_EDGE : bool = true;
|
||||
@ -425,7 +453,10 @@ fn execinterval_to_abb_instances(trace: &Vec<ExecInterval>, read_trace: &Vec<Vec
|
||||
return instance_time;
|
||||
}
|
||||
|
||||
impl StgFeedback {
|
||||
impl<SYS> StgFeedback<SYS>
|
||||
where
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
pub fn new(dump_name: Option<PathBuf>) -> Self {
|
||||
// Self {name: String::from("STGFeedback"), last_node_trace: None, last_edge_trace: None, last_intervals: None }
|
||||
let mut s = Self::default();
|
||||
@ -442,7 +473,7 @@ impl StgFeedback {
|
||||
/// newly discovered node?
|
||||
/// side effect:
|
||||
/// 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_edge_trace = vec![];
|
||||
let mut interesting = false;
|
||||
@ -453,14 +484,14 @@ impl StgFeedback {
|
||||
let mut instance_time = execinterval_to_abb_instances(trace, read_trace);
|
||||
// add all missing state+abb combinations to the graph
|
||||
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 next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) {
|
||||
// already present
|
||||
*idx
|
||||
} else {
|
||||
// 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);
|
||||
fbs.stgnode_index.insert(h_node, idx);
|
||||
fbs.state_abb_hash_index.insert(h, idx);
|
||||
@ -524,14 +555,18 @@ impl StgFeedback {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> StateInitializer<S> for StgFeedback {}
|
||||
|
||||
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for StgFeedback
|
||||
impl<S, SYS> StateInitializer<S> for StgFeedback<SYS>
|
||||
where
|
||||
S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata,
|
||||
SYS: TargetSystem,
|
||||
{}
|
||||
|
||||
impl<EM, I, OT, S, SYS> Feedback<EM, I, OT, S> for StgFeedback<SYS>
|
||||
where
|
||||
S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata + HasMetadata,
|
||||
S::Input: Default,
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_interesting(
|
||||
@ -545,7 +580,9 @@ where
|
||||
where
|
||||
<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");
|
||||
let clock_observer = observers.match_name::<QemuClockObserver>("clocktime")
|
||||
.expect("QemuClockObserver not found");
|
||||
@ -555,21 +592,22 @@ where
|
||||
let last_runtime = clock_observer.last_runtime();
|
||||
let feedbackstate = match state
|
||||
.named_metadata_map_mut()
|
||||
.get_mut::<STGFeedbackState>("stgfeedbackstate") {
|
||||
.get_mut::<STGFeedbackState<SYS>>("stgfeedbackstate") {
|
||||
Some(s) => s,
|
||||
Option::None => {
|
||||
let n=STGFeedbackState::default();
|
||||
let n=STGFeedbackState::<SYS>::default();
|
||||
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
|
||||
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")]
|
||||
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")]
|
||||
if let Some(worst_instance) = worst_target_instance {
|
||||
@ -586,17 +624,17 @@ where
|
||||
set_observer_map(&edgetrace.iter().map(|x| x.0).collect::<Vec<_>>());
|
||||
|
||||
// --------------------------------- 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()) {
|
||||
// eprintln!("Job instance already present");
|
||||
x.try_update(i.1)
|
||||
} else {
|
||||
// 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
|
||||
}
|
||||
};
|
||||
self.last_job_trace = Some(observer.job_instances.clone());
|
||||
self.last_job_trace = Some(trace.jobs().clone());
|
||||
// dbg!(&observer.job_instances);
|
||||
|
||||
{
|
||||
@ -619,11 +657,11 @@ where
|
||||
#[cfg(feature = "trace_job_response_times")]
|
||||
let tmp = {
|
||||
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();
|
||||
StgFeedback::abbs_in_exec_order(&t)
|
||||
let t = trace.intervals().iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect();
|
||||
StgFeedback::<SYS>::abbs_in_exec_order(&t)
|
||||
} else {
|
||||
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 {
|
||||
Vec::new()
|
||||
}
|
||||
@ -684,7 +722,7 @@ where
|
||||
// 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_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);
|
||||
|
||||
if let Some(dp) = &self.dump_path {
|
||||
@ -716,7 +754,9 @@ where
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Named for StgFeedback
|
||||
impl<SYS> Named for StgFeedback<SYS>
|
||||
where
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
|
@ -86,37 +86,3 @@ pub struct tskTaskControlBlock {
|
||||
pub type tskTCB = tskTaskControlBlock;
|
||||
pub type TCB_t = tskTCB;
|
||||
/*========== 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);
|
548
fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs
Normal file
548
fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs
Normal 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(¤t_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
|
||||
}
|
1154
fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs
Normal file
1154
fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs
Normal file
File diff suppressed because it is too large
Load Diff
110
fuzzers/FRET/src/systemstate/target_os/mod.rs
Normal file
110
fuzzers/FRET/src/systemstate/target_os/mod.rs
Normal 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()
|
||||
}
|
@ -23,6 +23,7 @@ use std::path::PathBuf;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::systemstate::observers::QemuSystemStateObserver;
|
||||
use crate::systemstate::target_os::TargetSystem;
|
||||
|
||||
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).
|
||||
/// It decides, if the given [`QemuClockObserver`] value of a run is interesting.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ClockTimeFeedback {
|
||||
pub struct ClockTimeFeedback<SYS> {
|
||||
exec_time: Option<Duration>,
|
||||
select_task: Option<String>,
|
||||
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
|
||||
S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata,
|
||||
<S as UsesInput>::Input: Default,
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
SYS: TargetSystem,
|
||||
{
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_interesting(
|
||||
@ -241,7 +244,7 @@ where
|
||||
#[cfg(feature="trace_job_response_times")]
|
||||
{
|
||||
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()));
|
||||
return Ok(false)
|
||||
}
|
||||
@ -274,14 +277,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for ClockTimeFeedback {
|
||||
impl<SYS> Named for ClockTimeFeedback<SYS> {
|
||||
#[inline]
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
&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.
|
||||
#[must_use]
|
||||
pub fn new(name: &'static str, select_task: Option<String>) -> Self {
|
||||
@ -289,6 +292,7 @@ impl ClockTimeFeedback {
|
||||
exec_time: None,
|
||||
select_task: select_task,
|
||||
name: Cow::from(name.to_string()),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,6 +303,7 @@ impl ClockTimeFeedback {
|
||||
exec_time: None,
|
||||
select_task: select_task.clone(),
|
||||
name: observer.name().clone(),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ use libafl::{
|
||||
Error,
|
||||
};
|
||||
|
||||
use crate::systemstate::target_os::TargetSystem;
|
||||
use crate::time::clock::QemuClockObserver;
|
||||
use crate::systemstate::FreeRTOSSystemStateMetadata;
|
||||
|
||||
use libafl::prelude::StateInitializer;
|
||||
|
||||
@ -72,8 +72,8 @@ where
|
||||
pub type LenTimeMaximizerCorpusScheduler<CS, O> =
|
||||
MinimizerScheduler<CS, MaxExecsLenFavFactor<<CS as UsesState>::State>, MapIndexesMetadata, O>;
|
||||
|
||||
pub type TimeStateMaximizerCorpusScheduler<CS, O> =
|
||||
MinimizerScheduler<CS, MaxTimeFavFactor<<CS as UsesState>::State>, FreeRTOSSystemStateMetadata, O>;
|
||||
pub type TimeStateMaximizerCorpusScheduler<CS, O, SYS> =
|
||||
MinimizerScheduler<CS, MaxTimeFavFactor<<CS as UsesState>::State>, <SYS as TargetSystem>::TraceData, O>;
|
||||
|
||||
/// Multiply the testcase size with the execution time.
|
||||
/// This favors small and quick testcases.
|
||||
|
@ -313,7 +313,6 @@ where
|
||||
}
|
||||
|
||||
let mut ret = None;
|
||||
let mut last = current_time();
|
||||
let monitor_timeout = STATS_TIMEOUT_DEFAULT;
|
||||
|
||||
let starttime = std::time::Instant::now();
|
||||
@ -349,7 +348,6 @@ where
|
||||
time: std::time::Instant
|
||||
) -> Result<CorpusId, Error> {
|
||||
let mut ret = None;
|
||||
let mut last = current_time();
|
||||
let monitor_timeout = STATS_TIMEOUT_DEFAULT;
|
||||
|
||||
while std::time::Instant::now() < time {
|
||||
@ -364,7 +362,7 @@ where
|
||||
|
||||
if ret.is_none() {
|
||||
eprintln!("Warning: fuzzing loop ended with no last element");
|
||||
ret = Some(crate::corpus::CorpusId(0));
|
||||
ret = Some(CorpusId(0));
|
||||
}
|
||||
Ok(ret.unwrap())
|
||||
}
|
||||
|
@ -181,6 +181,7 @@ pub fn generate(
|
||||
.allowlist_function("vm_start")
|
||||
.allowlist_function("qemu_main_loop")
|
||||
.allowlist_function("qemu_cleanup")
|
||||
.allowlist_function("icount_get_raw")
|
||||
.blocklist_function("main_loop_wait") // bindgen issue #1313
|
||||
.blocklist_type("siginfo_t")
|
||||
.raw_line("use libc::siginfo_t;")
|
||||
|
Loading…
x
Reference in New Issue
Block a user