add stg edge feedback

This commit is contained in:
Alwin Berger 2024-05-06 14:46:35 +02:00
parent 3453d02b1d
commit 0393f18a47
4 changed files with 92 additions and 45 deletions

View File

@ -2,42 +2,19 @@
//! //!
use core::time::Duration; use core::time::Duration;
use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range, ptr::addr_of_mut, ffi::OsStr}; use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range, ptr::addr_of_mut, ffi::OsStr};
use hashbrown::HashMap; use hashbrown::HashMap;
use libafl_bolts::{ use libafl_bolts::{
core_affinity::Cores, core_affinity::Cores, current_nanos, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsMutSlice, AsSlice
current_nanos,
rands::StdRand,
shmem::{ShMemProvider, StdShMemProvider},
tuples::tuple_list,
AsSlice,
AsMutSlice
}; };
use libafl::{ use libafl::{
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::{ExitKind, TimeoutExecutor}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, observers::VariableMapObserver, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HasBytesVec, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, state::{HasCorpus, HasMetadata, HasNamedMetadata, StdState}, Error, Evaluator
events::EventConfig,
events::launcher::Launcher,
executors::{ExitKind, TimeoutExecutor},
feedback_or,
feedback_or_fast,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
monitors::MultiMonitor,
observers::{VariableMapObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata},
Error,
prelude::{SimpleMonitor, SimpleEventManager, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator, HitcountsMapObserver, CorpusId}, Evaluator, stages::StdMutationalStage,
}; };
use libafl_qemu::{ use libafl_qemu::{
edges::{self, edges_map_mut_slice, MAX_EDGES_NUM}, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, edges::{self, edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM}, elf::EasyElf, emu::{libafl_qemu_remove_native_breakpoint, libafl_qemu_set_native_breakpoint, Emulator}, GuestAddr, GuestPhysAddr, QemuExecutor, QemuHooks, QemuInstrumentationFilter, Regs
QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr,
emu::libafl_qemu_set_native_breakpoint, emu::libafl_qemu_remove_native_breakpoint,
}; };
use rand::{SeedableRng, StdRng, Rng}; use rand::{SeedableRng, StdRng, Rng};
use crate::{ use crate::{
clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::StgFeedback}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler}
}; };
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
@ -544,6 +521,13 @@ pub fn fuzz() {
#[cfg(feature = "observer_hitcounts")] #[cfg(feature = "observer_hitcounts")]
let edges_observer = HitcountsMapObserver::new(edges_observer); let edges_observer = HitcountsMapObserver::new(edges_observer);
// #[cfg(feature = "feed_stg")]
let stg_observer = unsafe { VariableMapObserver::from_mut_slice(
"stg",
stg_map_mut_slice(),
addr_of_mut!(MAX_STG_NUM)
)};
// 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");
@ -573,7 +557,7 @@ pub fn fuzz() {
// Feedback to reward any input which increses the execution time // Feedback to reward any input which increses the execution time
ExecTimeIncFeedback::new() ExecTimeIncFeedback::new()
); );
#[cfg(all(feature = "systemstate",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] #[cfg(all(feature = "systemstate"))]
let mut feedback = feedback_or!( let mut feedback = feedback_or!(
feedback, feedback,
DumpSystraceFeedback::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None}) DumpSystraceFeedback::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None})
@ -583,6 +567,11 @@ pub fn fuzz() {
feedback, feedback,
StgFeedback::default() StgFeedback::default()
); );
#[cfg(feature = "feed_stg")]
let mut feedback = feedback_or!(
feedback,
MaxMapFeedback::tracking(&stg_observer, true, true)
);
#[cfg(feature = "feed_systemtrace")] #[cfg(feature = "feed_systemtrace")]
let mut feedback = feedback_or!( let mut feedback = feedback_or!(
feedback, feedback,
@ -647,7 +636,7 @@ pub fn fuzz() {
#[cfg(not(feature = "systemstate"))] #[cfg(not(feature = "systemstate"))]
let observer_list = tuple_list!(edges_observer, clock_time_observer); let observer_list = tuple_list!(edges_observer, clock_time_observer);
#[cfg(feature = "systemstate")] #[cfg(feature = "systemstate")]
let observer_list = tuple_list!(edges_observer, clock_time_observer, systemstate_observer); let observer_list = tuple_list!(edges_observer, clock_time_observer, systemstate_observer, stg_observer);
// Create a QEMU in-process executor // Create a QEMU in-process executor
let executor = QemuExecutor::new( let executor = QemuExecutor::new(

View File

@ -251,10 +251,10 @@ where
std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file"); std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file");
self.dumpfile = None self.dumpfile = None
}, },
None => if !self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);}
}; };
if self.dump_metadata {self.last_trace=Some(observer.last_run.clone());} if self.dump_metadata {self.last_trace=Some(observer.last_run.clone());}
Ok(!self.dump_metadata) Ok(false)
} }
/// Append to the testcase the generated metadata in case of a new corpus item /// Append to the testcase the generated metadata in case of a new corpus item
#[inline] #[inline]

