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

View File

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

View File

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

View File

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

View File

@ -129,7 +129,7 @@ impl<'a> Client<'a> {
let (emu, asan_lib) = init_qemu_with_asan_guest(&mut args, &mut env)?; let (emu, asan_lib) = init_qemu_with_asan_guest(&mut args, &mut env)?;
(emu, None, Some(asan_lib)) (emu, None, Some(asan_lib))
} else { } 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}, tuples::{tuple_list, Merge},
}; };
use libafl_qemu::{ use libafl_qemu::{
command::NopCommandManager,
modules::{ modules::{
cmplog::CmpLogObserver, cmplog::CmpLogObserver,
edges::{edges_map_mut_ptr, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND}, edges::{edges_map_mut_ptr, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
EmulatorModuleTuple, EmulatorModuleTuple,
}, },
Emulator, NopEmulatorExitHandler, Qemu, QemuExecutor, Emulator, Qemu, QemuExecutor,
}; };
use typed_builder::TypedBuilder; use typed_builder::TypedBuilder;
@ -150,18 +149,15 @@ impl<'a, M: Monitor> Instance<'a, M> {
let harness = Harness::new(self.qemu)?; let harness = Harness::new(self.qemu)?;
let mut harness = 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 // A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let emulator = Emulator::new_with_qemu( let emulator = Emulator::empty()
*self.qemu, .qemu(*self.qemu)
modules, .modules(modules)
NopEmulatorExitHandler, .build()?;
NopCommandManager,
)
.unwrap();
if self.options.is_cmplog_core(self.core_id) { if self.options.is_cmplog_core(self.core_id) {
// Create a QEMU in-process executor // Create a QEMU in-process executor

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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/runstate.h"
#include "sysemu/replay.h" #include "sysemu/replay.h"
#include "libafl/system.h"
#include "libafl/qemu_snapshot.h" #include "libafl/qemu_snapshot.h"
#include "libafl/syx-snapshot/device-save.h" #include "libafl/syx-snapshot/device-save.h"
#include "libafl/syx-snapshot/syx-snapshot.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/instruction.h"
#include "libafl/hooks/tcg/read_write.h" #include "libafl/hooks/tcg/read_write.h"
#include "libafl/hooks/cpu_run.h" #include "libafl/hooks/cpu_run.h"
#include "libafl/hooks/thread.h"
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
#include "libafl/hooks/thread.h"
#include "libafl/hooks/syscall.h" #include "libafl/hooks/syscall.h"
#endif #endif
@ -151,7 +152,6 @@ pub fn generate(
.allowlist_type("Syx.*") .allowlist_type("Syx.*")
.allowlist_type("libafl_mapinfo") .allowlist_type("libafl_mapinfo")
.allowlist_type("IntervalTreeRoot") .allowlist_type("IntervalTreeRoot")
.allowlist_function("qemu_user_init")
.allowlist_function("qemu_system_debug_request") .allowlist_function("qemu_system_debug_request")
.allowlist_function("target_mmap") .allowlist_function("target_mmap")
.allowlist_function("target_mprotect") .allowlist_function("target_mprotect")
@ -172,6 +172,9 @@ pub fn generate(
.allowlist_function("read_self_maps") .allowlist_function("read_self_maps")
.allowlist_function("free_self_maps") .allowlist_function("free_self_maps")
.allowlist_function("pageflags_get_root") .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 .blocklist_function("main_loop_wait") // bindgen issue #1313
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); .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_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
pub const QEMU_DIRNAME: &str = "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)] #[allow(clippy::module_name_repetitions)]
pub struct BuildResult { pub struct BuildResult {
@ -144,7 +144,6 @@ fn configure_qemu(
.arg("--disable-iconv") .arg("--disable-iconv")
.arg("--disable-jack") .arg("--disable-jack")
.arg("--disable-keyring") .arg("--disable-keyring")
.arg("--disable-kvm")
.arg("--disable-libdaxctl") .arg("--disable-libdaxctl")
.arg("--disable-libiscsi") .arg("--disable-libiscsi")
.arg("--disable-libnfs") .arg("--disable-libnfs")

View File

@ -51,10 +51,10 @@ mod usermode;
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
pub use usermode::*; pub use usermode::*;
#[cfg(emulation_mode = "systemmode")] // #[cfg(emulation_mode = "systemmode")]
mod systemmode; // mod systemmode;
#[cfg(emulation_mode = "systemmode")] // #[cfg(emulation_mode = "systemmode")]
pub use systemmode::*; // pub use systemmode::*;
/// Safe linking with of extern "C" functions. /// Safe linking with of extern "C" functions.
/// This macro makes sure the declared symbol is defined *at link time*, avoiding declaring non-existant symbols /// 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}; use crate::{extern_c_checked, libafl_mapinfo, GuestAddr, MmapPerms};
extern_c_checked! { 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 exec_path: *const u8;
pub static guest_base: usize; pub static guest_base: usize;
pub static mut mmap_next_start: GuestAddr; pub static mut mmap_next_start: GuestAddr;

View File

@ -1,5 +1,5 @@
/* 1.82.0-nightly */ /* 1.82.0-nightly */
/* qemu git hash: 31ee26f97071d5bed1ac1e7de75beea755b198d6 */ /* qemu git hash: ec91486cedfd9e636be0063d88bd6e7eaff74915 */
/* automatically generated by rust-bindgen 0.69.4 */ /* automatically generated by rust-bindgen 0.69.4 */
#[repr(C)] #[repr(C)]
@ -13761,6 +13761,9 @@ extern "C" {
extern "C" { extern "C" {
pub fn libafl_set_brk(new_brk: u64) -> u64; 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)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct AccelCPUClass { pub struct AccelCPUClass {
@ -15194,7 +15197,7 @@ extern "C" {
pub fn libafl_qemu_remove_new_thread_hook(num: usize) -> ::std::os::raw::c_int; pub fn libafl_qemu_remove_new_thread_hook(num: usize) -> ::std::os::raw::c_int;
} }
extern "C" { 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)] #[repr(C)]
#[derive(Debug, Default, Copy, Clone)] #[derive(Debug, Default, Copy, Clone)]

View File

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

View File

@ -1,10 +1,10 @@
use std::{ use std::{
fmt::{Debug, Display, Error, Formatter}, fmt,
rc::Rc, fmt::{Debug, Display, Formatter},
marker::PhantomData,
}; };
use enum_map::{Enum, EnumMap}; use enum_map::{Enum, EnumMap};
use hashbrown::HashMap;
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
use hashbrown::HashSet; use hashbrown::HashSet;
use libafl::{ use libafl::{
@ -12,7 +12,9 @@ use libafl::{
inputs::{HasTargetBytes, UsesInput}, inputs::{HasTargetBytes, UsesInput},
}; };
use libafl_bolts::AsSlice; use libafl_bolts::AsSlice;
use libc::c_uint;
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use paste::paste;
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
use crate::modules::QemuInstrumentationPagingFilter; use crate::modules::QemuInstrumentationPagingFilter;
@ -24,11 +26,12 @@ use crate::{
}, },
get_exit_arch_regs, get_exit_arch_regs,
modules::{ modules::{
HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter, EmulatorModuleTuple, HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter,
StdInstrumentationFilter,
}, },
sync_exit::ExitArgs, sync_exit::ExitArgs,
Emulator, ExitHandlerError, ExitHandlerResult, GuestReg, InputLocation, IsSnapshotManager, Emulator, EmulatorDriverError, EmulatorDriverResult, GuestReg, InputLocation,
Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorExitHandler, CPU, IsSnapshotManager, Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorDriver, CPU,
}; };
pub mod parser; pub mod parser;
@ -50,111 +53,131 @@ mod bindings {
pub const VERSION: u64 = bindings::LIBAFL_QEMU_HDR_VERSION_NUMBER as u64; pub const VERSION: u64 = bindings::LIBAFL_QEMU_HDR_VERSION_NUMBER as u64;
macro_rules! define_std_command_manager { macro_rules! define_std_command_manager {
($name:ident, [$($native_command_parser:ident),+]) => { ($name:ident, [$($command:ty),+], [$($native_command_parser:ty),+]) => {
pub struct $name<ET, S, SM> paste! {
where pub struct $name<S> {
S: UsesInput, phantom: PhantomData<S>,
{
native_command_parsers:
HashMap<GuestReg, Box<dyn NativeCommandParser<Self, StdEmulatorExitHandler<SM>, ET, 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");
} }
impl<S> Clone for $name<S> {
fn clone(&self) -> Self {
Self { Self {
native_command_parsers: parsers, phantom: PhantomData
} }
} }
} }
impl<ET, S, SM> CommandManager<StdEmulatorExitHandler<SM>, ET, S> for $name<ET, S, SM> impl<S> Debug for $name<S> {
where fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
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<ET, S, SM> Debug for $name<ET, S, SM>
where
S: UsesInput,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
write!(f, stringify!($name)) write!(f, stringify!($name))
} }
} }
impl<ET, S, SM> Default for $name<ET, S, SM> impl<S> Default for $name<S> {
fn default() -> Self {
Self {
phantom: PhantomData
}
}
}
impl<ET, S, SM> CommandManager<StdEmulatorDriver, ET, S, SM> for $name<S>
where where
ET: StdInstrumentationFilter + Unpin, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
S: UsesInput + Unpin, S: UsesInput + Unpin,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
fn default() -> Self { type Commands = [<$name Commands>];
Self::new()
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; pub trait CommandManager<ED, ET, S, SM>: Sized + Debug
impl<EH, ET, S> CommandManager<EH, ET, S> for NopCommandManager
where where
S: UsesInput, S: UsesInput,
{ {
fn parse(&self, _qemu: Qemu) -> Result<Rc<dyn IsCommand<Self, EH, ET, S>>, CommandError> { type Commands: IsCommand<Self, ED, ET, S, SM>;
Ok(Rc::new(NopCommand))
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!( define_std_command_manager!(
StdCommandManager, StdCommandManager,
[
StartCommand,
InputCommand,
SaveCommand,
LoadCommand,
EndCommand,
VersionCommand,
AddressRangeFilterCommand
],
[ [
StartPhysCommandParser, StartPhysCommandParser,
StartVirtCommandParser, 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)] #[derive(Debug, Clone, Enum, TryFromPrimitive)]
#[repr(u64)] #[repr(u64)]
pub enum NativeExitKind { 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 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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
/// Used to know whether the command can be run during a backdoor, or if it is necessary to go out of /// 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. /// - `ret_reg`: The register in which the guest return value should be written, if any.
/// Returns /// Returns
/// - `InnerHandlerResult`: How the high-level handler should behave /// - `InnerHandlerResult`: How the high-level handler should behave
#[allow(clippy::type_complexity)]
fn run( fn run(
&self, &self,
emu: &mut Emulator<CM, EH, ET, S>, emu: &mut Emulator<CM, ED, ET, S, SM>,
driver: &mut ED,
input: &S::Input, input: &S::Input,
ret_reg: Option<Regs>, 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")] #[cfg(emulation_mode = "systemmode")]
@ -226,13 +245,14 @@ impl From<QemuRWError> for CommandError {
pub struct NopCommand; pub struct NopCommand;
impl Display for 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") 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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
fn usable_at_runtime(&self) -> bool { fn usable_at_runtime(&self) -> bool {
@ -241,20 +261,21 @@ where
fn run( fn run(
&self, &self,
_emu: &mut Emulator<CM, EH, ET, S>, _emu: &mut Emulator<CM, ED, ET, S, SM>,
_driver: &mut ED,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, EH, ET, S>>, ExitHandlerError> { ) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
Ok(None) Ok(None)
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SaveCommand; pub struct SaveCommand;
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorDriver, ET, S, SM> for SaveCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for SaveCommand
where where
ET: StdInstrumentationFilter + Unpin, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput + Unpin, S: UsesInput + Unpin,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -264,21 +285,18 @@ where
fn run( fn run(
&self, &self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>, emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
driver: &mut StdEmulatorDriver,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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 qemu = emu.qemu();
let snapshot_id = emu.snapshot_manager_mut().save(qemu);
{ driver
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) .set_snapshot_id(snapshot_id)
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?; .map_err(|_| EmulatorDriverError::MultipleSnapshotDefinition)?;
}
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
{ {
@ -304,8 +322,9 @@ where
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LoadCommand; 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 where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput, S: UsesInput,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -315,26 +334,22 @@ where
fn run( fn run(
&self, &self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>, emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
driver: &mut StdEmulatorDriver,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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 qemu = emu.qemu();
let emu_exit_handler = emu.exit_handler().borrow_mut();
let snapshot_id = emu_exit_handler let snapshot_id = driver
.snapshot_id() .snapshot_id()
.ok_or(ExitHandlerError::SnapshotNotFound)?; .ok_or(EmulatorDriverError::SnapshotNotFound)?;
emu_exit_handler emu.snapshot_manager_mut().restore(qemu, &snapshot_id)?;
.snapshot_manager_borrow_mut()
.restore(&snapshot_id, qemu)?;
#[cfg(feature = "paranoid_debug")] #[cfg(feature = "paranoid_debug")]
emu_exit_handler emu.snapshot_manager_mut().check(qemu, &snapshot_id)?;
.snapshot_manager_borrow()
.check(&snapshot_id, emu.qemu())?;
Ok(None) Ok(None)
} }
@ -346,8 +361,9 @@ pub struct InputCommand {
cpu: CPU, 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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
{ {
@ -357,11 +373,11 @@ where
fn run( fn run(
&self, &self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>, emu: &mut Emulator<CM, ED, ET, S, SM>,
_driver: &mut ED,
input: &S::Input, input: &S::Input,
ret_reg: Option<Regs>, 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 qemu = emu.qemu();
let ret_value = self.location.write(qemu, input.target_bytes().as_slice()); let ret_value = self.location.write(qemu, input.target_bytes().as_slice());
@ -379,8 +395,9 @@ pub struct StartCommand {
input_location: QemuMemoryChunk, 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 where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput, S: UsesInput,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
@ -391,20 +408,20 @@ where
fn run( fn run(
&self, &self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>, emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
driver: &mut StdEmulatorDriver,
input: &S::Input, input: &S::Input,
ret_reg: Option<Regs>, 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 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) .set_snapshot_id(snapshot_id)
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?; .map_err(|_| EmulatorDriverError::MultipleSnapshotDefinition)?;
emu_exit_handler driver
.set_input_location(InputLocation::new( .set_input_location(InputLocation::new(
self.input_location.clone(), self.input_location.clone(),
qemu.current_cpu().unwrap(), qemu.current_cpu().unwrap(),
@ -425,10 +442,13 @@ where
} }
#[derive(Debug, Clone)] #[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 where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput, S: UsesInput,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -438,35 +458,35 @@ where
fn run( fn run(
&self, &self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>, emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
driver: &mut StdEmulatorDriver,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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() .snapshot_id()
.ok_or(ExitHandlerError::SnapshotNotFound)?; .ok_or(EmulatorDriverError::SnapshotNotFound)?;
emu_exit_handler emu.snapshot_manager_mut().restore(qemu, &snapshot_id)?;
.snapshot_manager_borrow_mut()
.restore(&snapshot_id, emu.qemu())?;
#[cfg(feature = "paranoid_debug")] #[cfg(feature = "paranoid_debug")]
emu_exit_handler emu.snapshot_manager_mut().check(qemu, &snapshot_id)?;
.snapshot_manager_borrow()
.check(&snapshot_id, emu.qemu())?;
Ok(Some(ExitHandlerResult::EndOfRun(self.0.unwrap()))) Ok(Some(EmulatorDriverResult::EndOfRun(
self.exit_kind.unwrap(),
)))
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct VersionCommand(u64); 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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
fn usable_at_runtime(&self) -> bool { fn usable_at_runtime(&self) -> bool {
@ -475,17 +495,17 @@ where
fn run( fn run(
&self, &self,
_emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>, _emu: &mut Emulator<CM, ED, ET, S, SM>,
_driver: &mut ED,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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; let guest_version = self.0;
if VERSION == guest_version { if VERSION == guest_version {
Ok(None) Ok(None)
} else { } else {
Err(ExitHandlerError::CommandError( Err(EmulatorDriverError::CommandError(
CommandError::VersionDifference(guest_version), CommandError::VersionDifference(guest_version),
)) ))
} }
@ -498,9 +518,10 @@ pub struct FilterCommand<T> {
} }
#[cfg(emulation_mode = "systemmode")] #[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 where
ET: StdInstrumentationFilter + Unpin, ET: StdInstrumentationFilter + Unpin,
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput + Unpin, S: UsesInput + Unpin,
{ {
fn usable_at_runtime(&self) -> bool { fn usable_at_runtime(&self) -> bool {
@ -509,11 +530,11 @@ where
fn run( fn run(
&self, &self,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>, emu: &mut Emulator<CM, ED, ET, S, SM>,
_driver: &mut ED,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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 qemu_modules = emu.modules_mut().modules_mut();
let paging_filter = 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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
fn usable_at_runtime(&self) -> bool { fn usable_at_runtime(&self) -> bool {
true true
} }
#[allow(clippy::type_complexity)] // TODO: refactor with correct type.
fn run( fn run(
&self, &self,
_emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>, _emu: &mut Emulator<CM, ED, ET, S, SM>,
_driver: &mut ED,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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 qemu_modules = &mut ();
let addr_range_filter = let addr_range_filter =
@ -597,7 +618,7 @@ impl Display for StartCommand {
impl Display for EndCommand { impl Display for EndCommand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 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 { impl EndCommand {
#[must_use] #[must_use]
pub fn new(exit_kind: Option<ExitKind>) -> Self { 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 enum_map::{enum_map, EnumMap};
use libafl::{ use libafl::{
@ -6,198 +6,202 @@ use libafl::{
inputs::{HasTargetBytes, UsesInput}, inputs::{HasTargetBytes, UsesInput},
}; };
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr}; use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr};
use libc::c_uint;
use crate::{ use crate::{
command::{ command::{
bindings, CommandError, EndCommand, FilterCommand, InputCommand, IsCommand, LoadCommand, bindings, CommandError, CommandManager, EndCommand, FilterCommand, InputCommand, IsCommand,
NativeExitKind, SaveCommand, StartCommand, VersionCommand, LoadCommand, NativeExitKind, SaveCommand, StartCommand, VersionCommand,
},
modules::{
EmulatorModuleTuple, QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter,
}, },
modules::{QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter},
sync_exit::ExitArgs, 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 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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
fn command_id(&self) -> GuestReg; type OutputCommand: IsCommand<CM, ED, ET, S, SM>;
const COMMAND_ID: c_uint;
fn parse( fn parse(
&self,
qemu: Qemu, qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>, arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, EH, ET, S>>, CommandError>; ) -> Result<Self::OutputCommand, CommandError>;
} }
pub struct InputPhysCommandParser; pub struct InputPhysCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for InputPhysCommandParser
for InputPhysCommandParser
where where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
{ {
fn command_id(&self) -> GuestReg { type OutputCommand = InputCommand;
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_PHYS.0)
} const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_PHYS.0;
fn parse( fn parse(
&self,
qemu: Qemu, qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>, 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 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])?; let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(Rc::new(InputCommand::new( Ok(InputCommand::new(
QemuMemoryChunk::phys( QemuMemoryChunk::phys(
input_phys_addr, input_phys_addr,
max_input_size, max_input_size,
Some(qemu.current_cpu().unwrap()), Some(qemu.current_cpu().unwrap()),
), ),
qemu.current_cpu().unwrap(), qemu.current_cpu().unwrap(),
))) ))
} }
} }
pub struct InputVirtCommandParser; pub struct InputVirtCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for InputVirtCommandParser
for InputVirtCommandParser
where where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
{ {
fn command_id(&self) -> GuestReg { type OutputCommand = InputCommand;
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_VIRT.0)
} const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_VIRT.0;
fn parse( fn parse(
&self,
qemu: Qemu, qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>, 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 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])?; 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()), QemuMemoryChunk::virt(input_virt_addr, max_input_size, qemu.current_cpu().unwrap()),
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; 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 where
ET: StdInstrumentationFilter + Unpin, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput + Unpin, S: UsesInput + Unpin,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
fn command_id(&self) -> GuestReg { type OutputCommand = SaveCommand;
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_SAVE.0)
} const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_SAVE.0;
fn parse( fn parse(
&self,
_qemu: Qemu, _qemu: Qemu,
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>, _arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> { ) -> Result<Self::OutputCommand, CommandError> {
Ok(Rc::new(SaveCommand)) Ok(SaveCommand)
} }
} }
pub struct LoadCommandParser; 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 where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput, S: UsesInput,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
fn command_id(&self) -> GuestReg { type OutputCommand = LoadCommand;
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_LOAD.0)
} const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_LOAD.0;
fn parse( fn parse(
&self,
_qemu: Qemu, _qemu: Qemu,
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>, _arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> { ) -> Result<Self::OutputCommand, CommandError> {
Ok(Rc::new(LoadCommand)) Ok(LoadCommand)
} }
} }
pub struct EndCommandParser; 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 where
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
S: UsesInput, S: UsesInput,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
fn command_id(&self) -> GuestReg { type OutputCommand = EndCommand;
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_END.0)
} const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_END.0;
fn parse( fn parse(
&self,
qemu: Qemu, qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>, 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: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let native_exit_kind: Result<NativeExitKind, _> = u64::from(native_exit_kind).try_into(); let native_exit_kind: Result<NativeExitKind, _> = u64::from(native_exit_kind).try_into();
@ -211,52 +215,51 @@ where
})[k] })[k]
}); });
Ok(Rc::new(EndCommand::new(exit_kind))) Ok(EndCommand::new(exit_kind))
} }
} }
pub struct VersionCommandParser; pub struct VersionCommandParser;
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for VersionCommandParser
for VersionCommandParser
where where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
fn command_id(&self) -> GuestReg { type OutputCommand = VersionCommand;
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VERSION.0)
} const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VERSION.0;
fn parse( fn parse(
&self,
qemu: Qemu, qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>, 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])?; 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; 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 for VaddrFilterAllowRangeCommandParser
where where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
fn command_id(&self) -> GuestReg { type OutputCommand = FilterCommand<QemuInstrumentationAddressRangeFilter>;
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VADDR_FILTER_ALLOW.0)
} const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VADDR_FILTER_ALLOW.0;
fn parse( fn parse(
&self,
qemu: Qemu, qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>, 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_start: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let vaddr_end: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?; 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)] #[allow(clippy::single_range_in_vec_init)]
QemuInstrumentationAddressRangeFilter::AllowList(vec![vaddr_start..vaddr_end]), 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")] #[cfg(emulation_mode = "usermode")]
use crate::qemu::{ use crate::qemu::{
closure_new_thread_hook_wrapper, closure_post_syscall_hook_wrapper, closure_post_syscall_hook_wrapper, closure_pre_syscall_hook_wrapper,
closure_pre_syscall_hook_wrapper, func_new_thread_hook_wrapper, func_post_syscall_hook_wrapper, func_post_syscall_hook_wrapper, func_pre_syscall_hook_wrapper, PostSyscallHook,
func_pre_syscall_hook_wrapper, NewThreadHook, NewThreadHookId, PostSyscallHook,
PostSyscallHookId, PreSyscallHook, PreSyscallHookId, SyscallHookResult, PostSyscallHookId, PreSyscallHook, PreSyscallHookId, SyscallHookResult,
}; };
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
use crate::qemu::{ use crate::qemu::{
CrashHookClosure, NewThreadHookClosure, PostSyscallHookClosure, PostSyscallHookFn, CrashHookClosure, PostSyscallHookClosure, PostSyscallHookFn, PreSyscallHookClosure,
PreSyscallHookClosure, PreSyscallHookFn, PreSyscallHookFn,
}; };
use crate::{ use crate::{
cpu_run_post_exec_hook_wrapper, cpu_run_pre_exec_hook_wrapper, cpu_run_post_exec_hook_wrapper, cpu_run_pre_exec_hook_wrapper,
modules::{EmulatorModule, EmulatorModuleTuple}, modules::{EmulatorModule, EmulatorModuleTuple},
qemu::{ qemu::{
block_0_exec_hook_wrapper, block_gen_hook_wrapper, block_post_gen_hook_wrapper, 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, closure_backdoor_hook_wrapper, closure_instruction_hook_wrapper,
cmp_1_exec_hook_wrapper, cmp_2_exec_hook_wrapper, cmp_3_exec_hook_wrapper, closure_new_thread_hook_wrapper, cmp_0_exec_hook_wrapper, cmp_1_exec_hook_wrapper,
cmp_gen_hook_wrapper, edge_0_exec_hook_wrapper, edge_gen_hook_wrapper, cmp_2_exec_hook_wrapper, cmp_3_exec_hook_wrapper, cmp_gen_hook_wrapper,
func_backdoor_hook_wrapper, func_instruction_hook_wrapper, read_0_exec_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_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, 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_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, write_4_exec_hook_wrapper, write_gen_hook_wrapper, BackdoorHook, BackdoorHookClosure,
BackdoorHookFn, BackdoorHookId, BlockExecHook, BlockGenHook, BlockHookId, BlockPostGenHook, BackdoorHookFn, BackdoorHookId, BlockExecHook, BlockGenHook, BlockHookId, BlockPostGenHook,
CmpExecHook, CmpGenHook, CmpHookId, EdgeExecHook, EdgeGenHook, EdgeHookId, Hook, HookRepr, CmpExecHook, CmpGenHook, CmpHookId, EdgeExecHook, EdgeGenHook, EdgeHookId, Hook, HookRepr,
InstructionHook, InstructionHookClosure, InstructionHookFn, InstructionHookId, QemuHooks, InstructionHook, InstructionHookClosure, InstructionHookFn, InstructionHookId,
ReadExecHook, ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook, NewThreadHook, NewThreadHookClosure, NewThreadHookId, QemuHooks, ReadExecHook,
WriteExecNHook, WriteGenHook, WriteHookId, ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook, WriteExecNHook,
WriteGenHook, WriteHookId,
}, },
CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, Qemu, CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, Qemu,
}; };
@ -114,7 +115,6 @@ where
S: UsesInput, S: UsesInput,
{ {
qemu_hooks: QemuHooks, qemu_hooks: QemuHooks,
phantom: PhantomData<(ET, S)>,
instruction_hooks: Vec<Pin<Box<(InstructionHookId, FatPtr)>>>, instruction_hooks: Vec<Pin<Box<(InstructionHookId, FatPtr)>>>,
backdoor_hooks: Vec<Pin<Box<(BackdoorHookId, FatPtr)>>>, backdoor_hooks: Vec<Pin<Box<(BackdoorHookId, FatPtr)>>>,
@ -126,17 +126,18 @@ where
cpu_run_hooks: Vec<Pin<Box<HookState<CpuRunHookId>>>>, cpu_run_hooks: Vec<Pin<Box<HookState<CpuRunHookId>>>>,
new_thread_hooks: Vec<Pin<Box<(NewThreadHookId, FatPtr)>>>,
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
pre_syscall_hooks: Vec<Pin<Box<(PreSyscallHookId, FatPtr)>>>, pre_syscall_hooks: Vec<Pin<Box<(PreSyscallHookId, FatPtr)>>>,
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
post_syscall_hooks: Vec<Pin<Box<(PostSyscallHookId, FatPtr)>>>, post_syscall_hooks: Vec<Pin<Box<(PostSyscallHookId, FatPtr)>>>,
#[cfg(emulation_mode = "usermode")]
new_thread_hooks: Vec<Pin<Box<(NewThreadHookId, FatPtr)>>>,
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
crash_hooks: Vec<HookRepr>, crash_hooks: Vec<HookRepr>,
phantom: PhantomData<(ET, S)>,
} }
impl<ET, S> EmulatorHooks<ET, S> impl<ET, S> EmulatorHooks<ET, S>
@ -158,15 +159,14 @@ where
cpu_run_hooks: Vec::new(), cpu_run_hooks: Vec::new(),
new_thread_hooks: Vec::new(),
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
pre_syscall_hooks: Vec::new(), pre_syscall_hooks: Vec::new(),
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
post_syscall_hooks: Vec::new(), post_syscall_hooks: Vec::new(),
#[cfg(emulation_mode = "usermode")]
new_thread_hooks: Vec::new(),
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
crash_hooks: Vec::new(), crash_hooks: Vec::new(),
} }
@ -700,6 +700,63 @@ where
Hook::Empty => None, // TODO error type 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")] #[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)) { 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.qemu_hooks.set_crash_hook(crash_hook_wrapper::<ET, S>);
self.crash_hooks self.crash_hooks
@ -1054,6 +1055,29 @@ where
pub fn backdoor_closure(&mut self, hook: BackdoorHookClosure<ET, S>) -> BackdoorHookId { pub fn backdoor_closure(&mut self, hook: BackdoorHookClosure<ET, S>) -> BackdoorHookId {
self.hooks.backdoor_closure(hook) 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> impl<ET, S> EmulatorModules<ET, S>
@ -1266,28 +1290,6 @@ where
self.hooks.after_syscalls_closure(hook) 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)) { pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules<ET, S>, target_signal: i32)) {
self.hooks.crash_function(hook); self.hooks.crash_function(hook);
} }

View File

@ -2,110 +2,102 @@
//! //!
//! [`Emulator`] is built above [`Qemu`] and provides convenient abstractions. //! [`Emulator`] is built above [`Qemu`] and provides convenient abstractions.
use core::{ use core::fmt::{self, Debug, Display, Formatter};
fmt::{self, Debug, Display, Formatter}, use std::{cell::RefCell, ops::Add, pin::Pin};
marker::PhantomData,
};
use std::{
cell::{OnceCell, Ref, RefCell, RefMut},
hash::Hash,
ops::Add,
pin::Pin,
rc::Rc,
};
use hashbrown::HashMap; use hashbrown::HashMap;
use libafl::{ use libafl::{
executors::ExitKind, executors::ExitKind,
inputs::{HasTargetBytes, UsesInput}, inputs::{HasTargetBytes, UsesInput},
observers::ObserversTuple, observers::ObserversTuple,
state::{HasExecutions, State},
}; };
use libafl_bolts::os::unix_signals::Signal;
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestUsize, GuestVirtAddr}; use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestUsize, GuestVirtAddr};
use typed_builder::TypedBuilder;
use crate::{ use crate::{
breakpoint::{Breakpoint, BreakpointId}, breakpoint::{Breakpoint, BreakpointId},
command::{CommandError, CommandManager, InputCommand, IsCommand}, command::{CommandError, CommandManager, NopCommandManager, StdCommandManager},
modules::EmulatorModuleTuple, modules::{EmulatorModuleTuple, StdInstrumentationFilter},
sync_exit::SyncExit, sync_exit::SyncExit,
Qemu, QemuExitError, QemuExitReason, QemuInitError, QemuMemoryChunk, QemuShutdownCause, Qemu, QemuExitError, QemuExitReason, QemuInitError, QemuMemoryChunk, QemuShutdownCause, Regs,
QemuSnapshotCheckResult, Regs, CPU, CPU,
}; };
mod hooks; mod hooks;
pub use hooks::*; pub use hooks::*;
mod builder;
pub use builder::*;
mod drivers;
pub use drivers::*;
mod snapshot;
pub use snapshot::*;
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
mod usermode; mod usermode;
#[cfg(emulation_mode = "usermode")]
pub use usermode::*;
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
mod systemmode; mod systemmode;
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
pub use 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)] #[derive(Clone, Copy)]
pub enum GuestAddrKind { pub enum GuestAddrKind {
Physical(GuestPhysAddr), Physical(GuestPhysAddr),
Virtual(GuestVirtAddr), Virtual(GuestVirtAddr),
} }
#[derive(Debug, Clone)] pub enum EmulatorExitResult<CM, ED, ET, S, SM>
pub enum EmulatorExitResult<CM, EH, ET, S>
where where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
QemuExit(QemuShutdownCause), // QEMU ended for some reason. QemuExit(QemuShutdownCause), // QEMU ended for some reason.
Breakpoint(Rc<RefCell<Breakpoint<CM, EH, ET, S>>>), // Breakpoint triggered. Contains the address of the trigger. Breakpoint(Breakpoint<CM, ED, ET, S, SM>), // 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. 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)] #[derive(Debug, Clone)]
pub enum EmulatorExitError { pub enum EmulatorExitError {
UnknownKind, UnknownKind,
@ -114,71 +106,6 @@ pub enum EmulatorExitError {
BreakpointNotFound(GuestAddr), 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)] #[derive(Debug, Clone)]
pub struct InputLocation { pub struct InputLocation {
mem_chunk: QemuMemoryChunk, mem_chunk: QemuMemoryChunk,
@ -186,69 +113,38 @@ pub struct InputLocation {
ret_register: Option<Regs>, ret_register: Option<Regs>,
} }
/// Synchronous Exit handler maintaining only one snapshot. #[derive(Debug)]
#[derive(Debug, Clone, TypedBuilder)] #[allow(clippy::type_complexity)]
pub struct StdEmulatorExitHandler<SM> { pub struct Emulator<CM, ED, ET, S, 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>
where where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
snapshot_manager: SM,
modules: Pin<Box<EmulatorModules<ET, S>>>, modules: Pin<Box<EmulatorModules<ET, S>>>,
command_manager: CM, command_manager: CM,
exit_handler: RefCell<EH>, driver: Option<ED>,
#[builder(default)] breakpoints_by_addr: RefCell<HashMap<GuestAddr, Breakpoint<CM, ED, ET, S, SM>>>, // TODO: change to RC here
breakpoints_by_addr: RefCell<HashMap<GuestAddr, BreakpointMutRef<CM, EH, ET, S>>>, breakpoints_by_id: RefCell<HashMap<BreakpointId, Breakpoint<CM, ED, ET, S, SM>>>,
#[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()))]
qemu: Qemu, qemu: Qemu,
first_exec: bool, 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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
#[must_use] #[must_use]
#[allow(clippy::match_wildcard_for_single_variants)] #[allow(clippy::match_wildcard_for_single_variants)]
pub fn end_of_run(&self) -> Option<ExitKind> { pub fn end_of_run(&self) -> Option<ExitKind> {
match self { match self {
ExitHandlerResult::EndOfRun(exit_kind) => Some(*exit_kind), EmulatorDriverResult::EndOfRun(exit_kind) => Some(*exit_kind),
_ => None, _ => 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 { impl Debug for GuestAddrKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
@ -270,7 +166,7 @@ impl Add<GuestUsize> for GuestAddrKind {
} }
impl Display 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 { match self {
GuestAddrKind::Physical(phys_addr) => write!(f, "hwaddr 0x{phys_addr:x}"), GuestAddrKind::Physical(phys_addr) => write!(f, "hwaddr 0x{phys_addr:x}"),
GuestAddrKind::Virtual(virt_addr) => write!(f, "vaddr 0x{virt_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 { 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 { 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> { impl From<EmulatorExitError> for EmulatorDriverError {
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 {
fn from(error: EmulatorExitError) -> Self { 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 { 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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
EmulatorExitResult::QemuExit(shutdown_cause) => write!(f, "End: {shutdown_cause:?}"), 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) => { 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 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, ET: Unpin,
S: UsesInput + 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 where
CM: CommandManager<ED, ET, S, SM>,
ET: EmulatorModuleTuple<S>, ET: EmulatorModuleTuple<S>,
S: UsesInput + Unpin, S: UsesInput + Unpin,
{ {
#[allow(clippy::must_use_candidate, clippy::similar_names)] #[allow(clippy::must_use_candidate, clippy::similar_names)]
pub fn new( pub fn new(
args: &[String], qemu_args: &[String],
env: &[(String, String)],
modules: ET, modules: ET,
exit_handler: EH, drivers: ED,
snapshot_manager: SM,
command_manager: CM, command_manager: CM,
) -> Result<Self, QemuInitError> { ) -> 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( pub fn new_with_qemu(
qemu: Qemu, qemu: Qemu,
modules: ET, modules: ET,
exit_handler: EH, driver: ED,
snapshot_manager: SM,
command_manager: CM, command_manager: CM,
) -> Result<Self, QemuInitError> { ) -> Result<Self, QemuInitError> {
Ok(Emulator { Ok(Emulator {
modules: EmulatorModules::new(qemu, modules), modules: EmulatorModules::new(qemu, modules),
command_manager, command_manager,
exit_handler: RefCell::new(exit_handler), snapshot_manager,
driver: Some(driver),
breakpoints_by_addr: RefCell::new(HashMap::new()), breakpoints_by_addr: RefCell::new(HashMap::new()),
breakpoints_by_id: RefCell::new(HashMap::new()), breakpoints_by_id: RefCell::new(HashMap::new()),
first_exec: true, first_exec: true,
_phantom: PhantomData,
qemu, 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 where
S: UsesInput, CM: CommandManager<ED, ET, S, SM>,
{ ED: EmulatorDriver<CM, ET, S, SM>,
pub fn modules(&self) -> &EmulatorModules<ET, S> { ET: StdInstrumentationFilter + Unpin,
&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>,
S: UsesInput, S: UsesInput,
{ {
/// This function will run the emulator until the exit handler decides to stop the execution for /// 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( pub unsafe fn run(
&mut self, &mut self,
input: &S::Input, 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 { 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 // Insert input if the location is already known
EH::qemu_pre_exec(self, input); driver.pre_exec(self, input);
// Run QEMU // Run QEMU
let exit_reason = self.run_qemu(); let mut exit_reason = self.run_qemu();
// Handle QEMU exit // 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); return Ok(exit_handler_result);
} }
} }
@ -579,7 +402,9 @@ where
/// ///
/// Should, in general, be safe to call. /// 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. /// 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() { match self.qemu.run() {
Ok(qemu_exit_reason) => Ok(match qemu_exit_reason { Ok(qemu_exit_reason) => Ok(match qemu_exit_reason {
QemuExitReason::End(qemu_shutdown_cause) => { QemuExitReason::End(qemu_shutdown_cause) => {
@ -594,9 +419,9 @@ where
.clone(); .clone();
EmulatorExitResult::Breakpoint(bp.clone()) EmulatorExitResult::Breakpoint(bp.clone())
} }
QemuExitReason::SyncExit => EmulatorExitResult::SyncExit(Rc::new(RefCell::new( QemuExitReason::SyncExit => EmulatorExitResult::SyncExit(SyncExit::new(
SyncExit::new(self.command_manager.parse(self.qemu)?), self.command_manager.parse(self.qemu)?,
))), )),
}), }),
Err(qemu_exit_reason_error) => Err(match qemu_exit_reason_error { Err(qemu_exit_reason_error) => Err(match qemu_exit_reason_error {
QemuExitError::UnexpectedExit => EmulatorExitError::UnexpectedExit, QemuExitError::UnexpectedExit => EmulatorExitError::UnexpectedExit,
@ -607,11 +432,16 @@ where
} }
#[allow(clippy::unused_self)] #[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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, 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 { if enable {
bp.enable(self.qemu); bp.enable(self.qemu);
} }
@ -619,12 +449,10 @@ where
let bp_id = bp.id(); let bp_id = bp.id();
let bp_addr = bp.addr(); let bp_addr = bp.addr();
let bp_ref = Rc::new(RefCell::new(bp));
assert!( assert!(
self.breakpoints_by_addr self.breakpoints_by_addr
.borrow_mut() .borrow_mut()
.insert(bp_addr, bp_ref.clone()) .insert(bp_addr, bp.clone())
.is_none(), .is_none(),
"Adding multiple breakpoints at the same address" "Adding multiple breakpoints at the same address"
); );
@ -632,7 +460,7 @@ where
assert!( assert!(
self.breakpoints_by_id self.breakpoints_by_id
.borrow_mut() .borrow_mut()
.insert(bp_id, bp_ref) .insert(bp_id, bp)
.is_none(), .is_none(),
"Adding the same breakpoint multiple times" "Adding the same breakpoint multiple times"
); );
@ -643,10 +471,7 @@ where
pub fn remove_breakpoint(&self, bp_id: BreakpointId) { pub fn remove_breakpoint(&self, bp_id: BreakpointId) {
let bp_addr = { let bp_addr = {
let mut bp_map = self.breakpoints_by_id.borrow_mut(); let mut bp_map = self.breakpoints_by_id.borrow_mut();
let mut bp = bp_map let bp = bp_map.get_mut(&bp_id).expect("Did not find the breakpoint");
.get_mut(&bp_id)
.expect("Did not find the breakpoint")
.borrow_mut();
bp.disable(self.qemu); bp.disable(self.qemu);
bp.addr() 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::{ use std::fmt::Debug;
fmt::Debug,
sync::atomic::{AtomicU64, Ordering},
};
use hashbrown::HashMap; use hashbrown::HashMap;
use libafl::inputs::UsesInput; use libafl::{
inputs::{HasTargetBytes, UsesInput},
prelude::{HasExecutions, State},
};
use libafl_qemu_sys::GuestPhysAddr; use libafl_qemu_sys::GuestPhysAddr;
use crate::{ use crate::{
emu::IsSnapshotManager, DeviceSnapshotFilter, Emulator, Qemu, QemuSnapshotCheckResult, command::{CommandManager, StdCommandManager},
SnapshotId, SnapshotManagerError, 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)] #[derive(Debug, Clone)]
pub enum SnapshotManager { pub enum SnapshotManager {
Qemu(QemuSnapshotManager), Qemu(QemuSnapshotManager),
Fast(FastSnapshotManager), Fast(FastSnapshotManager),
} }
pub type StdSnapshotManager = FastSnapshotManager;
impl IsSnapshotManager for SnapshotManager { impl IsSnapshotManager for SnapshotManager {
fn save(&mut self, qemu: Qemu) -> SnapshotId { fn save(&mut self, qemu: Qemu) -> SnapshotId {
match self { match self {
@ -42,23 +32,23 @@ impl IsSnapshotManager for SnapshotManager {
fn restore( fn restore(
&mut self, &mut self,
snapshot_id: &SnapshotId,
qemu: Qemu, qemu: Qemu,
snapshot_id: &SnapshotId,
) -> Result<(), SnapshotManagerError> { ) -> Result<(), SnapshotManagerError> {
match self { match self {
SnapshotManager::Qemu(qemu_sm) => qemu_sm.restore(snapshot_id, qemu), SnapshotManager::Qemu(qemu_sm) => qemu_sm.restore(qemu, snapshot_id),
SnapshotManager::Fast(fast_sm) => fast_sm.restore(snapshot_id, qemu), SnapshotManager::Fast(fast_sm) => fast_sm.restore(qemu, snapshot_id),
} }
} }
fn do_check( fn do_check(
&self, &self,
reference_snapshot_id: &SnapshotId,
qemu: Qemu, qemu: Qemu,
reference_snapshot_id: &SnapshotId,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> { ) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
match self { match self {
SnapshotManager::Qemu(qemu_sm) => qemu_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(reference_snapshot_id, qemu), SnapshotManager::Fast(fast_sm) => fast_sm.do_check(qemu, reference_snapshot_id),
} }
} }
} }
@ -115,8 +105,8 @@ impl IsSnapshotManager for QemuSnapshotManager {
fn restore( fn restore(
&mut self, &mut self,
snapshot_id: &SnapshotId,
qemu: Qemu, qemu: Qemu,
snapshot_id: &SnapshotId,
) -> Result<(), SnapshotManagerError> { ) -> Result<(), SnapshotManagerError> {
qemu.load_snapshot(self.snapshot_id_to_name(snapshot_id).as_str(), self.is_sync); qemu.load_snapshot(self.snapshot_id_to_name(snapshot_id).as_str(), self.is_sync);
Ok(()) Ok(())
@ -124,8 +114,8 @@ impl IsSnapshotManager for QemuSnapshotManager {
fn do_check( fn do_check(
&self, &self,
_reference_snapshot_id: &SnapshotId,
_qemu: Qemu, _qemu: Qemu,
_reference_snapshot_id: &SnapshotId,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> { ) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
// We consider the qemu implementation to be 'ideal' for now. // We consider the qemu implementation to be 'ideal' for now.
Ok(QemuSnapshotCheckResult::default()) Ok(QemuSnapshotCheckResult::default())
@ -142,8 +132,8 @@ impl IsSnapshotManager for FastSnapshotManager {
fn restore( fn restore(
&mut self, &mut self,
snapshot_id: &SnapshotId,
qemu: Qemu, qemu: Qemu,
snapshot_id: &SnapshotId,
) -> Result<(), SnapshotManagerError> { ) -> Result<(), SnapshotManagerError> {
let fast_snapshot_ptr = *self let fast_snapshot_ptr = *self
.snapshots .snapshots
@ -159,8 +149,8 @@ impl IsSnapshotManager for FastSnapshotManager {
fn do_check( fn do_check(
&self, &self,
reference_snapshot_id: &SnapshotId,
qemu: Qemu, qemu: Qemu,
reference_snapshot_id: &SnapshotId,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> { ) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
let fast_snapshot_ptr = *self.snapshots.get(reference_snapshot_id).ok_or( let fast_snapshot_ptr = *self.snapshots.get(reference_snapshot_id).ok_or(
SnapshotManagerError::SnapshotIdNotFound(*reference_snapshot_id), 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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
/// Write a value to a phsical guest address, including ROM areas. /// Write a value to a phsical guest address, including ROM areas.

View File

@ -1,10 +1,13 @@
use libafl::inputs::UsesInput; use libafl::inputs::UsesInput;
use libafl_qemu_sys::{GuestAddr, MmapPerms, VerifyAccess}; 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 where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, S: UsesInput,
{ {
/// This function gets the memory mappings from the emulator. /// This function gets the memory mappings from the emulator.

View File

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

View File

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

View File

@ -685,6 +685,7 @@ pub fn init_qemu_with_asan(
let add_asan = let add_asan =
|e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..]; |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; let mut added = false;
for (k, v) in &mut *env { for (k, v) in &mut *env {
if k == "QEMU_SET_ENV" { if k == "QEMU_SET_ENV" {
@ -717,7 +718,7 @@ pub fn init_qemu_with_asan(
ASAN_INITED = true; ASAN_INITED = true;
} }
let qemu = Qemu::init(args, env)?; let qemu = Qemu::init(args)?;
let rt = AsanGiovese::new(qemu.hooks()); let rt = AsanGiovese::new(qemu.hooks());
Ok((qemu, rt)) Ok((qemu, rt))

View File

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

View File

@ -1,5 +1,8 @@
#[cfg(not(cpu_target = "hexagon"))] #[cfg(not(cpu_target = "hexagon"))]
pub mod drcov; pub mod drcov;
use std::fmt::Debug;
#[cfg(not(cpu_target = "hexagon"))] #[cfg(not(cpu_target = "hexagon"))]
pub use drcov::DrCovModule; pub use drcov::DrCovModule;
@ -28,12 +31,12 @@ pub use asan_guest::{init_qemu_with_asan_guest, AsanGuestModule};
use crate::modules::{HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter}; use crate::modules::{HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter};
pub trait StdInstrumentationFilter: pub trait StdInstrumentationFilter:
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> + Debug
{ {
} }
impl<Head> StdInstrumentationFilter for (Head, ()) where 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 let args = config
.to_string() .to_string()
.split(' ') .split(' ')
.map(std::string::ToString::to_string) .map(ToString::to_string)
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let qemu = Qemu::init(&args, &[])?; let qemu = Qemu::init(&args)?;
QEMU_CONFIG QEMU_CONFIG
.set(config) .set(config)
.map_err(|_| unreachable!("BUG: QEMU_CONFIG was already set but Qemu was not init!"))?; .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 // New thread hook wrappers
#[cfg(emulation_mode = "usermode")]
create_hook_types!( create_hook_types!(
NewThread, NewThread,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, env: CPUArchStatePtr, tid: u32) -> bool, 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 extern "C" fn(*const (), env: CPUArchStatePtr, tid: u32) -> bool
); );
#[cfg(emulation_mode = "usermode")]
create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false); create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false);
#[cfg(emulation_mode = "usermode")]
create_wrapper!(new_thread, (env: CPUArchStatePtr, tid: u32), bool); create_wrapper!(new_thread, (env: CPUArchStatePtr, tid: u32), bool);
// CPU Run hook wrappers // CPU Run hook wrappers
@ -943,6 +940,19 @@ impl QemuHooks {
BackdoorHookId(num) 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")] #[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)] #[allow(clippy::type_complexity)]
pub fn add_post_syscall_hook<T: Into<HookData>>( pub fn add_post_syscall_hook<T: Into<HookData>>(
&self, &self,

View File

@ -16,14 +16,12 @@ use std::{
}; };
use libafl_bolts::os::unix_signals::Signal; use libafl_bolts::os::unix_signals::Signal;
#[cfg(emulation_mode = "systemmode")]
use libafl_qemu_sys::qemu_init;
#[cfg(emulation_mode = "usermode")] #[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::{ use libafl_qemu_sys::{
libafl_flush_jit, libafl_get_exit_reason, libafl_page_from_addr, libafl_qemu_add_gdb_cmd, 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_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_remove_breakpoint, libafl_qemu_set_breakpoint, libafl_qemu_trigger_breakpoint,
libafl_qemu_write_reg, CPUArchState, CPUStatePtr, FatPtr, GuestAddr, GuestPhysAddr, GuestUsize, libafl_qemu_write_reg, CPUArchState, CPUStatePtr, FatPtr, GuestAddr, GuestPhysAddr, GuestUsize,
GuestVirtAddr, GuestVirtAddr,
@ -33,8 +31,8 @@ use strum::IntoEnumIterator;
use crate::{GuestAddrKind, GuestReg, Regs}; use crate::{GuestAddrKind, GuestReg, Regs};
pub mod qemu_config; pub mod config;
use qemu_config::{QemuConfig, QemuConfigBuilder, QEMU_CONFIG}; use config::{QemuConfig, QemuConfigBuilder, QEMU_CONFIG};
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
mod 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. 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)] #[derive(Debug, Clone)]
pub enum QemuRWErrorKind { pub enum QemuRWErrorKind {
Read, 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. /// The thin wrapper around QEMU.
/// It is considered unsafe to use it directly. /// It is considered unsafe to use it directly.
/// Prefer using `Emulator` instead in case of doubt. /// Prefer using `Emulator` instead in case of doubt.
@ -529,7 +513,7 @@ impl Qemu {
} }
#[allow(clippy::must_use_candidate, clippy::similar_names)] #[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() { if args.is_empty() {
return Err(QemuInitError::EmptyArgs); return Err(QemuInitError::EmptyArgs);
} }
@ -555,22 +539,16 @@ impl Qemu {
.collect(); .collect();
let mut argv: Vec<*const u8> = args.iter().map(|x| x.as_ptr() as *const u8).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. 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 { unsafe {
#[cfg(emulation_mode = "usermode")] libafl_qemu_init(argc, argv.as_ptr() as *mut *mut i8);
qemu_user_init(argc, argv.as_ptr(), envp.as_ptr()); }
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
{ unsafe {
qemu_init(argc, argv.as_ptr());
libc::atexit(qemu_cleanup_atexit); libc::atexit(qemu_cleanup_atexit);
libafl_qemu_sys::syx_snapshot_init(true); libafl_qemu_sys::syx_snapshot_init(true);
} }
}
Ok(Qemu { _private: () }) Ok(Qemu { _private: () })
} }
@ -993,9 +971,9 @@ pub mod pybind {
impl Qemu { impl Qemu {
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
#[new] #[new]
fn new(args: Vec<String>, env: Vec<(String, String)>) -> PyResult<Qemu> { fn new(args: Vec<String>) -> PyResult<Qemu> {
let qemu = super::Qemu::init(&args, &env) let qemu =
.map_err(|e| PyValueError::new_err(format!("{e}")))?; super::Qemu::init(&args).map_err(|e| PyValueError::new_err(format!("{e}")))?;
Ok(Qemu { qemu }) 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, libafl_save_qemu_snapshot, qemu_cleanup, qemu_main_loop, vm_start, GuestAddr, GuestPhysAddr,
GuestUsize, GuestVirtAddr, GuestUsize, GuestVirtAddr,
}; };
use libc::EXIT_SUCCESS;
use num_traits::Zero; use num_traits::Zero;
use crate::{ use crate::{
@ -21,7 +22,7 @@ use crate::{
pub(super) extern "C" fn qemu_cleanup_atexit() { pub(super) extern "C" fn qemu_cleanup_atexit() {
unsafe { unsafe {
qemu_cleanup(); qemu_cleanup(EXIT_SUCCESS);
} }
} }
@ -254,9 +255,7 @@ impl Qemu {
) -> QemuSnapshotCheckResult { ) -> QemuSnapshotCheckResult {
let check_result = libafl_qemu_sys::syx_snapshot_check(ref_snapshot); let check_result = libafl_qemu_sys::syx_snapshot_check(ref_snapshot);
QemuSnapshotCheckResult { QemuSnapshotCheckResult::new(check_result.nb_inconsistencies)
nb_page_inconsistencies: check_result.nb_inconsistencies,
}
} }
pub fn list_devices(&self) -> Vec<String> { pub fn list_devices(&self) -> Vec<String> {

View File

@ -1,12 +1,9 @@
use std::{ use std::fmt::{Debug, Formatter};
fmt::{Display, Formatter},
rc::Rc,
};
use enum_map::Enum; use enum_map::Enum;
use libafl::inputs::UsesInput; 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)] #[derive(Debug, Clone, Enum)]
pub enum ExitArgs { pub enum ExitArgs {
@ -20,26 +17,49 @@ pub enum ExitArgs {
Arg6, Arg6,
} }
#[derive(Debug)] pub struct SyncExit<CM, ED, ET, S, SM>
pub struct SyncExit<CM, EH, ET, S>
where where
CM: CommandManager<ED, ET, S, SM>,
S: UsesInput, 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 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, S: UsesInput,
{ {
#[must_use] #[must_use]
pub fn new(command: Rc<dyn IsCommand<CM, EH, ET, S>>) -> Self { pub fn new(command: CM::Commands) -> Self {
Self { command } Self { command }
} }
#[must_use] #[must_use]
pub fn command(&self) -> Rc<dyn IsCommand<CM, EH, ET, S>> { pub fn command(&self) -> &CM::Commands {
self.command.clone() &self.command
} }
pub fn ret(&self, cpu: &CPU, value: GuestReg) { pub fn ret(&self, cpu: &CPU, value: GuestReg) {
@ -52,12 +72,3 @@ where
get_exit_arch_regs()[ExitArgs::Ret] 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; use libafl_qemu::modules::CmpLogModule;
pub use libafl_qemu::qemu::Qemu; pub use libafl_qemu::qemu::Qemu;
use libafl_qemu::{ use libafl_qemu::{
command::NopCommandManager,
modules::edges::{self, EdgeCoverageModule}, modules::edges::{self, EdgeCoverageModule},
Emulator, NopEmulatorExitHandler, QemuExecutor, Emulator, QemuExecutor,
}; };
use libafl_targets::{edges_map_mut_ptr, CmpLogObserver}; use libafl_targets::{edges_map_mut_ptr, CmpLogObserver};
use typed_builder::TypedBuilder; 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 target = input.target_bytes();
let buf = target.as_slice(); let buf = target.as_slice();
harness_bytes(buf); harness_bytes(buf);
ExitKind::Ok ExitKind::Ok
}; };
let emulator = Emulator::new_with_qemu( let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
qemu,
modules,
NopEmulatorExitHandler,
NopCommandManager,
)?;
let executor = QemuExecutor::new( let executor = QemuExecutor::new(
emulator, emulator,
@ -345,21 +339,16 @@ where
} }
} }
} else { } 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 target = input.target_bytes();
let buf = target.as_slice(); let buf = target.as_slice();
harness_bytes(buf); harness_bytes(buf);
ExitKind::Ok ExitKind::Ok
}; };
let emulator = Emulator::new_with_qemu( let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
qemu,
tools,
NopEmulatorExitHandler,
NopCommandManager,
)?;
let mut executor = QemuExecutor::new( let mut executor = QemuExecutor::new(
emulator, emulator,