WIP: per-task response times

This commit is contained in:
Alwin Berger 2024-08-19 16:08:45 +02:00
parent bf7ad374a0
commit e9fb73e65b
11 changed files with 267 additions and 71 deletions

View File

@ -5,7 +5,7 @@ authors = ["Alwin Berger <alwin.berger@tu-dortmund.de>"]
edition = "2021"
[features]
default = ["std", "snapshot_restore", "snapshot_fast", "singlecore", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int" ]
default = ["std", "snapshot_restore", "snapshot_fast", "singlecore", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "trace_job_response_times" ]
std = []
# Exec environemnt basics
snapshot_restore = []
@ -19,6 +19,7 @@ observe_edges = [] # observe cfg edges
observe_hitcounts = [ "observe_edges" ] # reduces edge granularity
observe_systemstate = []
do_hash_notify_state = []
trace_job_response_times = [ "trace_stg" ]
trace_stg = [ "observe_systemstate" ]
trace_reads = [ "trace_stg" ]
# feedbacks
@ -60,7 +61,7 @@ libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
hashbrown = { version = "0.14.0", features = ["serde"] } # A faster hashmap, nostd compatible
petgraph = { version="0.6.4", features = ["serde-1"] }
petgraph = { version="0.6.5", features = ["serde-1"] }
ron = "0.7" # write serialized data - including hashmaps
rand = "0.5"
clap = { version = "4.4.11", features = ["derive"] }

View File

@ -1,6 +1,6 @@
import csv
import os
def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,do_hash_notify_state"
def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,do_hash_notify_state,trace_job_response_times"
remote="remote/"
RUNTIME=1800
TARGET_REPS_A=2
@ -200,7 +200,7 @@ rule tarnsform_trace:
output:
"{remote}timedump/{fuzzer}/{target}.{num}.trace.csv"
shell:
"$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} > {output[0]}"
"$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} {output[0]} {output[0]}2"
rule trace2gantt:
input:
@ -236,11 +236,11 @@ rule all_compare_afl_int:
rule all_images:
input:
expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['afl','feedgeneration10','state'], target=['waters','watersv2'],num=range(0,3))
expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['frafl','feedgeneration10','state'], target=['waters'],num=range(0,3))
rule all_images_int:
input:
expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['afl_int','feedgeneration10_int','state_int'], target=['waters_int','watersv2_int'],num=range(0,3))
expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['frafl_int','feedgeneration10_int','state_int'], target=['waters_int'],num=range(0,3))
rule clusterfuzz:
input:

View File

@ -1,32 +1,33 @@
kernel,main_function,input_symbol,input_size,return_function
mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return
audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return
epic,epic_main,epic_image,4096,epic_return
dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return
fft,fft_main,fft_twidtable,2046,fft_return
bsort,bsort_main,bsort_Array,400,bsort_return
insertsort,insertsort_main,insertsort_a,400,insertsort_return
g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return
rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return
rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return
huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return
huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return
gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return
tmr,main,FUZZ_INPUT,32,trigger_Qemu_break
tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break
lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break
waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
waterspartv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
waterspart_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
waterspartv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break
micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break
micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break
minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break
gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break
interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break
interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break
kernel,main_function,input_symbol,input_size,return_function,target_task_name
mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return,
audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return,
epic,epic_main,epic_image,4096,epic_return,
dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return,
fft,fft_main,fft_twidtable,2046,fft_return,
bsort,bsort_main,bsort_Array,400,bsort_return,
insertsort,insertsort_main,insertsort_a,400,insertsort_return,
g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return,
rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return,
rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return,
huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return,
huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return,
gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return,
tmr,main,FUZZ_INPUT,32,trigger_Qemu_break,
tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break,
lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break,
waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129
watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129
waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129
waterspartv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129
waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129
watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129
waterspart_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129
waterspartv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129
micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break,
micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break,
micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break,
minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,
gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,
interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,
interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,

