From 5fbe2415e1206bf3300f672b6f1d3b8a4bbff31c Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Thu, 23 May 2024 22:57:13 +0200 Subject: [PATCH] Generic QEMU snapshot checking (#2240) * generic snapshot checking. --- .../qemu_systemmode/src/fuzzer_breakpoint.rs | 2 +- .../qemu_systemmode/src/fuzzer_sync_exit.rs | 2 +- libafl_qemu/libafl_qemu_build/src/build.rs | 2 +- libafl_qemu/src/command/mod.rs | 10 ++++ libafl_qemu/src/emu/mod.rs | 37 ++++++++++++- libafl_qemu/src/emu/systemmode.rs | 54 ++++++++++++------- libafl_qemu/src/qemu/mod.rs | 14 +++++ libafl_qemu/src/qemu/systemmode.rs | 13 +++-- 8 files changed, 108 insertions(+), 26 deletions(-) diff --git a/fuzzers/qemu_systemmode/src/fuzzer_breakpoint.rs b/fuzzers/qemu_systemmode/src/fuzzer_breakpoint.rs index e7ba9bf0dd..8f9a16261f 100644 --- a/fuzzers/qemu_systemmode/src/fuzzer_breakpoint.rs +++ b/fuzzers/qemu_systemmode/src/fuzzer_breakpoint.rs @@ -90,7 +90,7 @@ pub fn fuzz() { // Choose Snapshot Builder // let emu_snapshot_manager = QemuSnapshotBuilder::new(true); - let emu_snapshot_manager = FastSnapshotManager::new(false); + let emu_snapshot_manager = FastSnapshotManager::new(); // Choose Exit Handler let emu_exit_handler = StdEmulatorExitHandler::new(emu_snapshot_manager); diff --git a/fuzzers/qemu_systemmode/src/fuzzer_sync_exit.rs b/fuzzers/qemu_systemmode/src/fuzzer_sync_exit.rs index b793b0e8ab..78fe90f09d 100644 --- a/fuzzers/qemu_systemmode/src/fuzzer_sync_exit.rs +++ b/fuzzers/qemu_systemmode/src/fuzzer_sync_exit.rs @@ -55,7 +55,7 @@ pub fn fuzz() { let env: Vec<(String, String)> = env::vars().collect(); // let emu_snapshot_manager = QemuSnapshotBuilder::new(true); - let emu_snapshot_manager = FastSnapshotManager::new(false); // Create a snapshot manager (normal or fast for now). + let emu_snapshot_manager = FastSnapshotManager::new(); // Create a snapshot manager (normal or fast for now). let emu_exit_handler: StdEmulatorExitHandler = StdEmulatorExitHandler::new(emu_snapshot_manager); // Create an exit handler: it is the entity taking the decision of what should be done when QEMU returns. diff --git a/libafl_qemu/libafl_qemu_build/src/build.rs b/libafl_qemu/libafl_qemu_build/src/build.rs index f482096a61..2cc39c76f8 100644 --- a/libafl_qemu/libafl_qemu_build/src/build.rs +++ b/libafl_qemu/libafl_qemu_build/src/build.rs @@ -11,7 +11,7 @@ use crate::cargo_add_rpath; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; -const QEMU_REVISION: &str = "9f3e2399ee9b106dfbb8c3afcdfdf30e235fc88f"; +const QEMU_REVISION: &str = "9d2197b73bf5e66e709f9f1669467d5c84062da0"; #[allow(clippy::module_name_repetitions)] pub struct BuildResult { diff --git a/libafl_qemu/src/command/mod.rs b/libafl_qemu/src/command/mod.rs index 7a2dcb6fdf..d112756cbe 100644 --- a/libafl_qemu/src/command/mod.rs +++ b/libafl_qemu/src/command/mod.rs @@ -317,6 +317,11 @@ where .snapshot_manager_borrow_mut() .restore(&snapshot_id, qemu)?; + #[cfg(feature = "paranoid_debug")] + emu_exit_handler + .snapshot_manager_borrow() + .check(&snapshot_id, emu.qemu())?; + Ok(None) } } @@ -445,6 +450,11 @@ where .snapshot_manager_borrow_mut() .restore(&snapshot_id, emu.qemu())?; + #[cfg(feature = "paranoid_debug")] + emu_exit_handler + .snapshot_manager_borrow() + .check(&snapshot_id, emu.qemu())?; + Ok(Some(ExitHandlerResult::EndOfRun(self.0.unwrap()))) } } diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index b10737aa0b..f152073521 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -35,7 +35,8 @@ use crate::{ sys::TCGTemp, BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, EmulatorMemoryChunk, GuestReg, HookData, HookId, InstructionHookId, MemAccessInfo, Qemu, QemuExitError, QemuExitReason, QemuHelperTuple, - QemuInitError, QemuShutdownCause, ReadHookId, Regs, StdInstrumentationFilter, WriteHookId, CPU, + QemuInitError, QemuShutdownCause, QemuSnapshotCheckResult, ReadHookId, Regs, + StdInstrumentationFilter, WriteHookId, CPU, }; #[cfg(emulation_mode = "usermode")] @@ -96,6 +97,7 @@ where pub enum ExitHandlerError { QemuExitReasonError(EmulatorExitError), SMError(SnapshotManagerError), + SMCheckError(SnapshotManagerCheckError), CommandError(CommandError), UnhandledSignal(Signal), MultipleSnapshotDefinition, @@ -109,6 +111,12 @@ pub enum SnapshotManagerError { MemoryInconsistencies(u64), } +#[derive(Debug, Clone)] +pub enum SnapshotManagerCheckError { + SnapshotManagerError(SnapshotManagerError), + SnapshotCheckError(QemuSnapshotCheckResult), +} + impl TryFrom> for ExitKind where CM: CommandManager + Debug, @@ -163,6 +171,12 @@ impl From for ExitHandlerError { } } +impl From for ExitHandlerError { + fn from(sm_check_error: SnapshotManagerCheckError) -> Self { + ExitHandlerError::SMCheckError(sm_check_error) + } +} + #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub struct SnapshotId { id: u64, @@ -175,6 +189,27 @@ pub trait IsSnapshotManager: Debug + Clone { snapshot_id: &SnapshotId, qemu: &Qemu, ) -> Result<(), SnapshotManagerError>; + fn do_check( + &self, + reference_snapshot_id: &SnapshotId, + qemu: &Qemu, + ) -> Result; + + fn check( + &self, + reference_snapshot_id: &SnapshotId, + qemu: &Qemu, + ) -> Result<(), SnapshotManagerCheckError> { + let check_result = self + .do_check(reference_snapshot_id, qemu) + .map_err(SnapshotManagerCheckError::SnapshotManagerError)?; + + if check_result == QemuSnapshotCheckResult::default() { + Ok(()) + } else { + Err(SnapshotManagerCheckError::SnapshotCheckError(check_result)) + } + } } // TODO: Rework with generics for command handlers? diff --git a/libafl_qemu/src/emu/systemmode.rs b/libafl_qemu/src/emu/systemmode.rs index d6873566af..3e8129a873 100644 --- a/libafl_qemu/src/emu/systemmode.rs +++ b/libafl_qemu/src/emu/systemmode.rs @@ -9,7 +9,8 @@ use libafl_qemu_sys::GuestPhysAddr; use crate::{ command::CommandManager, emu::IsSnapshotManager, DeviceSnapshotFilter, Emulator, - EmulatorExitHandler, Qemu, QemuHelperTuple, SnapshotId, SnapshotManagerError, + EmulatorExitHandler, Qemu, QemuHelperTuple, QemuSnapshotCheckResult, SnapshotId, + SnapshotManagerError, }; impl SnapshotId { @@ -50,6 +51,17 @@ impl IsSnapshotManager for SnapshotManager { SnapshotManager::Fast(fast_sm) => fast_sm.restore(snapshot_id, qemu), } } + + fn do_check( + &self, + reference_snapshot_id: &SnapshotId, + qemu: &Qemu, + ) -> Result { + match self { + SnapshotManager::Qemu(qemu_sm) => qemu_sm.do_check(reference_snapshot_id, qemu), + SnapshotManager::Fast(fast_sm) => fast_sm.do_check(reference_snapshot_id, qemu), + } + } } pub type FastSnapshotPtr = *mut libafl_qemu_sys::SyxSnapshot; @@ -57,20 +69,18 @@ pub type FastSnapshotPtr = *mut libafl_qemu_sys::SyxSnapshot; #[derive(Debug, Clone)] pub struct FastSnapshotManager { snapshots: HashMap, - check_memory_consistency: bool, } impl Default for FastSnapshotManager { fn default() -> Self { - Self::new(false) + Self::new() } } impl FastSnapshotManager { - pub fn new(check_memory_consistency: bool) -> Self { + pub fn new() -> Self { Self { snapshots: HashMap::new(), - check_memory_consistency, } } @@ -112,6 +122,15 @@ impl IsSnapshotManager for QemuSnapshotManager { qemu.load_snapshot(self.snapshot_id_to_name(snapshot_id).as_str(), self.is_sync); Ok(()) } + + fn do_check( + &self, + _reference_snapshot_id: &SnapshotId, + _qemu: &Qemu, + ) -> Result { + // We consider the qemu implementation to be 'ideal' for now. + Ok(QemuSnapshotCheckResult::default()) + } } impl IsSnapshotManager for FastSnapshotManager { @@ -136,18 +155,19 @@ impl IsSnapshotManager for FastSnapshotManager { qemu.restore_fast_snapshot(fast_snapshot_ptr); } - if self.check_memory_consistency { - let nb_inconsistencies = - unsafe { qemu.check_fast_snapshot_memory_consistency(fast_snapshot_ptr) }; + Ok(()) + } - if nb_inconsistencies > 0 { - return Err(SnapshotManagerError::MemoryInconsistencies( - nb_inconsistencies, - )); - } - } + fn do_check( + &self, + reference_snapshot_id: &SnapshotId, + qemu: &Qemu, + ) -> Result { + let fast_snapshot_ptr = *self.snapshots.get(reference_snapshot_id).ok_or( + SnapshotManagerError::SnapshotIdNotFound(*reference_snapshot_id), + )?; - Ok(()) + unsafe { Ok(qemu.check_fast_snapshot(fast_snapshot_ptr)) } } } @@ -194,10 +214,6 @@ where self.qemu.restore_fast_snapshot(snapshot) } - pub unsafe fn check_fast_snapshot_memory_consistency(&self, snapshot: FastSnapshotPtr) -> u64 { - self.qemu.check_fast_snapshot_memory_consistency(snapshot) - } - pub fn list_devices(&self) -> Vec { self.qemu.list_devices() } diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index a47eb3bfda..f33cef46b6 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -106,6 +106,20 @@ pub enum QemuExitError { UnexpectedExit, // Qemu exited without going through an expected exit point. Can be caused by a crash for example. } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct QemuSnapshotCheckResult { + nb_page_inconsistencies: u64, +} + +/// Represents a QEMU snapshot check result for which no error was detected +impl Default for QemuSnapshotCheckResult { + fn default() -> Self { + Self { + nb_page_inconsistencies: 0, + } + } +} + /// The thin wrapper around QEMU. /// It is considered unsafe to use it directly. /// Prefer using `Emulator` instead in case of doubt. diff --git a/libafl_qemu/src/qemu/systemmode.rs b/libafl_qemu/src/qemu/systemmode.rs index 2693bf5909..5cdb2f8366 100644 --- a/libafl_qemu/src/qemu/systemmode.rs +++ b/libafl_qemu/src/qemu/systemmode.rs @@ -16,7 +16,7 @@ use num_traits::Zero; use crate::{ EmulatorMemoryChunk, FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuExitError, - QemuExitReason, CPU, + QemuExitReason, QemuSnapshotCheckResult, CPU, }; pub(super) extern "C" fn qemu_cleanup_atexit() { @@ -256,8 +256,15 @@ impl Qemu { libafl_qemu_sys::syx_snapshot_root_restore(snapshot) } - pub unsafe fn check_fast_snapshot_memory_consistency(&self, snapshot: FastSnapshotPtr) -> u64 { - libafl_qemu_sys::syx_snapshot_check_memory_consistency(snapshot) + pub unsafe fn check_fast_snapshot( + &self, + ref_snapshot: FastSnapshotPtr, + ) -> QemuSnapshotCheckResult { + let check_result = libafl_qemu_sys::syx_snapshot_check(ref_snapshot); + + QemuSnapshotCheckResult { + nb_page_inconsistencies: check_result.nb_inconsistencies, + } } pub fn list_devices(&self) -> Vec {