refactoring

This commit is contained in:
Alwin Berger 2025-01-27 13:56:43 +01:00
parent cc2a2e6422
commit f7e61665be
19 changed files with 487 additions and 1062 deletions

View File

@ -1,6 +1,6 @@
BDIR=remote
plot () {
[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/$BDIR/${1}${2}_all.png ] && Rscript plot_multi.r $BDIR/timedump ${1}${2} ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/$BDIR
[ ! -f ../benchmark/$BDIR/${1}${2}_all.png ] && Rscript plot_multi.r $BDIR/timedump ${1}${2} ../benchmark/$BDIR
}
# Only bytes

View File

@ -24,5 +24,5 @@ do
# fi
done < <(find ./remote/timedump -maxdepth 2 -type 'f' -iregex '.*\.case')
# echo "${PLOTS[@]}"
snakemake -c 6 --keep-incomplete "${PLOTS[@]}"
echo "${PLOTS[@]}"
snakemake -c 6 --rerun-incomplete --keep-incomplete "${PLOTS[@]}"

View File

@ -16,7 +16,7 @@ if (length(args)==0) {
target="waters"
#target="waters_int"
#target="watersv2_int"
outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/"
outputpath="../benchmark"
#MY_SELECTION <- c('state', 'afl', 'graph', 'random')
SAVE_FILE=TRUE
} else {
@ -39,7 +39,7 @@ if (is.null(worst_case)) {
#MY_COLORS=c("green","blue","red", "orange", "pink", "black")
MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown")
BENCHDIR=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s",runtype)
BENCHDIR=sprintf("../benchmark/%s",runtype)
BASENAMES=Filter(function(x) x!="" && substr(x,1,1)!='.',list.dirs(BENCHDIR,full.names=FALSE))
PATTERNS="%s#[0-9]*.time$"
#RIBBON='sd'

View File

@ -77,7 +77,7 @@ pub fn get_target_symbols(elf: &EasyElf) -> HashMap<&'static str, GuestAddr> {
}
pub fn get_target_ranges(
elf: &EasyElf,
_elf: &EasyElf,
symbols: &HashMap<&'static str, GuestAddr>,
) -> HashMap<&'static str, std::ops::Range<GuestAddr>> {
let mut ranges = HashMap::new();
@ -91,7 +91,5 @@ pub fn get_target_ranges(
symbols["__API_CODE_START__"]..symbols["__API_CODE_END__"],
);
crate::systemstate::target_os::freertos::config::add_target_ranges(elf, symbols, &mut ranges);
ranges
}

View File