1 kernel main_function input_symbol input_size return_function target_task_name
2 mpeg2 mpeg2_main mpeg2_oldorgframe 90112 mpeg2_return
3 audiobeam audiobeam_main audiobeam_input 11520 audiobeam_return
4 epic epic_main epic_image 4096 epic_return
5 dijkstra dijkstra_main dijkstra_AdjMatrix 10000 dijkstra_return
6 fft fft_main fft_twidtable 2046 fft_return
7 bsort bsort_main bsort_Array 400 bsort_return
8 insertsort insertsort_main insertsort_a 400 insertsort_return
9 g723_enc g723_enc_main g723_enc_INPUT 1024 g723_enc_return
10 rijndael_dec rijndael_dec_main rijndael_dec_data 32768 rijndael_dec_return
11 rijndael_enc rijndael_enc_main rijndael_enc_data 31369 rijndael_enc_return
12 huff_dec huff_dec_main huff_dec_encoded 419 huff_dec_return
13 huff_enc huff_enc_main huff_enc_plaintext 600 huff_enc_return
14 gsm_enc gsm_enc_main gsm_enc_pcmdata 6400 gsm_enc_return
15 tmr main FUZZ_INPUT 32 trigger_Qemu_break
16 tacle_rtos prvStage0 FUZZ_INPUT 604 trigger_Qemu_break
17 lift main_lift FUZZ_INPUT 100 trigger_Qemu_break
18 waters main_waters FUZZ_INPUT 4096 trigger_Qemu_break 1129
19 watersv2 main_waters FUZZ_INPUT 4096 trigger_Qemu_break 1129
20 waterspart main_waters FUZZ_INPUT 4096 trigger_Qemu_break 1129
21 waterspartv2 main_waters FUZZ_INPUT 4096 trigger_Qemu_break 1129
22 waters_int main_waters FUZZ_INPUT 4096 trigger_Qemu_break 1129
23 watersv2_int main_waters FUZZ_INPUT 4096 trigger_Qemu_break 1129
24 waterspart_int main_waters FUZZ_INPUT 4096 trigger_Qemu_break 1129
25 waterspartv2_int main_waters FUZZ_INPUT 4096 trigger_Qemu_break 1129
26 micro_branchless main_branchless FUZZ_INPUT 4 trigger_Qemu_break
27 micro_int main_int FUZZ_INPUT 16 trigger_Qemu_break
28 micro_longint main_micro_longint FUZZ_INPUT 16 trigger_Qemu_break
29 minimal main_minimal FUZZ_INPUT 4096 trigger_Qemu_break
30 gen3 main_minimal FUZZ_INPUT 4096 trigger_Qemu_break
31 interact main_interact FUZZ_INPUT 4096 trigger_Qemu_break
32 interact_int main_interact FUZZ_INPUT 4096 trigger_Qemu_break
33

View File

@ -34,6 +34,10 @@ pub struct Cli {
#[arg(short='g', long)]
pub dump_graph: bool,
/// select a task for measurments
#[arg(short='s', long)]
pub select_task: Option<String>,
#[command(subcommand)]
pub command: Commands,
}

View File

@ -219,6 +219,7 @@ let app_range = app_start..app_end;
let api_start = load_symbol(&elf, "__API_CODE_START__", false);
let api_end = load_symbol(&elf, "__API_CODE_END__", false);
let api_range = api_start..api_end;
let job_done_addr = load_symbol(&elf, "trigger_job_done", false);
let breakpoint = elf
.resolve_symbol(
@ -386,13 +387,13 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
)}.track_indices();
#[cfg(feature = "observe_systemstate")]
let systemstate_observer = QemuSystemStateObserver::new();
let systemstate_observer = QemuSystemStateObserver::new(&cli.select_task);
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!(
// Time feedback, this one does not need a feedback state
ClockTimeFeedback::new_with_observer(&clock_time_observer)
ClockTimeFeedback::new_with_observer(&clock_time_observer, &cli.select_task)
);
#[cfg(feature = "feed_genetic")]
let mut feedback = feedback_or!(
@ -465,7 +466,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
let qhelpers = tuple_list!();
#[cfg(feature = "observe_systemstate")]
let qhelpers = (QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock,scheduler_running, critical_section,input_counter_ptr,app_range.clone()), qhelpers);
let qhelpers = (QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock,scheduler_running, critical_section,input_counter_ptr,app_range.clone(), job_done_addr), qhelpers);
#[cfg(feature = "observe_edges")]
let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers);
let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers);

