centralise target symbols
This commit is contained in:
parent
a13dca6f39
commit
79d3f89254
@ -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 {
|
||||
|
97
fuzzers/FRET/src/config.rs
Normal file
97
fuzzers/FRET/src/config.rs
Normal file
@ -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<GuestAddr>> {
|
||||
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
|
||||
}
|
@ -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<GuestAddr>) -> HashMap<String,std::ops::Range<GuestAddr>> {
|
||||
let mut api_addreses : HashMap<String,std::ops::Range<GuestAddr>> = HashMap::new();
|
||||
pub fn get_all_fn_symbol_ranges(elf: &EasyElf, range: std::ops::Range<GuestAddr>) -> HashMap<String,std::ops::Range<GuestAddr>> {
|
||||
let mut ret : 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() && 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<GuestAddr>> = get_target_ranges(&elf, &TARGET_SYMBOLS);
|
||||
let TARGET_GROUPS: HashMap<&'static str, HashMap<String, Range<GuestAddr>>> = 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::<u64>(&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<String,std::ops::Range<GuestAddr>> = 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<GuestAddr, String> = 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<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();
|
||||
|
||||
/// Setup the interrupt inputs. Noop if interrupts are not fuzzed
|
||||
fn setup_interrupt_inputs(mut input : MultipartInput<BytesInput>, interrupt_config : &Vec<(usize,u32)>, mut random: Option<&mut StdRng>) -> MultipartInput<BytesInput> {
|
||||
@ -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<BytesInput>| {
|
||||
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<u32, _> { 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};
|
||||
|
@ -7,4 +7,6 @@ pub mod systemstate;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod cli;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod templates;
|
||||
pub mod templates;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod config;
|
@ -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() {
|
||||
|
@ -596,6 +596,7 @@ where
|
||||
Some(s) => s,
|
||||
Option::None => {
|
||||
let n=STGFeedbackState::<SYS>::default();
|
||||
unsafe{libafl_bolts::prelude::RegistryBuilder::register::<STGFeedbackState<SYS>>()};
|
||||
state.named_metadata_map_mut().insert("stgfeedbackstate",n);
|
||||
state.named_metadata_map_mut().get_mut::<STGFeedbackState<SYS>>("stgfeedbackstate").unwrap()
|
||||
}
|
||||
|
125
fuzzers/FRET/src/systemstate/target_os/freertos/config.rs
Normal file
125
fuzzers/FRET/src/systemstate/target_os/freertos/config.rs
Normal file
@ -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<GuestAddr>>,
|
||||
) {
|
||||
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<String, std::ops::Range<GuestAddr>> = 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<GuestAddr>>,
|
||||
) -> HashMap<&'static str, hashbrown::HashMap<String, std::ops::Range<u32>>> {
|
||||
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<String, std::ops::Range<GuestAddr>> = 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;
|
||||
}
|
@ -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;
|
||||
|
@ -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<GuestAddr>,
|
||||
// Address of API functions
|
||||
pub api_fn_addrs: HashMap<GuestAddr, String>,
|
||||
pub api_fn_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
|
||||
// Address of interrupt routines
|
||||
pub isr_addrs: HashMap<GuestAddr, String>,
|
||||
pub isr_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
|
||||
pub isr_fn_addrs: HashMap<GuestAddr, String>,
|
||||
pub isr_fn_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
|
||||
// Address of input memory
|
||||
pub input_mem: Range<GuestAddr>,
|
||||
// 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<GuestAddr>,
|
||||
pub app_range: Range<GuestAddr>,
|
||||
pub job_done_addrs: GuestAddr,
|
||||
}
|
||||
|
||||
impl FreeRTOSSystemStateHelper {
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
api_fn_addrs: HashMap<GuestAddr, String>,
|
||||
api_fn_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
|
||||
isr_addrs: HashMap<GuestAddr, String>,
|
||||
isr_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
|
||||
input_mem: Range<GuestAddr>,
|
||||
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<GuestAddr>,
|
||||
app_range: Range<GuestAddr>,
|
||||
job_done_addrs: GuestAddr,
|
||||
target_symbols: &HashMap<&'static str, GuestAddr>,
|
||||
target_ranges: &HashMap<&'static str, Range<GuestAddr>>,
|
||||
target_groups: &HashMap<&'static str, HashMap<String, Range<GuestAddr>>>,
|
||||
) -> 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<S>,
|
||||
{
|
||||
for wp in self.isr_addrs.keys() {
|
||||
for wp in self.isr_fn_addrs.keys() {
|
||||
emulator_modules.instructions(*wp, Hook::Function(exec_isr_hook::<ET, S>), 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<QT, S>(
|
||||
// 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<QT, S>(
|
||||
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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user