diff --git a/fuzzers/FRET/src/cli.rs b/fuzzers/FRET/src/cli.rs index de4ee3dd11..bcb659a496 100644 --- a/fuzzers/FRET/src/cli.rs +++ b/fuzzers/FRET/src/cli.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; // Argument parsing ================================================================================ -#[derive(Parser)] +#[derive(Parser,Debug)] #[command(author, version, about, long_about = None)] pub struct Cli { /// Kernel Image @@ -41,7 +41,7 @@ pub struct Cli { #[command(subcommand)] pub command: Commands, } -#[derive(Subcommand,Clone)] +#[derive(Subcommand,Clone,Debug)] pub enum Commands { /// run a single input Showmap { diff --git a/fuzzers/FRET/src/config.rs b/fuzzers/FRET/src/config.rs new file mode 100644 index 0000000000..34ee6935a0 --- /dev/null +++ b/fuzzers/FRET/src/config.rs @@ -0,0 +1,97 @@ +use hashbrown::HashMap; +use libafl_qemu::{elf::EasyElf, GuestAddr}; +use std::env; + +use crate::systemstate::helpers::{load_symbol, try_load_symbol}; + +pub fn get_target_symbols(elf: &EasyElf) -> HashMap<&'static str, GuestAddr> { + let mut addrs = HashMap::new(); + + addrs.insert( + "__APP_CODE_START__", + load_symbol(&elf, "__APP_CODE_START__", false), + ); + addrs.insert( + "__APP_CODE_END__", + load_symbol(&elf, "__APP_CODE_END__", false), + ); + addrs.insert( + "__API_CODE_START__", + load_symbol(&elf, "__API_CODE_START__", false), + ); + addrs.insert( + "__API_CODE_END__", + load_symbol(&elf, "__API_CODE_END__", false), + ); + addrs.insert( + "trigger_job_done", + load_symbol(&elf, "trigger_job_done", false), + ); + + crate::systemstate::target_os::freertos::config::add_target_symbols(elf, &mut addrs); + + // 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 { + addrs.insert("FUZZ_MAIN", main_addr); + } + + let input_addr = load_symbol( + &elf, + &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), + true, + ); + addrs.insert("FUZZ_INPUT", input_addr); + + let input_length_ptr = try_load_symbol( + &elf, + &env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()), + true, + ); + if let Some(input_length_ptr) = input_length_ptr { + addrs.insert("FUZZ_LENGTH", input_length_ptr); + } + let input_counter_ptr = try_load_symbol( + &elf, + &env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), + true, + ); + if let Some(input_counter_ptr) = input_counter_ptr { + addrs.insert("FUZZ_POINTER", input_counter_ptr); + } + addrs.insert( + "BREAKPOINT", + elf.resolve_symbol( + &env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()), + 0, + ) + .expect("Symbol or env BREAKPOINT not found"), + ); + + addrs +} + +pub fn get_target_ranges( + elf: &EasyElf, + symbols: &HashMap<&'static str, GuestAddr>, +) -> HashMap<&'static str, std::ops::Range> { + let mut ranges = HashMap::new(); + + ranges.insert( + "APP_CODE", + symbols["__APP_CODE_START__"]..symbols["__APP_CODE_END__"], + ); + ranges.insert( + "API_CODE", + symbols["__API_CODE_START__"]..symbols["__API_CODE_END__"], + ); + + crate::systemstate::target_os::freertos::config::add_target_ranges(elf, symbols, &mut ranges); + + ranges +} diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 37cdcb41c0..ae4f8d4b17 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -1,7 +1,7 @@ //! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! 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, ptr::addr_of_mut, ffi::OsStr}; use hashbrown::HashMap; use libafl_bolts::{ core_affinity::Cores, ownedref::OwnedMutSlice, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice, SimpleStderrLogger @@ -14,7 +14,7 @@ elf::EasyElf, emu::Emulator, modules::{edges::{self}, FilterList}, GuestAddr, Gu }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{ + config::{get_target_ranges, get_target_symbols}, systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{config::get_range_groups, qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{ clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, RateLimitedMonitor, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} } }; @@ -30,6 +30,7 @@ use clap::Parser; use log; use rand::RngCore; use crate::templates; +use std::ops::Range; // Constants ================================================================================ @@ -40,30 +41,27 @@ pub const FIRST_INT : u32 = 200000; pub const MAX_NUM_INTERRUPT: usize = 128; pub const NUM_INTERRUPT_SOURCES: usize = 6; // Keep in sync with qemu-libafl-bridge/hw/timer/armv7m_systick.c:319 and FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c:216 pub const DO_NUM_INTERRUPT: usize = 128; -pub static mut MAX_INPUT_SIZE: usize = 32; +pub static mut MAX_INPUT_SIZE: usize = 1024; -pub fn get_all_fn_symbol_ranges(elf: &EasyElf, api_range: std::ops::Range) -> HashMap> { -let mut api_addreses : HashMap> = HashMap::new(); +pub fn get_all_fn_symbol_ranges(elf: &EasyElf, range: std::ops::Range) -> HashMap> { + let mut ret : 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() && 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) { + ret.insert(sym_name.to_string(), r); + } } } -} -for i in api_addreses.iter() { - println!("{} {:#x}..{:#x}", i.0, i.1.start, i.1.end); -} -return api_addreses; + return ret; } #[allow(unused)] @@ -160,6 +158,7 @@ pub fn fuzz() { log::set_max_level(log::LevelFilter::Info); SimpleStderrLogger::set_logger().unwrap(); let cli = Cli::parse(); +dbg!(&cli); set_env_from_config(&cli.kernel, &cli.config); let interrupt_config = crate::cli::get_interrupt_config(&cli.kernel, &cli.config); unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} @@ -181,52 +180,10 @@ let elf = EasyElf::from_file( ) .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 TARGET_SYMBOLS: HashMap<&'static str, GuestAddr> = get_target_symbols(&elf); +let TARGET_RANGES: HashMap<&'static str, Range> = get_target_ranges(&elf, &TARGET_SYMBOLS); +let TARGET_GROUPS: HashMap<&'static str, HashMap>> = get_range_groups(&elf, &TARGET_SYMBOLS, &TARGET_RANGES); -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 job_done_addr = load_symbol(&elf, "trigger_job_done", false); - -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; NUM_INTERRUPT_SOURCES]; } @@ -240,37 +197,9 @@ if let Ok(seed) = env::var("SEED_RANDOM") { unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} } -println!("API functions:"); -let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); -println!("APP functions:"); -let app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone()); -let mut isr_ranges : HashMap> = systemstate::target_os::freertos::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); -systemstate::target_os::freertos::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 : Vec<_> =isr_ranges.values().map(|x| x.clone()).collect(); +let denylist: Vec<_> = TARGET_GROUPS["ISR_FN"].values().map(|x| x.clone()).collect(); let denylist = FilterList::DenyList(denylist); // do not count isr jumps, which are useless -#[cfg(feature = "observe_systemstate")] -let mut isr_addreses : HashMap = systemstate::target_os::freertos::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect(); -#[cfg(feature = "observe_systemstate")] -systemstate::target_os::freertos::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::target_os::freertos::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(); /// Setup the interrupt inputs. Noop if interrupts are not fuzzed fn setup_interrupt_inputs(mut input : MultipartInput, interrupt_config : &Vec<(usize,u32)>, mut random: Option<&mut StdRng>) -> MultipartInput { @@ -321,7 +250,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let env: Vec<(String, String)> = env::vars().collect(); let qemu = Qemu::init(&args).expect("Emulator creation failed"); - if let Some(main_addr) = main_addr { + if let Some(&main_addr) = TARGET_SYMBOLS.get("FUZZ_MAIN") { qemu.set_breakpoint(main_addr); unsafe { match qemu.run() { @@ -332,7 +261,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { qemu.remove_breakpoint(main_addr); } - qemu.set_breakpoint(breakpoint); // BREAKPOINT + qemu.set_breakpoint(TARGET_SYMBOLS["BREAKPOINT"]); // BREAKPOINT let devices = qemu.list_devices(); println!("Devices = {devices:?}"); @@ -342,6 +271,10 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(not(feature = "snapshot_fast"))] let initial_snap = None; + let harness_input_addr = TARGET_SYMBOLS["FUZZ_INPUT"]; + let harness_input_length_ptr = TARGET_SYMBOLS.get("FUZZ_LENGTH").copied(); + let harness_breakpoint = TARGET_SYMBOLS["BREAKPOINT"]; + // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, state: &mut _, input: &MultipartInput| { unsafe { @@ -367,8 +300,8 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { } // Note: I could not find a difference between write_mem and write_phys_mem for my usecase - qemu.write_mem(input_addr, bytes); - if let Some(s) = input_length_ptr { + qemu.write_mem(harness_input_addr, bytes); + if let Some(s) = harness_input_length_ptr { qemu.write_mem(s, &len.to_le_bytes()); } @@ -379,7 +312,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { .map(|i| qemu.cpu_from_index(i)) .map(|cpu| -> Result { cpu.read_reg(Regs::Pc) }); match pcs - .find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0))) + .find(|pc| (harness_breakpoint..harness_breakpoint + 5).contains(pc.as_ref().unwrap_or(&0))) { Some(_) => ExitKind::Ok, Option::None => ExitKind::Crash, @@ -491,7 +424,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let qhelpers = tuple_list!(); #[cfg(feature = "observe_systemstate")] - let qhelpers = (FreeRTOSSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock,scheduler_running, critical_section,input_counter_ptr,app_range.clone(), job_done_addr), qhelpers); + let qhelpers = (FreeRTOSSystemStateHelper::new(&TARGET_SYMBOLS,&TARGET_RANGES,&TARGET_GROUPS), qhelpers); #[cfg(feature = "observe_edges")] let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers); let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers); @@ -526,7 +459,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let stages = (systemstate::report::SchedulerStatsStage::default(),()); let stages = (StdMutationalStage::new(mutator), stages); #[cfg(feature = "mutate_stg")] - let mut stages = (STGSnippetStage::<_,_,_,FreeRTOSSystem>::new(input_addr), stages); + let mut stages = (STGSnippetStage::<_,_,_,FreeRTOSSystem>::new(TARGET_SYMBOLS["FUZZ_INPUT"]), stages); #[cfg(feature = "fuzz_int")] let mut stages = (InterruptShiftStage::<_,_,_,FreeRTOSSystem>::new(&interrupt_config), stages); @@ -662,14 +595,14 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let env: Vec<(String, String)> = env::vars().collect(); let emu = Qemu::init(&args).expect("Emu creation failed"); - if let Some(main_addr) = main_addr { + if let Some(&main_addr) = TARGET_SYMBOLS.get("FUZZ_MAIN") { emu.set_breakpoint(main_addr); // BREAKPOINT } unsafe { emu.run(); let mut buf = [0u8].repeat(MAX_INPUT_SIZE); - emu.read_mem(input_addr, buf.as_mut_slice()); + emu.read_mem(TARGET_SYMBOLS["FUZZ_INPUT"], buf.as_mut_slice()); let dir = env::var("SEED_DIR").map_or("./corpus".to_string(), |x| x); let filename = if input_dump == "" {"input"} else {&input_dump}; diff --git a/fuzzers/FRET/src/lib.rs b/fuzzers/FRET/src/lib.rs index 9b1705ef7b..2513679039 100644 --- a/fuzzers/FRET/src/lib.rs +++ b/fuzzers/FRET/src/lib.rs @@ -7,4 +7,6 @@ pub mod systemstate; #[cfg(target_os = "linux")] mod cli; #[cfg(target_os = "linux")] -pub mod templates; \ No newline at end of file +pub mod templates; +#[cfg(target_os = "linux")] +mod config; \ No newline at end of file diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index 2498e834bb..b06ccb413e 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -9,6 +9,8 @@ mod systemstate; mod cli; #[cfg(target_os = "linux")] mod templates; +#[cfg(target_os = "linux")] +mod config; #[cfg(target_os = "linux")] pub fn main() { diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index cfddc9664f..b550a21e18 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -596,6 +596,7 @@ where Some(s) => s, Option::None => { let n=STGFeedbackState::::default(); + unsafe{libafl_bolts::prelude::RegistryBuilder::register::>()}; state.named_metadata_map_mut().insert("stgfeedbackstate",n); state.named_metadata_map_mut().get_mut::>("stgfeedbackstate").unwrap() } diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs new file mode 100644 index 0000000000..072dfbe5ab --- /dev/null +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs @@ -0,0 +1,125 @@ +use hashbrown::HashMap; +use libafl_qemu::{elf::EasyElf, GuestAddr}; + +use crate::{ + fuzzer::get_all_fn_symbol_ranges, + systemstate::{self, helpers::{get_function_range, load_symbol}, target_os::freertos::ISR_SYMBOLS}, +}; + +pub fn add_target_symbols(elf: &EasyElf, addrs: &mut HashMap<&'static str, GuestAddr>) { + // required for system state observation + addrs.insert("pxCurrentTCB", load_symbol(&elf, "pxCurrentTCB", false)); // loads to the address specified in elf, without respecting program headers + addrs.insert( + "pxReadyTasksLists", + load_symbol(&elf, "pxReadyTasksLists", false), + ); + addrs.insert( + "pxDelayedTaskList", + load_symbol(&elf, "pxDelayedTaskList", false), + ); + addrs.insert( + "pxOverflowDelayedTaskList", + load_symbol(&elf, "pxOverflowDelayedTaskList", false), + ); + addrs.insert( + "uxSchedulerSuspended", + load_symbol(&elf, "uxSchedulerSuspended", false), + ); + addrs.insert( + "xSchedulerRunning", + load_symbol(&elf, "xSchedulerRunning", false), + ); + addrs.insert( + "uxCriticalNesting", + load_symbol(&elf, "uxCriticalNesting", false), + ); +} + +pub fn add_target_ranges( + elf: &EasyElf, + symbols: &HashMap<&'static str, GuestAddr>, + ranges: &mut HashMap<&'static str, std::ops::Range>, +) { + let api_range = ranges.get("API_CODE").unwrap(); + let app_range = ranges.get("APP_CODE").unwrap(); + + let mut api_fn_ranges = get_all_fn_symbol_ranges(&elf, api_range.clone()); + let mut app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone()); + + // Regular ISR functions, remove from API functions + let mut isr_fn_ranges: HashMap> = ISR_SYMBOLS + .iter() + .filter_map(|x| { + api_fn_ranges + .remove(&x.to_string()) + .map(|y| (x.to_string(), y.clone())) + }) + .collect(); + // User-defined ISR functions, remove from APP functions + ISR_SYMBOLS.iter().for_each(|x| { + let _ = (app_fn_ranges + .remove(&x.to_string()) + .map(|y| (x.to_string(), y.clone()))) + .map(|z| isr_fn_ranges.insert(z.0, z.1)); + }); + + // Add the rest of the ISR function, if not already found + for i in ISR_SYMBOLS { + if isr_fn_ranges.get(&i.to_string()).is_none() { + if let Some(fr) = get_function_range(&elf, i) { + isr_fn_ranges.insert(i.to_string(), fr); + } + } + } + + let mut groups = HashMap::new(); + + groups.insert("API_FN", api_fn_ranges); + groups.insert("APP_FN", app_fn_ranges); + groups.insert("ISR_FN", isr_fn_ranges); +} + +pub fn get_range_groups( + elf: &EasyElf, + _addrs: &HashMap<&'static str, GuestAddr>, + ranges: &HashMap<&'static str, std::ops::Range>, +) -> HashMap<&'static str, hashbrown::HashMap>> { + let api_range = ranges.get("API_CODE").unwrap(); + let app_range = ranges.get("APP_CODE").unwrap(); + + let mut api_fn_ranges = get_all_fn_symbol_ranges(&elf, api_range.clone()); + let mut app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone()); + + // Regular ISR functions, remove from API functions + let mut isr_fn_ranges: HashMap> = ISR_SYMBOLS + .iter() + .filter_map(|x| { + api_fn_ranges + .remove(&x.to_string()) + .map(|y| (x.to_string(), y.clone())) + }) + .collect(); + // User-defined ISR functions, remove from APP functions + ISR_SYMBOLS.iter().for_each(|x| { + let _ = (app_fn_ranges + .remove(&x.to_string()) + .map(|y| (x.to_string(), y.clone()))) + .map(|z| isr_fn_ranges.insert(z.0, z.1)); + }); + + // Add the rest of the ISR function, if not already found + for i in ISR_SYMBOLS { + if isr_fn_ranges.get(&i.to_string()).is_none() { + if let Some(fr) = get_function_range(&elf, i) { + isr_fn_ranges.insert(i.to_string(), fr); + } + } + } + + let mut groups = HashMap::new(); + + groups.insert("API_FN", api_fn_ranges); + groups.insert("APP_FN", app_fn_ranges); + groups.insert("ISR_FN", isr_fn_ranges); + return groups; +} diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs index d30cbac8c1..732e3b2179 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs @@ -9,6 +9,7 @@ use crate::{ pub mod bindings; pub mod qemu_module; +pub mod config; use bindings::*; use super::QemuLookup; @@ -61,18 +62,6 @@ impl SystemState for FreeRTOSSystemState { fn current_task_mut(&mut self) -> &mut Self::TCB { &mut self.current_task } - - // fn get_edge(&self) -> (GuestAddr, GuestAddr) { - // (self.edge.0, self.edge.1) - // } - - // fn get_capture_point(&self) -> (CaptureEvent, String) { - // self.capture_point.clone() - // } - - // fn get_mem_reads(&self) -> &Vec<(u32, u8)> { - // &self.mem_reads - // } } //============================================================================= Data structures @@ -210,11 +199,11 @@ fn trigger_collection( systemstate.capture_point = (CaptureEvent::APIEnd, s.to_string()); } CaptureEvent::ISRStart => { - let s = h.isr_addrs.get(&edge.1).unwrap(); + let s = h.isr_fn_addrs.get(&edge.1).unwrap(); systemstate.capture_point = (CaptureEvent::ISRStart, s.to_string()); } CaptureEvent::ISREnd => { - let s = h.isr_addrs.get(&edge.0).unwrap(); + let s = h.isr_fn_addrs.get(&edge.0).unwrap(); systemstate.capture_point = (CaptureEvent::ISREnd, s.to_string()); } CaptureEvent::End => { @@ -230,15 +219,6 @@ fn trigger_collection( systemstate.qemu_tick = get_icount(emulator); - let mut buf: [u8; 4] = [0, 0, 0, 0]; - match h.input_counter { - Some(s) => unsafe { - emulator.read_mem(s, &mut buf); - }, - Option::None => (), - }; - systemstate.input_counter = GuestAddr::from_le_bytes(buf); - let curr_tcb_addr: freertos::void_ptr = QemuLookup::lookup(emulator, h.tcb_addr); if curr_tcb_addr == 0 { return; diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs index 36ba6e5758..27180610fd 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs @@ -13,11 +13,13 @@ use libafl_qemu::{ EmulatorModules, GuestAddr, Hook, MemAccessInfo, }; -use crate::systemstate::{ +use libafl_bolts::tuples::Map; + +use crate::{fuzzer::MAX_INPUT_SIZE, systemstate::{ helpers::{get_icount, in_any_range, read_rec_return_stackframe}, target_os::{freertos::FreeRTOSStruct::*, *}, AtomicBasicBlock, CaptureEvent, RTOSJob, -}; +}}; use super::{ bindings::{self, *}, @@ -30,13 +32,17 @@ use super::{ /// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs #[derive(Debug)] pub struct FreeRTOSSystemStateHelper { + // Address of the application code + pub app_range: Range, // Address of API functions pub api_fn_addrs: HashMap, pub api_fn_ranges: Vec<(String, std::ops::Range)>, // Address of interrupt routines - pub isr_addrs: HashMap, - pub isr_ranges: Vec<(String, std::ops::Range)>, + pub isr_fn_addrs: HashMap, + pub isr_fn_ranges: Vec<(String, std::ops::Range)>, + // Address of input memory pub input_mem: Range, + // FreeRTOS specific addresses pub tcb_addr: GuestAddr, pub ready_queues: GuestAddr, pub delay_queue: GuestAddr, @@ -44,45 +50,48 @@ pub struct FreeRTOSSystemStateHelper { pub scheduler_lock_addr: GuestAddr, pub scheduler_running_addr: GuestAddr, pub critical_addr: GuestAddr, - pub input_counter: Option, - pub app_range: Range, pub job_done_addrs: GuestAddr, } impl FreeRTOSSystemStateHelper { #[must_use] pub fn new( - api_fn_addrs: HashMap, - api_fn_ranges: Vec<(String, std::ops::Range)>, - isr_addrs: HashMap, - isr_ranges: Vec<(String, std::ops::Range)>, - input_mem: Range, - tcb_addr: GuestAddr, - ready_queues: GuestAddr, - delay_queue: GuestAddr, - delay_queue_overflow: GuestAddr, - scheduler_lock_addr: GuestAddr, - scheduler_running_addr: GuestAddr, - critical_addr: GuestAddr, - input_counter: Option, - app_range: Range, - job_done_addrs: GuestAddr, + target_symbols: &HashMap<&'static str, GuestAddr>, + target_ranges: &HashMap<&'static str, Range>, + target_groups: &HashMap<&'static str, HashMap>>, ) -> Self { + let app_range = target_ranges.get("APP_CODE").unwrap().clone(); + + let api_fn_ranges : Vec<_> = target_groups.get("API_FN").unwrap().iter().sorted_by_key(|x|x.1.start).map(|(n,r)| (n.clone(),r.clone())).collect(); + let api_fn_addrs = api_fn_ranges.iter().map(|(n,r)| (r.start,n.clone())).collect(); + let isr_fn_ranges : Vec<_> = target_groups.get("ISR_FN").unwrap().iter().sorted_by_key(|x|x.1.start).map(|(n,r)| (n.clone(),r.clone())).collect(); + let isr_fn_addrs = isr_fn_ranges.iter().map(|(n,r)| (r.start,n.clone())).collect(); + + let input_mem = target_symbols.get("FUZZ_INPUT").map(|x| *x..(*x+unsafe{MAX_INPUT_SIZE as GuestAddr})).unwrap(); + + let tcb_addr = *target_symbols.get("pxCurrentTCB").unwrap(); + let ready_queues = *target_symbols.get("pxReadyTasksLists").unwrap(); + let delay_queue = *target_symbols.get("pxDelayedTaskList").unwrap(); + let delay_queue_overflow = *target_symbols.get("pxOverflowDelayedTaskList").unwrap(); + let scheduler_lock_addr = *target_symbols.get("uxSchedulerSuspended").unwrap(); + let scheduler_running_addr = *target_symbols.get("xSchedulerRunning").unwrap(); + let critical_addr = *target_symbols.get("uxCriticalNesting").unwrap(); + let job_done_addrs = *target_symbols.get("trigger_job_done").unwrap(); + FreeRTOSSystemStateHelper { + app_range, api_fn_addrs, api_fn_ranges, - isr_addrs, - isr_ranges, + isr_fn_addrs, + isr_fn_ranges, input_mem, - tcb_addr: tcb_addr, - ready_queues: ready_queues, + tcb_addr, + ready_queues, delay_queue, delay_queue_overflow, scheduler_lock_addr, scheduler_running_addr, critical_addr, - input_counter: input_counter, - app_range, job_done_addrs, } } @@ -96,7 +105,7 @@ where where ET: EmulatorModuleTuple, { - for wp in self.isr_addrs.keys() { + for wp in self.isr_fn_addrs.keys() { emulator_modules.instructions(*wp, Hook::Function(exec_isr_hook::), false); } emulator_modules.jmps( @@ -338,7 +347,7 @@ where { if h.app_range.contains(&src) && !h.app_range.contains(&dest) - && in_any_range(&h.isr_ranges, src).is_none() + && in_any_range(&h.isr_fn_ranges, src).is_none() { if let Some(_) = in_any_range(&h.api_fn_ranges, dest) { // println!("New jmp {:x} {:x}", src, dest); @@ -353,7 +362,7 @@ where // println!("API Return Edge {:#x}", src); return Some(2); } - if let Some(_) = in_any_range(&h.isr_ranges, src) { + if let Some(_) = in_any_range(&h.isr_fn_ranges, src) { // println!("ISR Return Edge {:#x}", src); return Some(3); } @@ -385,7 +394,7 @@ pub fn trace_jmp( // API return // Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space. if in_any_range(&h.api_fn_ranges, dest).is_none() - && in_any_range(&h.isr_ranges, dest).is_none() + && in_any_range(&h.isr_fn_ranges, dest).is_none() { let mut edge = (0, 0); edge.0 = in_any_range(&h.api_fn_ranges, src).unwrap().start; @@ -399,7 +408,7 @@ pub fn trace_jmp( dest = read_rec_return_stackframe(&emulator, dest); let mut edge = (0, 0); - edge.0 = in_any_range(&h.isr_ranges, src).unwrap().start; + edge.0 = in_any_range(&h.isr_fn_ranges, src).unwrap().start; edge.1 = dest; trigger_collection(&emulator, edge, CaptureEvent::ISREnd, h);