From 20c32316eb4fc9743d43bad5386cc7d163e11e13 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 7 Mar 2023 13:16:51 +0100 Subject: [PATCH] Define custom collectors for QemuCallTracerHelper (#1099) * Define custom collectors for QemuCallTracerHelper and create OnCrashBacktraceCollector * fmt * clippy --- libafl/src/observers/stacktrace.rs | 17 +- libafl_qemu/src/asan.rs | 14 +- libafl_qemu/src/calls.rs | 644 +++++++++++++++++++++-------- libafl_qemu/src/drcov.rs | 14 +- libafl_qemu/src/emu.rs | 2 +- libafl_qemu/src/executor.rs | 18 +- libafl_qemu/src/helper.rs | 46 ++- libafl_qemu/src/hooks.rs | 4 +- 8 files changed, 567 insertions(+), 192 deletions(-) diff --git a/libafl/src/observers/stacktrace.rs b/libafl/src/observers/stacktrace.rs index 552ed7116c..708d980ca9 100644 --- a/libafl/src/observers/stacktrace.rs +++ b/libafl/src/observers/stacktrace.rs @@ -52,6 +52,8 @@ pub enum HarnessType { InProcess, /// Harness type when the target is a child process Child, + /// Harness type with an external component filling the backtrace hash (e.g. `CrashBacktraceCollector` in `libafl_qemu`) + External, } /// An observer looking at the backtrace after the harness crashes @@ -87,6 +89,17 @@ impl<'a> BacktraceObserver<'a> { fn clear_hash(&mut self) { *self.hash.as_mut() = None; } + + /// Fill the hash value if the harness type is external + pub fn fill_external(&mut self, hash: u64, exit_kind: &ExitKind) { + if self.harness_type == HarnessType::External { + if *exit_kind == ExitKind::Crash { + self.update_hash(hash); + } else { + self.clear_hash(); + } + } + } } impl<'a> ObserverWithHashField for BacktraceObserver<'a> { @@ -108,7 +121,7 @@ where exit_kind: &ExitKind, ) -> Result<(), Error> { if self.harness_type == HarnessType::InProcess { - if exit_kind == &ExitKind::Crash { + if *exit_kind == ExitKind::Crash { self.update_hash(collect_backtrace()); } else { self.clear_hash(); @@ -124,7 +137,7 @@ where exit_kind: &ExitKind, ) -> Result<(), Error> { if self.harness_type == HarnessType::Child { - if exit_kind == &ExitKind::Crash { + if *exit_kind == ExitKind::Crash { self.update_hash(collect_backtrace()); } else { self.clear_hash(); diff --git a/libafl_qemu/src/asan.rs b/libafl_qemu/src/asan.rs index 4dd2028f6e..5f2ca8305f 100644 --- a/libafl_qemu/src/asan.rs +++ b/libafl_qemu/src/asan.rs @@ -6,7 +6,9 @@ use std::{ sync::Mutex, }; -use libafl::{executors::ExitKind, inputs::UsesInput, state::HasMetadata}; +use libafl::{ + executors::ExitKind, inputs::UsesInput, observers::ObserversTuple, state::HasMetadata, +}; use libc::{ c_void, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE, }; @@ -730,7 +732,15 @@ where } } - fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, exit_kind: &mut ExitKind) { + fn post_exec( + &mut self, + emulator: &Emulator, + _input: &S::Input, + _observers: &mut OT, + exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + { if self.reset(emulator) == AsanRollback::HasLeaks { *exit_kind = ExitKind::Crash; } diff --git a/libafl_qemu/src/calls.rs b/libafl_qemu/src/calls.rs index 8a073e251b..54d7ed8f6c 100644 --- a/libafl_qemu/src/calls.rs +++ b/libafl_qemu/src/calls.rs @@ -1,27 +1,234 @@ +use core::fmt::Debug; + use capstone::prelude::*; -use libafl::inputs::UsesInput; +use libafl::{ + bolts::tuples::{MatchFirstType, Named}, + executors::ExitKind, + inputs::{Input, UsesInput}, + observers::{stacktrace::BacktraceObserver, ObserversTuple}, +}; use crate::{ capstone, + emu::Emulator, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, hooks::QemuHooks, - Emulator, GuestAddr, Regs, + GuestAddr, Regs, }; -#[derive(Debug)] -pub struct QemuCallTracerHelper { - filter: QemuInstrumentationFilter, - cs: Capstone, - callstack: Vec, +pub trait CallTraceCollector: 'static + Debug { + fn on_call( + &mut self, + hooks: &mut QemuHooks<'_, QT, S>, + state: Option<&mut S>, + pc: GuestAddr, + call_len: usize, + ) where + S: UsesInput, + QT: QemuHelperTuple; + + fn on_ret( + &mut self, + hooks: &mut QemuHooks<'_, QT, S>, + state: Option<&mut S>, + pc: GuestAddr, + ret_addr: GuestAddr, + ) where + S: UsesInput, + QT: QemuHelperTuple; + + // Frowarded from the `QemuCallTracerHelper` + fn pre_exec(&mut self, _emulator: &Emulator, _input: &I) + where + I: Input, + { + } + + fn post_exec( + &mut self, + _emulator: &Emulator, + _input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + S: UsesInput, + { + } } -impl QemuCallTracerHelper { +pub trait CallTraceCollectorTuple: 'static + MatchFirstType + Debug { + fn on_call_all( + &mut self, + hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + pc: GuestAddr, + call_len: usize, + ) where + S: UsesInput, + QT: QemuHelperTuple; + + fn on_ret_all( + &mut self, + hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + _pc: GuestAddr, + ret_addr: GuestAddr, + ) where + S: UsesInput, + QT: QemuHelperTuple; + + fn pre_exec_all(&mut self, _emulator: &Emulator, input: &I) + where + I: Input; + + fn post_exec_all( + &mut self, + _emulator: &Emulator, + input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + S: UsesInput; +} + +impl CallTraceCollectorTuple for () { + fn on_call_all( + &mut self, + _hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + _pc: GuestAddr, + _call_len: usize, + ) where + S: UsesInput, + QT: QemuHelperTuple, + { + } + + fn on_ret_all( + &mut self, + _hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + _pc: GuestAddr, + _ret_addr: GuestAddr, + ) where + S: UsesInput, + QT: QemuHelperTuple, + { + } + + fn pre_exec_all(&mut self, _emulator: &Emulator, _input: &I) + where + I: Input, + { + } + + fn post_exec_all( + &mut self, + _emulator: &Emulator, + _input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + S: UsesInput, + { + } +} + +impl CallTraceCollectorTuple for (Head, Tail) +where + Head: CallTraceCollector, + Tail: CallTraceCollectorTuple, +{ + fn on_call_all( + &mut self, + hooks: &mut QemuHooks<'_, QT, S>, + mut state: Option<&mut S>, + pc: GuestAddr, + call_len: usize, + ) where + S: UsesInput, + QT: QemuHelperTuple, + { + self.0.on_call( + hooks, + match state.as_mut() { + Some(s) => Some(*s), + None => None, + }, + pc, + call_len, + ); + self.1.on_call_all(hooks, state, pc, call_len); + } + + fn on_ret_all( + &mut self, + hooks: &mut QemuHooks<'_, QT, S>, + mut state: Option<&mut S>, + pc: GuestAddr, + ret_addr: GuestAddr, + ) where + S: UsesInput, + QT: QemuHelperTuple, + { + self.0.on_ret( + hooks, + match state.as_mut() { + Some(s) => Some(*s), + None => None, + }, + pc, + ret_addr, + ); + self.1.on_ret_all(hooks, state, pc, ret_addr); + } + + fn pre_exec_all(&mut self, emulator: &Emulator, input: &I) + where + I: Input, + { + self.0.pre_exec(emulator, input); + self.1.pre_exec_all(emulator, input); + } + + fn post_exec_all( + &mut self, + emulator: &Emulator, + input: &S::Input, + observers: &mut OT, + exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + S: UsesInput, + { + self.0.post_exec(emulator, input, observers, exit_kind); + self.1.post_exec_all(emulator, input, observers, exit_kind); + } +} + +#[derive(Debug)] +pub struct QemuCallTracerHelper +where + T: CallTraceCollectorTuple, +{ + filter: QemuInstrumentationFilter, + cs: Capstone, + collectors: Option, +} + +impl QemuCallTracerHelper +where + T: CallTraceCollectorTuple, +{ #[must_use] - pub fn new(filter: QemuInstrumentationFilter) -> Self { + pub fn new(filter: QemuInstrumentationFilter, collectors: T) -> Self { Self { filter, cs: capstone().detail(true).build().unwrap(), - callstack: vec![], + collectors: Some(collectors), } } @@ -30,181 +237,280 @@ impl QemuCallTracerHelper { self.filter.allowed(addr) } - #[must_use] - pub fn callstack(&self) -> &[GuestAddr] { - &self.callstack + fn on_ret(hooks: &mut QemuHooks<'_, QT, S>, state: Option<&mut S>, pc: GuestAddr) + where + S: UsesInput, + QT: QemuHelperTuple, + { + #[cfg(cpu_target = "x86_64")] + let ret_addr = { + let emu = hooks.emulator(); + let stack_ptr: GuestAddr = emu.read_reg(Regs::Rsp).unwrap(); + let mut ret_addr = [0; 8]; + unsafe { emu.read_mem(stack_ptr, &mut ret_addr) }; + GuestAddr::from_le_bytes(ret_addr) + }; + + #[cfg(cpu_target = "i386")] + let ret_addr = { + let emu = hooks.emulator(); + let stack_ptr: GuestAddr = emu.read_reg(Regs::Esp).unwrap(); + let mut ret_addr = [0; 4]; + unsafe { emu.read_mem(stack_ptr, &mut ret_addr) }; + GuestAddr::from_le_bytes(ret_addr) + }; + + #[cfg(any(cpu_target = "arm", cpu_target = "aarch64"))] + let ret_addr = { + let emu = hooks.emulator(); + let ret_addr: GuestAddr = emu.read_reg(Regs::Lr).unwrap(); + ret_addr + }; + + #[cfg(cpu_target = "mips")] + let ret_addr = { + let emu = hooks.emulator(); + let ret_addr: GuestAddr = emu.read_reg(Regs::Ra).unwrap(); + ret_addr + }; + + // log::info!("RET @ 0x{:#x}", ret_addr); + + let mut collectors = if let Some(h) = hooks.helpers_mut().match_first_type_mut::() { + h.collectors.take() + } else { + return; + }; + collectors + .as_mut() + .unwrap() + .on_ret_all(hooks, state, pc, ret_addr); + hooks + .helpers_mut() + .match_first_type_mut::() + .unwrap() + .collectors = collectors; } - pub fn reset(&mut self) { - self.callstack.clear(); + fn gen_blocks_calls( + hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + pc: GuestAddr, + ) -> Option + where + S: UsesInput, + QT: QemuHelperTuple, + { + let emu = hooks.emulator(); + if let Some(h) = hooks.helpers().match_first_type::() { + if !h.must_instrument(pc) { + return None; + } + + #[allow(unused_mut)] + let mut code = { + #[cfg(emulation_mode = "usermode")] + unsafe { + std::slice::from_raw_parts(emu.g2h(pc), 512) + } + #[cfg(emulation_mode = "systemmode")] + &mut [0; 512] + }; + #[cfg(emulation_mode = "systemmode")] + unsafe { + emu.read_mem(pc, code) + }; // TODO handle faults + + let mut iaddr = pc; + + 'disasm: while let Ok(insns) = h.cs.disasm_count(code, iaddr.into(), 1) { + if insns.is_empty() { + break; + } + let insn = insns.first().unwrap(); + let insn_detail: InsnDetail = h.cs.insn_detail(insn).unwrap(); + for detail in insn_detail.groups() { + match u32::from(detail.0) { + capstone::InsnGroupType::CS_GRP_CALL => { + let call_len = insn.bytes().len(); + // TODO do not use a closure, find a more efficient way to pass call_len + let call_cb = move |hooks: &mut QemuHooks<'_, QT, S>, + state: Option<&mut S>, + pc| { + // eprintln!("CALL @ 0x{:#x}", pc + call_len); + let mut collectors = if let Some(h) = + hooks.helpers_mut().match_first_type_mut::() + { + h.collectors.take() + } else { + return; + }; + collectors + .as_mut() + .unwrap() + .on_call_all(hooks, state, pc, call_len); + hooks + .helpers_mut() + .match_first_type_mut::() + .unwrap() + .collectors = collectors; + }; + unsafe { + hooks.instruction_closure( + insn.address() as GuestAddr, + Box::new(call_cb), + false, + ); + } + } + capstone::InsnGroupType::CS_GRP_RET => { + hooks.instruction(insn.address() as GuestAddr, Self::on_ret, false); + break 'disasm; + } + capstone::InsnGroupType::CS_GRP_INVALID + | capstone::InsnGroupType::CS_GRP_JUMP + | capstone::InsnGroupType::CS_GRP_IRET + | capstone::InsnGroupType::CS_GRP_PRIVILEGE => { + break 'disasm; + } + _ => {} + } + } + + iaddr += insn.bytes().len() as GuestAddr; + + #[cfg(emulation_mode = "usermode")] + unsafe { + code = std::slice::from_raw_parts(emu.g2h(iaddr), 512); + } + #[cfg(emulation_mode = "systemmode")] + unsafe { + emu.read_mem(pc, code); + } // TODO handle faults + } + } + + None } } -impl Default for QemuCallTracerHelper { - fn default() -> Self { - Self::new(QemuInstrumentationFilter::None) - } -} - -impl QemuHelper for QemuCallTracerHelper +impl QemuHelper for QemuCallTracerHelper where S: UsesInput, + T: CallTraceCollectorTuple, { - fn init_hooks(&self, hooks: &QemuHooks<'_, QT, S>) + fn first_exec(&self, hooks: &QemuHooks<'_, QT, S>) where QT: QemuHelperTuple, { - hooks.blocks(Some(gen_blocks_calls::), None); + hooks.blocks(Some(Self::gen_blocks_calls::), None); } - fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) { + fn pre_exec(&mut self, emulator: &Emulator, input: &S::Input) { + self.collectors + .as_mut() + .unwrap() + .pre_exec_all(emulator, input); + } + + fn post_exec( + &mut self, + emulator: &Emulator, + input: &S::Input, + observers: &mut OT, + exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + { + self.collectors + .as_mut() + .unwrap() + .post_exec_all(emulator, input, observers, exit_kind); + } +} + +#[derive(Debug)] +pub struct OnCrashBacktraceCollector { + callstack_hash: u64, + observer_name: String, +} + +impl OnCrashBacktraceCollector { + #[must_use] + pub fn new(observer: &BacktraceObserver<'_>) -> Self { + Self { + callstack_hash: 0, + observer_name: observer.name().to_string(), + } + } + + #[must_use] + pub fn with_name(observer_name: String) -> Self { + Self { + callstack_hash: 0, + observer_name, + } + } + + #[must_use] + pub fn callstack_hash(&self) -> u64 { + self.callstack_hash + } + + pub fn reset(&mut self) { + self.callstack_hash = 0; + } +} + +impl CallTraceCollector for OnCrashBacktraceCollector { + #[allow(clippy::unnecessary_cast)] + fn on_call( + &mut self, + _hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + pc: GuestAddr, + call_len: usize, + ) where + S: UsesInput, + QT: QemuHelperTuple, + { + self.callstack_hash ^= pc as u64 + call_len as u64; + } + + #[allow(clippy::unnecessary_cast)] + fn on_ret( + &mut self, + _hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + _pc: GuestAddr, + ret_addr: GuestAddr, + ) where + S: UsesInput, + QT: QemuHelperTuple, + { + self.callstack_hash ^= ret_addr as u64; + } + + fn pre_exec(&mut self, _emulator: &Emulator, _input: &I) + where + I: Input, + { self.reset(); } -} -/*pub fn on_call(hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, pc: GuestAddr) -where - - QT: QemuHelperTuple, -{ -}*/ - -pub fn on_ret(hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _pc: GuestAddr) -where - S: UsesInput, - QT: QemuHelperTuple, -{ - #[cfg(cpu_target = "x86_64")] - let ret_addr = { - let emu = hooks.emulator(); - let stack_ptr: GuestAddr = emu.read_reg(Regs::Rsp).unwrap(); - let mut ret_addr = [0; 8]; - unsafe { emu.read_mem(stack_ptr, &mut ret_addr) }; - GuestAddr::from_le_bytes(ret_addr) - }; - - #[cfg(cpu_target = "i386")] - let ret_addr = { - let emu = hooks.emulator(); - let stack_ptr: GuestAddr = emu.read_reg(Regs::Esp).unwrap(); - let mut ret_addr = [0; 4]; - unsafe { emu.read_mem(stack_ptr, &mut ret_addr) }; - GuestAddr::from_le_bytes(ret_addr) - }; - - #[cfg(any(cpu_target = "arm", cpu_target = "aarch64"))] - let ret_addr = { - let emu = hooks.emulator(); - let ret_addr: GuestAddr = emu.read_reg(Regs::Lr).unwrap(); - ret_addr - }; - - #[cfg(cpu_target = "mips")] - let ret_addr = { - let emu = hooks.emulator(); - let ret_addr: GuestAddr = emu.read_reg(Regs::Ra).unwrap(); - ret_addr - }; - - // log::info!("RET @ 0x{:#x}", ret_addr); - - if let Some(h) = hooks - .helpers_mut() - .match_first_type_mut::() + fn post_exec( + &mut self, + _emulator: &Emulator, + _input: &S::Input, + observers: &mut OT, + exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + S: UsesInput, { - while let Some(addr) = h.callstack.pop() { - if addr == ret_addr { - break; - } - } + let observer = observers + .match_name_mut::>(&self.observer_name) + .expect("A OnCrashBacktraceCollector needs a BacktraceObserver"); + observer.fill_external(self.callstack_hash, exit_kind); } } - -pub fn gen_blocks_calls( - hooks: &mut QemuHooks<'_, QT, S>, - _state: Option<&mut S>, - pc: GuestAddr, -) -> Option -where - S: UsesInput, - QT: QemuHelperTuple, -{ - let emu = hooks.emulator(); - if let Some(h) = hooks.helpers().match_first_type::() { - if !h.must_instrument(pc) { - return None; - } - - #[allow(unused_mut)] - let mut code = { - #[cfg(emulation_mode = "usermode")] - unsafe { - std::slice::from_raw_parts(emu.g2h(pc), 512) - } - #[cfg(emulation_mode = "systemmode")] - &mut [0; 512] - }; - #[cfg(emulation_mode = "systemmode")] - unsafe { - emu.read_mem(pc, code) - }; // TODO handle faults - - let mut iaddr = pc; - - 'disasm: while let Ok(insns) = h.cs.disasm_count(code, iaddr.into(), 1) { - if insns.is_empty() { - break; - } - let insn = insns.first().unwrap(); - let insn_detail: InsnDetail = h.cs.insn_detail(insn).unwrap(); - for detail in insn_detail.groups() { - match u32::from(detail.0) { - capstone::InsnGroupType::CS_GRP_CALL => { - // hooks.instruction_closure(insn.address() as GuestAddr, on_call, false); - let call_len = insn.bytes().len() as GuestAddr; - let call_cb = move |hooks: &mut QemuHooks<'_, QT, S>, _, pc| { - // eprintln!("CALL @ 0x{:#x}", pc + call_len); - if let Some(h) = hooks - .helpers_mut() - .match_first_type_mut::() - { - h.callstack.push(pc + call_len); - } - }; - unsafe { - hooks.instruction_closure( - insn.address() as GuestAddr, - Box::new(call_cb), - false, - ); - } - } - capstone::InsnGroupType::CS_GRP_RET => { - hooks.instruction(insn.address() as GuestAddr, on_ret, false); - break 'disasm; - } - capstone::InsnGroupType::CS_GRP_INVALID - | capstone::InsnGroupType::CS_GRP_JUMP - | capstone::InsnGroupType::CS_GRP_IRET - | capstone::InsnGroupType::CS_GRP_PRIVILEGE => { - break 'disasm; - } - _ => {} - } - } - - iaddr += insn.bytes().len() as GuestAddr; - - #[cfg(emulation_mode = "usermode")] - unsafe { - code = std::slice::from_raw_parts(emu.g2h(iaddr), 512); - } - #[cfg(emulation_mode = "systemmode")] - unsafe { - emu.read_mem(pc, code); - } // TODO handle faults - } - } - - None -} diff --git a/libafl_qemu/src/drcov.rs b/libafl_qemu/src/drcov.rs index 085cb00a6a..2bf72df58f 100644 --- a/libafl_qemu/src/drcov.rs +++ b/libafl_qemu/src/drcov.rs @@ -1,7 +1,9 @@ use std::{path::PathBuf, sync::Mutex}; use hashbrown::{hash_map::Entry, HashMap}; -use libafl::{executors::ExitKind, inputs::UsesInput, state::HasMetadata}; +use libafl::{ + executors::ExitKind, inputs::UsesInput, observers::ObserversTuple, state::HasMetadata, +}; use libafl_targets::drcov::{DrCovBasicBlock, DrCovWriter}; use rangemap::RangeMap; use serde::{Deserialize, Serialize}; @@ -84,7 +86,15 @@ where fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) {} - fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _exit_kind: &mut ExitKind) { + fn post_exec( + &mut self, + emulator: &Emulator, + _input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + { if self.full_trace { if DRCOV_IDS.lock().unwrap().as_ref().unwrap().len() > self.drcov_len { let mut drcov_vec = Vec::::new(); diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index c3fb065acd..f3cc4e29a4 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -463,7 +463,7 @@ impl Drop for GuestMaps { } #[repr(C)] -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct FatPtr(pub *const c_void, pub *const c_void); static mut GDB_COMMANDS: Vec = vec![]; diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 27094ed991..0666a71667 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -126,9 +126,12 @@ where } self.hooks.helpers_mut().pre_exec_all(&emu, input); let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?; - self.hooks - .helpers_mut() - .post_exec_all(&emu, input, &mut exit_kind); + self.hooks.helpers_mut().post_exec_all( + &emu, + input, + self.inner.observers_mut(), + &mut exit_kind, + ); Ok(exit_kind) } } @@ -288,9 +291,12 @@ where } self.hooks.helpers_mut().pre_exec_all(&emu, input); let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?; - self.hooks - .helpers_mut() - .post_exec_all(&emu, input, &mut exit_kind); + self.hooks.helpers_mut().post_exec_all( + &emu, + input, + self.inner.observers_mut(), + &mut exit_kind, + ); Ok(exit_kind) } } diff --git a/libafl_qemu/src/helper.rs b/libafl_qemu/src/helper.rs index bb3f1da344..d1ce2e3de0 100644 --- a/libafl_qemu/src/helper.rs +++ b/libafl_qemu/src/helper.rs @@ -1,6 +1,9 @@ use core::{fmt::Debug, ops::Range}; -use libafl::{bolts::tuples::MatchFirstType, executors::ExitKind, inputs::UsesInput}; +use libafl::{ + bolts::tuples::MatchFirstType, executors::ExitKind, inputs::UsesInput, + observers::ObserversTuple, +}; use crate::{ emu::{Emulator, GuestAddr}, @@ -29,7 +32,16 @@ where fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) {} - fn post_exec(&mut self, _emulator: &Emulator, _input: &S::Input, _exit_kind: &mut ExitKind) {} + fn post_exec( + &mut self, + _emulator: &Emulator, + _input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + { + } } pub trait QemuHelperTuple: MatchFirstType + Debug @@ -48,7 +60,14 @@ where fn pre_exec_all(&mut self, _emulator: &Emulator, input: &S::Input); - fn post_exec_all(&mut self, _emulator: &Emulator, input: &S::Input, _exit_kind: &mut ExitKind); + fn post_exec_all( + &mut self, + _emulator: &Emulator, + input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple; } impl QemuHelperTuple for () @@ -71,12 +90,15 @@ where fn pre_exec_all(&mut self, _emulator: &Emulator, _input: &S::Input) {} - fn post_exec_all( + fn post_exec_all( &mut self, _emulator: &Emulator, _input: &S::Input, + _observers: &mut OT, _exit_kind: &mut ExitKind, - ) { + ) where + OT: ObserversTuple, + { } } @@ -109,9 +131,17 @@ where self.1.pre_exec_all(emulator, input); } - fn post_exec_all(&mut self, emulator: &Emulator, input: &S::Input, exit_kind: &mut ExitKind) { - self.0.post_exec(emulator, input, exit_kind); - self.1.post_exec_all(emulator, input, exit_kind); + fn post_exec_all( + &mut self, + emulator: &Emulator, + input: &S::Input, + observers: &mut OT, + exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + { + self.0.post_exec(emulator, input, observers, exit_kind); + self.1.post_exec_all(emulator, input, observers, exit_kind); } } diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index 1cc81e169e..dee439d3b8 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -19,8 +19,8 @@ use crate::{ }; // all kinds of hooks -#[derive(Clone, Copy, PartialEq, Eq)] -enum Hook { +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(crate) enum Hook { Function(*const c_void), Closure(FatPtr), #[cfg(emulation_mode = "usermode")]