From d460bab404486e7ab2e7532446beb987cac02ef6 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 9 Jan 2022 19:22:40 +0100 Subject: [PATCH 01/83] TO_REMOVE relative qemu_path --- libafl_qemu/build.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libafl_qemu/build.rs b/libafl_qemu/build.rs index 87ae41bcd8..5beec3039c 100644 --- a/libafl_qemu/build.rs +++ b/libafl_qemu/build.rs @@ -86,7 +86,8 @@ fn main() { build_dep_check(&["git", "make"]); let qemu_rev = out_dir_path.join("QEMU_REVISION"); - let qemu_path = out_dir_path.join(QEMU_DIRNAME); + // let qemu_path = out_dir_path.join(QEMU_DIRNAME); + let qemu_path = Path::new("../../qemu-libafl-bridge"); if qemu_rev.exists() && fs::read_to_string(&qemu_rev).expect("Failed to read QEMU_REVISION") != QEMU_REVISION From edff09540174019308a60a10a116503350a6c171 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 10 Jan 2022 00:10:17 +0100 Subject: [PATCH 02/83] Add systemmode to libafl_qemu Use the new configuration feature systemmode --- libafl_qemu/Cargo.toml | 2 ++ libafl_qemu/build.rs | 22 ++++++++++-- libafl_qemu/src/emu.rs | 55 ++++++++++++++++++++++++++++ libafl_qemu/src/lib.rs | 8 +++-- libafl_qemu/src/snapshot_sys.rs | 63 +++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 libafl_qemu/src/snapshot_sys.rs diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index d53e98f3b0..3d39a6cfce 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -22,6 +22,8 @@ aarch64 = [] # build qemu for aarch64 clippy = [] # special feature for clippy, don't use in normal projects§ +systemmode = [] # Emulate system images instead of user-mode binaries + [dependencies] libafl = { path = "../libafl", version = "0.7.1" } libafl_targets = { path = "../libafl_targets", version = "0.7.1" } diff --git a/libafl_qemu/build.rs b/libafl_qemu/build.rs index 5beec3039c..1c6ee21041 100644 --- a/libafl_qemu/build.rs +++ b/libafl_qemu/build.rs @@ -145,6 +145,9 @@ fn main() { } let build_dir = qemu_path.join("build"); + #[cfg(feature = "systemmode")] + let output_lib = build_dir.join(&format!("libqemu-system-{}.so", cpu_target)); + #[cfg(not(feature = "systemmode"))] let output_lib = build_dir.join(&format!("libqemu-{}.so", cpu_target)); if !output_lib.is_file() { drop( @@ -157,7 +160,7 @@ fn main() { .current_dir(&qemu_path) //.arg("--as-static-lib") .arg("--as-shared-lib") - .arg(&format!("--target-list={}-linux-user", cpu_target)) + .arg(&format!("--target-list={}-linux-user,{}-softmmu", cpu_target,cpu_target)) .args(&[ "--audio-drv-list=", "--disable-blobs", @@ -170,7 +173,7 @@ fn main() { "--disable-curl", "--disable-curses", "--disable-dmg", - "--disable-fdt", + "--enable-fdt", "--disable-gcrypt", "--disable-glusterfs", "--disable-gnutls", @@ -199,7 +202,7 @@ fn main() { "--disable-smartcard", "--disable-snappy", "--disable-spice", - "--disable-system", + "--enable-system", "--disable-tools", "--disable-tpm", "--disable-usb-redir", @@ -248,6 +251,9 @@ fn main() { let mut objects = vec![]; for dir in &[ build_dir.join("libcommon.fa.p"), + #[cfg(feature = "systemmode")] + build_dir.join(&format!("libqemu-{}-softmmu.fa.p", cpu_target)), + #[cfg(not(feature = "systemmode"))] build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)), build_dir.join("libcommon-user.fa.p"), //build_dir.join("libqemuutil.a.p"), @@ -315,6 +321,13 @@ fn main() { #[cfg(not(feature = "python"))] { + #[cfg(feature = "systemmode")] + fs::copy( + build_dir.join(&format!("libqemu-system-{}.so", cpu_target)), + target_dir.join(&format!("libqemu-system-{}.so", cpu_target)), + ) + .expect("Failed to copy the QEMU shared object"); + #[cfg(not(feature = "systemmode"))] fs::copy( build_dir.join(&format!("libqemu-{}.so", cpu_target)), target_dir.join(&format!("libqemu-{}.so", cpu_target)), @@ -325,6 +338,9 @@ fn main() { "cargo:rustc-link-search=native={}", &target_dir.to_string_lossy().to_string() ); + #[cfg(feature = "systemmode")] + println!("cargo:rustc-link-lib=qemu-system-{}", cpu_target); + #[cfg(not(feature = "systemmode"))] println!("cargo:rustc-link-lib=qemu-{}", cpu_target); println!("cargo:rustc-env=LD_LIBRARY_PATH={}", target_dir.display()); diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index b4485bbc3f..85cafc43fb 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -1,5 +1,6 @@ //! Expose QEMU user `LibAFL` C api to Rust +use libc::c_char; use core::{ convert::Into, ffi::c_void, @@ -170,12 +171,18 @@ impl MapInfo { } extern "C" { + #[cfg(not(feature = "systemmode"))] fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32; + #[cfg(feature = "systemmode")] + fn libafl_qemu_sys_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32; fn libafl_qemu_write_reg(reg: i32, val: *const u8) -> i32; fn libafl_qemu_read_reg(reg: i32, val: *mut u8) -> i32; fn libafl_qemu_num_regs() -> i32; + #[cfg(not(feature = "systemmode"))] fn libafl_qemu_set_breakpoint(addr: u64) -> i32; + #[cfg(feature = "systemmode")] + fn libafl_qemu_set_native_breakpoint(addr: u64) -> i32; fn libafl_qemu_remove_breakpoint(addr: u64) -> i32; fn libafl_qemu_set_hook(addr: u64, callback: extern "C" fn(u64), val: u64) -> i32; fn libafl_qemu_remove_hook(addr: u64) -> i32; @@ -184,6 +191,15 @@ extern "C" { fn libafl_get_brk() -> u64; fn libafl_set_brk(brk: u64) -> u64; + #[cfg(feature = "systemmode")] + fn libafl_phys_write(addr: u64, buf: *const u8, len: i32); + #[cfg(feature = "systemmode")] + fn libafl_phys_read(addr: u64, buf: *mut u8, len: i32); + #[cfg(feature = "systemmode")] + fn libafl_snapshot_save(name: *const c_char) -> i32; + #[cfg(feature = "systemmode")] + fn libafl_snapshot_load(name: *const c_char) -> i32; + fn strlen(s: *const u8) -> usize; /// abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, int flags, int fd, abi_ulong offset) @@ -318,11 +334,18 @@ impl Emulator { #[allow(clippy::cast_possible_wrap)] let argc = argv.len() as i32; unsafe { + #[cfg(not(feature = "systemmode"))] qemu_user_init( argc, argv.as_ptr() as *const *const u8, envp.as_ptr() as *const *const u8, ); + #[cfg(feature = "systemmode")] + libafl_qemu_sys_init( + argc, + argv.as_ptr() as *const *const u8, + envp.as_ptr() as *const *const u8, + ); EMULATOR_IS_INITIALIZED = true; } Emulator { _private: () } @@ -338,6 +361,11 @@ impl Emulator { GuestMaps::new() } + #[cfg(feature = "systemmode")] + pub unsafe fn write_mem(&self, addr: u64, buf: &[u8]) { + unsafe { libafl_phys_write(addr, buf.as_ptr(), buf.len().try_into().unwrap()); } + } + #[cfg(not(feature = "systemmode"))] pub unsafe fn write_mem(&self, addr: u64, buf: &[T]) { let host_addr = self.g2h(addr); copy_nonoverlapping( @@ -347,6 +375,11 @@ impl Emulator { ); } + #[cfg(feature = "systemmode")] + pub unsafe fn read_mem(&self, addr: u64, buf: &mut [u8]) { + unsafe { libafl_phys_read(addr, buf.as_mut_ptr(), buf.len().try_into().unwrap()); } + } + #[cfg(not(feature = "systemmode"))] pub unsafe fn read_mem(&self, addr: u64, buf: &mut [T]) { let host_addr = self.g2h(addr); copy_nonoverlapping( @@ -392,6 +425,9 @@ impl Emulator { pub fn set_breakpoint(&self, addr: u64) { unsafe { + #[cfg(feature = "systemmode")] + libafl_qemu_set_native_breakpoint(addr); + #[cfg(not(feature = "systemmode"))] libafl_qemu_set_breakpoint(addr); } } @@ -619,6 +655,25 @@ impl Emulator { libafl_post_syscall_hook = hook; } } + #[cfg(feature = "systemmode")] + pub fn snapshot_save(&self, name: &str) -> bool{ + let cname = std::ffi::CString::new(name).expect("Snapshot name not CString compatible"); + let ret = unsafe { libafl_snapshot_save(cname.as_ptr()) }; + match ret { + 0 => false, + _ => true, + } + } + + #[cfg(feature = "systemmode")] + pub fn snapshot_load(&self, name: &str) -> bool{ + let cname = std::ffi::CString::new(name).expect("Snapshot name not CString compatible"); + let ret = unsafe { libafl_snapshot_load(cname.as_ptr()) }; + match ret { + 0 => false, + _ => true, + } + } } #[cfg(feature = "python")] diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index 499c82d3fd..e7bbc3aaf3 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -35,10 +35,14 @@ pub use edges::QemuEdgeCoverageHelper; pub mod cmplog; #[cfg(target_os = "linux")] pub use cmplog::QemuCmpLogHelper; -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux",not(feature = "systemmode")))] pub mod snapshot; -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux",not(feature = "systemmode")))] pub use snapshot::QemuSnapshotHelper; +#[cfg(all(target_os = "linux",feature = "systemmode"))] +pub mod snapshot_sys; +#[cfg(all(target_os = "linux",feature = "systemmode"))] +pub use snapshot_sys::QemuSysSnapshotHelper; #[cfg(target_os = "linux")] pub mod asan; #[cfg(target_os = "linux")] diff --git a/libafl_qemu/src/snapshot_sys.rs b/libafl_qemu/src/snapshot_sys.rs new file mode 100644 index 0000000000..0441c1b037 --- /dev/null +++ b/libafl_qemu/src/snapshot_sys.rs @@ -0,0 +1,63 @@ +use crate::Emulator; +use crate::QemuExecutor; +use crate::QemuHelper; +use crate::QemuHelperTuple; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; + +use crate::{ + emu, +}; +// TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html +#[derive(Debug)] +pub struct QemuSysSnapshotHelper { + pub empty: bool, +} + +impl QemuSysSnapshotHelper { + #[must_use] + pub fn new() -> Self { + Self { + empty: true, + } + } + + pub fn snapshot(&mut self, emulator: &Emulator) { + if self.empty { + let ret = emulator.snapshot_save("Start"); + if !ret { panic!("QemuSysSnapshotHelper failed to take a snapshot") }; + self.empty = false; + } + } + pub fn reset(&mut self, emulator: &Emulator) { + let ret = emulator.snapshot_load("Start"); + if !ret { panic!("QemuSysSnapshotHelper failed to load a snapshot") }; + } +} + +impl Default for QemuSysSnapshotHelper { + fn default() -> Self { + Self::new() + } +} + +impl QemuHelper for QemuSysSnapshotHelper +where + I: Input, + S: HasMetadata, +{ + fn init<'a, H, OT, QT>(&self, _executor: &QemuExecutor<'a, H, I, OT, QT, S>) + where + H: FnMut(&I) -> ExitKind, + OT: ObserversTuple, + QT: QemuHelperTuple, + { + } + + fn pre_exec(&mut self, emulator: &Emulator, _input: &I) { + if self.empty { + self.snapshot(emulator); + } else { + self.reset(emulator); + } + } +} \ No newline at end of file From de4481e70d6c2a5acf6826f55b63fb1281d4f524 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 10 Jan 2022 00:22:57 +0100 Subject: [PATCH 03/83] add a basic demo --- fuzzers/wcet_qemu_sys/Cargo.toml | 20 ++ fuzzers/wcet_qemu_sys/README.md | 10 + fuzzers/wcet_qemu_sys/src/main.rs | 33 +++ fuzzers/wcet_qemu_sys/src/showmap.rs | 316 +++++++++++++++++++++++++++ fuzzers/wcet_qemu_sys/src/worst.rs | 218 ++++++++++++++++++ fuzzers/wcet_qemu_sys/starter.sh | 3 + 6 files changed, 600 insertions(+) create mode 100644 fuzzers/wcet_qemu_sys/Cargo.toml create mode 100644 fuzzers/wcet_qemu_sys/README.md create mode 100644 fuzzers/wcet_qemu_sys/src/main.rs create mode 100644 fuzzers/wcet_qemu_sys/src/showmap.rs create mode 100644 fuzzers/wcet_qemu_sys/src/worst.rs create mode 100755 fuzzers/wcet_qemu_sys/starter.sh diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml new file mode 100644 index 0000000000..868c2bc2b6 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "wcet_qemu_sys" +version = "0.1.2" +authors = [ "Alwin Berger " ] +edition = "2021" + +[features] +default = ["std"] +std = [] + +[profile.release] +debug = true + +[dependencies] +libafl = { path = "../../libafl/" } +libafl_qemu = { path = "../../libafl_qemu/", features = ["systemmode", "arm"] } +clap = { version = "3.0.0-beta.2", features = ["default"] } +serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib +hashbrown = { version = "0.11", features = ["serde", "ahash-compile-time-rng"], default-features=false } # A faster hashmap, nostd compatible +nix = "0.23.0" diff --git a/fuzzers/wcet_qemu_sys/README.md b/fuzzers/wcet_qemu_sys/README.md new file mode 100644 index 0000000000..d917ff87cf --- /dev/null +++ b/fuzzers/wcet_qemu_sys/README.md @@ -0,0 +1,10 @@ +# Systemmode Fuzzing + +This folder contains an example fuzzer tailored for system-images. +For the moment it is mostly hardcoded. + +## Build + +To build this example, run `cargo build --release` or `cargo build --release`. +Also build the FreeRTOS demo named CORTEX_M3_MPS2_QEMU_GCC. +Then run `./starter.sh RTOSDemo.axf` and observe. diff --git a/fuzzers/wcet_qemu_sys/src/main.rs b/fuzzers/wcet_qemu_sys/src/main.rs new file mode 100644 index 0000000000..5d7a3b5017 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/main.rs @@ -0,0 +1,33 @@ +#[cfg(target_os = "linux")] +// pub mod fuzzer; +pub mod showmap; +pub mod worst; +use libafl_qemu::{ + edges, + edges::QemuEdgeCoverageHelper, + emu, filter_qemu_args, +}; + +fn main() { + // let mut args2: Vec = vec![ + // "qemu-system-arm", + // "-machine","mps2-an385", + // "-monitor", "null", + // "-semihosting", + // "--semihosting-config", "enable=on,target=native", + // "-kernel", "RTOSDemo.axf", + // "-serial", "stdio", "-nographic", + // "-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2", + // "-S" + // ].iter().map(|x| x.to_string()).collect(); + // init(&mut args2, &Vec::new()); + // let succ = emu::snapshot_save("Start"); + // println!("Snaphsot: {}",succ); + // emu::set_breakpoint(0x00004f5c); + // emu::run(); + // let succ = emu::snapshot_load("Start"); + // emu::run(); + #[cfg(target_os = "linux")] + // fuzzer::main() + showmap::main() +} diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs new file mode 100644 index 0000000000..74e7e3d9bf --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -0,0 +1,316 @@ +//! A singlethreaded QEMU fuzzer that can auto-restart. + +use libafl::corpus::Corpus; +use libafl::state::HasCorpus; +use libafl::Fuzzer; +use libafl::bolts::tuples::Merge; +use libafl::mutators::tokens_mutations; +use libafl::mutators::havoc_mutations; +use libafl::mutators::StdScheduledMutator; +use libafl::stages::StdMutationalStage; +use libafl_qemu::QemuExecutor; +use libafl::bolts::tuples::Named; +use libafl::observers::ObserversTuple; +use libafl::events::EventFirer; +use libafl::state::HasClientPerfMonitor; +use libafl::feedbacks::Feedback; +use libafl::Evaluator; +use libafl::inputs::Input; +use libafl::corpus::InMemoryCorpus; +use libafl::events::SimpleEventManager; +use libafl::stats::SimpleStats; +use crate::worst::HitcountsMapObserver; +use crate::worst::HitFeedback; +use clap::{App, Arg}; +use std::{ + env, + fs::{self}, + path::PathBuf, +}; + +use libafl::{ + bolts::{ + current_nanos, + rands::StdRand, + tuples::{tuple_list}, + }, + corpus::{QueueCorpusScheduler}, + executors::{ExitKind}, + fuzzer::{StdFuzzer}, + inputs::{BytesInput, HasTargetBytes}, + observers::{VariableMapObserver}, + state::{StdState}, + Error, +}; +use libafl_qemu::{ + edges, + edges::QemuEdgeCoverageHelper, + emu::Emulator, filter_qemu_args, + snapshot_sys, + snapshot_sys::QemuSysSnapshotHelper, + elf::EasyElf, +}; + + +/// The fuzzer main +pub fn main() { + // Registry the metadata types used in this fuzzer + // Needed only on no_std + //RegistryBuilder::register::(); + + let mut args: Vec = env::args().collect(); + + let res = match App::new("libafl_qemu_fuzzbench") + .version("0.4.0") + .author("AFLplusplus team") + .about("LibAFL-based fuzzer with QEMU for Fuzzbench") + .arg( + Arg::new("out") + .long("libafl-out") + .required(false) + .takes_value(true), + ) + .arg( + Arg::new("in") + .long("libafl-in") + .required(true) + .takes_value(true), + ) + .arg( + Arg::new("tokens") + .long("libafl-tokens") + .takes_value(true), + ) + .arg( + Arg::new("logfile") + .long("libafl-logfile") + .default_value("libafl.log"), + ) + .arg( + Arg::new("timeout") + .long("libafl-timeout") + .default_value("1000"), + ) + .try_get_matches_from(filter_qemu_args()) + { + Ok(res) => res, + Err(err) => { + println!( + "Syntax: {}, --libafl-in \n{:?}", + env::current_exe() + .unwrap_or_else(|_| "fuzzer".into()) + .to_string_lossy(), + err.info, + ); + return; + } + }; + + println!( + "Workdir: {:?}", + env::current_dir().unwrap().to_string_lossy().to_string() + ); + + // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. + let mut out_dir = PathBuf::from(res.value_of("out").unwrap().to_string()); + if fs::create_dir(&out_dir).is_err() { + println!("Out dir at {:?} already exists.", &out_dir); + if !out_dir.is_dir() { + println!("Out dir at {:?} is not a valid directory!", &out_dir); + return; + } + } + let mut worstcases = out_dir.clone(); + worstcases.push("worstcase"); + out_dir.push("queue"); + + let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string()); + if !in_dir.is_dir() { + println!("In dir at {:?} is not a valid directory!", &in_dir); + return; + } + + fuzz(in_dir) + .expect("An error occurred while fuzzing"); +} + +/// The actual fuzzer +fn fuzz( + seed_dir: PathBuf, +) -> Result<(), Error> { + //=========== Setup emulator + let mut env: Vec<(String, String)> = env::vars().collect(); + let mut args2: Vec = vec![ + "qemu-system-arm", + "-machine","mps2-an385", + "-monitor", "null", + "-semihosting", + "--semihosting-config", "enable=on,target=native", + "-kernel", "RTOSDemo.axf", + "-serial", "stdio", "-nographic", + "-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2", + "-S" + ].iter().map(|x| x.to_string()).collect(); + let emu = Emulator::new(&mut args2, &mut env); + //=========== Analyze the binary to find the target function address + // let mut elf_buffer = Vec::new(); + // let elf = EasyElf::from_file(emu::binary_path(), &mut elf_buffer)?; + + // let test_one_input_ptr = elf + // .resolve_symbol("LLVMFuzzerTestOneInput", 0) + // .expect("Symbol LLVMFuzzerTestOneInput not found"); + // println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr); + + + + //====== Create the input field + let input_addr = 0x00006de4+0xc; + println!("Placing input at {:#x}", input_addr); + emu.set_breakpoint(0x00004f5c); + + //====== Create the most simple status display and managers. + + let stats = SimpleStats::new(|s| println!("{}", s)); + + let mut mgr = SimpleEventManager::new(stats); + + + //========== EDGES_MAP is static field which somehow(?) gets handed over to the Qemu instrumentation + //========== Otherwise setup generic observers + // Create an observation channel using the coverage map + let edges = unsafe { &mut edges::EDGES_MAP }; + let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM }; + let edges_observer = + HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); + + //========= Feedback-Function evaluate the Maps. Need to dump it for debugging and check if it reaches targets. + let feedback = DumpMapFeedback::new(); + + // A feedback to choose if an input is a solution or not + let objective = HitFeedback::new(); + + // create a State from scratch + let mut state = StdState::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + InMemoryCorpus::new(), + // States of the feedbacks. + // They are the data related to the feedbacks that you want to persist in the State. + (), + ); + + // A minimization+queue policy to get testcasess from the corpus + let scheduler = QueueCorpusScheduler::new(); + + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + + //======== The harness has to start the execution of the emulator + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |input: &BytesInput| { + let target = input.target_bytes(); + let mut buf = target.as_slice(); + let mut len = buf.len(); + if len > 4096 { + buf = &buf[0..4096]; + len = 4096; + } + + unsafe { + emu.write_mem(0x00006de4+0xc,buf); + // println!("{:#?}",edges_copy); + + emu.run(); + // println!("{:#?}",edges_copy); + } + + ExitKind::Ok + }; + + //======= Construct the executor, including the Helpers. The edges_observer still contains the ref to EDGES_MAP + let mut executor = QemuExecutor::new( + &mut harness, + &emu, + tuple_list!( + QemuEdgeCoverageHelper::new(), + // QemuCmpLogHelper::new(), + // QemuAsanHelper::new(), + QemuSysSnapshotHelper::new() + ), + tuple_list!(edges_observer), + &mut fuzzer, + &mut state, + &mut mgr, + )?; + let firstinput = match seed_dir.clone().is_dir() { + true => seed_dir.clone().read_dir().expect("Directory not a directory?").next().expect("Directory empty?").expect("File not in directory?").path(), + false => seed_dir.clone() + }; + fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, Input::from_file(&firstinput).expect("Could not load file")).expect("Evaluation failed"); + // fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, Input::from_file(&firstinput).expect("Could not load file")).expect("Evaluation failed"); + // let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); + // let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + // if state.corpus().count() < 1 { + // state + // .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) + // .expect("Failed to load initial corpus"); + // println!("We imported {} inputs from disk.", state.corpus().count()); + // } + // fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + // .expect("Error in the fuzzing loop"); + return Ok(()); +} + +//=========================== Debugging Feedback +/// A [`Feedback`] meant to dump the edgemap for debugging. +#[derive(Debug)] +pub struct DumpMapFeedback {} + +impl Feedback for DumpMapFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = _observers.match_name::>>("edges") + .expect("HitcountsMapObserver not found"); + println!("{:#?}",observer.edgemap); + Ok(true) + } +} + +impl Named for DumpMapFeedback { + #[inline] + fn name(&self) -> &str { + "HitFeedback" + } +} + +impl DumpMapFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new() -> Self { + Self {} + } +} + +impl Default for DumpMapFeedback { + fn default() -> Self { + Self::new() + } +} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs new file mode 100644 index 0000000000..9b207b99ad --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -0,0 +1,218 @@ +use libafl::observers::VariableMapObserver; +use hashbrown::{HashMap}; +use libafl::observers::ObserversTuple; +use libafl::executors::ExitKind; +use libafl::events::EventFirer; +use libafl::state::HasClientPerfMonitor; +use libafl::inputs::Input; +use libafl::feedbacks::Feedback; +use libafl::state::HasMetadata; +use libafl_qemu::edges::QemuEdgesMapMetadata; +use libafl::observers::MapObserver; +use serde::{Deserialize, Serialize}; + +use libafl::{ + bolts::{ + tuples::Named, + HasLen, + }, + observers::Observer, + Error, +}; + +/// Map observer with hitcounts postprocessing +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "M: serde::de::DeserializeOwned")] +pub struct HitcountsMapObserver +where + M: serde::Serialize + serde::de::DeserializeOwned, +{ + base: M, + pub edgemap: HashMap<(u64,u64),u8>, +} + +static COUNT_CLASS_LOOKUP: [u8; 256] = [ + 0, 1, 2, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, +]; + +impl Observer for HitcountsMapObserver +where + M: MapObserver + Observer, + S: HasMetadata, // Need to grab the HashMap from a Helper +{ + #[inline] + fn pre_exec(&mut self, state: &mut S, input: &I) -> Result<(), Error> { + self.edgemap = HashMap::new(); + self.base.pre_exec(state, input) + } + + #[inline] + fn post_exec(&mut self, state: &mut S, input: &I) -> Result<(), Error> { + //new stuff + let original_hashmap=&state.metadata().get::().unwrap().map; + for (key, val) in original_hashmap.iter() { + self.edgemap.insert(*key,*self.base.get(*val as usize)); + } + // println!("Post-Exec Len: {} Cap: {}",self.edgemap.len(),self.edgemap.capacity()); + // println!("{:#?}",self.edgemap); + //end new stuff + let cnt = self.usable_count(); + for i in 0..cnt { + *self.get_mut(i) = COUNT_CLASS_LOOKUP[*self.get(i) as usize]; + } + self.base.post_exec(state, input) + } +} + +impl Named for HitcountsMapObserver +where + M: Named + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn name(&self) -> &str { + self.base.name() + } +} + +impl HasLen for HitcountsMapObserver +where + M: MapObserver, +{ + #[inline] + fn len(&self) -> usize { + self.base.len() + } +} + +impl MapObserver for HitcountsMapObserver +where + M: MapObserver, +{ + #[inline] + fn map(&self) -> Option<&[u8]> { + self.base.map() + } + + #[inline] + fn map_mut(&mut self) -> Option<&mut [u8]> { + self.base.map_mut() + } + + #[inline] + fn usable_count(&self) -> usize { + self.base.usable_count() + } + + #[inline] + fn initial(&self) -> u8 { + self.base.initial() + } + + #[inline] + fn initial_mut(&mut self) -> &mut u8 { + self.base.initial_mut() + } + + #[inline] + fn set_initial(&mut self, initial: u8) { + self.base.set_initial(initial); + } +} + +impl HitcountsMapObserver +where + M: serde::Serialize + serde::de::DeserializeOwned, +{ + /// Creates a new [`MapObserver`] + pub fn new(base: M) -> Self { + Self { edgemap:HashMap::new(), base } + } +} + +//=================================================================== + + +/// A [`HitFeedback`] reports as interesting when all predicted worst case edges have been matched. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct HitFeedback {} + +impl Feedback for HitFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + // TODO Replace with match_name_type when stable + // let observer = _observers.match_name::>("edges").expect("SelectedEdgeObserver not found"); + let observer = _observers.match_name::>>("edges") + .expect("HitcountsMapObserver not found"); + let mut hit_target: bool = true; + //check if we've hit any targets. + // let inc : u64 = (0x401180); + // let call1 : u64 = (0x40119f); + // let call2 : u64 = (0x4011b0); + // let call3 : u64 = (0x4011c8); + let to_check : Vec<(u64,u64)> = vec![ + (0x401190,0x4011c8), // start -> call + (0x40119b,0x4011b0), // cmp -> call + (0x4011b5,0x40119f), // cmp -> call + ]; + let expected : Vec = vec![ 1, 1, 1]; + let combo = to_check.iter().zip(expected.iter()); + for (edg, val) in combo { + // println!("Feedback Len: {} Cap: {}",observer.edgemap.len(),observer.edgemap.capacity()); + match observer.edgemap.get(edg) { + Some(x) => hit_target &= val == x, + None =>hit_target = false + } + } + if hit_target { + Ok(true) + } else { + Ok(false) + } + } +} + +impl Named for HitFeedback { + #[inline] + fn name(&self) -> &str { + "HitFeedback" + } +} + +impl HitFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new() -> Self { + Self {} + } +} + +impl Default for HitFeedback { + fn default() -> Self { + Self::new() + } +} diff --git a/fuzzers/wcet_qemu_sys/starter.sh b/fuzzers/wcet_qemu_sys/starter.sh new file mode 100755 index 0000000000..654c6c4c91 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/starter.sh @@ -0,0 +1,3 @@ +mkdir -p target/test_in target/test_out +[ ! -f target/test_in/test ] && echo " !test" > target/test_in/test +LD_LIBRARY_PATH=target/debug target/debug/wcet_qemu_sys $1 --libafl-out target/test_out --libafl-in target/test_in From c1db0752c1d1b0215aab109416fa628f6aef0013 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 11 Jan 2022 19:32:25 +0100 Subject: [PATCH 04/83] remove hardcoded addresses --- fuzzers/wcet_qemu_sys/Cargo.toml | 1 + fuzzers/wcet_qemu_sys/src/showmap.rs | 71 +++++++++++++++++++++------- fuzzers/wcet_qemu_sys/starter.sh | 2 +- 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 868c2bc2b6..f415c52cfb 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -18,3 +18,4 @@ clap = { version = "3.0.0-beta.2", features = ["default"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib hashbrown = { version = "0.11", features = ["serde", "ahash-compile-time-rng"], default-features=false } # A faster hashmap, nostd compatible nix = "0.23.0" +goblin = "0.4.2" diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index 74e7e3d9bf..2447256c4a 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use std::path::Path; use libafl::corpus::Corpus; use libafl::state::HasCorpus; use libafl::Fuzzer; @@ -58,12 +59,18 @@ pub fn main() { // Needed only on no_std //RegistryBuilder::register::(); - let mut args: Vec = env::args().collect(); + let args: Vec = env::args().collect(); - let res = match App::new("libafl_qemu_fuzzbench") + let res = match App::new("wcet_qemu_fuzzer") .version("0.4.0") - .author("AFLplusplus team") - .about("LibAFL-based fuzzer with QEMU for Fuzzbench") + .author("Alwin Berger") + .about("LibAFL-based fuzzer for WCET in System Kernels.") + .arg( + Arg::new("k") + .long("libafl-kernel") + .required(true) + .takes_value(true), + ) .arg( Arg::new("out") .long("libafl-out") @@ -130,43 +137,72 @@ pub fn main() { return; } - fuzz(in_dir) + let kernel = PathBuf::from(res.value_of("k").unwrap().to_string()); + + + fuzz(in_dir, kernel) .expect("An error occurred while fuzzing"); } +fn virt2phys(vaddr : u64, tab : &goblin::elf::Elf) -> u64 { + let ret; + for i in &tab.program_headers { + if i.vm_range().contains(&vaddr.try_into().expect("Can not cast u64 to usize")) { + ret = vaddr-i.p_vaddr+i.p_paddr; + return ret - (ret % 2); + } + } + ret = vaddr; + // unlike the arm-toolcahin goblin produces some off-by one errors when parsing arm + return ret - (ret % 2); +} + /// The actual fuzzer fn fuzz( seed_dir: PathBuf, + kernel: PathBuf, ) -> Result<(), Error> { //=========== Setup emulator let mut env: Vec<(String, String)> = env::vars().collect(); - let mut args2: Vec = vec![ + let mut args: Vec = vec![ "qemu-system-arm", "-machine","mps2-an385", "-monitor", "null", "-semihosting", "--semihosting-config", "enable=on,target=native", - "-kernel", "RTOSDemo.axf", + "-kernel", kernel.to_str().unwrap(), "-serial", "stdio", "-nographic", "-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2", "-S" ].iter().map(|x| x.to_string()).collect(); - let emu = Emulator::new(&mut args2, &mut env); + let emu = Emulator::new(&mut args, &mut env); //=========== Analyze the binary to find the target function address - // let mut elf_buffer = Vec::new(); - // let elf = EasyElf::from_file(emu::binary_path(), &mut elf_buffer)?; + let mut elf_buffer = Vec::new(); + let bin_path=Path::new("./RTOSDemo.axf"); + let elf = EasyElf::from_file(bin_path, &mut elf_buffer)?; - // let test_one_input_ptr = elf - // .resolve_symbol("LLVMFuzzerTestOneInput", 0) - // .expect("Symbol LLVMFuzzerTestOneInput not found"); - // println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr); + let test_one_input_ptr = elf + .resolve_symbol("FUZZ_INPUT", 0) + .expect("Symbol FUZZ_INPUT not found"); + let test_one_input_ptr = virt2phys(test_one_input_ptr,&elf.goblin()); + println!("FUZZ_INPUT @ {:#x}", test_one_input_ptr); + let test_length_ptr = elf + .resolve_symbol("FUZZ_LENGTH", 0) + .expect("Symbol FUZZ_LENGTH not found"); + let test_length_ptr = virt2phys(test_length_ptr,&elf.goblin()); + println!("FUZZ_LENGTH @ {:#x}", test_length_ptr); + let check_breakpoint = elf + .resolve_symbol("trigger_Qemu_break", 0) + .expect("Symbol trigger_Qemu_break not found"); + let check_breakpoint = virt2phys(check_breakpoint,&elf.goblin()); + println!("Breakpoint at {:#x}", check_breakpoint); //====== Create the input field - let input_addr = 0x00006de4+0xc; + let input_addr = test_one_input_ptr; println!("Placing input at {:#x}", input_addr); - emu.set_breakpoint(0x00004f5c); + emu.set_breakpoint(check_breakpoint); //====== Create the most simple status display and managers. @@ -221,7 +257,8 @@ fn fuzz( } unsafe { - emu.write_mem(0x00006de4+0xc,buf); + emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); + emu.write_mem(input_addr,buf); // println!("{:#?}",edges_copy); emu.run(); diff --git a/fuzzers/wcet_qemu_sys/starter.sh b/fuzzers/wcet_qemu_sys/starter.sh index 654c6c4c91..12c56008de 100755 --- a/fuzzers/wcet_qemu_sys/starter.sh +++ b/fuzzers/wcet_qemu_sys/starter.sh @@ -1,3 +1,3 @@ mkdir -p target/test_in target/test_out [ ! -f target/test_in/test ] && echo " !test" > target/test_in/test -LD_LIBRARY_PATH=target/debug target/debug/wcet_qemu_sys $1 --libafl-out target/test_out --libafl-in target/test_in +LD_LIBRARY_PATH=target/debug target/debug/wcet_qemu_sys --libafl-kernel $1 --libafl-out target/test_out --libafl-in target/test_in From ac181eb99d13da2a09fbf29fe8e9e20e31806abb Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 16 Jan 2022 00:21:06 +0100 Subject: [PATCH 05/83] add some basic fuzzer --- fuzzers/wcet_qemu_sys/Cargo.toml | 1 + fuzzers/wcet_qemu_sys/src/fuzzer.rs | 417 ++++++++++++++++++++++++++++ fuzzers/wcet_qemu_sys/src/main.rs | 27 +- fuzzers/wcet_qemu_sys/src/worst.rs | 58 ++++ 4 files changed, 481 insertions(+), 22 deletions(-) create mode 100644 fuzzers/wcet_qemu_sys/src/fuzzer.rs diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index f415c52cfb..306d737b3f 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [features] default = ["std"] std = [] +showmap = [] [profile.release] debug = true diff --git a/fuzzers/wcet_qemu_sys/src/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/fuzzer.rs new file mode 100644 index 0000000000..b5d0d59698 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/fuzzer.rs @@ -0,0 +1,417 @@ +//! A singlethreaded QEMU fuzzer that can auto-restart. + +use libafl::stats::SimpleStats; +use libafl::events::SimpleEventManager; +use clap::{App, Arg}; +use core::{cell::RefCell, time::Duration}; +#[cfg(unix)] +use nix::{self, unistd::dup}; +#[cfg(unix)] +use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::{ + env, + fs::{self, File, OpenOptions}, + io::{self, Write}, + path::PathBuf, + process, +}; + +use libafl::{ + bolts::{ + current_nanos, current_time, + os::dup2, + rands::StdRand, + shmem::{ShMemProvider, StdShMemProvider}, + tuples::{tuple_list, Merge}, + }, + corpus::{ + Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, PowerQueueCorpusScheduler, + }, + events::SimpleRestartingEventManager, + executors::{ExitKind, ShadowExecutor, TimeoutExecutor}, + feedback_or, + feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + inputs::{BytesInput, HasTargetBytes}, + monitors::SimpleMonitor, + mutators::{ + scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations, + StdMOptMutator, StdScheduledMutator, Tokens, + }, + observers::{TimeObserver, VariableMapObserver}, + stages::{ + calibrate::CalibrationStage, + power::{PowerMutationalStage, PowerSchedule}, + ShadowTracingStage, StdMutationalStage, + }, + state::{HasCorpus, HasMetadata, StdState}, + Error, +}; +use libafl_qemu::{ + //asan::QemuAsanHelper, + cmplog, + cmplog::{CmpLogObserver, QemuCmpLogHelper}, + edges, + edges::QemuEdgeCoverageHelper, + elf::EasyElf, + emu::Emulator, + filter_qemu_args, + snapshot_sys::QemuSysSnapshotHelper, + MmapPerms, + QemuExecutor, + Regs, +}; +use crate::worst::HitcountsMapObserver; +use crate::worst::MapHitIncreaseFeedback; + + +/// The fuzzer main +pub fn main() { + // Registry the metadata types used in this fuzzer + // Needed only on no_std + //RegistryBuilder::register::(); + + let res = match App::new("wcet_qemu_fuzzer") + .version("0.4.0") + .author("Alwin Berger") + .about("LibAFL-based fuzzer for WCET in System Kernels.") + .arg( + Arg::new("k") + .long("libafl-kernel") + .required(true) + .takes_value(true), + ) + .arg( + Arg::new("out") + .help("The directory to place finds in ('corpus')") + .long("libafl-out") + .required(true) + .takes_value(true), + ) + .arg( + Arg::new("in") + .help("The directory to read initial inputs from ('seeds')") + .long("libafl-in") + .required(true) + .takes_value(true), + ) + .arg( + Arg::new("tokens") + .long("libafl-tokens") + .help("A file to read tokens from, to be used during fuzzing") + .takes_value(true), + ) + .arg( + Arg::new("logfile") + .long("libafl-logfile") + .help("Duplicates all output to this file") + .default_value("libafl.log"), + ) + .arg( + Arg::new("timeout") + .long("libafl-timeout") + .help("Timeout for each individual execution, in milliseconds") + .default_value("1000"), + ) + .try_get_matches_from(filter_qemu_args()) + { + Ok(res) => res, + Err(err) => { + println!( + "Syntax: {}, --libafl-in --libafl-out \n{:?}", + env::current_exe() + .unwrap_or_else(|_| "fuzzer".into()) + .to_string_lossy(), + err.info, + ); + return; + } + }; + + println!( + "Workdir: {:?}", + env::current_dir().unwrap().to_string_lossy().to_string() + ); + + // For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir. + let mut out_dir = PathBuf::from(res.value_of("out").unwrap().to_string()); + if fs::create_dir(&out_dir).is_err() { + println!("Out dir at {:?} already exists.", &out_dir); + if !out_dir.is_dir() { + println!("Out dir at {:?} is not a valid directory!", &out_dir); + return; + } + } + let mut crashes = out_dir.clone(); + crashes.push("crashes"); + out_dir.push("queue"); + + let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string()); + if !in_dir.is_dir() { + println!("In dir at {:?} is not a valid directory!", &in_dir); + return; + } + + let tokens = res.value_of("tokens").map(PathBuf::from); + + let logfile = PathBuf::from(res.value_of("logfile").unwrap().to_string()); + + let timeout = Duration::from_millis( + res.value_of("timeout") + .unwrap() + .to_string() + .parse() + .expect("Could not parse timeout in milliseconds"), + ); + + let kernel = PathBuf::from(res.value_of("k").unwrap().to_string()); + + fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel) + .expect("An error occurred while fuzzing"); +} + +fn virt2phys(vaddr : u64, tab : &goblin::elf::Elf) -> u64 { + let ret; + for i in &tab.program_headers { + if i.vm_range().contains(&vaddr.try_into().expect("Can not cast u64 to usize")) { + ret = vaddr-i.p_vaddr+i.p_paddr; + return ret - (ret % 2); + } + } + ret = vaddr; + // unlike the arm-toolcahin goblin produces some off-by one errors when parsing arm + return ret - (ret % 2); +} + +/// The actual fuzzer +fn fuzz( + corpus_dir: PathBuf, + objective_dir: PathBuf, + seed_dir: PathBuf, + tokenfile: Option, + logfile: PathBuf, + timeout: Duration, + kernel: PathBuf, +) -> Result<(), Error> { + env::remove_var("LD_LIBRARY_PATH"); + + //=========== Initialize the Emulator + let mut args: Vec = vec![ + "qemu-system-arm", + "-machine","mps2-an385", + "-monitor", "null", + "-semihosting", + "--semihosting-config", "enable=on,target=native", + "-kernel", kernel.to_str().unwrap(), + "-serial", "stdio", "-nographic", + "-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2", + "-S" + ].iter().map(|x| x.to_string()).collect(); + let env: Vec<(String, String)> = env::vars().collect(); + let emu = Emulator::new(&args, &env); + + //=========== Analyze the binary to find the target function address + let mut elf_buffer = Vec::new(); + let bin_path=kernel; + let elf = EasyElf::from_file(bin_path, &mut elf_buffer)?; + + let test_one_input_ptr = elf + .resolve_symbol("FUZZ_INPUT", 0) + .expect("Symbol FUZZ_INPUT not found"); + let test_one_input_ptr = virt2phys(test_one_input_ptr,&elf.goblin()); + println!("FUZZ_INPUT @ {:#x}", test_one_input_ptr); + let test_length_ptr = elf + .resolve_symbol("FUZZ_LENGTH", 0) + .expect("Symbol FUZZ_LENGTH not found"); + let test_length_ptr = virt2phys(test_length_ptr,&elf.goblin()); + println!("FUZZ_LENGTH @ {:#x}", test_length_ptr); + let check_breakpoint = elf + .resolve_symbol("trigger_Qemu_break", 0) + .expect("Symbol trigger_Qemu_break not found"); + let check_breakpoint = virt2phys(check_breakpoint,&elf.goblin()); + println!("Breakpoint at {:#x}", check_breakpoint); + + + //====== Note the input field + let input_addr = test_one_input_ptr; + println!("Placing input at {:#x}", input_addr); + emu.set_breakpoint(check_breakpoint); // trigger_Qemu_break + + //====== Setup log and stdout + let log = RefCell::new( + OpenOptions::new() + .append(true) + .create(true) + .open(&logfile)?, + ); + + #[cfg(unix)] + let mut stdout_cpy = unsafe { + let new_fd = dup(io::stdout().as_raw_fd())?; + File::from_raw_fd(new_fd) + }; + #[cfg(unix)] + let file_null = File::open("/dev/null")?; + + //====== Create the most simple status display and managers. + + // 'While the stats are state, they are usually used in the broker - which is likely never restarted + let monitor = SimpleMonitor::new(|s| { + #[cfg(unix)] + writeln!(&mut stdout_cpy, "{}", s).unwrap(); + #[cfg(windows)] + println!("{}", s); + writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap(); + }); + + let mut shmem_provider = StdShMemProvider::new()?; + + //====== Create the most simple status display and managers. + let mut mgr = SimpleEventManager::new(monitor); + + // Create an observation channel using the coverage map + let edges = unsafe { &mut edges::EDGES_MAP }; + let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM }; + let edges_observer = + HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); + + // Create an observation channel to keep track of the execution time + let time_observer = TimeObserver::new("time"); + + // Create an observation channel using cmplog map + let cmplog_observer = CmpLogObserver::new("cmplog", unsafe { &mut cmplog::CMPLOG_MAP }, true); + + // The state of the edges feedback. + let feedback_state = MapFeedbackState::with_observer(&edges_observer); + + // Feedback to rate the interestingness of an input + // This one is composed by two Feedbacks in OR + let feedback = feedback_or!( + // New maximization map feedback linked to the edges observer and the feedback state + MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), + MapHitIncreaseFeedback::new(), + // Time feedback, this one does not need a feedback state + TimeFeedback::new_with_observer(&time_observer) + ); + + // A feedback to choose if an input is a solution or not + let objective = CrashFeedback::new(); + + // create a State from scratch + let mut state = { + StdState::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + OnDiskCorpus::new(corpus_dir).unwrap(), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(objective_dir).unwrap(), + // States of the feedbacks. + // They are the data related to the feedbacks that you want to persist in the State. + tuple_list!(feedback_state), + ) + }; + + let calibration = CalibrationStage::new(&mut state, &edges_observer); + + // Setup a randomic Input2State stage + let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new()))); + + // Setup a MOPT mutator + let mutator = StdMOptMutator::new(&mut state, havoc_mutations().merge(tokens_mutations()), 5)?; + + let power = PowerMutationalStage::new(mutator, PowerSchedule::FAST, &edges_observer); + + // A minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); + + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |input: &BytesInput| { + let target = input.target_bytes(); + let mut buf = target.as_slice(); + let mut len = buf.len(); + if len > 32 { + buf = &buf[0..32]; + len = 32; + } + + unsafe { + emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); + emu.write_mem(input_addr,buf); + // println!("{:#?}",edges_copy); + + emu.run(); + // println!("{:#?}",edges_copy); + } + + ExitKind::Ok + }; + + let executor = QemuExecutor::new( + &mut harness, + &emu, + tuple_list!( + QemuEdgeCoverageHelper::new(), + QemuCmpLogHelper::new(), + //QemuAsanHelper::new(), + QemuSysSnapshotHelper::new() + ), + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut mgr, + )?; + + // Create the executor for an in-process function with one observer for edge coverage and one for the execution time + let executor = TimeoutExecutor::new(executor, timeout); + // Show the cmplog observer + let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer)); + + // Read tokens + if let Some(tokenfile) = tokenfile { + if state.metadata().get::().is_none() { + state.add_metadata(Tokens::from_tokens_file(tokenfile)?); + } + } + + if state.corpus().count() < 1 { + state + .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) + .unwrap_or_else(|_| { + println!("Failed to load initial corpus at {:?}", &seed_dir); + process::exit(0); + }); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + let tracing = ShadowTracingStage::new(&mut executor); + + // The order of the stages matter! + let mut stages = tuple_list!(calibration, tracing, i2s, power); + + // Remove target ouput (logs still survive) + #[cfg(unix)] + { + let null_fd = file_null.as_raw_fd(); + dup2(null_fd, io::stdout().as_raw_fd())?; + dup2(null_fd, io::stderr().as_raw_fd())?; + } + // reopen file to make sure we're at the end + log.replace( + OpenOptions::new() + .append(true) + .create(true) + .open(&logfile)?, + ); + + fuzzer + .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + .expect("Error in the fuzzing loop"); + + // Never reached + Ok(()) +} diff --git a/fuzzers/wcet_qemu_sys/src/main.rs b/fuzzers/wcet_qemu_sys/src/main.rs index 5d7a3b5017..fcdf8ce275 100644 --- a/fuzzers/wcet_qemu_sys/src/main.rs +++ b/fuzzers/wcet_qemu_sys/src/main.rs @@ -1,5 +1,5 @@ #[cfg(target_os = "linux")] -// pub mod fuzzer; +pub mod fuzzer; pub mod showmap; pub mod worst; use libafl_qemu::{ @@ -9,25 +9,8 @@ use libafl_qemu::{ }; fn main() { - // let mut args2: Vec = vec![ - // "qemu-system-arm", - // "-machine","mps2-an385", - // "-monitor", "null", - // "-semihosting", - // "--semihosting-config", "enable=on,target=native", - // "-kernel", "RTOSDemo.axf", - // "-serial", "stdio", "-nographic", - // "-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2", - // "-S" - // ].iter().map(|x| x.to_string()).collect(); - // init(&mut args2, &Vec::new()); - // let succ = emu::snapshot_save("Start"); - // println!("Snaphsot: {}",succ); - // emu::set_breakpoint(0x00004f5c); - // emu::run(); - // let succ = emu::snapshot_load("Start"); - // emu::run(); - #[cfg(target_os = "linux")] - // fuzzer::main() - showmap::main() + #[cfg(all(target_os = "linux", feature = "showmap"))] + showmap::main(); + #[cfg(all(target_os = "linux", not(feature = "showmap")))] + fuzzer::main(); } diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 9b207b99ad..32e21026c4 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -216,3 +216,61 @@ impl Default for HitFeedback { Self::new() } } + +//=================================================================== + + +/// A [`MapHitIncreaseFeedback`] reports as interesting when the total number of used edges increases. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct MapHitIncreaseFeedback { + record_high : u64, +} + +impl Feedback for MapHitIncreaseFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = _observers.match_name::>>("edges") + .expect("HitcountsMapObserver not found"); + let cur = observer.edgemap.values().fold(0,|a,b| a+(*b as u64)); + if cur > self.record_high { + self.record_high = cur; + return Ok(true); + } + return Ok(false); + } +} + +impl Named for MapHitIncreaseFeedback { + #[inline] + fn name(&self) -> &str { + "HitFeedback" + } +} + +impl MapHitIncreaseFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new() -> Self { + Self {record_high: 0} + } +} + +impl Default for MapHitIncreaseFeedback { + fn default() -> Self { + Self::new() + } +} \ No newline at end of file From d2d2862727ae71987f4af757fd9aa427a7d00b03 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 17 Jan 2022 18:45:53 +0100 Subject: [PATCH 06/83] showmap dumps edges to file --- fuzzers/wcet_qemu_sys/Cargo.toml | 1 + fuzzers/wcet_qemu_sys/src/showmap.rs | 32 +++++++++++++++++++++++----- fuzzers/wcet_qemu_sys/starter.sh | 2 +- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 306d737b3f..7eba5fb3ab 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -17,6 +17,7 @@ libafl = { path = "../../libafl/" } libafl_qemu = { path = "../../libafl_qemu/", features = ["systemmode", "arm"] } clap = { version = "3.0.0-beta.2", features = ["default"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib +ron = "0.7" # write serialized data - including hashmaps hashbrown = { version = "0.11", features = ["serde", "ahash-compile-time-rng"], default-features=false } # A faster hashmap, nostd compatible nix = "0.23.0" goblin = "0.4.2" diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index 2447256c4a..7c764f9348 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use std::fs::File; use std::path::Path; use libafl::corpus::Corpus; use libafl::state::HasCorpus; @@ -98,6 +99,11 @@ pub fn main() { .long("libafl-timeout") .default_value("1000"), ) + .arg( + Arg::new("edges") + .long("libafl-edges") + .takes_value(true), + ) .try_get_matches_from(filter_qemu_args()) { Ok(res) => res, @@ -138,9 +144,13 @@ pub fn main() { } let kernel = PathBuf::from(res.value_of("k").unwrap().to_string()); + let edges = match res.value_of("edges") { + Some(st) => Some(PathBuf::from(st.to_string())), + None => None + }; - fuzz(in_dir, kernel) + fuzz(in_dir, kernel, edges) .expect("An error occurred while fuzzing"); } @@ -161,6 +171,7 @@ fn virt2phys(vaddr : u64, tab : &goblin::elf::Elf) -> u64 { fn fuzz( seed_dir: PathBuf, kernel: PathBuf, + dump_edges: Option, ) -> Result<(), Error> { //=========== Setup emulator let mut env: Vec<(String, String)> = env::vars().collect(); @@ -220,7 +231,7 @@ fn fuzz( HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); //========= Feedback-Function evaluate the Maps. Need to dump it for debugging and check if it reaches targets. - let feedback = DumpMapFeedback::new(); + let feedback = DumpMapFeedback::with_dump(dump_edges); // A feedback to choose if an input is a solution or not let objective = HitFeedback::new(); @@ -305,7 +316,9 @@ fn fuzz( //=========================== Debugging Feedback /// A [`Feedback`] meant to dump the edgemap for debugging. #[derive(Debug)] -pub struct DumpMapFeedback {} +pub struct DumpMapFeedback { + dumpfile: Option +} impl Feedback for DumpMapFeedback where @@ -326,7 +339,13 @@ where { let observer = _observers.match_name::>>("edges") .expect("HitcountsMapObserver not found"); - println!("{:#?}",observer.edgemap); + match &self.dumpfile { + Some(s) => { + fs::write(s,ron::to_string(&observer.edgemap).expect("Error serializing hashmap")).expect("Can not dump to file"); + self.dumpfile = None + }, + None => println!("{:#?}",observer.edgemap), + }; Ok(true) } } @@ -342,7 +361,10 @@ impl DumpMapFeedback { /// Creates a new [`HitFeedback`] #[must_use] pub fn new() -> Self { - Self {} + Self {dumpfile: None} + } + pub fn with_dump(dumpfile: Option) -> Self { + Self {dumpfile: dumpfile} } } diff --git a/fuzzers/wcet_qemu_sys/starter.sh b/fuzzers/wcet_qemu_sys/starter.sh index 12c56008de..a933fd629c 100755 --- a/fuzzers/wcet_qemu_sys/starter.sh +++ b/fuzzers/wcet_qemu_sys/starter.sh @@ -1,3 +1,3 @@ mkdir -p target/test_in target/test_out [ ! -f target/test_in/test ] && echo " !test" > target/test_in/test -LD_LIBRARY_PATH=target/debug target/debug/wcet_qemu_sys --libafl-kernel $1 --libafl-out target/test_out --libafl-in target/test_in +LD_LIBRARY_PATH=target/debug target/debug/wcet_qemu_sys --libafl-out target/test_out --libafl-in target/test_in --libafl-kernel $@ From d4d86927b76c8754cce4a80d2417d42989a75121 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 17 Jan 2022 20:51:52 +0100 Subject: [PATCH 07/83] add feedback for improving msd --- fuzzers/wcet_qemu_sys/src/fuzzer.rs | 38 +++++++-- fuzzers/wcet_qemu_sys/src/showmap.rs | 3 +- fuzzers/wcet_qemu_sys/src/worst.rs | 112 +++++++++++++++++++++------ 3 files changed, 124 insertions(+), 29 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/fuzzer.rs index b5d0d59698..a6b95f8fde 100644 --- a/fuzzers/wcet_qemu_sys/src/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/fuzzer.rs @@ -1,5 +1,9 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use libafl::feedbacks::Feedback; +use crate::worst::HitImprovingFeedback; +use crate::worst::HitFeedback; +use hashbrown::HashMap; use libafl::stats::SimpleStats; use libafl::events::SimpleEventManager; use clap::{App, Arg}; @@ -113,6 +117,11 @@ pub fn main() { .help("Timeout for each individual execution, in milliseconds") .default_value("1000"), ) + .arg( + Arg::new("edges") + .long("libafl-edges") + .takes_value(true), + ) .try_get_matches_from(filter_qemu_args()) { Ok(res) => res, @@ -165,8 +174,12 @@ pub fn main() { ); let kernel = PathBuf::from(res.value_of("k").unwrap().to_string()); + let edges = match res.value_of("edges") { + Some(st) => Some(PathBuf::from(st.to_string())), + None => None + }; - fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel) + fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel, edges) .expect("An error occurred while fuzzing"); } @@ -192,6 +205,7 @@ fn fuzz( logfile: PathBuf, timeout: Duration, kernel: PathBuf, + dump_edges: Option, ) -> Result<(), Error> { env::remove_var("LD_LIBRARY_PATH"); @@ -286,16 +300,28 @@ fn fuzz( // Feedback to rate the interestingness of an input // This one is composed by two Feedbacks in OR + let target_map : HashMap<(u64,u64),u8> = match dump_edges { + None => HashMap::new(), + Some(ref s) => { + let raw = fs::read(s).expect("Can not read dumped edges"); + let hmap : HashMap<(u64,u64),u8> = ron::from_str(&String::from_utf8_lossy(&raw)).expect("Can not parse HashMap"); + hmap + }, + }; let feedback = feedback_or!( - // New maximization map feedback linked to the edges observer and the feedback state MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), - MapHitIncreaseFeedback::new(), - // Time feedback, this one does not need a feedback state - TimeFeedback::new_with_observer(&time_observer) + HitImprovingFeedback::new(target_map.clone()) ); + // let feedback = feedback_or!( + // // New maximization map feedback linked to the edges observer and the feedback state + // MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), + // MapHitIncreaseFeedback::new(), + // // Time feedback, this one does not need a feedback state + // TimeFeedback::new_with_observer(&time_observer) + // ); // A feedback to choose if an input is a solution or not - let objective = CrashFeedback::new(); + let objective = HitFeedback::new(target_map,0.0); // create a State from scratch let mut state = { diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index 7c764f9348..d112bb765a 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use libafl::feedbacks::CrashFeedback; use std::fs::File; use std::path::Path; use libafl::corpus::Corpus; @@ -234,7 +235,7 @@ fn fuzz( let feedback = DumpMapFeedback::with_dump(dump_edges); // A feedback to choose if an input is a solution or not - let objective = HitFeedback::new(); + let objective = CrashFeedback::new(); // create a State from scratch let mut state = StdState::new( diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 32e21026c4..1982b7bbab 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -10,6 +10,7 @@ use libafl::state::HasMetadata; use libafl_qemu::edges::QemuEdgesMapMetadata; use libafl::observers::MapObserver; use serde::{Deserialize, Serialize}; +use std::cmp; use libafl::{ bolts::{ @@ -145,7 +146,10 @@ where /// A [`HitFeedback`] reports as interesting when all predicted worst case edges have been matched. #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct HitFeedback {} +pub struct HitFeedback { + target_map: HashMap<(u64,u64),u8>, + target_msd: f64, +} impl Feedback for HitFeedback where @@ -168,26 +172,17 @@ where // let observer = _observers.match_name::>("edges").expect("SelectedEdgeObserver not found"); let observer = _observers.match_name::>>("edges") .expect("HitcountsMapObserver not found"); - let mut hit_target: bool = true; - //check if we've hit any targets. - // let inc : u64 = (0x401180); - // let call1 : u64 = (0x40119f); - // let call2 : u64 = (0x4011b0); - // let call3 : u64 = (0x4011c8); - let to_check : Vec<(u64,u64)> = vec![ - (0x401190,0x4011c8), // start -> call - (0x40119b,0x4011b0), // cmp -> call - (0x4011b5,0x40119f), // cmp -> call - ]; - let expected : Vec = vec![ 1, 1, 1]; - let combo = to_check.iter().zip(expected.iter()); - for (edg, val) in combo { - // println!("Feedback Len: {} Cap: {}",observer.edgemap.len(),observer.edgemap.capacity()); - match observer.edgemap.get(edg) { - Some(x) => hit_target &= val == x, - None =>hit_target = false + if self.target_map.len() == 0 { return Ok(true) }; + + let mut sum_of_square_difference : u64 = 0; // does not include found edges not in target + for (edg, val) in &self.target_map { + match observer.edgemap.get(&edg) { + Some(x) => sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)) as u64).pow(2), + None => sum_of_square_difference+=(*val as u64).pow(2), } } + let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); + let hit_target = mean_sum_of_squares <= 1.0; if hit_target { Ok(true) } else { @@ -206,14 +201,14 @@ impl Named for HitFeedback { impl HitFeedback { /// Creates a new [`HitFeedback`] #[must_use] - pub fn new() -> Self { - Self {} + pub fn new(target_map: HashMap<(u64,u64),u8>, target_msd: f64) -> Self { + Self {target_map: target_map, target_msd: target_msd} } } impl Default for HitFeedback { fn default() -> Self { - Self::new() + Self::new(HashMap::new(),0.0) } } @@ -273,4 +268,77 @@ impl Default for MapHitIncreaseFeedback { fn default() -> Self { Self::new() } +} + +//=================================================================== + + +/// A [`HitFeedback`] reports as interesting when all predicted worst case edges have been matched. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct HitImprovingFeedback { + target_map: HashMap<(u64,u64),u8>, + best_msd: f64, +} + +impl Feedback for HitImprovingFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + // TODO Replace with match_name_type when stable + // let observer = _observers.match_name::>("edges").expect("SelectedEdgeObserver not found"); + let observer = _observers.match_name::>>("edges") + .expect("HitcountsMapObserver not found"); + if self.target_map.len() == 0 { return Ok(true) }; + + let mut sum_of_square_difference : u64 = 0; // does not include found edges not in target + for (edg, val) in &self.target_map { + match observer.edgemap.get(&edg) { + Some(x) => sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)) as u64).pow(2), + None => sum_of_square_difference+=(*val as u64).pow(2), + } + } + let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); + let hit_target = mean_sum_of_squares <= self.best_msd; + eprintln!("in worst, {}",hit_target); + if hit_target { + self.best_msd = mean_sum_of_squares; + Ok(true) + } else { + Ok(false) + } + } +} + +impl Named for HitImprovingFeedback { + #[inline] + fn name(&self) -> &str { + "HitFeedback" + } +} + +impl HitImprovingFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new(target_map: HashMap<(u64,u64),u8>) -> Self { + Self {target_map: target_map, best_msd: f64::MAX} + } +} + +impl Default for HitImprovingFeedback { + fn default() -> Self { + Self::new(HashMap::new()) + } } \ No newline at end of file From 45bdab50005af26dd24c9974ed8ef01edde4c988 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Jan 2022 00:17:17 +0100 Subject: [PATCH 08/83] struct parsing experiment --- fuzzers/wcet_qemu_sys/src/freertos.rs | 524 ++++++++++++++++++++++++++ fuzzers/wcet_qemu_sys/src/main.rs | 1 + fuzzers/wcet_qemu_sys/src/showmap.rs | 52 ++- 3 files changed, 566 insertions(+), 11 deletions(-) create mode 100644 fuzzers/wcet_qemu_sys/src/freertos.rs diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/freertos.rs new file mode 100644 index 0000000000..53e290a7d2 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/freertos.rs @@ -0,0 +1,524 @@ +/* automatically generated by rust-bindgen 0.59.2 */ + +use std::ops::Deref; + +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct __uint8_t(pub ::std::os::raw::c_uchar); +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct __uint16_t(pub ::std::os::raw::c_ushort); +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct __uint32_t(pub ::std::os::raw::c_uint); +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct StackType_t(pub u32); +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct StackType_t_ptr(pub u32); +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct UBaseType_t(pub ::std::os::raw::c_uint); +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Default)] +pub struct TickType_t(pub u32); + +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Default)] +pub struct void_ptr(pub ::std::os::raw::c_uint); + +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Default)] +pub struct xLIST_ptr(pub ::std::os::raw::c_uint); + +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Default)] +pub struct xLIST_ITEM_ptr(pub ::std::os::raw::c_uint); + +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct xLIST_ITEM { + pub xItemValue: TickType_t, + pub pxNext: xLIST_ITEM_ptr, + pub pxPrevious: xLIST_ITEM_ptr, + pub pvOwner: void_ptr, + pub pvContainer: xLIST_ptr, +} +#[test] +fn bindgen_test_layout_xLIST_ITEM() { + assert_eq!( + ::std::mem::size_of::(), + 20usize, + concat!("Size of: ", stringify!(xLIST_ITEM)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(xLIST_ITEM)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).xItemValue as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(xLIST_ITEM), + "::", + stringify!(xItemValue) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pxNext as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(xLIST_ITEM), + "::", + stringify!(pxNext) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pxPrevious as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(xLIST_ITEM), + "::", + stringify!(pxPrevious) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pvOwner as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(xLIST_ITEM), + "::", + stringify!(pvOwner) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pvContainer as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(xLIST_ITEM), + "::", + stringify!(pvContainer) + ) + ); +} +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct ListItem_t(pub xLIST_ITEM); +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct xMINI_LIST_ITEM { + pub xItemValue: TickType_t, + pub pxNext: xLIST_ITEM_ptr, + pub pxPrevious: xLIST_ITEM_ptr, +} +#[test] +fn bindgen_test_layout_xMINI_LIST_ITEM() { + assert_eq!( + ::std::mem::size_of::(), + 12usize, + concat!("Size of: ", stringify!(xMINI_LIST_ITEM)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(xMINI_LIST_ITEM)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).xItemValue as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(xMINI_LIST_ITEM), + "::", + stringify!(xItemValue) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pxNext as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(xMINI_LIST_ITEM), + "::", + stringify!(pxNext) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pxPrevious as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(xMINI_LIST_ITEM), + "::", + stringify!(pxPrevious) + ) + ); +} +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct MiniListItem_t(pub xMINI_LIST_ITEM); +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct xLIST { + pub uxNumberOfItems: UBaseType_t, + pub pxIndex: xLIST_ITEM_ptr, + pub xListEnd: MiniListItem_t, +} +#[test] +fn bindgen_test_layout_xLIST() { + assert_eq!( + ::std::mem::size_of::(), + 20usize, + concat!("Size of: ", stringify!(xLIST)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(xLIST)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).uxNumberOfItems as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(xLIST), + "::", + stringify!(uxNumberOfItems) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pxIndex as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(xLIST), + "::", + stringify!(pxIndex) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).xListEnd as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(xLIST), + "::", + stringify!(xListEnd) + ) + ); +} +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct List_t(pub xLIST); +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct TaskHandle_t(pub *mut tskTaskControlBlock); +pub const eTaskState_eRunning: eTaskState = 0; +pub const eTaskState_eReady: eTaskState = 1; +pub const eTaskState_eBlocked: eTaskState = 2; +pub const eTaskState_eSuspended: eTaskState = 3; +pub const eTaskState_eDeleted: eTaskState = 4; +pub const eTaskState_eInvalid: eTaskState = 5; +pub type eTaskState = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct xTASK_STATUS { + pub xHandle: TaskHandle_t, + pub pcTaskName: u32, + pub xTaskNumber: UBaseType_t, + pub eCurrentState: eTaskState, + pub uxCurrentPriority: UBaseType_t, + pub uxBasePriority: UBaseType_t, + pub ulRunTimeCounter: u32, + pub pxStackBase: StackType_t_ptr, + pub usStackHighWaterMark: u16, +} +#[test] +fn bindgen_test_layout_xTASK_STATUS() { + assert_eq!( + ::std::mem::size_of::(), + 36usize, + concat!("Size of: ", stringify!(xTASK_STATUS)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(xTASK_STATUS)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).xHandle as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(xTASK_STATUS), + "::", + stringify!(xHandle) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pcTaskName as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(xTASK_STATUS), + "::", + stringify!(pcTaskName) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).xTaskNumber as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(xTASK_STATUS), + "::", + stringify!(xTaskNumber) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).eCurrentState as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(xTASK_STATUS), + "::", + stringify!(eCurrentState) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).uxCurrentPriority as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(xTASK_STATUS), + "::", + stringify!(uxCurrentPriority) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).uxBasePriority as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(xTASK_STATUS), + "::", + stringify!(uxBasePriority) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ulRunTimeCounter as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(xTASK_STATUS), + "::", + stringify!(ulRunTimeCounter) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pxStackBase as *const _ as usize }, + 28usize, + concat!( + "Offset of field: ", + stringify!(xTASK_STATUS), + "::", + stringify!(pxStackBase) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).usStackHighWaterMark as *const _ as usize + }, + 32usize, + concat!( + "Offset of field: ", + stringify!(xTASK_STATUS), + "::", + stringify!(usStackHighWaterMark) + ) + ); +} +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct TaskStatus_t(pub xTASK_STATUS); +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct tskTaskControlBlock { + pub pxTopOfStack: StackType_t_ptr, + pub xStateListItem: ListItem_t, + pub xEventListItem: ListItem_t, + pub uxPriority: UBaseType_t, + pub pxStack: StackType_t_ptr, + pub pcTaskName: [::std::os::raw::c_char; 10usize], + pub uxBasePriority: UBaseType_t, + pub uxMutexesHeld: UBaseType_t, + pub ulNotifiedValue: [u32; 1usize], + pub ucNotifyState: [u8; 1usize], + pub ucStaticallyAllocated: u8, + pub ucDelayAborted: u8, +} +#[test] +fn bindgen_test_layout_tskTaskControlBlock() { + assert_eq!( + ::std::mem::size_of::(), + 80usize, + concat!("Size of: ", stringify!(tskTaskControlBlock)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(tskTaskControlBlock)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).pxTopOfStack as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(pxTopOfStack) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).xStateListItem as *const _ as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(xStateListItem) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).xEventListItem as *const _ as usize + }, + 24usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(xEventListItem) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).uxPriority as *const _ as usize }, + 44usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(uxPriority) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pxStack as *const _ as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(pxStack) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pcTaskName as *const _ as usize }, + 52usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(pcTaskName) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).uxBasePriority as *const _ as usize + }, + 64usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(uxBasePriority) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).uxMutexesHeld as *const _ as usize + }, + 68usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(uxMutexesHeld) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).ulNotifiedValue as *const _ as usize + }, + 72usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(ulNotifiedValue) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).ucNotifyState as *const _ as usize + }, + 76usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(ucNotifyState) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).ucStaticallyAllocated as *const _ + as usize + }, + 77usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(ucStaticallyAllocated) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).ucDelayAborted as *const _ as usize + }, + 78usize, + concat!( + "Offset of field: ", + stringify!(tskTaskControlBlock), + "::", + stringify!(ucDelayAborted) + ) + ); +} +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct tskTCB(pub tskTaskControlBlock); +#[repr(transparent)] +#[derive(Debug, Copy, Clone)] +pub struct TCB_t(pub tskTCB); diff --git a/fuzzers/wcet_qemu_sys/src/main.rs b/fuzzers/wcet_qemu_sys/src/main.rs index fcdf8ce275..0440b6238c 100644 --- a/fuzzers/wcet_qemu_sys/src/main.rs +++ b/fuzzers/wcet_qemu_sys/src/main.rs @@ -2,6 +2,7 @@ pub mod fuzzer; pub mod showmap; pub mod worst; +pub mod freertos; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index d112bb765a..ad34f75bc8 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -1,16 +1,7 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. use libafl::feedbacks::CrashFeedback; -use std::fs::File; use std::path::Path; -use libafl::corpus::Corpus; -use libafl::state::HasCorpus; -use libafl::Fuzzer; -use libafl::bolts::tuples::Merge; -use libafl::mutators::tokens_mutations; -use libafl::mutators::havoc_mutations; -use libafl::mutators::StdScheduledMutator; -use libafl::stages::StdMutationalStage; use libafl_qemu::QemuExecutor; use libafl::bolts::tuples::Named; use libafl::observers::ObserversTuple; @@ -23,7 +14,6 @@ use libafl::corpus::InMemoryCorpus; use libafl::events::SimpleEventManager; use libafl::stats::SimpleStats; use crate::worst::HitcountsMapObserver; -use crate::worst::HitFeedback; use clap::{App, Arg}; use std::{ env, @@ -49,10 +39,10 @@ use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, emu::Emulator, filter_qemu_args, - snapshot_sys, snapshot_sys::QemuSysSnapshotHelper, elf::EasyElf, }; +use crate::freertos; /// The fuzzer main @@ -208,6 +198,16 @@ fn fuzz( .expect("Symbol trigger_Qemu_break not found"); let check_breakpoint = virt2phys(check_breakpoint,&elf.goblin()); println!("Breakpoint at {:#x}", check_breakpoint); + let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers + .resolve_symbol("pxCurrentTCB", 0) + .expect("Symbol pxCurrentTCBC not found"); + // let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf.goblin()); + println!("TCB pointer at {:#x}", curr_tcb_pointer); + // let task_queue_addr = elf + // .resolve_symbol("pxReadyTasksLists", 0) + // .expect("Symbol pxReadyTasksLists not found"); + // // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); + // println!("Task Queue at {:#x}", task_queue_addr); @@ -269,12 +269,42 @@ fn fuzz( } unsafe { + // let mut addr_buf : [u8; 4] = [0u8; 4]; + // emu.read_mem(test_length_ptr,&mut addr_buf); + // println!("current len: {}",u32::from_le_bytes(addr_buf)); + //==end test emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); + //== next test + // emu.read_mem(test_length_ptr,&mut addr_buf); + // println!("new len: {}, expected: {}",u32::from_le_bytes(addr_buf),len); // println!("{:#?}",edges_copy); emu.run(); // println!("{:#?}",edges_copy); + // let mut buf_struct : [u8; 5*std::mem::size_of::()] = [1u8; 5*std::mem::size_of::()]; + // emu.read_mem(task_queue_addr, &mut buf_struct); + // let mut prio_lists : Vec = Vec::new(); + // for i in 0..5 { + // let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; + // emu.read_mem(task_queue_addr+i*40, &mut tmp); + // let list_struct = std::mem::transmute::<[u8; std::mem::size_of::()], freertos::List_t>(tmp); + // prio_lists.push(list_struct); + // } + // println!("Raw Buffer: {:?}",buf_struct); + // println!("Lists: {:?}",prio_lists); + //====== experiment inspecting the current tcb + let mut curr_tcb_addr : [u8; 4] = [1u8; 4]; + emu.read_mem(curr_tcb_pointer,&mut curr_tcb_addr); + let curr_tcb_addr = u32::from_le_bytes(curr_tcb_addr); + println!("tcb addr: {:x}",curr_tcb_addr); + + let mut tmp : [u8; std::mem::size_of::()] = [1u8; std::mem::size_of::()]; + emu.read_mem(curr_tcb_addr.into(), &mut tmp); + println!("Raw TCB: {:?}",tmp); + let tcb = std::mem::transmute::<[u8; std::mem::size_of::()], freertos::TCB_t>(tmp); + println!("TCB: {:?}",tcb); + } ExitKind::Ok From e0f197424c2bd7e63265f6aa6f3ffe9865a3eb93 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Jan 2022 20:09:36 +0100 Subject: [PATCH 09/83] wrap emu memory lookup --- fuzzers/wcet_qemu_sys/src/freertos.rs | 207 ++++++++++++++------------ fuzzers/wcet_qemu_sys/src/showmap.rs | 47 ++---- 2 files changed, 121 insertions(+), 133 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/freertos.rs index 53e290a7d2..56c0dc4168 100644 --- a/fuzzers/wcet_qemu_sys/src/freertos.rs +++ b/fuzzers/wcet_qemu_sys/src/freertos.rs @@ -1,43 +1,26 @@ +// Manual Types +use libafl_qemu::Emulator; + +pub type xLIST_ITEM_ptr = ::std::os::raw::c_uint; +pub type xLIST_ptr = ::std::os::raw::c_uint; +pub type ListItem_t_ptr = ::std::os::raw::c_uint; +pub type StackType_t_ptr = ::std::os::raw::c_uint; +pub type char_ptr = ::std::os::raw::c_uint; +pub type void_ptr = ::std::os::raw::c_uint; +pub type TCB_t_ptr = ::std::os::raw::c_uint; + + + /* automatically generated by rust-bindgen 0.59.2 */ -use std::ops::Deref; - -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct __uint8_t(pub ::std::os::raw::c_uchar); -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct __uint16_t(pub ::std::os::raw::c_ushort); -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct __uint32_t(pub ::std::os::raw::c_uint); -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct StackType_t(pub u32); -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct StackType_t_ptr(pub u32); -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct UBaseType_t(pub ::std::os::raw::c_uint); -#[repr(transparent)] -#[derive(Debug, Copy, Clone, Default)] -pub struct TickType_t(pub u32); - -#[repr(transparent)] -#[derive(Debug, Copy, Clone, Default)] -pub struct void_ptr(pub ::std::os::raw::c_uint); - -#[repr(transparent)] -#[derive(Debug, Copy, Clone, Default)] -pub struct xLIST_ptr(pub ::std::os::raw::c_uint); - -#[repr(transparent)] -#[derive(Debug, Copy, Clone, Default)] -pub struct xLIST_ITEM_ptr(pub ::std::os::raw::c_uint); - +pub type __uint8_t = ::std::os::raw::c_uchar; +pub type __uint16_t = ::std::os::raw::c_ushort; +pub type __uint32_t = ::std::os::raw::c_uint; +pub type StackType_t = u32; +pub type UBaseType_t = ::std::os::raw::c_uint; +pub type TickType_t = u32; #[repr(C)] -#[derive(Debug, Copy, Clone, Default)] +#[derive(Debug, Copy, Clone)] pub struct xLIST_ITEM { pub xItemValue: TickType_t, pub pxNext: xLIST_ITEM_ptr, @@ -49,12 +32,12 @@ pub struct xLIST_ITEM { fn bindgen_test_layout_xLIST_ITEM() { assert_eq!( ::std::mem::size_of::(), - 20usize, + 40usize, concat!("Size of: ", stringify!(xLIST_ITEM)) ); assert_eq!( ::std::mem::align_of::(), - 4usize, + 8usize, concat!("Alignment of ", stringify!(xLIST_ITEM)) ); assert_eq!( @@ -69,7 +52,7 @@ fn bindgen_test_layout_xLIST_ITEM() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pxNext as *const _ as usize }, - 4usize, + 8usize, concat!( "Offset of field: ", stringify!(xLIST_ITEM), @@ -79,7 +62,7 @@ fn bindgen_test_layout_xLIST_ITEM() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pxPrevious as *const _ as usize }, - 8usize, + 16usize, concat!( "Offset of field: ", stringify!(xLIST_ITEM), @@ -89,7 +72,7 @@ fn bindgen_test_layout_xLIST_ITEM() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pvOwner as *const _ as usize }, - 12usize, + 24usize, concat!( "Offset of field: ", stringify!(xLIST_ITEM), @@ -99,7 +82,7 @@ fn bindgen_test_layout_xLIST_ITEM() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pvContainer as *const _ as usize }, - 16usize, + 32usize, concat!( "Offset of field: ", stringify!(xLIST_ITEM), @@ -108,9 +91,7 @@ fn bindgen_test_layout_xLIST_ITEM() { ) ); } -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct ListItem_t(pub xLIST_ITEM); +pub type ListItem_t = xLIST_ITEM; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct xMINI_LIST_ITEM { @@ -122,12 +103,12 @@ pub struct xMINI_LIST_ITEM { fn bindgen_test_layout_xMINI_LIST_ITEM() { assert_eq!( ::std::mem::size_of::(), - 12usize, + 24usize, concat!("Size of: ", stringify!(xMINI_LIST_ITEM)) ); assert_eq!( ::std::mem::align_of::(), - 4usize, + 8usize, concat!("Alignment of ", stringify!(xMINI_LIST_ITEM)) ); assert_eq!( @@ -142,7 +123,7 @@ fn bindgen_test_layout_xMINI_LIST_ITEM() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pxNext as *const _ as usize }, - 4usize, + 8usize, concat!( "Offset of field: ", stringify!(xMINI_LIST_ITEM), @@ -152,7 +133,7 @@ fn bindgen_test_layout_xMINI_LIST_ITEM() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pxPrevious as *const _ as usize }, - 8usize, + 16usize, concat!( "Offset of field: ", stringify!(xMINI_LIST_ITEM), @@ -161,26 +142,24 @@ fn bindgen_test_layout_xMINI_LIST_ITEM() { ) ); } -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct MiniListItem_t(pub xMINI_LIST_ITEM); +pub type MiniListItem_t = xMINI_LIST_ITEM; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct xLIST { pub uxNumberOfItems: UBaseType_t, - pub pxIndex: xLIST_ITEM_ptr, + pub pxIndex: ListItem_t_ptr, pub xListEnd: MiniListItem_t, } #[test] fn bindgen_test_layout_xLIST() { assert_eq!( ::std::mem::size_of::(), - 20usize, + 40usize, concat!("Size of: ", stringify!(xLIST)) ); assert_eq!( ::std::mem::align_of::(), - 4usize, + 8usize, concat!("Alignment of ", stringify!(xLIST)) ); assert_eq!( @@ -195,7 +174,7 @@ fn bindgen_test_layout_xLIST() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pxIndex as *const _ as usize }, - 4usize, + 8usize, concat!( "Offset of field: ", stringify!(xLIST), @@ -205,7 +184,7 @@ fn bindgen_test_layout_xLIST() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).xListEnd as *const _ as usize }, - 8usize, + 16usize, concat!( "Offset of field: ", stringify!(xLIST), @@ -214,12 +193,8 @@ fn bindgen_test_layout_xLIST() { ) ); } -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct List_t(pub xLIST); -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct TaskHandle_t(pub *mut tskTaskControlBlock); +pub type List_t = xLIST; +pub type TaskHandle_t = *mut tskTaskControlBlock; pub const eTaskState_eRunning: eTaskState = 0; pub const eTaskState_eReady: eTaskState = 1; pub const eTaskState_eBlocked: eTaskState = 2; @@ -231,7 +206,7 @@ pub type eTaskState = ::std::os::raw::c_uint; #[derive(Debug, Copy, Clone)] pub struct xTASK_STATUS { pub xHandle: TaskHandle_t, - pub pcTaskName: u32, + pub pcTaskName: char_ptr, pub xTaskNumber: UBaseType_t, pub eCurrentState: eTaskState, pub uxCurrentPriority: UBaseType_t, @@ -244,12 +219,12 @@ pub struct xTASK_STATUS { fn bindgen_test_layout_xTASK_STATUS() { assert_eq!( ::std::mem::size_of::(), - 36usize, + 72usize, concat!("Size of: ", stringify!(xTASK_STATUS)) ); assert_eq!( ::std::mem::align_of::(), - 4usize, + 8usize, concat!("Alignment of ", stringify!(xTASK_STATUS)) ); assert_eq!( @@ -264,7 +239,7 @@ fn bindgen_test_layout_xTASK_STATUS() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pcTaskName as *const _ as usize }, - 4usize, + 8usize, concat!( "Offset of field: ", stringify!(xTASK_STATUS), @@ -274,7 +249,7 @@ fn bindgen_test_layout_xTASK_STATUS() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).xTaskNumber as *const _ as usize }, - 8usize, + 16usize, concat!( "Offset of field: ", stringify!(xTASK_STATUS), @@ -284,7 +259,7 @@ fn bindgen_test_layout_xTASK_STATUS() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).eCurrentState as *const _ as usize }, - 12usize, + 24usize, concat!( "Offset of field: ", stringify!(xTASK_STATUS), @@ -294,7 +269,7 @@ fn bindgen_test_layout_xTASK_STATUS() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).uxCurrentPriority as *const _ as usize }, - 16usize, + 32usize, concat!( "Offset of field: ", stringify!(xTASK_STATUS), @@ -304,7 +279,7 @@ fn bindgen_test_layout_xTASK_STATUS() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).uxBasePriority as *const _ as usize }, - 20usize, + 40usize, concat!( "Offset of field: ", stringify!(xTASK_STATUS), @@ -314,7 +289,7 @@ fn bindgen_test_layout_xTASK_STATUS() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).ulRunTimeCounter as *const _ as usize }, - 24usize, + 48usize, concat!( "Offset of field: ", stringify!(xTASK_STATUS), @@ -324,7 +299,7 @@ fn bindgen_test_layout_xTASK_STATUS() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pxStackBase as *const _ as usize }, - 28usize, + 56usize, concat!( "Offset of field: ", stringify!(xTASK_STATUS), @@ -336,7 +311,7 @@ fn bindgen_test_layout_xTASK_STATUS() { unsafe { &(*(::std::ptr::null::())).usStackHighWaterMark as *const _ as usize }, - 32usize, + 64usize, concat!( "Offset of field: ", stringify!(xTASK_STATUS), @@ -345,9 +320,7 @@ fn bindgen_test_layout_xTASK_STATUS() { ) ); } -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct TaskStatus_t(pub xTASK_STATUS); +pub type TaskStatus_t = xTASK_STATUS; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct tskTaskControlBlock { @@ -368,12 +341,12 @@ pub struct tskTaskControlBlock { fn bindgen_test_layout_tskTaskControlBlock() { assert_eq!( ::std::mem::size_of::(), - 80usize, + 152usize, concat!("Size of: ", stringify!(tskTaskControlBlock)) ); assert_eq!( ::std::mem::align_of::(), - 4usize, + 8usize, concat!("Alignment of ", stringify!(tskTaskControlBlock)) ); assert_eq!( @@ -392,7 +365,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { unsafe { &(*(::std::ptr::null::())).xStateListItem as *const _ as usize }, - 4usize, + 8usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -404,7 +377,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { unsafe { &(*(::std::ptr::null::())).xEventListItem as *const _ as usize }, - 24usize, + 48usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -414,7 +387,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).uxPriority as *const _ as usize }, - 44usize, + 88usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -424,7 +397,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pxStack as *const _ as usize }, - 48usize, + 96usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -434,7 +407,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { ); assert_eq!( unsafe { &(*(::std::ptr::null::())).pcTaskName as *const _ as usize }, - 52usize, + 104usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -446,7 +419,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { unsafe { &(*(::std::ptr::null::())).uxBasePriority as *const _ as usize }, - 64usize, + 120usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -458,7 +431,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { unsafe { &(*(::std::ptr::null::())).uxMutexesHeld as *const _ as usize }, - 68usize, + 128usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -470,7 +443,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { unsafe { &(*(::std::ptr::null::())).ulNotifiedValue as *const _ as usize }, - 72usize, + 136usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -482,7 +455,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { unsafe { &(*(::std::ptr::null::())).ucNotifyState as *const _ as usize }, - 76usize, + 144usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -495,7 +468,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { &(*(::std::ptr::null::())).ucStaticallyAllocated as *const _ as usize }, - 77usize, + 145usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -507,7 +480,7 @@ fn bindgen_test_layout_tskTaskControlBlock() { unsafe { &(*(::std::ptr::null::())).ucDelayAborted as *const _ as usize }, - 78usize, + 146usize, concat!( "Offset of field: ", stringify!(tskTaskControlBlock), @@ -516,9 +489,49 @@ fn bindgen_test_layout_tskTaskControlBlock() { ) ); } -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct tskTCB(pub tskTaskControlBlock); -#[repr(transparent)] -#[derive(Debug, Copy, Clone)] -pub struct TCB_t(pub tskTCB); +pub type tskTCB = tskTaskControlBlock; +pub type TCB_t = tskTCB; + +pub trait emu_lookup { + fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> Self; +} + +impl emu_lookup for xLIST_ITEM { + fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> xLIST_ITEM { + let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; + unsafe { + emu.read_mem(addr.into(), &mut tmp); + std::mem::transmute::<[u8; std::mem::size_of::()], xLIST_ITEM>(tmp) + } + } +} + +impl emu_lookup for TCB_t { + fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> TCB_t { + let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; + unsafe { + emu.read_mem(addr.into(), &mut tmp); + std::mem::transmute::<[u8; std::mem::size_of::()], TCB_t>(tmp) + } + } +} + +impl emu_lookup for void_ptr { + fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> void_ptr { + let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; + unsafe { + emu.read_mem(addr.into(), &mut tmp); + std::mem::transmute::<[u8; std::mem::size_of::()], void_ptr>(tmp) + } + } +} + +impl emu_lookup for List_t { + fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> List_t { + let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; + unsafe { + emu.read_mem(addr.into(), &mut tmp); + std::mem::transmute::<[u8; std::mem::size_of::()], List_t>(tmp) + } + } +} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index ad34f75bc8..6f481ae659 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -203,11 +203,11 @@ fn fuzz( .expect("Symbol pxCurrentTCBC not found"); // let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf.goblin()); println!("TCB pointer at {:#x}", curr_tcb_pointer); - // let task_queue_addr = elf - // .resolve_symbol("pxReadyTasksLists", 0) - // .expect("Symbol pxReadyTasksLists not found"); - // // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); - // println!("Task Queue at {:#x}", task_queue_addr); + let task_queue_addr = elf + .resolve_symbol("pxReadyTasksLists", 0) + .expect("Symbol pxReadyTasksLists not found"); + // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); + println!("Task Queue at {:#x}", task_queue_addr); @@ -269,42 +269,17 @@ fn fuzz( } unsafe { - // let mut addr_buf : [u8; 4] = [0u8; 4]; - // emu.read_mem(test_length_ptr,&mut addr_buf); - // println!("current len: {}",u32::from_le_bytes(addr_buf)); - //==end test emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); - //== next test - // emu.read_mem(test_length_ptr,&mut addr_buf); - // println!("new len: {}, expected: {}",u32::from_le_bytes(addr_buf),len); - // println!("{:#?}",edges_copy); emu.run(); - // println!("{:#?}",edges_copy); - // let mut buf_struct : [u8; 5*std::mem::size_of::()] = [1u8; 5*std::mem::size_of::()]; - // emu.read_mem(task_queue_addr, &mut buf_struct); - // let mut prio_lists : Vec = Vec::new(); - // for i in 0..5 { - // let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; - // emu.read_mem(task_queue_addr+i*40, &mut tmp); - // let list_struct = std::mem::transmute::<[u8; std::mem::size_of::()], freertos::List_t>(tmp); - // prio_lists.push(list_struct); - // } - // println!("Raw Buffer: {:?}",buf_struct); - // println!("Lists: {:?}",prio_lists); //====== experiment inspecting the current tcb - let mut curr_tcb_addr : [u8; 4] = [1u8; 4]; - emu.read_mem(curr_tcb_pointer,&mut curr_tcb_addr); - let curr_tcb_addr = u32::from_le_bytes(curr_tcb_addr); - println!("tcb addr: {:x}",curr_tcb_addr); - - let mut tmp : [u8; std::mem::size_of::()] = [1u8; std::mem::size_of::()]; - emu.read_mem(curr_tcb_addr.into(), &mut tmp); - println!("Raw TCB: {:?}",tmp); - let tcb = std::mem::transmute::<[u8; std::mem::size_of::()], freertos::TCB_t>(tmp); - println!("TCB: {:?}",tcb); - + let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(&emu, curr_tcb_pointer.try_into().unwrap()); + println!("Current TCB addr: {:x}",curr_tcb_addr); + let current_tcb : freertos::TCB_t = freertos::emu_lookup::lookup(&emu,curr_tcb_addr); + println!("Current TCB: {:?}",current_tcb); + let ready_queue : freertos::List_t = freertos::emu_lookup::lookup(&emu,task_queue_addr.try_into().unwrap()); + println!("Ready Queue: {:?}",ready_queue); } ExitKind::Ok From 29f89b4b299898d428c99e85aa40930e0c11fd29 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 23 Jan 2022 16:15:14 +0100 Subject: [PATCH 10/83] disable syscall hooks in systemmode --- libafl_qemu/src/asan.rs | 1 + libafl_qemu/src/emu.rs | 4 ++++ libafl_qemu/src/executor.rs | 2 ++ 3 files changed, 7 insertions(+) diff --git a/libafl_qemu/src/asan.rs b/libafl_qemu/src/asan.rs index 8e91798d5c..ee8e089e48 100644 --- a/libafl_qemu/src/asan.rs +++ b/libafl_qemu/src/asan.rs @@ -432,6 +432,7 @@ where executor.hook_write1_execution(trace_write1_asan::); executor.hook_write_n_execution(trace_write_n_asan::); + #[cfg(not(feature = "systemmode"))] executor.hook_syscalls(qasan_fake_syscall::); } diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 85cafc43fb..04e7248056 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -242,8 +242,10 @@ extern "C" { static mut libafl_exec_cmp_hook8: unsafe extern "C" fn(u64, u64, u64); static mut libafl_gen_cmp_hook: unsafe extern "C" fn(u64, u32) -> u64; + #[cfg(not(feature = "systemmode"))] static mut libafl_pre_syscall_hook: unsafe extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult; + #[cfg(not(feature = "systemmode"))] static mut libafl_post_syscall_hook: unsafe extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64; } @@ -638,6 +640,7 @@ impl Emulator { } } + #[cfg(not(feature = "systemmode"))] pub fn set_pre_syscall_hook( &self, hook: extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult, @@ -647,6 +650,7 @@ impl Emulator { } } + #[cfg(not(feature = "systemmode"))] pub fn set_post_syscall_hook( &self, hook: extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64, diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 8805ce15ba..2c782bdb2f 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -733,6 +733,7 @@ where #[allow(clippy::unused_self)] #[allow(clippy::type_complexity)] + #[cfg(not(feature = "systemmode"))] pub fn hook_syscalls( &self, hook: fn( @@ -759,6 +760,7 @@ where #[allow(clippy::unused_self)] #[allow(clippy::type_complexity)] + #[cfg(not(feature = "systemmode"))] pub fn hook_after_syscalls( &self, hook: fn( From 44a32398d91eddb0640e39b48d6ad2525053cc82 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 23 Jan 2022 22:23:14 +0100 Subject: [PATCH 11/83] proof of concept syscall-hook tcb tracer --- fuzzers/wcet_qemu_sys/.gitignore | 5 + fuzzers/wcet_qemu_sys/src/freertos.rs | 15 +-- fuzzers/wcet_qemu_sys/src/main.rs | 1 + fuzzers/wcet_qemu_sys/src/showmap.rs | 42 +++++---- fuzzers/wcet_qemu_sys/src/system_trace.rs | 110 ++++++++++++++++++++++ 5 files changed, 148 insertions(+), 25 deletions(-) create mode 100644 fuzzers/wcet_qemu_sys/.gitignore create mode 100644 fuzzers/wcet_qemu_sys/src/system_trace.rs diff --git a/fuzzers/wcet_qemu_sys/.gitignore b/fuzzers/wcet_qemu_sys/.gitignore new file mode 100644 index 0000000000..456d0151b4 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/.gitignore @@ -0,0 +1,5 @@ +*.axf +*.qcow2 +demo +*.ron +*.bsp diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/freertos.rs index 56c0dc4168..f26695011d 100644 --- a/fuzzers/wcet_qemu_sys/src/freertos.rs +++ b/fuzzers/wcet_qemu_sys/src/freertos.rs @@ -1,3 +1,4 @@ +use serde::{Deserialize, Serialize}; // Manual Types use libafl_qemu::Emulator; @@ -20,7 +21,7 @@ pub type StackType_t = u32; pub type UBaseType_t = ::std::os::raw::c_uint; pub type TickType_t = u32; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] pub struct xLIST_ITEM { pub xItemValue: TickType_t, pub pxNext: xLIST_ITEM_ptr, @@ -93,7 +94,7 @@ fn bindgen_test_layout_xLIST_ITEM() { } pub type ListItem_t = xLIST_ITEM; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] pub struct xMINI_LIST_ITEM { pub xItemValue: TickType_t, pub pxNext: xLIST_ITEM_ptr, @@ -144,7 +145,7 @@ fn bindgen_test_layout_xMINI_LIST_ITEM() { } pub type MiniListItem_t = xMINI_LIST_ITEM; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default)] pub struct xLIST { pub uxNumberOfItems: UBaseType_t, pub pxIndex: ListItem_t_ptr, @@ -194,7 +195,7 @@ fn bindgen_test_layout_xLIST() { ); } pub type List_t = xLIST; -pub type TaskHandle_t = *mut tskTaskControlBlock; +pub type TaskHandle_t = ::std::os::raw::c_uint; pub const eTaskState_eRunning: eTaskState = 0; pub const eTaskState_eReady: eTaskState = 1; pub const eTaskState_eBlocked: eTaskState = 2; @@ -203,7 +204,7 @@ pub const eTaskState_eDeleted: eTaskState = 4; pub const eTaskState_eInvalid: eTaskState = 5; pub type eTaskState = ::std::os::raw::c_uint; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct xTASK_STATUS { pub xHandle: TaskHandle_t, pub pcTaskName: char_ptr, @@ -322,14 +323,14 @@ fn bindgen_test_layout_xTASK_STATUS() { } pub type TaskStatus_t = xTASK_STATUS; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] pub struct tskTaskControlBlock { pub pxTopOfStack: StackType_t_ptr, pub xStateListItem: ListItem_t, pub xEventListItem: ListItem_t, pub uxPriority: UBaseType_t, pub pxStack: StackType_t_ptr, - pub pcTaskName: [::std::os::raw::c_char; 10usize], + pub pcTaskName: [::std::os::raw::c_uchar; 10usize], pub uxBasePriority: UBaseType_t, pub uxMutexesHeld: UBaseType_t, pub ulNotifiedValue: [u32; 1usize], diff --git a/fuzzers/wcet_qemu_sys/src/main.rs b/fuzzers/wcet_qemu_sys/src/main.rs index 0440b6238c..cca2809009 100644 --- a/fuzzers/wcet_qemu_sys/src/main.rs +++ b/fuzzers/wcet_qemu_sys/src/main.rs @@ -3,6 +3,7 @@ pub mod fuzzer; pub mod showmap; pub mod worst; pub mod freertos; +pub mod system_trace; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index 6f481ae659..f9f9ef5755 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use crate::system_trace::QemuSystemStateHelper; use libafl::feedbacks::CrashFeedback; use std::path::Path; use libafl_qemu::QemuExecutor; @@ -208,6 +209,21 @@ fn fuzz( .expect("Symbol pxReadyTasksLists not found"); // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); println!("Task Queue at {:#x}", task_queue_addr); + let shv = elf + .resolve_symbol("xPortSysTickHandler", 0) + .expect("Symbol xPortSysTickHandler not found"); + let shv = virt2phys(shv,&elf.goblin()); + println!("SysTick at {:#x}", shv); + let shv = elf + .resolve_symbol("vPortSVCHandler", 0) + .expect("Symbol vPortSVCHandler not found"); + let shv = virt2phys(shv,&elf.goblin()); + println!("SVChandle at {:#x}", shv); + let shv = elf + .resolve_symbol("xPortPendSVHandler", 0) + .expect("Symbol xPortPendSVHandler not found"); + let shv = virt2phys(shv,&elf.goblin()); + println!("PendHandle at {:#x}", shv); @@ -274,12 +290,12 @@ fn fuzz( emu.run(); //====== experiment inspecting the current tcb - let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(&emu, curr_tcb_pointer.try_into().unwrap()); - println!("Current TCB addr: {:x}",curr_tcb_addr); - let current_tcb : freertos::TCB_t = freertos::emu_lookup::lookup(&emu,curr_tcb_addr); - println!("Current TCB: {:?}",current_tcb); - let ready_queue : freertos::List_t = freertos::emu_lookup::lookup(&emu,task_queue_addr.try_into().unwrap()); - println!("Ready Queue: {:?}",ready_queue); + // let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(&emu, curr_tcb_pointer.try_into().unwrap()); + // println!("Current TCB addr: {:x}",curr_tcb_addr); + // let current_tcb : freertos::TCB_t = freertos::emu_lookup::lookup(&emu,curr_tcb_addr); + // println!("Current TCB: {:?}",current_tcb); + // let ready_queue : freertos::List_t = freertos::emu_lookup::lookup(&emu,task_queue_addr.try_into().unwrap()); + // println!("Ready Queue: {:?}",ready_queue); } ExitKind::Ok @@ -293,7 +309,8 @@ fn fuzz( QemuEdgeCoverageHelper::new(), // QemuCmpLogHelper::new(), // QemuAsanHelper::new(), - QemuSysSnapshotHelper::new() + QemuSysSnapshotHelper::new(), + QemuSystemStateHelper::new() ), tuple_list!(edges_observer), &mut fuzzer, @@ -305,17 +322,6 @@ fn fuzz( false => seed_dir.clone() }; fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, Input::from_file(&firstinput).expect("Could not load file")).expect("Evaluation failed"); - // fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, Input::from_file(&firstinput).expect("Could not load file")).expect("Evaluation failed"); - // let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); - // let mut stages = tuple_list!(StdMutationalStage::new(mutator)); - // if state.corpus().count() < 1 { - // state - // .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) - // .expect("Failed to load initial corpus"); - // println!("We imported {} inputs from disk.", state.corpus().count()); - // } - // fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) - // .expect("Error in the fuzzing loop"); return Ok(()); } diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs new file mode 100644 index 0000000000..823a6acecf --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/system_trace.rs @@ -0,0 +1,110 @@ +use crate::freertos; +use hashbrown::HashMap; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; +use serde::{Deserialize, Serialize}; + +use libafl_qemu::{ + emu::Emulator, + executor::QemuExecutor, + helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, +}; + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct QemuSystemStateMetadata { + pub tcbs: Vec, + // pub map: HashMap, + // pub current_id: u64, +} + +impl QemuSystemStateMetadata { + #[must_use] + pub fn new() -> Self { + Self { + tcbs: Vec::new(), + // map: HashMap::new(), + // current_id: 0, + } + } +} + +libafl::impl_serdeany!(QemuSystemStateMetadata); + +#[derive(Debug)] +pub struct QemuSystemStateHelper { + filter: QemuInstrumentationFilter, +} + +impl QemuSystemStateHelper { + #[must_use] + pub fn new() -> Self { + Self { + filter: QemuInstrumentationFilter::None, + } + } + + #[must_use] + pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self { + Self { filter } + } + + #[must_use] + pub fn must_instrument(&self, addr: u64) -> bool { + self.filter.allowed(addr) + } +} + +impl Default for QemuSystemStateHelper { + fn default() -> Self { + Self::new() + } +} + +impl QemuHelper for QemuSystemStateHelper +where + I: Input, + S: HasMetadata, +{ + fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) + where + H: FnMut(&I) -> ExitKind, + OT: ObserversTuple, + QT: QemuHelperTuple, + { + executor.hook_block_execution(exec_syscall_hook::); + } +} + +pub fn exec_syscall_hook( + emulator: &Emulator, + helpers: &mut QT, + state: &mut S, + pc: u64, +) +where + S: HasMetadata, + I: Input, + QT: QemuHelperTuple, +{ + // if pc == 0x2e8 { //vPortSVCHandler + // if pc == 0x3c4 { //SystemTick + if pc == 0x37c { //xPortPendSVHandler + if let Some(h) = helpers.match_first_type::() { + if !h.must_instrument(pc) { + return; + } + } + if state.metadata().get::().is_none() { + state.add_metadata(QemuSystemStateMetadata::new()); + } + let meta = state + .metadata_mut() + .get_mut::() + .unwrap(); + let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, 0x20006ff0.try_into().unwrap()); + // println!("Current TCB addr: {:x}",curr_tcb_addr); + let current_tcb : freertos::TCB_t = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); + println!("{}", std::str::from_utf8(¤t_tcb.pcTaskName).unwrap()); + meta.tcbs.push(current_tcb); + let id = meta.tcbs.len(); + } +} From 8676342776672c77ef7c4aabc489b1137192353c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 26 Jan 2022 23:14:38 +0100 Subject: [PATCH 12/83] generalize system state hook --- fuzzers/wcet_qemu_sys/src/freertos.rs | 8 ++- fuzzers/wcet_qemu_sys/src/showmap.rs | 23 ++++---- fuzzers/wcet_qemu_sys/src/system_trace.rs | 70 +++++++++++------------ 3 files changed, 54 insertions(+), 47 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/freertos.rs index f26695011d..e176b43ea6 100644 --- a/fuzzers/wcet_qemu_sys/src/freertos.rs +++ b/fuzzers/wcet_qemu_sys/src/freertos.rs @@ -145,7 +145,7 @@ fn bindgen_test_layout_xMINI_LIST_ITEM() { } pub type MiniListItem_t = xMINI_LIST_ITEM; #[repr(C)] -#[derive(Debug, Copy, Clone, Default)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] pub struct xLIST { pub uxNumberOfItems: UBaseType_t, pub pxIndex: ListItem_t_ptr, @@ -535,4 +535,10 @@ impl emu_lookup for List_t { std::mem::transmute::<[u8; std::mem::size_of::()], List_t>(tmp) } } +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub enum rtos_struct { + TCB_struct(TCB_t), + List_struct(List_t), } \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index f9f9ef5755..d77756ccc3 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use libafl_qemu::QemuInstrumentationFilter; use crate::system_trace::QemuSystemStateHelper; use libafl::feedbacks::CrashFeedback; use std::path::Path; @@ -209,21 +210,21 @@ fn fuzz( .expect("Symbol pxReadyTasksLists not found"); // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); println!("Task Queue at {:#x}", task_queue_addr); - let shv = elf + let systick_handler = elf .resolve_symbol("xPortSysTickHandler", 0) .expect("Symbol xPortSysTickHandler not found"); - let shv = virt2phys(shv,&elf.goblin()); - println!("SysTick at {:#x}", shv); - let shv = elf + let systick_handler = virt2phys(systick_handler,&elf.goblin()); + println!("SysTick at {:#x}", systick_handler); + let svc_handle = elf .resolve_symbol("vPortSVCHandler", 0) .expect("Symbol vPortSVCHandler not found"); - let shv = virt2phys(shv,&elf.goblin()); - println!("SVChandle at {:#x}", shv); - let shv = elf + let svc_handle = virt2phys(svc_handle,&elf.goblin()); + println!("SVChandle at {:#x}", svc_handle); + let svh = elf .resolve_symbol("xPortPendSVHandler", 0) .expect("Symbol xPortPendSVHandler not found"); - let shv = virt2phys(shv,&elf.goblin()); - println!("PendHandle at {:#x}", shv); + let svh = virt2phys(svh,&elf.goblin()); + println!("PendHandle at {:#x}", svh); @@ -300,6 +301,8 @@ fn fuzz( ExitKind::Ok }; + //======= Set System-State watchpoints + let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1]); //======= Construct the executor, including the Helpers. The edges_observer still contains the ref to EDGES_MAP let mut executor = QemuExecutor::new( @@ -310,7 +313,7 @@ fn fuzz( // QemuCmpLogHelper::new(), // QemuAsanHelper::new(), QemuSysSnapshotHelper::new(), - QemuSystemStateHelper::new() + QemuSystemStateHelper::with_instrumentation_filter(system_state_filter,curr_tcb_pointer.try_into().unwrap(),task_queue_addr.try_into().unwrap()) ), tuple_list!(edges_observer), &mut fuzzer, diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs index 823a6acecf..cd7b2106cf 100644 --- a/fuzzers/wcet_qemu_sys/src/system_trace.rs +++ b/fuzzers/wcet_qemu_sys/src/system_trace.rs @@ -1,3 +1,4 @@ +use crate::freertos::rtos_struct::*; use crate::freertos; use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; @@ -11,18 +12,14 @@ use libafl_qemu::{ #[derive(Debug, Default, Serialize, Deserialize)] pub struct QemuSystemStateMetadata { - pub tcbs: Vec, - // pub map: HashMap, - // pub current_id: u64, + pub rtos_states: Vec<(freertos::TCB_t,HashMap)>, } impl QemuSystemStateMetadata { #[must_use] pub fn new() -> Self { Self { - tcbs: Vec::new(), - // map: HashMap::new(), - // current_id: 0, + rtos_states: Vec::new(), } } } @@ -32,19 +29,23 @@ libafl::impl_serdeany!(QemuSystemStateMetadata); #[derive(Debug)] pub struct QemuSystemStateHelper { filter: QemuInstrumentationFilter, + tcb_addr: u32, + ready_queues: u32, } impl QemuSystemStateHelper { #[must_use] - pub fn new() -> Self { + pub fn new(tcb_addr: u32, ready_queues: u32) -> Self { Self { filter: QemuInstrumentationFilter::None, + tcb_addr: tcb_addr, + ready_queues: ready_queues, } } #[must_use] - pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self { - Self { filter } + pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter, tcb_addr: u32, ready_queues: u32) -> Self { + Self { filter, tcb_addr, ready_queues} } #[must_use] @@ -53,12 +54,6 @@ impl QemuSystemStateHelper { } } -impl Default for QemuSystemStateHelper { - fn default() -> Self { - Self::new() - } -} - impl QemuHelper for QemuSystemStateHelper where I: Input, @@ -85,26 +80,29 @@ where I: Input, QT: QemuHelperTuple, { - // if pc == 0x2e8 { //vPortSVCHandler - // if pc == 0x3c4 { //SystemTick - if pc == 0x37c { //xPortPendSVHandler - if let Some(h) = helpers.match_first_type::() { - if !h.must_instrument(pc) { - return; - } - } - if state.metadata().get::().is_none() { - state.add_metadata(QemuSystemStateMetadata::new()); - } - let meta = state - .metadata_mut() - .get_mut::() - .unwrap(); - let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, 0x20006ff0.try_into().unwrap()); - // println!("Current TCB addr: {:x}",curr_tcb_addr); - let current_tcb : freertos::TCB_t = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); - println!("{}", std::str::from_utf8(¤t_tcb.pcTaskName).unwrap()); - meta.tcbs.push(current_tcb); - let id = meta.tcbs.len(); + let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + if !h.must_instrument(pc) { + return; } + if state.metadata().get::().is_none() { + state.add_metadata(QemuSystemStateMetadata::new()); + } + let meta = state + .metadata_mut() + .get_mut::() + .unwrap(); + let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); + // println!("Current TCB addr: {:x}",curr_tcb_addr); + let current_tcb : freertos::TCB_t = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); + println!("{:?}",current_tcb); + println!("{}", std::str::from_utf8(¤t_tcb.pcTaskName).unwrap()); + let mut result_tup : (freertos::TCB_t,HashMap) = (current_tcb, HashMap::with_capacity(5)); + for i in 0..4 { + let target : u32= (std::mem::size_of::()*i).try_into().unwrap(); + let ready_list : freertos::List_t = freertos::emu_lookup::lookup(emulator, h.ready_queues+target); + let a : freertos::rtos_struct = List_struct(ready_list); + // println!("{:?}",ready_list); + // println!("Prio: {} Number: {} first {:x}",i,ready_list.uxNumberOfItems, ready_list.pxIndex); + } + meta.rtos_states.push(result_tup); } From 8a82ecac9d05aafd731e9393588739e3411ec5e3 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 26 Jan 2022 23:17:12 +0100 Subject: [PATCH 13/83] fix missing insert --- fuzzers/wcet_qemu_sys/src/system_trace.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs index cd7b2106cf..8b0c4860c8 100644 --- a/fuzzers/wcet_qemu_sys/src/system_trace.rs +++ b/fuzzers/wcet_qemu_sys/src/system_trace.rs @@ -101,6 +101,7 @@ where let target : u32= (std::mem::size_of::()*i).try_into().unwrap(); let ready_list : freertos::List_t = freertos::emu_lookup::lookup(emulator, h.ready_queues+target); let a : freertos::rtos_struct = List_struct(ready_list); + result_tup.1.insert(target,a); // println!("{:?}",ready_list); // println!("Prio: {} Number: {} first {:x}",i,ready_list.uxNumberOfItems, ready_list.pxIndex); } From ba85877ab43da2510b404cade2f2ab3adba6f7af Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 28 Jan 2022 11:35:05 +0100 Subject: [PATCH 14/83] fix elf path --- fuzzers/wcet_qemu_sys/src/showmap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index d77756ccc3..13153a9e0d 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -182,7 +182,7 @@ fn fuzz( let emu = Emulator::new(&mut args, &mut env); //=========== Analyze the binary to find the target function address let mut elf_buffer = Vec::new(); - let bin_path=Path::new("./RTOSDemo.axf"); + let bin_path=kernel; let elf = EasyElf::from_file(bin_path, &mut elf_buffer)?; let test_one_input_ptr = elf From b3b8e8119070e1aa52da0c574f6dc754050fcaf2 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 31 Jan 2022 19:39:22 +0100 Subject: [PATCH 15/83] add QemuClockObserver --- fuzzers/wcet_qemu_sys/src/fuzzer.rs | 1 + fuzzers/wcet_qemu_sys/src/showmap.rs | 10 +- libafl_qemu/src/clock.rs | 141 +++++++++++++++++++++++++++ libafl_qemu/src/emu.rs | 7 ++ libafl_qemu/src/lib.rs | 4 + 5 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 libafl_qemu/src/clock.rs diff --git a/fuzzers/wcet_qemu_sys/src/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/fuzzer.rs index a6b95f8fde..f175e02894 100644 --- a/fuzzers/wcet_qemu_sys/src/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/fuzzer.rs @@ -219,6 +219,7 @@ fn fuzz( "-kernel", kernel.to_str().unwrap(), "-serial", "stdio", "-nographic", "-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2", + "-icount", "shift=auto,align=off,sleep=off", "-S" ].iter().map(|x| x.to_string()).collect(); let env: Vec<(String, String)> = env::vars().collect(); diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index 13153a9e0d..06ed342c7d 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -43,6 +43,8 @@ use libafl_qemu::{ emu::Emulator, filter_qemu_args, snapshot_sys::QemuSysSnapshotHelper, elf::EasyElf, + clock, + clock::{QemuClockObserver,QemuClockIncreaseFeedback}, }; use crate::freertos; @@ -177,6 +179,7 @@ fn fuzz( "-kernel", kernel.to_str().unwrap(), "-serial", "stdio", "-nographic", "-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2", + "-icount", "shift=auto,align=off,sleep=off", "-S" ].iter().map(|x| x.to_string()).collect(); let emu = Emulator::new(&mut args, &mut env); @@ -248,11 +251,14 @@ fn fuzz( let edges_observer = HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); + //========== Observe Execution Cycles + let clock_observer = QemuClockObserver::default(); + //========= Feedback-Function evaluate the Maps. Need to dump it for debugging and check if it reaches targets. let feedback = DumpMapFeedback::with_dump(dump_edges); // A feedback to choose if an input is a solution or not - let objective = CrashFeedback::new(); + let objective = QemuClockIncreaseFeedback::new(); // create a State from scratch let mut state = StdState::new( @@ -315,7 +321,7 @@ fn fuzz( QemuSysSnapshotHelper::new(), QemuSystemStateHelper::with_instrumentation_filter(system_state_filter,curr_tcb_pointer.try_into().unwrap(),task_queue_addr.try_into().unwrap()) ), - tuple_list!(edges_observer), + tuple_list!(edges_observer,clock_observer), &mut fuzzer, &mut state, &mut mgr, diff --git a/libafl_qemu/src/clock.rs b/libafl_qemu/src/clock.rs new file mode 100644 index 0000000000..66a316f1e9 --- /dev/null +++ b/libafl_qemu/src/clock.rs @@ -0,0 +1,141 @@ +use hashbrown::{hash_map::Entry, HashMap}; +use libafl::{ + bolts::{ + current_nanos, + rands::StdRand, + tuples::{tuple_list}, + }, + corpus::{QueueCorpusScheduler}, + executors::{ExitKind}, + fuzzer::{StdFuzzer}, + inputs::{BytesInput, HasTargetBytes}, + observers::{Observer,VariableMapObserver}, + state::{StdState}, + Error, + observers::ObserversTuple, +}; +use serde::{Deserialize, Serialize}; +use std::{cell::UnsafeCell, cmp::max}; +use libafl::bolts::tuples::Named; + +use crate::{ + emu, + emu::Emulator, + executor::QemuExecutor, + helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, +}; +use libafl::events::EventFirer; +use libafl::state::HasClientPerfMonitor; +use libafl::inputs::Input; +use libafl::feedbacks::Feedback; + +//========== Observer + +/// A simple observer, just overlooking the runtime of the target. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct QemuClockObserver { + name: String, + last_runtime: i64, +} + +impl QemuClockObserver { + /// Creates a new [`QemuClockObserver`] with the given name. + #[must_use] + pub fn new(name: &'static str) -> Self { + Self { + name: name.to_string(), + last_runtime: 0, + } + } + + /// Gets the runtime for the last execution of this target. + #[must_use] + pub fn last_runtime(&self) -> i64 { + self.last_runtime + } +} + +impl Observer for QemuClockObserver { + fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.last_runtime=0; + Ok(()) + } + + fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + unsafe { self.last_runtime = emu::libafl_get_clock() }; + println!("Observer Clock: {}",self.last_runtime()); + Ok(()) + } +} + +impl Named for QemuClockObserver { + #[inline] + fn name(&self) -> &str { + &self.name + } +} + +impl Default for QemuClockObserver { + fn default() -> Self { + Self { + name: String::from("clock"), + last_runtime: 0, + } + } +} + +//========== Observer + +/// A [`Feedback`] rewarding increasing the execution cycles on Qemu. +#[derive(Debug)] +pub struct QemuClockIncreaseFeedback { + maximum: i64 +} + +impl Feedback for QemuClockIncreaseFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = _observers.match_name::("clock") + .expect("QemuClockObserver not found"); + if observer.last_runtime() >= self.maximum { + self.maximum = observer.last_runtime(); + return Ok(true); + } + Ok(false) + } +} + +impl Named for QemuClockIncreaseFeedback { + #[inline] + fn name(&self) -> &str { + "QemuClockFeedback" + } +} + +impl QemuClockIncreaseFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new() -> Self { + Self {maximum: 0} + } +} + +impl Default for QemuClockIncreaseFeedback { + fn default() -> Self { + Self::new() + } +} \ No newline at end of file diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 04e7248056..532766f483 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -199,6 +199,8 @@ extern "C" { fn libafl_snapshot_save(name: *const c_char) -> i32; #[cfg(feature = "systemmode")] fn libafl_snapshot_load(name: *const c_char) -> i32; + #[cfg(feature = "systemmode")] + pub fn libafl_get_clock() -> i64; fn strlen(s: *const u8) -> usize; @@ -678,6 +680,11 @@ impl Emulator { _ => true, } } + + #[cfg(feature = "systemmode")] + pub fn get_ticks(&self) -> i64{ + return unsafe { libafl_get_clock() }; + } } #[cfg(feature = "python")] diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index e7bbc3aaf3..66d7ca71db 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -43,6 +43,10 @@ pub use snapshot::QemuSnapshotHelper; pub mod snapshot_sys; #[cfg(all(target_os = "linux",feature = "systemmode"))] pub use snapshot_sys::QemuSysSnapshotHelper; +#[cfg(all(target_os = "linux",feature = "systemmode"))] +pub mod clock; +#[cfg(all(target_os = "linux",feature = "systemmode"))] +pub use clock::QemuClockObserver; #[cfg(target_os = "linux")] pub mod asan; #[cfg(target_os = "linux")] From 9a1251875fab406d2cfe4a6d77f51c15d993dae4 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 3 Feb 2022 21:19:17 +0100 Subject: [PATCH 16/83] small improvements --- fuzzers/wcet_qemu_sys/src/fuzzer.rs | 6 +- fuzzers/wcet_qemu_sys/src/main.rs | 1 + fuzzers/wcet_qemu_sys/src/showmap.rs | 92 +++++++-------------- fuzzers/wcet_qemu_sys/src/worst.rs | 118 ++++++++++++++++++++++++++- libafl_qemu/src/clock.rs | 45 ++++++++-- 5 files changed, 191 insertions(+), 71 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/fuzzer.rs index f175e02894..9e89c3535a 100644 --- a/fuzzers/wcet_qemu_sys/src/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/fuzzer.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use libafl_qemu::QemuClockObserver; use libafl::feedbacks::Feedback; use crate::worst::HitImprovingFeedback; use crate::worst::HitFeedback; @@ -152,7 +153,7 @@ pub fn main() { } } let mut crashes = out_dir.clone(); - crashes.push("crashes"); + crashes.push("wcets"); out_dir.push("queue"); let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string()); @@ -292,6 +293,7 @@ fn fuzz( // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); + let clock_observer = QemuClockObserver::default(); // Create an observation channel using cmplog map let cmplog_observer = CmpLogObserver::new("cmplog", unsafe { &mut cmplog::CMPLOG_MAP }, true); @@ -387,7 +389,7 @@ fn fuzz( //QemuAsanHelper::new(), QemuSysSnapshotHelper::new() ), - tuple_list!(edges_observer, time_observer), + tuple_list!(edges_observer, time_observer, clock_observer), &mut fuzzer, &mut state, &mut mgr, diff --git a/fuzzers/wcet_qemu_sys/src/main.rs b/fuzzers/wcet_qemu_sys/src/main.rs index cca2809009..6b319d5ac9 100644 --- a/fuzzers/wcet_qemu_sys/src/main.rs +++ b/fuzzers/wcet_qemu_sys/src/main.rs @@ -1,3 +1,4 @@ +#![feature(iter_advance_by)] #[cfg(target_os = "linux")] pub mod fuzzer; pub mod showmap; diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index 06ed342c7d..22535170c7 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -1,5 +1,12 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use crate::worst::DumpMapFeedback; +use crate::worst::DummyFeedback; +use libafl::corpus::Corpus; +use libafl::state::HasCorpus; +use libafl::Fuzzer; +use libafl::mutators::BitFlipMutator; +use libafl::stages::StdMutationalStage; use libafl_qemu::QemuInstrumentationFilter; use crate::system_trace::QemuSystemStateHelper; use libafl::feedbacks::CrashFeedback; @@ -258,7 +265,7 @@ fn fuzz( let feedback = DumpMapFeedback::with_dump(dump_edges); // A feedback to choose if an input is a solution or not - let objective = QemuClockIncreaseFeedback::new(); + let objective = DummyFeedback::new(false); // create a State from scratch let mut state = StdState::new( @@ -330,67 +337,28 @@ fn fuzz( true => seed_dir.clone().read_dir().expect("Directory not a directory?").next().expect("Directory empty?").expect("File not in directory?").path(), false => seed_dir.clone() }; + // let secondinput = match seed_dir.clone().is_dir() { + // true => { + // let mut a = seed_dir.clone().read_dir().expect("Directory not a directory?"); + // a.advance_by(1); + // a.next().unwrap().expect("File not in directory?").path() + // }, + // false => seed_dir.clone() + // }; fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, Input::from_file(&firstinput).expect("Could not load file")).expect("Evaluation failed"); + // fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, Input::from_file(&secondinput).expect("Could not load file")).expect("Evaluation failed"); + // println!("Nach Eval"); + // if state.corpus().count() < 1 { + // state + // .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) + // .unwrap_or_else(|_| { + // println!("Failed to load initial corpus at {:?}", &seed_dir); + // return; + // }); + // println!("We imported {} inputs from disk.", state.corpus().count()); + // } + // fuzzer + // .fuzz_one(&mut tuple_list!(StdMutationalStage::new(BitFlipMutator::new())), &mut executor, &mut state, &mut mgr) + // .expect("Error in the fuzzing loop"); return Ok(()); } - -//=========================== Debugging Feedback -/// A [`Feedback`] meant to dump the edgemap for debugging. -#[derive(Debug)] -pub struct DumpMapFeedback { - dumpfile: Option -} - -impl Feedback for DumpMapFeedback -where - I: Input, - S: HasClientPerfMonitor, -{ - fn is_interesting( - &mut self, - _state: &mut S, - _manager: &mut EM, - _input: &I, - _observers: &OT, - _exit_kind: &ExitKind, - ) -> Result - where - EM: EventFirer, - OT: ObserversTuple, - { - let observer = _observers.match_name::>>("edges") - .expect("HitcountsMapObserver not found"); - match &self.dumpfile { - Some(s) => { - fs::write(s,ron::to_string(&observer.edgemap).expect("Error serializing hashmap")).expect("Can not dump to file"); - self.dumpfile = None - }, - None => println!("{:#?}",observer.edgemap), - }; - Ok(true) - } -} - -impl Named for DumpMapFeedback { - #[inline] - fn name(&self) -> &str { - "HitFeedback" - } -} - -impl DumpMapFeedback { - /// Creates a new [`HitFeedback`] - #[must_use] - pub fn new() -> Self { - Self {dumpfile: None} - } - pub fn with_dump(dumpfile: Option) -> Self { - Self {dumpfile: dumpfile} - } -} - -impl Default for DumpMapFeedback { - fn default() -> Self { - Self::new() - } -} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 1982b7bbab..16ec309e3e 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; +use std::fs; use libafl::observers::VariableMapObserver; use hashbrown::{HashMap}; use libafl::observers::ObserversTuple; @@ -182,7 +184,8 @@ where } } let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); - let hit_target = mean_sum_of_squares <= 1.0; + let hit_target = mean_sum_of_squares <= self.target_msd; + eprintln!("hit target: {} {} {:?}",hit_target,mean_sum_of_squares, _input); if hit_target { Ok(true) } else { @@ -312,7 +315,7 @@ where } let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); let hit_target = mean_sum_of_squares <= self.best_msd; - eprintln!("in worst, {}",hit_target); + eprintln!("improving: {}",hit_target); if hit_target { self.best_msd = mean_sum_of_squares; Ok(true) @@ -341,4 +344,115 @@ impl Default for HitImprovingFeedback { fn default() -> Self { Self::new(HashMap::new()) } +} + +//=========================== Debugging Feedback +/// A [`Feedback`] meant to dump the edgemap for debugging. +#[derive(Debug)] +pub struct DumpMapFeedback { + dumpfile: Option +} + +impl Feedback for DumpMapFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = _observers.match_name::>>("edges") + .expect("HitcountsMapObserver not found"); + match &self.dumpfile { + Some(s) => { + fs::write(s,ron::to_string(&observer.edgemap).expect("Error serializing hashmap")).expect("Can not dump to file"); + self.dumpfile = None + }, + None => ()//println!("{:#?}",observer.edgemap), + }; + Ok(true) + } +} + +impl Named for DumpMapFeedback { + #[inline] + fn name(&self) -> &str { + "HitFeedback" + } +} + +impl DumpMapFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new() -> Self { + Self {dumpfile: None} + } + pub fn with_dump(dumpfile: Option) -> Self { + Self {dumpfile: dumpfile} + } +} + +impl Default for DumpMapFeedback { + fn default() -> Self { + Self::new() + } +} + +//=========================== Debugging Feedback +/// A [`Feedback`] meant to dump the edgemap for debugging. +#[derive(Debug)] +pub struct DummyFeedback { + response: bool +} + +impl Feedback for DummyFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + eprintln!("Input was: {:?}",_input); + Ok(self.response) + } +} + +impl Named for DummyFeedback { + #[inline] + fn name(&self) -> &str { + "DummyFeedback" + } +} + +impl DummyFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new(response: bool) -> Self { + Self {response: response} + } +} + +impl Default for DummyFeedback { + fn default() -> Self { + Self::new(true) + } } \ No newline at end of file diff --git a/libafl_qemu/src/clock.rs b/libafl_qemu/src/clock.rs index 66a316f1e9..04c669afb6 100644 --- a/libafl_qemu/src/clock.rs +++ b/libafl_qemu/src/clock.rs @@ -28,6 +28,16 @@ use libafl::events::EventFirer; use libafl::state::HasClientPerfMonitor; use libafl::inputs::Input; use libafl::feedbacks::Feedback; +use libafl::SerdeAny; +use libafl::state::HasMetadata; +use libafl::corpus::testcase::Testcase; + +//========== Metadata +#[derive(Debug, Serialize, Deserialize, SerdeAny)] +pub struct QemuIcountMetadata { + runtime: i64, +} +// libafl::impl_serdeany!(QemuIcountMetadata); //========== Observer @@ -55,7 +65,11 @@ impl QemuClockObserver { } } -impl Observer for QemuClockObserver { +impl Observer for QemuClockObserver +where + I: Input, + S: HasMetadata, +{ fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { self.last_runtime=0; Ok(()) @@ -63,6 +77,12 @@ impl Observer for QemuClockObserver { fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { unsafe { self.last_runtime = emu::libafl_get_clock() }; + // if !_state.has_metadata::() { + // _state.add_metadata(QemuIcountMetadata{runtime: self.last_runtime()}); + // println!("Added Metadata"); + // } else { + // println!("Found Metadata"); + // } println!("Observer Clock: {}",self.last_runtime()); Ok(()) } @@ -84,12 +104,13 @@ impl Default for QemuClockObserver { } } -//========== Observer +//========== Feedback /// A [`Feedback`] rewarding increasing the execution cycles on Qemu. #[derive(Debug)] pub struct QemuClockIncreaseFeedback { - maximum: i64 + maximum: i64, + last_runtime: i64, } impl Feedback for QemuClockIncreaseFeedback @@ -111,12 +132,26 @@ where { let observer = _observers.match_name::("clock") .expect("QemuClockObserver not found"); - if observer.last_runtime() >= self.maximum { + if observer.last_runtime() > self.maximum { self.maximum = observer.last_runtime(); return Ok(true); } Ok(false) } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime}); + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + Ok(()) + } + } impl Named for QemuClockIncreaseFeedback { @@ -130,7 +165,7 @@ impl QemuClockIncreaseFeedback { /// Creates a new [`HitFeedback`] #[must_use] pub fn new() -> Self { - Self {maximum: 0} + Self {maximum: 0, last_runtime: 0} } } From 1edc70c6f1134e9de3d6cc5b01d5e431a129d8f1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 3 Feb 2022 21:43:09 +0100 Subject: [PATCH 17/83] limit to 32 bytes --- fuzzers/wcet_qemu_sys/src/showmap.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index 22535170c7..8459f782de 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -293,9 +293,9 @@ fn fuzz( let target = input.target_bytes(); let mut buf = target.as_slice(); let mut len = buf.len(); - if len > 4096 { - buf = &buf[0..4096]; - len = 4096; + if len > 32 { + buf = &buf[0..32]; + len = 32; } unsafe { From 2ead941419265cd7e5f8cec7b68a15082d9ed9ba Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 3 Feb 2022 21:53:04 +0100 Subject: [PATCH 18/83] remove some unecessary stuff --- fuzzers/wcet_qemu_sys/src/freertos.rs | 1 + fuzzers/wcet_qemu_sys/src/fuzzer.rs | 17 ++++------------- fuzzers/wcet_qemu_sys/src/main.rs | 5 ----- fuzzers/wcet_qemu_sys/src/showmap.rs | 25 +------------------------ 4 files changed, 6 insertions(+), 42 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/freertos.rs index e176b43ea6..230b5b0235 100644 --- a/fuzzers/wcet_qemu_sys/src/freertos.rs +++ b/fuzzers/wcet_qemu_sys/src/freertos.rs @@ -1,3 +1,4 @@ +#![allow(non_camel_case_types,non_snake_case,non_upper_case_globals,deref_nullptr)] use serde::{Deserialize, Serialize}; // Manual Types use libafl_qemu::Emulator; diff --git a/fuzzers/wcet_qemu_sys/src/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/fuzzer.rs index 9e89c3535a..48ac651d88 100644 --- a/fuzzers/wcet_qemu_sys/src/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/fuzzer.rs @@ -1,11 +1,7 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. use libafl_qemu::QemuClockObserver; -use libafl::feedbacks::Feedback; -use crate::worst::HitImprovingFeedback; -use crate::worst::HitFeedback; use hashbrown::HashMap; -use libafl::stats::SimpleStats; use libafl::events::SimpleEventManager; use clap::{App, Arg}; use core::{cell::RefCell, time::Duration}; @@ -26,16 +22,14 @@ use libafl::{ current_nanos, current_time, os::dup2, rands::StdRand, - shmem::{ShMemProvider, StdShMemProvider}, tuples::{tuple_list, Merge}, }, corpus::{ Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, PowerQueueCorpusScheduler, }, - events::SimpleRestartingEventManager, executors::{ExitKind, ShadowExecutor, TimeoutExecutor}, feedback_or, - feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback}, + feedbacks::{MapFeedbackState, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::SimpleMonitor, @@ -62,12 +56,9 @@ use libafl_qemu::{ emu::Emulator, filter_qemu_args, snapshot_sys::QemuSysSnapshotHelper, - MmapPerms, QemuExecutor, - Regs, }; -use crate::worst::HitcountsMapObserver; -use crate::worst::MapHitIncreaseFeedback; +use crate::worst::{HitFeedback,HitcountsMapObserver,HitImprovingFeedback}; /// The fuzzer main @@ -211,7 +202,7 @@ fn fuzz( env::remove_var("LD_LIBRARY_PATH"); //=========== Initialize the Emulator - let mut args: Vec = vec![ + let args: Vec = vec![ "qemu-system-arm", "-machine","mps2-an385", "-monitor", "null", @@ -280,7 +271,7 @@ fn fuzz( writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap(); }); - let mut shmem_provider = StdShMemProvider::new()?; + // let mut shmem_provider = StdShMemProvider::new()?; //====== Create the most simple status display and managers. let mut mgr = SimpleEventManager::new(monitor); diff --git a/fuzzers/wcet_qemu_sys/src/main.rs b/fuzzers/wcet_qemu_sys/src/main.rs index 6b319d5ac9..c9c76a02db 100644 --- a/fuzzers/wcet_qemu_sys/src/main.rs +++ b/fuzzers/wcet_qemu_sys/src/main.rs @@ -5,11 +5,6 @@ pub mod showmap; pub mod worst; pub mod freertos; pub mod system_trace; -use libafl_qemu::{ - edges, - edges::QemuEdgeCoverageHelper, - emu, filter_qemu_args, -}; fn main() { #[cfg(all(target_os = "linux", feature = "showmap"))] diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/showmap.rs index 8459f782de..9a21240f5d 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/showmap.rs @@ -2,21 +2,9 @@ use crate::worst::DumpMapFeedback; use crate::worst::DummyFeedback; -use libafl::corpus::Corpus; -use libafl::state::HasCorpus; -use libafl::Fuzzer; -use libafl::mutators::BitFlipMutator; -use libafl::stages::StdMutationalStage; use libafl_qemu::QemuInstrumentationFilter; use crate::system_trace::QemuSystemStateHelper; -use libafl::feedbacks::CrashFeedback; -use std::path::Path; use libafl_qemu::QemuExecutor; -use libafl::bolts::tuples::Named; -use libafl::observers::ObserversTuple; -use libafl::events::EventFirer; -use libafl::state::HasClientPerfMonitor; -use libafl::feedbacks::Feedback; use libafl::Evaluator; use libafl::inputs::Input; use libafl::corpus::InMemoryCorpus; @@ -50,10 +38,8 @@ use libafl_qemu::{ emu::Emulator, filter_qemu_args, snapshot_sys::QemuSysSnapshotHelper, elf::EasyElf, - clock, - clock::{QemuClockObserver,QemuClockIncreaseFeedback}, + clock::{QemuClockObserver}, }; -use crate::freertos; /// The fuzzer main @@ -62,8 +48,6 @@ pub fn main() { // Needed only on no_std //RegistryBuilder::register::(); - let args: Vec = env::args().collect(); - let res = match App::new("wcet_qemu_fuzzer") .version("0.4.0") .author("Alwin Berger") @@ -303,13 +287,6 @@ fn fuzz( emu.write_mem(input_addr,buf); emu.run(); - //====== experiment inspecting the current tcb - // let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(&emu, curr_tcb_pointer.try_into().unwrap()); - // println!("Current TCB addr: {:x}",curr_tcb_addr); - // let current_tcb : freertos::TCB_t = freertos::emu_lookup::lookup(&emu,curr_tcb_addr); - // println!("Current TCB: {:?}",current_tcb); - // let ready_queue : freertos::List_t = freertos::emu_lookup::lookup(&emu,task_queue_addr.try_into().unwrap()); - // println!("Ready Queue: {:?}",ready_queue); } ExitKind::Ok From 06d382bff8c6dd1a2a2efede6c949e3b30fbe1cb Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 6 Feb 2022 19:36:28 +0100 Subject: [PATCH 19/83] Various improvements Replace TimeObserver with ClockObserver Add a Maximization Scheduler for Clocks Factor out icount Metadata from Feedback Allow Breakpoint removal in systemmode --- fuzzers/wcet_qemu_sys/src/fuzzer.rs | 19 +++- fuzzers/wcet_qemu_sys/src/worst.rs | 38 ++++++- libafl_qemu/src/clock.rs | 162 ++++++++++++++++++++++++---- libafl_qemu/src/emu.rs | 9 +- 4 files changed, 199 insertions(+), 29 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/fuzzer.rs index 48ac651d88..1042acc6eb 100644 --- a/fuzzers/wcet_qemu_sys/src/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/fuzzer.rs @@ -1,6 +1,9 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. -use libafl_qemu::QemuClockObserver; +use libafl::mutators::ByteFlipMutator; +use libafl::mutators::BitFlipMutator; +use crate::worst::LenTimeMaximizerCorpusScheduler; +use libafl::corpus::MinimizerCorpusScheduler; use hashbrown::HashMap; use libafl::events::SimpleEventManager; use clap::{App, Arg}; @@ -57,6 +60,10 @@ use libafl_qemu::{ filter_qemu_args, snapshot_sys::QemuSysSnapshotHelper, QemuExecutor, + clock, + QemuClockObserver, + clock::ClockFeedback, + clock::QemuClockIncreaseFeedback }; use crate::worst::{HitFeedback,HitcountsMapObserver,HitImprovingFeedback}; @@ -283,7 +290,7 @@ fn fuzz( HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); // Create an observation channel to keep track of the execution time - let time_observer = TimeObserver::new("time"); + // let time_observer = TimeObserver::new("time"); let clock_observer = QemuClockObserver::default(); // Create an observation channel using cmplog map @@ -304,7 +311,9 @@ fn fuzz( }; let feedback = feedback_or!( MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), - HitImprovingFeedback::new(target_map.clone()) + HitImprovingFeedback::new(target_map.clone()), + QemuClockIncreaseFeedback::default(), + ClockFeedback::new_with_observer(&clock_observer) ); // let feedback = feedback_or!( // // New maximization map feedback linked to the edges observer and the feedback state @@ -329,7 +338,7 @@ fn fuzz( OnDiskCorpus::new(objective_dir).unwrap(), // States of the feedbacks. // They are the data related to the feedbacks that you want to persist in the State. - tuple_list!(feedback_state), + tuple_list!(feedback_state,clock::MaxIcountMetadata::default()), ) }; @@ -380,7 +389,7 @@ fn fuzz( //QemuAsanHelper::new(), QemuSysSnapshotHelper::new() ), - tuple_list!(edges_observer, time_observer, clock_observer), + tuple_list!(edges_observer, clock_observer), &mut fuzzer, &mut state, &mut mgr, diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 16ec309e3e..2c0a3d5dc2 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -1,3 +1,8 @@ +use libafl::feedbacks::MapIndexesMetadata; +use libafl::corpus::Testcase; +use libafl::corpus::FavFactor; +use core::marker::PhantomData; +use libafl::corpus::MinimizerCorpusScheduler; use std::path::PathBuf; use std::fs; use libafl::observers::VariableMapObserver; @@ -185,7 +190,7 @@ where } let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); let hit_target = mean_sum_of_squares <= self.target_msd; - eprintln!("hit target: {} {} {:?}",hit_target,mean_sum_of_squares, _input); + // eprintln!("hit target: {} {} {:?}",hit_target,mean_sum_of_squares, _input); if hit_target { Ok(true) } else { @@ -314,9 +319,10 @@ where } } let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); - let hit_target = mean_sum_of_squares <= self.best_msd; - eprintln!("improving: {}",hit_target); + let hit_target = mean_sum_of_squares < self.best_msd; + // eprintln!("improving: {}",hit_target); if hit_target { + // println!("Hit Improving: {}",mean_sum_of_squares); self.best_msd = mean_sum_of_squares; Ok(true) } else { @@ -408,7 +414,7 @@ impl Default for DumpMapFeedback { } //=========================== Debugging Feedback -/// A [`Feedback`] meant to dump the edgemap for debugging. +/// A NoOp [`Feedback`] with fixed response. #[derive(Debug)] pub struct DummyFeedback { response: bool @@ -455,4 +461,28 @@ impl Default for DummyFeedback { fn default() -> Self { Self::new(true) } +} + +pub type LenTimeMaximizerCorpusScheduler = + MinimizerCorpusScheduler, I, MapIndexesMetadata, S>; + +/// Multiply the testcase size with the execution time. +/// This favors small and quick testcases. +#[derive(Debug, Clone)] +pub struct MaxLenTimeFavFactor +where + I: Input + HasLen, +{ + phantom: PhantomData, +} + +impl FavFactor for MaxLenTimeFavFactor +where + I: Input + HasLen, +{ + fn compute(entry: &mut Testcase) -> Result { + // TODO maybe enforce entry.exec_time().is_some() + let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64()) as u64; + Ok(execs_per_hour) + } } \ No newline at end of file diff --git a/libafl_qemu/src/clock.rs b/libafl_qemu/src/clock.rs index 04c669afb6..9e05c4b959 100644 --- a/libafl_qemu/src/clock.rs +++ b/libafl_qemu/src/clock.rs @@ -31,21 +31,66 @@ use libafl::feedbacks::Feedback; use libafl::SerdeAny; use libafl::state::HasMetadata; use libafl::corpus::testcase::Testcase; +use core::{fmt::Debug, time::Duration}; +use libafl::feedbacks::FeedbackState; +use libafl::state::HasFeedbackStates; +use libafl::bolts::tuples::MatchName; //========== Metadata #[derive(Debug, Serialize, Deserialize, SerdeAny)] pub struct QemuIcountMetadata { - runtime: i64, + runtime: u64, } // libafl::impl_serdeany!(QemuIcountMetadata); +/// Metadata for [`QemuClockIncreaseFeedback`] +#[derive(Debug, Serialize, Deserialize, SerdeAny)] +pub struct MaxIcountMetadata { + pub max_icount_seen: u64, + pub name: String, +} + +impl FeedbackState for MaxIcountMetadata +{ + fn reset(&mut self) -> Result<(), Error> { + self.max_icount_seen = 0; + Ok(()) + } +} + +impl Named for MaxIcountMetadata +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl MaxIcountMetadata +{ + /// Create new `MaxIcountMetadata` + #[must_use] + pub fn new(name: &'static str) -> Self { + Self { + max_icount_seen: 0, + name: name.to_string(), + } + } +} + +impl Default for MaxIcountMetadata { + fn default() -> Self { + Self::new("MaxClock") + } +} + //========== Observer /// A simple observer, just overlooking the runtime of the target. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct QemuClockObserver { name: String, - last_runtime: i64, + last_runtime: u64, } impl QemuClockObserver { @@ -60,7 +105,7 @@ impl QemuClockObserver { /// Gets the runtime for the last execution of this target. #[must_use] - pub fn last_runtime(&self) -> i64 { + pub fn last_runtime(&self) -> u64 { self.last_runtime } } @@ -83,7 +128,7 @@ where // } else { // println!("Found Metadata"); // } - println!("Observer Clock: {}",self.last_runtime()); + // println!("Observer Clock: {}",self.last_runtime()); Ok(()) } } @@ -105,15 +150,16 @@ impl Default for QemuClockObserver { } //========== Feedback - -/// A [`Feedback`] rewarding increasing the execution cycles on Qemu. -#[derive(Debug)] -pub struct QemuClockIncreaseFeedback { - maximum: i64, - last_runtime: i64, +/// Nop feedback that annotates execution time in the new testcase, if any +/// for this Feedback, the testcase is never interesting (use with an OR) +/// It decides, if the given [`ClockObserver`] value of a run is interesting. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ClockFeedback { + exec_time: Option, + name: String, } -impl Feedback for QemuClockIncreaseFeedback +impl Feedback for ClockFeedback where I: Input, S: HasClientPerfMonitor, @@ -123,6 +169,81 @@ where _state: &mut S, _manager: &mut EM, _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + // TODO Replace with match_name_type when stable + let observer = observers.match_name::(self.name()).unwrap(); + self.exec_time = Some(observer.last_runtime()); + Ok(false) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + *testcase.exec_time_mut() = match self.exec_time { + Some(s) => Some(Duration::new(0,360*s as u32)), + None => None, + }; + self.exec_time = None; + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.exec_time = None; + Ok(()) + } +} + +impl Named for ClockFeedback { + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl ClockFeedback { + /// Creates a new [`ClockFeedback`], deciding if the value of a [`TimeObserver`] with the given `name` of a run is interesting. + #[must_use] + pub fn new(name: &'static str) -> Self { + Self { + exec_time: None, + name: name.to_string(), + } + } + + /// Creates a new [`ClockFeedback`], deciding if the given [`TimeObserver`] value of a run is interesting. + #[must_use] + pub fn new_with_observer(observer: &QemuClockObserver) -> Self { + Self { + exec_time: None, + name: observer.name().to_string(), + } + } +} + +/// A [`Feedback`] rewarding increasing the execution cycles on Qemu. +#[derive(Debug)] +pub struct QemuClockIncreaseFeedback { + name: String, +} + +impl Feedback for QemuClockIncreaseFeedback +where + I: Input, + S: HasFeedbackStates + HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &I, _observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -132,8 +253,13 @@ where { let observer = _observers.match_name::("clock") .expect("QemuClockObserver not found"); - if observer.last_runtime() > self.maximum { - self.maximum = observer.last_runtime(); + let clock_state = state + .feedback_states_mut() + .match_name_mut::(&self.name) + .unwrap(); + if observer.last_runtime() > clock_state.max_icount_seen { + // println!("Clock improving {}",observer.last_runtime()); + clock_state.max_icount_seen = observer.last_runtime(); return Ok(true); } Ok(false) @@ -142,7 +268,7 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { - testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime}); + // testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime}); Ok(()) } @@ -157,20 +283,20 @@ where impl Named for QemuClockIncreaseFeedback { #[inline] fn name(&self) -> &str { - "QemuClockFeedback" + &self.name } } impl QemuClockIncreaseFeedback { /// Creates a new [`HitFeedback`] #[must_use] - pub fn new() -> Self { - Self {maximum: 0, last_runtime: 0} + pub fn new(name: &'static str) -> Self { + Self {name: String::from(name)} } } impl Default for QemuClockIncreaseFeedback { fn default() -> Self { - Self::new() + Self::new("MaxClock") } } \ No newline at end of file diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 532766f483..6f5d46a20c 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -183,6 +183,8 @@ extern "C" { fn libafl_qemu_set_breakpoint(addr: u64) -> i32; #[cfg(feature = "systemmode")] fn libafl_qemu_set_native_breakpoint(addr: u64) -> i32; + #[cfg(feature = "systemmode")] + fn libafl_qemu_remove_native_breakpoint(addr: u64) -> i32; fn libafl_qemu_remove_breakpoint(addr: u64) -> i32; fn libafl_qemu_set_hook(addr: u64, callback: extern "C" fn(u64), val: u64) -> i32; fn libafl_qemu_remove_hook(addr: u64) -> i32; @@ -200,7 +202,7 @@ extern "C" { #[cfg(feature = "systemmode")] fn libafl_snapshot_load(name: *const c_char) -> i32; #[cfg(feature = "systemmode")] - pub fn libafl_get_clock() -> i64; + pub fn libafl_get_clock() -> u64; fn strlen(s: *const u8) -> usize; @@ -438,6 +440,9 @@ impl Emulator { pub fn remove_breakpoint(&self, addr: u64) { unsafe { + #[cfg(feature = "systemmode")] + libafl_qemu_remove_native_breakpoint(addr); + #[cfg(not(feature = "systemmode"))] libafl_qemu_remove_breakpoint(addr); } } @@ -682,7 +687,7 @@ impl Emulator { } #[cfg(feature = "systemmode")] - pub fn get_ticks(&self) -> i64{ + pub fn get_ticks(&self) -> u64{ return unsafe { libafl_get_clock() }; } } From dbd6391b5933f876ec43b862c197051f39a25301 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 9 Feb 2022 11:56:20 +0100 Subject: [PATCH 20/83] switch scheduler --- fuzzers/wcet_qemu_sys/src/fuzzer.rs | 4 +++- fuzzers/wcet_qemu_sys/src/worst.rs | 35 +++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/fuzzer.rs index 1042acc6eb..ed22a4f6dd 100644 --- a/fuzzers/wcet_qemu_sys/src/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/fuzzer.rs @@ -353,7 +353,9 @@ fn fuzz( let power = PowerMutationalStage::new(mutator, PowerSchedule::FAST, &edges_observer); // A minimization+queue policy to get testcasess from the corpus - let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); + // let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); + let scheduler = LenTimeMaximizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); + // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 2c0a3d5dc2..165694c599 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -463,20 +463,22 @@ impl Default for DummyFeedback { } } -pub type LenTimeMaximizerCorpusScheduler = - MinimizerCorpusScheduler, I, MapIndexesMetadata, S>; +//=========================== Scheduler + +pub type TimeMaximizerCorpusScheduler = + MinimizerCorpusScheduler, I, MapIndexesMetadata, S>; /// Multiply the testcase size with the execution time. /// This favors small and quick testcases. #[derive(Debug, Clone)] -pub struct MaxLenTimeFavFactor +pub struct MaxTimeFavFactor where I: Input + HasLen, { phantom: PhantomData, } -impl FavFactor for MaxLenTimeFavFactor +impl FavFactor for MaxTimeFavFactor where I: Input + HasLen, { @@ -485,4 +487,29 @@ where let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64()) as u64; Ok(execs_per_hour) } +} + +pub type LenTimeMaximizerCorpusScheduler = + MinimizerCorpusScheduler, I, MapIndexesMetadata, S>; + +/// Multiply the testcase size with the execution time. +/// This favors small and quick testcases. +#[derive(Debug, Clone)] +pub struct MaxExecsLenFavFactor +where + I: Input + HasLen, +{ + phantom: PhantomData, +} + +impl FavFactor for MaxExecsLenFavFactor +where + I: Input + HasLen, +{ + fn compute(entry: &mut Testcase) -> Result { + // TODO maybe enforce entry.exec_time().is_some() + let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64()) as u64; + let execs_times_length_per_hour = execs_per_hour*entry.cached_len()? as u64; + Ok(execs_times_length_per_hour) + } } \ No newline at end of file From 9369eae37be72ffcf93c3f2076da33a11e5674fc Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 9 Feb 2022 12:55:21 +0100 Subject: [PATCH 21/83] crate structure --- fuzzers/wcet_qemu_sys/src/{ => bin}/fuzzer.rs | 4 ++-- fuzzers/wcet_qemu_sys/src/{ => bin}/showmap.rs | 8 ++++---- fuzzers/wcet_qemu_sys/src/lib.rs | 5 +++++ fuzzers/wcet_qemu_sys/src/main.rs | 14 -------------- 4 files changed, 11 insertions(+), 20 deletions(-) rename fuzzers/wcet_qemu_sys/src/{ => bin}/fuzzer.rs (99%) rename fuzzers/wcet_qemu_sys/src/{ => bin}/showmap.rs (98%) create mode 100644 fuzzers/wcet_qemu_sys/src/lib.rs delete mode 100644 fuzzers/wcet_qemu_sys/src/main.rs diff --git a/fuzzers/wcet_qemu_sys/src/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs similarity index 99% rename from fuzzers/wcet_qemu_sys/src/fuzzer.rs rename to fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index ed22a4f6dd..a71e55aa98 100644 --- a/fuzzers/wcet_qemu_sys/src/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -2,7 +2,7 @@ use libafl::mutators::ByteFlipMutator; use libafl::mutators::BitFlipMutator; -use crate::worst::LenTimeMaximizerCorpusScheduler; +use wcet_qemu_sys::worst::LenTimeMaximizerCorpusScheduler; use libafl::corpus::MinimizerCorpusScheduler; use hashbrown::HashMap; use libafl::events::SimpleEventManager; @@ -65,7 +65,7 @@ use libafl_qemu::{ clock::ClockFeedback, clock::QemuClockIncreaseFeedback }; -use crate::worst::{HitFeedback,HitcountsMapObserver,HitImprovingFeedback}; +use wcet_qemu_sys::worst::{HitFeedback,HitcountsMapObserver,HitImprovingFeedback}; /// The fuzzer main diff --git a/fuzzers/wcet_qemu_sys/src/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs similarity index 98% rename from fuzzers/wcet_qemu_sys/src/showmap.rs rename to fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 9a21240f5d..ae692202fe 100644 --- a/fuzzers/wcet_qemu_sys/src/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -1,16 +1,16 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. -use crate::worst::DumpMapFeedback; -use crate::worst::DummyFeedback; +use wcet_qemu_sys::worst::DumpMapFeedback; +use wcet_qemu_sys::worst::DummyFeedback; use libafl_qemu::QemuInstrumentationFilter; -use crate::system_trace::QemuSystemStateHelper; +use wcet_qemu_sys::system_trace::QemuSystemStateHelper; use libafl_qemu::QemuExecutor; use libafl::Evaluator; use libafl::inputs::Input; use libafl::corpus::InMemoryCorpus; use libafl::events::SimpleEventManager; use libafl::stats::SimpleStats; -use crate::worst::HitcountsMapObserver; +use wcet_qemu_sys::worst::HitcountsMapObserver; use clap::{App, Arg}; use std::{ env, diff --git a/fuzzers/wcet_qemu_sys/src/lib.rs b/fuzzers/wcet_qemu_sys/src/lib.rs new file mode 100644 index 0000000000..5c4c7d64f1 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/lib.rs @@ -0,0 +1,5 @@ +#![feature(iter_advance_by)] +#[cfg(target_os = "linux")] +pub mod worst; +pub mod freertos; +pub mod system_trace; diff --git a/fuzzers/wcet_qemu_sys/src/main.rs b/fuzzers/wcet_qemu_sys/src/main.rs deleted file mode 100644 index c9c76a02db..0000000000 --- a/fuzzers/wcet_qemu_sys/src/main.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![feature(iter_advance_by)] -#[cfg(target_os = "linux")] -pub mod fuzzer; -pub mod showmap; -pub mod worst; -pub mod freertos; -pub mod system_trace; - -fn main() { - #[cfg(all(target_os = "linux", feature = "showmap"))] - showmap::main(); - #[cfg(all(target_os = "linux", not(feature = "showmap")))] - fuzzer::main(); -} From 99bd30c2335985235ba105e97c0ed0849a30b914 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 9 Feb 2022 22:17:27 +0100 Subject: [PATCH 22/83] add snapshot path, debug prints --- fuzzers/wcet_qemu_sys/.gitignore | 1 + fuzzers/wcet_qemu_sys/Cargo.toml | 1 - fuzzers/wcet_qemu_sys/fuzzer.sh | 4 +++ fuzzers/wcet_qemu_sys/showmap.sh | 4 +++ fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 28 +++++++++-------- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 39 ++++++++++++++---------- fuzzers/wcet_qemu_sys/src/worst.rs | 12 ++------ fuzzers/wcet_qemu_sys/starter.sh | 3 -- 8 files changed, 51 insertions(+), 41 deletions(-) create mode 100755 fuzzers/wcet_qemu_sys/fuzzer.sh create mode 100755 fuzzers/wcet_qemu_sys/showmap.sh delete mode 100755 fuzzers/wcet_qemu_sys/starter.sh diff --git a/fuzzers/wcet_qemu_sys/.gitignore b/fuzzers/wcet_qemu_sys/.gitignore index 456d0151b4..0daf8ec693 100644 --- a/fuzzers/wcet_qemu_sys/.gitignore +++ b/fuzzers/wcet_qemu_sys/.gitignore @@ -3,3 +3,4 @@ demo *.ron *.bsp +tmp* diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 7eba5fb3ab..75f939db12 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" [features] default = ["std"] std = [] -showmap = [] [profile.release] debug = true diff --git a/fuzzers/wcet_qemu_sys/fuzzer.sh b/fuzzers/wcet_qemu_sys/fuzzer.sh new file mode 100755 index 0000000000..319fc1c1e9 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/fuzzer.sh @@ -0,0 +1,4 @@ +mkdir -p tmp/test_in tmp/test_out +[ ! -f tmp/test_in/test ] && echo " !test" > tmp/test_in/test +[ ! -f tmp/dummy.qcow2 ] && qemu-img create -f qcow2 tmp/dummy.qcow2 32M +LD_LIBRARY_PATH=target/debug target/debug/fuzzer --libafl-snapshot tmp/dummy.qcow2 --libafl-out tmp/test_out --libafl-in tmp/test_in --libafl-kernel $@ diff --git a/fuzzers/wcet_qemu_sys/showmap.sh b/fuzzers/wcet_qemu_sys/showmap.sh new file mode 100755 index 0000000000..9cba846cdb --- /dev/null +++ b/fuzzers/wcet_qemu_sys/showmap.sh @@ -0,0 +1,4 @@ +mkdir -p tmp/test_in tmp/test_out +[ ! -f tmp/test_in/test ] && echo " !test" > tmp/test_in/test +[ ! -f tmp/dummy.qcow2 ] && qemu-img create -f qcow2 tmp/dummy.qcow2 32M +LD_LIBRARY_PATH=target/debug target/debug/showmap --libafl-snapshot tmp/dummy.qcow2 --libafl-out tmp/test_out --libafl-in tmp/test_in --libafl-kernel $@ diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index a71e55aa98..48eac07ef9 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,11 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. -use libafl::mutators::ByteFlipMutator; -use libafl::mutators::BitFlipMutator; -use wcet_qemu_sys::worst::LenTimeMaximizerCorpusScheduler; -use libafl::corpus::MinimizerCorpusScheduler; use hashbrown::HashMap; -use libafl::events::SimpleEventManager; use clap::{App, Arg}; use core::{cell::RefCell, time::Duration}; #[cfg(unix)] @@ -28,7 +23,7 @@ use libafl::{ tuples::{tuple_list, Merge}, }, corpus::{ - Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, PowerQueueCorpusScheduler, + Corpus, OnDiskCorpus, PowerQueueCorpusScheduler, }, executors::{ExitKind, ShadowExecutor, TimeoutExecutor}, feedback_or, @@ -40,13 +35,14 @@ use libafl::{ scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations, StdMOptMutator, StdScheduledMutator, Tokens, }, - observers::{TimeObserver, VariableMapObserver}, + observers::{VariableMapObserver}, stages::{ calibrate::CalibrationStage, power::{PowerMutationalStage, PowerSchedule}, ShadowTracingStage, StdMutationalStage, }, state::{HasCorpus, HasMetadata, StdState}, + events::SimpleEventManager, Error, }; use libafl_qemu::{ @@ -65,7 +61,7 @@ use libafl_qemu::{ clock::ClockFeedback, clock::QemuClockIncreaseFeedback }; -use wcet_qemu_sys::worst::{HitFeedback,HitcountsMapObserver,HitImprovingFeedback}; +use wcet_qemu_sys::worst::{HitFeedback,HitcountsMapObserver,HitImprovingFeedback,LenTimeMaximizerCorpusScheduler}; /// The fuzzer main @@ -121,6 +117,13 @@ pub fn main() { .long("libafl-edges") .takes_value(true), ) + .arg( + Arg::new("snapshot") + .help("The qcow2 file used for snapshots") + .long("libafl-snapshot") + .required(true) + .takes_value(true), + ) .try_get_matches_from(filter_qemu_args()) { Ok(res) => res, @@ -178,7 +181,9 @@ pub fn main() { None => None }; - fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel, edges) + let snapshot = PathBuf::from(res.value_of("snapshot").unwrap().to_string()); + + fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel, edges, snapshot) .expect("An error occurred while fuzzing"); } @@ -205,6 +210,7 @@ fn fuzz( timeout: Duration, kernel: PathBuf, dump_edges: Option, + snapshot: PathBuf, ) -> Result<(), Error> { env::remove_var("LD_LIBRARY_PATH"); @@ -217,7 +223,7 @@ fn fuzz( "--semihosting-config", "enable=on,target=native", "-kernel", kernel.to_str().unwrap(), "-serial", "stdio", "-nographic", - "-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2", + "-snapshot", "-drive", format!("if=none,format=qcow2,file={}",snapshot.to_string_lossy()).as_str(), "-icount", "shift=auto,align=off,sleep=off", "-S" ].iter().map(|x| x.to_string()).collect(); @@ -373,10 +379,8 @@ fn fuzz( unsafe { emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); - // println!("{:#?}",edges_copy); emu.run(); - // println!("{:#?}",edges_copy); } ExitKind::Ok diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index ae692202fe..5baeb16cff 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -1,16 +1,9 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. -use wcet_qemu_sys::worst::DumpMapFeedback; -use wcet_qemu_sys::worst::DummyFeedback; -use libafl_qemu::QemuInstrumentationFilter; -use wcet_qemu_sys::system_trace::QemuSystemStateHelper; -use libafl_qemu::QemuExecutor; -use libafl::Evaluator; -use libafl::inputs::Input; -use libafl::corpus::InMemoryCorpus; -use libafl::events::SimpleEventManager; -use libafl::stats::SimpleStats; -use wcet_qemu_sys::worst::HitcountsMapObserver; +use wcet_qemu_sys::{ + worst::{DumpMapFeedback,DummyFeedback,HitcountsMapObserver}, + system_trace::QemuSystemStateHelper, +}; use clap::{App, Arg}; use std::{ env, @@ -24,21 +17,26 @@ use libafl::{ rands::StdRand, tuples::{tuple_list}, }, - corpus::{QueueCorpusScheduler}, + corpus::{InMemoryCorpus,QueueCorpusScheduler}, executors::{ExitKind}, fuzzer::{StdFuzzer}, - inputs::{BytesInput, HasTargetBytes}, + inputs::{Input,BytesInput, HasTargetBytes}, observers::{VariableMapObserver}, state::{StdState}, Error, + Evaluator, + stats::SimpleStats, + events::SimpleEventManager, }; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, emu::Emulator, filter_qemu_args, - snapshot_sys::QemuSysSnapshotHelper, elf::EasyElf, + snapshot_sys::QemuSysSnapshotHelper, clock::{QemuClockObserver}, + QemuInstrumentationFilter, + QemuExecutor, }; @@ -90,6 +88,13 @@ pub fn main() { .long("libafl-edges") .takes_value(true), ) + .arg( + Arg::new("snapshot") + .help("The qcow2 file used for snapshots") + .long("libafl-snapshot") + .required(true) + .takes_value(true), + ) .try_get_matches_from(filter_qemu_args()) { Ok(res) => res, @@ -135,8 +140,9 @@ pub fn main() { None => None }; + let snapshot = PathBuf::from(res.value_of("snapshot").unwrap().to_string()); - fuzz(in_dir, kernel, edges) + fuzz(in_dir, kernel, edges, snapshot) .expect("An error occurred while fuzzing"); } @@ -158,6 +164,7 @@ fn fuzz( seed_dir: PathBuf, kernel: PathBuf, dump_edges: Option, + snapshot: PathBuf, ) -> Result<(), Error> { //=========== Setup emulator let mut env: Vec<(String, String)> = env::vars().collect(); @@ -169,7 +176,7 @@ fn fuzz( "--semihosting-config", "enable=on,target=native", "-kernel", kernel.to_str().unwrap(), "-serial", "stdio", "-nographic", - "-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2", + "-snapshot", "-drive", format!("if=none,format=qcow2,file={}",snapshot.to_string_lossy()).as_str(), "-icount", "shift=auto,align=off,sleep=off", "-S" ].iter().map(|x| x.to_string()).collect(); diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 165694c599..45257d7fe2 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -175,8 +175,6 @@ where EM: EventFirer, OT: ObserversTuple, { - // TODO Replace with match_name_type when stable - // let observer = _observers.match_name::>("edges").expect("SelectedEdgeObserver not found"); let observer = _observers.match_name::>>("edges") .expect("HitcountsMapObserver not found"); if self.target_map.len() == 0 { return Ok(true) }; @@ -190,8 +188,8 @@ where } let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); let hit_target = mean_sum_of_squares <= self.target_msd; - // eprintln!("hit target: {} {} {:?}",hit_target,mean_sum_of_squares, _input); if hit_target { + #[cfg(debug_assertions)] eprintln!("Hit Feedback trigger"); Ok(true) } else { Ok(false) @@ -305,8 +303,6 @@ where EM: EventFirer, OT: ObserversTuple, { - // TODO Replace with match_name_type when stable - // let observer = _observers.match_name::>("edges").expect("SelectedEdgeObserver not found"); let observer = _observers.match_name::>>("edges") .expect("HitcountsMapObserver not found"); if self.target_map.len() == 0 { return Ok(true) }; @@ -320,9 +316,8 @@ where } let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); let hit_target = mean_sum_of_squares < self.best_msd; - // eprintln!("improving: {}",hit_target); if hit_target { - // println!("Hit Improving: {}",mean_sum_of_squares); + #[cfg(debug_assertions)] eprintln!("Hit Improving: {}",mean_sum_of_squares); self.best_msd = mean_sum_of_squares; Ok(true) } else { @@ -383,7 +378,7 @@ where fs::write(s,ron::to_string(&observer.edgemap).expect("Error serializing hashmap")).expect("Can not dump to file"); self.dumpfile = None }, - None => ()//println!("{:#?}",observer.edgemap), + None => println!("{:?}",observer.edgemap), }; Ok(true) } @@ -507,7 +502,6 @@ where I: Input + HasLen, { fn compute(entry: &mut Testcase) -> Result { - // TODO maybe enforce entry.exec_time().is_some() let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64()) as u64; let execs_times_length_per_hour = execs_per_hour*entry.cached_len()? as u64; Ok(execs_times_length_per_hour) diff --git a/fuzzers/wcet_qemu_sys/starter.sh b/fuzzers/wcet_qemu_sys/starter.sh deleted file mode 100755 index a933fd629c..0000000000 --- a/fuzzers/wcet_qemu_sys/starter.sh +++ /dev/null @@ -1,3 +0,0 @@ -mkdir -p target/test_in target/test_out -[ ! -f target/test_in/test ] && echo " !test" > target/test_in/test -LD_LIBRARY_PATH=target/debug target/debug/wcet_qemu_sys --libafl-out target/test_out --libafl-in target/test_in --libafl-kernel $@ From 64dfd6a6428f098fbd586717cec2dd24b092c146 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 10 Feb 2022 14:40:42 +0100 Subject: [PATCH 23/83] showmap debug options --- fuzzers/wcet_qemu_sys/Cargo.toml | 1 + fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 74 +++++++++++++++--------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 75f939db12..1fedc8942a 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -20,3 +20,4 @@ ron = "0.7" # write serialized data - including hashmaps hashbrown = { version = "0.11", features = ["serde", "ahash-compile-time-rng"], default-features=false } # A faster hashmap, nostd compatible nix = "0.23.0" goblin = "0.4.2" +either = "1.6.1" diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 5baeb16cff..3c00a127c9 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -17,16 +17,19 @@ use libafl::{ rands::StdRand, tuples::{tuple_list}, }, - corpus::{InMemoryCorpus,QueueCorpusScheduler}, + corpus::{Corpus,InMemoryCorpus,QueueCorpusScheduler}, executors::{ExitKind}, fuzzer::{StdFuzzer}, inputs::{Input,BytesInput, HasTargetBytes}, observers::{VariableMapObserver}, - state::{StdState}, + state::{HasCorpus,StdState}, Error, Evaluator, stats::SimpleStats, events::SimpleEventManager, + stages::StdMutationalStage, + mutators::BitFlipMutator, + Fuzzer, }; use libafl_qemu::{ edges, @@ -39,6 +42,7 @@ use libafl_qemu::{ QemuExecutor, }; +use either::{Either,Left,Right}; /// The fuzzer main pub fn main() { @@ -95,6 +99,11 @@ pub fn main() { .required(true) .takes_value(true), ) + .arg( + Arg::new("single") + .long("libafl-single") + .takes_value(true) + ) .try_get_matches_from(filter_qemu_args()) { Ok(res) => res, @@ -128,11 +137,17 @@ pub fn main() { worstcases.push("worstcase"); out_dir.push("queue"); - let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string()); - if !in_dir.is_dir() { - println!("In dir at {:?} is not a valid directory!", &in_dir); - return; - } + let seed = match res.value_of("single") { + Some(s) => Left(s.to_string()), + None => { + let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string()); + if !in_dir.is_dir() { + println!("In dir at {:?} is not a valid directory!", &in_dir); + return; + } + Right(in_dir) + }, + }; let kernel = PathBuf::from(res.value_of("k").unwrap().to_string()); let edges = match res.value_of("edges") { @@ -142,7 +157,7 @@ pub fn main() { let snapshot = PathBuf::from(res.value_of("snapshot").unwrap().to_string()); - fuzz(in_dir, kernel, edges, snapshot) + fuzz(seed, kernel, edges, snapshot) .expect("An error occurred while fuzzing"); } @@ -161,7 +176,7 @@ fn virt2phys(vaddr : u64, tab : &goblin::elf::Elf) -> u64 { /// The actual fuzzer fn fuzz( - seed_dir: PathBuf, + seed: Either, kernel: PathBuf, dump_edges: Option, snapshot: PathBuf, @@ -317,10 +332,30 @@ fn fuzz( &mut state, &mut mgr, )?; - let firstinput = match seed_dir.clone().is_dir() { - true => seed_dir.clone().read_dir().expect("Directory not a directory?").next().expect("Directory empty?").expect("File not in directory?").path(), - false => seed_dir.clone() - }; + match seed { + Right(pb) => { + if state.corpus().count() < 1 { + state + .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[pb.clone()]) + .unwrap_or_else(|_| { + println!("Failed to load initial corpus at {:?}", &pb); + return; + }); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + fuzzer + .fuzz_one(&mut tuple_list!(StdMutationalStage::new(BitFlipMutator::new())), &mut executor, &mut state, &mut mgr) + .expect("Error in the fuzzing loop"); + + }, + Left(s) => { + fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(s.as_bytes().to_vec())).expect("Evaluation failed"); + } + } + // let firstinput = match seed.clone().is_dir() { + // true => seed.clone().read_dir().expect("Directory not a directory?").next().expect("Directory empty?").expect("File not in directory?").path(), + // false => seed.clone() + // }; // let secondinput = match seed_dir.clone().is_dir() { // true => { // let mut a = seed_dir.clone().read_dir().expect("Directory not a directory?"); @@ -329,20 +364,7 @@ fn fuzz( // }, // false => seed_dir.clone() // }; - fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, Input::from_file(&firstinput).expect("Could not load file")).expect("Evaluation failed"); // fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, Input::from_file(&secondinput).expect("Could not load file")).expect("Evaluation failed"); // println!("Nach Eval"); - // if state.corpus().count() < 1 { - // state - // .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) - // .unwrap_or_else(|_| { - // println!("Failed to load initial corpus at {:?}", &seed_dir); - // return; - // }); - // println!("We imported {} inputs from disk.", state.corpus().count()); - // } - // fuzzer - // .fuzz_one(&mut tuple_list!(StdMutationalStage::new(BitFlipMutator::new())), &mut executor, &mut state, &mut mgr) - // .expect("Error in the fuzzing loop"); return Ok(()); } From 2c3e1a4e4d9b59d1d8434887d06cd2760cf60515 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 10 Feb 2022 14:41:04 +0100 Subject: [PATCH 24/83] no target map -> false --- fuzzers/wcet_qemu_sys/src/worst.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 45257d7fe2..af77b67415 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -177,7 +177,7 @@ where { let observer = _observers.match_name::>>("edges") .expect("HitcountsMapObserver not found"); - if self.target_map.len() == 0 { return Ok(true) }; + if self.target_map.len() == 0 { return Ok(false) }; let mut sum_of_square_difference : u64 = 0; // does not include found edges not in target for (edg, val) in &self.target_map { @@ -305,7 +305,7 @@ where { let observer = _observers.match_name::>>("edges") .expect("HitcountsMapObserver not found"); - if self.target_map.len() == 0 { return Ok(true) }; + if self.target_map.len() == 0 { return Ok(false) }; let mut sum_of_square_difference : u64 = 0; // does not include found edges not in target for (edg, val) in &self.target_map { From d7c0193a5eaa45d7f1a7e1b1eceb2d86a115266b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 10 Feb 2022 14:41:24 +0100 Subject: [PATCH 25/83] add debug prints --- libafl_qemu/src/clock.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libafl_qemu/src/clock.rs b/libafl_qemu/src/clock.rs index 9e05c4b959..3a9cdd803a 100644 --- a/libafl_qemu/src/clock.rs +++ b/libafl_qemu/src/clock.rs @@ -122,13 +122,7 @@ where fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { unsafe { self.last_runtime = emu::libafl_get_clock() }; - // if !_state.has_metadata::() { - // _state.add_metadata(QemuIcountMetadata{runtime: self.last_runtime()}); - // println!("Added Metadata"); - // } else { - // println!("Found Metadata"); - // } - // println!("Observer Clock: {}",self.last_runtime()); + #[cfg(debug_assertions)] println!("Observer Clock: {}",self.last_runtime()); Ok(()) } } From 5c5f1f77bd26e6fe559ea00fedac1a8c11011f0b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 11 Feb 2022 13:41:42 +0100 Subject: [PATCH 26/83] add feedback for decreasing bytes --- fuzzers/wcet_qemu_sys/src/lib.rs | 1 + fuzzers/wcet_qemu_sys/src/worst.rs | 69 ++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/lib.rs b/fuzzers/wcet_qemu_sys/src/lib.rs index 5c4c7d64f1..ab85cf7d2e 100644 --- a/fuzzers/wcet_qemu_sys/src/lib.rs +++ b/fuzzers/wcet_qemu_sys/src/lib.rs @@ -1,4 +1,5 @@ #![feature(iter_advance_by)] +#![feature(is_sorted)] #[cfg(target_os = "linux")] pub mod worst; pub mod freertos; diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index af77b67415..e7ef49345b 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -1,3 +1,6 @@ +use core::cmp::Ordering::{Greater,Less,Equal}; +use libafl::inputs::BytesInput; +use libafl::inputs::HasTargetBytes; use libafl::feedbacks::MapIndexesMetadata; use libafl::corpus::Testcase; use libafl::corpus::FavFactor; @@ -189,7 +192,7 @@ where let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); let hit_target = mean_sum_of_squares <= self.target_msd; if hit_target { - #[cfg(debug_assertions)] eprintln!("Hit Feedback trigger"); + // #[cfg(debug_assertions)] eprintln!("Hit Feedback trigger"); Ok(true) } else { Ok(false) @@ -317,7 +320,7 @@ where let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); let hit_target = mean_sum_of_squares < self.best_msd; if hit_target { - #[cfg(debug_assertions)] eprintln!("Hit Improving: {}",mean_sum_of_squares); + // #[cfg(debug_assertions)] eprintln!("Hit Improving: {}",mean_sum_of_squares); self.best_msd = mean_sum_of_squares; Ok(true) } else { @@ -432,7 +435,7 @@ where EM: EventFirer, OT: ObserversTuple, { - eprintln!("Input was: {:?}",_input); + // eprintln!("Input was: {:?}",_input); Ok(self.response) } } @@ -506,4 +509,62 @@ where let execs_times_length_per_hour = execs_per_hour*entry.cached_len()? as u64; Ok(execs_times_length_per_hour) } -} \ No newline at end of file +} + +/// A Feedback reporting if the Input consists of strictly decreasing bytes. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct SortedFeedback { +} + +impl Feedback for SortedFeedback +where + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &BytesInput, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let t = _input.target_bytes(); + let tmp = t.as_slice(); + if tmp.len()<32 {return Ok(false);} + let tmp = Vec::::from(&tmp[0..32]); + // tmp.reverse(); + if tmp.is_sorted_by(|a,b| match a.partial_cmp(b).unwrap_or(Less) { + Less => Some(Greater), + Equal => Some(Greater), + Greater => Some(Less), + }) {return Ok(true)}; + return Ok(false); + } +} + +impl Named for SortedFeedback { + #[inline] + fn name(&self) -> &str { + "Sorted" + } +} + +impl SortedFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new() -> Self { + Self {} + } +} + +impl Default for SortedFeedback { + fn default() -> Self { + Self::new() + } +} + +//=================================================================== \ No newline at end of file From 1713824c3b2bcaa200c0efa5096c69c7d2b77150 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 11 Feb 2022 15:13:34 +0100 Subject: [PATCH 27/83] fuzzer parallelization --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 167 ++++++++++++------------ libafl/src/bolts/llmp.rs | 11 +- 2 files changed, 89 insertions(+), 89 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 48eac07ef9..af43c4bd3b 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -18,9 +18,11 @@ use std::{ use libafl::{ bolts::{ current_nanos, current_time, - os::dup2, + os::{dup2,Cores}, rands::StdRand, tuples::{tuple_list, Merge}, + shmem::{ShMemProvider,StdShMemProvider}, + launcher::Launcher, }, corpus::{ Corpus, OnDiskCorpus, PowerQueueCorpusScheduler, @@ -30,7 +32,7 @@ use libafl::{ feedbacks::{MapFeedbackState, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, - monitors::SimpleMonitor, + monitors::{MultiMonitor,SimpleMonitor}, mutators::{ scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations, StdMOptMutator, StdScheduledMutator, Tokens, @@ -42,7 +44,8 @@ use libafl::{ ShadowTracingStage, StdMutationalStage, }, state::{HasCorpus, HasMetadata, StdState}, - events::SimpleEventManager, + events::{SimpleEventManager,EventConfig}, + generators::RandBytesGenerator, Error, }; use libafl_qemu::{ @@ -61,7 +64,7 @@ use libafl_qemu::{ clock::ClockFeedback, clock::QemuClockIncreaseFeedback }; -use wcet_qemu_sys::worst::{HitFeedback,HitcountsMapObserver,HitImprovingFeedback,LenTimeMaximizerCorpusScheduler}; +use wcet_qemu_sys::worst::{SortedFeedback,HitFeedback,HitcountsMapObserver,HitImprovingFeedback,LenTimeMaximizerCorpusScheduler}; /// The fuzzer main @@ -214,7 +217,27 @@ fn fuzz( ) -> Result<(), Error> { env::remove_var("LD_LIBRARY_PATH"); - //=========== Initialize the Emulator + //=========== Analyze the binary to find the target function address + let mut elf_buffer = Vec::new(); + let elf = EasyElf::from_file(&kernel, &mut elf_buffer)?; + + let input_addr = elf + .resolve_symbol("FUZZ_INPUT", 0) + .expect("Symbol FUZZ_INPUT not found"); + let input_addr = virt2phys(input_addr,&elf.goblin()); + println!("FUZZ_INPUT @ {:#x}", input_addr); + let test_length_ptr = elf + .resolve_symbol("FUZZ_LENGTH", 0) + .expect("Symbol FUZZ_LENGTH not found"); + let test_length_ptr = virt2phys(test_length_ptr,&elf.goblin()); + println!("FUZZ_LENGTH @ {:#x}", test_length_ptr); + let check_breakpoint = elf + .resolve_symbol("trigger_Qemu_break", 0) + .expect("Symbol trigger_Qemu_break not found"); + let check_breakpoint = virt2phys(check_breakpoint,&elf.goblin()); + println!("Breakpoint at {:#x}", check_breakpoint); + + //=========== Prepare Emulator Args let args: Vec = vec![ "qemu-system-arm", "-machine","mps2-an385", @@ -228,36 +251,8 @@ fn fuzz( "-S" ].iter().map(|x| x.to_string()).collect(); let env: Vec<(String, String)> = env::vars().collect(); - let emu = Emulator::new(&args, &env); - - //=========== Analyze the binary to find the target function address - let mut elf_buffer = Vec::new(); - let bin_path=kernel; - let elf = EasyElf::from_file(bin_path, &mut elf_buffer)?; - - let test_one_input_ptr = elf - .resolve_symbol("FUZZ_INPUT", 0) - .expect("Symbol FUZZ_INPUT not found"); - let test_one_input_ptr = virt2phys(test_one_input_ptr,&elf.goblin()); - println!("FUZZ_INPUT @ {:#x}", test_one_input_ptr); - let test_length_ptr = elf - .resolve_symbol("FUZZ_LENGTH", 0) - .expect("Symbol FUZZ_LENGTH not found"); - let test_length_ptr = virt2phys(test_length_ptr,&elf.goblin()); - println!("FUZZ_LENGTH @ {:#x}", test_length_ptr); - let check_breakpoint = elf - .resolve_symbol("trigger_Qemu_break", 0) - .expect("Symbol trigger_Qemu_break not found"); - let check_breakpoint = virt2phys(check_breakpoint,&elf.goblin()); - println!("Breakpoint at {:#x}", check_breakpoint); - //====== Note the input field - let input_addr = test_one_input_ptr; - println!("Placing input at {:#x}", input_addr); - emu.set_breakpoint(check_breakpoint); // trigger_Qemu_break - - //====== Setup log and stdout let log = RefCell::new( OpenOptions::new() .append(true) @@ -265,30 +260,18 @@ fn fuzz( .open(&logfile)?, ); - #[cfg(unix)] - let mut stdout_cpy = unsafe { - let new_fd = dup(io::stdout().as_raw_fd())?; - File::from_raw_fd(new_fd) - }; - #[cfg(unix)] - let file_null = File::open("/dev/null")?; - - //====== Create the most simple status display and managers. - - // 'While the stats are state, they are usually used in the broker - which is likely never restarted - let monitor = SimpleMonitor::new(|s| { - #[cfg(unix)] - writeln!(&mut stdout_cpy, "{}", s).unwrap(); - #[cfg(windows)] - println!("{}", s); - writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap(); + let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); + let monitor = MultiMonitor::new(|s| { + println!("{}",s); + writeln!(log.borrow_mut(), "{}",s).unwrap(); }); - // let mut shmem_provider = StdShMemProvider::new()?; - - //====== Create the most simple status display and managers. - let mut mgr = SimpleEventManager::new(monitor); + //====== Child Function + let mut run_client = |state: Option>, mut mgr, _core_id| { + //====== Set up Emu and termination-point + let emu = Emulator::new(&args, &env); + emu.set_breakpoint(check_breakpoint); // trigger_Qemu_break // Create an observation channel using the coverage map let edges = unsafe { &mut edges::EDGES_MAP }; let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM }; @@ -316,37 +299,32 @@ fn fuzz( }, }; let feedback = feedback_or!( + // New maximization map feedback linked to the edges observer and the feedback state MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), - HitImprovingFeedback::new(target_map.clone()), + // HitImprovingFeedback::new(target_map.clone()), QemuClockIncreaseFeedback::default(), ClockFeedback::new_with_observer(&clock_observer) ); - // let feedback = feedback_or!( - // // New maximization map feedback linked to the edges observer and the feedback state - // MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), - // MapHitIncreaseFeedback::new(), - // // Time feedback, this one does not need a feedback state - // TimeFeedback::new_with_observer(&time_observer) - // ); // A feedback to choose if an input is a solution or not - let objective = HitFeedback::new(target_map,0.0); + // let objective = HitFeedback::new(target_map,0.0); + let objective = SortedFeedback::new(); // create a State from scratch - let mut state = { + let mut state = state.unwrap_or_else(||{ StdState::new( // RNG StdRand::with_seed(current_nanos()), - // Corpus that will be evolved, we keep it in memory for performance - OnDiskCorpus::new(corpus_dir).unwrap(), + // Corpus that will be evolved + OnDiskCorpus::new(&corpus_dir).unwrap(), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer - OnDiskCorpus::new(objective_dir).unwrap(), + OnDiskCorpus::new(&objective_dir).unwrap(), // States of the feedbacks. // They are the data related to the feedbacks that you want to persist in the State. tuple_list!(feedback_state,clock::MaxIcountMetadata::default()), ) - }; + }); let calibration = CalibrationStage::new(&mut state, &edges_observer); @@ -407,20 +385,27 @@ fn fuzz( let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer)); // Read tokens - if let Some(tokenfile) = tokenfile { + if let Some(tokenfile) = &tokenfile { if state.metadata().get::().is_none() { - state.add_metadata(Tokens::from_tokens_file(tokenfile)?); + state.add_metadata(Tokens::from_tokens_file(&tokenfile)?); } } if state.corpus().count() < 1 { - state - .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]) - .unwrap_or_else(|_| { - println!("Failed to load initial corpus at {:?}", &seed_dir); - process::exit(0); - }); - println!("We imported {} inputs from disk.", state.corpus().count()); + if _core_id == 0 && state.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]).is_ok() { + // println!("We imported {} inputs from disk.", state.corpus().count()); + } else { + let mut generator = RandBytesGenerator::new(32); + state + .generate_initial_inputs( + &mut fuzzer, + &mut executor, + &mut generator, + &mut mgr, + 8, + ) + .expect("Failed to generate the initial corpus"); + } } let tracing = ShadowTracingStage::new(&mut executor); @@ -430,18 +415,13 @@ fn fuzz( // Remove target ouput (logs still survive) #[cfg(unix)] + let file_null = File::open("/dev/null")?; + #[cfg(unix)] { let null_fd = file_null.as_raw_fd(); dup2(null_fd, io::stdout().as_raw_fd())?; dup2(null_fd, io::stderr().as_raw_fd())?; } - // reopen file to make sure we're at the end - log.replace( - OpenOptions::new() - .append(true) - .create(true) - .open(&logfile)?, - ); fuzzer .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) @@ -449,4 +429,23 @@ fn fuzz( // Never reached Ok(()) + + }; + + match Launcher::builder() + .shmem_provider(shmem_provider) + .configuration(EventConfig::AlwaysUnique) + .monitor(monitor) + .run_client(&mut run_client) + .cores(&Cores::from_cmdline("all").unwrap()) + // .broker_port(1337) + // .remote_broker_addr(remote_broker_addr) + //.stdout_file(Some("/dev/null")) + .build() + .launch() + { + Ok(_) | Err(Error::ShuttingDown) => (), + Err(e) => panic!("{:?}", e), + }; + Ok(()) } diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index de7b75a189..fd3894a401 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -2219,11 +2219,12 @@ where loop { match listener.accept() { ListenerStream::Tcp(mut stream, addr) => { - eprintln!( - "New connection: {:?}/{:?}", - addr, - stream.peer_addr().unwrap() - ); + // For some reason stderr is not avalible in child + // eprintln!( + // "New connection: {:?}/{:?}", + // addr, + // stream.peer_addr().unwrap() + // ); // Send initial information, without anyone asking. // This makes it a tiny bit easier to map the broker map for new Clients. From c252d6cad0bb29770263765e91bab961e3e387c5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 11 Feb 2022 15:14:36 +0100 Subject: [PATCH 28/83] redirect log --- fuzzers/wcet_qemu_sys/fuzzer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/wcet_qemu_sys/fuzzer.sh b/fuzzers/wcet_qemu_sys/fuzzer.sh index 319fc1c1e9..ee53330472 100755 --- a/fuzzers/wcet_qemu_sys/fuzzer.sh +++ b/fuzzers/wcet_qemu_sys/fuzzer.sh @@ -1,4 +1,4 @@ mkdir -p tmp/test_in tmp/test_out [ ! -f tmp/test_in/test ] && echo " !test" > tmp/test_in/test [ ! -f tmp/dummy.qcow2 ] && qemu-img create -f qcow2 tmp/dummy.qcow2 32M -LD_LIBRARY_PATH=target/debug target/debug/fuzzer --libafl-snapshot tmp/dummy.qcow2 --libafl-out tmp/test_out --libafl-in tmp/test_in --libafl-kernel $@ +LD_LIBRARY_PATH=target/debug target/debug/fuzzer --libafl-logfile tmp/libafl.log --libafl-snapshot tmp/dummy.qcow2 --libafl-out tmp/test_out --libafl-in tmp/test_in --libafl-kernel $@ From bec4743978006590e0f2359802e0c5518c0f97da Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 11 Feb 2022 19:44:12 +0100 Subject: [PATCH 29/83] make generic edge-map post-processor --- fuzzers/wcet_qemu_sys/Cargo.toml | 1 + fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 11 +- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 7 +- fuzzers/wcet_qemu_sys/src/worst.rs | 297 ++++++++++++----------- 4 files changed, 170 insertions(+), 146 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 1fedc8942a..1f69ba6957 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -21,3 +21,4 @@ hashbrown = { version = "0.11", features = ["serde", "ahash-compile-time-rng"], nix = "0.23.0" goblin = "0.4.2" either = "1.6.1" +num-traits = "0.2" diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index af43c4bd3b..a5bf2c827c 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::worst::QemuHashMapObserver; use hashbrown::HashMap; use clap::{App, Arg}; use core::{cell::RefCell, time::Duration}; @@ -64,7 +65,7 @@ use libafl_qemu::{ clock::ClockFeedback, clock::QemuClockIncreaseFeedback }; -use wcet_qemu_sys::worst::{SortedFeedback,HitFeedback,HitcountsMapObserver,HitImprovingFeedback,LenTimeMaximizerCorpusScheduler}; +use wcet_qemu_sys::worst::{SortedFeedback,HitFeedback,HitImprovingFeedback,LenTimeMaximizerCorpusScheduler}; /// The fuzzer main @@ -276,7 +277,7 @@ fn fuzz( let edges = unsafe { &mut edges::EDGES_MAP }; let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM }; let edges_observer = - HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); + QemuHashMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); // Create an observation channel to keep track of the execution time // let time_observer = TimeObserver::new("time"); @@ -301,14 +302,14 @@ fn fuzz( let feedback = feedback_or!( // New maximization map feedback linked to the edges observer and the feedback state MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), - // HitImprovingFeedback::new(target_map.clone()), + HitImprovingFeedback::new(target_map.clone(), &edges_observer), QemuClockIncreaseFeedback::default(), ClockFeedback::new_with_observer(&clock_observer) ); // A feedback to choose if an input is a solution or not - // let objective = HitFeedback::new(target_map,0.0); - let objective = SortedFeedback::new(); + let objective = HitFeedback::new(target_map,0.0,&edges_observer); + // let objective = SortedFeedback::new(); // create a State from scratch let mut state = state.unwrap_or_else(||{ diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 3c00a127c9..8030fa7731 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -1,7 +1,8 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::{ - worst::{DumpMapFeedback,DummyFeedback,HitcountsMapObserver}, + worst::{DumpMapFeedback,DummyFeedback}, system_trace::QemuSystemStateHelper, }; use clap::{App, Arg}; @@ -262,13 +263,13 @@ fn fuzz( let edges = unsafe { &mut edges::EDGES_MAP }; let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM }; let edges_observer = - HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); + QemuHashMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); //========== Observe Execution Cycles let clock_observer = QemuClockObserver::default(); //========= Feedback-Function evaluate the Maps. Need to dump it for debugging and check if it reaches targets. - let feedback = DumpMapFeedback::with_dump(dump_edges); + let feedback = DumpMapFeedback::with_dump(dump_edges, &edges_observer); // A feedback to choose if an input is a solution or not let objective = DummyFeedback::new(false); diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index e7ef49345b..8d9eb3024f 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -1,3 +1,5 @@ +use num_traits::PrimInt; +use core::fmt::Debug; use core::cmp::Ordering::{Greater,Less,Equal}; use libafl::inputs::BytesInput; use libafl::inputs::HasTargetBytes; @@ -8,7 +10,6 @@ use core::marker::PhantomData; use libafl::corpus::MinimizerCorpusScheduler; use std::path::PathBuf; use std::fs; -use libafl::observers::VariableMapObserver; use hashbrown::{HashMap}; use libafl::observers::ObserversTuple; use libafl::executors::ExitKind; @@ -30,36 +31,23 @@ use libafl::{ observers::Observer, Error, }; - -/// Map observer with hitcounts postprocessing +//=================================================================== +/// A wrapper around some other [`MapObserver`], using [`QemuEdgesMapMetadata`] to offer a convinient Hashmap. #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(bound = "M: serde::de::DeserializeOwned")] -pub struct HitcountsMapObserver +pub struct QemuHashMapObserver where M: serde::Serialize + serde::de::DeserializeOwned, + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { base: M, - pub edgemap: HashMap<(u64,u64),u8>, + pub edgemap: HashMap<(u64,u64),T>, } -static COUNT_CLASS_LOOKUP: [u8; 256] = [ - 0, 1, 2, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, -]; - -impl Observer for HitcountsMapObserver +impl Observer for QemuHashMapObserver where - M: MapObserver + Observer, + M: MapObserver + Observer, + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, S: HasMetadata, // Need to grab the HashMap from a Helper { #[inline] @@ -70,25 +58,18 @@ where #[inline] fn post_exec(&mut self, state: &mut S, input: &I) -> Result<(), Error> { - //new stuff - let original_hashmap=&state.metadata().get::().unwrap().map; + let original_hashmap=&state.metadata().get::().expect("QemuEdgesMapMetadata not found").map; for (key, val) in original_hashmap.iter() { self.edgemap.insert(*key,*self.base.get(*val as usize)); } - // println!("Post-Exec Len: {} Cap: {}",self.edgemap.len(),self.edgemap.capacity()); - // println!("{:#?}",self.edgemap); - //end new stuff - let cnt = self.usable_count(); - for i in 0..cnt { - *self.get_mut(i) = COUNT_CLASS_LOOKUP[*self.get(i) as usize]; - } self.base.post_exec(state, input) } } -impl Named for HitcountsMapObserver +impl Named for QemuHashMapObserver where M: Named + serde::Serialize + serde::de::DeserializeOwned, + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { #[inline] fn name(&self) -> &str { @@ -96,9 +77,10 @@ where } } -impl HasLen for HitcountsMapObserver +impl HasLen for QemuHashMapObserver where M: MapObserver, + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { #[inline] fn len(&self) -> usize { @@ -106,9 +88,10 @@ where } } -impl MapObserver for HitcountsMapObserver +impl MapObserver for QemuHashMapObserver where M: MapObserver, + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { #[inline] fn map(&self) -> Option<&[u8]> { @@ -141,9 +124,10 @@ where } } -impl HitcountsMapObserver +impl QemuHashMapObserver where M: serde::Serialize + serde::de::DeserializeOwned, + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { /// Creates a new [`MapObserver`] pub fn new(base: M) -> Self { @@ -156,15 +140,23 @@ where /// A [`HitFeedback`] reports as interesting when all predicted worst case edges have been matched. #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct HitFeedback { - target_map: HashMap<(u64,u64),u8>, +#[serde(bound = "T: serde::de::DeserializeOwned")] +pub struct HitFeedback +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, +{ + target_map: HashMap<(u64,u64),T>, target_msd: f64, + phantom: PhantomData<(O, T)>, } -impl Feedback for HitFeedback +impl Feedback for HitFeedback where I: Input, S: HasClientPerfMonitor, + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, { fn is_interesting( &mut self, @@ -178,15 +170,17 @@ where EM: EventFirer, OT: ObserversTuple, { - let observer = _observers.match_name::>>("edges") - .expect("HitcountsMapObserver not found"); + let observer = _observers.match_name::>("edges") + .expect("QemuHashMapObserver not found"); if self.target_map.len() == 0 { return Ok(false) }; let mut sum_of_square_difference : u64 = 0; // does not include found edges not in target for (edg, val) in &self.target_map { match observer.edgemap.get(&edg) { - Some(x) => sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)) as u64).pow(2), - None => sum_of_square_difference+=(*val as u64).pow(2), + Some(x) => { + sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)).to_u64().unwrap()).pow(2); + }, + None => sum_of_square_difference+=((*val).to_u64().unwrap()).pow(2), } } let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); @@ -200,99 +194,111 @@ where } } -impl Named for HitFeedback { +impl Named for HitFeedback +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, +{ #[inline] fn name(&self) -> &str { "HitFeedback" } } -impl HitFeedback { +impl HitFeedback +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, +{ /// Creates a new [`HitFeedback`] #[must_use] - pub fn new(target_map: HashMap<(u64,u64),u8>, target_msd: f64) -> Self { - Self {target_map: target_map, target_msd: target_msd} - } -} - -impl Default for HitFeedback { - fn default() -> Self { - Self::new(HashMap::new(),0.0) + pub fn new(target_map: HashMap<(u64,u64),T>, target_msd: f64, _map_observer: &O) -> Self { + Self {target_map: target_map, target_msd: target_msd, phantom: PhantomData} } } //=================================================================== -/// A [`MapHitIncreaseFeedback`] reports as interesting when the total number of used edges increases. -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct MapHitIncreaseFeedback { - record_high : u64, -} +// /// A [`MapHitIncreaseFeedback`] reports as interesting when the total number of used edges increases. +// #[derive(Serialize, Deserialize, Clone, Debug)] +// pub struct MapHitIncreaseFeedback { +// record_high : u64, +// } -impl Feedback for MapHitIncreaseFeedback -where - I: Input, - S: HasClientPerfMonitor, -{ - fn is_interesting( - &mut self, - _state: &mut S, - _manager: &mut EM, - _input: &I, - _observers: &OT, - _exit_kind: &ExitKind, - ) -> Result - where - EM: EventFirer, - OT: ObserversTuple, - { - let observer = _observers.match_name::>>("edges") - .expect("HitcountsMapObserver not found"); - let cur = observer.edgemap.values().fold(0,|a,b| a+(*b as u64)); - if cur > self.record_high { - self.record_high = cur; - return Ok(true); - } - return Ok(false); - } -} +// impl Feedback for MapHitIncreaseFeedback +// where +// I: Input, +// S: HasClientPerfMonitor, +// T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, +// O: MapObserver, +// { +// fn is_interesting( +// &mut self, +// _state: &mut S, +// _manager: &mut EM, +// _input: &I, +// _observers: &OT, +// _exit_kind: &ExitKind, +// ) -> Result +// where +// EM: EventFirer, +// OT: ObserversTuple, +// { +// let observer = _observers.match_name::>("edges") +// .expect("QemuHashMapObserver not found"); +// let cur = observer.edgemap.values().fold(0,|a,b| a+(*b as u64)); +// if cur > self.record_high { +// self.record_high = cur; +// return Ok(true); +// } +// return Ok(false); +// } +// } -impl Named for MapHitIncreaseFeedback { - #[inline] - fn name(&self) -> &str { - "HitFeedback" - } -} +// impl Named for MapHitIncreaseFeedback { +// #[inline] +// fn name(&self) -> &str { +// "HitFeedback" +// } +// } -impl MapHitIncreaseFeedback { - /// Creates a new [`HitFeedback`] - #[must_use] - pub fn new() -> Self { - Self {record_high: 0} - } -} +// impl MapHitIncreaseFeedback { +// /// Creates a new [`HitFeedback`] +// #[must_use] +// pub fn new() -> Self { +// Self {record_high: 0} +// } +// } -impl Default for MapHitIncreaseFeedback { - fn default() -> Self { - Self::new() - } -} +// impl Default for MapHitIncreaseFeedback { +// fn default() -> Self { +// Self::new() +// } +// } //=================================================================== /// A [`HitFeedback`] reports as interesting when all predicted worst case edges have been matched. #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct HitImprovingFeedback { - target_map: HashMap<(u64,u64),u8>, +#[serde(bound = "T: serde::de::DeserializeOwned")] +pub struct HitImprovingFeedback +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, +{ + target_map: HashMap<(u64,u64),T>, best_msd: f64, + phantom: PhantomData<(O, T)>, } -impl Feedback for HitImprovingFeedback +impl Feedback for HitImprovingFeedback where I: Input, S: HasClientPerfMonitor, + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, { fn is_interesting( &mut self, @@ -306,15 +312,17 @@ where EM: EventFirer, OT: ObserversTuple, { - let observer = _observers.match_name::>>("edges") - .expect("HitcountsMapObserver not found"); + let observer = _observers.match_name::>("edges") + .expect("QemuHashMapObserver not found"); if self.target_map.len() == 0 { return Ok(false) }; let mut sum_of_square_difference : u64 = 0; // does not include found edges not in target for (edg, val) in &self.target_map { match observer.edgemap.get(&edg) { - Some(x) => sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)) as u64).pow(2), - None => sum_of_square_difference+=(*val as u64).pow(2), + Some(x) => { + sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)).to_u64().unwrap()).pow(2); + }, + None => sum_of_square_difference+=((*val).to_u64().unwrap()).pow(2), } } let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); @@ -329,38 +337,47 @@ where } } -impl Named for HitImprovingFeedback { +impl Named for HitImprovingFeedback +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, +{ #[inline] fn name(&self) -> &str { - "HitFeedback" + "HitImprovingFeedback" } } -impl HitImprovingFeedback { - /// Creates a new [`HitFeedback`] +impl HitImprovingFeedback +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, +{ + /// Creates a new [`HitImprovingFeedback`] #[must_use] - pub fn new(target_map: HashMap<(u64,u64),u8>) -> Self { - Self {target_map: target_map, best_msd: f64::MAX} - } -} - -impl Default for HitImprovingFeedback { - fn default() -> Self { - Self::new(HashMap::new()) + pub fn new(target_map: HashMap<(u64,u64),T>, _map_observer: &O) -> Self { + Self {target_map: target_map, best_msd: f64::MAX, phantom: PhantomData} } } //=========================== Debugging Feedback /// A [`Feedback`] meant to dump the edgemap for debugging. #[derive(Debug)] -pub struct DumpMapFeedback { - dumpfile: Option +pub struct DumpMapFeedback +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, +{ + dumpfile: Option, + phantom: PhantomData<(O, T)>, } -impl Feedback for DumpMapFeedback +impl Feedback for DumpMapFeedback where I: Input, S: HasClientPerfMonitor, + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, { fn is_interesting( &mut self, @@ -374,8 +391,8 @@ where EM: EventFirer, OT: ObserversTuple, { - let observer = _observers.match_name::>>("edges") - .expect("HitcountsMapObserver not found"); + let observer = _observers.match_name::>("edges") + .expect("QemuHashMapObserver not found"); match &self.dumpfile { Some(s) => { fs::write(s,ron::to_string(&observer.edgemap).expect("Error serializing hashmap")).expect("Can not dump to file"); @@ -387,27 +404,29 @@ where } } -impl Named for DumpMapFeedback { +impl Named for DumpMapFeedback +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, +{ #[inline] fn name(&self) -> &str { "HitFeedback" } } -impl DumpMapFeedback { +impl DumpMapFeedback +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + O: MapObserver, +{ /// Creates a new [`HitFeedback`] #[must_use] - pub fn new() -> Self { - Self {dumpfile: None} + pub fn new(_map_observer: &O) -> Self { + Self {dumpfile: None, phantom: PhantomData} } - pub fn with_dump(dumpfile: Option) -> Self { - Self {dumpfile: dumpfile} - } -} - -impl Default for DumpMapFeedback { - fn default() -> Self { - Self::new() + pub fn with_dump(dumpfile: Option,_map_observer: &O) -> Self { + Self {dumpfile: dumpfile, phantom: PhantomData} } } @@ -511,6 +530,8 @@ where } } +//=================================================================== + /// A Feedback reporting if the Input consists of strictly decreasing bytes. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct SortedFeedback { From 819f759be9a7a3c052d8ea34d3cdc68247bd2365 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 13 Feb 2022 20:18:27 +0100 Subject: [PATCH 30/83] fix GEN_BLOCK_HOOK_PTR --- libafl_qemu/src/executor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 2c782bdb2f..21dce9e53f 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -68,7 +68,7 @@ where let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); - let func: fn(&Emulator, &mut QT, &mut S, u64) -> Option = transmute(GEN_EDGE_HOOK_PTR); + let func: fn(&Emulator, &mut QT, &mut S, u64) -> Option = transmute(GEN_BLOCK_HOOK_PTR); (func)(&emulator, helpers, state, pc).map_or(SKIP_EXEC_HOOK, |id| id) } } From c3f398a3150eb55bbe9fb63e0b9cb4c2659d538b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 13 Feb 2022 20:19:50 +0100 Subject: [PATCH 31/83] prepare system state observer+feedback --- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 3 +- fuzzers/wcet_qemu_sys/src/system_trace.rs | 165 +++++++++++++++++++--- 2 files changed, 148 insertions(+), 20 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 8030fa7731..39b23c933f 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::system_trace::QemuSysStateObserver; use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::{ worst::{DumpMapFeedback,DummyFeedback}, @@ -328,7 +329,7 @@ fn fuzz( QemuSysSnapshotHelper::new(), QemuSystemStateHelper::with_instrumentation_filter(system_state_filter,curr_tcb_pointer.try_into().unwrap(),task_queue_addr.try_into().unwrap()) ), - tuple_list!(edges_observer,clock_observer), + tuple_list!(edges_observer,clock_observer,QemuSysStateObserver::new()), &mut fuzzer, &mut state, &mut mgr, diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs index 8b0c4860c8..8cc16d3360 100644 --- a/fuzzers/wcet_qemu_sys/src/system_trace.rs +++ b/fuzzers/wcet_qemu_sys/src/system_trace.rs @@ -1,3 +1,10 @@ +use libafl::events::EventFirer; +use libafl::state::HasClientPerfMonitor; +use libafl::feedbacks::Feedback; +use libafl::bolts::HasLen; +use libafl::bolts::tuples::Named; +use libafl::Error; +use libafl::observers::Observer; use crate::freertos::rtos_struct::*; use crate::freertos; use hashbrown::HashMap; @@ -5,27 +12,36 @@ use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, stat use serde::{Deserialize, Serialize}; use libafl_qemu::{ + emu, emu::Emulator, executor::QemuExecutor, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, }; +//============================= Datatypes + +/// Info Dump from Qemu +pub type SysState = (u64,freertos::TCB_t,HashMap); + +/// Refined information about the states an execution transitioned between #[derive(Debug, Default, Serialize, Deserialize)] pub struct QemuSystemStateMetadata { - pub rtos_states: Vec<(freertos::TCB_t,HashMap)>, } impl QemuSystemStateMetadata { #[must_use] pub fn new() -> Self { - Self { - rtos_states: Vec::new(), - } + Self {} } } libafl::impl_serdeany!(QemuSystemStateMetadata); + +//============================= Qemu Helper +static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; + +/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur #[derive(Debug)] pub struct QemuSystemStateHelper { filter: QemuInstrumentationFilter, @@ -65,6 +81,8 @@ where OT: ObserversTuple, QT: QemuHelperTuple, { + // emu::Emulator{_private: ()}.set_gen_block_hook(test_gen_hook); + executor.hook_block_generation(gen_not_exec_block_hook::); executor.hook_block_execution(exec_syscall_hook::); } } @@ -84,26 +102,135 @@ where if !h.must_instrument(pc) { return; } - if state.metadata().get::().is_none() { - state.add_metadata(QemuSystemStateMetadata::new()); - } - let meta = state - .metadata_mut() - .get_mut::() - .unwrap(); + let current_clock = emulator.get_ticks(); let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); - // println!("Current TCB addr: {:x}",curr_tcb_addr); let current_tcb : freertos::TCB_t = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); - println!("{:?}",current_tcb); - println!("{}", std::str::from_utf8(¤t_tcb.pcTaskName).unwrap()); - let mut result_tup : (freertos::TCB_t,HashMap) = (current_tcb, HashMap::with_capacity(5)); + let mut result_tup : SysState = (current_clock,current_tcb, HashMap::with_capacity(5)); for i in 0..4 { let target : u32= (std::mem::size_of::()*i).try_into().unwrap(); let ready_list : freertos::List_t = freertos::emu_lookup::lookup(emulator, h.ready_queues+target); let a : freertos::rtos_struct = List_struct(ready_list); - result_tup.1.insert(target,a); - // println!("{:?}",ready_list); - // println!("Prio: {} Number: {} first {:x}",i,ready_list.uxNumberOfItems, ready_list.pxIndex); + result_tup.2.insert(target,a); } - meta.rtos_states.push(result_tup); + + unsafe { CURRENT_SYSSTATE_VEC.push(result_tup); } } + +extern "C" fn test_gen_hook(pc: u64) -> u64 { + u32::MAX as u64 +} + +pub fn gen_not_exec_block_hook( + _emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + pc: u64, +) +-> Option where + S: HasMetadata, + I: Input, + QT: QemuHelperTuple, +{ + let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + if !h.must_instrument(pc) { + None + } else {Some(1)} +} + +//============================= Observer + +/// The QemuSysState Observer retrieves the SysState +/// that will get updated by the target. +#[derive(Serialize, Deserialize, Debug, Default)] +#[allow(clippy::unsafe_derive_deserialize)] +pub struct QemuSysStateObserver +{ + last_run: Vec, + name: String, +} + +impl Observer for QemuSysStateObserver +{ + #[inline] + fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + unsafe {CURRENT_SYSSTATE_VEC.clear(); } + Ok(()) + } + + #[inline] + fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + unsafe {self.last_run.append(&mut CURRENT_SYSSTATE_VEC);} + Ok(()) + } +} + +impl Named for QemuSysStateObserver +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl HasLen for QemuSysStateObserver +{ + #[inline] + fn len(&self) -> usize { + self.last_run.len() + } +} + +impl QemuSysStateObserver { + pub fn new() -> Self { + Self{last_run: vec![], name: "sysstate".to_string()} + } +} + +//============================= Feedback + +/// A Feedback reporting interesting System-State Transitions +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct SysStateFeedback +{ +} + +impl Feedback for SysStateFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = _observers.match_name::("sysstate") + .expect("QemuSysStateObserver not found"); + // Do Stuff + Ok(false) + } +} + +impl Named for SysStateFeedback +{ + #[inline] + fn name(&self) -> &str { + "sysstate" + } +} + +impl SysStateFeedback +{ + /// Creates a new [`SysStateFeedback`] + #[must_use] + pub fn new() -> Self { + Self {} + } +} \ No newline at end of file From be1ead84f46da503ecbf7f800cc9d7b50a7797ff Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 13 Feb 2022 20:20:09 +0100 Subject: [PATCH 32/83] fix some types --- fuzzers/wcet_qemu_sys/src/worst.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 8d9eb3024f..1d1d0d73f5 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -212,7 +212,7 @@ where { /// Creates a new [`HitFeedback`] #[must_use] - pub fn new(target_map: HashMap<(u64,u64),T>, target_msd: f64, _map_observer: &O) -> Self { + pub fn new(target_map: HashMap<(u64,u64),T>, target_msd: f64, _map_observer: &QemuHashMapObserver) -> Self { Self {target_map: target_map, target_msd: target_msd, phantom: PhantomData} } } @@ -355,7 +355,7 @@ where { /// Creates a new [`HitImprovingFeedback`] #[must_use] - pub fn new(target_map: HashMap<(u64,u64),T>, _map_observer: &O) -> Self { + pub fn new(target_map: HashMap<(u64,u64),T>, _map_observer: &QemuHashMapObserver) -> Self { Self {target_map: target_map, best_msd: f64::MAX, phantom: PhantomData} } } @@ -422,10 +422,10 @@ where { /// Creates a new [`HitFeedback`] #[must_use] - pub fn new(_map_observer: &O) -> Self { + pub fn new(_map_observer: &QemuHashMapObserver) -> Self { Self {dumpfile: None, phantom: PhantomData} } - pub fn with_dump(dumpfile: Option,_map_observer: &O) -> Self { + pub fn with_dump(dumpfile: Option,_map_observer: &QemuHashMapObserver) -> Self { Self {dumpfile: dumpfile, phantom: PhantomData} } } From 44faa805565815037382848af8aea2a63125b232 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 15 Feb 2022 23:23:52 +0100 Subject: [PATCH 33/83] dump ready lists --- fuzzers/wcet_qemu_sys/src/freertos.rs | 12 +++++ fuzzers/wcet_qemu_sys/src/system_trace.rs | 57 ++++++++++++++++++----- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/freertos.rs index 230b5b0235..9833d9240a 100644 --- a/fuzzers/wcet_qemu_sys/src/freertos.rs +++ b/fuzzers/wcet_qemu_sys/src/freertos.rs @@ -538,8 +538,20 @@ impl emu_lookup for List_t { } } +impl emu_lookup for MiniListItem_t { + fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> MiniListItem_t { + let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; + unsafe { + emu.read_mem(addr.into(), &mut tmp); + std::mem::transmute::<[u8; std::mem::size_of::()], MiniListItem_t>(tmp) + } + } +} + #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum rtos_struct { TCB_struct(TCB_t), List_struct(List_t), + List_Item_struct(ListItem_t), + List_MiniItem_struct(MiniListItem_t), } \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs index 8cc16d3360..0b8d6d4de6 100644 --- a/fuzzers/wcet_qemu_sys/src/system_trace.rs +++ b/fuzzers/wcet_qemu_sys/src/system_trace.rs @@ -1,3 +1,5 @@ +use crate::freertos::TCB_t; +use crate::freertos::rtos_struct::List_Item_struct; use libafl::events::EventFirer; use libafl::state::HasClientPerfMonitor; use libafl::feedbacks::Feedback; @@ -18,10 +20,19 @@ use libafl_qemu::{ helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, }; +const NUM_PRIOS: usize = 5; + //============================= Datatypes /// Info Dump from Qemu -pub type SysState = (u64,freertos::TCB_t,HashMap); +// pub type SysState = (u64,freertos::TCB_t,HashMap); +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct FreeRTOSSystemStateRaw { + qemu_tick: u64, + current_tcb: TCB_t, + prio_ready_lists: [freertos::List_t; NUM_PRIOS], + dumping_ground: HashMap, +} /// Refined information about the states an execution transitioned between #[derive(Debug, Default, Serialize, Deserialize)] @@ -39,7 +50,7 @@ libafl::impl_serdeany!(QemuSystemStateMetadata); //============================= Qemu Helper -static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; +static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; /// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur #[derive(Debug)] @@ -102,18 +113,40 @@ where if !h.must_instrument(pc) { return; } - let current_clock = emulator.get_ticks(); + let LISTBYTES : u32 = u32::try_from(std::mem::size_of::()).unwrap(); + let mut sysstate = FreeRTOSSystemStateRaw::default(); + sysstate.qemu_tick = emulator.get_ticks(); + let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); - let current_tcb : freertos::TCB_t = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); - let mut result_tup : SysState = (current_clock,current_tcb, HashMap::with_capacity(5)); - for i in 0..4 { - let target : u32= (std::mem::size_of::()*i).try_into().unwrap(); - let ready_list : freertos::List_t = freertos::emu_lookup::lookup(emulator, h.ready_queues+target); - let a : freertos::rtos_struct = List_struct(ready_list); - result_tup.2.insert(target,a); + sysstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); + // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); + + for i in 0..NUM_PRIOS { + let mut target : u32 = LISTBYTES*u32::try_from(i).unwrap()+h.ready_queues; + sysstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target); + // println!("List at {}: {:?}",target, sysstate.prio_ready_lists[i]); + let mut next_index = sysstate.prio_ready_lists[i].pxIndex; + for _j in 0..sysstate.prio_ready_lists[i].uxNumberOfItems { + // always jump over the xListEnd marker + if (target..target+LISTBYTES).contains(&next_index) { + let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + let new_next_index=next_item.pxNext; + sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); + next_index = new_next_index; + } + let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + // println!("Item at {}: {:?}",next_index,next_item); + assert_eq!(next_item.pvContainer,target); + let new_next_index=next_item.pxNext; + let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner); + // println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb); + sysstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone())); + sysstate.dumping_ground.insert(next_index,List_Item_struct(next_item)); + next_index=new_next_index; + } } - unsafe { CURRENT_SYSSTATE_VEC.push(result_tup); } + unsafe { CURRENT_SYSSTATE_VEC.push(sysstate); } } extern "C" fn test_gen_hook(pc: u64) -> u64 { @@ -145,7 +178,7 @@ pub fn gen_not_exec_block_hook( #[allow(clippy::unsafe_derive_deserialize)] pub struct QemuSysStateObserver { - last_run: Vec, + last_run: Vec, name: String, } From 1650545424b25c3bce892f4e72e08d6179eb1077 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 15 Feb 2022 23:39:48 +0100 Subject: [PATCH 34/83] emu lookup as macro --- fuzzers/wcet_qemu_sys/src/freertos.rs | 71 ++++++++------------------- 1 file changed, 21 insertions(+), 50 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/freertos.rs index 9833d9240a..332ef5b672 100644 --- a/fuzzers/wcet_qemu_sys/src/freertos.rs +++ b/fuzzers/wcet_qemu_sys/src/freertos.rs @@ -498,55 +498,6 @@ pub trait emu_lookup { fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> Self; } -impl emu_lookup for xLIST_ITEM { - fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> xLIST_ITEM { - let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; - unsafe { - emu.read_mem(addr.into(), &mut tmp); - std::mem::transmute::<[u8; std::mem::size_of::()], xLIST_ITEM>(tmp) - } - } -} - -impl emu_lookup for TCB_t { - fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> TCB_t { - let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; - unsafe { - emu.read_mem(addr.into(), &mut tmp); - std::mem::transmute::<[u8; std::mem::size_of::()], TCB_t>(tmp) - } - } -} - -impl emu_lookup for void_ptr { - fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> void_ptr { - let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; - unsafe { - emu.read_mem(addr.into(), &mut tmp); - std::mem::transmute::<[u8; std::mem::size_of::()], void_ptr>(tmp) - } - } -} - -impl emu_lookup for List_t { - fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> List_t { - let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; - unsafe { - emu.read_mem(addr.into(), &mut tmp); - std::mem::transmute::<[u8; std::mem::size_of::()], List_t>(tmp) - } - } -} - -impl emu_lookup for MiniListItem_t { - fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> MiniListItem_t { - let mut tmp : [u8; std::mem::size_of::()] = [0u8; std::mem::size_of::()]; - unsafe { - emu.read_mem(addr.into(), &mut tmp); - std::mem::transmute::<[u8; std::mem::size_of::()], MiniListItem_t>(tmp) - } - } -} #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum rtos_struct { @@ -554,4 +505,24 @@ pub enum rtos_struct { List_struct(List_t), List_Item_struct(ListItem_t), List_MiniItem_struct(MiniListItem_t), -} \ No newline at end of file +} + +#[macro_export] +macro_rules! impl_emu_lookup { + ($struct_name:ident) => { + impl $crate::freertos::emu_lookup for $struct_name { + fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> $struct_name { + let mut tmp : [u8; std::mem::size_of::<$struct_name>()] = [0u8; std::mem::size_of::<$struct_name>()]; + unsafe { + emu.read_mem(addr.into(), &mut tmp); + std::mem::transmute::<[u8; std::mem::size_of::<$struct_name>()], $struct_name>(tmp) + } + } + } + }; +} +impl_emu_lookup!(TCB_t); +impl_emu_lookup!(List_t); +impl_emu_lookup!(ListItem_t); +impl_emu_lookup!(MiniListItem_t); +impl_emu_lookup!(void_ptr); \ No newline at end of file From 5bf3269c8fc705c96765bfd7eba4b833ad94050d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 16 Feb 2022 15:18:45 +0100 Subject: [PATCH 35/83] slim down generated code --- fuzzers/wcet_qemu_sys/src/freertos.rs | 425 +------------------------- 1 file changed, 9 insertions(+), 416 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/freertos.rs index 332ef5b672..aeeb2d51ba 100644 --- a/fuzzers/wcet_qemu_sys/src/freertos.rs +++ b/fuzzers/wcet_qemu_sys/src/freertos.rs @@ -3,16 +3,14 @@ use serde::{Deserialize, Serialize}; // Manual Types use libafl_qemu::Emulator; -pub type xLIST_ITEM_ptr = ::std::os::raw::c_uint; -pub type xLIST_ptr = ::std::os::raw::c_uint; +/*========== Start of generated Code =============*/ +pub type char_ptr = ::std::os::raw::c_uint; pub type ListItem_t_ptr = ::std::os::raw::c_uint; pub type StackType_t_ptr = ::std::os::raw::c_uint; -pub type char_ptr = ::std::os::raw::c_uint; pub type void_ptr = ::std::os::raw::c_uint; -pub type TCB_t_ptr = ::std::os::raw::c_uint; - - - +pub type tskTaskControlBlock_ptr = ::std::os::raw::c_uint; +pub type xLIST_ptr = ::std::os::raw::c_uint; +pub type xLIST_ITEM_ptr = ::std::os::raw::c_uint; /* automatically generated by rust-bindgen 0.59.2 */ pub type __uint8_t = ::std::os::raw::c_uchar; @@ -30,69 +28,6 @@ pub struct xLIST_ITEM { pub pvOwner: void_ptr, pub pvContainer: xLIST_ptr, } -#[test] -fn bindgen_test_layout_xLIST_ITEM() { - assert_eq!( - ::std::mem::size_of::(), - 40usize, - concat!("Size of: ", stringify!(xLIST_ITEM)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(xLIST_ITEM)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).xItemValue as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(xLIST_ITEM), - "::", - stringify!(xItemValue) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pxNext as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(xLIST_ITEM), - "::", - stringify!(pxNext) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pxPrevious as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(xLIST_ITEM), - "::", - stringify!(pxPrevious) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pvOwner as *const _ as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(xLIST_ITEM), - "::", - stringify!(pvOwner) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pvContainer as *const _ as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(xLIST_ITEM), - "::", - stringify!(pvContainer) - ) - ); -} pub type ListItem_t = xLIST_ITEM; #[repr(C)] #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] @@ -101,49 +36,6 @@ pub struct xMINI_LIST_ITEM { pub pxNext: xLIST_ITEM_ptr, pub pxPrevious: xLIST_ITEM_ptr, } -#[test] -fn bindgen_test_layout_xMINI_LIST_ITEM() { - assert_eq!( - ::std::mem::size_of::(), - 24usize, - concat!("Size of: ", stringify!(xMINI_LIST_ITEM)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(xMINI_LIST_ITEM)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).xItemValue as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(xMINI_LIST_ITEM), - "::", - stringify!(xItemValue) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pxNext as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(xMINI_LIST_ITEM), - "::", - stringify!(pxNext) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pxPrevious as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(xMINI_LIST_ITEM), - "::", - stringify!(pxPrevious) - ) - ); -} pub type MiniListItem_t = xMINI_LIST_ITEM; #[repr(C)] #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] @@ -152,51 +44,8 @@ pub struct xLIST { pub pxIndex: ListItem_t_ptr, pub xListEnd: MiniListItem_t, } -#[test] -fn bindgen_test_layout_xLIST() { - assert_eq!( - ::std::mem::size_of::(), - 40usize, - concat!("Size of: ", stringify!(xLIST)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(xLIST)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).uxNumberOfItems as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(xLIST), - "::", - stringify!(uxNumberOfItems) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pxIndex as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(xLIST), - "::", - stringify!(pxIndex) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).xListEnd as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(xLIST), - "::", - stringify!(xListEnd) - ) - ); -} pub type List_t = xLIST; -pub type TaskHandle_t = ::std::os::raw::c_uint; +pub type TaskHandle_t = tskTaskControlBlock_ptr; pub const eTaskState_eRunning: eTaskState = 0; pub const eTaskState_eReady: eTaskState = 1; pub const eTaskState_eBlocked: eTaskState = 2; @@ -205,7 +54,7 @@ pub const eTaskState_eDeleted: eTaskState = 4; pub const eTaskState_eInvalid: eTaskState = 5; pub type eTaskState = ::std::os::raw::c_uint; #[repr(C)] -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] pub struct xTASK_STATUS { pub xHandle: TaskHandle_t, pub pcTaskName: char_ptr, @@ -217,111 +66,6 @@ pub struct xTASK_STATUS { pub pxStackBase: StackType_t_ptr, pub usStackHighWaterMark: u16, } -#[test] -fn bindgen_test_layout_xTASK_STATUS() { - assert_eq!( - ::std::mem::size_of::(), - 72usize, - concat!("Size of: ", stringify!(xTASK_STATUS)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(xTASK_STATUS)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).xHandle as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(xTASK_STATUS), - "::", - stringify!(xHandle) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pcTaskName as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(xTASK_STATUS), - "::", - stringify!(pcTaskName) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).xTaskNumber as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(xTASK_STATUS), - "::", - stringify!(xTaskNumber) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).eCurrentState as *const _ as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(xTASK_STATUS), - "::", - stringify!(eCurrentState) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).uxCurrentPriority as *const _ as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(xTASK_STATUS), - "::", - stringify!(uxCurrentPriority) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).uxBasePriority as *const _ as usize }, - 40usize, - concat!( - "Offset of field: ", - stringify!(xTASK_STATUS), - "::", - stringify!(uxBasePriority) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).ulRunTimeCounter as *const _ as usize }, - 48usize, - concat!( - "Offset of field: ", - stringify!(xTASK_STATUS), - "::", - stringify!(ulRunTimeCounter) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pxStackBase as *const _ as usize }, - 56usize, - concat!( - "Offset of field: ", - stringify!(xTASK_STATUS), - "::", - stringify!(pxStackBase) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).usStackHighWaterMark as *const _ as usize - }, - 64usize, - concat!( - "Offset of field: ", - stringify!(xTASK_STATUS), - "::", - stringify!(usStackHighWaterMark) - ) - ); -} pub type TaskStatus_t = xTASK_STATUS; #[repr(C)] #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] @@ -331,7 +75,7 @@ pub struct tskTaskControlBlock { pub xEventListItem: ListItem_t, pub uxPriority: UBaseType_t, pub pxStack: StackType_t_ptr, - pub pcTaskName: [::std::os::raw::c_uchar; 10usize], + pub pcTaskName: [::std::os::raw::c_char; 10usize], pub uxBasePriority: UBaseType_t, pub uxMutexesHeld: UBaseType_t, pub ulNotifiedValue: [u32; 1usize], @@ -339,160 +83,9 @@ pub struct tskTaskControlBlock { pub ucStaticallyAllocated: u8, pub ucDelayAborted: u8, } -#[test] -fn bindgen_test_layout_tskTaskControlBlock() { - assert_eq!( - ::std::mem::size_of::(), - 152usize, - concat!("Size of: ", stringify!(tskTaskControlBlock)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(tskTaskControlBlock)) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).pxTopOfStack as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(pxTopOfStack) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).xStateListItem as *const _ as usize - }, - 8usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(xStateListItem) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).xEventListItem as *const _ as usize - }, - 48usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(xEventListItem) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).uxPriority as *const _ as usize }, - 88usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(uxPriority) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pxStack as *const _ as usize }, - 96usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(pxStack) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pcTaskName as *const _ as usize }, - 104usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(pcTaskName) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).uxBasePriority as *const _ as usize - }, - 120usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(uxBasePriority) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).uxMutexesHeld as *const _ as usize - }, - 128usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(uxMutexesHeld) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).ulNotifiedValue as *const _ as usize - }, - 136usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(ulNotifiedValue) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).ucNotifyState as *const _ as usize - }, - 144usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(ucNotifyState) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).ucStaticallyAllocated as *const _ - as usize - }, - 145usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(ucStaticallyAllocated) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).ucDelayAborted as *const _ as usize - }, - 146usize, - concat!( - "Offset of field: ", - stringify!(tskTaskControlBlock), - "::", - stringify!(ucDelayAborted) - ) - ); -} pub type tskTCB = tskTaskControlBlock; pub type TCB_t = tskTCB; +/*========== End of generated Code =============*/ pub trait emu_lookup { fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> Self; From b85e0a6d5b1ec1826d81a9f012ef109598412888 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 16 Feb 2022 23:36:23 +0100 Subject: [PATCH 36/83] parse raw freertos system state --- fuzzers/wcet_qemu_sys/src/freertos.rs | 3 +- fuzzers/wcet_qemu_sys/src/system_trace.rs | 103 +++++++++++++++++++--- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/freertos.rs index aeeb2d51ba..2b22eb6cb0 100644 --- a/fuzzers/wcet_qemu_sys/src/freertos.rs +++ b/fuzzers/wcet_qemu_sys/src/freertos.rs @@ -118,4 +118,5 @@ impl_emu_lookup!(TCB_t); impl_emu_lookup!(List_t); impl_emu_lookup!(ListItem_t); impl_emu_lookup!(MiniListItem_t); -impl_emu_lookup!(void_ptr); \ No newline at end of file +impl_emu_lookup!(void_ptr); +impl_emu_lookup!(TaskStatus_t); diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs index 0b8d6d4de6..041ed78208 100644 --- a/fuzzers/wcet_qemu_sys/src/system_trace.rs +++ b/fuzzers/wcet_qemu_sys/src/system_trace.rs @@ -1,3 +1,6 @@ +use crate::freertos::emu_lookup; +use crate::freertos::rtos_struct; +use crate::freertos::List_t; use crate::freertos::TCB_t; use crate::freertos::rtos_struct::List_Item_struct; use libafl::events::EventFirer; @@ -34,16 +37,40 @@ pub struct FreeRTOSSystemStateRaw { dumping_ground: HashMap, } +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct MiniTCB { + task_name: String, + priority: u32, + base_priority: u32, + mutexes_held: u32, + notify_value: u32, + notify_state: u8, +} + +impl MiniTCB { + pub fn from_tcb(input: &TCB_t) -> Self { + unsafe { + let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); + let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + Self { + task_name: name, + priority: input.uxPriority, + base_priority: input.uxBasePriority, + mutexes_held: input.uxMutexesHeld, + notify_value: input.ulNotifiedValue[0], + notify_state: input.ucNotifyState[0], + } + } + } +} + /// Refined information about the states an execution transitioned between #[derive(Debug, Default, Serialize, Deserialize)] pub struct QemuSystemStateMetadata { -} - -impl QemuSystemStateMetadata { - #[must_use] - pub fn new() -> Self { - Self {} - } + start_tick: u64, + end_tick: u64, + current_task: MiniTCB, + ready_list_after: Vec, } libafl::impl_serdeany!(QemuSystemStateMetadata); @@ -122,7 +149,7 @@ where // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); for i in 0..NUM_PRIOS { - let mut target : u32 = LISTBYTES*u32::try_from(i).unwrap()+h.ready_queues; + let target : u32 = LISTBYTES*u32::try_from(i).unwrap()+h.ready_queues; sysstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target); // println!("List at {}: {:?}",target, sysstate.prio_ready_lists[i]); let mut next_index = sysstate.prio_ready_lists[i].pxIndex; @@ -144,15 +171,16 @@ where sysstate.dumping_ground.insert(next_index,List_Item_struct(next_item)); next_index=new_next_index; } + // Handle edge case where the end marker was not included yet + if (target..target+LISTBYTES).contains(&next_index) { + let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); + } } unsafe { CURRENT_SYSSTATE_VEC.push(sysstate); } } -extern "C" fn test_gen_hook(pc: u64) -> u64 { - u32::MAX as u64 -} - pub fn gen_not_exec_block_hook( _emulator: &Emulator, helpers: &mut QT, @@ -193,6 +221,7 @@ impl Observer for QemuSysStateObserver #[inline] fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { unsafe {self.last_run.append(&mut CURRENT_SYSSTATE_VEC);} + println!("{:#?}",self.parse_last()); Ok(()) } } @@ -217,8 +246,58 @@ impl QemuSysStateObserver { pub fn new() -> Self { Self{last_run: vec![], name: "sysstate".to_string()} } + + pub fn parse_last(&self) -> Vec { + let mut ret = Vec::::new(); + let mut start_tick : u64 = 0; + for i in 0..self.last_run.len() { + let mut collector = Vec::::new(); + for j in self.last_run[i].prio_ready_lists.iter().rev() { + let mut tmp = list_to_tcb_vec(j,&self.last_run[i].dumping_ground).iter().map(|x| MiniTCB::from_tcb(x)).collect(); + collector.append(&mut tmp); + } + ret.push(QemuSystemStateMetadata { + current_task: MiniTCB::from_tcb(&self.last_run[i].current_tcb), + start_tick: start_tick, + end_tick: self.last_run[i].qemu_tick, + ready_list_after: collector, + }); + start_tick=self.last_run[i].qemu_tick; + } + return ret; + } } +pub fn list_to_tcb_vec(list: &List_t, dump: &HashMap) -> Vec +{ + let mut ret : Vec = Vec::new(); + if list.uxNumberOfItems == 0 {return ret;} + let first_list_index = match dump.get(&list.pxIndex).expect("List_t entry was not in Hashmap") { + List_Item_struct(li) => li.pxNext, + List_MiniItem_struct(mli) => match dump.get(&mli.pxNext).expect("MiniListItem pointer invaild") { + List_Item_struct(li) => li.pxNext, + _ => panic!("MiniListItem of a non empty List does not point to ListItem"), + }, + _ => panic!("List_t entry was not a ListItem"), + }; + let mut next_index = first_list_index; + for _ in 0..list.uxNumberOfItems { + let next_list_item = match dump.get(&next_index).expect("List_t entry was not in Hashmap") { + List_Item_struct(li) => li, + List_MiniItem_struct(mli) => match dump.get(&mli.pxNext).expect("MiniListItem pointer invaild") { + List_Item_struct(li) => li, + _ => panic!("MiniListItem of a non empty List does not point to ListItem"), + }, + _ => panic!("List_t entry was not a ListItem"), + }; + match dump.get(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { + TCB_struct(t) => {ret.push(*t)}, + _ => panic!("List content does not equal type"), + } + next_index=next_list_item.pxNext; + } + ret +} //============================= Feedback /// A Feedback reporting interesting System-State Transitions From b73a971c51a78a9aae4e52c04c61bbc6e4ac37c5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 17 Feb 2022 19:47:18 +0100 Subject: [PATCH 37/83] add sysstate feedback --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 35 ++- fuzzers/wcet_qemu_sys/src/system_trace.rs | 266 +++++++++++++++++----- 2 files changed, 236 insertions(+), 65 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index a5bf2c827c..8762b992eb 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,11 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use libafl_qemu::QemuInstrumentationFilter; +use wcet_qemu_sys::system_trace::QemuSystemStateHelper; +use wcet_qemu_sys::system_trace::QemuSysStateObserver; +use wcet_qemu_sys::system_trace::FreeRTOSSystemStateMetadata; +use wcet_qemu_sys::system_trace::SysStateFeedbackState; +use wcet_qemu_sys::system_trace::NovelSysStateFeedback; use wcet_qemu_sys::worst::QemuHashMapObserver; use hashbrown::HashMap; use clap::{App, Arg}; @@ -238,6 +244,21 @@ fn fuzz( let check_breakpoint = virt2phys(check_breakpoint,&elf.goblin()); println!("Breakpoint at {:#x}", check_breakpoint); + let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers + .resolve_symbol("pxCurrentTCB", 0) + .expect("Symbol pxCurrentTCBC not found"); + // let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf.goblin()); + println!("TCB pointer at {:#x}", curr_tcb_pointer); + let task_queue_addr = elf + .resolve_symbol("pxReadyTasksLists", 0) + .expect("Symbol pxReadyTasksLists not found"); + // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); + println!("Task Queue at {:#x}", task_queue_addr); + let svh = elf + .resolve_symbol("xPortPendSVHandler", 0) + .expect("Symbol xPortPendSVHandler not found"); + let svh = virt2phys(svh,&elf.goblin()); + //=========== Prepare Emulator Args let args: Vec = vec![ "qemu-system-arm", @@ -288,6 +309,9 @@ fn fuzz( // The state of the edges feedback. let feedback_state = MapFeedbackState::with_observer(&edges_observer); + + let sysstate_observer = QemuSysStateObserver::new(); + let sysstate_feedback_state = SysStateFeedbackState::default(); // Feedback to rate the interestingness of an input // This one is composed by two Feedbacks in OR @@ -304,7 +328,8 @@ fn fuzz( MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), HitImprovingFeedback::new(target_map.clone(), &edges_observer), QemuClockIncreaseFeedback::default(), - ClockFeedback::new_with_observer(&clock_observer) + ClockFeedback::new_with_observer(&clock_observer), + NovelSysStateFeedback::default() ); // A feedback to choose if an input is a solution or not @@ -323,7 +348,7 @@ fn fuzz( OnDiskCorpus::new(&objective_dir).unwrap(), // States of the feedbacks. // They are the data related to the feedbacks that you want to persist in the State. - tuple_list!(feedback_state,clock::MaxIcountMetadata::default()), + tuple_list!(feedback_state,clock::MaxIcountMetadata::default(),sysstate_feedback_state), ) }); @@ -365,6 +390,7 @@ fn fuzz( ExitKind::Ok }; + let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1]); let executor = QemuExecutor::new( &mut harness, &emu, @@ -372,9 +398,10 @@ fn fuzz( QemuEdgeCoverageHelper::new(), QemuCmpLogHelper::new(), //QemuAsanHelper::new(), - QemuSysSnapshotHelper::new() + QemuSysSnapshotHelper::new(), + QemuSystemStateHelper::with_instrumentation_filter(system_state_filter,curr_tcb_pointer.try_into().unwrap(),task_queue_addr.try_into().unwrap()) ), - tuple_list!(edges_observer, clock_observer), + tuple_list!(edges_observer, clock_observer,sysstate_observer), &mut fuzzer, &mut state, &mut mgr, diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs index 041ed78208..ee955099ba 100644 --- a/fuzzers/wcet_qemu_sys/src/system_trace.rs +++ b/fuzzers/wcet_qemu_sys/src/system_trace.rs @@ -1,3 +1,10 @@ +use libafl::feedbacks::FeedbackState; +use libafl::corpus::Testcase; +use libafl::state::HasFeedbackStates; +use libafl::bolts::tuples::MatchName; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; +use std::hash::Hash; use crate::freertos::emu_lookup; use crate::freertos::rtos_struct; use crate::freertos::List_t; @@ -37,7 +44,7 @@ pub struct FreeRTOSSystemStateRaw { dumping_ground: HashMap, } -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct MiniTCB { task_name: String, priority: u32, @@ -47,6 +54,16 @@ pub struct MiniTCB { notify_state: u8, } +impl Hash for MiniTCB { + fn hash(&self, state: &mut H) { + self.task_name.hash(state); + // self.priority.hash(state); + // self.mutexes_held.hash(state); + // self.notify_state.hash(state); + // self.notify_value.hash(state); + } +} + impl MiniTCB { pub fn from_tcb(input: &TCB_t) -> Self { unsafe { @@ -62,18 +79,49 @@ impl MiniTCB { } } } + pub fn from_tcb_owned(input: TCB_t) -> Self { + unsafe { + let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); + let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + Self { + task_name: name, + priority: input.uxPriority, + base_priority: input.uxBasePriority, + mutexes_held: input.uxMutexesHeld, + notify_value: input.ulNotifiedValue[0], + notify_state: input.ucNotifyState[0], + } + } + } } /// Refined information about the states an execution transitioned between -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct QemuSystemStateMetadata { +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct MiniFreeRTOSSystemState { start_tick: u64, end_tick: u64, current_task: MiniTCB, ready_list_after: Vec, } -libafl::impl_serdeany!(QemuSystemStateMetadata); +impl Hash for MiniFreeRTOSSystemState { + fn hash(&self, state: &mut H) { + self.current_task.hash(state); + // self.ready_list_after.hash(state); + } +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct FreeRTOSSystemStateMetadata { + inner: Vec, +} +impl FreeRTOSSystemStateMetadata { + pub fn new(inner: Vec) -> Self{ + Self {inner: inner} + } +} + +libafl::impl_serdeany!(FreeRTOSSystemStateMetadata); //============================= Qemu Helper @@ -206,7 +254,7 @@ pub fn gen_not_exec_block_hook( #[allow(clippy::unsafe_derive_deserialize)] pub struct QemuSysStateObserver { - last_run: Vec, + last_run: Vec, name: String, } @@ -220,8 +268,15 @@ impl Observer for QemuSysStateObserver #[inline] fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - unsafe {self.last_run.append(&mut CURRENT_SYSSTATE_VEC);} - println!("{:#?}",self.parse_last()); + unsafe {self.last_run = parse_last(&mut CURRENT_SYSSTATE_VEC);} + // let mut hasher = DefaultHasher::new(); + // let mut a = self.parse_last(); + // a[0].start_tick=21355; + // a[0].end_tick=2131; + // a.hash(&mut hasher); + // let somehash = hasher.finish(); + // println!("HashValue: {}",somehash); + // println!("{:#?}",self.parse_last()); Ok(()) } } @@ -241,31 +296,31 @@ impl HasLen for QemuSysStateObserver self.last_run.len() } } +pub fn parse_last(input: &mut Vec) -> Vec { +let mut ret = Vec::::new(); +let mut start_tick : u64 = 0; +for i in input.into_iter() { + let mut collector = Vec::::new(); + for j in i.prio_ready_lists.into_iter().rev() { + let mut tmp = list_to_tcb_vec_owned(j,&mut i.dumping_ground).iter().map(|x| MiniTCB::from_tcb(x)).collect(); + collector.append(&mut tmp); + } + ret.push(MiniFreeRTOSSystemState { + current_task: MiniTCB::from_tcb_owned(i.current_tcb), + start_tick: start_tick, + end_tick: i.qemu_tick, + ready_list_after: collector, + }); + start_tick=i.qemu_tick; +} +return ret; +} impl QemuSysStateObserver { pub fn new() -> Self { Self{last_run: vec![], name: "sysstate".to_string()} } - pub fn parse_last(&self) -> Vec { - let mut ret = Vec::::new(); - let mut start_tick : u64 = 0; - for i in 0..self.last_run.len() { - let mut collector = Vec::::new(); - for j in self.last_run[i].prio_ready_lists.iter().rev() { - let mut tmp = list_to_tcb_vec(j,&self.last_run[i].dumping_ground).iter().map(|x| MiniTCB::from_tcb(x)).collect(); - collector.append(&mut tmp); - } - ret.push(QemuSystemStateMetadata { - current_task: MiniTCB::from_tcb(&self.last_run[i].current_tcb), - start_tick: start_tick, - end_tick: self.last_run[i].qemu_tick, - ready_list_after: collector, - }); - start_tick=self.last_run[i].qemu_tick; - } - return ret; - } } pub fn list_to_tcb_vec(list: &List_t, dump: &HashMap) -> Vec @@ -298,51 +353,140 @@ pub fn list_to_tcb_vec(list: &List_t, dump: &HashMap) -> Vec) -> Vec +{ + let mut ret : Vec = Vec::new(); + if list.uxNumberOfItems == 0 {return ret;} + let last_list_item = match dump.remove(&list.pxIndex).expect("List_t entry was not in Hashmap") { + List_Item_struct(li) => li, + List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { + List_Item_struct(li) => li, + _ => panic!("MiniListItem of a non empty List does not point to ListItem"), + }, + _ => panic!("List_t entry was not a ListItem"), + }; + let mut next_index = last_list_item.pxNext; + let last_tcb = match dump.remove(&last_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { + TCB_struct(t) => t, + _ => panic!("List content does not equal type"), + }; + for _ in 0..list.uxNumberOfItems-1 { + let next_list_item = match dump.remove(&next_index).expect("List_t entry was not in Hashmap") { + List_Item_struct(li) => li, + List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { + List_Item_struct(li) => li, + _ => panic!("MiniListItem of a non empty List does not point to ListItem"), + }, + _ => panic!("List_t entry was not a ListItem"), + }; + match dump.remove(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { + TCB_struct(t) => {ret.push(t)}, + _ => panic!("List content does not equal type"), + } + next_index=next_list_item.pxNext; + } + ret.push(last_tcb); + ret +} //============================= Feedback -/// A Feedback reporting interesting System-State Transitions -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct SysStateFeedback +/// Shared Metadata for a SysStateFeedback +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysStateFeedbackState { + known_traces: HashMap, + longest: Vec, } - -impl Feedback for SysStateFeedback -where - I: Input, - S: HasClientPerfMonitor, -{ - fn is_interesting( - &mut self, - _state: &mut S, - _manager: &mut EM, - _input: &I, - _observers: &OT, - _exit_kind: &ExitKind, - ) -> Result - where - EM: EventFirer, - OT: ObserversTuple, - { - let observer = _observers.match_name::("sysstate") - .expect("QemuSysStateObserver not found"); - // Do Stuff - Ok(false) - } -} - -impl Named for SysStateFeedback +impl Named for SysStateFeedbackState { #[inline] fn name(&self) -> &str { "sysstate" } } - -impl SysStateFeedback +impl FeedbackState for SysStateFeedbackState { - /// Creates a new [`SysStateFeedback`] - #[must_use] - pub fn new() -> Self { - Self {} + fn reset(&mut self) -> Result<(), Error> { + self.longest.clear(); + self.known_traces.clear(); + Ok(()) + } +} + +/// A Feedback reporting novel System-State Transitions +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct NovelSysStateFeedback +{ + last_trace: Option>, + // known_traces: HashMap, +} + +impl Feedback for NovelSysStateFeedback +where + I: Input, + S: HasClientPerfMonitor + HasFeedbackStates, +{ + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("sysstate") + .expect("QemuSysStateObserver not found"); + let feedbackstate = state + .feedback_states_mut() + .match_name_mut::("sysstate") + .unwrap(); + // Do Stuff + let mut hasher = DefaultHasher::new(); + observer.last_run.hash(&mut hasher); + let somehash = hasher.finish(); + let mut is_novel = false; + match feedbackstate.known_traces.get_mut(&somehash) { + None => { + is_novel = true; + feedbackstate.known_traces.insert(somehash,(1,observer.last_run.len())); + } + Some(s) => s.0+=1, + } + if observer.last_run.len() > feedbackstate.longest.len() { + feedbackstate.longest=observer.last_run.clone(); + } + self.last_trace = Some(observer.last_run.clone()); + // if (!is_novel) { println!("not novel") }; + Ok(is_novel) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + let a = self.last_trace.take(); + match a { + Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)), + None => (), + } + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.last_trace = None; + Ok(()) + } +} + +impl Named for NovelSysStateFeedback +{ + #[inline] + fn name(&self) -> &str { + "sysstate" } } \ No newline at end of file From ffdaf22b1d0b32e0d341bb1a9ca9fa33fa895aa0 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 22 Feb 2022 00:09:15 +0100 Subject: [PATCH 38/83] fix byte input reading --- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 39b23c933f..d005e471b0 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use std::io::Read; use wcet_qemu_sys::system_trace::QemuSysStateObserver; use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::{ @@ -140,7 +141,13 @@ pub fn main() { out_dir.push("queue"); let seed = match res.value_of("single") { - Some(s) => Left(s.to_string()), + Some(s) => if s=="-" { + let mut buf = Vec::::new(); + std::io::stdin().read_to_end(&mut buf).expect("Could not read Stdin"); + Left(buf) + } else { + Left(s.as_bytes().to_owned()) + }, None => { let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string()); if !in_dir.is_dir() { @@ -150,6 +157,7 @@ pub fn main() { Right(in_dir) }, }; + println!("{:?}",seed); let kernel = PathBuf::from(res.value_of("k").unwrap().to_string()); let edges = match res.value_of("edges") { @@ -178,7 +186,7 @@ fn virt2phys(vaddr : u64, tab : &goblin::elf::Elf) -> u64 { /// The actual fuzzer fn fuzz( - seed: Either, + seed: Either,PathBuf>, kernel: PathBuf, dump_edges: Option, snapshot: PathBuf, @@ -316,7 +324,7 @@ fn fuzz( ExitKind::Ok }; //======= Set System-State watchpoints - let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1]); + let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1,systick_handler..systick_handler+1,svc_handle..svc_handle+1]); //======= Construct the executor, including the Helpers. The edges_observer still contains the ref to EDGES_MAP let mut executor = QemuExecutor::new( @@ -351,7 +359,7 @@ fn fuzz( }, Left(s) => { - fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(s.as_bytes().to_vec())).expect("Evaluation failed"); + fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(s)).expect("Evaluation failed"); } } // let firstinput = match seed.clone().is_dir() { From 53bd7556475caebefacc2afe3c382529578f8576 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 22 Feb 2022 00:10:17 +0100 Subject: [PATCH 39/83] add clock count to systemstate feedback --- fuzzers/wcet_qemu_sys/src/system_trace.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs index ee955099ba..c511dc99cc 100644 --- a/fuzzers/wcet_qemu_sys/src/system_trace.rs +++ b/fuzzers/wcet_qemu_sys/src/system_trace.rs @@ -1,3 +1,4 @@ +use libafl_qemu::QemuClockObserver; use libafl::feedbacks::FeedbackState; use libafl::corpus::Testcase; use libafl::state::HasFeedbackStates; @@ -394,7 +395,7 @@ pub fn list_to_tcb_vec_owned(list: List_t, dump: &mut HashMap) #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct SysStateFeedbackState { - known_traces: HashMap, + known_traces: HashMap, // encounters,ticks,length longest: Vec, } impl Named for SysStateFeedbackState @@ -440,6 +441,8 @@ where { let observer = observers.match_name::("sysstate") .expect("QemuSysStateObserver not found"); + let clock_observer = observers.match_name::("clock") //TODO not fixed + .expect("QemuSysStateObserver not found"); let feedbackstate = state .feedback_states_mut() .match_name_mut::("sysstate") @@ -449,19 +452,26 @@ where observer.last_run.hash(&mut hasher); let somehash = hasher.finish(); let mut is_novel = false; + let mut takes_longer = false; match feedbackstate.known_traces.get_mut(&somehash) { None => { is_novel = true; - feedbackstate.known_traces.insert(somehash,(1,observer.last_run.len())); + feedbackstate.known_traces.insert(somehash,(1,clock_observer.last_runtime(),observer.last_run.len())); + } + Some(s) => { + s.0+=1; + if s.1 < clock_observer.last_runtime() { + s.1 = clock_observer.last_runtime(); + takes_longer = true; + } } - Some(s) => s.0+=1, } if observer.last_run.len() > feedbackstate.longest.len() { feedbackstate.longest=observer.last_run.clone(); } self.last_trace = Some(observer.last_run.clone()); // if (!is_novel) { println!("not novel") }; - Ok(is_novel) + Ok(is_novel | takes_longer) } /// Append to the testcase the generated metadata in case of a new corpus item From f5bf5605f1e9eb1c6eac979361f58e2d7e8ec5a7 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 22 Feb 2022 16:49:02 +0100 Subject: [PATCH 40/83] split system_state module, add tracedump --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 9 +- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 30 +- fuzzers/wcet_qemu_sys/src/lib.rs | 6 +- .../wcet_qemu_sys/src/sysstate/feedbacks.rs | 191 +++++++ .../src/{ => sysstate}/freertos.rs | 2 +- fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs | 138 +++++ fuzzers/wcet_qemu_sys/src/sysstate/mod.rs | 108 ++++ .../wcet_qemu_sys/src/sysstate/observers.rs | 131 +++++ fuzzers/wcet_qemu_sys/src/system_trace.rs | 502 ------------------ 9 files changed, 597 insertions(+), 520 deletions(-) create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs rename fuzzers/wcet_qemu_sys/src/{ => sysstate}/freertos.rs (98%) create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/mod.rs create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/observers.rs delete mode 100644 fuzzers/wcet_qemu_sys/src/system_trace.rs diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 8762b992eb..251c82e324 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,11 +1,10 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. use libafl_qemu::QemuInstrumentationFilter; -use wcet_qemu_sys::system_trace::QemuSystemStateHelper; -use wcet_qemu_sys::system_trace::QemuSysStateObserver; -use wcet_qemu_sys::system_trace::FreeRTOSSystemStateMetadata; -use wcet_qemu_sys::system_trace::SysStateFeedbackState; -use wcet_qemu_sys::system_trace::NovelSysStateFeedback; +use wcet_qemu_sys::sysstate::helpers::QemuSystemStateHelper; +use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; +use wcet_qemu_sys::sysstate::feedbacks::SysStateFeedbackState; +use wcet_qemu_sys::sysstate::feedbacks::NovelSysStateFeedback; use wcet_qemu_sys::worst::QemuHashMapObserver; use hashbrown::HashMap; use clap::{App, Arg}; diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index d005e471b0..772a117104 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -1,11 +1,12 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. use std::io::Read; -use wcet_qemu_sys::system_trace::QemuSysStateObserver; +use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; +use wcet_qemu_sys::sysstate::feedbacks::DumpSystraceFeedback; use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::{ worst::{DumpMapFeedback,DummyFeedback}, - system_trace::QemuSystemStateHelper, + sysstate::helpers::QemuSystemStateHelper, }; use clap::{App, Arg}; use std::{ @@ -23,7 +24,7 @@ use libafl::{ corpus::{Corpus,InMemoryCorpus,QueueCorpusScheduler}, executors::{ExitKind}, fuzzer::{StdFuzzer}, - inputs::{Input,BytesInput, HasTargetBytes}, + inputs::{BytesInput, HasTargetBytes}, observers::{VariableMapObserver}, state::{HasCorpus,StdState}, Error, @@ -33,6 +34,7 @@ use libafl::{ stages::StdMutationalStage, mutators::BitFlipMutator, Fuzzer, + feedback_or, }; use libafl_qemu::{ edges, @@ -95,6 +97,11 @@ pub fn main() { .long("libafl-edges") .takes_value(true), ) + .arg( + Arg::new("traces") + .long("libafl-traces") + .takes_value(true), + ) .arg( Arg::new("snapshot") .help("The qcow2 file used for snapshots") @@ -165,9 +172,14 @@ pub fn main() { None => None }; + let traces = match res.value_of("traces") { + Some(st) => Some(PathBuf::from(st.to_string())), + None => None + }; + let snapshot = PathBuf::from(res.value_of("snapshot").unwrap().to_string()); - fuzz(seed, kernel, edges, snapshot) + fuzz(seed, kernel, edges, traces, snapshot) .expect("An error occurred while fuzzing"); } @@ -189,6 +201,7 @@ fn fuzz( seed: Either,PathBuf>, kernel: PathBuf, dump_edges: Option, + dump_traces: Option, snapshot: PathBuf, ) -> Result<(), Error> { //=========== Setup emulator @@ -278,7 +291,7 @@ fn fuzz( let clock_observer = QemuClockObserver::default(); //========= Feedback-Function evaluate the Maps. Need to dump it for debugging and check if it reaches targets. - let feedback = DumpMapFeedback::with_dump(dump_edges, &edges_observer); + let feedback = feedback_or!(DumpMapFeedback::with_dump(dump_edges, &edges_observer),DumpSystraceFeedback::with_dump(dump_traces)); // A feedback to choose if an input is a solution or not let objective = DummyFeedback::new(false); @@ -353,10 +366,9 @@ fn fuzz( }); println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer - .fuzz_one(&mut tuple_list!(StdMutationalStage::new(BitFlipMutator::new())), &mut executor, &mut state, &mut mgr) - .expect("Error in the fuzzing loop"); - + // fuzzer + // .fuzz_one(&mut tuple_list!(StdMutationalStage::new(BitFlipMutator::new())), &mut executor, &mut state, &mut mgr) + // .expect("Error in the fuzzing loop"); }, Left(s) => { fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(s)).expect("Evaluation failed"); diff --git a/fuzzers/wcet_qemu_sys/src/lib.rs b/fuzzers/wcet_qemu_sys/src/lib.rs index ab85cf7d2e..34b31773e6 100644 --- a/fuzzers/wcet_qemu_sys/src/lib.rs +++ b/fuzzers/wcet_qemu_sys/src/lib.rs @@ -1,6 +1,6 @@ #![feature(iter_advance_by)] #![feature(is_sorted)] #[cfg(target_os = "linux")] -pub mod worst; -pub mod freertos; -pub mod system_trace; +pub mod sysstate; + +pub mod worst; \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs new file mode 100644 index 0000000000..cfbe0af0fe --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs @@ -0,0 +1,191 @@ +use std::path::PathBuf; +use libafl_qemu::QemuClockObserver; +use libafl::feedbacks::FeedbackState; +use libafl::corpus::Testcase; +use libafl::state::HasFeedbackStates; +use libafl::bolts::tuples::MatchName; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; +use std::hash::Hash; +use libafl::events::EventFirer; +use libafl::state::HasClientPerfMonitor; +use libafl::feedbacks::Feedback; +use libafl::bolts::tuples::Named; +use libafl::Error; +use hashbrown::HashMap; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; +use serde::{Deserialize, Serialize}; + +use super::MiniFreeRTOSSystemState; +use super::FreeRTOSSystemStateMetadata; +use super::observers::QemuSysStateObserver; + +//============================= Feedback + +/// Shared Metadata for a SysStateFeedback +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysStateFeedbackState +{ + known_traces: HashMap, // encounters,ticks,length + longest: Vec, +} +impl Named for SysStateFeedbackState +{ + #[inline] + fn name(&self) -> &str { + "sysstate" + } +} +impl FeedbackState for SysStateFeedbackState +{ + fn reset(&mut self) -> Result<(), Error> { + self.longest.clear(); + self.known_traces.clear(); + Ok(()) + } +} + +/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct NovelSysStateFeedback +{ + last_trace: Option>, + // known_traces: HashMap, +} + +impl Feedback for NovelSysStateFeedback +where + I: Input, + S: HasClientPerfMonitor + HasFeedbackStates, +{ + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("sysstate") + .expect("QemuSysStateObserver not found"); + let clock_observer = observers.match_name::("clock") //TODO not fixed + .expect("QemuSysStateObserver not found"); + let feedbackstate = state + .feedback_states_mut() + .match_name_mut::("sysstate") + .unwrap(); + // Do Stuff + let mut hasher = DefaultHasher::new(); + observer.last_run.hash(&mut hasher); + let somehash = hasher.finish(); + let mut is_novel = false; + let mut takes_longer = false; + match feedbackstate.known_traces.get_mut(&somehash) { + None => { + is_novel = true; + feedbackstate.known_traces.insert(somehash,(1,clock_observer.last_runtime(),observer.last_run.len())); + } + Some(s) => { + s.0+=1; + if s.1 < clock_observer.last_runtime() { + s.1 = clock_observer.last_runtime(); + takes_longer = true; + } + } + } + if observer.last_run.len() > feedbackstate.longest.len() { + feedbackstate.longest=observer.last_run.clone(); + } + self.last_trace = Some(observer.last_run.clone()); + // if (!is_novel) { println!("not novel") }; + Ok(is_novel | takes_longer) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + let a = self.last_trace.take(); + match a { + Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)), + None => (), + } + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.last_trace = None; + Ok(()) + } +} + +impl Named for NovelSysStateFeedback +{ + #[inline] + fn name(&self) -> &str { + "sysstate" + } +} + +//=========================== Debugging Feedback +/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSysStateObserver`] +#[derive(Debug)] +pub struct DumpSystraceFeedback +{ + dumpfile: Option, +} + +impl Feedback for DumpSystraceFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("sysstate") + .expect("QemuSysStateObserver not found"); + match &self.dumpfile { + Some(s) => { + std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file"); + self.dumpfile = None + }, + None => println!("{:?}",observer.last_run), + }; + Ok(true) + } +} + +impl Named for DumpSystraceFeedback +{ + #[inline] + fn name(&self) -> &str { + "DumpSysState" + } +} + +impl DumpSystraceFeedback +{ + /// Creates a new [`DumpSystraceFeedback`] + #[must_use] + pub fn new() -> Self { + Self {dumpfile: None} + } + pub fn with_dump(dumpfile: Option) -> Self { + Self {dumpfile: dumpfile} + } +} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/freertos.rs b/fuzzers/wcet_qemu_sys/src/sysstate/freertos.rs similarity index 98% rename from fuzzers/wcet_qemu_sys/src/freertos.rs rename to fuzzers/wcet_qemu_sys/src/sysstate/freertos.rs index 2b22eb6cb0..31bdbcbeec 100644 --- a/fuzzers/wcet_qemu_sys/src/freertos.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/freertos.rs @@ -103,7 +103,7 @@ pub enum rtos_struct { #[macro_export] macro_rules! impl_emu_lookup { ($struct_name:ident) => { - impl $crate::freertos::emu_lookup for $struct_name { + impl $crate::sysstate::freertos::emu_lookup for $struct_name { fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> $struct_name { let mut tmp : [u8; std::mem::size_of::<$struct_name>()] = [0u8; std::mem::size_of::<$struct_name>()]; unsafe { diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs new file mode 100644 index 0000000000..8513a778b3 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs @@ -0,0 +1,138 @@ +use crate::sysstate::FreeRTOSSystemStateRaw; +use crate::sysstate::CURRENT_SYSSTATE_VEC; +use crate::sysstate::NUM_PRIOS; +use super::freertos::TCB_t; +use super::freertos::rtos_struct::List_Item_struct; +use super::freertos::rtos_struct::*; +use super::freertos; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; + +use libafl_qemu::{ + emu::Emulator, + executor::QemuExecutor, + helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, +}; + +//============================= Struct definitions + + +//============================= Qemu Helper + +/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur +#[derive(Debug)] +pub struct QemuSystemStateHelper { + filter: QemuInstrumentationFilter, + tcb_addr: u32, + ready_queues: u32, +} + +impl QemuSystemStateHelper { + #[must_use] + pub fn new(tcb_addr: u32, ready_queues: u32) -> Self { + Self { + filter: QemuInstrumentationFilter::None, + tcb_addr: tcb_addr, + ready_queues: ready_queues, + } + } + + #[must_use] + pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter, tcb_addr: u32, ready_queues: u32) -> Self { + Self { filter, tcb_addr, ready_queues} + } + + #[must_use] + pub fn must_instrument(&self, addr: u64) -> bool { + self.filter.allowed(addr) + } +} + +impl QemuHelper for QemuSystemStateHelper +where + I: Input, + S: HasMetadata, +{ + fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) + where + H: FnMut(&I) -> ExitKind, + OT: ObserversTuple, + QT: QemuHelperTuple, + { + // emu::Emulator{_private: ()}.set_gen_block_hook(test_gen_hook); + executor.hook_block_generation(gen_not_exec_block_hook::); + executor.hook_block_execution(exec_syscall_hook::); + } +} + +pub fn exec_syscall_hook( + emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + pc: u64, +) +where + S: HasMetadata, + I: Input, + QT: QemuHelperTuple, +{ + let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + if !h.must_instrument(pc) { + return; + } + let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); + let mut sysstate = FreeRTOSSystemStateRaw::default(); + sysstate.qemu_tick = emulator.get_ticks(); + + let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); + sysstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); + // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); + + for i in 0..NUM_PRIOS { + let target : u32 = listbytes*u32::try_from(i).unwrap()+h.ready_queues; + sysstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target); + // println!("List at {}: {:?}",target, sysstate.prio_ready_lists[i]); + let mut next_index = sysstate.prio_ready_lists[i].pxIndex; + for _j in 0..sysstate.prio_ready_lists[i].uxNumberOfItems { + // always jump over the xListEnd marker + if (target..target+listbytes).contains(&next_index) { + let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + let new_next_index=next_item.pxNext; + sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); + next_index = new_next_index; + } + let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + // println!("Item at {}: {:?}",next_index,next_item); + assert_eq!(next_item.pvContainer,target); + let new_next_index=next_item.pxNext; + let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner); + // println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb); + sysstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone())); + sysstate.dumping_ground.insert(next_index,List_Item_struct(next_item)); + next_index=new_next_index; + } + // Handle edge case where the end marker was not included yet + if (target..target+listbytes).contains(&next_index) { + let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); + } + } + + unsafe { CURRENT_SYSSTATE_VEC.push(sysstate); } +} + +pub fn gen_not_exec_block_hook( + _emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + pc: u64, +) +-> Option where + S: HasMetadata, + I: Input, + QT: QemuHelperTuple, +{ + let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + if !h.must_instrument(pc) { + None + } else {Some(1)} +} diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs new file mode 100644 index 0000000000..9a0c2c2208 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -0,0 +1,108 @@ +//! Sysstate referes to the State of a FreeRTOS fuzzing target +use std::hash::Hasher; +use std::hash::Hash; +use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; + +use freertos::TCB_t; + +pub mod freertos; +pub mod helpers; +pub mod observers; +pub mod feedbacks; + +// Constants +const NUM_PRIOS: usize = 5; + +//============================= Struct definitions +/// Raw info Dump from Qemu +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct FreeRTOSSystemStateRaw { + qemu_tick: u64, + current_tcb: TCB_t, + prio_ready_lists: [freertos::List_t; NUM_PRIOS], + dumping_ground: HashMap, +} +/// List of system state dumps from QemuHelpers +static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; + +/// A reduced version of freertos::TCB_t +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct MiniTCB { + task_name: String, + priority: u32, + base_priority: u32, + mutexes_held: u32, + notify_value: u32, + notify_state: u8, +} + +impl Hash for MiniTCB { + fn hash(&self, state: &mut H) { + self.task_name.hash(state); + // self.priority.hash(state); + // self.mutexes_held.hash(state); + // self.notify_state.hash(state); + // self.notify_value.hash(state); + } +} + +impl MiniTCB { + pub fn from_tcb(input: &TCB_t) -> Self { + unsafe { + let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); + let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + Self { + task_name: name, + priority: input.uxPriority, + base_priority: input.uxBasePriority, + mutexes_held: input.uxMutexesHeld, + notify_value: input.ulNotifiedValue[0], + notify_state: input.ucNotifyState[0], + } + } + } + pub fn from_tcb_owned(input: TCB_t) -> Self { + unsafe { + let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); + let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + Self { + task_name: name, + priority: input.uxPriority, + base_priority: input.uxBasePriority, + mutexes_held: input.uxMutexesHeld, + notify_value: input.ulNotifiedValue[0], + notify_state: input.ucNotifyState[0], + } + } + } +} + +/// Refined information about the states an execution transitioned between +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct MiniFreeRTOSSystemState { + start_tick: u64, + end_tick: u64, + current_task: MiniTCB, + ready_list_after: Vec, +} + +impl Hash for MiniFreeRTOSSystemState { + fn hash(&self, state: &mut H) { + self.current_task.hash(state); + // self.ready_list_after.hash(state); + } +} + +// Wrapper around Vec to attach as Metadata +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct FreeRTOSSystemStateMetadata { + inner: Vec, +} +impl FreeRTOSSystemStateMetadata { + pub fn new(inner: Vec) -> Self{ + Self {inner: inner} + } +} + +libafl::impl_serdeany!(FreeRTOSSystemStateMetadata); diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs new file mode 100644 index 0000000000..30bbd5b7a7 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -0,0 +1,131 @@ +use libafl::bolts::HasLen; +use libafl::bolts::tuples::Named; +use libafl::Error; +use libafl::observers::Observer; +use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; + +use super::{ + CURRENT_SYSSTATE_VEC, + FreeRTOSSystemStateRaw, + MiniTCB, + MiniFreeRTOSSystemState, + freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*}, +}; + +//============================= Observer + +/// The QemuSysState Observer retrieves the SysState +/// that will get updated by the target. +#[derive(Serialize, Deserialize, Debug, Default)] +#[allow(clippy::unsafe_derive_deserialize)] +pub struct QemuSysStateObserver +{ + pub last_run: Vec, + name: String, +} + +impl Observer for QemuSysStateObserver +{ + #[inline] + fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + unsafe {CURRENT_SYSSTATE_VEC.clear(); } + Ok(()) + } + + #[inline] + fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + unsafe {self.last_run = refine_system_states(&mut CURRENT_SYSSTATE_VEC);} + // let mut hasher = DefaultHasher::new(); + // let mut a = self.parse_last(); + // a[0].start_tick=21355; + // a[0].end_tick=2131; + // a.hash(&mut hasher); + // let somehash = hasher.finish(); + // println!("HashValue: {}",somehash); + // println!("{:#?}",self.parse_last()); + Ok(()) + } +} + +impl Named for QemuSysStateObserver +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl HasLen for QemuSysStateObserver +{ + #[inline] + fn len(&self) -> usize { + self.last_run.len() + } +} + +impl QemuSysStateObserver { + pub fn new() -> Self { + Self{last_run: vec![], name: "sysstate".to_string()} + } + +} + +//============================= Parsing helpers + +/// Parse a List_t containing TCB_t into Vec from cache. Consumes the elements from cache +fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> Vec +{ + let mut ret : Vec = Vec::new(); + if list.uxNumberOfItems == 0 {return ret;} + let last_list_item = match dump.remove(&list.pxIndex).expect("List_t entry was not in Hashmap") { + List_Item_struct(li) => li, + List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { + List_Item_struct(li) => li, + _ => panic!("MiniListItem of a non empty List does not point to ListItem"), + }, + _ => panic!("List_t entry was not a ListItem"), + }; + let mut next_index = last_list_item.pxNext; + let last_tcb = match dump.remove(&last_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { + TCB_struct(t) => t, + _ => panic!("List content does not equal type"), + }; + for _ in 0..list.uxNumberOfItems-1 { + let next_list_item = match dump.remove(&next_index).expect("List_t entry was not in Hashmap") { + List_Item_struct(li) => li, + List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { + List_Item_struct(li) => li, + _ => panic!("MiniListItem of a non empty List does not point to ListItem"), + }, + _ => panic!("List_t entry was not a ListItem"), + }; + match dump.remove(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { + TCB_struct(t) => {ret.push(t)}, + _ => panic!("List content does not equal type"), + } + next_index=next_list_item.pxNext; + } + ret.push(last_tcb); + ret +} +/// Drains a List of raw SystemStates to produce a refined trace +fn refine_system_states(input: &mut Vec) -> Vec { +let mut ret = Vec::::new(); +let mut start_tick : u64 = 0; +for i in input.into_iter() { + let mut collector = Vec::::new(); + for j in i.prio_ready_lists.into_iter().rev() { + let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| MiniTCB::from_tcb(x)).collect(); + collector.append(&mut tmp); + } + ret.push(MiniFreeRTOSSystemState { + current_task: MiniTCB::from_tcb_owned(i.current_tcb), + start_tick: start_tick, + end_tick: i.qemu_tick, + ready_list_after: collector, + }); + start_tick=i.qemu_tick; +} +return ret; +} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/system_trace.rs b/fuzzers/wcet_qemu_sys/src/system_trace.rs deleted file mode 100644 index c511dc99cc..0000000000 --- a/fuzzers/wcet_qemu_sys/src/system_trace.rs +++ /dev/null @@ -1,502 +0,0 @@ -use libafl_qemu::QemuClockObserver; -use libafl::feedbacks::FeedbackState; -use libafl::corpus::Testcase; -use libafl::state::HasFeedbackStates; -use libafl::bolts::tuples::MatchName; -use std::collections::hash_map::DefaultHasher; -use std::hash::Hasher; -use std::hash::Hash; -use crate::freertos::emu_lookup; -use crate::freertos::rtos_struct; -use crate::freertos::List_t; -use crate::freertos::TCB_t; -use crate::freertos::rtos_struct::List_Item_struct; -use libafl::events::EventFirer; -use libafl::state::HasClientPerfMonitor; -use libafl::feedbacks::Feedback; -use libafl::bolts::HasLen; -use libafl::bolts::tuples::Named; -use libafl::Error; -use libafl::observers::Observer; -use crate::freertos::rtos_struct::*; -use crate::freertos; -use hashbrown::HashMap; -use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; -use serde::{Deserialize, Serialize}; - -use libafl_qemu::{ - emu, - emu::Emulator, - executor::QemuExecutor, - helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, -}; - -const NUM_PRIOS: usize = 5; - -//============================= Datatypes - -/// Info Dump from Qemu -// pub type SysState = (u64,freertos::TCB_t,HashMap); -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct FreeRTOSSystemStateRaw { - qemu_tick: u64, - current_tcb: TCB_t, - prio_ready_lists: [freertos::List_t; NUM_PRIOS], - dumping_ground: HashMap, -} - -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct MiniTCB { - task_name: String, - priority: u32, - base_priority: u32, - mutexes_held: u32, - notify_value: u32, - notify_state: u8, -} - -impl Hash for MiniTCB { - fn hash(&self, state: &mut H) { - self.task_name.hash(state); - // self.priority.hash(state); - // self.mutexes_held.hash(state); - // self.notify_state.hash(state); - // self.notify_value.hash(state); - } -} - -impl MiniTCB { - pub fn from_tcb(input: &TCB_t) -> Self { - unsafe { - let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); - let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); - Self { - task_name: name, - priority: input.uxPriority, - base_priority: input.uxBasePriority, - mutexes_held: input.uxMutexesHeld, - notify_value: input.ulNotifiedValue[0], - notify_state: input.ucNotifyState[0], - } - } - } - pub fn from_tcb_owned(input: TCB_t) -> Self { - unsafe { - let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); - let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); - Self { - task_name: name, - priority: input.uxPriority, - base_priority: input.uxBasePriority, - mutexes_held: input.uxMutexesHeld, - notify_value: input.ulNotifiedValue[0], - notify_state: input.ucNotifyState[0], - } - } - } -} - -/// Refined information about the states an execution transitioned between -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct MiniFreeRTOSSystemState { - start_tick: u64, - end_tick: u64, - current_task: MiniTCB, - ready_list_after: Vec, -} - -impl Hash for MiniFreeRTOSSystemState { - fn hash(&self, state: &mut H) { - self.current_task.hash(state); - // self.ready_list_after.hash(state); - } -} - -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct FreeRTOSSystemStateMetadata { - inner: Vec, -} -impl FreeRTOSSystemStateMetadata { - pub fn new(inner: Vec) -> Self{ - Self {inner: inner} - } -} - -libafl::impl_serdeany!(FreeRTOSSystemStateMetadata); - - -//============================= Qemu Helper -static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; - -/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur -#[derive(Debug)] -pub struct QemuSystemStateHelper { - filter: QemuInstrumentationFilter, - tcb_addr: u32, - ready_queues: u32, -} - -impl QemuSystemStateHelper { - #[must_use] - pub fn new(tcb_addr: u32, ready_queues: u32) -> Self { - Self { - filter: QemuInstrumentationFilter::None, - tcb_addr: tcb_addr, - ready_queues: ready_queues, - } - } - - #[must_use] - pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter, tcb_addr: u32, ready_queues: u32) -> Self { - Self { filter, tcb_addr, ready_queues} - } - - #[must_use] - pub fn must_instrument(&self, addr: u64) -> bool { - self.filter.allowed(addr) - } -} - -impl QemuHelper for QemuSystemStateHelper -where - I: Input, - S: HasMetadata, -{ - fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) - where - H: FnMut(&I) -> ExitKind, - OT: ObserversTuple, - QT: QemuHelperTuple, - { - // emu::Emulator{_private: ()}.set_gen_block_hook(test_gen_hook); - executor.hook_block_generation(gen_not_exec_block_hook::); - executor.hook_block_execution(exec_syscall_hook::); - } -} - -pub fn exec_syscall_hook( - emulator: &Emulator, - helpers: &mut QT, - state: &mut S, - pc: u64, -) -where - S: HasMetadata, - I: Input, - QT: QemuHelperTuple, -{ - let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - if !h.must_instrument(pc) { - return; - } - let LISTBYTES : u32 = u32::try_from(std::mem::size_of::()).unwrap(); - let mut sysstate = FreeRTOSSystemStateRaw::default(); - sysstate.qemu_tick = emulator.get_ticks(); - - let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); - sysstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); - // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); - - for i in 0..NUM_PRIOS { - let target : u32 = LISTBYTES*u32::try_from(i).unwrap()+h.ready_queues; - sysstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target); - // println!("List at {}: {:?}",target, sysstate.prio_ready_lists[i]); - let mut next_index = sysstate.prio_ready_lists[i].pxIndex; - for _j in 0..sysstate.prio_ready_lists[i].uxNumberOfItems { - // always jump over the xListEnd marker - if (target..target+LISTBYTES).contains(&next_index) { - let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); - let new_next_index=next_item.pxNext; - sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); - next_index = new_next_index; - } - let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index); - // println!("Item at {}: {:?}",next_index,next_item); - assert_eq!(next_item.pvContainer,target); - let new_next_index=next_item.pxNext; - let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner); - // println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb); - sysstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone())); - sysstate.dumping_ground.insert(next_index,List_Item_struct(next_item)); - next_index=new_next_index; - } - // Handle edge case where the end marker was not included yet - if (target..target+LISTBYTES).contains(&next_index) { - let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); - sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); - } - } - - unsafe { CURRENT_SYSSTATE_VEC.push(sysstate); } -} - -pub fn gen_not_exec_block_hook( - _emulator: &Emulator, - helpers: &mut QT, - _state: &mut S, - pc: u64, -) --> Option where - S: HasMetadata, - I: Input, - QT: QemuHelperTuple, -{ - let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - if !h.must_instrument(pc) { - None - } else {Some(1)} -} - -//============================= Observer - -/// The QemuSysState Observer retrieves the SysState -/// that will get updated by the target. -#[derive(Serialize, Deserialize, Debug, Default)] -#[allow(clippy::unsafe_derive_deserialize)] -pub struct QemuSysStateObserver -{ - last_run: Vec, - name: String, -} - -impl Observer for QemuSysStateObserver -{ - #[inline] - fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - unsafe {CURRENT_SYSSTATE_VEC.clear(); } - Ok(()) - } - - #[inline] - fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - unsafe {self.last_run = parse_last(&mut CURRENT_SYSSTATE_VEC);} - // let mut hasher = DefaultHasher::new(); - // let mut a = self.parse_last(); - // a[0].start_tick=21355; - // a[0].end_tick=2131; - // a.hash(&mut hasher); - // let somehash = hasher.finish(); - // println!("HashValue: {}",somehash); - // println!("{:#?}",self.parse_last()); - Ok(()) - } -} - -impl Named for QemuSysStateObserver -{ - #[inline] - fn name(&self) -> &str { - self.name.as_str() - } -} - -impl HasLen for QemuSysStateObserver -{ - #[inline] - fn len(&self) -> usize { - self.last_run.len() - } -} -pub fn parse_last(input: &mut Vec) -> Vec { -let mut ret = Vec::::new(); -let mut start_tick : u64 = 0; -for i in input.into_iter() { - let mut collector = Vec::::new(); - for j in i.prio_ready_lists.into_iter().rev() { - let mut tmp = list_to_tcb_vec_owned(j,&mut i.dumping_ground).iter().map(|x| MiniTCB::from_tcb(x)).collect(); - collector.append(&mut tmp); - } - ret.push(MiniFreeRTOSSystemState { - current_task: MiniTCB::from_tcb_owned(i.current_tcb), - start_tick: start_tick, - end_tick: i.qemu_tick, - ready_list_after: collector, - }); - start_tick=i.qemu_tick; -} -return ret; -} - -impl QemuSysStateObserver { - pub fn new() -> Self { - Self{last_run: vec![], name: "sysstate".to_string()} - } - -} - -pub fn list_to_tcb_vec(list: &List_t, dump: &HashMap) -> Vec -{ - let mut ret : Vec = Vec::new(); - if list.uxNumberOfItems == 0 {return ret;} - let first_list_index = match dump.get(&list.pxIndex).expect("List_t entry was not in Hashmap") { - List_Item_struct(li) => li.pxNext, - List_MiniItem_struct(mli) => match dump.get(&mli.pxNext).expect("MiniListItem pointer invaild") { - List_Item_struct(li) => li.pxNext, - _ => panic!("MiniListItem of a non empty List does not point to ListItem"), - }, - _ => panic!("List_t entry was not a ListItem"), - }; - let mut next_index = first_list_index; - for _ in 0..list.uxNumberOfItems { - let next_list_item = match dump.get(&next_index).expect("List_t entry was not in Hashmap") { - List_Item_struct(li) => li, - List_MiniItem_struct(mli) => match dump.get(&mli.pxNext).expect("MiniListItem pointer invaild") { - List_Item_struct(li) => li, - _ => panic!("MiniListItem of a non empty List does not point to ListItem"), - }, - _ => panic!("List_t entry was not a ListItem"), - }; - match dump.get(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { - TCB_struct(t) => {ret.push(*t)}, - _ => panic!("List content does not equal type"), - } - next_index=next_list_item.pxNext; - } - ret -} -pub fn list_to_tcb_vec_owned(list: List_t, dump: &mut HashMap) -> Vec -{ - let mut ret : Vec = Vec::new(); - if list.uxNumberOfItems == 0 {return ret;} - let last_list_item = match dump.remove(&list.pxIndex).expect("List_t entry was not in Hashmap") { - List_Item_struct(li) => li, - List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { - List_Item_struct(li) => li, - _ => panic!("MiniListItem of a non empty List does not point to ListItem"), - }, - _ => panic!("List_t entry was not a ListItem"), - }; - let mut next_index = last_list_item.pxNext; - let last_tcb = match dump.remove(&last_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { - TCB_struct(t) => t, - _ => panic!("List content does not equal type"), - }; - for _ in 0..list.uxNumberOfItems-1 { - let next_list_item = match dump.remove(&next_index).expect("List_t entry was not in Hashmap") { - List_Item_struct(li) => li, - List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { - List_Item_struct(li) => li, - _ => panic!("MiniListItem of a non empty List does not point to ListItem"), - }, - _ => panic!("List_t entry was not a ListItem"), - }; - match dump.remove(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { - TCB_struct(t) => {ret.push(t)}, - _ => panic!("List content does not equal type"), - } - next_index=next_list_item.pxNext; - } - ret.push(last_tcb); - ret -} -//============================= Feedback - -/// Shared Metadata for a SysStateFeedback -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct SysStateFeedbackState -{ - known_traces: HashMap, // encounters,ticks,length - longest: Vec, -} -impl Named for SysStateFeedbackState -{ - #[inline] - fn name(&self) -> &str { - "sysstate" - } -} -impl FeedbackState for SysStateFeedbackState -{ - fn reset(&mut self) -> Result<(), Error> { - self.longest.clear(); - self.known_traces.clear(); - Ok(()) - } -} - -/// A Feedback reporting novel System-State Transitions -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct NovelSysStateFeedback -{ - last_trace: Option>, - // known_traces: HashMap, -} - -impl Feedback for NovelSysStateFeedback -where - I: Input, - S: HasClientPerfMonitor + HasFeedbackStates, -{ - fn is_interesting( - &mut self, - state: &mut S, - _manager: &mut EM, - _input: &I, - observers: &OT, - _exit_kind: &ExitKind, - ) -> Result - where - EM: EventFirer, - OT: ObserversTuple, - { - let observer = observers.match_name::("sysstate") - .expect("QemuSysStateObserver not found"); - let clock_observer = observers.match_name::("clock") //TODO not fixed - .expect("QemuSysStateObserver not found"); - let feedbackstate = state - .feedback_states_mut() - .match_name_mut::("sysstate") - .unwrap(); - // Do Stuff - let mut hasher = DefaultHasher::new(); - observer.last_run.hash(&mut hasher); - let somehash = hasher.finish(); - let mut is_novel = false; - let mut takes_longer = false; - match feedbackstate.known_traces.get_mut(&somehash) { - None => { - is_novel = true; - feedbackstate.known_traces.insert(somehash,(1,clock_observer.last_runtime(),observer.last_run.len())); - } - Some(s) => { - s.0+=1; - if s.1 < clock_observer.last_runtime() { - s.1 = clock_observer.last_runtime(); - takes_longer = true; - } - } - } - if observer.last_run.len() > feedbackstate.longest.len() { - feedbackstate.longest=observer.last_run.clone(); - } - self.last_trace = Some(observer.last_run.clone()); - // if (!is_novel) { println!("not novel") }; - Ok(is_novel | takes_longer) - } - - /// Append to the testcase the generated metadata in case of a new corpus item - #[inline] - fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { - let a = self.last_trace.take(); - match a { - Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)), - None => (), - } - Ok(()) - } - - /// Discard the stored metadata in case that the testcase is not added to the corpus - #[inline] - fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - self.last_trace = None; - Ok(()) - } -} - -impl Named for NovelSysStateFeedback -{ - #[inline] - fn name(&self) -> &str { - "sysstate" - } -} \ No newline at end of file From 5df99365f6828c2206c1db1daab29fc2533800c4 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 22 Feb 2022 16:50:17 +0100 Subject: [PATCH 41/83] switch to simple stages --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 35 +++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 251c82e324..a9e56533d6 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use libafl::corpus::QueueCorpusScheduler; use libafl_qemu::QemuInstrumentationFilter; use wcet_qemu_sys::sysstate::helpers::QemuSystemStateHelper; use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; @@ -126,6 +127,11 @@ pub fn main() { .long("libafl-edges") .takes_value(true), ) + .arg( + Arg::new("traces") + .long("libafl-traces") + .takes_value(true), + ) .arg( Arg::new("snapshot") .help("The qcow2 file used for snapshots") @@ -190,9 +196,14 @@ pub fn main() { None => None }; + let traces = match res.value_of("traces") { + Some(st) => Some(PathBuf::from(st.to_string())), + None => None + }; + let snapshot = PathBuf::from(res.value_of("snapshot").unwrap().to_string()); - fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel, edges, snapshot) + fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel, edges, traces, snapshot) .expect("An error occurred while fuzzing"); } @@ -216,9 +227,10 @@ fn fuzz( seed_dir: PathBuf, tokenfile: Option, logfile: PathBuf, - timeout: Duration, + _timeout: Duration, kernel: PathBuf, dump_edges: Option, + dump_traces: Option, snapshot: PathBuf, ) -> Result<(), Error> { env::remove_var("LD_LIBRARY_PATH"); @@ -351,19 +363,20 @@ fn fuzz( ) }); - let calibration = CalibrationStage::new(&mut state, &edges_observer); + // let calibration = CalibrationStage::new(&mut state, &edges_observer); // Setup a randomic Input2State stage - let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new()))); + // let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new()))); // Setup a MOPT mutator let mutator = StdMOptMutator::new(&mut state, havoc_mutations().merge(tokens_mutations()), 5)?; - let power = PowerMutationalStage::new(mutator, PowerSchedule::FAST, &edges_observer); + // let power = PowerMutationalStage::new(mutator, PowerSchedule::FAST, &edges_observer); + let mutation = StdMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus // let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); - let scheduler = LenTimeMaximizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); + let scheduler = QueueCorpusScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler @@ -390,7 +403,7 @@ fn fuzz( }; let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1]); - let executor = QemuExecutor::new( + let mut executor = QemuExecutor::new( &mut harness, &emu, tuple_list!( @@ -407,9 +420,9 @@ fn fuzz( )?; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let executor = TimeoutExecutor::new(executor, timeout); + // let executor = TimeoutExecutor::new(executor, timeout); // Show the cmplog observer - let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer)); + // let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer)); // Read tokens if let Some(tokenfile) = &tokenfile { @@ -435,10 +448,10 @@ fn fuzz( } } - let tracing = ShadowTracingStage::new(&mut executor); + // let tracing = ShadowTracingStage::new(&mut executor); // The order of the stages matter! - let mut stages = tuple_list!(calibration, tracing, i2s, power); + let mut stages = tuple_list!(mutation); // Remove target ouput (logs still survive) #[cfg(unix)] From a6294af2c399ff990e5a87ba262e194caf61e05b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 22 Feb 2022 23:14:05 +0100 Subject: [PATCH 42/83] add HitSysStateFeedback --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 18 +++-- .../wcet_qemu_sys/src/sysstate/feedbacks.rs | 69 +++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index a9e56533d6..54c1d99f3b 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,7 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::sysstate::feedbacks::HitSysStateFeedback; +use wcet_qemu_sys::sysstate::MiniFreeRTOSSystemState; use libafl::corpus::QueueCorpusScheduler; use libafl_qemu::QemuInstrumentationFilter; use wcet_qemu_sys::sysstate::helpers::QemuSystemStateHelper; @@ -324,8 +326,6 @@ fn fuzz( let sysstate_observer = QemuSysStateObserver::new(); let sysstate_feedback_state = SysStateFeedbackState::default(); - // Feedback to rate the interestingness of an input - // This one is composed by two Feedbacks in OR let target_map : HashMap<(u64,u64),u8> = match dump_edges { None => HashMap::new(), Some(ref s) => { @@ -334,17 +334,27 @@ fn fuzz( hmap }, }; + let target_trace : Option> = match dump_traces { + None => None, + Some(ref s) => { + let raw = fs::read(s).expect("Can not read dumped traces"); + let trace : Vec = ron::from_str(&String::from_utf8_lossy(&raw)).expect("Can not parse traces"); + Some(trace) + }, + }; + // Feedback to rate the interestingness of an input + // This one is composed by two Feedbacks in OR let feedback = feedback_or!( // New maximization map feedback linked to the edges observer and the feedback state MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), HitImprovingFeedback::new(target_map.clone(), &edges_observer), QemuClockIncreaseFeedback::default(), - ClockFeedback::new_with_observer(&clock_observer), + ClockFeedback::new_with_observer(&clock_observer), NovelSysStateFeedback::default() ); // A feedback to choose if an input is a solution or not - let objective = HitFeedback::new(target_map,0.0,&edges_observer); + let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer),HitSysStateFeedback::new(target_trace)); // let objective = SortedFeedback::new(); // create a State from scratch diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs index cfbe0af0fe..fee5317482 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs @@ -132,6 +132,75 @@ impl Named for NovelSysStateFeedback } } +//============================= + +pub fn match_traces(target: &Vec, last: &Vec) -> bool { + let mut ret = true; + if target.len() > last.len() {return false;} + for i in 0..target.len() { + ret &= target[i].current_task.task_name==last[i].current_task.task_name; + } + ret +} +pub fn match_traces_name(target: &Vec, last: &Vec) -> bool { + let mut ret = true; + if target.len() > last.len() {return false;} + for i in 0..target.len() { + ret &= target[i]==last[i].current_task.task_name; + } + ret +} + +/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct HitSysStateFeedback +{ + target: Option>, +} + +impl Feedback for HitSysStateFeedback +where + I: Input, + S: HasClientPerfMonitor + HasFeedbackStates, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("sysstate") + .expect("QemuSysStateObserver not found"); + // Do Stuff + match &self.target { + Some(s) => { + // #[cfg(debug_assertions)] eprintln!("Hit SysState Feedback trigger"); + Ok(match_traces_name(s, &observer.last_run)) + }, + None => Ok(false), + } + } +} + +impl Named for HitSysStateFeedback +{ + #[inline] + fn name(&self) -> &str { + "hit_sysstate" + } +} + +impl HitSysStateFeedback { + pub fn new(target: Option>) -> Self { + Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())} + } +} //=========================== Debugging Feedback /// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSysStateObserver`] #[derive(Debug)] From 5d08f3a9d770a619d3d441857c264cc122bebaad Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 27 Feb 2022 22:16:55 +0100 Subject: [PATCH 43/83] make multicore optional --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 45 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 54c1d99f3b..5325863177 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -302,7 +302,7 @@ fn fuzz( }); //====== Child Function - let mut run_client = |state: Option>, mut mgr, _core_id| { + let mut run_client = |state: Option>, mut mgr, _core_id| -> std::result::Result<(), libafl::Error> { //====== Set up Emu and termination-point let emu = Emulator::new(&args, &env); @@ -467,6 +467,7 @@ fn fuzz( #[cfg(unix)] let file_null = File::open("/dev/null")?; #[cfg(unix)] + #[cfg(feature = "multicore")] { let null_fd = file_null.as_raw_fd(); dup2(null_fd, io::stdout().as_raw_fd())?; @@ -482,20 +483,32 @@ fn fuzz( }; - match Launcher::builder() - .shmem_provider(shmem_provider) - .configuration(EventConfig::AlwaysUnique) - .monitor(monitor) - .run_client(&mut run_client) - .cores(&Cores::from_cmdline("all").unwrap()) - // .broker_port(1337) - // .remote_broker_addr(remote_broker_addr) - //.stdout_file(Some("/dev/null")) - .build() - .launch() + // Multicore Variant + #[cfg(feature = "multicore")] { - Ok(_) | Err(Error::ShuttingDown) => (), - Err(e) => panic!("{:?}", e), - }; - Ok(()) + match Launcher::builder() + .shmem_provider(shmem_provider) + .configuration(EventConfig::AlwaysUnique) + .monitor(monitor) + .run_client(&mut run_client) + .cores(&Cores::from_cmdline("all").unwrap()) + // .broker_port(1337) + // .remote_broker_addr(remote_broker_addr) + //.stdout_file(Some("/dev/null")) + .build() + .launch() + { + Ok(_) | Err(Error::ShuttingDown) => (), + Err(e) => panic!("{:?}", e), + }; + Ok(()) + } + + // Simple Variant + #[cfg(not(feature = "multicore"))] + { + let stats = SimpleStats::new(|s| println!("{}", s)); + let mgr = SimpleEventManager::new(stats); + run_client(None, mgr, 0) + } } From 71280b89afefcd5a85d98448761e468d3cf62251 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 27 Feb 2022 23:03:10 +0100 Subject: [PATCH 44/83] add multicore feature --- fuzzers/wcet_qemu_sys/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 1f69ba6957..da6876e87e 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [features] default = ["std"] std = [] +multicore = [] [profile.release] debug = true From 5176828dbb715850ab0364d68627014da40da4e6 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 27 Feb 2022 23:14:04 +0100 Subject: [PATCH 45/83] add TimeStateMaximizerCorpusScheduler --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 3 ++- fuzzers/wcet_qemu_sys/src/sysstate/mod.rs | 33 ++++++++++++++++++++++- fuzzers/wcet_qemu_sys/src/worst.rs | 4 +++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 5325863177..bc046a7c30 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -386,7 +386,8 @@ fn fuzz( // A minimization+queue policy to get testcasess from the corpus // let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); - let scheduler = QueueCorpusScheduler::new(); + let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); + // let scheduler = QueueCorpusScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs index 9a0c2c2208..1fb8d69692 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -1,4 +1,7 @@ //! Sysstate referes to the State of a FreeRTOS fuzzing target +use std::collections::hash_map::DefaultHasher; +use libafl::bolts::HasRefCnt; +use libafl::bolts::AsSlice; use std::hash::Hasher; use std::hash::Hash; use hashbrown::HashMap; @@ -98,10 +101,38 @@ impl Hash for MiniFreeRTOSSystemState { #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { inner: Vec, + indices: Vec, // Hashed enumeration of States + tcref: isize, } impl FreeRTOSSystemStateMetadata { pub fn new(inner: Vec) -> Self{ - Self {inner: inner} + let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect(); + Self {inner: inner, indices: tmp, tcref: 0} + } +} +pub fn compute_hash(obj: T) -> u64 +where + T: Hash +{ + let mut s = DefaultHasher::new(); + obj.hash(&mut s); + s.finish() +} + +impl AsSlice for FreeRTOSSystemStateMetadata { + /// Convert the slice of system-states to a slice of hashes over enumerated states + fn as_slice(&self) -> &[usize] { + self.indices.as_slice() + } +} + +impl HasRefCnt for FreeRTOSSystemStateMetadata { + fn refcnt(&self) -> isize { + self.tcref + } + + fn refcnt_mut(&mut self) -> &mut isize { + &mut self.tcref } } diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 1d1d0d73f5..8fef77acf0 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -1,3 +1,4 @@ +use crate::sysstate::FreeRTOSSystemStateMetadata; use num_traits::PrimInt; use core::fmt::Debug; use core::cmp::Ordering::{Greater,Less,Equal}; @@ -509,6 +510,9 @@ where pub type LenTimeMaximizerCorpusScheduler = MinimizerCorpusScheduler, I, MapIndexesMetadata, S>; +pub type TimeStateMaximizerCorpusScheduler = + MinimizerCorpusScheduler, I, FreeRTOSSystemStateMetadata, S>; + /// Multiply the testcase size with the execution time. /// This favors small and quick testcases. #[derive(Debug, Clone)] From 8691c77eb2486406d54508f7b7b758ed2875ed1f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 27 Feb 2022 23:15:59 +0100 Subject: [PATCH 46/83] small fixes --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 3 ++- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 2 +- fuzzers/wcet_qemu_sys/src/sysstate/observers.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index bc046a7c30..1a44424d69 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use libafl::stats::SimpleStats; use wcet_qemu_sys::sysstate::feedbacks::HitSysStateFeedback; use wcet_qemu_sys::sysstate::MiniFreeRTOSSystemState; use libafl::corpus::QueueCorpusScheduler; @@ -73,7 +74,7 @@ use libafl_qemu::{ clock::ClockFeedback, clock::QemuClockIncreaseFeedback }; -use wcet_qemu_sys::worst::{SortedFeedback,HitFeedback,HitImprovingFeedback,LenTimeMaximizerCorpusScheduler}; +use wcet_qemu_sys::worst::{SortedFeedback,HitFeedback,HitImprovingFeedback,TimeStateMaximizerCorpusScheduler}; /// The fuzzer main diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 772a117104..4180be6eeb 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -337,7 +337,7 @@ fn fuzz( ExitKind::Ok }; //======= Set System-State watchpoints - let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1,systick_handler..systick_handler+1,svc_handle..svc_handle+1]); + let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1]); //======= Construct the executor, including the Helpers. The edges_observer still contains the ref to EDGES_MAP let mut executor = QemuExecutor::new( diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs index 30bbd5b7a7..8c5fdb12eb 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -113,7 +113,7 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> fn refine_system_states(input: &mut Vec) -> Vec { let mut ret = Vec::::new(); let mut start_tick : u64 = 0; -for i in input.into_iter() { +for mut i in input.drain(..) { let mut collector = Vec::::new(); for j in i.prio_ready_lists.into_iter().rev() { let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| MiniTCB::from_tcb(x)).collect(); From 0693422e4463b467c0d3c9544c4c8bb1fff29b53 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 9 Mar 2022 12:30:23 +0100 Subject: [PATCH 47/83] add SysMapFeedback --- fuzzers/wcet_qemu_sys/Cargo.toml | 1 + .../wcet_qemu_sys/src/sysstate/feedbacks.rs | 132 ++++++++++++++++++ fuzzers/wcet_qemu_sys/src/sysstate/mod.rs | 12 +- 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index da6876e87e..15e4ab7942 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -23,3 +23,4 @@ nix = "0.23.0" goblin = "0.4.2" either = "1.6.1" num-traits = "0.2" +petgraph = { version="0.6.0", features = ["serde-1"] } \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs index fee5317482..2bf7c143e8 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs @@ -19,6 +19,138 @@ use serde::{Deserialize, Serialize}; use super::MiniFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSysStateObserver; +use petgraph::prelude::DiGraph; +use petgraph::graph::NodeIndex; +use petgraph::Direction; +use std::cmp::Ordering; + +//============================= Graph Feedback + +/// Improved System State Graph +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysMapFeedbackState +{ + graph: DiGraph, + entrypoint: NodeIndex, + name: String, +} +impl SysMapFeedbackState +{ + pub fn new() -> Self { + let mut graph = DiGraph::::new(); + let ind = graph.add_node(MiniFreeRTOSSystemState::default()); + Self {graph: graph, entrypoint: ind, name: String::from("SysMap")} + } + fn insert(&mut self, list: Vec) { + let mut current_index = self.entrypoint; + for n in list { + let mut done = false; + for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { + if n == self.graph[i] { + done = true; + current_index = i; + break; + } + } + if !done { + let j = self.graph.add_node(n); + self.graph.add_edge(current_index, j, ()); + current_index = j; + } + } + } + fn update(&mut self, list: &Vec) -> bool { + let mut current_index = self.entrypoint; + let mut novel = false; + for n in list { + let mut matching : Option = None; + for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { + let tmp = &self.graph[i]; + if n == tmp { + matching = Some(i); + current_index = i; + break; + } + } + match matching { + None => { + novel = true; + let j = self.graph.add_node(n.clone()); + self.graph.add_edge(current_index, j, ()); + current_index = j; + }, + Some(i) => { + if n.get_time() > self.graph[i].get_time() { + novel = true; + self.graph[i]=n.clone(); + } + } + } + } + return novel; + } +} +impl Named for SysMapFeedbackState +{ + #[inline] + fn name(&self) -> &str { + &self.name + } +} +impl FeedbackState for SysMapFeedbackState +{ + fn reset(&mut self) -> Result<(), Error> { + self.graph.clear(); + self.entrypoint = self.graph.add_node(MiniFreeRTOSSystemState::default()); + Ok(()) + } +} + +/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysMapFeedback +{ + name: String +} +impl SysMapFeedback { + pub fn new() -> Self { + Self {name: String::from("SysMapFeedback") } + } +} + +impl Feedback for SysMapFeedback +where + I: Input, + S: HasClientPerfMonitor + HasFeedbackStates, +{ + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("sysstate") + .expect("QemuSysStateObserver not found"); + let feedbackstate = state + .feedback_states_mut() + .match_name_mut::("SysMap") + .unwrap(); + Ok(feedbackstate.update(&observer.last_run)) + } +} +impl Named for SysMapFeedback +{ + #[inline] + fn name(&self) -> &str { + &self.name + } +} //============================= Feedback diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs index 1fb8d69692..af2ca9b0ae 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -30,7 +30,7 @@ pub struct FreeRTOSSystemStateRaw { static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; /// A reduced version of freertos::TCB_t -#[derive(Debug, Default, Serialize, Deserialize, Clone)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] pub struct MiniTCB { task_name: String, priority: u32, @@ -89,6 +89,11 @@ pub struct MiniFreeRTOSSystemState { current_task: MiniTCB, ready_list_after: Vec, } +impl PartialEq for MiniFreeRTOSSystemState { + fn eq(&self, other: &Self) -> bool { + self.current_task == other.current_task && self.ready_list_after == other.ready_list_after + } +} impl Hash for MiniFreeRTOSSystemState { fn hash(&self, state: &mut H) { @@ -96,6 +101,11 @@ impl Hash for MiniFreeRTOSSystemState { // self.ready_list_after.hash(state); } } +impl MiniFreeRTOSSystemState { + fn get_time(&self) -> u64 { + self.end_tick-self.start_tick + } +} // Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] From 89cf096b9d3e4a4117291696934dc738843450f9 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 15 Mar 2022 16:47:00 +0100 Subject: [PATCH 48/83] add input_counter to system state --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 11 +++++++++- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 11 +++++++++- fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs | 20 ++++++++++++++++--- fuzzers/wcet_qemu_sys/src/sysstate/mod.rs | 2 ++ .../wcet_qemu_sys/src/sysstate/observers.rs | 1 + 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 1a44424d69..75ccd07dc7 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -252,6 +252,11 @@ fn fuzz( .expect("Symbol FUZZ_LENGTH not found"); let test_length_ptr = virt2phys(test_length_ptr,&elf.goblin()); println!("FUZZ_LENGTH @ {:#x}", test_length_ptr); + let input_counter = elf + .resolve_symbol("FUZZ_POINTER", 0) + .expect("Symbol FUZZ_POINTER not found"); + // let input_counter = virt2phys(input_counter,&elf.goblin()); + println!("FUZZ_LENGTH @ {:#x}", test_length_ptr); let check_breakpoint = elf .resolve_symbol("trigger_Qemu_break", 0) .expect("Symbol trigger_Qemu_break not found"); @@ -423,7 +428,11 @@ fn fuzz( QemuCmpLogHelper::new(), //QemuAsanHelper::new(), QemuSysSnapshotHelper::new(), - QemuSystemStateHelper::with_instrumentation_filter(system_state_filter,curr_tcb_pointer.try_into().unwrap(),task_queue_addr.try_into().unwrap()) + QemuSystemStateHelper::with_instrumentation_filter( + system_state_filter,curr_tcb_pointer.try_into().unwrap(), + task_queue_addr.try_into().unwrap(), + input_counter.try_into().unwrap() + ) ), tuple_list!(edges_observer, clock_observer,sysstate_observer), &mut fuzzer, diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 4180be6eeb..ab30216796 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -234,6 +234,11 @@ fn fuzz( .expect("Symbol FUZZ_LENGTH not found"); let test_length_ptr = virt2phys(test_length_ptr,&elf.goblin()); println!("FUZZ_LENGTH @ {:#x}", test_length_ptr); + let input_counter = elf + .resolve_symbol("FUZZ_POINTER", 0) + .expect("Symbol FUZZ_POINTER not found"); + // let input_counter = virt2phys(input_counter,&elf.goblin()); + println!("FUZZ_LENGTH @ {:#x}", test_length_ptr); let check_breakpoint = elf .resolve_symbol("trigger_Qemu_break", 0) .expect("Symbol trigger_Qemu_break not found"); @@ -348,7 +353,11 @@ fn fuzz( // QemuCmpLogHelper::new(), // QemuAsanHelper::new(), QemuSysSnapshotHelper::new(), - QemuSystemStateHelper::with_instrumentation_filter(system_state_filter,curr_tcb_pointer.try_into().unwrap(),task_queue_addr.try_into().unwrap()) + QemuSystemStateHelper::with_instrumentation_filter( + system_state_filter,curr_tcb_pointer.try_into().unwrap(), + task_queue_addr.try_into().unwrap(), + input_counter.try_into().unwrap() + ) ), tuple_list!(edges_observer,clock_observer,QemuSysStateObserver::new()), &mut fuzzer, diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs index 8513a778b3..ab84191108 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs @@ -24,21 +24,32 @@ pub struct QemuSystemStateHelper { filter: QemuInstrumentationFilter, tcb_addr: u32, ready_queues: u32, + input_counter: u32, } impl QemuSystemStateHelper { #[must_use] - pub fn new(tcb_addr: u32, ready_queues: u32) -> Self { + pub fn new( + tcb_addr: u32, + ready_queues: u32, + input_counter: u32 + ) -> Self { Self { filter: QemuInstrumentationFilter::None, tcb_addr: tcb_addr, ready_queues: ready_queues, + input_counter: input_counter, } } #[must_use] - pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter, tcb_addr: u32, ready_queues: u32) -> Self { - Self { filter, tcb_addr, ready_queues} + pub fn with_instrumentation_filter( + filter: QemuInstrumentationFilter, + tcb_addr: u32, + ready_queues: u32, + input_counter: u32 + ) -> Self { + Self { filter, tcb_addr, ready_queues, input_counter} } #[must_use] @@ -82,6 +93,9 @@ where let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); let mut sysstate = FreeRTOSSystemStateRaw::default(); sysstate.qemu_tick = emulator.get_ticks(); + let mut buf : [u8; 4] = [0,0,0,0]; + unsafe { emulator.read_mem(h.input_counter.into(), &mut buf) }; + sysstate.input_counter = u32::from_le_bytes(buf); let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); sysstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs index af2ca9b0ae..ea04918788 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -25,6 +25,7 @@ pub struct FreeRTOSSystemStateRaw { current_tcb: TCB_t, prio_ready_lists: [freertos::List_t; NUM_PRIOS], dumping_ground: HashMap, + input_counter: u32, } /// List of system state dumps from QemuHelpers static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; @@ -86,6 +87,7 @@ impl MiniTCB { pub struct MiniFreeRTOSSystemState { start_tick: u64, end_tick: u64, + input_counter: u32, current_task: MiniTCB, ready_list_after: Vec, } diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs index 8c5fdb12eb..76f762ed85 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -124,6 +124,7 @@ for mut i in input.drain(..) { start_tick: start_tick, end_tick: i.qemu_tick, ready_list_after: collector, + input_counter: i.input_counter, }); start_tick=i.qemu_tick; } From 1d0c43081a1eddcfa8ee7ca9f12927b6f1c2486b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 17 Mar 2022 22:37:14 +0100 Subject: [PATCH 49/83] add graph nodes --- .../wcet_qemu_sys/src/sysstate/feedbacks.rs | 64 +++++++++++++++---- .../wcet_qemu_sys/src/sysstate/observers.rs | 7 +- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs index 2bf7c143e8..7561919628 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs @@ -1,3 +1,5 @@ +use libafl::bolts::ownedref::OwnedSlice; +use libafl::inputs::BytesInput; use std::path::PathBuf; use libafl_qemu::QemuClockObserver; use libafl::feedbacks::FeedbackState; @@ -25,48 +27,84 @@ use petgraph::Direction; use std::cmp::Ordering; //============================= Graph Feedback +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] +struct VariantTuple +{ + start_tick: u64, + end_tick: u64, + input_counter: u32, + input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime +} + +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +struct SysGraphNode +{ + base: MiniFreeRTOSSystemState, + variants: Vec, +} +impl SysGraphNode { + fn from(base: MiniFreeRTOSSystemState, input: Vec) -> Self { + SysGraphNode{variants: vec![VariantTuple{ + start_tick: base.start_tick, + end_tick: base.end_tick, + input_counter: base.input_counter, + input:input}], base:base } + } + /// unites the variants of this value with another, draining the other if the bases are equal + fn unite(mut self, other: &mut SysGraphNode) -> bool { + if &self!=other {return false;} + self.variants.append(&mut other.variants); + self.variants.dedup(); + return true; + } +} +impl PartialEq for SysGraphNode { + fn eq(&self, other: &SysGraphNode) -> bool { + self.base==other.base + } +} /// Improved System State Graph #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct SysMapFeedbackState { - graph: DiGraph, + graph: DiGraph, entrypoint: NodeIndex, name: String, } impl SysMapFeedbackState { pub fn new() -> Self { - let mut graph = DiGraph::::new(); - let ind = graph.add_node(MiniFreeRTOSSystemState::default()); + let mut graph = DiGraph::::new(); + let ind = graph.add_node(SysGraphNode::default()); Self {graph: graph, entrypoint: ind, name: String::from("SysMap")} } - fn insert(&mut self, list: Vec) { + fn insert(&mut self, list: Vec, input: &Vec) { let mut current_index = self.entrypoint; for n in list { let mut done = false; for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { - if n == self.graph[i] { + if n == self.graph[i].base { done = true; current_index = i; break; } } if !done { - let j = self.graph.add_node(n); + let j = self.graph.add_node(SysGraphNode::from(n,input.clone())); self.graph.add_edge(current_index, j, ()); current_index = j; } } } - fn update(&mut self, list: &Vec) -> bool { + fn update(&mut self, list: &Vec, input: &Vec) -> bool { let mut current_index = self.entrypoint; let mut novel = false; for n in list { let mut matching : Option = None; for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { let tmp = &self.graph[i]; - if n == tmp { + if n == &tmp.base { matching = Some(i); current_index = i; break; @@ -75,14 +113,14 @@ impl SysMapFeedbackState match matching { None => { novel = true; - let j = self.graph.add_node(n.clone()); + let j = self.graph.add_node(SysGraphNode::from(n.clone(),input.clone())); self.graph.add_edge(current_index, j, ()); current_index = j; }, Some(i) => { - if n.get_time() > self.graph[i].get_time() { + if n.get_time() > self.graph[i].base.get_time() { novel = true; - self.graph[i]=n.clone(); + self.graph[i]=SysGraphNode::from(n.clone(),input.clone()); } } } @@ -101,7 +139,7 @@ impl FeedbackState for SysMapFeedbackState { fn reset(&mut self) -> Result<(), Error> { self.graph.clear(); - self.entrypoint = self.graph.add_node(MiniFreeRTOSSystemState::default()); + self.entrypoint = self.graph.add_node(SysGraphNode::default()); Ok(()) } } @@ -141,7 +179,7 @@ where .feedback_states_mut() .match_name_mut::("SysMap") .unwrap(); - Ok(feedbackstate.update(&observer.last_run)) + Ok(feedbackstate.update(&observer.last_run, &observer.last_input)) } } impl Named for SysMapFeedback diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs index 76f762ed85..8e39aced75 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -1,3 +1,4 @@ +use libafl::inputs::HasTargetBytes; use libafl::bolts::HasLen; use libafl::bolts::tuples::Named; use libafl::Error; @@ -22,10 +23,13 @@ use super::{ pub struct QemuSysStateObserver { pub last_run: Vec, + pub last_input: Vec, name: String, } impl Observer for QemuSysStateObserver +where + I: HasTargetBytes { #[inline] fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { @@ -36,6 +40,7 @@ impl Observer for QemuSysStateObserver #[inline] fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { unsafe {self.last_run = refine_system_states(&mut CURRENT_SYSSTATE_VEC);} + self.last_input=_input.target_bytes().as_slice().to_owned(); // let mut hasher = DefaultHasher::new(); // let mut a = self.parse_last(); // a[0].start_tick=21355; @@ -66,7 +71,7 @@ impl HasLen for QemuSysStateObserver impl QemuSysStateObserver { pub fn new() -> Self { - Self{last_run: vec![], name: "sysstate".to_string()} + Self{last_run: vec![], last_input: vec![], name: "sysstate".to_string()} } } From c92cbe78d8d6ec39e1bc15a760ef0de2d8d88c48 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 20 Mar 2022 21:29:25 +0100 Subject: [PATCH 50/83] add interesting metrics, reorganize --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 25 +- .../wcet_qemu_sys/src/sysstate/feedbacks.rs | 176 +------------- fuzzers/wcet_qemu_sys/src/sysstate/graph.rs | 220 ++++++++++++++++++ fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs | 4 +- fuzzers/wcet_qemu_sys/src/sysstate/mod.rs | 31 +-- .../wcet_qemu_sys/src/sysstate/observers.rs | 20 +- 6 files changed, 269 insertions(+), 207 deletions(-) create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/graph.rs diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 75ccd07dc7..6faef9e6fe 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,8 +1,10 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::sysstate::graph::SysMapFeedback; +use wcet_qemu_sys::sysstate::graph::SysGraphFeedbackState; use libafl::stats::SimpleStats; use wcet_qemu_sys::sysstate::feedbacks::HitSysStateFeedback; -use wcet_qemu_sys::sysstate::MiniFreeRTOSSystemState; +use wcet_qemu_sys::sysstate::RefinedFreeRTOSSystemState; use libafl::corpus::QueueCorpusScheduler; use libafl_qemu::QemuInstrumentationFilter; use wcet_qemu_sys::sysstate::helpers::QemuSystemStateHelper; @@ -330,7 +332,8 @@ fn fuzz( let feedback_state = MapFeedbackState::with_observer(&edges_observer); let sysstate_observer = QemuSysStateObserver::new(); - let sysstate_feedback_state = SysStateFeedbackState::default(); + // let sysstate_feedback_state = SysStateFeedbackState::default(); + let sysstate_feedback_state = SysGraphFeedbackState::new(); let target_map : HashMap<(u64,u64),u8> = match dump_edges { None => HashMap::new(), @@ -340,11 +343,11 @@ fn fuzz( hmap }, }; - let target_trace : Option> = match dump_traces { + let target_trace : Option> = match dump_traces { None => None, Some(ref s) => { let raw = fs::read(s).expect("Can not read dumped traces"); - let trace : Vec = ron::from_str(&String::from_utf8_lossy(&raw)).expect("Can not parse traces"); + let trace : Vec = ron::from_str(&String::from_utf8_lossy(&raw)).expect("Can not parse traces"); Some(trace) }, }; @@ -353,14 +356,16 @@ fn fuzz( let feedback = feedback_or!( // New maximization map feedback linked to the edges observer and the feedback state MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), - HitImprovingFeedback::new(target_map.clone(), &edges_observer), - QemuClockIncreaseFeedback::default(), + // HitImprovingFeedback::new(target_map.clone(), &edges_observer), + // QemuClockIncreaseFeedback::default(), ClockFeedback::new_with_observer(&clock_observer), - NovelSysStateFeedback::default() + // NovelSysStateFeedback::default(), + SysMapFeedback::new() ); // A feedback to choose if an input is a solution or not - let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer),HitSysStateFeedback::new(target_trace)); + // let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer),HitSysStateFeedback::new(target_trace)); + let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer)); // let objective = SortedFeedback::new(); // create a State from scratch @@ -392,8 +397,8 @@ fn fuzz( // A minimization+queue policy to get testcasess from the corpus // let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); - let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); - // let scheduler = QueueCorpusScheduler::new(); + // let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); + let scheduler = QueueCorpusScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs index 7561919628..988ba3eb37 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs @@ -18,7 +18,7 @@ use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use serde::{Deserialize, Serialize}; -use super::MiniFreeRTOSSystemState; +use super::RefinedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSysStateObserver; use petgraph::prelude::DiGraph; @@ -26,170 +26,6 @@ use petgraph::graph::NodeIndex; use petgraph::Direction; use std::cmp::Ordering; -//============================= Graph Feedback -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] -struct VariantTuple -{ - start_tick: u64, - end_tick: u64, - input_counter: u32, - input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime -} - -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -struct SysGraphNode -{ - base: MiniFreeRTOSSystemState, - variants: Vec, -} -impl SysGraphNode { - fn from(base: MiniFreeRTOSSystemState, input: Vec) -> Self { - SysGraphNode{variants: vec![VariantTuple{ - start_tick: base.start_tick, - end_tick: base.end_tick, - input_counter: base.input_counter, - input:input}], base:base } - } - /// unites the variants of this value with another, draining the other if the bases are equal - fn unite(mut self, other: &mut SysGraphNode) -> bool { - if &self!=other {return false;} - self.variants.append(&mut other.variants); - self.variants.dedup(); - return true; - } -} -impl PartialEq for SysGraphNode { - fn eq(&self, other: &SysGraphNode) -> bool { - self.base==other.base - } -} - -/// Improved System State Graph -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct SysMapFeedbackState -{ - graph: DiGraph, - entrypoint: NodeIndex, - name: String, -} -impl SysMapFeedbackState -{ - pub fn new() -> Self { - let mut graph = DiGraph::::new(); - let ind = graph.add_node(SysGraphNode::default()); - Self {graph: graph, entrypoint: ind, name: String::from("SysMap")} - } - fn insert(&mut self, list: Vec, input: &Vec) { - let mut current_index = self.entrypoint; - for n in list { - let mut done = false; - for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { - if n == self.graph[i].base { - done = true; - current_index = i; - break; - } - } - if !done { - let j = self.graph.add_node(SysGraphNode::from(n,input.clone())); - self.graph.add_edge(current_index, j, ()); - current_index = j; - } - } - } - fn update(&mut self, list: &Vec, input: &Vec) -> bool { - let mut current_index = self.entrypoint; - let mut novel = false; - for n in list { - let mut matching : Option = None; - for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { - let tmp = &self.graph[i]; - if n == &tmp.base { - matching = Some(i); - current_index = i; - break; - } - } - match matching { - None => { - novel = true; - let j = self.graph.add_node(SysGraphNode::from(n.clone(),input.clone())); - self.graph.add_edge(current_index, j, ()); - current_index = j; - }, - Some(i) => { - if n.get_time() > self.graph[i].base.get_time() { - novel = true; - self.graph[i]=SysGraphNode::from(n.clone(),input.clone()); - } - } - } - } - return novel; - } -} -impl Named for SysMapFeedbackState -{ - #[inline] - fn name(&self) -> &str { - &self.name - } -} -impl FeedbackState for SysMapFeedbackState -{ - fn reset(&mut self) -> Result<(), Error> { - self.graph.clear(); - self.entrypoint = self.graph.add_node(SysGraphNode::default()); - Ok(()) - } -} - -/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`] -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct SysMapFeedback -{ - name: String -} -impl SysMapFeedback { - pub fn new() -> Self { - Self {name: String::from("SysMapFeedback") } - } -} - -impl Feedback for SysMapFeedback -where - I: Input, - S: HasClientPerfMonitor + HasFeedbackStates, -{ - fn is_interesting( - &mut self, - state: &mut S, - _manager: &mut EM, - _input: &I, - observers: &OT, - _exit_kind: &ExitKind, - ) -> Result - where - EM: EventFirer, - OT: ObserversTuple, - { - let observer = observers.match_name::("sysstate") - .expect("QemuSysStateObserver not found"); - let feedbackstate = state - .feedback_states_mut() - .match_name_mut::("SysMap") - .unwrap(); - Ok(feedbackstate.update(&observer.last_run, &observer.last_input)) - } -} -impl Named for SysMapFeedback -{ - #[inline] - fn name(&self) -> &str { - &self.name - } -} - //============================= Feedback /// Shared Metadata for a SysStateFeedback @@ -197,7 +33,7 @@ impl Named for SysMapFeedback pub struct SysStateFeedbackState { known_traces: HashMap, // encounters,ticks,length - longest: Vec, + longest: Vec, } impl Named for SysStateFeedbackState { @@ -219,7 +55,7 @@ impl FeedbackState for SysStateFeedbackState #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct NovelSysStateFeedback { - last_trace: Option>, + last_trace: Option>, // known_traces: HashMap, } @@ -304,7 +140,7 @@ impl Named for NovelSysStateFeedback //============================= -pub fn match_traces(target: &Vec, last: &Vec) -> bool { +pub fn match_traces(target: &Vec, last: &Vec) -> bool { let mut ret = true; if target.len() > last.len() {return false;} for i in 0..target.len() { @@ -312,7 +148,7 @@ pub fn match_traces(target: &Vec, last: &Vec, last: &Vec) -> bool { +pub fn match_traces_name(target: &Vec, last: &Vec) -> bool { let mut ret = true; if target.len() > last.len() {return false;} for i in 0..target.len() { @@ -367,7 +203,7 @@ impl Named for HitSysStateFeedback } impl HitSysStateFeedback { - pub fn new(target: Option>) -> Self { + pub fn new(target: Option>) -> Self { Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())} } } diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs new file mode 100644 index 0000000000..fff4377058 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -0,0 +1,220 @@ + +/// Feedbacks organizing SystemStates as a graph +use libafl::bolts::ownedref::OwnedSlice; +use libafl::inputs::BytesInput; +use std::path::PathBuf; +use libafl_qemu::QemuClockObserver; +use libafl::feedbacks::FeedbackState; +use libafl::corpus::Testcase; +use libafl::state::HasFeedbackStates; +use libafl::bolts::tuples::MatchName; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; +use std::hash::Hash; +use libafl::events::EventFirer; +use libafl::state::HasClientPerfMonitor; +use libafl::feedbacks::Feedback; +use libafl::bolts::tuples::Named; +use libafl::Error; +use hashbrown::HashMap; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; +use serde::{Deserialize, Serialize}; + +use super::RefinedFreeRTOSSystemState; +use super::FreeRTOSSystemStateMetadata; +use super::observers::QemuSysStateObserver; +use petgraph::prelude::DiGraph; +use petgraph::graph::NodeIndex; +use petgraph::Direction; +use std::cmp::Ordering; + +//============================= Data Structures +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] +struct VariantTuple +{ + start_tick: u64, + end_tick: u64, + input_counter: u32, + input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime +} +impl VariantTuple { + fn from(other: &RefinedFreeRTOSSystemState,input: Vec) -> Self { + VariantTuple{ + start_tick: other.start_tick, + end_tick: other.end_tick, + input_counter: other.input_counter, + input: input, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +struct SysGraphNode +{ + base: RefinedFreeRTOSSystemState, + variants: Vec, +} +impl SysGraphNode { + fn from(base: RefinedFreeRTOSSystemState, input: Vec) -> Self { + SysGraphNode{variants: vec![VariantTuple::from(&base, input)], base:base } + } + /// unites the variants of this value with another, draining the other if the bases are equal + fn unite(&mut self, other: &mut SysGraphNode) -> bool { + if self!=other {return false;} + self.variants.append(&mut other.variants); + self.variants.dedup(); + return true; + } + /// add a Varint from a [`RefinedFreeRTOSSystemState`] + fn unite_raw(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec) -> bool { + if &self.base!=other {return false;} + self.variants.push(VariantTuple::from(other, input.clone())); + self.variants.dedup(); + return true; + } + /// add a Varint from a [`RefinedFreeRTOSSystemState`], if it's interesting + fn unite_interesting(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec) -> bool { + if &self.base!=other {return false;} + let interesting = + self.variants.iter().all(|x| x.end_tick-x.start_tickother.end_tick-other.start_tick) || // shortest variant + self.variants.iter().all(|x| x.input_counter>other.input_counter) || // longest input + self.variants.iter().all(|x| x.input_counter bool { + self.base==other.base + } +} + +//============================= Graph Feedback + +/// Improved System State Graph +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysGraphFeedbackState +{ + graph: DiGraph, + entrypoint: NodeIndex, + name: String, +} +impl SysGraphFeedbackState +{ + pub fn new() -> Self { + let mut graph = DiGraph::::new(); + let ind = graph.add_node(SysGraphNode::default()); + Self {graph: graph, entrypoint: ind, name: String::from("SysMap")} + } + fn insert(&mut self, list: Vec, input: &Vec) { + let mut current_index = self.entrypoint; + for n in list { + let mut done = false; + for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { + if n == self.graph[i].base { + done = true; + current_index = i; + break; + } + } + if !done { + let j = self.graph.add_node(SysGraphNode::from(n,input.clone())); + self.graph.add_edge(current_index, j, ()); + current_index = j; + } + } + } + /// Try adding a system state path from a [Vec], return true if the path was interesting + fn update(&mut self, list: &Vec, input: &Vec) -> bool { + let mut current_index = self.entrypoint; + let mut novel = false; + for n in list { + let mut matching : Option = None; + for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { + let tmp = &self.graph[i]; + if n == &tmp.base { + matching = Some(i); + current_index = i; + break; + } + } + match matching { + None => { + novel = true; + let j = self.graph.add_node(SysGraphNode::from(n.clone(),input.clone())); + self.graph.add_edge(current_index, j, ()); + current_index = j; + }, + Some(i) => { + novel |= self.graph[i].unite_interesting(&n, input); + } + } + } + return novel; + } +} +impl Named for SysGraphFeedbackState +{ + #[inline] + fn name(&self) -> &str { + &self.name + } +} +impl FeedbackState for SysGraphFeedbackState +{ + fn reset(&mut self) -> Result<(), Error> { + self.graph.clear(); + self.entrypoint = self.graph.add_node(SysGraphNode::default()); + Ok(()) + } +} + +/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysMapFeedback +{ + name: String +} +impl SysMapFeedback { + pub fn new() -> Self { + Self {name: String::from("SysMapFeedback") } + } +} + +impl Feedback for SysMapFeedback +where + I: Input, + S: HasClientPerfMonitor + HasFeedbackStates, +{ + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("sysstate") + .expect("QemuSysStateObserver not found"); + let feedbackstate = state + .feedback_states_mut() + .match_name_mut::("SysMap") + .unwrap(); + Ok(feedbackstate.update(&observer.last_run, &observer.last_input)) + } +} +impl Named for SysMapFeedback +{ + #[inline] + fn name(&self) -> &str { + &self.name + } +} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs index ab84191108..793081dc6c 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs @@ -1,4 +1,4 @@ -use crate::sysstate::FreeRTOSSystemStateRaw; +use crate::sysstate::RawFreeRTOSSystemState; use crate::sysstate::CURRENT_SYSSTATE_VEC; use crate::sysstate::NUM_PRIOS; use super::freertos::TCB_t; @@ -91,7 +91,7 @@ where return; } let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); - let mut sysstate = FreeRTOSSystemStateRaw::default(); + let mut sysstate = RawFreeRTOSSystemState::default(); sysstate.qemu_tick = emulator.get_ticks(); let mut buf : [u8; 4] = [0,0,0,0]; unsafe { emulator.read_mem(h.input_counter.into(), &mut buf) }; diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs index ea04918788..cd97a928a7 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -13,6 +13,7 @@ pub mod freertos; pub mod helpers; pub mod observers; pub mod feedbacks; +pub mod graph; // Constants const NUM_PRIOS: usize = 5; @@ -20,7 +21,7 @@ const NUM_PRIOS: usize = 5; //============================= Struct definitions /// Raw info Dump from Qemu #[derive(Debug, Default, Serialize, Deserialize)] -pub struct FreeRTOSSystemStateRaw { +pub struct RawFreeRTOSSystemState { qemu_tick: u64, current_tcb: TCB_t, prio_ready_lists: [freertos::List_t; NUM_PRIOS], @@ -28,11 +29,11 @@ pub struct FreeRTOSSystemStateRaw { input_counter: u32, } /// List of system state dumps from QemuHelpers -static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; +static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; /// A reduced version of freertos::TCB_t #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] -pub struct MiniTCB { +pub struct RefinedTCB { task_name: String, priority: u32, base_priority: u32, @@ -41,7 +42,7 @@ pub struct MiniTCB { notify_state: u8, } -impl Hash for MiniTCB { +impl Hash for RefinedTCB { fn hash(&self, state: &mut H) { self.task_name.hash(state); // self.priority.hash(state); @@ -51,7 +52,7 @@ impl Hash for MiniTCB { } } -impl MiniTCB { +impl RefinedTCB { pub fn from_tcb(input: &TCB_t) -> Self { unsafe { let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); @@ -84,40 +85,40 @@ impl MiniTCB { /// Refined information about the states an execution transitioned between #[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct MiniFreeRTOSSystemState { +pub struct RefinedFreeRTOSSystemState { start_tick: u64, end_tick: u64, input_counter: u32, - current_task: MiniTCB, - ready_list_after: Vec, + current_task: RefinedTCB, + ready_list_after: Vec, } -impl PartialEq for MiniFreeRTOSSystemState { +impl PartialEq for RefinedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { self.current_task == other.current_task && self.ready_list_after == other.ready_list_after } } -impl Hash for MiniFreeRTOSSystemState { +impl Hash for RefinedFreeRTOSSystemState { fn hash(&self, state: &mut H) { self.current_task.hash(state); - // self.ready_list_after.hash(state); + self.ready_list_after.hash(state); } } -impl MiniFreeRTOSSystemState { +impl RefinedFreeRTOSSystemState { fn get_time(&self) -> u64 { self.end_tick-self.start_tick } } -// Wrapper around Vec to attach as Metadata +// Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { - inner: Vec, + inner: Vec, indices: Vec, // Hashed enumeration of States tcref: isize, } impl FreeRTOSSystemStateMetadata { - pub fn new(inner: Vec) -> Self{ + pub fn new(inner: Vec) -> Self{ let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect(); Self {inner: inner, indices: tmp, tcref: 0} } diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs index 8e39aced75..fc7ff1c6a2 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -8,9 +8,9 @@ use serde::{Deserialize, Serialize}; use super::{ CURRENT_SYSSTATE_VEC, - FreeRTOSSystemStateRaw, - MiniTCB, - MiniFreeRTOSSystemState, + RawFreeRTOSSystemState, + RefinedTCB, + RefinedFreeRTOSSystemState, freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*}, }; @@ -22,7 +22,7 @@ use super::{ #[allow(clippy::unsafe_derive_deserialize)] pub struct QemuSysStateObserver { - pub last_run: Vec, + pub last_run: Vec, pub last_input: Vec, name: String, } @@ -115,17 +115,17 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> ret } /// Drains a List of raw SystemStates to produce a refined trace -fn refine_system_states(input: &mut Vec) -> Vec { -let mut ret = Vec::::new(); +fn refine_system_states(input: &mut Vec) -> Vec { +let mut ret = Vec::::new(); let mut start_tick : u64 = 0; for mut i in input.drain(..) { - let mut collector = Vec::::new(); + let mut collector = Vec::::new(); for j in i.prio_ready_lists.into_iter().rev() { - let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| MiniTCB::from_tcb(x)).collect(); + let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); collector.append(&mut tmp); } - ret.push(MiniFreeRTOSSystemState { - current_task: MiniTCB::from_tcb_owned(i.current_tcb), + ret.push(RefinedFreeRTOSSystemState { + current_task: RefinedTCB::from_tcb_owned(i.current_tcb), start_tick: start_tick, end_tick: i.qemu_tick, ready_list_after: collector, From 66babddb02453f7034accb0a495d67c20b2d55b5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 21 Mar 2022 00:00:32 +0100 Subject: [PATCH 51/83] add graph trace metadata, scheduler --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 4 +- fuzzers/wcet_qemu_sys/src/sysstate/graph.rs | 70 +++++++++++++++++++-- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 6faef9e6fe..a4b05b9be5 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::sysstate::graph::GraphMaximizerCorpusScheduler; use wcet_qemu_sys::sysstate::graph::SysMapFeedback; use wcet_qemu_sys::sysstate::graph::SysGraphFeedbackState; use libafl::stats::SimpleStats; @@ -398,7 +399,8 @@ fn fuzz( // A minimization+queue policy to get testcasess from the corpus // let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); // let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); - let scheduler = QueueCorpusScheduler::new(); + let scheduler = GraphMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); + // let scheduler = QueueCorpusScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs index fff4377058..418e4a9954 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -1,5 +1,9 @@ /// Feedbacks organizing SystemStates as a graph +use crate::worst::MaxExecsLenFavFactor; +use libafl::corpus::MinimizerCorpusScheduler; +use libafl::bolts::HasRefCnt; +use libafl::bolts::AsSlice; use libafl::bolts::ownedref::OwnedSlice; use libafl::inputs::BytesInput; use std::path::PathBuf; @@ -93,6 +97,39 @@ impl PartialEq for SysGraphNode { } } +// Wrapper around Vec to attach as Metadata +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct SysGraphMetadata { + indices: Vec, // Hashed enumeration of States + tcref: isize, +} +impl SysGraphMetadata { + pub fn new(inner: Vec) -> Self{ + Self {indices: inner.into_iter().map(|x| x.index()).collect(), tcref: 0} + } +} +impl AsSlice for SysGraphMetadata { + /// Convert the slice of system-states to a slice of hashes over enumerated states + fn as_slice(&self) -> &[usize] { + self.indices.as_slice() + } +} + +impl HasRefCnt for SysGraphMetadata { + fn refcnt(&self) -> isize { + self.tcref + } + + fn refcnt_mut(&mut self) -> &mut isize { + &mut self.tcref + } +} + +libafl::impl_serdeany!(SysGraphMetadata); + +pub type GraphMaximizerCorpusScheduler = + MinimizerCorpusScheduler, I, SysGraphMetadata, S>; + //============================= Graph Feedback /// Improved System State Graph @@ -129,9 +166,10 @@ impl SysGraphFeedbackState } } /// Try adding a system state path from a [Vec], return true if the path was interesting - fn update(&mut self, list: &Vec, input: &Vec) -> bool { + fn update(&mut self, list: &Vec, input: &Vec) -> (bool, Vec) { let mut current_index = self.entrypoint; let mut novel = false; + let mut trace : Vec = vec![current_index]; for n in list { let mut matching : Option = None; for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { @@ -153,8 +191,9 @@ impl SysGraphFeedbackState novel |= self.graph[i].unite_interesting(&n, input); } } + trace.push(current_index); } - return novel; + return (novel, trace); } } impl Named for SysGraphFeedbackState @@ -177,11 +216,12 @@ impl FeedbackState for SysGraphFeedbackState #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct SysMapFeedback { - name: String + name: String, + last_trace: Option>, } impl SysMapFeedback { pub fn new() -> Self { - Self {name: String::from("SysMapFeedback") } + Self {name: String::from("SysMapFeedback"), last_trace: None } } } @@ -208,7 +248,27 @@ where .feedback_states_mut() .match_name_mut::("SysMap") .unwrap(); - Ok(feedbackstate.update(&observer.last_run, &observer.last_input)) + let ret = feedbackstate.update(&observer.last_run, &observer.last_input); + self.last_trace = Some(ret.1); + Ok(ret.0) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + let a = self.last_trace.take(); + match a { + Some(s) => testcase.metadata_mut().insert(SysGraphMetadata::new(s)), + None => (), + } + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.last_trace = None; + Ok(()) } } impl Named for SysMapFeedback From 4e3acf85ad217d8203f5144c5e45861ee3618fe1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 23 Mar 2022 01:37:04 +0100 Subject: [PATCH 52/83] add RandGraphSnippetMutator --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 6 +- fuzzers/wcet_qemu_sys/src/sysstate/graph.rs | 121 +++++++++++++++++++- 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index a4b05b9be5..c67d2db867 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::sysstate::graph::RandGraphSnippetMutator; use wcet_qemu_sys::sysstate::graph::GraphMaximizerCorpusScheduler; use wcet_qemu_sys::sysstate::graph::SysMapFeedback; use wcet_qemu_sys::sysstate::graph::SysGraphFeedbackState; @@ -391,7 +392,10 @@ fn fuzz( // let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new()))); // Setup a MOPT mutator - let mutator = StdMOptMutator::new(&mut state, havoc_mutations().merge(tokens_mutations()), 5)?; + let mutator = StdMOptMutator::new(&mut state, havoc_mutations() + .merge(tokens_mutations()) + .merge(tuple_list!(RandGraphSnippetMutator::new())), + 5)?; // let power = PowerMutationalStage::new(mutator, PowerSchedule::FAST, &edges_observer); let mutation = StdMutationalStage::new(mutator); diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs index 418e4a9954..1c495526a2 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -1,5 +1,14 @@ /// Feedbacks organizing SystemStates as a graph +use libafl::inputs::HasBytesVec; +use libafl::bolts::rands::RandomSeed; +use libafl::bolts::rands::StdRand; +use libafl::mutators::Mutator; +use libafl::mutators::MutationResult; +use core::marker::PhantomData; +use libafl::state::HasCorpus; +use libafl::state::HasSolutions; +use libafl::state::HasRand; use crate::worst::MaxExecsLenFavFactor; use libafl::corpus::MinimizerCorpusScheduler; use libafl::bolts::HasRefCnt; @@ -32,6 +41,10 @@ use petgraph::graph::NodeIndex; use petgraph::Direction; use std::cmp::Ordering; +use libafl::bolts::rands::Rand; +use rand::SeedableRng; +use rand::seq::SliceRandom; + //============================= Data Structures #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] struct VariantTuple @@ -100,12 +113,13 @@ impl PartialEq for SysGraphNode { // Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct SysGraphMetadata { - indices: Vec, // Hashed enumeration of States + inner: Vec, + indices: Vec, tcref: isize, } impl SysGraphMetadata { pub fn new(inner: Vec) -> Self{ - Self {indices: inner.into_iter().map(|x| x.index()).collect(), tcref: 0} + Self {indices: inner.iter().map(|x| x.index()).collect(), inner: inner, tcref: 0} } } impl AsSlice for SysGraphMetadata { @@ -277,4 +291,107 @@ impl Named for SysMapFeedback fn name(&self) -> &str { &self.name } +} + +//============================= Mutators + +pub struct RandGraphSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + phantom: PhantomData<(I, S)>, +} +impl RandGraphSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + pub fn new() -> Self { + RandGraphSnippetMutator{phantom: PhantomData} + } +} +impl Mutator for RandGraphSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32 + ) -> Result + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + drop(tmp); + + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let g = &feedbackstate.graph; + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace =tmp.expect("SysGraphMetadata not found"); + // follow the path, extract snippets from last reads, find common snippets. + // those are likley keys parts. choose random parts from other sibling traces + let sibling_inputs : Vec<&Vec>= g[*trace.inner.last().unwrap()].variants.iter().map(|x| &x.input).collect(); + let mut snippet_collector = vec![]; + let mut per_input_counters = HashMap::<&Vec,usize>::new(); // ugly workaround to track multiple inputs + for t in &trace.inner { + let node = &g[*t]; + let mut per_node_snippets = HashMap::<&Vec,&[u8]>::new(); + for v in &node.variants { + match per_input_counters.get_mut(&v.input) { + None => { + if sibling_inputs.iter().any(|x| *x==&v.input) { // only collect info about siblin inputs from target + per_input_counters.insert(&v.input, v.input_counter.try_into().unwrap()); + } + }, + Some(x) => { + let x_u = *x; + if x_u = vec![]; + for c in snippet_collector { + new_input.clone_from_slice(myrand.choose(c).1); + } + for i in new_input.iter().enumerate() { + input.bytes_mut()[i.0]=*i.1; + } + + Ok(MutationResult::Mutated) + } + + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option + ) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for RandGraphSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn name(&self) -> &str { + "RandGraphSnippetMutator" + } } \ No newline at end of file From e8b24b0c7a6c808c838cb106aadaebcae942f4ff Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 5 Apr 2022 19:05:36 +0200 Subject: [PATCH 53/83] remove broken imports --- fuzzers/wcet_qemu_sys/src/sysstate/graph.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs index 1c495526a2..434fa47c81 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -42,8 +42,6 @@ use petgraph::Direction; use std::cmp::Ordering; use libafl::bolts::rands::Rand; -use rand::SeedableRng; -use rand::seq::SliceRandom; //============================= Data Structures #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] From 47a4bc19d4d94d907e5515342f4f7ef0fbf37d6e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 6 Apr 2022 21:03:21 +0200 Subject: [PATCH 54/83] save last post interrupt edge in app --- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 12 ++++- fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs | 23 +++++++- fuzzers/wcet_qemu_sys/src/sysstate/mod.rs | 6 ++- .../wcet_qemu_sys/src/sysstate/observers.rs | 1 + libafl_qemu/src/edges.rs | 53 +++++++++++++++++-- 5 files changed, 89 insertions(+), 6 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index ab30216796..0888b33cee 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -269,6 +269,16 @@ fn fuzz( .expect("Symbol xPortPendSVHandler not found"); let svh = virt2phys(svh,&elf.goblin()); println!("PendHandle at {:#x}", svh); + let app_code_start = elf + .resolve_symbol("__APP_CODE_START__", 0) + .expect("Symbol __APP_CODE_START__ not found"); + let app_code_start = virt2phys(app_code_start,&elf.goblin()); + let app_code_end = elf + .resolve_symbol("__APP_CODE_END__", 0) + .expect("Symbol __APP_CODE_END__ not found"); + let app_code_end = virt2phys(app_code_end,&elf.goblin()); + let app_range = app_code_start..app_code_end; + println!("App Code {:x}-{:x}",app_code_start,app_code_end); @@ -349,7 +359,7 @@ fn fuzz( &mut harness, &emu, tuple_list!( - QemuEdgeCoverageHelper::new(), + QemuEdgeCoverageHelper::with_app_range(app_range), // QemuCmpLogHelper::new(), // QemuAsanHelper::new(), QemuSysSnapshotHelper::new(), diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs index 793081dc6c..30c4b4fa52 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs @@ -1,3 +1,4 @@ +use libafl_qemu::edges::QemuEdgesMapMetadata; use crate::sysstate::RawFreeRTOSSystemState; use crate::sysstate::CURRENT_SYSSTATE_VEC; use crate::sysstate::NUM_PRIOS; @@ -11,6 +12,7 @@ use libafl_qemu::{ emu::Emulator, executor::QemuExecutor, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, + edges::SAVED_JUMP, }; //============================= Struct definitions @@ -78,7 +80,7 @@ where pub fn exec_syscall_hook( emulator: &Emulator, helpers: &mut QT, - _state: &mut S, + state: &mut S, pc: u64, ) where @@ -99,6 +101,25 @@ where let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); sysstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); + + unsafe { + match SAVED_JUMP { + Some(s) => { + let meta = state + .metadata_mut() + .get::() + .unwrap(); + for (k,v) in meta.map.iter() { + if *v==s { + // println!("Jump Saved {:x}",k.1); + sysstate.last_pc = Some(k.1); /* Currently save APP entry points */ + break; + } + } + }, + None => (), + } + } // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); for i in 0..NUM_PRIOS { diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs index cd97a928a7..16649c61be 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -27,6 +27,7 @@ pub struct RawFreeRTOSSystemState { prio_ready_lists: [freertos::List_t; NUM_PRIOS], dumping_ground: HashMap, input_counter: u32, + last_pc: Option, } /// List of system state dumps from QemuHelpers static mut CURRENT_SYSSTATE_VEC: Vec = vec![]; @@ -88,13 +89,15 @@ impl RefinedTCB { pub struct RefinedFreeRTOSSystemState { start_tick: u64, end_tick: u64, + last_pc: Option, input_counter: u32, current_task: RefinedTCB, ready_list_after: Vec, } impl PartialEq for RefinedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { - self.current_task == other.current_task && self.ready_list_after == other.ready_list_after + self.current_task == other.current_task && self.ready_list_after == other.ready_list_after && + self.last_pc == other.last_pc } } @@ -102,6 +105,7 @@ impl Hash for RefinedFreeRTOSSystemState { fn hash(&self, state: &mut H) { self.current_task.hash(state); self.ready_list_after.hash(state); + self.last_pc.hash(state); } } impl RefinedFreeRTOSSystemState { diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs index fc7ff1c6a2..3328cc1db0 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -130,6 +130,7 @@ for mut i in input.drain(..) { end_tick: i.qemu_tick, ready_list_after: collector, input_counter: i.input_counter, + last_pc: i.last_pc, }); start_tick=i.qemu_tick; } diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index 7da9b452f8..545d337470 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -1,3 +1,4 @@ +use std::ops::Range; use hashbrown::{hash_map::Entry, HashMap}; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; pub use libafl_targets::{EDGES_MAP, EDGES_MAP_SIZE, MAX_EDGES_NUM}; @@ -31,6 +32,7 @@ libafl::impl_serdeany!(QemuEdgesMapMetadata); #[derive(Debug)] pub struct QemuEdgeCoverageHelper { filter: QemuInstrumentationFilter, + app_range: Option>, } impl QemuEdgeCoverageHelper { @@ -38,18 +40,37 @@ impl QemuEdgeCoverageHelper { pub fn new() -> Self { Self { filter: QemuInstrumentationFilter::None, + app_range: None, } } #[must_use] - pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self { - Self { filter } + pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter, app_range: Option>) -> Self { + Self { filter, app_range } + } + + #[must_use] + pub fn with_app_range(app_range: Range) -> Self { + Self { filter: QemuInstrumentationFilter::None, app_range: Some(app_range) } } #[must_use] pub fn must_instrument(&self, addr: u64) -> bool { self.filter.allowed(addr) } + + #[must_use] + pub fn must_save(&self, src: u64, dst: u64) -> bool { + match &self.app_range { + None => false, + Some(s) => { + // println!("must_save {} {:x} {:x}",s.contains(&src) != s.contains(&dst),src,dst); + // s.contains(&src) && !s.contains(&dst) + // println!("must_save {} {:x} {:x}",src==0&&dst!=0x9cc,src,dst); + src==0&&dst!=0x9cc + }, + } + } } impl Default for QemuEdgeCoverageHelper { @@ -70,7 +91,14 @@ where QT: QemuHelperTuple, { executor.hook_edge_generation(gen_unique_edge_ids::); - executor.emulator().set_exec_edge_hook(trace_edge_hitcount); + match self.app_range { + None => executor.emulator().set_exec_edge_hook(trace_edge_hitcount), + Some(_) => executor.emulator().set_exec_edge_hook(trace_edge_hitcount_and_app_range), + } + } + + fn pre_exec(&mut self, _emulator: &Emulator, _input: &I) { + unsafe { SAVED_JUMP=None; } } } @@ -83,6 +111,9 @@ fn hash_me(mut x: u64) -> u64 { x } +pub static mut MUST_SAVE: [bool; EDGES_MAP_SIZE] = [false; EDGES_MAP_SIZE]; +pub static mut SAVED_JUMP: Option = None; + pub fn gen_unique_edge_ids( _emulator: &Emulator, helpers: &mut QT, @@ -95,10 +126,12 @@ where I: Input, QT: QemuHelperTuple, { + let mut must_save = false; if let Some(h) = helpers.match_first_type::() { if !h.must_instrument(src) && !h.must_instrument(dest) { return None; } + must_save = h.must_save(src, dest); } if state.metadata().get::().is_none() { state.add_metadata(QemuEdgesMapMetadata::new()); @@ -114,6 +147,9 @@ where let nxt = (id as usize + 1) & (EDGES_MAP_SIZE - 1); unsafe { MAX_EDGES_NUM = max(MAX_EDGES_NUM, nxt); + if must_save { + MUST_SAVE[id as usize] = true; + } } Some(id) } @@ -123,6 +159,9 @@ where meta.current_id = (id + 1) & (EDGES_MAP_SIZE as u64 - 1); unsafe { MAX_EDGES_NUM = meta.current_id as usize; + if must_save { + MUST_SAVE[id as usize] = true; + } } Some(id as u64) } @@ -148,6 +187,14 @@ where Some(hash_me(src) ^ hash_me(dest)) } + +pub extern "C" fn trace_edge_hitcount_and_app_range(id: u64) { + unsafe { + if MUST_SAVE[id as usize] { SAVED_JUMP=Some(id); } + EDGES_MAP[id as usize] = EDGES_MAP[id as usize].wrapping_add(1); + } +} + pub extern "C" fn trace_edge_hitcount(id: u64) { unsafe { EDGES_MAP[id as usize] = EDGES_MAP[id as usize].wrapping_add(1); From fc4b72274e9ae698b51385392f8065d2fb6e91cc Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 8 Apr 2022 00:53:05 +0200 Subject: [PATCH 55/83] remember branches from app to os --- fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs | 4 ++-- libafl_qemu/src/edges.rs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs index 30c4b4fa52..8b80ce6f86 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs @@ -103,7 +103,7 @@ where sysstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); unsafe { - match SAVED_JUMP { + match SAVED_JUMP.take() { Some(s) => { let meta = state .metadata_mut() @@ -112,7 +112,7 @@ where for (k,v) in meta.map.iter() { if *v==s { // println!("Jump Saved {:x}",k.1); - sysstate.last_pc = Some(k.1); /* Currently save APP entry points */ + sysstate.last_pc = Some(k.0); /* Currently save APP entry points */ break; } } diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index 545d337470..aa2e0c79ba 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -64,10 +64,12 @@ impl QemuEdgeCoverageHelper { match &self.app_range { None => false, Some(s) => { - // println!("must_save {} {:x} {:x}",s.contains(&src) != s.contains(&dst),src,dst); - // s.contains(&src) && !s.contains(&dst) + // if src != 0 { + // println!("must_save {} {:x} {:x}",s.contains(&src) && !s.contains(&dst),src,dst); + // } + s.contains(&src) && !s.contains(&dst) // println!("must_save {} {:x} {:x}",src==0&&dst!=0x9cc,src,dst); - src==0&&dst!=0x9cc + // src==0&&dst!=0x9cc }, } } From 865f2786a82d754f1bd544f29585591afd9bc68e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 11 Apr 2022 22:58:43 +0200 Subject: [PATCH 56/83] add jmp instrumentaion --- fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs | 12 +---- libafl_qemu/src/edges.rs | 48 +++++++++++------ libafl_qemu/src/emu.rs | 18 +++++++ libafl_qemu/src/executor.rs | 54 +++++++++++++++++++ 4 files changed, 104 insertions(+), 28 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs index 8b80ce6f86..4650d52fb0 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs @@ -105,17 +105,7 @@ where unsafe { match SAVED_JUMP.take() { Some(s) => { - let meta = state - .metadata_mut() - .get::() - .unwrap(); - for (k,v) in meta.map.iter() { - if *v==s { - // println!("Jump Saved {:x}",k.1); - sysstate.last_pc = Some(k.0); /* Currently save APP entry points */ - break; - } - } + sysstate.last_pc = Some(s.0); }, None => (), } diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index aa2e0c79ba..85f26a9890 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -93,9 +93,10 @@ where QT: QemuHelperTuple, { executor.hook_edge_generation(gen_unique_edge_ids::); - match self.app_range { - None => executor.emulator().set_exec_edge_hook(trace_edge_hitcount), - Some(_) => executor.emulator().set_exec_edge_hook(trace_edge_hitcount_and_app_range), + executor.emulator().set_exec_edge_hook(trace_edge_hitcount); + if self.app_range.is_some() { + executor.hook_jmp_generation(gen_jmp_instrument::); + executor.emulator().set_exec_jmp_hook(trace_jmp) } } @@ -113,8 +114,31 @@ fn hash_me(mut x: u64) -> u64 { x } -pub static mut MUST_SAVE: [bool; EDGES_MAP_SIZE] = [false; EDGES_MAP_SIZE]; -pub static mut SAVED_JUMP: Option = None; +/// Save the source and destination of last interesting jump +pub static mut SAVED_JUMP: Option<(u64, u64)> = None; + +pub fn gen_jmp_instrument( + _emulator: &Emulator, + helpers: &mut QT, + state: &mut S, + src: u64, + dest: u64, +) -> Option +where + S: HasMetadata, + I: Input, + QT: QemuHelperTuple, +{ + if let Some(h) = helpers.match_first_type::() { + if !h.must_instrument(src) && !h.must_instrument(dest) { + return None; + } + if !h.must_save(src, dest) { + return None; + } + } + Some(1) +} pub fn gen_unique_edge_ids( _emulator: &Emulator, @@ -128,12 +152,10 @@ where I: Input, QT: QemuHelperTuple, { - let mut must_save = false; if let Some(h) = helpers.match_first_type::() { if !h.must_instrument(src) && !h.must_instrument(dest) { return None; } - must_save = h.must_save(src, dest); } if state.metadata().get::().is_none() { state.add_metadata(QemuEdgesMapMetadata::new()); @@ -149,9 +171,6 @@ where let nxt = (id as usize + 1) & (EDGES_MAP_SIZE - 1); unsafe { MAX_EDGES_NUM = max(MAX_EDGES_NUM, nxt); - if must_save { - MUST_SAVE[id as usize] = true; - } } Some(id) } @@ -161,9 +180,6 @@ where meta.current_id = (id + 1) & (EDGES_MAP_SIZE as u64 - 1); unsafe { MAX_EDGES_NUM = meta.current_id as usize; - if must_save { - MUST_SAVE[id as usize] = true; - } } Some(id as u64) } @@ -189,11 +205,9 @@ where Some(hash_me(src) ^ hash_me(dest)) } - -pub extern "C" fn trace_edge_hitcount_and_app_range(id: u64) { +pub extern "C" fn trace_jmp(src: u64, des: u64) { unsafe { - if MUST_SAVE[id as usize] { SAVED_JUMP=Some(id); } - EDGES_MAP[id as usize] = EDGES_MAP[id as usize].wrapping_add(1); + SAVED_JUMP=Some((src, des)); } } diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 6f5d46a20c..1a2387b68b 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -223,6 +223,10 @@ extern "C" { static mut libafl_exec_edge_hook: unsafe extern "C" fn(u64); static mut libafl_gen_edge_hook: unsafe extern "C" fn(u64, u64) -> u64; + #[cfg(feature = "systemmode")] + static mut libafl_exec_jmp_hook: unsafe extern "C" fn(u64, u64); + #[cfg(feature = "systemmode")] + static mut libafl_gen_jmp_hook: unsafe extern "C" fn(u64, u64) -> u64; static mut libafl_exec_block_hook: unsafe extern "C" fn(u64); static mut libafl_gen_block_hook: unsafe extern "C" fn(u64) -> u64; @@ -532,6 +536,20 @@ impl Emulator { } } + #[cfg(feature = "systemmode")] + pub fn set_exec_jmp_hook(&self, hook: extern "C" fn(src: u64, dest: u64)) { + unsafe { + libafl_exec_jmp_hook = hook; + } + } + + #[cfg(feature = "systemmode")] + pub fn set_gen_jmp_hook(&self, hook: extern "C" fn(src: u64, dest: u64) -> u64) { + unsafe { + libafl_gen_jmp_hook = hook; + } + } + pub fn set_exec_block_hook(&self, hook: extern "C" fn(pc: u64)) { unsafe { libafl_exec_block_hook = hook; diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 21dce9e53f..f6c4b6c4d8 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -27,6 +27,37 @@ use crate::{ static mut QEMU_HELPERS_PTR: *const c_void = ptr::null(); +static mut GEN_JMP_HOOK_PTR: *const c_void = ptr::null(); +extern "C" fn gen_jmp_hook_wrapper(src: u64, dst: u64) -> u64 +where + I: Input, + QT: QemuHelperTuple, +{ + unsafe { + let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); + let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) -> Option = + transmute(GEN_JMP_HOOK_PTR); + (func)(&emulator, helpers, state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id) + } +} + +static mut JMP_HOOKS: Vec<*const c_void> = vec![]; +extern "C" fn jmp_hooks_wrapper(src: u64, dst: u64) +where + I: Input, + QT: QemuHelperTuple, +{ + let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; + let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); + for hook in unsafe { &JMP_HOOKS } { + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, src, dst); + } +} + static mut GEN_EDGE_HOOK_PTR: *const c_void = ptr::null(); extern "C" fn gen_edge_hook_wrapper(src: u64, dst: u64) -> u64 where @@ -530,6 +561,29 @@ where .set_exec_edge_hook(edge_hooks_wrapper::); } + #[cfg(feature = "systemmode")] + #[allow(clippy::unused_self)] + pub fn hook_jmp_generation( + &self, + hook: fn(&Emulator, &mut QT, &mut S, src: u64, dest: u64) -> Option, + ) { + unsafe { + GEN_JMP_HOOK_PTR = hook as *const _; + } + self.emulator + .set_gen_jmp_hook(gen_jmp_hook_wrapper::); + } + + #[cfg(feature = "systemmode")] + #[allow(clippy::unused_self)] + pub fn hook_jmp_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, src: u64, dest: u64)) { + unsafe { + JMP_HOOKS.push(hook as *const _); + } + self.emulator + .set_exec_jmp_hook(jmp_hooks_wrapper::); + } + #[allow(clippy::unused_self)] pub fn hook_block_generation( &self, From 5c4238e0ee77faac85203748ef3eddd417038f4f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 11 Apr 2022 22:59:06 +0200 Subject: [PATCH 57/83] remove output from clock observer --- libafl_qemu/src/clock.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/libafl_qemu/src/clock.rs b/libafl_qemu/src/clock.rs index 3a9cdd803a..3b12dce3dc 100644 --- a/libafl_qemu/src/clock.rs +++ b/libafl_qemu/src/clock.rs @@ -122,7 +122,6 @@ where fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { unsafe { self.last_runtime = emu::libafl_get_clock() }; - #[cfg(debug_assertions)] println!("Observer Clock: {}",self.last_runtime()); Ok(()) } } From 6551fc31f4ce2e888169598a60ea98e200d2c691 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 19 Apr 2022 23:43:58 +0200 Subject: [PATCH 58/83] add interrupts --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 12 +++++++++++- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 12 +++++++++++- fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs | 17 +++++++++++++++++ libafl_qemu/src/emu.rs | 4 ++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index c67d2db867..76af11c4b4 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::sysstate::helpers::INTR_OFFSET; use wcet_qemu_sys::sysstate::graph::RandGraphSnippetMutator; use wcet_qemu_sys::sysstate::graph::GraphMaximizerCorpusScheduler; use wcet_qemu_sys::sysstate::graph::SysMapFeedback; @@ -415,12 +416,21 @@ fn fuzz( let target = input.target_bytes(); let mut buf = target.as_slice(); let mut len = buf.len(); - if len > 32 { + let mut int_tick : Option = None; + if len > 4 { + let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt + t.copy_from_slice(&buf[0..4]); + int_tick = Some(u32::from_le_bytes(t) as u64); + buf = &buf[4..]; + len = buf.len(); + } + if len >= 32 { buf = &buf[0..32]; len = 32; } unsafe { + INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 0888b33cee..5588aa46d4 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::sysstate::helpers::INTR_OFFSET; use std::io::Read; use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; use wcet_qemu_sys::sysstate::feedbacks::DumpSystraceFeedback; @@ -337,12 +338,21 @@ fn fuzz( let target = input.target_bytes(); let mut buf = target.as_slice(); let mut len = buf.len(); - if len > 32 { + let mut int_tick : Option = None; + if len > 4 { + let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt + t.copy_from_slice(&buf[0..4]); + int_tick = Some(u32::from_le_bytes(t) as u64); + buf = &buf[4..]; + len = buf.len(); + } + if len >= 32 { buf = &buf[0..32]; len = 32; } unsafe { + INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs index 4650d52fb0..030c25a830 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs @@ -17,6 +17,8 @@ use libafl_qemu::{ //============================= Struct definitions +pub static mut INTR_OFFSET : Option = None; +pub static mut INTR_DONE : bool = true; //============================= Qemu Helper @@ -88,6 +90,21 @@ where I: Input, QT: QemuHelperTuple, { + unsafe { + match INTR_OFFSET { + None => (), + Some(off) => { + if emulator.get_ticks() > off { + if !INTR_DONE { + libafl_qemu::emu::libafl_send_irq(0); + INTR_DONE = true; + } + } else { + INTR_DONE = false; + } + }, + } + } let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); if !h.must_instrument(pc) { return; diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 1a2387b68b..7740a0903d 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -218,6 +218,10 @@ extern "C" { fn libafl_maps_next(map_info: *const c_void, ret: *mut MapInfo) -> *const c_void; + #[cfg(feature = "systemmode")] + #[cfg(feature = "arm")] + pub fn libafl_send_irq(irqn: u32); + static exec_path: *const u8; static guest_base: usize; From 67165640c5f396f5906bd79bb5db676a36e0ed47 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 24 Apr 2022 20:47:30 +0200 Subject: [PATCH 59/83] fuzz until first solution --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 2 +- libafl/src/fuzzer/mod.rs | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 76af11c4b4..dafa03fc46 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -507,7 +507,7 @@ fn fuzz( } fuzzer - .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + .fuzz_for_solution(&mut stages, &mut executor, &mut state, &mut mgr) .expect("Error in the fuzzing loop"); // Never reached diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index cc049bd84c..b89459645a 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -150,7 +150,7 @@ pub trait Fuzzer where I: Input, EM: ProgressReporter, - S: HasExecutions + HasClientPerfMonitor, + S: HasExecutions + HasClientPerfMonitor + HasSolutions, { /// Fuzz for a single iteration /// Returns the index of the last fuzzed corpus item @@ -182,6 +182,26 @@ where } } + /// Fuzz until the first solution. + fn fuzz_for_solution( + &mut self, + stages: &mut ST, + executor: &mut E, + state: &mut S, + manager: &mut EM, + ) -> Result { + let mut last = current_time(); + let monitor_timeout = STATS_TIMEOUT_DEFAULT; + let mut done = false; + let mut tiem = 0; + while !done { + self.fuzz_one(stages, executor, state, manager)?; + last = manager.maybe_report_progress(state, last, monitor_timeout)?; + done = state.solutions().count() > 1; + } + return Ok(0); + } + /// Fuzz for n iterations /// Returns the index of the last fuzzed corpus item /// @@ -513,7 +533,7 @@ where EM: EventManager, F: Feedback, I: Input, - S: HasClientPerfMonitor + HasExecutions, + S: HasClientPerfMonitor + HasExecutions + HasSolutions, OF: Feedback, ST: StagesTuple, { From 0b638d90202be5d9724ee1e87447ed7c0535f7d9 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 26 Apr 2022 00:35:11 +0200 Subject: [PATCH 60/83] new setup for interrupts --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 10 +++++----- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 9 +++++---- libafl_qemu/src/emu.rs | 3 +++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index dafa03fc46..c3142bb3c2 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -70,8 +70,7 @@ use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, - emu::Emulator, - filter_qemu_args, + emu::Emulator, filter_qemu_args, libafl_int_offset, snapshot_sys::QemuSysSnapshotHelper, QemuExecutor, clock, @@ -416,11 +415,11 @@ fn fuzz( let target = input.target_bytes(); let mut buf = target.as_slice(); let mut len = buf.len(); - let mut int_tick : Option = None; + let mut int_tick : Option = None; if len > 4 { let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt t.copy_from_slice(&buf[0..4]); - int_tick = Some(u32::from_le_bytes(t) as u64); + int_tick = Some(u32::from_le_bytes(t)); buf = &buf[4..]; len = buf.len(); } @@ -430,7 +429,8 @@ fn fuzz( } unsafe { - INTR_OFFSET = int_tick; + libafl_int_offset = int_tick.unwrap_or(0); + // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 5588aa46d4..98a65c1f21 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -40,7 +40,7 @@ use libafl::{ use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, - emu::Emulator, filter_qemu_args, + emu::Emulator, filter_qemu_args, libafl_int_offset, elf::EasyElf, snapshot_sys::QemuSysSnapshotHelper, clock::{QemuClockObserver}, @@ -338,11 +338,11 @@ fn fuzz( let target = input.target_bytes(); let mut buf = target.as_slice(); let mut len = buf.len(); - let mut int_tick : Option = None; + let mut int_tick : Option = None; if len > 4 { let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt t.copy_from_slice(&buf[0..4]); - int_tick = Some(u32::from_le_bytes(t) as u64); + int_tick = Some(u32::from_le_bytes(t)); buf = &buf[4..]; len = buf.len(); } @@ -352,7 +352,8 @@ fn fuzz( } unsafe { - INTR_OFFSET = int_tick; + libafl_int_offset = int_tick.unwrap_or(0); + // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 7740a0903d..1dcc12c6ad 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -221,6 +221,9 @@ extern "C" { #[cfg(feature = "systemmode")] #[cfg(feature = "arm")] pub fn libafl_send_irq(irqn: u32); + #[cfg(feature = "systemmode")] + #[cfg(feature = "arm")] + pub static mut libafl_int_offset: u32; static exec_path: *const u8; static guest_base: usize; From 01e40ded1d1ecef2c13aeef9038db662c8c81201 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 1 May 2022 22:07:37 +0200 Subject: [PATCH 61/83] fix interrupt input, graph output --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 36 +++++++++++++++---- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 11 +++--- fuzzers/wcet_qemu_sys/src/sysstate/graph.rs | 25 ++++++++++--- fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs | 15 -------- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index c3142bb3c2..8c1b70c111 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,8 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use libafl::bolts::tuples::MatchName; +use libafl::state::HasFeedbackStates; +use wcet_qemu_sys::sysstate::graph::SysGraphMetadata; use wcet_qemu_sys::sysstate::helpers::INTR_OFFSET; use wcet_qemu_sys::sysstate::graph::RandGraphSnippetMutator; use wcet_qemu_sys::sysstate::graph::GraphMaximizerCorpusScheduler; @@ -29,6 +32,8 @@ use std::{ path::PathBuf, process, }; +use petgraph::prelude::DiGraph; +use petgraph::dot::{Dot, Config}; use libafl::{ bolts::{ @@ -292,7 +297,7 @@ fn fuzz( "-kernel", kernel.to_str().unwrap(), "-serial", "stdio", "-nographic", "-snapshot", "-drive", format!("if=none,format=qcow2,file={}",snapshot.to_string_lossy()).as_str(), - "-icount", "shift=auto,align=off,sleep=off", + "-icount", "shift=3,align=off,sleep=off", "-S" ].iter().map(|x| x.to_string()).collect(); let env: Vec<(String, String)> = env::vars().collect(); @@ -416,11 +421,12 @@ fn fuzz( let mut buf = target.as_slice(); let mut len = buf.len(); let mut int_tick : Option = None; - if len > 4 { + if len > 2 { let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt - t.copy_from_slice(&buf[0..4]); + t[0]=buf[0]; + t[1]=buf[1]; int_tick = Some(u32::from_le_bytes(t)); - buf = &buf[4..]; + buf = &buf[2..]; len = buf.len(); } if len >= 32 { @@ -429,7 +435,7 @@ fn fuzz( } unsafe { - libafl_int_offset = int_tick.unwrap_or(0); + libafl_int_offset = 347780+int_tick.unwrap_or(0); // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); @@ -506,9 +512,27 @@ fn fuzz( dup2(null_fd, io::stderr().as_raw_fd())?; } + // fuzzer + // .fuzz_for_solution(&mut stages, &mut executor, &mut state, &mut mgr) + // .expect("Error in the fuzzing loop"); fuzzer - .fuzz_for_solution(&mut stages, &mut executor, &mut state, &mut mgr) + .fuzz_loop_for(&mut stages, &mut executor, &mut state, &mut mgr, 20) .expect("Error in the fuzzing loop"); + + + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let newgraph = feedbackstate.graph.map( + |_, n| n.get_taskname(), + |_, e| e, + ); + // println!("{:?}",feedbackstate.graph); + // println!("{:?}",Dot::with_config(&feedbackstate.graph, &[Config::EdgeNoLabel])); + let tempg = format!("{:?}",Dot::with_config(&newgraph, &[Config::EdgeNoLabel])); + fs::write("./graph.dot",tempg).expect("Graph can not be written"); + // Never reached Ok(()) diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 98a65c1f21..bcb0d16734 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -216,7 +216,7 @@ fn fuzz( "-kernel", kernel.to_str().unwrap(), "-serial", "stdio", "-nographic", "-snapshot", "-drive", format!("if=none,format=qcow2,file={}",snapshot.to_string_lossy()).as_str(), - "-icount", "shift=auto,align=off,sleep=off", + "-icount", "shift=3,align=off,sleep=off", "-S" ].iter().map(|x| x.to_string()).collect(); let emu = Emulator::new(&mut args, &mut env); @@ -339,11 +339,12 @@ fn fuzz( let mut buf = target.as_slice(); let mut len = buf.len(); let mut int_tick : Option = None; - if len > 4 { + if len > 2 { let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt - t.copy_from_slice(&buf[0..4]); + t[0]=buf[0]; + t[1]=buf[1]; int_tick = Some(u32::from_le_bytes(t)); - buf = &buf[4..]; + buf = &buf[2..]; len = buf.len(); } if len >= 32 { @@ -352,7 +353,7 @@ fn fuzz( } unsafe { - libafl_int_offset = int_tick.unwrap_or(0); + libafl_int_offset = 347780+int_tick.unwrap_or(0); // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs index 434fa47c81..a9601d0bf0 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -64,7 +64,7 @@ impl VariantTuple { } #[derive(Serialize, Deserialize, Clone, Debug, Default)] -struct SysGraphNode +pub struct SysGraphNode { base: RefinedFreeRTOSSystemState, variants: Vec, @@ -101,6 +101,9 @@ impl SysGraphNode { } return interesting; } + pub fn get_taskname(&self) -> &str { + &self.base.current_task.task_name + } } impl PartialEq for SysGraphNode { fn eq(&self, other: &SysGraphNode) -> bool { @@ -148,16 +151,22 @@ pub type GraphMaximizerCorpusScheduler = #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct SysGraphFeedbackState { - graph: DiGraph, + pub graph: DiGraph, entrypoint: NodeIndex, + exit: NodeIndex, name: String, } impl SysGraphFeedbackState { pub fn new() -> Self { let mut graph = DiGraph::::new(); - let ind = graph.add_node(SysGraphNode::default()); - Self {graph: graph, entrypoint: ind, name: String::from("SysMap")} + let mut entry = SysGraphNode::default(); + entry.base.current_task.task_name="Start".to_string(); + let mut exit = SysGraphNode::default(); + exit.base.current_task.task_name="End".to_string(); + let entry = graph.add_node(entry); + let exit = graph.add_node(exit); + Self {graph: graph, entrypoint: entry, exit: exit, name: String::from("SysMap")} } fn insert(&mut self, list: Vec, input: &Vec) { let mut current_index = self.entrypoint; @@ -205,6 +214,7 @@ impl SysGraphFeedbackState } trace.push(current_index); } + self.graph.update_edge(current_index, self.exit, ()); // every path ends in the exit noded return (novel, trace); } } @@ -219,7 +229,12 @@ impl FeedbackState for SysGraphFeedbackState { fn reset(&mut self) -> Result<(), Error> { self.graph.clear(); - self.entrypoint = self.graph.add_node(SysGraphNode::default()); + let mut entry = SysGraphNode::default(); + entry.base.current_task.task_name="Start".to_string(); + let mut exit = SysGraphNode::default(); + exit.base.current_task.task_name="End".to_string(); + self.entrypoint = self.graph.add_node(entry); + self.exit = self.graph.add_node(exit); Ok(()) } } diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs index 030c25a830..a61ae25fd5 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs @@ -90,21 +90,6 @@ where I: Input, QT: QemuHelperTuple, { - unsafe { - match INTR_OFFSET { - None => (), - Some(off) => { - if emulator.get_ticks() > off { - if !INTR_DONE { - libafl_qemu::emu::libafl_send_irq(0); - INTR_DONE = true; - } - } else { - INTR_DONE = false; - } - }, - } - } let h = helpers.match_first_type::().expect("QemuSystemHelper not found in helper tupel"); if !h.must_instrument(pc) { return; From 865e97cca7736b72ae01151c099dc44bbe0706af Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 4 May 2022 21:11:19 +0200 Subject: [PATCH 62/83] fuzz for sulution with timeout --- libafl/src/fuzzer/mod.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index b89459645a..c61a8c3e90 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -193,11 +193,29 @@ where let mut last = current_time(); let monitor_timeout = STATS_TIMEOUT_DEFAULT; let mut done = false; - let mut tiem = 0; while !done { self.fuzz_one(stages, executor, state, manager)?; last = manager.maybe_report_progress(state, last, monitor_timeout)?; - done = state.solutions().count() > 1; + done = state.solutions().count() > 0; + } + return Ok(0); + } + + /// Fuzz until solution with limit + fn fuzz_for_solution_or_n( + &mut self, + stages: &mut ST, + executor: &mut E, + state: &mut S, + manager: &mut EM, + iters: u64, + ) -> Result { + let mut last = current_time(); + let monitor_timeout = STATS_TIMEOUT_DEFAULT; + for _ in 0..iters { + self.fuzz_one(stages, executor, state, manager)?; + last = manager.maybe_report_progress(state, last, monitor_timeout)?; + if state.solutions().count() > 0 {break;} } return Ok(0); } From 18a592d763c1fd6b2a8bc125930796dc562b6638 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 4 May 2022 21:11:39 +0200 Subject: [PATCH 63/83] update clock to time calc --- libafl_qemu/src/clock.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libafl_qemu/src/clock.rs b/libafl_qemu/src/clock.rs index 3b12dce3dc..cab72171bd 100644 --- a/libafl_qemu/src/clock.rs +++ b/libafl_qemu/src/clock.rs @@ -145,7 +145,6 @@ impl Default for QemuClockObserver { //========== Feedback /// Nop feedback that annotates execution time in the new testcase, if any /// for this Feedback, the testcase is never interesting (use with an OR) -/// It decides, if the given [`ClockObserver`] value of a run is interesting. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ClockFeedback { exec_time: Option, @@ -179,7 +178,7 @@ where #[inline] fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { *testcase.exec_time_mut() = match self.exec_time { - Some(s) => Some(Duration::new(0,360*s as u32)), + Some(s) => Some(Duration::from_nanos(s << 3)), //emulated time is << 3, real time more like * 360 None => None, }; self.exec_time = None; From 70dec21c8ca04e175eb767af0b2488a14a0f7209 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 4 May 2022 22:55:26 +0200 Subject: [PATCH 64/83] add feature flags for fuzz components --- fuzzers/wcet_qemu_sys/Cargo.toml | 13 ++++ fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 82 ++++++++++++++----------- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 15e4ab7942..86ada0c635 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -9,6 +9,19 @@ default = ["std"] std = [] multicore = [] +# select which feedbacks to use. enable at least one. +feed_afl = [] +feed_clock = [] +feed_state = [] +feed_graph = [] + +# choose exactly one scheduler +sched_queue = [] +sched_state = [] +sched_graph = [] + +muta_snip = [ "sched_graph" ] + [profile.release] debug = true diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 8c1b70c111..3b8cb330ac 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,8 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use libafl::inputs::Input; +use libafl::feedbacks::Feedback; +use libafl::HasFeedback; use libafl::bolts::tuples::MatchName; use libafl::state::HasFeedbackStates; use wcet_qemu_sys::sysstate::graph::SysGraphMetadata; @@ -333,13 +336,15 @@ fn fuzz( let clock_observer = QemuClockObserver::default(); // Create an observation channel using cmplog map - let cmplog_observer = CmpLogObserver::new("cmplog", unsafe { &mut cmplog::CMPLOG_MAP }, true); + // let cmplog_observer = CmpLogObserver::new("cmplog", unsafe { &mut cmplog::CMPLOG_MAP }, true); // The state of the edges feedback. let feedback_state = MapFeedbackState::with_observer(&edges_observer); let sysstate_observer = QemuSysStateObserver::new(); - // let sysstate_feedback_state = SysStateFeedbackState::default(); + #[cfg(feature = "sched_state")] + let sysstate_feedback_state = SysStateFeedbackState::default(); + #[cfg(not(feature = "sched_state"))] let sysstate_feedback_state = SysGraphFeedbackState::new(); let target_map : HashMap<(u64,u64),u8> = match dump_edges { @@ -359,20 +364,19 @@ fn fuzz( }, }; // Feedback to rate the interestingness of an input - // This one is composed by two Feedbacks in OR - let feedback = feedback_or!( - // New maximization map feedback linked to the edges observer and the feedback state - MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), - // HitImprovingFeedback::new(target_map.clone(), &edges_observer), - // QemuClockIncreaseFeedback::default(), - ClockFeedback::new_with_observer(&clock_observer), - // NovelSysStateFeedback::default(), - SysMapFeedback::new() - ); + let feedback = ClockFeedback::new_with_observer(&clock_observer); + #[cfg(feature = "feed_afl")] + let feedback = feedback_or!(feedback, MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false)); + #[cfg(feature = "feed_clock")] + let feedback = feedback_or!(feedback, QemuClockIncreaseFeedback::default()); + #[cfg(feature = "feed_state")] + let feedback = feedback_or!(feedback, NovelSysStateFeedback::default()); + #[cfg(feature = "feed_graph")] + let feedback = feedback_or!(feedback, SysMapFeedback::new()); // A feedback to choose if an input is a solution or not - // let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer),HitSysStateFeedback::new(target_trace)); - let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer)); + let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer),HitSysStateFeedback::new(target_trace)); + // let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer)); // let objective = SortedFeedback::new(); // create a State from scratch @@ -396,20 +400,25 @@ fn fuzz( // Setup a randomic Input2State stage // let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new()))); + #[cfg(feature = "muta_snip")] + let mutator_list = havoc_mutations().merge(tokens_mutations()) + .merge(tuple_list!(RandGraphSnippetMutator::new())); + #[cfg(not(feature = "muta_snip"))] + let mutator_list = havoc_mutations().merge(tokens_mutations()); // Setup a MOPT mutator - let mutator = StdMOptMutator::new(&mut state, havoc_mutations() - .merge(tokens_mutations()) - .merge(tuple_list!(RandGraphSnippetMutator::new())), - 5)?; + let mutator = StdMOptMutator::new(&mut state, mutator_list,5)?; // let power = PowerMutationalStage::new(mutator, PowerSchedule::FAST, &edges_observer); let mutation = StdMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus // let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); - // let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); + #[cfg(feature = "sched_queue")] + let scheduler = QueueCorpusScheduler::new(); + #[cfg(feature = "sched_state")] + let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); + #[cfg(feature = "sched_graph")] let scheduler = GraphMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); - // let scheduler = QueueCorpusScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler @@ -512,26 +521,27 @@ fn fuzz( dup2(null_fd, io::stderr().as_raw_fd())?; } - // fuzzer - // .fuzz_for_solution(&mut stages, &mut executor, &mut state, &mut mgr) - // .expect("Error in the fuzzing loop"); fuzzer - .fuzz_loop_for(&mut stages, &mut executor, &mut state, &mut mgr, 20) + .fuzz_for_solution_or_n(&mut stages, &mut executor, &mut state, &mut mgr, 10000) .expect("Error in the fuzzing loop"); + // fuzzer + // .fuzz_loop_for(&mut stages, &mut executor, &mut state, &mut mgr, 20) + // .expect("Error in the fuzzing loop"); - let feedbackstate = state - .feedback_states() - .match_name::("SysMap") - .unwrap(); - let newgraph = feedbackstate.graph.map( - |_, n| n.get_taskname(), - |_, e| e, - ); - // println!("{:?}",feedbackstate.graph); - // println!("{:?}",Dot::with_config(&feedbackstate.graph, &[Config::EdgeNoLabel])); - let tempg = format!("{:?}",Dot::with_config(&newgraph, &[Config::EdgeNoLabel])); - fs::write("./graph.dot",tempg).expect("Graph can not be written"); + #[cfg(feature = "feed_graph")] + { + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let newgraph = feedbackstate.graph.map( + |_, n| n.get_taskname(), + |_, e| e, + ); + let tempg = format!("{:?}",Dot::with_config(&newgraph, &[Config::EdgeNoLabel])); + fs::write("./graph.dot",tempg).expect("Graph can not be written"); + } // Never reached From 5d28754e1b3b46ef8b6b595543d3bdefd2cf0979 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 4 May 2022 22:56:40 +0200 Subject: [PATCH 65/83] add Makefile for benchmarks --- fuzzers/wcet_qemu_sys/Makefile | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 fuzzers/wcet_qemu_sys/Makefile diff --git a/fuzzers/wcet_qemu_sys/Makefile b/fuzzers/wcet_qemu_sys/Makefile new file mode 100644 index 0000000000..a6120ca39e --- /dev/null +++ b/fuzzers/wcet_qemu_sys/Makefile @@ -0,0 +1,50 @@ +BENCHDIR = target/bench +TARGET = tmr.axf +EXAMPLE = "\xff\05\x84\x84\x84\x84\x84\x84\x84\x84" +TARGET_TRACE = tmp/target_trace.ron +TARGET_EDGES = tmp/target_edges.ron + +$(BENCHDIR)/bin: + mkdir -p $@ + +$(BENCHDIR)/bin/fuzz_vanilla: $(BENCHDIR)/bin + cargo build --features feed_afl,sched_queue + cp target/debug/fuzzer $@ + +$(BENCHDIR)/bin/fuzz_state: $(BENCHDIR)/bin + cargo build --features feed_state,sched_state + cp target/debug/fuzzer $@ + +$(BENCHDIR)/bin/fuzz_graph: $(BENCHDIR)/bin + cargo build --features feed_graph,sched_graph + cp target/debug/fuzzer $@ + +$(BENCHDIR)/bin/fuzz_graph_snip: $(BENCHDIR)/bin + cargo build --features feed_graph,sched_graph,muta_snip + cp target/debug/fuzzer $@ + +$(BENCHDIR)/bin/fuzz_graph_afl: $(BENCHDIR)/bin + cargo build --features feed_graph,sched_graph,feed_afl + cp target/debug/fuzzer $@ + +$(BENCHDIR)/bin/fuzz_graph_all: $(BENCHDIR)/bin + cargo build --features feed_graph,sched_graph,feed_afl,muta_snip + cp target/debug/fuzzer $@ + +binaries: $(BENCHDIR)/bin/fuzz_vanilla $(BENCHDIR)/bin/fuzz_state $(BENCHDIR)/bin/fuzz_graph $(BENCHDIR)/bin/fuzz_graph_snip $(BENCHDIR)/bin/fuzz_graph_afl $(BENCHDIR)/bin/fuzz_graph_all + +# variants: vanilla, state, graph, graph_snip, graph_afl, graph_all +$(BENCHDIR)/bench_%.log: $(BENCHDIR)/bin/fuzz_% $(TARGET_TRACE) + for i in {1..5}; do ./fuzzer_bench.sh $< $(TARGET) --libafl-traces $(TARGET_TRACE) > $@_$$i; done + for i in $@_*; do tail -n 1 $$i >> $@; done + +benchmarks: target/bench/bench_vanilla.log target/bench/bench_state.log target/bench/bench_graph.log target/bench/bench_graph_snip.log target/bench/bench_graph_afl.log target/bench/bench_graph_all.log + +all: binaries + +clean_bench: + rm -rf $(BENCHDIR)/bench_* + +clean: + rm -rf target/bench + \ No newline at end of file From 86ca7863caf3585709b289e08a5a608659330843 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 8 May 2022 13:45:10 +0200 Subject: [PATCH 66/83] add RandGraphSuffixMutator --- fuzzers/wcet_qemu_sys/src/sysstate/graph.rs | 94 ++++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs index a9601d0bf0..22e8b63d57 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -307,7 +307,7 @@ impl Named for SysMapFeedback } //============================= Mutators - +//=============================== Snippets pub struct RandGraphSnippetMutator where I: Input + HasBytesVec, @@ -380,7 +380,7 @@ where } let mut new_input : Vec = vec![]; for c in snippet_collector { - new_input.clone_from_slice(myrand.choose(c).1); + new_input.extend_from_slice(myrand.choose(c).1); } for i in new_input.iter().enumerate() { input.bytes_mut()[i.0]=*i.1; @@ -407,4 +407,94 @@ where fn name(&self) -> &str { "RandGraphSnippetMutator" } +} +//============================= Mutators +//=============================== Suffix +pub struct RandGraphSuffixMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + phantom: PhantomData<(I, S)>, +} +impl RandGraphSuffixMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + pub fn new() -> Self { + RandGraphSuffixMutator{phantom: PhantomData} + } +} +impl Mutator for RandGraphSuffixMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32 + ) -> Result + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + drop(tmp); + + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let g = &feedbackstate.graph; + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace =tmp.expect("SysGraphMetadata not found"); + // follow the path, extract snippets from last reads, find common snippets. + // those are likley keys parts. choose random parts from other sibling traces + let inp_c_end = g[*trace.inner.last().unwrap()].base.input_counter; + let mut num_to_reverse = 1; + for t in trace.inner.iter().rev() { + let int_c_prefix = g[*t].base.input_counter; + if int_c_prefix < inp_c_end { + num_to_reverse-=1; + if num_to_reverse<=0 { + let mut new_input=input.bytes()[..(int_c_prefix as usize)].to_vec(); + let mut ext : Vec = (int_c_prefix..inp_c_end).map(|_| myrand.next().to_le_bytes()).flatten().collect(); + new_input.append(&mut ext); + for i in new_input.iter().enumerate() { + if input.bytes_mut().len()>i.0 { + input.bytes_mut()[i.0]=*i.1; + } + else { break }; + } + break; + } + } + } + Ok(MutationResult::Mutated) + } + + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option + ) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for RandGraphSuffixMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn name(&self) -> &str { + "RandGraphSuffixMutator" + } } \ No newline at end of file From 517b3d3da755b3bd90d24c83d389f050575757c6 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 26 May 2022 16:11:30 +0200 Subject: [PATCH 67/83] add instrumentation call after breakpoint --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 6 ++++-- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 7 +++++-- libafl_qemu/src/emu.rs | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 3b8cb330ac..036bf9175c 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -78,7 +78,7 @@ use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, - emu::Emulator, filter_qemu_args, libafl_int_offset, + emu::Emulator, filter_qemu_args, libafl_int_offset, libafl_exec_block_hook, snapshot_sys::QemuSysSnapshotHelper, QemuExecutor, clock, @@ -450,12 +450,14 @@ fn fuzz( emu.write_mem(input_addr,buf); emu.run(); + // since the breakpoint interrupted the last task the last state needs to be recorded + libafl_exec_block_hook(check_breakpoint); } ExitKind::Ok }; - let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1]); + let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1,check_breakpoint..check_breakpoint+1]); let mut executor = QemuExecutor::new( &mut harness, &emu, diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index bcb0d16734..7ae8a95d47 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -40,7 +40,7 @@ use libafl::{ use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, - emu::Emulator, filter_qemu_args, libafl_int_offset, + emu::Emulator, filter_qemu_args, libafl_int_offset, libafl_exec_block_hook, elf::EasyElf, snapshot_sys::QemuSysSnapshotHelper, clock::{QemuClockObserver}, @@ -359,12 +359,15 @@ fn fuzz( emu.write_mem(input_addr,buf); emu.run(); + // since the breakpoint interrupted the last task the last state needs to be recorded + libafl_exec_block_hook(check_breakpoint); + println!("Qemu Ticks: {}",emu.get_ticks()); } ExitKind::Ok }; //======= Set System-State watchpoints - let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1]); + let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1,check_breakpoint..check_breakpoint+1]); //======= Construct the executor, including the Helpers. The edges_observer still contains the ref to EDGES_MAP let mut executor = QemuExecutor::new( diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 1dcc12c6ad..faf83ce2ca 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -234,7 +234,7 @@ extern "C" { static mut libafl_exec_jmp_hook: unsafe extern "C" fn(u64, u64); #[cfg(feature = "systemmode")] static mut libafl_gen_jmp_hook: unsafe extern "C" fn(u64, u64) -> u64; - static mut libafl_exec_block_hook: unsafe extern "C" fn(u64); + pub static mut libafl_exec_block_hook: unsafe extern "C" fn(u64); static mut libafl_gen_block_hook: unsafe extern "C" fn(u64) -> u64; static mut libafl_exec_read_hook1: unsafe extern "C" fn(u64, u64); From f994bc62bcb85af0f3982ec044e5f506f029eb4e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 26 May 2022 23:55:53 +0200 Subject: [PATCH 68/83] add jmp_as_edge cfg, extend libafl_exec_jmp_hook --- libafl_qemu/Cargo.toml | 2 ++ libafl_qemu/src/edges.rs | 14 +++++++++++--- libafl_qemu/src/emu.rs | 4 ++-- libafl_qemu/src/executor.rs | 4 ++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index 3d39a6cfce..26c79cc40e 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -24,6 +24,8 @@ clippy = [] # special feature for clippy, don't use in normal projects§ systemmode = [] # Emulate system images instead of user-mode binaries +jmp_as_edge = [] # Add all jumps in app code to edges, circumvents bugs in the original instrumentation + [dependencies] libafl = { path = "../libafl", version = "0.7.1" } libafl_targets = { path = "../libafl_targets", version = "0.7.1" } diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index 85f26a9890..fba60f5049 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -1,3 +1,4 @@ +use crate::libafl_exec_edge_one_off; use std::ops::Range; use hashbrown::{hash_map::Entry, HashMap}; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; @@ -67,7 +68,7 @@ impl QemuEdgeCoverageHelper { // if src != 0 { // println!("must_save {} {:x} {:x}",s.contains(&src) && !s.contains(&dst),src,dst); // } - s.contains(&src) && !s.contains(&dst) + s.contains(&src) || s.contains(&dst) // println!("must_save {} {:x} {:x}",src==0&&dst!=0x9cc,src,dst); // src==0&&dst!=0x9cc }, @@ -137,7 +138,11 @@ where return None; } } - Some(1) + // Temporary fux for missing edges + #[cfg(feature = "jmp_as_edge")] + return gen_unique_edge_ids(_emulator,helpers,state,src,dest); + #[cfg(not(feature = "jmp_as_edge"))] + return Some(1); } pub fn gen_unique_edge_ids( @@ -205,10 +210,13 @@ where Some(hash_me(src) ^ hash_me(dest)) } -pub extern "C" fn trace_jmp(src: u64, des: u64) { +pub extern "C" fn trace_jmp(src: u64, des: u64, id: u64) { unsafe { SAVED_JUMP=Some((src, des)); } + // temporary hack to catch app code blocks + #[cfg(feature = "jmp_as_edge")] + unsafe { trace_edge_hitcount(id); } } pub extern "C" fn trace_edge_hitcount(id: u64) { diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index faf83ce2ca..ffd3eca275 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -231,7 +231,7 @@ extern "C" { static mut libafl_exec_edge_hook: unsafe extern "C" fn(u64); static mut libafl_gen_edge_hook: unsafe extern "C" fn(u64, u64) -> u64; #[cfg(feature = "systemmode")] - static mut libafl_exec_jmp_hook: unsafe extern "C" fn(u64, u64); + static mut libafl_exec_jmp_hook: unsafe extern "C" fn(u64, u64, u64); #[cfg(feature = "systemmode")] static mut libafl_gen_jmp_hook: unsafe extern "C" fn(u64, u64) -> u64; pub static mut libafl_exec_block_hook: unsafe extern "C" fn(u64); @@ -544,7 +544,7 @@ impl Emulator { } #[cfg(feature = "systemmode")] - pub fn set_exec_jmp_hook(&self, hook: extern "C" fn(src: u64, dest: u64)) { + pub fn set_exec_jmp_hook(&self, hook: extern "C" fn(src: u64, dest: u64, id: u64)) { unsafe { libafl_exec_jmp_hook = hook; } diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index f6c4b6c4d8..e5259146e7 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -44,7 +44,7 @@ where } static mut JMP_HOOKS: Vec<*const c_void> = vec![]; -extern "C" fn jmp_hooks_wrapper(src: u64, dst: u64) +extern "C" fn jmp_hooks_wrapper(src: u64, dst: u64, id: u64) where I: Input, QT: QemuHelperTuple, @@ -576,7 +576,7 @@ where #[cfg(feature = "systemmode")] #[allow(clippy::unused_self)] - pub fn hook_jmp_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, src: u64, dest: u64)) { + pub fn hook_jmp_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, src: u64, dest: u64, id: u64)) { unsafe { JMP_HOOKS.push(hook as *const _); } From 14e8a81d17170be0757fa879f7dca8cd597d5afb Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 27 May 2022 00:36:25 +0200 Subject: [PATCH 69/83] fixup cargo.toml --- fuzzers/wcet_qemu_sys/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 86ada0c635..8a208ac5a7 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -17,6 +17,7 @@ feed_graph = [] # choose exactly one scheduler sched_queue = [] +sched_mapmax = [] sched_state = [] sched_graph = [] @@ -27,7 +28,7 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_qemu = { path = "../../libafl_qemu/", features = ["systemmode", "arm"] } +libafl_qemu = { path = "../../libafl_qemu/", features = ["systemmode", "arm", "jmp_as_edge"] } clap = { version = "3.0.0-beta.2", features = ["default"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib ron = "0.7" # write serialized data - including hashmaps From ee4056a008c14f90511de423f763e73c0a7affa2 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 27 May 2022 01:10:12 +0200 Subject: [PATCH 70/83] add objective selection, dump exec times --- fuzzers/wcet_qemu_sys/Cargo.toml | 8 +- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 57 +++++++++-- fuzzers/wcet_qemu_sys/src/worst.rs | 122 +++++++++++++++++++++++- libafl_qemu/src/edges.rs | 1 - 4 files changed, 179 insertions(+), 9 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 8a208ac5a7..a200fd110e 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -5,7 +5,7 @@ authors = [ "Alwin Berger " ] edition = "2021" [features] -default = ["std"] +default = ["std", "obj_collect"] std = [] multicore = [] @@ -21,6 +21,12 @@ sched_mapmax = [] sched_state = [] sched_graph = [] +# objective selection, additive +obj_collect = [] +obj_trace = [] +obj_edges = [] +obj_ticks = [] + muta_snip = [ "sched_graph" ] [profile.release] diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 036bf9175c..468892eb31 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,9 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::worst::DummyFeedback; +use wcet_qemu_sys::worst::ExecTimeCollectorFeedback; +use wcet_qemu_sys::worst::EXEC_TIME_COLLECTION; +use wcet_qemu_sys::worst::ExecTimeReachedFeedback; use libafl::inputs::Input; use libafl::feedbacks::Feedback; use libafl::HasFeedback; @@ -86,7 +90,7 @@ use libafl_qemu::{ clock::ClockFeedback, clock::QemuClockIncreaseFeedback }; -use wcet_qemu_sys::worst::{SortedFeedback,HitFeedback,HitImprovingFeedback,TimeStateMaximizerCorpusScheduler}; +use wcet_qemu_sys::worst::{SortedFeedback,HitFeedback,HitImprovingFeedback,TimeStateMaximizerCorpusScheduler,LenTimeMaximizerCorpusScheduler}; /// The fuzzer main @@ -154,6 +158,16 @@ pub fn main() { .required(true) .takes_value(true), ) + .arg( + Arg::new("exectimes") + .long("libafl-exectimes") + .takes_value(true), + ) + .arg( + Arg::new("targettime") + .long("libafl-targettime") + .takes_value(true), + ) .try_get_matches_from(filter_qemu_args()) { Ok(res) => res, @@ -216,9 +230,19 @@ pub fn main() { None => None }; + let exectimes = match res.value_of("exectimes") { + Some(st) => Some(PathBuf::from(st.to_string())), + None => None + }; + + let targettime = match res.value_of("targettime") { + Some(st) => Some(st.to_string().parse::().expect("targettime not a number")), + None => None + }; + let snapshot = PathBuf::from(res.value_of("snapshot").unwrap().to_string()); - fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel, edges, traces, snapshot) + fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel, edges, traces, exectimes, targettime, snapshot) .expect("An error occurred while fuzzing"); } @@ -246,6 +270,8 @@ fn fuzz( kernel: PathBuf, dump_edges: Option, dump_traces: Option, + dump_exectimes: Option, + targettime: Option, snapshot: PathBuf, ) -> Result<(), Error> { env::remove_var("LD_LIBRARY_PATH"); @@ -375,9 +401,15 @@ fn fuzz( let feedback = feedback_or!(feedback, SysMapFeedback::new()); // A feedback to choose if an input is a solution or not - let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer),HitSysStateFeedback::new(target_trace)); - // let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer)); - // let objective = SortedFeedback::new(); + let objective = DummyFeedback::new(false); + #[cfg(feature = "obj_collect")] + let objective = ExecTimeCollectorFeedback::new(); + #[cfg(feature = "obj_trace")] + let objective = feedback_or!(HitSysStateFeedback::new(target_trace)); + #[cfg(feature = "obj_edges")] + let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer), objective); + #[cfg(feature = "obj_ticks")] + let objective = feedback_or!(ExecTimeReachedFeedback::new(targettime)); // create a State from scratch let mut state = state.unwrap_or_else(||{ @@ -412,9 +444,10 @@ fn fuzz( let mutation = StdMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus - // let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new()); #[cfg(feature = "sched_queue")] let scheduler = QueueCorpusScheduler::new(); + #[cfg(feature = "sched_mapmax")] + let scheduler = LenTimeMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); #[cfg(feature = "sched_state")] let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); #[cfg(feature = "sched_graph")] @@ -545,6 +578,18 @@ fn fuzz( fs::write("./graph.dot",tempg).expect("Graph can not be written"); } + // Wite out the collected exec times + #[cfg(feature = "obj_collect")] + match dump_exectimes { + Some(et) => unsafe { + let mut stringforms : Vec = Vec::new(); + for i in EXEC_TIME_COLLECTION.drain(..) { + stringforms.push(format!("{}\n",i)); + } + fs::write(et,stringforms.concat()).expect("Exec times can not be written"); + } + None => (), + } // Never reached Ok(()) diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 8fef77acf0..3bd837a9e0 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -1,3 +1,5 @@ +use libafl::feedbacks::FeedbackState; +use libafl_qemu::QemuClockObserver; use crate::sysstate::FreeRTOSSystemStateMetadata; use num_traits::PrimInt; use core::fmt::Debug; @@ -592,4 +594,122 @@ impl Default for SortedFeedback { } } -//=================================================================== \ No newline at end of file +//=================================================================== +/// A Feedback which expects a certain minimum execution time +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ExecTimeReachedFeedback +{ + target_time: u64, +} + +impl Feedback for ExecTimeReachedFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = _observers.match_name::("clock") + .expect("QemuClockObserver not found"); + Ok(observer.last_runtime() >= self.target_time) + } +} + +impl Named for ExecTimeReachedFeedback +{ + #[inline] + fn name(&self) -> &str { + "ExecTimeReachedFeedback" + } +} + +impl ExecTimeReachedFeedback +where +{ + /// Creates a new [`ExecTimeReachedFeedback`] + #[must_use] + pub fn new(target_time : u64) -> Self { + Self {target_time: target_time} + } +} + +pub static mut EXEC_TIME_COLLECTION : Vec = Vec::new(); + +/// A Noop Feedback which records a list of all execution times +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ExecTimeCollectorFeedback +{ +} + +impl Feedback for ExecTimeCollectorFeedback +where + I: Input, + S: HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = _observers.match_name::("clock") + .expect("QemuClockObserver not found"); + unsafe { EXEC_TIME_COLLECTION.push(observer.last_runtime().try_into().unwrap()); } + Ok(false) + } +} + +impl Named for ExecTimeCollectorFeedback +{ + #[inline] + fn name(&self) -> &str { + "ExecTimeCollectorFeedback" + } +} + +impl ExecTimeCollectorFeedback +where +{ + /// Creates a new [`ExecTimeCollectorFeedback`] + #[must_use] + pub fn new() -> Self { + Self {} + } +} + +/// Shared Metadata for a SysStateFeedback +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct ExecTimeCollectorFeedbackState +{ + collection: Vec, +} +impl Named for ExecTimeCollectorFeedbackState +{ + #[inline] + fn name(&self) -> &str { + "ExecTimeCollectorFeedbackState" + } +} +impl FeedbackState for ExecTimeCollectorFeedbackState +{ + fn reset(&mut self) -> Result<(), Error> { + self.collection.clear(); + Ok(()) + } +} \ No newline at end of file diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index fba60f5049..cdd9047844 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -1,4 +1,3 @@ -use crate::libafl_exec_edge_one_off; use std::ops::Range; use hashbrown::{hash_map::Entry, HashMap}; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; From 6a18fa75f451483402539ac749e8d3b40707cfa6 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 29 May 2022 00:43:02 +0200 Subject: [PATCH 71/83] add RandInputSnippetMutator --- fuzzers/wcet_qemu_sys/src/sysstate/graph.rs | 92 ++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs index 22e8b63d57..82182fd50b 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -408,7 +408,97 @@ where "RandGraphSnippetMutator" } } -//============================= Mutators +//=============================== Snippets +pub struct RandInputSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + phantom: PhantomData<(I, S)>, +} +impl RandInputSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + pub fn new() -> Self { + RandInputSnippetMutator{phantom: PhantomData} + } +} +impl Mutator for RandInputSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32 + ) -> Result + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + drop(tmp); + + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let g = &feedbackstate.graph; + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace = tmp.expect("SysGraphMetadata not found"); + + let mut collection : Vec> = Vec::new(); + let mut current_pointer : usize = 0; + let INPUT_BYTES_OFFSET = 0; // Offset for interrupt bytes + for t in &trace.inner { + let node = &g[*t]; + for v in &node.variants { + if v.input == input.bytes() { + if v.input_counter > current_pointer.try_into().unwrap() { + collection.push(v.input[INPUT_BYTES_OFFSET+current_pointer..INPUT_BYTES_OFFSET+v.input_counter as usize].to_owned()); + current_pointer = v.input_counter as usize; + } + break; + } + } + } + let index_to_mutate = myrand.below(collection.len() as u64) as usize; + for i in 0..collection[index_to_mutate].len() { + collection[index_to_mutate][i] = myrand.below(0xFF) as u8; + } + for i in collection.concat().iter().enumerate() { + input.bytes_mut()[INPUT_BYTES_OFFSET+i.0]=*i.1; + } + + Ok(MutationResult::Mutated) + } + + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option + ) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for RandInputSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn name(&self) -> &str { + "RandInputSnippetMutator" + } +} //=============================== Suffix pub struct RandGraphSuffixMutator where From d1c685ccf9d98f3b33c1af1c5dcb8bddbf75ab7c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 29 May 2022 00:45:17 +0200 Subject: [PATCH 72/83] prepare for benchmarking new features for mutators fuzzer corpus in memory for benchmarking showmap single file input --- fuzzers/wcet_qemu_sys/Cargo.toml | 4 +++ fuzzers/wcet_qemu_sys/Makefile | 33 +++++++++++++++--------- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 28 +++++++++++++++++--- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 4 +-- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index a200fd110e..769772a60f 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -27,7 +27,11 @@ obj_trace = [] obj_edges = [] obj_ticks = [] +muta_input = [ "sched_graph" ] muta_snip = [ "sched_graph" ] +muta_suffix = [ "sched_graph" ] + +benchmark = [] # don't save corpus to disk, easy parallelizable [profile.release] debug = true diff --git a/fuzzers/wcet_qemu_sys/Makefile b/fuzzers/wcet_qemu_sys/Makefile index a6120ca39e..86057c2a23 100644 --- a/fuzzers/wcet_qemu_sys/Makefile +++ b/fuzzers/wcet_qemu_sys/Makefile @@ -1,50 +1,59 @@ BENCHDIR = target/bench TARGET = tmr.axf -EXAMPLE = "\xff\05\x84\x84\x84\x84\x84\x84\x84\x84" -TARGET_TRACE = tmp/target_trace.ron -TARGET_EDGES = tmp/target_edges.ron +TARGET_TRACE = $(BENCHDIR)/traces/tmr_worst.ron +TARGET_EDGES = $(BENCHDIR)/edges/tmr_worst.ron $(BENCHDIR)/bin: mkdir -p $@ $(BENCHDIR)/bin/fuzz_vanilla: $(BENCHDIR)/bin - cargo build --features feed_afl,sched_queue + cargo build --features benchmark,feed_afl,sched_mapmax cp target/debug/fuzzer $@ $(BENCHDIR)/bin/fuzz_state: $(BENCHDIR)/bin - cargo build --features feed_state,sched_state + cargo build --features benchmark,feed_state,sched_state cp target/debug/fuzzer $@ $(BENCHDIR)/bin/fuzz_graph: $(BENCHDIR)/bin - cargo build --features feed_graph,sched_graph + cargo build --features benchmark,feed_graph,sched_graph cp target/debug/fuzzer $@ $(BENCHDIR)/bin/fuzz_graph_snip: $(BENCHDIR)/bin - cargo build --features feed_graph,sched_graph,muta_snip + cargo build --features benchmark,feed_graph,sched_graph,muta_snip,muta_input,muta_suffix cp target/debug/fuzzer $@ $(BENCHDIR)/bin/fuzz_graph_afl: $(BENCHDIR)/bin - cargo build --features feed_graph,sched_graph,feed_afl + cargo build --features benchmark,feed_graph,sched_graph,feed_afl cp target/debug/fuzzer $@ $(BENCHDIR)/bin/fuzz_graph_all: $(BENCHDIR)/bin - cargo build --features feed_graph,sched_graph,feed_afl,muta_snip + cargo build --features benchmark,feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix cp target/debug/fuzzer $@ binaries: $(BENCHDIR)/bin/fuzz_vanilla $(BENCHDIR)/bin/fuzz_state $(BENCHDIR)/bin/fuzz_graph $(BENCHDIR)/bin/fuzz_graph_snip $(BENCHDIR)/bin/fuzz_graph_afl $(BENCHDIR)/bin/fuzz_graph_all # variants: vanilla, state, graph, graph_snip, graph_afl, graph_all $(BENCHDIR)/bench_%.log: $(BENCHDIR)/bin/fuzz_% $(TARGET_TRACE) - for i in {1..5}; do ./fuzzer_bench.sh $< $(TARGET) --libafl-traces $(TARGET_TRACE) > $@_$$i; done + for i in {1..1}; do ./fuzzer_bench.sh $< $(TARGET) --libafl-traces $(TARGET_TRACE) --libafl-exectimes $@.exec_$$i > $@_$$i; done for i in $@_*; do tail -n 1 $$i >> $@; done benchmarks: target/bench/bench_vanilla.log target/bench/bench_state.log target/bench/bench_graph.log target/bench/bench_graph_snip.log target/bench/bench_graph_afl.log target/bench/bench_graph_all.log -all: binaries +all: binaries benchmarks clean_bench: rm -rf $(BENCHDIR)/bench_* clean: rm -rf target/bench - \ No newline at end of file + +%.case: %_inputs + mkdir -p $(BENCHDIR)/traces $(BENCHDIR)/edges + for i in $ { let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string()); @@ -353,7 +353,7 @@ fn fuzz( } unsafe { - libafl_int_offset = 347780+int_tick.unwrap_or(0); + // libafl_int_offset = 347780+int_tick.unwrap_or(0); // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); From 5b4f730187585f7792dfe7ecfc61c4ba769e5d1c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 1 Jun 2022 15:55:17 +0200 Subject: [PATCH 73/83] add feed_known_edges, fix TimeMaximizerCorpusScheduler --- fuzzers/wcet_qemu_sys/Cargo.toml | 1 + fuzzers/wcet_qemu_sys/Makefile | 51 ++++++++++++++----------- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 6 ++- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 769772a60f..f9b49b3ba7 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -10,6 +10,7 @@ std = [] multicore = [] # select which feedbacks to use. enable at least one. +feed_known_edges = [] feed_afl = [] feed_clock = [] feed_state = [] diff --git a/fuzzers/wcet_qemu_sys/Makefile b/fuzzers/wcet_qemu_sys/Makefile index 86057c2a23..67cc9b4307 100644 --- a/fuzzers/wcet_qemu_sys/Makefile +++ b/fuzzers/wcet_qemu_sys/Makefile @@ -6,38 +6,41 @@ TARGET_EDGES = $(BENCHDIR)/edges/tmr_worst.ron $(BENCHDIR)/bin: mkdir -p $@ -$(BENCHDIR)/bin/fuzz_vanilla: $(BENCHDIR)/bin - cargo build --features benchmark,feed_afl,sched_mapmax - cp target/debug/fuzzer $@ +$(BENCHDIR)/target_known_edges: $(BENCHDIR)/bin + cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_known_edges,sched_queue,obj_edges -$(BENCHDIR)/bin/fuzz_state: $(BENCHDIR)/bin - cargo build --features benchmark,feed_state,sched_state - cp target/debug/fuzzer $@ +$(BENCHDIR)/target_vanilla: $(BENCHDIR)/bin + cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_afl,sched_mapmax -$(BENCHDIR)/bin/fuzz_graph: $(BENCHDIR)/bin - cargo build --features benchmark,feed_graph,sched_graph - cp target/debug/fuzzer $@ +$(BENCHDIR)/target_state: $(BENCHDIR)/bin + cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_state,sched_state -$(BENCHDIR)/bin/fuzz_graph_snip: $(BENCHDIR)/bin - cargo build --features benchmark,feed_graph,sched_graph,muta_snip,muta_input,muta_suffix - cp target/debug/fuzzer $@ +$(BENCHDIR)/target_graph: $(BENCHDIR)/bin + cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_graph,sched_graph -$(BENCHDIR)/bin/fuzz_graph_afl: $(BENCHDIR)/bin - cargo build --features benchmark,feed_graph,sched_graph,feed_afl - cp target/debug/fuzzer $@ +$(BENCHDIR)/target_graph_snip: $(BENCHDIR)/bin + cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_graph,sched_graph,muta_snip,muta_input,muta_suffix -$(BENCHDIR)/bin/fuzz_graph_all: $(BENCHDIR)/bin - cargo build --features benchmark,feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix - cp target/debug/fuzzer $@ +$(BENCHDIR)/target_graph_afl: $(BENCHDIR)/bin + cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_graph,sched_graph,feed_afl -binaries: $(BENCHDIR)/bin/fuzz_vanilla $(BENCHDIR)/bin/fuzz_state $(BENCHDIR)/bin/fuzz_graph $(BENCHDIR)/bin/fuzz_graph_snip $(BENCHDIR)/bin/fuzz_graph_afl $(BENCHDIR)/bin/fuzz_graph_all +$(BENCHDIR)/target_graph_all: $(BENCHDIR)/bin + cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix + +binaries: $(BENCHDIR)/target_known_edges $(BENCHDIR)/target_vanilla $(BENCHDIR)/target_state $(BENCHDIR)/target_graph $(BENCHDIR)/target_graph_snip $(BENCHDIR)/target_graph_afl $(BENCHDIR)/target_graph_all # variants: vanilla, state, graph, graph_snip, graph_afl, graph_all -$(BENCHDIR)/bench_%.log: $(BENCHDIR)/bin/fuzz_% $(TARGET_TRACE) - for i in {1..1}; do ./fuzzer_bench.sh $< $(TARGET) --libafl-traces $(TARGET_TRACE) --libafl-exectimes $@.exec_$$i > $@_$$i; done +$(BENCHDIR)/bench_%.log: $(BENCHDIR)/target_% $(TARGET_TRACE) + for i in {1..10}; do \ + CASE=$$(basename -s.log $@ | cut -d'_' -f 2- ); \ + echo $$CASE Iteration $$i; \ + ./fuzzer_bench.sh $ $@_$$i; \ + sed -i "1 i\\$$CASE " $(BENCHDIR)/bench_$$CASE\_$$i.exec; \ + done for i in $@_*; do tail -n 1 $$i >> $@; done -benchmarks: target/bench/bench_vanilla.log target/bench/bench_state.log target/bench/bench_graph.log target/bench/bench_graph_snip.log target/bench/bench_graph_afl.log target/bench/bench_graph_all.log +benchmarks: target/bench/bench_known_edges.log target/bench/bench_vanilla.log target/bench/bench_state.log target/bench/bench_graph.log target/bench/bench_graph_snip.log target/bench/bench_graph_afl.log target/bench/bench_graph_all.log all: binaries benchmarks @@ -56,4 +59,6 @@ clean: --libafl-edges $(BENCHDIR)/edges/$$PROG\_$$CASE.ron \ --libafl-traces $(BENCHDIR)/traces/$$PROG\_$$CASE.ron | \ grep "Qemu Ticks:"; \ - done \ No newline at end of file + done + +$(TARGET_TRACE): tmr.case \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 23bdeff435..b05b0922b2 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use wcet_qemu_sys::worst::TimeMaximizerCorpusScheduler; use libafl::corpus::InMemoryCorpus; use wcet_qemu_sys::sysstate::graph::RandGraphSuffixMutator; use wcet_qemu_sys::sysstate::graph::RandInputSnippetMutator; @@ -394,6 +395,8 @@ fn fuzz( }; // Feedback to rate the interestingness of an input let feedback = ClockFeedback::new_with_observer(&clock_observer); + #[cfg(feature = "feed_known_edges")] + let feedback = feedback_or!(feedback, HitImprovingFeedback::new(target_map.clone(), &edges_observer)); #[cfg(feature = "feed_afl")] let feedback = feedback_or!(feedback, MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false)); #[cfg(feature = "feed_clock")] @@ -467,7 +470,7 @@ fn fuzz( #[cfg(feature = "sched_queue")] let scheduler = QueueCorpusScheduler::new(); #[cfg(feature = "sched_mapmax")] - let scheduler = LenTimeMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); + let scheduler = TimeMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); #[cfg(feature = "sched_state")] let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new()); #[cfg(feature = "sched_graph")] @@ -584,6 +587,7 @@ fn fuzz( // .expect("Error in the fuzzing loop"); + #[cfg(not(feature = "benchmark"))] #[cfg(feature = "feed_graph")] { let feedbackstate = state From 2e81f386eeaf63f50f7a1de74d9c80943985e36f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 1 Jun 2022 22:02:19 +0200 Subject: [PATCH 74/83] change u8 to u16 --- .../wcet_qemu_sys/src/sysstate/feedbacks.rs | 32 +++++++-- fuzzers/wcet_qemu_sys/src/worst.rs | 72 +++++++++++++++---- libafl/src/feedbacks/map.rs | 3 +- libafl_targets/src/coverage.rs | 6 +- 4 files changed, 92 insertions(+), 21 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs index 988ba3eb37..e2838bab94 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs @@ -213,6 +213,8 @@ impl HitSysStateFeedback { pub struct DumpSystraceFeedback { dumpfile: Option, + dump_metadata: bool, + last_trace: Option>, } impl Feedback for DumpSystraceFeedback @@ -239,9 +241,28 @@ where std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file"); self.dumpfile = None }, - None => println!("{:?}",observer.last_run), + None => if !self.dump_metadata {println!("{:?}",observer.last_run);} }; - Ok(true) + if self.dump_metadata {self.last_trace=Some(observer.last_run.clone());} + Ok(!self.dump_metadata) + } + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + if !self.dump_metadata {return Ok(());} + let a = self.last_trace.take(); + match a { + Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)), + None => (), + } + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.last_trace = None; + Ok(()) } } @@ -258,9 +279,12 @@ impl DumpSystraceFeedback /// Creates a new [`DumpSystraceFeedback`] #[must_use] pub fn new() -> Self { - Self {dumpfile: None} + Self {dumpfile: None, dump_metadata: false, last_trace: None} } pub fn with_dump(dumpfile: Option) -> Self { - Self {dumpfile: dumpfile} + Self {dumpfile: dumpfile, dump_metadata: false, last_trace: None} + } + pub fn metadata_only() -> Self { + Self {dumpfile: None, dump_metadata: true, last_trace: None} } } \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 3bd837a9e0..dba07641a8 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -82,7 +82,7 @@ where impl HasLen for QemuHashMapObserver where - M: MapObserver, + M: MapObserver, T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { #[inline] @@ -91,18 +91,18 @@ where } } -impl MapObserver for QemuHashMapObserver +impl MapObserver for QemuHashMapObserver where - M: MapObserver, + M: MapObserver, T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { #[inline] - fn map(&self) -> Option<&[u8]> { + fn map(&self) -> Option<&[T]> { self.base.map() } #[inline] - fn map_mut(&mut self) -> Option<&mut [u8]> { + fn map_mut(&mut self) -> Option<&mut [T]> { self.base.map_mut() } @@ -112,17 +112,17 @@ where } #[inline] - fn initial(&self) -> u8 { + fn initial(&self) -> T { self.base.initial() } #[inline] - fn initial_mut(&mut self) -> &mut u8 { + fn initial_mut(&mut self) -> &mut T { self.base.initial_mut() } #[inline] - fn set_initial(&mut self, initial: u8) { + fn set_initial(&mut self, initial: T) { self.base.set_initial(initial); } } @@ -364,6 +364,23 @@ where } //=========================== Debugging Feedback +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct DumpEdgesMapMetadata +{ + pub map: HashMap<(u64, u64), usize>, +} + +impl DumpEdgesMapMetadata +{ + #[must_use] + pub fn new(input: HashMap<(u64,u64),usize>) -> Self { + Self { + map: input, + } + } +} + +libafl::impl_serdeany!(DumpEdgesMapMetadata); /// A [`Feedback`] meant to dump the edgemap for debugging. #[derive(Debug)] pub struct DumpMapFeedback @@ -373,6 +390,8 @@ where { dumpfile: Option, phantom: PhantomData<(O, T)>, + dump_metadata: bool, + last_map: Option>, } impl Feedback for DumpMapFeedback @@ -401,9 +420,34 @@ where fs::write(s,ron::to_string(&observer.edgemap).expect("Error serializing hashmap")).expect("Can not dump to file"); self.dumpfile = None }, - None => println!("{:?}",observer.edgemap), + None => if !self.dump_metadata {println!("{:?}",observer.edgemap);} }; - Ok(true) + if self.dump_metadata {self.last_map=Some(observer.edgemap.clone());} + Ok(!self.dump_metadata) + } + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + if !self.dump_metadata {return Ok(());} + let a = self.last_map.take(); + match a { + Some(s) => { + let mut tmp : HashMap<(u64,u64),usize> = HashMap::new(); + for (k, v) in s.iter() { + tmp.insert(*k, T::to_usize(v).unwrap()); + } + testcase.metadata_mut().insert(DumpEdgesMapMetadata::new(tmp)); + } + None => (), + } + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.last_map = None; + Ok(()) } } @@ -426,11 +470,15 @@ where /// Creates a new [`HitFeedback`] #[must_use] pub fn new(_map_observer: &QemuHashMapObserver) -> Self { - Self {dumpfile: None, phantom: PhantomData} + Self {dumpfile: None, phantom: PhantomData, dump_metadata: false, last_map: None} } pub fn with_dump(dumpfile: Option,_map_observer: &QemuHashMapObserver) -> Self { - Self {dumpfile: dumpfile, phantom: PhantomData} + Self {dumpfile: dumpfile, phantom: PhantomData, dump_metadata: false, last_map: None} } + pub fn metadata_only(_map_observer: &QemuHashMapObserver) -> Self { + Self {dumpfile: None, phantom: PhantomData, dump_metadata: true, last_map: None} + } + } //=========================== Debugging Feedback diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index 8abf2dd24d..9fef754200 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -294,7 +294,7 @@ where impl MapFeedbackState where - T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, + T: Debug + PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, { /// Create new `MapFeedbackState` #[must_use] @@ -309,7 +309,6 @@ where pub fn with_observer(map_observer: &O) -> Self where O: MapObserver, - T: Debug, { Self { history_map: vec![T::min_value(); map_observer.len()], diff --git a/libafl_targets/src/coverage.rs b/libafl_targets/src/coverage.rs index feb2fb5b91..280ae47dd5 100644 --- a/libafl_targets/src/coverage.rs +++ b/libafl_targets/src/coverage.rs @@ -4,7 +4,7 @@ use crate::EDGES_MAP_SIZE; /// The map for edges. #[no_mangle] -pub static mut __afl_area_ptr_local: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; +pub static mut __afl_area_ptr_local: [u16; EDGES_MAP_SIZE] = [0u16; EDGES_MAP_SIZE]; pub use __afl_area_ptr_local as EDGES_MAP; /// The max count of edges tracked. @@ -12,7 +12,7 @@ pub static mut MAX_EDGES_NUM: usize = 0; extern "C" { /// The area pointer points to the edges map. - pub static mut __afl_area_ptr: *mut u8; + pub static mut __afl_area_ptr: *mut u16; } pub use __afl_area_ptr as EDGES_MAP_PTR; @@ -29,7 +29,7 @@ use libafl::bolts::ownedref::OwnedSliceMut; /// This function will crash if `EDGES_MAP_PTR` is not a valid pointer. /// The `EDGES_MAP_PTR_SIZE` needs to be smaller than, or equal to the size of the map. #[must_use] -pub unsafe fn edges_map_from_ptr<'a>() -> OwnedSliceMut<'a, u8> { +pub unsafe fn edges_map_from_ptr<'a>() -> OwnedSliceMut<'a, u16> { debug_assert!(!EDGES_MAP_PTR.is_null()); OwnedSliceMut::from_raw_parts_mut(EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE) } From eabfe020b6f7cf4947e2c19fb52bedb2e6c2e934 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 1 Jun 2022 22:09:39 +0200 Subject: [PATCH 75/83] update info duming and makefile --- fuzzers/wcet_qemu_sys/Cargo.toml | 1 + fuzzers/wcet_qemu_sys/Makefile | 56 +++++++----- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 108 +++++++++++++++++++++--- 3 files changed, 132 insertions(+), 33 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index f9b49b3ba7..6dd9dfd9ff 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -33,6 +33,7 @@ muta_snip = [ "sched_graph" ] muta_suffix = [ "sched_graph" ] benchmark = [] # don't save corpus to disk, easy parallelizable +dump_infos = [] # dump select corpus items for analysis [profile.release] debug = true diff --git a/fuzzers/wcet_qemu_sys/Makefile b/fuzzers/wcet_qemu_sys/Makefile index 67cc9b4307..d119027b56 100644 --- a/fuzzers/wcet_qemu_sys/Makefile +++ b/fuzzers/wcet_qemu_sys/Makefile @@ -2,45 +2,57 @@ BENCHDIR = target/bench TARGET = tmr.axf TARGET_TRACE = $(BENCHDIR)/traces/tmr_worst.ron TARGET_EDGES = $(BENCHDIR)/edges/tmr_worst.ron +RUNTIME = 1 $(BENCHDIR)/bin: mkdir -p $@ -$(BENCHDIR)/target_known_edges: $(BENCHDIR)/bin - cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_known_edges,sched_queue,obj_edges +$(BENCHDIR)/target_known_edges: + cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_known_edges,sched_queue -$(BENCHDIR)/target_vanilla: $(BENCHDIR)/bin - cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_afl,sched_mapmax +$(BENCHDIR)/target_afl_queue: + cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_afl,sched_queue -$(BENCHDIR)/target_state: $(BENCHDIR)/bin - cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_state,sched_state +$(BENCHDIR)/target_afl_mapmax: + cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_afl,sched_mapmax -$(BENCHDIR)/target_graph: $(BENCHDIR)/bin - cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_graph,sched_graph +$(BENCHDIR)/target_state: + cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_state,sched_state -$(BENCHDIR)/target_graph_snip: $(BENCHDIR)/bin - cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_graph,sched_graph,muta_snip,muta_input,muta_suffix +$(BENCHDIR)/target_state_afl: + cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_state,sched_state -$(BENCHDIR)/target_graph_afl: $(BENCHDIR)/bin - cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_graph,sched_graph,feed_afl +$(BENCHDIR)/target_graph: + cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph -$(BENCHDIR)/target_graph_all: $(BENCHDIR)/bin - cargo build --bin fuzzer --target-dir $@ --features benchmark,feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix +$(BENCHDIR)/target_graph_muta: + cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph,muta_snip,muta_input,muta_suffix -binaries: $(BENCHDIR)/target_known_edges $(BENCHDIR)/target_vanilla $(BENCHDIR)/target_state $(BENCHDIR)/target_graph $(BENCHDIR)/target_graph_snip $(BENCHDIR)/target_graph_afl $(BENCHDIR)/target_graph_all +$(BENCHDIR)/target_graph_afl: + cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph,feed_afl -# variants: vanilla, state, graph, graph_snip, graph_afl, graph_all +$(BENCHDIR)/target_graph_all: + cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix + +binaries: $(BENCHDIR)/target_known_edges $(BENCHDIR)/target_afl_queue $(BENCHDIR)/target_afl_mapmax $(BENCHDIR)/target_state $(BENCHDIR)/target_state_afl \ + $(BENCHDIR)/target_graph $(BENCHDIR)/target_graph_muta $(BENCHDIR)/target_graph_afl $(BENCHDIR)/target_graph_all + +# variants: known_edges, afl_queue, afl_mapmax, state, state_afl, graph, graph_muta, graph_afl, graph_all $(BENCHDIR)/bench_%.log: $(BENCHDIR)/target_% $(TARGET_TRACE) - for i in {1..10}; do \ + mkdir -p $(BENCHDIR)/execs + for i in {1..3}; do \ CASE=$$(basename -s.log $@ | cut -d'_' -f 2- ); \ - echo $$CASE Iteration $$i; \ - ./fuzzer_bench.sh $ $@_$$i; \ - sed -i "1 i\\$$CASE " $(BENCHDIR)/bench_$$CASE\_$$i.exec; \ + echo $$CASE iteration $$i; \ + mkdir -p $(BENCHDIR)/infos/$$CASE ; \ + ./fuzzer_bench.sh $ $@_$$i; \ + sed -i "1 i\\$$CASE " $(BENCHDIR)/execs/$$CASE\_$$i.exec; \ done for i in $@_*; do tail -n 1 $$i >> $@; done -benchmarks: target/bench/bench_known_edges.log target/bench/bench_vanilla.log target/bench/bench_state.log target/bench/bench_graph.log target/bench/bench_graph_snip.log target/bench/bench_graph_afl.log target/bench/bench_graph_all.log +benchmarks: target/bench/bench_known_edges.log target/bench/bench_afl_queue.log target/bench/bench_afl_mapmax.log target/bench/bench_state.log target/bench/bench_state_afl.log \ + target/bench/bench_graph.log target/bench/bench_graph_muta.log target/bench/bench_graph_afl.log target/bench/bench_graph_all.log all: binaries benchmarks diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index b05b0922b2..efef1d896a 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,5 +1,11 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. - +use std::str::FromStr; +use wcet_qemu_sys::worst::DumpEdgesMapMetadata; +use wcet_qemu_sys::worst::DumpMapFeedback; +use libafl::inputs::HasBytesVec; +use wcet_qemu_sys::sysstate::FreeRTOSSystemStateMetadata; +use wcet_qemu_sys::sysstate::feedbacks::DumpSystraceFeedback; +use libafl::state::HasSolutions; use wcet_qemu_sys::worst::TimeMaximizerCorpusScheduler; use libafl::corpus::InMemoryCorpus; use wcet_qemu_sys::sysstate::graph::RandGraphSuffixMutator; @@ -172,6 +178,21 @@ pub fn main() { .long("libafl-targettime") .takes_value(true), ) + .arg( + Arg::new("dump") + .long("libafl-dump") + .takes_value(true), + ) + .arg( + Arg::new("fuzztime") + .long("libafl-fuzztime") + .takes_value(true), + ) + .arg( + Arg::new("graphdump") + .long("libafl-graphdump") + .takes_value(true), + ) .try_get_matches_from(filter_qemu_args()) { Ok(res) => res, @@ -246,7 +267,22 @@ pub fn main() { let snapshot = PathBuf::from(res.value_of("snapshot").unwrap().to_string()); - fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel, edges, traces, exectimes, targettime, snapshot) + let dump_path = match res.value_of("dump") { + Some(st) => Some(PathBuf::from(st.to_string())), + None => None + }; + + let fuzztime = match res.value_of("fuzztime") { + Some(st) => Some(u64::from_str(st).expect("Failed parsing fuzztime")), + None => None + }; + + let graph_dump = match res.value_of("graphdump") { + Some(st) => Some(PathBuf::from(st.to_string())), + None => None + }; + + fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel, edges, traces, exectimes, targettime, snapshot, dump_path, fuzztime, graph_dump) .expect("An error occurred while fuzzing"); } @@ -277,6 +313,9 @@ fn fuzz( dump_exectimes: Option, targettime: Option, snapshot: PathBuf, + dump_path: Option, + fuzztime: Option, + graph_dump: Option, ) -> Result<(), Error> { env::remove_var("LD_LIBRARY_PATH"); @@ -377,11 +416,11 @@ fn fuzz( #[cfg(not(feature = "sched_state"))] let sysstate_feedback_state = SysGraphFeedbackState::new(); - let target_map : HashMap<(u64,u64),u8> = match dump_edges { + let target_map : HashMap<(u64,u64),u16> = match dump_edges { None => HashMap::new(), Some(ref s) => { let raw = fs::read(s).expect("Can not read dumped edges"); - let hmap : HashMap<(u64,u64),u8> = ron::from_str(&String::from_utf8_lossy(&raw)).expect("Can not parse HashMap"); + let hmap : HashMap<(u64,u64),u16> = ron::from_str(&String::from_utf8_lossy(&raw)).expect("Can not parse HashMap"); hmap }, }; @@ -395,6 +434,10 @@ fn fuzz( }; // Feedback to rate the interestingness of an input let feedback = ClockFeedback::new_with_observer(&clock_observer); + #[cfg(all(not(feature = "feed_state"), feature = "dump_infos"))] // for diagnostic purposes it's necessary to collect the state in any case + let feedback = feedback_or!(feedback, DumpSystraceFeedback::metadata_only()); + #[cfg(feature = "dump_infos")] // for diagnostic purposes it's necessary to collect the state in any case + let feedback = feedback_or!(feedback, DumpMapFeedback::metadata_only(&edges_observer)); #[cfg(feature = "feed_known_edges")] let feedback = feedback_or!(feedback, HitImprovingFeedback::new(target_map.clone(), &edges_observer)); #[cfg(feature = "feed_afl")] @@ -579,12 +622,14 @@ fn fuzz( dup2(null_fd, io::stderr().as_raw_fd())?; } - fuzzer - .fuzz_for_solution_or_n(&mut stages, &mut executor, &mut state, &mut mgr, 10000) - .expect("Error in the fuzzing loop"); - // fuzzer - // .fuzz_loop_for(&mut stages, &mut executor, &mut state, &mut mgr, 20) - // .expect("Error in the fuzzing loop"); + match fuzztime { + Some(t) => fuzzer + .fuzz_for_solution_or_n(&mut stages, &mut executor, &mut state, &mut mgr, t) + .expect("Error in the fuzzing loop"), + None => fuzzer + .fuzz_for_solution(&mut stages, &mut executor, &mut state, &mut mgr) + .expect("Error in the fuzzing loop"), + }; #[cfg(not(feature = "benchmark"))] @@ -599,7 +644,10 @@ fn fuzz( |_, e| e, ); let tempg = format!("{:?}",Dot::with_config(&newgraph, &[Config::EdgeNoLabel])); - fs::write("./graph.dot",tempg).expect("Graph can not be written"); + match graph_dump { + Some(gd) => fs::write(gd,tempg).expect("Graph can not be written"), + None => (), + } } // Wite out the collected exec times @@ -615,6 +663,44 @@ fn fuzz( None => (), } + #[cfg(feature = "dump_infos")] + { + println!("Start dumping {}", state.corpus().count()); + let c = if state.solutions().count()>0 { state.solutions() } else { state.corpus() }; + let mut worst = Duration::new(0,0); + let mut worst_input : Option> = Some(vec![]); + let mut worst_trace : Option = None; + let mut worst_map : Option = None; + for i in 0..c.count() { + let tc = c.get(i).expect("Could not get element from corpus").borrow(); + if worst < tc.exec_time().expect("Testcase missing duration") { + worst = tc.exec_time().expect("Testcase missing duration"); + let metadata = tc.metadata(); + worst_trace = metadata.get::().cloned(); + worst_map = metadata.get::().cloned(); + worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); + } + } + let mut dump_path = dump_path; + match &mut dump_path { + Some(dp) => { + dp.set_extension("trace"); + println!("Path found: {:?}",dp); + fs::write(&dp,ron::to_string(&worst_trace).expect("Error serializing Trace")).expect("Exec times can not be written"); + dp.set_extension("map"); + println!("Path found: {:?}",dp); + fs::write(&dp,ron::to_string(&worst_map).expect("Error serializing hashmap")).expect("Exec times can not be written"); + dp.set_extension("input"); + println!("Path found: {:?}",dp); + fs::write(&dp,worst_input.unwrap()); + dp.set_extension("time"); + println!("Path found: {:?}",dp); + fs::write(&dp,format!("{}",worst.as_nanos()>>3)); + }, + None => (), + } + } + // Never reached Ok(()) From 2466fc5cb6b61c5a2691a29f22a3d82e79b1de0e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 1 Jun 2022 23:46:23 +0200 Subject: [PATCH 76/83] add a testcase minimizer --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 3 +- fuzzers/wcet_qemu_sys/src/lib.rs | 3 +- fuzzers/wcet_qemu_sys/src/minimizer.rs | 70 +++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 fuzzers/wcet_qemu_sys/src/minimizer.rs diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index efef1d896a..3c4b6059eb 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -35,6 +35,7 @@ use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; use wcet_qemu_sys::sysstate::feedbacks::SysStateFeedbackState; use wcet_qemu_sys::sysstate::feedbacks::NovelSysStateFeedback; use wcet_qemu_sys::worst::QemuHashMapObserver; +use wcet_qemu_sys::minimizer::QemuCaseMinimizerStage; use hashbrown::HashMap; use clap::{App, Arg}; use core::{cell::RefCell, time::Duration}; @@ -609,7 +610,7 @@ fn fuzz( // let tracing = ShadowTracingStage::new(&mut executor); // The order of the stages matter! - let mut stages = tuple_list!(mutation); + let mut stages = tuple_list!(QemuCaseMinimizerStage::new(16),mutation); // Remove target ouput (logs still survive) #[cfg(unix)] diff --git a/fuzzers/wcet_qemu_sys/src/lib.rs b/fuzzers/wcet_qemu_sys/src/lib.rs index 34b31773e6..a9c17174e1 100644 --- a/fuzzers/wcet_qemu_sys/src/lib.rs +++ b/fuzzers/wcet_qemu_sys/src/lib.rs @@ -3,4 +3,5 @@ #[cfg(target_os = "linux")] pub mod sysstate; -pub mod worst; \ No newline at end of file +pub mod worst; +pub mod minimizer; \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/minimizer.rs b/fuzzers/wcet_qemu_sys/src/minimizer.rs new file mode 100644 index 0000000000..8822bca5d0 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/minimizer.rs @@ -0,0 +1,70 @@ + +use libafl::inputs::HasBytesVec; +use libafl::corpus::Corpus; +use libafl::bolts::rands::Rand; +use libafl::inputs::BytesInput; +use libafl::stages::Stage; +use libafl::stages::MutationalStage; +use libafl::stages::mutational::DEFAULT_MUTATIONAL_MAX_ITERATIONS; +use core::marker::PhantomData; +use libafl::Evaluator; +use libafl::state::HasRand; +use libafl::state::HasCorpus; +use libafl::Error; +use libafl::state::HasClientPerfMonitor; +use libafl::mutators::Mutator; +use libafl::inputs::Input; + +#[derive(Clone, Debug)] +pub struct QemuCaseMinimizerStage +where + S: HasClientPerfMonitor + HasCorpus + HasRand, + Z: Evaluator, +{ + max_input_length: usize, + #[allow(clippy::type_complexity)] + phantom: PhantomData<(E, EM, S, Z)>, +} + +impl Stage for QemuCaseMinimizerStage +where + S: HasClientPerfMonitor + HasCorpus + HasRand, + Z: Evaluator, +{ + #[inline] + #[allow(clippy::let_and_return)] + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut S, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + let mut corpus = state.corpus_mut(); + let mut case = corpus.get(corpus_idx).unwrap().borrow_mut(); + let mut input = case.input_mut().as_mut().unwrap(); + let mut bytes = input.bytes_mut(); + if bytes.len() > self.max_input_length { + bytes.drain(self.max_input_length..); + } + + #[cfg(feature = "introspection")] + state.introspection_monitor_mut().finish_stage(); + Ok(()) + } +} + +impl QemuCaseMinimizerStage +where + S: HasClientPerfMonitor + HasCorpus + HasRand, + Z: Evaluator, +{ + /// Creates a new default mutational stage + pub fn new(max: usize) -> Self { + Self { + max_input_length: max, + phantom: PhantomData, + } + } +} \ No newline at end of file From eb3914e5c1942d2c7689a8c421fef5f1e4e2d487 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 6 Jun 2022 00:42:45 +0200 Subject: [PATCH 77/83] introduce INPUT_BYTES_OFFSET constant --- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 24 ++++++++++++------- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 21 +++++++++------- fuzzers/wcet_qemu_sys/src/sysstate/graph.rs | 10 ++++---- fuzzers/wcet_qemu_sys/src/sysstate/mod.rs | 2 ++ .../wcet_qemu_sys/src/sysstate/observers.rs | 3 ++- 5 files changed, 38 insertions(+), 22 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 3c4b6059eb..d9c0f828a3 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -34,6 +34,7 @@ use wcet_qemu_sys::sysstate::helpers::QemuSystemStateHelper; use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; use wcet_qemu_sys::sysstate::feedbacks::SysStateFeedbackState; use wcet_qemu_sys::sysstate::feedbacks::NovelSysStateFeedback; +use wcet_qemu_sys::sysstate::INPUT_BYTES_OFFSET; use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::minimizer::QemuCaseMinimizerStage; use hashbrown::HashMap; @@ -530,13 +531,15 @@ fn fuzz( let mut buf = target.as_slice(); let mut len = buf.len(); let mut int_tick : Option = None; - if len > 2 { - let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt - t[0]=buf[0]; - t[1]=buf[1]; - int_tick = Some(u32::from_le_bytes(t)); - buf = &buf[2..]; - len = buf.len(); + if INPUT_BYTES_OFFSET!= 0 { + if len > 2 { + let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt + t[0]=buf[0]; + t[1]=buf[1]; + int_tick = Some(u32::from_le_bytes(t)); + buf = &buf[2..]; + len = buf.len(); + } } if len >= 32 { buf = &buf[0..32]; @@ -544,7 +547,9 @@ fn fuzz( } unsafe { - libafl_int_offset = 347780+int_tick.unwrap_or(0); + if INPUT_BYTES_OFFSET!= 0 { + libafl_int_offset = 347780+int_tick.unwrap_or(0); + } // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); @@ -610,7 +615,7 @@ fn fuzz( // let tracing = ShadowTracingStage::new(&mut executor); // The order of the stages matter! - let mut stages = tuple_list!(QemuCaseMinimizerStage::new(16),mutation); + let mut stages = tuple_list!(mutation,QemuCaseMinimizerStage::new(16)); // Remove target ouput (logs still survive) #[cfg(unix)] @@ -642,6 +647,7 @@ fn fuzz( .unwrap(); let newgraph = feedbackstate.graph.map( |_, n| n.get_taskname(), + // |_, n| format!("{} {:?}",n.get_taskname(),n.get_input_counts().iter().min().unwrap_or(&0)), |_, e| e, ); let tempg = format!("{:?}",Dot::with_config(&newgraph, &[Config::EdgeNoLabel])); diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index b79308050d..43404ae230 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -4,6 +4,7 @@ use wcet_qemu_sys::sysstate::helpers::INTR_OFFSET; use std::io::Read; use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; use wcet_qemu_sys::sysstate::feedbacks::DumpSystraceFeedback; +use wcet_qemu_sys::sysstate::INPUT_BYTES_OFFSET; use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::{ worst::{DumpMapFeedback,DummyFeedback}, @@ -339,13 +340,15 @@ fn fuzz( let mut buf = target.as_slice(); let mut len = buf.len(); let mut int_tick : Option = None; - if len > 2 { - let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt - t[0]=buf[0]; - t[1]=buf[1]; - int_tick = Some(u32::from_le_bytes(t)); - buf = &buf[2..]; - len = buf.len(); + if INPUT_BYTES_OFFSET!= 0 { + if len > 2 { + let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt + t[0]=buf[0]; + t[1]=buf[1]; + int_tick = Some(u32::from_le_bytes(t)); + buf = &buf[2..]; + len = buf.len(); + } } if len >= 32 { buf = &buf[0..32]; @@ -353,7 +356,9 @@ fn fuzz( } unsafe { - // libafl_int_offset = 347780+int_tick.unwrap_or(0); + if INPUT_BYTES_OFFSET!= 0 { + libafl_int_offset = 347780+int_tick.unwrap_or(0); + } // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); emu.write_mem(input_addr,buf); diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs index 82182fd50b..e12e490ed1 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -104,6 +104,9 @@ impl SysGraphNode { pub fn get_taskname(&self) -> &str { &self.base.current_task.task_name } + pub fn get_input_counts(&self) -> Vec { + self.variants.iter().map(|x| x.input_counter).collect() + } } impl PartialEq for SysGraphNode { fn eq(&self, other: &SysGraphNode) -> bool { @@ -456,13 +459,12 @@ where let mut collection : Vec> = Vec::new(); let mut current_pointer : usize = 0; - let INPUT_BYTES_OFFSET = 0; // Offset for interrupt bytes for t in &trace.inner { let node = &g[*t]; for v in &node.variants { if v.input == input.bytes() { if v.input_counter > current_pointer.try_into().unwrap() { - collection.push(v.input[INPUT_BYTES_OFFSET+current_pointer..INPUT_BYTES_OFFSET+v.input_counter as usize].to_owned()); + collection.push(v.input[current_pointer..v.input_counter as usize].to_owned()); current_pointer = v.input_counter as usize; } break; @@ -474,7 +476,7 @@ where collection[index_to_mutate][i] = myrand.below(0xFF) as u8; } for i in collection.concat().iter().enumerate() { - input.bytes_mut()[INPUT_BYTES_OFFSET+i.0]=*i.1; + input.bytes_mut()[i.0]=*i.1; } Ok(MutationResult::Mutated) @@ -547,7 +549,7 @@ where // follow the path, extract snippets from last reads, find common snippets. // those are likley keys parts. choose random parts from other sibling traces let inp_c_end = g[*trace.inner.last().unwrap()].base.input_counter; - let mut num_to_reverse = 1; + let mut num_to_reverse = myrand.below(trace.inner.len().try_into().unwrap()); for t in trace.inner.iter().rev() { let int_c_prefix = g[*t].base.input_counter; if int_c_prefix < inp_c_end { diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs index 16649c61be..ac609edf99 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -15,6 +15,8 @@ pub mod observers; pub mod feedbacks; pub mod graph; +pub const INPUT_BYTES_OFFSET : u32 = 2; // Offset for interrupt bytes + // Constants const NUM_PRIOS: usize = 5; diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs index 3328cc1db0..0a33266787 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -1,3 +1,4 @@ +use crate::sysstate::INPUT_BYTES_OFFSET; use libafl::inputs::HasTargetBytes; use libafl::bolts::HasLen; use libafl::bolts::tuples::Named; @@ -129,7 +130,7 @@ for mut i in input.drain(..) { start_tick: start_tick, end_tick: i.qemu_tick, ready_list_after: collector, - input_counter: i.input_counter, + input_counter: i.input_counter+INPUT_BYTES_OFFSET, last_pc: i.last_pc, }); start_tick=i.qemu_tick; From 4dabfc5f00cf456c0c3b54199b006d82e633ffce Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 7 Jun 2022 01:50:21 +0200 Subject: [PATCH 78/83] Add InterruptShifterMutator Also add features fuzz_interrupt and muta_interrupt, which dictate if interrupts are used during fuzzing Also allow makefile to skip finished iterations --- fuzzers/wcet_qemu_sys/Cargo.toml | 3 + fuzzers/wcet_qemu_sys/Makefile | 45 ++++--- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 26 ++-- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 23 ++-- fuzzers/wcet_qemu_sys/src/sysstate/graph.rs | 12 +- fuzzers/wcet_qemu_sys/src/sysstate/mod.rs | 7 +- .../wcet_qemu_sys/src/sysstate/mutators.rs | 123 ++++++++++++++++++ .../wcet_qemu_sys/src/sysstate/observers.rs | 4 +- 8 files changed, 196 insertions(+), 47 deletions(-) create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/mutators.rs diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 6dd9dfd9ff..11875ac0da 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -9,6 +9,8 @@ default = ["std", "obj_collect"] std = [] multicore = [] +fuzz_interrupt = [] # use the first bytes of the input for a timed interrupts + # select which feedbacks to use. enable at least one. feed_known_edges = [] feed_afl = [] @@ -31,6 +33,7 @@ obj_ticks = [] muta_input = [ "sched_graph" ] muta_snip = [ "sched_graph" ] muta_suffix = [ "sched_graph" ] +muta_interrupt = [] benchmark = [] # don't save corpus to disk, easy parallelizable dump_infos = [] # dump select corpus items for analysis diff --git a/fuzzers/wcet_qemu_sys/Makefile b/fuzzers/wcet_qemu_sys/Makefile index d119027b56..dace03ae5d 100644 --- a/fuzzers/wcet_qemu_sys/Makefile +++ b/fuzzers/wcet_qemu_sys/Makefile @@ -2,46 +2,54 @@ BENCHDIR = target/bench TARGET = tmr.axf TARGET_TRACE = $(BENCHDIR)/traces/tmr_worst.ron TARGET_EDGES = $(BENCHDIR)/edges/tmr_worst.ron -RUNTIME = 1 +RUNTIME = 3600 +COMMON_FLAGS = benchmark,dump_infos,fuzz_interrupt $(BENCHDIR)/bin: mkdir -p $@ $(BENCHDIR)/target_known_edges: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_known_edges,sched_queue + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_known_edges,sched_queue $(BENCHDIR)/target_afl_queue: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_afl,sched_queue + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_afl,sched_queue $(BENCHDIR)/target_afl_mapmax: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_afl,sched_mapmax + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_afl,sched_mapmax $(BENCHDIR)/target_state: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_state,sched_state + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_state,sched_state $(BENCHDIR)/target_state_afl: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_state,sched_state + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_state,sched_state + +$(BENCHDIR)/target_state_afl_int: + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_state,sched_state,muta_interrupt $(BENCHDIR)/target_graph: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph $(BENCHDIR)/target_graph_muta: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph,muta_snip,muta_input,muta_suffix + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,muta_snip,muta_input,muta_suffix $(BENCHDIR)/target_graph_afl: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph,feed_afl + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,feed_afl + +$(BENCHDIR)/target_graph_muta_afl: + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix $(BENCHDIR)/target_graph_all: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix,muta_interrupt -binaries: $(BENCHDIR)/target_known_edges $(BENCHDIR)/target_afl_queue $(BENCHDIR)/target_afl_mapmax $(BENCHDIR)/target_state $(BENCHDIR)/target_state_afl \ - $(BENCHDIR)/target_graph $(BENCHDIR)/target_graph_muta $(BENCHDIR)/target_graph_afl $(BENCHDIR)/target_graph_all +binaries: $(BENCHDIR)/target_known_edges $(BENCHDIR)/target_afl_queue $(BENCHDIR)/target_afl_mapmax $(BENCHDIR)/target_state $(BENCHDIR)/target_state_afl $(BENCHDIR)/target_state_afl_int \ + $(BENCHDIR)/target_graph $(BENCHDIR)/target_graph_muta $(BENCHDIR)/target_graph_afl $(BENCHDIR)/target_graph_muta_afl $(BENCHDIR)/target_graph_all # variants: known_edges, afl_queue, afl_mapmax, state, state_afl, graph, graph_muta, graph_afl, graph_all $(BENCHDIR)/bench_%.log: $(BENCHDIR)/target_% $(TARGET_TRACE) mkdir -p $(BENCHDIR)/execs - for i in {1..3}; do \ + for i in {1..10}; do \ CASE=$$(basename -s.log $@ | cut -d'_' -f 2- ); \ + [ -f $(BENCHDIR)/execs/$$CASE\_$$i.exec -a -f $@_$$i ] && continue; \ echo $$CASE iteration $$i; \ mkdir -p $(BENCHDIR)/infos/$$CASE ; \ ./fuzzer_bench.sh $> $@; done -benchmarks: target/bench/bench_known_edges.log target/bench/bench_afl_queue.log target/bench/bench_afl_mapmax.log target/bench/bench_state.log target/bench/bench_state_afl.log \ - target/bench/bench_graph.log target/bench/bench_graph_muta.log target/bench/bench_graph_afl.log target/bench/bench_graph_all.log +benchmarks_all: target/bench/bench_known_edges.log target/bench/bench_afl_queue.log target/bench/bench_afl_mapmax.log target/bench/bench_state.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log \ + target/bench/bench_graph.log target/bench/bench_graph_muta.log target/bench/bench_graph_afl.log target/bench/bench_graph_muta_afl.log target/bench/bench_graph_all.log -all: binaries benchmarks +benchmarks_int_mut: target/bench/bench_graph_muta_afl.log target/bench/bench_graph_all.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log + +all: binaries benchmarks_all clean_bench: rm -rf $(BENCHDIR)/bench_* +clean_aggregate: + rm -rf $(BENCHDIR)/bench_*.log + clean: rm -rf target/bench diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index d9c0f828a3..3c2f1bd41e 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,4 +1,7 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use core::cmp::min; +use wcet_qemu_sys::sysstate::mutators::InterruptShifterMutator; +use wcet_qemu_sys::sysstate::IRQ_INPUT_OFFSET; use std::str::FromStr; use wcet_qemu_sys::worst::DumpEdgesMapMetadata; use wcet_qemu_sys::worst::DumpMapFeedback; @@ -34,7 +37,7 @@ use wcet_qemu_sys::sysstate::helpers::QemuSystemStateHelper; use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; use wcet_qemu_sys::sysstate::feedbacks::SysStateFeedbackState; use wcet_qemu_sys::sysstate::feedbacks::NovelSysStateFeedback; -use wcet_qemu_sys::sysstate::INPUT_BYTES_OFFSET; +use wcet_qemu_sys::sysstate::IRQ_INPUT_BYTES_NUMBER; use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::minimizer::QemuCaseMinimizerStage; use hashbrown::HashMap; @@ -505,6 +508,8 @@ fn fuzz( let mutator_list = mutator_list.merge(tuple_list!(RandGraphSuffixMutator::new())); #[cfg(feature = "muta_snip")] let mutator_list = mutator_list.merge(tuple_list!(RandGraphSnippetMutator::new())); + #[cfg(all(feature = "muta_interrupt",feature = "fuzz_interrupt"))] + let mutator_list = mutator_list.merge(tuple_list!(InterruptShifterMutator::new())); // Setup a MOPT mutator let mutator = StdMOptMutator::new(&mut state, mutator_list,5)?; @@ -531,15 +536,14 @@ fn fuzz( let mut buf = target.as_slice(); let mut len = buf.len(); let mut int_tick : Option = None; - if INPUT_BYTES_OFFSET!= 0 { - if len > 2 { - let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt - t[0]=buf[0]; - t[1]=buf[1]; - int_tick = Some(u32::from_le_bytes(t)); - buf = &buf[2..]; - len = buf.len(); + if len > IRQ_INPUT_BYTES_NUMBER as usize { + let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt + for i in 0..min(4,IRQ_INPUT_BYTES_NUMBER) { + t[i as usize]=buf[i as usize]; } + int_tick = Some(u32::from_le_bytes(t)); + buf = &buf[IRQ_INPUT_BYTES_NUMBER as usize..]; + len = buf.len(); } if len >= 32 { buf = &buf[0..32]; @@ -547,8 +551,8 @@ fn fuzz( } unsafe { - if INPUT_BYTES_OFFSET!= 0 { - libafl_int_offset = 347780+int_tick.unwrap_or(0); + if IRQ_INPUT_BYTES_NUMBER!= 0 { + libafl_int_offset = IRQ_INPUT_OFFSET+int_tick.unwrap_or(0); } // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 43404ae230..39c7d6dd62 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -1,10 +1,12 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use core::cmp::min; +use wcet_qemu_sys::sysstate::IRQ_INPUT_OFFSET; +use wcet_qemu_sys::sysstate::IRQ_INPUT_BYTES_NUMBER; use wcet_qemu_sys::sysstate::helpers::INTR_OFFSET; use std::io::Read; use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; use wcet_qemu_sys::sysstate::feedbacks::DumpSystraceFeedback; -use wcet_qemu_sys::sysstate::INPUT_BYTES_OFFSET; use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::{ worst::{DumpMapFeedback,DummyFeedback}, @@ -340,15 +342,14 @@ fn fuzz( let mut buf = target.as_slice(); let mut len = buf.len(); let mut int_tick : Option = None; - if INPUT_BYTES_OFFSET!= 0 { - if len > 2 { - let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt - t[0]=buf[0]; - t[1]=buf[1]; - int_tick = Some(u32::from_le_bytes(t)); - buf = &buf[2..]; - len = buf.len(); + if len > IRQ_INPUT_BYTES_NUMBER as usize { + let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt + for i in 0..min(4,IRQ_INPUT_BYTES_NUMBER) { + t[i as usize]=buf[i as usize]; } + int_tick = Some(u32::from_le_bytes(t)); + buf = &buf[IRQ_INPUT_BYTES_NUMBER as usize..]; + len = buf.len(); } if len >= 32 { buf = &buf[0..32]; @@ -356,8 +357,8 @@ fn fuzz( } unsafe { - if INPUT_BYTES_OFFSET!= 0 { - libafl_int_offset = 347780+int_tick.unwrap_or(0); + if IRQ_INPUT_BYTES_NUMBER!= 0 { + libafl_int_offset = IRQ_INPUT_OFFSET+int_tick.unwrap_or(0); } // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs index e12e490ed1..c26cd66e53 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -45,12 +45,12 @@ use libafl::bolts::rands::Rand; //============================= Data Structures #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] -struct VariantTuple +pub struct VariantTuple { - start_tick: u64, - end_tick: u64, + pub start_tick: u64, + pub end_tick: u64, input_counter: u32, - input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime + pub input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime } impl VariantTuple { fn from(other: &RefinedFreeRTOSSystemState,input: Vec) -> Self { @@ -67,7 +67,7 @@ impl VariantTuple { pub struct SysGraphNode { base: RefinedFreeRTOSSystemState, - variants: Vec, + pub variants: Vec, } impl SysGraphNode { fn from(base: RefinedFreeRTOSSystemState, input: Vec) -> Self { @@ -117,7 +117,7 @@ impl PartialEq for SysGraphNode { // Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct SysGraphMetadata { - inner: Vec, + pub inner: Vec, indices: Vec, tcref: isize, } diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs index ac609edf99..c5eabb82d0 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -14,8 +14,13 @@ pub mod helpers; pub mod observers; pub mod feedbacks; pub mod graph; +pub mod mutators; -pub const INPUT_BYTES_OFFSET : u32 = 2; // Offset for interrupt bytes +#[cfg(feature = "fuzz_interrupt")] +pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes +#[cfg(not(feature = "fuzz_interrupt"))] +pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes +pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start // Constants const NUM_PRIOS: usize = 5; diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mutators.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mutators.rs new file mode 100644 index 0000000000..29082a572c --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mutators.rs @@ -0,0 +1,123 @@ +use crate::sysstate::graph::SysGraphMetadata; +use crate::sysstate::graph::SysGraphNode; +use libafl::state::HasFeedbackStates; +use crate::sysstate::IRQ_INPUT_OFFSET; +use crate::sysstate::IRQ_INPUT_BYTES_NUMBER; +use crate::sysstate::graph::SysGraphFeedbackState; +use libafl::inputs::HasBytesVec; +use libafl::bolts::rands::RandomSeed; +use libafl::bolts::rands::StdRand; +use libafl::mutators::Mutator; +use libafl::mutators::MutationResult; +use core::marker::PhantomData; +use libafl::state::HasCorpus; +use libafl::state::HasSolutions; +use libafl::state::HasRand; + +use libafl::bolts::tuples::MatchName; +use libafl::bolts::tuples::Named; +use libafl::Error; +use libafl::{inputs::Input, state::HasMetadata}; + +use super::FreeRTOSSystemStateMetadata; + +use libafl::bolts::rands::Rand; + + +//=============================== Interrupt +/// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata +pub struct InterruptShifterMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + phantom: PhantomData<(I, S)>, +} +impl InterruptShifterMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + pub fn new() -> Self { + InterruptShifterMutator{phantom: PhantomData} + } +} +impl Mutator for InterruptShifterMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32 + ) -> Result + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + drop(tmp); + + let target_bytes = input.bytes_mut(); + let mut target_tick = 0; + + #[cfg(feature = "sched_state")] + { + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace =tmp.expect("FreeRTOSSystemStateMetadata not found"); + let target_block = myrand.choose(trace.inner.iter()); + target_tick = myrand.between(target_block.start_tick,target_block.end_tick)-IRQ_INPUT_OFFSET as u64; + } + #[cfg(feature = "sched_state")] + { + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let g = &feedbackstate.graph; + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace = tmp.expect("SysGraphMetadata not found"); + let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())]; + target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) { + Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64, + None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64, + }; + + } + if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 { + for i in 0..IRQ_INPUT_BYTES_NUMBER as usize { + target_bytes[i] = u64::to_le_bytes(target_tick)[i]; + } + return Ok(MutationResult::Mutated); + } else { + return Ok(MutationResult::Skipped); + } + } + + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option + ) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for InterruptShifterMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn name(&self) -> &str { + "InterruptShifterMutator" + } +} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs index 0a33266787..91b5480a51 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -1,4 +1,4 @@ -use crate::sysstate::INPUT_BYTES_OFFSET; +use crate::sysstate::IRQ_INPUT_BYTES_NUMBER; use libafl::inputs::HasTargetBytes; use libafl::bolts::HasLen; use libafl::bolts::tuples::Named; @@ -130,7 +130,7 @@ for mut i in input.drain(..) { start_tick: start_tick, end_tick: i.qemu_tick, ready_list_after: collector, - input_counter: i.input_counter+INPUT_BYTES_OFFSET, + input_counter: i.input_counter+IRQ_INPUT_BYTES_NUMBER, last_pc: i.last_pc, }); start_tick=i.qemu_tick; From 6969b5de5a51ffd6cca044945574ee091d1554da Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 8 Jun 2022 02:21:17 +0200 Subject: [PATCH 79/83] add random generator fuzzer --- fuzzers/wcet_qemu_sys/Cargo.toml | 1 + fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 41 +++++++++++++++++++------ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 11875ac0da..d6dd0056bd 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -10,6 +10,7 @@ std = [] multicore = [] fuzz_interrupt = [] # use the first bytes of the input for a timed interrupts +fuzz_random = [] # just process random inputs # select which feedbacks to use. enable at least one. feed_known_edges = [] diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index 3c2f1bd41e..255000666f 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,4 +1,6 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use libafl::Evaluator; +use libafl::generators::Generator; use core::cmp::min; use wcet_qemu_sys::sysstate::mutators::InterruptShifterMutator; use wcet_qemu_sys::sysstate::IRQ_INPUT_OFFSET; @@ -43,6 +45,7 @@ use wcet_qemu_sys::minimizer::QemuCaseMinimizerStage; use hashbrown::HashMap; use clap::{App, Arg}; use core::{cell::RefCell, time::Duration}; +use std::time::SystemTime; #[cfg(unix)] use nix::{self, unistd::dup}; #[cfg(unix)] @@ -441,6 +444,8 @@ fn fuzz( let feedback = ClockFeedback::new_with_observer(&clock_observer); #[cfg(all(not(feature = "feed_state"), feature = "dump_infos"))] // for diagnostic purposes it's necessary to collect the state in any case let feedback = feedback_or!(feedback, DumpSystraceFeedback::metadata_only()); + #[cfg(all(feature = "obj_collect", feature = "fuzz_random"))] // random fuzzing does not trigger objective feedbacks + let feedback = feedback_or!(feedback, ExecTimeCollectorFeedback::new()); #[cfg(feature = "dump_infos")] // for diagnostic purposes it's necessary to collect the state in any case let feedback = feedback_or!(feedback, DumpMapFeedback::metadata_only(&edges_observer)); #[cfg(feature = "feed_known_edges")] @@ -456,7 +461,7 @@ fn fuzz( // A feedback to choose if an input is a solution or not let objective = DummyFeedback::new(false); - #[cfg(feature = "obj_collect")] + #[cfg(all(feature = "obj_collect",not(feature = "fuzz_random")))] let objective = ExecTimeCollectorFeedback::new(); #[cfg(feature = "obj_trace")] let objective = feedback_or!(HitSysStateFeedback::new(target_trace)); @@ -599,6 +604,7 @@ fn fuzz( } } + #[cfg(not(feature = "fuzz_random"))] if state.corpus().count() < 1 { if _core_id == 0 && state.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]).is_ok() { // println!("We imported {} inputs from disk.", state.corpus().count()); @@ -632,14 +638,30 @@ fn fuzz( dup2(null_fd, io::stderr().as_raw_fd())?; } - match fuzztime { - Some(t) => fuzzer - .fuzz_for_solution_or_n(&mut stages, &mut executor, &mut state, &mut mgr, t) - .expect("Error in the fuzzing loop"), - None => fuzzer - .fuzz_for_solution(&mut stages, &mut executor, &mut state, &mut mgr) - .expect("Error in the fuzzing loop"), - }; + #[cfg(not(feature = "fuzz_random"))] + { + match fuzztime { + Some(t) => fuzzer + .fuzz_for_solution_or_n(&mut stages, &mut executor, &mut state, &mut mgr, t) + .expect("Error in the fuzzing loop"), + None => fuzzer + .fuzz_for_solution(&mut stages, &mut executor, &mut state, &mut mgr) + .expect("Error in the fuzzing loop"), + }; + } + #[cfg(feature = "fuzz_random")] + { + let mut generator = RandBytesGenerator::new(32); + let target_duration = Duration::from_secs(fuzztime.expect("Random fuzzing needs target time")); + let start_time = SystemTime::now(); + while start_time.elapsed().unwrap() < target_duration { + let inp = generator.generate(&mut state).unwrap(); + fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp); + } + match fuzzer.fuzz_for_solution_or_n(&mut stages, &mut executor, &mut state, &mut mgr, 0) { + _ => (), + } // Just here for the typecheker + } #[cfg(not(feature = "benchmark"))] @@ -676,7 +698,6 @@ fn fuzz( #[cfg(feature = "dump_infos")] { - println!("Start dumping {}", state.corpus().count()); let c = if state.solutions().count()>0 { state.solutions() } else { state.corpus() }; let mut worst = Duration::new(0,0); let mut worst_input : Option> = Some(vec![]); From 948ce540e84be386d4671783c014a3465ea4c9af Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 8 Jun 2022 02:21:47 +0200 Subject: [PATCH 80/83] parallel benchmarks with semaphores --- fuzzers/wcet_qemu_sys/Makefile | 26 ++++++++++++++++--- fuzzers/wcet_qemu_sys/bash_sem.sh | 43 +++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) create mode 100755 fuzzers/wcet_qemu_sys/bash_sem.sh diff --git a/fuzzers/wcet_qemu_sys/Makefile b/fuzzers/wcet_qemu_sys/Makefile index dace03ae5d..c1e58ecbab 100644 --- a/fuzzers/wcet_qemu_sys/Makefile +++ b/fuzzers/wcet_qemu_sys/Makefile @@ -4,10 +4,18 @@ TARGET_TRACE = $(BENCHDIR)/traces/tmr_worst.ron TARGET_EDGES = $(BENCHDIR)/edges/tmr_worst.ron RUNTIME = 3600 COMMON_FLAGS = benchmark,dump_infos,fuzz_interrupt +NUM_JOB = 3 +NUM_ITERATIONS = 10 +LOCKFILE = /tmp/bench_sem +LOCK = ./bash_sem.sh $(LOCKFILE) lock +RELEASE = ./bash_sem.sh $(LOCKFILE) release $(BENCHDIR)/bin: mkdir -p $@ +$(BENCHDIR)/target_random: + cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,fuzz_interrupt,sched_queue,fuzz_random + $(BENCHDIR)/target_known_edges: cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_known_edges,sched_queue @@ -41,21 +49,23 @@ $(BENCHDIR)/target_graph_muta_afl: $(BENCHDIR)/target_graph_all: cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix,muta_interrupt -binaries: $(BENCHDIR)/target_known_edges $(BENCHDIR)/target_afl_queue $(BENCHDIR)/target_afl_mapmax $(BENCHDIR)/target_state $(BENCHDIR)/target_state_afl $(BENCHDIR)/target_state_afl_int \ +binaries: $(BENCHDIR)/target_random $(BENCHDIR)/target_known_edges $(BENCHDIR)/target_afl_queue $(BENCHDIR)/target_afl_mapmax $(BENCHDIR)/target_state $(BENCHDIR)/target_state_afl $(BENCHDIR)/target_state_afl_int \ $(BENCHDIR)/target_graph $(BENCHDIR)/target_graph_muta $(BENCHDIR)/target_graph_afl $(BENCHDIR)/target_graph_muta_afl $(BENCHDIR)/target_graph_all # variants: known_edges, afl_queue, afl_mapmax, state, state_afl, graph, graph_muta, graph_afl, graph_all $(BENCHDIR)/bench_%.log: $(BENCHDIR)/target_% $(TARGET_TRACE) mkdir -p $(BENCHDIR)/execs - for i in {1..10}; do \ + for i in {1..$(NUM_ITERATIONS)}; do \ CASE=$$(basename -s.log $@ | cut -d'_' -f 2- ); \ [ -f $(BENCHDIR)/execs/$$CASE\_$$i.exec -a -f $@_$$i ] && continue; \ + $(LOCK); \ echo $$CASE iteration $$i; \ mkdir -p $(BENCHDIR)/infos/$$CASE ; \ ./fuzzer_bench.sh $ $@_$$i; \ - sed -i "1 i\\$$CASE " $(BENCHDIR)/execs/$$CASE\_$$i.exec; \ + --libafl-edges $(TARGET_EDGES) --libafl-exectimes $(BENCHDIR)/execs/$$CASE\_$$i.exec > $@_$$i || \ + sed -i "1 i\\$$CASE " $(BENCHDIR)/execs/$$CASE\_$$i.exec || \ + $(RELEASE) & \ done for i in $@_*; do tail -n 1 $$i >> $@; done @@ -64,6 +74,8 @@ benchmarks_all: target/bench/bench_known_edges.log target/bench/bench_afl_queue. benchmarks_int_mut: target/bench/bench_graph_muta_afl.log target/bench/bench_graph_all.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log +benchmark_random: target/bench/bench_random.log + all: binaries benchmarks_all clean_bench: @@ -74,6 +86,12 @@ clean_aggregate: clean: rm -rf target/bench + echo $(NUM_JOB) > $(LOCKFILE) + rm -rf $(LOCKFILE)_lockdir + +reset_sem: + echo $(NUM_JOB) > $(LOCKFILE) + rm -rf $(LOCKFILE)_lockdir %.case: %_inputs mkdir -p $(BENCHDIR)/traces $(BENCHDIR)/edges diff --git a/fuzzers/wcet_qemu_sys/bash_sem.sh b/fuzzers/wcet_qemu_sys/bash_sem.sh new file mode 100755 index 0000000000..c93986b563 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/bash_sem.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# A generic counting semaphore in bash +# Parameter is the lockfile and operation +# Setup: +# rm /tmp/test +# echo $num > /tmp/test + +set -e +if [[ ! -f $1 ]]; then echo "Parameter 1: File Does not exist"; exit; fi +if [[ $2 != "lock" ]] && [[ $2 != "release" ]]; then echo "Parameter 2: must be lock or release"; exit; fi +if [[ $2 = "lock" ]]; then + SEM='' + while [[ -z $SEM ]]; do + if mkdir $1_lockdir > /dev/null 2>&1 ; then + VAL=$(cat $1) + if (( $VAL > 0 )) + then + SEM=$(sed -i "s@$VAL@$(( $VAL - 1))@w /dev/stdout" $1) + echo "Take $VAL -> $SEM" + else + sleep 1; wait + fi + else + sleep 0.1; + fi + done + rmdir $1_lockdir +else + SEM='' + while [[ -z $SEM ]]; do + if mkdir $1_lockdir > /dev/null 2>&1 ; then + VAL=$(cat $1) + SEM=$(sed -i "s@$VAL@$(( $VAL + 1))@w /dev/stdout" $1) + echo "Give $VAL -> $(( $VAL + 1 ))" + else + sleep 0.1; + fi + done + rmdir $1_lockdir +fi + +#SEM=''; while [[ -z SEM ]]; do VAL=$(cat /tmp/test); if (( $VAL > 0 )); then SEM=$(sed -i "s@$VAL@$(( $VAL - 1))@w /dev/stdout" /tmp/test); else sleep 1; wait; fi; done From aa7d78affed5b361f01a648d637f1f71a95a58ba Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 14 Jun 2022 23:54:47 +0200 Subject: [PATCH 81/83] finish eval scripts --- fuzzers/wcet_qemu_sys/Makefile | 36 +++++++++++++++++-------------- fuzzers/wcet_qemu_sys/bash_sem.sh | 9 +++++--- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/Makefile b/fuzzers/wcet_qemu_sys/Makefile index c1e58ecbab..bcae7c69b5 100644 --- a/fuzzers/wcet_qemu_sys/Makefile +++ b/fuzzers/wcet_qemu_sys/Makefile @@ -1,9 +1,11 @@ BENCHDIR = target/bench -TARGET = tmr.axf -TARGET_TRACE = $(BENCHDIR)/traces/tmr_worst.ron -TARGET_EDGES = $(BENCHDIR)/edges/tmr_worst.ron +TNAME = tmrint +TARGET = $(TNAME).axf +TARGET_TRACE = $(BENCHDIR)/traces/$(TNAME)_worst.ron +TARGET_EDGES = $(BENCHDIR)/edges/$(TNAME)_worst.ron RUNTIME = 3600 -COMMON_FLAGS = benchmark,dump_infos,fuzz_interrupt +INT_FLAG = ,fuzz_interrupt +COMMON_FLAGS = benchmark,dump_infos$(INT_FLAG) NUM_JOB = 3 NUM_ITERATIONS = 10 LOCKFILE = /tmp/bench_sem @@ -14,7 +16,7 @@ $(BENCHDIR)/bin: mkdir -p $@ $(BENCHDIR)/target_random: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,fuzz_interrupt,sched_queue,fuzz_random + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),sched_queue,fuzz_random $(BENCHDIR)/target_known_edges: cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_known_edges,sched_queue @@ -29,7 +31,7 @@ $(BENCHDIR)/target_state: cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_state,sched_state $(BENCHDIR)/target_state_afl: - cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_state,sched_state + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_state,feed_afl,sched_state $(BENCHDIR)/target_state_afl_int: cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_state,sched_state,muta_interrupt @@ -46,11 +48,11 @@ $(BENCHDIR)/target_graph_afl: $(BENCHDIR)/target_graph_muta_afl: cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix -$(BENCHDIR)/target_graph_all: +$(BENCHDIR)/target_graph_muta_afl_int: cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix,muta_interrupt binaries: $(BENCHDIR)/target_random $(BENCHDIR)/target_known_edges $(BENCHDIR)/target_afl_queue $(BENCHDIR)/target_afl_mapmax $(BENCHDIR)/target_state $(BENCHDIR)/target_state_afl $(BENCHDIR)/target_state_afl_int \ - $(BENCHDIR)/target_graph $(BENCHDIR)/target_graph_muta $(BENCHDIR)/target_graph_afl $(BENCHDIR)/target_graph_muta_afl $(BENCHDIR)/target_graph_all + $(BENCHDIR)/target_graph $(BENCHDIR)/target_graph_muta $(BENCHDIR)/target_graph_afl $(BENCHDIR)/target_graph_muta_afl $(BENCHDIR)/target_graph_muta_afl_int # variants: known_edges, afl_queue, afl_mapmax, state, state_afl, graph, graph_muta, graph_afl, graph_all $(BENCHDIR)/bench_%.log: $(BENCHDIR)/target_% $(TARGET_TRACE) @@ -63,20 +65,22 @@ $(BENCHDIR)/bench_%.log: $(BENCHDIR)/target_% $(TARGET_TRACE) mkdir -p $(BENCHDIR)/infos/$$CASE ; \ ./fuzzer_bench.sh $ $@_$$i || \ - sed -i "1 i\\$$CASE " $(BENCHDIR)/execs/$$CASE\_$$i.exec || \ + --libafl-edges $(TARGET_EDGES) --libafl-exectimes $(BENCHDIR)/execs/$$CASE\_$$i.exec > $@_$$i && \ + sed -i "1 i\\$$CASE " $(BENCHDIR)/execs/$$CASE\_$$i.exec && \ $(RELEASE) & \ done - for i in $@_*; do tail -n 1 $$i >> $@; done + wait + for i in $@_*; do tail -n 1 $$i >> $@; done -benchmarks_all: target/bench/bench_known_edges.log target/bench/bench_afl_queue.log target/bench/bench_afl_mapmax.log target/bench/bench_state.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log \ - target/bench/bench_graph.log target/bench/bench_graph_muta.log target/bench/bench_graph_afl.log target/bench/bench_graph_muta_afl.log target/bench/bench_graph_all.log +benchmarks_noint: target/bench/bench_known_edges.log target/bench/bench_afl_queue.log target/bench/bench_afl_mapmax.log target/bench/bench_state.log target/bench/bench_state_afl.log \ + target/bench/bench_graph.log target/bench/bench_graph_muta.log target/bench/bench_graph_afl.log target/bench/bench_graph_muta_afl.log +# target/bench/bench_graph_all.log target/bench/bench_state_afl_int.log -benchmarks_int_mut: target/bench/bench_graph_muta_afl.log target/bench/bench_graph_all.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log +benchmarks_int: target/bench/bench_graph_muta_afl.log target/bench/bench_graph_muta_afl_int.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log target/bench/bench_afl_mapmax.log benchmark_random: target/bench/bench_random.log -all: binaries benchmarks_all +all: binaries benchmarks_noint clean_bench: rm -rf $(BENCHDIR)/bench_* @@ -104,4 +108,4 @@ reset_sem: grep "Qemu Ticks:"; \ done -$(TARGET_TRACE): tmr.case \ No newline at end of file +$(TARGET_TRACE): $(TNAME).case \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/bash_sem.sh b/fuzzers/wcet_qemu_sys/bash_sem.sh index c93986b563..261b54f4b3 100755 --- a/fuzzers/wcet_qemu_sys/bash_sem.sh +++ b/fuzzers/wcet_qemu_sys/bash_sem.sh @@ -12,21 +12,24 @@ if [[ $2 != "lock" ]] && [[ $2 != "release" ]]; then echo "Parameter 2: must be if [[ $2 = "lock" ]]; then SEM='' while [[ -z $SEM ]]; do + if (( $(cat $1 ) == 0 )); then sleep 1; wait; continue; fi if mkdir $1_lockdir > /dev/null 2>&1 ; then VAL=$(cat $1) if (( $VAL > 0 )) then SEM=$(sed -i "s@$VAL@$(( $VAL - 1))@w /dev/stdout" $1) echo "Take $VAL -> $SEM" + rmdir $1_lockdir else + rmdir $1_lockdir sleep 1; wait fi else - sleep 0.1; + sleep 0.5; fi done - rmdir $1_lockdir else + echo "Attempt unlock" SEM='' while [[ -z $SEM ]]; do if mkdir $1_lockdir > /dev/null 2>&1 ; then @@ -36,8 +39,8 @@ else else sleep 0.1; fi + rmdir $1_lockdir done - rmdir $1_lockdir fi #SEM=''; while [[ -z SEM ]]; do VAL=$(cat /tmp/test); if (( $VAL > 0 )); then SEM=$(sed -i "s@$VAL@$(( $VAL - 1))@w /dev/stdout" /tmp/test); else sleep 1; wait; fi; done From e8ff45b205dc1e06d3f66975be2535e3bad954b7 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 10 Jul 2022 16:21:50 +0200 Subject: [PATCH 82/83] update evaluation scripts --- fuzzers/wcet_qemu_sys/Makefile | 14 ++++++++------ fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 2 +- fuzzers/wcet_qemu_sys/tmr_inputs/.gitignore | 1 + fuzzers/wcet_qemu_sys/tmr_inputs/inputs.sh | 4 ++++ fuzzers/wcet_qemu_sys/tmrint_inputs/.gitignore | 1 + fuzzers/wcet_qemu_sys/tmrint_inputs/inputs.sh | 6 ++++++ 6 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 fuzzers/wcet_qemu_sys/tmr_inputs/.gitignore create mode 100644 fuzzers/wcet_qemu_sys/tmr_inputs/inputs.sh create mode 100644 fuzzers/wcet_qemu_sys/tmrint_inputs/.gitignore create mode 100644 fuzzers/wcet_qemu_sys/tmrint_inputs/inputs.sh diff --git a/fuzzers/wcet_qemu_sys/Makefile b/fuzzers/wcet_qemu_sys/Makefile index bcae7c69b5..2dc44488b5 100644 --- a/fuzzers/wcet_qemu_sys/Makefile +++ b/fuzzers/wcet_qemu_sys/Makefile @@ -5,7 +5,7 @@ TARGET_TRACE = $(BENCHDIR)/traces/$(TNAME)_worst.ron TARGET_EDGES = $(BENCHDIR)/edges/$(TNAME)_worst.ron RUNTIME = 3600 INT_FLAG = ,fuzz_interrupt -COMMON_FLAGS = benchmark,dump_infos$(INT_FLAG) +COMMON_FLAGS = benchmark,dump_infos#,$(INT_FLAG) NUM_JOB = 3 NUM_ITERATIONS = 10 LOCKFILE = /tmp/bench_sem @@ -16,7 +16,7 @@ $(BENCHDIR)/bin: mkdir -p $@ $(BENCHDIR)/target_random: - cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),sched_queue,fuzz_random + cargo build --target-dir $@ --features $(COMMON_FLAGS),sched_queue,fuzz_random $(BENCHDIR)/target_known_edges: cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_known_edges,sched_queue @@ -70,13 +70,13 @@ $(BENCHDIR)/bench_%.log: $(BENCHDIR)/target_% $(TARGET_TRACE) $(RELEASE) & \ done wait - for i in $@_*; do tail -n 1 $$i >> $@; done + for i in $@_*; do grep Stats $$i | tail -n 1 >> $@; done benchmarks_noint: target/bench/bench_known_edges.log target/bench/bench_afl_queue.log target/bench/bench_afl_mapmax.log target/bench/bench_state.log target/bench/bench_state_afl.log \ target/bench/bench_graph.log target/bench/bench_graph_muta.log target/bench/bench_graph_afl.log target/bench/bench_graph_muta_afl.log # target/bench/bench_graph_all.log target/bench/bench_state_afl_int.log -benchmarks_int: target/bench/bench_graph_muta_afl.log target/bench/bench_graph_muta_afl_int.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log target/bench/bench_afl_mapmax.log +benchmarks_int: target/bench/bench_graph_muta_afl.log target/bench/bench_graph_muta_afl_int.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log target/bench/bench_afl_mapmax.log target/bench/bench_afl_queue.log benchmark_random: target/bench/bench_random.log @@ -97,12 +97,14 @@ reset_sem: echo $(NUM_JOB) > $(LOCKFILE) rm -rf $(LOCKFILE)_lockdir -%.case: %_inputs +%.case: %_inputs $(BENCHDIR)/target_random mkdir -p $(BENCHDIR)/traces $(BENCHDIR)/edges for i in $ = None; - if len > IRQ_INPUT_BYTES_NUMBER as usize { + if len > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER!=0{ let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt for i in 0..min(4,IRQ_INPUT_BYTES_NUMBER) { t[i as usize]=buf[i as usize]; diff --git a/fuzzers/wcet_qemu_sys/tmr_inputs/.gitignore b/fuzzers/wcet_qemu_sys/tmr_inputs/.gitignore new file mode 100644 index 0000000000..f9f3041727 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/tmr_inputs/.gitignore @@ -0,0 +1 @@ +*.case diff --git a/fuzzers/wcet_qemu_sys/tmr_inputs/inputs.sh b/fuzzers/wcet_qemu_sys/tmr_inputs/inputs.sh new file mode 100644 index 0000000000..da2decba55 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/tmr_inputs/inputs.sh @@ -0,0 +1,4 @@ +echo "" > best.case +echo "\x00\x00" > best_success.case +echo "\x05\x29\x07\x1f\x0b\x17\x00\x17" > worst_trace.case +echo "\xFF\xF6\xFC\xF8\xFD\xFD\xFF\xFD" > worst.case diff --git a/fuzzers/wcet_qemu_sys/tmrint_inputs/.gitignore b/fuzzers/wcet_qemu_sys/tmrint_inputs/.gitignore new file mode 100644 index 0000000000..f9f3041727 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/tmrint_inputs/.gitignore @@ -0,0 +1 @@ +*.case diff --git a/fuzzers/wcet_qemu_sys/tmrint_inputs/inputs.sh b/fuzzers/wcet_qemu_sys/tmrint_inputs/inputs.sh new file mode 100644 index 0000000000..6d1a06f8a2 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/tmrint_inputs/inputs.sh @@ -0,0 +1,6 @@ +echo "\xff\xff" > best.case +echo "\xff\xff\x00\x00" > best_success.case +echo "\xff\x05\x05\x29\x07\x1f\x0b\x17\x00\x17" > worst_trace.case +echo "\xff\x05\xFF\xF6\xFC\xF8\xFD\xFD\xFF\xFD" > worst.case +echo "\x3F\x05\xFF\xF6\xFC\xF8\xFD\xFD\xFF\xFD" > worst_preempt.case +echo "\x93\x1F\xFF\xF6\xFC\xF8\xFD\xFD\xFF\xFD" > worst_preempt_adv.case From 8a661de371250a818afad4e057bbe6a21102c5a7 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 7 Sep 2022 21:28:40 +0200 Subject: [PATCH 83/83] add missing benchmark script --- fuzzers/wcet_qemu_sys/Makefile | 4 ++-- fuzzers/wcet_qemu_sys/bench_fuzzer.sh | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100755 fuzzers/wcet_qemu_sys/bench_fuzzer.sh diff --git a/fuzzers/wcet_qemu_sys/Makefile b/fuzzers/wcet_qemu_sys/Makefile index 2dc44488b5..c318f66582 100644 --- a/fuzzers/wcet_qemu_sys/Makefile +++ b/fuzzers/wcet_qemu_sys/Makefile @@ -63,7 +63,7 @@ $(BENCHDIR)/bench_%.log: $(BENCHDIR)/target_% $(TARGET_TRACE) $(LOCK); \ echo $$CASE iteration $$i; \ mkdir -p $(BENCHDIR)/infos/$$CASE ; \ - ./fuzzer_bench.sh $ $@_$$i && \ sed -i "1 i\\$$CASE " $(BENCHDIR)/execs/$$CASE\_$$i.exec && \ @@ -110,4 +110,4 @@ reset_sem: grep "Qemu Ticks:"; \ done -$(TARGET_TRACE): $(TNAME).case \ No newline at end of file +$(TARGET_TRACE): $(TNAME).case diff --git a/fuzzers/wcet_qemu_sys/bench_fuzzer.sh b/fuzzers/wcet_qemu_sys/bench_fuzzer.sh new file mode 100755 index 0000000000..78135e5f48 --- /dev/null +++ b/fuzzers/wcet_qemu_sys/bench_fuzzer.sh @@ -0,0 +1,5 @@ +mkdir -p tmp/test_in tmp/test_out +[ ! -f tmp/test_in/test ] && echo " !test" > tmp/test_in/test +[ ! -f tmp/dummy.qcow2 ] && qemu-img create -f qcow2 tmp/dummy.qcow2 32M +export LD_LIBRARY_PATH=target/debug +$1 --libafl-snapshot tmp/dummy.qcow2 --libafl-out tmp/test_out --libafl-in tmp/test_in --libafl-kernel ${@:2}