Custom QEMU emulator typed builder + ExitHandler / Commands refactoring (#2486)

* Add a custom typed builder for Emulator

* Unify qemu_init for usermode and systemmode

* Remove env from qemu init args (it is unused in QEMU in practice)

* expose thread hooks to systemmode

* rename qemu_config to config

* Replace ExitHandler by EmulatorDriver

* Reorder generics alphabetically for Qemu{,Fork}Executor

* Moved snapshot manager to Emulator to continue centralizing mains objects in the same structure

* Reimplementation of CommandManager working with enums instead of tables

* Macro has been adapted to do this work automatically

* Moved snapshot stuff to dedicated module

* Removed many Rc<RefCell<...>>, now useless with the removal of vtables

* Builder given by Emulator via `Emulator::builder`. Reduced trait bound overhead
This commit is contained in:
Romain Malmain 2024-08-21 16:36:45 +02:00 committed by GitHub
parent 16aa218457
commit 4b87d7f4eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 1492 additions and 1117 deletions

View File

@ -46,15 +46,14 @@ use libafl_bolts::{
AsSlice, AsSliceMut,
};
use libafl_qemu::{
command::NopCommandManager,
elf::EasyElf,
filter_qemu_args,
modules::{
cmplog::{CmpLogChildModule, CmpLogMap, CmpLogObserver},
edges::{EdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
},
Emulator, GuestReg, MmapPerms, NopEmulatorExitHandler, QemuExitError, QemuExitReason,
QemuForkExecutor, QemuShutdownCause, Regs,
Emulator, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuForkExecutor,
QemuShutdownCause, Regs,
};
#[cfg(unix)]
use nix::unistd::dup;
@ -156,14 +155,10 @@ fn fuzz(
CmpLogChildModule::default(),
);
let emulator = Emulator::new(
args.as_slice(),
env.as_slice(),
emulator_modules,
NopEmulatorExitHandler,
NopCommandManager,
)
.unwrap();
let emulator = Emulator::empty()
.qemu_cli(args)
.modules(emulator_modules)
.build()?;
let qemu = emulator.qemu();

View File

@ -46,7 +46,6 @@ use libafl_bolts::{
AsSlice,
};
use libafl_qemu::{
command::NopCommandManager,
elf::EasyElf,
filter_qemu_args,
// asan::{init_with_asan, QemuAsanHelper},
@ -58,7 +57,6 @@ use libafl_qemu::{
GuestReg,
//snapshot::QemuSnapshotHelper,
MmapPerms,
NopEmulatorExitHandler,
Qemu,
QemuExecutor,
QemuExitError,
@ -177,8 +175,7 @@ fn fuzz(
env::remove_var("LD_LIBRARY_PATH");
let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect();
let qemu = Qemu::init(&args, &env).unwrap();
let qemu = Qemu::init(&args).unwrap();
// let (emu, asan) = init_with_asan(&mut args, &mut env).unwrap();
let mut elf_buffer = Vec::new();
@ -329,7 +326,7 @@ fn fuzz(
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
let target = input.target_bytes();
let mut buf = target.as_slice();
let mut len = buf.len();
@ -366,8 +363,7 @@ fn fuzz(
//QemuSnapshotHelper::new()
);
let emulator =
Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?;
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(

View File

@ -27,12 +27,10 @@ use libafl_bolts::{
AsSlice, AsSliceMut,
};
use libafl_qemu::{
command::NopCommandManager,
elf::EasyElf,
modules::edges::{EdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms,
NopEmulatorExitHandler, Qemu, QemuExitError, QemuExitReason, QemuForkExecutor,
QemuShutdownCause, Regs,
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitError,
QemuExitReason, QemuForkExecutor, QemuShutdownCause, Regs,
};
#[derive(Default)]
@ -113,8 +111,7 @@ pub fn fuzz() -> Result<(), Error> {
log::debug!("ARGS: {:#?}", options.args);
env::remove_var("LD_LIBRARY_PATH");
let env: Vec<(String, String)> = env::vars().collect();
let qemu = Qemu::init(&options.args, &env).unwrap();
let qemu = Qemu::init(&options.args).unwrap();
let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
@ -223,8 +220,7 @@ pub fn fuzz() -> Result<(), Error> {
let modules = tuple_list!(EdgeCoverageChildModule::default(),);
let emulator =
Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?;
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
let mut executor = QemuForkExecutor::new(
emulator,

View File

@ -26,12 +26,10 @@ use libafl_bolts::{
AsSlice,
};
use libafl_qemu::{
command::NopCommandManager,
elf::EasyElf,
modules::{drcov::DrCovModule, QemuInstrumentationAddressRangeFilter},
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms,
NopEmulatorExitHandler, Qemu, QemuExecutor, QemuExitReason, QemuRWError, QemuShutdownCause,
Regs,
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor,
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
};
#[derive(Default)]
@ -119,9 +117,8 @@ pub fn fuzz() {
log::debug!("ARGS: {:#?}", options.args);
env::remove_var("LD_LIBRARY_PATH");
let env: Vec<(String, String)> = env::vars().collect();
let qemu = Qemu::init(&options.args, &env).unwrap();
let qemu = Qemu::init(&options.args).unwrap();
let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
@ -178,7 +175,7 @@ pub fn fuzz() {
}
};
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
let target = input.target_bytes();
let mut buf = target.as_slice();
let mut len = buf.len();
@ -241,13 +238,10 @@ pub fn fuzz() {
false,
));
let emulator = Emulator::new_with_qemu(
qemu,
emulator_modules,
NopEmulatorExitHandler,
NopCommandManager,
)
.unwrap();
let emulator = Emulator::empty()
.qemu(qemu)
.modules(emulator_modules)
.build()?;
let mut executor = QemuExecutor::new(
emulator,

View File

@ -129,7 +129,7 @@ impl<'a> Client<'a> {
let (emu, asan_lib) = init_qemu_with_asan_guest(&mut args, &mut env)?;
(emu, None, Some(asan_lib))
} else {
(Qemu::init(&args, &env)?, None, None)
(Qemu::init(&args)?, None, None)
}
};

View File

@ -38,13 +38,12 @@ use libafl_bolts::{
tuples::{tuple_list, Merge},
};
use libafl_qemu::{
command::NopCommandManager,
modules::{
cmplog::CmpLogObserver,
edges::{edges_map_mut_ptr, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
EmulatorModuleTuple,
},
Emulator, NopEmulatorExitHandler, Qemu, QemuExecutor,
Emulator, Qemu, QemuExecutor,
};
use typed_builder::TypedBuilder;
@ -150,18 +149,15 @@ impl<'a, M: Monitor> Instance<'a, M> {
let harness = Harness::new(self.qemu)?;
let mut harness =
|_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| harness.run(input);
|_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| harness.run(input);
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let emulator = Emulator::new_with_qemu(
*self.qemu,
modules,
NopEmulatorExitHandler,
NopCommandManager,
)
.unwrap();
let emulator = Emulator::empty()
.qemu(*self.qemu)
.modules(modules)
.build()?;
if self.options.is_cmplog_core(self.core_id) {
// Create a QEMU in-process executor

View File

@ -29,14 +29,14 @@ use libafl_bolts::{
};
use libafl_qemu::{
breakpoint::Breakpoint,
command::{EndCommand, StartCommand, StdCommandManager},
command::{EndCommand, StartCommand},
elf::EasyElf,
emu::Emulator,
executor::QemuExecutor,
modules::edges::{
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
},
FastSnapshotManager, GuestPhysAddr, GuestReg, QemuMemoryChunk, StdEmulatorExitHandler,
GuestPhysAddr, GuestReg, QemuMemoryChunk,
};
// use libafl_qemu::QemuSnapshotBuilder; // for normal qemu snapshot
@ -85,25 +85,14 @@ pub fn fuzz() {
println!("Breakpoint address = {breakpoint:#x}");
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
// Initialize QEMU
let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect();
// Choose Snapshot Builder
// let emu_snapshot_manager = QemuSnapshotBuilder::new(true);
let emu_snapshot_manager = FastSnapshotManager::new();
// Choose Exit Handler
let emu_exit_handler = StdEmulatorExitHandler::new(emu_snapshot_manager);
// Choose Command Manager
let cmd_manager = StdCommandManager::new();
// Instantiate hooks
let modules = tuple_list!(EdgeCoverageModule::default());
// Create emulator
let emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap();
// Initialize QEMU Emulator
let emu = Emulator::builder()
.qemu_cli(args)
.add_module(EdgeCoverageModule::default())
.build()
.unwrap();
// Set breakpoints of interest with corresponding commands.
emu.add_breakpoint(
@ -113,13 +102,18 @@ pub fn fuzz() {
input_addr,
unsafe { MAX_INPUT_SIZE } as GuestReg,
None,
)),
))
.into(),
true,
),
true,
);
emu.add_breakpoint(
Breakpoint::with_command(breakpoint, EndCommand::new(Some(ExitKind::Ok)), false),
Breakpoint::with_command(
breakpoint,
EndCommand::new(Some(ExitKind::Ok)).into(),
false,
),
true,
);
@ -127,7 +121,7 @@ pub fn fuzz() {
println!("Devices = {:?}", devices);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| unsafe {
let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| unsafe {
emulator.run(input).unwrap().try_into().unwrap()
};

View File

@ -30,14 +30,13 @@ use libafl_bolts::{
AsSlice,
};
use libafl_qemu::{
command::NopCommandManager,
config,
elf::EasyElf,
executor::QemuExecutor,
modules::edges::{
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
},
qemu_config, Emulator, NopEmulatorExitHandler, Qemu, QemuExitError, QemuExitReason,
QemuRWError, QemuShutdownCause, Regs,
Emulator, Qemu, QemuExitError, QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
};
use libafl_qemu_sys::GuestPhysAddr;
@ -91,14 +90,14 @@ pub fn fuzz() {
// Initialize QEMU
let qemu = Qemu::builder()
.machine("mps2-an385")
.monitor(qemu_config::Monitor::Null)
.monitor(config::Monitor::Null)
.kernel(format!("{target_dir}/example.elf"))
.serial(qemu_config::Serial::Null)
.serial(config::Serial::Null)
.no_graphic(true)
.snapshot(true)
.drives([qemu_config::Drive::builder()
.interface(qemu_config::DriveInterface::None)
.format(qemu_config::DiskImageFileFormat::Qcow2)
.drives([config::Drive::builder()
.interface(config::DriveInterface::None)
.format(config::DiskImageFileFormat::Qcow2)
.file(format!("{target_dir}/dummy.qcow2"))
.build()])
.start_cpu(false)
@ -107,13 +106,10 @@ pub fn fuzz() {
let emulator_modules = tuple_list!(EdgeCoverageModule::default());
let mut emulator = Emulator::new_with_qemu(
qemu,
emulator_modules,
NopEmulatorExitHandler,
NopCommandManager,
)
.unwrap();
let emulator = Emulator::empty()
.qemu(qemu)
.modules(emulator_modules)
.build()?;
qemu.set_breakpoint(main_addr);
@ -140,7 +136,7 @@ pub fn fuzz() {
let snap = qemu.create_fast_snapshot(true);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
let target = input.target_bytes();
let mut buf = target.as_slice();
let len = buf.len();

View File

@ -27,13 +27,11 @@ use libafl_bolts::{
tuples::tuple_list,
};
use libafl_qemu::{
command::StdCommandManager,
emu::Emulator,
executor::QemuExecutor,
modules::edges::{
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
},
FastSnapshotManager, StdEmulatorExitHandler,
};
// use libafl_qemu::QemuSnapshotBuilder; for normal qemu snapshot
@ -54,25 +52,20 @@ pub fn fuzz() {
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
// Initialize QEMU
let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect();
// let emu_snapshot_manager = QemuSnapshotBuilder::new(true);
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.
let cmd_manager = StdCommandManager::new();
// Choose modules to use
let modules = tuple_list!(EdgeCoverageModule::default());
let emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap(); // Create the emulator
let emu = Emulator::builder()
.qemu_cli(args)
.modules(modules)
.build()?;
let devices = emu.list_devices();
println!("Devices = {:?}", devices);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| unsafe {
let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| unsafe {
emulator.run(input).unwrap().try_into().unwrap()
};

View File

@ -720,7 +720,7 @@ where
where
MO: MapObserver + Truncate, // TODO maybe enforce Entry = u8 for the cov map
A: Observer<S> + AsRef<MO> + AsMut<MO>,
OT: ObserversTuple<S> + Prepend<MO, PreprendResult = OT>,
OT: ObserversTuple<S> + Prepend<MO>,
S: UsesInput,
S::Input: Input + HasTargetBytes,
SP: ShMemProvider,

View File

@ -39,9 +39,7 @@
//! fn fuzz_with_qemu(mut options: FuzzerOptions) {
//! env::remove_var("LD_LIBRARY_PATH");
//!
//! let env: Vec<(String, String)> = env::vars().collect();
//!
//! let qemu = Qemu::init(&mut options.qemu_args.to_vec(), &mut env).unwrap();
//! let qemu = Qemu::init(&mut options.qemu_args.to_vec()).unwrap();
//! // do other stuff...
//! }
//!

View File

@ -657,54 +657,32 @@ where
/// Allows prepending of values to a tuple
pub trait Prepend<T> {
/// The Resulting [`TupleList`], of an [`Prepend::prepend()`] call,
/// including the prepended entry.
type PreprendResult;
/// Prepend a value to this tuple, returning a new tuple with prepended value.
#[must_use]
fn prepend(self, value: T) -> (T, Self::PreprendResult);
fn prepend(self, value: T) -> (T, Self);
}
/// Implement prepend for tuple list.
impl<Tail, T> Prepend<T> for Tail {
type PreprendResult = Self;
fn prepend(self, value: T) -> (T, Self::PreprendResult) {
fn prepend(self, value: T) -> (T, Self) {
(value, self)
}
}
/// Append to a tuple
pub trait Append<T> {
/// The Resulting [`TupleList`], of an [`Append::append()`] call,
/// including the appended entry.
type AppendResult;
pub trait Append<T>
where
Self: Sized,
{
/// Append Value and return the tuple
#[must_use]
fn append(self, value: T) -> Self::AppendResult;
fn append(self, value: T) -> (Self, T);
}
/// Implement append for an empty tuple list.
impl<T> Append<T> for () {
type AppendResult = (T, ());
fn append(self, value: T) -> Self::AppendResult {
(value, ())
}
}
/// Implement append for non-empty tuple list.
impl<Head, Tail, T> Append<T> for (Head, Tail)
where
Tail: Append<T>,
{
type AppendResult = (Head, Tail::AppendResult);
fn append(self, value: T) -> Self::AppendResult {
let (head, tail) = self;
(head, tail.append(value))
/// Implement append for tuple list.
impl<Head, T> Append<T> for Head {
fn append(self, value: T) -> (Self, T) {
(self, value)
}
}

View File

@ -1 +0,0 @@
../libafl_libfuzzer/runtime/build.rs

View File

@ -0,0 +1,25 @@
use std::{env, path::Path};
#[allow(clippy::too_many_lines)]
fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
println!("cargo:rerun-if-changed=src/harness_wrap.h");
println!("cargo:rerun-if-changed=src/harness_wrap.cpp");
let build = bindgen::builder()
.header("src/harness_wrap.h")
.generate_comments(true)
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("Couldn't generate the harness wrapper!");
build
.write_to_file(Path::new(&out_dir).join("harness_wrap.rs"))
.expect("Couldn't write the harness wrapper!");
cc::Build::new()
.cpp(true)
.file("src/harness_wrap.cpp")
.compile("harness_wrap");
}

View File

@ -61,6 +61,7 @@ const WRAPPER_HEADER: &str = r#"
#include "sysemu/runstate.h"
#include "sysemu/replay.h"
#include "libafl/system.h"
#include "libafl/qemu_snapshot.h"
#include "libafl/syx-snapshot/device-save.h"
#include "libafl/syx-snapshot/syx-snapshot.h"
@ -98,9 +99,9 @@ const WRAPPER_HEADER: &str = r#"
#include "libafl/hooks/tcg/instruction.h"
#include "libafl/hooks/tcg/read_write.h"
#include "libafl/hooks/cpu_run.h"
#include "libafl/hooks/thread.h"
#ifdef CONFIG_USER_ONLY
#include "libafl/hooks/thread.h"
#include "libafl/hooks/syscall.h"
#endif
@ -151,7 +152,6 @@ pub fn generate(
.allowlist_type("Syx.*")
.allowlist_type("libafl_mapinfo")
.allowlist_type("IntervalTreeRoot")
.allowlist_function("qemu_user_init")
.allowlist_function("qemu_system_debug_request")
.allowlist_function("target_mmap")
.allowlist_function("target_mprotect")
@ -172,6 +172,9 @@ pub fn generate(
.allowlist_function("read_self_maps")
.allowlist_function("free_self_maps")
.allowlist_function("pageflags_get_root")
.allowlist_function("vm_start")
.allowlist_function("qemu_main_loop")
.allowlist_function("qemu_cleanup")
.blocklist_function("main_loop_wait") // bindgen issue #1313
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()));

View File

@ -11,7 +11,7 @@ use crate::cargo_add_rpath;
pub const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
pub const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
pub const QEMU_REVISION: &str = "7f468ebba6ec4b74d6ca6f4267f365f551c71fe4";
pub const QEMU_REVISION: &str = "ee43af7f80d1117857e58b0b7ef556652d5893d5";
#[allow(clippy::module_name_repetitions)]
pub struct BuildResult {
@ -144,7 +144,6 @@ fn configure_qemu(
.arg("--disable-iconv")
.arg("--disable-jack")
.arg("--disable-keyring")
.arg("--disable-kvm")
.arg("--disable-libdaxctl")
.arg("--disable-libiscsi")
.arg("--disable-libnfs")

View File

@ -51,10 +51,10 @@ mod usermode;
#[cfg(emulation_mode = "usermode")]
pub use usermode::*;
#[cfg(emulation_mode = "systemmode")]
mod systemmode;
#[cfg(emulation_mode = "systemmode")]
pub use systemmode::*;
// #[cfg(emulation_mode = "systemmode")]
// mod systemmode;
// #[cfg(emulation_mode = "systemmode")]
// pub use systemmode::*;
/// Safe linking with of extern "C" functions.
/// This macro makes sure the declared symbol is defined *at link time*, avoiding declaring non-existant symbols

View File

@ -1,11 +0,0 @@
use paste::paste;
use crate::extern_c_checked;
extern_c_checked! {
pub fn qemu_init(argc: i32, argv: *const *const u8);
pub fn vm_start();
pub fn qemu_main_loop();
pub fn qemu_cleanup();
}

View File

@ -10,8 +10,6 @@ use strum_macros::EnumIter;
use crate::{extern_c_checked, libafl_mapinfo, GuestAddr, MmapPerms};
extern_c_checked! {
pub fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
pub static exec_path: *const u8;
pub static guest_base: usize;
pub static mut mmap_next_start: GuestAddr;

View File

@ -1,5 +1,5 @@
/* 1.82.0-nightly */
/* qemu git hash: 31ee26f97071d5bed1ac1e7de75beea755b198d6 */
/* qemu git hash: ec91486cedfd9e636be0063d88bd6e7eaff74915 */
/* automatically generated by rust-bindgen 0.69.4 */
#[repr(C)]
@ -13761,6 +13761,9 @@ extern "C" {
extern "C" {
pub fn libafl_set_brk(new_brk: u64) -> u64;
}
extern "C" {
pub fn libafl_qemu_init(argc: ::std::os::raw::c_int, argv: *mut *mut ::std::os::raw::c_char);
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct AccelCPUClass {
@ -15194,7 +15197,7 @@ extern "C" {
pub fn libafl_qemu_remove_new_thread_hook(num: usize) -> ::std::os::raw::c_int;
}
extern "C" {
pub fn libafl_hook_new_thread_run(env: *mut CPUArchState) -> bool;
pub fn libafl_hook_new_thread_run(env: *mut CPUArchState, tid: u32) -> bool;
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]

View File

@ -1,8 +1,7 @@
use std::{
borrow::Borrow,
fmt::{Display, Formatter},
fmt::{Debug, Display, Formatter},
hash::{Hash, Hasher},
rc::Rc,
sync::{
atomic::{AtomicU64, Ordering},
OnceLock,
@ -12,25 +11,51 @@ use std::{
use libafl::inputs::UsesInput;
use libafl_qemu_sys::GuestAddr;
use crate::{command::IsCommand, Qemu};
use crate::{command::CommandManager, Qemu};
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct BreakpointId(u64);
// TODO: distinguish breakpoints with IDs instead of addresses to avoid collisions.
#[derive(Debug)]
pub struct Breakpoint<CM, EH, ET, S>
pub struct Breakpoint<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
id: BreakpointId,
addr: GuestAddr,
cmd: Option<Rc<dyn IsCommand<CM, EH, ET, S>>>,
cmd: Option<CM::Commands>,
disable_on_trigger: bool,
enabled: bool,
}
impl<CM, ED, ET, S, SM> Clone for Breakpoint<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn clone(&self) -> Self {
Self {
id: self.id,
addr: self.addr,
cmd: self.cmd.clone(),
disable_on_trigger: self.disable_on_trigger,
enabled: self.enabled,
}
}
}
impl<CM, ED, ET, S, SM> Debug for Breakpoint<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "BP {:?} @ addr {:?}", self.id, self.addr)
}
}
impl BreakpointId {
pub fn new() -> Self {
static mut BREAKPOINT_ID_COUNTER: OnceLock<AtomicU64> = OnceLock::new();
@ -47,8 +72,9 @@ impl Default for BreakpointId {
}
}
impl<CM, EH, ET, S> Hash for Breakpoint<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Hash for Breakpoint<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn hash<H: Hasher>(&self, state: &mut H) {
@ -56,8 +82,9 @@ where
}
}
impl<CM, EH, ET, S> PartialEq for Breakpoint<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> PartialEq for Breakpoint<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn eq(&self, other: &Self) -> bool {
@ -65,10 +92,16 @@ where
}
}
impl<CM, EH, ET, S> Eq for Breakpoint<CM, EH, ET, S> where S: UsesInput {}
impl<CM, EH, ET, S> Display for Breakpoint<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Eq for Breakpoint<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
}
impl<CM, ED, ET, S, SM> Display for Breakpoint<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
@ -76,8 +109,9 @@ where
}
}
impl<CM, EH, ET, S> Borrow<BreakpointId> for Breakpoint<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Borrow<BreakpointId> for Breakpoint<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn borrow(&self) -> &BreakpointId {
@ -85,8 +119,9 @@ where
}
}
impl<CM, EH, ET, S> Borrow<GuestAddr> for Breakpoint<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Borrow<GuestAddr> for Breakpoint<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn borrow(&self) -> &GuestAddr {
@ -94,8 +129,9 @@ where
}
}
impl<CM, EH, ET, S> Breakpoint<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Breakpoint<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
// Emu will return with the breakpoint as exit reason.
@ -112,15 +148,11 @@ where
// Emu will execute the command when it meets the breakpoint.
#[must_use]
pub fn with_command<C: IsCommand<CM, EH, ET, S> + 'static>(
addr: GuestAddr,
cmd: C,
disable_on_trigger: bool,
) -> Self {
pub fn with_command(addr: GuestAddr, cmd: CM::Commands, disable_on_trigger: bool) -> Self {
Self {
id: BreakpointId::new(),
addr,
cmd: Some(Rc::new(cmd)),
cmd: Some(cmd),
disable_on_trigger,
enabled: false,
}
@ -150,7 +182,7 @@ where
}
}
pub fn trigger(&mut self, qemu: Qemu) -> Option<Rc<dyn IsCommand<CM, EH, ET, S>>> {
pub fn trigger(&mut self, qemu: Qemu) -> Option<CM::Commands> {
if self.disable_on_trigger {
self.disable(qemu);
}

View File

@ -1,10 +1,10 @@
use std::{
fmt::{Debug, Display, Error, Formatter},
rc::Rc,
fmt,
fmt::{Debug, Display, Formatter},
marker::PhantomData,
};
use enum_map::{Enum, EnumMap};
use hashbrown::HashMap;
#[cfg(emulation_mode = "systemmode")]
use hashbrown::HashSet;
use libafl::{
@ -12,7 +12,9 @@ use libafl::{
inputs::{HasTargetBytes, UsesInput},
};
use libafl_bolts::AsSlice;
use libc::c_uint;
use num_enum::TryFromPrimitive;
use paste::paste;
#[cfg(emulation_mode = "systemmode")]
use crate::modules::QemuInstrumentationPagingFilter;
@ -24,11 +26,12 @@ use crate::{
},
get_exit_arch_regs,
modules::{
HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter,
EmulatorModuleTuple, HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter,
StdInstrumentationFilter,
},
sync_exit::ExitArgs,
Emulator, ExitHandlerError, ExitHandlerResult, GuestReg, InputLocation, IsSnapshotManager,
Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorExitHandler, CPU,
Emulator, EmulatorDriverError, EmulatorDriverResult, GuestReg, InputLocation,
IsSnapshotManager, Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorDriver, CPU,
};
pub mod parser;
@ -50,111 +53,131 @@ mod bindings {
pub const VERSION: u64 = bindings::LIBAFL_QEMU_HDR_VERSION_NUMBER as u64;
macro_rules! define_std_command_manager {
($name:ident, [$($native_command_parser:ident),+]) => {
pub struct $name<ET, S, SM>
where
S: UsesInput,
{
native_command_parsers:
HashMap<GuestReg, Box<dyn NativeCommandParser<Self, StdEmulatorExitHandler<SM>, ET, S>>>,
}
($name:ident, [$($command:ty),+], [$($native_command_parser:ty),+]) => {
paste! {
pub struct $name<S> {
phantom: PhantomData<S>,
}
impl<ET, S, SM> $name<ET, S, SM>
where
ET: StdInstrumentationFilter + Unpin,
S: UsesInput + Unpin,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
#[must_use]
pub fn new() -> Self {
let native_parsers = Box::new(
vec![$(Box::new($native_command_parser)
as Box<
dyn NativeCommandParser<
Self,
StdEmulatorExitHandler<SM>,
ET,
S,
>,
>),*]
.into_iter(),
);
let mut parsers: HashMap<
GuestReg,
Box<dyn NativeCommandParser<Self, StdEmulatorExitHandler<SM>, ET, S>>,
> = HashMap::new();
for parser in native_parsers {
assert!(parsers
.insert(parser.command_id(), parser)
.is_none(), "Trying to use native commands with the same ID");
}
Self {
native_command_parsers: parsers,
impl<S> Clone for $name<S> {
fn clone(&self) -> Self {
Self {
phantom: PhantomData
}
}
}
}
impl<ET, S, SM> CommandManager<StdEmulatorExitHandler<SM>, ET, S> for $name<ET, S, SM>
where
S: UsesInput,
{
fn parse(
&self,
qemu: Qemu,
) -> Result<Rc<dyn IsCommand<Self, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let arch_regs_map: &'static EnumMap<ExitArgs, Regs> = get_exit_arch_regs();
let cmd_id: GuestReg = qemu.read_reg::<Regs, GuestReg>(arch_regs_map[ExitArgs::Cmd])?;
let cmd_parser = self
.native_command_parsers
.get(&cmd_id)
.ok_or(CommandError::UnknownCommand(cmd_id))?;
let cmd = cmd_parser.parse(qemu, arch_regs_map)?;
Ok(cmd)
impl<S> Debug for $name<S> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, stringify!($name))
}
}
}
impl<ET, S, SM> Debug for $name<ET, S, SM>
where
S: UsesInput,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
write!(f, stringify!($name))
impl<S> Default for $name<S> {
fn default() -> Self {
Self {
phantom: PhantomData
}
}
}
}
impl<ET, S, SM> Default for $name<ET, S, SM>
where
ET: StdInstrumentationFilter + Unpin,
S: UsesInput + Unpin,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
fn default() -> Self {
Self::new()
impl<ET, S, SM> CommandManager<StdEmulatorDriver, ET, S, SM> for $name<S>
where
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
S: UsesInput + Unpin,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
type Commands = [<$name Commands>];
fn parse(&self, qemu: Qemu) -> Result<Self::Commands, CommandError> {
let arch_regs_map: &'static EnumMap<ExitArgs, Regs> = get_exit_arch_regs();
let cmd_id = qemu.read_reg::<Regs, GuestReg>(arch_regs_map[ExitArgs::Cmd])? as c_uint;
match cmd_id {
// <StartPhysCommandParser as NativeCommandParser<S>>::COMMAND_ID => Ok(StdCommandManagerCommands::StartPhysCommandParserCmd(<StartPhysCommandParser as NativeCommandParser<S>>::parse(qemu, arch_regs_map)?)),
$(<$native_command_parser as NativeCommandParser<Self, StdEmulatorDriver, ET, S, SM>>::COMMAND_ID => Ok(<$native_command_parser as NativeCommandParser<Self, StdEmulatorDriver, ET, S, SM>>::parse(qemu, arch_regs_map)?.into())),+,
_ => Err(CommandError::UnknownCommand(cmd_id.into())),
}
}
}
#[derive(Clone, Debug)]
pub enum [<$name Commands>]
{
// StartPhysCommand(StartPhysCommand)
$($command($command)),+,
}
impl<ET, S, SM> IsCommand<$name<S>, StdEmulatorDriver, ET, S, SM> for [<$name Commands>]
where
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
S: UsesInput + Unpin,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
fn usable_at_runtime(&self) -> bool {
match self {
$([<$name Commands>]::$command(cmd) => <$command as IsCommand<$name<S>, StdEmulatorDriver, ET, S, SM>>::usable_at_runtime(cmd)),+
}
}
fn run(&self,
emu: &mut Emulator<$name<S>, StdEmulatorDriver, ET, S, SM>,
driver: &mut StdEmulatorDriver,
input: &S::Input,
ret_reg: Option<Regs>
) -> Result<Option<EmulatorDriverResult<$name<S>, StdEmulatorDriver, ET, S, SM>>, EmulatorDriverError> {
match self {
$([<$name Commands>]::$command(cmd) => cmd.run(emu, driver, input, ret_reg)),+
}
}
}
$(
impl From<$command> for [<$name Commands>] {
fn from(cmd: $command) -> [<$name Commands>] {
[<$name Commands>]::$command(cmd)
}
}
)+
}
};
}
pub struct NopCommandManager;
impl<EH, ET, S> CommandManager<EH, ET, S> for NopCommandManager
pub trait CommandManager<ED, ET, S, SM>: Sized + Debug
where
S: UsesInput,
{
fn parse(&self, _qemu: Qemu) -> Result<Rc<dyn IsCommand<Self, EH, ET, S>>, CommandError> {
Ok(Rc::new(NopCommand))
type Commands: IsCommand<Self, ED, ET, S, SM>;
fn parse(&self, qemu: Qemu) -> Result<Self::Commands, CommandError>;
}
#[derive(Clone, Debug)]
pub struct NopCommandManager;
impl<ED, ET, S, SM> CommandManager<ED, ET, S, SM> for NopCommandManager
where
S: UsesInput,
{
type Commands = NopCommand;
fn parse(&self, _qemu: Qemu) -> Result<Self::Commands, CommandError> {
Ok(NopCommand)
}
}
define_std_command_manager!(
StdCommandManager,
[
StartCommand,
InputCommand,
SaveCommand,
LoadCommand,
EndCommand,
VersionCommand,
AddressRangeFilterCommand
],
[
StartPhysCommandParser,
StartVirtCommandParser,
@ -168,13 +191,6 @@ define_std_command_manager!(
]
);
pub trait CommandManager<EH, ET, S>: Sized
where
S: UsesInput,
{
fn parse(&self, qemu: Qemu) -> Result<Rc<dyn IsCommand<Self, EH, ET, S>>, CommandError>;
}
#[derive(Debug, Clone, Enum, TryFromPrimitive)]
#[repr(u64)]
pub enum NativeExitKind {
@ -183,8 +199,9 @@ pub enum NativeExitKind {
Crash = bindings::LibaflQemuEndStatus_LIBAFL_QEMU_END_CRASH.0 as u64, // Crash reported in the VM
}
pub trait IsCommand<CM, EH, ET, S>: Debug + Display
pub trait IsCommand<CM, ED, ET, S, SM>: Clone + Debug
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
/// Used to know whether the command can be run during a backdoor, or if it is necessary to go out of
@ -196,12 +213,14 @@ where
/// - `ret_reg`: The register in which the guest return value should be written, if any.
/// Returns
/// - `InnerHandlerResult`: How the high-level handler should behave
#[allow(clippy::type_complexity)]
fn run(
&self,
emu: &mut Emulator<CM, EH, ET, S>,
emu: &mut Emulator<CM, ED, ET, S, SM>,
driver: &mut ED,
input: &S::Input,
ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, EH, ET, S>>, ExitHandlerError>;
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError>;
}
#[cfg(emulation_mode = "systemmode")]
@ -226,13 +245,14 @@ impl From<QemuRWError> for CommandError {
pub struct NopCommand;
impl Display for NopCommand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "NopCommand")
}
}
impl<CM, EH, ET, S> IsCommand<CM, EH, ET, S> for NopCommand
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for NopCommand
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn usable_at_runtime(&self) -> bool {
@ -241,20 +261,21 @@ where
fn run(
&self,
_emu: &mut Emulator<CM, EH, ET, S>,
_emu: &mut Emulator<CM, ED, ET, S, SM>,
_driver: &mut ED,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, EH, ET, S>>, ExitHandlerError> {
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
Ok(None)
}
}
#[derive(Debug, Clone)]
pub struct SaveCommand;
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for SaveCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorDriver, ET, S, SM> for SaveCommand
where
ET: StdInstrumentationFilter + Unpin,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput + Unpin,
SM: IsSnapshotManager,
{
@ -264,21 +285,18 @@ where
fn run(
&self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
driver: &mut StdEmulatorDriver,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
) -> Result<Option<EmulatorDriverResult<CM, StdEmulatorDriver, ET, S, SM>>, EmulatorDriverError>
{
let qemu = emu.qemu();
let snapshot_id = emu.snapshot_manager_mut().save(qemu);
{
let emu_exit_handler = emu.exit_handler().borrow_mut();
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
emu_exit_handler
.set_snapshot_id(snapshot_id)
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?;
}
driver
.set_snapshot_id(snapshot_id)
.map_err(|_| EmulatorDriverError::MultipleSnapshotDefinition)?;
#[cfg(emulation_mode = "systemmode")]
{
@ -304,8 +322,9 @@ where
#[derive(Debug, Clone)]
pub struct LoadCommand;
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for LoadCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorDriver, ET, S, SM> for LoadCommand
where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput,
SM: IsSnapshotManager,
{
@ -315,26 +334,22 @@ where
fn run(
&self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
driver: &mut StdEmulatorDriver,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
) -> Result<Option<EmulatorDriverResult<CM, StdEmulatorDriver, ET, S, SM>>, EmulatorDriverError>
{
let qemu = emu.qemu();
let emu_exit_handler = emu.exit_handler().borrow_mut();
let snapshot_id = emu_exit_handler
let snapshot_id = driver
.snapshot_id()
.ok_or(ExitHandlerError::SnapshotNotFound)?;
.ok_or(EmulatorDriverError::SnapshotNotFound)?;
emu_exit_handler
.snapshot_manager_borrow_mut()
.restore(&snapshot_id, qemu)?;
emu.snapshot_manager_mut().restore(qemu, &snapshot_id)?;
#[cfg(feature = "paranoid_debug")]
emu_exit_handler
.snapshot_manager_borrow()
.check(&snapshot_id, emu.qemu())?;
emu.snapshot_manager_mut().check(qemu, &snapshot_id)?;
Ok(None)
}
@ -346,8 +361,9 @@ pub struct InputCommand {
cpu: CPU,
}
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for InputCommand
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for InputCommand
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
S::Input: HasTargetBytes,
{
@ -357,11 +373,11 @@ where
fn run(
&self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
emu: &mut Emulator<CM, ED, ET, S, SM>,
_driver: &mut ED,
input: &S::Input,
ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
let qemu = emu.qemu();
let ret_value = self.location.write(qemu, input.target_bytes().as_slice());
@ -379,8 +395,9 @@ pub struct StartCommand {
input_location: QemuMemoryChunk,
}
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for StartCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorDriver, ET, S, SM> for StartCommand
where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
@ -391,20 +408,20 @@ where
fn run(
&self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
driver: &mut StdEmulatorDriver,
input: &S::Input,
ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
) -> Result<Option<EmulatorDriverResult<CM, StdEmulatorDriver, ET, S, SM>>, EmulatorDriverError>
{
let emu_exit_handler = emu.exit_handler().borrow_mut();
let qemu = emu.qemu();
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
let snapshot_id = emu.snapshot_manager_mut().save(qemu);
emu_exit_handler
driver
.set_snapshot_id(snapshot_id)
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?;
.map_err(|_| EmulatorDriverError::MultipleSnapshotDefinition)?;
emu_exit_handler
driver
.set_input_location(InputLocation::new(
self.input_location.clone(),
qemu.current_cpu().unwrap(),
@ -425,10 +442,13 @@ where
}
#[derive(Debug, Clone)]
pub struct EndCommand(Option<ExitKind>);
pub struct EndCommand {
exit_kind: Option<ExitKind>,
}
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for EndCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorDriver, ET, S, SM> for EndCommand
where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput,
SM: IsSnapshotManager,
{
@ -438,35 +458,35 @@ where
fn run(
&self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
driver: &mut StdEmulatorDriver,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
) -> Result<Option<EmulatorDriverResult<CM, StdEmulatorDriver, ET, S, SM>>, EmulatorDriverError>
{
let emu_exit_handler = emu.exit_handler().borrow_mut();
let qemu = emu.qemu();
let snapshot_id = emu_exit_handler
let snapshot_id = driver
.snapshot_id()
.ok_or(ExitHandlerError::SnapshotNotFound)?;
.ok_or(EmulatorDriverError::SnapshotNotFound)?;
emu_exit_handler
.snapshot_manager_borrow_mut()
.restore(&snapshot_id, emu.qemu())?;
emu.snapshot_manager_mut().restore(qemu, &snapshot_id)?;
#[cfg(feature = "paranoid_debug")]
emu_exit_handler
.snapshot_manager_borrow()
.check(&snapshot_id, emu.qemu())?;
emu.snapshot_manager_mut().check(qemu, &snapshot_id)?;
Ok(Some(ExitHandlerResult::EndOfRun(self.0.unwrap())))
Ok(Some(EmulatorDriverResult::EndOfRun(
self.exit_kind.unwrap(),
)))
}
}
#[derive(Debug, Clone)]
pub struct VersionCommand(u64);
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for VersionCommand
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for VersionCommand
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn usable_at_runtime(&self) -> bool {
@ -475,17 +495,17 @@ where
fn run(
&self,
_emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_emu: &mut Emulator<CM, ED, ET, S, SM>,
_driver: &mut ED,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
let guest_version = self.0;
if VERSION == guest_version {
Ok(None)
} else {
Err(ExitHandlerError::CommandError(
Err(EmulatorDriverError::CommandError(
CommandError::VersionDifference(guest_version),
))
}
@ -498,9 +518,10 @@ pub struct FilterCommand<T> {
}
#[cfg(emulation_mode = "systemmode")]
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for PagingFilterCommand
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for PagingFilterCommand
where
ET: StdInstrumentationFilter + Unpin,
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput + Unpin,
{
fn usable_at_runtime(&self) -> bool {
@ -509,11 +530,11 @@ where
fn run(
&self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
emu: &mut Emulator<CM, ED, ET, S, SM>,
_driver: &mut ED,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
let qemu_modules = emu.modules_mut().modules_mut();
let paging_filter =
@ -525,22 +546,22 @@ where
}
}
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for AddressRangeFilterCommand
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for AddressRangeFilterCommand
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn usable_at_runtime(&self) -> bool {
true
}
#[allow(clippy::type_complexity)] // TODO: refactor with correct type.
fn run(
&self,
_emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_emu: &mut Emulator<CM, ED, ET, S, SM>,
_driver: &mut ED,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
let qemu_modules = &mut ();
let addr_range_filter =
@ -597,7 +618,7 @@ impl Display for StartCommand {
impl Display for EndCommand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Exit of kind {:?}", self.0)
write!(f, "Exit of kind {:?}", self.exit_kind)
}
}
@ -630,7 +651,7 @@ impl StartCommand {
impl EndCommand {
#[must_use]
pub fn new(exit_kind: Option<ExitKind>) -> Self {
Self(exit_kind)
Self { exit_kind }
}
}

View File

@ -1,4 +1,4 @@
use std::{rc::Rc, sync::OnceLock};
use std::sync::OnceLock;
use enum_map::{enum_map, EnumMap};
use libafl::{
@ -6,198 +6,202 @@ use libafl::{
inputs::{HasTargetBytes, UsesInput},
};
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr};
use libc::c_uint;
use crate::{
command::{
bindings, CommandError, EndCommand, FilterCommand, InputCommand, IsCommand, LoadCommand,
NativeExitKind, SaveCommand, StartCommand, VersionCommand,
bindings, CommandError, CommandManager, EndCommand, FilterCommand, InputCommand, IsCommand,
LoadCommand, NativeExitKind, SaveCommand, StartCommand, VersionCommand,
},
modules::{
EmulatorModuleTuple, QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter,
},
modules::{QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter},
sync_exit::ExitArgs,
GuestReg, IsSnapshotManager, Qemu, QemuMemoryChunk, Regs, StdEmulatorExitHandler,
GuestReg, IsSnapshotManager, Qemu, QemuMemoryChunk, Regs, StdEmulatorDriver,
};
pub static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
pub trait NativeCommandParser<CM, EH, ET, S>
pub trait NativeCommandParser<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn command_id(&self) -> GuestReg;
type OutputCommand: IsCommand<CM, ED, ET, S, SM>;
const COMMAND_ID: c_uint;
fn parse(
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, EH, ET, S>>, CommandError>;
) -> Result<Self::OutputCommand, CommandError>;
}
pub struct InputPhysCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for InputPhysCommandParser
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for InputPhysCommandParser
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
S::Input: HasTargetBytes,
{
fn command_id(&self) -> GuestReg {
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_PHYS.0)
}
type OutputCommand = InputCommand;
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_PHYS.0;
fn parse(
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
) -> Result<Self::OutputCommand, CommandError> {
let input_phys_addr: GuestPhysAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(Rc::new(InputCommand::new(
Ok(InputCommand::new(
QemuMemoryChunk::phys(
input_phys_addr,
max_input_size,
Some(qemu.current_cpu().unwrap()),
),
qemu.current_cpu().unwrap(),
)))
))
}
}
pub struct InputVirtCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for InputVirtCommandParser
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for InputVirtCommandParser
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
S::Input: HasTargetBytes,
{
fn command_id(&self) -> GuestReg {
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_VIRT.0)
}
type OutputCommand = InputCommand;
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_VIRT.0;
fn parse(
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
) -> Result<Self::OutputCommand, CommandError> {
let input_virt_addr: GuestVirtAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(Rc::new(InputCommand::new(
Ok(InputCommand::new(
QemuMemoryChunk::virt(input_virt_addr, max_input_size, qemu.current_cpu().unwrap()),
qemu.current_cpu().unwrap(),
))
}
}
pub struct StartPhysCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorDriver, ET, S, SM> for StartPhysCommandParser
where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
type OutputCommand = StartCommand;
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_PHYS.0;
fn parse(
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Self::OutputCommand, CommandError> {
let input_phys_addr: GuestPhysAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(StartCommand::new(QemuMemoryChunk::phys(
input_phys_addr,
max_input_size,
Some(qemu.current_cpu().unwrap()),
)))
}
}
pub struct StartVirtCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorDriver, ET, S, SM> for StartVirtCommandParser
where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
type OutputCommand = StartCommand;
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_VIRT.0;
fn parse(
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Self::OutputCommand, CommandError> {
let input_virt_addr: GuestVirtAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(StartCommand::new(QemuMemoryChunk::virt(
input_virt_addr,
max_input_size,
qemu.current_cpu().unwrap(),
)))
}
}
pub struct StartPhysCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for StartPhysCommandParser
where
S: UsesInput,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
fn command_id(&self) -> GuestReg {
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_PHYS.0)
}
fn parse(
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let input_phys_addr: GuestPhysAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(Rc::new(StartCommand::new(QemuMemoryChunk::phys(
input_phys_addr,
max_input_size,
Some(qemu.current_cpu().unwrap()),
))))
}
}
pub struct StartVirtCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for StartVirtCommandParser
where
S: UsesInput,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
fn command_id(&self) -> GuestReg {
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_VIRT.0)
}
fn parse(
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let input_virt_addr: GuestVirtAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(Rc::new(StartCommand::new(QemuMemoryChunk::virt(
input_virt_addr,
max_input_size,
qemu.current_cpu().unwrap(),
))))
}
}
pub struct SaveCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> for SaveCommandParser
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorDriver, ET, S, SM> for SaveCommandParser
where
ET: StdInstrumentationFilter + Unpin,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput + Unpin,
SM: IsSnapshotManager,
{
fn command_id(&self) -> GuestReg {
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_SAVE.0)
}
type OutputCommand = SaveCommand;
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_SAVE.0;
fn parse(
&self,
_qemu: Qemu,
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
Ok(Rc::new(SaveCommand))
) -> Result<Self::OutputCommand, CommandError> {
Ok(SaveCommand)
}
}
pub struct LoadCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> for LoadCommandParser
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorDriver, ET, S, SM> for LoadCommandParser
where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput,
SM: IsSnapshotManager,
{
fn command_id(&self) -> GuestReg {
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_LOAD.0)
}
type OutputCommand = LoadCommand;
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_LOAD.0;
fn parse(
&self,
_qemu: Qemu,
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
Ok(Rc::new(LoadCommand))
) -> Result<Self::OutputCommand, CommandError> {
Ok(LoadCommand)
}
}
pub struct EndCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> for EndCommandParser
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorDriver, ET, S, SM> for EndCommandParser
where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput,
SM: IsSnapshotManager,
{
fn command_id(&self) -> GuestReg {
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_END.0)
}
type OutputCommand = EndCommand;
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_END.0;
fn parse(
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
) -> Result<Self::OutputCommand, CommandError> {
let native_exit_kind: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let native_exit_kind: Result<NativeExitKind, _> = u64::from(native_exit_kind).try_into();
@ -211,52 +215,51 @@ where
})[k]
});
Ok(Rc::new(EndCommand::new(exit_kind)))
Ok(EndCommand::new(exit_kind))
}
}
pub struct VersionCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for VersionCommandParser
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for VersionCommandParser
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn command_id(&self) -> GuestReg {
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VERSION.0)
}
type OutputCommand = VersionCommand;
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VERSION.0;
fn parse(
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
) -> Result<Self::OutputCommand, CommandError> {
let client_version = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
Ok(Rc::new(VersionCommand::new(client_version)))
Ok(VersionCommand::new(client_version))
}
}
pub struct VaddrFilterAllowRangeCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM>
for VaddrFilterAllowRangeCommandParser
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn command_id(&self) -> GuestReg {
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VADDR_FILTER_ALLOW.0)
}
type OutputCommand = FilterCommand<QemuInstrumentationAddressRangeFilter>;
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VADDR_FILTER_ALLOW.0;
fn parse(
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
) -> Result<Self::OutputCommand, CommandError> {
let vaddr_start: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let vaddr_end: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(Rc::new(FilterCommand::new(
Ok(FilterCommand::new(
#[allow(clippy::single_range_in_vec_init)]
QemuInstrumentationAddressRangeFilter::AllowList(vec![vaddr_start..vaddr_end]),
)))
))
}
}

View File

@ -0,0 +1,237 @@
use std::{fmt::Debug, marker::PhantomData};
use libafl::{
inputs::{HasTargetBytes, UsesInput},
state::{HasExecutions, State},
};
use libafl_bolts::tuples::{tuple_list, Prepend};
#[cfg(emulation_mode = "systemmode")]
use crate::FastSnapshotManager;
use crate::{
command::{CommandManager, NopCommandManager, StdCommandManager},
config::QemuConfig,
modules::{EmulatorModule, EmulatorModuleTuple},
Emulator, NopEmulatorDriver, NopSnapshotManager, Qemu, QemuInitError, StdEmulatorDriver,
StdSnapshotManager,
};
#[derive(Clone, Debug)]
enum QemuBuilder {
Qemu(Qemu),
QemuConfig(QemuConfig),
QemuString(Vec<String>),
}
#[derive(Clone, Debug)]
pub struct EmulatorBuilder<CM, ED, ET, S, SM>
where
S: UsesInput,
{
modules: ET,
driver: ED,
snapshot_manager: SM,
command_manager: CM,
qemu_builder: Option<QemuBuilder>,
phantom: PhantomData<S>,
}
impl<S> EmulatorBuilder<NopCommandManager, NopEmulatorDriver, (), S, NopSnapshotManager>
where
S: UsesInput,
{
#[must_use]
pub fn empty() -> Self {
Self {
modules: tuple_list!(),
driver: NopEmulatorDriver,
snapshot_manager: NopSnapshotManager,
command_manager: NopCommandManager,
qemu_builder: None,
phantom: PhantomData,
}
}
}
#[cfg(emulation_mode = "usermode")]
impl<S> EmulatorBuilder<StdCommandManager<S>, StdEmulatorDriver, (), S, StdSnapshotManager>
where
S: State + HasExecutions + Unpin,
S::Input: HasTargetBytes,
{
#[must_use]
#[allow(clippy::should_implement_trait)]
pub fn default() -> Self {
Self {
modules: tuple_list!(),
command_manager: StdCommandManager::default(),
snapshot_manager: StdSnapshotManager::default(),
driver: StdEmulatorDriver::default(),
qemu_builder: None,
phantom: PhantomData,
}
}
}
#[cfg(emulation_mode = "systemmode")]
impl<S> EmulatorBuilder<StdCommandManager<S>, StdEmulatorDriver, (), S, StdSnapshotManager>
where
S: State + HasExecutions + Unpin,
S::Input: HasTargetBytes,
{
pub fn default() -> Self {
Self {
modules: (),
command_manager: StdCommandManager::default(),
snapshot_manager: FastSnapshotManager::default(),
driver: StdEmulatorDriver::default(),
qemu_builder: None,
phantom: PhantomData,
}
}
}
impl<CM, ED, ET, S, SM> EmulatorBuilder<CM, ED, ET, S, SM>
where
S: UsesInput + Unpin,
{
fn new(
modules: ET,
driver: ED,
command_manager: CM,
snapshot_manager: SM,
qemu_builder: Option<QemuBuilder>,
) -> Self {
Self {
modules,
command_manager,
driver,
snapshot_manager,
qemu_builder,
phantom: PhantomData,
}
}
pub fn build(self) -> Result<Emulator<CM, ED, ET, S, SM>, QemuInitError>
where
CM: CommandManager<ED, ET, S, SM>,
ET: EmulatorModuleTuple<S>,
{
let qemu_builder = self.qemu_builder.ok_or(QemuInitError::EmptyArgs)?;
let qemu: Qemu = match qemu_builder {
QemuBuilder::Qemu(qemu) => qemu,
QemuBuilder::QemuConfig(qemu_config) => {
let res: Result<Qemu, QemuInitError> = qemu_config.into();
res?
}
QemuBuilder::QemuString(qemu_string) => Qemu::init(&qemu_string)?,
};
Emulator::new_with_qemu(
qemu,
self.modules,
self.driver,
self.snapshot_manager,
self.command_manager,
)
}
}
impl<CM, ED, ET, S, SM> EmulatorBuilder<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput + Unpin,
{
#[must_use]
pub fn qemu_config(self, qemu_config: QemuConfig) -> EmulatorBuilder<CM, ED, ET, S, SM> {
EmulatorBuilder::new(
self.modules,
self.driver,
self.command_manager,
self.snapshot_manager,
Some(QemuBuilder::QemuConfig(qemu_config)),
)
}
#[must_use]
pub fn qemu_cli(self, qemu_cli: Vec<String>) -> EmulatorBuilder<CM, ED, ET, S, SM> {
EmulatorBuilder::new(
self.modules,
self.driver,
self.command_manager,
self.snapshot_manager,
Some(QemuBuilder::QemuString(qemu_cli)),
)
}
#[must_use]
pub fn qemu(self, qemu: Qemu) -> EmulatorBuilder<CM, ED, ET, S, SM> {
EmulatorBuilder::new(
self.modules,
self.driver,
self.command_manager,
self.snapshot_manager,
Some(QemuBuilder::Qemu(qemu)),
)
}
pub fn add_module<EM>(self, module: EM) -> EmulatorBuilder<CM, ED, (EM, ET), S, SM>
where
EM: EmulatorModule<S> + Unpin,
ET: EmulatorModuleTuple<S>,
{
EmulatorBuilder::new(
self.modules.prepend(module),
self.driver,
self.command_manager,
self.snapshot_manager,
self.qemu_builder,
)
}
pub fn driver<ED2>(self, driver: ED2) -> EmulatorBuilder<CM, ED2, ET, S, SM> {
EmulatorBuilder::new(
self.modules,
driver,
self.command_manager,
self.snapshot_manager,
self.qemu_builder,
)
}
pub fn command_manager<CM2>(self, command_manager: CM2) -> EmulatorBuilder<CM2, ED, ET, S, SM>
where
CM2: CommandManager<ED, ET, S, SM>,
{
EmulatorBuilder::new(
self.modules,
self.driver,
command_manager,
self.snapshot_manager,
self.qemu_builder,
)
}
pub fn modules<ET2>(self, modules: ET2) -> EmulatorBuilder<CM, ED, ET2, S, SM> {
EmulatorBuilder::new(
modules,
self.driver,
self.command_manager,
self.snapshot_manager,
self.qemu_builder,
)
}
pub fn snapshot_manager<SM2>(
self,
snapshot_manager: SM2,
) -> EmulatorBuilder<CM, ED, ET, S, SM2> {
EmulatorBuilder::new(
self.modules,
self.driver,
self.command_manager,
snapshot_manager,
self.qemu_builder,
)
}
}

View File

@ -0,0 +1,194 @@
//! Emulator Drivers, as the name suggests, drive QEMU execution
//! They are used to perform specific actions on the emulator before and / or after QEMU runs.
use std::{cell::OnceCell, fmt::Debug};
use libafl::{
executors::ExitKind,
inputs::{HasTargetBytes, UsesInput},
};
use libafl_bolts::os::unix_signals::Signal;
use crate::{
command::{CommandError, CommandManager, InputCommand, IsCommand},
Emulator, EmulatorExitError, EmulatorExitResult, InputLocation, IsSnapshotManager,
QemuShutdownCause, Regs, SnapshotId, SnapshotManagerCheckError, SnapshotManagerError,
};
#[derive(Debug, Clone)]
pub enum EmulatorDriverResult<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
/// Return to the harness immediately. Can happen at any point of the run when the handler is not supposed to handle a request.
ReturnToHarness(EmulatorExitResult<CM, ED, ET, S, SM>),
/// The run is over and the emulator is ready for the next iteration.
EndOfRun(ExitKind),
}
#[derive(Debug, Clone)]
pub enum EmulatorDriverError {
QemuExitReasonError(EmulatorExitError),
SMError(SnapshotManagerError),
SMCheckError(SnapshotManagerCheckError),
CommandError(CommandError),
UnhandledSignal(Signal),
MultipleSnapshotDefinition,
MultipleInputDefinition,
SnapshotNotFound,
}
/// An Emulator Driver.
// TODO remove 'static when specialization will be stable
pub trait EmulatorDriver<CM, ET, S, SM>: 'static + Sized
where
CM: CommandManager<Self, ET, S, SM>,
S: UsesInput,
{
fn pre_exec(&mut self, _emulator: &mut Emulator<CM, Self, ET, S, SM>, _input: &S::Input) {}
#[allow(clippy::type_complexity)]
fn post_exec(
&mut self,
_emulator: &mut Emulator<CM, Self, ET, S, SM>,
exit_reason: &mut Result<EmulatorExitResult<CM, Self, ET, S, SM>, EmulatorExitError>,
_input: &S::Input,
) -> Result<Option<EmulatorDriverResult<CM, Self, ET, S, SM>>, EmulatorDriverError> {
match exit_reason {
Ok(reason) => Ok(Some(EmulatorDriverResult::ReturnToHarness(reason.clone()))),
Err(error) => Err(error.clone().into()),
}
}
}
pub struct NopEmulatorDriver;
impl<CM, ET, S, SM> EmulatorDriver<CM, ET, S, SM> for NopEmulatorDriver
where
CM: CommandManager<Self, ET, S, SM>,
S: UsesInput,
{
}
#[derive(Clone, Debug)]
pub struct StdEmulatorDriver {
snapshot_id: OnceCell<SnapshotId>,
input_location: OnceCell<InputLocation>,
}
impl Default for StdEmulatorDriver {
fn default() -> Self {
StdEmulatorDriver::new()
}
}
impl StdEmulatorDriver {
#[must_use]
pub fn new() -> Self {
Self {
snapshot_id: OnceCell::new(),
input_location: OnceCell::new(),
}
}
pub fn set_input_location(&self, input_location: InputLocation) -> Result<(), InputLocation> {
self.input_location.set(input_location)
}
pub fn set_snapshot_id(&self, snapshot_id: SnapshotId) -> Result<(), SnapshotId> {
self.snapshot_id.set(snapshot_id)
}
pub fn snapshot_id(&self) -> Option<SnapshotId> {
Some(*self.snapshot_id.get()?)
}
}
// TODO: replace handlers with generics to permit compile-time customization of handlers
impl<CM, ET, S, SM> EmulatorDriver<CM, ET, S, SM> for StdEmulatorDriver
where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput + Unpin,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
fn pre_exec(&mut self, emulator: &mut Emulator<CM, Self, ET, S, SM>, input: &S::Input) {
let input_location = { self.input_location.get().cloned() };
if let Some(input_location) = input_location {
let input_command =
InputCommand::new(input_location.mem_chunk.clone(), input_location.cpu);
input_command
.run(emulator, self, input, input_location.ret_register)
.unwrap();
}
}
fn post_exec(
&mut self,
emulator: &mut Emulator<CM, Self, ET, S, SM>,
exit_reason: &mut Result<EmulatorExitResult<CM, Self, ET, S, SM>, EmulatorExitError>,
input: &S::Input,
) -> Result<Option<EmulatorDriverResult<CM, Self, ET, S, SM>>, EmulatorDriverError> {
let qemu = emulator.qemu();
let mut exit_reason = match exit_reason {
Ok(exit_reason) => exit_reason,
Err(exit_error) => match exit_error {
EmulatorExitError::UnexpectedExit => {
if let Some(snapshot_id) = self.snapshot_id.get() {
emulator.snapshot_manager.restore(qemu, snapshot_id)?;
}
return Ok(Some(EmulatorDriverResult::EndOfRun(ExitKind::Crash)));
}
_ => Err(exit_error.clone())?,
},
};
#[allow(clippy::type_complexity)]
let (command, ret_reg): (Option<CM::Commands>, Option<Regs>) = match &mut exit_reason {
EmulatorExitResult::QemuExit(shutdown_cause) => match shutdown_cause {
QemuShutdownCause::HostSignal(signal) => {
signal.handle();
return Err(EmulatorDriverError::UnhandledSignal(*signal));
}
QemuShutdownCause::GuestPanic => {
return Ok(Some(EmulatorDriverResult::EndOfRun(ExitKind::Crash)))
}
_ => panic!("Unhandled QEMU shutdown cause: {shutdown_cause:?}."),
},
EmulatorExitResult::Breakpoint(bp) => (bp.trigger(qemu), None),
EmulatorExitResult::SyncExit(sync_backdoor) => {
let command = sync_backdoor.command().clone();
(Some(command), Some(sync_backdoor.ret_reg()))
}
};
if let Some(cmd) = command {
cmd.run(emulator, self, input, ret_reg)
} else {
Ok(Some(EmulatorDriverResult::ReturnToHarness(
exit_reason.clone(),
)))
}
}
}
impl<CM, ED, ET, S, SM> TryFrom<EmulatorDriverResult<CM, ED, ET, S, SM>> for ExitKind
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
type Error = String;
fn try_from(value: EmulatorDriverResult<CM, ED, ET, S, SM>) -> Result<Self, Self::Error> {
match value {
EmulatorDriverResult::ReturnToHarness(unhandled_qemu_exit) => {
Err(format!("Unhandled QEMU exit: {:?}", &unhandled_qemu_exit))
}
EmulatorDriverResult::EndOfRun(exit_kind) => Ok(exit_kind),
}
}
}

View File

@ -9,34 +9,35 @@ use libafl_qemu_sys::{CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, GuestUsiz
#[cfg(emulation_mode = "usermode")]
use crate::qemu::{
closure_new_thread_hook_wrapper, closure_post_syscall_hook_wrapper,
closure_pre_syscall_hook_wrapper, func_new_thread_hook_wrapper, func_post_syscall_hook_wrapper,
func_pre_syscall_hook_wrapper, NewThreadHook, NewThreadHookId, PostSyscallHook,
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,
};
#[cfg(emulation_mode = "usermode")]
use crate::qemu::{
CrashHookClosure, NewThreadHookClosure, PostSyscallHookClosure, PostSyscallHookFn,
PreSyscallHookClosure, PreSyscallHookFn,
CrashHookClosure, PostSyscallHookClosure, PostSyscallHookFn, PreSyscallHookClosure,
PreSyscallHookFn,
};
use crate::{
cpu_run_post_exec_hook_wrapper, cpu_run_pre_exec_hook_wrapper,
modules::{EmulatorModule, EmulatorModuleTuple},
qemu::{
block_0_exec_hook_wrapper, block_gen_hook_wrapper, block_post_gen_hook_wrapper,
closure_backdoor_hook_wrapper, closure_instruction_hook_wrapper, cmp_0_exec_hook_wrapper,
cmp_1_exec_hook_wrapper, cmp_2_exec_hook_wrapper, cmp_3_exec_hook_wrapper,
cmp_gen_hook_wrapper, edge_0_exec_hook_wrapper, edge_gen_hook_wrapper,
func_backdoor_hook_wrapper, func_instruction_hook_wrapper, read_0_exec_hook_wrapper,
closure_backdoor_hook_wrapper, closure_instruction_hook_wrapper,
closure_new_thread_hook_wrapper, cmp_0_exec_hook_wrapper, cmp_1_exec_hook_wrapper,
cmp_2_exec_hook_wrapper, cmp_3_exec_hook_wrapper, cmp_gen_hook_wrapper,
edge_0_exec_hook_wrapper, edge_gen_hook_wrapper, func_backdoor_hook_wrapper,
func_instruction_hook_wrapper, func_new_thread_hook_wrapper, read_0_exec_hook_wrapper,
read_1_exec_hook_wrapper, read_2_exec_hook_wrapper, read_3_exec_hook_wrapper,
read_4_exec_hook_wrapper, read_gen_hook_wrapper, write_0_exec_hook_wrapper,
write_1_exec_hook_wrapper, write_2_exec_hook_wrapper, write_3_exec_hook_wrapper,
write_4_exec_hook_wrapper, write_gen_hook_wrapper, BackdoorHook, BackdoorHookClosure,
BackdoorHookFn, BackdoorHookId, BlockExecHook, BlockGenHook, BlockHookId, BlockPostGenHook,
CmpExecHook, CmpGenHook, CmpHookId, EdgeExecHook, EdgeGenHook, EdgeHookId, Hook, HookRepr,
InstructionHook, InstructionHookClosure, InstructionHookFn, InstructionHookId, QemuHooks,
ReadExecHook, ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook,
WriteExecNHook, WriteGenHook, WriteHookId,
InstructionHook, InstructionHookClosure, InstructionHookFn, InstructionHookId,
NewThreadHook, NewThreadHookClosure, NewThreadHookId, QemuHooks, ReadExecHook,
ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook, WriteExecNHook,
WriteGenHook, WriteHookId,
},
CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, Qemu,
};
@ -114,7 +115,6 @@ where
S: UsesInput,
{
qemu_hooks: QemuHooks,
phantom: PhantomData<(ET, S)>,
instruction_hooks: Vec<Pin<Box<(InstructionHookId, FatPtr)>>>,
backdoor_hooks: Vec<Pin<Box<(BackdoorHookId, FatPtr)>>>,
@ -126,17 +126,18 @@ where
cpu_run_hooks: Vec<Pin<Box<HookState<CpuRunHookId>>>>,
new_thread_hooks: Vec<Pin<Box<(NewThreadHookId, FatPtr)>>>,
#[cfg(emulation_mode = "usermode")]
pre_syscall_hooks: Vec<Pin<Box<(PreSyscallHookId, FatPtr)>>>,
#[cfg(emulation_mode = "usermode")]
post_syscall_hooks: Vec<Pin<Box<(PostSyscallHookId, FatPtr)>>>,
#[cfg(emulation_mode = "usermode")]
new_thread_hooks: Vec<Pin<Box<(NewThreadHookId, FatPtr)>>>,
#[cfg(emulation_mode = "usermode")]
crash_hooks: Vec<HookRepr>,
phantom: PhantomData<(ET, S)>,
}
impl<ET, S> EmulatorHooks<ET, S>
@ -158,15 +159,14 @@ where
cpu_run_hooks: Vec::new(),
new_thread_hooks: Vec::new(),
#[cfg(emulation_mode = "usermode")]
pre_syscall_hooks: Vec::new(),
#[cfg(emulation_mode = "usermode")]
post_syscall_hooks: Vec::new(),
#[cfg(emulation_mode = "usermode")]
new_thread_hooks: Vec::new(),
#[cfg(emulation_mode = "usermode")]
crash_hooks: Vec::new(),
}
@ -700,6 +700,63 @@ where
Hook::Empty => None, // TODO error type
}
}
pub fn thread_creation(&mut self, hook: NewThreadHook<ET, S>) -> Option<NewThreadHookId> {
match hook {
Hook::Function(f) => Some(self.thread_creation_function(f)),
Hook::Closure(c) => Some(self.thread_creation_closure(c)),
Hook::Raw(r) => {
let z: *const () = ptr::null::<()>();
Some(self.qemu_hooks.add_new_thread_hook(z, r))
}
Hook::Empty => None, // TODO error type
}
}
pub fn thread_creation_function(
&mut self,
hook: fn(
&mut EmulatorModules<ET, S>,
Option<&mut S>,
env: CPUArchStatePtr,
tid: u32,
) -> bool,
) -> NewThreadHookId {
unsafe {
self.qemu_hooks
.add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::<ET, S>)
}
}
pub fn thread_creation_closure(
&mut self,
hook: NewThreadHookClosure<ET, S>,
) -> NewThreadHookId {
unsafe {
let fat: FatPtr = transmute(hook);
self.new_thread_hooks
.push(Box::pin((NewThreadHookId::invalid(), fat)));
let hook_state = &mut self
.new_thread_hooks
.last_mut()
.unwrap()
.as_mut()
.get_unchecked_mut()
.1 as *mut FatPtr;
let id = self
.qemu_hooks
.add_new_thread_hook(&mut *hook_state, closure_new_thread_hook_wrapper::<ET, S>);
self.new_thread_hooks
.last_mut()
.unwrap()
.as_mut()
.get_unchecked_mut()
.0 = id;
id
}
}
}
#[cfg(emulation_mode = "usermode")]
@ -811,62 +868,6 @@ where
}
}
pub fn thread_creation(&mut self, hook: NewThreadHook<ET, S>) -> Option<NewThreadHookId> {
match hook {
Hook::Function(f) => Some(self.thread_creation_function(f)),
Hook::Closure(c) => Some(self.thread_creation_closure(c)),
Hook::Raw(r) => {
let z: *const () = ptr::null::<()>();
Some(self.qemu_hooks.add_new_thread_hook(z, r))
}
Hook::Empty => None, // TODO error type
}
}
pub fn thread_creation_function(
&mut self,
hook: fn(
&mut EmulatorModules<ET, S>,
Option<&mut S>,
env: CPUArchStatePtr,
tid: u32,
) -> bool,
) -> NewThreadHookId {
unsafe {
self.qemu_hooks
.add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::<ET, S>)
}
}
pub fn thread_creation_closure(
&mut self,
hook: NewThreadHookClosure<ET, S>,
) -> NewThreadHookId {
unsafe {
let fat: FatPtr = transmute(hook);
self.new_thread_hooks
.push(Box::pin((NewThreadHookId::invalid(), fat)));
let hook_state = &mut self
.new_thread_hooks
.last_mut()
.unwrap()
.as_mut()
.get_unchecked_mut()
.1 as *mut FatPtr;
let id = self
.qemu_hooks
.add_new_thread_hook(&mut *hook_state, closure_new_thread_hook_wrapper::<ET, S>);
self.new_thread_hooks
.last_mut()
.unwrap()
.as_mut()
.get_unchecked_mut()
.0 = id;
id
}
}
pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules<ET, S>, target_signal: i32)) {
self.qemu_hooks.set_crash_hook(crash_hook_wrapper::<ET, S>);
self.crash_hooks
@ -1054,6 +1055,29 @@ where
pub fn backdoor_closure(&mut self, hook: BackdoorHookClosure<ET, S>) -> BackdoorHookId {
self.hooks.backdoor_closure(hook)
}
pub fn thread_creation(&mut self, hook: NewThreadHook<ET, S>) -> Option<NewThreadHookId> {
self.hooks.thread_creation(hook)
}
pub fn thread_creation_function(
&mut self,
hook: fn(
&mut EmulatorModules<ET, S>,
Option<&mut S>,
env: CPUArchStatePtr,
tid: u32,
) -> bool,
) -> NewThreadHookId {
self.hooks.thread_creation_function(hook)
}
pub fn thread_creation_closure(
&mut self,
hook: NewThreadHookClosure<ET, S>,
) -> NewThreadHookId {
self.hooks.thread_creation_closure(hook)
}
}
impl<ET, S> EmulatorModules<ET, S>
@ -1266,28 +1290,6 @@ where
self.hooks.after_syscalls_closure(hook)
}
pub fn thread_creation(&mut self, hook: NewThreadHook<ET, S>) -> Option<NewThreadHookId> {
self.hooks.thread_creation(hook)
}
pub fn thread_creation_function(
&mut self,
hook: fn(
&mut EmulatorModules<ET, S>,
Option<&mut S>,
env: CPUArchStatePtr,
tid: u32,
) -> bool,
) -> NewThreadHookId {
self.hooks.thread_creation_function(hook)
}
pub fn thread_creation_closure(
&mut self,
hook: NewThreadHookClosure<ET, S>,
) -> NewThreadHookId {
self.hooks.thread_creation_closure(hook)
}
pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules<ET, S>, target_signal: i32)) {
self.hooks.crash_function(hook);
}

View File

@ -2,110 +2,102 @@
//!
//! [`Emulator`] is built above [`Qemu`] and provides convenient abstractions.
use core::{
fmt::{self, Debug, Display, Formatter},
marker::PhantomData,
};
use std::{
cell::{OnceCell, Ref, RefCell, RefMut},
hash::Hash,
ops::Add,
pin::Pin,
rc::Rc,
};
use core::fmt::{self, Debug, Display, Formatter};
use std::{cell::RefCell, ops::Add, pin::Pin};
use hashbrown::HashMap;
use libafl::{
executors::ExitKind,
inputs::{HasTargetBytes, UsesInput},
observers::ObserversTuple,
state::{HasExecutions, State},
};
use libafl_bolts::os::unix_signals::Signal;
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestUsize, GuestVirtAddr};
use typed_builder::TypedBuilder;
use crate::{
breakpoint::{Breakpoint, BreakpointId},
command::{CommandError, CommandManager, InputCommand, IsCommand},
modules::EmulatorModuleTuple,
command::{CommandError, CommandManager, NopCommandManager, StdCommandManager},
modules::{EmulatorModuleTuple, StdInstrumentationFilter},
sync_exit::SyncExit,
Qemu, QemuExitError, QemuExitReason, QemuInitError, QemuMemoryChunk, QemuShutdownCause,
QemuSnapshotCheckResult, Regs, CPU,
Qemu, QemuExitError, QemuExitReason, QemuInitError, QemuMemoryChunk, QemuShutdownCause, Regs,
CPU,
};
mod hooks;
pub use hooks::*;
mod builder;
pub use builder::*;
mod drivers;
pub use drivers::*;
mod snapshot;
pub use snapshot::*;
#[cfg(emulation_mode = "usermode")]
mod usermode;
#[cfg(emulation_mode = "usermode")]
pub use usermode::*;
#[cfg(emulation_mode = "systemmode")]
mod systemmode;
#[cfg(emulation_mode = "systemmode")]
pub use systemmode::*;
type CommandRef<CM, E, ET, S> = Rc<dyn IsCommand<CM, E, ET, S>>;
type BreakpointMutRef<CM, E, ET, S> = Rc<RefCell<Breakpoint<CM, E, ET, S>>>;
pub trait IsSnapshotManager: Clone + Debug {
fn save(&mut self, qemu: Qemu) -> SnapshotId;
fn restore(&mut self, 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))
}
}
}
pub trait EmulatorExitHandler<ET, S>: Sized + Debug + Clone
where
S: UsesInput,
{
fn qemu_pre_exec<CM: CommandManager<Self, ET, S>>(
emu: &mut Emulator<CM, Self, ET, S>,
input: &S::Input,
);
fn qemu_post_exec<CM: CommandManager<Self, ET, S>>(
emu: &mut Emulator<CM, Self, ET, S>,
exit_reason: Result<EmulatorExitResult<CM, Self, ET, S>, EmulatorExitError>,
input: &S::Input,
) -> Result<Option<ExitHandlerResult<CM, Self, ET, S>>, ExitHandlerError>;
}
#[derive(Clone, Copy)]
pub enum GuestAddrKind {
Physical(GuestPhysAddr),
Virtual(GuestVirtAddr),
}
#[derive(Debug, Clone)]
pub enum EmulatorExitResult<CM, EH, ET, S>
pub enum EmulatorExitResult<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
QemuExit(QemuShutdownCause), // QEMU ended for some reason.
Breakpoint(Rc<RefCell<Breakpoint<CM, EH, ET, S>>>), // Breakpoint triggered. Contains the address of the trigger.
SyncExit(Rc<RefCell<SyncExit<CM, EH, ET, S>>>), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL.
QemuExit(QemuShutdownCause), // QEMU ended for some reason.
Breakpoint(Breakpoint<CM, ED, ET, S, SM>), // Breakpoint triggered. Contains the address of the trigger.
SyncExit(SyncExit<CM, ED, ET, S, SM>), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL.
}
impl<CM, ED, ET, S, SM> Clone for EmulatorExitResult<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn clone(&self) -> Self {
match self {
EmulatorExitResult::QemuExit(qemu_exit) => {
EmulatorExitResult::QemuExit(qemu_exit.clone())
}
EmulatorExitResult::Breakpoint(bp) => EmulatorExitResult::Breakpoint(bp.clone()),
EmulatorExitResult::SyncExit(sync_exit) => {
EmulatorExitResult::SyncExit(sync_exit.clone())
}
}
}
}
impl<CM, ED, ET, S, SM> Debug for EmulatorExitResult<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
EmulatorExitResult::QemuExit(qemu_exit) => {
write!(f, "{qemu_exit:?}")
}
EmulatorExitResult::Breakpoint(bp) => {
write!(f, "{bp:?}")
}
EmulatorExitResult::SyncExit(sync_exit) => {
write!(f, "{sync_exit:?}")
}
}
}
}
#[derive(Debug, Clone)]
pub enum EmulatorExitError {
UnknownKind,
@ -114,71 +106,6 @@ pub enum EmulatorExitError {
BreakpointNotFound(GuestAddr),
}
#[derive(Debug, Clone)]
pub enum ExitHandlerError {
QemuExitReasonError(EmulatorExitError),
SMError(SnapshotManagerError),
SMCheckError(SnapshotManagerCheckError),
CommandError(CommandError),
UnhandledSignal(Signal),
MultipleSnapshotDefinition,
MultipleInputDefinition,
SnapshotNotFound,
}
#[derive(Debug, Clone)]
pub enum ExitHandlerResult<CM, EH, ET, S>
where
S: UsesInput,
{
ReturnToHarness(EmulatorExitResult<CM, EH, ET, S>), // Return to the harness immediately. Can happen at any point of the run when the handler is not supposed to handle a request.
EndOfRun(ExitKind), // The run is over and the emulator is ready for the next iteration.
}
#[derive(Debug, Clone)]
pub enum SnapshotManagerError {
SnapshotIdNotFound(SnapshotId),
MemoryInconsistencies(u64),
}
#[derive(Debug, Clone)]
pub enum SnapshotManagerCheckError {
SnapshotManagerError(SnapshotManagerError),
SnapshotCheckError(QemuSnapshotCheckResult),
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub struct SnapshotId {
id: u64,
}
/// Special kind of Exit handler with no data embedded.
/// As a result, it is safe to transmute from any `Emulator` implementing `EmuExitHandler` to this one,
/// since it won't use any data which could cause type confusion.
#[derive(Clone, Debug)]
pub struct NopEmulatorExitHandler;
impl<ET, S> EmulatorExitHandler<ET, S> for NopEmulatorExitHandler
where
S: UsesInput,
{
fn qemu_pre_exec<CM: CommandManager<Self, ET, S>>(
_: &mut Emulator<CM, Self, ET, S>,
_: &S::Input,
) {
}
fn qemu_post_exec<CM: CommandManager<Self, ET, S>>(
_: &mut Emulator<CM, Self, ET, S>,
exit_reason: Result<EmulatorExitResult<CM, Self, ET, S>, EmulatorExitError>,
_: &S::Input,
) -> Result<Option<ExitHandlerResult<CM, Self, ET, S>>, ExitHandlerError> {
match exit_reason {
Ok(reason) => Ok(Some(ExitHandlerResult::ReturnToHarness(reason))),
Err(error) => Err(error)?,
}
}
}
#[derive(Debug, Clone)]
pub struct InputLocation {
mem_chunk: QemuMemoryChunk,
@ -186,69 +113,38 @@ pub struct InputLocation {
ret_register: Option<Regs>,
}
/// Synchronous Exit handler maintaining only one snapshot.
#[derive(Debug, Clone, TypedBuilder)]
pub struct StdEmulatorExitHandler<SM> {
snapshot_manager: RefCell<SM>,
#[builder(default)]
snapshot_id: OnceCell<SnapshotId>,
#[builder(default)]
input_location: OnceCell<InputLocation>,
}
// TODO: Replace TypedBuilder by something better, it does not work correctly with default and
// inter-dependent fields.
#[derive(Debug, TypedBuilder)]
pub struct Emulator<CM, EH, ET, S>
#[derive(Debug)]
#[allow(clippy::type_complexity)]
pub struct Emulator<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
snapshot_manager: SM,
modules: Pin<Box<EmulatorModules<ET, S>>>,
command_manager: CM,
exit_handler: RefCell<EH>,
#[builder(default)]
breakpoints_by_addr: RefCell<HashMap<GuestAddr, BreakpointMutRef<CM, EH, ET, S>>>,
#[builder(default)]
breakpoints_by_id: RefCell<HashMap<BreakpointId, BreakpointMutRef<CM, EH, ET, S>>>,
#[builder(setter(transform = |args: &[String], env: &[(String, String)]| Qemu::init(args, env).unwrap()))]
driver: Option<ED>,
breakpoints_by_addr: RefCell<HashMap<GuestAddr, Breakpoint<CM, ED, ET, S, SM>>>, // TODO: change to RC here
breakpoints_by_id: RefCell<HashMap<BreakpointId, Breakpoint<CM, ED, ET, S, SM>>>,
qemu: Qemu,
first_exec: bool,
_phantom: PhantomData<(ET, S)>,
}
impl<CM, EH, ET, S> ExitHandlerResult<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> EmulatorDriverResult<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
#[must_use]
#[allow(clippy::match_wildcard_for_single_variants)]
pub fn end_of_run(&self) -> Option<ExitKind> {
match self {
ExitHandlerResult::EndOfRun(exit_kind) => Some(*exit_kind),
EmulatorDriverResult::EndOfRun(exit_kind) => Some(*exit_kind),
_ => None,
}
}
}
impl<CM, EH, ET, S> TryFrom<ExitHandlerResult<CM, EH, ET, S>> for ExitKind
where
CM: Debug,
EH: Debug,
ET: Debug,
S: UsesInput + Debug,
{
type Error = String;
fn try_from(value: ExitHandlerResult<CM, EH, ET, S>) -> Result<Self, Self::Error> {
match value {
ExitHandlerResult::ReturnToHarness(unhandled_qemu_exit) => {
Err(format!("Unhandled QEMU exit: {:?}", &unhandled_qemu_exit))
}
ExitHandlerResult::EndOfRun(exit_kind) => Ok(exit_kind),
}
}
}
impl Debug for GuestAddrKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
@ -270,7 +166,7 @@ impl Add<GuestUsize> for GuestAddrKind {
}
impl Display for GuestAddrKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
GuestAddrKind::Physical(phys_addr) => write!(f, "hwaddr 0x{phys_addr:x}"),
GuestAddrKind::Virtual(virt_addr) => write!(f, "vaddr 0x{virt_addr:x}"),
@ -278,15 +174,15 @@ impl Display for GuestAddrKind {
}
}
impl From<SnapshotManagerError> for ExitHandlerError {
impl From<SnapshotManagerError> for EmulatorDriverError {
fn from(sm_error: SnapshotManagerError) -> Self {
ExitHandlerError::SMError(sm_error)
EmulatorDriverError::SMError(sm_error)
}
}
impl From<SnapshotManagerCheckError> for ExitHandlerError {
impl From<SnapshotManagerCheckError> for EmulatorDriverError {
fn from(sm_check_error: SnapshotManagerCheckError) -> Self {
ExitHandlerError::SMCheckError(sm_check_error)
EmulatorDriverError::SMCheckError(sm_check_error)
}
}
@ -301,140 +197,29 @@ impl InputLocation {
}
}
impl<SM> StdEmulatorExitHandler<SM> {
pub fn new(snapshot_manager: SM) -> Self {
Self {
snapshot_manager: RefCell::new(snapshot_manager),
snapshot_id: OnceCell::new(),
input_location: OnceCell::new(),
}
}
pub fn set_input_location(&self, input_location: InputLocation) -> Result<(), InputLocation> {
self.input_location.set(input_location)
}
pub fn set_snapshot_id(&self, snapshot_id: SnapshotId) -> Result<(), SnapshotId> {
self.snapshot_id.set(snapshot_id)
}
pub fn snapshot_id(&self) -> Option<SnapshotId> {
Some(*self.snapshot_id.get()?)
}
pub fn snapshot_manager_borrow(&self) -> Ref<SM> {
self.snapshot_manager.borrow()
}
pub fn snapshot_manager_borrow_mut(&self) -> RefMut<SM> {
self.snapshot_manager.borrow_mut()
}
}
// TODO: replace handlers with generics to permit compile-time customization of handlers
impl<ET, S, SM> EmulatorExitHandler<ET, S> for StdEmulatorExitHandler<SM>
where
S: UsesInput,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
fn qemu_pre_exec<CM: CommandManager<Self, ET, S>>(
emu: &mut Emulator<CM, Self, ET, S>,
input: &S::Input,
) {
let input_location = {
let exit_handler = emu.exit_handler.borrow();
exit_handler.input_location.get().cloned()
};
if let Some(input_location) = input_location {
let input_command =
InputCommand::new(input_location.mem_chunk.clone(), input_location.cpu);
input_command
.run(emu, input, input_location.ret_register)
.unwrap();
}
}
fn qemu_post_exec<CM: CommandManager<Self, ET, S>>(
emu: &mut Emulator<CM, Self, ET, S>,
exit_reason: Result<EmulatorExitResult<CM, Self, ET, S>, EmulatorExitError>,
input: &S::Input,
) -> Result<Option<ExitHandlerResult<CM, Self, ET, S>>, ExitHandlerError> {
let exit_handler = emu.exit_handler().borrow_mut();
let qemu = emu.qemu();
let mut exit_reason = match exit_reason {
Ok(exit_reason) => exit_reason,
Err(exit_error) => match exit_error {
EmulatorExitError::UnexpectedExit => {
if let Some(snapshot_id) = exit_handler.snapshot_id.get() {
exit_handler
.snapshot_manager
.borrow_mut()
.restore(snapshot_id, qemu)?;
}
return Ok(Some(ExitHandlerResult::EndOfRun(ExitKind::Crash)));
}
_ => Err(exit_error)?,
},
};
#[allow(clippy::type_complexity)]
let (command, ret_reg): (Option<CommandRef<CM, Self, ET, S>>, Option<Regs>) =
match &mut exit_reason {
EmulatorExitResult::QemuExit(shutdown_cause) => match shutdown_cause {
QemuShutdownCause::HostSignal(signal) => {
signal.handle();
return Err(ExitHandlerError::UnhandledSignal(*signal));
}
QemuShutdownCause::GuestPanic => {
return Ok(Some(ExitHandlerResult::EndOfRun(ExitKind::Crash)))
}
_ => panic!("Unhandled QEMU shutdown cause: {shutdown_cause:?}."),
},
EmulatorExitResult::Breakpoint(bp) => (bp.borrow_mut().trigger(qemu), None),
EmulatorExitResult::SyncExit(sync_backdoor) => {
let sync_backdoor = sync_backdoor.borrow();
let command = sync_backdoor.command();
(Some(command), Some(sync_backdoor.ret_reg()))
}
};
// manually drop ref cell here to avoid keeping it alive in cmd.
drop(exit_handler);
if let Some(cmd) = command {
cmd.run(emu, input, ret_reg)
} else {
Ok(Some(ExitHandlerResult::ReturnToHarness(exit_reason)))
}
}
}
impl From<EmulatorExitError> for ExitHandlerError {
impl From<EmulatorExitError> for EmulatorDriverError {
fn from(error: EmulatorExitError) -> Self {
ExitHandlerError::QemuExitReasonError(error)
EmulatorDriverError::QemuExitReasonError(error)
}
}
impl From<CommandError> for ExitHandlerError {
impl From<CommandError> for EmulatorDriverError {
fn from(error: CommandError) -> Self {
ExitHandlerError::CommandError(error)
EmulatorDriverError::CommandError(error)
}
}
impl<CM, EH, ET, S> Display for EmulatorExitResult<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Display for EmulatorExitResult<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
EmulatorExitResult::QemuExit(shutdown_cause) => write!(f, "End: {shutdown_cause:?}"),
EmulatorExitResult::Breakpoint(bp) => write!(f, "{}", bp.borrow()),
EmulatorExitResult::Breakpoint(bp) => write!(f, "{bp}"),
EmulatorExitResult::SyncExit(sync_exit) => {
write!(f, "Sync exit: {}", sync_exit.borrow())
write!(f, "Sync exit: {sync_exit:?}")
}
}
}
@ -446,8 +231,67 @@ impl From<CommandError> for EmulatorExitError {
}
}
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
impl<S> Emulator<NopCommandManager, NopEmulatorDriver, (), S, NopSnapshotManager>
where
S: UsesInput,
{
#[must_use]
pub fn empty(
) -> EmulatorBuilder<NopCommandManager, NopEmulatorDriver, (), S, NopSnapshotManager> {
EmulatorBuilder::empty()
}
}
impl<S> Emulator<StdCommandManager<S>, StdEmulatorDriver, (), S, StdSnapshotManager>
where
S: State + HasExecutions + Unpin,
S::Input: HasTargetBytes,
{
#[must_use]
pub fn builder(
) -> EmulatorBuilder<StdCommandManager<S>, StdEmulatorDriver, (), S, StdSnapshotManager> {
EmulatorBuilder::default()
}
}
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
pub fn modules(&self) -> &EmulatorModules<ET, S> {
&self.modules
}
#[must_use]
pub fn qemu(&self) -> Qemu {
self.qemu
}
#[must_use]
pub fn driver(&self) -> &ED {
self.driver.as_ref().unwrap()
}
#[must_use]
pub fn driver_mut(&mut self) -> &mut ED {
self.driver.as_mut().unwrap()
}
#[must_use]
pub fn snapshot_manager(&self) -> &SM {
&self.snapshot_manager
}
#[must_use]
pub fn snapshot_manager_mut(&mut self) -> &mut SM {
&mut self.snapshot_manager
}
}
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
ET: Unpin,
S: UsesInput + Unpin,
{
@ -456,38 +300,40 @@ where
}
}
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
ET: EmulatorModuleTuple<S>,
S: UsesInput + Unpin,
{
#[allow(clippy::must_use_candidate, clippy::similar_names)]
pub fn new(
args: &[String],
env: &[(String, String)],
qemu_args: &[String],
modules: ET,
exit_handler: EH,
drivers: ED,
snapshot_manager: SM,
command_manager: CM,
) -> Result<Self, QemuInitError> {
let qemu = Qemu::init(args, env)?;
let qemu = Qemu::init(qemu_args)?;
Self::new_with_qemu(qemu, modules, exit_handler, command_manager)
Self::new_with_qemu(qemu, modules, drivers, snapshot_manager, command_manager)
}
pub fn new_with_qemu(
qemu: Qemu,
modules: ET,
exit_handler: EH,
driver: ED,
snapshot_manager: SM,
command_manager: CM,
) -> Result<Self, QemuInitError> {
Ok(Emulator {
modules: EmulatorModules::new(qemu, modules),
command_manager,
exit_handler: RefCell::new(exit_handler),
snapshot_manager,
driver: Some(driver),
breakpoints_by_addr: RefCell::new(HashMap::new()),
breakpoints_by_id: RefCell::new(HashMap::new()),
first_exec: true,
_phantom: PhantomData,
qemu,
})
}
@ -515,29 +361,11 @@ where
}
}
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
where
S: UsesInput,
{
pub fn modules(&self) -> &EmulatorModules<ET, S> {
&self.modules
}
#[must_use]
pub fn qemu(&self) -> Qemu {
self.qemu
}
#[must_use]
pub fn exit_handler(&self) -> &RefCell<EH> {
&self.exit_handler
}
}
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
where
EH: EmulatorExitHandler<ET, S>,
CM: CommandManager<EH, ET, S>,
CM: CommandManager<ED, ET, S, SM>,
ED: EmulatorDriver<CM, ET, S, SM>,
ET: StdInstrumentationFilter + Unpin,
S: UsesInput,
{
/// This function will run the emulator until the exit handler decides to stop the execution for
@ -551,24 +379,19 @@ where
pub unsafe fn run(
&mut self,
input: &S::Input,
) -> Result<ExitHandlerResult<CM, EH, ET, S>, ExitHandlerError> {
) -> Result<EmulatorDriverResult<CM, ED, ET, S, SM>, EmulatorDriverError> {
let mut driver = self.driver.take().unwrap();
loop {
// if self.first_exec {
// self.modules_mut().first_exec_all();
// self.first_exec = false;
// }
// // First run modules callback functions
// self.modules_mut().pre_exec_all(input);
// Insert input if the location is already known
EH::qemu_pre_exec(self, input);
driver.pre_exec(self, input);
// Run QEMU
let exit_reason = self.run_qemu();
let mut exit_reason = self.run_qemu();
// Handle QEMU exit
if let Some(exit_handler_result) = EH::qemu_post_exec(self, exit_reason, input)? {
if let Some(exit_handler_result) = driver.post_exec(self, &mut exit_reason, input)? {
self.driver = Some(driver);
return Ok(exit_handler_result);
}
}
@ -579,7 +402,9 @@ where
///
/// Should, in general, be safe to call.
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
pub unsafe fn run_qemu(&self) -> Result<EmulatorExitResult<CM, EH, ET, S>, EmulatorExitError> {
pub unsafe fn run_qemu(
&self,
) -> Result<EmulatorExitResult<CM, ED, ET, S, SM>, EmulatorExitError> {
match self.qemu.run() {
Ok(qemu_exit_reason) => Ok(match qemu_exit_reason {
QemuExitReason::End(qemu_shutdown_cause) => {
@ -594,9 +419,9 @@ where
.clone();
EmulatorExitResult::Breakpoint(bp.clone())
}
QemuExitReason::SyncExit => EmulatorExitResult::SyncExit(Rc::new(RefCell::new(
SyncExit::new(self.command_manager.parse(self.qemu)?),
))),
QemuExitReason::SyncExit => EmulatorExitResult::SyncExit(SyncExit::new(
self.command_manager.parse(self.qemu)?,
)),
}),
Err(qemu_exit_reason_error) => Err(match qemu_exit_reason_error {
QemuExitError::UnexpectedExit => EmulatorExitError::UnexpectedExit,
@ -607,11 +432,16 @@ where
}
#[allow(clippy::unused_self)]
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
pub fn add_breakpoint(&self, mut bp: Breakpoint<CM, EH, ET, S>, enable: bool) -> BreakpointId {
pub fn add_breakpoint(
&self,
mut bp: Breakpoint<CM, ED, ET, S, SM>,
enable: bool,
) -> BreakpointId {
if enable {
bp.enable(self.qemu);
}
@ -619,12 +449,10 @@ where
let bp_id = bp.id();
let bp_addr = bp.addr();
let bp_ref = Rc::new(RefCell::new(bp));
assert!(
self.breakpoints_by_addr
.borrow_mut()
.insert(bp_addr, bp_ref.clone())
.insert(bp_addr, bp.clone())
.is_none(),
"Adding multiple breakpoints at the same address"
);
@ -632,7 +460,7 @@ where
assert!(
self.breakpoints_by_id
.borrow_mut()
.insert(bp_id, bp_ref)
.insert(bp_id, bp)
.is_none(),
"Adding the same breakpoint multiple times"
);
@ -643,10 +471,7 @@ where
pub fn remove_breakpoint(&self, bp_id: BreakpointId) {
let bp_addr = {
let mut bp_map = self.breakpoints_by_id.borrow_mut();
let mut bp = bp_map
.get_mut(&bp_id)
.expect("Did not find the breakpoint")
.borrow_mut();
let bp = bp_map.get_mut(&bp_id).expect("Did not find the breakpoint");
bp.disable(self.qemu);
bp.addr()
};

View File

@ -0,0 +1,119 @@
use std::{
fmt::Debug,
sync::atomic::{AtomicU64, Ordering},
};
use crate::Qemu;
pub trait IsSnapshotManager: Clone + Debug {
fn save(&mut self, qemu: Qemu) -> SnapshotId;
fn restore(&mut self, qemu: Qemu, snapshot_id: &SnapshotId)
-> Result<(), SnapshotManagerError>;
fn do_check(
&self,
qemu: Qemu,
reference_snapshot_id: &SnapshotId,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError>;
fn check(
&self,
qemu: Qemu,
reference_snapshot_id: &SnapshotId,
) -> Result<(), SnapshotManagerCheckError> {
let check_result = self
.do_check(qemu, reference_snapshot_id)
.map_err(SnapshotManagerCheckError::SnapshotManagerError)?;
if check_result == QemuSnapshotCheckResult::default() {
Ok(())
} else {
Err(SnapshotManagerCheckError::SnapshotCheckError(check_result))
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QemuSnapshotCheckResult {
nb_page_inconsistencies: u64,
}
#[derive(Debug, Clone)]
pub enum SnapshotManagerError {
SnapshotIdNotFound(SnapshotId),
MemoryInconsistencies(u64),
}
#[derive(Debug, Clone)]
pub enum SnapshotManagerCheckError {
SnapshotManagerError(SnapshotManagerError),
SnapshotCheckError(QemuSnapshotCheckResult),
}
#[derive(Debug, Clone, Copy)]
pub struct NopSnapshotManager;
impl Default for NopSnapshotManager {
fn default() -> Self {
NopSnapshotManager
}
}
impl IsSnapshotManager for NopSnapshotManager {
fn save(&mut self, _qemu: Qemu) -> SnapshotId {
SnapshotId { id: 0 }
}
fn restore(
&mut self,
_qemu: Qemu,
_snapshot_id: &SnapshotId,
) -> Result<(), SnapshotManagerError> {
Ok(())
}
fn do_check(
&self,
_qemu: Qemu,
_reference_snapshot_id: &SnapshotId,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
Ok(QemuSnapshotCheckResult::default())
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub struct SnapshotId {
id: 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,
}
}
}
impl QemuSnapshotCheckResult {
#[must_use]
pub fn new(nb_page_inconsistencies: u64) -> Self {
Self {
nb_page_inconsistencies,
}
}
}
impl SnapshotId {
pub fn gen_unique_id() -> SnapshotId {
static UNIQUE_ID: AtomicU64 = AtomicU64::new(0);
let unique_id = UNIQUE_ID.fetch_add(1, Ordering::SeqCst);
SnapshotId { id: unique_id }
}
#[must_use]
pub fn inner(&self) -> u64 {
self.id
}
}

View File

@ -1,37 +1,27 @@
use std::{
fmt::Debug,
sync::atomic::{AtomicU64, Ordering},
};
use std::fmt::Debug;
use hashbrown::HashMap;
use libafl::inputs::UsesInput;
use libafl::{
inputs::{HasTargetBytes, UsesInput},
prelude::{HasExecutions, State},
};
use libafl_qemu_sys::GuestPhysAddr;
use crate::{
emu::IsSnapshotManager, DeviceSnapshotFilter, Emulator, Qemu, QemuSnapshotCheckResult,
SnapshotId, SnapshotManagerError,
command::{CommandManager, StdCommandManager},
emu::{IsSnapshotManager, QemuSnapshotCheckResult},
DeviceSnapshotFilter, Emulator, EmulatorBuilder, Qemu, SnapshotId, SnapshotManagerError,
StdEmulatorDriver,
};
impl SnapshotId {
fn gen_unique_id() -> SnapshotId {
static UNIQUE_ID: AtomicU64 = AtomicU64::new(0);
let unique_id = UNIQUE_ID.fetch_add(1, Ordering::SeqCst);
SnapshotId { id: unique_id }
}
fn inner(&self) -> u64 {
self.id
}
}
#[derive(Debug, Clone)]
pub enum SnapshotManager {
Qemu(QemuSnapshotManager),
Fast(FastSnapshotManager),
}
pub type StdSnapshotManager = FastSnapshotManager;
impl IsSnapshotManager for SnapshotManager {
fn save(&mut self, qemu: Qemu) -> SnapshotId {
match self {
@ -42,23 +32,23 @@ impl IsSnapshotManager for SnapshotManager {
fn restore(
&mut self,
snapshot_id: &SnapshotId,
qemu: Qemu,
snapshot_id: &SnapshotId,
) -> Result<(), SnapshotManagerError> {
match self {
SnapshotManager::Qemu(qemu_sm) => qemu_sm.restore(snapshot_id, qemu),
SnapshotManager::Fast(fast_sm) => fast_sm.restore(snapshot_id, qemu),
SnapshotManager::Qemu(qemu_sm) => qemu_sm.restore(qemu, snapshot_id),
SnapshotManager::Fast(fast_sm) => fast_sm.restore(qemu, snapshot_id),
}
}
fn do_check(
&self,
reference_snapshot_id: &SnapshotId,
qemu: Qemu,
reference_snapshot_id: &SnapshotId,
) -> 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),
SnapshotManager::Qemu(qemu_sm) => qemu_sm.do_check(qemu, reference_snapshot_id),
SnapshotManager::Fast(fast_sm) => fast_sm.do_check(qemu, reference_snapshot_id),
}
}
}
@ -115,8 +105,8 @@ impl IsSnapshotManager for QemuSnapshotManager {
fn restore(
&mut self,
snapshot_id: &SnapshotId,
qemu: Qemu,
snapshot_id: &SnapshotId,
) -> Result<(), SnapshotManagerError> {
qemu.load_snapshot(self.snapshot_id_to_name(snapshot_id).as_str(), self.is_sync);
Ok(())
@ -124,8 +114,8 @@ impl IsSnapshotManager for QemuSnapshotManager {
fn do_check(
&self,
_reference_snapshot_id: &SnapshotId,
_qemu: Qemu,
_reference_snapshot_id: &SnapshotId,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
// We consider the qemu implementation to be 'ideal' for now.
Ok(QemuSnapshotCheckResult::default())
@ -142,8 +132,8 @@ impl IsSnapshotManager for FastSnapshotManager {
fn restore(
&mut self,
snapshot_id: &SnapshotId,
qemu: Qemu,
snapshot_id: &SnapshotId,
) -> Result<(), SnapshotManagerError> {
let fast_snapshot_ptr = *self
.snapshots
@ -159,8 +149,8 @@ impl IsSnapshotManager for FastSnapshotManager {
fn do_check(
&self,
reference_snapshot_id: &SnapshotId,
qemu: Qemu,
reference_snapshot_id: &SnapshotId,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
let fast_snapshot_ptr = *self.snapshots.get(reference_snapshot_id).ok_or(
SnapshotManagerError::SnapshotIdNotFound(*reference_snapshot_id),
@ -170,8 +160,9 @@ impl IsSnapshotManager for FastSnapshotManager {
}
}
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
/// Write a value to a phsical guest address, including ROM areas.

View File

@ -1,10 +1,13 @@
use libafl::inputs::UsesInput;
use libafl_qemu_sys::{GuestAddr, MmapPerms, VerifyAccess};
use crate::{Emulator, GuestMaps};
use crate::{command::CommandManager, Emulator, GuestMaps, NopSnapshotManager};
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
pub type StdSnapshotManager = NopSnapshotManager;
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
/// This function gets the memory mappings from the emulator.

View File

@ -42,16 +42,17 @@ use libc::siginfo_t;
#[cfg(emulation_mode = "usermode")]
use crate::EmulatorModules;
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorExitHandler};
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator};
pub struct QemuExecutor<'a, CM, EH, H, OT, ET, S>
pub struct QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
where
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
S: State,
OT: ObserversTuple<S>,
CM: CommandManager<ED, ET, S, SM>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: State,
{
inner: StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>>,
inner: StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, ED, ET, S, SM>>,
}
#[cfg(emulation_mode = "usermode")]
@ -93,12 +94,13 @@ pub unsafe fn inproc_qemu_timeout_handler<E, EM, OF, Z>(
}
}
impl<'a, CM, EH, H, OT, ET, S> Debug for QemuExecutor<'a, CM, EH, H, OT, ET, S>
impl<'a, CM, ED, ET, H, OT, S, SM> Debug for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
where
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
S: State,
OT: ObserversTuple<S> + Debug,
CM: CommandManager<ED, ET, S, SM>,
ET: EmulatorModuleTuple<S> + Debug,
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
OT: ObserversTuple<S> + Debug,
S: State,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuExecutor")
@ -107,15 +109,16 @@ where
}
}
impl<'a, CM, EH, H, OT, ET, S> QemuExecutor<'a, CM, EH, H, OT, ET, S>
impl<'a, CM, ED, ET, H, OT, S, SM> QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
where
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
S: State,
OT: ObserversTuple<S>,
CM: CommandManager<ED, ET, S, SM>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: State,
{
pub fn new<EM, OF, Z>(
emulator: Emulator<CM, EH, ET, S>,
emulator: Emulator<CM, ED, ET, S, SM>,
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
@ -156,7 +159,7 @@ where
#[cfg(emulation_mode = "systemmode")]
{
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::<
StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>>,
StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, ED, ET, S, SM>>,
EM,
OF,
Z,
@ -166,7 +169,7 @@ where
Ok(Self { inner })
}
pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>> {
pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, ED, ET, S, SM>> {
&self.inner
}
@ -179,16 +182,18 @@ where
pub fn inner_mut(
&mut self,
) -> &mut StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>> {
) -> &mut StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, ED, ET, S, SM>> {
&mut self.inner
}
}
impl<'a, CM, EH, EM, H, OT, ET, S, Z> Executor<EM, Z> for QemuExecutor<'a, CM, EH, H, OT, ET, S>
impl<'a, CM, ED, EM, ET, H, OT, S, SM, Z> Executor<EM, Z>
for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
EM: UsesState<State = S>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: State + HasExecutions + Unpin,
Z: UsesState<State = S>,
@ -216,32 +221,35 @@ where
}
}
impl<'a, CM, EH, H, OT, ET, S> UsesState for QemuExecutor<'a, CM, EH, H, OT, ET, S>
impl<'a, CM, ED, ET, H, OT, S, SM> UsesState for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
where
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
S: State,
OT: ObserversTuple<S>,
CM: CommandManager<ED, ET, S, SM>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: State,
{
type State = S;
}
impl<'a, CM, EH, H, OT, ET, S> UsesObservers for QemuExecutor<'a, CM, EH, H, OT, ET, S>
impl<'a, CM, ED, ET, H, OT, S, SM> UsesObservers for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
where
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
S: State,
OT: ObserversTuple<S>,
CM: CommandManager<ED, ET, S, SM>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: State,
{
type Observers = OT;
}
impl<'a, CM, EH, H, OT, ET, S> HasObservers for QemuExecutor<'a, CM, EH, H, OT, ET, S>
impl<'a, CM, ED, ET, H, OT, S, SM> HasObservers for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
where
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
S: State,
OT: ObserversTuple<S>,
CM: CommandManager<ED, ET, S, SM>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: State,
{
#[inline]
fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> {
@ -255,8 +263,9 @@ where
}
#[cfg(feature = "fork")]
pub struct QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
pub struct QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
where
CM: CommandManager<ED, ET, S, SM>,
EM: UsesState<State = S>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
@ -266,20 +275,21 @@ where
Z: UsesState<State = S>,
{
inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>,
emulator: Emulator<CM, EH, ET, S>,
emulator: Emulator<CM, ED, ET, S, SM>,
}
#[cfg(feature = "fork")]
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> Debug
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> Debug
for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
where
CM: Debug,
CM: CommandManager<ED, ET, S, SM> + Debug,
EM: UsesState<State = S>,
EH: Debug,
ED: Debug,
ET: EmulatorModuleTuple<S> + Debug,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S> + Debug,
S: UsesInput + Debug,
SM: Debug,
SP: ShMemProvider,
Z: UsesState<State = S>,
{
@ -292,8 +302,10 @@ where
}
#[cfg(feature = "fork")]
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
where
CM: CommandManager<ED, ET, S, SM>,
EM: EventFirer<State = S> + EventRestarter<State = S>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
@ -303,7 +315,7 @@ where
Z: HasObjective<State = S>,
{
pub fn new(
emulator: Emulator<CM, EH, ET, S>,
emulator: Emulator<CM, ED, ET, S, SM>,
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
@ -336,21 +348,20 @@ where
&mut self.inner
}
pub fn emulator(&self) -> &Emulator<CM, EH, ET, S> {
pub fn emulator(&self) -> &Emulator<CM, ED, ET, S, SM> {
&self.emulator
}
pub fn emulator_mut(&mut self) -> &Emulator<CM, EH, ET, S> {
pub fn emulator_mut(&mut self) -> &Emulator<CM, ED, ET, S, SM> {
&mut self.emulator
}
}
#[cfg(feature = "fork")]
impl<'a, CM, EH, EM, H, OT, ET, S, Z, SP, OF> Executor<EM, Z>
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
impl<'a, CM, ED, EM, ET, H, OF, OT, S, SM, SP, Z> Executor<EM, Z>
for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
CM: CommandManager<ED, ET, S, SM>,
EM: EventManager<InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>, Z, State = S>,
H: FnMut(&S::Input) -> ExitKind,
S: Unpin + State + HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + HasSolutions,
@ -372,9 +383,10 @@ where
}
#[cfg(feature = "fork")]
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> UsesObservers
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> UsesObservers
for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
where
CM: CommandManager<ED, ET, S, SM>,
EM: UsesState<State = S>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
@ -387,9 +399,10 @@ where
}
#[cfg(feature = "fork")]
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> UsesState
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> UsesState
for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
where
CM: CommandManager<ED, ET, S, SM>,
EM: UsesState<State = S>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
@ -402,9 +415,10 @@ where
}
#[cfg(feature = "fork")]
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> HasObservers
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> HasObservers
for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
where
CM: CommandManager<ED, ET, S, SM>,
EM: UsesState<State = S>,
ET: EmulatorModuleTuple<S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,

View File

@ -1,3 +1,5 @@
use std::fmt::Debug;
use libafl_qemu_sys::GuestPhysAddr;
use crate::modules::{
@ -6,7 +8,8 @@ use crate::modules::{
};
pub trait StdInstrumentationFilter:
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
Debug
+ HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
+ HasInstrumentationFilter<QemuInstrumentationPagingFilter>
{
}
@ -14,6 +17,7 @@ pub trait StdInstrumentationFilter:
impl<Head> crate::modules::StdInstrumentationFilter for (Head, ()) where
Head: HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
+ HasInstrumentationFilter<QemuInstrumentationPagingFilter>
+ Debug
{
}

View File

@ -685,6 +685,7 @@ pub fn init_qemu_with_asan(
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" {
@ -717,7 +718,7 @@ pub fn init_qemu_with_asan(
ASAN_INITED = true;
}
let qemu = Qemu::init(args, env)?;
let qemu = Qemu::init(args)?;
let rt = AsanGiovese::new(qemu.hooks());
Ok((qemu, rt))

View File

@ -94,7 +94,7 @@ pub fn init_qemu_with_asan_guest(
ASAN_GUEST_INITED = true;
}
let emu = Qemu::init(args, env)?;
let emu = Qemu::init(args)?;
Ok((emu, asan_lib))
}

View File

@ -1,5 +1,8 @@
#[cfg(not(cpu_target = "hexagon"))]
pub mod drcov;
use std::fmt::Debug;
#[cfg(not(cpu_target = "hexagon"))]
pub use drcov::DrCovModule;
@ -28,12 +31,12 @@ pub use asan_guest::{init_qemu_with_asan_guest, AsanGuestModule};
use crate::modules::{HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter};
pub trait StdInstrumentationFilter:
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> + Debug
{
}
impl<Head> StdInstrumentationFilter for (Head, ()) where
Head: HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
Head: HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> + Debug
{
}

View File

@ -365,9 +365,9 @@ impl From<QemuConfig> for Result<Qemu, QemuInitError> {
let args = config
.to_string()
.split(' ')
.map(std::string::ToString::to_string)
.map(ToString::to_string)
.collect::<Vec<String>>();
let qemu = Qemu::init(&args, &[])?;
let qemu = Qemu::init(&args)?;
QEMU_CONFIG
.set(config)
.map_err(|_| unreachable!("BUG: QEMU_CONFIG was already set but Qemu was not init!"))?;

View File

@ -497,7 +497,6 @@ create_wrapper!(
);
// New thread hook wrappers
#[cfg(emulation_mode = "usermode")]
create_hook_types!(
NewThread,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, env: CPUArchStatePtr, tid: u32) -> bool,
@ -511,9 +510,7 @@ create_hook_types!(
>,
extern "C" fn(*const (), env: CPUArchStatePtr, tid: u32) -> bool
);
#[cfg(emulation_mode = "usermode")]
create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false);
#[cfg(emulation_mode = "usermode")]
create_wrapper!(new_thread, (env: CPUArchStatePtr, tid: u32), bool);
// CPU Run hook wrappers
@ -943,6 +940,19 @@ impl QemuHooks {
BackdoorHookId(num)
}
}
pub fn add_new_thread_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(T, env: CPUArchStatePtr, tid: u32) -> bool,
) -> NewThreadHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, CPUArchStatePtr, u32) -> bool = transmute(callback);
let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data);
NewThreadHookId(num)
}
}
}
#[cfg(emulation_mode = "usermode")]
@ -983,19 +993,6 @@ impl QemuHooks {
}
}
pub fn add_new_thread_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(T, env: CPUArchStatePtr, tid: u32) -> bool,
) -> NewThreadHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, CPUArchStatePtr, u32) -> bool = transmute(callback);
let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data);
NewThreadHookId(num)
}
}
#[allow(clippy::type_complexity)]
pub fn add_post_syscall_hook<T: Into<HookData>>(
&self,

View File

@ -16,14 +16,12 @@ use std::{
};
use libafl_bolts::os::unix_signals::Signal;
#[cfg(emulation_mode = "systemmode")]
use libafl_qemu_sys::qemu_init;
#[cfg(emulation_mode = "usermode")]
use libafl_qemu_sys::{guest_base, qemu_user_init, VerifyAccess};
use libafl_qemu_sys::{guest_base, VerifyAccess};
use libafl_qemu_sys::{
libafl_flush_jit, libafl_get_exit_reason, libafl_page_from_addr, libafl_qemu_add_gdb_cmd,
libafl_qemu_cpu_index, libafl_qemu_current_cpu, libafl_qemu_gdb_reply, libafl_qemu_get_cpu,
libafl_qemu_num_cpus, libafl_qemu_num_regs, libafl_qemu_read_reg,
libafl_qemu_init, libafl_qemu_num_cpus, libafl_qemu_num_regs, libafl_qemu_read_reg,
libafl_qemu_remove_breakpoint, libafl_qemu_set_breakpoint, libafl_qemu_trigger_breakpoint,
libafl_qemu_write_reg, CPUArchState, CPUStatePtr, FatPtr, GuestAddr, GuestPhysAddr, GuestUsize,
GuestVirtAddr,
@ -33,8 +31,8 @@ use strum::IntoEnumIterator;
use crate::{GuestAddrKind, GuestReg, Regs};
pub mod qemu_config;
use qemu_config::{QemuConfig, QemuConfigBuilder, QEMU_CONFIG};
pub mod config;
use config::{QemuConfig, QemuConfigBuilder, QEMU_CONFIG};
#[cfg(emulation_mode = "usermode")]
mod usermode;
@ -72,11 +70,6 @@ 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,
}
#[derive(Debug, Clone)]
pub enum QemuRWErrorKind {
Read,
@ -132,15 +125,6 @@ impl QemuRWError {
}
}
/// 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.
@ -529,7 +513,7 @@ impl Qemu {
}
#[allow(clippy::must_use_candidate, clippy::similar_names)]
pub fn init(args: &[String], env: &[(String, String)]) -> Result<Self, QemuInitError> {
pub fn init(args: &[String]) -> Result<Self, QemuInitError> {
if args.is_empty() {
return Err(QemuInitError::EmptyArgs);
}
@ -555,21 +539,15 @@ impl Qemu {
.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.
let env_strs: Vec<String> = env
.iter()
.map(|(k, v)| format!("{}={}\0", &k, &v))
.collect();
let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect();
envp.push(ptr::null());
unsafe {
#[cfg(emulation_mode = "usermode")]
qemu_user_init(argc, argv.as_ptr(), envp.as_ptr());
#[cfg(emulation_mode = "systemmode")]
{
qemu_init(argc, argv.as_ptr());
libc::atexit(qemu_cleanup_atexit);
libafl_qemu_sys::syx_snapshot_init(true);
}
libafl_qemu_init(argc, argv.as_ptr() as *mut *mut i8);
}
#[cfg(emulation_mode = "systemmode")]
unsafe {
libc::atexit(qemu_cleanup_atexit);
libafl_qemu_sys::syx_snapshot_init(true);
}
Ok(Qemu { _private: () })
@ -993,9 +971,9 @@ pub mod pybind {
impl Qemu {
#[allow(clippy::needless_pass_by_value)]
#[new]
fn new(args: Vec<String>, env: Vec<(String, String)>) -> PyResult<Qemu> {
let qemu = super::Qemu::init(&args, &env)
.map_err(|e| PyValueError::new_err(format!("{e}")))?;
fn new(args: Vec<String>) -> PyResult<Qemu> {
let qemu =
super::Qemu::init(&args).map_err(|e| PyValueError::new_err(format!("{e}")))?;
Ok(Qemu { qemu })
}

View File

@ -12,6 +12,7 @@ use libafl_qemu_sys::{
libafl_save_qemu_snapshot, qemu_cleanup, qemu_main_loop, vm_start, GuestAddr, GuestPhysAddr,
GuestUsize, GuestVirtAddr,
};
use libc::EXIT_SUCCESS;
use num_traits::Zero;
use crate::{
@ -21,7 +22,7 @@ use crate::{
pub(super) extern "C" fn qemu_cleanup_atexit() {
unsafe {
qemu_cleanup();
qemu_cleanup(EXIT_SUCCESS);
}
}
@ -254,9 +255,7 @@ impl Qemu {
) -> QemuSnapshotCheckResult {
let check_result = libafl_qemu_sys::syx_snapshot_check(ref_snapshot);
QemuSnapshotCheckResult {
nb_page_inconsistencies: check_result.nb_inconsistencies,
}
QemuSnapshotCheckResult::new(check_result.nb_inconsistencies)
}
pub fn list_devices(&self) -> Vec<String> {

View File

@ -1,12 +1,9 @@
use std::{
fmt::{Display, Formatter},
rc::Rc,
};
use std::fmt::{Debug, Formatter};
use enum_map::Enum;
use libafl::inputs::UsesInput;
use crate::{command::IsCommand, get_exit_arch_regs, GuestReg, Regs, CPU};
use crate::{command::CommandManager, get_exit_arch_regs, GuestReg, Regs, CPU};
#[derive(Debug, Clone, Enum)]
pub enum ExitArgs {
@ -20,26 +17,49 @@ pub enum ExitArgs {
Arg6,
}
#[derive(Debug)]
pub struct SyncExit<CM, EH, ET, S>
pub struct SyncExit<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
command: Rc<dyn IsCommand<CM, EH, ET, S>>,
command: CM::Commands,
}
impl<CM, EH, ET, S> SyncExit<CM, EH, ET, S>
impl<CM, ED, ET, S, SM> Clone for SyncExit<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn clone(&self) -> Self {
Self {
command: self.command.clone(),
}
}
}
impl<CM, ED, ET, S, SM> Debug for SyncExit<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Sync Exit")
}
}
impl<CM, ED, ET, S, SM> SyncExit<CM, ED, ET, S, SM>
where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput,
{
#[must_use]
pub fn new(command: Rc<dyn IsCommand<CM, EH, ET, S>>) -> Self {
pub fn new(command: CM::Commands) -> Self {
Self { command }
}
#[must_use]
pub fn command(&self) -> Rc<dyn IsCommand<CM, EH, ET, S>> {
self.command.clone()
pub fn command(&self) -> &CM::Commands {
&self.command
}
pub fn ret(&self, cpu: &CPU, value: GuestReg) {
@ -52,12 +72,3 @@ where
get_exit_arch_regs()[ExitArgs::Ret]
}
}
impl<CM, EH, ET, S> Display for SyncExit<CM, EH, ET, S>
where
S: UsesInput,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.command)
}
}

View File

@ -39,9 +39,8 @@ use libafl_bolts::{
use libafl_qemu::modules::CmpLogModule;
pub use libafl_qemu::qemu::Qemu;
use libafl_qemu::{
command::NopCommandManager,
modules::edges::{self, EdgeCoverageModule},
Emulator, NopEmulatorExitHandler, QemuExecutor,
Emulator, QemuExecutor,
};
use libafl_targets::{edges_map_mut_ptr, CmpLogObserver};
use typed_builder::TypedBuilder;
@ -230,19 +229,14 @@ where
}
};
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
harness_bytes(buf);
ExitKind::Ok
};
let emulator = Emulator::new_with_qemu(
qemu,
modules,
NopEmulatorExitHandler,
NopCommandManager,
)?;
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
let executor = QemuExecutor::new(
emulator,
@ -345,21 +339,16 @@ where
}
}
} else {
let tools = tuple_list!(EdgeCoverageModule::default());
let modules = tuple_list!(EdgeCoverageModule::default());
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
harness_bytes(buf);
ExitKind::Ok
};
let emulator = Emulator::new_with_qemu(
qemu,
tools,
NopEmulatorExitHandler,
NopCommandManager,
)?;
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
let mut executor = QemuExecutor::new(
emulator,