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 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)
}

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 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,41 +33,41 @@ 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 {
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
let ret = elf
.resolve_symbol(symbol, 0);
if do_translation {
if do_translation {
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>> {
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 {
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 {
@ -87,19 +88,19 @@ pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option<std::ops::Range
};
}
}
}
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 {
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
@ -107,17 +108,17 @@ pub fn get_all_fn_symbol_ranges(elf: &EasyElf, api_range: std::ops::Range<GuestA
api_addreses.insert(sym_name.to_string(), r);
}
}
}
for i in api_addreses.iter() {
}
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,47 +126,47 @@ 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 {
/// run a single input
Showmap {
/// take this input
#[arg(short, long)]
input: PathBuf,
},
/// start fuzzing campaign
Fuzz {
},
/// start fuzzing campaign
Fuzz {
/// disable heuristic
#[arg(short, long)]
random: bool,
@ -175,12 +176,12 @@ enum Commands {
/// 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) => {
( $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);
@ -198,12 +199,12 @@ macro_rules! do_dump_case {
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) => {
($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()
@ -218,12 +219,12 @@ macro_rules! do_dump_times {
}
}
}
};
};
}
/// Takes a state and a bool, writes out the current graph
macro_rules! do_dump_stg {
($state:expr, $cli:expr, $c:expr) => {
($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});
@ -236,12 +237,12 @@ macro_rules! do_dump_stg {
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) => {
($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});
@ -254,12 +255,12 @@ macro_rules! do_dump_toprated {
}
}
}
};
};
}
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 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
@ -268,7 +269,7 @@ fn env_from_config(kernel : &PathBuf, path : &PathBuf) {
let pair = l.split_once('=').expect("Non VAR=VAL line in config");
std::env::set_var(pair.0, pair.1);
}
} else {
} 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();
@ -282,127 +283,132 @@ fn env_from_config(kernel : &PathBuf, path : &PathBuf) {
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) {
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 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(
let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(
&cli.kernel,
&mut elf_buffer,
)
.unwrap();
)
.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
// 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);
}
}
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_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);
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;
#[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
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 {
println!("Breakpoint address = {:#x}", breakpoint);
unsafe {
libafl_num_interrupts = 0;
}
}
if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") {
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);}
}
unsafe {dbg!(MAX_INPUT_SIZE);}
if let Ok(seed) = env::var("SEED_RANDOM") {
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 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();
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();
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 {
#[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_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();
#[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 ================================================================================
// Client setup ================================================================================
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
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",
&format!("shift={},align=off,sleep=off", QEMU_ICOUNT_SHIFT),
"-machine",
"mps2-an385",
"-cpu",
"cortex-m3",
"-monitor",
"null",
"-kernel",
@ -411,9 +417,9 @@ pub fn fuzz() {
"null",
"-nographic",
"-S",
"-semihosting",
"--semihosting-config",
"enable=on,target=native",
// "-semihosting",
// "--semihosting-config",
// "enable=on,target=native",
"-snapshot",
"-drive",
"if=none,format=qcow2,file=dummy.qcow2",
@ -447,6 +453,9 @@ pub fn fuzz() {
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 {

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
};
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

View File

@ -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
// 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));
}
}

View File

@ -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);