diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index bbec365dbf..fc965d67dd 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -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::(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) } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 277adc5dc0..365adae756 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -4,17 +4,17 @@ use core::time::Duration; use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range, ptr::addr_of_mut, ffi::OsStr}; use hashbrown::HashMap; use libafl_bolts::{ - core_affinity::Cores, current_nanos, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsMutSlice, AsSlice +core_affinity::Cores, current_nanos, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsMutSlice, AsSlice }; use libafl::{ - corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::{ExitKind, TimeoutExecutor}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, observers::VariableMapObserver, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HasBytesVec, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, state::{HasCorpus, HasMetadata, HasNamedMetadata, StdState}, Error, Evaluator +corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::{ExitKind, TimeoutExecutor}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, observers::VariableMapObserver, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HasBytesVec, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, state::{HasCorpus, HasMetadata, HasNamedMetadata, StdState}, Error, Evaluator }; use libafl_qemu::{ - edges::{self, edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM}, elf::EasyElf, emu::{libafl_qemu_remove_native_breakpoint, libafl_qemu_set_native_breakpoint, Emulator}, GuestAddr, GuestPhysAddr, QemuExecutor, QemuHooks, QemuInstrumentationFilter, Regs +edges::{self, edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM}, elf::EasyElf, emu::{libafl_qemu_remove_native_breakpoint, libafl_qemu_set_native_breakpoint, Emulator}, GuestAddr, GuestPhysAddr, QemuExecutor, QemuHooks, QemuInstrumentationFilter, Regs }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} +clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; @@ -24,6 +24,7 @@ use petgraph::graph::EdgeIndex; use petgraph::graph::NodeIndex; use petgraph::prelude::DiGraph; use crate::systemstate::stg::STGFeedbackState; +use crate::clock::QEMU_ICOUNT_SHIFT; // Constants ================================================================================ @@ -32,92 +33,92 @@ pub static mut RNG_SEED: u64 = 1; pub static mut LIMIT : u32 = u32::MAX; pub const FIRST_INT : u32 = 500000; -pub const MAX_NUM_INTERRUPT: usize = 32; -pub const DO_NUM_INTERRUPT: usize = 32; +pub const MAX_NUM_INTERRUPT: usize = 128; +pub const DO_NUM_INTERRUPT: usize = 128; pub static mut MAX_INPUT_SIZE: usize = 32; /// Read ELF program headers to resolve physical load addresses. fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { - let ret; - for i in &tab.goblin().program_headers { - if i.vm_range().contains(&vaddr.try_into().unwrap()) { - ret = vaddr - TryInto::::try_into(i.p_vaddr).unwrap() - + TryInto::::try_into(i.p_paddr).unwrap(); - return ret - (ret % 2); - } +let ret; +for i in &tab.goblin().program_headers { + if i.vm_range().contains(&vaddr.try_into().unwrap()) { + ret = vaddr - TryInto::::try_into(i.p_vaddr).unwrap() + + TryInto::::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 { - let ret = elf - .resolve_symbol(symbol, 0); - if do_translation { - Option::map_or(ret, None, |x| Some(virt2phys(x as GuestPhysAddr,&elf) as GuestAddr)) - } else {ret} +let ret = elf + .resolve_symbol(symbol, 0); +if do_translation { + Option::map_or(ret, None, |x| Some(virt2phys(x as GuestPhysAddr,&elf) as GuestAddr)) +} else {ret} } pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option> { - let gob = elf.goblin(); +let gob = elf.goblin(); - let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect(); - funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); +let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect(); +funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); - for sym in &gob.syms { - if let Some(sym_name) = gob.strtab.get_at(sym.st_name) { - if sym_name == symbol { - if sym.st_value == 0 { - return None; - } else { - //#[cfg(cpu_target = "arm")] - // Required because of arm interworking addresses aka bit(0) for thumb mode - let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr); - //#[cfg(not(cpu_target = "arm"))] - //let addr = sym.st_value as GuestAddr; - // look for first function after addr - let sym_end = funcs.iter().find(|x| x.st_value > sym.st_value); - if let Some(sym_end) = sym_end { - // println!("{} {:#x}..{} {:#x}", gob.strtab.get_at(sym.st_name).unwrap_or(""),addr, gob.strtab.get_at(sym_end.st_name).unwrap_or(""),sym_end.st_value & !0x1); - return Some(addr..((sym_end.st_value & !0x1) as GuestAddr)); - } - return None; - }; - } +for sym in &gob.syms { + if let Some(sym_name) = gob.strtab.get_at(sym.st_name) { + if sym_name == symbol { + if sym.st_value == 0 { + return None; + } else { + //#[cfg(cpu_target = "arm")] + // Required because of arm interworking addresses aka bit(0) for thumb mode + let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr); + //#[cfg(not(cpu_target = "arm"))] + //let addr = sym.st_value as GuestAddr; + // look for first function after addr + let sym_end = funcs.iter().find(|x| x.st_value > sym.st_value); + if let Some(sym_end) = sym_end { + // println!("{} {:#x}..{} {:#x}", gob.strtab.get_at(sym.st_name).unwrap_or(""),addr, gob.strtab.get_at(sym_end.st_name).unwrap_or(""),sym_end.st_value & !0x1); + return Some(addr..((sym_end.st_value & !0x1) as GuestAddr)); + } + return None; + }; } } - return None; +} +return None; } pub fn get_all_fn_symbol_ranges(elf: &EasyElf, api_range: std::ops::Range) -> HashMap> { - let mut api_addreses : HashMap> = HashMap::new(); +let mut api_addreses : HashMap> = HashMap::new(); - let gob = elf.goblin(); +let gob = elf.goblin(); - let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function() && api_range.contains(&x.st_value.try_into().unwrap())).collect(); - funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); +let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function() && api_range.contains(&x.st_value.try_into().unwrap())).collect(); +funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); - for sym in &funcs { - let sym_name = gob.strtab.get_at(sym.st_name); - if let Some(sym_name) = sym_name { - // if ISR_SYMBOLS.contains(&sym_name) {continue;}; // skip select symbols, which correspond to ISR-safe system calls - if let Some(r) = get_function_range(elf, sym_name) { - api_addreses.insert(sym_name.to_string(), r); - } +for sym in &funcs { + let sym_name = gob.strtab.get_at(sym.st_name); + if let Some(sym_name) = sym_name { + // if ISR_SYMBOLS.contains(&sym_name) {continue;}; // skip select symbols, which correspond to ISR-safe system calls + if let Some(r) = get_function_range(elf, sym_name) { + api_addreses.insert(sym_name.to_string(), r); } } - for i in api_addreses.iter() { - println!("{} {:#x}..{:#x}", i.0, i.1.start, i.1.end); - } +} +for i in api_addreses.iter() { + println!("{} {:#x}..{:#x}", i.0, i.1.start, i.1.end); +} - return api_addreses; +return api_addreses; } extern "C" { - static mut libafl_interrupt_offsets : [u32; 32]; - static mut libafl_num_interrupts : usize; +static mut libafl_interrupt_offsets : [u32; MAX_NUM_INTERRUPT]; +static mut libafl_num_interrupts : usize; } // Argument parsing ================================================================================ @@ -125,329 +126,337 @@ extern "C" { #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Cli { - /// Kernel Image - #[arg(short, long, value_name = "FILE")] - kernel: PathBuf, +/// Kernel Image +#[arg(short, long, value_name = "FILE")] +kernel: PathBuf, - /// Sets a custom config file - #[arg(short, long, value_name = "FILE")] - config: PathBuf, +/// Sets a custom config file +#[arg(short, long, value_name = "FILE")] +config: PathBuf, - /// Sets the prefix of dumed files - #[arg(short='n', long, value_name = "FILENAME")] - dump_name: Option, +/// Sets the prefix of dumed files +#[arg(short='n', long, value_name = "FILENAME")] +dump_name: Option, - /// do time dumps - #[arg(short='t', long)] - dump_times: bool, +/// do time dumps +#[arg(short='t', long)] +dump_times: bool, - /// do worst-case dumps - #[arg(short='a', long)] - dump_cases: bool, +/// do worst-case dumps +#[arg(short='a', long)] +dump_cases: bool, - /// do trace dumps (if supported) - #[arg(short='r', long)] - dump_traces: bool, +/// do trace dumps (if supported) +#[arg(short='r', long)] +dump_traces: bool, - /// do graph dumps (if supported) - #[arg(short='g', long)] - dump_graph: bool, +/// do graph dumps (if supported) +#[arg(short='g', long)] +dump_graph: bool, - #[command(subcommand)] - command: Commands, +#[command(subcommand)] +command: Commands, } #[derive(Subcommand,Clone)] enum Commands { - /// run a single input - Showmap { - /// take this input - #[arg(short, long)] - input: PathBuf, - }, - /// start fuzzing campaign - Fuzz { - /// disable heuristic - #[arg(short, long)] - random: bool, - /// seed for randomness - #[arg(short, long)] - seed: Option, - /// runtime in seconds - #[arg(short, long)] - time: Option, - } +/// run a single input +Showmap { + /// take this input + #[arg(short, long)] + input: PathBuf, +}, +/// start fuzzing campaign +Fuzz { + /// disable heuristic + #[arg(short, long)] + random: bool, + /// seed for randomness + #[arg(short, long)] + seed: Option, + /// runtime in seconds + #[arg(short, long)] + time: Option, +} } /// Takes a state, cli and a suffix, writes out the current worst case macro_rules! do_dump_case { - ( $s:expr,$cli:expr, $c:expr) => { - if ($cli.dump_cases) { - let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"case"} else {$c}); - println!("Dumping worst case to {:?}", &dump_path); - let corpus = $s.corpus(); - let mut worst = Duration::new(0,0); - let mut worst_input = None; - for i in 0..corpus.count() { - let tc = corpus.get(corpus.nth(i.into())).expect("Could not get element from corpus").borrow(); - if worst < tc.exec_time().expect("Testcase missing duration") { - worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); - worst = tc.exec_time().expect("Testcase missing duration"); - } - } - if let Some(wi) = worst_input { - fs::write(dump_path,wi).expect("Failed to write worst corpus element"); +( $s:expr,$cli:expr, $c:expr) => { + if ($cli.dump_cases) { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"case"} else {$c}); + println!("Dumping worst case to {:?}", &dump_path); + let corpus = $s.corpus(); + let mut worst = Duration::new(0,0); + let mut worst_input = None; + for i in 0..corpus.count() { + let tc = corpus.get(corpus.nth(i.into())).expect("Could not get element from corpus").borrow(); + if worst < tc.exec_time().expect("Testcase missing duration") { + worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); + worst = tc.exec_time().expect("Testcase missing duration"); } } + if let Some(wi) = worst_input { + fs::write(dump_path,wi).expect("Failed to write worst corpus element"); + } } } +} /// Takes a state, cli and a suffix, appends icount history macro_rules! do_dump_times { - ($state:expr, $cli:expr, $c:expr) => { - if $cli.dump_times { - let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"time"} else {$c}); - let mut file = std::fs::OpenOptions::new() - .read(true) - .write(true) - .create(true) - .append(true) - .open(dump_path).expect("Could not open timedump"); - if let Ok(ichist) = $state.metadata_mut::() { - for i in ichist.0.drain(..) { - writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); - } +($state:expr, $cli:expr, $c:expr) => { + if $cli.dump_times { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"time"} else {$c}); + let mut file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .append(true) + .open(dump_path).expect("Could not open timedump"); + if let Ok(ichist) = $state.metadata_mut::() { + for i in ichist.0.drain(..) { + writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } } - }; + } +}; } /// Takes a state and a bool, writes out the current graph macro_rules! do_dump_stg { - ($state:expr, $cli:expr, $c:expr) => { - #[cfg(feature = "trace_stg")] - if $cli.dump_graph { - let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c}); - println!("Dumping graph to {:?}", &dump_path); - if let Some(md) = $state.named_metadata_map_mut().get_mut::("stgfeedbackstate") { - let out = md.graph.map(|_i,x| x.color_print(), |_i,x| x.color_print()); - let outs = Dot::with_config(&out, &[]).to_string(); - let outs = outs.replace("\\\"","\""); - let outs = outs.replace(';',"\\n"); - fs::write(dump_path,outs).expect("Failed to write graph"); - } +($state:expr, $cli:expr, $c:expr) => { + #[cfg(feature = "trace_stg")] + if $cli.dump_graph { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c}); + println!("Dumping graph to {:?}", &dump_path); + if let Some(md) = $state.named_metadata_map_mut().get_mut::("stgfeedbackstate") { + let out = md.graph.map(|_i,x| x.color_print(), |_i,x| x.color_print()); + let outs = Dot::with_config(&out, &[]).to_string(); + let outs = outs.replace("\\\"","\""); + let outs = outs.replace(';',"\\n"); + fs::write(dump_path,outs).expect("Failed to write graph"); } - }; + } +}; } /// Takes a state and a bool, writes out top rated inputs macro_rules! do_dump_toprated { - ($state:expr, $cli:expr, $c:expr) => { - if $cli.dump_cases { - { - let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"toprated"} else {$c}); - println!("Dumping toprated to {:?}", &dump_path); - if let Some(md) = $state.metadata_map_mut().get_mut::() { - let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); - uniq.sort(); - uniq.dedup(); - fs::write(dump_path,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); - } - } - } - }; -} - -fn env_from_config(kernel : &PathBuf, path : &PathBuf) { - let is_csv = path.as_path().extension().map_or(false, |x| x=="csv"); - if !is_csv { - let lines = std::fs::read_to_string(path).expect("Config file not found"); - let lines = lines.lines().filter( - |x| x.len()>0 - ); - for l in lines { - let pair = l.split_once('=').expect("Non VAR=VAL line in config"); - std::env::set_var(pair.0, pair.1); - } - } else { - let mut reader = csv::Reader::from_path(path).expect("CSV read from config failed"); - let p = kernel.as_path(); - let stem = p.file_stem().expect("Kernel filename error").to_str().unwrap(); - for r in reader.records() { - let rec = r.expect("CSV entry error"); - if stem == &rec[0] { - std::env::set_var("FUZZ_MAIN", &rec[1]); - std::env::set_var("FUZZ_INPUT", &rec[2]); - std::env::set_var("FUZZ_INPUT_LEN", &rec[3]); - std::env::set_var("BREAKPOINT", &rec[4]); - break; +($state:expr, $cli:expr, $c:expr) => { + if $cli.dump_cases { + { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"toprated"} else {$c}); + println!("Dumping toprated to {:?}", &dump_path); + if let Some(md) = $state.metadata_map_mut().get_mut::() { + let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); + uniq.sort(); + uniq.dedup(); + fs::write(dump_path,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); } } } +}; +} + +fn env_from_config(kernel : &PathBuf, path : &PathBuf) { +let is_csv = path.as_path().extension().map_or(false, |x| x=="csv"); +if !is_csv { + let lines = std::fs::read_to_string(path).expect("Config file not found"); + let lines = lines.lines().filter( + |x| x.len()>0 + ); + for l in lines { + let pair = l.split_once('=').expect("Non VAR=VAL line in config"); + std::env::set_var(pair.0, pair.1); + } +} else { + let mut reader = csv::Reader::from_path(path).expect("CSV read from config failed"); + let p = kernel.as_path(); + let stem = p.file_stem().expect("Kernel filename error").to_str().unwrap(); + for r in reader.records() { + let rec = r.expect("CSV entry error"); + if stem == &rec[0] { + std::env::set_var("FUZZ_MAIN", &rec[1]); + std::env::set_var("FUZZ_INPUT", &rec[2]); + std::env::set_var("FUZZ_INPUT_LEN", &rec[3]); + std::env::set_var("BREAKPOINT", &rec[4]); + break; + } + } +} } // Fuzzer setup ================================================================================ pub fn fuzz() { - let cli = Cli::parse(); - env_from_config(&cli.kernel, &cli.config); - unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} - if cli.dump_name.is_none() && (cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph) { - panic!("Dump name not give but dump is requested"); - } - let mut starttime = std::time::Instant::now(); - // Hardcoded parameters - let timeout = Duration::from_secs(10); - let broker_port = 1337; - let cores = Cores::from_cmdline("1").unwrap(); - let corpus_dirs = [PathBuf::from("./corpus")]; - let objective_dir = PathBuf::from("./crashes"); +let cli = Cli::parse(); +env_from_config(&cli.kernel, &cli.config); +unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} +if cli.dump_name.is_none() && (cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph) { + panic!("Dump name not give but dump is requested"); +} +let mut starttime = std::time::Instant::now(); +// Hardcoded parameters +let timeout = Duration::from_secs(10); +let broker_port = 1337; +let cores = Cores::from_cmdline("1").unwrap(); +let corpus_dirs = [PathBuf::from("./corpus")]; +let objective_dir = PathBuf::from("./crashes"); - let mut elf_buffer = Vec::new(); - let elf = EasyElf::from_file( - &cli.kernel, - &mut elf_buffer, +let mut elf_buffer = Vec::new(); +let elf = EasyElf::from_file( + &cli.kernel, + &mut elf_buffer, +) +.unwrap(); + +// the main address where the fuzzer starts +// if this is set for freeRTOS it has an influence on where the data will have to be written, +// since the startup routine copies the data segemnt to it's virtual address +let main_addr = elf + .resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_MAIN".to_owned()), 0); +if let Some(main_addr) = main_addr { + println!("main address = {:#x}", main_addr); +} + +let input_addr = load_symbol(&elf, &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), true); +println!("FUZZ_INPUT @ {:#x}", input_addr); + +let input_length_ptr = try_load_symbol(&elf, &env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()), true); +let input_counter_ptr = try_load_symbol(&elf, &env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), true); + +#[cfg(feature = "observe_systemstate")] +let curr_tcb_pointer = load_symbol(&elf, "pxCurrentTCB", false); // loads to the address specified in elf, without respecting program headers +#[cfg(feature = "observe_systemstate")] +println!("TCB pointer at {:#x}", curr_tcb_pointer); +#[cfg(feature = "observe_systemstate")] +let task_queue_addr = load_symbol(&elf, "pxReadyTasksLists", false); +#[cfg(feature = "observe_systemstate")] +let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false); +#[cfg(feature = "observe_systemstate")] +let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false); +#[cfg(feature = "observe_systemstate")] +let scheduler_lock = load_symbol(&elf, "uxSchedulerSuspended", false); +#[cfg(feature = "observe_systemstate")] +let scheduler_running = load_symbol(&elf, "xSchedulerRunning", false); +#[cfg(feature = "observe_systemstate")] +let critical_section = load_symbol(&elf, "uxCriticalNesting", false); +let app_start = load_symbol(&elf, "__APP_CODE_START__", false); +let app_end = load_symbol(&elf, "__APP_CODE_END__", false); +let app_range = app_start..app_end; +let api_start = load_symbol(&elf, "__API_CODE_START__", false); +let api_end = load_symbol(&elf, "__API_CODE_END__", false); +let api_range = api_start..api_end; + +let breakpoint = elf + .resolve_symbol( + &env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()), + 0, ) - .unwrap(); + .expect("Symbol or env BREAKPOINT not found"); +println!("Breakpoint address = {:#x}", breakpoint); +unsafe { + libafl_num_interrupts = 0; +} + +if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") { + unsafe {MAX_INPUT_SIZE = str::parse::(&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::(&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> = 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 = 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 = 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 = vec![ + "target/debug/fret", + "-icount", + &format!("shift={},align=off,sleep=off", QEMU_ICOUNT_SHIFT), + "-machine", + "mps2-an385", + "-cpu", + "cortex-m3", + "-monitor", + "null", + "-kernel", + &cli.kernel.as_os_str().to_str().expect("kernel path is not a string"), + "-serial", + "null", + "-nographic", + "-S", + // "-semihosting", + // "--semihosting-config", + // "enable=on,target=native", + "-snapshot", + "-drive", + "if=none,format=qcow2,file=dummy.qcow2", + ].into_iter().map(String::from).collect(); + let env: Vec<(String, String)> = env::vars().collect(); + let emu = Emulator::new(&args, &env).expect("Emulator creation failed"); - // the main address where the fuzzer starts - // if this is set for freeRTOS it has an influence on where the data will have to be written, - // since the startup routine copies the data segemnt to it's virtual address - let main_addr = elf - .resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_MAIN".to_owned()), 0); if let Some(main_addr) = main_addr { - println!("main address = {:#x}", main_addr); - } - - let input_addr = load_symbol(&elf, &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), true); - println!("FUZZ_INPUT @ {:#x}", input_addr); - - let input_length_ptr = try_load_symbol(&elf, &env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()), true); - let input_counter_ptr = try_load_symbol(&elf, &env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), true); - - #[cfg(feature = "observe_systemstate")] - let curr_tcb_pointer = load_symbol(&elf, "pxCurrentTCB", false); // loads to the address specified in elf, without respecting program headers - #[cfg(feature = "observe_systemstate")] - println!("TCB pointer at {:#x}", curr_tcb_pointer); - #[cfg(feature = "observe_systemstate")] - let task_queue_addr = load_symbol(&elf, "pxReadyTasksLists", false); - #[cfg(feature = "observe_systemstate")] - let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false); - #[cfg(feature = "observe_systemstate")] - let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false); - #[cfg(feature = "observe_systemstate")] - let scheduler_lock = load_symbol(&elf, "uxSchedulerSuspended", false); - #[cfg(feature = "observe_systemstate")] - let scheduler_running = load_symbol(&elf, "xSchedulerRunning", false); - #[cfg(feature = "observe_systemstate")] - let critical_section = load_symbol(&elf, "uxCriticalNesting", false); - let app_start = load_symbol(&elf, "__APP_CODE_START__", false); - let app_end = load_symbol(&elf, "__APP_CODE_END__", false); - let app_range = app_start..app_end; - let api_start = load_symbol(&elf, "__API_CODE_START__", false); - let api_end = load_symbol(&elf, "__API_CODE_END__", false); - let api_range = api_start..api_end; - - let breakpoint = elf - .resolve_symbol( - &env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()), - 0, - ) - .expect("Symbol or env BREAKPOINT not found"); - println!("Breakpoint address = {:#x}", breakpoint); - unsafe { - libafl_num_interrupts = 0; - } - - if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") { - unsafe {MAX_INPUT_SIZE = str::parse::(&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::(&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> = 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 = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect(); - - #[cfg(feature = "observe_systemstate")] - for i in systemstate::helpers::ISR_SYMBOLS { - if isr_ranges.get(&i.to_string()).is_none() { - if let Some(fr) = get_function_range(&elf, i) { - isr_addreses.insert(fr.start, i.to_string()); - isr_ranges.insert(i.to_string(), fr); - } + unsafe { + libafl_qemu_set_native_breakpoint(main_addr); + emu.run(); + libafl_qemu_remove_native_breakpoint(main_addr); } } - #[cfg(feature = "observe_systemstate")] - let api_addreses : HashMap = api_ranges.iter().map(|(k,v)| (v.start,k.clone())).collect(); + unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT - #[cfg(feature = "observe_systemstate")] - let api_ranges : Vec<_> = api_ranges.into_iter().collect(); - #[cfg(feature = "observe_systemstate")] - let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |input: &BytesInput| { + let target = input.target_bytes(); + let mut buf = target.as_slice(); + let mut len = buf.len(); + unsafe { + #[cfg(feature = "fuzz_int")] + { + let t = input_bytes_to_interrupt_times(buf); + for i in 0..t.len() {libafl_interrupt_offsets[i]=t[i];} + libafl_num_interrupts=t.len(); - // Client setup ================================================================================ - - let mut run_client = |state: Option<_>, mut mgr, _core_id| { - // Initialize QEMU - let args: Vec = vec![ - "target/debug/fret", - "-icount", - "shift=4,align=off,sleep=off", - "-machine", - "mps2-an385", - "-monitor", - "null", - "-kernel", - &cli.kernel.as_os_str().to_str().expect("kernel path is not a string"), - "-serial", - "null", - "-nographic", - "-S", - "-semihosting", - "--semihosting-config", - "enable=on,target=native", - "-snapshot", - "-drive", - "if=none,format=qcow2,file=dummy.qcow2", - ].into_iter().map(String::from).collect(); - let env: Vec<(String, String)> = env::vars().collect(); - let emu = Emulator::new(&args, &env).expect("Emulator creation failed"); - - if let Some(main_addr) = main_addr { - unsafe { - libafl_qemu_set_native_breakpoint(main_addr); - emu.run(); - libafl_qemu_remove_native_breakpoint(main_addr); - } - } - - unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT - - // The wrapped harness function, calling out to the LLVM-style harness - let mut harness = |input: &BytesInput| { - let target = input.target_bytes(); - let mut buf = target.as_slice(); - let mut len = buf.len(); - unsafe { - #[cfg(feature = "fuzz_int")] - { - let t = input_bytes_to_interrupt_times(buf); - for i in 0..t.len() {libafl_interrupt_offsets[i]=t[i];} - libafl_num_interrupts=t.len(); - - if buf.len() > libafl_num_interrupts*4 { - buf = &buf[libafl_num_interrupts*4..]; - len = buf.len(); - } - // println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec()); + if buf.len() > libafl_num_interrupts*4 { + buf = &buf[libafl_num_interrupts*4..]; + len = buf.len(); + } + // for i in 0 .. libafl_num_interrupts { + // libafl_interrupt_offsets[i] = FIRST_INT+TryInto::::try_into(i).unwrap()*MINIMUM_INTER_ARRIVAL_TIME; + // } + // println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec()); } if len > MAX_INPUT_SIZE { buf = &buf[0..MAX_INPUT_SIZE]; diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index f9d0b40937..d020ef918d 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -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 diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 7c7be729a9..fa418dc8ce 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -109,9 +109,9 @@ where where QT: QemuHelperTuple, { - for wp in self.api_fn_addrs.keys() { - _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::), false); - } + // for wp in self.api_fn_addrs.keys() { + // _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::), false); + // } for wp in self.isr_addrs.keys() { _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); } @@ -213,7 +213,11 @@ fn trigger_collection(emulator: &Emulator, edge: (Option,Option { edge.0=Some(s.0); trigger_collection(emulator, edge, h); }, None => (), } - *x.get()=None; }); } @@ -343,11 +346,13 @@ where QT: QemuHelperTuple, { if let Some(h) = hooks.helpers().match_first_type::() { - 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( hooks: &mut QemuHooks, _state: Option<&mut S>, @@ -373,11 +390,10 @@ where QT: QemuHelperTuple, { 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::().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::().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::().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)); } } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 4f04b63f97..6275462d65 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -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) -> (Vec) -> (Vec, meta: Vec<(u64, CaptureEvent, String, (Option, Option))>) -> (Vec, HashMap) { + if trace.len() == 0 {return (Vec::new(), HashMap::new());} let mut isr_stack : VecDeque = 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, 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, 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, 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, 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, table: &HashMap = 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, table: &HashMap { - // 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, table: &HashMap { 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);