centralise target symbols

This commit is contained in:
Alwin Berger 2024-12-18 16:28:51 +01:00
parent a13dca6f39
commit 79d3f89254
9 changed files with 309 additions and 160 deletions

View File

@ -3,7 +3,7 @@ use std::path::PathBuf;
// Argument parsing ================================================================================ // Argument parsing ================================================================================
#[derive(Parser)] #[derive(Parser,Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Cli { pub struct Cli {
/// Kernel Image /// Kernel Image
@ -41,7 +41,7 @@ pub struct Cli {
#[command(subcommand)] #[command(subcommand)]
pub command: Commands, pub command: Commands,
} }
#[derive(Subcommand,Clone)] #[derive(Subcommand,Clone,Debug)]
pub enum Commands { pub enum Commands {
/// run a single input /// run a single input
Showmap { Showmap {

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

View File

@ -1,7 +1,7 @@
//! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! A fuzzer using qemu in systemmode for binary-only coverage of kernels
//! //!
use core::time::Duration; 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 hashbrown::HashMap;
use libafl_bolts::{ use libafl_bolts::{
core_affinity::Cores, ownedref::OwnedMutSlice, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice, SimpleStderrLogger 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 rand::{SeedableRng, StdRng, Rng};
use crate::{ 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} 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 log;
use rand::RngCore; use rand::RngCore;
use crate::templates; use crate::templates;
use std::ops::Range;
// Constants ================================================================================ // Constants ================================================================================
@ -40,14 +41,14 @@ pub const FIRST_INT : u32 = 200000;
pub const MAX_NUM_INTERRUPT: usize = 128; 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 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 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>> { pub fn get_all_fn_symbol_ranges(elf: &EasyElf, range: std::ops::Range<GuestAddr>) -> HashMap<String,std::ops::Range<GuestAddr>> {
let mut api_addreses : HashMap<String,std::ops::Range<GuestAddr>> = HashMap::new(); let mut 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(); 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)); funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value));
for sym in &funcs { for sym in &funcs {
@ -55,15 +56,12 @@ for sym in &funcs {
if let Some(sym_name) = sym_name { if let Some(sym_name) = sym_name {
// if ISR_SYMBOLS.contains(&sym_name) {continue;}; // skip select symbols, which correspond to ISR-safe system calls // if ISR_SYMBOLS.contains(&sym_name) {continue;}; // skip select symbols, which correspond to ISR-safe system calls
if let Some(r) = get_function_range(elf, sym_name) { if let Some(r) = get_function_range(elf, sym_name) {
api_addreses.insert(sym_name.to_string(), r); 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)] #[allow(unused)]
@ -160,6 +158,7 @@ pub fn fuzz() {
log::set_max_level(log::LevelFilter::Info); log::set_max_level(log::LevelFilter::Info);
SimpleStderrLogger::set_logger().unwrap(); SimpleStderrLogger::set_logger().unwrap();
let cli = Cli::parse(); let cli = Cli::parse();
dbg!(&cli);
set_env_from_config(&cli.kernel, &cli.config); set_env_from_config(&cli.kernel, &cli.config);
let interrupt_config = crate::cli::get_interrupt_config(&cli.kernel, &cli.config); let interrupt_config = crate::cli::get_interrupt_config(&cli.kernel, &cli.config);
unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();}
@ -181,52 +180,10 @@ let elf = EasyElf::from_file(
) )
.unwrap(); .unwrap();
// the main address where the fuzzer starts let TARGET_SYMBOLS: HashMap<&'static str, GuestAddr> = get_target_symbols(&elf);
// if this is set for freeRTOS it has an influence on where the data will have to be written, let TARGET_RANGES: HashMap<&'static str, Range<GuestAddr>> = get_target_ranges(&elf, &TARGET_SYMBOLS);
// since the startup routine copies the data segemnt to it's virtual address let TARGET_GROUPS: HashMap<&'static str, HashMap<String, Range<GuestAddr>>> = get_range_groups(&elf, &TARGET_SYMBOLS, &TARGET_RANGES);
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 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 { unsafe {
libafl_num_interrupts = [0; NUM_INTERRUPT_SOURCES]; 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.");} 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(); let denylist: Vec<_> = TARGET_GROUPS["ISR_FN"].values().map(|x| x.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 = FilterList::DenyList(denylist); // do not count isr jumps, which are useless 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 /// 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> { 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 env: Vec<(String, String)> = env::vars().collect();
let qemu = Qemu::init(&args).expect("Emulator creation failed"); 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); qemu.set_breakpoint(main_addr);
unsafe { unsafe {
match qemu.run() { match qemu.run() {
@ -332,7 +261,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
qemu.remove_breakpoint(main_addr); qemu.remove_breakpoint(main_addr);
} }
qemu.set_breakpoint(breakpoint); // BREAKPOINT qemu.set_breakpoint(TARGET_SYMBOLS["BREAKPOINT"]); // BREAKPOINT
let devices = qemu.list_devices(); let devices = qemu.list_devices();
println!("Devices = {devices:?}"); println!("Devices = {devices:?}");
@ -342,6 +271,10 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
#[cfg(not(feature = "snapshot_fast"))] #[cfg(not(feature = "snapshot_fast"))]
let initial_snap = None; 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 // The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, state: &mut _, input: &MultipartInput<BytesInput>| { let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, state: &mut _, input: &MultipartInput<BytesInput>| {
unsafe { 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 // Note: I could not find a difference between write_mem and write_phys_mem for my usecase
qemu.write_mem(input_addr, bytes); qemu.write_mem(harness_input_addr, bytes);
if let Some(s) = input_length_ptr { if let Some(s) = harness_input_length_ptr {
qemu.write_mem(s, &len.to_le_bytes()); 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(|i| qemu.cpu_from_index(i))
.map(|cpu| -> Result<u32, _> { cpu.read_reg(Regs::Pc) }); .map(|cpu| -> Result<u32, _> { cpu.read_reg(Regs::Pc) });
match pcs 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, Some(_) => ExitKind::Ok,
Option::None => ExitKind::Crash, Option::None => ExitKind::Crash,
@ -491,7 +424,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
let qhelpers = tuple_list!(); let qhelpers = tuple_list!();
#[cfg(feature = "observe_systemstate")] #[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")] #[cfg(feature = "observe_edges")]
let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers); let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers);
let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), 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 = (systemstate::report::SchedulerStatsStage::default(),());
let stages = (StdMutationalStage::new(mutator), stages); let stages = (StdMutationalStage::new(mutator), stages);
#[cfg(feature = "mutate_stg")] #[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")] #[cfg(feature = "fuzz_int")]
let mut stages = (InterruptShiftStage::<_,_,_,FreeRTOSSystem>::new(&interrupt_config), stages); 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 env: Vec<(String, String)> = env::vars().collect();
let emu = Qemu::init(&args).expect("Emu creation failed"); 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 emu.set_breakpoint(main_addr); // BREAKPOINT
} }
unsafe { unsafe {
emu.run(); emu.run();
let mut buf = [0u8].repeat(MAX_INPUT_SIZE); 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 dir = env::var("SEED_DIR").map_or("./corpus".to_string(), |x| x);
let filename = if input_dump == "" {"input"} else {&input_dump}; let filename = if input_dump == "" {"input"} else {&input_dump};

View File

@ -8,3 +8,5 @@ pub mod systemstate;
mod cli; mod cli;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub mod templates; pub mod templates;
#[cfg(target_os = "linux")]
mod config;

View File

@ -9,6 +9,8 @@ mod systemstate;
mod cli; mod cli;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
mod templates; mod templates;
#[cfg(target_os = "linux")]
mod config;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn main() { pub fn main() {

View File

@ -596,6 +596,7 @@ where
Some(s) => s, Some(s) => s,
Option::None => { Option::None => {
let n=STGFeedbackState::<SYS>::default(); 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().insert("stgfeedbackstate",n);
state.named_metadata_map_mut().get_mut::<STGFeedbackState<SYS>>("stgfeedbackstate").unwrap() state.named_metadata_map_mut().get_mut::<STGFeedbackState<SYS>>("stgfeedbackstate").unwrap()
} }

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

View File

@ -9,6 +9,7 @@ use crate::{
pub mod bindings; pub mod bindings;
pub mod qemu_module; pub mod qemu_module;
pub mod config;
use bindings::*; use bindings::*;
use super::QemuLookup; use super::QemuLookup;
@ -61,18 +62,6 @@ impl SystemState for FreeRTOSSystemState {
fn current_task_mut(&mut self) -> &mut Self::TCB { fn current_task_mut(&mut self) -> &mut Self::TCB {
&mut self.current_task &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 //============================================================================= Data structures
@ -210,11 +199,11 @@ fn trigger_collection(
systemstate.capture_point = (CaptureEvent::APIEnd, s.to_string()); systemstate.capture_point = (CaptureEvent::APIEnd, s.to_string());
} }
CaptureEvent::ISRStart => { 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()); systemstate.capture_point = (CaptureEvent::ISRStart, s.to_string());
} }
CaptureEvent::ISREnd => { 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()); systemstate.capture_point = (CaptureEvent::ISREnd, s.to_string());
} }
CaptureEvent::End => { CaptureEvent::End => {
@ -230,15 +219,6 @@ fn trigger_collection(
systemstate.qemu_tick = get_icount(emulator); 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); let curr_tcb_addr: freertos::void_ptr = QemuLookup::lookup(emulator, h.tcb_addr);
if curr_tcb_addr == 0 { if curr_tcb_addr == 0 {
return; return;

View File

@ -13,11 +13,13 @@ use libafl_qemu::{
EmulatorModules, GuestAddr, Hook, MemAccessInfo, 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}, helpers::{get_icount, in_any_range, read_rec_return_stackframe},
target_os::{freertos::FreeRTOSStruct::*, *}, target_os::{freertos::FreeRTOSStruct::*, *},
AtomicBasicBlock, CaptureEvent, RTOSJob, AtomicBasicBlock, CaptureEvent, RTOSJob,
}; }};
use super::{ use super::{
bindings::{self, *}, 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 /// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs
#[derive(Debug)] #[derive(Debug)]
pub struct FreeRTOSSystemStateHelper { pub struct FreeRTOSSystemStateHelper {
// Address of the application code
pub app_range: Range<GuestAddr>,
// Address of API functions // Address of API functions
pub api_fn_addrs: HashMap<GuestAddr, String>, pub api_fn_addrs: HashMap<GuestAddr, String>,
pub api_fn_ranges: Vec<(String, std::ops::Range<GuestAddr>)>, pub api_fn_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
// Address of interrupt routines // Address of interrupt routines
pub isr_addrs: HashMap<GuestAddr, String>, pub isr_fn_addrs: HashMap<GuestAddr, String>,
pub isr_ranges: Vec<(String, std::ops::Range<GuestAddr>)>, pub isr_fn_ranges: Vec<(String, std::ops::Range<GuestAddr>)>,
// Address of input memory
pub input_mem: Range<GuestAddr>, pub input_mem: Range<GuestAddr>,
// FreeRTOS specific addresses
pub tcb_addr: GuestAddr, pub tcb_addr: GuestAddr,
pub ready_queues: GuestAddr, pub ready_queues: GuestAddr,
pub delay_queue: GuestAddr, pub delay_queue: GuestAddr,
@ -44,45 +50,48 @@ pub struct FreeRTOSSystemStateHelper {
pub scheduler_lock_addr: GuestAddr, pub scheduler_lock_addr: GuestAddr,
pub scheduler_running_addr: GuestAddr, pub scheduler_running_addr: GuestAddr,
pub critical_addr: GuestAddr, pub critical_addr: GuestAddr,
pub input_counter: Option<GuestAddr>,
pub app_range: Range<GuestAddr>,
pub job_done_addrs: GuestAddr, pub job_done_addrs: GuestAddr,
} }
impl FreeRTOSSystemStateHelper { impl FreeRTOSSystemStateHelper {
#[must_use] #[must_use]
pub fn new( pub fn new(
api_fn_addrs: HashMap<GuestAddr, String>, target_symbols: &HashMap<&'static str, GuestAddr>,
api_fn_ranges: Vec<(String, std::ops::Range<GuestAddr>)>, target_ranges: &HashMap<&'static str, Range<GuestAddr>>,
isr_addrs: HashMap<GuestAddr, String>, target_groups: &HashMap<&'static str, HashMap<String, Range<GuestAddr>>>,
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,
) -> Self { ) -> 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 { FreeRTOSSystemStateHelper {
app_range,
api_fn_addrs, api_fn_addrs,
api_fn_ranges, api_fn_ranges,
isr_addrs, isr_fn_addrs,
isr_ranges, isr_fn_ranges,
input_mem, input_mem,
tcb_addr: tcb_addr, tcb_addr,
ready_queues: ready_queues, ready_queues,
delay_queue, delay_queue,
delay_queue_overflow, delay_queue_overflow,
scheduler_lock_addr, scheduler_lock_addr,
scheduler_running_addr, scheduler_running_addr,
critical_addr, critical_addr,
input_counter: input_counter,
app_range,
job_done_addrs, job_done_addrs,
} }
} }
@ -96,7 +105,7 @@ where
where where
ET: EmulatorModuleTuple<S>, 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.instructions(*wp, Hook::Function(exec_isr_hook::<ET, S>), false);
} }
emulator_modules.jmps( emulator_modules.jmps(
@ -338,7 +347,7 @@ where
{ {
if h.app_range.contains(&src) if h.app_range.contains(&src)
&& !h.app_range.contains(&dest) && !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) { if let Some(_) = in_any_range(&h.api_fn_ranges, dest) {
// println!("New jmp {:x} {:x}", src, dest); // println!("New jmp {:x} {:x}", src, dest);
@ -353,7 +362,7 @@ where
// println!("API Return Edge {:#x}", src); // println!("API Return Edge {:#x}", src);
return Some(2); 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); // println!("ISR Return Edge {:#x}", src);
return Some(3); return Some(3);
} }
@ -385,7 +394,7 @@ pub fn trace_jmp<QT, S>(
// API return // API return
// Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space. // Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space.
if in_any_range(&h.api_fn_ranges, dest).is_none() 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); let mut edge = (0, 0);
edge.0 = in_any_range(&h.api_fn_ranges, src).unwrap().start; 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); dest = read_rec_return_stackframe(&emulator, dest);
let mut edge = (0, 0); 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; edge.1 = dest;
trigger_collection(&emulator, edge, CaptureEvent::ISREnd, h); trigger_collection(&emulator, edge, CaptureEvent::ISREnd, h);