View File

@ -3,6 +3,6 @@ mod fuzzer;
#[cfg(target_os = "linux")]
mod time;
#[cfg(target_os = "linux")]
mod systemstate;
pub mod systemstate;
#[cfg(target_os = "linux")]
mod cli;

View File

@ -60,6 +60,7 @@ pub struct NovelSystemStateFeedback
impl<S> Feedback<S> for NovelSystemStateFeedback
where
S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata,
S::Input: Default,
{
fn is_interesting<EM, OT>(
&mut self,
@ -71,9 +72,10 @@ where
) -> Result<bool, Error>
where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>
OT: ObserversTuple<S>,
S::Input: Default
{
let observer = observers.match_name::<QemuSystemStateObserver<S::Input>>("systemstate")
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");
@ -81,12 +83,16 @@ where
.named_metadata_map_mut()
.get_mut::<SystemStateFeedbackState>("systemstate") {
Some(s) => s,
None => {
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")
@ -98,14 +104,14 @@ where
let mut is_novel = false;
let mut takes_longer = false;
match feedbackstate.known_traces.get_mut(&somehash) {
None => {
Option::None => {
is_novel = true;
feedbackstate.known_traces.insert(somehash,(1,clock_observer.last_runtime(),observer.last_run.len()));
feedbackstate.known_traces.insert(somehash,(1,last_runtime,observer.last_run.len()));
}
Some(s) => {
s.0+=1;
if s.1 < clock_observer.last_runtime() {
s.1 = clock_observer.last_runtime();
if s.1 < last_runtime {
s.1 = last_runtime;
takes_longer = true;
}
}
@ -124,7 +130,7 @@ where
let a = self.last_trace.take();
match a {
Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)),
None => (),
Option::None => (),
}
Ok(())
}
@ -186,10 +192,10 @@ where
let names : Vec<String> = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect();
match &self.dumpfile {
Some(s) => {
std::fs::write(s,ron::to_string(&(&observer.last_trace,&observer.last_states)).expect("Error serializing hashmap")).expect("Can not dump to file");
std::fs::write(s,ron::to_string(&(&observer.last_trace,&observer.last_states,&observer.job_instances)).expect("Error serializing hashmap")).expect("Can not dump to file");
self.dumpfile = None
},
None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);}
Option::None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);}
};
// if self.dump_metadata {self.last_trace=Some(observer.last_trace.clone());}
Ok(false)

View File

