Qemu Helpers and basic snapshotting (#310)

* store executor_ptr

* QemuHelpers

* working hooks and snapshot helper

* walk only the list of dirty pages on restore()

* mem hooks for snpashot

* brk snapshot

* snapshot method

* macos shit

* sugar and clippy
This commit is contained in:
Andrea Fioraldi 2021-10-01 12:17:28 +02:00 committed by GitHub
parent f63b862160
commit 90928d3d97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 908 additions and 171 deletions

View File

@ -40,8 +40,13 @@ use libafl::{
Error, Error,
}; };
use libafl_qemu::{ use libafl_qemu::{
amd64::Amd64Regs, elf::EasyElf, emu, filter_qemu_args, hooks, hooks::CmpLogObserver, MmapPerms, amd64::Amd64Regs,
QemuExecutor, elf::EasyElf,
emu, filter_qemu_args,
helpers::{QemuCmpLogHelper, QemuEdgeCoverageHelper, QemuSnapshotHelper},
hooks,
hooks::CmpLogObserver,
MmapPerms, QemuExecutor,
}; };
/// The fuzzer main /// The fuzzer main
@ -296,21 +301,13 @@ fn fuzz(
let executor = QemuExecutor::new( let executor = QemuExecutor::new(
&mut harness, &mut harness,
tuple_list!(QemuEdgeCoverageHelper::new(), QemuCmpLogHelper::new(), QemuSnapshotHelper::new()),
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),
&mut fuzzer, &mut fuzzer,
&mut state, &mut state,
&mut mgr, &mut mgr,
)?; )?;
// Track edge coverage
executor.hook_edge_generation(hooks::gen_unique_edge_ids);
executor.hook_edge_execution(hooks::trace_edge_hitcount);
// Track 2-4-8 cmps with CmpLog
executor.hook_cmp_generation(hooks::gen_unique_cmp_ids);
executor.hook_cmp8_execution(hooks::trace_cmp8_cmplog);
executor.hook_cmp4_execution(hooks::trace_cmp4_cmplog);
executor.hook_cmp2_execution(hooks::trace_cmp2_cmplog);
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time // 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 // Show the cmplog observer

View File