@ -15,7 +15,7 @@ elf::EasyElf, emu::Emulator, modules::{edges::{self}, EdgeCoverageModule, Filter
use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND};
use rand::{SeedableRng, StdRng, Rng};
use crate::{
config::{get_target_ranges, get_target_symbols}, 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::{config::get_range_groups, qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{
config::{get_target_ranges, get_target_symbols}, systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, input_bytes_to_interrupt_times, load_symbol, try_load_symbol}, mutational::{InterruptShiftStage, STGSnippetStage}, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{config::get_range_groups, 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}
}
};
@ -322,7 +322,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
};
// Create an observation channel to keep track of the execution time
let clock_time_observer = QemuClockObserver::new("clocktime", if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None} );
let clock_time_observer = QemuClockObserver::new("clocktime"); // if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None}
// Create an observation channel using the coverage map
#[cfg(feature = "observe_edges")]
@ -343,14 +343,11 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
addr_of_mut!(MAX_STG_NUM)
)}.track_indices();
#[cfg(feature = "observe_systemstate")]
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::<FreeRTOSSystem>::new_with_observer(&clock_time_observer, &cli.select_task)
ClockTimeFeedback::<FreeRTOSSystem>::new_with_observer(&clock_time_observer, &cli.select_task, if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None})
);
#[cfg(feature = "feed_genetic")]
let mut feedback = feedback_or!(
@ -373,12 +370,12 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
#[cfg(all(feature = "observe_systemstate"))]
let mut feedback = feedback_or!(
feedback,
DumpSystraceFeedback::<FreeRTOSSystem>::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None}, &cli.select_task)
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::<FreeRTOSSystem>::new(if cli.dump_graph {cli.dump_name.clone()} else {None})
StgFeedback::<FreeRTOSSystem>::new(cli.select_task.clone(), if cli.dump_graph {cli.dump_name.clone()} else {None})
);
#[cfg(feature = "feed_stg_edge")]
let mut feedback = feedback_or!(
@ -441,7 +438,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
let observer_list = tuple_list!();
#[cfg(feature = "observe_systemstate")]
let observer_list = (systemstate_observer, (stg_coverage_observer, observer_list)); // must come after clock
let observer_list = (stg_coverage_observer, observer_list); // must come after clock
#[cfg(feature = "observe_edges")]
let observer_list = (edges_observer, observer_list);
let observer_list = (clock_time_observer, observer_list);

View File

@ -4,3 +4,9 @@
- ``helpers::QemuSystemStateHelper`` captures a series of ``RawFreeRTOSSystemState``
- ``observers::QemuSystemStateObserver`` divides this into ``ReducedFreeRTOSSystemState`` and ``ExecInterval``, the first contains the raw states and the second contains information about the flow between states
- ``stg::StgFeedback`` builds an stg from the intervals
## Target-specific (systemstate/target_os)
- config ``add_target_symbols`` and ``get_range_groups`` resolve important symbols
- provides a helper (e.g. ``FreeRTOSSystemStateHelper`` ) to capture the state
- collects locally into e.g. ``CURRENT_SYSTEMSTATE_VEC``
- post-processing
- replaces ``SystemTraceData`` in state metadata

View File

@ -1,179 +1,23 @@
use crate::time::clock::QemuClockObserver;
use hashbrown::HashMap;
use libafl::common::HasNamedMetadata;
use libafl::corpus::Testcase;
use libafl::{
common::HasMetadata,
executors::ExitKind,
feedbacks::Feedback,
observers::ObserversTuple,
prelude::{State, UsesInput},
state::MaybeHasClientPerfMonitor,
Error,
};
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::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, Clone)]
pub struct SystemStateFeedbackState<SYS>
where
SYS: TargetSystem,
{
name: Cow<'static, str>,
known_traces: HashMap<u64, (u64, u64, usize)>, // encounters,ticks,length
longest: Vec<SYS::State>,
}
libafl_bolts::impl_serdeany!(SystemStateFeedbackState<SYS: SerdeAny+TargetSystem>);
impl<SYS> Named for SystemStateFeedbackState<SYS>
where
SYS: TargetSystem,
{
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
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(),
}
}
}
// /// 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)]
@ -183,8 +27,6 @@ where
{
name: Cow<'static, str>,
dumpfile: Option<PathBuf>,
dump_metadata: bool,
select_task: Option<String>, // TODO: use some global config for this
phantom: PhantomData<SYS>,
}
@ -206,122 +48,25 @@ where
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where {
if self.dumpfile.is_none() {
return Ok(false);
};
match &self.dumpfile {
Some(s) => {
let trace = state
.metadata::<SYS::TraceData>()
.expect("TraceData not found");
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) = 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<_> = 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 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| {
x.sort_by_key(|y| y.abb.as_ref().unwrap().start);
x.chunk_by(|y, z| {
y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start
})
.for_each(|y| {
match ret.get_mut(&y[0].get_task_name_unchecked()) {
Option::None => {
ret.insert(
y[0].get_task_name_unchecked(),
HashMap::from([(
y[0].abb.as_ref().unwrap().start,
(
y.len(),
y.iter().filter(|x| x.is_abb_end()).count(),
y.iter().map(|z| z.get_exec_time()).sum::<_>(),
),
)]),
);
}
Some(x) => {
x.insert(
y[0].abb.as_ref().unwrap().start,
(
y.len(),
y.iter().filter(|x| x.is_abb_end()).count(),
y.iter().map(|z| z.get_exec_time()).sum(),
),
);
}
}
});
});
// dbg!(&ret);
ret
} else {
HashMap::new()
};
std::fs::write(
s,
ron::to_string(&(
&trace,
per_task_metadata,
))
ron::to_string(trace)
.expect("Error serializing hashmap"),
)
.expect("Can not dump to file");
self.dumpfile = None
}
Option::None => {
if self.dump_metadata {
println!("{:?}\n{:?}", trace, names);
}
()
}
};
// if self.dump_metadata {self.last_trace=Some(observer.last_trace.clone());}
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(());
}
// let a = self.last_trace.take();
// match a {
// Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)),
// 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> {
Ok(())
}
}
impl<SYS> Named for DumpSystraceFeedback<SYS>
@ -344,29 +89,15 @@ where
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>, select_task: &Option<String>) -> Self {
pub fn with_dump(dumpfile: Option<PathBuf>) -> Self {
Self {
name: Cow::from("Dumpsystemstate".to_string()),
dumpfile: dumpfile,
dump_metadata: false,
phantom: PhantomData,
select_task: select_task.clone(),
}
}
#[allow(unused)]
pub fn metadata_only() -> Self {
Self {
name: Cow::from("Dumpsystemstate".to_string()),
dumpfile: None,
dump_metadata: true,
phantom: PhantomData,
select_task: None,
}
}
}
@ -386,58 +117,43 @@ impl<S, SYS> StateInitializer<S> for SystraceErrorFeedback<SYS> where SYS: Targe
impl<EM, I, OT, S, SYS> Feedback<EM, I, OT, S> for SystraceErrorFeedback<SYS>
where
S: State + UsesInput + MaybeHasClientPerfMonitor,
S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata,
EM: EventFirer<State = S>,
OT: ObserversTuple<I, S>,
SYS: TargetSystem,
{
fn is_interesting(
&mut self,
_state: &mut S,
state: &mut S,
_manager: &mut EM,
_input: &I,
observers: &OT,
_observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where {
#[cfg(feature = "trace_stg")]
{
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 is_err {
let need_to_debug = state
.metadata::<SYS::TraceData>()
.expect("TraceData not found")
.need_to_debug();
if need_to_debug {
self.max_reports = Some(m - 1);
}
return Ok(self.dump_case && need_to_debug);
} else {
return Ok(false);
}
return Ok(self.dump_case && is_err);
}
#[cfg(not(feature = "trace_stg"))]
{
return 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> {
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> {
Ok(())
}
}
impl<SYS> Named for SystraceErrorFeedback<SYS>

View File

@ -1,29 +1,18 @@
use std::ops::Range;
use hashbrown::HashMap;
use hashbrown::HashSet;
use libafl::prelude::ExitKind;
use libafl::prelude::UsesInput;
use libafl_qemu::elf::EasyElf;
use libafl_qemu::modules::NopAddressFilter;
use libafl_qemu::modules::NopPageFilter;
use libafl_qemu::read_user_reg_unchecked;
use libafl_qemu::GuestAddr;
use libafl_qemu::GuestPhysAddr;
use libafl_qemu::QemuHooks;
use libafl_qemu::Hook;
use libafl_qemu::modules::{EmulatorModule, EmulatorModuleTuple};
use libafl_qemu::sys::TCGTemp;
use libafl_qemu::qemu::MemAccessInfo;
use super::CaptureEvent;
use libafl_qemu::EmulatorModules;
use libafl::prelude::ObserversTuple;
use libafl_bolts::prelude::{SerdeAny, SerdeAnyMap};
use libafl_qemu::{elf::EasyElf, read_user_reg_unchecked, GuestAddr, GuestPhysAddr};
use std::ops::Range;
use std::cmp::min;
use crate::{fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, time::clock::QEMU_ISNS_PER_USEC};
use super::{
target_os::{SystemTraceData, TargetSystem},
ExecInterval,
};
//============================= API symbols
/// Read ELF program headers to resolve physical load addresses.
fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
let ret;
@ -42,12 +31,16 @@ pub fn load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> Gues
try_load_symbol(elf, symbol, do_translation).expect(&format!("Symbol {} not found", symbol))
}
/// Lookup a symbol in the ELF file, optionally resolve segment offsets
pub fn try_load_symbol(elf: &EasyElf, symbol: &str, do_translation: bool) -> Option<GuestAddr> {
let ret = elf
.resolve_symbol(symbol, 0);
let ret = elf.resolve_symbol(symbol, 0);
if do_translation {
Option::map_or(ret, None, |x| Some(virt2phys(x as GuestPhysAddr,&elf) as GuestAddr))
} else {ret}
Option::map_or(ret, None, |x| {
Some(virt2phys(x as GuestPhysAddr, &elf) as GuestAddr)
})
} else {
ret
}
}
/// Try looking up the address range of a function in the ELF file
@ -82,8 +75,20 @@ pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option<std::ops::Range
return None;
}
/// Check if an address is in any of the ranges
pub fn in_any_range<'a>(
ranges: &'a Vec<(String, Range<u32>)>,
addr: GuestAddr,
) -> Option<&'a std::ops::Range<GuestAddr>> {
for (_, r) in ranges {
if r.contains(&addr) {
return Some(r);
}
}
return None;
}
//============================= Utility functions
//============================= QEMU related utility functions
pub fn get_icount(emulator: &libafl_qemu::Qemu) -> u64 {
unsafe {
@ -97,29 +102,127 @@ pub fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 {
}
}
pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec<u32> {
let len = buf.len();
let mut start_tick;
let mut ret = Vec::with_capacity(min(DO_NUM_INTERRUPT, len/4));
for i in 0..DO_NUM_INTERRUPT {
let mut buf4b : [u8; 4] = [0,0,0,0];
if len >= (i+1)*4 {
for j in 0usize..4usize {
buf4b[j]=buf[i*4+j];
}
start_tick = u32::from_le_bytes(buf4b);
if start_tick < FIRST_INT {start_tick=0;}
ret.push(start_tick);
} else {break;}
}
ret.sort_unstable();
// obey the minimum inter arrival time while maintaining the sort
for i in 0..ret.len() {
if ret[i]==0 {continue;}
for j in i+1..ret.len()-1 {
if ret[j]-ret[i] < config.1 * QEMU_ISNS_PER_USEC {
// ret[j] = u32::saturating_add(ret[i],config.1 * QEMU_ISNS_PER_USEC);
ret[j] = 0; // remove the interrupt
ret.sort_unstable();
} else {break;}
}
}
ret
}
pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec<u8> {
let mut ret = Vec::with_capacity(interrupt_times.len()*4);
for i in interrupt_times {
ret.extend(u32::to_le_bytes(*i));
}
ret
}
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 {
// if 0xFFFFFFF0/1 0xFFFFFFF8/9 -> "main stack" MSP
let mut buf = [0u8; 4];
let sp : GuestAddr = if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF0 { // PSP
let sp: GuestAddr = if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF0 {
// PSP
read_user_reg_unchecked(emu) as u32
} else {
emu.read_reg(13).unwrap()
};
let ret_pc = sp + 0x18; // https://developer.arm.com/documentation/dui0552/a/the-cortex-m3-processor/exception-model/exception-entry-and-return
emu.read_mem(ret_pc, buf.as_mut_slice());
emu.read_mem(ret_pc, buf.as_mut_slice())
.expect("Failed to read return address");
return u32::from_le_bytes(buf);
// elseif 0xfffffffc/d
}} else {
} else {
return lr;
};
}
pub fn in_any_range<'a>(ranges: &'a Vec<(String, Range<u32>)>, addr : GuestAddr) -> Option<&'a std::ops::Range<GuestAddr>> {
for (_,r) in ranges {
if r.contains(&addr) {return Some(r);}
//============================= Tracing related utility functions
pub fn metadata_insert_or_update_get<T>(
metadata: &mut SerdeAnyMap,
default: impl FnOnce() -> T,
update: impl FnOnce(&mut T),
) -> &mut T
where
T: SerdeAny,
{
if metadata.contains::<T>() {
let v = metadata.get_mut::<T>().unwrap();
update(v);
return v;
} else {
return metadata.get_or_insert_with(default);
}
return None;
}
/// Build an ABB-profile from a stretch of intervals
/// returns mapping: task_name -> (abb_addr -> (interval_count, exec_count, exec_time))
pub fn abb_profile(
mut intervals: Vec<ExecInterval>,
) -> HashMap<String, HashMap<u32, (usize, usize, u64)>> {
let mut ret: HashMap<String, HashMap<u32, (usize, usize, u64)>> = HashMap::new();
intervals.sort_by_key(|x| x.get_task_name_unchecked());
intervals
.chunk_by_mut(|x, y| x.get_task_name_unchecked() == y.get_task_name_unchecked())
.for_each(|x| {
x.sort_by_key(|y| y.abb.as_ref().unwrap().start);
x.chunk_by(|y, z| y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start)
.for_each(|y| match ret.get_mut(&y[0].get_task_name_unchecked()) {
Option::None => {
ret.insert(
y[0].get_task_name_unchecked(),
HashMap::from([(
y[0].abb.as_ref().unwrap().start,
(
y.len(),
y.iter().filter(|x| x.is_abb_end()).count(),
y.iter().map(|z| z.get_exec_time()).sum::<_>(),
),
)]),
);
}
Some(x) => {
x.insert(
y[0].abb.as_ref().unwrap().start,
(
y.len(),
y.iter().filter(|x| x.is_abb_end()).count(),
y.iter().map(|z| z.get_exec_time()).sum(),
),
);
}
});
});
ret
}
pub fn unmut<T>(x: &mut T) -> &T {
&(*x)
}

View File

@ -11,7 +11,6 @@ use serde::{Deserialize, Serialize};
use itertools::Itertools;
pub mod helpers;
pub mod observers;
pub mod feedbacks;
pub mod schedulers;
pub mod stg;
@ -254,6 +253,9 @@ impl RTOSJob {
self.hash_cache
}
}
pub fn response_time(&self) -> u64 {
self.response-self.release
}
}
// ============================= Generalized job instances

