721 lines
28 KiB
Rust

use hashbrown::HashSet;
use libafl::inputs::Input;
/// Feedbacks organizing SystemStates as a graph
use libafl::SerdeAny;
use libafl_bolts::ownedref::OwnedMutSlice;
use petgraph::graph::EdgeIndex;
use libafl::prelude::UsesInput;
use libafl::common::HasNamedMetadata;
use libafl::state::UsesState;
use libafl::prelude::State;
use libafl::schedulers::MinimizerScheduler;
use libafl_bolts::HasRefCnt;
use std::path::PathBuf;
use libafl::corpus::Testcase;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use std::hash::Hash;
use libafl::events::EventFirer;
use libafl::state::MaybeHasClientPerfMonitor;
use libafl::feedbacks::Feedback;
use libafl_bolts::Named;
use libafl::Error;
use libafl_qemu::edges::EDGES_MAP_SIZE_IN_USE;
use hashbrown::HashMap;
use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata};
use serde::{Deserialize, Serialize};
use super::AtomicBasicBlock;
use super::CaptureEvent;
use super::ExecInterval;
use super::JobInstance;
use super::ReducedFreeRTOSSystemState;
use super::observers::QemuSystemStateObserver;
use super::TaskJob;
use petgraph::prelude::DiGraph;
use petgraph::graph::NodeIndex;
use petgraph::Direction;
use crate::time::clock::QemuClockObserver;
use crate::time::clock::FUZZ_START_TIMESTAMP;
use crate::time::worst::MaxTimeFavFactor;
use std::time::SystemTime;
use std::{fs::OpenOptions, io::Write};
use std::borrow::Cow;
use std::ops::Deref;
use std::ops::DerefMut;
use std::rc::Rc;
use petgraph::visit::EdgeRef;
//============================= Data Structures
#[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)]
pub struct STGNode
{
base: ReducedFreeRTOSSystemState,
abb: AtomicBasicBlock,
}
impl STGNode {
pub fn _pretty_print(&self) -> String {
format!("{}\nl{} {:x}-{:x}\n{}", self.base.current_task.task_name, self.abb.level, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.print_lists())
}
pub fn color_print(&self) -> String {
let color = match self.abb.level {
1 => "\", shape=box, style=filled, fillcolor=\"lightblue",
2 => "\", shape=box, style=filled, fillcolor=\"yellow",
0 => "\", shape=box, style=filled, fillcolor=\"white",
_ => "\", style=filled, fillcolor=\"lightgray",
};
let message = match self.abb.level {
1 => format!("API Call"),
2 => format!("ISR"),
0 => format!("Task: {}",self.base.current_task.task_name),
_ => format!(""),
};
let mut label = format!("{}\nABB: {:x}-{:x}\nHash:{:X}\n{}", message, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.get_hash()>>48, self.base.print_lists());
label.push_str(color);
label
}
fn get_hash(&self) -> u64 {
let mut s = DefaultHasher::new();
self.base.hash(&mut s);
self.abb.hash(&mut s);
s.finish()
}
}
impl PartialEq for STGNode {
fn eq(&self, other: &STGNode) -> bool {
self.base==other.base
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
pub struct STGEdge
{
pub event: CaptureEvent,
pub name: String,
pub worst: Option<(u64, Vec<(u32, u8)>)>,
}
impl STGEdge {
pub fn _pretty_print(&self) -> String {
let mut short = match self.event {
CaptureEvent::APIStart => "Call: ",
CaptureEvent::APIEnd => "Ret: ",
CaptureEvent::ISRStart => "Int: ",
CaptureEvent::ISREnd => "IRet: ",
CaptureEvent::End => "End: ",
CaptureEvent::Undefined => "",
}.to_string();
short.push_str(&self.name);
short
}
pub fn color_print(&self) -> String {
let mut short = self.name.clone();
short.push_str(match self.event {
CaptureEvent::APIStart => "\", color=\"blue",
CaptureEvent::APIEnd => "\", color=\"black",
CaptureEvent::ISRStart => "\", color=red, style=\"dashed",
CaptureEvent::ISREnd => "\", color=red, style=\"solid",
CaptureEvent::End => "",
CaptureEvent::Undefined => "",
});
short
}
pub fn is_abb_end(&self) -> bool {
match self.event {
CaptureEvent::APIStart | CaptureEvent::APIEnd | CaptureEvent::ISREnd | CaptureEvent::End => true,
_ => false
}
}
}
impl Hash for STGEdge {
fn hash<H: Hasher>(&self, state: &mut H) {
self.event.hash(state);
self.name.hash(state);
}
}
/// Shared Metadata for a systemstateFeedback
#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)]
pub struct STGFeedbackState
{
name: Cow<'static, str>,
// aggregated traces as a graph
pub graph: DiGraph<STGNode, STGEdge>,
systemstate_index: HashMap<u64, ReducedFreeRTOSSystemState>,
pub state_abb_hash_index: HashMap<(u64, u64), NodeIndex>,
stgnode_index: HashMap<u64, NodeIndex>,
entrypoint: NodeIndex,
exitpoint: NodeIndex,
// Metadata about aggregated traces. aggegated meaning, order has been removed
worst_observed_per_aggegated_path: HashMap<Vec<AtomicBasicBlock>,u64>,
worst_observed_per_abb_path: HashMap<u64,u64>,
worst_observed_per_stg_path: HashMap<u64,u64>,
worst_abb_exec_count: HashMap<AtomicBasicBlock, usize>,
// Metadata about job instances
pub worst_task_jobs: HashMap<u64, TaskJob>,
}
impl Default for STGFeedbackState {
fn default() -> STGFeedbackState {
let mut graph = DiGraph::new();
let mut entry = STGNode::default();
entry.base.current_task.task_name="Start".to_string();
let mut exit = STGNode::default();
exit.base.current_task.task_name="End".to_string();
let systemstate_index = HashMap::from([(entry.base.get_hash(), entry.base.clone()), (exit.base.get_hash(), exit.base.clone())]);
let h_entry = entry.get_hash();
let h_exit = exit.get_hash();
let entrypoint = graph.add_node(entry.clone());
let exitpoint = graph.add_node(exit.clone());
let state_abb_hash_index = HashMap::from([((entry.base.get_hash(), entry.abb.get_hash()), entrypoint), ((exit.base.get_hash(), exit.abb.get_hash()), exitpoint)]);
let index = HashMap::from([(h_entry, entrypoint), (h_exit, exitpoint)]);
STGFeedbackState {
name: Cow::from("stgfeedbackstate".to_string()),
graph,
stgnode_index: index,
entrypoint,
exitpoint,
worst_observed_per_aggegated_path: HashMap::new(),
worst_observed_per_abb_path: HashMap::new(),
worst_observed_per_stg_path: HashMap::new(),
worst_abb_exec_count: HashMap::new(),
systemstate_index,
state_abb_hash_index,
worst_task_jobs: HashMap::new(),
}
}
}
impl Named for STGFeedbackState
{
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
// Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct STGNodeMetadata {
nodes: Vec<NodeIndex>,
edges: Vec<EdgeIndex>,
abbs: u64,
aggregate: u64,
top_abb_counts: Vec<u64>,
intervals: Vec<ExecInterval>,
jobs: Vec<JobInstance>,
indices: Vec<usize>,
tcref: isize,
}
impl STGNodeMetadata {
pub fn new(nodes: Vec<NodeIndex>, edges: Vec<EdgeIndex>, abb_trace: Vec<AtomicBasicBlock>, abbs_pathhash: u64, aggregate: u64, top_abb_counts: Vec<u64>, intervals: Vec<ExecInterval>, jobs: Vec<JobInstance>) -> Self {
#[allow(unused)]
let mut indices : Vec<_> = vec![];
#[cfg(feature = "sched_stg_edge")]
{
indices = edges.iter().map(|x| x.index()).collect();
indices.sort_unstable();
indices.dedup();
}
#[cfg(feature = "sched_stg_pathhash")]
{
indices.push(get_generic_hash(&edges) as usize);
}
#[cfg(feature = "sched_stg_abbhash")]
{
indices.push(abbs_pathhash as usize);
}
#[cfg(feature = "sched_stg_aggregatehash")]
{
// indices.push(aggregate as usize);
indices = top_abb_counts.iter().map(|x| (*x) as usize).collect();
}
Self {indices, intervals, jobs, nodes, abbs: abbs_pathhash, aggregate, top_abb_counts, edges, tcref: 0}
}
pub fn nodes(&self) -> &Vec<NodeIndex> {
&self.nodes
}
pub fn edges(&self) -> &Vec<EdgeIndex> {
&self.edges
}
pub fn abbs(&self) -> u64 {
self.abbs
}
pub fn aggregate(&self) -> u64 {
self.aggregate
}
pub fn top_abb_counts(&self) -> &Vec<u64> {
&self.top_abb_counts
}
pub fn intervals(&self) -> &Vec<ExecInterval> {
&self.intervals
}
pub fn jobs(&self) -> &Vec<JobInstance> {
&self.jobs
}
}
impl Deref for STGNodeMetadata {
type Target = [usize];
/// Convert to a slice
fn deref(&self) -> &[usize] {
&self.indices
}
}
impl DerefMut for STGNodeMetadata {
/// Convert to a slice
fn deref_mut(&mut self) -> &mut [usize] {
&mut self.indices
}
}
impl HasRefCnt for STGNodeMetadata {
fn refcnt(&self) -> isize {
self.tcref
}
fn refcnt_mut(&mut self) -> &mut isize {
&mut self.tcref
}
}
libafl_bolts::impl_serdeany!(STGNodeMetadata);
pub type GraphMaximizerCorpusScheduler<CS, O> =
MinimizerScheduler<CS, MaxTimeFavFactor<<CS as UsesState>::State>,STGNodeMetadata,O>;
// AI generated, human verified
/// Count the occurrences of each element in a vector, assumes the vector is sorted
fn count_occurrences_sorted<T>(vec: &Vec<T>) -> HashMap<&T, usize>
where
T: PartialEq + Eq + Hash + Clone,
{
let mut counts = HashMap::new();
if vec.is_empty() {
return counts;
}
let mut current_obj = &vec[0];
let mut current_count = 1;
for obj in vec.iter().skip(1) {
if obj == current_obj {
current_count += 1;
} else {
counts.insert(current_obj, current_count);
current_obj = obj;
current_count = 1;
}
}
// Insert the count of the last object
counts.insert(current_obj, current_count);
counts
}
//============================= Graph Feedback
pub static mut STG_MAP: [u16; EDGES_MAP_SIZE_IN_USE] = [0; EDGES_MAP_SIZE_IN_USE];
pub static mut MAX_STG_NUM: usize = 0;
pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u16> {
OwnedMutSlice::from_raw_parts_mut(STG_MAP.as_mut_ptr(), STG_MAP.len())
}
/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`]
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct StgFeedback
{
name: Cow<'static, str>,
last_node_trace: Option<Vec<NodeIndex>>,
last_edge_trace: Option<Vec<EdgeIndex>>,
last_intervals: Option<Vec<ExecInterval>>,
last_abb_trace: Option<Vec<AtomicBasicBlock>>,
last_abbs_hash: Option<u64>, // only set, if it was interesting
last_aggregate_hash: Option<u64>, // only set, if it was interesting
last_top_abb_hashes: Option<Vec<u64>>, // only set, if it was interesting
last_job_trace: Option<Vec<JobInstance>>, // only set, if it was interesting
dump_path: Option<PathBuf>
}
#[cfg(feature = "feed_stg")]
const INTEREST_EDGE : bool = true;
#[cfg(feature = "feed_stg")]
const INTEREST_NODE : bool = true;
#[cfg(feature = "feed_stg_pathhash")]
const INTEREST_PATH : bool = true;
#[cfg(feature = "feed_stg_abbhash")]
const INTEREST_ABBPATH : bool = true;
#[cfg(feature = "feed_stg_aggregatehash")]
const INTEREST_AGGREGATE : bool = true;
#[cfg(not(feature = "feed_stg"))]
const INTEREST_EDGE : bool = false;
#[cfg(not(feature = "feed_stg"))]
const INTEREST_NODE : bool = false;
#[cfg(not(feature = "feed_stg_pathhash"))]
const INTEREST_PATH : bool = false;
#[cfg(not(feature = "feed_stg_abbhash"))]
const INTEREST_ABBPATH : bool = false;
#[cfg(not(feature = "feed_stg_aggregatehash"))]
const INTEREST_AGGREGATE : bool = false;
const INTEREST_JOB_INSTANCE : bool = true;
fn set_observer_map(trace : &Vec<EdgeIndex>) {
// dbg!(trace);
unsafe {
for i in 0..MAX_STG_NUM {
STG_MAP[i] = 0;
}
for i in trace {
if MAX_STG_NUM < i.index() {
MAX_STG_NUM = i.index();
}
STG_MAP[i.index()] = STG_MAP[i.index()].saturating_add(1);
}
}
}
fn get_generic_hash<H>(input: &H) -> u64
where
H: Hash,
{
let mut s = DefaultHasher::new();
input.hash(&mut s);
s.finish()
}
fn execinterval_to_abb_instances(trace: &Vec<ExecInterval>, read_trace: &Vec<Vec<(u32, u8)>>) -> HashMap<usize, (u64, Vec<(u32, u8)>)>{
let mut instance_time: HashMap<usize, (u64, Vec<(u32, u8)>)> = HashMap::new();
for (_i,interval) in trace.iter().enumerate() { // Iterate intervals
// sum up execution time and accesses per ABB
let temp = interval.abb.as_ref().map(|abb| abb.instance_id).unwrap_or(usize::MAX);
match instance_time.get_mut(&temp) {
Some(x) => {
x.0 += interval.get_exec_time();
x.1.extend(read_trace[_i].clone());
},
None => {
if temp != usize::MAX {
instance_time.insert(temp, (interval.get_exec_time(), read_trace[_i].clone()));
}
}
};
}
return instance_time;
}
impl StgFeedback {
pub fn new(dump_name: Option<PathBuf>) -> Self {
// Self {name: String::from("STGFeedback"), last_node_trace: None, last_edge_trace: None, last_intervals: None }
let mut s = Self::default();
s.dump_path = dump_name.map(|x| x.with_extension("stgsize"));
s
}
/// params:
/// tarce of intervals
/// hashtable of states
/// feedbackstate
/// produces:
/// tarce of node indexes representing the path trough the graph
/// newly discovered node?
/// side effect:
/// the graph gets new nodes and edge
fn update_stg_interval(trace: &Vec<ExecInterval>, read_trace: &Vec<Vec<(u32, u8)>>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, fbs: &mut STGFeedbackState) -> (Vec<(NodeIndex, u64)>, Vec<(EdgeIndex, u64)>, bool, bool) {
let mut return_node_trace = vec![(fbs.entrypoint, 0)]; // Assuming entrypoint timestamp is 0
let mut return_edge_trace = vec![];
let mut interesting = false;
let mut updated = false;
if trace.is_empty() {
return (return_node_trace, return_edge_trace, interesting, updated);
}
let mut instance_time = execinterval_to_abb_instances(trace, read_trace);
// add all missing state+abb combinations to the graph
for (_i,interval) in trace.iter().enumerate() { // Iterate intervals
let node = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()};
let h_node = node.get_hash();
let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) {
// already present
*idx
} else {
// not present
let h = (node.base.get_hash(), node.abb.get_hash());
let idx = fbs.graph.add_node(node);
fbs.stgnode_index.insert(h_node, idx);
fbs.state_abb_hash_index.insert(h, idx);
interesting |= INTEREST_NODE;
updated = true;
idx
};
// connect in graph if edge not present
let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1].0, Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx);
if let Some(e_) = e {
return_edge_trace.push((petgraph::visit::EdgeRef::id(&e_), interval.start_tick));
if let Some((time, accesses)) = instance_time.get_mut(&interval.abb.as_ref().unwrap().instance_id) {
let ref_ = &mut fbs.graph.edge_weight_mut(e_.id()).unwrap().worst;
if ref_.is_some() {
let w = ref_.as_mut().unwrap();
if w.0 < *time {*w = (*time, accesses.clone())};
} else {
*ref_ = Some((*time, accesses.clone()));
}
}
} else {
let mut e__ = STGEdge{event: interval.start_capture.0, name: interval.start_capture.1.clone(), worst: None};
if e__.is_abb_end() {
if let Some((time,accesses)) = instance_time.get_mut(&interval.abb.as_ref().unwrap().instance_id) {
e__.worst = Some((*time, accesses.clone()));
}
}
let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1].0, next_idx, e__);
return_edge_trace.push((e_, interval.start_tick));
interesting |= INTEREST_EDGE;
updated = true;
}
return_node_trace.push((next_idx, interval.start_tick));
}
// every path terminates at the end
if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1].0, Direction::Outgoing).any(|x| x == fbs.exitpoint) {
let mut e__ = STGEdge { event: CaptureEvent::End, name: String::from("End"), worst: None };
if e__.is_abb_end() {
if let Some((time, accesses)) = instance_time.get_mut(&trace[trace.len()-1].abb.as_ref().unwrap().instance_id) {
e__.worst = Some((*time, accesses.clone()));
}
}
let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1].0, fbs.exitpoint, e__);
return_edge_trace.push((e_, trace[trace.len()-1].start_tick));
interesting |= INTEREST_EDGE;
updated = true;
}
return_node_trace.push((fbs.exitpoint, trace[trace.len()-1].start_tick));
(return_node_trace, return_edge_trace, interesting, updated)
}
fn abbs_in_exec_order(trace: &Vec<ExecInterval>) -> Vec<AtomicBasicBlock> {
let mut ret = Vec::new();
for i in 0..trace.len() {
if trace[i].abb != None &&
(trace[i].end_capture.0 == CaptureEvent::APIStart || trace[i].end_capture.0 == CaptureEvent::APIEnd || trace[i].end_capture.0 == CaptureEvent::End || trace[i].end_capture.0 == CaptureEvent::ISREnd) {
ret.push(trace[i].abb.as_ref().unwrap().clone());
}
}
ret
}
}
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>(
&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>,
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") {
Some(s) => s,
Option::None => {
let n=STGFeedbackState::default();
state.named_metadata_map_mut().insert("stgfeedbackstate",n);
state.named_metadata_map_mut().get_mut::<STGFeedbackState>("stgfeedbackstate").unwrap()
}
};
// --------------------------------- Update STG
let (mut nodetrace, mut edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_reads, &observer.last_states, feedbackstate);
#[cfg(feature = "trace_job_response_times")]
let worst_target_instance = observer.job_instances.iter().filter(|x| Some(x.name.clone()) == observer.select_task).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release)));
#[cfg(feature = "trace_job_response_times")]
if let Some(worst_instance) = worst_target_instance {
edgetrace = edgetrace.into_iter().filter(|x| x.1 <= worst_instance.response && x.1 >= worst_instance.release ).collect();
nodetrace = nodetrace.into_iter().filter(|x| x.1 <= worst_instance.response && x.1 >= worst_instance.release ).collect();
} else {
if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here
edgetrace = Vec::new();
nodetrace = Vec::new();
}
}
#[cfg(feature = "feed_stg")]
set_observer_map(&edgetrace.iter().map(|x| x.0).collect::<Vec<_>>());
// --------------------------------- Update job instances
for i in observer.worst_job_instances.iter() {
interesting |= INTEREST_JOB_INSTANCE && if let Some(x) = feedbackstate.worst_task_jobs.get_mut(&i.1.get_hash_cached()) {
// eprintln!("Job instance already present");
x.try_update(i.1)
} else {
// eprintln!("New Job instance");
feedbackstate.worst_task_jobs.insert(i.1.get_hash_cached(), TaskJob::from_instance(&i.1));
true
}
};
self.last_job_trace = Some(observer.job_instances.clone());
// dbg!(&observer.job_instances);
{
let h = get_generic_hash(&edgetrace);
if let Some(x) = feedbackstate.worst_observed_per_stg_path.get_mut(&h) {
let t = last_runtime;
if t > *x {
*x = t;
interesting |= INTEREST_PATH;
}
} else {
feedbackstate.worst_observed_per_stg_path.insert(h, last_runtime);
updated = true;
interesting |= INTEREST_PATH;
}
}
#[cfg(not(feature = "trace_job_response_times"))]
let tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace);
#[cfg(feature = "trace_job_response_times")]
let tmp = {
if let Some(worst_instance) = worst_target_instance {
let t = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect();
StgFeedback::abbs_in_exec_order(&t)
} else {
if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here
StgFeedback::abbs_in_exec_order(&observer.last_trace)
} else {
Vec::new()
}
}
};
if INTEREST_AGGREGATE || INTEREST_ABBPATH {
if INTEREST_ABBPATH {
let h = get_generic_hash(&tmp);
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 = last_runtime;
if t > *x {
*x = t;
interesting |= INTEREST_ABBPATH;
}
} else {
feedbackstate.worst_observed_per_abb_path.insert(h, last_runtime);
interesting |= INTEREST_ABBPATH;
}
}
if INTEREST_AGGREGATE {
// aggegation by sorting, order of states is not relevant
let mut _tmp = tmp.clone();
_tmp.sort(); // use sort+count, because we need the sorted trace anyways
let counts = count_occurrences_sorted(&_tmp);
let mut top_indices = Vec::new();
for (k,c) in counts {
if let Some(reference) = feedbackstate.worst_abb_exec_count.get_mut(k) {
if *reference < c {
*reference = c;
top_indices.push(get_generic_hash(k));
}
} else {
top_indices.push(get_generic_hash(k));
feedbackstate.worst_abb_exec_count.insert(k.clone(), c);
}
}
self.last_top_abb_hashes = Some(top_indices);
self.last_aggregate_hash = Some(get_generic_hash(&_tmp));
if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&_tmp) {
let t = last_runtime;
if t > *x {
*x = t;
interesting |= INTEREST_AGGREGATE;
}
} else {
feedbackstate.worst_observed_per_aggegated_path.insert(_tmp, last_runtime);
interesting |= INTEREST_AGGREGATE;
}
}
}
// let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| "");
// let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string();
// let outs = outs.replace(';',"\\n");
// fs::write("./mystg.dot",outs).expect("Failed to write graph");
self.last_node_trace = Some(nodetrace.into_iter().map(|x| x.0).collect::<Vec<_>>());
self.last_edge_trace = Some(edgetrace.into_iter().map(|x| x.0).collect::<Vec<_>>());
self.last_intervals = Some(observer.last_trace.clone());
self.last_abb_trace = Some(tmp);
if let Some(dp) = &self.dump_path {
if updated {
let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.append(true)
.open(dp).expect("Could not open stgsize");
writeln!(file, "{},{},{},{},{}", feedbackstate.graph.edge_count(), feedbackstate.graph.node_count(), feedbackstate.worst_observed_per_aggegated_path.len(),feedbackstate.worst_observed_per_stg_path.len(), timestamp).expect("Write to dump failed");
}
}
Ok(interesting)
}
/// Append to the testcase the generated metadata in case of a new corpus item
#[inline]
fn append_metadata<EM, OT>(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
let meta = STGNodeMetadata::new(self.last_node_trace.take().unwrap_or_default(), self.last_edge_trace.take().unwrap_or_default(), self.last_abb_trace.take().unwrap_or_default(), self.last_abbs_hash.take().unwrap_or_default(), self.last_aggregate_hash.take().unwrap_or_default(), self.last_top_abb_hashes.take().unwrap_or_default(), self.last_intervals.take().unwrap_or_default(), self.last_job_trace.take().unwrap_or_default());
testcase.metadata_map_mut().insert(meta);
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> {
Ok(())
}
}
impl Named for StgFeedback
{
#[inline]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}