timeshift variable, handle nested isr+api, bump max_interrupts

This commit is contained in:
Alwin Berger 2024-06-06 14:39:45 +02:00
parent c7bf1be8b1
commit b9e388d9d5
5 changed files with 420 additions and 379 deletions

View File

@ -40,6 +40,16 @@ use std::path::PathBuf;
pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH; 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 //========== Metadata
#[derive(Debug, SerdeAny, Serialize, Deserialize)] #[derive(Debug, SerdeAny, Serialize, Deserialize)]
pub struct QemuIcountMetadata { pub struct QemuIcountMetadata {
@ -222,7 +232,7 @@ where
{ {
// TODO Replace with match_name_type when stable // TODO Replace with match_name_type when stable
let observer = observers.match_name::<QemuClockObserver>(self.name()).unwrap(); 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) Ok(false)
} }

View File

@ -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 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, 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::{ 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::{ 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 rand::{SeedableRng, StdRng, Rng};
use crate::{ 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 std::time::{SystemTime, UNIX_EPOCH};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
@ -24,6 +24,7 @@ use petgraph::graph::EdgeIndex;
use petgraph::graph::NodeIndex; use petgraph::graph::NodeIndex;
use petgraph::prelude::DiGraph; use petgraph::prelude::DiGraph;
use crate::systemstate::stg::STGFeedbackState; use crate::systemstate::stg::STGFeedbackState;
use crate::clock::QEMU_ICOUNT_SHIFT;
// Constants ================================================================================ // Constants ================================================================================
@ -32,92 +33,92 @@ pub static mut RNG_SEED: u64 = 1;
pub static mut LIMIT : u32 = u32::MAX; pub static mut LIMIT : u32 = u32::MAX;
pub const FIRST_INT : u32 = 500000; pub const FIRST_INT : u32 = 500000;
pub const MAX_NUM_INTERRUPT: usize = 32; pub const MAX_NUM_INTERRUPT: usize = 128;
pub const DO_NUM_INTERRUPT: usize = 32; pub const DO_NUM_INTERRUPT: usize = 128;
pub static mut MAX_INPUT_SIZE: usize = 32; pub static mut MAX_INPUT_SIZE: usize = 32;
/// Read ELF program headers to resolve physical load addresses. /// Read ELF program headers to resolve physical load addresses.
fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
let ret; let ret;
for i in &tab.goblin().program_headers { for i in &tab.goblin().program_headers {
if i.vm_range().contains(&vaddr.try_into().unwrap()) { if i.vm_range().contains(&vaddr.try_into().unwrap()) {
ret = vaddr - TryInto::<GuestPhysAddr>::try_into(i.p_vaddr).unwrap() ret = vaddr - TryInto::<GuestPhysAddr>::try_into(i.p_vaddr).unwrap()
+ TryInto::<GuestPhysAddr>::try_into(i.p_paddr).unwrap(); + TryInto::<GuestPhysAddr>::try_into(i.p_paddr).unwrap();
return ret - (ret % 2); return ret - (ret % 2);
}
} }
return vaddr; }
return vaddr;
} }
pub fn load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> GuestAddr { 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> { pub fn try_load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> Option<GuestAddr> {
let ret = elf let ret = elf
.resolve_symbol(symbol, 0); .resolve_symbol(symbol, 0);
if do_translation { if do_translation {
Option::map_or(ret, None, |x| Some(virt2phys(x as GuestPhysAddr,&elf) as GuestAddr)) Option::map_or(ret, None, |x| Some(virt2phys(x as GuestPhysAddr,&elf) as GuestAddr))
} else {ret} } else {ret}
} }
pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option<std::ops::Range<GuestAddr>> { 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(); 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)); funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value));
for sym in &gob.syms { for sym in &gob.syms {
if let Some(sym_name) = gob.strtab.get_at(sym.st_name) { if let Some(sym_name) = gob.strtab.get_at(sym.st_name) {
if sym_name == symbol { if sym_name == symbol {
if sym.st_value == 0 { if sym.st_value == 0 {
return None; return None;
} else { } else {
//#[cfg(cpu_target = "arm")] //#[cfg(cpu_target = "arm")]
// Required because of arm interworking addresses aka bit(0) for thumb mode // Required because of arm interworking addresses aka bit(0) for thumb mode
let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr); let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr);
//#[cfg(not(cpu_target = "arm"))] //#[cfg(not(cpu_target = "arm"))]
//let addr = sym.st_value as GuestAddr; //let addr = sym.st_value as GuestAddr;
// look for first function after addr // look for first function after addr
let sym_end = funcs.iter().find(|x| x.st_value > sym.st_value); let sym_end = funcs.iter().find(|x| x.st_value > sym.st_value);
if let Some(sym_end) = sym_end { 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); // 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 Some(addr..((sym_end.st_value & !0x1) as GuestAddr));
} }
return None; 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>> { 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(); 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)); funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value));
for sym in &funcs { for sym in &funcs {
let sym_name = gob.strtab.get_at(sym.st_name); let sym_name = gob.strtab.get_at(sym.st_name);
if let Some(sym_name) = sym_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 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) { if let Some(r) = get_function_range(elf, sym_name) {
api_addreses.insert(sym_name.to_string(), r); 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" { extern "C" {
static mut libafl_interrupt_offsets : [u32; 32]; static mut libafl_interrupt_offsets : [u32; MAX_NUM_INTERRUPT];
static mut libafl_num_interrupts : usize; static mut libafl_num_interrupts : usize;
} }
// Argument parsing ================================================================================ // Argument parsing ================================================================================
@ -125,329 +126,337 @@ extern "C" {
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Cli { struct Cli {
/// Kernel Image /// Kernel Image
#[arg(short, long, value_name = "FILE")] #[arg(short, long, value_name = "FILE")]
kernel: PathBuf, kernel: PathBuf,
/// Sets a custom config file /// Sets a custom config file
#[arg(short, long, value_name = "FILE")] #[arg(short, long, value_name = "FILE")]
config: PathBuf, config: PathBuf,
/// Sets the prefix of dumed files /// Sets the prefix of dumed files
#[arg(short='n', long, value_name = "FILENAME")] #[arg(short='n', long, value_name = "FILENAME")]
dump_name: Option<PathBuf>, dump_name: Option<PathBuf>,
/// do time dumps /// do time dumps
#[arg(short='t', long)] #[arg(short='t', long)]
dump_times: bool, dump_times: bool,
/// do worst-case dumps /// do worst-case dumps
#[arg(short='a', long)] #[arg(short='a', long)]
dump_cases: bool, dump_cases: bool,
/// do trace dumps (if supported) /// do trace dumps (if supported)
#[arg(short='r', long)] #[arg(short='r', long)]
dump_traces: bool, dump_traces: bool,
/// do graph dumps (if supported) /// do graph dumps (if supported)
#[arg(short='g', long)] #[arg(short='g', long)]
dump_graph: bool, dump_graph: bool,
#[command(subcommand)] #[command(subcommand)]
command: Commands, command: Commands,
} }
#[derive(Subcommand,Clone)] #[derive(Subcommand,Clone)]
enum Commands { enum Commands {
/// run a single input /// run a single input
Showmap { Showmap {
/// take this input /// take this input
#[arg(short, long)] #[arg(short, long)]
input: PathBuf, input: PathBuf,
}, },
/// start fuzzing campaign /// start fuzzing campaign
Fuzz { Fuzz {
/// disable heuristic /// disable heuristic
#[arg(short, long)] #[arg(short, long)]
random: bool, random: bool,
/// seed for randomness /// seed for randomness
#[arg(short, long)] #[arg(short, long)]
seed: Option<u64>, seed: Option<u64>,
/// runtime in seconds /// runtime in seconds
#[arg(short, long)] #[arg(short, long)]
time: Option<u64>, time: Option<u64>,
} }
} }
/// Takes a state, cli and a suffix, writes out the current worst case /// Takes a state, cli and a suffix, writes out the current worst case
macro_rules! do_dump_case { macro_rules! do_dump_case {
( $s:expr,$cli:expr, $c:expr) => { ( $s:expr,$cli:expr, $c:expr) => {
if ($cli.dump_cases) { if ($cli.dump_cases) {
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"case"} else {$c}); let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"case"} else {$c});
println!("Dumping worst case to {:?}", &dump_path); println!("Dumping worst case to {:?}", &dump_path);
let corpus = $s.corpus(); let corpus = $s.corpus();
let mut worst = Duration::new(0,0); let mut worst = Duration::new(0,0);
let mut worst_input = None; let mut worst_input = None;
for i in 0..corpus.count() { for i in 0..corpus.count() {
let tc = corpus.get(corpus.nth(i.into())).expect("Could not get element from corpus").borrow(); 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") { if worst < tc.exec_time().expect("Testcase missing duration") {
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
worst = tc.exec_time().expect("Testcase missing duration"); 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");
} }
} }
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 /// Takes a state, cli and a suffix, appends icount history
macro_rules! do_dump_times { macro_rules! do_dump_times {
($state:expr, $cli:expr, $c:expr) => { ($state:expr, $cli:expr, $c:expr) => {
if $cli.dump_times { if $cli.dump_times {
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"time"} else {$c}); let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"time"} else {$c});
let mut file = std::fs::OpenOptions::new() let mut file = std::fs::OpenOptions::new()
.read(true) .read(true)
.write(true) .write(true)
.create(true) .create(true)
.append(true) .append(true)
.open(dump_path).expect("Could not open timedump"); .open(dump_path).expect("Could not open timedump");
if let Ok(ichist) = $state.metadata_mut::<IcHist>() { if let Ok(ichist) = $state.metadata_mut::<IcHist>() {
for i in ichist.0.drain(..) { for i in ichist.0.drain(..) {
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
}
} }
} }
}; }
};
} }
/// Takes a state and a bool, writes out the current graph /// Takes a state and a bool, writes out the current graph
macro_rules! do_dump_stg { macro_rules! do_dump_stg {
($state:expr, $cli:expr, $c:expr) => { ($state:expr, $cli:expr, $c:expr) => {
#[cfg(feature = "trace_stg")] #[cfg(feature = "trace_stg")]
if $cli.dump_graph { if $cli.dump_graph {
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c}); let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c});
println!("Dumping graph to {:?}", &dump_path); println!("Dumping graph to {:?}", &dump_path);
if let Some(md) = $state.named_metadata_map_mut().get_mut::<STGFeedbackState>("stgfeedbackstate") { 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 out = md.graph.map(|_i,x| x.color_print(), |_i,x| x.color_print());
let outs = Dot::with_config(&out, &[]).to_string(); let outs = Dot::with_config(&out, &[]).to_string();
let outs = outs.replace("\\\"","\""); let outs = outs.replace("\\\"","\"");
let outs = outs.replace(';',"\\n"); let outs = outs.replace(';',"\\n");
fs::write(dump_path,outs).expect("Failed to write graph"); fs::write(dump_path,outs).expect("Failed to write graph");
}
} }
}; }
};
} }
/// Takes a state and a bool, writes out top rated inputs /// Takes a state and a bool, writes out top rated inputs
macro_rules! do_dump_toprated { macro_rules! do_dump_toprated {
($state:expr, $cli:expr, $c:expr) => { ($state:expr, $cli:expr, $c:expr) => {
if $cli.dump_cases { if $cli.dump_cases {
{ {
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"toprated"} else {$c}); let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"toprated"} else {$c});
println!("Dumping toprated to {:?}", &dump_path); println!("Dumping toprated to {:?}", &dump_path);
if let Some(md) = $state.metadata_map_mut().get_mut::<TopRatedsMetadata>() { if let Some(md) = $state.metadata_map_mut().get_mut::<TopRatedsMetadata>() {
let mut uniq: Vec<CorpusId> = md.map.values().map(|x| x.clone()).collect(); let mut uniq: Vec<CorpusId> = md.map.values().map(|x| x.clone()).collect();
uniq.sort(); uniq.sort();
uniq.dedup(); uniq.dedup();
fs::write(dump_path,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); 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;
} }
} }
} }
};
}
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 ================================================================================ // Fuzzer setup ================================================================================
pub fn fuzz() { pub fn fuzz() {
let cli = Cli::parse(); let cli = Cli::parse();
env_from_config(&cli.kernel, &cli.config); env_from_config(&cli.kernel, &cli.config);
unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();}
if cli.dump_name.is_none() && (cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph) { 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"); panic!("Dump name not give but dump is requested");
} }
let mut starttime = std::time::Instant::now(); let mut starttime = std::time::Instant::now();
// Hardcoded parameters // Hardcoded parameters
let timeout = Duration::from_secs(10); let timeout = Duration::from_secs(10);
let broker_port = 1337; let broker_port = 1337;
let cores = Cores::from_cmdline("1").unwrap(); let cores = Cores::from_cmdline("1").unwrap();
let corpus_dirs = [PathBuf::from("./corpus")]; let corpus_dirs = [PathBuf::from("./corpus")];
let objective_dir = PathBuf::from("./crashes"); let objective_dir = PathBuf::from("./crashes");
let mut elf_buffer = Vec::new(); let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file( let elf = EasyElf::from_file(
&cli.kernel, &cli.kernel,
&mut elf_buffer, &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 { if let Some(main_addr) = main_addr {
println!("main address = {:#x}", main_addr); unsafe {
} libafl_qemu_set_native_breakpoint(main_addr);
emu.run();
let input_addr = load_symbol(&elf, &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), true); libafl_qemu_remove_native_breakpoint(main_addr);
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);
}
} }
} }
#[cfg(feature = "observe_systemstate")] unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT
let api_addreses : HashMap<GuestAddr, String> = api_ranges.iter().map(|(k,v)| (v.start,k.clone())).collect();
#[cfg(feature = "observe_systemstate")] // The wrapped harness function, calling out to the LLVM-style harness
let api_ranges : Vec<_> = api_ranges.into_iter().collect(); let mut harness = |input: &BytesInput| {
#[cfg(feature = "observe_systemstate")] let target = input.target_bytes();
let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); 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 ================================================================================ if buf.len() > libafl_num_interrupts*4 {
buf = &buf[libafl_num_interrupts*4..];
let mut run_client = |state: Option<_>, mut mgr, _core_id| { len = buf.len();
// Initialize QEMU }
let args: Vec<String> = vec![ // for i in 0 .. libafl_num_interrupts {
"target/debug/fret", // libafl_interrupt_offsets[i] = FIRST_INT+TryInto::<u32>::try_into(i).unwrap()*MINIMUM_INTER_ARRIVAL_TIME;
"-icount", // }
"shift=4,align=off,sleep=off", // println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec());
"-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 len > MAX_INPUT_SIZE { if len > MAX_INPUT_SIZE {
buf = &buf[0..MAX_INPUT_SIZE]; buf = &buf[0..MAX_INPUT_SIZE];

View File

@ -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 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 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 // one isn per 2**4 ns
// virtual insn/sec 62500000 = 1/16 GHz // virtual insn/sec 62500000 = 1/16 GHz
// 1ms = 62500 insn // 1ms = 62500 insn

View File

@ -109,9 +109,9 @@ where
where where
QT: QemuHelperTuple<S>, QT: QemuHelperTuple<S>,
{ {
for wp in self.api_fn_addrs.keys() { // for wp in self.api_fn_addrs.keys() {
_hooks.instruction(*wp, Hook::Function(exec_syscall_hook::<QT, S>), false); // _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::<QT, S>), false);
} // }
for wp in self.isr_addrs.keys() { for wp in self.isr_addrs.keys() {
_hooks.instruction(*wp, Hook::Function(exec_isr_hook::<QT, S>), false); _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); println!("API Not found: {:#x} {:#x}", src, dest);
} }
} else if let Some(s) = h.api_fn_addrs.get(&dest) { // API Call } 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 { } else {
println!("API Not found: {:#x}", src); println!("API Not found: {:#x}", src);
} }
@ -315,14 +319,13 @@ where
let mut edge = (None, Some(pc)); let mut edge = (None, Some(pc));
unsafe { unsafe {
LAST_API_CALL.with(|x| { LAST_API_CALL.with(|x| {
match *x.get() { match (*x.get()).take() {
Some(s) => { Some(s) => {
edge.0=Some(s.0); edge.0=Some(s.0);
trigger_collection(emulator, edge, h); trigger_collection(emulator, edge, h);
}, },
None => (), None => (),
} }
*x.get()=None;
}); });
} }
@ -343,11 +346,13 @@ where
QT: QemuHelperTuple<S>, QT: QemuHelperTuple<S>,
{ {
if let Some(h) = hooks.helpers().match_first_type::<QemuSystemStateHelper>() { 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) { if let Some(_) = in_any_range(&h.api_fn_ranges,dest) {
// println!("New jmp {:x} {:x}", src, dest); // println!("New jmp {:x} {:x}", src, dest);
// println!("API Call Edge {:x} {:x}", src, dest); // println!("API Call Edge {:x} {:x}", src, dest);
return Some(1); 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) && } else if dest == 0 { // !h.app_range.contains(&src) &&
if let Some(_) = in_any_range(&h.api_fn_ranges, src) { if let Some(_) = in_any_range(&h.api_fn_ranges, src) {
@ -363,6 +368,18 @@ where
return None; 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>( pub fn trace_api_call<QT, S>(
hooks: &mut QemuHooks<QT, S>, hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>, _state: Option<&mut S>,
@ -373,11 +390,10 @@ where
QT: QemuHelperTuple<S>, QT: QemuHelperTuple<S>,
{ {
if id == 1 { // API call if id == 1 { // API call
unsafe { let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
let p = LAST_API_CALL.with(|x| x.get()); let emulator = hooks.emulator();
*p = Some((src,dest)); trigger_collection(emulator, (Some(src), Some(dest)), h);
// println!("Jump {:#x} {:#x}", src, dest); // println!("Exec API Call {:#x} {:#x} {}", src, dest, get_icount(emulator));
}
} else if id == 2 { // API return } else if id == 2 { // API return
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel"); 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. // 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); edge.1=Some(dest);
trigger_collection(emulator, edge, h); 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 } else if id == 3 { // ISR return
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel"); 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); edge.0=Some(in_any_range(&h.isr_ranges, src).unwrap().start);
trigger_collection(emulator, edge, h); 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));
} }
} }

View File

@ -53,7 +53,7 @@ where
// unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} // unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));}
unsafe { unsafe {
let mut temp = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC); 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(); self.last_run = temp.0.clone();
// println!("{:?}",temp); // println!("{:?}",temp);
let mut temp = states2intervals(temp.0, temp.1); 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, delay_list_after: delay_list,
// input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, // 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)); ret.1.push((i.qemu_tick, i.capture_point.0, i.capture_point.1.to_string(), i.edge));
} }
return ret; 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 /// 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>) { 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 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.insert(curr_name, 0);
} }
*level_of_task.get_mut(curr_name).unwrap()=0; *level_of_task.get_mut(curr_name).unwrap()=0;
&0 0
}, },
CaptureEvent::APIStart => { // API start can only be called in the app CaptureEvent::APIStart => { // API start can only be called in the app
*level_of_task.get_mut(curr_name).unwrap()=1; *level_of_task.get_mut(curr_name).unwrap()=1;
&1 1
}, },
CaptureEvent::ISREnd => { CaptureEvent::ISREnd => {
// special case where the next block is an app start // 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 // nested isr, TODO: Test level > 2
if isr_stack.len() > 1 { if isr_stack.len() > 1 {
isr_stack.pop_back(); isr_stack.pop_back().unwrap();
isr_stack.back().unwrap() *isr_stack.back().unwrap()
} else { } else {
isr_stack.pop_back(); isr_stack.pop_back();
// possibly go back to an api call that is still running for this task // 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 => { CaptureEvent::ISRStart => {
@ -207,16 +209,16 @@ fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, Capt
// } else { // } else {
// regular case // regular case
if isr_stack.len() > 0 { if isr_stack.len() > 0 {
let l = isr_stack.back().unwrap(); let l = *isr_stack.back().unwrap();
isr_stack.push_back(*l); isr_stack.push_back(l+1);
isr_stack.back().unwrap() l+1
} else { } else {
isr_stack.push_back(2); isr_stack.push_back(2);
&2 2
} }
// } // }
} }
_ => &100 _ => 100
}; };
// if trace[i].2 == CaptureEvent::End {break;} // if trace[i].2 == CaptureEvent::End {break;}
let next_hash=trace[i+1].get_hash(); 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, end_state: next_hash,
start_capture: (meta[i].1, meta[i].2.clone()), start_capture: (meta[i].1, meta[i].2.clone()),
end_capture: (meta[i+1].1, meta[i+1].2.clone()), end_capture: (meta[i+1].1, meta[i+1].2.clone()),
level: *level, level: level,
tick_spend_preempted: 0, tick_spend_preempted: 0,
abb: None 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 curr_name = &table[&trace[i].start_state].current_task.task_name;
// let last : Option<&usize> = last_abb_start_of_task.get(&curr_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) { match (&trace[i].start_capture.0, &trace[i].end_capture.0) {
// case with abb to block correspondence // 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 { match trace[i].start_capture.0 {
// generic api abb start // generic api abb start
CaptureEvent::APIStart => { 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); 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}}))); 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 // generic isr abb start
CaptureEvent::ISRStart => { CaptureEvent::ISRStart => {
// 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 {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);
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}}))); 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 // generic app abb start
CaptureEvent::APIEnd => { CaptureEvent::APIEnd => {
// 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 {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);
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}}))); 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 // generic continued blocks
CaptureEvent::ISREnd => { CaptureEvent::ISREnd => {
// special case app abb start // special case app abb start
if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) { 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}}))); 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()); task_has_started.insert(curr_name.clone());
} else { } 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()); wip_abb_trace.push(wip_abb_trace[*last].clone());
} else { } 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}}))) 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 => { CaptureEvent::APIStart => {
let _t = &wip_abb_trace[i]; let _t = &wip_abb_trace[i];
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); 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 // generic api abb end
CaptureEvent::APIEnd => { CaptureEvent::APIEnd => {
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); 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 // generic isr abb end
CaptureEvent::ISREnd => { CaptureEvent::ISREnd => {
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); 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 // end anything
CaptureEvent::End => { CaptureEvent::End => {
RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); 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 => (), CaptureEvent::ISRStart => (),
_ => panic!("Undefined block end") _ => 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); drop(open_abb_at_this_task_or_level);