@ -79,10 +79,7 @@ where
&mut data.current_input_ptr, &mut data.current_input_ptr,
input as *const _ as *const c_void, input as *const _ as *const c_void,
); );
write_volatile( write_volatile(&mut data.executor_ptr, self as *const _ as *const c_void);
&mut data.observers_ptr,
&self.observers as *const _ as *const c_void,
);
data.crash_handler = self.crash_handler; data.crash_handler = self.crash_handler;
data.timeout_handler = self.timeout_handler; data.timeout_handler = self.timeout_handler;
// Direct raw pointers access /aliasing is pretty undefined behavior. // Direct raw pointers access /aliasing is pretty undefined behavior.
@ -99,10 +96,7 @@ where
&mut data.current_input_ptr, &mut data.current_input_ptr,
input as *const _ as *const c_void, input as *const _ as *const c_void,
); );
write_volatile( write_volatile(&mut data.executor_ptr, self as *const _ as *const c_void);
&mut data.observers_ptr,
&self.observers as *const _ as *const c_void,
);
data.crash_handler = self.crash_handler; data.crash_handler = self.crash_handler;
data.timeout_handler = self.timeout_handler; data.timeout_handler = self.timeout_handler;
// Direct raw pointers access /aliasing is pretty undefined behavior. // Direct raw pointers access /aliasing is pretty undefined behavior.
@ -182,9 +176,18 @@ where
Ok(Self { Ok(Self {
harness_fn, harness_fn,
observers, observers,
crash_handler: unix_signal_handler::inproc_crash_handler::<EM, I, OC, OF, OT, S, Z> crash_handler: unix_signal_handler::inproc_crash_handler::<
as *const _, Self,
EM,
I,
OC,
OF,
OT,
S,
Z,
> as *const _,
timeout_handler: unix_signal_handler::inproc_timeout_handler::< timeout_handler: unix_signal_handler::inproc_timeout_handler::<
Self,
EM, EM,
I, I,
OC, OC,
@ -206,6 +209,7 @@ where
harness_fn, harness_fn,
observers, observers,
crash_handler: windows_exception_handler::inproc_crash_handler::< crash_handler: windows_exception_handler::inproc_crash_handler::<
Self,
EM, EM,
I, I,
OC, OC,
@ -215,6 +219,7 @@ where
Z, Z,
> as *const _, > as *const _,
timeout_handler: windows_exception_handler::inproc_timeout_handler::< timeout_handler: windows_exception_handler::inproc_timeout_handler::<
Self,
EM, EM,
I, I,
OC, OC,
@ -254,7 +259,7 @@ pub struct InProcessExecutorHandlerData {
pub state_ptr: *mut c_void, pub state_ptr: *mut c_void,
pub event_mgr_ptr: *mut c_void, pub event_mgr_ptr: *mut c_void,
pub fuzzer_ptr: *mut c_void, pub fuzzer_ptr: *mut c_void,
pub observers_ptr: *const c_void, pub executor_ptr: *const c_void,
pub current_input_ptr: *const c_void, pub current_input_ptr: *const c_void,
pub crash_handler: *const c_void, pub crash_handler: *const c_void,
pub timeout_handler: *const c_void, pub timeout_handler: *const c_void,
@ -273,8 +278,8 @@ pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHan
event_mgr_ptr: ptr::null_mut(), event_mgr_ptr: ptr::null_mut(),
/// The fuzzer ptr for signal handling /// The fuzzer ptr for signal handling
fuzzer_ptr: ptr::null_mut(), fuzzer_ptr: ptr::null_mut(),
/// The observers ptr for signal handling /// The executor ptr for signal handling
observers_ptr: ptr::null(), executor_ptr: ptr::null(),
/// The current input for signal handling /// The current input for signal handling
current_input_ptr: ptr::null(), current_input_ptr: ptr::null(),
/// The crash handler fn /// The crash handler fn
@ -286,20 +291,25 @@ pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHan
}; };
#[must_use] #[must_use]
pub fn inprocess_run_state<'a, S>() -> Option<&'a mut S> { pub fn inprocess_get_state<'a, S>() -> Option<&'a mut S> {
unsafe { (GLOBAL_STATE.state_ptr as *mut S).as_mut() } unsafe { (GLOBAL_STATE.state_ptr as *mut S).as_mut() }
} }
#[must_use] #[must_use]
pub fn inprocess_run_event_manager<'a, EM>() -> Option<&'a mut EM> { pub fn inprocess_get_event_manager<'a, EM>() -> Option<&'a mut EM> {
unsafe { (GLOBAL_STATE.event_mgr_ptr as *mut EM).as_mut() } unsafe { (GLOBAL_STATE.event_mgr_ptr as *mut EM).as_mut() }
} }
#[must_use] #[must_use]
pub fn inprocess_run_event_fuzzer<'a, F>() -> Option<&'a mut F> { pub fn inprocess_get_fuzzer<'a, F>() -> Option<&'a mut F> {
unsafe { (GLOBAL_STATE.fuzzer_ptr as *mut F).as_mut() } unsafe { (GLOBAL_STATE.fuzzer_ptr as *mut F).as_mut() }
} }
#[must_use]
pub fn inprocess_get_executor<'a, E>() -> Option<&'a mut E> {
unsafe { (GLOBAL_STATE.executor_ptr as *mut E).as_mut() }
}
#[cfg(unix)] #[cfg(unix)]
mod unix_signal_handler { mod unix_signal_handler {
use alloc::vec::Vec; use alloc::vec::Vec;
@ -315,7 +325,7 @@ mod unix_signal_handler {
executors::{ executors::{
inprocess::{InProcessExecutorHandlerData, GLOBAL_STATE}, inprocess::{InProcessExecutorHandlerData, GLOBAL_STATE},
timeout::unix_remove_timeout, timeout::unix_remove_timeout,
ExitKind, ExitKind, HasObservers,
}, },
feedbacks::Feedback, feedbacks::Feedback,
fuzzer::HasObjective, fuzzer::HasObjective,
@ -374,12 +384,13 @@ mod unix_signal_handler {
} }
#[cfg(unix)] #[cfg(unix)]
pub unsafe fn inproc_timeout_handler<EM, I, OC, OF, OT, S, Z>( pub unsafe fn inproc_timeout_handler<E, EM, I, OC, OF, OT, S, Z>(
_signal: Signal, _signal: Signal,
_info: siginfo_t, _info: siginfo_t,
_context: &mut ucontext_t, _context: &mut ucontext_t,
data: &mut InProcessExecutorHandlerData, data: &mut InProcessExecutorHandlerData,
) where ) where
E: HasObservers<I, OT, S>,
EM: EventFirer<I, S> + EventRestarter<S>, EM: EventFirer<I, S> + EventRestarter<S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
OC: Corpus<I>, OC: Corpus<I>,
@ -391,7 +402,8 @@ mod unix_signal_handler {
let state = (data.state_ptr as *mut S).as_mut().unwrap(); let state = (data.state_ptr as *mut S).as_mut().unwrap();
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap();
let observers = (data.observers_ptr as *const OT).as_ref().unwrap(); let executor = (data.executor_ptr as *const E).as_ref().unwrap();
let observers = executor.observers();
if data.current_input_ptr.is_null() { if data.current_input_ptr.is_null() {
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -450,12 +462,13 @@ mod unix_signal_handler {
/// Will be used for signal handling. /// Will be used for signal handling.
/// It will store the current State to shmem, then exit. /// It will store the current State to shmem, then exit.
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub unsafe fn inproc_crash_handler<EM, I, OC, OF, OT, S, Z>( pub unsafe fn inproc_crash_handler<E, EM, I, OC, OF, OT, S, Z>(
signal: Signal, signal: Signal,
_info: siginfo_t, _info: siginfo_t,
_context: &mut ucontext_t, _context: &mut ucontext_t,
data: &mut InProcessExecutorHandlerData, data: &mut InProcessExecutorHandlerData,
) where ) where
E: HasObservers<I, OT, S>,
EM: EventFirer<I, S> + EventRestarter<S>, EM: EventFirer<I, S> + EventRestarter<S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
OC: Corpus<I>, OC: Corpus<I>,
@ -506,7 +519,8 @@ mod unix_signal_handler {
let state = (data.state_ptr as *mut S).as_mut().unwrap(); let state = (data.state_ptr as *mut S).as_mut().unwrap();
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap();
let observers = (data.observers_ptr as *const OT).as_ref().unwrap(); let executor = (data.executor_ptr as *const E).as_ref().unwrap();
let observers = executor.observers();
#[cfg(feature = "std")] #[cfg(feature = "std")]
println!("Child crashed!"); println!("Child crashed!");
@ -629,7 +643,7 @@ mod windows_exception_handler {
executors::{ executors::{
inprocess::{InProcessExecutorHandlerData, GLOBAL_STATE}, inprocess::{InProcessExecutorHandlerData, GLOBAL_STATE},
timeout::windows_delete_timer_queue, timeout::windows_delete_timer_queue,
ExitKind, ExitKind, HasObservers,
}, },
feedbacks::Feedback, feedbacks::Feedback,
fuzzer::HasObjective, fuzzer::HasObjective,
@ -665,10 +679,11 @@ mod windows_exception_handler {
} }
} }
pub unsafe extern "system" fn inproc_timeout_handler<EM, I, OC, OF, OT, S, Z>( pub unsafe extern "system" fn inproc_timeout_handler<E, EM, I, OC, OF, OT, S, Z>(
global_state: *mut c_void, global_state: *mut c_void,
_p1: u8, _p1: u8,
) where ) where
E: HasObservers<I, OT, S>,
EM: EventFirer<I, S> + EventRestarter<S>, EM: EventFirer<I, S> + EventRestarter<S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
OC: Corpus<I>, OC: Corpus<I>,
@ -683,7 +698,8 @@ mod windows_exception_handler {
let state = (data.state_ptr as *mut S).as_mut().unwrap(); let state = (data.state_ptr as *mut S).as_mut().unwrap();
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap();
let observers = (data.observers_ptr as *const OT).as_ref().unwrap(); let executor = (data.executor_ptr as *const E).as_ref().unwrap();
let observers = executor.observers();
if data.current_input_ptr.is_null() { if data.current_input_ptr.is_null() {
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -738,11 +754,12 @@ mod windows_exception_handler {
// println!("TIMER INVOKED!"); // println!("TIMER INVOKED!");
} }
pub unsafe fn inproc_crash_handler<EM, I, OC, OF, OT, S, Z>( pub unsafe fn inproc_crash_handler<E, EM, I, OC, OF, OT, S, Z>(
code: ExceptionCode, code: ExceptionCode,
exception_pointers: *mut EXCEPTION_POINTERS, exception_pointers: *mut EXCEPTION_POINTERS,
data: &mut InProcessExecutorHandlerData, data: &mut InProcessExecutorHandlerData,
) where ) where
E: HasObservers<I, OT, S>,
EM: EventFirer<I, S> + EventRestarter<S>, EM: EventFirer<I, S> + EventRestarter<S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
OC: Corpus<I>, OC: Corpus<I>,
@ -789,7 +806,8 @@ mod windows_exception_handler {
let state = (data.state_ptr as *mut S).as_mut().unwrap(); let state = (data.state_ptr as *mut S).as_mut().unwrap();
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap();
let observers = (data.observers_ptr as *const OT).as_ref().unwrap(); let executor = (data.executor_ptr as *const E).as_ref().unwrap();
let observers = executor.observers();
#[cfg(feature = "std")] #[cfg(feature = "std")]
println!("Child crashed!"); println!("Child crashed!");

View File

@ -1,13 +1,9 @@
#[cfg(not(feature = "python"))] use std::{env, fs, path::Path, process::Command};
use std::fs::copy;
#[cfg(feature = "python")]
use std::fs::read_dir;
use std::{env, path::Path, process::Command};
use which::which; use which::which;
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
const QEMU_REVISION: &str = "22daaa7d0c76b32f8391bad40c0b220f3e659f66"; const QEMU_REVISION: &str = "6065cb8a84b305146d37ae540926bac439fc5601";
fn build_dep_check(tools: &[&str]) { fn build_dep_check(tools: &[&str]) {
for tool in tools { for tool in tools {
@ -42,7 +38,15 @@ fn main() {
build_dep_check(&["git", "make"]); 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);
if qemu_rev.exists()
&& fs::read_to_string(&qemu_rev).expect("Failed to read QEMU_REVISION") != QEMU_REVISION
{
fs::remove_dir_all(&qemu_path).unwrap();
}
if !qemu_path.is_dir() { if !qemu_path.is_dir() {
println!( println!(
"cargo:warning=Qemu not found, cloning with git ({})...", "cargo:warning=Qemu not found, cloning with git ({})...",
@ -60,6 +64,7 @@ fn main() {
.arg(QEMU_REVISION) .arg(QEMU_REVISION)
.status() .status()
.unwrap(); .unwrap();
fs::write(&qemu_rev, QEMU_REVISION).unwrap();
} }
let build_dir = qemu_path.join("build"); let build_dir = qemu_path.join("build");
@ -164,7 +169,7 @@ fn main() {
//build_dir.join("libhwcore.fa.p"), //build_dir.join("libhwcore.fa.p"),
//build_dir.join("libcapstone.a.p"), //build_dir.join("libcapstone.a.p"),
] { ] {
for path in read_dir(dir).unwrap() { for path in fs::read_dir(dir).unwrap() {
let path = path.unwrap().path(); let path = path.unwrap().path();
if path.is_file() { if path.is_file() {
if let Some(name) = path.file_name() { if let Some(name) = path.file_name() {
@ -224,7 +229,7 @@ fn main() {
#[cfg(not(feature = "python"))] #[cfg(not(feature = "python"))]
{ {
copy( fs::copy(
build_dir.join(&format!("libqemu-{}.so", cpu_target)), build_dir.join(&format!("libqemu-{}.so", cpu_target)),
target_dir.join(&format!("libqemu-{}.so", cpu_target)), target_dir.join(&format!("libqemu-{}.so", cpu_target)),
) )

View File

@ -20,6 +20,7 @@ pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)] #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
#[repr(i32)] #[repr(i32)]
pub enum MmapPerms { pub enum MmapPerms {
None = 0,
Read = libc::PROT_READ, Read = libc::PROT_READ,
Write = libc::PROT_WRITE, Write = libc::PROT_WRITE,
Execute = libc::PROT_EXEC, Execute = libc::PROT_EXEC,
@ -29,6 +30,41 @@ pub enum MmapPerms {
ReadWriteExecute = libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC, ReadWriteExecute = libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
} }
impl MmapPerms {
#[must_use]
pub fn is_r(&self) -> bool {
matches!(
self,
MmapPerms::Read
| MmapPerms::ReadWrite
| MmapPerms::ReadExecute
| MmapPerms::ReadWriteExecute
)
}
#[must_use]
pub fn is_w(&self) -> bool {
matches!(
self,
MmapPerms::Write
| MmapPerms::ReadWrite
| MmapPerms::WriteExecute
| MmapPerms::ReadWriteExecute
)
}
#[must_use]
pub fn is_x(&self) -> bool {
matches!(
self,
MmapPerms::Execute
| MmapPerms::ReadExecute
| MmapPerms::WriteExecute
| MmapPerms::ReadWriteExecute
)
}
}
#[cfg(feature = "python")] #[cfg(feature = "python")]
impl IntoPy<PyObject> for MmapPerms { impl IntoPy<PyObject> for MmapPerms {
fn into_py(self, py: Python) -> PyObject { fn into_py(self, py: Python) -> PyObject {
@ -144,6 +180,8 @@ extern "C" {
fn libafl_qemu_remove_breakpoint(addr: u64) -> i32; fn libafl_qemu_remove_breakpoint(addr: u64) -> i32;
fn libafl_qemu_run() -> i32; fn libafl_qemu_run() -> i32;
fn libafl_load_addr() -> u64; fn libafl_load_addr() -> u64;
fn libafl_get_brk() -> u64;
fn libafl_set_brk(brk: u64) -> u64;
fn strlen(s: *const u8) -> usize; fn strlen(s: *const u8) -> usize;
@ -383,6 +421,15 @@ pub fn load_addr() -> u64 {
unsafe { libafl_load_addr() } unsafe { libafl_load_addr() }
} }
#[must_use]
pub fn get_brk() -> u64 {
unsafe { libafl_get_brk() }
}
pub fn set_brk(brk: u64) {
unsafe { libafl_set_brk(brk) };
}
pub fn map_private(addr: u64, size: usize, perms: MmapPerms) -> Result<u64, String> { pub fn map_private(addr: u64, size: usize, perms: MmapPerms) -> Result<u64, String> {
let res = unsafe { let res = unsafe {
target_mmap( target_mmap(
@ -409,6 +456,8 @@ pub fn unmap(addr: u64, size: usize) -> Result<(), String> {
} }
} }
// TODO add has_X_hook() and panic when setting a hook for the second time
pub fn set_exec_edge_hook(hook: extern "C" fn(id: u64)) { pub fn set_exec_edge_hook(hook: extern "C" fn(id: u64)) {
unsafe { unsafe {
libafl_exec_edge_hook = hook; libafl_exec_edge_hook = hook;
@ -499,6 +548,7 @@ pub fn set_exec_write_n_hook(hook: extern "C" fn(id: u64, addr: u64, size: u32))
} }
} }
// TODO add pc arg
pub fn set_gen_write_hook(hook: extern "C" fn(size: u32) -> u64) { pub fn set_gen_write_hook(hook: extern "C" fn(size: u32) -> u64) {
unsafe { unsafe {
libafl_gen_write_hook = hook; libafl_gen_write_hook = hook;

View File

@ -3,7 +3,9 @@ use core::{ffi::c_void, mem::transmute, ptr};
use libafl::{ use libafl::{
corpus::Corpus, corpus::Corpus,
events::{EventFirer, EventRestarter}, events::{EventFirer, EventRestarter},
executors::{inprocess::GLOBAL_STATE, Executor, ExitKind, HasObservers, InProcessExecutor}, executors::{
inprocess::inprocess_get_state, Executor, ExitKind, HasObservers, InProcessExecutor,
},
feedbacks::Feedback, feedbacks::Feedback,
fuzzer::HasObjective, fuzzer::HasObjective,
inputs::Input, inputs::Input,
@ -13,56 +15,306 @@ use libafl::{
}; };
pub use crate::emu::SyscallHookResult; pub use crate::emu::SyscallHookResult;
use crate::{emu, emu::SKIP_EXEC_HOOK}; use crate::{emu, emu::SKIP_EXEC_HOOK, helpers::QemuHelperTuple};
static mut QEMU_HELPERS_PTR: *const c_void = ptr::null();
static mut GEN_EDGE_HOOK_PTR: *const c_void = ptr::null(); static mut GEN_EDGE_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_edge_hook_wrapper<I, QT, S>(src: u64, dst: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, u64, u64) -> Option<u64> = transmute(GEN_EDGE_HOOK_PTR);
(func)(helpers, state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut EDGE_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn edge_hooks_wrapper<I, QT, S>(id: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &EDGE_HOOKS } {
let func: fn(&mut QT, &mut S, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id);
}
}
static mut GEN_BLOCK_HOOK_PTR: *const c_void = ptr::null(); static mut GEN_BLOCK_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_block_hook_wrapper<I, QT, S>(pc: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, u64) -> Option<u64> = transmute(GEN_EDGE_HOOK_PTR);
(func)(helpers, state, pc).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut BLOCK_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn block_hooks_wrapper<I, QT, S>(id: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &BLOCK_HOOKS } {
let func: fn(&mut QT, &mut S, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id);
}
}
static mut GEN_READ_HOOK_PTR: *const c_void = ptr::null(); static mut GEN_READ_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_read_hook_wrapper<I, QT, S>(size: u32) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, usize) -> Option<u64> = transmute(GEN_READ_HOOK_PTR);
(func)(helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut GEN_WRITE_HOOK_PTR: *const c_void = ptr::null(); static mut GEN_WRITE_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_write_hook_wrapper<I, QT, S>(size: u32) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, usize) -> Option<u64> = transmute(GEN_WRITE_HOOK_PTR);
(func)(helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut READ1_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read1_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &READ1_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
}
}
static mut READ2_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read2_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &READ2_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
}
}
static mut READ4_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read4_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &READ4_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
}
}
static mut READ8_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read8_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &READ8_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
}
}
static mut READ_N_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read_n_hooks_wrapper<I, QT, S>(id: u64, addr: u64, size: u32)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &READ_N_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr, size as usize);
}
}
static mut WRITE1_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write1_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &WRITE1_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
}
}
static mut WRITE2_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write2_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &WRITE2_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
}
}
static mut WRITE4_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write4_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &WRITE4_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
}
}
static mut WRITE8_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write8_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &WRITE8_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
}
}
static mut WRITE_N_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write_n_hooks_wrapper<I, QT, S>(id: u64, addr: u64, size: u32)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &WRITE_N_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr, size as usize);
}
}
static mut GEN_CMP_HOOK_PTR: *const c_void = ptr::null(); static mut GEN_CMP_HOOK_PTR: *const c_void = ptr::null();
static mut SYSCALL_HOOK_PTR: *const c_void = ptr::null(); extern "C" fn gen_cmp_hook_wrapper<I, QT, S>(pc: u64, size: u32) -> u64
where
extern "C" fn gen_edge_hook_wrapper<S>(src: u64, dst: u64) -> u64 { I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe { unsafe {
let state = (GLOBAL_STATE.state_ptr as *mut S).as_mut().unwrap(); let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let func: fn(&mut S, u64, u64) -> Option<u64> = transmute(GEN_EDGE_HOOK_PTR); let state = inprocess_get_state::<S>().unwrap();
(func)(state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id) let func: fn(&mut QT, &mut S, u64, usize) -> Option<u64> = transmute(GEN_CMP_HOOK_PTR);
(func)(helpers, state, pc, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
} }
} }
extern "C" fn gen_block_hook_wrapper<S>(pc: u64) -> u64 { static mut CMP1_HOOKS: Vec<*const c_void> = vec![];
unsafe { extern "C" fn cmp1_hooks_wrapper<I, QT, S>(id: u64, v0: u8, v1: u8)
let state = (GLOBAL_STATE.state_ptr as *mut S).as_mut().unwrap(); where
let func: fn(&mut S, u64) -> Option<u64> = transmute(GEN_BLOCK_HOOK_PTR); I: Input,
(func)(state, pc).map_or(SKIP_EXEC_HOOK, |id| id) QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &CMP1_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u8, u8) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1);
} }
} }
extern "C" fn gen_read_hook_wrapper<S>(size: u32) -> u64 { static mut CMP2_HOOKS: Vec<*const c_void> = vec![];
unsafe { extern "C" fn cmp2_hooks_wrapper<I, QT, S>(id: u64, v0: u16, v1: u16)
let state = (GLOBAL_STATE.state_ptr as *mut S).as_mut().unwrap(); where
let func: fn(&mut S, usize) -> Option<u64> = transmute(GEN_READ_HOOK_PTR); I: Input,
(func)(state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &CMP2_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u16, u16) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1);
} }
} }
extern "C" fn gen_write_hook_wrapper<S>(size: u32) -> u64 { static mut CMP4_HOOKS: Vec<*const c_void> = vec![];
unsafe { extern "C" fn cmp4_hooks_wrapper<I, QT, S>(id: u64, v0: u32, v1: u32)
let state = (GLOBAL_STATE.state_ptr as *mut S).as_mut().unwrap(); where
let func: fn(&mut S, usize) -> Option<u64> = transmute(GEN_WRITE_HOOK_PTR); I: Input,
(func)(state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &CMP4_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u32, u32) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1);
} }
} }
extern "C" fn gen_cmp_hook_wrapper<S>(pc: u64, size: u32) -> u64 { static mut CMP8_HOOKS: Vec<*const c_void> = vec![];
unsafe { extern "C" fn cmp8_hooks_wrapper<I, QT, S>(id: u64, v0: u64, v1: u64)
let state = (GLOBAL_STATE.state_ptr as *mut S).as_mut().unwrap(); where
let func: fn(&mut S, u64, usize) -> Option<u64> = transmute(GEN_CMP_HOOK_PTR); I: Input,
(func)(state, pc, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
for hook in unsafe { &CMP8_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1);
} }
} }
extern "C" fn gen_syscall_hook_wrapper<S>( static mut SYSCALL_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn syscall_hooks_wrapper<I, QT, S>(
sys_num: i32, sys_num: i32,
a0: u64, a0: u64,
a1: u64, a1: u64,
@ -72,32 +324,58 @@ extern "C" fn gen_syscall_hook_wrapper<S>(
a5: u64, a5: u64,
a6: u64, a6: u64,
a7: u64, a7: u64,
) -> SyscallHookResult { ) -> SyscallHookResult
unsafe { where
let state = (GLOBAL_STATE.state_ptr as *mut S).as_mut().unwrap(); I: Input,
let func: fn(&mut S, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult = QT: QemuHelperTuple<I, S>,
transmute(SYSCALL_HOOK_PTR); {
(func)(state, sys_num, a0, a1, a2, a3, a4, a5, a6, a7) let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let mut res = SyscallHookResult::new(None);
for hook in unsafe { &SYSCALL_HOOKS } {
let func: fn(
&mut QT,
&mut S,
i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> SyscallHookResult = unsafe { transmute(*hook) };
let r = (func)(helpers, state, sys_num, a0, a1, a2, a3, a4, a5, a6, a7);
if r.skip_syscall {
res.skip_syscall = true;
res.retval = r.retval;
}
} }
res
} }
pub struct QemuExecutor<'a, H, I, OT, S> pub struct QemuExecutor<'a, H, I, OT, QT, S>
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,
I: Input, I: Input,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
{ {
helpers: QT,
inner: InProcessExecutor<'a, H, I, OT, S>, inner: InProcessExecutor<'a, H, I, OT, S>,
} }
impl<'a, H, I, OT, S> QemuExecutor<'a, H, I, OT, S> impl<'a, H, I, OT, QT, S> QemuExecutor<'a, H, I, OT, QT, S>
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,
I: Input, I: Input,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
{ {
pub fn new<EM, OC, OF, Z>( pub fn new<EM, OC, OF, Z>(
harness_fn: &'a mut H, harness_fn: &'a mut H,
helpers: QT,
observers: OT, observers: OT,
fuzzer: &mut Z, fuzzer: &mut Z,
state: &mut S, state: &mut S,
@ -110,9 +388,12 @@ where
S: HasSolutions<OC, I> + HasClientPerfStats, S: HasSolutions<OC, I> + HasClientPerfStats,
Z: HasObjective<I, OF, S>, Z: HasObjective<I, OF, S>,
{ {
Ok(Self { let slf = Self {
helpers,
inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?, inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?,
}) };
slf.helpers.init_all(&slf);
Ok(slf)
} }
pub fn inner(&self) -> &InProcessExecutor<'a, H, I, OT, S> { pub fn inner(&self) -> &InProcessExecutor<'a, H, I, OT, S> {
@ -124,129 +405,191 @@ where
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_edge_generation(&self, hook: fn(&mut S, src: u64, dest: u64) -> Option<u64>) { pub fn hook_edge_generation(
&self,
hook: fn(&mut QT, &mut S, src: u64, dest: u64) -> Option<u64>,
) {
unsafe { unsafe {
GEN_EDGE_HOOK_PTR = hook as *const _; GEN_EDGE_HOOK_PTR = hook as *const _;
} }
emu::set_gen_edge_hook(gen_edge_hook_wrapper::<S>); emu::set_gen_edge_hook(gen_edge_hook_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_edge_execution(&self, hook: extern "C" fn(id: u64)) { pub fn hook_edge_execution(&self, hook: fn(&mut QT, &mut S, id: u64)) {
emu::set_exec_edge_hook(hook); unsafe {
EDGE_HOOKS.push(hook as *const _);
}
emu::set_exec_edge_hook(edge_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_block_generation(&self, hook: fn(&mut S, pc: u64) -> Option<u64>) { pub fn hook_block_generation(&self, hook: fn(&mut QT, &mut S, pc: u64) -> Option<u64>) {
unsafe { unsafe {
GEN_BLOCK_HOOK_PTR = hook as *const _; GEN_BLOCK_HOOK_PTR = hook as *const _;
} }
emu::set_gen_block_hook(gen_block_hook_wrapper::<S>); emu::set_gen_block_hook(gen_block_hook_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_block_execution(&self, hook: extern "C" fn(id: u64)) { pub fn hook_block_execution(&self, hook: fn(&mut QT, &mut S, id: u64)) {
emu::set_exec_block_hook(hook); unsafe {
BLOCK_HOOKS.push(hook as *const _);
}
emu::set_exec_block_hook(block_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read_generation(&self, hook: fn(&mut S, size: usize) -> Option<u64>) { pub fn hook_read_generation(&self, hook: fn(&mut QT, &mut S, size: usize) -> Option<u64>) {
unsafe { unsafe {
GEN_READ_HOOK_PTR = hook as *const _; GEN_READ_HOOK_PTR = hook as *const _;
} }
emu::set_gen_read_hook(gen_read_hook_wrapper::<S>); emu::set_gen_read_hook(gen_read_hook_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read1_execution(&self, hook: extern "C" fn(id: u64, addr: u64)) { pub fn hook_read1_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) {
emu::set_exec_read1_hook(hook); unsafe {
READ1_HOOKS.push(hook as *const _);
}
emu::set_exec_read1_hook(read1_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read2_execution(&self, hook: extern "C" fn(id: u64, addr: u64)) { pub fn hook_read2_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) {
emu::set_exec_read2_hook(hook); unsafe {
READ2_HOOKS.push(hook as *const _);
}
emu::set_exec_read2_hook(read2_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read4_execution(&self, hook: extern "C" fn(id: u64, addr: u64)) { pub fn hook_read4_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) {
emu::set_exec_read4_hook(hook); unsafe {
READ4_HOOKS.push(hook as *const _);
}
emu::set_exec_read4_hook(read4_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read8_execution(&self, hook: extern "C" fn(id: u64, addr: u64)) { pub fn hook_read8_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) {
emu::set_exec_read8_hook(hook); unsafe {
READ8_HOOKS.push(hook as *const _);
}
emu::set_exec_read8_hook(read8_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read_n_execution(&self, hook: extern "C" fn(id: u64, addr: u64, size: u32)) { pub fn hook_read_n_execution(
emu::set_exec_read_n_hook(hook); &self,
hook: fn(&mut QT, &mut S, id: u64, addr: u64, size: usize),
) {
unsafe {
READ_N_HOOKS.push(hook as *const _);
}
emu::set_exec_read_n_hook(read_n_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write_generation(&self, hook: fn(&mut S, size: usize) -> Option<u64>) { pub fn hook_write_generation(&self, hook: fn(&mut QT, &mut S, size: usize) -> Option<u64>) {
unsafe { unsafe {
GEN_WRITE_HOOK_PTR = hook as *const _; GEN_WRITE_HOOK_PTR = hook as *const _;
} }
emu::set_gen_write_hook(gen_write_hook_wrapper::<S>); emu::set_gen_write_hook(gen_write_hook_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write1_execution(&self, hook: extern "C" fn(id: u64, addr: u64)) { pub fn hook_write1_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) {
emu::set_exec_write1_hook(hook); unsafe {
WRITE1_HOOKS.push(hook as *const _);
}
emu::set_exec_write1_hook(write1_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write2_execution(&self, hook: extern "C" fn(id: u64, addr: u64)) { pub fn hook_write2_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) {
emu::set_exec_write2_hook(hook); unsafe {
WRITE2_HOOKS.push(hook as *const _);
}
emu::set_exec_write2_hook(write2_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write4_execution(&self, hook: extern "C" fn(id: u64, addr: u64)) { pub fn hook_write4_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) {
emu::set_exec_write4_hook(hook); unsafe {
WRITE4_HOOKS.push(hook as *const _);
}
emu::set_exec_write4_hook(write4_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write8_execution(&self, hook: extern "C" fn(id: u64, addr: u64)) { pub fn hook_write8_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) {
emu::set_exec_write8_hook(hook); unsafe {
WRITE8_HOOKS.push(hook as *const _);
}
emu::set_exec_write8_hook(write8_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write_n_execution(&self, hook: extern "C" fn(id: u64, addr: u64, size: u32)) { pub fn hook_write_n_execution(
emu::set_exec_write_n_hook(hook); &self,
hook: fn(&mut QT, &mut S, id: u64, addr: u64, size: usize),
) {
unsafe {
WRITE_N_HOOKS.push(hook as *const _);
}
emu::set_exec_write_n_hook(write_n_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_cmp_generation(&self, hook: fn(&mut S, pc: u64, size: usize) -> Option<u64>) { pub fn hook_cmp_generation(
&self,
hook: fn(&mut QT, &mut S, pc: u64, size: usize) -> Option<u64>,
) {
unsafe { unsafe {
GEN_CMP_HOOK_PTR = hook as *const _; GEN_CMP_HOOK_PTR = hook as *const _;
} }
emu::set_gen_cmp_hook(gen_cmp_hook_wrapper::<S>); emu::set_gen_cmp_hook(gen_cmp_hook_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_cmp1_execution(&self, hook: extern "C" fn(id: u64, v0: u8, v1: u8)) { pub fn hook_cmp1_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u8, v1: u8)) {
emu::set_exec_cmp1_hook(hook); unsafe {
CMP1_HOOKS.push(hook as *const _);
}
emu::set_exec_cmp1_hook(cmp1_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_cmp2_execution(&self, hook: extern "C" fn(id: u64, v0: u16, v1: u16)) { pub fn hook_cmp2_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u16, v1: u16)) {
emu::set_exec_cmp2_hook(hook); unsafe {
CMP2_HOOKS.push(hook as *const _);
}
emu::set_exec_cmp2_hook(cmp2_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_cmp4_execution(&self, hook: extern "C" fn(id: u64, v0: u32, v1: u32)) { pub fn hook_cmp4_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u32, v1: u32)) {
emu::set_exec_cmp4_hook(hook); unsafe {
CMP4_HOOKS.push(hook as *const _);
}
emu::set_exec_cmp4_hook(cmp4_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_cmp8_execution(&self, hook: extern "C" fn(id: u64, v0: u64, v1: u64)) { pub fn hook_cmp8_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u64, v1: u64)) {
emu::set_exec_cmp8_hook(hook); unsafe {
CMP8_HOOKS.push(hook as *const _);
}
emu::set_exec_cmp8_hook(cmp8_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_syscalls( pub fn hook_syscalls(
&self, &self,
hook: extern "C" fn( hook: fn(
&mut QT,
&mut S,
sys_num: i32, sys_num: i32,
u64, u64,
u64, u64,
@ -257,27 +600,20 @@ where
u64, u64,
u64, u64,
) -> SyscallHookResult, ) -> SyscallHookResult,
) {
emu::set_syscall_hook(hook);
}
#[allow(clippy::unused_self)]
pub fn hook_syscalls_state(
&self,
hook: fn(&mut S, sys_num: i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult,
) { ) {
unsafe { unsafe {
SYSCALL_HOOK_PTR = hook as *const _; SYSCALL_HOOKS.push(hook as *const _);
} }
emu::set_syscall_hook(gen_syscall_hook_wrapper::<S>); emu::set_syscall_hook(syscall_hooks_wrapper::<I, QT, S>);
} }
} }
impl<'a, EM, H, I, OT, S, Z> Executor<EM, I, S, Z> for QemuExecutor<'a, H, I, OT, S> impl<'a, EM, H, I, OT, QT, S, Z> Executor<EM, I, S, Z> for QemuExecutor<'a, H, I, OT, QT, S>
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,
I: Input, I: Input,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
{ {
fn run_target( fn run_target(
&mut self, &mut self,
@ -286,15 +622,21 @@ where
mgr: &mut EM, mgr: &mut EM,
input: &I, input: &I,
) -> Result<ExitKind, Error> { ) -> Result<ExitKind, Error> {
self.inner.run_target(fuzzer, state, mgr, input) unsafe { QEMU_HELPERS_PTR = &self.helpers as *const _ as *const c_void };
self.helpers.pre_exec_all(input);
let r = self.inner.run_target(fuzzer, state, mgr, input);
self.helpers.post_exec_all(input);
unsafe { QEMU_HELPERS_PTR = ptr::null() };
r
} }
} }
impl<'a, H, I, OT, S> HasObservers<I, OT, S> for QemuExecutor<'a, H, I, OT, S> impl<'a, H, I, OT, QT, S> HasObservers<I, OT, S> for QemuExecutor<'a, H, I, OT, QT, S>
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,
I: Input, I: Input,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
{ {
#[inline] #[inline]
fn observers(&self) -> &OT { fn observers(&self) -> &OT {

261
libafl_qemu/src/helpers.rs Normal file
View File

@ -0,0 +1,261 @@
use std::collections::HashMap;
use libafl::{
bolts::tuples::MatchFirstType, executors::ExitKind, inputs::Input, observers::ObserversTuple,
state::HasMetadata,
};
use crate::{emu, emu::GuestMaps, executor::QemuExecutor, hooks};
// TODO remove 'static when specialization will be stable
pub trait QemuHelper<I, S>: 'static
where
I: Input,
{
fn init<'a, H, OT, QT>(&self, _executor: &QemuExecutor<'a, H, I, OT, QT, S>)
where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
{
}
fn pre_exec(&mut self, _input: &I) {}
fn post_exec(&mut self, _input: &I) {}
}
pub trait QemuHelperTuple<I, S>: MatchFirstType
where
I: Input,
{
fn init_all<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>)
where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>;
fn pre_exec_all(&mut self, input: &I);
fn post_exec_all(&mut self, input: &I);
}
impl<I, S> QemuHelperTuple<I, S> for ()
where
I: Input,
{
fn init_all<'a, H, OT, QT>(&self, _executor: &QemuExecutor<'a, H, I, OT, QT, S>)
where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
{
}
fn pre_exec_all(&mut self, _input: &I) {}
fn post_exec_all(&mut self, _input: &I) {}
}
impl<Head, Tail, I, S> QemuHelperTuple<I, S> for (Head, Tail)
where
Head: QemuHelper<I, S>,
Tail: QemuHelperTuple<I, S>,
I: Input,
{
fn init_all<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>)
where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
{
self.0.init(executor);
self.1.init_all(executor);
}
fn pre_exec_all(&mut self, input: &I) {
self.0.pre_exec(input);
self.1.pre_exec_all(input);
}
fn post_exec_all(&mut self, input: &I) {
self.0.post_exec(input);
self.1.post_exec_all(input);
}
}
pub struct QemuEdgeCoverageHelper {}
impl QemuEdgeCoverageHelper {
#[must_use]
pub fn new() -> Self {
Self {}
}
}
impl<I, S> QemuHelper<I, S> for QemuEdgeCoverageHelper
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<I, S>,
QT: QemuHelperTuple<I, S>,
{
executor.hook_edge_generation(hooks::gen_unique_edge_ids::<I, QT, S>);
emu::set_exec_edge_hook(hooks::trace_edge_hitcount);
}
}
pub struct QemuCmpLogHelper {}
impl QemuCmpLogHelper {
#[must_use]
pub fn new() -> Self {
Self {}
}
}
impl<I, S> QemuHelper<I, S> for QemuCmpLogHelper
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<I, S>,
QT: QemuHelperTuple<I, S>,
{
executor.hook_cmp_generation(hooks::gen_unique_cmp_ids::<I, QT, S>);
emu::set_exec_cmp8_hook(hooks::trace_cmp8_cmplog);
emu::set_exec_cmp4_hook(hooks::trace_cmp4_cmplog);
emu::set_exec_cmp2_hook(hooks::trace_cmp2_cmplog);
emu::set_exec_cmp1_hook(hooks::trace_cmp1_cmplog);
}
}
pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
pub struct SnapshotPageInfo {
pub addr: u64,
pub dirty: bool,
pub data: [u8; SNAPSHOT_PAGE_SIZE],
}
// TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html
pub struct QemuSnapshotHelper {
pub access_cache: [u64; 4],
pub access_cache_idx: usize,
pub pages: HashMap<u64, SnapshotPageInfo>,
pub dirty: Vec<u64>,
pub brk: u64,
pub empty: bool,
}
impl QemuSnapshotHelper {
#[must_use]
pub fn new() -> Self {
Self {
access_cache: [u64::MAX; 4],
access_cache_idx: 0,
pages: HashMap::default(),
dirty: vec![],
brk: 0,
empty: true,
}
}
pub fn snapshot(&mut self) {
self.brk = emu::get_brk();
self.pages.clear();
for map in GuestMaps::new() {
// TODO track all the pages OR track mproctect
if !map.flags().is_w() {
continue;
}
let mut addr = map.start();
while addr < map.end() {
let mut info = SnapshotPageInfo {
addr,
dirty: false,
data: [0; SNAPSHOT_PAGE_SIZE],
};
emu::read_mem(addr, &mut info.data);
self.pages.insert(addr, info);
addr += SNAPSHOT_PAGE_SIZE as u64;
}
}
self.empty = false;
}
pub fn page_access(&mut self, page: u64) {
if self.access_cache[0] == page
|| self.access_cache[1] == page
|| self.access_cache[2] == page
|| self.access_cache[3] == page
{
return;
}
self.access_cache[self.access_cache_idx] = page;
self.access_cache_idx = (self.access_cache_idx + 1) & 3;
if let Some(info) = self.pages.get_mut(&page) {
if info.dirty {
return;
}
info.dirty = true;
}
self.dirty.push(page);
}
pub fn access(&mut self, addr: u64, size: usize) {
debug_assert!(size > 0);
let page = addr & (SNAPSHOT_PAGE_SIZE as u64 - 1);
self.page_access(page);
let second_page = (addr + size as u64 - 1) & (SNAPSHOT_PAGE_SIZE as u64 - 1);
if page != second_page {
self.page_access(second_page);
}
}
pub fn reset(&mut self) {
self.access_cache = [u64::MAX; 4];
self.access_cache_idx = 0;
while let Some(page) = self.dirty.pop() {
if let Some(info) = self.pages.get_mut(&page) {
emu::write_mem(page, &info.data);
info.dirty = false;
}
}
emu::set_brk(self.brk);
}
}
impl<I, S> QemuHelper<I, S> for QemuSnapshotHelper
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<I, S>,
QT: QemuHelperTuple<I, S>,
{
executor.hook_write8_execution(hooks::trace_write8_snapshot::<I, QT, S>);
executor.hook_write4_execution(hooks::trace_write4_snapshot::<I, QT, S>);
executor.hook_write2_execution(hooks::trace_write2_snapshot::<I, QT, S>);
executor.hook_write1_execution(hooks::trace_write1_snapshot::<I, QT, S>);
executor.hook_write_n_execution(hooks::trace_write_n_snapshot::<I, QT, S>);
}
fn pre_exec(&mut self, _input: &I) {
if self.empty {
self.snapshot();
} else {
self.reset();
}
}
}

View File

@ -2,12 +2,14 @@ use core::{cell::UnsafeCell, cmp::max};
use hashbrown::HashMap; use hashbrown::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use libafl::state::HasMetadata; use libafl::{inputs::Input, state::HasMetadata};
pub use libafl_targets::{ pub use libafl_targets::{
cmplog::__libafl_targets_cmplog_instructions, CmpLogObserver, CMPLOG_MAP, CMPLOG_MAP_W, cmplog::__libafl_targets_cmplog_instructions, CmpLogObserver, CMPLOG_MAP, CMPLOG_MAP_W,
EDGES_MAP, EDGES_MAP_SIZE, MAX_EDGES_NUM, EDGES_MAP, EDGES_MAP_SIZE, MAX_EDGES_NUM,
}; };
use crate::helpers::{QemuHelperTuple, QemuSnapshotHelper};
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
pub struct QemuEdgesMapMetadata { pub struct QemuEdgesMapMetadata {
pub map: HashMap<(u64, u64), u64>, pub map: HashMap<(u64, u64), u64>,
@ -53,7 +55,12 @@ fn hash_me(mut x: u64) -> u64 {
x x
} }
pub fn gen_unique_edge_ids<S>(state: &mut S, src: u64, dest: u64) -> Option<u64> pub fn gen_unique_edge_ids<I, QT, S>(
_helpers: &mut QT,
state: &mut S,
src: u64,
dest: u64,
) -> Option<u64>
where where
S: HasMetadata, S: HasMetadata,
{ {
@ -76,7 +83,12 @@ where
} }
} }
pub fn gen_hashed_edge_ids<S>(_state: &mut S, src: u64, dest: u64) -> Option<u64> { pub fn gen_hashed_edge_ids<I, QT, S>(
_helpers: &mut QT,
_state: &mut S,
src: u64,
dest: u64,
) -> Option<u64> {
Some(hash_me(src) ^ hash_me(dest)) Some(hash_me(src) ^ hash_me(dest))
} }
@ -92,11 +104,11 @@ pub extern "C" fn trace_edge_single(id: u64) {
} }
} }
pub fn gen_addr_block_ids<S>(_state: &mut S, pc: u64) -> Option<u64> { pub fn gen_addr_block_ids<I, QT, S>(_helpers: &mut QT, _state: &mut S, pc: u64) -> Option<u64> {
Some(pc) Some(pc)
} }
pub fn gen_hashed_block_ids<S>(_state: &mut S, pc: u64) -> Option<u64> { pub fn gen_hashed_block_ids<I, QT, S>(_helpers: &mut QT, _state: &mut S, pc: u64) -> Option<u64> {
Some(hash_me(pc)) Some(hash_me(pc))
} }
@ -120,7 +132,12 @@ pub extern "C" fn trace_block_transition_single(id: u64) {
} }
} }
pub fn gen_unique_cmp_ids<S>(state: &mut S, pc: u64, _size: usize) -> Option<u64> pub fn gen_unique_cmp_ids<I, QT, S>(
_helpers: &mut QT,
state: &mut S,
pc: u64,
_size: usize,
) -> Option<u64>
where where
S: HasMetadata, S: HasMetadata,
{ {
@ -163,3 +180,63 @@ pub extern "C" fn trace_cmp8_cmplog(id: u64, v0: u64, v1: u64) {
__libafl_targets_cmplog_instructions(id as usize, 8, v0, v1); __libafl_targets_cmplog_instructions(id as usize, 8, v0, v1);
} }
} }
pub fn trace_write1_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
h.access(addr, 1);
}
pub fn trace_write2_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
h.access(addr, 2);
}
pub fn trace_write4_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
h.access(addr, 4);
}
pub fn trace_write8_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
h.access(addr, 8);
}
pub fn trace_write_n_snapshot<I, QT, S>(
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
size: usize,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
h.access(addr, size);
}

View File

@ -4,6 +4,7 @@ pub mod amd64;
pub mod x86; pub mod x86;
pub mod elf; pub mod elf;
#[cfg(target_os = "linux")]
pub mod hooks; pub mod hooks;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -16,6 +17,11 @@ pub mod emu;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub use emu::*; pub use emu::*;
#[cfg(target_os = "linux")]
pub mod helpers;
#[cfg(target_os = "linux")]
pub use helpers::*;
#[must_use] #[must_use]
pub fn filter_qemu_args() -> Vec<String> { pub fn filter_qemu_args() -> Vec<String> {
let mut args = vec![env::args().next().unwrap()]; let mut args = vec![env::args().next().unwrap()];
@ -114,6 +120,10 @@ pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
Err(PyValueError::new_err("Invalid perms")) Err(PyValueError::new_err("Invalid perms"))
} }
} }
#[pyfn(m)]
fn unmap(addr: u64, size: usize) -> PyResult<()> {
emu::unmap(addr, size).map_err(PyValueError::new_err)
}
extern "C" fn py_syscall_hook_wrapper( extern "C" fn py_syscall_hook_wrapper(
sys_num: i32, sys_num: i32,

View File

@ -30,7 +30,7 @@ use libafl::{
}; };
pub use libafl_qemu::emu; pub use libafl_qemu::emu;
use libafl_qemu::{hooks, QemuExecutor}; use libafl_qemu::{hooks, QemuEdgeCoverageHelper, QemuExecutor};
use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS}; use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS};
@ -66,21 +66,6 @@ where
/// Bytes harness /// Bytes harness
#[builder(setter(strip_option))] #[builder(setter(strip_option))]
harness: Option<H>, harness: Option<H>,
// Syscall hook
#[builder(default = None, setter(strip_option))]
syscall_hook: Option<
extern "C" fn(
sys_num: i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> emu::SyscallHookResult,
>,
} }
impl<'a, H> QemuBytesCoverageSugar<'a, H> impl<'a, H> QemuBytesCoverageSugar<'a, H>
@ -178,21 +163,13 @@ where
let executor = QemuExecutor::new( let executor = QemuExecutor::new(
&mut harness, &mut harness,
tuple_list!(QemuEdgeCoverageHelper::new()),
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),
&mut fuzzer, &mut fuzzer,
&mut state, &mut state,
&mut mgr, &mut mgr,
)?; )?;
// Track edge coverage
executor.hook_edge_generation(hooks::gen_unique_edge_ids);
executor.hook_edge_execution(hooks::trace_edge_hitcount);
// Hook the syscalls
if let Some(hook) = self.syscall_hook {
executor.hook_syscalls(hook);
}
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time // Create the executor for an in-process function with one observer for edge coverage and one for the execution time
let mut executor = TimeoutExecutor::new(executor, timeout); let mut executor = TimeoutExecutor::new(executor, timeout);