@ -111,6 +111,7 @@ pub struct QemuSystemStateHelper {
critical_addr: GuestAddr,
input_counter: Option<GuestAddr>,
app_range: Range<GuestAddr>,
job_done_addrs: GuestAddr,
}
impl QemuSystemStateHelper {
@ -130,6 +131,7 @@ impl QemuSystemStateHelper {
critical_addr: GuestAddr,
input_counter: Option<GuestAddr>,
app_range: Range<GuestAddr>,
job_done_addrs: GuestAddr,
) -> Self {
QemuSystemStateHelper {
api_fn_addrs,
@ -146,6 +148,7 @@ impl QemuSystemStateHelper {
critical_addr,
input_counter: input_counter,
app_range,
job_done_addrs,
}
}
}
@ -165,6 +168,8 @@ where
_hooks.instruction(*wp, Hook::Function(exec_isr_hook::<QT, S>), false);
}
_hooks.jmps(Hook::Function(gen_jmp_is_syscall::<QT, S>), Hook::Function(trace_jmp::<QT, S>));
#[cfg(feature = "trace_job_response_times")]
_hooks.instruction(self.job_done_addrs, Hook::Function(job_done_hook::<QT, S>), false);
#[cfg(feature = "trace_reads")]
_hooks.reads(Hook::Function(gen_read_is_input::<QT, S>), Hook::Empty,Hook::Empty,Hook::Empty,Hook::Empty,Hook::Function(trace_reads::<QT, S>));
unsafe { INPUT_MEM = self.input_mem.clone() };
@ -314,6 +319,31 @@ fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr)
unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); }
}
//============================= Trace job response times
pub static mut JOBS_DONE : Vec<(u64, String)> = vec![];
pub fn job_done_hook<QT, S>(
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
)
where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.qemu();
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr);
if curr_tcb_addr == 0 {
return;
};
let current_tcb : TCB_t = freertos::emu_lookup::lookup(emulator,curr_tcb_addr);
let tmp = unsafe {std::mem::transmute::<[i8; 10],[u8; 10]>(current_tcb.pcTaskName)};
let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::<String>();
unsafe { JOBS_DONE.push((get_icount(emulator), name)); }
}
//============================= Trace interrupt service routines
pub fn exec_isr_hook<QT, S>(

View File

@ -1,5 +1,6 @@
use libafl::prelude::ExitKind;
use libafl::prelude::UsesInput;
use libafl::HasMetadata;
use libafl_bolts::HasLen;
use libafl_bolts::Named;
use libafl::Error;
@ -7,6 +8,9 @@ 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;
@ -19,6 +23,7 @@ use super::{
RefinedTCB,
ReducedFreeRTOSSystemState,
freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*},
helpers::JOBS_DONE,
};
//============================= Observer
@ -34,13 +39,17 @@ pub struct QemuSystemStateObserver<I>
pub last_trace: Vec<ExecInterval>,
pub last_reads: Vec<HashSet<u32>>,
pub last_input: I,
pub job_instances: Vec<(u64, u64, String)>,
pub worst_job_instances: HashMap<String, u64>,
pub select_task: Option<String>,
pub success: bool,
name: Cow<'static, str>,
}
impl<S> Observer<S> for QemuSystemStateObserver<S::Input>
where
S: UsesInput,
S: UsesInput + HasMetadata,
S::Input: Default
{
#[inline]
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
@ -61,7 +70,46 @@ where
self.last_reads = temp.1;
self.last_states = temp.2;
self.success = temp.3;
// println!("{:?}",temp);
#[cfg(feature="trace_job_response_times")]
{
let metadata =_state.metadata_map_mut();
let releases = get_releases(&self.last_trace, &self.last_states);
// println!("Releases: {:?}",&releases);
let jobs_done = JOBS_DONE.split_off(0);
self.job_instances = get_release_response_pairs(&releases, &jobs_done);
// println!("Instances: {:?}",&self.job_instances);
let observer = &self;
let mut worst_case_per_task = HashMap::new();
observer.job_instances.iter().for_each(|x| {
let time = (x.1-x.0);
if worst_case_per_task.get(&x.2).is_some() {
let old = worst_case_per_task.get_mut(&x.2).unwrap();
if time > *old {
*old=time;
}
} else {
worst_case_per_task.insert(x.2.clone(), time);
}
});
self.worst_job_instances = worst_case_per_task;
// copy-paste form clock observer
{
let hist = metadata.get_mut::<IcHist>();
let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
match hist {
Option::None => {
metadata.insert(IcHist(vec![(self.last_runtime(), timestamp)],
(self.last_runtime(), timestamp)));
}
Some(v) => {
v.0.push((self.last_runtime(), timestamp));
if v.1.0 < self.last_runtime() {
v.1 = (self.last_runtime(), timestamp);
}
}
}
}
}
}
// let abbs = extract_abbs_from_trace(&self.last_run);
// println!("{:?}",abbs);
@ -89,14 +137,17 @@ impl<I> HasLen for QemuSystemStateObserver<I>
impl<I> QemuSystemStateObserver<I>
where I: Default {
pub fn new() -> Self {
Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false }
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(), 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).unwrap_or(&unsafe{libafl_qemu::sys::icount_get_raw()}).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()})
}
}
impl<I> Default for QemuSystemStateObserver<I>
where I: Default {
fn default() -> Self {
Self::new()
Self::new(&None)
}
}
@ -167,6 +218,75 @@ fn refine_system_states(mut input: Vec<RawFreeRTOSSystemState>) -> (Vec<ReducedF
return ret;
}
// Find all task release times. A release is SysTickHandler isr block that moves a task from the delay list to the ready list
fn get_releases(tarce: &Vec<ExecInterval>, states: &HashMap<u64, ReducedFreeRTOSSystemState>) -> Vec<(u64, String)> {
let mut ret = Vec::new();
let mut initial_released = false;
for (_n, i) in tarce.iter().enumerate() {
if !initial_released && i.start_capture.0 == CaptureEvent::ISREnd && i.start_capture.1 == "xPortPendSVHandler" {
let start_state = states.get(&i.start_state).expect("State not found");
initial_released = true;
start_state.ready_list_after.iter().for_each(|x| {
ret.push((i.start_tick, x.task_name.clone()));
});
continue;
}
if i.start_capture.0 == CaptureEvent::ISRStart && ( i.start_capture.1 == "xPortSysTickHandler" || i.start_capture.1 == "isr_starter" ) && i.end_capture.0 == CaptureEvent::ISREnd {
let start_state = states.get(&i.start_state).expect("State not found");
let end_state = states.get(&i.end_state).expect("State not found");
end_state.ready_list_after.iter().for_each(|x| {
if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) {
ret.push((i.end_tick, x.task_name.clone()));
}
});
// start_state.delay_list_after.iter().for_each(|x| {
// if !end_state.delay_list_after.iter().any(|y| x.task_name == y.task_name) {
// ret.push((i.end_tick, x.task_name.clone()));
// }
// });
}
}
ret
}
fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String)>) -> Vec<(u64, u64, String)> {
let mut ret = Vec::new();
let mut ready : HashMap<&String, u64> = HashMap::new();
let mut r = rel.iter().peekable();
let mut d = resp.iter().peekable();
loop {
while let Some(peek_rel) = r.peek() {
if !ready.contains_key(&peek_rel.1) {
ready.insert(&peek_rel.1, peek_rel.0);
r.next();
} else {
if let Some(peek_resp) = d.peek() {
if peek_resp.0 >= peek_rel.0 { // multiple releases before respopnse, only use the latest release
eprintln!("Task {} released multiple times before response", peek_rel.1);
ready.insert(&peek_rel.1, peek_rel.0);
r.next();
}
}
break;
}
}
if let Some(peek_resp) = d.next() {
if ready.contains_key(&peek_resp.1) {
assert!(peek_resp.0 >= ready[&peek_resp.1]);
ret.push((ready[&peek_resp.1], peek_resp.0, peek_resp.1.clone()));
ready.remove(&peek_resp.1);
}
else {
eprintln!("Task {} not found in ready list", peek_resp.1);
}
} else {
// TODO: should remaining released tasks be counted as finished?
return ret;
}
}
}
/// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success
fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, CaptureEvent, String, (u32, u32), HashSet<u32>)>) -> (Vec<ExecInterval>, Vec<HashSet<u32>>, HashMap<u64, ReducedFreeRTOSSystemState>, bool) {
if trace.len() == 0 {return (Vec::new(), Vec::new(), HashMap::new(), true);}

View File

@ -490,6 +490,7 @@ impl StgFeedback {
impl<S> Feedback<S> for StgFeedback
where
S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata,
S::Input: Default,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
@ -503,11 +504,16 @@ where
where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
S::Input: Default,
{
let observer = observers.match_name::<QemuSystemStateObserver<S::Input>>("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>("stgfeedbackstate") {
@ -524,13 +530,13 @@ where
{
let h = get_generic_hash(&edgetrace);
if let Some(x) = feedbackstate.worst_observed_per_stg_path.get_mut(&h) {
let t = clock_observer.last_runtime();
let t = last_runtime;
if t > *x {
*x = t;
interesting |= INTEREST_PATH;
}
} else {
feedbackstate.worst_observed_per_stg_path.insert(h, clock_observer.last_runtime());
feedbackstate.worst_observed_per_stg_path.insert(h, last_runtime);
updated = true;
interesting |= INTEREST_PATH;
}
@ -543,13 +549,13 @@ where
self.last_abbs_hash = Some(h);
// order of execution is relevant
if let Some(x) = feedbackstate.worst_observed_per_abb_path.get_mut(&h) {
let t = clock_observer.last_runtime();
let t = last_runtime;
if t > *x {
*x = t;
interesting |= INTEREST_ABBPATH;
}
} else {
feedbackstate.worst_observed_per_abb_path.insert(h, clock_observer.last_runtime());
feedbackstate.worst_observed_per_abb_path.insert(h, last_runtime);
interesting |= INTEREST_ABBPATH;
}
}
@ -574,13 +580,13 @@ where
self.last_aggregate_hash = Some(get_generic_hash(&_tmp));
if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&_tmp) {
let t = clock_observer.last_runtime();
let t = last_runtime;
if t > *x {
*x = t;
interesting |= INTEREST_AGGREGATE;
}
} else {
feedbackstate.worst_observed_per_aggegated_path.insert(_tmp, clock_observer.last_runtime());
feedbackstate.worst_observed_per_aggegated_path.insert(_tmp, last_runtime);
interesting |= INTEREST_AGGREGATE;
}
}

View File

@ -1,3 +1,4 @@
use hashbrown::HashMap;
use libafl_bolts::Named;
use libafl::{
executors::ExitKind,
@ -21,6 +22,8 @@ use std::time::{SystemTime, UNIX_EPOCH};
use std::path::PathBuf;
use std::borrow::Cow;
use crate::systemstate::observers::QemuSystemStateObserver;
pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH;
pub const QEMU_ICOUNT_SHIFT : u32 = 5;
@ -140,14 +143,22 @@ where
let hist = metadata.get_mut::<IcHist>();
let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
match hist {
None => {
metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)],
(self.end_tick - self.start_tick, timestamp)));
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) => {
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);
#[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()
@ -193,6 +204,7 @@ impl Default for QemuClockObserver {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ClockTimeFeedback {
exec_time: Option<Duration>,
select_task: Option<String>,
name: Cow<'static, str>,
}
@ -213,6 +225,19 @@ where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
#[cfg(feature="trace_job_response_times")]
{
if let Some(t) = &self.select_task {
let observer = observers.match_name::<QemuSystemStateObserver<S::Input>>("systemstate").unwrap();
if let Some(time) = observer.worst_job_instances.get(t) {
self.exec_time = Some(Duration::from_nanos(*time));
return Ok(true);
} else {
self.exec_time = Some(Duration::from_nanos(0));
return Ok(true);
}
}
}
// 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() << QEMU_ICOUNT_SHIFT)); // Assume a somewhat realistic multiplier of clock, it does not matter
@ -251,18 +276,20 @@ impl Named for ClockTimeFeedback {
impl ClockTimeFeedback {
/// 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) -> Self {
pub fn new(name: &'static str, select_task: Option<String>) -> Self {
Self {
exec_time: None,
select_task: select_task,
name: Cow::from(name.to_string()),
}
}
/// Creates a new [`ClockFeedback`], deciding if the given [`QemuClockObserver`] value of a run is interesting.
#[must_use]
pub fn new_with_observer(observer: &QemuClockObserver) -> Self {
pub fn new_with_observer(observer: &QemuClockObserver, select_task: &Option<String>) -> Self {
Self {
exec_time: None,
select_task: select_task.clone(),
name: observer.name().clone(),
}
}