timeshift variable, handle nested isr+api, bump max_interrupts
This commit is contained in:
parent
c7bf1be8b1
commit
b9e388d9d5
@ -40,6 +40,16 @@ use std::path::PathBuf;
|
||||
|
||||
pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH;
|
||||
|
||||
pub const QEMU_ICOUNT_SHIFT : u32 = 5;
|
||||
pub const QEMU_ISNS_PER_SEC : u32 = u32::pow(10, 9) / u32::pow(2, QEMU_ICOUNT_SHIFT);
|
||||
pub const QEMU_ISNS_PER_USEC : u32 = QEMU_ISNS_PER_SEC / 1000000;
|
||||
pub const QEMU_NS_PER_ISN : u32 = 1 << QEMU_ICOUNT_SHIFT;
|
||||
pub const TARGET_SYSCLK_FREQ : u32 = 25 * 1000 * 1000;
|
||||
pub const TARGET_MHZ_PER_MIPS : f32 = TARGET_SYSCLK_FREQ as f32 / QEMU_ISNS_PER_SEC as f32;
|
||||
pub const TARGET_MIPS_PER_MHZ : f32 = QEMU_ISNS_PER_SEC as f32 / TARGET_SYSCLK_FREQ as f32;
|
||||
pub const TARGET_SYSCLK_PER_QEMU_SEC : u32 = (TARGET_SYSCLK_FREQ as f32 * TARGET_MIPS_PER_MHZ) as u32;
|
||||
pub const QEMU_SYSCLK_PER_TARGET_SEC : u32 = (TARGET_SYSCLK_FREQ as f32 * TARGET_MHZ_PER_MIPS) as u32;
|
||||
|
||||
//========== Metadata
|
||||
#[derive(Debug, SerdeAny, Serialize, Deserialize)]
|
||||
pub struct QemuIcountMetadata {
|
||||
@ -222,7 +232,7 @@ where
|
||||
{
|
||||
// TODO Replace with match_name_type when stable
|
||||
let observer = observers.match_name::<QemuClockObserver>(self.name()).unwrap();
|
||||
self.exec_time = Some(Duration::from_nanos(observer.last_runtime() << 4)); // Assume a somewhat realistic multiplier of clock, it does not matter
|
||||
self.exec_time = Some(Duration::from_nanos(observer.last_runtime() << QEMU_ICOUNT_SHIFT)); // Assume a somewhat realistic multiplier of clock, it does not matter
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
|
@ -4,17 +4,17 @@ 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 hashbrown::HashMap;
|
||||
use libafl_bolts::{
|
||||
core_affinity::Cores, current_nanos, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsMutSlice, AsSlice
|
||||
core_affinity::Cores, current_nanos, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsMutSlice, AsSlice
|
||||
};
|
||||
use libafl::{
|
||||
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
|
||||
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
|
||||
};
|
||||
use libafl_qemu::{
|
||||
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
|
||||
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
|
||||
};
|
||||
use rand::{SeedableRng, StdRng, Rng};
|
||||
use crate::{
|
||||
clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler}
|
||||
clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler}
|
||||
};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use clap::{Parser, Subcommand};
|
||||
@ -24,6 +24,7 @@ use petgraph::graph::EdgeIndex;
|
||||
use petgraph::graph::NodeIndex;
|
||||
use petgraph::prelude::DiGraph;
|
||||
use crate::systemstate::stg::STGFeedbackState;
|
||||
use crate::clock::QEMU_ICOUNT_SHIFT;
|
||||
|
||||
// Constants ================================================================================
|
||||
|
||||
@ -32,92 +33,92 @@ pub static mut RNG_SEED: u64 = 1;
|
||||
pub static mut LIMIT : u32 = u32::MAX;
|
||||
pub const FIRST_INT : u32 = 500000;
|
||||
|
||||
pub const MAX_NUM_INTERRUPT: usize = 32;
|
||||
pub const DO_NUM_INTERRUPT: usize = 32;
|
||||
pub const MAX_NUM_INTERRUPT: usize = 128;
|
||||
pub const DO_NUM_INTERRUPT: usize = 128;
|
||||
pub static mut MAX_INPUT_SIZE: usize = 32;
|
||||
/// Read ELF program headers to resolve physical load addresses.
|
||||
fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
|
||||
let ret;
|
||||
for i in &tab.goblin().program_headers {
|
||||
if i.vm_range().contains(&vaddr.try_into().unwrap()) {
|
||||
ret = vaddr - TryInto::<GuestPhysAddr>::try_into(i.p_vaddr).unwrap()
|
||||
+ TryInto::<GuestPhysAddr>::try_into(i.p_paddr).unwrap();
|
||||
return ret - (ret % 2);
|
||||
}
|
||||
let ret;
|
||||
for i in &tab.goblin().program_headers {
|
||||
if i.vm_range().contains(&vaddr.try_into().unwrap()) {
|
||||
ret = vaddr - TryInto::<GuestPhysAddr>::try_into(i.p_vaddr).unwrap()
|
||||
+ TryInto::<GuestPhysAddr>::try_into(i.p_paddr).unwrap();
|
||||
return ret - (ret % 2);
|
||||
}
|
||||
return vaddr;
|
||||
}
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
pub fn load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> GuestAddr {
|
||||
try_load_symbol(elf, symbol, do_translation).expect(&format!("Symbol {} not found", symbol))
|
||||
try_load_symbol(elf, symbol, do_translation).expect(&format!("Symbol {} not found", symbol))
|
||||
}
|
||||
|
||||
pub fn try_load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> Option<GuestAddr> {
|
||||
let ret = elf
|
||||
.resolve_symbol(symbol, 0);
|
||||
if do_translation {
|
||||
Option::map_or(ret, None, |x| Some(virt2phys(x as GuestPhysAddr,&elf) as GuestAddr))
|
||||
} else {ret}
|
||||
let ret = elf
|
||||
.resolve_symbol(symbol, 0);
|
||||
if do_translation {
|
||||
Option::map_or(ret, None, |x| Some(virt2phys(x as GuestPhysAddr,&elf) as GuestAddr))
|
||||
} else {ret}
|
||||
}
|
||||
|
||||
pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option<std::ops::Range<GuestAddr>> {
|
||||
let gob = elf.goblin();
|
||||
let gob = elf.goblin();
|
||||
|
||||
let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect();
|
||||
funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value));
|
||||
let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect();
|
||||
funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value));
|
||||
|
||||
for sym in &gob.syms {
|
||||
if let Some(sym_name) = gob.strtab.get_at(sym.st_name) {
|
||||
if sym_name == symbol {
|
||||
if sym.st_value == 0 {
|
||||
return None;
|
||||
} else {
|
||||
//#[cfg(cpu_target = "arm")]
|
||||
// Required because of arm interworking addresses aka bit(0) for thumb mode
|
||||
let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr);
|
||||
//#[cfg(not(cpu_target = "arm"))]
|
||||
//let addr = sym.st_value as GuestAddr;
|
||||
// look for first function after addr
|
||||
let sym_end = funcs.iter().find(|x| x.st_value > sym.st_value);
|
||||
if let Some(sym_end) = sym_end {
|
||||
// println!("{} {:#x}..{} {:#x}", gob.strtab.get_at(sym.st_name).unwrap_or(""),addr, gob.strtab.get_at(sym_end.st_name).unwrap_or(""),sym_end.st_value & !0x1);
|
||||
return Some(addr..((sym_end.st_value & !0x1) as GuestAddr));
|
||||
}
|
||||
return None;
|
||||
};
|
||||
}
|
||||
for sym in &gob.syms {
|
||||
if let Some(sym_name) = gob.strtab.get_at(sym.st_name) {
|
||||
if sym_name == symbol {
|
||||
if sym.st_value == 0 {
|
||||
return None;
|
||||
} else {
|
||||
//#[cfg(cpu_target = "arm")]
|
||||
// Required because of arm interworking addresses aka bit(0) for thumb mode
|
||||
let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr);
|
||||
//#[cfg(not(cpu_target = "arm"))]
|
||||
//let addr = sym.st_value as GuestAddr;
|
||||
// look for first function after addr
|
||||
let sym_end = funcs.iter().find(|x| x.st_value > sym.st_value);
|
||||
if let Some(sym_end) = sym_end {
|
||||
// println!("{} {:#x}..{} {:#x}", gob.strtab.get_at(sym.st_name).unwrap_or(""),addr, gob.strtab.get_at(sym_end.st_name).unwrap_or(""),sym_end.st_value & !0x1);
|
||||
return Some(addr..((sym_end.st_value & !0x1) as GuestAddr));
|
||||
}
|
||||
return None;
|
||||
};
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn get_all_fn_symbol_ranges(elf: &EasyElf, api_range: std::ops::Range<GuestAddr>) -> HashMap<String,std::ops::Range<GuestAddr>> {
|
||||
let mut api_addreses : HashMap<String,std::ops::Range<GuestAddr>> = HashMap::new();
|
||||
let mut api_addreses : HashMap<String,std::ops::Range<GuestAddr>> = HashMap::new();
|
||||
|
||||
let gob = elf.goblin();
|
||||
let gob = elf.goblin();
|
||||
|
||||
let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function() && api_range.contains(&x.st_value.try_into().unwrap())).collect();
|
||||
funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value));
|
||||
let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function() && api_range.contains(&x.st_value.try_into().unwrap())).collect();
|
||||
funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value));
|
||||
|
||||
for sym in &funcs {
|
||||
let sym_name = gob.strtab.get_at(sym.st_name);
|
||||
if let Some(sym_name) = sym_name {
|
||||
// if ISR_SYMBOLS.contains(&sym_name) {continue;}; // skip select symbols, which correspond to ISR-safe system calls
|
||||
if let Some(r) = get_function_range(elf, sym_name) {
|
||||
api_addreses.insert(sym_name.to_string(), r);
|
||||
}
|
||||
for sym in &funcs {
|
||||
let sym_name = gob.strtab.get_at(sym.st_name);
|
||||
if let Some(sym_name) = sym_name {
|
||||
// if ISR_SYMBOLS.contains(&sym_name) {continue;}; // skip select symbols, which correspond to ISR-safe system calls
|
||||
if let Some(r) = get_function_range(elf, sym_name) {
|
||||
api_addreses.insert(sym_name.to_string(), r);
|
||||
}
|
||||
}
|
||||
for i in api_addreses.iter() {
|
||||
println!("{} {:#x}..{:#x}", i.0, i.1.start, i.1.end);
|
||||
}
|
||||
}
|
||||
for i in api_addreses.iter() {
|
||||
println!("{} {:#x}..{:#x}", i.0, i.1.start, i.1.end);
|
||||
}
|
||||
|
||||
return api_addreses;
|
||||
return api_addreses;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
static mut libafl_interrupt_offsets : [u32; 32];
|
||||
static mut libafl_num_interrupts : usize;
|
||||
static mut libafl_interrupt_offsets : [u32; MAX_NUM_INTERRUPT];
|
||||
static mut libafl_num_interrupts : usize;
|
||||
}
|
||||
|
||||
// Argument parsing ================================================================================
|
||||
@ -125,329 +126,337 @@ extern "C" {
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
/// Kernel Image
|
||||
#[arg(short, long, value_name = "FILE")]
|
||||
kernel: PathBuf,
|
||||
/// Kernel Image
|
||||
#[arg(short, long, value_name = "FILE")]
|
||||
kernel: PathBuf,
|
||||
|
||||
/// Sets a custom config file
|
||||
#[arg(short, long, value_name = "FILE")]
|
||||
config: PathBuf,
|
||||
/// Sets a custom config file
|
||||
#[arg(short, long, value_name = "FILE")]
|
||||
config: PathBuf,
|
||||
|
||||
/// Sets the prefix of dumed files
|
||||
#[arg(short='n', long, value_name = "FILENAME")]
|
||||
dump_name: Option<PathBuf>,
|
||||
/// Sets the prefix of dumed files
|
||||
#[arg(short='n', long, value_name = "FILENAME")]
|
||||
dump_name: Option<PathBuf>,
|
||||
|
||||
/// do time dumps
|
||||
#[arg(short='t', long)]
|
||||
dump_times: bool,
|
||||
/// do time dumps
|
||||
#[arg(short='t', long)]
|
||||
dump_times: bool,
|
||||
|
||||
/// do worst-case dumps
|
||||
#[arg(short='a', long)]
|
||||
dump_cases: bool,
|
||||
/// do worst-case dumps
|
||||
#[arg(short='a', long)]
|
||||
dump_cases: bool,
|
||||
|
||||
/// do trace dumps (if supported)
|
||||
#[arg(short='r', long)]
|
||||
dump_traces: bool,
|
||||
/// do trace dumps (if supported)
|
||||
#[arg(short='r', long)]
|
||||
dump_traces: bool,
|
||||
|
||||
/// do graph dumps (if supported)
|
||||
#[arg(short='g', long)]
|
||||
dump_graph: bool,
|
||||
/// do graph dumps (if supported)
|
||||
#[arg(short='g', long)]
|
||||
dump_graph: bool,
|
||||
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
#[derive(Subcommand,Clone)]
|
||||
enum Commands {
|
||||
/// run a single input
|
||||
Showmap {
|
||||
/// take this input
|
||||
#[arg(short, long)]
|
||||
input: PathBuf,
|
||||
},
|
||||
/// start fuzzing campaign
|
||||
Fuzz {
|
||||
/// disable heuristic
|
||||
#[arg(short, long)]
|
||||
random: bool,
|
||||
/// seed for randomness
|
||||
#[arg(short, long)]
|
||||
seed: Option<u64>,
|
||||
/// runtime in seconds
|
||||
#[arg(short, long)]
|
||||
time: Option<u64>,
|
||||
}
|
||||
/// run a single input
|
||||
Showmap {
|
||||
/// take this input
|
||||
#[arg(short, long)]
|
||||
input: PathBuf,
|
||||
},
|
||||
/// start fuzzing campaign
|
||||
Fuzz {
|
||||
/// disable heuristic
|
||||
#[arg(short, long)]
|
||||
random: bool,
|
||||
/// seed for randomness
|
||||
#[arg(short, long)]
|
||||
seed: Option<u64>,
|
||||
/// runtime in seconds
|
||||
#[arg(short, long)]
|
||||
time: Option<u64>,
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes a state, cli and a suffix, writes out the current worst case
|
||||
macro_rules! do_dump_case {
|
||||
( $s:expr,$cli:expr, $c:expr) => {
|
||||
if ($cli.dump_cases) {
|
||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"case"} else {$c});
|
||||
println!("Dumping worst case to {:?}", &dump_path);
|
||||
let corpus = $s.corpus();
|
||||
let mut worst = Duration::new(0,0);
|
||||
let mut worst_input = None;
|
||||
for i in 0..corpus.count() {
|
||||
let tc = corpus.get(corpus.nth(i.into())).expect("Could not get element from corpus").borrow();
|
||||
if worst < tc.exec_time().expect("Testcase missing duration") {
|
||||
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
|
||||
worst = tc.exec_time().expect("Testcase missing duration");
|
||||
}
|
||||
}
|
||||
if let Some(wi) = worst_input {
|
||||
fs::write(dump_path,wi).expect("Failed to write worst corpus element");
|
||||
( $s:expr,$cli:expr, $c:expr) => {
|
||||
if ($cli.dump_cases) {
|
||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"case"} else {$c});
|
||||
println!("Dumping worst case to {:?}", &dump_path);
|
||||
let corpus = $s.corpus();
|
||||
let mut worst = Duration::new(0,0);
|
||||
let mut worst_input = None;
|
||||
for i in 0..corpus.count() {
|
||||
let tc = corpus.get(corpus.nth(i.into())).expect("Could not get element from corpus").borrow();
|
||||
if worst < tc.exec_time().expect("Testcase missing duration") {
|
||||
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
|
||||
worst = tc.exec_time().expect("Testcase missing duration");
|
||||
}
|
||||
}
|
||||
if let Some(wi) = worst_input {
|
||||
fs::write(dump_path,wi).expect("Failed to write worst corpus element");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes a state, cli and a suffix, appends icount history
|
||||
macro_rules! do_dump_times {
|
||||
($state:expr, $cli:expr, $c:expr) => {
|
||||
if $cli.dump_times {
|
||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"time"} else {$c});
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(dump_path).expect("Could not open timedump");
|
||||
if let Ok(ichist) = $state.metadata_mut::<IcHist>() {
|
||||
for i in ichist.0.drain(..) {
|
||||
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
||||
}
|
||||
($state:expr, $cli:expr, $c:expr) => {
|
||||
if $cli.dump_times {
|
||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"time"} else {$c});
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(dump_path).expect("Could not open timedump");
|
||||
if let Ok(ichist) = $state.metadata_mut::<IcHist>() {
|
||||
for i in ichist.0.drain(..) {
|
||||
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Takes a state and a bool, writes out the current graph
|
||||
macro_rules! do_dump_stg {
|
||||
($state:expr, $cli:expr, $c:expr) => {
|
||||
#[cfg(feature = "trace_stg")]
|
||||
if $cli.dump_graph {
|
||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c});
|
||||
println!("Dumping graph to {:?}", &dump_path);
|
||||
if let Some(md) = $state.named_metadata_map_mut().get_mut::<STGFeedbackState>("stgfeedbackstate") {
|
||||
let out = md.graph.map(|_i,x| x.color_print(), |_i,x| x.color_print());
|
||||
let outs = Dot::with_config(&out, &[]).to_string();
|
||||
let outs = outs.replace("\\\"","\"");
|
||||
let outs = outs.replace(';',"\\n");
|
||||
fs::write(dump_path,outs).expect("Failed to write graph");
|
||||
}
|
||||
($state:expr, $cli:expr, $c:expr) => {
|
||||
#[cfg(feature = "trace_stg")]
|
||||
if $cli.dump_graph {
|
||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c});
|
||||
println!("Dumping graph to {:?}", &dump_path);
|
||||
if let Some(md) = $state.named_metadata_map_mut().get_mut::<STGFeedbackState>("stgfeedbackstate") {
|
||||
let out = md.graph.map(|_i,x| x.color_print(), |_i,x| x.color_print());
|
||||
let outs = Dot::with_config(&out, &[]).to_string();
|
||||
let outs = outs.replace("\\\"","\"");
|
||||
let outs = outs.replace(';',"\\n");
|
||||
fs::write(dump_path,outs).expect("Failed to write graph");
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Takes a state and a bool, writes out top rated inputs
|
||||
macro_rules! do_dump_toprated {
|
||||
($state:expr, $cli:expr, $c:expr) => {
|
||||
if $cli.dump_cases {
|
||||
{
|
||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"toprated"} else {$c});
|
||||
println!("Dumping toprated to {:?}", &dump_path);
|
||||
if let Some(md) = $state.metadata_map_mut().get_mut::<TopRatedsMetadata>() {
|
||||
let mut uniq: Vec<CorpusId> = md.map.values().map(|x| x.clone()).collect();
|
||||
uniq.sort();
|
||||
uniq.dedup();
|
||||
fs::write(dump_path,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn env_from_config(kernel : &PathBuf, path : &PathBuf) {
|
||||
let is_csv = path.as_path().extension().map_or(false, |x| x=="csv");
|
||||
if !is_csv {
|
||||
let lines = std::fs::read_to_string(path).expect("Config file not found");
|
||||
let lines = lines.lines().filter(
|
||||
|x| x.len()>0
|
||||
);
|
||||
for l in lines {
|
||||
let pair = l.split_once('=').expect("Non VAR=VAL line in config");
|
||||
std::env::set_var(pair.0, pair.1);
|
||||
}
|
||||
} else {
|
||||
let mut reader = csv::Reader::from_path(path).expect("CSV read from config failed");
|
||||
let p = kernel.as_path();
|
||||
let stem = p.file_stem().expect("Kernel filename error").to_str().unwrap();
|
||||
for r in reader.records() {
|
||||
let rec = r.expect("CSV entry error");
|
||||
if stem == &rec[0] {
|
||||
std::env::set_var("FUZZ_MAIN", &rec[1]);
|
||||
std::env::set_var("FUZZ_INPUT", &rec[2]);
|
||||
std::env::set_var("FUZZ_INPUT_LEN", &rec[3]);
|
||||
std::env::set_var("BREAKPOINT", &rec[4]);
|
||||
break;
|
||||
($state:expr, $cli:expr, $c:expr) => {
|
||||
if $cli.dump_cases {
|
||||
{
|
||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"toprated"} else {$c});
|
||||
println!("Dumping toprated to {:?}", &dump_path);
|
||||
if let Some(md) = $state.metadata_map_mut().get_mut::<TopRatedsMetadata>() {
|
||||
let mut uniq: Vec<CorpusId> = md.map.values().map(|x| x.clone()).collect();
|
||||
uniq.sort();
|
||||
uniq.dedup();
|
||||
fs::write(dump_path,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn env_from_config(kernel : &PathBuf, path : &PathBuf) {
|
||||
let is_csv = path.as_path().extension().map_or(false, |x| x=="csv");
|
||||
if !is_csv {
|
||||
let lines = std::fs::read_to_string(path).expect("Config file not found");
|
||||
let lines = lines.lines().filter(
|
||||
|x| x.len()>0
|
||||
);
|
||||
for l in lines {
|
||||
let pair = l.split_once('=').expect("Non VAR=VAL line in config");
|
||||
std::env::set_var(pair.0, pair.1);
|
||||
}
|
||||
} else {
|
||||
let mut reader = csv::Reader::from_path(path).expect("CSV read from config failed");
|
||||
let p = kernel.as_path();
|
||||
let stem = p.file_stem().expect("Kernel filename error").to_str().unwrap();
|
||||
for r in reader.records() {
|
||||
let rec = r.expect("CSV entry error");
|
||||
if stem == &rec[0] {
|
||||
std::env::set_var("FUZZ_MAIN", &rec[1]);
|
||||
std::env::set_var("FUZZ_INPUT", &rec[2]);
|
||||
std::env::set_var("FUZZ_INPUT_LEN", &rec[3]);
|
||||
std::env::set_var("BREAKPOINT", &rec[4]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fuzzer setup ================================================================================
|
||||
|
||||
pub fn fuzz() {
|
||||
let cli = Cli::parse();
|
||||
env_from_config(&cli.kernel, &cli.config);
|
||||
unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();}
|
||||
if cli.dump_name.is_none() && (cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph) {
|
||||
panic!("Dump name not give but dump is requested");
|
||||
}
|
||||
let mut starttime = std::time::Instant::now();
|
||||
// Hardcoded parameters
|
||||
let timeout = Duration::from_secs(10);
|
||||
let broker_port = 1337;
|
||||
let cores = Cores::from_cmdline("1").unwrap();
|
||||
let corpus_dirs = [PathBuf::from("./corpus")];
|
||||
let objective_dir = PathBuf::from("./crashes");
|
||||
let cli = Cli::parse();
|
||||
env_from_config(&cli.kernel, &cli.config);
|
||||
unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();}
|
||||
if cli.dump_name.is_none() && (cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph) {
|
||||
panic!("Dump name not give but dump is requested");
|
||||
}
|
||||
let mut starttime = std::time::Instant::now();
|
||||
// Hardcoded parameters
|
||||
let timeout = Duration::from_secs(10);
|
||||
let broker_port = 1337;
|
||||
let cores = Cores::from_cmdline("1").unwrap();
|
||||
let corpus_dirs = [PathBuf::from("./corpus")];
|
||||
let objective_dir = PathBuf::from("./crashes");
|
||||
|
||||
let mut elf_buffer = Vec::new();
|
||||
let elf = EasyElf::from_file(
|
||||
&cli.kernel,
|
||||
&mut elf_buffer,
|
||||
let mut elf_buffer = Vec::new();
|
||||
let elf = EasyElf::from_file(
|
||||
&cli.kernel,
|
||||
&mut elf_buffer,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// the main address where the fuzzer starts
|
||||
// if this is set for freeRTOS it has an influence on where the data will have to be written,
|
||||
// since the startup routine copies the data segemnt to it's virtual address
|
||||
let main_addr = elf
|
||||
.resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_MAIN".to_owned()), 0);
|
||||
if let Some(main_addr) = main_addr {
|
||||
println!("main address = {:#x}", main_addr);
|
||||
}
|
||||
|
||||
let input_addr = load_symbol(&elf, &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), true);
|
||||
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
||||
|
||||
let input_length_ptr = try_load_symbol(&elf, &env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()), true);
|
||||
let input_counter_ptr = try_load_symbol(&elf, &env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), true);
|
||||
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let curr_tcb_pointer = load_symbol(&elf, "pxCurrentTCB", false); // loads to the address specified in elf, without respecting program headers
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
println!("TCB pointer at {:#x}", curr_tcb_pointer);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let task_queue_addr = load_symbol(&elf, "pxReadyTasksLists", false);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let scheduler_lock = load_symbol(&elf, "uxSchedulerSuspended", false);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let scheduler_running = load_symbol(&elf, "xSchedulerRunning", false);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let critical_section = load_symbol(&elf, "uxCriticalNesting", false);
|
||||
let app_start = load_symbol(&elf, "__APP_CODE_START__", false);
|
||||
let app_end = load_symbol(&elf, "__APP_CODE_END__", false);
|
||||
let app_range = app_start..app_end;
|
||||
let api_start = load_symbol(&elf, "__API_CODE_START__", false);
|
||||
let api_end = load_symbol(&elf, "__API_CODE_END__", false);
|
||||
let api_range = api_start..api_end;
|
||||
|
||||
let breakpoint = elf
|
||||
.resolve_symbol(
|
||||
&env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()),
|
||||
0,
|
||||
)
|
||||
.unwrap();
|
||||
.expect("Symbol or env BREAKPOINT not found");
|
||||
println!("Breakpoint address = {:#x}", breakpoint);
|
||||
unsafe {
|
||||
libafl_num_interrupts = 0;
|
||||
}
|
||||
|
||||
if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") {
|
||||
unsafe {MAX_INPUT_SIZE = str::parse::<usize>(&input_len).expect("FUZZ_INPUT_LEN was not a number");}
|
||||
}
|
||||
unsafe {dbg!(MAX_INPUT_SIZE);}
|
||||
|
||||
if let Ok(seed) = env::var("SEED_RANDOM") {
|
||||
unsafe {RNG_SEED = str::parse::<u64>(&seed).expect("SEED_RANDOM must be an integer.");}
|
||||
}
|
||||
|
||||
let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range);
|
||||
let app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone());
|
||||
|
||||
let mut isr_ranges : HashMap<String,std::ops::Range<GuestAddr>> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect();
|
||||
systemstate::helpers::ISR_SYMBOLS.iter().for_each(|x| {let _ =(app_fn_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone()))).map(|y| isr_ranges.insert(y.0,y.1));}); // add used defined isr
|
||||
let denylist=isr_ranges.values().map(|x| x.clone()).collect();
|
||||
let denylist = QemuInstrumentationFilter::DenyList(denylist); // do not count isr jumps, which are useless
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let mut isr_addreses : HashMap<GuestAddr, String> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect();
|
||||
systemstate::helpers::ISR_SYMBOLS.iter().for_each(|x| {let _ =(app_fn_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone()))).map(|y| isr_addreses.insert(y.1.start, y.0));}); // add used defined isr
|
||||
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
for i in systemstate::helpers::ISR_SYMBOLS {
|
||||
if isr_ranges.get(&i.to_string()).is_none() {
|
||||
if let Some(fr) = get_function_range(&elf, i) {
|
||||
isr_addreses.insert(fr.start, i.to_string());
|
||||
isr_ranges.insert(i.to_string(), fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let api_addreses : HashMap<GuestAddr, String> = api_ranges.iter().map(|(k,v)| (v.start,k.clone())).collect();
|
||||
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let api_ranges : Vec<_> = api_ranges.into_iter().collect();
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let isr_ranges : Vec<_> = isr_ranges.into_iter().collect();
|
||||
|
||||
// Client setup ================================================================================
|
||||
|
||||
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||
// Initialize QEMU
|
||||
let args: Vec<String> = vec![
|
||||
"target/debug/fret",
|
||||
"-icount",
|
||||
&format!("shift={},align=off,sleep=off", QEMU_ICOUNT_SHIFT),
|
||||
"-machine",
|
||||
"mps2-an385",
|
||||
"-cpu",
|
||||
"cortex-m3",
|
||||
"-monitor",
|
||||
"null",
|
||||
"-kernel",
|
||||
&cli.kernel.as_os_str().to_str().expect("kernel path is not a string"),
|
||||
"-serial",
|
||||
"null",
|
||||
"-nographic",
|
||||
"-S",
|
||||
// "-semihosting",
|
||||
// "--semihosting-config",
|
||||
// "enable=on,target=native",
|
||||
"-snapshot",
|
||||
"-drive",
|
||||
"if=none,format=qcow2,file=dummy.qcow2",
|
||||
].into_iter().map(String::from).collect();
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
let emu = Emulator::new(&args, &env).expect("Emulator creation failed");
|
||||
|
||||
// the main address where the fuzzer starts
|
||||
// if this is set for freeRTOS it has an influence on where the data will have to be written,
|
||||
// since the startup routine copies the data segemnt to it's virtual address
|
||||
let main_addr = elf
|
||||
.resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_MAIN".to_owned()), 0);
|
||||
if let Some(main_addr) = main_addr {
|
||||
println!("main address = {:#x}", main_addr);
|
||||
}
|
||||
|
||||
let input_addr = load_symbol(&elf, &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), true);
|
||||
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
||||
|
||||
let input_length_ptr = try_load_symbol(&elf, &env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()), true);
|
||||
let input_counter_ptr = try_load_symbol(&elf, &env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), true);
|
||||
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let curr_tcb_pointer = load_symbol(&elf, "pxCurrentTCB", false); // loads to the address specified in elf, without respecting program headers
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
println!("TCB pointer at {:#x}", curr_tcb_pointer);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let task_queue_addr = load_symbol(&elf, "pxReadyTasksLists", false);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let scheduler_lock = load_symbol(&elf, "uxSchedulerSuspended", false);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let scheduler_running = load_symbol(&elf, "xSchedulerRunning", false);
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let critical_section = load_symbol(&elf, "uxCriticalNesting", false);
|
||||
let app_start = load_symbol(&elf, "__APP_CODE_START__", false);
|
||||
let app_end = load_symbol(&elf, "__APP_CODE_END__", false);
|
||||
let app_range = app_start..app_end;
|
||||
let api_start = load_symbol(&elf, "__API_CODE_START__", false);
|
||||
let api_end = load_symbol(&elf, "__API_CODE_END__", false);
|
||||
let api_range = api_start..api_end;
|
||||
|
||||
let breakpoint = elf
|
||||
.resolve_symbol(
|
||||
&env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()),
|
||||
0,
|
||||
)
|
||||
.expect("Symbol or env BREAKPOINT not found");
|
||||
println!("Breakpoint address = {:#x}", breakpoint);
|
||||
unsafe {
|
||||
libafl_num_interrupts = 0;
|
||||
}
|
||||
|
||||
if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") {
|
||||
unsafe {MAX_INPUT_SIZE = str::parse::<usize>(&input_len).expect("FUZZ_INPUT_LEN was not a number");}
|
||||
}
|
||||
unsafe {dbg!(MAX_INPUT_SIZE);}
|
||||
|
||||
if let Ok(seed) = env::var("SEED_RANDOM") {
|
||||
unsafe {RNG_SEED = str::parse::<u64>(&seed).expect("SEED_RANDOM must be an integer.");}
|
||||
}
|
||||
|
||||
let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range);
|
||||
|
||||
let mut isr_ranges : HashMap<String,std::ops::Range<GuestAddr>> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect();
|
||||
let denylist=isr_ranges.values().map(|x| x.clone()).collect();
|
||||
let denylist = QemuInstrumentationFilter::DenyList(denylist); // do not count isr jumps, which are useless
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let mut isr_addreses : HashMap<GuestAddr, String> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect();
|
||||
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
for i in systemstate::helpers::ISR_SYMBOLS {
|
||||
if isr_ranges.get(&i.to_string()).is_none() {
|
||||
if let Some(fr) = get_function_range(&elf, i) {
|
||||
isr_addreses.insert(fr.start, i.to_string());
|
||||
isr_ranges.insert(i.to_string(), fr);
|
||||
}
|
||||
unsafe {
|
||||
libafl_qemu_set_native_breakpoint(main_addr);
|
||||
emu.run();
|
||||
libafl_qemu_remove_native_breakpoint(main_addr);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let api_addreses : HashMap<GuestAddr, String> = api_ranges.iter().map(|(k,v)| (v.start,k.clone())).collect();
|
||||
unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT
|
||||
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let api_ranges : Vec<_> = api_ranges.into_iter().collect();
|
||||
#[cfg(feature = "observe_systemstate")]
|
||||
let isr_ranges : Vec<_> = isr_ranges.into_iter().collect();
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let mut buf = target.as_slice();
|
||||
let mut len = buf.len();
|
||||
unsafe {
|
||||
#[cfg(feature = "fuzz_int")]
|
||||
{
|
||||
let t = input_bytes_to_interrupt_times(buf);
|
||||
for i in 0..t.len() {libafl_interrupt_offsets[i]=t[i];}
|
||||
libafl_num_interrupts=t.len();
|
||||
|
||||
// Client setup ================================================================================
|
||||
|
||||
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||
// Initialize QEMU
|
||||
let args: Vec<String> = vec![
|
||||
"target/debug/fret",
|
||||
"-icount",
|
||||
"shift=4,align=off,sleep=off",
|
||||
"-machine",
|
||||
"mps2-an385",
|
||||
"-monitor",
|
||||
"null",
|
||||
"-kernel",
|
||||
&cli.kernel.as_os_str().to_str().expect("kernel path is not a string"),
|
||||
"-serial",
|
||||
"null",
|
||||
"-nographic",
|
||||
"-S",
|
||||
"-semihosting",
|
||||
"--semihosting-config",
|
||||
"enable=on,target=native",
|
||||
"-snapshot",
|
||||
"-drive",
|
||||
"if=none,format=qcow2,file=dummy.qcow2",
|
||||
].into_iter().map(String::from).collect();
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
let emu = Emulator::new(&args, &env).expect("Emulator creation failed");
|
||||
|
||||
if let Some(main_addr) = main_addr {
|
||||
unsafe {
|
||||
libafl_qemu_set_native_breakpoint(main_addr);
|
||||
emu.run();
|
||||
libafl_qemu_remove_native_breakpoint(main_addr);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let mut buf = target.as_slice();
|
||||
let mut len = buf.len();
|
||||
unsafe {
|
||||
#[cfg(feature = "fuzz_int")]
|
||||
{
|
||||
let t = input_bytes_to_interrupt_times(buf);
|
||||
for i in 0..t.len() {libafl_interrupt_offsets[i]=t[i];}
|
||||
libafl_num_interrupts=t.len();
|
||||
|
||||
if buf.len() > libafl_num_interrupts*4 {
|
||||
buf = &buf[libafl_num_interrupts*4..];
|
||||
len = buf.len();
|
||||
}
|
||||
// println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec());
|
||||
if buf.len() > libafl_num_interrupts*4 {
|
||||
buf = &buf[libafl_num_interrupts*4..];
|
||||
len = buf.len();
|
||||
}
|
||||
// for i in 0 .. libafl_num_interrupts {
|
||||
// libafl_interrupt_offsets[i] = FIRST_INT+TryInto::<u32>::try_into(i).unwrap()*MINIMUM_INTER_ARRIVAL_TIME;
|
||||
// }
|
||||
// println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec());
|
||||
}
|
||||
if len > MAX_INPUT_SIZE {
|
||||
buf = &buf[0..MAX_INPUT_SIZE];
|
||||
|
@ -13,9 +13,9 @@ use libafl::{
|
||||
corpus::{self, Corpus}, fuzzer::Evaluator, mark_feature_time, prelude::{new_hash_feedback, CorpusId, HasBytesVec, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasMetadata, HasNamedMetadata, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error
|
||||
};
|
||||
use libafl::prelude::State;
|
||||
use crate::{clock::IcHist, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}};
|
||||
use crate::{clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}};
|
||||
|
||||
pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*ms*/ * 62500;
|
||||
pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC;
|
||||
// one isn per 2**4 ns
|
||||
// virtual insn/sec 62500000 = 1/16 GHz
|
||||
// 1ms = 62500 insn
|
||||
|
@ -109,9 +109,9 @@ where
|
||||
where
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
for wp in self.api_fn_addrs.keys() {
|
||||
_hooks.instruction(*wp, Hook::Function(exec_syscall_hook::<QT, S>), false);
|
||||
}
|
||||
// for wp in self.api_fn_addrs.keys() {
|
||||
// _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::<QT, S>), false);
|
||||
// }
|
||||
for wp in self.isr_addrs.keys() {
|
||||
_hooks.instruction(*wp, Hook::Function(exec_isr_hook::<QT, S>), false);
|
||||
}
|
||||
@ -213,7 +213,11 @@ fn trigger_collection(emulator: &Emulator, edge: (Option<GuestAddr>,Option<Guest
|
||||
println!("API Not found: {:#x} {:#x}", src, dest);
|
||||
}
|
||||
} else if let Some(s) = h.api_fn_addrs.get(&dest) { // API Call
|
||||
systemstate.capture_point=(CaptureEvent::APIStart, s.to_string());
|
||||
// if let None = in_any_range(&h.isr_ranges, src) {
|
||||
systemstate.capture_point=(CaptureEvent::APIStart, s.to_string());
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
} else {
|
||||
println!("API Not found: {:#x}", src);
|
||||
}
|
||||
@ -315,14 +319,13 @@ where
|
||||
let mut edge = (None, Some(pc));
|
||||
unsafe {
|
||||
LAST_API_CALL.with(|x| {
|
||||
match *x.get() {
|
||||
match (*x.get()).take() {
|
||||
Some(s) => {
|
||||
edge.0=Some(s.0);
|
||||
trigger_collection(emulator, edge, h);
|
||||
},
|
||||
None => (),
|
||||
}
|
||||
*x.get()=None;
|
||||
});
|
||||
}
|
||||
|
||||
@ -343,11 +346,13 @@ where
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
if let Some(h) = hooks.helpers().match_first_type::<QemuSystemStateHelper>() {
|
||||
if h.app_range.contains(&src) && !h.app_range.contains(&dest) {
|
||||
if h.app_range.contains(&src) && !h.app_range.contains(&dest) && in_any_range(&h.isr_ranges,src).is_none() {
|
||||
if let Some(_) = in_any_range(&h.api_fn_ranges,dest) {
|
||||
// println!("New jmp {:x} {:x}", src, dest);
|
||||
// println!("API Call Edge {:x} {:x}", src, dest);
|
||||
return Some(1);
|
||||
// TODO: trigger collection right here
|
||||
// otherwise there can be a race-condition, where LAST_API_CALL is set before the api starts, if the interrupt handler calls an api function, it will misidentify the callsite of that api call
|
||||
}
|
||||
} else if dest == 0 { // !h.app_range.contains(&src) &&
|
||||
if let Some(_) = in_any_range(&h.api_fn_ranges, src) {
|
||||
@ -363,6 +368,18 @@ where
|
||||
return None;
|
||||
}
|
||||
|
||||
fn get_icount(emulator : &Emulator) -> u64 {
|
||||
unsafe {
|
||||
// TODO: investigate why can_do_io is not set sometimes, as this is just a workaround
|
||||
let c = emulator.cpu_from_index(0);
|
||||
let can_do_io = (*c.raw_ptr()).neg.can_do_io;
|
||||
(*c.raw_ptr()).neg.can_do_io = true;
|
||||
let r = emu::icount_get_raw();
|
||||
(*c.raw_ptr()).neg.can_do_io = can_do_io;
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trace_api_call<QT, S>(
|
||||
hooks: &mut QemuHooks<QT, S>,
|
||||
_state: Option<&mut S>,
|
||||
@ -373,11 +390,10 @@ where
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
if id == 1 { // API call
|
||||
unsafe {
|
||||
let p = LAST_API_CALL.with(|x| x.get());
|
||||
*p = Some((src,dest));
|
||||
// println!("Jump {:#x} {:#x}", src, dest);
|
||||
}
|
||||
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
|
||||
let emulator = hooks.emulator();
|
||||
trigger_collection(emulator, (Some(src), Some(dest)), h);
|
||||
// println!("Exec API Call {:#x} {:#x} {}", src, dest, get_icount(emulator));
|
||||
} else if id == 2 { // API return
|
||||
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
|
||||
// Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space.
|
||||
@ -389,7 +405,7 @@ where
|
||||
edge.1=Some(dest);
|
||||
|
||||
trigger_collection(emulator, edge, h);
|
||||
// println!("Exec API Return Edge {:#x} {:#x}", src, dest);
|
||||
// println!("Exec API Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator));
|
||||
}
|
||||
} else if id == 3 { // ISR return
|
||||
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
|
||||
@ -399,7 +415,7 @@ where
|
||||
edge.0=Some(in_any_range(&h.isr_ranges, src).unwrap().start);
|
||||
|
||||
trigger_collection(emulator, edge, h);
|
||||
// println!("Exec ISR Return Edge {:#x} {:#x}", src, dest);
|
||||
// println!("Exec ISR Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ where
|
||||
// unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));}
|
||||
unsafe {
|
||||
let mut temp = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC);
|
||||
fix_broken_trace(&mut temp.1);
|
||||
// fix_broken_trace(&mut temp.1);
|
||||
self.last_run = temp.0.clone();
|
||||
// println!("{:?}",temp);
|
||||
let mut temp = states2intervals(temp.0, temp.1);
|
||||
@ -154,6 +154,7 @@ fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> (Vec<Reduced
|
||||
delay_list_after: delay_list,
|
||||
// input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER,
|
||||
});
|
||||
// println!("Refine: {:?} {:?} {:x}-{:x}",i.capture_point.0, i.capture_point.1.to_string(), i.edge.0.unwrap_or(0), i.edge.1.unwrap_or(0));
|
||||
ret.1.push((i.qemu_tick, i.capture_point.0, i.capture_point.1.to_string(), i.edge));
|
||||
}
|
||||
return ret;
|
||||
@ -161,6 +162,7 @@ fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> (Vec<Reduced
|
||||
|
||||
/// Transform the states and metadata into a list of ExecIntervals
|
||||
fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, CaptureEvent, String, (Option<u32>, Option<u32>))>) -> (Vec<ExecInterval>, HashMap<u64, ReducedFreeRTOSSystemState>) {
|
||||
if trace.len() == 0 {return (Vec::new(), HashMap::new());}
|
||||
let mut isr_stack : VecDeque<u8> = VecDeque::from([]); // 2+ = ISR, 1 = systemcall, 0 = APP. Trace starts with an ISREnd and executes the app
|
||||
|
||||
|
||||
@ -179,11 +181,11 @@ fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, Capt
|
||||
level_of_task.insert(curr_name, 0);
|
||||
}
|
||||
*level_of_task.get_mut(curr_name).unwrap()=0;
|
||||
&0
|
||||
0
|
||||
},
|
||||
CaptureEvent::APIStart => { // API start can only be called in the app
|
||||
*level_of_task.get_mut(curr_name).unwrap()=1;
|
||||
&1
|
||||
1
|
||||
},
|
||||
CaptureEvent::ISREnd => {
|
||||
// special case where the next block is an app start
|
||||
@ -192,12 +194,12 @@ fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, Capt
|
||||
}
|
||||
// nested isr, TODO: Test level > 2
|
||||
if isr_stack.len() > 1 {
|
||||
isr_stack.pop_back();
|
||||
isr_stack.back().unwrap()
|
||||
isr_stack.pop_back().unwrap();
|
||||
*isr_stack.back().unwrap()
|
||||
} else {
|
||||
isr_stack.pop_back();
|
||||
// possibly go back to an api call that is still running for this task
|
||||
level_of_task.get(curr_name).unwrap()
|
||||
*level_of_task.get(curr_name).unwrap()
|
||||
}
|
||||
},
|
||||
CaptureEvent::ISRStart => {
|
||||
@ -207,16 +209,16 @@ fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, Capt
|
||||
// } else {
|
||||
// regular case
|
||||
if isr_stack.len() > 0 {
|
||||
let l = isr_stack.back().unwrap();
|
||||
isr_stack.push_back(*l);
|
||||
isr_stack.back().unwrap()
|
||||
let l = *isr_stack.back().unwrap();
|
||||
isr_stack.push_back(l+1);
|
||||
l+1
|
||||
} else {
|
||||
isr_stack.push_back(2);
|
||||
&2
|
||||
2
|
||||
}
|
||||
// }
|
||||
}
|
||||
_ => &100
|
||||
_ => 100
|
||||
};
|
||||
// if trace[i].2 == CaptureEvent::End {break;}
|
||||
let next_hash=trace[i+1].get_hash();
|
||||
@ -230,7 +232,7 @@ fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, Capt
|
||||
end_state: next_hash,
|
||||
start_capture: (meta[i].1, meta[i].2.clone()),
|
||||
end_capture: (meta[i+1].1, meta[i+1].2.clone()),
|
||||
level: *level,
|
||||
level: level,
|
||||
tick_spend_preempted: 0,
|
||||
abb: None
|
||||
});
|
||||
@ -250,8 +252,9 @@ fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeR
|
||||
let curr_name = &table[&trace[i].start_state].current_task.task_name;
|
||||
// let last : Option<&usize> = last_abb_start_of_task.get(&curr_name);
|
||||
|
||||
let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level
|
||||
let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level
|
||||
|
||||
// println!("Edge {:x}-{:x}", edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff));
|
||||
|
||||
match (&trace[i].start_capture.0, &trace[i].end_capture.0) {
|
||||
// case with abb to block correspondence
|
||||
@ -269,35 +272,38 @@ fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeR
|
||||
match trace[i].start_capture.0 {
|
||||
// generic api abb start
|
||||
CaptureEvent::APIStart => {
|
||||
// assert_eq!(open_abb, None);
|
||||
assert_eq!(open_abb, None);
|
||||
open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i);
|
||||
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}})));
|
||||
},
|
||||
// generic isr abb start
|
||||
CaptureEvent::ISRStart => {
|
||||
// assert_eq!(open_abb, None);
|
||||
open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i);
|
||||
assert_eq!(open_abb, None);
|
||||
open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i);
|
||||
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}})));
|
||||
},
|
||||
// generic app abb start
|
||||
CaptureEvent::APIEnd => {
|
||||
// assert_eq!(open_abb, None);
|
||||
open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i);
|
||||
assert_eq!(open_abb, None);
|
||||
open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i);
|
||||
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}})));
|
||||
},
|
||||
// generic continued blocks
|
||||
CaptureEvent::ISREnd => {
|
||||
// special case app abb start
|
||||
if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) {
|
||||
// assert_eq!(open_abb, None);
|
||||
assert_eq!(open_abb, None);
|
||||
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: 0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}})));
|
||||
open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i);
|
||||
open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i);
|
||||
task_has_started.insert(curr_name.clone());
|
||||
} else {
|
||||
if let Some(last) = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})) {
|
||||
// assert_ne!(open_abb,None);
|
||||
if let Some(last) = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})) {
|
||||
wip_abb_trace.push(wip_abb_trace[*last].clone());
|
||||
} else {
|
||||
eprintln!("Continued block with no start {} {:?} {:?} {} {}", trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, task_has_started.contains(curr_name),trace[i].level);
|
||||
// panic!();
|
||||
eprintln!("Continued block with no start {} {:?} {:?} {:x}-{:x} {} {}", trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff),task_has_started.contains(curr_name),trace[i].level);
|
||||
panic!();
|
||||
wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].1.unwrap_or(0), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}})))
|
||||
}
|
||||
}
|
||||
@ -309,29 +315,29 @@ fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeR
|
||||
CaptureEvent::APIStart => {
|
||||
let _t = &wip_abb_trace[i];
|
||||
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap());
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}));
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
|
||||
},
|
||||
// generic api abb end
|
||||
CaptureEvent::APIEnd => {
|
||||
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap());
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}));
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
|
||||
},
|
||||
// generic isr abb end
|
||||
CaptureEvent::ISREnd => {
|
||||
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap());
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}));
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
|
||||
},
|
||||
// end anything
|
||||
CaptureEvent::End => {
|
||||
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap());
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}));
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
|
||||
},
|
||||
CaptureEvent::ISRStart => (),
|
||||
_ => panic!("Undefined block end")
|
||||
}
|
||||
}
|
||||
}
|
||||
// println!("{} {} {:x}-{:x} {:x}-{:X} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick);
|
||||
// println!("{} {} {:x}-{:x} {:x}-{:x} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick);
|
||||
}
|
||||
drop(open_abb_at_this_task_or_level);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user