diff --git a/fuzzers/aflpp_libafl_qemu/Cargo.toml b/fuzzers/aflpp_libafl_qemu/Cargo.toml new file mode 100644 index 0000000000..150a7ac077 --- /dev/null +++ b/fuzzers/aflpp_libafl_qemu/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "aflpp_libafl_qemu" +version = "0.11.2" +authors = ["Andrea Fioraldi "] +edition = "2021" + +[profile.release] +lto = true +codegen-units = 1 +opt-level = 3 +debug = true + +[dependencies] +libafl = { path = "../../libafl/" } +libafl_bolts = { path = "../../libafl_bolts/" } +libafl_targets = { path = "../../libafl_targets/", features = ["pointer_maps"] } +libafl_qemu = { path = "../../libafl_qemu/", features = ["usermode"] } +nix = { version = "0.27", default-features = false } # match libafl_bolts version diff --git a/fuzzers/aflpp_libafl_qemu/corpus/0 b/fuzzers/aflpp_libafl_qemu/corpus/0 new file mode 100644 index 0000000000..5c0ac06f55 --- /dev/null +++ b/fuzzers/aflpp_libafl_qemu/corpus/0 @@ -0,0 +1 @@ +00000 diff --git a/fuzzers/aflpp_libafl_qemu/src/main.rs b/fuzzers/aflpp_libafl_qemu/src/main.rs new file mode 100644 index 0000000000..2242d91886 --- /dev/null +++ b/fuzzers/aflpp_libafl_qemu/src/main.rs @@ -0,0 +1,113 @@ +use libafl::prelude::*; +use libafl_bolts::prelude::*; +use libafl_qemu::{ + asan::{ QemuAsanHelper, QemuAsanOptions}, + cmplog::{CmpLogObserver, QemuCmpLogHelper, QemuCmpLogRoutinesHelper}, + calls::{QemuCallTracerHelper, FullBacktraceCollector}, + edges::{edges_map_mut_slice, std_edges_map_observer, QemuEdgeCoverageClassicHelper, QemuEdgeCoverageHelper, MAX_EDGES_NUM}, + elf::EasyElf, + emu::Emulator, + helper::{QemuFilterList, HasInstrumentationFilter, QemuHelper, QemuHelperTuple}, + hooks::QemuHooks, + snapshot::QemuSnapshotHelper, + QemuExecutor, Regs, SYS_close, SYS_faccessat, SYS_lseek, SYS_newfstatat, SYS_openat, SYS_read, + SYS_rt_sigprocmask, SYS_write, SyscallHookResult, +}; +use libafl_targets::forkserver::{ForkserverHooks, start_forkserver_with_hooks, map_shared_memory}; +use std::env; +use nix::unistd::{close, pipe, read, write}; +use nix::libc; + +const TSL_FD: std::os::fd::RawFd = 196; + +struct TSLHooks { + read_end: std::os::fd::RawFd, +} + +impl TSLHooks { + fn new() -> Self { + Self { read_end: 0 } + } + + fn wait_tsl(&mut self) -> Result<(), ()> { + Ok(()) + } +} + +impl ForkserverHooks for TSLHooks { + extern "C" fn pre_fork(&mut self) { + // Establish a channel with child to grab translation commands. + // We'll read from the pipe read end, child will write to TSL_FD. + let (read_end, write_end) = pipe().expect("Could not create TSL pipe"); + self.read_end = read_end; + dup2(write_end, TSL_FD).expect("Could not dup2 the TSL pipe write end"); + close(write_end); + } + extern "C" fn post_fork(&mut self, pid: libc::pid_t) { + if pid != 0 { + // Parent + close(TSL_FD); + } else { + // Child + close(self.read_end); + } + } + extern "C" fn iter_start(&mut self) { + self.wait_tsl().expect("Wait for TSL failed"); + } + extern "C" fn iter_end(&mut self, status: i32) {} +} + +fn main() { + let in_afl = env::var("__AFL_SHM_ID").is_ok(); + + // Initialize QEMU + let mut args: Vec = env::args().collect(); + let mut env: Vec<(String, String)> = env::vars().collect(); + + //let (emu, asan) = init_with_asan(&mut args, &mut env).unwrap(); + let emu = Emulator::new(&mut args, &mut env).unwrap(); + + let mut elf_buffer = Vec::new(); + let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap(); + + let entry_point = elf + .entry_point(emu.load_addr()) + .expect("Entry point not found"); + + // Break at the entry point after the loading process + emu.set_breakpoint(entry_point); + unsafe { emu.run() }; + emu.remove_breakpoint(entry_point); + + // Now that the libs are loaded, build the intrumentation filter + let mut allow_list = vec![]; + for region in emu.mappings() { + if let Some(path) = region.path() { + if path.contains(emu.binary_path()) { + allow_list.push(region.start()..region.end()); + // println!("Instrument {:?} {:#x}-{:#x}", path, region.start(), region.end()); + } + } + } + + if in_afl { map_shared_memory(); } + + let mut hooks = QemuHooks::reproducer( + emu.clone(), + tuple_list!( + QemuEdgeCoverageClassicHelper::new(QemuFilterList::None) + ), + ); + + let input = BytesInput::new(vec![]); + + let mut test_harness = |_: &BytesInput| { + unsafe { emu.run() }; + ExitKind::Ok + }; + + if in_afl { start_forkserver(); } + + hooks.repro_run(&mut test_harness, &input); +} diff --git a/libafl_targets/src/forkserver.c b/libafl_targets/src/forkserver.c index 70d85c4dc7..c599ba87e9 100644 --- a/libafl_targets/src/forkserver.c +++ b/libafl_targets/src/forkserver.c @@ -67,8 +67,8 @@ uint8_t *__afl_fuzz_ptr; static uint32_t __afl_fuzz_len_local; uint32_t *__afl_fuzz_len = &__afl_fuzz_len_local; -int already_initialized_shm; -int already_initialized_forkserver; +static int already_initialized_shm; +static int already_initialized_forkserver; static int child_pid; static void (*old_sigterm_handler)(int) = 0; @@ -210,7 +210,15 @@ static void map_input_shared_memory() { /* Fork server logic. */ -void __afl_start_forkserver(void) { +struct libafl_forkserver_hooks { + void *data; + void (*pre_fork_hook)(void* data); + void (*post_fork_hook)(void* data, pid_t child_pid); + void (*iteration_start_hook)(void* data); + void (*iteration_end_hook)(void* data, int status); +}; + +void __libafl_start_forkserver(struct libafl_forkserver_hooks* hooks) { if (already_initialized_forkserver) return; already_initialized_forkserver = 1; @@ -312,6 +320,11 @@ void __afl_start_forkserver(void) { } if (!child_stopped) { + + if (hooks && hooks->pre_fork_hook) { + hooks->pre_fork_hook(hooks->data); + } + /* Once woken up, create a clone of our process. */ child_pid = fork(); @@ -320,6 +333,10 @@ void __afl_start_forkserver(void) { _exit(1); } + if (hooks && hooks->post_fork_hook) { + hooks->post_fork_hook(hooks->data, child_pid); + } + /* In child process: close fds, resume execution. */ if (!child_pid) { @@ -347,6 +364,10 @@ void __afl_start_forkserver(void) { write_error("write to afl-fuzz"); _exit(1); } + + if (hooks && hooks->iteration_start_hook) { + hooks->iteration_start_hook(hooks->data); + } if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) { write_error("waitpid"); @@ -359,6 +380,10 @@ void __afl_start_forkserver(void) { if (WIFSTOPPED(status)) child_stopped = 1; + if (hooks && hooks->iteration_end_hook) { + hooks->iteration_end_hook(hooks->data, status); + } + /* Relay wait status to pipe, then loop back. */ if (write(FORKSRV_FD + 1, &status, 4) != 4) { @@ -367,3 +392,7 @@ void __afl_start_forkserver(void) { } } } + +void __afl_start_forkserver(void) { + __libafl_start_forkserver(NULL); +} diff --git a/libafl_targets/src/forkserver.rs b/libafl_targets/src/forkserver.rs index ed9a595c6e..6a966f6add 100644 --- a/libafl_targets/src/forkserver.rs +++ b/libafl_targets/src/forkserver.rs @@ -1,10 +1,35 @@ //! Forkserver logic into targets +/// Trait to implement hooks executed in the forkserver +pub trait ForkserverHooks { + /// Execute before the fork that creates the child + extern "C" fn pre_fork(&mut self); + /// Executed after the fork (pid == 0 means child process) + extern "C" fn post_fork(&mut self, pid: libc::pid_t); + /// Execute in the parent after the iteration start in the child + extern "C" fn iter_start(&mut self); + /// Execute in the parent after the iteration end (by waitpid) in the child + extern "C" fn iter_end(&mut self, status: i32); +} + +#[repr(C)] +struct CForkserverHooks { + data: *mut H, + pre_fork_hook: extern "C" fn(*mut H), + post_fork_hook: extern "C" fn(*mut H, libc::pid_t), + iteration_start_hook: extern "C" fn(*mut H), + iteration_end_hook: extern "C" fn(*mut H, i32), +} + extern "C" { /// Map a shared memory region for the edge coverage map. fn __afl_map_shm(); /// Start the forkserver. fn __afl_start_forkserver(); + /// Start the forkserver with the hooks. + fn __libafl_start_forkserver(hooks: *mut ()); + /// Set persistent mode + fn __afl_set_persistent_mode(mode: u8); } /// Map a shared memory region for the edge coverage map. @@ -24,3 +49,36 @@ pub fn map_shared_memory() { pub fn start_forkserver() { unsafe { __afl_start_forkserver() } } + +/// Start the forkserver from this point. Any shared memory must be created before. +/// You must specify an object of a type implementing `ForkserverHooks`. +/// +/// # Note +/// +/// The forkserver logic is written in C and this code is a wrapper. +pub fn start_forkserver_with_hooks(hooks: &mut H) { + unsafe { + let mut c = CForkserverHooks { + data: hooks, + pre_fork_hook: core::mem::transmute(H::pre_fork as extern "C" fn(&mut H)), + post_fork_hook: core::mem::transmute( + H::post_fork as extern "C" fn(&mut H, libc::pid_t), + ), + iteration_start_hook: core::mem::transmute(H::iter_start as extern "C" fn(&mut H)), + iteration_end_hook: core::mem::transmute(H::iter_end as extern "C" fn(&mut H, i32)), + }; + + __libafl_start_forkserver(core::mem::transmute((&mut c) as *mut _)) + } +} + +/// Set the forkserver to persistent mode. +/// +/// # Note +/// +/// This sets a global behaviour +pub fn set_persistent_mode() { + unsafe { + __afl_set_persistent_mode(1); + } +}