View File

@ -148,6 +148,7 @@ impl Hash for RefinedFreeRTOSSystemState {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.current_task.hash(state); self.current_task.hash(state);
self.ready_list_after.hash(state); self.ready_list_after.hash(state);
self.delay_list_after.hash(state);
// self.last_pc.hash(state); // self.last_pc.hash(state);
} }
} }
@ -155,6 +156,18 @@ impl RefinedFreeRTOSSystemState {
fn get_time(&self) -> u64 { fn get_time(&self) -> u64 {
self.end_tick-self.start_tick self.end_tick-self.start_tick
} }
pub fn print_lists(&self) -> String {
let mut ret = String::from("+");
for j in self.ready_list_after.iter() {
ret.push_str(format!(" {}#{}", j.0.task_name, j.1).as_str());
}
ret.push_str("\n-");
for j in self.delay_list_after.iter() {
ret.push_str(format!(" {}#{}", j.0.task_name, j.1).as_str());
}
ret
}
} }
// Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata // Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata
@ -206,6 +219,17 @@ pub struct AtomicBasicBlock {
start: GuestAddr, start: GuestAddr,
ends: HashSet<GuestAddr>, ends: HashSet<GuestAddr>,
} }
impl Hash for AtomicBasicBlock {
fn hash<H: Hasher>(&self, state: &mut H) {
// Use a combination of the start address and the set of ending addresses to compute the hash value
self.start.hash(state);
let mut keys : Vec<_> = self.ends.iter().collect();
keys.sort();
keys.hash(state);
}
}
impl fmt::Display for AtomicBasicBlock { impl fmt::Display for AtomicBasicBlock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut ends_str = String::new(); let mut ends_str = String::new();

View File

@ -2,6 +2,8 @@
use libafl::SerdeAny; use libafl::SerdeAny;
/// Feedbacks organizing SystemStates as a graph /// Feedbacks organizing SystemStates as a graph
use libafl::inputs::HasBytesVec; use libafl::inputs::HasBytesVec;
use libafl_bolts::ownedref::OwnedMutSlice;
use petgraph::graph::EdgeIndex;
use std::fs; use std::fs;
use libafl_bolts::rands::RandomSeed; use libafl_bolts::rands::RandomSeed;
use libafl_bolts::rands::StdRand; use libafl_bolts::rands::StdRand;
@ -37,6 +39,7 @@ use libafl::state::MaybeHasClientPerfMonitor;
use libafl::feedbacks::Feedback; use libafl::feedbacks::Feedback;
use libafl_bolts::Named; use libafl_bolts::Named;
use libafl::Error; use libafl::Error;
use libafl_qemu::edges::EDGES_MAP_SIZE;
use hashbrown::HashMap; use hashbrown::HashMap;
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -64,7 +67,7 @@ pub struct STGNode
} }
impl STGNode { impl STGNode {
pub fn pretty_print(&self) -> String { pub fn pretty_print(&self) -> String {
format!("{}\n{:x}-{:x}", self.base.current_task.0.task_name, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0)) format!("{}\n{:x}-{:x}\n{}", self.base.current_task.0.task_name, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0), self.base.print_lists())
} }
fn calculate_hash(&self) -> u64 { fn calculate_hash(&self) -> u64 {
let mut s = DefaultHasher::new(); let mut s = DefaultHasher::new();
@ -124,6 +127,12 @@ impl Named for STGFeedbackState
//============================= Graph Feedback //============================= Graph Feedback
pub static mut STG_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE];
pub static mut MAX_STG_NUM: usize = 0;
pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u8> {
OwnedMutSlice::from_raw_parts_mut(STG_MAP.as_mut_ptr(), STG_MAP.len())
}
/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`]
#[derive(Serialize, Deserialize, Clone, Debug, Default)] #[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct StgFeedback pub struct StgFeedback
@ -131,6 +140,21 @@ pub struct StgFeedback
name: String, name: String,
last_trace: Option<Vec<NodeIndex>>, last_trace: Option<Vec<NodeIndex>>,
} }
const INTEREST_EDGE : bool = true;
const INTEREST_NODE : bool = true;
fn set_observer_map(trace : &Vec<EdgeIndex>) {
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()]+=1;
}
}
}
impl StgFeedback { impl StgFeedback {
pub fn new() -> Self { pub fn new() -> Self {
Self {name: String::from("SysMapFeedback"), last_trace: None } Self {name: String::from("SysMapFeedback"), last_trace: None }
@ -143,9 +167,10 @@ impl StgFeedback {
/// newly discovered node? /// newly discovered node?
/// side effect: /// side effect:
/// the graph gets new nodes /// the graph gets new nodes
fn update_stg(trace: &Vec<RefinedFreeRTOSSystemState>, map: Vec<(String, Rc<AtomicBasicBlock>, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec<NodeIndex>, bool) { fn update_stg(trace: &Vec<RefinedFreeRTOSSystemState>, map: Vec<(String, Rc<AtomicBasicBlock>, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec<NodeIndex>, Vec<EdgeIndex>, bool) {
let mut returntrace = vec![fbs.entrypoint]; let mut return_node_trace = vec![fbs.entrypoint];
let mut new_stg_edge = false; let mut return_edge_trace = vec![];
let mut interesting = false;
// add all missing state+abb combinations to the graph // add all missing state+abb combinations to the graph
for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact
if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { // if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { //
@ -158,14 +183,19 @@ impl StgFeedback {
// not present // not present
let idx = fbs.graph.add_node(node); let idx = fbs.graph.add_node(node);
fbs.index.insert(h_node, idx); fbs.index.insert(h_node, idx);
interesting |= INTEREST_NODE;
idx idx
}; };
// connect in graph if edge not present // connect in graph if edge not present
if !fbs.graph.neighbors_directed(returntrace[returntrace.len()-1],Direction::Outgoing).any(|x| x == next_idx) { let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx);
fbs.graph.add_edge(returntrace[returntrace.len()-1], next_idx, ()); if let Some(e_) = e {
new_stg_edge = true; return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_));
} else {
let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, ());
return_edge_trace.push(e_);
interesting |= INTEREST_EDGE;
} }
returntrace.push(next_idx); return_node_trace.push(next_idx);
/* /*
Ideas: Ideas:
Mark edges triggered by interrupts Mark edges triggered by interrupts
@ -175,12 +205,16 @@ impl StgFeedback {
*/ */
} }
} }
if !fbs.graph.neighbors_directed(returntrace[returntrace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { // every path terminates at the end
fbs.graph.add_edge(returntrace[returntrace.len()-1], fbs.exit, ()); if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) {
new_stg_edge = true; let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, ());
return_edge_trace.push(e_);
interesting |= INTEREST_EDGE;
} }
returntrace.push(fbs.exit); return_node_trace.push(fbs.exit);
(returntrace, new_stg_edge) #[cfg(feature = "feed_stg")]
set_observer_map(&return_edge_trace);
(return_node_trace, return_edge_trace, interesting)
} }
} }
@ -217,7 +251,7 @@ where
let abbs = trace_to_state_abb(&observer.last_run); let abbs = trace_to_state_abb(&observer.last_run);
// println!("{:?}",abbs); // println!("{:?}",abbs);
let (trace, new_edge) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); let (trace, _, new_edge) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate);
// let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| ""); // let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| "");
// let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); // let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string();