make generic edge-map post-processor

This commit is contained in:
Alwin Berger 2022-02-11 19:44:12 +01:00
parent c252d6cad0
commit bec4743978
4 changed files with 170 additions and 146 deletions

View File

@ -21,3 +21,4 @@ hashbrown = { version = "0.11", features = ["serde", "ahash-compile-time-rng"],
nix = "0.23.0"
goblin = "0.4.2"
either = "1.6.1"
num-traits = "0.2"

View File

@ -1,5 +1,6 @@
//! A singlethreaded QEMU fuzzer that can auto-restart.
use wcet_qemu_sys::worst::QemuHashMapObserver;
use hashbrown::HashMap;
use clap::{App, Arg};
use core::{cell::RefCell, time::Duration};
@ -64,7 +65,7 @@ use libafl_qemu::{
clock::ClockFeedback,
clock::QemuClockIncreaseFeedback
};
use wcet_qemu_sys::worst::{SortedFeedback,HitFeedback,HitcountsMapObserver,HitImprovingFeedback,LenTimeMaximizerCorpusScheduler};
use wcet_qemu_sys::worst::{SortedFeedback,HitFeedback,HitImprovingFeedback,LenTimeMaximizerCorpusScheduler};
/// The fuzzer main
@ -276,7 +277,7 @@ fn fuzz(
let edges = unsafe { &mut edges::EDGES_MAP };
let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
let edges_observer =
HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
QemuHashMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
// Create an observation channel to keep track of the execution time
// let time_observer = TimeObserver::new("time");
@ -301,14 +302,14 @@ fn fuzz(
let feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
// HitImprovingFeedback::new(target_map.clone()),
HitImprovingFeedback::new(target_map.clone(), &edges_observer),
QemuClockIncreaseFeedback::default(),
ClockFeedback::new_with_observer(&clock_observer)
);
// A feedback to choose if an input is a solution or not
// let objective = HitFeedback::new(target_map,0.0);
let objective = SortedFeedback::new();
let objective = HitFeedback::new(target_map,0.0,&edges_observer);
// let objective = SortedFeedback::new();
// create a State from scratch
let mut state = state.unwrap_or_else(||{

View File

@ -1,7 +1,8 @@
//! A singlethreaded QEMU fuzzer that can auto-restart.
use wcet_qemu_sys::worst::QemuHashMapObserver;
use wcet_qemu_sys::{
worst::{DumpMapFeedback,DummyFeedback,HitcountsMapObserver},
worst::{DumpMapFeedback,DummyFeedback},
system_trace::QemuSystemStateHelper,
};
use clap::{App, Arg};
@ -262,13 +263,13 @@ fn fuzz(
let edges = unsafe { &mut edges::EDGES_MAP };
let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
let edges_observer =
HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
QemuHashMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
//========== Observe Execution Cycles
let clock_observer = QemuClockObserver::default();
//========= Feedback-Function evaluate the Maps. Need to dump it for debugging and check if it reaches targets.
let feedback = DumpMapFeedback::with_dump(dump_edges);
let feedback = DumpMapFeedback::with_dump(dump_edges, &edges_observer);
// A feedback to choose if an input is a solution or not
let objective = DummyFeedback::new(false);

View File

@ -1,3 +1,5 @@
use num_traits::PrimInt;
use core::fmt::Debug;
use core::cmp::Ordering::{Greater,Less,Equal};
use libafl::inputs::BytesInput;
use libafl::inputs::HasTargetBytes;
@ -8,7 +10,6 @@ use core::marker::PhantomData;
use libafl::corpus::MinimizerCorpusScheduler;
use std::path::PathBuf;
use std::fs;
use libafl::observers::VariableMapObserver;
use hashbrown::{HashMap};
use libafl::observers::ObserversTuple;
use libafl::executors::ExitKind;
@ -30,36 +31,23 @@ use libafl::{
observers::Observer,
Error,
};
/// Map observer with hitcounts postprocessing
//===================================================================
/// A wrapper around some other [`MapObserver`], using [`QemuEdgesMapMetadata`] to offer a convinient Hashmap.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "M: serde::de::DeserializeOwned")]
pub struct HitcountsMapObserver<M>
pub struct QemuHashMapObserver<M,T>
where
M: serde::Serialize + serde::de::DeserializeOwned,
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
{
base: M,
pub edgemap: HashMap<(u64,u64),u8>,
pub edgemap: HashMap<(u64,u64),T>,
}
static COUNT_CLASS_LOOKUP: [u8; 256] = [
0, 1, 2, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
];
impl<I, S, M> Observer<I, S> for HitcountsMapObserver<M>
impl<I, S, M, T> Observer<I, S> for QemuHashMapObserver<M, T>
where
M: MapObserver<u8> + Observer<I, S>,
M: MapObserver<T> + Observer<I, S>,
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
S: HasMetadata, // Need to grab the HashMap from a Helper
{
#[inline]
@ -70,25 +58,18 @@ where
#[inline]
fn post_exec(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
//new stuff
let original_hashmap=&state.metadata().get::<QemuEdgesMapMetadata>().unwrap().map;
let original_hashmap=&state.metadata().get::<QemuEdgesMapMetadata>().expect("QemuEdgesMapMetadata not found").map;
for (key, val) in original_hashmap.iter() {
self.edgemap.insert(*key,*self.base.get(*val as usize));
}
// println!("Post-Exec Len: {} Cap: {}",self.edgemap.len(),self.edgemap.capacity());
// println!("{:#?}",self.edgemap);
//end new stuff
let cnt = self.usable_count();
for i in 0..cnt {
*self.get_mut(i) = COUNT_CLASS_LOOKUP[*self.get(i) as usize];
}
self.base.post_exec(state, input)
}
}
impl<M> Named for HitcountsMapObserver<M>
impl<M, T> Named for QemuHashMapObserver<M, T>
where
M: Named + serde::Serialize + serde::de::DeserializeOwned,
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
{
#[inline]
fn name(&self) -> &str {
@ -96,9 +77,10 @@ where
}
}
impl<M> HasLen for HitcountsMapObserver<M>
impl<M, T> HasLen for QemuHashMapObserver<M, T>
where
M: MapObserver<u8>,
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
{
#[inline]
fn len(&self) -> usize {
@ -106,9 +88,10 @@ where
}
}
impl<M> MapObserver<u8> for HitcountsMapObserver<M>
impl<M, T> MapObserver<u8> for QemuHashMapObserver<M, T>
where
M: MapObserver<u8>,
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
{
#[inline]
fn map(&self) -> Option<&[u8]> {
@ -141,9 +124,10 @@ where
}
}
impl<M> HitcountsMapObserver<M>
impl<M, T> QemuHashMapObserver<M, T>
where
M: serde::Serialize + serde::de::DeserializeOwned,
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
{
/// Creates a new [`MapObserver`]
pub fn new(base: M) -> Self {
@ -156,15 +140,23 @@ where
/// A [`HitFeedback`] reports as interesting when all predicted worst case edges have been matched.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct HitFeedback {
target_map: HashMap<(u64,u64),u8>,
#[serde(bound = "T: serde::de::DeserializeOwned")]
pub struct HitFeedback<O, T>
where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
target_map: HashMap<(u64,u64),T>,
target_msd: f64,
phantom: PhantomData<(O, T)>,
}
impl<I, S> Feedback<I, S> for HitFeedback
impl<I, S, T, O> Feedback<I, S> for HitFeedback<O, T>
where
I: Input,
S: HasClientPerfMonitor,
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
fn is_interesting<EM, OT>(
&mut self,
@ -178,15 +170,17 @@ where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
{
let observer = _observers.match_name::<HitcountsMapObserver<VariableMapObserver<u8>>>("edges")
.expect("HitcountsMapObserver not found");
let observer = _observers.match_name::<QemuHashMapObserver<O, T>>("edges")
.expect("QemuHashMapObserver not found");
if self.target_map.len() == 0 { return Ok(false) };
let mut sum_of_square_difference : u64 = 0; // does not include found edges not in target
for (edg, val) in &self.target_map {
match observer.edgemap.get(&edg) {
Some(x) => sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)) as u64).pow(2),
None => sum_of_square_difference+=(*val as u64).pow(2),
Some(x) => {
sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)).to_u64().unwrap()).pow(2);
},
None => sum_of_square_difference+=((*val).to_u64().unwrap()).pow(2),
}
}
let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64);
@ -200,99 +194,111 @@ where
}
}
impl Named for HitFeedback {
impl<O, T> Named for HitFeedback<O, T>
where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
#[inline]
fn name(&self) -> &str {
"HitFeedback"
}
}
impl HitFeedback {
impl<O, T> HitFeedback<O, T>
where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
/// Creates a new [`HitFeedback`]
#[must_use]
pub fn new(target_map: HashMap<(u64,u64),u8>, target_msd: f64) -> Self {
Self {target_map: target_map, target_msd: target_msd}
}
}
impl Default for HitFeedback {
fn default() -> Self {
Self::new(HashMap::new(),0.0)
pub fn new(target_map: HashMap<(u64,u64),T>, target_msd: f64, _map_observer: &O) -> Self {
Self {target_map: target_map, target_msd: target_msd, phantom: PhantomData}
}
}
//===================================================================
/// A [`MapHitIncreaseFeedback`] reports as interesting when the total number of used edges increases.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct MapHitIncreaseFeedback {
record_high : u64,
}
// /// A [`MapHitIncreaseFeedback`] reports as interesting when the total number of used edges increases.
// #[derive(Serialize, Deserialize, Clone, Debug)]
// pub struct MapHitIncreaseFeedback {
// record_high : u64,
// }
impl<I, S> Feedback<I, S> for MapHitIncreaseFeedback
where
I: Input,
S: HasClientPerfMonitor,
{
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::<HitcountsMapObserver<VariableMapObserver<u8>>>("edges")
.expect("HitcountsMapObserver not found");
let cur = observer.edgemap.values().fold(0,|a,b| a+(*b as u64));
if cur > self.record_high {
self.record_high = cur;
return Ok(true);
}
return Ok(false);
}
}
// impl<I, S, O, T> Feedback<I, S> for MapHitIncreaseFeedback
// where
// I: Input,
// S: HasClientPerfMonitor,
// T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
// O: MapObserver<T>,
// {
// 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::<QemuHashMapObserver<O, T>>("edges")
// .expect("QemuHashMapObserver not found");
// let cur = observer.edgemap.values().fold(0,|a,b| a+(*b as u64));
// if cur > self.record_high {
// self.record_high = cur;
// return Ok(true);
// }
// return Ok(false);
// }
// }
impl Named for MapHitIncreaseFeedback {
#[inline]
fn name(&self) -> &str {
"HitFeedback"
}
}
// impl Named for MapHitIncreaseFeedback {
// #[inline]
// fn name(&self) -> &str {
// "HitFeedback"
// }
// }
impl MapHitIncreaseFeedback {
/// Creates a new [`HitFeedback`]
#[must_use]
pub fn new() -> Self {
Self {record_high: 0}
}
}
// impl MapHitIncreaseFeedback {
// /// Creates a new [`HitFeedback`]
// #[must_use]
// pub fn new() -> Self {
// Self {record_high: 0}
// }
// }
impl Default for MapHitIncreaseFeedback {
fn default() -> Self {
Self::new()
}
}
// impl Default for MapHitIncreaseFeedback {
// fn default() -> Self {
// Self::new()
// }
// }
//===================================================================
/// A [`HitFeedback`] reports as interesting when all predicted worst case edges have been matched.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct HitImprovingFeedback {
target_map: HashMap<(u64,u64),u8>,
#[serde(bound = "T: serde::de::DeserializeOwned")]
pub struct HitImprovingFeedback<O, T>
where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
target_map: HashMap<(u64,u64),T>,
best_msd: f64,
phantom: PhantomData<(O, T)>,
}
impl<I, S> Feedback<I, S> for HitImprovingFeedback
impl<O, T, I, S> Feedback<I, S> for HitImprovingFeedback<O, T>
where
I: Input,
S: HasClientPerfMonitor,
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
fn is_interesting<EM, OT>(
&mut self,
@ -306,15 +312,17 @@ where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
{
let observer = _observers.match_name::<HitcountsMapObserver<VariableMapObserver<u8>>>("edges")
.expect("HitcountsMapObserver not found");
let observer = _observers.match_name::<QemuHashMapObserver<O, T>>("edges")
.expect("QemuHashMapObserver not found");
if self.target_map.len() == 0 { return Ok(false) };
let mut sum_of_square_difference : u64 = 0; // does not include found edges not in target
for (edg, val) in &self.target_map {
match observer.edgemap.get(&edg) {
Some(x) => sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)) as u64).pow(2),
None => sum_of_square_difference+=(*val as u64).pow(2),
Some(x) => {
sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)).to_u64().unwrap()).pow(2);
},
None => sum_of_square_difference+=((*val).to_u64().unwrap()).pow(2),
}
}
let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64);
@ -329,38 +337,47 @@ where
}
}
impl Named for HitImprovingFeedback {
impl<O, T> Named for HitImprovingFeedback<O, T>
where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
#[inline]
fn name(&self) -> &str {
"HitFeedback"
"HitImprovingFeedback"
}
}
impl HitImprovingFeedback {
/// Creates a new [`HitFeedback`]
impl<O, T> HitImprovingFeedback<O, T>
where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
/// Creates a new [`HitImprovingFeedback`]
#[must_use]
pub fn new(target_map: HashMap<(u64,u64),u8>) -> Self {
Self {target_map: target_map, best_msd: f64::MAX}
}
}
impl Default for HitImprovingFeedback {
fn default() -> Self {
Self::new(HashMap::new())
pub fn new(target_map: HashMap<(u64,u64),T>, _map_observer: &O) -> Self {
Self {target_map: target_map, best_msd: f64::MAX, phantom: PhantomData}
}
}
//=========================== Debugging Feedback
/// A [`Feedback`] meant to dump the edgemap for debugging.
#[derive(Debug)]
pub struct DumpMapFeedback {
dumpfile: Option<PathBuf>
pub struct DumpMapFeedback<O, T>
where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
dumpfile: Option<PathBuf>,
phantom: PhantomData<(O, T)>,
}
impl<I, S> Feedback<I, S> for DumpMapFeedback
impl<I, S, O, T> Feedback<I, S> for DumpMapFeedback<O, T>
where
I: Input,
S: HasClientPerfMonitor,
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
fn is_interesting<EM, OT>(
&mut self,
@ -374,8 +391,8 @@ where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
{
let observer = _observers.match_name::<HitcountsMapObserver<VariableMapObserver<u8>>>("edges")
.expect("HitcountsMapObserver not found");
let observer = _observers.match_name::<QemuHashMapObserver<O, T>>("edges")
.expect("QemuHashMapObserver not found");
match &self.dumpfile {
Some(s) => {
fs::write(s,ron::to_string(&observer.edgemap).expect("Error serializing hashmap")).expect("Can not dump to file");
@ -387,27 +404,29 @@ where
}
}
impl Named for DumpMapFeedback {
impl<O, T> Named for DumpMapFeedback<O, T>
where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
#[inline]
fn name(&self) -> &str {
"HitFeedback"
}
}
impl DumpMapFeedback {
impl<O, T> DumpMapFeedback<O, T>
where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
O: MapObserver<T>,
{
/// Creates a new [`HitFeedback`]
#[must_use]
pub fn new() -> Self {
Self {dumpfile: None}
pub fn new(_map_observer: &O) -> Self {
Self {dumpfile: None, phantom: PhantomData}
}
pub fn with_dump(dumpfile: Option<PathBuf>) -> Self {
Self {dumpfile: dumpfile}
}
}
impl Default for DumpMapFeedback {
fn default() -> Self {
Self::new()
pub fn with_dump(dumpfile: Option<PathBuf>,_map_observer: &O) -> Self {
Self {dumpfile: dumpfile, phantom: PhantomData}
}
}
@ -511,6 +530,8 @@ where
}
}
//===================================================================
/// A Feedback reporting if the Input consists of strictly decreasing bytes.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SortedFeedback {