From af203435b1a388b33d8cbbb47753ec4fe07555c8 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 19 Nov 2024 16:28:36 +0100 Subject: [PATCH 01/21] qemu config refactoring --- libafl_qemu/src/emu/builder.rs | 60 +++++++++++++--------------------- libafl_qemu/src/qemu/config.rs | 43 ++++++++---------------- libafl_qemu/src/qemu/mod.rs | 26 +++++++++++---- 3 files changed, 55 insertions(+), 74 deletions(-) diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs index 67396d2c10..a62b1fcf03 100644 --- a/libafl_qemu/src/emu/builder.rs +++ b/libafl_qemu/src/emu/builder.rs @@ -1,4 +1,7 @@ -use std::{fmt::Debug, marker::PhantomData}; +use std::{ + fmt::{Debug, Formatter}, + marker::PhantomData, +}; use libafl::{ inputs::{HasTargetBytes, UsesInput}, @@ -17,10 +20,9 @@ use crate::{ }; #[derive(Clone, Debug)] -enum QemuBuilder { - Qemu(Qemu), - QemuConfig(QemuConfig), - QemuString(Vec), +pub enum QemuParams { + Config(QemuConfig), + Cli(Vec), } #[derive(Clone, Debug)] @@ -32,7 +34,7 @@ where driver: ED, snapshot_manager: SM, command_manager: CM, - qemu_builder: Option, + qemu_parameters: Option, phantom: PhantomData, } @@ -47,7 +49,7 @@ where driver: NopEmulatorDriver, snapshot_manager: NopSnapshotManager, command_manager: NopCommandManager, - qemu_builder: None, + qemu_parameters: None, phantom: PhantomData, } } @@ -67,7 +69,7 @@ where command_manager: StdCommandManager::default(), snapshot_manager: StdSnapshotManager::default(), driver: StdEmulatorDriver::builder().build(), - qemu_builder: None, + qemu_parameters: None, phantom: PhantomData, } } @@ -85,7 +87,7 @@ where command_manager: StdCommandManager::default(), snapshot_manager: FastSnapshotManager::default(), driver: StdEmulatorDriver::builder().build(), - qemu_builder: None, + qemu_parameters: None, phantom: PhantomData, } } @@ -99,14 +101,14 @@ where driver: ED, command_manager: CM, snapshot_manager: SM, - qemu_builder: Option, + qemu_parameters: Option, ) -> Self { Self { modules, command_manager, driver, snapshot_manager, - qemu_builder, + qemu_parameters, phantom: PhantomData, } } @@ -116,20 +118,13 @@ where CM: CommandManager, ET: EmulatorModuleTuple, { - let qemu_builder = self.qemu_builder.ok_or(QemuInitError::EmptyArgs)?; + let qemu_parameters = self.qemu_parameters.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) => { - let res: Result = qemu_config.into(); - res? - } - QemuBuilder::QemuString(qemu_string) => Qemu::init(&qemu_string)?, - }; + let qemu = Qemu::init_with_params(&qemu_parameters)?; unsafe { Ok(Emulator::new_with_qemu( @@ -156,7 +151,7 @@ where self.driver, self.command_manager, self.snapshot_manager, - Some(QemuBuilder::QemuConfig(qemu_config)), + Some(QemuParams::Config(qemu_config)), ) } @@ -167,18 +162,7 @@ where self.driver, self.command_manager, self.snapshot_manager, - Some(QemuBuilder::QemuString(qemu_cli)), - ) - } - - #[must_use] - pub fn qemu(self, qemu: Qemu) -> EmulatorBuilder { - EmulatorBuilder::new( - self.modules, - self.driver, - self.command_manager, - self.snapshot_manager, - Some(QemuBuilder::Qemu(qemu)), + Some(QemuParams::Cli(qemu_cli)), ) } @@ -192,7 +176,7 @@ where self.driver, self.command_manager, self.snapshot_manager, - self.qemu_builder, + self.qemu_parameters, ) } @@ -202,7 +186,7 @@ where driver, self.command_manager, self.snapshot_manager, - self.qemu_builder, + self.qemu_parameters, ) } @@ -215,7 +199,7 @@ where self.driver, command_manager, self.snapshot_manager, - self.qemu_builder, + self.qemu_parameters, ) } @@ -225,7 +209,7 @@ where self.driver, self.command_manager, self.snapshot_manager, - self.qemu_builder, + self.qemu_parameters, ) } @@ -238,7 +222,7 @@ where self.driver, self.command_manager, snapshot_manager, - self.qemu_builder, + self.qemu_parameters, ) } } diff --git a/libafl_qemu/src/qemu/config.rs b/libafl_qemu/src/qemu/config.rs index b17b3e5913..b53d4d541b 100644 --- a/libafl_qemu/src/qemu/config.rs +++ b/libafl_qemu/src/qemu/config.rs @@ -2,20 +2,13 @@ use core::{ fmt, fmt::{Display, Formatter}, }; -use std::{ - path::{Path, PathBuf}, - sync::OnceLock, -}; +use std::path::{Path, PathBuf}; use getset::Getters; use libafl_derive; use strum_macros; use typed_builder::TypedBuilder; -use crate::{Qemu, QemuInitError}; - -pub(super) static QEMU_CONFIG: OnceLock = OnceLock::new(); - #[cfg(feature = "systemmode")] #[derive(Debug, strum_macros::Display, Clone)] #[strum(prefix = "-accel ", serialize_all = "lowercase")] @@ -304,14 +297,16 @@ impl> From for Program { } #[derive(Debug, Clone, libafl_derive::Display, TypedBuilder, Getters)] -#[builder(build_method(into = Result), builder_method(vis = "pub(crate)", +#[builder(builder_method( + vis = "pub(crate)", doc = "Since Qemu is a zero sized struct, this is not a completely standard builder pattern. \ The Qemu configuration is not stored in the Qemu struct after build() but in QEMU_CONFIG \ Therefore, to use the derived builder and avoid boilerplate a builder for QemuConfig is \ derived. \ The QemuConfig::builder is called in Qemu::builder() which is the only place where it should \ be called, in this way the one to one matching of Qemu and QemuConfig is enforced. Therefore \ - its visibility is pub(crate)"))] + its visibility is pub(crate)" +))] #[getset(get = "pub")] pub struct QemuConfig { #[cfg(feature = "systemmode")] @@ -350,40 +345,28 @@ pub struct QemuConfig { program: Program, } // Adding something here? Please leave Program as the last field -impl From for Result { - /// This method is necessary to make the API resemble a typical builder pattern, i.e. - /// `Qemu::builder().foo(bar).build()`, while still leveraging `TypedBuilder` for this - /// non-standard use case where `Qemu` doesn't store the configuration. - /// Internally, `TypedBuilder` is used to generate a builder for `QemuConfig`. - /// This `QemuConfig.into()` method is used by the derived `QemuConfigBuilder.build()` - /// to go from `QemuConfigBuilder` to `QemuConfig`, and finally to `Qemu` in one fn. - /// - /// # Errors - /// returns `QemuInitError` if the Qemu initialization fails, including cases where Qemu has - /// already been initialized. - fn from(config: QemuConfig) -> Self { - let args = config +impl From<&QemuConfig> for Vec { + /// Generate the QEMU-compatible initialization cli string from the QEMU config. + fn from(config: &QemuConfig) -> Self { + config .to_string() .split(' ') .map(ToString::to_string) - .collect::>(); - let qemu = Qemu::init(&args)?; - QEMU_CONFIG - .set(config) - .map_err(|_| unreachable!("BUG: QEMU_CONFIG was already set but Qemu was not init!"))?; - Ok(qemu) + .collect::>() } } #[cfg(test)] mod test { use super::*; + use crate::Qemu; #[test] #[cfg(feature = "usermode")] fn usermode() { let program = "/bin/pwd"; - let qemu = Qemu::builder().program("/bin/pwd").build().unwrap(); + let qemu_config = QemuConfig::builder().program("/bin/pwd").build(); + let qemu = Qemu::init_with_config(&qemu_config).unwrap(); let config = qemu.get_config().unwrap(); assert_eq!(config.to_string().trim(), program.trim()); } diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index e742dfbda1..3204cb1674 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -14,6 +14,7 @@ use std::{ mem::MaybeUninit, ops::Range, pin::Pin, + sync::OnceLock, }; use libafl_bolts::os::unix_signals::Signal; @@ -28,10 +29,10 @@ use libafl_qemu_sys::{ use num_traits::Num; use strum::IntoEnumIterator; -use crate::{GuestAddrKind, GuestReg, Regs}; +use crate::{GuestAddrKind, GuestReg, QemuParams, Regs}; pub mod config; -use config::{QemuConfig, QemuConfigBuilder, QEMU_CONFIG}; +use config::QemuConfig; #[cfg(feature = "usermode")] mod usermode; @@ -49,6 +50,8 @@ pub use hooks::*; static mut QEMU_IS_INITIALIZED: bool = false; +pub(super) static QEMU_CONFIG: OnceLock = OnceLock::new(); + #[derive(Debug)] pub enum QemuError { Init(QemuInitError), @@ -574,10 +577,21 @@ impl From for HookData { #[allow(clippy::unused_self)] impl Qemu { - /// For more details about the parameters check - /// [the QEMU documentation](https://www.qemu.org/docs/master/about/). - pub fn builder() -> QemuConfigBuilder { - QemuConfig::builder() + pub fn init_with_params(params: &QemuParams) -> Result { + match params { + QemuParams::Config(config) => Self::init_with_config(config), + QemuParams::Cli(cli) => Self::init(cli.as_ref()), + } + } + + pub fn init_with_config(config: &QemuConfig) -> Result { + let qemu_args: Vec = config.into(); + + QEMU_CONFIG + .set(config.clone()) + .map_err(|_| unreachable!("BUG: QEMU_CONFIG was already set but Qemu was not init!"))?; + + Self::init(qemu_args.as_ref()) } #[allow(clippy::must_use_candidate, clippy::similar_names)] From 28655d8fa182f64d505824b9be390b4d1d16b374 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Mon, 25 Nov 2024 11:58:20 +0100 Subject: [PATCH 02/21] * QEMU error refactoring* * back to one QEMU init function * other small things --- .../qemu_baremetal/src/fuzzer_low_level.rs | 17 +- libafl_qemu/src/emu/builder.rs | 15 +- libafl_qemu/src/executor.rs | 3 +- libafl_qemu/src/qemu/config.rs | 12 - libafl_qemu/src/qemu/error.rs | 141 +++++++++++ libafl_qemu/src/qemu/mod.rs | 226 +++++------------- libafl_qemu/src/qemu/systemmode.rs | 4 +- 7 files changed, 224 insertions(+), 194 deletions(-) create mode 100644 libafl_qemu/src/qemu/error.rs diff --git a/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs b/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs index fa2d6bcc31..c8b026792c 100644 --- a/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs +++ b/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs @@ -29,9 +29,9 @@ use libafl_bolts::{ AsSlice, }; use libafl_qemu::{ - config, elf::EasyElf, executor::QemuExecutor, modules::edges::StdEdgeCoverageModuleBuilder, - Emulator, GuestPhysAddr, Qemu, QemuExitError, QemuExitReason, QemuRWError, QemuShutdownCause, - Regs, + config, config::QemuConfig, elf::EasyElf, executor::QemuExecutor, + modules::edges::StdEdgeCoverageModuleBuilder, Emulator, GuestPhysAddr, QemuExitError, + QemuExitReason, QemuRWError, QemuShutdownCause, Regs, }; use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND}; @@ -94,7 +94,7 @@ pub fn fuzz() { }; // Initialize QEMU - let qemu = Qemu::builder() + let qemu_config = QemuConfig::builder() .machine("mps2-an385") .monitor(config::Monitor::Null) .kernel(format!("{target_dir}/example.elf")) @@ -107,18 +107,21 @@ pub fn fuzz() { .file(format!("{target_dir}/dummy.qcow2")) .build()]) .start_cpu(false) - .build() - .expect("Failed to initialized QEMU"); + .build(); + + // .expect("Failed to initialized QEMU"); let emulator_modules = tuple_list!(StdEdgeCoverageModuleBuilder::default() .map_observer(edges_observer.as_mut()) .build()?); let emulator = Emulator::empty() - .qemu(qemu) + .qemu_config(qemu_config) .modules(emulator_modules) .build()?; + let qemu = emulator.qemu(); + qemu.set_breakpoint(main_addr); unsafe { diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs index a62b1fcf03..423e2ae128 100644 --- a/libafl_qemu/src/emu/builder.rs +++ b/libafl_qemu/src/emu/builder.rs @@ -1,7 +1,4 @@ -use std::{ - fmt::{Debug, Formatter}, - marker::PhantomData, -}; +use std::{fmt::Debug, marker::PhantomData}; use libafl::{ inputs::{HasTargetBytes, UsesInput}, @@ -16,15 +13,9 @@ use crate::{ config::QemuConfig, modules::{EmulatorModule, EmulatorModuleTuple}, Emulator, EmulatorHooks, NopEmulatorDriver, NopSnapshotManager, Qemu, QemuHooks, QemuInitError, - StdEmulatorDriver, StdSnapshotManager, + QemuParams, StdEmulatorDriver, StdSnapshotManager, }; -#[derive(Clone, Debug)] -pub enum QemuParams { - Config(QemuConfig), - Cli(Vec), -} - #[derive(Clone, Debug)] pub struct EmulatorBuilder where @@ -124,7 +115,7 @@ where self.modules.pre_qemu_init_all(&mut emulator_hooks); - let qemu = Qemu::init_with_params(&qemu_parameters)?; + let qemu = Qemu::init(qemu_parameters)?; unsafe { Ok(Emulator::new_with_qemu( diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 6a56c06c4b..4b2ddacbca 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -9,6 +9,8 @@ use std::ptr; #[cfg(feature = "systemmode")] use std::sync::atomic::{AtomicBool, Ordering}; +#[cfg(feature = "usermode")] +use libafl::inputs::UsesInput; use libafl::{ corpus::Corpus, events::{EventFirer, EventRestarter}, @@ -20,7 +22,6 @@ use libafl::{ }, feedbacks::Feedback, fuzzer::HasObjective, - inputs::UsesInput, observers::ObserversTuple, state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState}, Error, ExecutionProcessor, HasScheduler, diff --git a/libafl_qemu/src/qemu/config.rs b/libafl_qemu/src/qemu/config.rs index b53d4d541b..389b4c2de7 100644 --- a/libafl_qemu/src/qemu/config.rs +++ b/libafl_qemu/src/qemu/config.rs @@ -298,7 +298,6 @@ impl> From for Program { #[derive(Debug, Clone, libafl_derive::Display, TypedBuilder, Getters)] #[builder(builder_method( - vis = "pub(crate)", doc = "Since Qemu is a zero sized struct, this is not a completely standard builder pattern. \ The Qemu configuration is not stored in the Qemu struct after build() but in QEMU_CONFIG \ Therefore, to use the derived builder and avoid boilerplate a builder for QemuConfig is \ @@ -345,17 +344,6 @@ pub struct QemuConfig { program: Program, } // Adding something here? Please leave Program as the last field -impl From<&QemuConfig> for Vec { - /// Generate the QEMU-compatible initialization cli string from the QEMU config. - fn from(config: &QemuConfig) -> Self { - config - .to_string() - .split(' ') - .map(ToString::to_string) - .collect::>() - } -} - #[cfg(test)] mod test { use super::*; diff --git a/libafl_qemu/src/qemu/error.rs b/libafl_qemu/src/qemu/error.rs new file mode 100644 index 0000000000..d3931c3182 --- /dev/null +++ b/libafl_qemu/src/qemu/error.rs @@ -0,0 +1,141 @@ +use core::fmt; +use std::fmt::Display; + +use libafl_qemu_sys::{CPUStatePtr, GuestAddr}; + +use crate::CallingConvention; + +#[derive(Debug)] +pub enum QemuError { + Init(QemuInitError), + Exit(QemuExitError), + RW(QemuRWError), +} + +#[derive(Debug)] +pub enum QemuInitError { + MultipleInstances, + EmptyArgs, + TooManyArgs(usize), +} + +#[derive(Debug, Clone)] +pub enum QemuExitError { + UnknownKind, // Exit reason was not NULL, but exit kind is unknown. Should never happen. + UnexpectedExit, // Qemu exited without going through an expected exit point. Can be caused by a crash for example. +} + +#[derive(Debug, Clone)] +pub enum QemuRWErrorKind { + Read, + Write, +} + +#[derive(Debug, Clone)] +pub enum QemuRWErrorCause { + WrongCallingConvention(CallingConvention, CallingConvention), // expected, given + WrongArgument(i32), + CurrentCpuNotFound, + Reg(i32), + WrongMemoryLocation(GuestAddr, usize), // addr, size +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct QemuRWError { + kind: QemuRWErrorKind, + cause: QemuRWErrorCause, + cpu: Option, // Only makes sense when cause != CurrentCpuNotFound +} + +impl std::error::Error for QemuInitError {} + +impl Display for QemuInitError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + QemuInitError::MultipleInstances => { + write!(f, "Only one instance of the QEMU Emulator is permitted") + } + QemuInitError::EmptyArgs => { + write!(f, "QEMU emulator args cannot be empty") + } + QemuInitError::TooManyArgs(n) => { + write!( + f, + "Too many arguments passed to QEMU emulator ({n} > i32::MAX)" + ) + } + } + } +} + +impl From for libafl::Error { + fn from(err: QemuInitError) -> Self { + libafl::Error::unknown(format!("{err}")) + } +} + +impl QemuRWError { + #[must_use] + pub fn new(kind: QemuRWErrorKind, cause: QemuRWErrorCause, cpu: Option) -> Self { + Self { kind, cause, cpu } + } + + pub fn wrong_reg(kind: QemuRWErrorKind, reg: R, cpu: Option) -> Self + where + R: Into + Clone, + { + Self::new(kind, QemuRWErrorCause::Reg(reg.into()), cpu) + } + + pub fn wrong_mem_location( + kind: QemuRWErrorKind, + cpu: CPUStatePtr, + addr: GuestAddr, + size: usize, + ) -> Self { + Self::new( + kind, + QemuRWErrorCause::WrongMemoryLocation(addr, size), + Some(cpu), + ) + } + + #[must_use] + pub fn current_cpu_not_found(kind: QemuRWErrorKind) -> Self { + Self::new(kind, QemuRWErrorCause::CurrentCpuNotFound, None) + } + + #[must_use] + pub fn new_argument_error(kind: QemuRWErrorKind, reg_id: i32) -> Self { + Self::new(kind, QemuRWErrorCause::WrongArgument(reg_id), None) + } + + pub fn check_conv( + kind: QemuRWErrorKind, + expected_conv: CallingConvention, + given_conv: CallingConvention, + ) -> Result<(), Self> { + if expected_conv != given_conv { + return Err(Self::new( + kind, + QemuRWErrorCause::WrongCallingConvention(expected_conv, given_conv), + None, + )); + } + + Ok(()) + } +} + +impl From for libafl::Error { + fn from(qemu_error: QemuError) -> Self { + libafl::Error::runtime(qemu_error) + } +} + +impl From for String { + fn from(qemu_error: QemuError) -> Self { + format!("LibAFL QEMU Error: {qemu_error:?}") + } +} diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index 3204cb1674..2964d0d737 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -29,11 +29,16 @@ use libafl_qemu_sys::{ use num_traits::Num; use strum::IntoEnumIterator; -use crate::{GuestAddrKind, GuestReg, QemuParams, Regs}; +use crate::{GuestAddrKind, GuestReg, Regs}; pub mod config; use config::QemuConfig; +pub mod error; +pub use error::{ + QemuError, QemuExitError, QemuInitError, QemuRWError, QemuRWErrorCause, QemuRWErrorKind, +}; + #[cfg(feature = "usermode")] mod usermode; #[cfg(feature = "usermode")] @@ -52,32 +57,6 @@ static mut QEMU_IS_INITIALIZED: bool = false; pub(super) static QEMU_CONFIG: OnceLock = OnceLock::new(); -#[derive(Debug)] -pub enum QemuError { - Init(QemuInitError), - Exit(QemuExitError), - RW(QemuRWError), -} - -impl From for libafl::Error { - fn from(qemu_error: QemuError) -> Self { - libafl::Error::runtime(qemu_error) - } -} - -impl From for String { - fn from(qemu_error: QemuError) -> Self { - format!("LibAFL QEMU Error: {qemu_error:?}") - } -} - -#[derive(Debug)] -pub enum QemuInitError { - MultipleInstances, - EmptyArgs, - TooManyArgs(usize), -} - #[derive(Debug, Clone)] pub enum QemuExitReason { /// QEMU ended for some internal reason @@ -93,81 +72,6 @@ pub enum QemuExitReason { Timeout, } -#[derive(Debug, Clone)] -pub enum QemuExitError { - UnknownKind, // Exit reason was not NULL, but exit kind is unknown. Should never happen. - UnexpectedExit, // Qemu exited without going through an expected exit point. Can be caused by a crash for example. -} - -#[derive(Debug, Clone)] -pub enum QemuRWErrorKind { - Read, - Write, -} - -#[derive(Debug, Clone)] -pub enum QemuRWErrorCause { - WrongCallingConvention(CallingConvention, CallingConvention), // expected, given - WrongArgument(i32), - CurrentCpuNotFound, - Reg(i32), - WrongMemoryLocation(GuestAddr, usize), // addr, size -} - -#[derive(Debug, Clone)] -#[allow(dead_code)] -pub struct QemuRWError { - kind: QemuRWErrorKind, - cause: QemuRWErrorCause, - cpu: Option, // Only makes sense when cause != CurrentCpuNotFound -} - -impl QemuRWError { - #[must_use] - pub fn new(kind: QemuRWErrorKind, cause: QemuRWErrorCause, cpu: Option) -> Self { - Self { kind, cause, cpu } - } - - pub fn wrong_mem_location( - kind: QemuRWErrorKind, - cpu: CPUStatePtr, - addr: GuestAddr, - size: usize, - ) -> Self { - Self::new( - kind, - QemuRWErrorCause::WrongMemoryLocation(addr, size), - Some(cpu), - ) - } - - #[must_use] - pub fn current_cpu_not_found(kind: QemuRWErrorKind) -> Self { - Self::new(kind, QemuRWErrorCause::CurrentCpuNotFound, None) - } - - #[must_use] - pub fn new_argument_error(kind: QemuRWErrorKind, reg_id: i32) -> Self { - Self::new(kind, QemuRWErrorCause::WrongArgument(reg_id), None) - } - - pub fn check_conv( - kind: QemuRWErrorKind, - expected_conv: CallingConvention, - given_conv: CallingConvention, - ) -> Result<(), Self> { - if expected_conv != given_conv { - return Err(Self::new( - kind, - QemuRWErrorCause::WrongCallingConvention(expected_conv, given_conv), - None, - )); - } - - Ok(()) - } -} - /// The thin wrapper around QEMU. /// It is considered unsafe to use it directly. /// Prefer using `Emulator` instead in case of doubt. @@ -176,6 +80,12 @@ pub struct Qemu { _private: (), } +#[derive(Clone, Debug)] +pub enum QemuParams { + Config(QemuConfig), + Cli(Vec), +} + #[derive(Debug, Clone)] pub struct QemuMemoryChunk { addr: GuestAddrKind, @@ -234,33 +144,6 @@ pub trait HookId { #[derive(Debug)] pub struct HookData(u64); -impl std::error::Error for QemuInitError {} - -impl Display for QemuInitError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - QemuInitError::MultipleInstances => { - write!(f, "Only one instance of the QEMU Emulator is permitted") - } - QemuInitError::EmptyArgs => { - write!(f, "QEMU emulator args cannot be empty") - } - QemuInitError::TooManyArgs(n) => { - write!( - f, - "Too many arguments passed to QEMU emulator ({n} > i32::MAX)" - ) - } - } - } -} - -impl From for libafl::Error { - fn from(err: QemuInitError) -> Self { - libafl::Error::unknown(format!("{err}")) - } -} - impl Display for QemuExitReason { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { @@ -272,6 +155,30 @@ impl Display for QemuExitReason { } } +impl From for QemuParams { + fn from(config: QemuConfig) -> Self { + QemuParams::Config(config) + } +} + +impl From<&[T]> for QemuParams +where + T: AsRef, +{ + fn from(cli: &[T]) -> Self { + QemuParams::Cli(cli.into_iter().map(|x| x.as_ref().into()).collect()) + } +} + +impl From> for QemuParams +where + T: AsRef, +{ + fn from(cli: Vec) -> Self { + cli.as_slice().into() + } +} + impl MemAccessInfo { #[must_use] pub fn memop(&self) -> libafl_qemu_sys::MemOp { @@ -372,11 +279,11 @@ impl CPU { let mut val = MaybeUninit::uninit(); let success = libafl_qemu_read_reg(self.ptr, reg_id, val.as_mut_ptr() as *mut u8); if success == 0 { - Err(QemuRWError { - kind: QemuRWErrorKind::Write, - cause: QemuRWErrorCause::Reg(reg.into()), - cpu: Some(self.ptr), - }) + Err(QemuRWError::wrong_reg( + QemuRWErrorKind::Write, + reg, + Some(self.ptr), + )) } else { #[cfg(feature = "be")] return Ok(GuestReg::from_be(val.assume_init()).into()); @@ -401,11 +308,11 @@ impl CPU { let success = unsafe { libafl_qemu_write_reg(self.ptr, reg_id, &raw const val as *mut u8) }; if success == 0 { - Err(QemuRWError { - kind: QemuRWErrorKind::Write, - cause: QemuRWErrorCause::Reg(reg.into()), - cpu: Some(self.ptr), - }) + Err(QemuRWError::wrong_reg( + QemuRWErrorKind::Write, + reg, + Some(self.ptr), + )) } else { Ok(()) } @@ -577,25 +484,27 @@ impl From for HookData { #[allow(clippy::unused_self)] impl Qemu { - pub fn init_with_params(params: &QemuParams) -> Result { - match params { - QemuParams::Config(config) => Self::init_with_config(config), - QemuParams::Cli(cli) => Self::init(cli.as_ref()), - } - } + #[allow(clippy::must_use_candidate, clippy::similar_names)] + pub fn init(params: T) -> Result + where + T: Into, + { + let params: QemuParams = params.into(); - pub fn init_with_config(config: &QemuConfig) -> Result { - let qemu_args: Vec = config.into(); + let args: Vec = match params { + QemuParams::Config(cfg) => { + let qemu_args: Vec = + cfg.to_string().split(" ").map(|x| x.to_string()).collect(); - QEMU_CONFIG - .set(config.clone()) - .map_err(|_| unreachable!("BUG: QEMU_CONFIG was already set but Qemu was not init!"))?; + QEMU_CONFIG.set(cfg.clone()).map_err(|_| { + unreachable!("QEMU_CONFIG was already set but Qemu was not init!") + })?; - Self::init(qemu_args.as_ref()) - } + qemu_args + } + QemuParams::Cli(cli) => cli, + }; - #[allow(clippy::must_use_candidate, clippy::similar_names)] - pub fn init(args: &[String]) -> Result { if args.is_empty() { return Err(QemuInitError::EmptyArgs); } @@ -609,6 +518,7 @@ impl Qemu { if QEMU_IS_INITIALIZED { return Err(QemuInitError::MultipleInstances); } + QEMU_IS_INITIALIZED = true; } @@ -617,7 +527,7 @@ impl Qemu { let args: Vec = args .iter() - .map(|x| CString::new(x.clone()).unwrap()) + .map(|x| CString::new(AsRef::::as_ref(x)).unwrap()) .collect(); let mut argv: Vec<*const u8> = args.iter().map(|x| x.as_ptr() as *const u8).collect(); argv.push(ptr::null()); // argv is always null terminated. @@ -921,11 +831,7 @@ impl Qemu { impl ArchExtras for Qemu { fn read_return_address(&self) -> Result { self.current_cpu() - .ok_or(QemuRWError { - kind: QemuRWErrorKind::Read, - cause: QemuRWErrorCause::CurrentCpuNotFound, - cpu: None, - })? + .ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Read))? .read_return_address() } diff --git a/libafl_qemu/src/qemu/systemmode.rs b/libafl_qemu/src/qemu/systemmode.rs index c481ecd16f..25e956cb43 100644 --- a/libafl_qemu/src/qemu/systemmode.rs +++ b/libafl_qemu/src/qemu/systemmode.rs @@ -16,8 +16,8 @@ use libc::EXIT_SUCCESS; use num_traits::Zero; use crate::{ - FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuMemoryChunk, QemuRWError, - QemuRWErrorCause, QemuRWErrorKind, QemuSnapshotCheckResult, CPU, + FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuMemoryChunk, QemuSnapshotCheckResult, + CPU, }; pub(super) extern "C" fn qemu_cleanup_atexit() { From b85ab067c581b9e3b839d53dff9ab14da74680d1 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Mon, 25 Nov 2024 12:01:16 +0100 Subject: [PATCH 03/21] update test --- libafl_qemu/src/qemu/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl_qemu/src/qemu/config.rs b/libafl_qemu/src/qemu/config.rs index 389b4c2de7..de43fbc6fd 100644 --- a/libafl_qemu/src/qemu/config.rs +++ b/libafl_qemu/src/qemu/config.rs @@ -354,7 +354,7 @@ mod test { fn usermode() { let program = "/bin/pwd"; let qemu_config = QemuConfig::builder().program("/bin/pwd").build(); - let qemu = Qemu::init_with_config(&qemu_config).unwrap(); + let qemu = Qemu::init(&qemu_config).unwrap(); let config = qemu.get_config().unwrap(); assert_eq!(config.to_string().trim(), program.trim()); } From 47d274139e6ea9f4357c381dba7454e40f9f7a34 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 26 Nov 2024 16:44:36 +0100 Subject: [PATCH 04/21] Light refactor of EmulatorModules * qemu is now a parameter to EmulatorModule callbacks and most function hooks. * EmulatorModules is initialized before QEMU is initialized. --- libafl_qemu/src/command/mod.rs | 4 +- libafl_qemu/src/emu/builder.rs | 18 +- libafl_qemu/src/emu/drivers.rs | 12 +- libafl_qemu/src/emu/hooks.rs | 71 +++--- libafl_qemu/src/emu/mod.rs | 22 +- libafl_qemu/src/modules/calls.rs | 34 +-- libafl_qemu/src/modules/cmplog.rs | 32 ++- libafl_qemu/src/modules/drcov.rs | 25 ++- libafl_qemu/src/modules/edges/helpers.rs | 20 +- libafl_qemu/src/modules/edges/mod.rs | 9 +- libafl_qemu/src/modules/mod.rs | 80 ++++--- libafl_qemu/src/qemu/hooks.rs | 271 ++++++++++++++++------- 12 files changed, 380 insertions(+), 218 deletions(-) diff --git a/libafl_qemu/src/command/mod.rs b/libafl_qemu/src/command/mod.rs index ae87cfcc3f..87402a7794 100644 --- a/libafl_qemu/src/command/mod.rs +++ b/libafl_qemu/src/command/mod.rs @@ -452,8 +452,8 @@ where // Unleash hooks if locked if emu.driver_mut().unlock_hooks() { // Prepare hooks - emu.modules_mut().first_exec_all(state); - emu.modules_mut().pre_exec_all(state, input); + emu.modules_mut().first_exec_all(qemu, state); + emu.modules_mut().pre_exec_all(qemu, state, input); } // Auto page filtering if option is enabled diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs index 423e2ae128..e38be3cdfb 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, EmulatorHooks, NopEmulatorDriver, NopSnapshotManager, Qemu, QemuHooks, QemuInitError, - QemuParams, StdEmulatorDriver, StdSnapshotManager, + Emulator, EmulatorHooks, EmulatorModules, NopEmulatorDriver, NopSnapshotManager, Qemu, + QemuHooks, QemuInitError, QemuParams, StdEmulatorDriver, StdSnapshotManager, }; #[derive(Clone, Debug)] @@ -111,17 +111,23 @@ where { let qemu_parameters = self.qemu_parameters.ok_or(QemuInitError::EmptyArgs)?; - let mut emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; + let emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; - self.modules.pre_qemu_init_all(&mut emulator_hooks); + let mut emulator_modules = EmulatorModules::new(emulator_hooks, self.modules); + + // TODO: fix things there properly. The biggest issue being that it creates 2 mut ref to the module with the callback being called + unsafe { + emulator_modules + .modules_mut() + .pre_qemu_init_all(EmulatorModules::::emulator_modules_mut_unchecked()); + } let qemu = Qemu::init(qemu_parameters)?; unsafe { Ok(Emulator::new_with_qemu( qemu, - emulator_hooks, - self.modules, + emulator_modules, self.driver, self.snapshot_manager, self.command_manager, diff --git a/libafl_qemu/src/emu/drivers.rs b/libafl_qemu/src/emu/drivers.rs index 0fe0c6f161..18d380e9a7 100644 --- a/libafl_qemu/src/emu/drivers.rs +++ b/libafl_qemu/src/emu/drivers.rs @@ -54,7 +54,7 @@ where /// Just before calling user's harness for the first time. /// Called only once fn first_harness_exec(emulator: &mut Emulator, state: &mut S) { - emulator.modules.first_exec_all(state); + emulator.modules.first_exec_all(emulator.qemu, state); } /// Just before calling user's harness @@ -63,7 +63,7 @@ where state: &mut S, input: &S::Input, ) { - emulator.modules.pre_exec_all(state, input); + emulator.modules.pre_exec_all(emulator.qemu, state, input); } /// Just after returning from user's harness @@ -78,7 +78,7 @@ where { emulator .modules - .post_exec_all(state, input, observers, exit_kind); + .post_exec_all(emulator.qemu, state, input, observers, exit_kind); } /// Just before entering QEMU @@ -169,7 +169,7 @@ where { fn first_harness_exec(emulator: &mut Emulator, state: &mut S) { if !emulator.driver.hooks_locked { - emulator.modules.first_exec_all(state); + emulator.modules.first_exec_all(emulator.qemu, state); } } @@ -179,7 +179,7 @@ where input: &S::Input, ) { if !emulator.driver.hooks_locked { - emulator.modules.pre_exec_all(state, input); + emulator.modules.pre_exec_all(emulator.qemu, state, input); } let input_location = { emulator.driver.input_location.get().cloned() }; @@ -206,7 +206,7 @@ where if !emulator.driver.hooks_locked { emulator .modules - .post_exec_all(state, input, observers, exit_kind); + .post_exec_all(emulator.qemu, state, input, observers, exit_kind); } } diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs index bb07522b60..61e3759ca9 100644 --- a/libafl_qemu/src/emu/hooks.rs +++ b/libafl_qemu/src/emu/hooks.rs @@ -3,7 +3,7 @@ use std::{fmt::Debug, marker::PhantomData, mem::transmute, pin::Pin, ptr}; use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple}; -use libafl_qemu_sys::{CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, GuestUsize, TCGTemp}; +use libafl_qemu_sys::{CPUStatePtr, FatPtr, GuestAddr, GuestUsize, TCGTemp}; #[cfg(feature = "usermode")] use crate::qemu::{ @@ -37,9 +37,15 @@ use crate::{ ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook, WriteExecNHook, WriteGenHook, WriteHookId, }, - CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, Qemu, + CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, NewThreadHookFn, Qemu, }; +/// Get a C-compatible function pointer from the input hook. +/// If the hook was already a c function, nothing is done. +/// +/// h: input hook +/// replacement: C-compatible function to call when C side should call the hook +/// fntype: type used to cast h into replacement macro_rules! get_raw_hook { ($h:expr, $replacement:expr, $fntype:ty) => { match $h { @@ -100,7 +106,6 @@ pub struct EmulatorModules where S: UsesInput, { - qemu: Qemu, modules: Pin>, hooks: EmulatorHooks, phantom: PhantomData, @@ -679,10 +684,7 @@ where } } - pub fn backdoor_function( - &self, - hook: fn(&mut EmulatorModules, Option<&mut S>, cpu: CPUArchStatePtr, pc: GuestAddr), - ) -> BackdoorHookId { + pub fn backdoor_function(&self, hook: BackdoorHookFn) -> BackdoorHookId { unsafe { self.qemu_hooks .add_backdoor_hook(transmute(hook), func_backdoor_hook_wrapper::) @@ -715,15 +717,7 @@ where } } - pub fn thread_creation_function( - &mut self, - hook: fn( - &mut EmulatorModules, - Option<&mut S>, - env: CPUArchStatePtr, - tid: u32, - ) -> bool, - ) -> NewThreadHookId { + pub fn thread_creation_function(&mut self, hook: NewThreadHookFn) -> NewThreadHookId { unsafe { self.qemu_hooks .add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::) @@ -965,7 +959,7 @@ where pub fn instruction_function( &mut self, addr: GuestAddr, - hook: fn(&mut EmulatorModules, Option<&mut S>, GuestAddr), + hook: InstructionHookFn, invalidate_block: bool, ) -> InstructionHookId { self.hooks @@ -1076,15 +1070,7 @@ where self.hooks.thread_creation(hook) } - pub fn thread_creation_function( - &mut self, - hook: fn( - &mut EmulatorModules, - Option<&mut S>, - env: CPUArchStatePtr, - tid: u32, - ) -> bool, - ) -> NewThreadHookId { + pub fn thread_creation_function(&mut self, hook: NewThreadHookFn) -> NewThreadHookId { self.hooks.thread_creation_function(hook) } @@ -1101,13 +1087,8 @@ where ET: EmulatorModuleTuple, S: UsesInput + Unpin, { - pub(super) fn new( - qemu: Qemu, - emulator_hooks: EmulatorHooks, - modules: ET, - ) -> Pin> { + pub(super) fn new(emulator_hooks: EmulatorHooks, modules: ET) -> Pin> { let mut modules = Box::pin(Self { - qemu, modules: Box::pin(modules), hooks: emulator_hooks, phantom: PhantomData, @@ -1130,35 +1111,41 @@ where modules } - pub fn post_qemu_init_all(&mut self) { + /// Run post-QEMU init module callbacks. + pub unsafe fn post_qemu_init_all(&mut self, qemu: Qemu) { // 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()); + .post_qemu_init_all(qemu, Self::emulator_modules_mut_unchecked()); } } - pub fn first_exec_all(&mut self, state: &mut S) { + pub fn first_exec_all(&mut self, qemu: Qemu, state: &mut S) { // # Safety // We assume that the emulator was initialized correctly unsafe { self.modules_mut() - .first_exec_all(Self::emulator_modules_mut_unchecked(), state); + .first_exec_all(qemu, Self::emulator_modules_mut_unchecked(), state); } } - pub fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) { + pub fn pre_exec_all(&mut self, qemu: Qemu, state: &mut S, input: &S::Input) { // # Safety // We assume that the emulator was initialized correctly unsafe { - self.modules_mut() - .pre_exec_all(Self::emulator_modules_mut_unchecked(), state, input); + self.modules_mut().pre_exec_all( + qemu, + Self::emulator_modules_mut_unchecked(), + state, + input, + ); } } pub fn post_exec_all( &mut self, + qemu: Qemu, state: &mut S, input: &S::Input, observers: &mut OT, @@ -1168,6 +1155,7 @@ where { unsafe { self.modules_mut().post_exec_all( + qemu, Self::emulator_modules_mut_unchecked(), state, input, @@ -1199,11 +1187,6 @@ impl EmulatorModules where S: UsesInput, { - #[must_use] - pub fn qemu(&self) -> Qemu { - self.qemu - } - #[must_use] pub fn modules(&self) -> &ET { self.modules.as_ref().get_ref() diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index 0fcf92e3f6..e3e140c0cf 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -329,17 +329,22 @@ where snapshot_manager: SM, command_manager: CM, ) -> Result { - let mut emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; + let emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; + let mut emulator_modules = EmulatorModules::new(emulator_hooks, modules); - modules.pre_qemu_init_all(&mut emulator_hooks); + // TODO: fix things there properly. The biggest issue being that it creates 2 mut ref to the module with the callback being called + unsafe { + emulator_modules + .modules_mut() + .pre_qemu_init_all(EmulatorModules::::emulator_modules_mut_unchecked()); + } let qemu = Qemu::init(qemu_args)?; unsafe { Ok(Self::new_with_qemu( qemu, - emulator_hooks, - modules, + emulator_modules, driver, snapshot_manager, command_manager, @@ -352,17 +357,16 @@ where /// /// # Safety /// - /// pre-init qemu hooks should be run by then. + /// pre-init qemu hooks should be run before calling this. pub(crate) unsafe fn new_with_qemu( qemu: Qemu, - emulator_hooks: EmulatorHooks, - modules: ET, + emulator_modules: Pin>>, driver: ED, snapshot_manager: SM, command_manager: CM, ) -> Self { let mut emulator = Emulator { - modules: EmulatorModules::new(qemu, emulator_hooks, modules), + modules: emulator_modules, command_manager, snapshot_manager, driver, @@ -371,7 +375,7 @@ where qemu, }; - emulator.modules.post_qemu_init_all(); + emulator.modules.post_qemu_init_all(qemu); emulator } diff --git a/libafl_qemu/src/modules/calls.rs b/libafl_qemu/src/modules/calls.rs index 1e280a0720..951f51e97f 100644 --- a/libafl_qemu/src/modules/calls.rs +++ b/libafl_qemu/src/modules/calls.rs @@ -243,6 +243,7 @@ where } fn on_ret( + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc: GuestAddr, @@ -250,7 +251,7 @@ where S: Unpin + UsesInput, ET: EmulatorModuleTuple, { - let ret_addr: GuestAddr = emulator_modules.qemu().read_return_address().unwrap(); + let ret_addr: GuestAddr = qemu.read_return_address().unwrap(); // log::info!("RET @ 0x{:#x}", ret_addr); @@ -270,6 +271,7 @@ where } fn gen_blocks_calls( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -292,8 +294,6 @@ where .unwrap(); } - let qemu = emulator_modules.qemu(); - let mut call_addrs: Vec<(GuestAddr, usize)> = Vec::new(); let mut ret_addrs: Vec = Vec::new(); @@ -362,7 +362,10 @@ where for (call_addr, call_len) in call_addrs { // TODO do not use a closure, find a more efficient way to pass call_len let call_cb = Box::new( - move |emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc| { + move |_qemu: Qemu, + emulator_modules: &mut EmulatorModules, + state: Option<&mut S>, + pc| { // eprintln!("CALL @ 0x{:#x}", pc + call_len); let mut collectors = if let Some(h) = emulator_modules.get_mut::() { h.collectors.take() @@ -399,7 +402,7 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -412,21 +415,20 @@ where fn pre_exec( &mut self, - emulator_modules: &mut EmulatorModules, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _state: &mut S, input: &S::Input, ) where ET: EmulatorModuleTuple, { - self.collectors - .as_mut() - .unwrap() - .pre_exec_all(emulator_modules.qemu(), input); + self.collectors.as_mut().unwrap().pre_exec_all(qemu, input); } fn post_exec( &mut self, - emulator_modules: &mut EmulatorModules, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _state: &mut S, input: &S::Input, observers: &mut OT, @@ -435,12 +437,10 @@ where OT: ObserversTuple, ET: EmulatorModuleTuple, { - self.collectors.as_mut().unwrap().post_exec_all( - emulator_modules.qemu(), - input, - observers, - exit_kind, - ); + self.collectors + .as_mut() + .unwrap() + .post_exec_all(qemu, input, observers, exit_kind); } fn address_filter(&self) -> &Self::ModuleAddressFilter { diff --git a/libafl_qemu/src/modules/cmplog.rs b/libafl_qemu/src/modules/cmplog.rs index b595971374..69ec5cb605 100644 --- a/libafl_qemu/src/modules/cmplog.rs +++ b/libafl_qemu/src/modules/cmplog.rs @@ -14,11 +14,12 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "systemmode")] use crate::modules::{NopPageFilter, NOP_PAGE_FILTER}; #[cfg(feature = "usermode")] -use crate::{capstone, qemu::ArchExtras, CallingConvention, Qemu}; +use crate::{capstone, qemu::ArchExtras, CallingConvention}; use crate::{ emu::EmulatorModules, modules::{hash_me, AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter}, qemu::Hook, + Qemu, }; #[cfg_attr( @@ -74,8 +75,12 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { emulator_modules.cmps( @@ -139,8 +144,12 @@ where const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { emulator_modules.cmps( @@ -172,6 +181,7 @@ where } pub fn gen_unique_cmp_ids( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc: GuestAddr, @@ -203,6 +213,7 @@ where } pub fn gen_hashed_cmp_ids( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -296,6 +307,7 @@ impl CmpLogRoutinesModule { } fn gen_blocks_calls( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -318,8 +330,6 @@ impl CmpLogRoutinesModule { .unwrap(); } - let qemu = emulator_modules.qemu(); - if let Some(h) = emulator_modules.get::() { #[allow(unused_mut)] let mut code = { @@ -391,8 +401,12 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { emulator_modules.blocks( diff --git a/libafl_qemu/src/modules/drcov.rs b/libafl_qemu/src/modules/drcov.rs index e40d3dda0e..9621202328 100644 --- a/libafl_qemu/src/modules/drcov.rs +++ b/libafl_qemu/src/modules/drcov.rs @@ -13,6 +13,7 @@ use crate::{ emu::EmulatorModules, modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, NopAddressFilter}, qemu::Hook, + Qemu, }; static DRCOV_IDS: Mutex>> = Mutex::new(None); @@ -264,7 +265,7 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -276,15 +277,17 @@ where } #[cfg(feature = "usermode")] - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { if self.module_mapping.is_none() { log::info!("Auto-filling module mapping for DrCov module from QEMU mapping."); - let qemu = emulator_modules.qemu(); - let mut module_mapping: RangeMap = RangeMap::new(); #[allow(clippy::unnecessary_cast)] // for GuestAddr -> u64 @@ -307,8 +310,12 @@ where } #[cfg(feature = "systemmode")] - fn first_exec(&mut self, _emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { assert!( @@ -319,6 +326,7 @@ where fn post_exec( &mut self, + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, @@ -359,6 +367,7 @@ where } pub fn gen_unique_block_ids( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc: GuestAddr, @@ -408,6 +417,7 @@ where } pub fn gen_block_lengths( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -430,6 +440,7 @@ pub fn gen_block_lengths( } pub fn exec_trace_block( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, diff --git a/libafl_qemu/src/modules/edges/helpers.rs b/libafl_qemu/src/modules/edges/helpers.rs index 760fce7272..e4de6642b4 100644 --- a/libafl_qemu/src/modules/edges/helpers.rs +++ b/libafl_qemu/src/modules/edges/helpers.rs @@ -60,7 +60,7 @@ mod generators { }; use crate::{ modules::{hash_me, AddressFilter, EdgeCoverageModule, EmulatorModuleTuple, PageFilter}, - EmulatorModules, + EmulatorModules, Qemu, }; fn get_mask() -> usize { @@ -78,6 +78,7 @@ mod generators { } pub fn gen_unique_edge_ids( + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: Option<&mut S>, src: GuestAddr, @@ -108,10 +109,7 @@ mod generators { #[cfg(feature = "systemmode")] { - let paging_id = emulator_modules - .qemu() - .current_cpu() - .and_then(|cpu| cpu.current_paging_id()); + let paging_id = qemu.current_cpu().and_then(|cpu| cpu.current_paging_id()); if !module.must_instrument(src, paging_id) && !module.must_instrument(dest, paging_id) @@ -157,6 +155,7 @@ mod generators { #[allow(clippy::unnecessary_cast)] pub fn gen_hashed_edge_ids( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, src: GuestAddr, @@ -179,10 +178,7 @@ mod generators { #[cfg(feature = "systemmode")] { - let paging_id = emulator_modules - .qemu() - .current_cpu() - .and_then(|cpu| cpu.current_paging_id()); + let paging_id = qemu.current_cpu().and_then(|cpu| cpu.current_paging_id()); if !module.must_instrument(src, paging_id) && !module.must_instrument(dest, paging_id) @@ -211,6 +207,7 @@ mod generators { #[allow(clippy::unnecessary_cast)] pub fn gen_hashed_block_ids( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -234,10 +231,7 @@ mod generators { } #[cfg(feature = "systemmode")] { - let page_id = emulator_modules - .qemu() - .current_cpu() - .and_then(|cpu| cpu.current_paging_id()); + let page_id = qemu.current_cpu().and_then(|cpu| cpu.current_paging_id()); if !module.must_instrument(pc, page_id) { return None; diff --git a/libafl_qemu/src/modules/edges/mod.rs b/libafl_qemu/src/modules/edges/mod.rs index db2538ca7e..1b9cb4026a 100644 --- a/libafl_qemu/src/modules/edges/mod.rs +++ b/libafl_qemu/src/modules/edges/mod.rs @@ -9,6 +9,7 @@ use libafl_qemu_sys::GuestPhysAddr; use crate::{ emu::EmulatorModules, modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, PageFilter}, + Qemu, }; mod helpers; @@ -327,8 +328,12 @@ where type ModulePageFilter = PF; const HOOKS_DO_SIDE_EFFECTS: bool = V::DO_SIDE_EFFECTS; - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { if self.use_hitcounts { diff --git a/libafl_qemu/src/modules/mod.rs b/libafl_qemu/src/modules/mod.rs index 04b80a84ee..06dea61e09 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, EmulatorHooks, Qemu}; +use crate::{emu::EmulatorModules, Qemu}; /// A module for `libafl_qemu`. // TODO remove 'static when specialization will be stable @@ -58,7 +58,7 @@ where /// 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, even before QEMU gets initialized. - fn pre_qemu_init(&self, _emulator_hooks: &mut EmulatorHooks) + fn pre_qemu_init(&self, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -67,7 +67,7 @@ where /// 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) + fn post_qemu_init(&self, _qemu: Qemu, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -77,8 +77,12 @@ where /// This call can be delayed to the point at which fuzzing is supposed to start. /// It is mostly used to avoid running hooks during VM initialization, either /// because it is useless or it would produce wrong results. - fn first_exec(&mut self, _emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { } @@ -87,6 +91,7 @@ where /// On the first run, it is executed after [`Self::first_exec`]. fn pre_exec( &mut self, + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, @@ -98,6 +103,7 @@ where /// Run after a fuzzing run ends. fn post_exec( &mut self, + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, @@ -146,20 +152,25 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool; - fn pre_qemu_init_all(&self, _emulator_hooks: &mut EmulatorHooks) + fn pre_qemu_init_all(&self, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple; - fn post_qemu_init_all(&self, _emulator_modules: &mut EmulatorModules) + fn post_qemu_init_all(&self, qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple; - fn first_exec_all(&mut self, emulator_modules: &mut EmulatorModules, state: &mut S) - where + fn first_exec_all( + &mut self, + qemu: Qemu, + emulator_modules: &mut EmulatorModules, + state: &mut S, + ) where ET: EmulatorModuleTuple; fn pre_exec_all( &mut self, + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: &mut S, input: &S::Input, @@ -168,6 +179,7 @@ where fn post_exec_all( &mut self, + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: &mut S, input: &S::Input, @@ -195,30 +207,35 @@ where impl EmulatorModuleTuple for () where - S: UsesInput, + S: UsesInput + Unpin, { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn pre_qemu_init_all(&self, _emulator_hooks: &mut EmulatorHooks) + fn pre_qemu_init_all(&self, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { } - fn post_qemu_init_all(&self, _emulator_modules: &mut EmulatorModules) + fn post_qemu_init_all(&self, _qemu: Qemu, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { } - fn first_exec_all(&mut self, _emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec_all( + &mut self, + _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { } fn pre_exec_all( &mut self, + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, @@ -229,6 +246,7 @@ where fn post_exec_all( &mut self, + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, @@ -258,44 +276,50 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS; - fn pre_qemu_init_all(&self, emulator_hooks: &mut EmulatorHooks) + fn pre_qemu_init_all(&self, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { - self.0.pre_qemu_init(emulator_hooks); - self.1.pre_qemu_init_all(emulator_hooks); + self.0.pre_qemu_init(emulator_modules); + self.1.pre_qemu_init_all(emulator_modules); } - fn post_qemu_init_all(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init_all(&self, qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { - self.0.post_qemu_init(emulator_modules); - self.1.post_qemu_init_all(emulator_modules); + self.0.post_qemu_init(qemu, emulator_modules); + self.1.post_qemu_init_all(qemu, emulator_modules); } - fn first_exec_all(&mut self, emulator_modules: &mut EmulatorModules, state: &mut S) - where + fn first_exec_all( + &mut self, + qemu: Qemu, + emulator_modules: &mut EmulatorModules, + state: &mut S, + ) where ET: EmulatorModuleTuple, { - self.0.first_exec(emulator_modules, state); - self.1.first_exec_all(emulator_modules, state); + self.0.first_exec(qemu, emulator_modules, state); + self.1.first_exec_all(qemu, emulator_modules, state); } fn pre_exec_all( &mut self, + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: &mut S, input: &S::Input, ) where ET: EmulatorModuleTuple, { - self.0.pre_exec(emulator_modules, state, input); - self.1.pre_exec_all(emulator_modules, state, input); + self.0.pre_exec(qemu, emulator_modules, state, input); + self.1.pre_exec_all(qemu, emulator_modules, state, input); } fn post_exec_all( &mut self, + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: &mut S, input: &S::Input, @@ -306,9 +330,9 @@ where ET: EmulatorModuleTuple, { self.0 - .post_exec(emulator_modules, state, input, observers, exit_kind); + .post_exec(qemu, emulator_modules, state, input, observers, exit_kind); self.1 - .post_exec_all(emulator_modules, state, input, observers, exit_kind); + .post_exec_all(qemu, emulator_modules, state, input, observers, exit_kind); } unsafe fn on_crash_all(&mut self) { diff --git a/libafl_qemu/src/qemu/hooks.rs b/libafl_qemu/src/qemu/hooks.rs index 4165742d17..97cc83e96c 100644 --- a/libafl_qemu/src/qemu/hooks.rs +++ b/libafl_qemu/src/qemu/hooks.rs @@ -100,16 +100,16 @@ impl Hook { } } -macro_rules! create_wrapper { +macro_rules! create_pre_init_wrapper { ($name:ident, ($($param:ident : $param_type:ty),*)) => { paste::paste! { - pub extern "C" fn [](hook: &mut c_void, $($param: $param_type),*) + pub extern "C" fn [](hook: &mut (), $($param: $param_type),*) where S: UsesInput + Unpin, { unsafe { let modules = EmulatorModules::::emulator_modules_mut_unchecked(); - let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::(hook)); + let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::<()>(hook)); func(modules, inprocess_get_state::(), $($param),*); } } @@ -128,13 +128,13 @@ macro_rules! create_wrapper { }; ($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty) => { paste::paste! { - pub extern "C" fn [](hook: &mut c_void, $($param: $param_type),*) -> $ret_type + pub extern "C" fn [](hook: &mut (), $($param: $param_type),*) -> $ret_type where S: UsesInput + Unpin, { unsafe { let modules = EmulatorModules::::emulator_modules_mut_unchecked(); - let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::(hook)); + let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::<()>(hook)); func(modules, inprocess_get_state::(), $($param),*) } } @@ -153,6 +153,63 @@ macro_rules! create_wrapper { }; } +macro_rules! create_wrapper { + ($name:ident, ($($param:ident : $param_type:ty),*)) => { + paste::paste! { + pub extern "C" fn [](hook: &mut (), $($param: $param_type),*) + where + S: UsesInput + Unpin, + { + unsafe { + let qemu = Qemu::get_unchecked(); + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::<()>(hook)); + func(qemu, modules, inprocess_get_state::(), $($param),*); + } + } + + pub extern "C" fn [](hook: &mut FatPtr, $($param: $param_type),*) + where + S: Unpin + UsesInput, + { + unsafe { + let qemu = Qemu::get_unchecked(); + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: &mut Box, Option<&mut S>, $($param_type),*)> = &mut *(ptr::from_mut::(hook) as *mut Box, Option<&mut S>, $($param_type),*)>); + func(qemu, modules, inprocess_get_state::(), $($param),*); + } + } + } + }; + ($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty) => { + paste::paste! { + pub extern "C" fn [](hook: &mut (), $($param: $param_type),*) -> $ret_type + where + S: UsesInput + Unpin, + { + unsafe { + let qemu = Qemu::get_unchecked(); + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::<()>(hook)); + func(qemu, modules, inprocess_get_state::(), $($param),*) + } + } + + pub extern "C" fn [](hook: &mut FatPtr, $($param: $param_type),*) -> $ret_type + where + S: UsesInput + Unpin, + { + unsafe { + let qemu = Qemu::get_unchecked(); + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: &mut Box, Option<&mut S>, $($param_type),*) -> $ret_type> = &mut *(ptr::from_mut::(hook) as *mut Box, Option<&mut S>, $($param_type),*) -> $ret_type>); + func(qemu, modules, inprocess_get_state::(), $($param),*) + } + } + } + }; +} + macro_rules! create_pre_exec_wrapper { ($name:ident, ($($param:ident : $param_type:ty),*), $hook_id:ident) => { paste::paste! { @@ -161,21 +218,22 @@ macro_rules! create_pre_exec_wrapper { S: UsesInput + Unpin, { unsafe { + let qemu = Qemu::get_unchecked(); let modules = EmulatorModules::::emulator_modules_mut_unchecked(); match &mut hook.pre_run { HookRepr::Function(ptr) => { - let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(*ptr); - func(modules, inprocess_get_state::(), $($param),*) + func(qemu, modules, inprocess_get_state::(), $($param),*) } HookRepr::Closure(ptr) => { let func: &mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*), > = &mut *(ptr::from_mut::(ptr) as *mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*), >); - func(modules, inprocess_get_state::(), $($param),*) + func(qemu, modules, inprocess_get_state::(), $($param),*) } _ => (), } @@ -193,21 +251,22 @@ macro_rules! create_post_exec_wrapper { S: UsesInput + Unpin, { unsafe { + let qemu = Qemu::get_unchecked(); let modules = EmulatorModules::::emulator_modules_mut_unchecked(); match &mut hook.post_run { HookRepr::Function(ptr) => { - let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(*ptr); - func(modules, inprocess_get_state::(), $($param),*); + func(qemu, modules, inprocess_get_state::(), $($param),*); } HookRepr::Closure(ptr) => { let func: &mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*), > = &mut *(ptr::from_mut::(ptr) as *mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*), >); - func(modules, inprocess_get_state::(), $($param),*); + func(qemu, modules, inprocess_get_state::(), $($param),*); } _ => (), } @@ -225,19 +284,20 @@ macro_rules! create_gen_wrapper { S: UsesInput + Unpin, { unsafe { + let qemu = Qemu::get_unchecked(); let modules = EmulatorModules::::emulator_modules_mut_unchecked(); match &mut hook.gen { HookRepr::Function(ptr) => { - let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) -> Option<$ret_type> = + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) -> Option<$ret_type> = transmute(*ptr); - func(modules, inprocess_get_state::(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id) + func(qemu, modules, inprocess_get_state::(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id) } HookRepr::Closure(ptr) => { let func: &mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*) -> Option<$ret_type>, - > = &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*) -> Option<$ret_type>>); - func(modules, inprocess_get_state::(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id) + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) -> Option<$ret_type>, + > = &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*) -> Option<$ret_type>>); + func(qemu, modules, inprocess_get_state::(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id) } _ => 0, } @@ -255,18 +315,20 @@ macro_rules! create_post_gen_wrapper { S: UsesInput + Unpin, { unsafe { + let qemu = Qemu::get_unchecked(); let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + match &mut hook.post_gen { HookRepr::Function(ptr) => { - let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(*ptr); - func(modules, inprocess_get_state::(), $($param),*); + func(qemu, modules, inprocess_get_state::(), $($param),*); } HookRepr::Closure(ptr) => { let func: &mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), - > = &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*)>); - func(modules, inprocess_get_state::(), $($param),*); + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*), + > = &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*)>); + func(qemu, modules, inprocess_get_state::(), $($param),*); } _ => (), } @@ -284,16 +346,18 @@ macro_rules! create_exec_wrapper { S: UsesInput + Unpin, { unsafe { + let qemu = Qemu::get_unchecked(); let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + match &mut hook.execs[$execidx] { HookRepr::Function(ptr) => { - let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(*ptr); - func(modules, inprocess_get_state::(), $($param),*); + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(*ptr); + func(qemu, modules, inprocess_get_state::(), $($param),*); } HookRepr::Closure(ptr) => { - let func: &mut Box, Option<&mut S>, $($param_type),*)> = - &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*)>); - func(modules, inprocess_get_state::(), $($param),*); + let func: &mut Box, Option<&mut S>, $($param_type),*)> = + &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*)>); + func(qemu, modules, inprocess_get_state::(), $($param),*); } _ => (), } @@ -359,8 +423,8 @@ macro_rules! create_hook_types { // Instruction hook wrappers create_hook_types!( Instruction, - fn(&mut EmulatorModules, Option<&mut S>, GuestAddr), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, GuestAddr)>, + fn(Qemu, &mut EmulatorModules, Option<&mut S>, GuestAddr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr)>, extern "C" fn(*const (), pc: GuestAddr) ); create_hook_id!(Instruction, libafl_qemu_remove_instruction_hook, true); @@ -369,9 +433,9 @@ create_wrapper!(instruction, (pc: GuestAddr)); // Backdoor hook wrappers create_hook_types!( Backdoor, - fn(&mut EmulatorModules, Option<&mut S>, cpu: CPUArchStatePtr, GuestAddr), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, GuestAddr)>, - extern "C" fn(*const (), cpu: CPUArchStatePtr, pc: GuestAddr) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, cpu: CPUArchStatePtr, GuestAddr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr)>, + extern "C" fn(libafl_qemu_opaque: *const (), cpu: CPUArchStatePtr, pc: GuestAddr) ); create_hook_id!(Backdoor, libafl_qemu_remove_backdoor_hook, true); create_wrapper!(backdoor, (cpu: CPUArchStatePtr, pc: GuestAddr)); @@ -381,6 +445,7 @@ create_wrapper!(backdoor, (cpu: CPUArchStatePtr, pc: GuestAddr)); create_hook_types!( PreSyscall, fn( + Qemu, &mut EmulatorModules, Option<&mut S>, sys_num: i32, @@ -395,6 +460,7 @@ create_hook_types!( ) -> SyscallHookResult, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, i32, @@ -409,6 +475,7 @@ create_hook_types!( ) -> SyscallHookResult, >, extern "C" fn( + Qemu, *const (), i32, GuestAddr, @@ -445,6 +512,7 @@ create_wrapper!( create_hook_types!( PostSyscall, fn( + Qemu, &mut EmulatorModules, Option<&mut S>, res: GuestAddr, @@ -460,6 +528,7 @@ create_hook_types!( ) -> GuestAddr, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&mut S>, GuestAddr, @@ -475,6 +544,7 @@ create_hook_types!( ) -> GuestAddr, >, extern "C" fn( + Qemu, *const (), GuestAddr, i32, @@ -520,23 +590,23 @@ create_hook_types!( u32, ) -> bool, >, - extern "C" fn(*const (), env: CPUArchStatePtr, tid: u32) -> bool + extern "C" fn(libafl_qemu_opaque: *const (), env: CPUArchStatePtr, tid: u32) -> bool ); create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false); -create_wrapper!(new_thread, (env: CPUArchStatePtr, tid: u32), bool); +create_pre_init_wrapper!(new_thread, (env: CPUArchStatePtr, tid: u32), bool); // CPU Run hook wrappers create_hook_types!( CpuPreRun, - fn(&mut EmulatorModules, Option<&mut S>, cpu: CPUStatePtr), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, CPUStatePtr)>, - extern "C" fn(*const (), cpu: CPUStatePtr) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, cpu: CPUStatePtr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, CPUStatePtr)>, + extern "C" fn(libafl_qemu_opaque: *const (), cpu: CPUStatePtr) ); create_hook_types!( CpuPostRun, - fn(&mut EmulatorModules, Option<&mut S>, cpu: CPUStatePtr), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, CPUStatePtr)>, - extern "C" fn(*const (), cpu: CPUStatePtr) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, cpu: CPUStatePtr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, CPUStatePtr)>, + extern "C" fn(libafl_qemu_opaque: *const (), cpu: CPUStatePtr) ); create_hook_id!(CpuRun, libafl_qemu_remove_cpu_run_hook, false); create_pre_exec_wrapper!(cpu_run, (cpu: CPUStatePtr), CpuRunHookId); @@ -546,22 +616,29 @@ create_wrapper!(cpu_run, (cpu: CPUStatePtr)); // Edge hook wrappers create_hook_types!( EdgeGen, - fn(&mut EmulatorModules, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option, + fn( + Qemu, + &mut EmulatorModules, + Option<&mut S>, + src: GuestAddr, + dest: GuestAddr, + ) -> Option, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr, GuestAddr, ) -> Option, >, - extern "C" fn(*const (), src: GuestAddr, dest: GuestAddr) -> u64 + extern "C" fn(libafl_qemu_opaque: *const (), src: GuestAddr, dest: GuestAddr) -> u64 ); create_hook_types!( EdgeExec, - fn(&mut EmulatorModules, Option<&mut S>, id: u64), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64)>, - unsafe extern "C" fn(*const (), id: u64) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, u64)>, + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64) ); create_hook_id!(Edge, libafl_qemu_remove_edge_hook, true); create_gen_wrapper!(edge, (src: GuestAddr, dest: GuestAddr), u64, 1, EdgeHookId); @@ -570,27 +647,36 @@ create_exec_wrapper!(edge, (id: u64), 0, 1, EdgeHookId); // Block hook wrappers create_hook_types!( BlockGen, - fn(&mut EmulatorModules, Option<&mut S>, pc: GuestAddr) -> Option, + fn(Qemu, &mut EmulatorModules, Option<&mut S>, pc: GuestAddr) -> Option, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr, ) -> Option, >, - unsafe extern "C" fn(*const (), pc: GuestAddr) -> u64 + unsafe extern "C" fn(libafl_qemu_opaque: *const (), pc: GuestAddr) -> u64 ); create_hook_types!( BlockPostGen, - fn(&mut EmulatorModules, Option<&mut S>, pc: GuestAddr, block_length: GuestUsize), - Box FnMut(&'a mut EmulatorModules, Option<&mut S>, GuestAddr, GuestUsize)>, - unsafe extern "C" fn(*const (), pc: GuestAddr, block_length: GuestUsize) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, pc: GuestAddr, block_length: GuestUsize), + Box< + dyn for<'a> FnMut( + Qemu, + &'a mut EmulatorModules, + Option<&mut S>, + GuestAddr, + GuestUsize, + ), + >, + unsafe extern "C" fn(libafl_qemu_opaque: *const (), pc: GuestAddr, block_length: GuestUsize) ); create_hook_types!( BlockExec, - fn(&mut EmulatorModules, Option<&mut S>, id: u64), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64)>, - unsafe extern "C" fn(*const (), id: u64) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, u64)>, + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64) ); create_hook_id!(Block, libafl_qemu_remove_block_hook, true); @@ -602,7 +688,8 @@ create_exec_wrapper!(block, (id: u64), 0, 1, BlockHookId); create_hook_types!( ReadGen, fn( - qemu_modules: &mut EmulatorModules, + Qemu, + emulator_modules: &mut EmulatorModules, Option<&mut S>, pc: GuestAddr, addr: *mut TCGTemp, @@ -610,6 +697,7 @@ create_hook_types!( ) -> Option, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr, @@ -617,21 +705,33 @@ create_hook_types!( MemAccessInfo, ) -> Option, >, - unsafe extern "C" fn(*const (), pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo) -> u64 + unsafe extern "C" fn( + libafl_qemu_opaque: *const (), + pc: GuestAddr, + addr: *mut TCGTemp, + info: MemAccessInfo, + ) -> u64 ); create_hook_types!( ReadExec, - fn(&mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr)>, + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr) ); create_hook_types!( ReadExecN, - fn(&mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), + fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), Box< - dyn for<'a> FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr, usize), + dyn for<'a> FnMut( + Qemu, + &'a mut EmulatorModules, + Option<&'a mut S>, + u64, + GuestAddr, + usize, + ), >, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize) + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr, size: usize) ); create_hook_id!(Read, libafl_qemu_remove_read_hook, true); create_gen_wrapper!(read, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, ReadHookId); @@ -651,6 +751,7 @@ create_exec_wrapper!( create_hook_types!( WriteGen, fn( + Qemu, &mut EmulatorModules, Option<&mut S>, pc: GuestAddr, @@ -659,6 +760,7 @@ create_hook_types!( ) -> Option, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr, @@ -666,21 +768,33 @@ create_hook_types!( MemAccessInfo, ) -> Option, >, - unsafe extern "C" fn(*const (), pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo) -> u64 + unsafe extern "C" fn( + libafl_qemu_opaque: *const (), + pc: GuestAddr, + addr: *mut TCGTemp, + info: MemAccessInfo, + ) -> u64 ); create_hook_types!( WriteExec, - fn(&mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr)>, + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr) ); create_hook_types!( WriteExecN, - fn(&mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), + fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), Box< - dyn for<'a> FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr, usize), + dyn for<'a> FnMut( + Qemu, + &'a mut EmulatorModules, + Option<&'a mut S>, + u64, + GuestAddr, + usize, + ), >, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize) + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr, size: usize) ); create_hook_id!(Write, libafl_qemu_remove_write_hook, true); create_gen_wrapper!(write, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, WriteHookId); @@ -699,16 +813,23 @@ create_exec_wrapper!( // Cmp hook wrappers create_hook_types!( CmpGen, - fn(&mut EmulatorModules, Option<&mut S>, pc: GuestAddr, size: usize) -> Option, + fn( + Qemu, + &mut EmulatorModules, + Option<&mut S>, + pc: GuestAddr, + size: usize, + ) -> Option, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr, usize, ) -> Option, >, - unsafe extern "C" fn(*const (), pc: GuestAddr, size: usize) -> u64 + unsafe extern "C" fn(libafl_qemu_opaque: *const (), pc: GuestAddr, size: usize) -> u64 ); pub type CmpExecHook = Hook< fn(&mut EmulatorModules, Option<&mut S>, id: u64, v0: SZ, v1: SZ), @@ -724,9 +845,9 @@ create_exec_wrapper!(cmp, (id: u64, v0: u64, v1: u64), 3, 4, CmpHookId); // Crash hook wrappers #[cfg(feature = "usermode")] -pub type CrashHookFn = fn(&mut EmulatorModules, i32); +pub type CrashHookFn = fn(Qemu, &mut EmulatorModules, i32); #[cfg(feature = "usermode")] -pub type CrashHookClosure = Box, i32)>; +pub type CrashHookClosure = Box, i32)>; /// The thin wrapper around QEMU hooks. /// It is considered unsafe to use it directly. From 1c61b3834b84847e2e1da863ceefcce84fd1a16c Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 26 Nov 2024 17:20:29 +0100 Subject: [PATCH 05/21] fmt --- fuzzers/baby/baby_fuzzer/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/baby/baby_fuzzer/src/main.rs b/fuzzers/baby/baby_fuzzer/src/main.rs index 02a695f296..c98b028026 100644 --- a/fuzzers/baby/baby_fuzzer/src/main.rs +++ b/fuzzers/baby/baby_fuzzer/src/main.rs @@ -9,7 +9,7 @@ use libafl::monitors::SimpleMonitor; use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus}, events::SimpleEventManager, - executors::{InProcessExecutor, ExitKind}, + executors::{ExitKind, InProcessExecutor}, feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, generators::RandPrintablesGenerator, From 525fd110471f7d5276bddd3758be51c2d9abfe8c Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 26 Nov 2024 20:30:28 +0100 Subject: [PATCH 06/21] * asan fixed size accesses working with generics * continue to propagate qemu argument as hook first parameter * use pre_syscall* and post_syscall* everywhere * fix some clippy stuff --- libafl_qemu/src/emu/hooks.rs | 120 ++----- libafl_qemu/src/executor.rs | 8 +- libafl_qemu/src/modules/edges/helpers.rs | 6 +- libafl_qemu/src/modules/usermode/asan.rs | 326 ++++-------------- .../src/modules/usermode/asan_guest.rs | 13 +- .../src/modules/usermode/injections.rs | 27 +- libafl_qemu/src/modules/usermode/snapshot.rs | 17 +- libafl_qemu/src/qemu/config.rs | 2 +- libafl_qemu/src/qemu/hooks.rs | 2 - libafl_qemu/src/qemu/mod.rs | 9 +- libafl_sugar/src/qemu.rs | 17 +- 11 files changed, 165 insertions(+), 382 deletions(-) diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs index 61e3759ca9..7bb703c6b1 100644 --- a/libafl_qemu/src/emu/hooks.rs +++ b/libafl_qemu/src/emu/hooks.rs @@ -9,7 +9,7 @@ use libafl_qemu_sys::{CPUStatePtr, FatPtr, GuestAddr, GuestUsize, TCGTemp}; use crate::qemu::{ closure_post_syscall_hook_wrapper, closure_pre_syscall_hook_wrapper, func_post_syscall_hook_wrapper, func_pre_syscall_hook_wrapper, PostSyscallHook, - PostSyscallHookId, PreSyscallHook, PreSyscallHookId, SyscallHookResult, + PostSyscallHookId, PreSyscallHook, PreSyscallHookId, }; #[cfg(feature = "usermode")] use crate::qemu::{ @@ -80,6 +80,7 @@ where { unsafe { let emulator_modules = EmulatorModules::::emulator_modules_mut().unwrap(); + let qemu = Qemu::get_unchecked(); let crash_hooks_ptr = &raw mut emulator_modules.hooks.crash_hooks; @@ -87,12 +88,12 @@ where match crash_hook { HookRepr::Function(ptr) => { let func: CrashHookFn = transmute(*ptr); - func(emulator_modules, target_sig); + func(qemu, emulator_modules, target_sig); } HookRepr::Closure(ptr) => { let func: &mut CrashHookClosure = &mut *(ptr::from_mut::(ptr) as *mut CrashHookClosure); - func(emulator_modules, target_sig); + func(qemu, emulator_modules, target_sig); } HookRepr::Empty => (), } @@ -762,10 +763,10 @@ where S: Unpin + UsesInput, { #[allow(clippy::type_complexity)] - pub fn syscalls(&mut self, hook: PreSyscallHook) -> Option { + pub fn pre_syscalls(&mut self, hook: PreSyscallHook) -> Option { match hook { - Hook::Function(f) => Some(self.syscalls_function(f)), - Hook::Closure(c) => Some(self.syscalls_closure(c)), + Hook::Function(f) => Some(self.pre_syscalls_function(f)), + Hook::Closure(c) => Some(self.pre_syscalls_closure(c)), Hook::Raw(r) => { let z: *const () = ptr::null::<()>(); Some(self.qemu_hooks.add_pre_syscall_hook(z, r)) @@ -775,7 +776,7 @@ where } #[allow(clippy::type_complexity)] - pub fn syscalls_function(&mut self, hook: PreSyscallHookFn) -> PreSyscallHookId { + pub fn pre_syscalls_function(&mut self, hook: PreSyscallHookFn) -> PreSyscallHookId { // # Safety // Will dereference the hook as [`FatPtr`]. unsafe { @@ -785,7 +786,7 @@ where } #[allow(clippy::type_complexity)] - pub fn syscalls_closure(&mut self, hook: PreSyscallHookClosure) -> PreSyscallHookId { + pub fn pre_syscalls_closure(&mut self, hook: PreSyscallHookClosure) -> PreSyscallHookId { // # Safety // Will dereference the hook as [`FatPtr`]. unsafe { @@ -816,10 +817,10 @@ where } #[allow(clippy::type_complexity)] - pub fn after_syscalls(&mut self, hook: PostSyscallHook) -> Option { + pub fn post_syscalls(&mut self, hook: PostSyscallHook) -> Option { match hook { - Hook::Function(f) => Some(self.after_syscalls_function(f)), - Hook::Closure(c) => Some(self.after_syscalls_closure(c)), + Hook::Function(f) => Some(self.post_syscalls_function(f)), + Hook::Closure(c) => Some(self.post_syscalls_closure(c)), Hook::Raw(r) => { let z: *const () = ptr::null::<()>(); Some(self.qemu_hooks.add_post_syscall_hook(z, r)) @@ -829,7 +830,7 @@ where } #[allow(clippy::type_complexity)] - pub fn after_syscalls_function(&mut self, hook: PostSyscallHookFn) -> PostSyscallHookId { + pub fn post_syscalls_function(&mut self, hook: PostSyscallHookFn) -> PostSyscallHookId { // # Safety // Will dereference the hook as [`FatPtr`]. This should be ok. unsafe { @@ -839,7 +840,7 @@ where } #[allow(clippy::type_complexity)] - pub fn after_syscalls_closure( + pub fn post_syscalls_closure( &mut self, hook: PostSyscallHookClosure, ) -> PostSyscallHookId { @@ -870,7 +871,7 @@ where } } - pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules, target_signal: i32)) { + pub fn crash_function(&mut self, hook: CrashHookFn) { // # Safety // Will cast the valid hook to a ptr. self.qemu_hooks.set_crash_hook(crash_hook_wrapper::); @@ -1111,8 +1112,7 @@ where modules } - /// Run post-QEMU init module callbacks. - pub unsafe fn post_qemu_init_all(&mut self, qemu: Qemu) { + pub fn post_qemu_init_all(&mut self, qemu: Qemu) { // 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 { @@ -1205,108 +1205,54 @@ where S: Unpin + UsesInput, { #[allow(clippy::type_complexity)] - pub fn syscalls(&mut self, hook: PreSyscallHook) -> Option { - self.hooks.syscalls(hook) + pub fn pre_syscalls(&mut self, hook: PreSyscallHook) -> Option { + self.hooks.pre_syscalls(hook) } /// # Safety /// Calls through to the, potentially unsafe, `syscalls_function` #[allow(clippy::type_complexity)] - pub unsafe fn syscalls_function( + pub unsafe fn pre_syscalls_function( &mut self, - hook: fn( - &mut EmulatorModules, - Option<&mut S>, - sys_num: i32, - a0: GuestAddr, - a1: GuestAddr, - a2: GuestAddr, - a3: GuestAddr, - a4: GuestAddr, - a5: GuestAddr, - a6: GuestAddr, - a7: GuestAddr, - ) -> SyscallHookResult, + hook: PreSyscallHookFn, ) -> PreSyscallHookId { - self.hooks.syscalls_function(hook) + self.hooks.pre_syscalls_function(hook) } /// # Safety /// Calls through to the, potentially unsafe, `syscalls_closure` #[allow(clippy::type_complexity)] - pub unsafe fn syscalls_closure( + pub unsafe fn pre_syscalls_closure( &mut self, - hook: Box< - dyn for<'a> FnMut( - &'a mut EmulatorModules, - Option<&'a mut S>, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> SyscallHookResult, - >, + hook: PreSyscallHookClosure, ) -> PreSyscallHookId { - self.hooks.syscalls_closure(hook) + self.hooks.pre_syscalls_closure(hook) } #[allow(clippy::type_complexity)] - pub fn after_syscalls(&mut self, hook: PostSyscallHook) -> Option { - self.hooks.after_syscalls(hook) + pub fn post_syscalls(&mut self, hook: PostSyscallHook) -> Option { + self.hooks.post_syscalls(hook) } /// # Safety /// Calls through to the, potentially unsafe, `after_syscalls_function` #[allow(clippy::type_complexity)] - pub unsafe fn after_syscalls_function( + pub unsafe fn post_syscalls_function( &mut self, - hook: fn( - &mut EmulatorModules, - Option<&mut S>, - res: GuestAddr, - sys_num: i32, - a0: GuestAddr, - a1: GuestAddr, - a2: GuestAddr, - a3: GuestAddr, - a4: GuestAddr, - a5: GuestAddr, - a6: GuestAddr, - a7: GuestAddr, - ) -> GuestAddr, + hook: PostSyscallHookFn, ) -> PostSyscallHookId { - self.hooks.after_syscalls_function(hook) + self.hooks.post_syscalls_function(hook) } #[allow(clippy::type_complexity)] - pub fn after_syscalls_closure( + pub fn post_syscalls_closure( &mut self, - hook: Box< - dyn for<'a> FnMut( - &'a mut EmulatorModules, - Option<&mut S>, - GuestAddr, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> GuestAddr, - >, + hook: PostSyscallHookClosure, ) -> PostSyscallHookId { - self.hooks.after_syscalls_closure(hook) + self.hooks.post_syscalls_closure(hook) } - pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules, target_signal: i32)) { + pub fn crash_function(&mut self, hook: CrashHookFn) { self.hooks.crash_function(hook); } diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 4b2ddacbca..e5662c913d 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -40,7 +40,9 @@ use libc::siginfo_t; #[cfg(feature = "usermode")] use crate::EmulatorModules; -use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver}; +use crate::{ + command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver, Qemu, +}; pub struct QemuExecutor<'a, CM, ED, ET, H, OT, S, SM> where @@ -182,12 +184,12 @@ where inner.inprocess_hooks_mut().crash_handler = inproc_qemu_crash_handler:: as *const c_void; - let handler = |emulator_modules: &mut EmulatorModules, host_sig| { + let handler = |qemu: Qemu, _emulator_modules: &mut EmulatorModules, host_sig| { eprintln!("Crashed with signal {host_sig}"); unsafe { libafl::executors::inprocess::generic_inproc_crash_handler::(); } - if let Some(cpu) = emulator_modules.qemu().current_cpu() { + if let Some(cpu) = qemu.current_cpu() { eprint!("Context:\n{}", cpu.display_context()); } }; diff --git a/libafl_qemu/src/modules/edges/helpers.rs b/libafl_qemu/src/modules/edges/helpers.rs index e4de6642b4..74b96f46eb 100644 --- a/libafl_qemu/src/modules/edges/helpers.rs +++ b/libafl_qemu/src/modules/edges/helpers.rs @@ -78,7 +78,7 @@ mod generators { } pub fn gen_unique_edge_ids( - qemu: Qemu, + _qemu: Qemu, emulator_modules: &mut EmulatorModules, state: Option<&mut S>, src: GuestAddr, @@ -155,7 +155,7 @@ mod generators { #[allow(clippy::unnecessary_cast)] pub fn gen_hashed_edge_ids( - qemu: Qemu, + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, src: GuestAddr, @@ -207,7 +207,7 @@ mod generators { #[allow(clippy::unnecessary_cast)] pub fn gen_hashed_block_ids( - qemu: Qemu, + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, diff --git a/libafl_qemu/src/modules/usermode/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs index 45ee1e8626..89f1a1eb85 100644 --- a/libafl_qemu/src/modules/usermode/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -150,7 +150,6 @@ use std::pin::Pin; use libafl_qemu_sys::GuestAddr; use object::{Object, ObjectSection}; - use crate::{ emu::EmulatorModules, modules::{AddressFilter, StdAddressFilter}, @@ -255,7 +254,7 @@ impl AsanGiovese { Self::unpoison(qemu, a1, a2 as usize); } QasanAction::IsPoison => { - if Self::is_invalid_access(qemu, a1, a2 as usize) { + if Self::is_invalid_access_n(qemu, a1, a2 as usize) { r = 1; } } @@ -285,51 +284,26 @@ impl AsanGiovese { #[inline] #[must_use] - pub fn is_invalid_access_1(qemu: Qemu, addr: GuestAddr) -> bool { - unsafe { - let h = qemu.g2h::<*const c_void>(addr) as isize; - let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); - let k = *shadow_addr as isize; - k != 0 && (h & 7).wrapping_add(1) > k - } - } - - #[inline] - #[must_use] - pub fn is_invalid_access_2(qemu: Qemu, addr: GuestAddr) -> bool { - unsafe { - let h = qemu.g2h::<*const c_void>(addr) as isize; - let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); - let k = *shadow_addr as isize; - k != 0 && (h & 7).wrapping_add(2) > k - } - } - - #[inline] - #[must_use] - pub fn is_invalid_access_4(qemu: Qemu, addr: GuestAddr) -> bool { - unsafe { - let h = qemu.g2h::<*const c_void>(addr) as isize; - let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); - let k = *shadow_addr as isize; - k != 0 && (h & 7).wrapping_add(4) > k - } - } + pub fn is_invalid_access(qemu: Qemu, addr: GuestAddr) -> bool + { + const { assert!(N == 1 || N == 2 || N == 4 || N == 8) }; - #[inline] - #[must_use] - pub fn is_invalid_access_8(qemu: Qemu, addr: GuestAddr) -> bool { unsafe { let h = qemu.g2h::<*const c_void>(addr) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); - *shadow_addr != 0 + if N < 8 { + let k = *shadow_addr as isize; + k != 0 && (h & 7).wrapping_add(N as isize) > k + } else { + *shadow_addr != 0 + } } } #[inline] #[must_use] #[allow(clippy::cast_sign_loss)] - pub fn is_invalid_access(qemu: Qemu, addr: GuestAddr, n: usize) -> bool { + pub fn is_invalid_access_n(qemu: Qemu, addr: GuestAddr, n: usize) -> bool { unsafe { if n == 0 { return false; @@ -724,7 +698,7 @@ pub fn init_qemu_with_asan( ASAN_INITED = true; } - let qemu = Qemu::init(args)?; + let qemu = Qemu::init(args.as_slice())?; let rt = AsanGiovese::new(qemu.hooks()); Ok((qemu, rt)) @@ -843,66 +817,30 @@ impl AsanModule { #[allow(clippy::unused_self)] #[must_use] pub fn is_poisoned(&self, qemu: Qemu, addr: GuestAddr, size: usize) -> bool { - AsanGiovese::is_invalid_access(qemu, addr, size) - } - - pub fn read_1(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_1(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 1)); - } + AsanGiovese::is_invalid_access_n(qemu, addr, size) } - pub fn read_2(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_2(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 2)); - } - } - - pub fn read_4(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_4(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 4)); - } - } - - pub fn read_8(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_8(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 8)); + pub fn read(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { + if self.enabled() && AsanGiovese::is_invalid_access::(qemu, addr) { + self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, N)); } } pub fn read_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) { - if self.enabled() && AsanGiovese::is_invalid_access(qemu, addr, size) { + if self.enabled() && AsanGiovese::is_invalid_access_n(qemu, addr, size) { self.rt .report_or_crash(qemu, pc, AsanError::Read(addr, size)); } } - pub fn write_1(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_1(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 1)); - } - } - - pub fn write_2(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_2(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 2)); - } - } - - pub fn write_4(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_4(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 4)); - } - } - - pub fn write_8(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_8(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 8)); + pub fn write(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { + if self.enabled() && AsanGiovese::is_invalid_access::(qemu, addr) { + self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, N)); } } pub fn write_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) { - if self.enabled() && AsanGiovese::is_invalid_access(qemu, addr, size) { + if self.enabled() && AsanGiovese::is_invalid_access_n(qemu, addr, size) { self.rt .report_or_crash(qemu, pc, AsanError::Write(addr, size)); } @@ -929,47 +867,51 @@ where type ModuleAddressFilter = StdAddressFilter; const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { - emulator_modules.syscalls(Hook::Function(qasan_fake_syscall::)); + emulator_modules.pre_syscalls(Hook::Function(qasan_fake_syscall::)); if self.rt.error_callback.is_some() { emulator_modules.crash_function(oncrash_asan::); } } - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { emulator_modules.reads( Hook::Function(gen_readwrite_asan::), - Hook::Function(trace_read1_asan::), - Hook::Function(trace_read2_asan::), - Hook::Function(trace_read4_asan::), - Hook::Function(trace_read8_asan::), + Hook::Function(trace_read_asan::), + Hook::Function(trace_read_asan::), + Hook::Function(trace_read_asan::), + Hook::Function(trace_read_asan::), Hook::Function(trace_read_n_asan::), ); if emulator_modules.get::().is_none() { emulator_modules.writes( Hook::Function(gen_readwrite_asan::), - Hook::Function(trace_write1_asan::), - Hook::Function(trace_write2_asan::), - Hook::Function(trace_write4_asan::), - Hook::Function(trace_write8_asan::), + Hook::Function(trace_write_asan::), + Hook::Function(trace_write_asan::), + Hook::Function(trace_write_asan::), + Hook::Function(trace_write_asan::), Hook::Function(trace_write_n_asan::), ); } else { // track writes for both modules as opt emulator_modules.writes( Hook::Function(gen_write_asan_snapshot::), - Hook::Function(trace_write1_asan_snapshot::), - Hook::Function(trace_write2_asan_snapshot::), - Hook::Function(trace_write4_asan_snapshot::), - Hook::Function(trace_write8_asan_snapshot::), + Hook::Function(trace_write_asan_snapshot::), + Hook::Function(trace_write_asan_snapshot::), + Hook::Function(trace_write_asan_snapshot::), + Hook::Function(trace_write_asan_snapshot::), Hook::Function(trace_write_n_asan_snapshot::), ); } @@ -977,21 +919,23 @@ where fn pre_exec( &mut self, - emulator_modules: &mut EmulatorModules, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, ) where ET: EmulatorModuleTuple, { if self.empty { - self.rt.snapshot(emulator_modules.qemu()); + self.rt.snapshot(qemu); self.empty = false; } } fn post_exec( &mut self, - emulator_modules: &mut EmulatorModules, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, _observers: &mut OT, @@ -1000,7 +944,7 @@ where OT: ObserversTuple, ET: EmulatorModuleTuple, { - if self.reset(emulator_modules.qemu()) == AsanRollback::HasLeaks { + if self.reset(qemu) == AsanRollback::HasLeaks { *exit_kind = ExitKind::Crash; } } @@ -1014,18 +958,21 @@ where } } -pub fn oncrash_asan(emulator_modules: &mut EmulatorModules, target_sig: i32) -where +pub fn oncrash_asan( + qemu: Qemu, + emulator_modules: &mut EmulatorModules, + target_sig: i32, +) where ET: EmulatorModuleTuple, S: Unpin + UsesInput, { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); let pc: GuestAddr = qemu.read_reg(Regs::Pc).unwrap(); h.rt.report(qemu, pc, AsanError::Signal(target_sig)); } pub fn gen_readwrite_asan( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -1044,7 +991,8 @@ where } } -pub fn trace_read1_asan( +pub fn trace_read_asan( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1053,54 +1001,12 @@ pub fn trace_read1_asan( ET: EmulatorModuleTuple, S: Unpin + UsesInput, { - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.read_1(qemu, id as GuestAddr, addr); -} - -pub fn trace_read2_asan( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.read_2(qemu, id as GuestAddr, addr); -} - -pub fn trace_read4_asan( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.read_4(qemu, id as GuestAddr, addr); -} - -pub fn trace_read8_asan( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); - h.read_8(qemu, id as GuestAddr, addr); + h.read::(qemu, id as GuestAddr, addr); } pub fn trace_read_n_asan( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1110,12 +1016,12 @@ pub fn trace_read_n_asan( S: Unpin + UsesInput, ET: EmulatorModuleTuple, { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); h.read_n(qemu, id as GuestAddr, addr, size); } -pub fn trace_write1_asan( +pub fn trace_write_asan( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1124,54 +1030,12 @@ pub fn trace_write1_asan( S: Unpin + UsesInput, ET: EmulatorModuleTuple, { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); - h.write_1(qemu, id as GuestAddr, addr); -} - -pub fn trace_write2_asan( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.write_2(qemu, id as GuestAddr, addr); -} - -pub fn trace_write4_asan( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.write_4(qemu, id as GuestAddr, addr); -} - -pub fn trace_write8_asan( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.write_8(qemu, id as GuestAddr, addr); + h.write::(qemu, id as GuestAddr, addr); } pub fn trace_write_n_asan( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1181,12 +1045,12 @@ pub fn trace_write_n_asan( S: Unpin + UsesInput, ET: EmulatorModuleTuple, { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); h.read_n(qemu, id as GuestAddr, addr, size); } pub fn gen_write_asan_snapshot( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -1205,61 +1069,8 @@ where } } -pub fn trace_write1_asan_snapshot( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - if id != 0 { - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.write_1(qemu, id as GuestAddr, addr); - } - let h = emulator_modules.get_mut::().unwrap(); - h.access(addr, 1); -} - -pub fn trace_write2_asan_snapshot( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - if id != 0 { - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.write_2(qemu, id as GuestAddr, addr); - } - let h = emulator_modules.get_mut::().unwrap(); - h.access(addr, 2); -} - -pub fn trace_write4_asan_snapshot( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - if id != 0 { - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.write_4(qemu, id as GuestAddr, addr); - } - let h = emulator_modules.get_mut::().unwrap(); - h.access(addr, 4); -} - -pub fn trace_write8_asan_snapshot( +pub fn trace_write_asan_snapshot( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1269,15 +1080,15 @@ pub fn trace_write8_asan_snapshot( ET: EmulatorModuleTuple, { if id != 0 { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); - h.write_8(qemu, id as GuestAddr, addr); + h.write::(qemu, id as GuestAddr, addr); } let h = emulator_modules.get_mut::().unwrap(); - h.access(addr, 8); + h.access(addr, N); } pub fn trace_write_n_asan_snapshot( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1288,7 +1099,6 @@ pub fn trace_write_n_asan_snapshot( ET: EmulatorModuleTuple, { if id != 0 { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); h.read_n(qemu, id as GuestAddr, addr, size); } @@ -1298,6 +1108,7 @@ pub fn trace_write_n_asan_snapshot( #[allow(clippy::too_many_arguments)] pub fn qasan_fake_syscall( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, sys_num: i32, @@ -1315,7 +1126,6 @@ where ET: EmulatorModuleTuple, { if sys_num == QASAN_FAKESYS_NR { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); match QasanAction::try_from(a0).expect("Invalid QASan action number") { QasanAction::CheckLoad => { diff --git a/libafl_qemu/src/modules/usermode/asan_guest.rs b/libafl_qemu/src/modules/usermode/asan_guest.rs index 567d98048e..5d53546f43 100644 --- a/libafl_qemu/src/modules/usermode/asan_guest.rs +++ b/libafl_qemu/src/modules/usermode/asan_guest.rs @@ -91,7 +91,7 @@ pub fn init_qemu_with_asan_guest( ASAN_GUEST_INITED = true; } - let emu = Qemu::init(args)?; + let emu = Qemu::init(args.as_slice())?; Ok((emu, asan_lib)) } @@ -206,6 +206,7 @@ where } fn gen_readwrite_guest_asan( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -244,6 +245,7 @@ where unsafe fn libafl_tcg_gen_asan(addr: *mut TCGTemp, size: usize) {} fn guest_trace_error_asan( + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, @@ -256,6 +258,7 @@ fn guest_trace_error_asan( } fn guest_trace_error_n_asan( + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, @@ -275,8 +278,12 @@ where { type ModuleAddressFilter = F; - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, S: Unpin + UsesInput, { diff --git a/libafl_qemu/src/modules/usermode/injections.rs b/libafl_qemu/src/modules/usermode/injections.rs index 9d62e7b017..fb13bb7b33 100644 --- a/libafl_qemu/src/modules/usermode/injections.rs +++ b/libafl_qemu/src/modules/usermode/injections.rs @@ -211,12 +211,15 @@ impl InjectionModule { }) } - fn on_call_check(emulator_modules: &mut EmulatorModules, id: usize, parameter: u8) - where + fn on_call_check( + qemu: Qemu, + emulator_modules: &mut EmulatorModules, + id: usize, + parameter: u8, + ) where ET: EmulatorModuleTuple, S: Unpin + UsesInput, { - let qemu = emulator_modules.qemu(); let reg: GuestAddr = qemu .current_cpu() .unwrap() @@ -262,18 +265,21 @@ where { type ModuleAddressFilter = NopAddressFilter; - fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { - emulator_modules.syscalls(Hook::Function(syscall_hook::)); + emulator_modules.pre_syscalls(Hook::Function(syscall_hook::)); } - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { - let qemu = emulator_modules.qemu(); let mut libs: Vec = Vec::new(); for region in qemu.mappings() { @@ -324,8 +330,8 @@ where for hook_addr in hook_addrs { emulator_modules.instructions( hook_addr, - Hook::Closure(Box::new(move |hooks, _state, _guest_addr| { - Self::on_call_check(hooks, id, param); + Hook::Closure(Box::new(move |qemu, hooks, _state, _guest_addr| { + Self::on_call_check(qemu, hooks, id, param); })), true, ); @@ -346,6 +352,7 @@ where #[allow(clippy::too_many_arguments)] fn syscall_hook( // Our instantiated [`EmulatorModules`] + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, // Syscall number diff --git a/libafl_qemu/src/modules/usermode/snapshot.rs b/libafl_qemu/src/modules/usermode/snapshot.rs index 4eabc858c2..b71a3cbc46 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 post_qemu_init(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -692,23 +692,24 @@ where } if !self.accurate_unmap { - emulator_modules.syscalls(Hook::Function(filter_mmap_snapshot::)); + emulator_modules.pre_syscalls(Hook::Function(filter_mmap_snapshot::)); } - emulator_modules.after_syscalls(Hook::Function(trace_mmap_snapshot::)); + emulator_modules.post_syscalls(Hook::Function(trace_mmap_snapshot::)); } fn pre_exec( &mut self, - emulator_modules: &mut EmulatorModules, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, ) where ET: EmulatorModuleTuple, { if self.empty { - self.snapshot(emulator_modules.qemu()); + self.snapshot(qemu); } else { - self.reset(emulator_modules.qemu()); + self.reset(qemu); } } @@ -722,6 +723,7 @@ where } pub fn trace_write_snapshot( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, @@ -735,6 +737,7 @@ pub fn trace_write_snapshot( } pub fn trace_write_n_snapshot( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, @@ -751,6 +754,7 @@ pub fn trace_write_n_snapshot( #[allow(clippy::too_many_arguments)] #[allow(non_upper_case_globals)] pub fn filter_mmap_snapshot( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, sys_num: i32, @@ -779,6 +783,7 @@ where #[allow(clippy::too_many_arguments, clippy::too_many_lines)] #[allow(non_upper_case_globals)] pub fn trace_mmap_snapshot( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, result: GuestAddr, diff --git a/libafl_qemu/src/qemu/config.rs b/libafl_qemu/src/qemu/config.rs index de43fbc6fd..6ab1e51f5d 100644 --- a/libafl_qemu/src/qemu/config.rs +++ b/libafl_qemu/src/qemu/config.rs @@ -354,7 +354,7 @@ mod test { fn usermode() { let program = "/bin/pwd"; let qemu_config = QemuConfig::builder().program("/bin/pwd").build(); - let qemu = Qemu::init(&qemu_config).unwrap(); + let qemu = Qemu::init(qemu_config).unwrap(); let config = qemu.get_config().unwrap(); assert_eq!(config.to_string().trim(), program.trim()); } diff --git a/libafl_qemu/src/qemu/hooks.rs b/libafl_qemu/src/qemu/hooks.rs index 97cc83e96c..3f37465bf0 100644 --- a/libafl_qemu/src/qemu/hooks.rs +++ b/libafl_qemu/src/qemu/hooks.rs @@ -475,7 +475,6 @@ create_hook_types!( ) -> SyscallHookResult, >, extern "C" fn( - Qemu, *const (), i32, GuestAddr, @@ -544,7 +543,6 @@ create_hook_types!( ) -> GuestAddr, >, extern "C" fn( - Qemu, *const (), GuestAddr, i32, diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index 2964d0d737..a1c18b1b14 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -166,7 +166,7 @@ where T: AsRef, { fn from(cli: &[T]) -> Self { - QemuParams::Cli(cli.into_iter().map(|x| x.as_ref().into()).collect()) + QemuParams::Cli(cli.iter().map(|x| x.as_ref().into()).collect()) } } @@ -493,8 +493,11 @@ impl Qemu { let args: Vec = match params { QemuParams::Config(cfg) => { - let qemu_args: Vec = - cfg.to_string().split(" ").map(|x| x.to_string()).collect(); + let qemu_args: Vec = cfg + .to_string() + .split(' ') + .map(ToString::to_string) + .collect(); QEMU_CONFIG.set(cfg.clone()).map_err(|_| { unreachable!("QEMU_CONFIG was already set but Qemu was not init!") diff --git a/libafl_sugar/src/qemu.rs b/libafl_sugar/src/qemu.rs index f9d3958f57..c0fbef4c59 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -118,7 +118,7 @@ where { /// Run the fuzzer #[allow(clippy::too_many_lines, clippy::similar_names)] - pub fn run(&mut self, qemu: Qemu) { + pub fn run(&mut self, qemu_cli: &[String]) { let conf = match self.configuration.as_ref() { Some(name) => EventConfig::from_name(name), None => EventConfig::AlwaysUnique, @@ -240,7 +240,10 @@ where ExitKind::Ok }; - let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?; + let emulator = Emulator::empty() + .qemu_cli(qemu_cli.to_owned()) + .modules(modules) + .build()?; let executor = QemuExecutor::new( emulator, @@ -357,7 +360,10 @@ where ExitKind::Ok }; - let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?; + let emulator = Emulator::empty() + .qemu_cli(qemu_cli.to_owned()) + .modules(modules) + .build()?; let mut executor = QemuExecutor::new( emulator, @@ -476,7 +482,6 @@ pub mod pybind { use std::path::PathBuf; use libafl_bolts::core_affinity::Cores; - use libafl_qemu::qemu::pybind::Qemu; use pyo3::{prelude::*, types::PyBytes}; use crate::qemu; @@ -533,7 +538,7 @@ pub mod pybind { /// Run the fuzzer #[allow(clippy::needless_pass_by_value)] - pub fn run(&self, qemu: &Qemu, harness: PyObject) { + pub fn run(&self, qemu_cli: Vec, harness: PyObject) { qemu::QemuBytesCoverageSugar::builder() .input_dirs(&self.input_dirs) .output_dir(self.output_dir.clone()) @@ -552,7 +557,7 @@ pub mod pybind { .tokens_file(self.tokens_file.clone()) .iterations(self.iterations) .build() - .run(qemu.qemu); + .run(&qemu_cli); } } From 5346f83ede3d52dc77e936deb5e68bb1dd7d7610 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 26 Nov 2024 20:30:41 +0100 Subject: [PATCH 07/21] fmt --- libafl_qemu/src/modules/usermode/asan.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libafl_qemu/src/modules/usermode/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs index 89f1a1eb85..5dc8c8e993 100644 --- a/libafl_qemu/src/modules/usermode/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -150,6 +150,7 @@ use std::pin::Pin; use libafl_qemu_sys::GuestAddr; use object::{Object, ObjectSection}; + use crate::{ emu::EmulatorModules, modules::{AddressFilter, StdAddressFilter}, @@ -284,8 +285,7 @@ impl AsanGiovese { #[inline] #[must_use] - pub fn is_invalid_access(qemu: Qemu, addr: GuestAddr) -> bool - { + pub fn is_invalid_access(qemu: Qemu, addr: GuestAddr) -> bool { const { assert!(N == 1 || N == 2 || N == 4 || N == 8) }; unsafe { From 17059d728c1c509d335eed7d31f29e9f27e48d32 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Wed, 27 Nov 2024 13:43:14 +0100 Subject: [PATCH 08/21] fix fuzzers --- .../binary_only/fuzzbench_qemu/src/fuzzer.rs | 48 ++++++++--------- fuzzers/binary_only/qemu_cmin/src/fuzzer.rs | 51 ++++++++++--------- libafl_qemu/src/qemu/mod.rs | 11 +++- 3 files changed, 61 insertions(+), 49 deletions(-) diff --git a/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs index a26a588bb4..1d522261ba 100644 --- a/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs @@ -55,7 +55,6 @@ use libafl_qemu::{ GuestReg, //snapshot::QemuSnapshotHelper, MmapPerms, - Qemu, QemuExecutor, QemuExitError, QemuExitReason, @@ -175,7 +174,30 @@ fn fuzz( env::remove_var("LD_LIBRARY_PATH"); let args: Vec = env::args().collect(); - let qemu = Qemu::init(&args).expect("QEMU init failed"); + + // Create an observation channel using the coverage map + let mut edges_observer = unsafe { + HitcountsMapObserver::new(VariableMapObserver::from_mut_slice( + "edges", + OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_ALLOCATED_SIZE), + &raw mut MAX_EDGES_FOUND, + )) + .track_indices() + }; + + let modules = tuple_list!( + StdEdgeCoverageModule::builder() + .map_observer(edges_observer.as_mut()) + .build() + .unwrap(), + CmpLogModule::default(), + // QemuAsanHelper::default(asan), + //QemuSnapshotHelper::new() + ); + + let emulator = Emulator::empty().qemu_cli(args).modules(modules).build()?; + let qemu = emulator.qemu(); + // let qemu = Qemu::init(&args).expect("QEMU init failed"); // let (emu, asan) = init_with_asan(&mut args, &mut env).unwrap(); let mut elf_buffer = Vec::new(); @@ -255,16 +277,6 @@ fn fuzz( }, }; - // Create an observation channel using the coverage map - let mut edges_observer = unsafe { - HitcountsMapObserver::new(VariableMapObserver::from_mut_slice( - "edges", - OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_ALLOCATED_SIZE), - &raw mut MAX_EDGES_FOUND, - )) - .track_indices() - }; - // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); @@ -364,18 +376,6 @@ fn fuzz( ExitKind::Ok }; - let modules = tuple_list!( - StdEdgeCoverageModule::builder() - .map_observer(edges_observer.as_mut()) - .build() - .unwrap(), - CmpLogModule::default(), - // QemuAsanHelper::default(asan), - //QemuSnapshotHelper::new() - ); - - let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?; - // Create the executor for an in-process function with one observer for edge coverage and one for the execution time let executor = QemuExecutor::new( emulator, diff --git a/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs b/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs index ba00041efd..6aba2d5dfd 100644 --- a/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs @@ -28,8 +28,8 @@ use libafl_bolts::{ }; use libafl_qemu::{ elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, CallingConvention, - Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitError, QemuExitReason, - QemuForkExecutor, QemuShutdownCause, Regs, + Emulator, GuestAddr, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuForkExecutor, + QemuShutdownCause, Regs, }; use libafl_targets::{EDGES_MAP_DEFAULT_SIZE, EDGES_MAP_PTR}; @@ -113,7 +113,31 @@ pub fn fuzz() -> Result<(), Error> { log::debug!("ARGS: {:#?}", options.args); env::remove_var("LD_LIBRARY_PATH"); - let qemu = Qemu::init(&options.args).unwrap(); + + let mut shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); + + let mut edges_shmem = shmem_provider.new_shmem(EDGES_MAP_DEFAULT_SIZE).unwrap(); + let edges = edges_shmem.as_slice_mut(); + unsafe { EDGES_MAP_PTR = edges.as_mut_ptr() }; + + let mut edges_observer = unsafe { + HitcountsMapObserver::new(ConstMapObserver::from_mut_ptr( + "edges", + NonNull::new(edges.as_mut_ptr()) + .expect("The edge map pointer is null.") + .cast::<[u8; EDGES_MAP_DEFAULT_SIZE]>(), + )) + }; + + let modules = tuple_list!(StdEdgeCoverageChildModule::builder() + .const_map_observer(edges_observer.as_mut()) + .build()?); + + let emulator = Emulator::empty() + .qemu_cli(options.args) + .modules(modules) + .build()?; + let qemu = emulator.qemu(); let mut elf_buffer = Vec::new(); let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap(); @@ -139,8 +163,6 @@ pub fn fuzz() -> Result<(), Error> { let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap(); - let mut shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); - let monitor = SimpleMonitor::with_user_monitor(|s| { println!("{s}"); }); @@ -157,19 +179,6 @@ pub fn fuzz() -> Result<(), Error> { }, }; - let mut edges_shmem = shmem_provider.new_shmem(EDGES_MAP_DEFAULT_SIZE).unwrap(); - let edges = edges_shmem.as_slice_mut(); - unsafe { EDGES_MAP_PTR = edges.as_mut_ptr() }; - - let mut edges_observer = unsafe { - HitcountsMapObserver::new(ConstMapObserver::from_mut_ptr( - "edges", - NonNull::new(edges.as_mut_ptr()) - .expect("The edge map pointer is null.") - .cast::<[u8; EDGES_MAP_DEFAULT_SIZE]>(), - )) - }; - let mut feedback = MaxMapFeedback::new(&edges_observer); #[allow(clippy::let_unit_value)] @@ -223,12 +232,6 @@ pub fn fuzz() -> Result<(), Error> { ExitKind::Ok }; - let modules = tuple_list!(StdEdgeCoverageChildModule::builder() - .const_map_observer(edges_observer.as_mut()) - .build()?); - - let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?; - let mut executor = QemuForkExecutor::new( emulator, &mut harness, diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index a1c18b1b14..73ea2a71f6 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -170,12 +170,21 @@ where } } +impl From<&Vec> for QemuParams +where + T: AsRef, +{ + fn from(cli: &Vec) -> Self { + cli.as_slice().into() + } +} + impl From> for QemuParams where T: AsRef, { fn from(cli: Vec) -> Self { - cli.as_slice().into() + (&cli).into() } } From c50ba05d8299385cd1c1accb8727287afc5c2c06 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Wed, 27 Nov 2024 19:00:14 +0100 Subject: [PATCH 09/21] * refactor asan and asanguest modules to avoid custom init of QEMU and use the module interface instead. * adapt qemu_launcher example to fully work with emulator, since qemu must now be initialized by emulator. --- .../binary_only/qemu_launcher/src/client.rs | 45 +--- .../binary_only/qemu_launcher/src/instance.rs | 33 ++- libafl_concolic/symcc_runtime/Cargo.toml | 2 +- libafl_qemu/Cargo.toml | 4 +- libafl_qemu/src/emu/builder.rs | 22 +- libafl_qemu/src/emu/hooks.rs | 8 + libafl_qemu/src/emu/mod.rs | 12 +- libafl_qemu/src/executor.rs | 4 +- libafl_qemu/src/modules/calls.rs | 2 +- libafl_qemu/src/modules/drcov.rs | 2 +- libafl_qemu/src/modules/edges/helpers.rs | 11 +- libafl_qemu/src/modules/mod.rs | 25 +- libafl_qemu/src/modules/usermode/asan.rs | 172 ++++++------ .../src/modules/usermode/asan_guest.rs | 246 +++++++++--------- .../src/modules/usermode/injections.rs | 2 +- libafl_qemu/src/modules/usermode/mod.rs | 4 +- libafl_qemu/src/modules/usermode/snapshot.rs | 2 +- libafl_qemu/src/qemu/mod.rs | 33 ++- libafl_qemu/src/qemu/systemmode.rs | 2 +- 19 files changed, 321 insertions(+), 310 deletions(-) diff --git a/fuzzers/binary_only/qemu_launcher/src/client.rs b/fuzzers/binary_only/qemu_launcher/src/client.rs index b41c5339bc..02e15a4fe1 100644 --- a/fuzzers/binary_only/qemu_launcher/src/client.rs +++ b/fuzzers/binary_only/qemu_launcher/src/client.rs @@ -10,15 +10,12 @@ use libafl::{ use libafl_bolts::{core_affinity::CoreId, rands::StdRand, tuples::tuple_list}; #[cfg(feature = "injections")] use libafl_qemu::modules::injections::InjectionModule; -use libafl_qemu::{ - modules::{ - asan::{init_qemu_with_asan, AsanModule}, - asan_guest::{init_qemu_with_asan_guest, AsanGuestModule}, - cmplog::CmpLogModule, - DrCovModule, - }, - Qemu, -}; +use libafl_qemu::{modules::{ + asan::AsanModule, + asan_guest::AsanGuestModule, + cmplog::CmpLogModule, + DrCovModule, +}}; use crate::{ harness::Harness, @@ -78,18 +75,6 @@ impl Client<'_> { Err(Error::empty_optional("Multiple ASAN modes configured"))?; } - let (qemu, mut asan, mut asan_lib) = { - if is_asan { - let (emu, asan) = init_qemu_with_asan(&mut args, &mut env)?; - (emu, Some(asan), None) - } else if is_asan_guest { - let (emu, asan_lib) = init_qemu_with_asan_guest(&mut args, &mut env)?; - (emu, None, Some(asan_lib)) - } else { - (Qemu::init(&args)?, None, None) - } - }; - #[cfg(not(feature = "injections"))] let injection_module = None; @@ -109,8 +94,6 @@ impl Client<'_> { } }); - let harness = Harness::init(qemu).expect("Error setting up harness."); - let is_cmplog = self.options.is_cmplog_core(core_id); let extra_tokens = injection_module @@ -120,8 +103,6 @@ impl Client<'_> { let instance_builder = Instance::builder() .options(self.options) - .qemu(qemu) - .harness(harness) .mgr(mgr) .core_id(core_id) .extra_tokens(extra_tokens); @@ -140,7 +121,7 @@ impl Client<'_> { instance_builder.build().run( tuple_list!( CmpLogModule::default(), - AsanModule::default(asan.take().unwrap()), + AsanModule::default(&env), injection_module, ), state, @@ -149,7 +130,7 @@ impl Client<'_> { instance_builder.build().run( tuple_list!( CmpLogModule::default(), - AsanModule::default(asan.take().unwrap()), + AsanModule::default(&env), ), state, ) @@ -159,7 +140,7 @@ impl Client<'_> { instance_builder.build().run( tuple_list!( CmpLogModule::default(), - AsanGuestModule::default(qemu, &asan_lib.take().unwrap()), + AsanGuestModule::default(&env), injection_module ), state, @@ -168,7 +149,7 @@ impl Client<'_> { instance_builder.build().run( tuple_list!( CmpLogModule::default(), - AsanGuestModule::default(qemu, &asan_lib.take().unwrap()), + AsanGuestModule::default(&env), ), state, ) @@ -176,17 +157,17 @@ impl Client<'_> { } else if is_asan { if let Some(injection_module) = injection_module { instance_builder.build().run( - tuple_list!(AsanModule::default(asan.take().unwrap()), injection_module), + tuple_list!(AsanModule::default(&env), injection_module), state, ) } else { instance_builder.build().run( - tuple_list!(AsanModule::default(asan.take().unwrap()),), + tuple_list!(AsanModule::default(&env),), state, ) } } else if is_asan_guest { - let modules = tuple_list!(AsanGuestModule::default(qemu, &asan_lib.take().unwrap())); + let modules = tuple_list!(AsanGuestModule::default(&env)); instance_builder.build().run(modules, state) } else if is_cmplog { if let Some(injection_module) = injection_module { diff --git a/fuzzers/binary_only/qemu_launcher/src/instance.rs b/fuzzers/binary_only/qemu_launcher/src/instance.rs index ab6e5588d6..2fa2c63054 100644 --- a/fuzzers/binary_only/qemu_launcher/src/instance.rs +++ b/fuzzers/binary_only/qemu_launcher/src/instance.rs @@ -37,16 +37,14 @@ use libafl_bolts::{ rands::StdRand, tuples::{tuple_list, Merge, Prepend}, }; -use libafl_qemu::{ - elf::EasyElf, - modules::{ - cmplog::CmpLogObserver, EmulatorModuleTuple, StdAddressFilter, StdEdgeCoverageModule, - }, - Emulator, GuestAddr, Qemu, QemuExecutor, -}; +use libafl_qemu::{elf::EasyElf, modules::{ + cmplog::CmpLogObserver, EmulatorModuleTuple, StdAddressFilter, StdEdgeCoverageModule, +}, Emulator, GuestAddr, Qemu, QemuExecutor}; use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND}; use typed_builder::TypedBuilder; - +use libafl_bolts::tuples::MatchFirstType; +use libafl_qemu::modules::{EdgeCoverageModule, EmulatorModule, NopPageFilter}; +use libafl_qemu::modules::edges::EdgeCoverageFullVariant; use crate::{harness::Harness, options::FuzzerOptions}; pub type ClientState = @@ -62,9 +60,6 @@ pub type ClientMgr = pub struct Instance<'a, M: Monitor> { options: &'a FuzzerOptions, /// The harness. We create it before forking, then `take()` it inside the client. - #[builder(setter(strip_option))] - harness: Option, - qemu: Qemu, mgr: ClientMgr, core_id: CoreId, #[builder(default)] @@ -124,10 +119,18 @@ impl Instance<'_, M> { let edge_coverage_module = StdEdgeCoverageModule::builder() .map_observer(edges_observer.as_mut()) - .address_filter(self.coverage_filter(self.qemu)?) .build()?; let modules = modules.prepend(edge_coverage_module); + let mut emulator = Emulator::empty().modules(modules).build()?; + let harness = Harness::init(emulator.qemu()).expect("Error setting up harness."); + let qemu = emulator.qemu(); + + // update address filter after qemu has been initialized + as EmulatorModule>::update_address_filter(emulator.modules_mut() + .modules_mut() + .match_first_type_mut::>() + .expect("Could not find back the edge module"), qemu, self.coverage_filter(qemu)?); // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); @@ -195,10 +198,6 @@ impl Instance<'_, M> { state.add_metadata(tokens); - let harness = self - .harness - .take() - .expect("The harness can never be None here!"); harness.post_fork(); let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, @@ -208,8 +207,6 @@ impl Instance<'_, M> { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let emulator = Emulator::empty().qemu(self.qemu).modules(modules).build()?; - if let Some(rerun_input) = &self.options.rerun_input { // TODO: We might want to support non-bytes inputs at some point? let bytes = fs::read(rerun_input) diff --git a/libafl_concolic/symcc_runtime/Cargo.toml b/libafl_concolic/symcc_runtime/Cargo.toml index c29e036254..a0f91e025e 100644 --- a/libafl_concolic/symcc_runtime/Cargo.toml +++ b/libafl_concolic/symcc_runtime/Cargo.toml @@ -44,7 +44,7 @@ cmake = { workspace = true } bindgen = { workspace = true } regex = { workspace = true } which = { workspace = true } -symcc_libafl = { workspace = true, default-features = true, version = "0.14.0" } +symcc_libafl = { workspace = true, default-features = true } [lints] workspace = true diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index b699533e0e..97eb7ac09d 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -90,7 +90,7 @@ clippy = ["libafl_qemu_sys/clippy"] [dependencies] libafl = { workspace = true, features = ["std", "derive", "regex"] } libafl_bolts = { workspace = true, features = ["std", "derive"] } -libafl_targets = { workspace = true, default-features = true, version = "0.14.0" } +libafl_targets = { workspace = true, default-features = true } libafl_qemu_sys = { workspace = true } libafl_derive = { workspace = true, default-features = true } @@ -131,7 +131,7 @@ getset = "0.1.3" document-features = { workspace = true, optional = true } [build-dependencies] -libafl_qemu_build = { workspace = true, default-features = true, version = "0.14.0" } +libafl_qemu_build = { workspace = true, default-features = true } pyo3-build-config = { version = "0.23.1", optional = true } rustversion = { workspace = true } bindgen = { workspace = true } diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs index e38be3cdfb..222f9ef178 100644 --- a/libafl_qemu/src/emu/builder.rs +++ b/libafl_qemu/src/emu/builder.rs @@ -4,7 +4,7 @@ use libafl::{ inputs::{HasTargetBytes, UsesInput}, state::{HasExecutions, State}, }; -use libafl_bolts::tuples::{tuple_list, Prepend}; +use libafl_bolts::tuples::{tuple_list, Append, Prepend}; #[cfg(feature = "systemmode")] use crate::FastSnapshotManager; @@ -109,7 +109,7 @@ where CM: CommandManager, ET: EmulatorModuleTuple, { - let qemu_parameters = self.qemu_parameters.ok_or(QemuInitError::EmptyArgs)?; + let mut qemu_parameters = self.qemu_parameters.ok_or(QemuInitError::EmptyArgs)?; let emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; @@ -119,7 +119,7 @@ where unsafe { emulator_modules .modules_mut() - .pre_qemu_init_all(EmulatorModules::::emulator_modules_mut_unchecked()); + .pre_qemu_init_all(EmulatorModules::::emulator_modules_mut_unchecked(), &mut qemu_parameters); } let qemu = Qemu::init(qemu_parameters)?; @@ -163,7 +163,7 @@ where ) } - pub fn add_module(self, module: EM) -> EmulatorBuilder + pub fn prepend_module(self, module: EM) -> EmulatorBuilder where EM: EmulatorModule + Unpin, ET: EmulatorModuleTuple, @@ -177,6 +177,20 @@ where ) } + pub fn append_module(self, module: EM) -> EmulatorBuilder + where + EM: EmulatorModule + Unpin, + ET: EmulatorModuleTuple, + { + EmulatorBuilder::new( + self.modules.append(module), + self.driver, + self.command_manager, + self.snapshot_manager, + self.qemu_parameters, + ) + } + pub fn driver(self, driver: ED2) -> EmulatorBuilder { EmulatorBuilder::new( self.modules, diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs index 7bb703c6b1..3973bbc079 100644 --- a/libafl_qemu/src/emu/hooks.rs +++ b/libafl_qemu/src/emu/hooks.rs @@ -176,6 +176,10 @@ where } } + pub fn qemu_hooks(&self) -> QemuHooks { + self.qemu_hooks + } + pub fn instruction_closure( &mut self, addr: GuestAddr, @@ -1192,6 +1196,10 @@ where self.modules.as_ref().get_ref() } + pub fn hooks(&mut self) -> &EmulatorHooks { + &self.hooks + } + pub fn hooks_mut(&mut self) -> &mut EmulatorHooks { &mut self.hooks } diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index e3e140c0cf..e111d4c739 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -14,14 +14,7 @@ use libafl::{ }; use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestUsize, GuestVirtAddr}; -use crate::{ - breakpoint::{Breakpoint, BreakpointId}, - command::{CommandError, CommandManager, NopCommandManager, StdCommandManager}, - modules::EmulatorModuleTuple, - sync_exit::SyncExit, - Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk, - QemuShutdownCause, Regs, CPU, -}; +use crate::{breakpoint::{Breakpoint, BreakpointId}, command::{CommandError, CommandManager, NopCommandManager, StdCommandManager}, modules::EmulatorModuleTuple, sync_exit::SyncExit, Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk, QemuParams, QemuShutdownCause, Regs, CPU}; mod hooks; pub use hooks::*; @@ -329,6 +322,7 @@ where snapshot_manager: SM, command_manager: CM, ) -> Result { + let mut qemu_parameters: QemuParams = qemu_args.into(); let emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; let mut emulator_modules = EmulatorModules::new(emulator_hooks, modules); @@ -336,7 +330,7 @@ where unsafe { emulator_modules .modules_mut() - .pre_qemu_init_all(EmulatorModules::::emulator_modules_mut_unchecked()); + .pre_qemu_init_all(EmulatorModules::::emulator_modules_mut_unchecked(), &mut qemu_parameters); } let qemu = Qemu::init(qemu_args)?; diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index e5662c913d..7aa9542bc0 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -41,8 +41,10 @@ use libc::siginfo_t; #[cfg(feature = "usermode")] use crate::EmulatorModules; use crate::{ - command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver, Qemu, + command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver, }; +#[cfg(feature = "usermode")] +use crate::Qemu; pub struct QemuExecutor<'a, CM, ED, ET, H, OT, S, SM> where diff --git a/libafl_qemu/src/modules/calls.rs b/libafl_qemu/src/modules/calls.rs index 951f51e97f..8088891050 100644 --- a/libafl_qemu/src/modules/calls.rs +++ b/libafl_qemu/src/modules/calls.rs @@ -402,7 +402,7 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn post_qemu_init(&self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { diff --git a/libafl_qemu/src/modules/drcov.rs b/libafl_qemu/src/modules/drcov.rs index 9621202328..8530575d80 100644 --- a/libafl_qemu/src/modules/drcov.rs +++ b/libafl_qemu/src/modules/drcov.rs @@ -265,7 +265,7 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn post_qemu_init(&self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { diff --git a/libafl_qemu/src/modules/edges/helpers.rs b/libafl_qemu/src/modules/edges/helpers.rs index 74b96f46eb..8cb8a8109f 100644 --- a/libafl_qemu/src/modules/edges/helpers.rs +++ b/libafl_qemu/src/modules/edges/helpers.rs @@ -77,8 +77,9 @@ mod generators { } } + #[allow(unused_variables)] pub fn gen_unique_edge_ids( - _qemu: Qemu, + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: Option<&mut S>, src: GuestAddr, @@ -153,9 +154,9 @@ mod generators { } } - #[allow(clippy::unnecessary_cast)] + #[allow(clippy::unnecessary_cast, unused_variables)] pub fn gen_hashed_edge_ids( - _qemu: Qemu, + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, src: GuestAddr, @@ -205,9 +206,9 @@ mod generators { } } - #[allow(clippy::unnecessary_cast)] + #[allow(clippy::unnecessary_cast, unused_variables)] pub fn gen_hashed_block_ids( - _qemu: Qemu, + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, diff --git a/libafl_qemu/src/modules/mod.rs b/libafl_qemu/src/modules/mod.rs index 06dea61e09..67cc2f8041 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, Qemu, QemuParams}; /// A module for `libafl_qemu`. // TODO remove 'static when specialization will be stable @@ -58,7 +58,10 @@ where /// 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, even before QEMU gets initialized. - fn pre_qemu_init(&self, _emulator_modules: &mut EmulatorModules) + /// + /// It is also possible to edit QEMU parameters, just before QEMU gets initialized. + /// Thus, the module can modify options for QEMU just before it gets initialized. + fn pre_qemu_init(&mut self, _emulator_modules: &mut EmulatorModules, _qemu_params: &mut QemuParams) where ET: EmulatorModuleTuple, { @@ -67,7 +70,7 @@ where /// 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, _qemu: Qemu, _emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&mut self, _qemu: Qemu, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -152,11 +155,11 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool; - fn pre_qemu_init_all(&self, emulator_modules: &mut EmulatorModules) + fn pre_qemu_init_all(&mut self, emulator_modules: &mut EmulatorModules, qemu_params: &mut QemuParams) where ET: EmulatorModuleTuple; - fn post_qemu_init_all(&self, qemu: Qemu, emulator_modules: &mut EmulatorModules) + fn post_qemu_init_all(&mut self, qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple; @@ -211,13 +214,13 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn pre_qemu_init_all(&self, _emulator_modules: &mut EmulatorModules) + fn pre_qemu_init_all(&mut self, _emulator_modules: &mut EmulatorModules, _qemu_params: &mut QemuParams) where ET: EmulatorModuleTuple, { } - fn post_qemu_init_all(&self, _qemu: Qemu, _emulator_modules: &mut EmulatorModules) + fn post_qemu_init_all(&mut self, _qemu: Qemu, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -276,15 +279,15 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS; - fn pre_qemu_init_all(&self, emulator_modules: &mut EmulatorModules) + fn pre_qemu_init_all(&mut self, emulator_modules: &mut EmulatorModules, qemu_params: &mut QemuParams) where ET: EmulatorModuleTuple, { - self.0.pre_qemu_init(emulator_modules); - self.1.pre_qemu_init_all(emulator_modules); + self.0.pre_qemu_init(emulator_modules, qemu_params); + self.1.pre_qemu_init_all(emulator_modules, qemu_params); } - fn post_qemu_init_all(&self, qemu: Qemu, emulator_modules: &mut EmulatorModules) + fn post_qemu_init_all(&mut self, qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { diff --git a/libafl_qemu/src/modules/usermode/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs index 5dc8c8e993..2024da9397 100644 --- a/libafl_qemu/src/modules/usermode/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -11,15 +11,10 @@ use meminterval::{Interval, IntervalTree}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use rangemap::RangeMap; -use crate::{ - modules::{ - calls::FullBacktraceCollector, snapshot::SnapshotModule, EmulatorModule, - EmulatorModuleTuple, - }, - qemu::{MemAccessInfo, QemuInitError}, - sys::TCGTemp, - Qemu, Regs, -}; +use crate::{modules::{ + calls::FullBacktraceCollector, snapshot::SnapshotModule, EmulatorModule, + EmulatorModuleTuple, +}, qemu::MemAccessInfo, sys::TCGTemp, Qemu, QemuParams, Regs}; // TODO at some point, merge parts with libafl_frida @@ -176,8 +171,8 @@ impl core::fmt::Debug for AsanGiovese { } impl AsanGiovese { - unsafe fn map_shadow() { - assert!( + unsafe fn init(self: &mut Pin>, qemu_hooks: QemuHooks) { + assert_ne!( libc::mmap( HIGH_SHADOW_ADDR, HIGH_SHADOW_SIZE, @@ -185,9 +180,9 @@ impl AsanGiovese { MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 - ) != MAP_FAILED + ), MAP_FAILED ); - assert!( + assert_ne!( libc::mmap( LOW_SHADOW_ADDR, LOW_SHADOW_SIZE, @@ -195,9 +190,9 @@ impl AsanGiovese { MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 - ) != MAP_FAILED + ), MAP_FAILED ); - assert!( + assert_ne!( libc::mmap( GAP_SHADOW_ADDR, GAP_SHADOW_SIZE, @@ -205,12 +200,14 @@ impl AsanGiovese { MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 - ) != MAP_FAILED + ), MAP_FAILED ); + + qemu_hooks.add_pre_syscall_hook(self.as_mut(), Self::fake_syscall); } #[must_use] - fn new(qemu_hooks: QemuHooks) -> Pin> { + fn new() -> Pin> { let res = Self { alloc_tree: Mutex::new(IntervalTree::new()), saved_tree: IntervalTree::new(), @@ -219,9 +216,7 @@ impl AsanGiovese { saved_shadow: HashMap::default(), snapshot_shadow: true, // By default, track the dirty shadow pages }; - let mut boxed = Box::pin(res); - qemu_hooks.add_pre_syscall_hook(boxed.as_mut(), Self::fake_syscall); - boxed + Box::pin(res) } extern "C" fn fake_syscall( @@ -646,64 +641,6 @@ impl AsanGiovese { } } -static mut ASAN_INITED: bool = false; - -pub fn init_qemu_with_asan( - args: &mut Vec, - env: &mut [(String, String)], -) -> Result<(Qemu, Pin>), QemuInitError> { - let current = env::current_exe().unwrap(); - let asan_lib = fs::canonicalize(current) - .unwrap() - .parent() - .unwrap() - .join("libqasan.so"); - let asan_lib = asan_lib - .to_str() - .expect("The path to the asan lib is invalid") - .to_string(); - let add_asan = - |e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..]; - - // TODO: adapt since qemu does not take envp anymore as parameter - let mut added = false; - for (k, v) in &mut *env { - if k == "QEMU_SET_ENV" { - let mut new_v = vec![]; - for e in v.split(',') { - if e.starts_with("LD_PRELOAD=") { - added = true; - new_v.push(add_asan(e)); - } else { - new_v.push(e.to_string()); - } - } - *v = new_v.join(","); - } - } - for i in 0..args.len() { - if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") { - added = true; - args[i + 1] = add_asan(&args[i + 1]); - } - } - - if !added { - args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib); - args.insert(1, "-E".into()); - } - - unsafe { - AsanGiovese::map_shadow(); - ASAN_INITED = true; - } - - let qemu = Qemu::init(args.as_slice())?; - let rt = AsanGiovese::new(qemu.hooks()); - - Ok((qemu, rt)) -} - pub enum QemuAsanOptions { None, Snapshot, @@ -715,6 +652,7 @@ pub type AsanChildModule = AsanModule; #[derive(Debug)] pub struct AsanModule { + env: Vec<(String, String)>, enabled: bool, detect_leaks: bool, empty: bool, @@ -724,25 +662,28 @@ pub struct AsanModule { impl AsanModule { #[must_use] - pub fn default(rt: Pin>) -> Self { - Self::new(rt, StdAddressFilter::default(), &QemuAsanOptions::Snapshot) + pub fn default(env: &[(String, String)]) -> Self { + Self::new(StdAddressFilter::default(), &QemuAsanOptions::Snapshot, env) } #[must_use] pub fn new( - mut rt: Pin>, filter: StdAddressFilter, options: &QemuAsanOptions, + env: &[(String, String)], ) -> Self { - assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_qemu_with_asan(...) instead of just Qemu::init(...)"); let (snapshot, detect_leaks) = match options { QemuAsanOptions::None => (false, false), QemuAsanOptions::Snapshot => (true, false), QemuAsanOptions::DetectLeaks => (false, true), QemuAsanOptions::SnapshotDetectLeaks => (true, true), }; + + let mut rt = AsanGiovese::new(); rt.set_snapshot_shadow(snapshot); + Self { + env: env.to_vec(), enabled: true, detect_leaks, empty: true, @@ -753,21 +694,24 @@ impl AsanModule { #[must_use] pub fn with_error_callback( - mut rt: Pin>, filter: StdAddressFilter, error_callback: AsanErrorCallback, options: &QemuAsanOptions, + env: &[(String, String)], ) -> Self { - assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_qemu_with_asan(...) instead of just Qemu::init(...)"); let (snapshot, detect_leaks) = match options { QemuAsanOptions::None => (false, false), QemuAsanOptions::Snapshot => (true, false), QemuAsanOptions::DetectLeaks => (false, true), QemuAsanOptions::SnapshotDetectLeaks => (true, true), }; + + let mut rt = AsanGiovese::new(); rt.set_snapshot_shadow(snapshot); rt.set_error_callback(error_callback); + Self { + env: env.to_vec(), enabled: true, detect_leaks, empty: true, @@ -780,15 +724,15 @@ impl AsanModule { /// The `ASan` error report accesses [`FullBacktraceCollector`] #[must_use] pub unsafe fn with_asan_report( - rt: Pin>, filter: StdAddressFilter, options: &QemuAsanOptions, + env: &[(String, String)], ) -> Self { Self::with_error_callback( - rt, filter, Box::new(|rt, qemu, pc, err| unsafe { asan_report(rt, qemu, pc, &err) }), options, + env, ) } @@ -867,7 +811,61 @@ where type ModuleAddressFilter = StdAddressFilter; const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn post_qemu_init(&self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) + fn pre_qemu_init(&mut self, emulator_modules: &mut EmulatorModules, qemu_params: &mut QemuParams) + where + ET: EmulatorModuleTuple, + { + let mut args: Vec = qemu_params.to_cli(); + + let current = env::current_exe().unwrap(); + let asan_lib = fs::canonicalize(current) + .unwrap() + .parent() + .unwrap() + .join("libqasan.so"); + let asan_lib = asan_lib + .to_str() + .expect("The path to the asan lib is invalid") + .to_string(); + let add_asan = + |e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..]; + + // TODO: adapt since qemu does not take envp anymore as parameter + let mut added = false; + for (k, v) in &mut self.env { + if k == "QEMU_SET_ENV" { + let mut new_v = vec![]; + for e in v.split(',') { + if e.starts_with("LD_PRELOAD=") { + added = true; + new_v.push(add_asan(e)); + } else { + new_v.push(e.to_string()); + } + } + *v = new_v.join(","); + } + } + for i in 0..args.len() { + if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") { + added = true; + args[i + 1] = add_asan(&args[i + 1]); + } + } + + if !added { + args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib); + args.insert(1, "-E".into()); + } + + unsafe { + AsanGiovese::init(&mut self.rt, emulator_modules.hooks().qemu_hooks()); + } + + *qemu_params = QemuParams::Cli(args); + } + + fn post_qemu_init(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { diff --git a/libafl_qemu/src/modules/usermode/asan_guest.rs b/libafl_qemu/src/modules/usermode/asan_guest.rs index 5d53546f43..6b482f7298 100644 --- a/libafl_qemu/src/modules/usermode/asan_guest.rs +++ b/libafl_qemu/src/modules/usermode/asan_guest.rs @@ -12,88 +12,7 @@ use libafl_qemu_sys::{GuestAddr, MapInfo}; #[cfg(not(feature = "clippy"))] use crate::sys::libafl_tcg_gen_asan; -use crate::{ - emu::EmulatorModules, - modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter}, - qemu::{Hook, MemAccessInfo, Qemu, QemuInitError}, - sys::TCGTemp, -}; - -static mut ASAN_GUEST_INITED: bool = false; - -pub fn init_qemu_with_asan_guest( - args: &mut Vec, - env: &mut [(String, String)], -) -> Result<(Qemu, String), QemuInitError> { - let current = env::current_exe().unwrap(); - let asan_lib = fs::canonicalize(current) - .unwrap() - .parent() - .unwrap() - .join("libgasan.so"); - - let asan_lib = env::var_os("CUSTOM_ASAN_PATH") - .map_or(asan_lib, |x| PathBuf::from(x.to_string_lossy().to_string())); - - assert!( - asan_lib.as_path().exists(), - "The ASAN library doesn't exist: {asan_lib:#?}" - ); - - let asan_lib = asan_lib - .to_str() - .expect("The path to the asan lib is invalid") - .to_string(); - - println!("Loading ASAN: {asan_lib:}"); - - let add_asan = - |e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..]; - - let mut added = false; - for (k, v) in &mut *env { - if k == "QEMU_SET_ENV" { - let mut new_v = vec![]; - for e in v.split(',') { - if e.starts_with("LD_PRELOAD=") { - added = true; - new_v.push(add_asan(e)); - } else { - new_v.push(e.to_string()); - } - } - *v = new_v.join(","); - } - } - for i in 0..args.len() { - if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") { - added = true; - args[i + 1] = add_asan(&args[i + 1]); - } - } - - if !added { - args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib); - args.insert(1, "-E".into()); - } - - if env::var("QASAN_DEBUG").is_ok() { - args.push("-E".into()); - args.push("QASAN_DEBUG=1".into()); - } - - if env::var("QASAN_LOG").is_ok() { - args.push("-E".into()); - args.push("QASAN_LOG=1".into()); - } - - unsafe { - ASAN_GUEST_INITED = true; - } - - let emu = Qemu::init(args.as_slice())?; - Ok((emu, asan_lib)) -} +use crate::{emu::EmulatorModules, modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter}, qemu::{Hook, MemAccessInfo, Qemu}, sys::TCGTemp, QemuParams}; #[derive(Clone)] struct QemuAsanGuestMapping { @@ -119,8 +38,10 @@ impl From<&MapInfo> for QemuAsanGuestMapping { #[derive(Debug)] pub struct AsanGuestModule { + env: Vec<(String, String)>, filter: F, - mappings: Vec, + mappings: Option>, + asan_lib: Option, } #[cfg(any( @@ -152,8 +73,8 @@ impl AsanGuestModule { impl AsanGuestModule { #[must_use] - pub fn default(qemu: Qemu, asan: &str) -> Self { - Self::new(qemu, asan, StdAddressFilter::default()) + pub fn default(env: &[(String, String)]) -> Self { + Self::new(env, StdAddressFilter::default()) } } @@ -162,41 +83,8 @@ where F: AddressFilter, { #[must_use] - pub fn new(qemu: Qemu, asan: &str, filter: F) -> Self { - for mapping in qemu.mappings() { - println!("mapping: {mapping:#?}"); - } - - let mappings = qemu - .mappings() - .map(|m| QemuAsanGuestMapping::from(&m)) - .collect::>(); - - for mapping in &mappings { - println!("guest mapping: {mapping:#?}"); - } - - mappings - .iter() - .find(|m| m.start <= Self::HIGH_SHADOW_START && m.end > Self::HIGH_SHADOW_END) - .expect("HighShadow not found, confirm ASAN DSO is loaded in the guest"); - - mappings - .iter() - .find(|m| m.start <= Self::LOW_SHADOW_START && m.end > Self::LOW_SHADOW_END) - .expect("LowShadow not found, confirm ASAN DSO is loaded in the guest"); - - let mappings = mappings - .iter() - .filter(|m| m.path == asan) - .cloned() - .collect::>(); - - for mapping in &mappings { - println!("asan mapping: {mapping:#?}"); - } - - Self { filter, mappings } + pub fn new(env: &[(String, String)], filter: F) -> Self { + Self { env: env.to_vec(), filter, mappings: None, asan_lib: None } } #[must_use] @@ -224,8 +112,10 @@ where } /* Don't sanitize the sanitizer! */ - if h.mappings.iter().any(|m| m.start <= pc && pc < m.end) { - return None; + unsafe { + if h.mappings.as_mut().unwrap_unchecked().iter().any(|m| m.start <= pc && pc < m.end) { + return None; + } } let size = info.size(); @@ -278,6 +168,118 @@ where { type ModuleAddressFilter = F; + fn pre_qemu_init(&mut self, _emulator_modules: &mut EmulatorModules, qemu_params: &mut QemuParams) + where + ET: EmulatorModuleTuple, + { + let mut args = qemu_params.to_cli(); + + let current = env::current_exe().unwrap(); + let asan_lib = fs::canonicalize(current) + .unwrap() + .parent() + .unwrap() + .join("libgasan.so"); + + let asan_lib = env::var_os("CUSTOM_ASAN_PATH") + .map_or(asan_lib, |x| PathBuf::from(x.to_string_lossy().to_string())); + + assert!( + asan_lib.as_path().exists(), + "The ASAN library doesn't exist: {asan_lib:#?}" + ); + + let asan_lib = asan_lib + .to_str() + .expect("The path to the asan lib is invalid") + .to_string(); + + println!("Loading ASAN: {asan_lib:}"); + + let add_asan = + |e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..]; + + let mut added = false; + for (k, v) in &mut self.env { + if k == "QEMU_SET_ENV" { + let mut new_v = vec![]; + for e in v.split(',') { + if e.starts_with("LD_PRELOAD=") { + added = true; + new_v.push(add_asan(e)); + } else { + new_v.push(e.to_string()); + } + } + *v = new_v.join(","); + } + } + for i in 0..args.len() { + if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") { + added = true; + args[i + 1] = add_asan(&args[i + 1]); + } + } + + if !added { + args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib); + args.insert(1, "-E".into()); + } + + if env::var("QASAN_DEBUG").is_ok() { + args.push("-E".into()); + args.push("QASAN_DEBUG=1".into()); + } + + if env::var("QASAN_LOG").is_ok() { + args.push("-E".into()); + args.push("QASAN_LOG=1".into()); + } + + *qemu_params = QemuParams::Cli(args); + + self.asan_lib = Some(asan_lib); + } + + fn post_qemu_init(&mut self, qemu: Qemu, _emulator_modules: &mut EmulatorModules) + where + ET: EmulatorModuleTuple, + { + for mapping in qemu.mappings() { + println!("mapping: {mapping:#?}"); + } + + let mappings = qemu + .mappings() + .map(|m| QemuAsanGuestMapping::from(&m)) + .collect::>(); + + for mapping in &mappings { + println!("guest mapping: {mapping:#?}"); + } + + mappings + .iter() + .find(|m| m.start <= Self::HIGH_SHADOW_START && m.end > Self::HIGH_SHADOW_END) + .expect("HighShadow not found, confirm ASAN DSO is loaded in the guest"); + + mappings + .iter() + .find(|m| m.start <= Self::LOW_SHADOW_START && m.end > Self::LOW_SHADOW_END) + .expect("LowShadow not found, confirm ASAN DSO is loaded in the guest"); + + let mappings = mappings + .iter() + .filter(|m| &m.path == self.asan_lib.as_ref().unwrap()) + .cloned() + .collect::>(); + + for mapping in &mappings { + println!("asan mapping: {mapping:#?}"); + } + + } + fn first_exec( &mut self, _qemu: Qemu, diff --git a/libafl_qemu/src/modules/usermode/injections.rs b/libafl_qemu/src/modules/usermode/injections.rs index fb13bb7b33..9de76866e2 100644 --- a/libafl_qemu/src/modules/usermode/injections.rs +++ b/libafl_qemu/src/modules/usermode/injections.rs @@ -265,7 +265,7 @@ where { type ModuleAddressFilter = NopAddressFilter; - fn post_qemu_init(&self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { diff --git a/libafl_qemu/src/modules/usermode/mod.rs b/libafl_qemu/src/modules/usermode/mod.rs index 1d9cc503d2..91dd6eb491 100644 --- a/libafl_qemu/src/modules/usermode/mod.rs +++ b/libafl_qemu/src/modules/usermode/mod.rs @@ -11,9 +11,9 @@ pub use snapshot::{IntervalSnapshotFilter, SnapshotModule}; #[cfg(not(cpu_target = "hexagon"))] pub mod asan; #[cfg(not(cpu_target = "hexagon"))] -pub use asan::{init_qemu_with_asan, AsanModule}; +pub use asan::{AsanModule}; #[cfg(not(cpu_target = "hexagon"))] pub mod asan_guest; #[cfg(not(cpu_target = "hexagon"))] -pub use asan_guest::{init_qemu_with_asan_guest, AsanGuestModule}; +pub use asan_guest::AsanGuestModule; diff --git a/libafl_qemu/src/modules/usermode/snapshot.rs b/libafl_qemu/src/modules/usermode/snapshot.rs index b71a3cbc46..ca85ad2672 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 post_qemu_init(&self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index 73ea2a71f6..fcf4b1c3a3 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -188,6 +188,23 @@ where } } +impl QemuParams { + pub fn to_cli(&self) -> Vec { + match self { + QemuParams::Config(cfg) => { + cfg + .to_string() + .split(' ') + .map(ToString::to_string) + .collect() + } + QemuParams::Cli(cli) => { + cli.clone() + } + } + } +} + impl MemAccessInfo { #[must_use] pub fn memop(&self) -> libafl_qemu_sys::MemOp { @@ -500,23 +517,17 @@ impl Qemu { { let params: QemuParams = params.into(); - let args: Vec = match params { + match ¶ms { QemuParams::Config(cfg) => { - let qemu_args: Vec = cfg - .to_string() - .split(' ') - .map(ToString::to_string) - .collect(); - QEMU_CONFIG.set(cfg.clone()).map_err(|_| { unreachable!("QEMU_CONFIG was already set but Qemu was not init!") })?; - - qemu_args - } - QemuParams::Cli(cli) => cli, + }, + QemuParams::Cli(_) => {}, }; + let args = params.to_cli(); + if args.is_empty() { return Err(QemuInitError::EmptyArgs); } diff --git a/libafl_qemu/src/qemu/systemmode.rs b/libafl_qemu/src/qemu/systemmode.rs index 25e956cb43..ddb2bfcf15 100644 --- a/libafl_qemu/src/qemu/systemmode.rs +++ b/libafl_qemu/src/qemu/systemmode.rs @@ -163,7 +163,7 @@ impl CPU { /// # Safety /// no check is done on the correctness of the operation. /// if a problem occurred during the operation, there will be no feedback - pub fn write_mem_unchecked(&self, addr: GuestAddr, buf: &[u8]) { + pub unsafe fn write_mem_unchecked(&self, addr: GuestAddr, buf: &[u8]) { // TODO use gdbstub's target_cpu_memory_rw_debug unsafe { libafl_qemu_sys::cpu_memory_rw_debug( From 8d8631eb55aa26a0351143a8ad2f5d8e91311f99 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Wed, 27 Nov 2024 19:02:46 +0100 Subject: [PATCH 10/21] fmt --- .../binary_only/qemu_launcher/src/client.rs | 26 +++++--------- .../binary_only/qemu_launcher/src/instance.rs | 17 +++++---- libafl_qemu/src/emu/builder.rs | 7 ++-- libafl_qemu/src/emu/mod.rs | 16 ++++++--- libafl_qemu/src/executor.rs | 4 +-- libafl_qemu/src/modules/mod.rs | 35 +++++++++++++------ libafl_qemu/src/modules/usermode/asan.rs | 29 ++++++++++----- .../src/modules/usermode/asan_guest.rs | 30 ++++++++++++---- libafl_qemu/src/modules/usermode/mod.rs | 2 +- libafl_qemu/src/qemu/mod.rs | 20 +++++------ 10 files changed, 113 insertions(+), 73 deletions(-) diff --git a/fuzzers/binary_only/qemu_launcher/src/client.rs b/fuzzers/binary_only/qemu_launcher/src/client.rs index 02e15a4fe1..77e982eb53 100644 --- a/fuzzers/binary_only/qemu_launcher/src/client.rs +++ b/fuzzers/binary_only/qemu_launcher/src/client.rs @@ -10,12 +10,9 @@ use libafl::{ use libafl_bolts::{core_affinity::CoreId, rands::StdRand, tuples::tuple_list}; #[cfg(feature = "injections")] use libafl_qemu::modules::injections::InjectionModule; -use libafl_qemu::{modules::{ - asan::AsanModule, - asan_guest::AsanGuestModule, - cmplog::CmpLogModule, - DrCovModule, -}}; +use libafl_qemu::modules::{ + asan::AsanModule, asan_guest::AsanGuestModule, cmplog::CmpLogModule, DrCovModule, +}; use crate::{ harness::Harness, @@ -128,10 +125,7 @@ impl Client<'_> { ) } else { instance_builder.build().run( - tuple_list!( - CmpLogModule::default(), - AsanModule::default(&env), - ), + tuple_list!(CmpLogModule::default(), AsanModule::default(&env),), state, ) } @@ -147,10 +141,7 @@ impl Client<'_> { ) } else { instance_builder.build().run( - tuple_list!( - CmpLogModule::default(), - AsanGuestModule::default(&env), - ), + tuple_list!(CmpLogModule::default(), AsanGuestModule::default(&env),), state, ) } @@ -161,10 +152,9 @@ impl Client<'_> { state, ) } else { - instance_builder.build().run( - tuple_list!(AsanModule::default(&env),), - state, - ) + instance_builder + .build() + .run(tuple_list!(AsanModule::default(&env),), state) } } else if is_asan_guest { let modules = tuple_list!(AsanGuestModule::default(&env)); diff --git a/fuzzers/binary_only/qemu_launcher/src/instance.rs b/fuzzers/binary_only/qemu_launcher/src/instance.rs index 2fa2c63054..36be5f44f3 100644 --- a/fuzzers/binary_only/qemu_launcher/src/instance.rs +++ b/fuzzers/binary_only/qemu_launcher/src/instance.rs @@ -35,16 +35,19 @@ use libafl_bolts::{ core_affinity::CoreId, ownedref::OwnedMutSlice, rands::StdRand, - tuples::{tuple_list, Merge, Prepend}, + tuples::{tuple_list, MatchFirstType, Merge, Prepend}, +}; +use libafl_qemu::{ + elf::EasyElf, + modules::{ + cmplog::CmpLogObserver, edges::EdgeCoverageFullVariant, EdgeCoverageModule, EmulatorModule, + EmulatorModuleTuple, NopPageFilter, StdAddressFilter, StdEdgeCoverageModule, + }, + Emulator, GuestAddr, Qemu, QemuExecutor, }; -use libafl_qemu::{elf::EasyElf, modules::{ - cmplog::CmpLogObserver, EmulatorModuleTuple, StdAddressFilter, StdEdgeCoverageModule, -}, Emulator, GuestAddr, Qemu, QemuExecutor}; use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND}; use typed_builder::TypedBuilder; -use libafl_bolts::tuples::MatchFirstType; -use libafl_qemu::modules::{EdgeCoverageModule, EmulatorModule, NopPageFilter}; -use libafl_qemu::modules::edges::EdgeCoverageFullVariant; + use crate::{harness::Harness, options::FuzzerOptions}; pub type ClientState = diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs index 222f9ef178..f77ae71fa2 100644 --- a/libafl_qemu/src/emu/builder.rs +++ b/libafl_qemu/src/emu/builder.rs @@ -117,9 +117,10 @@ where // TODO: fix things there properly. The biggest issue being that it creates 2 mut ref to the module with the callback being called unsafe { - emulator_modules - .modules_mut() - .pre_qemu_init_all(EmulatorModules::::emulator_modules_mut_unchecked(), &mut qemu_parameters); + emulator_modules.modules_mut().pre_qemu_init_all( + EmulatorModules::::emulator_modules_mut_unchecked(), + &mut qemu_parameters, + ); } let qemu = Qemu::init(qemu_parameters)?; diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index e111d4c739..75d66a59fe 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -14,7 +14,14 @@ use libafl::{ }; use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestUsize, GuestVirtAddr}; -use crate::{breakpoint::{Breakpoint, BreakpointId}, command::{CommandError, CommandManager, NopCommandManager, StdCommandManager}, modules::EmulatorModuleTuple, sync_exit::SyncExit, Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk, QemuParams, QemuShutdownCause, Regs, CPU}; +use crate::{ + breakpoint::{Breakpoint, BreakpointId}, + command::{CommandError, CommandManager, NopCommandManager, StdCommandManager}, + modules::EmulatorModuleTuple, + sync_exit::SyncExit, + Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk, QemuParams, + QemuShutdownCause, Regs, CPU, +}; mod hooks; pub use hooks::*; @@ -328,9 +335,10 @@ where // TODO: fix things there properly. The biggest issue being that it creates 2 mut ref to the module with the callback being called unsafe { - emulator_modules - .modules_mut() - .pre_qemu_init_all(EmulatorModules::::emulator_modules_mut_unchecked(), &mut qemu_parameters); + emulator_modules.modules_mut().pre_qemu_init_all( + EmulatorModules::::emulator_modules_mut_unchecked(), + &mut qemu_parameters, + ); } let qemu = Qemu::init(qemu_args)?; diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 7aa9542bc0..c0fc3f44c3 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -40,11 +40,9 @@ use libc::siginfo_t; #[cfg(feature = "usermode")] use crate::EmulatorModules; -use crate::{ - command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver, -}; #[cfg(feature = "usermode")] use crate::Qemu; +use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver}; pub struct QemuExecutor<'a, CM, ED, ET, H, OT, S, SM> where diff --git a/libafl_qemu/src/modules/mod.rs b/libafl_qemu/src/modules/mod.rs index 67cc2f8041..8b470b8142 100644 --- a/libafl_qemu/src/modules/mod.rs +++ b/libafl_qemu/src/modules/mod.rs @@ -61,8 +61,11 @@ where /// /// It is also possible to edit QEMU parameters, just before QEMU gets initialized. /// Thus, the module can modify options for QEMU just before it gets initialized. - fn pre_qemu_init(&mut self, _emulator_modules: &mut EmulatorModules, _qemu_params: &mut QemuParams) - where + fn pre_qemu_init( + &mut self, + _emulator_modules: &mut EmulatorModules, + _qemu_params: &mut QemuParams, + ) where ET: EmulatorModuleTuple, { } @@ -155,8 +158,11 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool; - fn pre_qemu_init_all(&mut self, emulator_modules: &mut EmulatorModules, qemu_params: &mut QemuParams) - where + fn pre_qemu_init_all( + &mut self, + emulator_modules: &mut EmulatorModules, + qemu_params: &mut QemuParams, + ) where ET: EmulatorModuleTuple; fn post_qemu_init_all(&mut self, qemu: Qemu, emulator_modules: &mut EmulatorModules) @@ -214,14 +220,20 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn pre_qemu_init_all(&mut self, _emulator_modules: &mut EmulatorModules, _qemu_params: &mut QemuParams) - where + fn pre_qemu_init_all( + &mut self, + _emulator_modules: &mut EmulatorModules, + _qemu_params: &mut QemuParams, + ) where ET: EmulatorModuleTuple, { } - fn post_qemu_init_all(&mut self, _qemu: Qemu, _emulator_modules: &mut EmulatorModules) - where + fn post_qemu_init_all( + &mut self, + _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + ) where ET: EmulatorModuleTuple, { } @@ -279,8 +291,11 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS; - fn pre_qemu_init_all(&mut self, emulator_modules: &mut EmulatorModules, qemu_params: &mut QemuParams) - where + fn pre_qemu_init_all( + &mut self, + emulator_modules: &mut EmulatorModules, + qemu_params: &mut QemuParams, + ) where ET: EmulatorModuleTuple, { self.0.pre_qemu_init(emulator_modules, qemu_params); diff --git a/libafl_qemu/src/modules/usermode/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs index 2024da9397..a62940d925 100644 --- a/libafl_qemu/src/modules/usermode/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -11,10 +11,15 @@ use meminterval::{Interval, IntervalTree}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use rangemap::RangeMap; -use crate::{modules::{ - calls::FullBacktraceCollector, snapshot::SnapshotModule, EmulatorModule, - EmulatorModuleTuple, -}, qemu::MemAccessInfo, sys::TCGTemp, Qemu, QemuParams, Regs}; +use crate::{ + modules::{ + calls::FullBacktraceCollector, snapshot::SnapshotModule, EmulatorModule, + EmulatorModuleTuple, + }, + qemu::MemAccessInfo, + sys::TCGTemp, + Qemu, QemuParams, Regs, +}; // TODO at some point, merge parts with libafl_frida @@ -180,7 +185,8 @@ impl AsanGiovese { MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 - ), MAP_FAILED + ), + MAP_FAILED ); assert_ne!( libc::mmap( @@ -190,7 +196,8 @@ impl AsanGiovese { MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 - ), MAP_FAILED + ), + MAP_FAILED ); assert_ne!( libc::mmap( @@ -200,7 +207,8 @@ impl AsanGiovese { MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 - ), MAP_FAILED + ), + MAP_FAILED ); qemu_hooks.add_pre_syscall_hook(self.as_mut(), Self::fake_syscall); @@ -811,8 +819,11 @@ where type ModuleAddressFilter = StdAddressFilter; const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn pre_qemu_init(&mut self, emulator_modules: &mut EmulatorModules, qemu_params: &mut QemuParams) - where + fn pre_qemu_init( + &mut self, + emulator_modules: &mut EmulatorModules, + qemu_params: &mut QemuParams, + ) where ET: EmulatorModuleTuple, { let mut args: Vec = qemu_params.to_cli(); diff --git a/libafl_qemu/src/modules/usermode/asan_guest.rs b/libafl_qemu/src/modules/usermode/asan_guest.rs index 6b482f7298..3f3c67bd31 100644 --- a/libafl_qemu/src/modules/usermode/asan_guest.rs +++ b/libafl_qemu/src/modules/usermode/asan_guest.rs @@ -12,7 +12,13 @@ use libafl_qemu_sys::{GuestAddr, MapInfo}; #[cfg(not(feature = "clippy"))] use crate::sys::libafl_tcg_gen_asan; -use crate::{emu::EmulatorModules, modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter}, qemu::{Hook, MemAccessInfo, Qemu}, sys::TCGTemp, QemuParams}; +use crate::{ + emu::EmulatorModules, + modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter}, + qemu::{Hook, MemAccessInfo, Qemu}, + sys::TCGTemp, + QemuParams, +}; #[derive(Clone)] struct QemuAsanGuestMapping { @@ -84,7 +90,12 @@ where { #[must_use] pub fn new(env: &[(String, String)], filter: F) -> Self { - Self { env: env.to_vec(), filter, mappings: None, asan_lib: None } + Self { + env: env.to_vec(), + filter, + mappings: None, + asan_lib: None, + } } #[must_use] @@ -113,7 +124,12 @@ where /* Don't sanitize the sanitizer! */ unsafe { - if h.mappings.as_mut().unwrap_unchecked().iter().any(|m| m.start <= pc && pc < m.end) { + if h.mappings + .as_mut() + .unwrap_unchecked() + .iter() + .any(|m| m.start <= pc && pc < m.end) + { return None; } } @@ -168,8 +184,11 @@ where { type ModuleAddressFilter = F; - fn pre_qemu_init(&mut self, _emulator_modules: &mut EmulatorModules, qemu_params: &mut QemuParams) - where + fn pre_qemu_init( + &mut self, + _emulator_modules: &mut EmulatorModules, + qemu_params: &mut QemuParams, + ) where ET: EmulatorModuleTuple, { let mut args = qemu_params.to_cli(); @@ -277,7 +296,6 @@ where for mapping in &mappings { println!("asan mapping: {mapping:#?}"); } - } fn first_exec( diff --git a/libafl_qemu/src/modules/usermode/mod.rs b/libafl_qemu/src/modules/usermode/mod.rs index 91dd6eb491..93a8f5247c 100644 --- a/libafl_qemu/src/modules/usermode/mod.rs +++ b/libafl_qemu/src/modules/usermode/mod.rs @@ -11,7 +11,7 @@ pub use snapshot::{IntervalSnapshotFilter, SnapshotModule}; #[cfg(not(cpu_target = "hexagon"))] pub mod asan; #[cfg(not(cpu_target = "hexagon"))] -pub use asan::{AsanModule}; +pub use asan::AsanModule; #[cfg(not(cpu_target = "hexagon"))] pub mod asan_guest; diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index fcf4b1c3a3..2cd93f44a9 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -191,16 +191,12 @@ where impl QemuParams { pub fn to_cli(&self) -> Vec { match self { - QemuParams::Config(cfg) => { - cfg - .to_string() - .split(' ') - .map(ToString::to_string) - .collect() - } - QemuParams::Cli(cli) => { - cli.clone() - } + QemuParams::Config(cfg) => cfg + .to_string() + .split(' ') + .map(ToString::to_string) + .collect(), + QemuParams::Cli(cli) => cli.clone(), } } } @@ -522,8 +518,8 @@ impl Qemu { QEMU_CONFIG.set(cfg.clone()).map_err(|_| { unreachable!("QEMU_CONFIG was already set but Qemu was not init!") })?; - }, - QemuParams::Cli(_) => {}, + } + QemuParams::Cli(_) => {} }; let args = params.to_cli(); From f2491c1eb98afdc095d7a6dd996dda613c3cf256 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Wed, 27 Nov 2024 19:03:37 +0100 Subject: [PATCH 11/21] clippy --- libafl_qemu/src/emu/hooks.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs index 3973bbc079..64c035695d 100644 --- a/libafl_qemu/src/emu/hooks.rs +++ b/libafl_qemu/src/emu/hooks.rs @@ -176,6 +176,7 @@ where } } + #[must_use] pub fn qemu_hooks(&self) -> QemuHooks { self.qemu_hooks } From 76480c191b065d5e62e1af4cc3f5d64be5b9921d Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Wed, 27 Nov 2024 19:33:17 +0100 Subject: [PATCH 12/21] fix qemu_coverage --- .../binary_only/qemu_coverage/src/fuzzer.rs | 137 +++++++++--------- 1 file changed, 69 insertions(+), 68 deletions(-) diff --git a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs index eb6a97c7af..10dbbefe2b 100644 --- a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs @@ -28,7 +28,7 @@ use libafl_bolts::{ use libafl_qemu::{ elf::EasyElf, modules::{drcov::DrCovModule, StdAddressFilter}, - ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor, + ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, QemuExecutor, QemuExitReason, QemuRWError, QemuShutdownCause, Regs, }; @@ -120,79 +120,92 @@ pub fn fuzz() { env::remove_var("LD_LIBRARY_PATH"); - let qemu = Qemu::init(&options.args).unwrap(); + let mut run_client = + |state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _>, core_id| { + let mut cov_path = options.coverage_path.clone(); - let mut elf_buffer = Vec::new(); - let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap(); + let emulator_modules = tuple_list!(DrCovModule::builder() + .filter(StdAddressFilter::default()) + .filename(cov_path.clone()) + .full_trace(false) + .build()); - let test_one_input_ptr = elf - .resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr()) - .expect("Symbol LLVMFuzzerTestOneInput not found"); - log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}"); + let emulator = Emulator::empty() + .qemu_cli(options.args.clone()) + .modules(emulator_modules) + .build() + .expect("QEMU initialization failed"); + let qemu = emulator.qemu(); + + let mut elf_buffer = Vec::new(); + let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap(); - qemu.entry_break(test_one_input_ptr); + let test_one_input_ptr = elf + .resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr()) + .expect("Symbol LLVMFuzzerTestOneInput not found"); + log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}"); - for m in qemu.mappings() { - log::debug!( + qemu.entry_break(test_one_input_ptr); + + for m in qemu.mappings() { + log::debug!( "Mapping: 0x{:016x}-0x{:016x}, {}", m.start(), m.end(), m.path().unwrap_or(&"".to_string()) ); - } + } - let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap(); - log::debug!("Break at {pc:#x}"); + let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap(); + log::debug!("Break at {pc:#x}"); - let ret_addr: GuestAddr = qemu.read_return_address().unwrap(); - log::debug!("Return address = {ret_addr:#x}"); + let ret_addr: GuestAddr = qemu.read_return_address().unwrap(); + log::debug!("Return address = {ret_addr:#x}"); - qemu.set_breakpoint(ret_addr); + qemu.set_breakpoint(ret_addr); - let input_addr = qemu - .map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite) - .unwrap(); - log::debug!("Placing input at {input_addr:#x}"); + let input_addr = qemu + .map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite) + .unwrap(); + log::debug!("Placing input at {input_addr:#x}"); - let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap(); + let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap(); - let reset = |buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> { - unsafe { - let _ = qemu.write_mem(input_addr, buf); - qemu.write_reg(Regs::Pc, test_one_input_ptr)?; - qemu.write_reg(Regs::Sp, stack_ptr)?; - qemu.write_return_address(ret_addr)?; - qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?; - qemu.write_function_argument(CallingConvention::Cdecl, 1, len)?; + let reset = |buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> { + unsafe { + let _ = qemu.write_mem(input_addr, buf); + qemu.write_reg(Regs::Pc, test_one_input_ptr)?; + qemu.write_reg(Regs::Sp, stack_ptr)?; + qemu.write_return_address(ret_addr)?; + qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?; + qemu.write_function_argument(CallingConvention::Cdecl, 1, len)?; - match qemu.run() { - Ok(QemuExitReason::Breakpoint(_)) => {} - Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => { - process::exit(0) - } - _ => panic!("Unexpected QEMU exit."), - } + match qemu.run() { + Ok(QemuExitReason::Breakpoint(_)) => {} + Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => { + process::exit(0) + } + _ => panic!("Unexpected QEMU exit."), + } - Ok(()) - } - }; - - let mut harness = - |_emulator: &mut Emulator<_, _, _, _, _>, _state: &mut _, input: &BytesInput| { - let target = input.target_bytes(); - let mut buf = target.as_slice(); - let mut len = buf.len(); - if len > MAX_INPUT_SIZE { - buf = &buf[0..MAX_INPUT_SIZE]; - len = MAX_INPUT_SIZE; - } - let len = len as GuestReg; - reset(buf, len).unwrap(); - ExitKind::Ok - }; + Ok(()) + } + }; + + let mut harness = + |_emulator: &mut Emulator<_, _, _, _, _>, _state: &mut _, input: &BytesInput| { + let target = input.target_bytes(); + let mut buf = target.as_slice(); + let mut len = buf.len(); + if len > MAX_INPUT_SIZE { + buf = &buf[0..MAX_INPUT_SIZE]; + len = MAX_INPUT_SIZE; + } + let len = len as GuestReg; + reset(buf, len).unwrap(); + ExitKind::Ok + }; - let mut run_client = - |state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _>, core_id| { let core_idx = options .cores .position(core_id) @@ -229,23 +242,11 @@ pub fn fuzz() { let scheduler = QueueScheduler::new(); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let mut cov_path = options.coverage_path.clone(); let coverage_name = cov_path.file_stem().unwrap().to_str().unwrap(); let coverage_extension = cov_path.extension().unwrap_or_default().to_str().unwrap(); let core = core_id.0; cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}")); - let emulator_modules = tuple_list!(DrCovModule::builder() - .filter(StdAddressFilter::default()) - .filename(cov_path) - .full_trace(false) - .build()); - - let emulator = Emulator::empty() - .qemu(qemu) - .modules(emulator_modules) - .build()?; - let mut executor = QemuExecutor::new( emulator, &mut harness, From 46f0da7778a44d158113795dac92feb4812f1124 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Wed, 27 Nov 2024 19:39:31 +0100 Subject: [PATCH 13/21] fmt --- fuzzers/binary_only/qemu_coverage/src/fuzzer.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs index 10dbbefe2b..3905458b33 100644 --- a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs @@ -149,11 +149,11 @@ pub fn fuzz() { for m in qemu.mappings() { log::debug!( - "Mapping: 0x{:016x}-0x{:016x}, {}", - m.start(), - m.end(), - m.path().unwrap_or(&"".to_string()) - ); + "Mapping: 0x{:016x}-0x{:016x}, {}", + m.start(), + m.end(), + m.path().unwrap_or(&"".to_string()) + ); } let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap(); @@ -182,9 +182,9 @@ pub fn fuzz() { match qemu.run() { Ok(QemuExitReason::Breakpoint(_)) => {} - Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => { - process::exit(0) - } + Ok(QemuExitReason::End(QemuShutdownCause::HostSignal( + Signal::SigInterrupt, + ))) => process::exit(0), _ => panic!("Unexpected QEMU exit."), } From 3464e44e351ac03b53950df0edf36b4e5ac6bcb5 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Thu, 28 Nov 2024 10:04:09 +0100 Subject: [PATCH 14/21] forgot qemu args in launcher --- fuzzers/binary_only/qemu_launcher/src/instance.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fuzzers/binary_only/qemu_launcher/src/instance.rs b/fuzzers/binary_only/qemu_launcher/src/instance.rs index 36be5f44f3..3166f45082 100644 --- a/fuzzers/binary_only/qemu_launcher/src/instance.rs +++ b/fuzzers/binary_only/qemu_launcher/src/instance.rs @@ -125,7 +125,10 @@ impl Instance<'_, M> { .build()?; let modules = modules.prepend(edge_coverage_module); - let mut emulator = Emulator::empty().modules(modules).build()?; + let mut emulator = Emulator::empty() + .qemu_cli(self.options.args.clone()) + .modules(modules) + .build()?; let harness = Harness::init(emulator.qemu()).expect("Error setting up harness."); let qemu = emulator.qemu(); From 005475b16279af5c6b8bec48dbbc44148dc87c0e Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Thu, 28 Nov 2024 11:22:05 +0100 Subject: [PATCH 15/21] use correct args --- .../binary_only/qemu_launcher/src/client.rs | 23 ++++++++---- .../binary_only/qemu_launcher/src/instance.rs | 12 ++++--- libafl_qemu/src/emu/builder.rs | 36 +++++-------------- libafl_qemu/src/emu/mod.rs | 18 ++++++---- 4 files changed, 43 insertions(+), 46 deletions(-) diff --git a/fuzzers/binary_only/qemu_launcher/src/client.rs b/fuzzers/binary_only/qemu_launcher/src/client.rs index 77e982eb53..8c63a778c4 100644 --- a/fuzzers/binary_only/qemu_launcher/src/client.rs +++ b/fuzzers/binary_only/qemu_launcher/src/client.rs @@ -112,10 +112,13 @@ impl Client<'_> { .filename(drcov.clone()) .full_trace(true) .build(); - instance_builder.build().run(tuple_list!(drcov), state) + instance_builder + .build() + .run(args, tuple_list!(drcov), state) } else if is_asan && is_cmplog { if let Some(injection_module) = injection_module { instance_builder.build().run( + args, tuple_list!( CmpLogModule::default(), AsanModule::default(&env), @@ -125,6 +128,7 @@ impl Client<'_> { ) } else { instance_builder.build().run( + args, tuple_list!(CmpLogModule::default(), AsanModule::default(&env),), state, ) @@ -132,6 +136,7 @@ impl Client<'_> { } else if is_asan_guest && is_cmplog { if let Some(injection_module) = injection_module { instance_builder.build().run( + args, tuple_list!( CmpLogModule::default(), AsanGuestModule::default(&env), @@ -141,6 +146,7 @@ impl Client<'_> { ) } else { instance_builder.build().run( + args, tuple_list!(CmpLogModule::default(), AsanGuestModule::default(&env),), state, ) @@ -148,34 +154,37 @@ impl Client<'_> { } else if is_asan { if let Some(injection_module) = injection_module { instance_builder.build().run( + args, tuple_list!(AsanModule::default(&env), injection_module), state, ) } else { instance_builder .build() - .run(tuple_list!(AsanModule::default(&env),), state) + .run(args, tuple_list!(AsanModule::default(&env),), state) } } else if is_asan_guest { - let modules = tuple_list!(AsanGuestModule::default(&env)); - instance_builder.build().run(modules, state) + instance_builder + .build() + .run(args, tuple_list!(AsanGuestModule::default(&env)), state) } else if is_cmplog { if let Some(injection_module) = injection_module { instance_builder.build().run( + args, tuple_list!(CmpLogModule::default(), injection_module), state, ) } else { instance_builder .build() - .run(tuple_list!(CmpLogModule::default()), state) + .run(args, tuple_list!(CmpLogModule::default()), state) } } else if let Some(injection_module) = injection_module { instance_builder .build() - .run(tuple_list!(injection_module), state) + .run(args, tuple_list!(injection_module), state) } else { - instance_builder.build().run(tuple_list!(), state) + instance_builder.build().run(args, tuple_list!(), state) } } } diff --git a/fuzzers/binary_only/qemu_launcher/src/instance.rs b/fuzzers/binary_only/qemu_launcher/src/instance.rs index 3166f45082..de3ee3b43d 100644 --- a/fuzzers/binary_only/qemu_launcher/src/instance.rs +++ b/fuzzers/binary_only/qemu_launcher/src/instance.rs @@ -106,7 +106,12 @@ impl Instance<'_, M> { } #[allow(clippy::too_many_lines)] - pub fn run(&mut self, modules: ET, state: Option) -> Result<(), Error> + pub fn run( + &mut self, + args: Vec, + modules: ET, + state: Option, + ) -> Result<(), Error> where ET: EmulatorModuleTuple + Debug, { @@ -125,10 +130,7 @@ impl Instance<'_, M> { .build()?; let modules = modules.prepend(edge_coverage_module); - let mut emulator = Emulator::empty() - .qemu_cli(self.options.args.clone()) - .modules(modules) - .build()?; + let mut emulator = Emulator::empty().qemu_cli(args).modules(modules).build()?; let harness = Harness::init(emulator.qemu()).expect("Error setting up harness."); let qemu = emulator.qemu(); diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs index f77ae71fa2..5a7c7883b5 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, EmulatorHooks, EmulatorModules, NopEmulatorDriver, NopSnapshotManager, Qemu, - QemuHooks, QemuInitError, QemuParams, StdEmulatorDriver, StdSnapshotManager, + Emulator, NopEmulatorDriver, NopSnapshotManager, QemuInitError, QemuParams, StdEmulatorDriver, + StdSnapshotManager, }; #[derive(Clone, Debug)] @@ -109,31 +109,13 @@ where CM: CommandManager, ET: EmulatorModuleTuple, { - let mut qemu_parameters = self.qemu_parameters.ok_or(QemuInitError::EmptyArgs)?; - - let emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; - - let mut emulator_modules = EmulatorModules::new(emulator_hooks, self.modules); - - // TODO: fix things there properly. The biggest issue being that it creates 2 mut ref to the module with the callback being called - unsafe { - emulator_modules.modules_mut().pre_qemu_init_all( - EmulatorModules::::emulator_modules_mut_unchecked(), - &mut qemu_parameters, - ); - } - - let qemu = Qemu::init(qemu_parameters)?; - - unsafe { - Ok(Emulator::new_with_qemu( - qemu, - emulator_modules, - self.driver, - self.snapshot_manager, - self.command_manager, - )) - } + Emulator::new( + self.qemu_parameters.ok_or(QemuInitError::EmptyArgs)?, + self.modules, + self.driver, + self.snapshot_manager, + self.command_manager, + ) } } diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index 75d66a59fe..e046824537 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -322,14 +322,18 @@ where S: UsesInput + Unpin, { #[allow(clippy::must_use_candidate, clippy::similar_names)] - pub fn new( - qemu_args: &[String], + pub fn new( + qemu_params: T, modules: ET, driver: ED, snapshot_manager: SM, command_manager: CM, - ) -> Result { - let mut qemu_parameters: QemuParams = qemu_args.into(); + ) -> Result + where + T: Into, + { + let mut qemu_params = qemu_params.into(); + let emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; let mut emulator_modules = EmulatorModules::new(emulator_hooks, modules); @@ -337,11 +341,11 @@ where unsafe { emulator_modules.modules_mut().pre_qemu_init_all( EmulatorModules::::emulator_modules_mut_unchecked(), - &mut qemu_parameters, + &mut qemu_params, ); } - let qemu = Qemu::init(qemu_args)?; + let qemu = Qemu::init(qemu_params)?; unsafe { Ok(Self::new_with_qemu( @@ -360,7 +364,7 @@ where /// # Safety /// /// pre-init qemu hooks should be run before calling this. - pub(crate) unsafe fn new_with_qemu( + unsafe fn new_with_qemu( qemu: Qemu, emulator_modules: Pin>>, driver: ED, From ea840cfb97734f4c85854d2a0f4ac6d4c6322b66 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Thu, 28 Nov 2024 15:18:27 +0100 Subject: [PATCH 16/21] adapt api --- fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs b/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs index f77e91507a..8509918976 100644 --- a/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs +++ b/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs @@ -108,7 +108,7 @@ pub fn fuzz() { // Initialize QEMU Emulator let emu = Emulator::builder() .qemu_cli(args) - .add_module( + .append_module( StdEdgeCoverageModule::builder() .map_observer(edges_observer.as_mut()) .build()?, From 2998445e02aa27686af1757b545920d3f7a75a13 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Thu, 28 Nov 2024 19:22:49 +0100 Subject: [PATCH 17/21] testing stuff --- fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs b/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs index 8509918976..af876f411d 100644 --- a/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs +++ b/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs @@ -108,13 +108,12 @@ pub fn fuzz() { // Initialize QEMU Emulator let emu = Emulator::builder() .qemu_cli(args) - .append_module( + .prepend_module( StdEdgeCoverageModule::builder() .map_observer(edges_observer.as_mut()) .build()?, ) - .build() - .unwrap(); + .build()?; // Set breakpoints of interest with corresponding commands. emu.add_breakpoint( From 0fe297748d310f1d0805aaf7c49c82a11ca18c5e Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Thu, 5 Dec 2024 15:35:05 +0100 Subject: [PATCH 18/21] new builder? --- .../binary_only/qemu_coverage/src/fuzzer.rs | 198 +++++++++--------- libafl_qemu/Cargo.toml | 1 + libafl_qemu/src/emu/builder.rs | 63 +++--- libafl_qemu/src/emu/mod.rs | 5 +- libafl_qemu/src/qemu/config.rs | 16 +- libafl_qemu/src/qemu/error.rs | 5 + libafl_qemu/src/qemu/mod.rs | 103 +++++---- 7 files changed, 207 insertions(+), 184 deletions(-) diff --git a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs index 18ee14db0b..5017b0360c 100644 --- a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs @@ -126,101 +126,101 @@ pub fn fuzz() { let mut run_client = |state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _>, client_description: ClientDescription| { - let mut cov_path = options.coverage_path.clone(); - - let emulator_modules = tuple_list!(DrCovModule::builder() - .filter(StdAddressFilter::default()) - .filename(cov_path.clone()) - .full_trace(false) - .build()); - - let emulator = Emulator::empty() - .qemu_cli(options.args.clone()) - .modules(emulator_modules) - .build() - .expect("QEMU initialization failed"); - let qemu = emulator.qemu(); - - let mut elf_buffer = Vec::new(); - let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap(); - - let test_one_input_ptr = elf - .resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr()) - .expect("Symbol LLVMFuzzerTestOneInput not found"); - log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}"); - - qemu.entry_break(test_one_input_ptr); - - for m in qemu.mappings() { - log::debug!( - "Mapping: 0x{:016x}-0x{:016x}, {}", - m.start(), - m.end(), - m.path().unwrap_or(&"".to_string()) - ); - } + let mut cov_path = options.coverage_path.clone(); + + let emulator_modules = tuple_list!(DrCovModule::builder() + .filter(StdAddressFilter::default()) + .filename(cov_path.clone()) + .full_trace(false) + .build()); + + let emulator = Emulator::empty() + .qemu_cli(options.args.clone()) + .modules(emulator_modules) + .build() + .expect("QEMU initialization failed"); + let qemu = emulator.qemu(); + + let mut elf_buffer = Vec::new(); + let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap(); + + let test_one_input_ptr = elf + .resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr()) + .expect("Symbol LLVMFuzzerTestOneInput not found"); + log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}"); + + qemu.entry_break(test_one_input_ptr); + + for m in qemu.mappings() { + log::debug!( + "Mapping: 0x{:016x}-0x{:016x}, {}", + m.start(), + m.end(), + m.path().unwrap_or(&"".to_string()) + ); + } - let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap(); - log::debug!("Break at {pc:#x}"); + let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap(); + log::debug!("Break at {pc:#x}"); - let ret_addr: GuestAddr = qemu.read_return_address().unwrap(); - log::debug!("Return address = {ret_addr:#x}"); + let ret_addr: GuestAddr = qemu.read_return_address().unwrap(); + log::debug!("Return address = {ret_addr:#x}"); - qemu.set_breakpoint(ret_addr); + qemu.set_breakpoint(ret_addr); - let input_addr = qemu - .map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite) - .unwrap(); - log::debug!("Placing input at {input_addr:#x}"); + let input_addr = qemu + .map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite) + .unwrap(); + log::debug!("Placing input at {input_addr:#x}"); - let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap(); + let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap(); - let reset = |buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> { - unsafe { - let _ = qemu.write_mem(input_addr, buf); - qemu.write_reg(Regs::Pc, test_one_input_ptr)?; - qemu.write_reg(Regs::Sp, stack_ptr)?; - qemu.write_return_address(ret_addr)?; - qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?; - qemu.write_function_argument(CallingConvention::Cdecl, 1, len)?; + let reset = |buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> { + unsafe { + let _ = qemu.write_mem(input_addr, buf); + qemu.write_reg(Regs::Pc, test_one_input_ptr)?; + qemu.write_reg(Regs::Sp, stack_ptr)?; + qemu.write_return_address(ret_addr)?; + qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?; + qemu.write_function_argument(CallingConvention::Cdecl, 1, len)?; - match qemu.run() { - Ok(QemuExitReason::Breakpoint(_)) => {} - Ok(QemuExitReason::End(QemuShutdownCause::HostSignal( - Signal::SigInterrupt, - ))) => process::exit(0), - _ => panic!("Unexpected QEMU exit."), - } + match qemu.run() { + Ok(QemuExitReason::Breakpoint(_)) => {} + Ok(QemuExitReason::End(QemuShutdownCause::HostSignal( + Signal::SigInterrupt, + ))) => process::exit(0), + _ => panic!("Unexpected QEMU exit."), + } - Ok(()) + Ok(()) + } + }; + + let mut harness = + |_emulator: &mut Emulator<_, _, _, _, _>, _state: &mut _, input: &BytesInput| { + let target = input.target_bytes(); + let mut buf = target.as_slice(); + let mut len = buf.len(); + if len > MAX_INPUT_SIZE { + buf = &buf[0..MAX_INPUT_SIZE]; + len = MAX_INPUT_SIZE; } + let len = len as GuestReg; + reset(buf, len).unwrap(); + ExitKind::Ok }; - let mut harness = - |_emulator: &mut Emulator<_, _, _, _, _>, _state: &mut _, input: &BytesInput| { - let target = input.target_bytes(); - let mut buf = target.as_slice(); - let mut len = buf.len(); - if len > MAX_INPUT_SIZE { - buf = &buf[0..MAX_INPUT_SIZE]; - len = MAX_INPUT_SIZE; - } - let len = len as GuestReg; - reset(buf, len).unwrap(); - ExitKind::Ok - }; - - let core_id = client_description.core_id(); - let core_idx = options - .cores - .position(core_id) - .expect("Failed to get core index"); - let files = corpus_files - .iter() - .skip(files_per_core * core_idx) - .take(files_per_core) - .map(|x| x.path()) - .collect::>(); + let core_id = client_description.core_id(); + let core_idx = options + .cores + .position(core_id) + .expect("Failed to get core index"); + let files = corpus_files + .iter() + .skip(files_per_core * core_idx) + .take(files_per_core) + .map(|x| x.path()) + .collect::>(); if files.is_empty() { mgr.send_exiting()?; @@ -247,21 +247,21 @@ pub fn fuzz() { let scheduler = QueueScheduler::new(); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let coverage_name = cov_path.file_stem().unwrap().to_str().unwrap(); - let coverage_extension = cov_path.extension().unwrap_or_default().to_str().unwrap(); - let core = core_id.0; - cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}")); - - let mut executor = QemuExecutor::new( - emulator, - &mut harness, - (), - &mut fuzzer, - &mut state, - &mut mgr, - options.timeout, - ) - .expect("Failed to create QemuExecutor"); + let coverage_name = cov_path.file_stem().unwrap().to_str().unwrap(); + let coverage_extension = cov_path.extension().unwrap_or_default().to_str().unwrap(); + let core = core_id.0; + cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}")); + + let mut executor = QemuExecutor::new( + emulator, + &mut harness, + (), + &mut fuzzer, + &mut state, + &mut mgr, + options.timeout, + ) + .expect("Failed to create QemuExecutor"); if state.must_load_initial_inputs() { state diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index d107dc4d0b..d61d478a95 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -125,6 +125,7 @@ bytes-utils = "0.1.4" typed-builder = { workspace = true } memmap2 = "0.9.5" getset = "0.1.3" +derive_builder = "0.20.2" # Document all features of this crate (for `cargo doc`) document-features = { workspace = true, optional = true } diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs index 5a7c7883b5..767071f217 100644 --- a/libafl_qemu/src/emu/builder.rs +++ b/libafl_qemu/src/emu/builder.rs @@ -1,4 +1,4 @@ -use std::{fmt::Debug, marker::PhantomData}; +use std::marker::PhantomData; use libafl::{ inputs::{HasTargetBytes, UsesInput}, @@ -10,14 +10,14 @@ use libafl_bolts::tuples::{tuple_list, Append, Prepend}; use crate::FastSnapshotManager; use crate::{ command::{CommandManager, NopCommandManager, StdCommandManager}, - config::QemuConfig, modules::{EmulatorModule, EmulatorModuleTuple}, Emulator, NopEmulatorDriver, NopSnapshotManager, QemuInitError, QemuParams, StdEmulatorDriver, StdSnapshotManager, }; +use crate::config::QemuConfigBuilder; -#[derive(Clone, Debug)] -pub struct EmulatorBuilder +#[derive(Clone)] +pub struct EmulatorBuilder where S: UsesInput, { @@ -25,11 +25,11 @@ where driver: ED, snapshot_manager: SM, command_manager: CM, - qemu_parameters: Option, + qemu_parameters: QB, phantom: PhantomData, } -impl EmulatorBuilder +impl EmulatorBuilder where S: UsesInput, { @@ -40,14 +40,14 @@ where driver: NopEmulatorDriver, snapshot_manager: NopSnapshotManager, command_manager: NopCommandManager, - qemu_parameters: None, + qemu_parameters: QemuConfigBuilder::default(), phantom: PhantomData, } } } #[cfg(feature = "usermode")] -impl EmulatorBuilder, StdEmulatorDriver, (), S, StdSnapshotManager> +impl EmulatorBuilder, StdEmulatorDriver, (), QemuConfigBuilder, S, StdSnapshotManager> where S: State + HasExecutions + Unpin, S::Input: HasTargetBytes, @@ -60,14 +60,14 @@ where command_manager: StdCommandManager::default(), snapshot_manager: StdSnapshotManager::default(), driver: StdEmulatorDriver::builder().build(), - qemu_parameters: None, + qemu_parameters: QemuConfigBuilder::default(), phantom: PhantomData, } } } #[cfg(feature = "systemmode")] -impl EmulatorBuilder, StdEmulatorDriver, (), S, StdSnapshotManager> +impl EmulatorBuilder, StdEmulatorDriver, (), QemuConfigBuilder, S, StdSnapshotManager> where S: State + HasExecutions + Unpin, S::Input: HasTargetBytes, @@ -78,12 +78,12 @@ where command_manager: StdCommandManager::default(), snapshot_manager: FastSnapshotManager::default(), driver: StdEmulatorDriver::builder().build(), - qemu_parameters: None, + qemu_parameters: Some(QemuConfigBuilder::default()), phantom: PhantomData, } } } -impl EmulatorBuilder +impl EmulatorBuilder where S: UsesInput + Unpin, { @@ -92,7 +92,7 @@ where driver: ED, command_manager: CM, snapshot_manager: SM, - qemu_parameters: Option, + qemu_parameters: QB, ) -> Self { Self { modules, @@ -108,9 +108,12 @@ where where CM: CommandManager, ET: EmulatorModuleTuple, + QB: TryInto { + let qemu_params: QemuParams = self.qemu_parameters.try_into()?; + Emulator::new( - self.qemu_parameters.ok_or(QemuInitError::EmptyArgs)?, + qemu_params, self.modules, self.driver, self.snapshot_manager, @@ -119,34 +122,26 @@ where } } -impl EmulatorBuilder +impl EmulatorBuilder where CM: CommandManager, S: UsesInput + Unpin, { #[must_use] - pub fn qemu_config(self, qemu_config: QemuConfig) -> EmulatorBuilder { - EmulatorBuilder::new( - self.modules, - self.driver, - self.command_manager, - self.snapshot_manager, - Some(QemuParams::Config(qemu_config)), - ) - } - - #[must_use] - pub fn qemu_cli(self, qemu_cli: Vec) -> EmulatorBuilder { + pub fn qemu_builder(self, qemu_config: QB2) -> EmulatorBuilder + where + QB2: Into + { EmulatorBuilder::new( self.modules, self.driver, self.command_manager, self.snapshot_manager, - Some(QemuParams::Cli(qemu_cli)), + qemu_config, ) } - pub fn prepend_module(self, module: EM) -> EmulatorBuilder + pub fn prepend_module(self, module: EM) -> EmulatorBuilder where EM: EmulatorModule + Unpin, ET: EmulatorModuleTuple, @@ -160,7 +155,7 @@ where ) } - pub fn append_module(self, module: EM) -> EmulatorBuilder + pub fn append_module(self, module: EM) -> EmulatorBuilder where EM: EmulatorModule + Unpin, ET: EmulatorModuleTuple, @@ -174,7 +169,7 @@ where ) } - pub fn driver(self, driver: ED2) -> EmulatorBuilder { + pub fn driver(self, driver: ED2) -> EmulatorBuilder { EmulatorBuilder::new( self.modules, driver, @@ -184,7 +179,7 @@ where ) } - pub fn command_manager(self, command_manager: CM2) -> EmulatorBuilder + pub fn command_manager(self, command_manager: CM2) -> EmulatorBuilder where CM2: CommandManager, { @@ -197,7 +192,7 @@ where ) } - pub fn modules(self, modules: ET2) -> EmulatorBuilder { + pub fn modules(self, modules: ET2) -> EmulatorBuilder { EmulatorBuilder::new( modules, self.driver, @@ -210,7 +205,7 @@ where pub fn snapshot_manager( self, snapshot_manager: SM2, - ) -> EmulatorBuilder { + ) -> EmulatorBuilder { EmulatorBuilder::new( self.modules, self.driver, diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index e046824537..37916591fc 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -44,6 +44,7 @@ pub use usermode::*; mod systemmode; #[cfg(feature = "systemmode")] pub use systemmode::*; +use crate::config::QemuConfigBuilder; #[derive(Clone, Copy)] pub enum GuestAddrKind { @@ -244,7 +245,7 @@ where { #[must_use] pub fn empty( - ) -> EmulatorBuilder { + ) -> EmulatorBuilder { EmulatorBuilder::empty() } } @@ -256,7 +257,7 @@ where { #[must_use] pub fn builder( - ) -> EmulatorBuilder, StdEmulatorDriver, (), S, StdSnapshotManager> { + ) -> EmulatorBuilder, StdEmulatorDriver, (), QemuConfigBuilder, S, StdSnapshotManager> { EmulatorBuilder::default() } } diff --git a/libafl_qemu/src/qemu/config.rs b/libafl_qemu/src/qemu/config.rs index 6ab1e51f5d..28c48f81e0 100644 --- a/libafl_qemu/src/qemu/config.rs +++ b/libafl_qemu/src/qemu/config.rs @@ -3,11 +3,10 @@ use core::{ fmt::{Display, Formatter}, }; use std::path::{Path, PathBuf}; - +use derive_builder::Builder; use getset::Getters; use libafl_derive; use strum_macros; -use typed_builder::TypedBuilder; #[cfg(feature = "systemmode")] #[derive(Debug, strum_macros::Display, Clone)] @@ -37,7 +36,7 @@ pub enum DiskImageFileFormat { Raw, } -#[derive(Debug, Clone, Default, TypedBuilder)] +#[derive(Debug, Clone, Default, Builder)] pub struct Drive { #[builder(default, setter(strip_option, into))] file: Option, @@ -296,16 +295,7 @@ impl> From for Program { } } -#[derive(Debug, Clone, libafl_derive::Display, TypedBuilder, Getters)] -#[builder(builder_method( - doc = "Since Qemu is a zero sized struct, this is not a completely standard builder pattern. \ - The Qemu configuration is not stored in the Qemu struct after build() but in QEMU_CONFIG \ - Therefore, to use the derived builder and avoid boilerplate a builder for QemuConfig is \ - derived. \ - The QemuConfig::builder is called in Qemu::builder() which is the only place where it should \ - be called, in this way the one to one matching of Qemu and QemuConfig is enforced. Therefore \ - its visibility is pub(crate)" -))] +#[derive(Debug, Clone, libafl_derive::Display, Builder, Getters)] #[getset(get = "pub")] pub struct QemuConfig { #[cfg(feature = "systemmode")] diff --git a/libafl_qemu/src/qemu/error.rs b/libafl_qemu/src/qemu/error.rs index d3931c3182..31d7d18330 100644 --- a/libafl_qemu/src/qemu/error.rs +++ b/libafl_qemu/src/qemu/error.rs @@ -4,6 +4,7 @@ use std::fmt::Display; use libafl_qemu_sys::{CPUStatePtr, GuestAddr}; use crate::CallingConvention; +use crate::config::QemuConfigBuilderError; #[derive(Debug)] pub enum QemuError { @@ -16,6 +17,7 @@ pub enum QemuError { pub enum QemuInitError { MultipleInstances, EmptyArgs, + ConfigurationError(QemuConfigBuilderError), TooManyArgs(usize), } @@ -59,6 +61,9 @@ impl Display for QemuInitError { QemuInitError::EmptyArgs => { write!(f, "QEMU emulator args cannot be empty") } + QemuInitError::ConfigurationError(config_error) => { + write!(f, "QEMU Configuration error: {config_error}") + } QemuInitError::TooManyArgs(n) => { write!( f, diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index 7e5bca26f7..e9f14704cf 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -52,11 +52,39 @@ pub use systemmode::*; mod hooks; pub use hooks::*; +use crate::config::QemuConfigBuilder; static mut QEMU_IS_INITIALIZED: bool = false; pub(super) static QEMU_CONFIG: OnceLock = OnceLock::new(); +#[allow(clippy::vec_box)] +static mut GDB_COMMANDS: Vec> = Vec::new(); + +pub trait HookId { + fn remove(&self, invalidate_block: bool) -> bool; +} + +pub trait ArchExtras { + fn read_return_address(&self) -> Result; + fn write_return_address(&self, val: T) -> Result<(), QemuRWError> + where + T: Into; + fn read_function_argument( + &self, + conv: CallingConvention, + idx: u8, + ) -> Result; + fn write_function_argument( + &self, + conv: CallingConvention, + idx: i32, + val: T, + ) -> Result<(), QemuRWError> + where + T: Into; +} + #[derive(Debug, Clone)] pub enum QemuExitReason { /// QEMU ended for some internal reason @@ -93,18 +121,6 @@ pub struct QemuMemoryChunk { cpu: Option, } -#[allow(clippy::vec_box)] -static mut GDB_COMMANDS: Vec> = Vec::new(); - -unsafe extern "C" fn gdb_cmd(data: *mut c_void, buf: *mut u8, len: usize) -> bool { - unsafe { - let closure = &mut *(data as *mut Box FnMut(Qemu, &'r str) -> bool>); - let cmd = std::str::from_utf8_unchecked(std::slice::from_raw_parts(buf, len)); - let qemu = Qemu::get_unchecked(); - closure(qemu, cmd) - } -} - #[derive(Debug, Clone)] pub enum QemuShutdownCause { None, @@ -137,13 +153,18 @@ pub enum CallingConvention { Cdecl, } -pub trait HookId { - fn remove(&self, invalidate_block: bool) -> bool; -} - #[derive(Debug)] pub struct HookData(u64); +unsafe extern "C" fn gdb_cmd(data: *mut c_void, buf: *mut u8, len: usize) -> bool { + unsafe { + let closure = &mut *(data as *mut Box FnMut(Qemu, &'r str) -> bool>); + let cmd = std::str::from_utf8_unchecked(std::slice::from_raw_parts(buf, len)); + let qemu = Qemu::get_unchecked(); + closure(qemu, cmd) + } +} + impl Display for QemuExitReason { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { @@ -161,6 +182,15 @@ impl From for QemuParams { } } +impl TryFrom for QemuParams { + type Error = QemuInitError; + + fn try_from(config_builder: QemuConfigBuilder) -> Result { + Ok(QemuParams::Config(config_builder.build() + .or_else(|e| Err(QemuInitError::ConfigurationError(e)))?)) + } +} + impl From<&[T]> for QemuParams where T: AsRef, @@ -251,26 +281,6 @@ impl From for MemAccessInfo { } } -pub trait ArchExtras { - fn read_return_address(&self) -> Result; - fn write_return_address(&self, val: T) -> Result<(), QemuRWError> - where - T: Into; - fn read_function_argument( - &self, - conv: CallingConvention, - idx: u8, - ) -> Result; - fn write_function_argument( - &self, - conv: CallingConvention, - idx: i32, - val: T, - ) -> Result<(), QemuRWError> - where - T: Into; -} - #[allow(clippy::unused_self)] impl CPU { #[must_use] @@ -504,8 +514,29 @@ impl From for HookData { } } +// impl QemuBuilder { +// pub fn new(params: T) -> Self { +// Self { +// params +// } +// } +// } + +// impl QemuBuilder +// where +// T: Into +// { +// pub fn build(self) -> Result { +// +// } +// } + #[allow(clippy::unused_self)] impl Qemu { + // pub fn builder() -> QemuBuilder { + // QemuBuilder::default() + // } + #[allow(clippy::must_use_candidate, clippy::similar_names)] pub fn init(params: T) -> Result where From b7f5240a0231e8c75663539a370a693fac3590a7 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Thu, 5 Dec 2024 16:44:03 +0100 Subject: [PATCH 19/21] use closure for qemu config from emulator builder. --- .../fuzzbench_fork_qemu/src/fuzzer.rs | 2 +- fuzzers/binary_only/qemu_cmin/src/fuzzer.rs | 2 +- .../binary_only/qemu_coverage/src/fuzzer.rs | 2 +- .../binary_only/qemu_launcher/src/instance.rs | 5 +- .../qemu_baremetal/src/fuzzer_breakpoint.rs | 2 +- .../qemu_baremetal/src/fuzzer_low_level.rs | 42 +++++---- .../qemu_baremetal/src/fuzzer_sync_exit.rs | 2 +- .../qemu_linux_kernel/src/fuzzer.rs | 7 +- .../qemu_linux_process/src/fuzzer.rs | 2 +- libafl_qemu/Cargo.toml | 5 +- libafl_qemu/libafl_qemu_build/Cargo.toml | 5 +- libafl_qemu/libafl_qemu_sys/Cargo.toml | 5 +- libafl_qemu/src/emu/builder.rs | 89 ++++++++++++++----- libafl_qemu/src/emu/mod.rs | 21 ++++- libafl_qemu/src/qemu/config.rs | 22 ++++- libafl_qemu/src/qemu/error.rs | 23 ++++- libafl_qemu/src/qemu/mod.rs | 37 +++----- libafl_sugar/src/qemu.rs | 4 +- 18 files changed, 177 insertions(+), 100 deletions(-) diff --git a/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs b/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs index 838f8d8df5..c6ec8acfd6 100644 --- a/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs +++ b/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs @@ -176,7 +176,7 @@ fn fuzz( ); let emulator = Emulator::empty() - .qemu_cli(args) + .qemu_config(|_| args) .modules(emulator_modules) .build()?; diff --git a/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs b/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs index 6aba2d5dfd..5c797249a7 100644 --- a/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs @@ -134,7 +134,7 @@ pub fn fuzz() -> Result<(), Error> { .build()?); let emulator = Emulator::empty() - .qemu_cli(options.args) + .qemu_config(|_| options.args) .modules(modules) .build()?; let qemu = emulator.qemu(); diff --git a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs index 5017b0360c..8cd99d6c41 100644 --- a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs @@ -135,7 +135,7 @@ pub fn fuzz() { .build()); let emulator = Emulator::empty() - .qemu_cli(options.args.clone()) + .qemu_config(|_| options.args.clone()) .modules(emulator_modules) .build() .expect("QEMU initialization failed"); diff --git a/fuzzers/binary_only/qemu_launcher/src/instance.rs b/fuzzers/binary_only/qemu_launcher/src/instance.rs index d42eb1b2a7..d55cabafb2 100644 --- a/fuzzers/binary_only/qemu_launcher/src/instance.rs +++ b/fuzzers/binary_only/qemu_launcher/src/instance.rs @@ -129,7 +129,10 @@ impl Instance<'_, M> { .build()?; let modules = modules.prepend(edge_coverage_module); - let mut emulator = Emulator::empty().qemu_cli(args).modules(modules).build()?; + let mut emulator = Emulator::empty() + .modules(modules) + .qemu_config(|_| args) + .build()?; let harness = Harness::init(emulator.qemu()).expect("Error setting up harness."); let qemu = emulator.qemu(); diff --git a/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs b/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs index ea93264cab..f6e5b09687 100644 --- a/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs +++ b/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs @@ -107,7 +107,7 @@ pub fn fuzz() { // Initialize QEMU Emulator let emu = Emulator::builder() - .qemu_cli(args) + .qemu_config(|_| args) .prepend_module( StdEdgeCoverageModule::builder() .map_observer(edges_observer.as_mut()) diff --git a/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs b/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs index a5b41589ef..787fa9fb84 100644 --- a/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs +++ b/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs @@ -29,9 +29,8 @@ use libafl_bolts::{ AsSlice, }; use libafl_qemu::{ - config, config::QemuConfig, elf::EasyElf, executor::QemuExecutor, - modules::edges::StdEdgeCoverageModuleBuilder, Emulator, GuestPhysAddr, QemuExitError, - QemuExitReason, QemuRWError, QemuShutdownCause, Regs, + config, elf::EasyElf, executor::QemuExecutor, modules::edges::StdEdgeCoverageModuleBuilder, + Emulator, GuestPhysAddr, QemuExitError, QemuExitReason, QemuRWError, QemuShutdownCause, Regs, }; use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND}; @@ -93,30 +92,29 @@ pub fn fuzz() { .track_indices() }; - // Initialize QEMU - let qemu_config = QemuConfig::builder() - .machine("mps2-an385") - .monitor(config::Monitor::Null) - .kernel(format!("{target_dir}/example.elf")) - .serial(config::Serial::Null) - .no_graphic(true) - .snapshot(true) - .drives([config::Drive::builder() - .interface(config::DriveInterface::None) - .format(config::DiskImageFileFormat::Qcow2) - .file(format!("{target_dir}/dummy.qcow2")) - .build()]) - .start_cpu(false) - .build(); - - // .expect("Failed to initialized QEMU"); - let emulator_modules = tuple_list!(StdEdgeCoverageModuleBuilder::default() .map_observer(edges_observer.as_mut()) .build()?); let emulator = Emulator::empty() - .qemu_config(qemu_config) + .qemu_config(|mut qemu_config| { + qemu_config + .machine("mps2-an385") + .monitor(config::Monitor::Null) + .kernel(format!("{target_dir}/example.elf")) + .serial(config::Serial::Null) + .no_graphic(true) + .snapshot(true) + .drives([config::Drive::builder() + .interface(config::DriveInterface::None) + .format(config::DiskImageFileFormat::Qcow2) + .file(format!("{target_dir}/dummy.qcow2")) + .build() + .expect("Could not build drives")]) + .start_cpu(false); + + qemu_config + }) .modules(emulator_modules) .build()?; diff --git a/fuzzers/full_system/qemu_baremetal/src/fuzzer_sync_exit.rs b/fuzzers/full_system/qemu_baremetal/src/fuzzer_sync_exit.rs index fe6bc314f4..411de715da 100644 --- a/fuzzers/full_system/qemu_baremetal/src/fuzzer_sync_exit.rs +++ b/fuzzers/full_system/qemu_baremetal/src/fuzzer_sync_exit.rs @@ -63,7 +63,7 @@ pub fn fuzz() { .build()?); let emu = Emulator::builder() - .qemu_cli(args) + .qemu_config(|_| args) .modules(modules) .build()?; diff --git a/fuzzers/full_system/qemu_linux_kernel/src/fuzzer.rs b/fuzzers/full_system/qemu_linux_kernel/src/fuzzer.rs index bb33be5f5f..633714ec94 100644 --- a/fuzzers/full_system/qemu_linux_kernel/src/fuzzer.rs +++ b/fuzzers/full_system/qemu_linux_kernel/src/fuzzer.rs @@ -67,14 +67,9 @@ pub fn fuzz() { CmpLogModule::default(), ); - // let driver = StdEmulatorDriver::builder() - // .print_commands(true) - // .build(); - let emu = Emulator::builder() - .qemu_cli(args) + .qemu_config(|_| args) .modules(modules) - // .driver(driver) .build()?; let devices = emu.list_devices(); diff --git a/fuzzers/full_system/qemu_linux_process/src/fuzzer.rs b/fuzzers/full_system/qemu_linux_process/src/fuzzer.rs index ec8cf7beed..b64eca7ecf 100644 --- a/fuzzers/full_system/qemu_linux_process/src/fuzzer.rs +++ b/fuzzers/full_system/qemu_linux_process/src/fuzzer.rs @@ -70,7 +70,7 @@ pub fn fuzz() { ); let emu = Emulator::builder() - .qemu_cli(args) + .qemu_config(|_| args) .modules(modules) .build()?; diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index d61d478a95..773a06de7f 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "libafl_qemu" version.workspace = true -authors = ["Andrea Fioraldi "] +authors = [ + "Andrea Fioraldi ", + "Romain Malmain ", +] description = "QEMU user backend library for LibAFL" documentation = "https://docs.rs/libafl_qemu" repository = "https://github.com/AFLplusplus/LibAFL/" diff --git a/libafl_qemu/libafl_qemu_build/Cargo.toml b/libafl_qemu/libafl_qemu_build/Cargo.toml index 7dbca22934..8b6c31b074 100644 --- a/libafl_qemu/libafl_qemu_build/Cargo.toml +++ b/libafl_qemu/libafl_qemu_build/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "libafl_qemu_build" version.workspace = true -authors = ["Andrea Fioraldi "] +authors = [ + "Andrea Fioraldi ", + "Romain Malmain ", +] description = "Builder for LibAFL QEMU" documentation = "https://docs.rs/libafl_qemu_build" repository = "https://github.com/AFLplusplus/LibAFL/" diff --git a/libafl_qemu/libafl_qemu_sys/Cargo.toml b/libafl_qemu/libafl_qemu_sys/Cargo.toml index 0ad98885bd..b5730812d8 100644 --- a/libafl_qemu/libafl_qemu_sys/Cargo.toml +++ b/libafl_qemu/libafl_qemu_sys/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "libafl_qemu_sys" version.workspace = true -authors = ["Andrea Fioraldi "] +authors = [ + "Andrea Fioraldi ", + "Romain Malmain ", +] description = "C to Rust bindings for the LibAFL QEMU bridge" documentation = "https://docs.rs/libafl_qemu_sys" repository = "https://github.com/AFLplusplus/LibAFL/" diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs index 767071f217..65e3d51b13 100644 --- a/libafl_qemu/src/emu/builder.rs +++ b/libafl_qemu/src/emu/builder.rs @@ -10,12 +10,15 @@ use libafl_bolts::tuples::{tuple_list, Append, Prepend}; use crate::FastSnapshotManager; use crate::{ command::{CommandManager, NopCommandManager, StdCommandManager}, + config::QemuConfigBuilder, modules::{EmulatorModule, EmulatorModuleTuple}, Emulator, NopEmulatorDriver, NopSnapshotManager, QemuInitError, QemuParams, StdEmulatorDriver, StdSnapshotManager, }; -use crate::config::QemuConfigBuilder; +/// `Emulator` builder. +/// +/// The default configuration of QEMU is always empty. #[derive(Clone)] pub struct EmulatorBuilder where @@ -25,11 +28,19 @@ where driver: ED, snapshot_manager: SM, command_manager: CM, - qemu_parameters: QB, + qemu_config: QB, phantom: PhantomData, } -impl EmulatorBuilder +impl + EmulatorBuilder< + NopCommandManager, + NopEmulatorDriver, + (), + QemuConfigBuilder, + S, + NopSnapshotManager, + > where S: UsesInput, { @@ -40,14 +51,22 @@ where driver: NopEmulatorDriver, snapshot_manager: NopSnapshotManager, command_manager: NopCommandManager, - qemu_parameters: QemuConfigBuilder::default(), + qemu_config: QemuConfigBuilder::default(), phantom: PhantomData, } } } #[cfg(feature = "usermode")] -impl EmulatorBuilder, StdEmulatorDriver, (), QemuConfigBuilder, S, StdSnapshotManager> +impl + EmulatorBuilder< + StdCommandManager, + StdEmulatorDriver, + (), + QemuConfigBuilder, + S, + StdSnapshotManager, + > where S: State + HasExecutions + Unpin, S::Input: HasTargetBytes, @@ -60,14 +79,22 @@ where command_manager: StdCommandManager::default(), snapshot_manager: StdSnapshotManager::default(), driver: StdEmulatorDriver::builder().build(), - qemu_parameters: QemuConfigBuilder::default(), + qemu_config: QemuConfigBuilder::default(), phantom: PhantomData, } } } #[cfg(feature = "systemmode")] -impl EmulatorBuilder, StdEmulatorDriver, (), QemuConfigBuilder, S, StdSnapshotManager> +impl + EmulatorBuilder< + StdCommandManager, + StdEmulatorDriver, + (), + QemuConfigBuilder, + S, + StdSnapshotManager, + > where S: State + HasExecutions + Unpin, S::Input: HasTargetBytes, @@ -78,7 +105,7 @@ where command_manager: StdCommandManager::default(), snapshot_manager: FastSnapshotManager::default(), driver: StdEmulatorDriver::builder().build(), - qemu_parameters: Some(QemuConfigBuilder::default()), + qemu_config: QemuConfigBuilder::default(), phantom: PhantomData, } } @@ -92,25 +119,26 @@ where driver: ED, command_manager: CM, snapshot_manager: SM, - qemu_parameters: QB, + qemu_config: QB, ) -> Self { Self { modules, command_manager, driver, snapshot_manager, - qemu_parameters, + qemu_config, phantom: PhantomData, } } - pub fn build(self) -> Result, QemuInitError> + pub fn build(self) -> Result, QemuInitError> where CM: CommandManager, ET: EmulatorModuleTuple, - QB: TryInto + QB: TryInto, + QemuInitError: From, { - let qemu_params: QemuParams = self.qemu_parameters.try_into()?; + let qemu_params: QemuParams = self.qemu_config.try_into()?; Emulator::new( qemu_params, @@ -127,17 +155,27 @@ where CM: CommandManager, S: UsesInput + Unpin, { - #[must_use] - pub fn qemu_builder(self, qemu_config: QB2) -> EmulatorBuilder + /// Main QEMU config function for building `Emulator`. + /// + /// The closure takes as parameter the current qemu configuration object and must return the new + /// QEMU configurator. For now, two configurators are supported: + /// - `QemuConfigBuilder` + /// - `Vec` + /// + /// Please check the documentation of `QemuConfig` for more information. + pub fn qemu_config( + self, + qemu_config_builder: F, + ) -> EmulatorBuilder where - QB2: Into + F: FnOnce(QB) -> QB2, { EmulatorBuilder::new( self.modules, self.driver, self.command_manager, self.snapshot_manager, - qemu_config, + qemu_config_builder(self.qemu_config), ) } @@ -151,7 +189,7 @@ where self.driver, self.command_manager, self.snapshot_manager, - self.qemu_parameters, + self.qemu_config, ) } @@ -165,7 +203,7 @@ where self.driver, self.command_manager, self.snapshot_manager, - self.qemu_parameters, + self.qemu_config, ) } @@ -175,11 +213,14 @@ where driver, self.command_manager, self.snapshot_manager, - self.qemu_parameters, + self.qemu_config, ) } - pub fn command_manager(self, command_manager: CM2) -> EmulatorBuilder + pub fn command_manager( + self, + command_manager: CM2, + ) -> EmulatorBuilder where CM2: CommandManager, { @@ -188,7 +229,7 @@ where self.driver, command_manager, self.snapshot_manager, - self.qemu_parameters, + self.qemu_config, ) } @@ -198,7 +239,7 @@ where self.driver, self.command_manager, self.snapshot_manager, - self.qemu_parameters, + self.qemu_config, ) } @@ -211,7 +252,7 @@ where self.driver, self.command_manager, snapshot_manager, - self.qemu_parameters, + self.qemu_config, ) } } diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index 37916591fc..38728aadd9 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -44,6 +44,7 @@ pub use usermode::*; mod systemmode; #[cfg(feature = "systemmode")] pub use systemmode::*; + use crate::config::QemuConfigBuilder; #[derive(Clone, Copy)] @@ -244,8 +245,14 @@ where S: UsesInput, { #[must_use] - pub fn empty( - ) -> EmulatorBuilder { + pub fn empty() -> EmulatorBuilder< + NopCommandManager, + NopEmulatorDriver, + (), + QemuConfigBuilder, + S, + NopSnapshotManager, + > { EmulatorBuilder::empty() } } @@ -256,8 +263,14 @@ where S::Input: HasTargetBytes, { #[must_use] - pub fn builder( - ) -> EmulatorBuilder, StdEmulatorDriver, (), QemuConfigBuilder, S, StdSnapshotManager> { + pub fn builder() -> EmulatorBuilder< + StdCommandManager, + StdEmulatorDriver, + (), + QemuConfigBuilder, + S, + StdSnapshotManager, + > { EmulatorBuilder::default() } } diff --git a/libafl_qemu/src/qemu/config.rs b/libafl_qemu/src/qemu/config.rs index 28c48f81e0..44520344b5 100644 --- a/libafl_qemu/src/qemu/config.rs +++ b/libafl_qemu/src/qemu/config.rs @@ -3,6 +3,7 @@ use core::{ fmt::{Display, Formatter}, }; use std::path::{Path, PathBuf}; + use derive_builder::Builder; use getset::Getters; use libafl_derive; @@ -46,6 +47,12 @@ pub struct Drive { interface: Option, } +impl Drive { + pub fn builder() -> DriveBuilder { + DriveBuilder::default() + } +} + impl Display for Drive { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "-drive")?; @@ -295,6 +302,10 @@ impl> From for Program { } } +/// Programmatic configurator for QEMU. +/// +/// It is supposed to be an equivalent to QEMU's CLI usual configuration, usable in a more +/// programmatic way and following the builder pattern. #[derive(Debug, Clone, libafl_derive::Display, Builder, Getters)] #[getset(get = "pub")] pub struct QemuConfig { @@ -334,6 +345,12 @@ pub struct QemuConfig { program: Program, } // Adding something here? Please leave Program as the last field +impl QemuConfig { + pub fn builder() -> QemuConfigBuilder { + QemuConfigBuilder::default() + } +} + #[cfg(test)] mod test { use super::*; @@ -343,7 +360,10 @@ mod test { #[cfg(feature = "usermode")] fn usermode() { let program = "/bin/pwd"; - let qemu_config = QemuConfig::builder().program("/bin/pwd").build(); + let qemu_config = QemuConfig::builder() + .program("/bin/pwd") + .build() + .expect("QEMU config failed."); let qemu = Qemu::init(qemu_config).unwrap(); let config = qemu.get_config().unwrap(); assert_eq!(config.to_string().trim(), program.trim()); diff --git a/libafl_qemu/src/qemu/error.rs b/libafl_qemu/src/qemu/error.rs index 31d7d18330..5a4c240dab 100644 --- a/libafl_qemu/src/qemu/error.rs +++ b/libafl_qemu/src/qemu/error.rs @@ -1,10 +1,9 @@ use core::fmt; -use std::fmt::Display; +use std::{convert::Infallible, fmt::Display}; use libafl_qemu_sys::{CPUStatePtr, GuestAddr}; -use crate::CallingConvention; -use crate::config::QemuConfigBuilderError; +use crate::{config::QemuConfigBuilderError, CallingConvention}; #[derive(Debug)] pub enum QemuError { @@ -18,9 +17,16 @@ pub enum QemuInitError { MultipleInstances, EmptyArgs, ConfigurationError(QemuConfigBuilderError), + Infallible, TooManyArgs(usize), } +impl From for QemuInitError { + fn from(_: Infallible) -> Self { + QemuInitError::Infallible + } +} + #[derive(Debug, Clone)] pub enum QemuExitError { UnknownKind, // Exit reason was not NULL, but exit kind is unknown. Should never happen. @@ -70,13 +76,22 @@ impl Display for QemuInitError { "Too many arguments passed to QEMU emulator ({n} > i32::MAX)" ) } + QemuInitError::Infallible => { + write!(f, "Infallible error, should never be reached.") + } } } } impl From for libafl::Error { fn from(err: QemuInitError) -> Self { - libafl::Error::unknown(format!("{err}")) + libafl::Error::runtime(format!("QEMU Init error: {err}")) + } +} + +impl From for libafl::Error { + fn from(err: QemuRWError) -> Self { + libafl::Error::runtime(format!("QEMU Runtime error: {err:?}")) } } diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index e9f14704cf..26ebdaf1d9 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -52,6 +52,7 @@ pub use systemmode::*; mod hooks; pub use hooks::*; + use crate::config::QemuConfigBuilder; static mut QEMU_IS_INITIALIZED: bool = false; @@ -186,8 +187,11 @@ impl TryFrom for QemuParams { type Error = QemuInitError; fn try_from(config_builder: QemuConfigBuilder) -> Result { - Ok(QemuParams::Config(config_builder.build() - .or_else(|e| Err(QemuInitError::ConfigurationError(e)))?)) + Ok(QemuParams::Config( + config_builder + .build() + .or_else(|e| Err(QemuInitError::ConfigurationError(e)))?, + )) } } @@ -199,7 +203,6 @@ where QemuParams::Cli(cli.iter().map(|x| x.as_ref().into()).collect()) } } - impl From<&Vec> for QemuParams where T: AsRef, @@ -514,29 +517,8 @@ impl From for HookData { } } -// impl QemuBuilder { -// pub fn new(params: T) -> Self { -// Self { -// params -// } -// } -// } - -// impl QemuBuilder -// where -// T: Into -// { -// pub fn build(self) -> Result { -// -// } -// } - #[allow(clippy::unused_self)] impl Qemu { - // pub fn builder() -> QemuBuilder { - // QemuBuilder::default() - // } - #[allow(clippy::must_use_candidate, clippy::similar_names)] pub fn init(params: T) -> Result where @@ -546,9 +528,10 @@ impl Qemu { match ¶ms { QemuParams::Config(cfg) => { - QEMU_CONFIG.set(cfg.clone()).map_err(|_| { - unreachable!("QEMU_CONFIG was already set but Qemu was not init!") - })?; + QEMU_CONFIG + .set(cfg.clone()) + .map_err(|_| unreachable!("QEMU_CONFIG was already set but Qemu was not init!")) + .unwrap(); } QemuParams::Cli(_) => {} }; diff --git a/libafl_sugar/src/qemu.rs b/libafl_sugar/src/qemu.rs index 52b41bf558..bb56a00461 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -241,7 +241,7 @@ where }; let emulator = Emulator::empty() - .qemu_cli(qemu_cli.to_owned()) + .qemu_config(|_| qemu_cli.to_owned()) .modules(modules) .build()?; @@ -361,7 +361,7 @@ where }; let emulator = Emulator::empty() - .qemu_cli(qemu_cli.to_owned()) + .qemu_config(|_| qemu_cli.to_owned()) .modules(modules) .build()?; From 413767feab1ca24ab117d794962f941de8ba4299 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Thu, 5 Dec 2024 17:35:33 +0100 Subject: [PATCH 20/21] better format --- fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs | 5 ++++- fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs | 6 ++---- libafl_qemu/src/qemu/config.rs | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs index 1d522261ba..c1ce8a7657 100644 --- a/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs @@ -195,7 +195,10 @@ fn fuzz( //QemuSnapshotHelper::new() ); - let emulator = Emulator::empty().qemu_cli(args).modules(modules).build()?; + let emulator = Emulator::empty() + .qemu_args(|_| args) + .modules(modules) + .build()?; let qemu = emulator.qemu(); // let qemu = Qemu::init(&args).expect("QEMU init failed"); // let (emu, asan) = init_with_asan(&mut args, &mut env).unwrap(); diff --git a/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs b/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs index 787fa9fb84..5dcf810380 100644 --- a/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs +++ b/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs @@ -97,7 +97,7 @@ pub fn fuzz() { .build()?); let emulator = Emulator::empty() - .qemu_config(|mut qemu_config| { + .qemu_config(|qemu_config| { qemu_config .machine("mps2-an385") .monitor(config::Monitor::Null) @@ -111,9 +111,7 @@ pub fn fuzz() { .file(format!("{target_dir}/dummy.qcow2")) .build() .expect("Could not build drives")]) - .start_cpu(false); - - qemu_config + .start_cpu(false) }) .modules(emulator_modules) .build()?; diff --git a/libafl_qemu/src/qemu/config.rs b/libafl_qemu/src/qemu/config.rs index 44520344b5..854fe5363d 100644 --- a/libafl_qemu/src/qemu/config.rs +++ b/libafl_qemu/src/qemu/config.rs @@ -308,6 +308,7 @@ impl> From for Program { /// programmatic way and following the builder pattern. #[derive(Debug, Clone, libafl_derive::Display, Builder, Getters)] #[getset(get = "pub")] +#[builder(pattern = "owned")] pub struct QemuConfig { #[cfg(feature = "systemmode")] #[builder(default, setter(strip_option))] From 8b04e3f0ccb9c5d42ab75656763e08a6d3e33273 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Thu, 5 Dec 2024 17:54:02 +0100 Subject: [PATCH 21/21] clippy + fmt --- libafl_qemu/src/qemu/config.rs | 5 ++++- libafl_qemu/src/qemu/mod.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libafl_qemu/src/qemu/config.rs b/libafl_qemu/src/qemu/config.rs index 854fe5363d..5fb0ed4b3d 100644 --- a/libafl_qemu/src/qemu/config.rs +++ b/libafl_qemu/src/qemu/config.rs @@ -48,6 +48,7 @@ pub struct Drive { } impl Drive { + #[must_use] pub fn builder() -> DriveBuilder { DriveBuilder::default() } @@ -347,6 +348,7 @@ pub struct QemuConfig { } // Adding something here? Please leave Program as the last field impl QemuConfig { + #[must_use] pub fn builder() -> QemuConfigBuilder { QemuConfigBuilder::default() } @@ -375,7 +377,8 @@ mod test { let drive = Drive::builder() .format(DiskImageFileFormat::Raw) .interface(DriveInterface::Ide) - .build(); + .build() + .expect("Drive builder failed."); assert_eq!(drive.to_string(), "-drive format=raw,if=ide"); } diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index 26ebdaf1d9..28157315ca 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -190,7 +190,7 @@ impl TryFrom for QemuParams { Ok(QemuParams::Config( config_builder .build() - .or_else(|e| Err(QemuInitError::ConfigurationError(e)))?, + .map_err(QemuInitError::ConfigurationError)?, )) } }