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:
parent
16aa218457
commit
4b87d7f4eb
@ -46,15 +46,14 @@ use libafl_bolts::{
|
||||
AsSlice, AsSliceMut,
|
||||
};
|
||||
use libafl_qemu::{
|
||||
command::NopCommandManager,
|
||||
elf::EasyElf,
|
||||
filter_qemu_args,
|
||||
modules::{
|
||||
cmplog::{CmpLogChildModule, CmpLogMap, CmpLogObserver},
|
||||
edges::{EdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
|
||||
},
|
||||
Emulator, GuestReg, MmapPerms, NopEmulatorExitHandler, QemuExitError, QemuExitReason,
|
||||
QemuForkExecutor, QemuShutdownCause, Regs,
|
||||
Emulator, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuForkExecutor,
|
||||
QemuShutdownCause, Regs,
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use nix::unistd::dup;
|
||||
@ -156,14 +155,10 @@ fn fuzz(
|
||||
CmpLogChildModule::default(),
|
||||
);
|
||||
|
||||
let emulator = Emulator::new(
|
||||
args.as_slice(),
|
||||
env.as_slice(),
|
||||
emulator_modules,
|
||||
NopEmulatorExitHandler,
|
||||
NopCommandManager,
|
||||
)
|
||||
.unwrap();
|
||||
let emulator = Emulator::empty()
|
||||
.qemu_cli(args)
|
||||
.modules(emulator_modules)
|
||||
.build()?;
|
||||
|
||||
let qemu = emulator.qemu();
|
||||
|
||||
|
@ -46,7 +46,6 @@ use libafl_bolts::{
|
||||
AsSlice,
|
||||
};
|
||||
use libafl_qemu::{
|
||||
command::NopCommandManager,
|
||||
elf::EasyElf,
|
||||
filter_qemu_args,
|
||||
// asan::{init_with_asan, QemuAsanHelper},
|
||||
@ -58,7 +57,6 @@ use libafl_qemu::{
|
||||
GuestReg,
|
||||
//snapshot::QemuSnapshotHelper,
|
||||
MmapPerms,
|
||||
NopEmulatorExitHandler,
|
||||
Qemu,
|
||||
QemuExecutor,
|
||||
QemuExitError,
|
||||
@ -177,8 +175,7 @@ fn fuzz(
|
||||
env::remove_var("LD_LIBRARY_PATH");
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
let qemu = Qemu::init(&args, &env).unwrap();
|
||||
let qemu = Qemu::init(&args).unwrap();
|
||||
// let (emu, asan) = init_with_asan(&mut args, &mut env).unwrap();
|
||||
|
||||
let mut elf_buffer = Vec::new();
|
||||
@ -329,7 +326,7 @@ fn fuzz(
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
|
||||
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let mut buf = target.as_slice();
|
||||
let mut len = buf.len();
|
||||
@ -366,8 +363,7 @@ fn fuzz(
|
||||
//QemuSnapshotHelper::new()
|
||||
);
|
||||
|
||||
let emulator =
|
||||
Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?;
|
||||
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
|
||||
|
||||
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
|
||||
let executor = QemuExecutor::new(
|
||||
|
@ -27,12 +27,10 @@ use libafl_bolts::{
|
||||
AsSlice, AsSliceMut,
|
||||
};
|
||||
use libafl_qemu::{
|
||||
command::NopCommandManager,
|
||||
elf::EasyElf,
|
||||
modules::edges::{EdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
|
||||
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms,
|
||||
NopEmulatorExitHandler, Qemu, QemuExitError, QemuExitReason, QemuForkExecutor,
|
||||
QemuShutdownCause, Regs,
|
||||
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitError,
|
||||
QemuExitReason, QemuForkExecutor, QemuShutdownCause, Regs,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -113,8 +111,7 @@ pub fn fuzz() -> Result<(), Error> {
|
||||
log::debug!("ARGS: {:#?}", options.args);
|
||||
|
||||
env::remove_var("LD_LIBRARY_PATH");
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
let qemu = Qemu::init(&options.args, &env).unwrap();
|
||||
let qemu = Qemu::init(&options.args).unwrap();
|
||||
|
||||
let mut elf_buffer = Vec::new();
|
||||
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
|
||||
@ -223,8 +220,7 @@ pub fn fuzz() -> Result<(), Error> {
|
||||
|
||||
let modules = tuple_list!(EdgeCoverageChildModule::default(),);
|
||||
|
||||
let emulator =
|
||||
Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?;
|
||||
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
|
||||
|
||||
let mut executor = QemuForkExecutor::new(
|
||||
emulator,
|
||||
|
@ -26,12 +26,10 @@ use libafl_bolts::{
|
||||
AsSlice,
|
||||
};
|
||||
use libafl_qemu::{
|
||||
command::NopCommandManager,
|
||||
elf::EasyElf,
|
||||
modules::{drcov::DrCovModule, QemuInstrumentationAddressRangeFilter},
|
||||
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms,
|
||||
NopEmulatorExitHandler, Qemu, QemuExecutor, QemuExitReason, QemuRWError, QemuShutdownCause,
|
||||
Regs,
|
||||
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor,
|
||||
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -119,9 +117,8 @@ pub fn fuzz() {
|
||||
log::debug!("ARGS: {:#?}", options.args);
|
||||
|
||||
env::remove_var("LD_LIBRARY_PATH");
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
|
||||
let qemu = Qemu::init(&options.args, &env).unwrap();
|
||||
let qemu = Qemu::init(&options.args).unwrap();
|
||||
|
||||
let mut elf_buffer = Vec::new();
|
||||
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
|
||||
@ -178,7 +175,7 @@ pub fn fuzz() {
|
||||
}
|
||||
};
|
||||
|
||||
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
|
||||
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let mut buf = target.as_slice();
|
||||
let mut len = buf.len();
|
||||
@ -241,13 +238,10 @@ pub fn fuzz() {
|
||||
false,
|
||||
));
|
||||
|
||||
let emulator = Emulator::new_with_qemu(
|
||||
qemu,
|
||||
emulator_modules,
|
||||
NopEmulatorExitHandler,
|
||||
NopCommandManager,
|
||||
)
|
||||
.unwrap();
|
||||
let emulator = Emulator::empty()
|
||||
.qemu(qemu)
|
||||
.modules(emulator_modules)
|
||||
.build()?;
|
||||
|
||||
let mut executor = QemuExecutor::new(
|
||||
emulator,
|
||||
|
@ -129,7 +129,7 @@ impl<'a> Client<'a> {
|
||||
let (emu, asan_lib) = init_qemu_with_asan_guest(&mut args, &mut env)?;
|
||||
(emu, None, Some(asan_lib))
|
||||
} else {
|
||||
(Qemu::init(&args, &env)?, None, None)
|
||||
(Qemu::init(&args)?, None, None)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -38,13 +38,12 @@ use libafl_bolts::{
|
||||
tuples::{tuple_list, Merge},
|
||||
};
|
||||
use libafl_qemu::{
|
||||
command::NopCommandManager,
|
||||
modules::{
|
||||
cmplog::CmpLogObserver,
|
||||
edges::{edges_map_mut_ptr, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
||||
EmulatorModuleTuple,
|
||||
},
|
||||
Emulator, NopEmulatorExitHandler, Qemu, QemuExecutor,
|
||||
Emulator, Qemu, QemuExecutor,
|
||||
};
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
@ -150,18 +149,15 @@ impl<'a, M: Monitor> Instance<'a, M> {
|
||||
|
||||
let harness = Harness::new(self.qemu)?;
|
||||
let mut harness =
|
||||
|_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| harness.run(input);
|
||||
|_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| harness.run(input);
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
let emulator = Emulator::new_with_qemu(
|
||||
*self.qemu,
|
||||
modules,
|
||||
NopEmulatorExitHandler,
|
||||
NopCommandManager,
|
||||
)
|
||||
.unwrap();
|
||||
let emulator = Emulator::empty()
|
||||
.qemu(*self.qemu)
|
||||
.modules(modules)
|
||||
.build()?;
|
||||
|
||||
if self.options.is_cmplog_core(self.core_id) {
|
||||
// Create a QEMU in-process executor
|
||||
|
@ -29,14 +29,14 @@ use libafl_bolts::{
|
||||
};
|
||||
use libafl_qemu::{
|
||||
breakpoint::Breakpoint,
|
||||
command::{EndCommand, StartCommand, StdCommandManager},
|
||||
command::{EndCommand, StartCommand},
|
||||
elf::EasyElf,
|
||||
emu::Emulator,
|
||||
executor::QemuExecutor,
|
||||
modules::edges::{
|
||||
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
|
||||
},
|
||||
FastSnapshotManager, GuestPhysAddr, GuestReg, QemuMemoryChunk, StdEmulatorExitHandler,
|
||||
GuestPhysAddr, GuestReg, QemuMemoryChunk,
|
||||
};
|
||||
|
||||
// use libafl_qemu::QemuSnapshotBuilder; // for normal qemu snapshot
|
||||
@ -85,25 +85,14 @@ pub fn fuzz() {
|
||||
println!("Breakpoint address = {breakpoint:#x}");
|
||||
|
||||
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||
// Initialize QEMU
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
|
||||
// Choose Snapshot Builder
|
||||
// let emu_snapshot_manager = QemuSnapshotBuilder::new(true);
|
||||
let emu_snapshot_manager = FastSnapshotManager::new();
|
||||
|
||||
// Choose Exit Handler
|
||||
let emu_exit_handler = StdEmulatorExitHandler::new(emu_snapshot_manager);
|
||||
|
||||
// Choose Command Manager
|
||||
let cmd_manager = StdCommandManager::new();
|
||||
|
||||
// Instantiate hooks
|
||||
let modules = tuple_list!(EdgeCoverageModule::default());
|
||||
|
||||
// Create emulator
|
||||
let emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap();
|
||||
// Initialize QEMU Emulator
|
||||
let emu = Emulator::builder()
|
||||
.qemu_cli(args)
|
||||
.add_module(EdgeCoverageModule::default())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Set breakpoints of interest with corresponding commands.
|
||||
emu.add_breakpoint(
|
||||
@ -113,13 +102,18 @@ pub fn fuzz() {
|
||||
input_addr,
|
||||
unsafe { MAX_INPUT_SIZE } as GuestReg,
|
||||
None,
|
||||
)),
|
||||
))
|
||||
.into(),
|
||||
true,
|
||||
),
|
||||
true,
|
||||
);
|
||||
emu.add_breakpoint(
|
||||
Breakpoint::with_command(breakpoint, EndCommand::new(Some(ExitKind::Ok)), false),
|
||||
Breakpoint::with_command(
|
||||
breakpoint,
|
||||
EndCommand::new(Some(ExitKind::Ok)).into(),
|
||||
false,
|
||||
),
|
||||
true,
|
||||
);
|
||||
|
||||
@ -127,7 +121,7 @@ pub fn fuzz() {
|
||||
println!("Devices = {:?}", devices);
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| unsafe {
|
||||
let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| unsafe {
|
||||
emulator.run(input).unwrap().try_into().unwrap()
|
||||
};
|
||||
|
||||
|
@ -30,14 +30,13 @@ use libafl_bolts::{
|
||||
AsSlice,
|
||||
};
|
||||
use libafl_qemu::{
|
||||
command::NopCommandManager,
|
||||
config,
|
||||
elf::EasyElf,
|
||||
executor::QemuExecutor,
|
||||
modules::edges::{
|
||||
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
|
||||
},
|
||||
qemu_config, Emulator, NopEmulatorExitHandler, Qemu, QemuExitError, QemuExitReason,
|
||||
QemuRWError, QemuShutdownCause, Regs,
|
||||
Emulator, Qemu, QemuExitError, QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
|
||||
};
|
||||
use libafl_qemu_sys::GuestPhysAddr;
|
||||
|
||||
@ -91,14 +90,14 @@ pub fn fuzz() {
|
||||
// Initialize QEMU
|
||||
let qemu = Qemu::builder()
|
||||
.machine("mps2-an385")
|
||||
.monitor(qemu_config::Monitor::Null)
|
||||
.monitor(config::Monitor::Null)
|
||||
.kernel(format!("{target_dir}/example.elf"))
|
||||
.serial(qemu_config::Serial::Null)
|
||||
.serial(config::Serial::Null)
|
||||
.no_graphic(true)
|
||||
.snapshot(true)
|
||||
.drives([qemu_config::Drive::builder()
|
||||
.interface(qemu_config::DriveInterface::None)
|
||||
.format(qemu_config::DiskImageFileFormat::Qcow2)
|
||||
.drives([config::Drive::builder()
|
||||
.interface(config::DriveInterface::None)
|
||||
.format(config::DiskImageFileFormat::Qcow2)
|
||||
.file(format!("{target_dir}/dummy.qcow2"))
|
||||
.build()])
|
||||
.start_cpu(false)
|
||||
@ -107,13 +106,10 @@ pub fn fuzz() {
|
||||
|
||||
let emulator_modules = tuple_list!(EdgeCoverageModule::default());
|
||||
|
||||
let mut emulator = Emulator::new_with_qemu(
|
||||
qemu,
|
||||
emulator_modules,
|
||||
NopEmulatorExitHandler,
|
||||
NopCommandManager,
|
||||
)
|
||||
.unwrap();
|
||||
let emulator = Emulator::empty()
|
||||
.qemu(qemu)
|
||||
.modules(emulator_modules)
|
||||
.build()?;
|
||||
|
||||
qemu.set_breakpoint(main_addr);
|
||||
|
||||
@ -140,7 +136,7 @@ pub fn fuzz() {
|
||||
let snap = qemu.create_fast_snapshot(true);
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
|
||||
let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let mut buf = target.as_slice();
|
||||
let len = buf.len();
|
||||
|
@ -27,13 +27,11 @@ use libafl_bolts::{
|
||||
tuples::tuple_list,
|
||||
};
|
||||
use libafl_qemu::{
|
||||
command::StdCommandManager,
|
||||
emu::Emulator,
|
||||
executor::QemuExecutor,
|
||||
modules::edges::{
|
||||
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
|
||||
},
|
||||
FastSnapshotManager, StdEmulatorExitHandler,
|
||||
};
|
||||
|
||||
// use libafl_qemu::QemuSnapshotBuilder; for normal qemu snapshot
|
||||
@ -54,25 +52,20 @@ pub fn fuzz() {
|
||||
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||
// Initialize QEMU
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
|
||||
// let emu_snapshot_manager = QemuSnapshotBuilder::new(true);
|
||||
let emu_snapshot_manager = FastSnapshotManager::new(); // Create a snapshot manager (normal or fast for now).
|
||||
let emu_exit_handler: StdEmulatorExitHandler<FastSnapshotManager> =
|
||||
StdEmulatorExitHandler::new(emu_snapshot_manager); // Create an exit handler: it is the entity taking the decision of what should be done when QEMU returns.
|
||||
|
||||
let cmd_manager = StdCommandManager::new();
|
||||
|
||||
// Choose modules to use
|
||||
let modules = tuple_list!(EdgeCoverageModule::default());
|
||||
|
||||
let emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap(); // Create the emulator
|
||||
let emu = Emulator::builder()
|
||||
.qemu_cli(args)
|
||||
.modules(modules)
|
||||
.build()?;
|
||||
|
||||
let devices = emu.list_devices();
|
||||
println!("Devices = {:?}", devices);
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| unsafe {
|
||||
let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| unsafe {
|
||||
emulator.run(input).unwrap().try_into().unwrap()
|
||||
};
|
||||
|
||||
|
@ -720,7 +720,7 @@ where
|
||||
where
|
||||
MO: MapObserver + Truncate, // TODO maybe enforce Entry = u8 for the cov map
|
||||
A: Observer<S> + AsRef<MO> + AsMut<MO>,
|
||||
OT: ObserversTuple<S> + Prepend<MO, PreprendResult = OT>,
|
||||
OT: ObserversTuple<S> + Prepend<MO>,
|
||||
S: UsesInput,
|
||||
S::Input: Input + HasTargetBytes,
|
||||
SP: ShMemProvider,
|
||||
|
@ -39,9 +39,7 @@
|
||||
//! fn fuzz_with_qemu(mut options: FuzzerOptions) {
|
||||
//! env::remove_var("LD_LIBRARY_PATH");
|
||||
//!
|
||||
//! let env: Vec<(String, String)> = env::vars().collect();
|
||||
//!
|
||||
//! let qemu = Qemu::init(&mut options.qemu_args.to_vec(), &mut env).unwrap();
|
||||
//! let qemu = Qemu::init(&mut options.qemu_args.to_vec()).unwrap();
|
||||
//! // do other stuff...
|
||||
//! }
|
||||
//!
|
||||
|
@ -657,54 +657,32 @@ where
|
||||
|
||||
/// Allows prepending of values to a tuple
|
||||
pub trait Prepend<T> {
|
||||
/// The Resulting [`TupleList`], of an [`Prepend::prepend()`] call,
|
||||
/// including the prepended entry.
|
||||
type PreprendResult;
|
||||
|
||||
/// Prepend a value to this tuple, returning a new tuple with prepended value.
|
||||
#[must_use]
|
||||
fn prepend(self, value: T) -> (T, Self::PreprendResult);
|
||||
fn prepend(self, value: T) -> (T, Self);
|
||||
}
|
||||
|
||||
/// Implement prepend for tuple list.
|
||||
impl<Tail, T> Prepend<T> for Tail {
|
||||
type PreprendResult = Self;
|
||||
|
||||
fn prepend(self, value: T) -> (T, Self::PreprendResult) {
|
||||
fn prepend(self, value: T) -> (T, Self) {
|
||||
(value, self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Append to a tuple
|
||||
pub trait Append<T> {
|
||||
/// The Resulting [`TupleList`], of an [`Append::append()`] call,
|
||||
/// including the appended entry.
|
||||
type AppendResult;
|
||||
|
||||
pub trait Append<T>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
/// Append Value and return the tuple
|
||||
#[must_use]
|
||||
fn append(self, value: T) -> Self::AppendResult;
|
||||
fn append(self, value: T) -> (Self, T);
|
||||
}
|
||||
|
||||
/// Implement append for an empty tuple list.
|
||||
impl<T> Append<T> for () {
|
||||
type AppendResult = (T, ());
|
||||
|
||||
fn append(self, value: T) -> Self::AppendResult {
|
||||
(value, ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement append for non-empty tuple list.
|
||||
impl<Head, Tail, T> Append<T> for (Head, Tail)
|
||||
where
|
||||
Tail: Append<T>,
|
||||
{
|
||||
type AppendResult = (Head, Tail::AppendResult);
|
||||
|
||||
fn append(self, value: T) -> Self::AppendResult {
|
||||
let (head, tail) = self;
|
||||
(head, tail.append(value))
|
||||
/// Implement append for tuple list.
|
||||
impl<Head, T> Append<T> for Head {
|
||||
fn append(self, value: T) -> (Self, T) {
|
||||
(self, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
../libafl_libfuzzer/runtime/build.rs
|
25
libafl_libfuzzer_runtime/build.rs
Normal file
25
libafl_libfuzzer_runtime/build.rs
Normal 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");
|
||||
}
|
@ -61,6 +61,7 @@ const WRAPPER_HEADER: &str = r#"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
#include "libafl/system.h"
|
||||
#include "libafl/qemu_snapshot.h"
|
||||
#include "libafl/syx-snapshot/device-save.h"
|
||||
#include "libafl/syx-snapshot/syx-snapshot.h"
|
||||
@ -98,9 +99,9 @@ const WRAPPER_HEADER: &str = r#"
|
||||
#include "libafl/hooks/tcg/instruction.h"
|
||||
#include "libafl/hooks/tcg/read_write.h"
|
||||
#include "libafl/hooks/cpu_run.h"
|
||||
#include "libafl/hooks/thread.h"
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#include "libafl/hooks/thread.h"
|
||||
#include "libafl/hooks/syscall.h"
|
||||
#endif
|
||||
|
||||
@ -151,7 +152,6 @@ pub fn generate(
|
||||
.allowlist_type("Syx.*")
|
||||
.allowlist_type("libafl_mapinfo")
|
||||
.allowlist_type("IntervalTreeRoot")
|
||||
.allowlist_function("qemu_user_init")
|
||||
.allowlist_function("qemu_system_debug_request")
|
||||
.allowlist_function("target_mmap")
|
||||
.allowlist_function("target_mprotect")
|
||||
@ -172,6 +172,9 @@ pub fn generate(
|
||||
.allowlist_function("read_self_maps")
|
||||
.allowlist_function("free_self_maps")
|
||||
.allowlist_function("pageflags_get_root")
|
||||
.allowlist_function("vm_start")
|
||||
.allowlist_function("qemu_main_loop")
|
||||
.allowlist_function("qemu_cleanup")
|
||||
.blocklist_function("main_loop_wait") // bindgen issue #1313
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()));
|
||||
|
||||
|
@ -11,7 +11,7 @@ use crate::cargo_add_rpath;
|
||||
|
||||
pub const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||
pub const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||
pub const QEMU_REVISION: &str = "7f468ebba6ec4b74d6ca6f4267f365f551c71fe4";
|
||||
pub const QEMU_REVISION: &str = "ee43af7f80d1117857e58b0b7ef556652d5893d5";
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub struct BuildResult {
|
||||
@ -144,7 +144,6 @@ fn configure_qemu(
|
||||
.arg("--disable-iconv")
|
||||
.arg("--disable-jack")
|
||||
.arg("--disable-keyring")
|
||||
.arg("--disable-kvm")
|
||||
.arg("--disable-libdaxctl")
|
||||
.arg("--disable-libiscsi")
|
||||
.arg("--disable-libnfs")
|
||||
|
@ -51,10 +51,10 @@ mod usermode;
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
pub use usermode::*;
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
mod systemmode;
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
pub use systemmode::*;
|
||||
// #[cfg(emulation_mode = "systemmode")]
|
||||
// mod systemmode;
|
||||
// #[cfg(emulation_mode = "systemmode")]
|
||||
// pub use systemmode::*;
|
||||
|
||||
/// Safe linking with of extern "C" functions.
|
||||
/// This macro makes sure the declared symbol is defined *at link time*, avoiding declaring non-existant symbols
|
||||
|
@ -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();
|
||||
}
|
@ -10,8 +10,6 @@ use strum_macros::EnumIter;
|
||||
use crate::{extern_c_checked, libafl_mapinfo, GuestAddr, MmapPerms};
|
||||
|
||||
extern_c_checked! {
|
||||
pub fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
|
||||
|
||||
pub static exec_path: *const u8;
|
||||
pub static guest_base: usize;
|
||||
pub static mut mmap_next_start: GuestAddr;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* 1.82.0-nightly */
|
||||
/* qemu git hash: 31ee26f97071d5bed1ac1e7de75beea755b198d6 */
|
||||
/* qemu git hash: ec91486cedfd9e636be0063d88bd6e7eaff74915 */
|
||||
/* automatically generated by rust-bindgen 0.69.4 */
|
||||
|
||||
#[repr(C)]
|
||||
@ -13761,6 +13761,9 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn libafl_set_brk(new_brk: u64) -> u64;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn libafl_qemu_init(argc: ::std::os::raw::c_int, argv: *mut *mut ::std::os::raw::c_char);
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct AccelCPUClass {
|
||||
@ -15194,7 +15197,7 @@ extern "C" {
|
||||
pub fn libafl_qemu_remove_new_thread_hook(num: usize) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn libafl_hook_new_thread_run(env: *mut CPUArchState) -> bool;
|
||||
pub fn libafl_hook_new_thread_run(env: *mut CPUArchState, tid: u32) -> bool;
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
|
@ -1,8 +1,7 @@
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
fmt::{Display, Formatter},
|
||||
fmt::{Debug, Display, Formatter},
|
||||
hash::{Hash, Hasher},
|
||||
rc::Rc,
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
OnceLock,
|
||||
@ -12,25 +11,51 @@ use std::{
|
||||
use libafl::inputs::UsesInput;
|
||||
use libafl_qemu_sys::GuestAddr;
|
||||
|
||||
use crate::{command::IsCommand, Qemu};
|
||||
use crate::{command::CommandManager, Qemu};
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct BreakpointId(u64);
|
||||
|
||||
// TODO: distinguish breakpoints with IDs instead of addresses to avoid collisions.
|
||||
#[derive(Debug)]
|
||||
pub struct Breakpoint<CM, EH, ET, S>
|
||||
pub struct Breakpoint<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
id: BreakpointId,
|
||||
addr: GuestAddr,
|
||||
cmd: Option<Rc<dyn IsCommand<CM, EH, ET, S>>>,
|
||||
cmd: Option<CM::Commands>,
|
||||
disable_on_trigger: bool,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl<CM, ED, ET, S, SM> Clone for Breakpoint<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
id: self.id,
|
||||
addr: self.addr,
|
||||
cmd: self.cmd.clone(),
|
||||
disable_on_trigger: self.disable_on_trigger,
|
||||
enabled: self.enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, ED, ET, S, SM> Debug for Breakpoint<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "BP {:?} @ addr {:?}", self.id, self.addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl BreakpointId {
|
||||
pub fn new() -> Self {
|
||||
static mut BREAKPOINT_ID_COUNTER: OnceLock<AtomicU64> = OnceLock::new();
|
||||
@ -47,8 +72,9 @@ impl Default for BreakpointId {
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Hash for Breakpoint<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Hash for Breakpoint<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
@ -56,8 +82,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> PartialEq for Breakpoint<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> PartialEq for Breakpoint<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
@ -65,10 +92,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Eq for Breakpoint<CM, EH, ET, S> where S: UsesInput {}
|
||||
|
||||
impl<CM, EH, ET, S> Display for Breakpoint<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Eq for Breakpoint<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
}
|
||||
|
||||
impl<CM, ED, ET, S, SM> Display for Breakpoint<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
@ -76,8 +109,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Borrow<BreakpointId> for Breakpoint<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Borrow<BreakpointId> for Breakpoint<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn borrow(&self) -> &BreakpointId {
|
||||
@ -85,8 +119,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Borrow<GuestAddr> for Breakpoint<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Borrow<GuestAddr> for Breakpoint<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn borrow(&self) -> &GuestAddr {
|
||||
@ -94,8 +129,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Breakpoint<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Breakpoint<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
// Emu will return with the breakpoint as exit reason.
|
||||
@ -112,15 +148,11 @@ where
|
||||
|
||||
// Emu will execute the command when it meets the breakpoint.
|
||||
#[must_use]
|
||||
pub fn with_command<C: IsCommand<CM, EH, ET, S> + 'static>(
|
||||
addr: GuestAddr,
|
||||
cmd: C,
|
||||
disable_on_trigger: bool,
|
||||
) -> Self {
|
||||
pub fn with_command(addr: GuestAddr, cmd: CM::Commands, disable_on_trigger: bool) -> Self {
|
||||
Self {
|
||||
id: BreakpointId::new(),
|
||||
addr,
|
||||
cmd: Some(Rc::new(cmd)),
|
||||
cmd: Some(cmd),
|
||||
disable_on_trigger,
|
||||
enabled: false,
|
||||
}
|
||||
@ -150,7 +182,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trigger(&mut self, qemu: Qemu) -> Option<Rc<dyn IsCommand<CM, EH, ET, S>>> {
|
||||
pub fn trigger(&mut self, qemu: Qemu) -> Option<CM::Commands> {
|
||||
if self.disable_on_trigger {
|
||||
self.disable(qemu);
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::{
|
||||
fmt::{Debug, Display, Error, Formatter},
|
||||
rc::Rc,
|
||||
fmt,
|
||||
fmt::{Debug, Display, Formatter},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use enum_map::{Enum, EnumMap};
|
||||
use hashbrown::HashMap;
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
use hashbrown::HashSet;
|
||||
use libafl::{
|
||||
@ -12,7 +12,9 @@ use libafl::{
|
||||
inputs::{HasTargetBytes, UsesInput},
|
||||
};
|
||||
use libafl_bolts::AsSlice;
|
||||
use libc::c_uint;
|
||||
use num_enum::TryFromPrimitive;
|
||||
use paste::paste;
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
use crate::modules::QemuInstrumentationPagingFilter;
|
||||
@ -24,11 +26,12 @@ use crate::{
|
||||
},
|
||||
get_exit_arch_regs,
|
||||
modules::{
|
||||
HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter,
|
||||
EmulatorModuleTuple, HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter,
|
||||
StdInstrumentationFilter,
|
||||
},
|
||||
sync_exit::ExitArgs,
|
||||
Emulator, ExitHandlerError, ExitHandlerResult, GuestReg, InputLocation, IsSnapshotManager,
|
||||
Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorExitHandler, CPU,
|
||||
Emulator, EmulatorDriverError, EmulatorDriverResult, GuestReg, InputLocation,
|
||||
IsSnapshotManager, Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorDriver, CPU,
|
||||
};
|
||||
|
||||
pub mod parser;
|
||||
@ -50,111 +53,131 @@ mod bindings {
|
||||
pub const VERSION: u64 = bindings::LIBAFL_QEMU_HDR_VERSION_NUMBER as u64;
|
||||
|
||||
macro_rules! define_std_command_manager {
|
||||
($name:ident, [$($native_command_parser:ident),+]) => {
|
||||
pub struct $name<ET, S, SM>
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
native_command_parsers:
|
||||
HashMap<GuestReg, Box<dyn NativeCommandParser<Self, StdEmulatorExitHandler<SM>, ET, S>>>,
|
||||
}
|
||||
|
||||
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");
|
||||
($name:ident, [$($command:ty),+], [$($native_command_parser:ty),+]) => {
|
||||
paste! {
|
||||
pub struct $name<S> {
|
||||
phantom: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S> Clone for $name<S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
native_command_parsers: parsers,
|
||||
phantom: PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<ET, S, SM> CommandManager<StdEmulatorExitHandler<SM>, ET, S> for $name<ET, S, SM>
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
fn parse(
|
||||
&self,
|
||||
qemu: Qemu,
|
||||
) -> Result<Rc<dyn IsCommand<Self, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
|
||||
let arch_regs_map: &'static EnumMap<ExitArgs, Regs> = get_exit_arch_regs();
|
||||
let cmd_id: GuestReg = qemu.read_reg::<Regs, GuestReg>(arch_regs_map[ExitArgs::Cmd])?;
|
||||
|
||||
let cmd_parser = self
|
||||
.native_command_parsers
|
||||
.get(&cmd_id)
|
||||
.ok_or(CommandError::UnknownCommand(cmd_id))?;
|
||||
let cmd = cmd_parser.parse(qemu, arch_regs_map)?;
|
||||
|
||||
Ok(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl<ET, S, SM> Debug for $name<ET, S, SM>
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||
impl<S> Debug for $name<S> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
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
|
||||
ET: StdInstrumentationFilter + Unpin,
|
||||
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
|
||||
S: UsesInput + Unpin,
|
||||
S::Input: HasTargetBytes,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
type Commands = [<$name Commands>];
|
||||
|
||||
fn parse(&self, qemu: Qemu) -> Result<Self::Commands, CommandError> {
|
||||
let arch_regs_map: &'static EnumMap<ExitArgs, Regs> = get_exit_arch_regs();
|
||||
let cmd_id = qemu.read_reg::<Regs, GuestReg>(arch_regs_map[ExitArgs::Cmd])? as c_uint;
|
||||
|
||||
match cmd_id {
|
||||
// <StartPhysCommandParser as NativeCommandParser<S>>::COMMAND_ID => Ok(StdCommandManagerCommands::StartPhysCommandParserCmd(<StartPhysCommandParser as NativeCommandParser<S>>::parse(qemu, arch_regs_map)?)),
|
||||
$(<$native_command_parser as NativeCommandParser<Self, StdEmulatorDriver, ET, S, SM>>::COMMAND_ID => Ok(<$native_command_parser as NativeCommandParser<Self, StdEmulatorDriver, ET, S, SM>>::parse(qemu, arch_regs_map)?.into())),+,
|
||||
_ => Err(CommandError::UnknownCommand(cmd_id.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum [<$name Commands>]
|
||||
{
|
||||
// StartPhysCommand(StartPhysCommand)
|
||||
$($command($command)),+,
|
||||
}
|
||||
|
||||
impl<ET, S, SM> IsCommand<$name<S>, StdEmulatorDriver, ET, S, SM> for [<$name Commands>]
|
||||
where
|
||||
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
|
||||
S: UsesInput + Unpin,
|
||||
S::Input: HasTargetBytes,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
fn usable_at_runtime(&self) -> bool {
|
||||
match self {
|
||||
$([<$name Commands>]::$command(cmd) => <$command as IsCommand<$name<S>, StdEmulatorDriver, ET, S, SM>>::usable_at_runtime(cmd)),+
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&self,
|
||||
emu: &mut Emulator<$name<S>, StdEmulatorDriver, ET, S, SM>,
|
||||
driver: &mut StdEmulatorDriver,
|
||||
input: &S::Input,
|
||||
ret_reg: Option<Regs>
|
||||
) -> Result<Option<EmulatorDriverResult<$name<S>, StdEmulatorDriver, ET, S, SM>>, EmulatorDriverError> {
|
||||
match self {
|
||||
$([<$name Commands>]::$command(cmd) => cmd.run(emu, driver, input, ret_reg)),+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl From<$command> for [<$name Commands>] {
|
||||
fn from(cmd: $command) -> [<$name Commands>] {
|
||||
[<$name Commands>]::$command(cmd)
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct NopCommandManager;
|
||||
|
||||
impl<EH, ET, S> CommandManager<EH, ET, S> for NopCommandManager
|
||||
pub trait CommandManager<ED, ET, S, SM>: Sized + Debug
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
fn parse(&self, _qemu: Qemu) -> Result<Rc<dyn IsCommand<Self, EH, ET, S>>, CommandError> {
|
||||
Ok(Rc::new(NopCommand))
|
||||
type Commands: IsCommand<Self, ED, ET, S, SM>;
|
||||
|
||||
fn parse(&self, qemu: Qemu) -> Result<Self::Commands, CommandError>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NopCommandManager;
|
||||
impl<ED, ET, S, SM> CommandManager<ED, ET, S, SM> for NopCommandManager
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
type Commands = NopCommand;
|
||||
|
||||
fn parse(&self, _qemu: Qemu) -> Result<Self::Commands, CommandError> {
|
||||
Ok(NopCommand)
|
||||
}
|
||||
}
|
||||
|
||||
define_std_command_manager!(
|
||||
StdCommandManager,
|
||||
[
|
||||
StartCommand,
|
||||
InputCommand,
|
||||
SaveCommand,
|
||||
LoadCommand,
|
||||
EndCommand,
|
||||
VersionCommand,
|
||||
AddressRangeFilterCommand
|
||||
],
|
||||
[
|
||||
StartPhysCommandParser,
|
||||
StartVirtCommandParser,
|
||||
@ -168,13 +191,6 @@ define_std_command_manager!(
|
||||
]
|
||||
);
|
||||
|
||||
pub trait CommandManager<EH, ET, S>: Sized
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
fn parse(&self, qemu: Qemu) -> Result<Rc<dyn IsCommand<Self, EH, ET, S>>, CommandError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Enum, TryFromPrimitive)]
|
||||
#[repr(u64)]
|
||||
pub enum NativeExitKind {
|
||||
@ -183,8 +199,9 @@ pub enum NativeExitKind {
|
||||
Crash = bindings::LibaflQemuEndStatus_LIBAFL_QEMU_END_CRASH.0 as u64, // Crash reported in the VM
|
||||
}
|
||||
|
||||
pub trait IsCommand<CM, EH, ET, S>: Debug + Display
|
||||
pub trait IsCommand<CM, ED, ET, S, SM>: Clone + Debug
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
/// Used to know whether the command can be run during a backdoor, or if it is necessary to go out of
|
||||
@ -196,12 +213,14 @@ where
|
||||
/// - `ret_reg`: The register in which the guest return value should be written, if any.
|
||||
/// Returns
|
||||
/// - `InnerHandlerResult`: How the high-level handler should behave
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn run(
|
||||
&self,
|
||||
emu: &mut Emulator<CM, EH, ET, S>,
|
||||
emu: &mut Emulator<CM, ED, ET, S, SM>,
|
||||
driver: &mut ED,
|
||||
input: &S::Input,
|
||||
ret_reg: Option<Regs>,
|
||||
) -> Result<Option<ExitHandlerResult<CM, EH, ET, S>>, ExitHandlerError>;
|
||||
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError>;
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
@ -226,13 +245,14 @@ impl From<QemuRWError> for CommandError {
|
||||
pub struct NopCommand;
|
||||
|
||||
impl Display for NopCommand {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "NopCommand")
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> IsCommand<CM, EH, ET, S> for NopCommand
|
||||
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for NopCommand
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn usable_at_runtime(&self) -> bool {
|
||||
@ -241,20 +261,21 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_emu: &mut Emulator<CM, EH, ET, S>,
|
||||
_emu: &mut Emulator<CM, ED, ET, S, SM>,
|
||||
_driver: &mut ED,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<Option<ExitHandlerResult<CM, EH, ET, S>>, ExitHandlerError> {
|
||||
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SaveCommand;
|
||||
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for SaveCommand
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorDriver, ET, S, SM> for SaveCommand
|
||||
where
|
||||
ET: StdInstrumentationFilter + Unpin,
|
||||
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
|
||||
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
|
||||
S: UsesInput + Unpin,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
@ -264,21 +285,18 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
|
||||
emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
|
||||
driver: &mut StdEmulatorDriver,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
|
||||
) -> Result<Option<EmulatorDriverResult<CM, StdEmulatorDriver, ET, S, SM>>, EmulatorDriverError>
|
||||
{
|
||||
let qemu = emu.qemu();
|
||||
let snapshot_id = emu.snapshot_manager_mut().save(qemu);
|
||||
|
||||
{
|
||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||
|
||||
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
|
||||
emu_exit_handler
|
||||
driver
|
||||
.set_snapshot_id(snapshot_id)
|
||||
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?;
|
||||
}
|
||||
.map_err(|_| EmulatorDriverError::MultipleSnapshotDefinition)?;
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
{
|
||||
@ -304,8 +322,9 @@ where
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoadCommand;
|
||||
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for LoadCommand
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorDriver, ET, S, SM> for LoadCommand
|
||||
where
|
||||
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
@ -315,26 +334,22 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
|
||||
emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
|
||||
driver: &mut StdEmulatorDriver,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
|
||||
) -> Result<Option<EmulatorDriverResult<CM, StdEmulatorDriver, ET, S, SM>>, EmulatorDriverError>
|
||||
{
|
||||
let qemu = emu.qemu();
|
||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||
|
||||
let snapshot_id = emu_exit_handler
|
||||
let snapshot_id = driver
|
||||
.snapshot_id()
|
||||
.ok_or(ExitHandlerError::SnapshotNotFound)?;
|
||||
.ok_or(EmulatorDriverError::SnapshotNotFound)?;
|
||||
|
||||
emu_exit_handler
|
||||
.snapshot_manager_borrow_mut()
|
||||
.restore(&snapshot_id, qemu)?;
|
||||
emu.snapshot_manager_mut().restore(qemu, &snapshot_id)?;
|
||||
|
||||
#[cfg(feature = "paranoid_debug")]
|
||||
emu_exit_handler
|
||||
.snapshot_manager_borrow()
|
||||
.check(&snapshot_id, emu.qemu())?;
|
||||
emu.snapshot_manager_mut().check(qemu, &snapshot_id)?;
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
@ -346,8 +361,9 @@ pub struct InputCommand {
|
||||
cpu: CPU,
|
||||
}
|
||||
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for InputCommand
|
||||
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for InputCommand
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
S::Input: HasTargetBytes,
|
||||
{
|
||||
@ -357,11 +373,11 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
|
||||
emu: &mut Emulator<CM, ED, ET, S, SM>,
|
||||
_driver: &mut ED,
|
||||
input: &S::Input,
|
||||
ret_reg: Option<Regs>,
|
||||
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
|
||||
{
|
||||
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
|
||||
let qemu = emu.qemu();
|
||||
|
||||
let ret_value = self.location.write(qemu, input.target_bytes().as_slice());
|
||||
@ -379,8 +395,9 @@ pub struct StartCommand {
|
||||
input_location: QemuMemoryChunk,
|
||||
}
|
||||
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for StartCommand
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorDriver, ET, S, SM> for StartCommand
|
||||
where
|
||||
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
S::Input: HasTargetBytes,
|
||||
SM: IsSnapshotManager,
|
||||
@ -391,20 +408,20 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
|
||||
emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
|
||||
driver: &mut StdEmulatorDriver,
|
||||
input: &S::Input,
|
||||
ret_reg: Option<Regs>,
|
||||
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
|
||||
) -> Result<Option<EmulatorDriverResult<CM, StdEmulatorDriver, ET, S, SM>>, EmulatorDriverError>
|
||||
{
|
||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||
let qemu = emu.qemu();
|
||||
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
|
||||
let snapshot_id = emu.snapshot_manager_mut().save(qemu);
|
||||
|
||||
emu_exit_handler
|
||||
driver
|
||||
.set_snapshot_id(snapshot_id)
|
||||
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?;
|
||||
.map_err(|_| EmulatorDriverError::MultipleSnapshotDefinition)?;
|
||||
|
||||
emu_exit_handler
|
||||
driver
|
||||
.set_input_location(InputLocation::new(
|
||||
self.input_location.clone(),
|
||||
qemu.current_cpu().unwrap(),
|
||||
@ -425,10 +442,13 @@ where
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EndCommand(Option<ExitKind>);
|
||||
pub struct EndCommand {
|
||||
exit_kind: Option<ExitKind>,
|
||||
}
|
||||
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for EndCommand
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorDriver, ET, S, SM> for EndCommand
|
||||
where
|
||||
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
@ -438,35 +458,35 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
|
||||
emu: &mut Emulator<CM, StdEmulatorDriver, ET, S, SM>,
|
||||
driver: &mut StdEmulatorDriver,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
|
||||
) -> Result<Option<EmulatorDriverResult<CM, StdEmulatorDriver, ET, S, SM>>, EmulatorDriverError>
|
||||
{
|
||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||
let qemu = emu.qemu();
|
||||
|
||||
let snapshot_id = emu_exit_handler
|
||||
let snapshot_id = driver
|
||||
.snapshot_id()
|
||||
.ok_or(ExitHandlerError::SnapshotNotFound)?;
|
||||
.ok_or(EmulatorDriverError::SnapshotNotFound)?;
|
||||
|
||||
emu_exit_handler
|
||||
.snapshot_manager_borrow_mut()
|
||||
.restore(&snapshot_id, emu.qemu())?;
|
||||
emu.snapshot_manager_mut().restore(qemu, &snapshot_id)?;
|
||||
|
||||
#[cfg(feature = "paranoid_debug")]
|
||||
emu_exit_handler
|
||||
.snapshot_manager_borrow()
|
||||
.check(&snapshot_id, emu.qemu())?;
|
||||
emu.snapshot_manager_mut().check(qemu, &snapshot_id)?;
|
||||
|
||||
Ok(Some(ExitHandlerResult::EndOfRun(self.0.unwrap())))
|
||||
Ok(Some(EmulatorDriverResult::EndOfRun(
|
||||
self.exit_kind.unwrap(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VersionCommand(u64);
|
||||
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for VersionCommand
|
||||
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for VersionCommand
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn usable_at_runtime(&self) -> bool {
|
||||
@ -475,17 +495,17 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
|
||||
_emu: &mut Emulator<CM, ED, ET, S, SM>,
|
||||
_driver: &mut ED,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
|
||||
{
|
||||
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
|
||||
let guest_version = self.0;
|
||||
|
||||
if VERSION == guest_version {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(ExitHandlerError::CommandError(
|
||||
Err(EmulatorDriverError::CommandError(
|
||||
CommandError::VersionDifference(guest_version),
|
||||
))
|
||||
}
|
||||
@ -498,9 +518,10 @@ pub struct FilterCommand<T> {
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for PagingFilterCommand
|
||||
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for PagingFilterCommand
|
||||
where
|
||||
ET: StdInstrumentationFilter + Unpin,
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput + Unpin,
|
||||
{
|
||||
fn usable_at_runtime(&self) -> bool {
|
||||
@ -509,11 +530,11 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
|
||||
emu: &mut Emulator<CM, ED, ET, S, SM>,
|
||||
_driver: &mut ED,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
|
||||
{
|
||||
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
|
||||
let qemu_modules = emu.modules_mut().modules_mut();
|
||||
|
||||
let paging_filter =
|
||||
@ -525,22 +546,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for AddressRangeFilterCommand
|
||||
impl<CM, ED, ET, S, SM> IsCommand<CM, ED, ET, S, SM> for AddressRangeFilterCommand
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn usable_at_runtime(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)] // TODO: refactor with correct type.
|
||||
fn run(
|
||||
&self,
|
||||
_emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
|
||||
_emu: &mut Emulator<CM, ED, ET, S, SM>,
|
||||
_driver: &mut ED,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
|
||||
{
|
||||
) -> Result<Option<EmulatorDriverResult<CM, ED, ET, S, SM>>, EmulatorDriverError> {
|
||||
let qemu_modules = &mut ();
|
||||
|
||||
let addr_range_filter =
|
||||
@ -597,7 +618,7 @@ impl Display for StartCommand {
|
||||
|
||||
impl Display for EndCommand {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Exit of kind {:?}", self.0)
|
||||
write!(f, "Exit of kind {:?}", self.exit_kind)
|
||||
}
|
||||
}
|
||||
|
||||
@ -630,7 +651,7 @@ impl StartCommand {
|
||||
impl EndCommand {
|
||||
#[must_use]
|
||||
pub fn new(exit_kind: Option<ExitKind>) -> Self {
|
||||
Self(exit_kind)
|
||||
Self { exit_kind }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::{rc::Rc, sync::OnceLock};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use enum_map::{enum_map, EnumMap};
|
||||
use libafl::{
|
||||
@ -6,198 +6,202 @@ use libafl::{
|
||||
inputs::{HasTargetBytes, UsesInput},
|
||||
};
|
||||
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr};
|
||||
use libc::c_uint;
|
||||
|
||||
use crate::{
|
||||
command::{
|
||||
bindings, CommandError, EndCommand, FilterCommand, InputCommand, IsCommand, LoadCommand,
|
||||
NativeExitKind, SaveCommand, StartCommand, VersionCommand,
|
||||
bindings, CommandError, CommandManager, EndCommand, FilterCommand, InputCommand, IsCommand,
|
||||
LoadCommand, NativeExitKind, SaveCommand, StartCommand, VersionCommand,
|
||||
},
|
||||
modules::{
|
||||
EmulatorModuleTuple, QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter,
|
||||
},
|
||||
modules::{QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter},
|
||||
sync_exit::ExitArgs,
|
||||
GuestReg, IsSnapshotManager, Qemu, QemuMemoryChunk, Regs, StdEmulatorExitHandler,
|
||||
GuestReg, IsSnapshotManager, Qemu, QemuMemoryChunk, Regs, StdEmulatorDriver,
|
||||
};
|
||||
|
||||
pub static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
|
||||
|
||||
pub trait NativeCommandParser<CM, EH, ET, S>
|
||||
pub trait NativeCommandParser<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn command_id(&self) -> GuestReg;
|
||||
type OutputCommand: IsCommand<CM, ED, ET, S, SM>;
|
||||
|
||||
const COMMAND_ID: c_uint;
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
qemu: Qemu,
|
||||
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Rc<dyn IsCommand<CM, EH, ET, S>>, CommandError>;
|
||||
) -> Result<Self::OutputCommand, CommandError>;
|
||||
}
|
||||
|
||||
pub struct InputPhysCommandParser;
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
|
||||
for InputPhysCommandParser
|
||||
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for InputPhysCommandParser
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
S::Input: HasTargetBytes,
|
||||
{
|
||||
fn command_id(&self) -> GuestReg {
|
||||
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_PHYS.0)
|
||||
}
|
||||
type OutputCommand = InputCommand;
|
||||
|
||||
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_PHYS.0;
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
qemu: Qemu,
|
||||
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
|
||||
) -> Result<Self::OutputCommand, CommandError> {
|
||||
let input_phys_addr: GuestPhysAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Ok(Rc::new(InputCommand::new(
|
||||
Ok(InputCommand::new(
|
||||
QemuMemoryChunk::phys(
|
||||
input_phys_addr,
|
||||
max_input_size,
|
||||
Some(qemu.current_cpu().unwrap()),
|
||||
),
|
||||
qemu.current_cpu().unwrap(),
|
||||
)))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InputVirtCommandParser;
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
|
||||
for InputVirtCommandParser
|
||||
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for InputVirtCommandParser
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
S::Input: HasTargetBytes,
|
||||
{
|
||||
fn command_id(&self) -> GuestReg {
|
||||
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_VIRT.0)
|
||||
}
|
||||
type OutputCommand = InputCommand;
|
||||
|
||||
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_VIRT.0;
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
qemu: Qemu,
|
||||
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
|
||||
) -> Result<Self::OutputCommand, CommandError> {
|
||||
let input_virt_addr: GuestVirtAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Ok(Rc::new(InputCommand::new(
|
||||
Ok(InputCommand::new(
|
||||
QemuMemoryChunk::virt(input_virt_addr, max_input_size, qemu.current_cpu().unwrap()),
|
||||
qemu.current_cpu().unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StartPhysCommandParser;
|
||||
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorDriver, ET, S, SM> for StartPhysCommandParser
|
||||
where
|
||||
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
S::Input: HasTargetBytes,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
type OutputCommand = StartCommand;
|
||||
|
||||
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_PHYS.0;
|
||||
|
||||
fn parse(
|
||||
qemu: Qemu,
|
||||
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Self::OutputCommand, CommandError> {
|
||||
let input_phys_addr: GuestPhysAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Ok(StartCommand::new(QemuMemoryChunk::phys(
|
||||
input_phys_addr,
|
||||
max_input_size,
|
||||
Some(qemu.current_cpu().unwrap()),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StartVirtCommandParser;
|
||||
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorDriver, ET, S, SM> for StartVirtCommandParser
|
||||
where
|
||||
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
S::Input: HasTargetBytes,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
type OutputCommand = StartCommand;
|
||||
|
||||
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_VIRT.0;
|
||||
|
||||
fn parse(
|
||||
qemu: Qemu,
|
||||
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Self::OutputCommand, CommandError> {
|
||||
let input_virt_addr: GuestVirtAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Ok(StartCommand::new(QemuMemoryChunk::virt(
|
||||
input_virt_addr,
|
||||
max_input_size,
|
||||
qemu.current_cpu().unwrap(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StartPhysCommandParser;
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
|
||||
for StartPhysCommandParser
|
||||
where
|
||||
S: UsesInput,
|
||||
S::Input: HasTargetBytes,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
fn command_id(&self) -> GuestReg {
|
||||
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_PHYS.0)
|
||||
}
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
qemu: Qemu,
|
||||
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
|
||||
let input_phys_addr: GuestPhysAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Ok(Rc::new(StartCommand::new(QemuMemoryChunk::phys(
|
||||
input_phys_addr,
|
||||
max_input_size,
|
||||
Some(qemu.current_cpu().unwrap()),
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StartVirtCommandParser;
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
|
||||
for StartVirtCommandParser
|
||||
where
|
||||
S: UsesInput,
|
||||
S::Input: HasTargetBytes,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
fn command_id(&self) -> GuestReg {
|
||||
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_VIRT.0)
|
||||
}
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
qemu: Qemu,
|
||||
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
|
||||
let input_virt_addr: GuestVirtAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Ok(Rc::new(StartCommand::new(QemuMemoryChunk::virt(
|
||||
input_virt_addr,
|
||||
max_input_size,
|
||||
qemu.current_cpu().unwrap(),
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SaveCommandParser;
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> for SaveCommandParser
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorDriver, ET, S, SM> for SaveCommandParser
|
||||
where
|
||||
ET: StdInstrumentationFilter + Unpin,
|
||||
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter,
|
||||
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
|
||||
S: UsesInput + Unpin,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
fn command_id(&self) -> GuestReg {
|
||||
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_SAVE.0)
|
||||
}
|
||||
type OutputCommand = SaveCommand;
|
||||
|
||||
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_SAVE.0;
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
_qemu: Qemu,
|
||||
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
|
||||
Ok(Rc::new(SaveCommand))
|
||||
) -> Result<Self::OutputCommand, CommandError> {
|
||||
Ok(SaveCommand)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LoadCommandParser;
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> for LoadCommandParser
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorDriver, ET, S, SM> for LoadCommandParser
|
||||
where
|
||||
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
fn command_id(&self) -> GuestReg {
|
||||
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_LOAD.0)
|
||||
}
|
||||
type OutputCommand = LoadCommand;
|
||||
|
||||
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_LOAD.0;
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
_qemu: Qemu,
|
||||
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
|
||||
Ok(Rc::new(LoadCommand))
|
||||
) -> Result<Self::OutputCommand, CommandError> {
|
||||
Ok(LoadCommand)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EndCommandParser;
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> for EndCommandParser
|
||||
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorDriver, ET, S, SM> for EndCommandParser
|
||||
where
|
||||
CM: CommandManager<StdEmulatorDriver, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
fn command_id(&self) -> GuestReg {
|
||||
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_END.0)
|
||||
}
|
||||
type OutputCommand = EndCommand;
|
||||
|
||||
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_END.0;
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
qemu: Qemu,
|
||||
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
|
||||
) -> Result<Self::OutputCommand, CommandError> {
|
||||
let native_exit_kind: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let native_exit_kind: Result<NativeExitKind, _> = u64::from(native_exit_kind).try_into();
|
||||
|
||||
@ -211,52 +215,51 @@ where
|
||||
})[k]
|
||||
});
|
||||
|
||||
Ok(Rc::new(EndCommand::new(exit_kind)))
|
||||
Ok(EndCommand::new(exit_kind))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VersionCommandParser;
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
|
||||
for VersionCommandParser
|
||||
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM> for VersionCommandParser
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn command_id(&self) -> GuestReg {
|
||||
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VERSION.0)
|
||||
}
|
||||
type OutputCommand = VersionCommand;
|
||||
|
||||
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VERSION.0;
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
qemu: Qemu,
|
||||
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
|
||||
) -> Result<Self::OutputCommand, CommandError> {
|
||||
let client_version = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
|
||||
Ok(Rc::new(VersionCommand::new(client_version)))
|
||||
Ok(VersionCommand::new(client_version))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VaddrFilterAllowRangeCommandParser;
|
||||
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
|
||||
impl<CM, ED, ET, S, SM> NativeCommandParser<CM, ED, ET, S, SM>
|
||||
for VaddrFilterAllowRangeCommandParser
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn command_id(&self) -> GuestReg {
|
||||
GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VADDR_FILTER_ALLOW.0)
|
||||
}
|
||||
type OutputCommand = FilterCommand<QemuInstrumentationAddressRangeFilter>;
|
||||
|
||||
const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VADDR_FILTER_ALLOW.0;
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
qemu: Qemu,
|
||||
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
|
||||
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
|
||||
) -> Result<Self::OutputCommand, CommandError> {
|
||||
let vaddr_start: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let vaddr_end: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Ok(Rc::new(FilterCommand::new(
|
||||
Ok(FilterCommand::new(
|
||||
#[allow(clippy::single_range_in_vec_init)]
|
||||
QemuInstrumentationAddressRangeFilter::AllowList(vec![vaddr_start..vaddr_end]),
|
||||
)))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
237
libafl_qemu/src/emu/builder.rs
Normal file
237
libafl_qemu/src/emu/builder.rs
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
194
libafl_qemu/src/emu/drivers.rs
Normal file
194
libafl_qemu/src/emu/drivers.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
@ -9,34 +9,35 @@ use libafl_qemu_sys::{CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, GuestUsiz
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
use crate::qemu::{
|
||||
closure_new_thread_hook_wrapper, closure_post_syscall_hook_wrapper,
|
||||
closure_pre_syscall_hook_wrapper, func_new_thread_hook_wrapper, func_post_syscall_hook_wrapper,
|
||||
func_pre_syscall_hook_wrapper, NewThreadHook, NewThreadHookId, PostSyscallHook,
|
||||
closure_post_syscall_hook_wrapper, closure_pre_syscall_hook_wrapper,
|
||||
func_post_syscall_hook_wrapper, func_pre_syscall_hook_wrapper, PostSyscallHook,
|
||||
PostSyscallHookId, PreSyscallHook, PreSyscallHookId, SyscallHookResult,
|
||||
};
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
use crate::qemu::{
|
||||
CrashHookClosure, NewThreadHookClosure, PostSyscallHookClosure, PostSyscallHookFn,
|
||||
PreSyscallHookClosure, PreSyscallHookFn,
|
||||
CrashHookClosure, PostSyscallHookClosure, PostSyscallHookFn, PreSyscallHookClosure,
|
||||
PreSyscallHookFn,
|
||||
};
|
||||
use crate::{
|
||||
cpu_run_post_exec_hook_wrapper, cpu_run_pre_exec_hook_wrapper,
|
||||
modules::{EmulatorModule, EmulatorModuleTuple},
|
||||
qemu::{
|
||||
block_0_exec_hook_wrapper, block_gen_hook_wrapper, block_post_gen_hook_wrapper,
|
||||
closure_backdoor_hook_wrapper, closure_instruction_hook_wrapper, cmp_0_exec_hook_wrapper,
|
||||
cmp_1_exec_hook_wrapper, cmp_2_exec_hook_wrapper, cmp_3_exec_hook_wrapper,
|
||||
cmp_gen_hook_wrapper, edge_0_exec_hook_wrapper, edge_gen_hook_wrapper,
|
||||
func_backdoor_hook_wrapper, func_instruction_hook_wrapper, read_0_exec_hook_wrapper,
|
||||
closure_backdoor_hook_wrapper, closure_instruction_hook_wrapper,
|
||||
closure_new_thread_hook_wrapper, cmp_0_exec_hook_wrapper, cmp_1_exec_hook_wrapper,
|
||||
cmp_2_exec_hook_wrapper, cmp_3_exec_hook_wrapper, cmp_gen_hook_wrapper,
|
||||
edge_0_exec_hook_wrapper, edge_gen_hook_wrapper, func_backdoor_hook_wrapper,
|
||||
func_instruction_hook_wrapper, func_new_thread_hook_wrapper, read_0_exec_hook_wrapper,
|
||||
read_1_exec_hook_wrapper, read_2_exec_hook_wrapper, read_3_exec_hook_wrapper,
|
||||
read_4_exec_hook_wrapper, read_gen_hook_wrapper, write_0_exec_hook_wrapper,
|
||||
write_1_exec_hook_wrapper, write_2_exec_hook_wrapper, write_3_exec_hook_wrapper,
|
||||
write_4_exec_hook_wrapper, write_gen_hook_wrapper, BackdoorHook, BackdoorHookClosure,
|
||||
BackdoorHookFn, BackdoorHookId, BlockExecHook, BlockGenHook, BlockHookId, BlockPostGenHook,
|
||||
CmpExecHook, CmpGenHook, CmpHookId, EdgeExecHook, EdgeGenHook, EdgeHookId, Hook, HookRepr,
|
||||
InstructionHook, InstructionHookClosure, InstructionHookFn, InstructionHookId, QemuHooks,
|
||||
ReadExecHook, ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook,
|
||||
WriteExecNHook, WriteGenHook, WriteHookId,
|
||||
InstructionHook, InstructionHookClosure, InstructionHookFn, InstructionHookId,
|
||||
NewThreadHook, NewThreadHookClosure, NewThreadHookId, QemuHooks, ReadExecHook,
|
||||
ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook, WriteExecNHook,
|
||||
WriteGenHook, WriteHookId,
|
||||
},
|
||||
CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, Qemu,
|
||||
};
|
||||
@ -114,7 +115,6 @@ where
|
||||
S: UsesInput,
|
||||
{
|
||||
qemu_hooks: QemuHooks,
|
||||
phantom: PhantomData<(ET, S)>,
|
||||
|
||||
instruction_hooks: Vec<Pin<Box<(InstructionHookId, FatPtr)>>>,
|
||||
backdoor_hooks: Vec<Pin<Box<(BackdoorHookId, FatPtr)>>>,
|
||||
@ -126,17 +126,18 @@ where
|
||||
|
||||
cpu_run_hooks: Vec<Pin<Box<HookState<CpuRunHookId>>>>,
|
||||
|
||||
new_thread_hooks: Vec<Pin<Box<(NewThreadHookId, FatPtr)>>>,
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
pre_syscall_hooks: Vec<Pin<Box<(PreSyscallHookId, FatPtr)>>>,
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
post_syscall_hooks: Vec<Pin<Box<(PostSyscallHookId, FatPtr)>>>,
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
new_thread_hooks: Vec<Pin<Box<(NewThreadHookId, FatPtr)>>>,
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
crash_hooks: Vec<HookRepr>,
|
||||
|
||||
phantom: PhantomData<(ET, S)>,
|
||||
}
|
||||
|
||||
impl<ET, S> EmulatorHooks<ET, S>
|
||||
@ -158,15 +159,14 @@ where
|
||||
|
||||
cpu_run_hooks: Vec::new(),
|
||||
|
||||
new_thread_hooks: Vec::new(),
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
pre_syscall_hooks: Vec::new(),
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
post_syscall_hooks: Vec::new(),
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
new_thread_hooks: Vec::new(),
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
crash_hooks: Vec::new(),
|
||||
}
|
||||
@ -700,6 +700,63 @@ where
|
||||
Hook::Empty => None, // TODO error type
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread_creation(&mut self, hook: NewThreadHook<ET, S>) -> Option<NewThreadHookId> {
|
||||
match hook {
|
||||
Hook::Function(f) => Some(self.thread_creation_function(f)),
|
||||
Hook::Closure(c) => Some(self.thread_creation_closure(c)),
|
||||
Hook::Raw(r) => {
|
||||
let z: *const () = ptr::null::<()>();
|
||||
Some(self.qemu_hooks.add_new_thread_hook(z, r))
|
||||
}
|
||||
Hook::Empty => None, // TODO error type
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread_creation_function(
|
||||
&mut self,
|
||||
hook: fn(
|
||||
&mut EmulatorModules<ET, S>,
|
||||
Option<&mut S>,
|
||||
env: CPUArchStatePtr,
|
||||
tid: u32,
|
||||
) -> bool,
|
||||
) -> NewThreadHookId {
|
||||
unsafe {
|
||||
self.qemu_hooks
|
||||
.add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::<ET, S>)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread_creation_closure(
|
||||
&mut self,
|
||||
hook: NewThreadHookClosure<ET, S>,
|
||||
) -> NewThreadHookId {
|
||||
unsafe {
|
||||
let fat: FatPtr = transmute(hook);
|
||||
self.new_thread_hooks
|
||||
.push(Box::pin((NewThreadHookId::invalid(), fat)));
|
||||
|
||||
let hook_state = &mut self
|
||||
.new_thread_hooks
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.get_unchecked_mut()
|
||||
.1 as *mut FatPtr;
|
||||
|
||||
let id = self
|
||||
.qemu_hooks
|
||||
.add_new_thread_hook(&mut *hook_state, closure_new_thread_hook_wrapper::<ET, S>);
|
||||
self.new_thread_hooks
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.get_unchecked_mut()
|
||||
.0 = id;
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
@ -811,62 +868,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread_creation(&mut self, hook: NewThreadHook<ET, S>) -> Option<NewThreadHookId> {
|
||||
match hook {
|
||||
Hook::Function(f) => Some(self.thread_creation_function(f)),
|
||||
Hook::Closure(c) => Some(self.thread_creation_closure(c)),
|
||||
Hook::Raw(r) => {
|
||||
let z: *const () = ptr::null::<()>();
|
||||
Some(self.qemu_hooks.add_new_thread_hook(z, r))
|
||||
}
|
||||
Hook::Empty => None, // TODO error type
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread_creation_function(
|
||||
&mut self,
|
||||
hook: fn(
|
||||
&mut EmulatorModules<ET, S>,
|
||||
Option<&mut S>,
|
||||
env: CPUArchStatePtr,
|
||||
tid: u32,
|
||||
) -> bool,
|
||||
) -> NewThreadHookId {
|
||||
unsafe {
|
||||
self.qemu_hooks
|
||||
.add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::<ET, S>)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread_creation_closure(
|
||||
&mut self,
|
||||
hook: NewThreadHookClosure<ET, S>,
|
||||
) -> NewThreadHookId {
|
||||
unsafe {
|
||||
let fat: FatPtr = transmute(hook);
|
||||
self.new_thread_hooks
|
||||
.push(Box::pin((NewThreadHookId::invalid(), fat)));
|
||||
|
||||
let hook_state = &mut self
|
||||
.new_thread_hooks
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.get_unchecked_mut()
|
||||
.1 as *mut FatPtr;
|
||||
|
||||
let id = self
|
||||
.qemu_hooks
|
||||
.add_new_thread_hook(&mut *hook_state, closure_new_thread_hook_wrapper::<ET, S>);
|
||||
self.new_thread_hooks
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.get_unchecked_mut()
|
||||
.0 = id;
|
||||
id
|
||||
}
|
||||
}
|
||||
pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules<ET, S>, target_signal: i32)) {
|
||||
self.qemu_hooks.set_crash_hook(crash_hook_wrapper::<ET, S>);
|
||||
self.crash_hooks
|
||||
@ -1054,6 +1055,29 @@ where
|
||||
pub fn backdoor_closure(&mut self, hook: BackdoorHookClosure<ET, S>) -> BackdoorHookId {
|
||||
self.hooks.backdoor_closure(hook)
|
||||
}
|
||||
|
||||
pub fn thread_creation(&mut self, hook: NewThreadHook<ET, S>) -> Option<NewThreadHookId> {
|
||||
self.hooks.thread_creation(hook)
|
||||
}
|
||||
|
||||
pub fn thread_creation_function(
|
||||
&mut self,
|
||||
hook: fn(
|
||||
&mut EmulatorModules<ET, S>,
|
||||
Option<&mut S>,
|
||||
env: CPUArchStatePtr,
|
||||
tid: u32,
|
||||
) -> bool,
|
||||
) -> NewThreadHookId {
|
||||
self.hooks.thread_creation_function(hook)
|
||||
}
|
||||
|
||||
pub fn thread_creation_closure(
|
||||
&mut self,
|
||||
hook: NewThreadHookClosure<ET, S>,
|
||||
) -> NewThreadHookId {
|
||||
self.hooks.thread_creation_closure(hook)
|
||||
}
|
||||
}
|
||||
|
||||
impl<ET, S> EmulatorModules<ET, S>
|
||||
@ -1266,28 +1290,6 @@ where
|
||||
self.hooks.after_syscalls_closure(hook)
|
||||
}
|
||||
|
||||
pub fn thread_creation(&mut self, hook: NewThreadHook<ET, S>) -> Option<NewThreadHookId> {
|
||||
self.hooks.thread_creation(hook)
|
||||
}
|
||||
|
||||
pub fn thread_creation_function(
|
||||
&mut self,
|
||||
hook: fn(
|
||||
&mut EmulatorModules<ET, S>,
|
||||
Option<&mut S>,
|
||||
env: CPUArchStatePtr,
|
||||
tid: u32,
|
||||
) -> bool,
|
||||
) -> NewThreadHookId {
|
||||
self.hooks.thread_creation_function(hook)
|
||||
}
|
||||
|
||||
pub fn thread_creation_closure(
|
||||
&mut self,
|
||||
hook: NewThreadHookClosure<ET, S>,
|
||||
) -> NewThreadHookId {
|
||||
self.hooks.thread_creation_closure(hook)
|
||||
}
|
||||
pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules<ET, S>, target_signal: i32)) {
|
||||
self.hooks.crash_function(hook);
|
||||
}
|
||||
|
@ -2,110 +2,102 @@
|
||||
//!
|
||||
//! [`Emulator`] is built above [`Qemu`] and provides convenient abstractions.
|
||||
|
||||
use core::{
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
marker::PhantomData,
|
||||
};
|
||||
use std::{
|
||||
cell::{OnceCell, Ref, RefCell, RefMut},
|
||||
hash::Hash,
|
||||
ops::Add,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
};
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
use std::{cell::RefCell, ops::Add, pin::Pin};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use libafl::{
|
||||
executors::ExitKind,
|
||||
inputs::{HasTargetBytes, UsesInput},
|
||||
observers::ObserversTuple,
|
||||
state::{HasExecutions, State},
|
||||
};
|
||||
use libafl_bolts::os::unix_signals::Signal;
|
||||
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestUsize, GuestVirtAddr};
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
use crate::{
|
||||
breakpoint::{Breakpoint, BreakpointId},
|
||||
command::{CommandError, CommandManager, InputCommand, IsCommand},
|
||||
modules::EmulatorModuleTuple,
|
||||
command::{CommandError, CommandManager, NopCommandManager, StdCommandManager},
|
||||
modules::{EmulatorModuleTuple, StdInstrumentationFilter},
|
||||
sync_exit::SyncExit,
|
||||
Qemu, QemuExitError, QemuExitReason, QemuInitError, QemuMemoryChunk, QemuShutdownCause,
|
||||
QemuSnapshotCheckResult, Regs, CPU,
|
||||
Qemu, QemuExitError, QemuExitReason, QemuInitError, QemuMemoryChunk, QemuShutdownCause, Regs,
|
||||
CPU,
|
||||
};
|
||||
|
||||
mod hooks;
|
||||
pub use hooks::*;
|
||||
|
||||
mod builder;
|
||||
pub use builder::*;
|
||||
|
||||
mod drivers;
|
||||
pub use drivers::*;
|
||||
|
||||
mod snapshot;
|
||||
pub use snapshot::*;
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
mod usermode;
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
pub use usermode::*;
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
mod systemmode;
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
pub use systemmode::*;
|
||||
|
||||
type CommandRef<CM, E, ET, S> = Rc<dyn IsCommand<CM, E, ET, S>>;
|
||||
type BreakpointMutRef<CM, E, ET, S> = Rc<RefCell<Breakpoint<CM, E, ET, S>>>;
|
||||
|
||||
pub trait IsSnapshotManager: Clone + Debug {
|
||||
fn save(&mut self, qemu: Qemu) -> SnapshotId;
|
||||
fn restore(&mut self, snapshot_id: &SnapshotId, qemu: Qemu)
|
||||
-> Result<(), SnapshotManagerError>;
|
||||
fn do_check(
|
||||
&self,
|
||||
reference_snapshot_id: &SnapshotId,
|
||||
qemu: Qemu,
|
||||
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError>;
|
||||
|
||||
fn check(
|
||||
&self,
|
||||
reference_snapshot_id: &SnapshotId,
|
||||
qemu: Qemu,
|
||||
) -> Result<(), SnapshotManagerCheckError> {
|
||||
let check_result = self
|
||||
.do_check(reference_snapshot_id, qemu)
|
||||
.map_err(SnapshotManagerCheckError::SnapshotManagerError)?;
|
||||
|
||||
if check_result == QemuSnapshotCheckResult::default() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SnapshotManagerCheckError::SnapshotCheckError(check_result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EmulatorExitHandler<ET, S>: Sized + Debug + Clone
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
fn qemu_pre_exec<CM: CommandManager<Self, ET, S>>(
|
||||
emu: &mut Emulator<CM, Self, ET, S>,
|
||||
input: &S::Input,
|
||||
);
|
||||
|
||||
fn qemu_post_exec<CM: CommandManager<Self, ET, S>>(
|
||||
emu: &mut Emulator<CM, Self, ET, S>,
|
||||
exit_reason: Result<EmulatorExitResult<CM, Self, ET, S>, EmulatorExitError>,
|
||||
input: &S::Input,
|
||||
) -> Result<Option<ExitHandlerResult<CM, Self, ET, S>>, ExitHandlerError>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum GuestAddrKind {
|
||||
Physical(GuestPhysAddr),
|
||||
Virtual(GuestVirtAddr),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EmulatorExitResult<CM, EH, ET, S>
|
||||
pub enum EmulatorExitResult<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
QemuExit(QemuShutdownCause), // QEMU ended for some reason.
|
||||
Breakpoint(Rc<RefCell<Breakpoint<CM, EH, ET, S>>>), // Breakpoint triggered. Contains the address of the trigger.
|
||||
SyncExit(Rc<RefCell<SyncExit<CM, EH, ET, S>>>), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL.
|
||||
Breakpoint(Breakpoint<CM, ED, ET, S, SM>), // Breakpoint triggered. Contains the address of the trigger.
|
||||
SyncExit(SyncExit<CM, ED, ET, S, SM>), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL.
|
||||
}
|
||||
|
||||
impl<CM, ED, ET, S, SM> Clone for EmulatorExitResult<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
EmulatorExitResult::QemuExit(qemu_exit) => {
|
||||
EmulatorExitResult::QemuExit(qemu_exit.clone())
|
||||
}
|
||||
EmulatorExitResult::Breakpoint(bp) => EmulatorExitResult::Breakpoint(bp.clone()),
|
||||
EmulatorExitResult::SyncExit(sync_exit) => {
|
||||
EmulatorExitResult::SyncExit(sync_exit.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, ED, ET, S, SM> Debug for EmulatorExitResult<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
EmulatorExitResult::QemuExit(qemu_exit) => {
|
||||
write!(f, "{qemu_exit:?}")
|
||||
}
|
||||
EmulatorExitResult::Breakpoint(bp) => {
|
||||
write!(f, "{bp:?}")
|
||||
}
|
||||
EmulatorExitResult::SyncExit(sync_exit) => {
|
||||
write!(f, "{sync_exit:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EmulatorExitError {
|
||||
UnknownKind,
|
||||
@ -114,71 +106,6 @@ pub enum EmulatorExitError {
|
||||
BreakpointNotFound(GuestAddr),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ExitHandlerError {
|
||||
QemuExitReasonError(EmulatorExitError),
|
||||
SMError(SnapshotManagerError),
|
||||
SMCheckError(SnapshotManagerCheckError),
|
||||
CommandError(CommandError),
|
||||
UnhandledSignal(Signal),
|
||||
MultipleSnapshotDefinition,
|
||||
MultipleInputDefinition,
|
||||
SnapshotNotFound,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ExitHandlerResult<CM, EH, ET, S>
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
ReturnToHarness(EmulatorExitResult<CM, EH, ET, S>), // Return to the harness immediately. Can happen at any point of the run when the handler is not supposed to handle a request.
|
||||
EndOfRun(ExitKind), // The run is over and the emulator is ready for the next iteration.
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SnapshotManagerError {
|
||||
SnapshotIdNotFound(SnapshotId),
|
||||
MemoryInconsistencies(u64),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SnapshotManagerCheckError {
|
||||
SnapshotManagerError(SnapshotManagerError),
|
||||
SnapshotCheckError(QemuSnapshotCheckResult),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub struct SnapshotId {
|
||||
id: u64,
|
||||
}
|
||||
|
||||
/// Special kind of Exit handler with no data embedded.
|
||||
/// As a result, it is safe to transmute from any `Emulator` implementing `EmuExitHandler` to this one,
|
||||
/// since it won't use any data which could cause type confusion.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NopEmulatorExitHandler;
|
||||
impl<ET, S> EmulatorExitHandler<ET, S> for NopEmulatorExitHandler
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
fn qemu_pre_exec<CM: CommandManager<Self, ET, S>>(
|
||||
_: &mut Emulator<CM, Self, ET, S>,
|
||||
_: &S::Input,
|
||||
) {
|
||||
}
|
||||
|
||||
fn qemu_post_exec<CM: CommandManager<Self, ET, S>>(
|
||||
_: &mut Emulator<CM, Self, ET, S>,
|
||||
exit_reason: Result<EmulatorExitResult<CM, Self, ET, S>, EmulatorExitError>,
|
||||
_: &S::Input,
|
||||
) -> Result<Option<ExitHandlerResult<CM, Self, ET, S>>, ExitHandlerError> {
|
||||
match exit_reason {
|
||||
Ok(reason) => Ok(Some(ExitHandlerResult::ReturnToHarness(reason))),
|
||||
Err(error) => Err(error)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InputLocation {
|
||||
mem_chunk: QemuMemoryChunk,
|
||||
@ -186,69 +113,38 @@ pub struct InputLocation {
|
||||
ret_register: Option<Regs>,
|
||||
}
|
||||
|
||||
/// Synchronous Exit handler maintaining only one snapshot.
|
||||
#[derive(Debug, Clone, TypedBuilder)]
|
||||
pub struct StdEmulatorExitHandler<SM> {
|
||||
snapshot_manager: RefCell<SM>,
|
||||
#[builder(default)]
|
||||
snapshot_id: OnceCell<SnapshotId>,
|
||||
#[builder(default)]
|
||||
input_location: OnceCell<InputLocation>,
|
||||
}
|
||||
|
||||
// TODO: Replace TypedBuilder by something better, it does not work correctly with default and
|
||||
// inter-dependent fields.
|
||||
#[derive(Debug, TypedBuilder)]
|
||||
pub struct Emulator<CM, EH, ET, S>
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct Emulator<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
snapshot_manager: SM,
|
||||
modules: Pin<Box<EmulatorModules<ET, S>>>,
|
||||
command_manager: CM,
|
||||
exit_handler: RefCell<EH>,
|
||||
#[builder(default)]
|
||||
breakpoints_by_addr: RefCell<HashMap<GuestAddr, BreakpointMutRef<CM, EH, ET, S>>>,
|
||||
#[builder(default)]
|
||||
breakpoints_by_id: RefCell<HashMap<BreakpointId, BreakpointMutRef<CM, EH, ET, S>>>,
|
||||
#[builder(setter(transform = |args: &[String], env: &[(String, String)]| Qemu::init(args, env).unwrap()))]
|
||||
driver: Option<ED>,
|
||||
breakpoints_by_addr: RefCell<HashMap<GuestAddr, Breakpoint<CM, ED, ET, S, SM>>>, // TODO: change to RC here
|
||||
breakpoints_by_id: RefCell<HashMap<BreakpointId, Breakpoint<CM, ED, ET, S, SM>>>,
|
||||
qemu: Qemu,
|
||||
first_exec: bool,
|
||||
_phantom: PhantomData<(ET, S)>,
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> ExitHandlerResult<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> EmulatorDriverResult<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
#[must_use]
|
||||
#[allow(clippy::match_wildcard_for_single_variants)]
|
||||
pub fn end_of_run(&self) -> Option<ExitKind> {
|
||||
match self {
|
||||
ExitHandlerResult::EndOfRun(exit_kind) => Some(*exit_kind),
|
||||
EmulatorDriverResult::EndOfRun(exit_kind) => Some(*exit_kind),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> TryFrom<ExitHandlerResult<CM, EH, ET, S>> for ExitKind
|
||||
where
|
||||
CM: Debug,
|
||||
EH: Debug,
|
||||
ET: Debug,
|
||||
S: UsesInput + Debug,
|
||||
{
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: ExitHandlerResult<CM, EH, ET, S>) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
ExitHandlerResult::ReturnToHarness(unhandled_qemu_exit) => {
|
||||
Err(format!("Unhandled QEMU exit: {:?}", &unhandled_qemu_exit))
|
||||
}
|
||||
ExitHandlerResult::EndOfRun(exit_kind) => Ok(exit_kind),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for GuestAddrKind {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@ -270,7 +166,7 @@ impl Add<GuestUsize> for GuestAddrKind {
|
||||
}
|
||||
|
||||
impl Display for GuestAddrKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
GuestAddrKind::Physical(phys_addr) => write!(f, "hwaddr 0x{phys_addr:x}"),
|
||||
GuestAddrKind::Virtual(virt_addr) => write!(f, "vaddr 0x{virt_addr:x}"),
|
||||
@ -278,15 +174,15 @@ impl Display for GuestAddrKind {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SnapshotManagerError> for ExitHandlerError {
|
||||
impl From<SnapshotManagerError> for EmulatorDriverError {
|
||||
fn from(sm_error: SnapshotManagerError) -> Self {
|
||||
ExitHandlerError::SMError(sm_error)
|
||||
EmulatorDriverError::SMError(sm_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SnapshotManagerCheckError> for ExitHandlerError {
|
||||
impl From<SnapshotManagerCheckError> for EmulatorDriverError {
|
||||
fn from(sm_check_error: SnapshotManagerCheckError) -> Self {
|
||||
ExitHandlerError::SMCheckError(sm_check_error)
|
||||
EmulatorDriverError::SMCheckError(sm_check_error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,140 +197,29 @@ impl InputLocation {
|
||||
}
|
||||
}
|
||||
|
||||
impl<SM> StdEmulatorExitHandler<SM> {
|
||||
pub fn new(snapshot_manager: SM) -> Self {
|
||||
Self {
|
||||
snapshot_manager: RefCell::new(snapshot_manager),
|
||||
snapshot_id: OnceCell::new(),
|
||||
input_location: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_input_location(&self, input_location: InputLocation) -> Result<(), InputLocation> {
|
||||
self.input_location.set(input_location)
|
||||
}
|
||||
|
||||
pub fn set_snapshot_id(&self, snapshot_id: SnapshotId) -> Result<(), SnapshotId> {
|
||||
self.snapshot_id.set(snapshot_id)
|
||||
}
|
||||
|
||||
pub fn snapshot_id(&self) -> Option<SnapshotId> {
|
||||
Some(*self.snapshot_id.get()?)
|
||||
}
|
||||
|
||||
pub fn snapshot_manager_borrow(&self) -> Ref<SM> {
|
||||
self.snapshot_manager.borrow()
|
||||
}
|
||||
|
||||
pub fn snapshot_manager_borrow_mut(&self) -> RefMut<SM> {
|
||||
self.snapshot_manager.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace handlers with generics to permit compile-time customization of handlers
|
||||
impl<ET, S, SM> EmulatorExitHandler<ET, S> for StdEmulatorExitHandler<SM>
|
||||
where
|
||||
S: UsesInput,
|
||||
S::Input: HasTargetBytes,
|
||||
SM: IsSnapshotManager,
|
||||
{
|
||||
fn qemu_pre_exec<CM: CommandManager<Self, ET, S>>(
|
||||
emu: &mut Emulator<CM, Self, ET, S>,
|
||||
input: &S::Input,
|
||||
) {
|
||||
let input_location = {
|
||||
let exit_handler = emu.exit_handler.borrow();
|
||||
exit_handler.input_location.get().cloned()
|
||||
};
|
||||
|
||||
if let Some(input_location) = input_location {
|
||||
let input_command =
|
||||
InputCommand::new(input_location.mem_chunk.clone(), input_location.cpu);
|
||||
|
||||
input_command
|
||||
.run(emu, input, input_location.ret_register)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn qemu_post_exec<CM: CommandManager<Self, ET, S>>(
|
||||
emu: &mut Emulator<CM, Self, ET, S>,
|
||||
exit_reason: Result<EmulatorExitResult<CM, Self, ET, S>, EmulatorExitError>,
|
||||
input: &S::Input,
|
||||
) -> Result<Option<ExitHandlerResult<CM, Self, ET, S>>, ExitHandlerError> {
|
||||
let exit_handler = emu.exit_handler().borrow_mut();
|
||||
let qemu = emu.qemu();
|
||||
|
||||
let mut exit_reason = match exit_reason {
|
||||
Ok(exit_reason) => exit_reason,
|
||||
Err(exit_error) => match exit_error {
|
||||
EmulatorExitError::UnexpectedExit => {
|
||||
if let Some(snapshot_id) = exit_handler.snapshot_id.get() {
|
||||
exit_handler
|
||||
.snapshot_manager
|
||||
.borrow_mut()
|
||||
.restore(snapshot_id, qemu)?;
|
||||
}
|
||||
return Ok(Some(ExitHandlerResult::EndOfRun(ExitKind::Crash)));
|
||||
}
|
||||
_ => Err(exit_error)?,
|
||||
},
|
||||
};
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
let (command, ret_reg): (Option<CommandRef<CM, Self, ET, S>>, Option<Regs>) =
|
||||
match &mut exit_reason {
|
||||
EmulatorExitResult::QemuExit(shutdown_cause) => match shutdown_cause {
|
||||
QemuShutdownCause::HostSignal(signal) => {
|
||||
signal.handle();
|
||||
return Err(ExitHandlerError::UnhandledSignal(*signal));
|
||||
}
|
||||
QemuShutdownCause::GuestPanic => {
|
||||
return Ok(Some(ExitHandlerResult::EndOfRun(ExitKind::Crash)))
|
||||
}
|
||||
_ => panic!("Unhandled QEMU shutdown cause: {shutdown_cause:?}."),
|
||||
},
|
||||
EmulatorExitResult::Breakpoint(bp) => (bp.borrow_mut().trigger(qemu), None),
|
||||
EmulatorExitResult::SyncExit(sync_backdoor) => {
|
||||
let sync_backdoor = sync_backdoor.borrow();
|
||||
let command = sync_backdoor.command();
|
||||
(Some(command), Some(sync_backdoor.ret_reg()))
|
||||
}
|
||||
};
|
||||
|
||||
// manually drop ref cell here to avoid keeping it alive in cmd.
|
||||
drop(exit_handler);
|
||||
|
||||
if let Some(cmd) = command {
|
||||
cmd.run(emu, input, ret_reg)
|
||||
} else {
|
||||
Ok(Some(ExitHandlerResult::ReturnToHarness(exit_reason)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EmulatorExitError> for ExitHandlerError {
|
||||
impl From<EmulatorExitError> for EmulatorDriverError {
|
||||
fn from(error: EmulatorExitError) -> Self {
|
||||
ExitHandlerError::QemuExitReasonError(error)
|
||||
EmulatorDriverError::QemuExitReasonError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommandError> for ExitHandlerError {
|
||||
impl From<CommandError> for EmulatorDriverError {
|
||||
fn from(error: CommandError) -> Self {
|
||||
ExitHandlerError::CommandError(error)
|
||||
EmulatorDriverError::CommandError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Display for EmulatorExitResult<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Display for EmulatorExitResult<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
EmulatorExitResult::QemuExit(shutdown_cause) => write!(f, "End: {shutdown_cause:?}"),
|
||||
EmulatorExitResult::Breakpoint(bp) => write!(f, "{}", bp.borrow()),
|
||||
EmulatorExitResult::Breakpoint(bp) => write!(f, "{bp}"),
|
||||
EmulatorExitResult::SyncExit(sync_exit) => {
|
||||
write!(f, "Sync exit: {}", sync_exit.borrow())
|
||||
write!(f, "Sync exit: {sync_exit:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -446,8 +231,67 @@ impl From<CommandError> for EmulatorExitError {
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
|
||||
impl<S> Emulator<NopCommandManager, NopEmulatorDriver, (), S, NopSnapshotManager>
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
#[must_use]
|
||||
pub fn empty(
|
||||
) -> EmulatorBuilder<NopCommandManager, NopEmulatorDriver, (), S, NopSnapshotManager> {
|
||||
EmulatorBuilder::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Emulator<StdCommandManager<S>, StdEmulatorDriver, (), S, StdSnapshotManager>
|
||||
where
|
||||
S: State + HasExecutions + Unpin,
|
||||
S::Input: HasTargetBytes,
|
||||
{
|
||||
#[must_use]
|
||||
pub fn builder(
|
||||
) -> EmulatorBuilder<StdCommandManager<S>, StdEmulatorDriver, (), S, StdSnapshotManager> {
|
||||
EmulatorBuilder::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
pub fn modules(&self) -> &EmulatorModules<ET, S> {
|
||||
&self.modules
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn qemu(&self) -> Qemu {
|
||||
self.qemu
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn driver(&self) -> &ED {
|
||||
self.driver.as_ref().unwrap()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn driver_mut(&mut self) -> &mut ED {
|
||||
self.driver.as_mut().unwrap()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn snapshot_manager(&self) -> &SM {
|
||||
&self.snapshot_manager
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn snapshot_manager_mut(&mut self) -> &mut SM {
|
||||
&mut self.snapshot_manager
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
ET: Unpin,
|
||||
S: UsesInput + Unpin,
|
||||
{
|
||||
@ -456,38 +300,40 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
S: UsesInput + Unpin,
|
||||
{
|
||||
#[allow(clippy::must_use_candidate, clippy::similar_names)]
|
||||
pub fn new(
|
||||
args: &[String],
|
||||
env: &[(String, String)],
|
||||
qemu_args: &[String],
|
||||
modules: ET,
|
||||
exit_handler: EH,
|
||||
drivers: ED,
|
||||
snapshot_manager: SM,
|
||||
command_manager: CM,
|
||||
) -> Result<Self, QemuInitError> {
|
||||
let qemu = Qemu::init(args, env)?;
|
||||
let qemu = Qemu::init(qemu_args)?;
|
||||
|
||||
Self::new_with_qemu(qemu, modules, exit_handler, command_manager)
|
||||
Self::new_with_qemu(qemu, modules, drivers, snapshot_manager, command_manager)
|
||||
}
|
||||
|
||||
pub fn new_with_qemu(
|
||||
qemu: Qemu,
|
||||
modules: ET,
|
||||
exit_handler: EH,
|
||||
driver: ED,
|
||||
snapshot_manager: SM,
|
||||
command_manager: CM,
|
||||
) -> Result<Self, QemuInitError> {
|
||||
Ok(Emulator {
|
||||
modules: EmulatorModules::new(qemu, modules),
|
||||
command_manager,
|
||||
exit_handler: RefCell::new(exit_handler),
|
||||
snapshot_manager,
|
||||
driver: Some(driver),
|
||||
breakpoints_by_addr: RefCell::new(HashMap::new()),
|
||||
breakpoints_by_id: RefCell::new(HashMap::new()),
|
||||
first_exec: true,
|
||||
_phantom: PhantomData,
|
||||
qemu,
|
||||
})
|
||||
}
|
||||
@ -515,29 +361,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
pub fn modules(&self) -> &EmulatorModules<ET, S> {
|
||||
&self.modules
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn qemu(&self) -> Qemu {
|
||||
self.qemu
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn exit_handler(&self) -> &RefCell<EH> {
|
||||
&self.exit_handler
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
|
||||
where
|
||||
EH: EmulatorExitHandler<ET, S>,
|
||||
CM: CommandManager<EH, ET, S>,
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
ED: EmulatorDriver<CM, ET, S, SM>,
|
||||
ET: StdInstrumentationFilter + Unpin,
|
||||
S: UsesInput,
|
||||
{
|
||||
/// This function will run the emulator until the exit handler decides to stop the execution for
|
||||
@ -551,24 +379,19 @@ where
|
||||
pub unsafe fn run(
|
||||
&mut self,
|
||||
input: &S::Input,
|
||||
) -> Result<ExitHandlerResult<CM, EH, ET, S>, ExitHandlerError> {
|
||||
) -> Result<EmulatorDriverResult<CM, ED, ET, S, SM>, EmulatorDriverError> {
|
||||
let mut driver = self.driver.take().unwrap();
|
||||
|
||||
loop {
|
||||
// if self.first_exec {
|
||||
// self.modules_mut().first_exec_all();
|
||||
// self.first_exec = false;
|
||||
// }
|
||||
|
||||
// // First run modules callback functions
|
||||
// self.modules_mut().pre_exec_all(input);
|
||||
|
||||
// Insert input if the location is already known
|
||||
EH::qemu_pre_exec(self, input);
|
||||
driver.pre_exec(self, input);
|
||||
|
||||
// Run QEMU
|
||||
let exit_reason = self.run_qemu();
|
||||
let mut exit_reason = self.run_qemu();
|
||||
|
||||
// Handle QEMU exit
|
||||
if let Some(exit_handler_result) = EH::qemu_post_exec(self, exit_reason, input)? {
|
||||
if let Some(exit_handler_result) = driver.post_exec(self, &mut exit_reason, input)? {
|
||||
self.driver = Some(driver);
|
||||
return Ok(exit_handler_result);
|
||||
}
|
||||
}
|
||||
@ -579,7 +402,9 @@ where
|
||||
///
|
||||
/// Should, in general, be safe to call.
|
||||
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
||||
pub unsafe fn run_qemu(&self) -> Result<EmulatorExitResult<CM, EH, ET, S>, EmulatorExitError> {
|
||||
pub unsafe fn run_qemu(
|
||||
&self,
|
||||
) -> Result<EmulatorExitResult<CM, ED, ET, S, SM>, EmulatorExitError> {
|
||||
match self.qemu.run() {
|
||||
Ok(qemu_exit_reason) => Ok(match qemu_exit_reason {
|
||||
QemuExitReason::End(qemu_shutdown_cause) => {
|
||||
@ -594,9 +419,9 @@ where
|
||||
.clone();
|
||||
EmulatorExitResult::Breakpoint(bp.clone())
|
||||
}
|
||||
QemuExitReason::SyncExit => EmulatorExitResult::SyncExit(Rc::new(RefCell::new(
|
||||
SyncExit::new(self.command_manager.parse(self.qemu)?),
|
||||
))),
|
||||
QemuExitReason::SyncExit => EmulatorExitResult::SyncExit(SyncExit::new(
|
||||
self.command_manager.parse(self.qemu)?,
|
||||
)),
|
||||
}),
|
||||
Err(qemu_exit_reason_error) => Err(match qemu_exit_reason_error {
|
||||
QemuExitError::UnexpectedExit => EmulatorExitError::UnexpectedExit,
|
||||
@ -607,11 +432,16 @@ where
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
pub fn add_breakpoint(&self, mut bp: Breakpoint<CM, EH, ET, S>, enable: bool) -> BreakpointId {
|
||||
pub fn add_breakpoint(
|
||||
&self,
|
||||
mut bp: Breakpoint<CM, ED, ET, S, SM>,
|
||||
enable: bool,
|
||||
) -> BreakpointId {
|
||||
if enable {
|
||||
bp.enable(self.qemu);
|
||||
}
|
||||
@ -619,12 +449,10 @@ where
|
||||
let bp_id = bp.id();
|
||||
let bp_addr = bp.addr();
|
||||
|
||||
let bp_ref = Rc::new(RefCell::new(bp));
|
||||
|
||||
assert!(
|
||||
self.breakpoints_by_addr
|
||||
.borrow_mut()
|
||||
.insert(bp_addr, bp_ref.clone())
|
||||
.insert(bp_addr, bp.clone())
|
||||
.is_none(),
|
||||
"Adding multiple breakpoints at the same address"
|
||||
);
|
||||
@ -632,7 +460,7 @@ where
|
||||
assert!(
|
||||
self.breakpoints_by_id
|
||||
.borrow_mut()
|
||||
.insert(bp_id, bp_ref)
|
||||
.insert(bp_id, bp)
|
||||
.is_none(),
|
||||
"Adding the same breakpoint multiple times"
|
||||
);
|
||||
@ -643,10 +471,7 @@ where
|
||||
pub fn remove_breakpoint(&self, bp_id: BreakpointId) {
|
||||
let bp_addr = {
|
||||
let mut bp_map = self.breakpoints_by_id.borrow_mut();
|
||||
let mut bp = bp_map
|
||||
.get_mut(&bp_id)
|
||||
.expect("Did not find the breakpoint")
|
||||
.borrow_mut();
|
||||
let bp = bp_map.get_mut(&bp_id).expect("Did not find the breakpoint");
|
||||
bp.disable(self.qemu);
|
||||
bp.addr()
|
||||
};
|
||||
|
119
libafl_qemu/src/emu/snapshot.rs
Normal file
119
libafl_qemu/src/emu/snapshot.rs
Normal 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
|
||||
}
|
||||
}
|
@ -1,37 +1,27 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use libafl::inputs::UsesInput;
|
||||
use libafl::{
|
||||
inputs::{HasTargetBytes, UsesInput},
|
||||
prelude::{HasExecutions, State},
|
||||
};
|
||||
use libafl_qemu_sys::GuestPhysAddr;
|
||||
|
||||
use crate::{
|
||||
emu::IsSnapshotManager, DeviceSnapshotFilter, Emulator, Qemu, QemuSnapshotCheckResult,
|
||||
SnapshotId, SnapshotManagerError,
|
||||
command::{CommandManager, StdCommandManager},
|
||||
emu::{IsSnapshotManager, QemuSnapshotCheckResult},
|
||||
DeviceSnapshotFilter, Emulator, EmulatorBuilder, Qemu, SnapshotId, SnapshotManagerError,
|
||||
StdEmulatorDriver,
|
||||
};
|
||||
|
||||
impl SnapshotId {
|
||||
fn gen_unique_id() -> SnapshotId {
|
||||
static UNIQUE_ID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
let unique_id = UNIQUE_ID.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
SnapshotId { id: unique_id }
|
||||
}
|
||||
|
||||
fn inner(&self) -> u64 {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SnapshotManager {
|
||||
Qemu(QemuSnapshotManager),
|
||||
Fast(FastSnapshotManager),
|
||||
}
|
||||
|
||||
pub type StdSnapshotManager = FastSnapshotManager;
|
||||
|
||||
impl IsSnapshotManager for SnapshotManager {
|
||||
fn save(&mut self, qemu: Qemu) -> SnapshotId {
|
||||
match self {
|
||||
@ -42,23 +32,23 @@ impl IsSnapshotManager for SnapshotManager {
|
||||
|
||||
fn restore(
|
||||
&mut self,
|
||||
snapshot_id: &SnapshotId,
|
||||
qemu: Qemu,
|
||||
snapshot_id: &SnapshotId,
|
||||
) -> Result<(), SnapshotManagerError> {
|
||||
match self {
|
||||
SnapshotManager::Qemu(qemu_sm) => qemu_sm.restore(snapshot_id, qemu),
|
||||
SnapshotManager::Fast(fast_sm) => fast_sm.restore(snapshot_id, qemu),
|
||||
SnapshotManager::Qemu(qemu_sm) => qemu_sm.restore(qemu, snapshot_id),
|
||||
SnapshotManager::Fast(fast_sm) => fast_sm.restore(qemu, snapshot_id),
|
||||
}
|
||||
}
|
||||
|
||||
fn do_check(
|
||||
&self,
|
||||
reference_snapshot_id: &SnapshotId,
|
||||
qemu: Qemu,
|
||||
reference_snapshot_id: &SnapshotId,
|
||||
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
|
||||
match self {
|
||||
SnapshotManager::Qemu(qemu_sm) => qemu_sm.do_check(reference_snapshot_id, qemu),
|
||||
SnapshotManager::Fast(fast_sm) => fast_sm.do_check(reference_snapshot_id, qemu),
|
||||
SnapshotManager::Qemu(qemu_sm) => qemu_sm.do_check(qemu, reference_snapshot_id),
|
||||
SnapshotManager::Fast(fast_sm) => fast_sm.do_check(qemu, reference_snapshot_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,8 +105,8 @@ impl IsSnapshotManager for QemuSnapshotManager {
|
||||
|
||||
fn restore(
|
||||
&mut self,
|
||||
snapshot_id: &SnapshotId,
|
||||
qemu: Qemu,
|
||||
snapshot_id: &SnapshotId,
|
||||
) -> Result<(), SnapshotManagerError> {
|
||||
qemu.load_snapshot(self.snapshot_id_to_name(snapshot_id).as_str(), self.is_sync);
|
||||
Ok(())
|
||||
@ -124,8 +114,8 @@ impl IsSnapshotManager for QemuSnapshotManager {
|
||||
|
||||
fn do_check(
|
||||
&self,
|
||||
_reference_snapshot_id: &SnapshotId,
|
||||
_qemu: Qemu,
|
||||
_reference_snapshot_id: &SnapshotId,
|
||||
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
|
||||
// We consider the qemu implementation to be 'ideal' for now.
|
||||
Ok(QemuSnapshotCheckResult::default())
|
||||
@ -142,8 +132,8 @@ impl IsSnapshotManager for FastSnapshotManager {
|
||||
|
||||
fn restore(
|
||||
&mut self,
|
||||
snapshot_id: &SnapshotId,
|
||||
qemu: Qemu,
|
||||
snapshot_id: &SnapshotId,
|
||||
) -> Result<(), SnapshotManagerError> {
|
||||
let fast_snapshot_ptr = *self
|
||||
.snapshots
|
||||
@ -159,8 +149,8 @@ impl IsSnapshotManager for FastSnapshotManager {
|
||||
|
||||
fn do_check(
|
||||
&self,
|
||||
reference_snapshot_id: &SnapshotId,
|
||||
qemu: Qemu,
|
||||
reference_snapshot_id: &SnapshotId,
|
||||
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
|
||||
let fast_snapshot_ptr = *self.snapshots.get(reference_snapshot_id).ok_or(
|
||||
SnapshotManagerError::SnapshotIdNotFound(*reference_snapshot_id),
|
||||
@ -170,8 +160,9 @@ impl IsSnapshotManager for FastSnapshotManager {
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
/// Write a value to a phsical guest address, including ROM areas.
|
||||
|
@ -1,10 +1,13 @@
|
||||
use libafl::inputs::UsesInput;
|
||||
use libafl_qemu_sys::{GuestAddr, MmapPerms, VerifyAccess};
|
||||
|
||||
use crate::{Emulator, GuestMaps};
|
||||
use crate::{command::CommandManager, Emulator, GuestMaps, NopSnapshotManager};
|
||||
|
||||
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
|
||||
pub type StdSnapshotManager = NopSnapshotManager;
|
||||
|
||||
impl<CM, ED, ET, S, SM> Emulator<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
/// This function gets the memory mappings from the emulator.
|
||||
|
@ -42,16 +42,17 @@ use libc::siginfo_t;
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
use crate::EmulatorModules;
|
||||
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorExitHandler};
|
||||
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator};
|
||||
|
||||
pub struct QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
||||
pub struct QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
|
||||
where
|
||||
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||
S: State,
|
||||
OT: ObserversTuple<S>,
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
inner: StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>>,
|
||||
inner: StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, ED, ET, S, SM>>,
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
@ -93,12 +94,13 @@ pub unsafe fn inproc_qemu_timeout_handler<E, EM, OF, Z>(
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, CM, EH, H, OT, ET, S> Debug for QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
||||
impl<'a, CM, ED, ET, H, OT, S, SM> Debug for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
|
||||
where
|
||||
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||
S: State,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
ET: EmulatorModuleTuple<S> + Debug,
|
||||
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
S: State,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("QemuExecutor")
|
||||
@ -107,15 +109,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, CM, EH, H, OT, ET, S> QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
||||
impl<'a, CM, ED, ET, H, OT, S, SM> QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
|
||||
where
|
||||
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||
S: State,
|
||||
OT: ObserversTuple<S>,
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
pub fn new<EM, OF, Z>(
|
||||
emulator: Emulator<CM, EH, ET, S>,
|
||||
emulator: Emulator<CM, ED, ET, S, SM>,
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
@ -156,7 +159,7 @@ where
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
{
|
||||
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::<
|
||||
StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>>,
|
||||
StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, ED, ET, S, SM>>,
|
||||
EM,
|
||||
OF,
|
||||
Z,
|
||||
@ -166,7 +169,7 @@ where
|
||||
Ok(Self { inner })
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>> {
|
||||
pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, ED, ET, S, SM>> {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
@ -179,16 +182,18 @@ where
|
||||
|
||||
pub fn inner_mut(
|
||||
&mut self,
|
||||
) -> &mut StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>> {
|
||||
) -> &mut StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, ED, ET, S, SM>> {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, CM, EH, EM, H, OT, ET, S, Z> Executor<EM, Z> for QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
||||
impl<'a, CM, ED, EM, ET, H, OT, S, SM, Z> Executor<EM, Z>
|
||||
for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
EM: UsesState<State = S>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State + HasExecutions + Unpin,
|
||||
Z: UsesState<State = S>,
|
||||
@ -216,32 +221,35 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, CM, EH, H, OT, ET, S> UsesState for QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
||||
impl<'a, CM, ED, ET, H, OT, S, SM> UsesState for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
|
||||
where
|
||||
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||
S: State,
|
||||
OT: ObserversTuple<S>,
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<'a, CM, EH, H, OT, ET, S> UsesObservers for QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
||||
impl<'a, CM, ED, ET, H, OT, S, SM> UsesObservers for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
|
||||
where
|
||||
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||
S: State,
|
||||
OT: ObserversTuple<S>,
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
type Observers = OT;
|
||||
}
|
||||
|
||||
impl<'a, CM, EH, H, OT, ET, S> HasObservers for QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
||||
impl<'a, CM, ED, ET, H, OT, S, SM> HasObservers for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
|
||||
where
|
||||
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||
S: State,
|
||||
OT: ObserversTuple<S>,
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&mut Emulator<CM, ED, ET, S, SM>, &S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> {
|
||||
@ -255,8 +263,9 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
pub struct QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
|
||||
pub struct QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
EM: UsesState<State = S>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
@ -266,20 +275,21 @@ where
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>,
|
||||
emulator: Emulator<CM, EH, ET, S>,
|
||||
emulator: Emulator<CM, ED, ET, S, SM>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> Debug
|
||||
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
|
||||
impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> Debug
|
||||
for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
|
||||
where
|
||||
CM: Debug,
|
||||
CM: CommandManager<ED, ET, S, SM> + Debug,
|
||||
EM: UsesState<State = S>,
|
||||
EH: Debug,
|
||||
ED: Debug,
|
||||
ET: EmulatorModuleTuple<S> + Debug,
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
S: UsesInput + Debug,
|
||||
SM: Debug,
|
||||
SP: ShMemProvider,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
@ -292,8 +302,10 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
|
||||
impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
|
||||
QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
@ -303,7 +315,7 @@ where
|
||||
Z: HasObjective<State = S>,
|
||||
{
|
||||
pub fn new(
|
||||
emulator: Emulator<CM, EH, ET, S>,
|
||||
emulator: Emulator<CM, ED, ET, S, SM>,
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
@ -336,21 +348,20 @@ where
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
pub fn emulator(&self) -> &Emulator<CM, EH, ET, S> {
|
||||
pub fn emulator(&self) -> &Emulator<CM, ED, ET, S, SM> {
|
||||
&self.emulator
|
||||
}
|
||||
|
||||
pub fn emulator_mut(&mut self) -> &Emulator<CM, EH, ET, S> {
|
||||
pub fn emulator_mut(&mut self) -> &Emulator<CM, ED, ET, S, SM> {
|
||||
&mut self.emulator
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, CM, EH, EM, H, OT, ET, S, Z, SP, OF> Executor<EM, Z>
|
||||
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
|
||||
impl<'a, CM, ED, EM, ET, H, OF, OT, S, SM, SP, Z> Executor<EM, Z>
|
||||
for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
|
||||
where
|
||||
CM: CommandManager<EH, ET, S>,
|
||||
EH: EmulatorExitHandler<ET, S>,
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
EM: EventManager<InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>, Z, State = S>,
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: Unpin + State + HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + HasSolutions,
|
||||
@ -372,9 +383,10 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> UsesObservers
|
||||
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
|
||||
impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> UsesObservers
|
||||
for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
EM: UsesState<State = S>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
@ -387,9 +399,10 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> UsesState
|
||||
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
|
||||
impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> UsesState
|
||||
for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
EM: UsesState<State = S>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
@ -402,9 +415,10 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> HasObservers
|
||||
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
|
||||
impl<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> HasObservers
|
||||
for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
EM: UsesState<State = S>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use libafl_qemu_sys::GuestPhysAddr;
|
||||
|
||||
use crate::modules::{
|
||||
@ -6,7 +8,8 @@ use crate::modules::{
|
||||
};
|
||||
|
||||
pub trait StdInstrumentationFilter:
|
||||
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
|
||||
Debug
|
||||
+ HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
|
||||
+ HasInstrumentationFilter<QemuInstrumentationPagingFilter>
|
||||
{
|
||||
}
|
||||
@ -14,6 +17,7 @@ pub trait StdInstrumentationFilter:
|
||||
impl<Head> crate::modules::StdInstrumentationFilter for (Head, ()) where
|
||||
Head: HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
|
||||
+ HasInstrumentationFilter<QemuInstrumentationPagingFilter>
|
||||
+ Debug
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -685,6 +685,7 @@ pub fn init_qemu_with_asan(
|
||||
let add_asan =
|
||||
|e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..];
|
||||
|
||||
// TODO: adapt since qemu does not take envp anymore as parameter
|
||||
let mut added = false;
|
||||
for (k, v) in &mut *env {
|
||||
if k == "QEMU_SET_ENV" {
|
||||
@ -717,7 +718,7 @@ pub fn init_qemu_with_asan(
|
||||
ASAN_INITED = true;
|
||||
}
|
||||
|
||||
let qemu = Qemu::init(args, env)?;
|
||||
let qemu = Qemu::init(args)?;
|
||||
let rt = AsanGiovese::new(qemu.hooks());
|
||||
|
||||
Ok((qemu, rt))
|
||||
|
@ -94,7 +94,7 @@ pub fn init_qemu_with_asan_guest(
|
||||
ASAN_GUEST_INITED = true;
|
||||
}
|
||||
|
||||
let emu = Qemu::init(args, env)?;
|
||||
let emu = Qemu::init(args)?;
|
||||
Ok((emu, asan_lib))
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
#[cfg(not(cpu_target = "hexagon"))]
|
||||
pub mod drcov;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[cfg(not(cpu_target = "hexagon"))]
|
||||
pub use drcov::DrCovModule;
|
||||
|
||||
@ -28,12 +31,12 @@ pub use asan_guest::{init_qemu_with_asan_guest, AsanGuestModule};
|
||||
use crate::modules::{HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter};
|
||||
|
||||
pub trait StdInstrumentationFilter:
|
||||
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
|
||||
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> + Debug
|
||||
{
|
||||
}
|
||||
|
||||
impl<Head> StdInstrumentationFilter for (Head, ()) where
|
||||
Head: HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
|
||||
Head: HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> + Debug
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -365,9 +365,9 @@ impl From<QemuConfig> for Result<Qemu, QemuInitError> {
|
||||
let args = config
|
||||
.to_string()
|
||||
.split(' ')
|
||||
.map(std::string::ToString::to_string)
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<String>>();
|
||||
let qemu = Qemu::init(&args, &[])?;
|
||||
let qemu = Qemu::init(&args)?;
|
||||
QEMU_CONFIG
|
||||
.set(config)
|
||||
.map_err(|_| unreachable!("BUG: QEMU_CONFIG was already set but Qemu was not init!"))?;
|
@ -497,7 +497,6 @@ create_wrapper!(
|
||||
);
|
||||
|
||||
// New thread hook wrappers
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
create_hook_types!(
|
||||
NewThread,
|
||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, env: CPUArchStatePtr, tid: u32) -> bool,
|
||||
@ -511,9 +510,7 @@ create_hook_types!(
|
||||
>,
|
||||
extern "C" fn(*const (), env: CPUArchStatePtr, tid: u32) -> bool
|
||||
);
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false);
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
create_wrapper!(new_thread, (env: CPUArchStatePtr, tid: u32), bool);
|
||||
|
||||
// CPU Run hook wrappers
|
||||
@ -943,6 +940,19 @@ impl QemuHooks {
|
||||
BackdoorHookId(num)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_new_thread_hook<T: Into<HookData>>(
|
||||
&self,
|
||||
data: T,
|
||||
callback: extern "C" fn(T, env: CPUArchStatePtr, tid: u32) -> bool,
|
||||
) -> NewThreadHookId {
|
||||
unsafe {
|
||||
let data: u64 = data.into().0;
|
||||
let callback: extern "C" fn(u64, CPUArchStatePtr, u32) -> bool = transmute(callback);
|
||||
let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data);
|
||||
NewThreadHookId(num)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
@ -983,19 +993,6 @@ impl QemuHooks {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_new_thread_hook<T: Into<HookData>>(
|
||||
&self,
|
||||
data: T,
|
||||
callback: extern "C" fn(T, env: CPUArchStatePtr, tid: u32) -> bool,
|
||||
) -> NewThreadHookId {
|
||||
unsafe {
|
||||
let data: u64 = data.into().0;
|
||||
let callback: extern "C" fn(u64, CPUArchStatePtr, u32) -> bool = transmute(callback);
|
||||
let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data);
|
||||
NewThreadHookId(num)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn add_post_syscall_hook<T: Into<HookData>>(
|
||||
&self,
|
||||
|
@ -16,14 +16,12 @@ use std::{
|
||||
};
|
||||
|
||||
use libafl_bolts::os::unix_signals::Signal;
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
use libafl_qemu_sys::qemu_init;
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
use libafl_qemu_sys::{guest_base, qemu_user_init, VerifyAccess};
|
||||
use libafl_qemu_sys::{guest_base, VerifyAccess};
|
||||
use libafl_qemu_sys::{
|
||||
libafl_flush_jit, libafl_get_exit_reason, libafl_page_from_addr, libafl_qemu_add_gdb_cmd,
|
||||
libafl_qemu_cpu_index, libafl_qemu_current_cpu, libafl_qemu_gdb_reply, libafl_qemu_get_cpu,
|
||||
libafl_qemu_num_cpus, libafl_qemu_num_regs, libafl_qemu_read_reg,
|
||||
libafl_qemu_init, libafl_qemu_num_cpus, libafl_qemu_num_regs, libafl_qemu_read_reg,
|
||||
libafl_qemu_remove_breakpoint, libafl_qemu_set_breakpoint, libafl_qemu_trigger_breakpoint,
|
||||
libafl_qemu_write_reg, CPUArchState, CPUStatePtr, FatPtr, GuestAddr, GuestPhysAddr, GuestUsize,
|
||||
GuestVirtAddr,
|
||||
@ -33,8 +31,8 @@ use strum::IntoEnumIterator;
|
||||
|
||||
use crate::{GuestAddrKind, GuestReg, Regs};
|
||||
|
||||
pub mod qemu_config;
|
||||
use qemu_config::{QemuConfig, QemuConfigBuilder, QEMU_CONFIG};
|
||||
pub mod config;
|
||||
use config::{QemuConfig, QemuConfigBuilder, QEMU_CONFIG};
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
mod usermode;
|
||||
@ -72,11 +70,6 @@ pub enum QemuExitError {
|
||||
UnexpectedExit, // Qemu exited without going through an expected exit point. Can be caused by a crash for example.
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct QemuSnapshotCheckResult {
|
||||
nb_page_inconsistencies: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum QemuRWErrorKind {
|
||||
Read,
|
||||
@ -132,15 +125,6 @@ impl QemuRWError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a QEMU snapshot check result for which no error was detected
|
||||
impl Default for QemuSnapshotCheckResult {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
nb_page_inconsistencies: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The thin wrapper around QEMU.
|
||||
/// It is considered unsafe to use it directly.
|
||||
/// Prefer using `Emulator` instead in case of doubt.
|
||||
@ -529,7 +513,7 @@ impl Qemu {
|
||||
}
|
||||
|
||||
#[allow(clippy::must_use_candidate, clippy::similar_names)]
|
||||
pub fn init(args: &[String], env: &[(String, String)]) -> Result<Self, QemuInitError> {
|
||||
pub fn init(args: &[String]) -> Result<Self, QemuInitError> {
|
||||
if args.is_empty() {
|
||||
return Err(QemuInitError::EmptyArgs);
|
||||
}
|
||||
@ -555,22 +539,16 @@ impl Qemu {
|
||||
.collect();
|
||||
let mut argv: Vec<*const u8> = args.iter().map(|x| x.as_ptr() as *const u8).collect();
|
||||
argv.push(ptr::null()); // argv is always null terminated.
|
||||
let env_strs: Vec<String> = env
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{}={}\0", &k, &v))
|
||||
.collect();
|
||||
let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect();
|
||||
envp.push(ptr::null());
|
||||
|
||||
unsafe {
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
qemu_user_init(argc, argv.as_ptr(), envp.as_ptr());
|
||||
libafl_qemu_init(argc, argv.as_ptr() as *mut *mut i8);
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
{
|
||||
qemu_init(argc, argv.as_ptr());
|
||||
unsafe {
|
||||
libc::atexit(qemu_cleanup_atexit);
|
||||
libafl_qemu_sys::syx_snapshot_init(true);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Qemu { _private: () })
|
||||
}
|
||||
@ -993,9 +971,9 @@ pub mod pybind {
|
||||
impl Qemu {
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
#[new]
|
||||
fn new(args: Vec<String>, env: Vec<(String, String)>) -> PyResult<Qemu> {
|
||||
let qemu = super::Qemu::init(&args, &env)
|
||||
.map_err(|e| PyValueError::new_err(format!("{e}")))?;
|
||||
fn new(args: Vec<String>) -> PyResult<Qemu> {
|
||||
let qemu =
|
||||
super::Qemu::init(&args).map_err(|e| PyValueError::new_err(format!("{e}")))?;
|
||||
|
||||
Ok(Qemu { qemu })
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use libafl_qemu_sys::{
|
||||
libafl_save_qemu_snapshot, qemu_cleanup, qemu_main_loop, vm_start, GuestAddr, GuestPhysAddr,
|
||||
GuestUsize, GuestVirtAddr,
|
||||
};
|
||||
use libc::EXIT_SUCCESS;
|
||||
use num_traits::Zero;
|
||||
|
||||
use crate::{
|
||||
@ -21,7 +22,7 @@ use crate::{
|
||||
|
||||
pub(super) extern "C" fn qemu_cleanup_atexit() {
|
||||
unsafe {
|
||||
qemu_cleanup();
|
||||
qemu_cleanup(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,9 +255,7 @@ impl Qemu {
|
||||
) -> QemuSnapshotCheckResult {
|
||||
let check_result = libafl_qemu_sys::syx_snapshot_check(ref_snapshot);
|
||||
|
||||
QemuSnapshotCheckResult {
|
||||
nb_page_inconsistencies: check_result.nb_inconsistencies,
|
||||
}
|
||||
QemuSnapshotCheckResult::new(check_result.nb_inconsistencies)
|
||||
}
|
||||
|
||||
pub fn list_devices(&self) -> Vec<String> {
|
||||
|
@ -1,12 +1,9 @@
|
||||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
rc::Rc,
|
||||
};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
use enum_map::Enum;
|
||||
use libafl::inputs::UsesInput;
|
||||
|
||||
use crate::{command::IsCommand, get_exit_arch_regs, GuestReg, Regs, CPU};
|
||||
use crate::{command::CommandManager, get_exit_arch_regs, GuestReg, Regs, CPU};
|
||||
|
||||
#[derive(Debug, Clone, Enum)]
|
||||
pub enum ExitArgs {
|
||||
@ -20,26 +17,49 @@ pub enum ExitArgs {
|
||||
Arg6,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SyncExit<CM, EH, ET, S>
|
||||
pub struct SyncExit<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
command: Rc<dyn IsCommand<CM, EH, ET, S>>,
|
||||
command: CM::Commands,
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> SyncExit<CM, EH, ET, S>
|
||||
impl<CM, ED, ET, S, SM> Clone for SyncExit<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
command: self.command.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, ED, ET, S, SM> Debug for SyncExit<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Sync Exit")
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, ED, ET, S, SM> SyncExit<CM, ED, ET, S, SM>
|
||||
where
|
||||
CM: CommandManager<ED, ET, S, SM>,
|
||||
S: UsesInput,
|
||||
{
|
||||
#[must_use]
|
||||
pub fn new(command: Rc<dyn IsCommand<CM, EH, ET, S>>) -> Self {
|
||||
pub fn new(command: CM::Commands) -> Self {
|
||||
Self { command }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn command(&self) -> Rc<dyn IsCommand<CM, EH, ET, S>> {
|
||||
self.command.clone()
|
||||
pub fn command(&self) -> &CM::Commands {
|
||||
&self.command
|
||||
}
|
||||
|
||||
pub fn ret(&self, cpu: &CPU, value: GuestReg) {
|
||||
@ -52,12 +72,3 @@ where
|
||||
get_exit_arch_regs()[ExitArgs::Ret]
|
||||
}
|
||||
}
|
||||
|
||||
impl<CM, EH, ET, S> Display for SyncExit<CM, EH, ET, S>
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.command)
|
||||
}
|
||||
}
|
||||
|
@ -39,9 +39,8 @@ use libafl_bolts::{
|
||||
use libafl_qemu::modules::CmpLogModule;
|
||||
pub use libafl_qemu::qemu::Qemu;
|
||||
use libafl_qemu::{
|
||||
command::NopCommandManager,
|
||||
modules::edges::{self, EdgeCoverageModule},
|
||||
Emulator, NopEmulatorExitHandler, QemuExecutor,
|
||||
Emulator, QemuExecutor,
|
||||
};
|
||||
use libafl_targets::{edges_map_mut_ptr, CmpLogObserver};
|
||||
use typed_builder::TypedBuilder;
|
||||
@ -230,19 +229,14 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
|
||||
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let buf = target.as_slice();
|
||||
harness_bytes(buf);
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
let emulator = Emulator::new_with_qemu(
|
||||
qemu,
|
||||
modules,
|
||||
NopEmulatorExitHandler,
|
||||
NopCommandManager,
|
||||
)?;
|
||||
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
|
||||
|
||||
let executor = QemuExecutor::new(
|
||||
emulator,
|
||||
@ -345,21 +339,16 @@ where
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let tools = tuple_list!(EdgeCoverageModule::default());
|
||||
let modules = tuple_list!(EdgeCoverageModule::default());
|
||||
|
||||
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
|
||||
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let buf = target.as_slice();
|
||||
harness_bytes(buf);
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
let emulator = Emulator::new_with_qemu(
|
||||
qemu,
|
||||
tools,
|
||||
NopEmulatorExitHandler,
|
||||
NopCommandManager,
|
||||
)?;
|
||||
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
|
||||
|
||||
let mut executor = QemuExecutor::new(
|
||||
emulator,
|
||||
|
Loading…
x
Reference in New Issue
Block a user