diff --git a/fuzzers/fuzzbench/fuzzbench_fork_qemu/src/fuzzer.rs b/fuzzers/fuzzbench/fuzzbench_fork_qemu/src/fuzzer.rs index 306831d84e..c6a9ceea1a 100644 --- a/fuzzers/fuzzbench/fuzzbench_fork_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench/fuzzbench_fork_qemu/src/fuzzer.rs @@ -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(); diff --git a/fuzzers/fuzzbench/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/fuzzbench/fuzzbench_qemu/src/fuzzer.rs index 659610b4dd..825fc3c410 100644 --- a/fuzzers/fuzzbench/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench/fuzzbench_qemu/src/fuzzer.rs @@ -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 = 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( diff --git a/fuzzers/qemu/qemu_cmin/src/fuzzer.rs b/fuzzers/qemu/qemu_cmin/src/fuzzer.rs index 4bf1ebec46..4c1517450a 100644 --- a/fuzzers/qemu/qemu_cmin/src/fuzzer.rs +++ b/fuzzers/qemu/qemu_cmin/src/fuzzer.rs @@ -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, diff --git a/fuzzers/qemu/qemu_coverage/src/fuzzer.rs b/fuzzers/qemu/qemu_coverage/src/fuzzer.rs index dac399fff2..8cea932a25 100644 --- a/fuzzers/qemu/qemu_coverage/src/fuzzer.rs +++ b/fuzzers/qemu/qemu_coverage/src/fuzzer.rs @@ -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, diff --git a/fuzzers/qemu/qemu_launcher/src/client.rs b/fuzzers/qemu/qemu_launcher/src/client.rs index 77ac70da02..9eaf39f931 100644 --- a/fuzzers/qemu/qemu_launcher/src/client.rs +++ b/fuzzers/qemu/qemu_launcher/src/client.rs @@ -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) } }; diff --git a/fuzzers/qemu/qemu_launcher/src/instance.rs b/fuzzers/qemu/qemu_launcher/src/instance.rs index 867b42b784..b703b221c5 100644 --- a/fuzzers/qemu/qemu_launcher/src/instance.rs +++ b/fuzzers/qemu/qemu_launcher/src/instance.rs @@ -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 diff --git a/fuzzers/qemu/qemu_systemmode/src/fuzzer_breakpoint.rs b/fuzzers/qemu/qemu_systemmode/src/fuzzer_breakpoint.rs index b0eee27c2a..0ac65bec52 100644 --- a/fuzzers/qemu/qemu_systemmode/src/fuzzer_breakpoint.rs +++ b/fuzzers/qemu/qemu_systemmode/src/fuzzer_breakpoint.rs @@ -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 = 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() }; diff --git a/fuzzers/qemu/qemu_systemmode/src/fuzzer_classic.rs b/fuzzers/qemu/qemu_systemmode/src/fuzzer_classic.rs index 2d4ec1945e..6b78864b1a 100644 --- a/fuzzers/qemu/qemu_systemmode/src/fuzzer_classic.rs +++ b/fuzzers/qemu/qemu_systemmode/src/fuzzer_classic.rs @@ -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(); diff --git a/fuzzers/qemu/qemu_systemmode/src/fuzzer_sync_exit.rs b/fuzzers/qemu/qemu_systemmode/src/fuzzer_sync_exit.rs index 29e1b6358d..f9dad9cbb1 100644 --- a/fuzzers/qemu/qemu_systemmode/src/fuzzer_sync_exit.rs +++ b/fuzzers/qemu/qemu_systemmode/src/fuzzer_sync_exit.rs @@ -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 = 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 = - 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() }; diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index cdc0edebd7..49620b4dcd 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -720,7 +720,7 @@ where where MO: MapObserver + Truncate, // TODO maybe enforce Entry = u8 for the cov map A: Observer + AsRef + AsMut, - OT: ObserversTuple + Prepend, + OT: ObserversTuple + Prepend, S: UsesInput, S::Input: Input + HasTargetBytes, SP: ShMemProvider, diff --git a/libafl_bolts/src/cli.rs b/libafl_bolts/src/cli.rs index 920c5a4c35..f97437b6ef 100644 --- a/libafl_bolts/src/cli.rs +++ b/libafl_bolts/src/cli.rs @@ -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... //! } //! diff --git a/libafl_bolts/src/tuples.rs b/libafl_bolts/src/tuples.rs index 5f304ffe39..3ada1d2e5f 100644 --- a/libafl_bolts/src/tuples.rs +++ b/libafl_bolts/src/tuples.rs @@ -657,54 +657,32 @@ where /// Allows prepending of values to a tuple pub trait Prepend { - /// 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 Prepend 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 { - /// The Resulting [`TupleList`], of an [`Append::append()`] call, - /// including the appended entry. - type AppendResult; - +pub trait Append +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 Append for () { - type AppendResult = (T, ()); - - fn append(self, value: T) -> Self::AppendResult { - (value, ()) - } -} - -/// Implement append for non-empty tuple list. -impl Append for (Head, Tail) -where - Tail: Append, -{ - 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 Append for Head { + fn append(self, value: T) -> (Self, T) { + (self, value) } } diff --git a/libafl_libfuzzer_runtime/build.rs b/libafl_libfuzzer_runtime/build.rs deleted file mode 120000 index 1d67421d3e..0000000000 --- a/libafl_libfuzzer_runtime/build.rs +++ /dev/null @@ -1 +0,0 @@ -../libafl_libfuzzer/runtime/build.rs \ No newline at end of file diff --git a/libafl_libfuzzer_runtime/build.rs b/libafl_libfuzzer_runtime/build.rs new file mode 100644 index 0000000000..f3926213dc --- /dev/null +++ b/libafl_libfuzzer_runtime/build.rs @@ -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"); +} diff --git a/libafl_qemu/libafl_qemu_build/src/bindings.rs b/libafl_qemu/libafl_qemu_build/src/bindings.rs index 7c76e8f0f9..25f78886c5 100644 --- a/libafl_qemu/libafl_qemu_build/src/bindings.rs +++ b/libafl_qemu/libafl_qemu_build/src/bindings.rs @@ -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())); diff --git a/libafl_qemu/libafl_qemu_build/src/build.rs b/libafl_qemu/libafl_qemu_build/src/build.rs index 5954d93d44..d4e67b75ad 100644 --- a/libafl_qemu/libafl_qemu_build/src/build.rs +++ b/libafl_qemu/libafl_qemu_build/src/build.rs @@ -11,7 +11,7 @@ use crate::cargo_add_rpath; 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") diff --git a/libafl_qemu/libafl_qemu_sys/src/lib.rs b/libafl_qemu/libafl_qemu_sys/src/lib.rs index a56a2eb35b..b377e195b6 100644 --- a/libafl_qemu/libafl_qemu_sys/src/lib.rs +++ b/libafl_qemu/libafl_qemu_sys/src/lib.rs @@ -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 diff --git a/libafl_qemu/libafl_qemu_sys/src/systemmode.rs b/libafl_qemu/libafl_qemu_sys/src/systemmode.rs index 5850390455..e69de29bb2 100644 --- a/libafl_qemu/libafl_qemu_sys/src/systemmode.rs +++ b/libafl_qemu/libafl_qemu_sys/src/systemmode.rs @@ -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(); -} diff --git a/libafl_qemu/libafl_qemu_sys/src/usermode.rs b/libafl_qemu/libafl_qemu_sys/src/usermode.rs index 54bb4d7523..ee4c4155e7 100644 --- a/libafl_qemu/libafl_qemu_sys/src/usermode.rs +++ b/libafl_qemu/libafl_qemu_sys/src/usermode.rs @@ -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; diff --git a/libafl_qemu/libafl_qemu_sys/src/x86_64_stub_bindings.rs b/libafl_qemu/libafl_qemu_sys/src/x86_64_stub_bindings.rs index 2e529936fc..e5566e77db 100644 --- a/libafl_qemu/libafl_qemu_sys/src/x86_64_stub_bindings.rs +++ b/libafl_qemu/libafl_qemu_sys/src/x86_64_stub_bindings.rs @@ -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)] diff --git a/libafl_qemu/src/breakpoint.rs b/libafl_qemu/src/breakpoint.rs index 4a857b2f1c..69e4e02ee1 100644 --- a/libafl_qemu/src/breakpoint.rs +++ b/libafl_qemu/src/breakpoint.rs @@ -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 +pub struct Breakpoint where + CM: CommandManager, S: UsesInput, { id: BreakpointId, addr: GuestAddr, - cmd: Option>>, + cmd: Option, disable_on_trigger: bool, enabled: bool, } +impl Clone for Breakpoint +where + CM: CommandManager, + 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 Debug for Breakpoint +where + CM: CommandManager, + 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 = OnceLock::new(); @@ -47,8 +72,9 @@ impl Default for BreakpointId { } } -impl Hash for Breakpoint +impl Hash for Breakpoint where + CM: CommandManager, S: UsesInput, { fn hash(&self, state: &mut H) { @@ -56,8 +82,9 @@ where } } -impl PartialEq for Breakpoint +impl PartialEq for Breakpoint where + CM: CommandManager, S: UsesInput, { fn eq(&self, other: &Self) -> bool { @@ -65,10 +92,16 @@ where } } -impl Eq for Breakpoint where S: UsesInput {} - -impl Display for Breakpoint +impl Eq for Breakpoint where + CM: CommandManager, + S: UsesInput, +{ +} + +impl Display for Breakpoint +where + CM: CommandManager, S: UsesInput, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -76,8 +109,9 @@ where } } -impl Borrow for Breakpoint +impl Borrow for Breakpoint where + CM: CommandManager, S: UsesInput, { fn borrow(&self) -> &BreakpointId { @@ -85,8 +119,9 @@ where } } -impl Borrow for Breakpoint +impl Borrow for Breakpoint where + CM: CommandManager, S: UsesInput, { fn borrow(&self) -> &GuestAddr { @@ -94,8 +129,9 @@ where } } -impl Breakpoint +impl Breakpoint where + CM: CommandManager, 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 + '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>> { + pub fn trigger(&mut self, qemu: Qemu) -> Option { if self.disable_on_trigger { self.disable(qemu); } diff --git a/libafl_qemu/src/command/mod.rs b/libafl_qemu/src/command/mod.rs index c0d1626989..f89df85d17 100644 --- a/libafl_qemu/src/command/mod.rs +++ b/libafl_qemu/src/command/mod.rs @@ -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 - where - S: UsesInput, - { - native_command_parsers: - HashMap, ET, S>>>, - } + ($name:ident, [$($command:ty),+], [$($native_command_parser:ty),+]) => { + paste! { + pub struct $name { + phantom: PhantomData, + } - impl $name - 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, - ET, - S, - >, - >),*] - .into_iter(), - ); - - let mut parsers: HashMap< - GuestReg, - Box, ET, S>>, - > = HashMap::new(); - - for parser in native_parsers { - assert!(parsers - .insert(parser.command_id(), parser) - .is_none(), "Trying to use native commands with the same ID"); - } - - Self { - native_command_parsers: parsers, + impl Clone for $name { + fn clone(&self) -> Self { + Self { + phantom: PhantomData + } } } - } - impl CommandManager, ET, S> for $name - where - S: UsesInput, - { - fn parse( - &self, - qemu: Qemu, - ) -> Result, ET, S>>, CommandError> { - let arch_regs_map: &'static EnumMap = get_exit_arch_regs(); - let cmd_id: GuestReg = qemu.read_reg::(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 Debug for $name { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, stringify!($name)) + } } - } - impl Debug for $name - where - S: UsesInput, - { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { - write!(f, stringify!($name)) + impl Default for $name { + fn default() -> Self { + Self { + phantom: PhantomData + } + } } - } - impl Default for $name - where - ET: StdInstrumentationFilter + Unpin, - S: UsesInput + Unpin, - S::Input: HasTargetBytes, - SM: IsSnapshotManager, - { - fn default() -> Self { - Self::new() + impl CommandManager for $name + where + ET: EmulatorModuleTuple + StdInstrumentationFilter, + S: UsesInput + Unpin, + S::Input: HasTargetBytes, + SM: IsSnapshotManager, + { + type Commands = [<$name Commands>]; + + fn parse(&self, qemu: Qemu) -> Result { + let arch_regs_map: &'static EnumMap = get_exit_arch_regs(); + let cmd_id = qemu.read_reg::(arch_regs_map[ExitArgs::Cmd])? as c_uint; + + match cmd_id { + // >::COMMAND_ID => Ok(StdCommandManagerCommands::StartPhysCommandParserCmd(>::parse(qemu, arch_regs_map)?)), + $(<$native_command_parser as NativeCommandParser>::COMMAND_ID => Ok(<$native_command_parser as NativeCommandParser>::parse(qemu, arch_regs_map)?.into())),+, + _ => Err(CommandError::UnknownCommand(cmd_id.into())), + } + } } + + #[derive(Clone, Debug)] + pub enum [<$name Commands>] + { + // StartPhysCommand(StartPhysCommand) + $($command($command)),+, + } + + impl IsCommand<$name, StdEmulatorDriver, ET, S, SM> for [<$name Commands>] + where + ET: EmulatorModuleTuple + 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, StdEmulatorDriver, ET, S, SM>>::usable_at_runtime(cmd)),+ + } + } + + fn run(&self, + emu: &mut Emulator<$name, StdEmulatorDriver, ET, S, SM>, + driver: &mut StdEmulatorDriver, + input: &S::Input, + ret_reg: Option + ) -> Result, 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 CommandManager for NopCommandManager +pub trait CommandManager: Sized + Debug where S: UsesInput, { - fn parse(&self, _qemu: Qemu) -> Result>, CommandError> { - Ok(Rc::new(NopCommand)) + type Commands: IsCommand; + + fn parse(&self, qemu: Qemu) -> Result; +} + +#[derive(Clone, Debug)] +pub struct NopCommandManager; +impl CommandManager for NopCommandManager +where + S: UsesInput, +{ + type Commands = NopCommand; + + fn parse(&self, _qemu: Qemu) -> Result { + 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: Sized -where - S: UsesInput, -{ - fn parse(&self, qemu: Qemu) -> Result>, 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: Debug + Display +pub trait IsCommand: Clone + Debug where + CM: CommandManager, 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, + emu: &mut Emulator, + driver: &mut ED, input: &S::Input, ret_reg: Option, - ) -> Result>, ExitHandlerError>; + ) -> Result>, EmulatorDriverError>; } #[cfg(emulation_mode = "systemmode")] @@ -226,13 +245,14 @@ impl From 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 IsCommand for NopCommand +impl IsCommand for NopCommand where + CM: CommandManager, S: UsesInput, { fn usable_at_runtime(&self) -> bool { @@ -241,20 +261,21 @@ where fn run( &self, - _emu: &mut Emulator, + _emu: &mut Emulator, + _driver: &mut ED, _input: &S::Input, _ret_reg: Option, - ) -> Result>, ExitHandlerError> { + ) -> Result>, EmulatorDriverError> { Ok(None) } } #[derive(Debug, Clone)] pub struct SaveCommand; - -impl IsCommand, ET, S> for SaveCommand +impl IsCommand for SaveCommand where - ET: StdInstrumentationFilter + Unpin, + ET: EmulatorModuleTuple + StdInstrumentationFilter, + CM: CommandManager, S: UsesInput + Unpin, SM: IsSnapshotManager, { @@ -264,21 +285,18 @@ where fn run( &self, - emu: &mut Emulator, ET, S>, + emu: &mut Emulator, + driver: &mut StdEmulatorDriver, _input: &S::Input, _ret_reg: Option, - ) -> Result, ET, S>>, ExitHandlerError> + ) -> Result>, EmulatorDriverError> { let qemu = emu.qemu(); + let snapshot_id = emu.snapshot_manager_mut().save(qemu); - { - let emu_exit_handler = emu.exit_handler().borrow_mut(); - - let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu); - emu_exit_handler - .set_snapshot_id(snapshot_id) - .map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?; - } + driver + .set_snapshot_id(snapshot_id) + .map_err(|_| EmulatorDriverError::MultipleSnapshotDefinition)?; #[cfg(emulation_mode = "systemmode")] { @@ -304,8 +322,9 @@ where #[derive(Debug, Clone)] pub struct LoadCommand; -impl IsCommand, ET, S> for LoadCommand +impl IsCommand for LoadCommand where + CM: CommandManager, S: UsesInput, SM: IsSnapshotManager, { @@ -315,26 +334,22 @@ where fn run( &self, - emu: &mut Emulator, ET, S>, + emu: &mut Emulator, + driver: &mut StdEmulatorDriver, _input: &S::Input, _ret_reg: Option, - ) -> Result, ET, S>>, ExitHandlerError> + ) -> Result>, 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 IsCommand, ET, S> for InputCommand +impl IsCommand for InputCommand where + CM: CommandManager, S: UsesInput, S::Input: HasTargetBytes, { @@ -357,11 +373,11 @@ where fn run( &self, - emu: &mut Emulator, ET, S>, + emu: &mut Emulator, + _driver: &mut ED, input: &S::Input, ret_reg: Option, - ) -> Result, ET, S>>, ExitHandlerError> - { + ) -> Result>, 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 IsCommand, ET, S> for StartCommand +impl IsCommand for StartCommand where + CM: CommandManager, S: UsesInput, S::Input: HasTargetBytes, SM: IsSnapshotManager, @@ -391,20 +408,20 @@ where fn run( &self, - emu: &mut Emulator, ET, S>, + emu: &mut Emulator, + driver: &mut StdEmulatorDriver, input: &S::Input, ret_reg: Option, - ) -> Result, ET, S>>, ExitHandlerError> + ) -> Result>, 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); +pub struct EndCommand { + exit_kind: Option, +} -impl IsCommand, ET, S> for EndCommand +impl IsCommand for EndCommand where + CM: CommandManager, S: UsesInput, SM: IsSnapshotManager, { @@ -438,35 +458,35 @@ where fn run( &self, - emu: &mut Emulator, ET, S>, + emu: &mut Emulator, + driver: &mut StdEmulatorDriver, _input: &S::Input, _ret_reg: Option, - ) -> Result, ET, S>>, ExitHandlerError> + ) -> Result>, 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 IsCommand, ET, S> for VersionCommand +impl IsCommand for VersionCommand where + CM: CommandManager, S: UsesInput, { fn usable_at_runtime(&self) -> bool { @@ -475,17 +495,17 @@ where fn run( &self, - _emu: &mut Emulator, ET, S>, + _emu: &mut Emulator, + _driver: &mut ED, _input: &S::Input, _ret_reg: Option, - ) -> Result, ET, S>>, ExitHandlerError> - { + ) -> Result>, 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 { } #[cfg(emulation_mode = "systemmode")] -impl IsCommand, ET, S> for PagingFilterCommand +impl IsCommand for PagingFilterCommand where ET: StdInstrumentationFilter + Unpin, + CM: CommandManager, S: UsesInput + Unpin, { fn usable_at_runtime(&self) -> bool { @@ -509,11 +530,11 @@ where fn run( &self, - emu: &mut Emulator, ET, S>, + emu: &mut Emulator, + _driver: &mut ED, _input: &S::Input, _ret_reg: Option, - ) -> Result, ET, S>>, ExitHandlerError> - { + ) -> Result>, EmulatorDriverError> { let qemu_modules = emu.modules_mut().modules_mut(); let paging_filter = @@ -525,22 +546,22 @@ where } } -impl IsCommand, ET, S> for AddressRangeFilterCommand +impl IsCommand for AddressRangeFilterCommand where + CM: CommandManager, S: UsesInput, { fn usable_at_runtime(&self) -> bool { true } - #[allow(clippy::type_complexity)] // TODO: refactor with correct type. fn run( &self, - _emu: &mut Emulator, ET, S>, + _emu: &mut Emulator, + _driver: &mut ED, _input: &S::Input, _ret_reg: Option, - ) -> Result, ET, S>>, ExitHandlerError> - { + ) -> Result>, 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) -> Self { - Self(exit_kind) + Self { exit_kind } } } diff --git a/libafl_qemu/src/command/parser.rs b/libafl_qemu/src/command/parser.rs index a1331aef89..267ee23de7 100644 --- a/libafl_qemu/src/command/parser.rs +++ b/libafl_qemu/src/command/parser.rs @@ -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>> = OnceLock::new(); -pub trait NativeCommandParser +pub trait NativeCommandParser where + CM: CommandManager, S: UsesInput, { - fn command_id(&self) -> GuestReg; + type OutputCommand: IsCommand; + + const COMMAND_ID: c_uint; fn parse( - &self, qemu: Qemu, arch_regs_map: &'static EnumMap, - ) -> Result>, CommandError>; + ) -> Result; } pub struct InputPhysCommandParser; -impl NativeCommandParser, ET, S> - for InputPhysCommandParser +impl NativeCommandParser for InputPhysCommandParser where + CM: CommandManager, 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, - ) -> Result, ET, S>>, CommandError> { + ) -> Result { 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 NativeCommandParser, ET, S> - for InputVirtCommandParser +impl NativeCommandParser for InputVirtCommandParser where + CM: CommandManager, 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, - ) -> Result, ET, S>>, CommandError> { + ) -> Result { 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 NativeCommandParser for StartPhysCommandParser +where + CM: CommandManager, + 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, + ) -> Result { + 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 NativeCommandParser for StartVirtCommandParser +where + CM: CommandManager, + 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, + ) -> Result { + 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 NativeCommandParser, 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, - ) -> Result, 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 NativeCommandParser, 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, - ) -> Result, 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 NativeCommandParser, ET, S> for SaveCommandParser +impl NativeCommandParser for SaveCommandParser where - ET: StdInstrumentationFilter + Unpin, + ET: EmulatorModuleTuple + StdInstrumentationFilter, + CM: CommandManager, 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, - ) -> Result, ET, S>>, CommandError> { - Ok(Rc::new(SaveCommand)) + ) -> Result { + Ok(SaveCommand) } } pub struct LoadCommandParser; -impl NativeCommandParser, ET, S> for LoadCommandParser +impl NativeCommandParser for LoadCommandParser where + CM: CommandManager, 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, - ) -> Result, ET, S>>, CommandError> { - Ok(Rc::new(LoadCommand)) + ) -> Result { + Ok(LoadCommand) } } pub struct EndCommandParser; -impl NativeCommandParser, ET, S> for EndCommandParser + +impl NativeCommandParser for EndCommandParser where + CM: CommandManager, 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, - ) -> Result, ET, S>>, CommandError> { + ) -> Result { let native_exit_kind: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?; let native_exit_kind: Result = 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 NativeCommandParser, ET, S> - for VersionCommandParser +impl NativeCommandParser for VersionCommandParser where + CM: CommandManager, 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, - ) -> Result, ET, S>>, CommandError> { + ) -> Result { 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 NativeCommandParser, ET, S> +impl NativeCommandParser for VaddrFilterAllowRangeCommandParser where + CM: CommandManager, S: UsesInput, { - fn command_id(&self) -> GuestReg { - GuestReg::from(bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VADDR_FILTER_ALLOW.0) - } + type OutputCommand = FilterCommand; + + const COMMAND_ID: c_uint = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_VADDR_FILTER_ALLOW.0; fn parse( - &self, qemu: Qemu, arch_regs_map: &'static EnumMap, - ) -> Result, ET, S>>, CommandError> { + ) -> Result { 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]), - ))) + )) } } diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs new file mode 100644 index 0000000000..9c9c00e6f1 --- /dev/null +++ b/libafl_qemu/src/emu/builder.rs @@ -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), +} + +#[derive(Clone, Debug)] +pub struct EmulatorBuilder +where + S: UsesInput, +{ + modules: ET, + driver: ED, + snapshot_manager: SM, + command_manager: CM, + qemu_builder: Option, + phantom: PhantomData, +} + +impl EmulatorBuilder +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 EmulatorBuilder, 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 EmulatorBuilder, 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 EmulatorBuilder +where + S: UsesInput + Unpin, +{ + fn new( + modules: ET, + driver: ED, + command_manager: CM, + snapshot_manager: SM, + qemu_builder: Option, + ) -> Self { + Self { + modules, + command_manager, + driver, + snapshot_manager, + qemu_builder, + phantom: PhantomData, + } + } + + pub fn build(self) -> Result, QemuInitError> + where + CM: CommandManager, + ET: EmulatorModuleTuple, + { + 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_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 EmulatorBuilder +where + CM: CommandManager, + S: UsesInput + Unpin, +{ + #[must_use] + pub fn qemu_config(self, qemu_config: QemuConfig) -> EmulatorBuilder { + 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) -> EmulatorBuilder { + 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 { + EmulatorBuilder::new( + self.modules, + self.driver, + self.command_manager, + self.snapshot_manager, + Some(QemuBuilder::Qemu(qemu)), + ) + } + + pub fn add_module(self, module: EM) -> EmulatorBuilder + where + EM: EmulatorModule + Unpin, + ET: EmulatorModuleTuple, + { + EmulatorBuilder::new( + self.modules.prepend(module), + self.driver, + self.command_manager, + self.snapshot_manager, + self.qemu_builder, + ) + } + + pub fn driver(self, driver: ED2) -> EmulatorBuilder { + EmulatorBuilder::new( + self.modules, + driver, + self.command_manager, + self.snapshot_manager, + self.qemu_builder, + ) + } + + pub fn command_manager(self, command_manager: CM2) -> EmulatorBuilder + where + CM2: CommandManager, + { + EmulatorBuilder::new( + self.modules, + self.driver, + command_manager, + self.snapshot_manager, + self.qemu_builder, + ) + } + + pub fn modules(self, modules: ET2) -> EmulatorBuilder { + EmulatorBuilder::new( + modules, + self.driver, + self.command_manager, + self.snapshot_manager, + self.qemu_builder, + ) + } + + pub fn snapshot_manager( + self, + snapshot_manager: SM2, + ) -> EmulatorBuilder { + EmulatorBuilder::new( + self.modules, + self.driver, + self.command_manager, + snapshot_manager, + self.qemu_builder, + ) + } +} diff --git a/libafl_qemu/src/emu/drivers.rs b/libafl_qemu/src/emu/drivers.rs new file mode 100644 index 0000000000..a07000502a --- /dev/null +++ b/libafl_qemu/src/emu/drivers.rs @@ -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 +where + CM: CommandManager, + 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), + + /// 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: 'static + Sized +where + CM: CommandManager, + S: UsesInput, +{ + fn pre_exec(&mut self, _emulator: &mut Emulator, _input: &S::Input) {} + + #[allow(clippy::type_complexity)] + fn post_exec( + &mut self, + _emulator: &mut Emulator, + exit_reason: &mut Result, EmulatorExitError>, + _input: &S::Input, + ) -> Result>, EmulatorDriverError> { + match exit_reason { + Ok(reason) => Ok(Some(EmulatorDriverResult::ReturnToHarness(reason.clone()))), + Err(error) => Err(error.clone().into()), + } + } +} + +pub struct NopEmulatorDriver; +impl EmulatorDriver for NopEmulatorDriver +where + CM: CommandManager, + S: UsesInput, +{ +} + +#[derive(Clone, Debug)] +pub struct StdEmulatorDriver { + snapshot_id: OnceCell, + input_location: OnceCell, +} + +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 { + Some(*self.snapshot_id.get()?) + } +} + +// TODO: replace handlers with generics to permit compile-time customization of handlers +impl EmulatorDriver for StdEmulatorDriver +where + CM: CommandManager, + S: UsesInput + Unpin, + S::Input: HasTargetBytes, + SM: IsSnapshotManager, +{ + fn pre_exec(&mut self, emulator: &mut Emulator, 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, + exit_reason: &mut Result, EmulatorExitError>, + input: &S::Input, + ) -> Result>, 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, Option) = 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 TryFrom> for ExitKind +where + CM: CommandManager, + S: UsesInput, +{ + type Error = String; + + fn try_from(value: EmulatorDriverResult) -> Result { + match value { + EmulatorDriverResult::ReturnToHarness(unhandled_qemu_exit) => { + Err(format!("Unhandled QEMU exit: {:?}", &unhandled_qemu_exit)) + } + EmulatorDriverResult::EndOfRun(exit_kind) => Ok(exit_kind), + } + } +} diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs index 550417b62a..a0fe5135e3 100644 --- a/libafl_qemu/src/emu/hooks.rs +++ b/libafl_qemu/src/emu/hooks.rs @@ -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>>, backdoor_hooks: Vec>>, @@ -126,17 +126,18 @@ where cpu_run_hooks: Vec>>>, + new_thread_hooks: Vec>>, + #[cfg(emulation_mode = "usermode")] pre_syscall_hooks: Vec>>, #[cfg(emulation_mode = "usermode")] post_syscall_hooks: Vec>>, - #[cfg(emulation_mode = "usermode")] - new_thread_hooks: Vec>>, - #[cfg(emulation_mode = "usermode")] crash_hooks: Vec, + + phantom: PhantomData<(ET, S)>, } impl EmulatorHooks @@ -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) -> Option { + 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, + Option<&mut S>, + env: CPUArchStatePtr, + tid: u32, + ) -> bool, + ) -> NewThreadHookId { + unsafe { + self.qemu_hooks + .add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::) + } + } + + pub fn thread_creation_closure( + &mut self, + hook: NewThreadHookClosure, + ) -> 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::); + 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) -> Option { - 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, - Option<&mut S>, - env: CPUArchStatePtr, - tid: u32, - ) -> bool, - ) -> NewThreadHookId { - unsafe { - self.qemu_hooks - .add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::) - } - } - - pub fn thread_creation_closure( - &mut self, - hook: NewThreadHookClosure, - ) -> 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::); - 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, target_signal: i32)) { self.qemu_hooks.set_crash_hook(crash_hook_wrapper::); self.crash_hooks @@ -1054,6 +1055,29 @@ where pub fn backdoor_closure(&mut self, hook: BackdoorHookClosure) -> BackdoorHookId { self.hooks.backdoor_closure(hook) } + + pub fn thread_creation(&mut self, hook: NewThreadHook) -> Option { + self.hooks.thread_creation(hook) + } + + pub fn thread_creation_function( + &mut self, + hook: fn( + &mut EmulatorModules, + Option<&mut S>, + env: CPUArchStatePtr, + tid: u32, + ) -> bool, + ) -> NewThreadHookId { + self.hooks.thread_creation_function(hook) + } + + pub fn thread_creation_closure( + &mut self, + hook: NewThreadHookClosure, + ) -> NewThreadHookId { + self.hooks.thread_creation_closure(hook) + } } impl EmulatorModules @@ -1266,28 +1290,6 @@ where self.hooks.after_syscalls_closure(hook) } - pub fn thread_creation(&mut self, hook: NewThreadHook) -> Option { - self.hooks.thread_creation(hook) - } - - pub fn thread_creation_function( - &mut self, - hook: fn( - &mut EmulatorModules, - Option<&mut S>, - env: CPUArchStatePtr, - tid: u32, - ) -> bool, - ) -> NewThreadHookId { - self.hooks.thread_creation_function(hook) - } - - pub fn thread_creation_closure( - &mut self, - hook: NewThreadHookClosure, - ) -> NewThreadHookId { - self.hooks.thread_creation_closure(hook) - } pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules, target_signal: i32)) { self.hooks.crash_function(hook); } diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index 60dde0c79a..d930409a2c 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -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 = Rc>; -type BreakpointMutRef = Rc>>; - -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; - - 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: Sized + Debug + Clone -where - S: UsesInput, -{ - fn qemu_pre_exec>( - emu: &mut Emulator, - input: &S::Input, - ); - - fn qemu_post_exec>( - emu: &mut Emulator, - exit_reason: Result, EmulatorExitError>, - input: &S::Input, - ) -> Result>, ExitHandlerError>; -} - #[derive(Clone, Copy)] pub enum GuestAddrKind { Physical(GuestPhysAddr), Virtual(GuestVirtAddr), } -#[derive(Debug, Clone)] -pub enum EmulatorExitResult +pub enum EmulatorExitResult where + CM: CommandManager, S: UsesInput, { - QemuExit(QemuShutdownCause), // QEMU ended for some reason. - Breakpoint(Rc>>), // Breakpoint triggered. Contains the address of the trigger. - SyncExit(Rc>>), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL. + QemuExit(QemuShutdownCause), // QEMU ended for some reason. + Breakpoint(Breakpoint), // Breakpoint triggered. Contains the address of the trigger. + SyncExit(SyncExit), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL. } +impl Clone for EmulatorExitResult +where + CM: CommandManager, + 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 Debug for EmulatorExitResult +where + CM: CommandManager, + 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 -where - S: UsesInput, -{ - ReturnToHarness(EmulatorExitResult), // 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 EmulatorExitHandler for NopEmulatorExitHandler -where - S: UsesInput, -{ - fn qemu_pre_exec>( - _: &mut Emulator, - _: &S::Input, - ) { - } - - fn qemu_post_exec>( - _: &mut Emulator, - exit_reason: Result, EmulatorExitError>, - _: &S::Input, - ) -> Result>, 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, } -/// Synchronous Exit handler maintaining only one snapshot. -#[derive(Debug, Clone, TypedBuilder)] -pub struct StdEmulatorExitHandler { - snapshot_manager: RefCell, - #[builder(default)] - snapshot_id: OnceCell, - #[builder(default)] - input_location: OnceCell, -} - -// TODO: Replace TypedBuilder by something better, it does not work correctly with default and -// inter-dependent fields. -#[derive(Debug, TypedBuilder)] -pub struct Emulator +#[derive(Debug)] +#[allow(clippy::type_complexity)] +pub struct Emulator where + CM: CommandManager, S: UsesInput, { + snapshot_manager: SM, modules: Pin>>, command_manager: CM, - exit_handler: RefCell, - #[builder(default)] - breakpoints_by_addr: RefCell>>, - #[builder(default)] - breakpoints_by_id: RefCell>>, - #[builder(setter(transform = |args: &[String], env: &[(String, String)]| Qemu::init(args, env).unwrap()))] + driver: Option, + breakpoints_by_addr: RefCell>>, // TODO: change to RC here + breakpoints_by_id: RefCell>>, qemu: Qemu, first_exec: bool, - _phantom: PhantomData<(ET, S)>, } -impl ExitHandlerResult +impl EmulatorDriverResult where + CM: CommandManager, S: UsesInput, { #[must_use] #[allow(clippy::match_wildcard_for_single_variants)] pub fn end_of_run(&self) -> Option { match self { - ExitHandlerResult::EndOfRun(exit_kind) => Some(*exit_kind), + EmulatorDriverResult::EndOfRun(exit_kind) => Some(*exit_kind), _ => None, } } } -impl TryFrom> for ExitKind -where - CM: Debug, - EH: Debug, - ET: Debug, - S: UsesInput + Debug, -{ - type Error = String; - - fn try_from(value: ExitHandlerResult) -> Result { - 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 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 for ExitHandlerError { +impl From for EmulatorDriverError { fn from(sm_error: SnapshotManagerError) -> Self { - ExitHandlerError::SMError(sm_error) + EmulatorDriverError::SMError(sm_error) } } -impl From for ExitHandlerError { +impl From 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 StdEmulatorExitHandler { - 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 { - Some(*self.snapshot_id.get()?) - } - - pub fn snapshot_manager_borrow(&self) -> Ref { - self.snapshot_manager.borrow() - } - - pub fn snapshot_manager_borrow_mut(&self) -> RefMut { - self.snapshot_manager.borrow_mut() - } -} - -// TODO: replace handlers with generics to permit compile-time customization of handlers -impl EmulatorExitHandler for StdEmulatorExitHandler -where - S: UsesInput, - S::Input: HasTargetBytes, - SM: IsSnapshotManager, -{ - fn qemu_pre_exec>( - emu: &mut Emulator, - 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>( - emu: &mut Emulator, - exit_reason: Result, EmulatorExitError>, - input: &S::Input, - ) -> Result>, 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>, Option) = - 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 for ExitHandlerError { +impl From for EmulatorDriverError { fn from(error: EmulatorExitError) -> Self { - ExitHandlerError::QemuExitReasonError(error) + EmulatorDriverError::QemuExitReasonError(error) } } -impl From for ExitHandlerError { +impl From for EmulatorDriverError { fn from(error: CommandError) -> Self { - ExitHandlerError::CommandError(error) + EmulatorDriverError::CommandError(error) } } -impl Display for EmulatorExitResult +impl Display for EmulatorExitResult where + CM: CommandManager, 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 for EmulatorExitError { } } -impl Emulator +impl Emulator where + S: UsesInput, +{ + #[must_use] + pub fn empty( + ) -> EmulatorBuilder { + EmulatorBuilder::empty() + } +} + +impl Emulator, StdEmulatorDriver, (), S, StdSnapshotManager> +where + S: State + HasExecutions + Unpin, + S::Input: HasTargetBytes, +{ + #[must_use] + pub fn builder( + ) -> EmulatorBuilder, StdEmulatorDriver, (), S, StdSnapshotManager> { + EmulatorBuilder::default() + } +} + +impl Emulator +where + CM: CommandManager, + S: UsesInput, +{ + pub fn modules(&self) -> &EmulatorModules { + &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 Emulator +where + CM: CommandManager, ET: Unpin, S: UsesInput + Unpin, { @@ -456,38 +300,40 @@ where } } -impl Emulator +impl Emulator where + CM: CommandManager, ET: EmulatorModuleTuple, 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 { - 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 { 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 Emulator +impl Emulator where - S: UsesInput, -{ - pub fn modules(&self) -> &EmulatorModules { - &self.modules - } - - #[must_use] - pub fn qemu(&self) -> Qemu { - self.qemu - } - - #[must_use] - pub fn exit_handler(&self) -> &RefCell { - &self.exit_handler - } -} - -impl Emulator -where - EH: EmulatorExitHandler, - CM: CommandManager, + CM: CommandManager, + ED: EmulatorDriver, + 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, ExitHandlerError> { + ) -> Result, 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, EmulatorExitError> { + pub unsafe fn run_qemu( + &self, + ) -> Result, 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 Emulator +impl Emulator where + CM: CommandManager, S: UsesInput, { - pub fn add_breakpoint(&self, mut bp: Breakpoint, enable: bool) -> BreakpointId { + pub fn add_breakpoint( + &self, + mut bp: Breakpoint, + 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() }; diff --git a/libafl_qemu/src/emu/snapshot.rs b/libafl_qemu/src/emu/snapshot.rs new file mode 100644 index 0000000000..7b3e9d64a8 --- /dev/null +++ b/libafl_qemu/src/emu/snapshot.rs @@ -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; + + 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 { + 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 + } +} diff --git a/libafl_qemu/src/emu/systemmode.rs b/libafl_qemu/src/emu/systemmode.rs index a3a4487602..685f2eebed 100644 --- a/libafl_qemu/src/emu/systemmode.rs +++ b/libafl_qemu/src/emu/systemmode.rs @@ -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 { 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 { // 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 { 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 Emulator +impl Emulator where + CM: CommandManager, S: UsesInput, { /// Write a value to a phsical guest address, including ROM areas. diff --git a/libafl_qemu/src/emu/usermode.rs b/libafl_qemu/src/emu/usermode.rs index f6c34f1afa..0866a3d5b0 100644 --- a/libafl_qemu/src/emu/usermode.rs +++ b/libafl_qemu/src/emu/usermode.rs @@ -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 Emulator +pub type StdSnapshotManager = NopSnapshotManager; + +impl Emulator where + CM: CommandManager, S: UsesInput, { /// This function gets the memory mappings from the emulator. diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 0d178923e4..645bc1613f 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -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, &S::Input) -> ExitKind, - S: State, - OT: ObserversTuple, + CM: CommandManager, ET: EmulatorModuleTuple, + H: FnMut(&mut Emulator, &S::Input) -> ExitKind, + OT: ObserversTuple, + S: State, { - inner: StatefulInProcessExecutor<'a, H, OT, S, Emulator>, + inner: StatefulInProcessExecutor<'a, H, OT, S, Emulator>, } #[cfg(emulation_mode = "usermode")] @@ -93,12 +94,13 @@ pub unsafe fn inproc_qemu_timeout_handler( } } -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, &S::Input) -> ExitKind, - S: State, - OT: ObserversTuple + Debug, + CM: CommandManager, ET: EmulatorModuleTuple + Debug, + H: FnMut(&mut Emulator, &S::Input) -> ExitKind, + OT: ObserversTuple + 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, &S::Input) -> ExitKind, - S: State, - OT: ObserversTuple, + CM: CommandManager, ET: EmulatorModuleTuple, + H: FnMut(&mut Emulator, &S::Input) -> ExitKind, + OT: ObserversTuple, + S: State, { pub fn new( - emulator: Emulator, + emulator: Emulator, 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>, + StatefulInProcessExecutor<'a, H, OT, S, Emulator>, EM, OF, Z, @@ -166,7 +169,7 @@ where Ok(Self { inner }) } - pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, Emulator> { + pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, Emulator> { &self.inner } @@ -179,16 +182,18 @@ where pub fn inner_mut( &mut self, - ) -> &mut StatefulInProcessExecutor<'a, H, OT, S, Emulator> { + ) -> &mut StatefulInProcessExecutor<'a, H, OT, S, Emulator> { &mut self.inner } } -impl<'a, CM, EH, EM, H, OT, ET, S, Z> Executor for QemuExecutor<'a, CM, EH, H, OT, ET, S> +impl<'a, CM, ED, EM, ET, H, OT, S, SM, Z> Executor + for QemuExecutor<'a, CM, ED, ET, H, OT, S, SM> where + CM: CommandManager, EM: UsesState, ET: EmulatorModuleTuple, - H: FnMut(&mut Emulator, &S::Input) -> ExitKind, + H: FnMut(&mut Emulator, &S::Input) -> ExitKind, OT: ObserversTuple, S: State + HasExecutions + Unpin, Z: UsesState, @@ -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, &S::Input) -> ExitKind, - S: State, - OT: ObserversTuple, + CM: CommandManager, ET: EmulatorModuleTuple, + H: FnMut(&mut Emulator, &S::Input) -> ExitKind, + OT: ObserversTuple, + 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, &S::Input) -> ExitKind, - S: State, - OT: ObserversTuple, + CM: CommandManager, ET: EmulatorModuleTuple, + H: FnMut(&mut Emulator, &S::Input) -> ExitKind, + OT: ObserversTuple, + 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, &S::Input) -> ExitKind, - S: State, - OT: ObserversTuple, + CM: CommandManager, ET: EmulatorModuleTuple, + H: FnMut(&mut Emulator, &S::Input) -> ExitKind, + OT: ObserversTuple, + 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, EM: UsesState, ET: EmulatorModuleTuple, H: FnMut(&S::Input) -> ExitKind + ?Sized, @@ -266,20 +275,21 @@ where Z: UsesState, { inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>, - emulator: Emulator, + emulator: Emulator, } #[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 + Debug, EM: UsesState, - EH: Debug, + ED: Debug, ET: EmulatorModuleTuple + Debug, H: FnMut(&S::Input) -> ExitKind + ?Sized, OT: ObserversTuple + Debug, S: UsesInput + Debug, + SM: Debug, SP: ShMemProvider, Z: UsesState, { @@ -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, EM: EventFirer + EventRestarter, ET: EmulatorModuleTuple, H: FnMut(&S::Input) -> ExitKind + ?Sized, @@ -303,7 +315,7 @@ where Z: HasObjective, { pub fn new( - emulator: Emulator, + emulator: Emulator, harness_fn: &'a mut H, observers: OT, fuzzer: &mut Z, @@ -336,21 +348,20 @@ where &mut self.inner } - pub fn emulator(&self) -> &Emulator { + pub fn emulator(&self) -> &Emulator { &self.emulator } - pub fn emulator_mut(&mut self) -> &Emulator { + pub fn emulator_mut(&mut self) -> &Emulator { &mut self.emulator } } #[cfg(feature = "fork")] -impl<'a, CM, EH, EM, H, OT, ET, S, Z, SP, OF> Executor - 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 + for QemuForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z> where - CM: CommandManager, - EH: EmulatorExitHandler, + CM: CommandManager, EM: EventManager, 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, EM: UsesState, ET: EmulatorModuleTuple, 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, EM: UsesState, ET: EmulatorModuleTuple, 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, EM: UsesState, ET: EmulatorModuleTuple, H: FnMut(&S::Input) -> ExitKind + ?Sized, diff --git a/libafl_qemu/src/modules/systemmode/mod.rs b/libafl_qemu/src/modules/systemmode/mod.rs index 326a26b9b3..4af5773832 100644 --- a/libafl_qemu/src/modules/systemmode/mod.rs +++ b/libafl_qemu/src/modules/systemmode/mod.rs @@ -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 + Debug + + HasInstrumentationFilter + HasInstrumentationFilter { } @@ -14,6 +17,7 @@ pub trait StdInstrumentationFilter: impl crate::modules::StdInstrumentationFilter for (Head, ()) where Head: HasInstrumentationFilter + HasInstrumentationFilter + + Debug { } diff --git a/libafl_qemu/src/modules/usermode/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs index ef7f8552c5..1e71e68632 100644 --- a/libafl_qemu/src/modules/usermode/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -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)) diff --git a/libafl_qemu/src/modules/usermode/asan_guest.rs b/libafl_qemu/src/modules/usermode/asan_guest.rs index f8ebdd2623..20582dc2d6 100644 --- a/libafl_qemu/src/modules/usermode/asan_guest.rs +++ b/libafl_qemu/src/modules/usermode/asan_guest.rs @@ -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)) } diff --git a/libafl_qemu/src/modules/usermode/mod.rs b/libafl_qemu/src/modules/usermode/mod.rs index 6f4e9a2175..e98b81efbb 100644 --- a/libafl_qemu/src/modules/usermode/mod.rs +++ b/libafl_qemu/src/modules/usermode/mod.rs @@ -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 + HasInstrumentationFilter + Debug { } impl StdInstrumentationFilter for (Head, ()) where - Head: HasInstrumentationFilter + Head: HasInstrumentationFilter + Debug { } diff --git a/libafl_qemu/src/qemu/qemu_config.rs b/libafl_qemu/src/qemu/config.rs similarity index 99% rename from libafl_qemu/src/qemu/qemu_config.rs rename to libafl_qemu/src/qemu/config.rs index d1ab3fff0a..b0aad5a636 100644 --- a/libafl_qemu/src/qemu/qemu_config.rs +++ b/libafl_qemu/src/qemu/config.rs @@ -365,9 +365,9 @@ impl From for Result { let args = config .to_string() .split(' ') - .map(std::string::ToString::to_string) + .map(ToString::to_string) .collect::>(); - 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!"))?; diff --git a/libafl_qemu/src/qemu/hooks.rs b/libafl_qemu/src/qemu/hooks.rs index b76ecac00a..3004858ac0 100644 --- a/libafl_qemu/src/qemu/hooks.rs +++ b/libafl_qemu/src/qemu/hooks.rs @@ -497,7 +497,6 @@ create_wrapper!( ); // New thread hook wrappers -#[cfg(emulation_mode = "usermode")] create_hook_types!( NewThread, fn(&mut EmulatorModules, 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>( + &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>( - &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>( &self, diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index b6767406f1..e2195419c2 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -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 { + pub fn init(args: &[String]) -> Result { if args.is_empty() { return Err(QemuInitError::EmptyArgs); } @@ -555,21 +539,15 @@ impl Qemu { .collect(); let mut argv: Vec<*const u8> = args.iter().map(|x| x.as_ptr() as *const u8).collect(); argv.push(ptr::null()); // argv is always null terminated. - let env_strs: Vec = env - .iter() - .map(|(k, v)| format!("{}={}\0", &k, &v)) - .collect(); - let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect(); - envp.push(ptr::null()); + unsafe { - #[cfg(emulation_mode = "usermode")] - qemu_user_init(argc, argv.as_ptr(), envp.as_ptr()); - #[cfg(emulation_mode = "systemmode")] - { - qemu_init(argc, argv.as_ptr()); - libc::atexit(qemu_cleanup_atexit); - libafl_qemu_sys::syx_snapshot_init(true); - } + libafl_qemu_init(argc, argv.as_ptr() as *mut *mut i8); + } + + #[cfg(emulation_mode = "systemmode")] + unsafe { + libc::atexit(qemu_cleanup_atexit); + libafl_qemu_sys::syx_snapshot_init(true); } Ok(Qemu { _private: () }) @@ -993,9 +971,9 @@ pub mod pybind { impl Qemu { #[allow(clippy::needless_pass_by_value)] #[new] - fn new(args: Vec, env: Vec<(String, String)>) -> PyResult { - let qemu = super::Qemu::init(&args, &env) - .map_err(|e| PyValueError::new_err(format!("{e}")))?; + fn new(args: Vec) -> PyResult { + let qemu = + super::Qemu::init(&args).map_err(|e| PyValueError::new_err(format!("{e}")))?; Ok(Qemu { qemu }) } diff --git a/libafl_qemu/src/qemu/systemmode.rs b/libafl_qemu/src/qemu/systemmode.rs index 338e3701b6..4f10a598ea 100644 --- a/libafl_qemu/src/qemu/systemmode.rs +++ b/libafl_qemu/src/qemu/systemmode.rs @@ -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 { diff --git a/libafl_qemu/src/sync_exit.rs b/libafl_qemu/src/sync_exit.rs index 82f58ab927..558e28bb60 100644 --- a/libafl_qemu/src/sync_exit.rs +++ b/libafl_qemu/src/sync_exit.rs @@ -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 +pub struct SyncExit where + CM: CommandManager, S: UsesInput, { - command: Rc>, + command: CM::Commands, } -impl SyncExit +impl Clone for SyncExit where + CM: CommandManager, + S: UsesInput, +{ + fn clone(&self) -> Self { + Self { + command: self.command.clone(), + } + } +} + +impl Debug for SyncExit +where + CM: CommandManager, + S: UsesInput, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "Sync Exit") + } +} + +impl SyncExit +where + CM: CommandManager, S: UsesInput, { #[must_use] - pub fn new(command: Rc>) -> Self { + pub fn new(command: CM::Commands) -> Self { Self { command } } #[must_use] - pub fn command(&self) -> Rc> { - 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 Display for SyncExit -where - S: UsesInput, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.command) - } -} diff --git a/libafl_sugar/src/qemu.rs b/libafl_sugar/src/qemu.rs index eed5c152b2..41ee724677 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -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,