refactoring
This commit is contained in:
parent
cc2a2e6422
commit
f7e61665be
@ -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
|
||||
|
@ -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[@]}"
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
@ -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);
|
||||
};
|
||||
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()
|
||||
};
|
||||
let trace = state
|
||||
.metadata::<SYS::TraceData>()
|
||||
.expect("TraceData not found");
|
||||
std::fs::write(
|
||||
s,
|
||||
ron::to_string(&(
|
||||
&trace,
|
||||
per_task_metadata,
|
||||
))
|
||||
.expect("Error serializing hashmap"),
|
||||
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>
|
||||
|
@ -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;
|
||||
@ -38,24 +27,28 @@ fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
|
||||
}
|
||||
|
||||
/// Lookup a symbol in the ELF file, optionally resolve segment offsets
|
||||
pub fn load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> GuestAddr {
|
||||
pub fn load_symbol(elf: &EasyElf, symbol: &str, do_translation: bool) -> GuestAddr {
|
||||
try_load_symbol(elf, symbol, do_translation).expect(&format!("Symbol {} not found", symbol))
|
||||
}
|
||||
|
||||
pub fn try_load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> Option<GuestAddr> {
|
||||
let ret = elf
|
||||
.resolve_symbol(symbol, 0);
|
||||
/// 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);
|
||||
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
|
||||
pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option<std::ops::Range<GuestAddr>> {
|
||||
let gob = elf.goblin();
|
||||
|
||||
let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect();
|
||||
funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value));
|
||||
let mut funcs: Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect();
|
||||
funcs.sort_unstable_by(|x, y| x.st_value.cmp(&y.st_value));
|
||||
|
||||
for sym in &gob.syms {
|
||||
if let Some(sym_name) = gob.strtab.get_at(sym.st_name) {
|
||||
@ -82,10 +75,22 @@ 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 {
|
||||
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);
|
||||
@ -97,29 +102,127 @@ pub fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_rec_return_stackframe(emu : &libafl_qemu::Qemu, lr : GuestAddr) -> GuestAddr {
|
||||
let lr_ = lr & u32::MAX-1;
|
||||
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());
|
||||
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())
|
||||
.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)
|
||||
}
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
// }
|
||||
// }
|
||||
// }
|
@ -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()
|
||||
|
@ -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>,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +1,51 @@
|
||||
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;
|
||||
pub static mut FUZZ_START_TIMESTAMP: SystemTime = UNIX_EPOCH;
|
||||
|
||||
pub const QEMU_ICOUNT_SHIFT : u32 = 5;
|
||||
pub const QEMU_ISNS_PER_SEC : u32 = u32::pow(10, 9) / u32::pow(2, QEMU_ICOUNT_SHIFT);
|
||||
pub const QEMU_ISNS_PER_USEC : u32 = QEMU_ISNS_PER_SEC / 1000000;
|
||||
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 QEMU_ICOUNT_SHIFT: u32 = 5;
|
||||
pub const QEMU_ISNS_PER_SEC: u32 = u32::pow(10, 9) / u32::pow(2, QEMU_ICOUNT_SHIFT);
|
||||
pub const QEMU_ISNS_PER_USEC: u32 = QEMU_ISNS_PER_SEC / 1000000;
|
||||
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 fn tick_to_time(ticks: u64) -> Duration {
|
||||
Duration::from_nanos((ticks << QEMU_ICOUNT_SHIFT) as u64)
|
||||
}
|
||||
|
||||
pub fn tick_to_ms(ticks: u64) -> f32 {
|
||||
(Duration::from_nanos(ticks << QEMU_ICOUNT_SHIFT).as_micros() as f32/10.0).round()/100.0
|
||||
(Duration::from_nanos(ticks << QEMU_ICOUNT_SHIFT).as_micros() as f32 / 10.0).round() / 100.0
|
||||
}
|
||||
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,8 +92,8 @@ impl Default for MaxIcountMetadata {
|
||||
}
|
||||
|
||||
/// A piece of metadata tracking all icounts
|
||||
#[derive(Debug, SerdeAny, Serialize, Deserialize)]
|
||||
pub struct IcHist (pub Vec<(u64, u128)>, pub (u64,u128));
|
||||
#[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;
|
||||
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> {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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() };
|
||||
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
|
||||
{
|
||||
#[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)
|
||||
where {
|
||||
#[cfg(feature = "trace_job_response_times")]
|
||||
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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,18 +31,21 @@ 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 tns : i64 = et.as_nanos().try_into().expect("failed to convert time");
|
||||
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();
|
||||
@ -344,7 +135,7 @@ where
|
||||
) -> Result<(), Error> {
|
||||
#[cfg(feature = "feed_afl")]
|
||||
if self.last_is_longest {
|
||||
let mim : Option<&mut MapIndexesMetadata>= testcase.metadata_map_mut().get_mut();
|
||||
let mim: Option<&mut MapIndexesMetadata> = testcase.metadata_map_mut().get_mut();
|
||||
// pretend that the longest input alone excercises some non-existing edge, to keep it relevant
|
||||
mim.unwrap().list.push(usize::MAX);
|
||||
};
|
||||
@ -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 tns : i64 = et.as_nanos().try_into().expect("failed to convert time");
|
||||
Ok(((tns as f64)/1000.0).powf(2.0)) //microseconds
|
||||
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 {
|
||||
@ -483,8 +271,10 @@ impl Monitor for RateLimitedMonitor {
|
||||
#[inline]
|
||||
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 {
|
||||
const RATE: Duration = Duration::from_secs(5);
|
||||
if (event_msg != "Testcase" && event_msg != "UserStats")
|
||||
|| now.duration_since(self.last) > RATE
|
||||
{
|
||||
self.inner.display(event_msg, sender_id);
|
||||
self.last = now;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user