View File

@ -19,7 +19,7 @@ use std::borrow::Cow;
use simple_moving_average::SMA;
use super::{stg::{STGEdge, STGNode}, target_os::TargetSystem, RTOSJob};
use super::{helpers::{input_bytes_to_interrupt_times, interrupt_times_to_input_bytes}, 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
@ -27,43 +27,6 @@ use super::{stg::{STGEdge, STGNode}, target_os::TargetSystem, RTOSJob};
// 1ms = 62500 insn
// 1us = 62.5 insn
pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec<u32> {
let len = buf.len();
let mut start_tick;
let mut ret = Vec::with_capacity(min(DO_NUM_INTERRUPT, len/4));
for i in 0..DO_NUM_INTERRUPT {
let mut buf4b : [u8; 4] = [0,0,0,0];
if len >= (i+1)*4 {
for j in 0usize..4usize {
buf4b[j]=buf[i*4+j];
}
start_tick = u32::from_le_bytes(buf4b);
if start_tick < FIRST_INT {start_tick=0;}
ret.push(start_tick);
} else {break;}
}
ret.sort_unstable();
// obey the minimum inter arrival time while maintaining the sort
for i in 0..ret.len() {
if ret[i]==0 {continue;}
for j in i+1..ret.len()-1 {
if ret[j]-ret[i] < config.1 as u32 * QEMU_ISNS_PER_USEC {
// ret[j] = u32::saturating_add(ret[i],config.1 * QEMU_ISNS_PER_USEC);
ret[j] = 0; // remove the interrupt
ret.sort_unstable();
} else {break;}
}
}
ret
}
pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec<u8> {
let mut ret = Vec::with_capacity(interrupt_times.len()*4);
for i in interrupt_times {
ret.extend(u32::to_le_bytes(*i));
}
ret
}
//======================= Custom mutator
@ -249,10 +212,9 @@ where
}
else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases
let feedbackstate = match state
.named_metadata_map()
.get::<STGFeedbackState<SYS>>("stgfeedbackstate") {
Some(s) => s,
Option::None => {
.metadata::<STGFeedbackState<SYS>>() {
Ok(s) => s,
Error => {
panic!("STGfeedbackstate not visible")
}
};
@ -565,10 +527,9 @@ where
// eprintln!("Run mutator {}", current_case.metadata_map().get::<STGNodeMetadata>().is_some());
if let Some(meta) = current_case.metadata_map().get::<STGNodeMetadata>() {
let feedbackstate = match state
.named_metadata_map()
.get::<STGFeedbackState<SYS>>("stgfeedbackstate") {
Some(s) => s,
Option::None => {
.metadata::<STGFeedbackState<SYS>>() {
Ok(s) => s,
Error => {
panic!("STGfeedbackstate not visible")
}
};

View File

@ -1,168 +0,0 @@
use libafl::prelude::ExitKind;
use libafl::prelude::UsesInput;
use libafl::HasMetadata;
use libafl_bolts::HasLen;
use libafl_bolts::Named;
use libafl::Error;
use libafl::observers::Observer;
use serde::{Deserialize, Serialize};
use hashbrown::{HashMap, HashSet};
use crate::systemstate::CaptureEvent;
use crate::time::clock::IcHist;
use crate::time::clock::FUZZ_START_TIMESTAMP;
use std::time::SystemTime;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::borrow::Cow;
use itertools::Itertools;
use super::target_os::TargetSystem;
use super::RTOSJob;
use super::{ AtomicBasicBlock, ExecInterval};
use crate::systemstate::target_os::SystemState;
use crate::systemstate::target_os::*;
//============================= Observer
/// The Qemusystemstate Observer retrieves the systemstate
/// that will get updated by the target.
#[derive(Serialize, Deserialize, Debug)]
#[allow(clippy::unsafe_derive_deserialize)]
pub struct QemuSystemStateObserver<I, SYS>
where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
{
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,
worst_job_instances: HashMap<String, RTOSJob>,
pub select_task: Option<String>,
pub success: bool,
name: Cow<'static, str>,
}
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> {
Ok(())
}
#[inline]
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 = state.metadata_mut::<IcHist>();
let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
match hist {
Err(_) => {
state.add_metadata(IcHist(vec![(self.last_runtime(), timestamp)],
(self.last_runtime(), timestamp)));
}
Ok(v) => {
v.0.push((self.last_runtime(), timestamp));
if v.1.0 < self.last_runtime() {
v.1 = (self.last_runtime(), timestamp);
}
}
}
}
Ok(())
}
}
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, SYS> HasLen for QemuSystemStateObserver<I, SYS>
where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
{
#[inline]
fn len(&self) -> usize {
self.last_run.len()
}
}
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![]}
}
pub fn last_runtime(&self) -> u64 {
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, SYS> Default for QemuSystemStateObserver<I, SYS>
where
SYS: TargetSystem,
for<'de2> SYS: Deserialize<'de2>,
I: Default {
fn default() -> Self {
Self::new(&None)
}
}
// /// restore the isr/api begin/end invariant
// fn fix_broken_trace(meta: &mut Vec<(u64, CaptureEvent, String, (Option<u32>, Option<u32>))>) {
// for i in meta.iter_mut() {
// if i.1 == CaptureEvent::APIStart && i.2.ends_with("FromISR") {
// i.1 = CaptureEvent::ISREnd;
// i.2 = "ISR_0_Handler".to_string();
// }
// }
// }
// /// invalidate subsequent intervals of equal states where an ISREnd follows an ISRStart. If the interrupt had no effect on the system we, are not interested.
// fn invalidate_ineffective_isr(trace: &mut Vec<ExecInterval>) {
// let mut i = 0;
// while i < trace.len() - 1 {
// if trace[i].is_valid() &&
// matches!(trace[i].start_capture.0, CaptureEvent::ISRStart) && matches!(trace[i].end_capture.0, CaptureEvent::ISREnd) &&
// trace[i].start_capture.1 == trace[i].end_capture.1 && trace[i].start_state == trace[i].end_state
// {
// trace[i].invaildate();
// }
// }
// }
// /// merge a sequence of intervals of the same state+abb. jump over all invalid blocks.
// fn merge_subsequent_abbs(trace: &mut Vec<ExecInterval>) {
// let mut i = 1;
// let mut lst_valid=0;
// while i < trace.len() - 1 {
// if trace[i].is_valid() {
// let mut temp = trace[i].clone();
// trace[lst_valid].try_unite_with_later_interval(&mut temp);
// trace[i] = temp;
// lst_valid = i;
// }
// }
// }

View File

@ -4,6 +4,7 @@ use libafl::inputs::Input;
/// Feedbacks organizing SystemStates as a graph
use libafl_bolts::prelude::SerdeAny;
use libafl_bolts::ownedref::OwnedMutSlice;
use log::Metadata;
use petgraph::graph::EdgeIndex;
use libafl::prelude::UsesInput;
use libafl::common::HasNamedMetadata;
@ -27,12 +28,12 @@ use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use super::helpers::metadata_insert_or_update_get;
use super::target_os::SystemState;
use super::AtomicBasicBlock;
use super::CaptureEvent;
use super::ExecInterval;
use super::RTOSJob;
use super::observers::QemuSystemStateObserver;
use super::RTOSTask;
use petgraph::prelude::DiGraph;
use petgraph::graph::NodeIndex;
@ -383,6 +384,7 @@ where
last_top_abb_hashes: Option<Vec<u64>>, // only set, if it was interesting
last_job_trace: Option<Vec<RTOSJob>>, // only set, if it was interesting
dump_path: Option<PathBuf>,
select_task: Option<String>,
_phantom_data: PhantomData<SYS>,
}
#[cfg(feature = "feed_stg")]
@ -457,10 +459,12 @@ impl<SYS> StgFeedback<SYS>
where
SYS: TargetSystem,
{
pub fn new(dump_name: Option<PathBuf>) -> Self {
pub fn new(select_task: Option<String>, 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();
unsafe{libafl_bolts::prelude::RegistryBuilder::register::<STGFeedbackState<SYS>>()};
s.dump_path = dump_name.map(|x| x.with_extension("stgsize"));
s.select_task = select_task;
s
}
@ -582,40 +586,31 @@ where
{
// 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");
#[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 = match state
.named_metadata_map_mut()
.get_mut::<STGFeedbackState<SYS>>("stgfeedbackstate") {
Some(s) => s,
Option::None => {
let n=STGFeedbackState::<SYS>::default();
unsafe{libafl_bolts::prelude::RegistryBuilder::register::<STGFeedbackState<SYS>>()};
state.named_metadata_map_mut().insert("stgfeedbackstate",n);
state.named_metadata_map_mut().get_mut::<STGFeedbackState<SYS>>("stgfeedbackstate").unwrap()
}
};
#[cfg(feature = "trace_job_response_times")]
let worst_jobs = trace.worst_jobs_per_task_by_response_time();
#[cfg(feature = "trace_job_response_times")]
let worst_select_job = if let Some(t) = self.select_task.as_ref() {worst_jobs.get(t)} else {None};
#[cfg(feature = "trace_job_response_times")]
let last_runtime = if let Some(t) = self.select_task.as_ref() {worst_select_job.map_or(0, |x| x.response_time())} else {last_runtime};
let feedbackstate = state.metadata_map_mut().get_or_insert_with(||{
STGFeedbackState::<SYS>::default()
});
// --------------------------------- Update STG
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 = 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 {
if let Some(worst_instance) = worst_select_job {
edgetrace = edgetrace.into_iter().filter(|x| x.1 <= worst_instance.response && x.1 >= worst_instance.release ).collect();
nodetrace = nodetrace.into_iter().filter(|x| x.1 <= worst_instance.response && x.1 >= worst_instance.release ).collect();
} else {
if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here
if self.select_task.is_some() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here
edgetrace = Vec::new();
nodetrace = Vec::new();
}
@ -625,7 +620,7 @@ where
set_observer_map(&edgetrace.iter().map(|x| x.0).collect::<Vec<_>>());
// --------------------------------- Update job instances
for i in trace.worst_jobs_per_task_by_time().iter() {
for i in worst_jobs.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)
@ -657,11 +652,11 @@ where
let tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace);
#[cfg(feature = "trace_job_response_times")]
let tmp = {
if let Some(worst_instance) = worst_target_instance {
if let Some(worst_instance) = worst_select_job {
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
if self.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here
StgFeedback::<SYS>::abbs_in_exec_order(trace.intervals())
} else {
Vec::new()

View File

@ -3,9 +3,10 @@ use libafl_qemu::{elf::EasyElf, GuestAddr};
use crate::{
fuzzer::get_all_fn_symbol_ranges,
systemstate::{self, helpers::{get_function_range, load_symbol}, target_os::freertos::ISR_SYMBOLS},
systemstate::{helpers::{get_function_range, load_symbol}, target_os::freertos::ISR_SYMBOLS},
};
// Add os-specific symbols to the target symbol hashmap
pub fn add_target_symbols(elf: &EasyElf, addrs: &mut HashMap<&'static str, GuestAddr>) {
// required for system state observation
addrs.insert("pxCurrentTCB", load_symbol(&elf, "pxCurrentTCB", false)); // loads to the address specified in elf, without respecting program headers
@ -35,50 +36,8 @@ pub fn add_target_symbols(elf: &EasyElf, addrs: &mut HashMap<&'static str, Guest
);
}
pub fn add_target_ranges(
elf: &EasyElf,
symbols: &HashMap<&'static str, GuestAddr>,
ranges: &mut HashMap<&'static str, std::ops::Range<GuestAddr>>,
) {
let api_range = ranges.get("API_CODE").unwrap();
let app_range = ranges.get("APP_CODE").unwrap();
let mut api_fn_ranges = get_all_fn_symbol_ranges(&elf, api_range.clone());
let mut app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone());
// Regular ISR functions, remove from API functions
let mut isr_fn_ranges: HashMap<String, std::ops::Range<GuestAddr>> = ISR_SYMBOLS
.iter()
.filter_map(|x| {
api_fn_ranges
.remove(&x.to_string())
.map(|y| (x.to_string(), y.clone()))
})
.collect();
// User-defined ISR functions, remove from APP functions
ISR_SYMBOLS.iter().for_each(|x| {
let _ = (app_fn_ranges
.remove(&x.to_string())
.map(|y| (x.to_string(), y.clone())))
.map(|z| isr_fn_ranges.insert(z.0, z.1));
});
// Add the rest of the ISR function, if not already found
for i in ISR_SYMBOLS {
if isr_fn_ranges.get(&i.to_string()).is_none() {
if let Some(fr) = get_function_range(&elf, i) {
isr_fn_ranges.insert(i.to_string(), fr);
}
}
}
let mut groups = HashMap::new();
groups.insert("API_FN", api_fn_ranges);
groups.insert("APP_FN", app_fn_ranges);
groups.insert("ISR_FN", isr_fn_ranges);
}
// Group functions into api, app and isr functions
pub fn get_range_groups(
elf: &EasyElf,
_addrs: &HashMap<&'static str, GuestAddr>,

View File

@ -453,10 +453,11 @@ pub struct FreeRTOSTraceMetadata
trace_length: usize,
indices: Vec<usize>, // Hashed enumeration of States
tcref: isize,
need_to_debug: bool,
}
impl FreeRTOSTraceMetadata
{
pub fn new(trace: Vec<<FreeRTOSTraceMetadata as SystemTraceData>::State>, intervals: Vec<ExecInterval>, mem_reads: Vec<Vec<(u32, u8)>>, jobs: Vec<RTOSJob>) -> Self {
pub fn new(trace: Vec<<FreeRTOSTraceMetadata as SystemTraceData>::State>, intervals: Vec<ExecInterval>, mem_reads: Vec<Vec<(u32, u8)>>, jobs: Vec<RTOSJob>, need_to_debug: bool) -> Self {
let hashes : Vec<_> = trace
.iter()
.map(|x| compute_hash(&x) as usize)
@ -470,6 +471,7 @@ impl FreeRTOSTraceMetadata
jobs: jobs,
indices: hashes,
tcref: 0,
need_to_debug: need_to_debug,
}
}
}
@ -512,6 +514,10 @@ impl SystemTraceData for FreeRTOSTraceMetadata
fn states_map(&self) -> &HashMap<u64, Self::State> {
&self.trace_map
}
fn need_to_debug(&self) -> bool {
self.need_to_debug
}
}
libafl_bolts::impl_serdeany!(FreeRTOSTraceMetadata);

View File

@ -13,8 +13,6 @@ use libafl_qemu::{
EmulatorModules, GuestAddr, Hook, MemAccessInfo,
};
use libafl_bolts::tuples::Map;
use crate::{fuzzer::MAX_INPUT_SIZE, systemstate::{
helpers::{get_icount, in_any_range, read_rec_return_stackframe},
target_os::{freertos::FreeRTOSStruct::*, *},
@ -159,6 +157,7 @@ where
OT: ObserversTuple<S::Input, S>,
ET: EmulatorModuleTuple<S>,
{
let mut need_to_debug = false;
if unsafe { CURRENT_SYSTEMSTATE_VEC.len() } == 0 {
eprintln!("No system states captured, aborting");
return;
@ -194,6 +193,7 @@ where
refine_system_states(unsafe { CURRENT_SYSTEMSTATE_VEC.split_off(0) });
let (intervals, mem_reads, dumped_states, success) =
states2intervals(refined_states.clone(), metadata);
need_to_debug |= !success;
#[cfg(not(feature = "trace_job_response_times"))]
let jobs = Vec::new();
#[cfg(feature = "trace_job_response_times")]
@ -201,6 +201,7 @@ where
let releases = get_releases(&intervals, &dumped_states);
let responses = unsafe { JOBS_DONE.split_off(0) };
let (job_spans, do_report) = get_release_response_pairs(&releases, &responses);
need_to_debug |= do_report;
let jobs : Vec<RTOSJob> = job_spans
.into_iter()
@ -252,7 +253,7 @@ where
.collect::<Vec<_>>();
jobs
};
_state.add_metadata(FreeRTOSTraceMetadata::new(refined_states, intervals, mem_reads, jobs));
_state.add_metadata(FreeRTOSTraceMetadata::new(refined_states, intervals, mem_reads, jobs, need_to_debug));
}
type ModuleAddressFilter = NopAddressFilter;

View File

@ -3,9 +3,7 @@ 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;
@ -13,15 +11,12 @@ use serde::{Deserialize, Serialize};
use itertools::Itertools;
use std::fmt::Debug;
use super::CaptureEvent;
use super::helpers::abb_profile;
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 {
@ -67,9 +62,51 @@ pub trait SystemTraceData: Serialize + Sized + for<'a> Deserialize<'a> + Default
})
}
#[inline]
fn worst_jobs_per_task_by_time(&self) -> HashMap<String, RTOSJob> {
fn worst_jobs_per_task_by_exec_time(&self) -> HashMap<String, RTOSJob> {
self.worst_jobs_per_task_by(&|old, x| x.exec_ticks > old.exec_ticks)
}
#[inline]
fn worst_jobs_per_task_by_response_time(&self) -> HashMap<String, RTOSJob> {
self.worst_jobs_per_task_by(&|old, x| x.response_time() > old.response_time())
}
#[inline]
/// Gives the response time of the worst job of the selected task, or 0 if the task is not found
fn wort_of_task(&self, select_task: &String) -> u64 {
self.worst_jobs_per_task_by_response_time().get(select_task).map_or(0, |job| job.response_time())
}
#[inline]
/// extract computation time spent in each task and abb
/// task_name -> (abb_addr -> (interval_count, exec_count, exec_time))
fn select_abb_profile(
&self,
select_task: Option<String>,
) -> HashMap<String, HashMap<u32, (usize, usize, u64)>> {
if let Some(select_task) = select_task.as_ref() {
// Task selected, only profile this task
if let Some(worst_instance) = self
.worst_jobs_per_task_by_response_time()
.get(select_task)
{
let t: Vec<_> = self
.intervals()
.iter()
.filter(|x| {
x.start_tick < worst_instance.response && x.end_tick > worst_instance.release
})
.cloned()
.collect();
abb_profile(t)
} else {
HashMap::new()
}
} else {
// Profile all tasks
abb_profile(self.intervals().clone())
}
}
fn need_to_debug(&self) -> bool;
}
@ -92,7 +129,7 @@ macro_rules! impl_emu_lookup {
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);
emu.read_mem(addr.into(), &mut tmp).unwrap();
std::mem::transmute::<[u8; std::mem::size_of::<$struct_name>()], $struct_name>(tmp)
}
}

View File

@ -1,29 +1,27 @@
use hashbrown::HashMap;
use libafl_bolts::Named;
use libafl::{
executors::ExitKind,
observers::Observer,
Error,
common::HasNamedMetadata,
observers::ObserversTuple, prelude::UsesInput,
common::HasNamedMetadata, executors::ExitKind, observers::Observer, observers::ObserversTuple,
prelude::UsesInput, Error,
};
use libafl_bolts::Named;
use serde::{Deserialize, Serialize};
use std::{fs::OpenOptions, io::Write};
use libafl::events::EventFirer;
use libafl::state::MaybeHasClientPerfMonitor;
use libafl::prelude::State;
use libafl::feedbacks::Feedback;
use libafl::SerdeAny;
use core::{fmt::Debug, time::Duration};
use libafl::common::HasMetadata;
use libafl::corpus::testcase::Testcase;
use core::{fmt::Debug, time::Duration};
use std::time::{SystemTime, UNIX_EPOCH};
use std::path::PathBuf;
use libafl::events::EventFirer;
use libafl::feedbacks::Feedback;
use libafl::prelude::State;
use libafl::state::MaybeHasClientPerfMonitor;
use libafl_bolts::tuples::MatchNameRef;
use libafl::SerdeAny;
use std::borrow::Cow;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::systemstate::observers::QemuSystemStateObserver;
use crate::systemstate::helpers::metadata_insert_or_update_get;
use crate::systemstate::target_os::TargetSystem;
use crate::systemstate::target_os::SystemTraceData;
pub static mut FUZZ_START_TIMESTAMP: SystemTime = UNIX_EPOCH;
@ -34,8 +32,10 @@ pub const _QEMU_NS_PER_ISN : u32 = 1 << QEMU_ICOUNT_SHIFT;
pub const _TARGET_SYSCLK_FREQ: u32 = 25 * 1000 * 1000;
pub const _TARGET_MHZ_PER_MIPS: f32 = _TARGET_SYSCLK_FREQ as f32 / QEMU_ISNS_PER_SEC as f32;
pub const _TARGET_MIPS_PER_MHZ: f32 = QEMU_ISNS_PER_SEC as f32 / _TARGET_SYSCLK_FREQ as f32;
pub const _TARGET_SYSCLK_PER_QEMU_SEC : u32 = (_TARGET_SYSCLK_FREQ as f32 * _TARGET_MIPS_PER_MHZ) as u32;
pub const _QEMU_SYSCLK_PER_TARGET_SEC : u32 = (_TARGET_SYSCLK_FREQ as f32 * _TARGET_MHZ_PER_MIPS) as u32;
pub const _TARGET_SYSCLK_PER_QEMU_SEC: u32 =
(_TARGET_SYSCLK_FREQ as f32 * _TARGET_MIPS_PER_MHZ) as u32;
pub const _QEMU_SYSCLK_PER_TARGET_SEC: u32 =
(_TARGET_SYSCLK_FREQ as f32 * _TARGET_MHZ_PER_MIPS) as u32;
pub fn tick_to_time(ticks: u64) -> Duration {
Duration::from_nanos((ticks << QEMU_ICOUNT_SHIFT) as u64)
@ -46,7 +46,6 @@ pub fn tick_to_ms(ticks: u64) -> f32 {
}
use libafl::prelude::StateInitializer;
//========== Metadata
#[derive(Debug, SerdeAny, Serialize, Deserialize)]
pub struct QemuIcountMetadata {
@ -68,16 +67,14 @@ pub struct MaxIcountMetadata {
// }
// }
impl Named for MaxIcountMetadata
{
impl Named for MaxIcountMetadata {
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl MaxIcountMetadata
{
impl MaxIcountMetadata {
/// Create new `MaxIcountMetadata`
#[must_use]
pub fn new(name: &'static str) -> Self {
@ -95,7 +92,7 @@ impl Default for MaxIcountMetadata {
}
/// A piece of metadata tracking all icounts
#[derive(Debug, SerdeAny, Serialize, Deserialize)]
#[derive(Debug, Default, SerdeAny, Serialize, Deserialize)]
pub struct IcHist(pub Vec<(u64, u128)>, pub (u64, u128));
//========== Observer
@ -106,18 +103,16 @@ pub struct QemuClockObserver {
name: Cow<'static, str>,
start_tick: u64,
end_tick: u64,
dump_path: Option<PathBuf>
}
impl QemuClockObserver {
/// Creates a new [`QemuClockObserver`] with the given name.
#[must_use]
pub fn new(name: &'static str, dump_path: Option<PathBuf>) -> Self {
pub fn new(name: &'static str) -> Self {
Self {
name: Cow::from(name),
start_tick: 0,
end_tick: 0,
dump_path
}
}
@ -133,59 +128,28 @@ where
S: UsesInput + HasMetadata,
{
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
self.start_tick = 0;
// Only remember the pre-run ticks if presistent mode ist used
#[cfg(not(feature = "snapshot_restore"))]
unsafe {
self.start_tick = emu::icount_get_raw();
self.end_tick = self.start_tick;
}
// unsafe {
// println!("clock pre {}",emu::icount_get_raw());
// }
Ok(())
}
fn post_exec(&mut self, _state: &mut S, _input: &I, _exit_kind: &ExitKind) -> Result<(), Error> {
fn post_exec(
&mut self,
_state: &mut S,
_input: &I,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
if _exit_kind != &ExitKind::Ok {
self.start_tick = 0;
self.end_tick = 0;
return Ok(());
}
unsafe { self.end_tick = libafl_qemu::sys::icount_get_raw() };
if let Some(td) = &self.dump_path {
// println!("clock post {}", self.end_tick);
// println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick);
let metadata =_state.metadata_map_mut();
let hist = metadata.get_mut::<IcHist>();
let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
match hist {
Option::None => {
#[cfg(not(feature="trace_job_response_times"))]
{
metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)],
(self.end_tick - self.start_tick, timestamp)));
}
#[cfg(feature="trace_job_response_times")]
metadata.insert(IcHist(vec![],(0,timestamp)));
}
Some(v) => {
#[cfg(not(feature="trace_job_response_times"))]
{
v.0.push((self.end_tick - self.start_tick, timestamp));
if v.1.0 < self.end_tick-self.start_tick {
v.1 = (self.end_tick - self.start_tick, timestamp);
}
}
if v.0.len() >= 100 {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.append(true)
.open(td).expect("Could not open timedump");
let newv : Vec<(u64, u128)> = Vec::with_capacity(110);
for i in std::mem::replace(&mut v.0, newv).into_iter() {
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
}
}
}
}
}
Ok(())
}
}
@ -203,7 +167,6 @@ impl Default for QemuClockObserver {
name: Cow::from(String::from("clock")),
start_tick: 0,
end_tick: 0,
dump_path: None
}
}
}
@ -211,12 +174,12 @@ impl Default for QemuClockObserver {
//========== Feedback
/// Nop feedback that annotates execution time in the new testcase, if any
/// 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<SYS> {
exec_time: Option<Duration>,
select_task: Option<String>,
name: Cow<'static, str>,
dump_path: Option<PathBuf>,
phantom: std::marker::PhantomData<SYS>,
}
@ -233,25 +196,71 @@ where
#[allow(clippy::wrong_self_convention)]
fn is_interesting(
&mut self,
_state: &mut S,
state: &mut S,
_manager: &mut EM,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
{
where {
#[cfg(feature = "trace_job_response_times")]
{
if self.select_task.is_some() {
let observer = observers.match_name::<QemuSystemStateObserver<S::Input, SYS>>("systemstate").unwrap();
self.exec_time = Some(Duration::from_nanos(observer.last_runtime()));
return Ok(false)
let icount = {
if let Some(select) = self.select_task.as_ref() {
let trace = state
.metadata::<SYS::TraceData>()
.expect("TraceData not found");
trace.wort_of_task(select)
} else {
let observer = observers
.match_name::<QemuClockObserver>(self.name())
.unwrap();
observer.last_runtime()
}
};
#[cfg(not(feature = "trace_job_response_times"))]
let icount = {
let observer = observers
.match_name::<QemuClockObserver>(self.name())
.unwrap();
observer.last_runtime()
};
self.exec_time = Some(Duration::from_nanos(icount * _QEMU_NS_PER_ISN as u64));
// Dump the icounts to a file
if let Some(td) = &self.dump_path {
let metadata = state.metadata_map_mut();
let timestamp = SystemTime::now()
.duration_since(unsafe { FUZZ_START_TIMESTAMP })
.unwrap()
.as_millis();
let hist = metadata_insert_or_update_get::<IcHist>(
metadata,
|| IcHist(
vec![(icount, timestamp)],
(icount, timestamp),
),
|hist| {
hist.0.push((icount, timestamp));
if hist.1 .0 < icount {
hist.1 = (icount, timestamp);
}
},
);
if hist.0.len() >= 100 {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.append(true)
.open(td)
.expect("Could not open timedump");
let newv: Vec<(u64, u128)> = Vec::with_capacity(110);
for i in std::mem::replace(&mut hist.0, newv).into_iter() {
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
}
}
}
// TODO Replace with match_name_type when stable
let observer = observers.match_name::<QemuClockObserver>(self.name()).unwrap();
self.exec_time = Some(Duration::from_nanos(observer.last_runtime()));
Ok(false)
}
@ -287,22 +296,24 @@ impl<SYS> Named for ClockTimeFeedback<SYS> {
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 {
pub fn new(name: &'static str, select_task: Option<String>, dump_path: Option<PathBuf>) -> Self {
Self {
exec_time: None,
select_task: select_task,
name: Cow::from(name.to_string()),
dump_path: dump_path,
phantom: std::marker::PhantomData,
}
}
/// Creates a new [`ClockFeedback`], deciding if the given [`QemuClockObserver`] value of a run is interesting.
#[must_use]
pub fn new_with_observer(observer: &QemuClockObserver, select_task: &Option<String>) -> Self {
pub fn new_with_observer(observer: &QemuClockObserver, select_task: &Option<String>, dump_path: Option<PathBuf>) -> Self {
Self {
exec_time: None,
select_task: select_task.clone(),
name: observer.name().clone(),
dump_path: dump_path,
phantom: std::marker::PhantomData,
}
}
@ -330,9 +341,9 @@ where
_observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
{
let observer = _observers.match_name::<QemuClockObserver>("clock")
where {
let observer = _observers
.match_name::<QemuClockObserver>("clock")
.expect("QemuClockObserver not found");
let clock_state = state
.named_metadata_map_mut()
@ -348,7 +359,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> {
// testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime});
Ok(())
}
@ -358,7 +375,6 @@ where
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
Ok(())
}
}
impl Named for QemuClockIncreaseFeedback {
@ -372,7 +388,9 @@ impl QemuClockIncreaseFeedback {
/// Creates a new [`HitFeedback`]
#[must_use]
pub fn new(name: &'static str) -> Self {
Self {name: Cow::from(String::from(name))}
Self {
name: Cow::from(String::from(name)),
}
}
}

View File

@ -1,47 +1,28 @@
use core::fmt::Debug;
use core::cmp::Ordering::{Greater,Less,Equal};
use libafl::inputs::BytesInput;
use libafl::inputs::HasTargetBytes;
use libafl::feedbacks::MapIndexesMetadata;
use libafl::corpus::Testcase;
use libafl::prelude::{ClientStats, Monitor, SimplePrintingMonitor, UsesInput};
use core::marker::PhantomData;
use libafl::schedulers::{MinimizerScheduler, ProbabilitySamplingScheduler, TestcaseScore};
use std::path::PathBuf;
use std::fs;
use hashbrown::{HashMap};
use libafl::observers::ObserversTuple;
use libafl::executors::ExitKind;
use libafl::events::EventFirer;
use libafl::state::{MaybeHasClientPerfMonitor, HasCorpus, UsesState};
use libafl::prelude::State;
use libafl::inputs::Input;
use libafl::feedbacks::Feedback;
use libafl::common::HasMetadata;
use libafl_qemu::modules::edges::EdgeCoverageModule;
use libafl::observers::MapObserver;
use serde::{Deserialize, Serialize};
use std::cmp;
use std::time::Duration;
use std::time::Instant;
use std::ops::Sub;
use core::{fmt::Debug, marker::PhantomData};
use libafl::corpus::Corpus;
use libafl_bolts::{
AsSlice, ClientId, HasLen, Named
use std::{
borrow::Cow, ops::Sub, time::{Duration, Instant}
};
use serde::{Serialize, Deserialize};
use libafl::{
observers::Observer,
common::HasMetadata,
corpus::{Corpus, Testcase},
events::EventFirer,
executors::ExitKind,
feedbacks::{Feedback, MapIndexesMetadata},
observers::ObserversTuple,
prelude::{ClientStats, Monitor, SimplePrintingMonitor, State, StateInitializer, UsesInput},
schedulers::{MinimizerScheduler, ProbabilitySamplingScheduler, TestcaseScore},
state::{HasCorpus, MaybeHasClientPerfMonitor, UsesState},
Error,
};
use libafl_bolts::{ClientId, HasLen, Named};
use crate::systemstate::target_os::TargetSystem;
use crate::time::clock::QemuClockObserver;
use libafl::prelude::StateInitializer;
use std::borrow::Cow;
//=========================== Scheduler
pub type TimeMaximizerCorpusScheduler<CS, O> =
@ -50,17 +31,20 @@ pub type TimeMaximizerCorpusScheduler<CS, O> =
/// Multiply the testcase size with the execution time.
/// This favors small and quick testcases.
#[derive(Debug, Clone)]
pub struct MaxTimeFavFactor
{
}
pub struct MaxTimeFavFactor {}
impl<S> TestcaseScore<S> for MaxTimeFavFactor
where
S: HasCorpus,
{
fn compute(state: &S, entry: &mut Testcase<<S::Corpus as Corpus>::Input> ) -> Result<f64, Error> {
fn compute(
_state: &S,
entry: &mut Testcase<<S::Corpus as Corpus>::Input>,
) -> Result<f64, Error> {
// TODO maybe enforce entry.exec_time().is_some()
let et = entry.exec_time().expect("testcase.exec_time is needed for scheduler");
let et = entry
.exec_time()
.expect("testcase.exec_time is needed for scheduler");
let tns: i64 = et.as_nanos().try_into().expect("failed to convert time");
Ok(-tns as f64)
}
@ -86,223 +70,30 @@ where
impl<S> TestcaseScore<S> for MaxExecsLenFavFactor<S>
where
S: HasCorpus + HasMetadata,
<<S as HasCorpus>::Corpus as libafl::corpus::Corpus>::Input: HasLen
<<S as HasCorpus>::Corpus as libafl::corpus::Corpus>::Input: HasLen,
{
fn compute( state: &S, entry: &mut Testcase<<S::Corpus as Corpus>::Input>) -> Result<f64, Error> {
let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64());
let execs_times_length_per_hour = execs_per_hour*entry.load_len(state.corpus()).unwrap() as f64;
fn compute(
state: &S,
entry: &mut Testcase<<S::Corpus as Corpus>::Input>,
) -> Result<f64, Error> {
let execs_per_hour = (3600.0
/ entry
.exec_time()
.expect("testcase.exec_time is needed for scheduler")
.as_secs_f64());
let execs_times_length_per_hour =
execs_per_hour * entry.load_len(state.corpus()).unwrap() as f64;
Ok(execs_times_length_per_hour)
}
}
//===================================================================
/// A Feedback reporting if the Input consists of strictly decreasing bytes.
/// A Feedback which rewards each increase in execution time
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SortedFeedback {
name: Cow<'static, str>
}
impl<S> StateInitializer<S> for SortedFeedback {}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for SortedFeedback
where
S: State + UsesInput<Input = I> + MaybeHasClientPerfMonitor,
S::Input: HasTargetBytes,
EM: EventFirer<State = S>,
OT: ObserversTuple<I, S>,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &S::Input,
_observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
{
let t = _input.target_bytes();
let tmp = t.as_slice();
if tmp.len()<32 {return Ok(false);}
let tmp = Vec::<u8>::from(&tmp[0..32]);
// tmp.reverse();
// if tmp.is_sorted_by(|a,b| match a.partial_cmp(b).unwrap_or(Less) {
// Less => Some(Greater),
// Equal => Some(Greater),
// Greater => Some(Less),
// }) {return Ok(true)};
let mut is_sorted = true;
if tmp[0]<tmp[1] {
for i in 1..tmp.len() {
is_sorted &= tmp[i-1]<=tmp[i];
if !is_sorted {break;}
}
} else {
for i in 1..tmp.len() {
is_sorted &= tmp[i-1]>=tmp[i];
if !is_sorted {break;}
}
}
return Ok(is_sorted);
}
}
impl Named for SortedFeedback {
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl SortedFeedback {
/// Creates a new [`HitFeedback`]
#[must_use]
pub fn new() -> Self {
Self {name: Cow::from("Sorted".to_string()),}
}
}
impl Default for SortedFeedback {
fn default() -> Self {
Self::new()
}
}
//===================================================================
/// A Feedback which expects a certain minimum execution time
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ExecTimeReachedFeedback
{
name: Cow<'static, str>,
target_time: u64,
}
impl<S> StateInitializer<S> for ExecTimeReachedFeedback {}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for ExecTimeReachedFeedback
where
S: State + UsesInput + MaybeHasClientPerfMonitor,
EM: EventFirer<State = S>,
OT: ObserversTuple<I, S>,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
{
let observer = observers.match_name::<QemuClockObserver>("clock")
.expect("QemuClockObserver not found");
Ok(observer.last_runtime() >= self.target_time)
}
}
impl Named for ExecTimeReachedFeedback
{
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl ExecTimeReachedFeedback
where
{
/// Creates a new [`ExecTimeReachedFeedback`]
#[must_use]
pub fn new(target_time : u64) -> Self {
Self {name: Cow::from("ExecTimeReachedFeedback".to_string()), target_time: target_time}
}
}
pub static mut EXEC_TIME_COLLECTION : Vec<u32> = Vec::new();
/// A Noop Feedback which records a list of all execution times
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ExecTimeCollectorFeedback
{
name: Cow<'static, str>
}
impl<S> StateInitializer<S> for ExecTimeCollectorFeedback {}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for ExecTimeCollectorFeedback
where
S: State + UsesInput + MaybeHasClientPerfMonitor,
EM: EventFirer<State = S>,
OT: ObserversTuple<I, S>,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
{
let observer = observers.match_name::<QemuClockObserver>("clock")
.expect("QemuClockObserver not found");
unsafe { EXEC_TIME_COLLECTION.push(observer.last_runtime().try_into().unwrap()); }
Ok(false)
}
}
impl Named for ExecTimeCollectorFeedback
{
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl ExecTimeCollectorFeedback
where
{
/// Creates a new [`ExecTimeCollectorFeedback`]
#[must_use]
pub fn new() -> Self {
Self {name: Cow::from("ExecTimeCollectorFeedback".to_string())}
}
}
/// Shared Metadata for a SysStateFeedback
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ExecTimeCollectorFeedbackState
{
name: Cow<'static, str>,
collection: Vec<u32>,
}
impl Named for ExecTimeCollectorFeedbackState
{
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl Default for ExecTimeCollectorFeedbackState {
fn default() -> Self {
Self {name: Cow::from("ExecTimeCollectorFeedbackState".to_string()), collection: Vec::new()}
}
}
//===================================================================
/// A Feedback which expects a certain minimum execution time
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ExecTimeIncFeedback
{
pub struct ExecTimeIncFeedback {
name: Cow<'static, str>,
longest_time: u64,
last_is_longest: bool
last_is_longest: bool,
}
impl<S> StateInitializer<S> for ExecTimeIncFeedback {}
@ -322,9 +113,9 @@ where
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
{
let observer = observers.match_name::<QemuClockObserver>("clocktime")
where {
let observer = observers
.match_name::<QemuClockObserver>("clocktime")
.expect("QemuClockObserver not found");
if observer.last_runtime() > self.longest_time {
self.longest_time = observer.last_runtime();
@ -352,35 +143,34 @@ where
}
}
impl Named for ExecTimeIncFeedback
{
impl Named for ExecTimeIncFeedback {
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl ExecTimeIncFeedback
where
{
impl ExecTimeIncFeedback {
/// Creates a new [`ExecTimeReachedFeedback`]
#[must_use]
pub fn new() -> Self {
Self {name: Cow::from("ExecTimeReachedFeedback".to_string()), longest_time: 0, last_is_longest: false}
Self {
name: Cow::from("ExecTimeReachedFeedback".to_string()),
longest_time: 0,
last_is_longest: false,
}
}
}
/// A Noop Feedback which records a list of all execution times
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AlwaysTrueFeedback
{
name: Cow<'static, str>
pub struct AlwaysTrueFeedback {
name: Cow<'static, str>,
}
impl<S> StateInitializer<S> for AlwaysTrueFeedback {}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for AlwaysTrueFeedback
where
S: State + UsesInput + MaybeHasClientPerfMonitor,
EM: EventFirer<State = S>,
@ -395,37 +185,31 @@ where
_observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
{
where {
Ok(true)
}
}
impl Named for AlwaysTrueFeedback
{
impl Named for AlwaysTrueFeedback {
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl AlwaysTrueFeedback
where
{
impl AlwaysTrueFeedback {
/// Creates a new [`ExecTimeCollectorFeedback`]
#[must_use]
pub fn new() -> Self {
Self {
name: Cow::from("AlwaysTrueFeedback".to_string())
name: Cow::from("AlwaysTrueFeedback".to_string()),
}
}
}
//=========================== Probability Mass Scheduler
pub type TimeProbMassScheduler<S> =
ProbabilitySamplingScheduler<TimeProbFactor<S>>;
pub type TimeProbMassScheduler<S> = ProbabilitySamplingScheduler<TimeProbFactor<S>>;
#[derive(Debug, Clone)]
pub struct TimeProbFactor<S>
@ -443,15 +227,19 @@ impl<S> TestcaseScore<S> for TimeProbFactor<S>
where
S: HasCorpus,
{
fn compute(_state: &S, entry: &mut Testcase<<S::Corpus as Corpus>::Input>) -> Result<f64, Error> {
fn compute(
_state: &S,
entry: &mut Testcase<<S::Corpus as Corpus>::Input>,
) -> Result<f64, Error> {
// TODO maybe enforce entry.exec_time().is_some()
let et = entry.exec_time().expect("testcase.exec_time is needed for scheduler");
let et = entry
.exec_time()
.expect("testcase.exec_time is needed for scheduler");
let tns: i64 = et.as_nanos().try_into().expect("failed to convert time");
Ok(((tns as f64) / 1000.0).powf(2.0)) //microseconds
}
}
/// Monitor that prints with a limited rate.
#[derive(Debug, Clone)]
pub struct RateLimitedMonitor {
@ -484,7 +272,9 @@ impl Monitor for RateLimitedMonitor {
fn display(&mut self, event_msg: &str, sender_id: ClientId) {
let now = Instant::now();
const RATE: Duration = Duration::from_secs(5);
if (event_msg!="Testcase" && event_msg!="UserStats") || now.duration_since(self.last) > RATE {
if (event_msg != "Testcase" && event_msg != "UserStats")
|| now.duration_since(self.last) > RATE
{
self.inner.display(event_msg, sender_id);
self.last = now;
}

View File

@ -1,14 +1,18 @@
#!/bin/sh
TEST_KERNEL=../benchmark/build/waters_seq_full.elf
TEST_SYMBOLS=../benchmark/target_symbols.csv
DEF_ARGS="-k $TEST_KERNEL -c $TEST_SYMBOLS -n ./dump/test"
# cargo build --no-default-features --features std,snapshot_restore,singlecore,feed_afl,observer_hitcounts
# Test basic fuzzing loop
../target/debug/fret -k ../benchmark/build/waters.elf -c ../benchmark/target_symbols.csv -n ./dump/waters -tar fuzz -t 10 -s 123
# ../target/debug/fret $DEF_ARGS -tar fuzz -t 10 -s 123
# Test reprodcibility
rm -f ./dump/demo.case.time
../target/debug/fret -k ../benchmark/build/waters.elf -c ../benchmark/target_symbols.csv -n ./dump/demo -tr showmap -i ./demo.case
if [[ $(cut -d, -f1 ./dump/demo.case.time) != $(cut -d, -f1 ./demo.example.time) ]]; then echo "Not reproducible!" && exit 1; else echo "Reproducible"; fi
rm -f ./dump/test.time
../target/debug/fret $DEF_ARGS -tr showmap -i ./waters.case.test
if [[ $(cut -d, -f1 ./dump/test.time) != $(cut -d, -f1 ./waters.time.test) ]]; then echo "Not reproducible!" && exit 1; else echo "Reproducible"; fi
# Test state dump
# cargo build --no-default-features --features std,snapshot_restore,singlecore,feed_afl,observer_hitcounts,systemstate
@ -22,6 +26,6 @@ if [[ -n "$(diff -q demo.example.abb.ron dump/demo.trace.ron)" ]]; then echo "AB
# ../target/debug/fret -k ../benchmark/build/minimal.elf -c ../benchmark/target_symbols.csv -n ./dump/minimal_worst -tr showmap -i ./dump/minimal.case
# Test fuzzing using systemtraces
cargo build --no-default-features --features std,snapshot_restore,singlecore,feed_systemtrace
cargo build --no-default-features --features std,snapshot_restore,singlecore,config_stg
../target/debug/fret -k ../benchmark/build/waters.elf -c ../benchmark/target_symbols.csv -n ./dump/waters -tar fuzz -t 10 -s 123
../target/debug/fret -k ../benchmark/build/waters_seq_full.elf -c ../benchmark/target_symbols.csv -n ./dump/waters -tar fuzz -t 10 -s 123