Skip to content

Commit

Permalink
Generic QEMU snapshot checking (AFLplusplus#2240)
Browse files Browse the repository at this point in the history
* generic snapshot checking.
  • Loading branch information
rmalmain authored May 23, 2024
1 parent 1fafaf6 commit 5fbe241
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 26 deletions.
2 changes: 1 addition & 1 deletion fuzzers/qemu_systemmode/src/fuzzer_breakpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion fuzzers/qemu_systemmode/src/fuzzer_sync_exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FastSnapshotManager> =
StdEmulatorExitHandler::new(emu_snapshot_manager); // Create an exit handler: it is the entity taking the decision of what should be done when QEMU returns.

Expand Down
2 changes: 1 addition & 1 deletion libafl_qemu/libafl_qemu_build/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 10 additions & 0 deletions libafl_qemu/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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())))
}
}
Expand Down
37 changes: 36 additions & 1 deletion libafl_qemu/src/emu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -96,6 +97,7 @@ where
pub enum ExitHandlerError {
QemuExitReasonError(EmulatorExitError),
SMError(SnapshotManagerError),
SMCheckError(SnapshotManagerCheckError),
CommandError(CommandError),
UnhandledSignal(Signal),
MultipleSnapshotDefinition,
Expand All @@ -109,6 +111,12 @@ pub enum SnapshotManagerError {
MemoryInconsistencies(u64),
}

#[derive(Debug, Clone)]
pub enum SnapshotManagerCheckError {
SnapshotManagerError(SnapshotManagerError),
SnapshotCheckError(QemuSnapshotCheckResult),
}

impl<CM, E, QT, S> TryFrom<ExitHandlerResult<CM, E, QT, S>> for ExitKind
where
CM: CommandManager<E, QT, S> + Debug,
Expand Down Expand Up @@ -163,6 +171,12 @@ impl From<SnapshotManagerError> for ExitHandlerError {
}
}

impl From<SnapshotManagerCheckError> 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,
Expand All @@ -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<QemuSnapshotCheckResult, SnapshotManagerError>;

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?
Expand Down
54 changes: 35 additions & 19 deletions libafl_qemu/src/emu/systemmode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -50,27 +51,36 @@ 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<QemuSnapshotCheckResult, SnapshotManagerError> {
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;

#[derive(Debug, Clone)]
pub struct FastSnapshotManager {
snapshots: HashMap<SnapshotId, FastSnapshotPtr>,
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,
}
}

Expand Down Expand Up @@ -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<QemuSnapshotCheckResult, SnapshotManagerError> {
// We consider the qemu implementation to be 'ideal' for now.
Ok(QemuSnapshotCheckResult::default())
}
}

impl IsSnapshotManager for FastSnapshotManager {
Expand All @@ -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<QemuSnapshotCheckResult, SnapshotManagerError> {
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)) }
}
}

Expand Down Expand Up @@ -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<String> {
self.qemu.list_devices()
}
Expand Down
14 changes: 14 additions & 0 deletions libafl_qemu/src/qemu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 10 additions & 3 deletions libafl_qemu/src/qemu/systemmode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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<String> {
Expand Down

0 comments on commit 5fbe241

Please sign in to comment.