WIP: add systemstate tracking
This commit is contained in:
parent
b07f7ccbca
commit
79bca99cc7
@ -21,3 +21,5 @@ libafl = { path = "../../libafl/" }
|
|||||||
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
|
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
|
||||||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||||
hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
|
hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
|
||||||
|
petgraph = { version="0.6.0", features = ["serde-1"] }
|
||||||
|
ron = "0.7" # write serialized data - including hashmaps
|
@ -5,4 +5,4 @@
|
|||||||
[ -n "$4" -a "$4" != "+" -a -z "$BREAKPOINT" ] && export BREAKPOINT="$4"
|
[ -n "$4" -a "$4" != "+" -a -z "$BREAKPOINT" ] && export BREAKPOINT="$4"
|
||||||
[ -n "$5" -a "$5" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$5"
|
[ -n "$5" -a "$5" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$5"
|
||||||
[ -n "$6" -a "$6" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="$6"
|
[ -n "$6" -a "$6" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="$6"
|
||||||
target/debug/qemu_systemmode -icount shift=3,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -S
|
target/debug/qemu_systemmode -icount shift=3,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native # -snapshot -drive if=none,format=qcow2,file=dummy.qcow2
|
@ -28,18 +28,31 @@ use libafl::{
|
|||||||
stages::StdMutationalStage,
|
stages::StdMutationalStage,
|
||||||
state::{HasCorpus, StdState},
|
state::{HasCorpus, StdState},
|
||||||
Error,
|
Error,
|
||||||
prelude::{SimpleMonitor, SimpleEventManager},
|
prelude::{SimpleMonitor, SimpleEventManager}, Evaluator,
|
||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor,
|
edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor,
|
||||||
QemuHooks, Regs,
|
QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::{QemuClockObserver, ClockTimeFeedback},
|
clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback},
|
||||||
qemustate::QemuStateRestoreHelper,
|
qemustate::QemuStateRestoreHelper,
|
||||||
|
systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::DumpSystraceFeedback},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub static mut MAX_INPUT_SIZE: usize = 50;
|
pub static mut MAX_INPUT_SIZE: usize = 32;
|
||||||
|
/// Read ELF program headers to resolve physical load addresses.
|
||||||
|
fn virt2phys(vaddr: GuestAddr, tab: &EasyElf) -> GuestAddr {
|
||||||
|
let ret;
|
||||||
|
for i in &tab.goblin().program_headers {
|
||||||
|
if i.vm_range().contains(&vaddr.try_into().unwrap()) {
|
||||||
|
ret = vaddr - TryInto::<GuestAddr>::try_into(i.p_vaddr).unwrap()
|
||||||
|
+ TryInto::<GuestAddr>::try_into(i.p_paddr).unwrap();
|
||||||
|
return ret - (ret % 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vaddr;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fuzz() {
|
pub fn fuzz() {
|
||||||
if let Ok(s) = env::var("FUZZ_SIZE") {
|
if let Ok(s) = env::var("FUZZ_SIZE") {
|
||||||
@ -64,7 +77,8 @@ pub fn fuzz() {
|
|||||||
&env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()),
|
&env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
.expect("Symbol or env FUZZ_INPUT not found") as GuestPhysAddr;
|
.expect("Symbol or env FUZZ_INPUT not found") ;
|
||||||
|
let input_addr = virt2phys(input_addr,&elf) as GuestPhysAddr;
|
||||||
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
||||||
|
|
||||||
let main_addr = elf
|
let main_addr = elf
|
||||||
@ -72,6 +86,24 @@ pub fn fuzz() {
|
|||||||
.expect("Symbol main not found");
|
.expect("Symbol main not found");
|
||||||
println!("main address = {:#x}", main_addr);
|
println!("main address = {:#x}", main_addr);
|
||||||
|
|
||||||
|
let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers
|
||||||
|
.resolve_symbol("pxCurrentTCB", 0)
|
||||||
|
.expect("Symbol pxCurrentTCBC not found");
|
||||||
|
// let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf);
|
||||||
|
println!("TCB pointer at {:#x}", curr_tcb_pointer);
|
||||||
|
let task_queue_addr = elf
|
||||||
|
.resolve_symbol("pxReadyTasksLists", 0)
|
||||||
|
.expect("Symbol pxReadyTasksLists not found");
|
||||||
|
// let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin());
|
||||||
|
println!("Task Queue at {:#x}", task_queue_addr);
|
||||||
|
let svh = elf
|
||||||
|
.resolve_symbol("xPortPendSVHandler", 0)
|
||||||
|
.expect("Symbol xPortPendSVHandler not found");
|
||||||
|
// let svh=virt2phys(svh, &elf);
|
||||||
|
// let svh = elf
|
||||||
|
// .resolve_symbol("vPortEnterCritical", 0)
|
||||||
|
// .expect("Symbol vPortEnterCritical not found");
|
||||||
|
|
||||||
let breakpoint = elf
|
let breakpoint = elf
|
||||||
.resolve_symbol(
|
.resolve_symbol(
|
||||||
&env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()),
|
&env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()),
|
||||||
@ -130,11 +162,15 @@ pub fn fuzz() {
|
|||||||
// Create an observation channel to keep track of the execution time
|
// Create an observation channel to keep track of the execution time
|
||||||
let clock_time_observer = QemuClockObserver::new("clocktime");
|
let clock_time_observer = QemuClockObserver::new("clocktime");
|
||||||
|
|
||||||
|
let systemstate_observer = QemuSystemStateObserver::new();
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
|
DumpSystraceFeedback::with_dump(None),
|
||||||
// New maximization map feedback linked to the edges observer and the feedback state
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
MaxMapFeedback::new_tracking(&edges_observer, true, true),
|
MaxMapFeedback::new_tracking(&edges_observer, true, true),
|
||||||
|
// QemuClockIncreaseFeedback::default(),
|
||||||
// Time feedback, this one does not need a feedback state
|
// Time feedback, this one does not need a feedback state
|
||||||
ClockTimeFeedback::new_with_observer(&clock_time_observer)
|
ClockTimeFeedback::new_with_observer(&clock_time_observer)
|
||||||
);
|
);
|
||||||
@ -166,14 +202,15 @@ pub fn fuzz() {
|
|||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
let mut hooks = QemuHooks::new(&emu,
|
||||||
let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default(),QemuStateRestoreHelper::new()));
|
tuple_list!(QemuEdgeCoverageHelper::default(),QemuStateRestoreHelper::new(),
|
||||||
|
QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,0)));
|
||||||
|
|
||||||
// Create a QEMU in-process executor
|
// Create a QEMU in-process executor
|
||||||
let executor = QemuExecutor::new(
|
let executor = QemuExecutor::new(
|
||||||
&mut hooks,
|
&mut hooks,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, clock_time_observer),
|
tuple_list!(edges_observer, clock_time_observer, systemstate_observer),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
@ -198,7 +235,7 @@ pub fn fuzz() {
|
|||||||
} else {
|
} else {
|
||||||
fs::read(s).expect("Input file for DO_SHOWMAP can not be read")
|
fs::read(s).expect("Input file for DO_SHOWMAP can not be read")
|
||||||
};
|
};
|
||||||
fuzzer.execute_input(&mut state, &mut executor, &mut mgr, &BytesInput::new(show_input))
|
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
|
@ -5,6 +5,8 @@ mod fuzzer;
|
|||||||
mod clock;
|
mod clock;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod qemustate;
|
mod qemustate;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod systemstate;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
292
fuzzers/FRET/src/systemstate/feedbacks.rs
Normal file
292
fuzzers/FRET/src/systemstate/feedbacks.rs
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
use libafl::SerdeAny;
|
||||||
|
use libafl::bolts::ownedref::OwnedSlice;
|
||||||
|
use libafl::inputs::BytesInput;
|
||||||
|
use libafl::prelude::UsesInput;
|
||||||
|
use libafl::state::HasNamedMetadata;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use crate::clock::QemuClockObserver;
|
||||||
|
use libafl::corpus::Testcase;
|
||||||
|
use libafl::bolts::tuples::MatchName;
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::Hasher;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use libafl::events::EventFirer;
|
||||||
|
use libafl::state::HasClientPerfMonitor;
|
||||||
|
use libafl::feedbacks::Feedback;
|
||||||
|
use libafl::bolts::tuples::Named;
|
||||||
|
use libafl::Error;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::RefinedFreeRTOSSystemState;
|
||||||
|
use super::FreeRTOSSystemStateMetadata;
|
||||||
|
use super::observers::QemuSystemStateObserver;
|
||||||
|
use petgraph::prelude::DiGraph;
|
||||||
|
use petgraph::graph::NodeIndex;
|
||||||
|
use petgraph::Direction;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
//============================= Feedback
|
||||||
|
|
||||||
|
/// Shared Metadata for a systemstateFeedback
|
||||||
|
#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone, Default)]
|
||||||
|
pub struct systemstateFeedbackState
|
||||||
|
{
|
||||||
|
known_traces: HashMap<u64,(u64,u64,usize)>, // encounters,ticks,length
|
||||||
|
longest: Vec<RefinedFreeRTOSSystemState>,
|
||||||
|
}
|
||||||
|
impl Named for systemstateFeedbackState
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"systemstate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// impl FeedbackState for systemstateFeedbackState
|
||||||
|
// {
|
||||||
|
// fn reset(&mut self) -> Result<(), Error> {
|
||||||
|
// self.longest.clear();
|
||||||
|
// self.known_traces.clear();
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// A Feedback reporting novel System-State Transitions. Depends on [`QemusystemstateObserver`]
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||||
|
pub struct NovelSystemStateFeedback
|
||||||
|
{
|
||||||
|
last_trace: Option<Vec<RefinedFreeRTOSSystemState>>,
|
||||||
|
// known_traces: HashMap<u64,(u64,usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for NovelSystemStateFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor + HasNamedMetadata,
|
||||||
|
{
|
||||||
|
fn is_interesting<EM, OT>(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
manager: &mut EM,
|
||||||
|
input: &S::Input,
|
||||||
|
observers: &OT,
|
||||||
|
exit_kind: &ExitKind,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
EM: EventFirer<State = S>,
|
||||||
|
OT: ObserversTuple<S>
|
||||||
|
{
|
||||||
|
let observer = observers.match_name::<QemuSystemStateObserver>("systemstate")
|
||||||
|
.expect("QemusystemstateObserver not found");
|
||||||
|
let clock_observer = observers.match_name::<QemuClockObserver>("clock") //TODO not fixed
|
||||||
|
.expect("QemusystemstateObserver not found");
|
||||||
|
let feedbackstate = state
|
||||||
|
.named_metadata_mut()
|
||||||
|
.get_mut::<systemstateFeedbackState>("systemstate")
|
||||||
|
.unwrap();
|
||||||
|
// 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) {
|
||||||
|
None => {
|
||||||
|
is_novel = true;
|
||||||
|
feedbackstate.known_traces.insert(somehash,(1,clock_observer.last_runtime(),observer.last_run.len()));
|
||||||
|
}
|
||||||
|
Some(s) => {
|
||||||
|
s.0+=1;
|
||||||
|
if s.1 < clock_observer.last_runtime() {
|
||||||
|
s.1 = clock_observer.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, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
|
||||||
|
let a = self.last_trace.take();
|
||||||
|
match a {
|
||||||
|
Some(s) => testcase.metadata_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: &S::Input) -> Result<(), Error> {
|
||||||
|
self.last_trace = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for NovelSystemStateFeedback
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"systemstate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================
|
||||||
|
|
||||||
|
pub fn match_traces(target: &Vec<RefinedFreeRTOSSystemState>, last: &Vec<RefinedFreeRTOSSystemState>) -> bool {
|
||||||
|
let mut ret = true;
|
||||||
|
if target.len() > last.len() {return false;}
|
||||||
|
for i in 0..target.len() {
|
||||||
|
ret &= target[i].current_task.task_name==last[i].current_task.task_name;
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
pub fn match_traces_name(target: &Vec<String>, last: &Vec<RefinedFreeRTOSSystemState>) -> bool {
|
||||||
|
let mut ret = true;
|
||||||
|
if target.len() > last.len() {return false;}
|
||||||
|
for i in 0..target.len() {
|
||||||
|
ret &= target[i]==last[i].current_task.task_name;
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Feedback reporting novel System-State Transitions. Depends on [`QemusystemstateObserver`]
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||||
|
pub struct HitsystemstateFeedback
|
||||||
|
{
|
||||||
|
target: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for HitsystemstateFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor,
|
||||||
|
{
|
||||||
|
fn is_interesting<EM, OT>(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
manager: &mut EM,
|
||||||
|
input: &S::Input,
|
||||||
|
observers: &OT,
|
||||||
|
exit_kind: &ExitKind,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
EM: EventFirer<State = S>,
|
||||||
|
OT: ObserversTuple<S>
|
||||||
|
{
|
||||||
|
let observer = observers.match_name::<QemuSystemStateObserver>("systemstate")
|
||||||
|
.expect("QemusystemstateObserver not found");
|
||||||
|
// Do Stuff
|
||||||
|
match &self.target {
|
||||||
|
Some(s) => {
|
||||||
|
// #[cfg(debug_assertions)] eprintln!("Hit systemstate Feedback trigger");
|
||||||
|
Ok(match_traces_name(s, &observer.last_run))
|
||||||
|
},
|
||||||
|
None => Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for HitsystemstateFeedback
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"hit_systemstate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HitsystemstateFeedback {
|
||||||
|
pub fn new(target: Option<Vec<RefinedFreeRTOSSystemState>>) -> Self {
|
||||||
|
Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//=========================== Debugging Feedback
|
||||||
|
/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemusystemstateObserver`]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DumpSystraceFeedback
|
||||||
|
{
|
||||||
|
dumpfile: Option<PathBuf>,
|
||||||
|
dump_metadata: bool,
|
||||||
|
last_trace: Option<Vec<RefinedFreeRTOSSystemState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for DumpSystraceFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor,
|
||||||
|
{
|
||||||
|
fn is_interesting<EM, OT>(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
manager: &mut EM,
|
||||||
|
input: &S::Input,
|
||||||
|
observers: &OT,
|
||||||
|
exit_kind: &ExitKind,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
EM: EventFirer<State = S>,
|
||||||
|
OT: ObserversTuple<S>
|
||||||
|
{
|
||||||
|
let observer = observers.match_name::<QemuSystemStateObserver>("systemstate")
|
||||||
|
.expect("QemusystemstateObserver not found");
|
||||||
|
match &self.dumpfile {
|
||||||
|
Some(s) => {
|
||||||
|
std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file");
|
||||||
|
self.dumpfile = None
|
||||||
|
},
|
||||||
|
None => if !self.dump_metadata {println!("{:?}",observer.last_run);}
|
||||||
|
};
|
||||||
|
if self.dump_metadata {self.last_trace=Some(observer.last_run.clone());}
|
||||||
|
Ok(!self.dump_metadata)
|
||||||
|
}
|
||||||
|
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||||
|
#[inline]
|
||||||
|
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
|
||||||
|
if !self.dump_metadata {return Ok(());}
|
||||||
|
let a = self.last_trace.take();
|
||||||
|
match a {
|
||||||
|
Some(s) => testcase.metadata_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: &S::Input) -> Result<(), Error> {
|
||||||
|
self.last_trace = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for DumpSystraceFeedback
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"Dumpsystemstate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DumpSystraceFeedback
|
||||||
|
{
|
||||||
|
/// Creates a new [`DumpSystraceFeedback`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {dumpfile: None, dump_metadata: false, last_trace: None}
|
||||||
|
}
|
||||||
|
pub fn with_dump(dumpfile: Option<PathBuf>) -> Self {
|
||||||
|
Self {dumpfile: dumpfile, dump_metadata: false, last_trace: None}
|
||||||
|
}
|
||||||
|
pub fn metadata_only() -> Self {
|
||||||
|
Self {dumpfile: None, dump_metadata: true, last_trace: None}
|
||||||
|
}
|
||||||
|
}
|
122
fuzzers/FRET/src/systemstate/freertos.rs
Normal file
122
fuzzers/FRET/src/systemstate/freertos.rs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#![allow(non_camel_case_types,non_snake_case,non_upper_case_globals,deref_nullptr)]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
// Manual Types
|
||||||
|
use libafl_qemu::Emulator;
|
||||||
|
|
||||||
|
/*========== Start of generated Code =============*/
|
||||||
|
pub type char_ptr = ::std::os::raw::c_uint;
|
||||||
|
pub type ListItem_t_ptr = ::std::os::raw::c_uint;
|
||||||
|
pub type StackType_t_ptr = ::std::os::raw::c_uint;
|
||||||
|
pub type void_ptr = ::std::os::raw::c_uint;
|
||||||
|
pub type tskTaskControlBlock_ptr = ::std::os::raw::c_uint;
|
||||||
|
pub type xLIST_ptr = ::std::os::raw::c_uint;
|
||||||
|
pub type xLIST_ITEM_ptr = ::std::os::raw::c_uint;
|
||||||
|
/* automatically generated by rust-bindgen 0.59.2 */
|
||||||
|
|
||||||
|
pub type __uint8_t = ::std::os::raw::c_uchar;
|
||||||
|
pub type __uint16_t = ::std::os::raw::c_ushort;
|
||||||
|
pub type __uint32_t = ::std::os::raw::c_uint;
|
||||||
|
pub type StackType_t = u32;
|
||||||
|
pub type UBaseType_t = ::std::os::raw::c_uint;
|
||||||
|
pub type TickType_t = u32;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct xLIST_ITEM {
|
||||||
|
pub xItemValue: TickType_t,
|
||||||
|
pub pxNext: xLIST_ITEM_ptr,
|
||||||
|
pub pxPrevious: xLIST_ITEM_ptr,
|
||||||
|
pub pvOwner: void_ptr,
|
||||||
|
pub pvContainer: xLIST_ptr,
|
||||||
|
}
|
||||||
|
pub type ListItem_t = xLIST_ITEM;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct xMINI_LIST_ITEM {
|
||||||
|
pub xItemValue: TickType_t,
|
||||||
|
pub pxNext: xLIST_ITEM_ptr,
|
||||||
|
pub pxPrevious: xLIST_ITEM_ptr,
|
||||||
|
}
|
||||||
|
pub type MiniListItem_t = xMINI_LIST_ITEM;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct xLIST {
|
||||||
|
pub uxNumberOfItems: UBaseType_t,
|
||||||
|
pub pxIndex: ListItem_t_ptr,
|
||||||
|
pub xListEnd: MiniListItem_t,
|
||||||
|
}
|
||||||
|
pub type List_t = xLIST;
|
||||||
|
pub type TaskHandle_t = tskTaskControlBlock_ptr;
|
||||||
|
pub const eTaskState_eRunning: eTaskState = 0;
|
||||||
|
pub const eTaskState_eReady: eTaskState = 1;
|
||||||
|
pub const eTaskState_eBlocked: eTaskState = 2;
|
||||||
|
pub const eTaskState_eSuspended: eTaskState = 3;
|
||||||
|
pub const eTaskState_eDeleted: eTaskState = 4;
|
||||||
|
pub const eTaskState_eInvalid: eTaskState = 5;
|
||||||
|
pub type eTaskState = ::std::os::raw::c_uint;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct xTASK_STATUS {
|
||||||
|
pub xHandle: TaskHandle_t,
|
||||||
|
pub pcTaskName: char_ptr,
|
||||||
|
pub xTaskNumber: UBaseType_t,
|
||||||
|
pub eCurrentState: eTaskState,
|
||||||
|
pub uxCurrentPriority: UBaseType_t,
|
||||||
|
pub uxBasePriority: UBaseType_t,
|
||||||
|
pub ulRunTimeCounter: u32,
|
||||||
|
pub pxStackBase: StackType_t_ptr,
|
||||||
|
pub usStackHighWaterMark: u16,
|
||||||
|
}
|
||||||
|
pub type TaskStatus_t = xTASK_STATUS;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct tskTaskControlBlock {
|
||||||
|
pub pxTopOfStack: StackType_t_ptr,
|
||||||
|
pub xStateListItem: ListItem_t,
|
||||||
|
pub xEventListItem: ListItem_t,
|
||||||
|
pub uxPriority: UBaseType_t,
|
||||||
|
pub pxStack: StackType_t_ptr,
|
||||||
|
pub pcTaskName: [::std::os::raw::c_char; 10usize],
|
||||||
|
pub uxBasePriority: UBaseType_t,
|
||||||
|
pub uxMutexesHeld: UBaseType_t,
|
||||||
|
pub ulNotifiedValue: [u32; 1usize],
|
||||||
|
pub ucNotifyState: [u8; 1usize],
|
||||||
|
pub ucStaticallyAllocated: u8,
|
||||||
|
pub ucDelayAborted: u8,
|
||||||
|
}
|
||||||
|
pub type tskTCB = tskTaskControlBlock;
|
||||||
|
pub type TCB_t = tskTCB;
|
||||||
|
/*========== End of generated Code =============*/
|
||||||
|
|
||||||
|
pub trait emu_lookup {
|
||||||
|
fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum rtos_struct {
|
||||||
|
TCB_struct(TCB_t),
|
||||||
|
List_struct(List_t),
|
||||||
|
List_Item_struct(ListItem_t),
|
||||||
|
List_MiniItem_struct(MiniListItem_t),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_emu_lookup {
|
||||||
|
($struct_name:ident) => {
|
||||||
|
impl $crate::systemstate::freertos::emu_lookup for $struct_name {
|
||||||
|
fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> $struct_name {
|
||||||
|
let mut tmp : [u8; std::mem::size_of::<$struct_name>()] = [0u8; std::mem::size_of::<$struct_name>()];
|
||||||
|
unsafe {
|
||||||
|
emu.read_mem(addr.into(), &mut tmp);
|
||||||
|
std::mem::transmute::<[u8; std::mem::size_of::<$struct_name>()], $struct_name>(tmp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
impl_emu_lookup!(TCB_t);
|
||||||
|
impl_emu_lookup!(List_t);
|
||||||
|
impl_emu_lookup!(ListItem_t);
|
||||||
|
impl_emu_lookup!(MiniListItem_t);
|
||||||
|
impl_emu_lookup!(void_ptr);
|
||||||
|
impl_emu_lookup!(TaskStatus_t);
|
590
fuzzers/FRET/src/systemstate/graph.rs
Normal file
590
fuzzers/FRET/src/systemstate/graph.rs
Normal file
@ -0,0 +1,590 @@
|
|||||||
|
|
||||||
|
/// Feedbacks organizing SystemStates as a graph
|
||||||
|
use libafl::inputs::HasBytesVec;
|
||||||
|
use libafl::bolts::rands::RandomSeed;
|
||||||
|
use libafl::bolts::rands::StdRand;
|
||||||
|
use libafl::mutators::Mutator;
|
||||||
|
use libafl::mutators::MutationResult;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use libafl::state::HasCorpus;
|
||||||
|
use libafl::state::HasSolutions;
|
||||||
|
use libafl::state::HasRand;
|
||||||
|
use crate::worst::MaxExecsLenFavFactor;
|
||||||
|
use libafl::corpus::MinimizerCorpusScheduler;
|
||||||
|
use libafl::bolts::HasRefCnt;
|
||||||
|
use libafl::bolts::AsSlice;
|
||||||
|
use libafl::bolts::ownedref::OwnedSlice;
|
||||||
|
use libafl::inputs::BytesInput;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use crate::clock::QemuClockObserver;
|
||||||
|
use libafl::corpus::Testcase;
|
||||||
|
use libafl::bolts::tuples::MatchName;
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::Hasher;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use libafl::events::EventFirer;
|
||||||
|
use libafl::state::HasClientPerfMonitor;
|
||||||
|
use libafl::feedbacks::Feedback;
|
||||||
|
use libafl::bolts::tuples::Named;
|
||||||
|
use libafl::Error;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::RefinedFreeRTOSSystemState;
|
||||||
|
use super::FreeRTOSSystemStateMetadata;
|
||||||
|
use super::observers::QemusystemstateObserver;
|
||||||
|
use petgraph::prelude::DiGraph;
|
||||||
|
use petgraph::graph::NodeIndex;
|
||||||
|
use petgraph::Direction;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use libafl::bolts::rands::Rand;
|
||||||
|
|
||||||
|
//============================= Data Structures
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
|
||||||
|
pub struct VariantTuple
|
||||||
|
{
|
||||||
|
pub start_tick: u64,
|
||||||
|
pub end_tick: u64,
|
||||||
|
input_counter: u32,
|
||||||
|
pub input: Vec<u8>, // in the end any kind of input are bytes, regardless of type and lifetime
|
||||||
|
}
|
||||||
|
impl VariantTuple {
|
||||||
|
fn from(other: &RefinedFreeRTOSSystemState,input: Vec<u8>) -> Self {
|
||||||
|
VariantTuple{
|
||||||
|
start_tick: other.start_tick,
|
||||||
|
end_tick: other.end_tick,
|
||||||
|
input_counter: other.input_counter,
|
||||||
|
input: input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||||
|
pub struct SysGraphNode
|
||||||
|
{
|
||||||
|
base: RefinedFreeRTOSSystemState,
|
||||||
|
pub variants: Vec<VariantTuple>,
|
||||||
|
}
|
||||||
|
impl SysGraphNode {
|
||||||
|
fn from(base: RefinedFreeRTOSSystemState, input: Vec<u8>) -> Self {
|
||||||
|
SysGraphNode{variants: vec![VariantTuple::from(&base, input)], base:base }
|
||||||
|
}
|
||||||
|
/// unites the variants of this value with another, draining the other if the bases are equal
|
||||||
|
fn unite(&mut self, other: &mut SysGraphNode) -> bool {
|
||||||
|
if self!=other {return false;}
|
||||||
|
self.variants.append(&mut other.variants);
|
||||||
|
self.variants.dedup();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/// add a Varint from a [`RefinedFreeRTOSSystemState`]
|
||||||
|
fn unite_raw(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec<u8>) -> bool {
|
||||||
|
if &self.base!=other {return false;}
|
||||||
|
self.variants.push(VariantTuple::from(other, input.clone()));
|
||||||
|
self.variants.dedup();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/// add a Varint from a [`RefinedFreeRTOSSystemState`], if it's interesting
|
||||||
|
fn unite_interesting(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec<u8>) -> bool {
|
||||||
|
if &self.base!=other {return false;}
|
||||||
|
let interesting =
|
||||||
|
self.variants.iter().all(|x| x.end_tick-x.start_tick<other.end_tick-other.start_tick) || // longest variant
|
||||||
|
self.variants.iter().all(|x| x.end_tick-x.start_tick>other.end_tick-other.start_tick) || // shortest variant
|
||||||
|
self.variants.iter().all(|x| x.input_counter>other.input_counter) || // longest input
|
||||||
|
self.variants.iter().all(|x| x.input_counter<other.input_counter); // shortest input
|
||||||
|
if interesting {
|
||||||
|
let var = VariantTuple::from(other, input.clone());
|
||||||
|
self.variants.push(var);
|
||||||
|
}
|
||||||
|
return interesting;
|
||||||
|
}
|
||||||
|
pub fn get_taskname(&self) -> &str {
|
||||||
|
&self.base.current_task.task_name
|
||||||
|
}
|
||||||
|
pub fn get_input_counts(&self) -> Vec<u32> {
|
||||||
|
self.variants.iter().map(|x| x.input_counter).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PartialEq for SysGraphNode {
|
||||||
|
fn eq(&self, other: &SysGraphNode) -> bool {
|
||||||
|
self.base==other.base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct SysGraphMetadata {
|
||||||
|
pub inner: Vec<NodeIndex>,
|
||||||
|
indices: Vec<usize>,
|
||||||
|
tcref: isize,
|
||||||
|
}
|
||||||
|
impl SysGraphMetadata {
|
||||||
|
pub fn new(inner: Vec<NodeIndex>) -> Self{
|
||||||
|
Self {indices: inner.iter().map(|x| x.index()).collect(), inner: inner, tcref: 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsSlice<usize> for SysGraphMetadata {
|
||||||
|
/// Convert the slice of system-states to a slice of hashes over enumerated states
|
||||||
|
fn as_slice(&self) -> &[usize] {
|
||||||
|
self.indices.as_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasRefCnt for SysGraphMetadata {
|
||||||
|
fn refcnt(&self) -> isize {
|
||||||
|
self.tcref
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refcnt_mut(&mut self) -> &mut isize {
|
||||||
|
&mut self.tcref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libafl::impl_serdeany!(SysGraphMetadata);
|
||||||
|
|
||||||
|
pub type GraphMaximizerCorpusScheduler<CS, I, S> =
|
||||||
|
MinimizerCorpusScheduler<CS, MaxExecsLenFavFactor<I>, I, SysGraphMetadata, S>;
|
||||||
|
|
||||||
|
//============================= Graph Feedback
|
||||||
|
|
||||||
|
/// Improved System State Graph
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||||
|
pub struct SysGraphFeedbackState
|
||||||
|
{
|
||||||
|
pub graph: DiGraph<SysGraphNode, ()>,
|
||||||
|
entrypoint: NodeIndex,
|
||||||
|
exit: NodeIndex,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
impl SysGraphFeedbackState
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut graph = DiGraph::<SysGraphNode, ()>::new();
|
||||||
|
let mut entry = SysGraphNode::default();
|
||||||
|
entry.base.current_task.task_name="Start".to_string();
|
||||||
|
let mut exit = SysGraphNode::default();
|
||||||
|
exit.base.current_task.task_name="End".to_string();
|
||||||
|
let entry = graph.add_node(entry);
|
||||||
|
let exit = graph.add_node(exit);
|
||||||
|
Self {graph: graph, entrypoint: entry, exit: exit, name: String::from("SysMap")}
|
||||||
|
}
|
||||||
|
fn insert(&mut self, list: Vec<RefinedFreeRTOSSystemState>, input: &Vec<u8>) {
|
||||||
|
let mut current_index = self.entrypoint;
|
||||||
|
for n in list {
|
||||||
|
let mut done = false;
|
||||||
|
for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) {
|
||||||
|
if n == self.graph[i].base {
|
||||||
|
done = true;
|
||||||
|
current_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !done {
|
||||||
|
let j = self.graph.add_node(SysGraphNode::from(n,input.clone()));
|
||||||
|
self.graph.add_edge(current_index, j, ());
|
||||||
|
current_index = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Try adding a system state path from a [Vec<RefinedFreeRTOSSystemState>], return true if the path was interesting
|
||||||
|
fn update(&mut self, list: &Vec<RefinedFreeRTOSSystemState>, input: &Vec<u8>) -> (bool, Vec<NodeIndex>) {
|
||||||
|
let mut current_index = self.entrypoint;
|
||||||
|
let mut novel = false;
|
||||||
|
let mut trace : Vec<NodeIndex> = vec![current_index];
|
||||||
|
for n in list {
|
||||||
|
let mut matching : Option<NodeIndex> = None;
|
||||||
|
for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) {
|
||||||
|
let tmp = &self.graph[i];
|
||||||
|
if n == &tmp.base {
|
||||||
|
matching = Some(i);
|
||||||
|
current_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match matching {
|
||||||
|
None => {
|
||||||
|
novel = true;
|
||||||
|
let j = self.graph.add_node(SysGraphNode::from(n.clone(),input.clone()));
|
||||||
|
self.graph.add_edge(current_index, j, ());
|
||||||
|
current_index = j;
|
||||||
|
},
|
||||||
|
Some(i) => {
|
||||||
|
novel |= self.graph[i].unite_interesting(&n, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace.push(current_index);
|
||||||
|
}
|
||||||
|
self.graph.update_edge(current_index, self.exit, ()); // every path ends in the exit noded
|
||||||
|
return (novel, trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Named for SysGraphFeedbackState
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FeedbackState for SysGraphFeedbackState
|
||||||
|
{
|
||||||
|
fn reset(&mut self) -> Result<(), Error> {
|
||||||
|
self.graph.clear();
|
||||||
|
let mut entry = SysGraphNode::default();
|
||||||
|
entry.base.current_task.task_name="Start".to_string();
|
||||||
|
let mut exit = SysGraphNode::default();
|
||||||
|
exit.base.current_task.task_name="End".to_string();
|
||||||
|
self.entrypoint = self.graph.add_node(entry);
|
||||||
|
self.exit = self.graph.add_node(exit);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Feedback reporting novel System-State Transitions. Depends on [`QemusystemstateObserver`]
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||||
|
pub struct SysMapFeedback
|
||||||
|
{
|
||||||
|
name: String,
|
||||||
|
last_trace: Option<Vec<NodeIndex>>,
|
||||||
|
}
|
||||||
|
impl SysMapFeedback {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {name: String::from("SysMapFeedback"), last_trace: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, S> Feedback<I, S> for SysMapFeedback
|
||||||
|
where
|
||||||
|
I: Input,
|
||||||
|
S: HasClientPerfMonitor + HasFeedbackStates,
|
||||||
|
{
|
||||||
|
fn is_interesting<EM, OT>(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
_manager: &mut EM,
|
||||||
|
_input: &I,
|
||||||
|
observers: &OT,
|
||||||
|
_exit_kind: &ExitKind,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
EM: EventFirer<I>,
|
||||||
|
OT: ObserversTuple<I, S>,
|
||||||
|
{
|
||||||
|
let observer = observers.match_name::<QemusystemstateObserver>("systemstate")
|
||||||
|
.expect("QemusystemstateObserver not found");
|
||||||
|
let feedbackstate = state
|
||||||
|
.feedback_states_mut()
|
||||||
|
.match_name_mut::<SysGraphFeedbackState>("SysMap")
|
||||||
|
.unwrap();
|
||||||
|
let ret = feedbackstate.update(&observer.last_run, &observer.last_input);
|
||||||
|
self.last_trace = Some(ret.1);
|
||||||
|
Ok(ret.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||||
|
#[inline]
|
||||||
|
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
|
||||||
|
let a = self.last_trace.take();
|
||||||
|
match a {
|
||||||
|
Some(s) => testcase.metadata_mut().insert(SysGraphMetadata::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> {
|
||||||
|
self.last_trace = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Named for SysMapFeedback
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================= Mutators
|
||||||
|
//=============================== Snippets
|
||||||
|
pub struct RandGraphSnippetMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||||
|
{
|
||||||
|
phantom: PhantomData<(I, S)>,
|
||||||
|
}
|
||||||
|
impl<I, S> RandGraphSnippetMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
RandGraphSnippetMutator{phantom: PhantomData}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<I, S> Mutator<I, S> for RandGraphSnippetMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||||
|
{
|
||||||
|
fn mutate(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
input: &mut I,
|
||||||
|
_stage_idx: i32
|
||||||
|
) -> Result<MutationResult, Error>
|
||||||
|
{
|
||||||
|
// need our own random generator, because borrowing rules
|
||||||
|
let mut myrand = StdRand::new();
|
||||||
|
let tmp = &mut state.rand_mut();
|
||||||
|
myrand.set_seed(tmp.next());
|
||||||
|
drop(tmp);
|
||||||
|
|
||||||
|
let feedbackstate = state
|
||||||
|
.feedback_states()
|
||||||
|
.match_name::<SysGraphFeedbackState>("SysMap")
|
||||||
|
.unwrap();
|
||||||
|
let g = &feedbackstate.graph;
|
||||||
|
let tmp = state.metadata().get::<SysGraphMetadata>();
|
||||||
|
if tmp.is_none() { // if there are no metadata it was probably not interesting anyways
|
||||||
|
return Ok(MutationResult::Skipped);
|
||||||
|
}
|
||||||
|
let trace =tmp.expect("SysGraphMetadata not found");
|
||||||
|
// follow the path, extract snippets from last reads, find common snippets.
|
||||||
|
// those are likley keys parts. choose random parts from other sibling traces
|
||||||
|
let sibling_inputs : Vec<&Vec<u8>>= g[*trace.inner.last().unwrap()].variants.iter().map(|x| &x.input).collect();
|
||||||
|
let mut snippet_collector = vec![];
|
||||||
|
let mut per_input_counters = HashMap::<&Vec<u8>,usize>::new(); // ugly workaround to track multiple inputs
|
||||||
|
for t in &trace.inner {
|
||||||
|
let node = &g[*t];
|
||||||
|
let mut per_node_snippets = HashMap::<&Vec<u8>,&[u8]>::new();
|
||||||
|
for v in &node.variants {
|
||||||
|
match per_input_counters.get_mut(&v.input) {
|
||||||
|
None => {
|
||||||
|
if sibling_inputs.iter().any(|x| *x==&v.input) { // only collect info about siblin inputs from target
|
||||||
|
per_input_counters.insert(&v.input, v.input_counter.try_into().unwrap());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(x) => {
|
||||||
|
let x_u = *x;
|
||||||
|
if x_u<v.input_counter as usize {
|
||||||
|
*x=v.input_counter as usize;
|
||||||
|
per_node_snippets.insert(&v.input,&v.input[x_u..v.input_counter as usize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snippet_collector.push(per_node_snippets);
|
||||||
|
}
|
||||||
|
let mut new_input : Vec<u8> = vec![];
|
||||||
|
for c in snippet_collector {
|
||||||
|
new_input.extend_from_slice(myrand.choose(c).1);
|
||||||
|
}
|
||||||
|
for i in new_input.iter().enumerate() {
|
||||||
|
input.bytes_mut()[i.0]=*i.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(MutationResult::Mutated)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec(
|
||||||
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
|
_stage_idx: i32,
|
||||||
|
_corpus_idx: Option<usize>
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, S> Named for RandGraphSnippetMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"RandGraphSnippetMutator"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//=============================== Snippets
|
||||||
|
pub struct RandInputSnippetMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||||
|
{
|
||||||
|
phantom: PhantomData<(I, S)>,
|
||||||
|
}
|
||||||
|
impl<I, S> RandInputSnippetMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
RandInputSnippetMutator{phantom: PhantomData}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<I, S> Mutator<I, S> for RandInputSnippetMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||||
|
{
|
||||||
|
fn mutate(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
input: &mut I,
|
||||||
|
_stage_idx: i32
|
||||||
|
) -> Result<MutationResult, Error>
|
||||||
|
{
|
||||||
|
// need our own random generator, because borrowing rules
|
||||||
|
let mut myrand = StdRand::new();
|
||||||
|
let tmp = &mut state.rand_mut();
|
||||||
|
myrand.set_seed(tmp.next());
|
||||||
|
drop(tmp);
|
||||||
|
|
||||||
|
let feedbackstate = state
|
||||||
|
.feedback_states()
|
||||||
|
.match_name::<SysGraphFeedbackState>("SysMap")
|
||||||
|
.unwrap();
|
||||||
|
let g = &feedbackstate.graph;
|
||||||
|
let tmp = state.metadata().get::<SysGraphMetadata>();
|
||||||
|
if tmp.is_none() { // if there are no metadata it was probably not interesting anyways
|
||||||
|
return Ok(MutationResult::Skipped);
|
||||||
|
}
|
||||||
|
let trace = tmp.expect("SysGraphMetadata not found");
|
||||||
|
|
||||||
|
let mut collection : Vec<Vec<u8>> = Vec::new();
|
||||||
|
let mut current_pointer : usize = 0;
|
||||||
|
for t in &trace.inner {
|
||||||
|
let node = &g[*t];
|
||||||
|
for v in &node.variants {
|
||||||
|
if v.input == input.bytes() {
|
||||||
|
if v.input_counter > current_pointer.try_into().unwrap() {
|
||||||
|
collection.push(v.input[current_pointer..v.input_counter as usize].to_owned());
|
||||||
|
current_pointer = v.input_counter as usize;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let index_to_mutate = myrand.below(collection.len() as u64) as usize;
|
||||||
|
for i in 0..collection[index_to_mutate].len() {
|
||||||
|
collection[index_to_mutate][i] = myrand.below(0xFF) as u8;
|
||||||
|
}
|
||||||
|
for i in collection.concat().iter().enumerate() {
|
||||||
|
input.bytes_mut()[i.0]=*i.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(MutationResult::Mutated)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec(
|
||||||
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
|
_stage_idx: i32,
|
||||||
|
_corpus_idx: Option<usize>
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, S> Named for RandInputSnippetMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"RandInputSnippetMutator"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//=============================== Suffix
|
||||||
|
pub struct RandGraphSuffixMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||||
|
{
|
||||||
|
phantom: PhantomData<(I, S)>,
|
||||||
|
}
|
||||||
|
impl<I, S> RandGraphSuffixMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
RandGraphSuffixMutator{phantom: PhantomData}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<I, S> Mutator<I, S> for RandGraphSuffixMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||||
|
{
|
||||||
|
fn mutate(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
input: &mut I,
|
||||||
|
_stage_idx: i32
|
||||||
|
) -> Result<MutationResult, Error>
|
||||||
|
{
|
||||||
|
// need our own random generator, because borrowing rules
|
||||||
|
let mut myrand = StdRand::new();
|
||||||
|
let tmp = &mut state.rand_mut();
|
||||||
|
myrand.set_seed(tmp.next());
|
||||||
|
drop(tmp);
|
||||||
|
|
||||||
|
let feedbackstate = state
|
||||||
|
.feedback_states()
|
||||||
|
.match_name::<SysGraphFeedbackState>("SysMap")
|
||||||
|
.unwrap();
|
||||||
|
let g = &feedbackstate.graph;
|
||||||
|
let tmp = state.metadata().get::<SysGraphMetadata>();
|
||||||
|
if tmp.is_none() { // if there are no metadata it was probably not interesting anyways
|
||||||
|
return Ok(MutationResult::Skipped);
|
||||||
|
}
|
||||||
|
let trace =tmp.expect("SysGraphMetadata not found");
|
||||||
|
// follow the path, extract snippets from last reads, find common snippets.
|
||||||
|
// those are likley keys parts. choose random parts from other sibling traces
|
||||||
|
let inp_c_end = g[*trace.inner.last().unwrap()].base.input_counter;
|
||||||
|
let mut num_to_reverse = myrand.below(trace.inner.len().try_into().unwrap());
|
||||||
|
for t in trace.inner.iter().rev() {
|
||||||
|
let int_c_prefix = g[*t].base.input_counter;
|
||||||
|
if int_c_prefix < inp_c_end {
|
||||||
|
num_to_reverse-=1;
|
||||||
|
if num_to_reverse<=0 {
|
||||||
|
let mut new_input=input.bytes()[..(int_c_prefix as usize)].to_vec();
|
||||||
|
let mut ext : Vec<u8> = (int_c_prefix..inp_c_end).map(|_| myrand.next().to_le_bytes()).flatten().collect();
|
||||||
|
new_input.append(&mut ext);
|
||||||
|
for i in new_input.iter().enumerate() {
|
||||||
|
if input.bytes_mut().len()>i.0 {
|
||||||
|
input.bytes_mut()[i.0]=*i.1;
|
||||||
|
}
|
||||||
|
else { break };
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(MutationResult::Mutated)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec(
|
||||||
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
|
_stage_idx: i32,
|
||||||
|
_corpus_idx: Option<usize>
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, S> Named for RandGraphSuffixMutator<I, S>
|
||||||
|
where
|
||||||
|
I: Input + HasBytesVec,
|
||||||
|
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"RandGraphSuffixMutator"
|
||||||
|
}
|
||||||
|
}
|
141
fuzzers/FRET/src/systemstate/helpers.rs
Normal file
141
fuzzers/FRET/src/systemstate/helpers.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
use std::io::Write;
|
||||||
|
use libafl::prelude::UsesInput;
|
||||||
|
use libafl_qemu::GuestAddr;
|
||||||
|
use libafl_qemu::QemuHooks;
|
||||||
|
use libafl_qemu::edges::QemuEdgesMapMetadata;
|
||||||
|
use libafl_qemu::emu;
|
||||||
|
use libafl_qemu::hooks;
|
||||||
|
use crate::systemstate::RawFreeRTOSSystemState;
|
||||||
|
use crate::systemstate::CURRENT_SYSTEMSTATE_VEC;
|
||||||
|
use crate::systemstate::NUM_PRIOS;
|
||||||
|
use super::freertos::TCB_t;
|
||||||
|
use super::freertos::rtos_struct::List_Item_struct;
|
||||||
|
use super::freertos::rtos_struct::*;
|
||||||
|
use super::freertos;
|
||||||
|
|
||||||
|
use libafl_qemu::{
|
||||||
|
helper::{QemuHelper, QemuHelperTuple},
|
||||||
|
// edges::SAVED_JUMP,
|
||||||
|
};
|
||||||
|
|
||||||
|
//============================= Struct definitions
|
||||||
|
|
||||||
|
pub static mut INTR_OFFSET : Option<u64> = None;
|
||||||
|
pub static mut INTR_DONE : bool = true;
|
||||||
|
|
||||||
|
// only used when inputs are injected
|
||||||
|
pub static mut NEXT_INPUT : Vec<u8> = Vec::new();
|
||||||
|
|
||||||
|
//============================= Qemu Helper
|
||||||
|
|
||||||
|
/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct QemuSystemStateHelper {
|
||||||
|
kerneladdr: u32,
|
||||||
|
tcb_addr: u32,
|
||||||
|
ready_queues: u32,
|
||||||
|
input_counter: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuSystemStateHelper {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
kerneladdr: u32,
|
||||||
|
tcb_addr: u32,
|
||||||
|
ready_queues: u32,
|
||||||
|
input_counter: u32,
|
||||||
|
) -> Self {
|
||||||
|
QemuSystemStateHelper {
|
||||||
|
kerneladdr,
|
||||||
|
tcb_addr: tcb_addr,
|
||||||
|
ready_queues: ready_queues,
|
||||||
|
input_counter: input_counter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> QemuHelper<S> for QemuSystemStateHelper
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
fn first_exec<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
|
||||||
|
where
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
_hooks.instruction(self.kerneladdr, exec_syscall_hook::<QT, S>, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exec_syscall_hook<QT, S>(
|
||||||
|
hooks: &mut QemuHooks<'_, QT, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
_pc: u32,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
let emulator = hooks.emulator();
|
||||||
|
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
|
||||||
|
let listbytes : u32 = u32::try_from(std::mem::size_of::<freertos::List_t>()).unwrap();
|
||||||
|
let mut systemstate = RawFreeRTOSSystemState::default();
|
||||||
|
unsafe {
|
||||||
|
// TODO: investigate why can_do_io is not set sometimes, as this is just a workaround
|
||||||
|
let c = emulator.current_cpu().unwrap();
|
||||||
|
let can_do_io = (*c.raw_ptr()).can_do_io;
|
||||||
|
(*c.raw_ptr()).can_do_io = 1;
|
||||||
|
systemstate.qemu_tick = emu::icount_get_raw();
|
||||||
|
(*c.raw_ptr()).can_do_io = can_do_io;
|
||||||
|
}
|
||||||
|
let mut buf : [u8; 4] = [0,0,0,0];
|
||||||
|
// unsafe { emulator.read_mem(h.input_counter.into(), &mut buf) };
|
||||||
|
systemstate.input_counter = u32::from_le_bytes(buf);
|
||||||
|
|
||||||
|
let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr);
|
||||||
|
if curr_tcb_addr == 0 {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr);
|
||||||
|
|
||||||
|
// unsafe {
|
||||||
|
// match SAVED_JUMP.take() {
|
||||||
|
// Some(s) => {
|
||||||
|
// systemstate.last_pc = Some(s.0);
|
||||||
|
// },
|
||||||
|
// None => (),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName));
|
||||||
|
|
||||||
|
for i in 0..NUM_PRIOS {
|
||||||
|
let target : u32 = listbytes*u32::try_from(i).unwrap()+h.ready_queues;
|
||||||
|
systemstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target);
|
||||||
|
// println!("List at {}: {:?}",target, systemstate.prio_ready_lists[i]);
|
||||||
|
let mut next_index = systemstate.prio_ready_lists[i].pxIndex;
|
||||||
|
for _j in 0..systemstate.prio_ready_lists[i].uxNumberOfItems {
|
||||||
|
// always jump over the xListEnd marker
|
||||||
|
if (target..target+listbytes).contains(&next_index) {
|
||||||
|
let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index);
|
||||||
|
let new_next_index=next_item.pxNext;
|
||||||
|
systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item));
|
||||||
|
next_index = new_next_index;
|
||||||
|
}
|
||||||
|
let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index);
|
||||||
|
// println!("Item at {}: {:?}",next_index,next_item);
|
||||||
|
assert_eq!(next_item.pvContainer,target);
|
||||||
|
let new_next_index=next_item.pxNext;
|
||||||
|
let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner);
|
||||||
|
// println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb);
|
||||||
|
systemstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone()));
|
||||||
|
systemstate.dumping_ground.insert(next_index,List_Item_struct(next_item));
|
||||||
|
next_index=new_next_index;
|
||||||
|
}
|
||||||
|
// Handle edge case where the end marker was not included yet
|
||||||
|
if (target..target+listbytes).contains(&next_index) {
|
||||||
|
let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index);
|
||||||
|
systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); }
|
||||||
|
}
|
165
fuzzers/FRET/src/systemstate/mod.rs
Normal file
165
fuzzers/FRET/src/systemstate/mod.rs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
//! systemstate referes to the State of a FreeRTOS fuzzing target
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use libafl::bolts::HasRefCnt;
|
||||||
|
use libafl::bolts::AsSlice;
|
||||||
|
use std::hash::Hasher;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use freertos::TCB_t;
|
||||||
|
|
||||||
|
pub mod freertos;
|
||||||
|
pub mod helpers;
|
||||||
|
pub mod observers;
|
||||||
|
pub mod feedbacks;
|
||||||
|
// pub mod graph;
|
||||||
|
// pub mod mutators;
|
||||||
|
|
||||||
|
#[cfg(feature = "fuzz_interrupt")]
|
||||||
|
pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes
|
||||||
|
#[cfg(not(feature = "fuzz_interrupt"))]
|
||||||
|
pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes
|
||||||
|
pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const NUM_PRIOS: usize = 5;
|
||||||
|
|
||||||
|
//============================= Struct definitions
|
||||||
|
/// Raw info Dump from Qemu
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct RawFreeRTOSSystemState {
|
||||||
|
qemu_tick: u64,
|
||||||
|
current_tcb: TCB_t,
|
||||||
|
prio_ready_lists: [freertos::List_t; NUM_PRIOS],
|
||||||
|
dumping_ground: HashMap<u32,freertos::rtos_struct>,
|
||||||
|
input_counter: u32,
|
||||||
|
last_pc: Option<u64>,
|
||||||
|
}
|
||||||
|
/// List of system state dumps from QemuHelpers
|
||||||
|
static mut CURRENT_SYSTEMSTATE_VEC: Vec<RawFreeRTOSSystemState> = vec![];
|
||||||
|
|
||||||
|
/// A reduced version of freertos::TCB_t
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
pub struct RefinedTCB {
|
||||||
|
task_name: String,
|
||||||
|
priority: u32,
|
||||||
|
base_priority: u32,
|
||||||
|
mutexes_held: u32,
|
||||||
|
notify_value: u32,
|
||||||
|
notify_state: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for RefinedTCB {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.task_name.hash(state);
|
||||||
|
// self.priority.hash(state);
|
||||||
|
// self.mutexes_held.hash(state);
|
||||||
|
// self.notify_state.hash(state);
|
||||||
|
// self.notify_value.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RefinedTCB {
|
||||||
|
pub fn from_tcb(input: &TCB_t) -> Self {
|
||||||
|
unsafe {
|
||||||
|
let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName);
|
||||||
|
let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::<String>();
|
||||||
|
Self {
|
||||||
|
task_name: name,
|
||||||
|
priority: input.uxPriority,
|
||||||
|
base_priority: input.uxBasePriority,
|
||||||
|
mutexes_held: input.uxMutexesHeld,
|
||||||
|
notify_value: input.ulNotifiedValue[0],
|
||||||
|
notify_state: input.ucNotifyState[0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_tcb_owned(input: TCB_t) -> Self {
|
||||||
|
unsafe {
|
||||||
|
let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName);
|
||||||
|
let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::<String>();
|
||||||
|
Self {
|
||||||
|
task_name: name,
|
||||||
|
priority: input.uxPriority,
|
||||||
|
base_priority: input.uxBasePriority,
|
||||||
|
mutexes_held: input.uxMutexesHeld,
|
||||||
|
notify_value: input.ulNotifiedValue[0],
|
||||||
|
notify_state: input.ucNotifyState[0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Refined information about the states an execution transitioned between
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct RefinedFreeRTOSSystemState {
|
||||||
|
start_tick: u64,
|
||||||
|
end_tick: u64,
|
||||||
|
last_pc: Option<u64>,
|
||||||
|
input_counter: u32,
|
||||||
|
current_task: RefinedTCB,
|
||||||
|
ready_list_after: Vec<RefinedTCB>,
|
||||||
|
}
|
||||||
|
impl PartialEq for RefinedFreeRTOSSystemState {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.current_task == other.current_task && self.ready_list_after == other.ready_list_after &&
|
||||||
|
self.last_pc == other.last_pc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for RefinedFreeRTOSSystemState {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.current_task.hash(state);
|
||||||
|
self.ready_list_after.hash(state);
|
||||||
|
self.last_pc.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl RefinedFreeRTOSSystemState {
|
||||||
|
fn get_time(&self) -> u64 {
|
||||||
|
self.end_tick-self.start_tick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct FreeRTOSSystemStateMetadata {
|
||||||
|
inner: Vec<RefinedFreeRTOSSystemState>,
|
||||||
|
indices: Vec<usize>, // Hashed enumeration of States
|
||||||
|
tcref: isize,
|
||||||
|
}
|
||||||
|
impl FreeRTOSSystemStateMetadata {
|
||||||
|
pub fn new(inner: Vec<RefinedFreeRTOSSystemState>) -> Self{
|
||||||
|
let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect();
|
||||||
|
Self {inner: inner, indices: tmp, tcref: 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn compute_hash<T>(obj: T) -> u64
|
||||||
|
where
|
||||||
|
T: Hash
|
||||||
|
{
|
||||||
|
let mut s = DefaultHasher::new();
|
||||||
|
obj.hash(&mut s);
|
||||||
|
s.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsSlice for FreeRTOSSystemStateMetadata {
|
||||||
|
/// Convert the slice of system-states to a slice of hashes over enumerated states
|
||||||
|
fn as_slice(&self) -> &[usize] {
|
||||||
|
self.indices.as_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Entry = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasRefCnt for FreeRTOSSystemStateMetadata {
|
||||||
|
fn refcnt(&self) -> isize {
|
||||||
|
self.tcref
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refcnt_mut(&mut self) -> &mut isize {
|
||||||
|
&mut self.tcref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libafl::impl_serdeany!(FreeRTOSSystemStateMetadata);
|
119
fuzzers/FRET/src/systemstate/mutators.rs
Normal file
119
fuzzers/FRET/src/systemstate/mutators.rs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
use crate::systemstate::graph::SysGraphMetadata;
|
||||||
|
use crate::systemstate::graph::SysGraphNode;
|
||||||
|
use crate::systemstate::IRQ_INPUT_OFFSET;
|
||||||
|
use crate::systemstate::IRQ_INPUT_BYTES_NUMBER;
|
||||||
|
use crate::systemstate::graph::SysGraphFeedbackState;
|
||||||
|
use libafl::inputs::HasBytesVec;
|
||||||
|
use libafl::bolts::rands::RandomSeed;
|
||||||
|
use libafl::bolts::rands::StdRand;
|
||||||
|
use libafl::mutators::Mutator;
|
||||||
|
use libafl::mutators::MutationResult;
|
||||||
|
use libafl::prelude::UsesInput;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use libafl::state::HasCorpus;
|
||||||
|
use libafl::state::HasSolutions;
|
||||||
|
use libafl::state::HasRand;
|
||||||
|
|
||||||
|
use libafl::bolts::tuples::MatchName;
|
||||||
|
use libafl::bolts::tuples::Named;
|
||||||
|
use libafl::Error;
|
||||||
|
use libafl::{inputs::Input, state::HasMetadata};
|
||||||
|
|
||||||
|
use super::FreeRTOSSystemStateMetadata;
|
||||||
|
|
||||||
|
use libafl::bolts::rands::Rand;
|
||||||
|
|
||||||
|
|
||||||
|
//=============================== Interrupt
|
||||||
|
/// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata
|
||||||
|
pub struct InterruptShifterMutator<S>
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
}
|
||||||
|
impl<S> InterruptShifterMutator<S>
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
InterruptShifterMutator{phantom: PhantomData}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<S> Mutator<S> for InterruptShifterMutator<S>
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
fn mutate(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
input: &mut S::Input,
|
||||||
|
_stage_idx: i32
|
||||||
|
) -> Result<MutationResult, Error>
|
||||||
|
{
|
||||||
|
// need our own random generator, because borrowing rules
|
||||||
|
let mut myrand = StdRand::new();
|
||||||
|
let tmp = &mut state.rand_mut();
|
||||||
|
myrand.set_seed(tmp.next());
|
||||||
|
drop(tmp);
|
||||||
|
|
||||||
|
let target_bytes = input.bytes_mut();
|
||||||
|
let mut target_tick = 0;
|
||||||
|
|
||||||
|
#[cfg(feature = "sched_state")]
|
||||||
|
{
|
||||||
|
let tmp = state.metadata().get::<FreeRTOSSystemStateMetadata>();
|
||||||
|
if tmp.is_none() { // if there are no metadata it was probably not interesting anyways
|
||||||
|
return Ok(MutationResult::Skipped);
|
||||||
|
}
|
||||||
|
let trace =tmp.expect("FreeRTOSSystemStateMetadata not found");
|
||||||
|
let target_block = myrand.choose(trace.inner.iter());
|
||||||
|
target_tick = myrand.between(target_block.start_tick,target_block.end_tick)-IRQ_INPUT_OFFSET as u64;
|
||||||
|
}
|
||||||
|
#[cfg(feature = "sched_state")]
|
||||||
|
{
|
||||||
|
let feedbackstate = state
|
||||||
|
.feedback_states()
|
||||||
|
.match_name::<SysGraphFeedbackState>("SysMap")
|
||||||
|
.unwrap();
|
||||||
|
let g = &feedbackstate.graph;
|
||||||
|
let tmp = state.metadata().get::<SysGraphMetadata>();
|
||||||
|
if tmp.is_none() { // if there are no metadata it was probably not interesting anyways
|
||||||
|
return Ok(MutationResult::Skipped);
|
||||||
|
}
|
||||||
|
let trace = tmp.expect("SysGraphMetadata not found");
|
||||||
|
let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())];
|
||||||
|
target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) {
|
||||||
|
Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64,
|
||||||
|
None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 {
|
||||||
|
for i in 0..IRQ_INPUT_BYTES_NUMBER as usize {
|
||||||
|
target_bytes[i] = u64::to_le_bytes(target_tick)[i];
|
||||||
|
}
|
||||||
|
return Ok(MutationResult::Mutated);
|
||||||
|
} else {
|
||||||
|
return Ok(MutationResult::Skipped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec(
|
||||||
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
|
_stage_idx: i32,
|
||||||
|
_corpus_idx: Option<usize>
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Named for InterruptShifterMutator<S>
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"InterruptShifterMutator"
|
||||||
|
}
|
||||||
|
}
|
133
fuzzers/FRET/src/systemstate/observers.rs
Normal file
133
fuzzers/FRET/src/systemstate/observers.rs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
use crate::systemstate::IRQ_INPUT_BYTES_NUMBER;
|
||||||
|
use libafl::prelude::{ExitKind, AsSlice};
|
||||||
|
use libafl::{inputs::HasTargetBytes, prelude::UsesInput};
|
||||||
|
use libafl::bolts::HasLen;
|
||||||
|
use libafl::bolts::tuples::Named;
|
||||||
|
use libafl::Error;
|
||||||
|
use libafl::observers::Observer;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
CURRENT_SYSTEMSTATE_VEC,
|
||||||
|
RawFreeRTOSSystemState,
|
||||||
|
RefinedTCB,
|
||||||
|
RefinedFreeRTOSSystemState,
|
||||||
|
freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*},
|
||||||
|
};
|
||||||
|
|
||||||
|
//============================= Observer
|
||||||
|
|
||||||
|
/// The Qemusystemstate Observer retrieves the systemstate
|
||||||
|
/// that will get updated by the target.
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
#[allow(clippy::unsafe_derive_deserialize)]
|
||||||
|
pub struct QemuSystemStateObserver
|
||||||
|
{
|
||||||
|
pub last_run: Vec<RefinedFreeRTOSSystemState>,
|
||||||
|
pub last_input: Vec<u8>,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Observer<S> for QemuSystemStateObserver
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
S::Input : HasTargetBytes,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||||
|
unsafe {CURRENT_SYSTEMSTATE_VEC.clear(); }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> {
|
||||||
|
unsafe {self.last_run = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC);}
|
||||||
|
self.last_input=_input.target_bytes().as_slice().to_owned();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for QemuSystemStateObserver
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
self.name.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasLen for QemuSystemStateObserver
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.last_run.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuSystemStateObserver {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self{last_run: vec![], last_input: vec![], name: "systemstate".to_string()}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================= Parsing helpers
|
||||||
|
|
||||||
|
/// Parse a List_t containing TCB_t into Vec<TCB_t> from cache. Consumes the elements from cache
|
||||||
|
fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap<u32,rtos_struct>) -> Vec<TCB_t>
|
||||||
|
{
|
||||||
|
let mut ret : Vec<TCB_t> = Vec::new();
|
||||||
|
if list.uxNumberOfItems == 0 {return ret;}
|
||||||
|
let last_list_item = match dump.remove(&list.pxIndex).expect("List_t entry was not in Hashmap") {
|
||||||
|
List_Item_struct(li) => li,
|
||||||
|
List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") {
|
||||||
|
List_Item_struct(li) => li,
|
||||||
|
_ => panic!("MiniListItem of a non empty List does not point to ListItem"),
|
||||||
|
},
|
||||||
|
_ => panic!("List_t entry was not a ListItem"),
|
||||||
|
};
|
||||||
|
let mut next_index = last_list_item.pxNext;
|
||||||
|
let last_tcb = match dump.remove(&last_list_item.pvOwner).expect("ListItem Owner not in Hashmap") {
|
||||||
|
TCB_struct(t) => t,
|
||||||
|
_ => panic!("List content does not equal type"),
|
||||||
|
};
|
||||||
|
for _ in 0..list.uxNumberOfItems-1 {
|
||||||
|
let next_list_item = match dump.remove(&next_index).expect("List_t entry was not in Hashmap") {
|
||||||
|
List_Item_struct(li) => li,
|
||||||
|
List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") {
|
||||||
|
List_Item_struct(li) => li,
|
||||||
|
_ => panic!("MiniListItem of a non empty List does not point to ListItem"),
|
||||||
|
},
|
||||||
|
_ => panic!("List_t entry was not a ListItem"),
|
||||||
|
};
|
||||||
|
match dump.remove(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") {
|
||||||
|
TCB_struct(t) => {ret.push(t)},
|
||||||
|
_ => panic!("List content does not equal type"),
|
||||||
|
}
|
||||||
|
next_index=next_list_item.pxNext;
|
||||||
|
}
|
||||||
|
ret.push(last_tcb);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
/// Drains a List of raw SystemStates to produce a refined trace
|
||||||
|
fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> Vec<RefinedFreeRTOSSystemState> {
|
||||||
|
let mut ret = Vec::<RefinedFreeRTOSSystemState>::new();
|
||||||
|
let mut start_tick : u64 = 0;
|
||||||
|
for mut i in input.drain(..) {
|
||||||
|
let mut collector = Vec::<RefinedTCB>::new();
|
||||||
|
for j in i.prio_ready_lists.into_iter().rev() {
|
||||||
|
let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect();
|
||||||
|
collector.append(&mut tmp);
|
||||||
|
}
|
||||||
|
ret.push(RefinedFreeRTOSSystemState {
|
||||||
|
current_task: RefinedTCB::from_tcb_owned(i.current_tcb),
|
||||||
|
start_tick: start_tick,
|
||||||
|
end_tick: i.qemu_tick,
|
||||||
|
ready_list_after: collector,
|
||||||
|
input_counter: i.input_counter+IRQ_INPUT_BYTES_NUMBER,
|
||||||
|
last_pc: i.last_pc,
|
||||||
|
});
|
||||||
|
start_tick=i.qemu_tick;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user