From b324e88631c34cd608b4d72d8174bd1fdf8d31c9 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Mon, 18 Nov 2024 19:47:14 +0100 Subject: [PATCH] Pre init module hooks (#2704) * differenciate pre qemu init and post qemu init hooks * api breakage: Emulator::new_with_qemu is not public anymore. --- libafl_qemu/src/emu/builder.rs | 25 +++++--- libafl_qemu/src/emu/hooks.rs | 39 +++++++------ libafl_qemu/src/emu/mod.rs | 58 +++++++++++++------ libafl_qemu/src/modules/calls.rs | 2 +- libafl_qemu/src/modules/drcov.rs | 2 +- libafl_qemu/src/modules/mod.rs | 45 +++++++++++--- libafl_qemu/src/modules/usermode/asan.rs | 2 +- .../src/modules/usermode/injections.rs | 2 +- libafl_qemu/src/modules/usermode/snapshot.rs | 2 +- 9 files changed, 120 insertions(+), 57 deletions(-) diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs index b5fe8171a4..67396d2c10 100644 --- a/libafl_qemu/src/emu/builder.rs +++ b/libafl_qemu/src/emu/builder.rs @@ -12,8 +12,8 @@ use crate::{ command::{CommandManager, NopCommandManager, StdCommandManager}, config::QemuConfig, modules::{EmulatorModule, EmulatorModuleTuple}, - Emulator, NopEmulatorDriver, NopSnapshotManager, Qemu, QemuInitError, StdEmulatorDriver, - StdSnapshotManager, + Emulator, EmulatorHooks, NopEmulatorDriver, NopSnapshotManager, Qemu, QemuHooks, QemuInitError, + StdEmulatorDriver, StdSnapshotManager, }; #[derive(Clone, Debug)] @@ -118,6 +118,10 @@ where { let qemu_builder = self.qemu_builder.ok_or(QemuInitError::EmptyArgs)?; + let mut emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; + + self.modules.pre_qemu_init_all(&mut emulator_hooks); + let qemu: Qemu = match qemu_builder { QemuBuilder::Qemu(qemu) => qemu, QemuBuilder::QemuConfig(qemu_config) => { @@ -127,13 +131,16 @@ where QemuBuilder::QemuString(qemu_string) => Qemu::init(&qemu_string)?, }; - Emulator::new_with_qemu( - qemu, - self.modules, - self.driver, - self.snapshot_manager, - self.command_manager, - ) + unsafe { + Ok(Emulator::new_with_qemu( + qemu, + emulator_hooks, + self.modules, + self.driver, + self.snapshot_manager, + self.command_manager, + )) + } } } diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs index 65666ff42f..bb07522b60 100644 --- a/libafl_qemu/src/emu/hooks.rs +++ b/libafl_qemu/src/emu/hooks.rs @@ -64,7 +64,7 @@ macro_rules! hook_to_repr { }; } -static mut EMULATOR_TOOLS: *mut () = ptr::null_mut(); +static mut EMULATOR_MODULES: *mut () = ptr::null_mut(); #[cfg(feature = "usermode")] pub extern "C" fn crash_hook_wrapper(target_sig: i32) @@ -919,14 +919,14 @@ where pub unsafe fn emulator_modules_mut_unchecked<'a>() -> &'a mut EmulatorModules { #[cfg(debug_assertions)] { - (EMULATOR_TOOLS as *mut EmulatorModules) + (EMULATOR_MODULES as *mut EmulatorModules) .as_mut() .unwrap() } #[cfg(not(debug_assertions))] { - &mut *(EMULATOR_TOOLS as *mut EmulatorModules) + &mut *(EMULATOR_MODULES as *mut EmulatorModules) } } @@ -940,7 +940,7 @@ where /// generic use (it will suppose they are the same as the ones used at initialization time). #[must_use] pub unsafe fn emulator_modules_mut<'a>() -> Option<&'a mut EmulatorModules> { - unsafe { (EMULATOR_TOOLS as *mut EmulatorModules).as_mut() } + unsafe { (EMULATOR_MODULES as *mut EmulatorModules).as_mut() } } } @@ -1101,11 +1101,15 @@ where ET: EmulatorModuleTuple, S: UsesInput + Unpin, { - pub(super) fn new(qemu: Qemu, modules: ET) -> Pin> { + pub(super) fn new( + qemu: Qemu, + emulator_hooks: EmulatorHooks, + modules: ET, + ) -> Pin> { let mut modules = Box::pin(Self { qemu, modules: Box::pin(modules), - hooks: EmulatorHooks::default(), + hooks: emulator_hooks, phantom: PhantomData, }); @@ -1116,24 +1120,25 @@ where // Set global EmulatorModules pointer unsafe { - if EMULATOR_TOOLS.is_null() { - EMULATOR_TOOLS = ptr::from_mut::(modules.as_mut().get_mut()) as *mut (); + if EMULATOR_MODULES.is_null() { + EMULATOR_MODULES = ptr::from_mut::(modules.as_mut().get_mut()) as *mut (); } else { panic!("Emulator Modules have already been set and is still active. It is not supported to have multiple instances of `EmulatorModules` at the same time yet.") } } - unsafe { - // We give access to EmulatorModuleTuple during init, the compiler complains (for good reasons) - // TODO: We should find a way to be able to check for a module without giving full access to the tuple. - modules - .modules - .init_modules_all(Self::emulator_modules_mut_unchecked()); - } - modules } + pub fn post_qemu_init_all(&mut self) { + // We give access to EmulatorModuleTuple during init, the compiler complains (for good reasons) + // TODO: We should find a way to be able to check for a module without giving full access to the tuple. + unsafe { + self.modules_mut() + .post_qemu_init_all(Self::emulator_modules_mut_unchecked()); + } + } + pub fn first_exec_all(&mut self, state: &mut S) { // # Safety // We assume that the emulator was initialized correctly @@ -1336,7 +1341,7 @@ where fn drop(&mut self) { // Make the global pointer null at drop time unsafe { - EMULATOR_TOOLS = ptr::null_mut(); + EMULATOR_MODULES = ptr::null_mut(); } } } diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index 772cdf30ef..0fcf92e3f6 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -19,8 +19,8 @@ use crate::{ command::{CommandError, CommandManager, NopCommandManager, StdCommandManager}, modules::EmulatorModuleTuple, sync_exit::SyncExit, - Qemu, QemuExitError, QemuExitReason, QemuInitError, QemuMemoryChunk, QemuShutdownCause, Regs, - CPU, + Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk, + QemuShutdownCause, Regs, CPU, }; mod hooks; @@ -325,31 +325,55 @@ where pub fn new( qemu_args: &[String], modules: ET, - drivers: ED, - snapshot_manager: SM, - command_manager: CM, - ) -> Result { - let qemu = Qemu::init(qemu_args)?; - - Self::new_with_qemu(qemu, modules, drivers, snapshot_manager, command_manager) - } - - pub fn new_with_qemu( - qemu: Qemu, - modules: ET, driver: ED, snapshot_manager: SM, command_manager: CM, ) -> Result { - Ok(Emulator { - modules: EmulatorModules::new(qemu, modules), + let mut emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; + + modules.pre_qemu_init_all(&mut emulator_hooks); + + let qemu = Qemu::init(qemu_args)?; + + unsafe { + Ok(Self::new_with_qemu( + qemu, + emulator_hooks, + modules, + driver, + snapshot_manager, + command_manager, + )) + } + } + + /// New emulator with already initialized QEMU. + /// We suppose modules init hooks have already been run. + /// + /// # Safety + /// + /// pre-init qemu hooks should be run by then. + pub(crate) unsafe fn new_with_qemu( + qemu: Qemu, + emulator_hooks: EmulatorHooks, + modules: ET, + driver: ED, + snapshot_manager: SM, + command_manager: CM, + ) -> Self { + let mut emulator = Emulator { + modules: EmulatorModules::new(qemu, emulator_hooks, modules), command_manager, snapshot_manager, driver, breakpoints_by_addr: RefCell::new(HashMap::new()), breakpoints_by_id: RefCell::new(HashMap::new()), qemu, - }) + }; + + emulator.modules.post_qemu_init_all(); + + emulator } } diff --git a/libafl_qemu/src/modules/calls.rs b/libafl_qemu/src/modules/calls.rs index ecb47e1b97..1e280a0720 100644 --- a/libafl_qemu/src/modules/calls.rs +++ b/libafl_qemu/src/modules/calls.rs @@ -399,7 +399,7 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn init_module(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { diff --git a/libafl_qemu/src/modules/drcov.rs b/libafl_qemu/src/modules/drcov.rs index 46aa92d0da..e40d3dda0e 100644 --- a/libafl_qemu/src/modules/drcov.rs +++ b/libafl_qemu/src/modules/drcov.rs @@ -264,7 +264,7 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn init_module(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { diff --git a/libafl_qemu/src/modules/mod.rs b/libafl_qemu/src/modules/mod.rs index 73ce932ade..04b80a84ee 100644 --- a/libafl_qemu/src/modules/mod.rs +++ b/libafl_qemu/src/modules/mod.rs @@ -40,7 +40,7 @@ pub mod drcov; #[cfg(not(cpu_target = "hexagon"))] pub use drcov::{DrCovMetadata, DrCovModule, DrCovModuleBuilder}; -use crate::{emu::EmulatorModules, Qemu}; +use crate::{emu::EmulatorModules, EmulatorHooks, Qemu}; /// A module for `libafl_qemu`. // TODO remove 'static when specialization will be stable @@ -55,10 +55,19 @@ where const HOOKS_DO_SIDE_EFFECTS: bool = true; - /// Initialize the module, mostly used to install some hooks early. + /// Hook run **before** QEMU is initialized. /// This is always run when Emulator gets initialized, in any case. - /// Install here hooks that should be alive for the whole execution of the VM. - fn init_module(&self, _emulator_modules: &mut EmulatorModules) + /// Install here hooks that should be alive for the whole execution of the VM, even before QEMU gets initialized. + fn pre_qemu_init(&self, _emulator_hooks: &mut EmulatorHooks) + where + ET: EmulatorModuleTuple, + { + } + + /// Hook run **after** QEMU is initialized. + /// This is always run when Emulator gets initialized, in any case. + /// Install here hooks that should be alive for the whole execution of the VM, after QEMU gets initialized. + fn post_qemu_init(&self, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -137,7 +146,11 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool; - fn init_modules_all(&self, _emulator_modules: &mut EmulatorModules) + fn pre_qemu_init_all(&self, _emulator_hooks: &mut EmulatorHooks) + where + ET: EmulatorModuleTuple; + + fn post_qemu_init_all(&self, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple; @@ -186,7 +199,13 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn init_modules_all(&self, _emulator_modules: &mut EmulatorModules) + fn pre_qemu_init_all(&self, _emulator_hooks: &mut EmulatorHooks) + where + ET: EmulatorModuleTuple, + { + } + + fn post_qemu_init_all(&self, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -239,12 +258,20 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS; - fn init_modules_all(&self, emulator_modules: &mut EmulatorModules) + fn pre_qemu_init_all(&self, emulator_hooks: &mut EmulatorHooks) where ET: EmulatorModuleTuple, { - self.0.init_module(emulator_modules); - self.1.init_modules_all(emulator_modules); + self.0.pre_qemu_init(emulator_hooks); + self.1.pre_qemu_init_all(emulator_hooks); + } + + fn post_qemu_init_all(&self, emulator_modules: &mut EmulatorModules) + where + ET: EmulatorModuleTuple, + { + self.0.post_qemu_init(emulator_modules); + self.1.post_qemu_init_all(emulator_modules); } fn first_exec_all(&mut self, emulator_modules: &mut EmulatorModules, state: &mut S) diff --git a/libafl_qemu/src/modules/usermode/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs index 365dd6b9b9..45ee1e8626 100644 --- a/libafl_qemu/src/modules/usermode/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -929,7 +929,7 @@ where type ModuleAddressFilter = StdAddressFilter; const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn init_module(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { diff --git a/libafl_qemu/src/modules/usermode/injections.rs b/libafl_qemu/src/modules/usermode/injections.rs index 56a3b22ce1..9d62e7b017 100644 --- a/libafl_qemu/src/modules/usermode/injections.rs +++ b/libafl_qemu/src/modules/usermode/injections.rs @@ -262,7 +262,7 @@ where { type ModuleAddressFilter = NopAddressFilter; - fn init_module(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { diff --git a/libafl_qemu/src/modules/usermode/snapshot.rs b/libafl_qemu/src/modules/usermode/snapshot.rs index d2447238ce..4eabc858c2 100644 --- a/libafl_qemu/src/modules/usermode/snapshot.rs +++ b/libafl_qemu/src/modules/usermode/snapshot.rs @@ -675,7 +675,7 @@ where { type ModuleAddressFilter = NopAddressFilter; - fn init_module(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, {