diff --git a/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs b/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs index 862bbc243a..3b44b674ec 100644 --- a/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs @@ -46,13 +46,15 @@ use libafl_bolts::{ AsSlice, AsSliceMut, }; use libafl_qemu::{ - cmplog::{CmpLogMap, CmpLogObserver, QemuCmpLogChildHelper}, - edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE}, + command::NopCommandManager, elf::EasyElf, filter_qemu_args, - hooks::QemuHooks, - GuestReg, MmapPerms, Qemu, QemuExitError, QemuExitReason, QemuForkExecutor, QemuShutdownCause, - Regs, + modules::{ + cmplog::{CmpLogChildModule, CmpLogMap, CmpLogObserver}, + edges::{EdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE}, + }, + Emulator, GuestReg, MmapPerms, NopEmulatorExitHandler, QemuExitError, QemuExitReason, + QemuForkExecutor, QemuShutdownCause, Regs, }; #[cfg(unix)] use nix::unistd::dup; @@ -148,7 +150,22 @@ fn fuzz( let args: Vec = env::args().collect(); let env: Vec<(String, String)> = env::vars().collect(); - let qemu = Qemu::init(&args, &env)?; + + let emulator_modules = tuple_list!( + EdgeCoverageChildModule::default(), + CmpLogChildModule::default(), + ); + + let mut emulator = Emulator::new( + args.as_slice(), + env.as_slice(), + emulator_modules, + NopEmulatorExitHandler, + NopCommandManager, + ) + .unwrap(); + + let qemu = emulator.qemu(); let mut elf_buffer = Vec::new(); let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer)?; @@ -217,7 +234,9 @@ fn fuzz( // Beginning of a page should be properly aligned. #[allow(clippy::cast_ptr_alignment)] - let cmplog_map_ptr = cmplog.as_mut_ptr().cast::(); + let cmplog_map_ptr = cmplog + .as_mut_ptr() + .cast::(); let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) { @@ -336,16 +355,8 @@ fn fuzz( ExitKind::Ok }; - let mut hooks = QemuHooks::new( - qemu.clone(), - tuple_list!( - QemuEdgeCoverageChildHelper::default(), - QemuCmpLogChildHelper::default(), - ), - ); - let executor = QemuForkExecutor::new( - &mut hooks, + &mut emulator, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, diff --git a/fuzzers/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/fuzzbench_qemu/src/fuzzer.rs index 1c60226ca0..6c65a08592 100644 --- a/fuzzers/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench_qemu/src/fuzzer.rs @@ -46,18 +46,19 @@ use libafl_bolts::{ AsSlice, }; use libafl_qemu::{ - // asan::{init_with_asan, QemuAsanHelper}, - cmplog::{CmpLogObserver, QemuCmpLogHelper}, - edges::edges_map_mut_ptr, - edges::QemuEdgeCoverageHelper, - edges::{EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND}, + command::NopCommandManager, elf::EasyElf, filter_qemu_args, - hooks::QemuHooks, + // asan::{init_with_asan, QemuAsanHelper}, + modules::cmplog::{CmpLogModule, CmpLogObserver}, + modules::edges::{ + edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND, + }, Emulator, GuestReg, //snapshot::QemuSnapshotHelper, MmapPerms, + NopEmulatorExitHandler, Qemu, QemuExecutor, QemuExitError, @@ -358,19 +359,19 @@ fn fuzz( ExitKind::Ok }; - let mut hooks = QemuHooks::new( - qemu.clone(), - tuple_list!( - QemuEdgeCoverageHelper::default(), - QemuCmpLogHelper::default(), - // QemuAsanHelper::default(asan), - //QemuSnapshotHelper::new() - ), + let modules = tuple_list!( + EdgeCoverageModule::default(), + CmpLogModule::default(), + // QemuAsanHelper::default(asan), + //QemuSnapshotHelper::new() ); + let mut emulator = + Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?; + // Create the executor for an in-process function with one observer for edge coverage and one for the execution time let executor = QemuExecutor::new( - &mut hooks, + &mut emulator, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, diff --git a/fuzzers/qemu_cmin/build.rs b/fuzzers/qemu_cmin/build.rs index 16317b1560..36035345bf 100644 --- a/fuzzers/qemu_cmin/build.rs +++ b/fuzzers/qemu_cmin/build.rs @@ -5,7 +5,7 @@ macro_rules! assert_unique_feature { () => {}; ($first:tt $(,$rest:tt)*) => { $( - #[cfg(all(not(any(doc, feature = "clippy")), feature = $first, feature = $rest))] + #[cfg(all(not(doc), feature = $first, feature = $rest))] compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together")); )* assert_unique_feature!($($rest),*); diff --git a/fuzzers/qemu_cmin/src/fuzzer.rs b/fuzzers/qemu_cmin/src/fuzzer.rs index efbdf5a614..b4b5201062 100644 --- a/fuzzers/qemu_cmin/src/fuzzer.rs +++ b/fuzzers/qemu_cmin/src/fuzzer.rs @@ -27,10 +27,12 @@ use libafl_bolts::{ AsSlice, AsSliceMut, }; use libafl_qemu::{ - edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE}, + command::NopCommandManager, elf::EasyElf, - ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitError, - QemuExitReason, QemuForkExecutor, QemuHooks, QemuShutdownCause, Regs, + modules::edges::{EdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE}, + ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, + NopEmulatorExitHandler, Qemu, QemuExitError, QemuExitReason, QemuForkExecutor, + QemuShutdownCause, Regs, }; #[derive(Default)] @@ -65,7 +67,7 @@ impl From for Str { name = format ! ("qemu_cmin-{}", env ! ("CPU_TARGET")), version = Version::default(), about, -long_about = "Tool for generating minimizing corpus using QEMU instrumentation" +long_about = "Module for generating minimizing corpus using QEMU instrumentation" )] pub struct FuzzerOptions { #[arg(long, help = "Output directory")] @@ -219,10 +221,13 @@ pub fn fuzz() -> Result<(), Error> { ExitKind::Ok }; - let mut hooks = QemuHooks::new(qemu, tuple_list!(QemuEdgeCoverageChildHelper::default(),)); + let modules = tuple_list!(EdgeCoverageChildModule::default(),); + + let mut emulator = + Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?; let mut executor = QemuForkExecutor::new( - &mut hooks, + &mut emulator, &mut harness, tuple_list!(edges_observer), &mut fuzzer, diff --git a/fuzzers/qemu_coverage/Makefile.toml b/fuzzers/qemu_coverage/Makefile.toml index 3ec4818066..6527cafc92 100644 --- a/fuzzers/qemu_coverage/Makefile.toml +++ b/fuzzers/qemu_coverage/Makefile.toml @@ -235,8 +235,8 @@ windows_alias = "unsupported" [tasks.run_unix] command = "${TARGET_DIR}/${PROFILE_DIR}/qemu_coverage-${CARGO_MAKE_PROFILE}" args = [ - "--coverage", "${TARGET_DIR}/drcov.log", - "--input", "./corpus", + "--coverage-path", "${TARGET_DIR}/drcov.log", + "--input-dir", "./corpus", "--", "${TARGET_DIR}/libpng-harness-${CARGO_MAKE_PROFILE}", ] diff --git a/fuzzers/qemu_coverage/build.rs b/fuzzers/qemu_coverage/build.rs index 16317b1560..36035345bf 100644 --- a/fuzzers/qemu_coverage/build.rs +++ b/fuzzers/qemu_coverage/build.rs @@ -5,7 +5,7 @@ macro_rules! assert_unique_feature { () => {}; ($first:tt $(,$rest:tt)*) => { $( - #[cfg(all(not(any(doc, feature = "clippy")), feature = $first, feature = $rest))] + #[cfg(all(not(doc), feature = $first, feature = $rest))] compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together")); )* assert_unique_feature!($($rest),*); diff --git a/fuzzers/qemu_coverage/src/fuzzer.rs b/fuzzers/qemu_coverage/src/fuzzer.rs index f79a08e4b9..78d2577347 100644 --- a/fuzzers/qemu_coverage/src/fuzzer.rs +++ b/fuzzers/qemu_coverage/src/fuzzer.rs @@ -26,11 +26,13 @@ use libafl_bolts::{ AsSlice, }; use libafl_qemu::{ - drcov::QemuDrCovHelper, elf::EasyElf, ArchExtras, CallingConvention, GuestAddr, GuestReg, - MmapPerms, Qemu, QemuExecutor, QemuExitReason, QemuHooks, - QemuInstrumentationAddressRangeFilter, QemuRWError, QemuShutdownCause, Regs, + command::NopCommandManager, + elf::EasyElf, + modules::{drcov::DrCovModule, QemuInstrumentationAddressRangeFilter}, + ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, + NopEmulatorExitHandler, Qemu, QemuExecutor, QemuExitReason, QemuRWError, QemuShutdownCause, + Regs, }; -use rangemap::RangeMap; #[derive(Default)] pub struct Version; @@ -69,14 +71,14 @@ impl From for Str { name = format!("qemu_coverage-{}",env!("CPU_TARGET")), version = Version::default(), about, - long_about = "Tool for generating DrCov coverage data using QEMU instrumentation" + long_about = "Module for generating DrCov coverage data using QEMU instrumentation" )] pub struct FuzzerOptions { #[arg(long, help = "Coverage file")] - coverage: String, + coverage_path: PathBuf, #[arg(long, help = "Input directory")] - input: String, + input_dir: PathBuf, #[arg(long, help = "Timeout in seconds", default_value = "5000", value_parser = timeout_from_millis_str)] timeout: Duration, @@ -99,9 +101,8 @@ pub const MAX_INPUT_SIZE: usize = 1048576; // 1MB pub fn fuzz() { let mut options = FuzzerOptions::parse(); - let corpus_dir = PathBuf::from(options.input); - - let corpus_files = corpus_dir + let corpus_files = options + .input_dir .read_dir() .expect("Failed to read corpus dir") .collect::, io::Error>>() @@ -119,6 +120,7 @@ pub fn fuzz() { env::remove_var("LD_LIBRARY_PATH"); let env: Vec<(String, String)> = env::vars().collect(); + let qemu = Qemu::init(&options.args, &env).unwrap(); let mut elf_buffer = Vec::new(); @@ -227,40 +229,28 @@ pub fn fuzz() { let scheduler = QueueScheduler::new(); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let rangemap = qemu - .mappings() - .filter_map(|m| { - m.path() - .map(|p| ((m.start() as usize)..(m.end() as usize), p.to_string())) - .filter(|(_, p)| !p.is_empty()) - }) - .enumerate() - .fold( - RangeMap::::new(), - |mut rm, (i, (r, p))| { - rm.insert(r, (i as u16, p)); - rm - }, - ); - - let mut coverage = PathBuf::from(&options.coverage); - let coverage_name = coverage.file_stem().unwrap().to_str().unwrap(); - let coverage_extension = coverage.extension().unwrap_or_default().to_str().unwrap(); + let mut cov_path = options.coverage_path.clone(); + let coverage_name = cov_path.file_stem().unwrap().to_str().unwrap(); + let coverage_extension = cov_path.extension().unwrap_or_default().to_str().unwrap(); let core = core_id.0; - coverage.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}")); + cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}")); - let mut hooks = QemuHooks::new( + let emulator_modules = tuple_list!(DrCovModule::new( + QemuInstrumentationAddressRangeFilter::None, + cov_path, + false, + )); + + let mut emulator = Emulator::new_with_qemu( qemu, - tuple_list!(QemuDrCovHelper::new( - QemuInstrumentationAddressRangeFilter::None, - rangemap, - coverage, - false, - )), - ); + emulator_modules, + NopEmulatorExitHandler, + NopCommandManager, + ) + .unwrap(); let mut executor = QemuExecutor::new( - &mut hooks, + &mut emulator, &mut harness, (), &mut fuzzer, @@ -274,7 +264,7 @@ pub fn fuzz() { state .load_initial_inputs_by_filenames(&mut fuzzer, &mut executor, &mut mgr, &files) .unwrap_or_else(|_| { - println!("Failed to load initial corpus at {:?}", &corpus_dir); + println!("Failed to load initial corpus at {:?}", &options.input_dir); process::exit(0); }); log::debug!("We imported {} inputs from disk.", state.corpus().count()); diff --git a/fuzzers/qemu_launcher/src/client.rs b/fuzzers/qemu_launcher/src/client.rs index 0259b31552..77ac70da02 100644 --- a/fuzzers/qemu_launcher/src/client.rs +++ b/fuzzers/qemu_launcher/src/client.rs @@ -9,14 +9,17 @@ use libafl::{ }; use libafl_bolts::{core_affinity::CoreId, rands::StdRand, tuples::tuple_list}; #[cfg(feature = "injections")] -use libafl_qemu::injections::QemuInjectionHelper; +use libafl_qemu::modules::injections::InjectionModule; use libafl_qemu::{ - asan::{init_qemu_with_asan, QemuAsanHelper}, - asan_guest::{init_qemu_with_asan_guest, QemuAsanGuestHelper}, - cmplog::QemuCmpLogHelper, - edges::QemuEdgeCoverageHelper, elf::EasyElf, - ArchExtras, GuestAddr, Qemu, QemuInstrumentationAddressRangeFilter, + modules::{ + asan::{init_qemu_with_asan, AsanModule}, + asan_guest::{init_qemu_with_asan_guest, AsanGuestModule}, + cmplog::CmpLogModule, + edges::EdgeCoverageModule, + QemuInstrumentationAddressRangeFilter, + }, + ArchExtras, GuestAddr, Qemu, }; use crate::{ @@ -134,25 +137,25 @@ impl<'a> Client<'a> { log::debug!("start_pc @ {start_pc:#x}"); #[cfg(not(feature = "injections"))] - let injection_helper = None; + let injection_module = None; #[cfg(feature = "injections")] - let injection_helper = self + let injection_module = self .options .injections .as_ref() .and_then(|injections_file| { let lower = injections_file.to_lowercase(); if lower.ends_with("yaml") || lower.ends_with("yml") { - Some(QemuInjectionHelper::from_yaml(injections_file).unwrap()) + Some(InjectionModule::from_yaml(injections_file).unwrap()) } else if lower.ends_with("toml") { - Some(QemuInjectionHelper::from_toml(injections_file).unwrap()) + Some(InjectionModule::from_toml(injections_file).unwrap()) } else { None } }); - let extra_tokens = injection_helper.as_ref().map(|h| h.tokens.clone()); + let extra_tokens = injection_module.as_ref().map(|h| h.tokens.clone()); qemu.entry_break(start_pc); @@ -164,7 +167,7 @@ impl<'a> Client<'a> { let is_cmplog = self.options.is_cmplog_core(core_id); - let edge_coverage_helper = QemuEdgeCoverageHelper::new(self.coverage_filter(&qemu)?); + let edge_coverage_module = EdgeCoverageModule::new(self.coverage_filter(&qemu)?); let instance = Instance::builder() .options(self.options) @@ -174,96 +177,96 @@ impl<'a> Client<'a> { .extra_tokens(extra_tokens); if is_asan && is_cmplog { - if let Some(injection_helper) = injection_helper { + if let Some(injection_module) = injection_module { instance.build().run( tuple_list!( - edge_coverage_helper, - QemuCmpLogHelper::default(), - QemuAsanHelper::default(asan.take().unwrap()), - injection_helper, + edge_coverage_module, + CmpLogModule::default(), + AsanModule::default(asan.take().unwrap()), + injection_module, ), state, ) } else { instance.build().run( tuple_list!( - edge_coverage_helper, - QemuCmpLogHelper::default(), - QemuAsanHelper::default(asan.take().unwrap()), + edge_coverage_module, + CmpLogModule::default(), + AsanModule::default(asan.take().unwrap()), ), state, ) } } else if is_asan_guest && is_cmplog { - if let Some(injection_helper) = injection_helper { + if let Some(injection_module) = injection_module { instance.build().run( tuple_list!( - edge_coverage_helper, - QemuCmpLogHelper::default(), - QemuAsanGuestHelper::default(&qemu, asan_lib.take().unwrap()), - injection_helper + edge_coverage_module, + CmpLogModule::default(), + AsanGuestModule::default(&qemu, asan_lib.take().unwrap()), + injection_module ), state, ) } else { instance.build().run( tuple_list!( - edge_coverage_helper, - QemuCmpLogHelper::default(), - QemuAsanGuestHelper::default(&qemu, asan_lib.take().unwrap()), + edge_coverage_module, + CmpLogModule::default(), + AsanGuestModule::default(&qemu, asan_lib.take().unwrap()), ), state, ) } } else if is_asan { - if let Some(injection_helper) = injection_helper { + if let Some(injection_module) = injection_module { instance.build().run( tuple_list!( - edge_coverage_helper, - QemuAsanHelper::default(asan.take().unwrap()), - injection_helper + edge_coverage_module, + AsanModule::default(asan.take().unwrap()), + injection_module ), state, ) } else { instance.build().run( tuple_list!( - edge_coverage_helper, - QemuAsanHelper::default(asan.take().unwrap()), + edge_coverage_module, + AsanModule::default(asan.take().unwrap()), ), state, ) } } else if is_asan_guest { - let helpers = tuple_list!( - edge_coverage_helper, - QemuAsanGuestHelper::default(&qemu, asan_lib.take().unwrap()) + let modules = tuple_list!( + edge_coverage_module, + AsanGuestModule::default(&qemu, asan_lib.take().unwrap()) ); - instance.build().run(helpers, state) + instance.build().run(modules, state) } else if is_cmplog { - if let Some(injection_helper) = injection_helper { + if let Some(injection_module) = injection_module { instance.build().run( tuple_list!( - edge_coverage_helper, - QemuCmpLogHelper::default(), - injection_helper + edge_coverage_module, + CmpLogModule::default(), + injection_module ), state, ) } else { instance.build().run( - tuple_list!(edge_coverage_helper, QemuCmpLogHelper::default()), + tuple_list!(edge_coverage_module, CmpLogModule::default()), state, ) } - } else if let Some(injection_helper) = injection_helper { + } else if let Some(injection_module) = injection_module { instance .build() - .run(tuple_list!(edge_coverage_helper, injection_helper), state) + .run(tuple_list!(edge_coverage_module, injection_module), state) } else { instance .build() - .run(tuple_list!(edge_coverage_helper), state) + .run(tuple_list!(edge_coverage_module), state) } } } diff --git a/fuzzers/qemu_launcher/src/instance.rs b/fuzzers/qemu_launcher/src/instance.rs index 519eaa6b89..12ba81c432 100644 --- a/fuzzers/qemu_launcher/src/instance.rs +++ b/fuzzers/qemu_launcher/src/instance.rs @@ -38,10 +38,13 @@ use libafl_bolts::{ tuples::{tuple_list, Merge}, }; use libafl_qemu::{ - cmplog::CmpLogObserver, - edges::{edges_map_mut_ptr, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND}, - helpers::QemuHelperTuple, - Qemu, QemuExecutor, QemuHooks, + command::NopCommandManager, + modules::{ + cmplog::CmpLogObserver, + edges::{edges_map_mut_ptr, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND}, + EmulatorModuleTuple, + }, + Emulator, NopEmulatorExitHandler, Qemu, QemuExecutor, }; use typed_builder::TypedBuilder; @@ -68,12 +71,10 @@ pub struct Instance<'a, M: Monitor> { } impl<'a, M: Monitor> Instance<'a, M> { - pub fn run(&mut self, helpers: QT, state: Option) -> Result<(), Error> + pub fn run(&mut self, modules: ET, state: Option) -> Result<(), Error> where - QT: QemuHelperTuple + Debug, + ET: EmulatorModuleTuple + Debug, { - let mut hooks = QemuHooks::new(*self.qemu, helpers); - // Create an observation channel using the coverage map let edges_observer = unsafe { HitcountsMapObserver::new(VariableMapObserver::from_mut_slice( @@ -153,10 +154,18 @@ impl<'a, M: Monitor> Instance<'a, M> { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + let mut emulator = Emulator::new_with_qemu( + *self.qemu, + modules, + NopEmulatorExitHandler, + NopCommandManager, + ) + .unwrap(); + if self.options.is_cmplog_core(self.core_id) { // Create a QEMU in-process executor let executor = QemuExecutor::new( - &mut hooks, + &mut emulator, &mut harness, observers, &mut fuzzer, @@ -194,7 +203,7 @@ impl<'a, M: Monitor> Instance<'a, M> { } else { // Create a QEMU in-process executor let mut executor = QemuExecutor::new( - &mut hooks, + &mut emulator, &mut harness, observers, &mut fuzzer, diff --git a/fuzzers/qemu_systemmode/src/fuzzer_breakpoint.rs b/fuzzers/qemu_systemmode/src/fuzzer_breakpoint.rs index 8f9a16261f..74c09f950c 100644 --- a/fuzzers/qemu_systemmode/src/fuzzer_breakpoint.rs +++ b/fuzzers/qemu_systemmode/src/fuzzer_breakpoint.rs @@ -30,12 +30,13 @@ use libafl_bolts::{ use libafl_qemu::{ breakpoint::Breakpoint, command::{EndCommand, StartCommand, StdCommandManager}, - edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND}, elf::EasyElf, emu::Emulator, executor::{stateful::StatefulQemuExecutor, QemuExecutorState}, - EmulatorMemoryChunk, FastSnapshotManager, GuestPhysAddr, GuestReg, QemuHooks, - StdEmulatorExitHandler, + modules::edges::{ + edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND, + }, + FastSnapshotManager, GuestPhysAddr, GuestReg, QemuMemoryChunk, StdEmulatorExitHandler, }; // use libafl_qemu::QemuSnapshotBuilder; // for normal qemu snapshot @@ -98,14 +99,17 @@ pub fn fuzz() { // Choose Command Manager let cmd_manager = StdCommandManager::new(); + // Instantiate hooks + let modules = tuple_list!(EdgeCoverageModule::default()); + // Create emulator - let emu = Emulator::new(&args, &env, emu_exit_handler, cmd_manager).unwrap(); + let mut emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap(); // Set breakpoints of interest with corresponding commands. emu.add_breakpoint( Breakpoint::with_command( main_addr, - StartCommand::new(EmulatorMemoryChunk::phys( + StartCommand::new(QemuMemoryChunk::phys( input_addr, unsafe { MAX_INPUT_SIZE } as GuestReg, None, @@ -124,8 +128,10 @@ pub fn fuzz() { // The wrapped harness function, calling out to the LLVM-style harness let mut harness = - |input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe { - emu.run(input, qemu_executor_state) + |input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _, _, _>| unsafe { + qemu_executor_state + .emulator_mut() + .run(input) .unwrap() .try_into() .unwrap() @@ -182,11 +188,6 @@ pub fn fuzz() { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let mut hooks = QemuHooks::new( - emu.qemu().clone(), - tuple_list!(QemuEdgeCoverageHelper::default()), - ); - // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations()); let calibration_feedback = MaxMapFeedback::new(&edges_observer); @@ -197,7 +198,7 @@ pub fn fuzz() { // Create a QEMU in-process executor let mut executor = StatefulQemuExecutor::new( - &mut hooks, + &mut emu, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, diff --git a/fuzzers/qemu_systemmode/src/fuzzer_classic.rs b/fuzzers/qemu_systemmode/src/fuzzer_classic.rs index 87575d6592..441cc5e1e6 100644 --- a/fuzzers/qemu_systemmode/src/fuzzer_classic.rs +++ b/fuzzers/qemu_systemmode/src/fuzzer_classic.rs @@ -30,10 +30,14 @@ use libafl_bolts::{ AsSlice, }; use libafl_qemu::{ - edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND}, + command::NopCommandManager, elf::EasyElf, - Qemu, QemuExecutor, QemuExitError, QemuExitReason, QemuHooks, QemuRWError, QemuShutdownCause, - Regs, + executor::{stateful::StatefulQemuExecutor, QemuExecutorState}, + modules::edges::{ + edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND, + }, + Emulator, NopEmulatorExitHandler, QemuExitError, QemuExitReason, QemuRWError, + QemuShutdownCause, Regs, }; use libafl_qemu_sys::GuestPhysAddr; @@ -86,15 +90,29 @@ pub fn fuzz() { // Initialize QEMU let args: Vec = env::args().collect(); let env: Vec<(String, String)> = env::vars().collect(); - let qemu = Qemu::init(&args, &env).unwrap(); + + let emulator_modules = tuple_list!(EdgeCoverageModule::default()); + + let mut emulator = Emulator::new( + args.as_slice(), + env.as_slice(), + emulator_modules, + NopEmulatorExitHandler, + NopCommandManager, + ) + .unwrap(); + + let qemu = emulator.qemu(); qemu.set_breakpoint(main_addr); + unsafe { match qemu.run() { Ok(QemuExitReason::Breakpoint(_)) => {} _ => panic!("Unexpected QEMU exit."), } } + qemu.remove_breakpoint(main_addr); qemu.set_breakpoint(breakpoint); // BREAKPOINT @@ -111,7 +129,8 @@ pub fn fuzz() { let snap = qemu.create_fast_snapshot(true); // The wrapped harness function, calling out to the LLVM-style harness - let mut harness = |input: &BytesInput| { + let mut harness = |input: &BytesInput, state: &mut QemuExecutorState<_, _, _, _>| { + let emulator = state.emulator_mut(); let target = input.target_bytes(); let mut buf = target.as_slice(); let len = buf.len(); @@ -123,7 +142,7 @@ pub fn fuzz() { qemu.write_phys_mem(input_addr, buf); - match qemu.run() { + match emulator.qemu().run() { Ok(QemuExitReason::Breakpoint(_)) => {} Ok(QemuExitReason::End(QemuShutdownCause::HostSignal( Signal::SigInterrupt, @@ -209,12 +228,9 @@ pub fn fuzz() { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let mut hooks = - QemuHooks::new(qemu.clone(), tuple_list!(QemuEdgeCoverageHelper::default())); - // Create a QEMU in-process executor - let mut executor = QemuExecutor::new( - &mut hooks, + let mut executor = StatefulQemuExecutor::new( + &mut emulator, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, diff --git a/fuzzers/qemu_systemmode/src/fuzzer_sync_exit.rs b/fuzzers/qemu_systemmode/src/fuzzer_sync_exit.rs index 78fe90f09d..ca60a95c0f 100644 --- a/fuzzers/qemu_systemmode/src/fuzzer_sync_exit.rs +++ b/fuzzers/qemu_systemmode/src/fuzzer_sync_exit.rs @@ -28,10 +28,12 @@ use libafl_bolts::{ }; use libafl_qemu::{ command::StdCommandManager, - edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND}, emu::Emulator, executor::{stateful::StatefulQemuExecutor, QemuExecutorState}, - FastSnapshotManager, QemuHooks, StdEmulatorExitHandler, + 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 @@ -61,15 +63,20 @@ pub fn fuzz() { let cmd_manager = StdCommandManager::new(); - let emu = Emulator::new(&args, &env, emu_exit_handler, cmd_manager).unwrap(); // Create the emulator + // Choose modules to use + let modules = tuple_list!(EdgeCoverageModule::default()); + + let mut emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap(); // Create the emulator let devices = emu.list_devices(); println!("Devices = {:?}", devices); // The wrapped harness function, calling out to the LLVM-style harness let mut harness = - |input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe { - emu.run(input, qemu_executor_state) + |input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _, _, _>| unsafe { + qemu_executor_state + .emulator_mut() + .run(input) .unwrap() .try_into() .unwrap() @@ -126,11 +133,6 @@ pub fn fuzz() { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let mut hooks = QemuHooks::new( - emu.qemu().clone(), - tuple_list!(QemuEdgeCoverageHelper::default()), - ); - // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations()); let calibration_feedback = MaxMapFeedback::new(&edges_observer); @@ -141,7 +143,7 @@ pub fn fuzz() { // Create a QEMU in-process executor let mut executor = StatefulQemuExecutor::new( - &mut hooks, + &mut emu, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs index 4cec427e60..9c1579f0a3 100644 --- a/libafl/src/executors/inprocess/stateful.rs +++ b/libafl/src/executors/inprocess/stateful.rs @@ -125,7 +125,7 @@ where } self.inner.hooks.pre_exec_all(state, input); - let ret = (self.harness_fn.borrow_mut())(input, &mut self.exposed_executor_state); + let ret = self.harness_fn.borrow_mut()(input, &mut self.exposed_executor_state); self.inner.hooks.post_exec_all(state, input); self.inner.leave_target(fuzzer, state, mgr, input); diff --git a/libafl_qemu/libafl_qemu_build/src/bindings.rs b/libafl_qemu/libafl_qemu_build/src/bindings.rs index a07a914948..6469981e9b 100644 --- a/libafl_qemu/libafl_qemu_build/src/bindings.rs +++ b/libafl_qemu/libafl_qemu_build/src/bindings.rs @@ -95,7 +95,13 @@ pub fn generate( ) -> Result { let wrapper_h = build_dir.join("wrapper.h"); - store_generated_content_if_different(&wrapper_h, WRAPPER_HEADER.as_bytes(), None, None, false); + store_generated_content_if_different( + &wrapper_h, + WRAPPER_HEADER.as_bytes(), + None, + vec![], + false, + ); let bindings = bindgen::Builder::default() .derive_debug(true) diff --git a/libafl_qemu/libafl_qemu_build/src/build.rs b/libafl_qemu/libafl_qemu_build/src/build.rs index 125f05db8a..ff4793b572 100644 --- a/libafl_qemu/libafl_qemu_build/src/build.rs +++ b/libafl_qemu/libafl_qemu_build/src/build.rs @@ -9,9 +9,9 @@ use which::which; use crate::cargo_add_rpath; -const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; -const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; -const QEMU_REVISION: &str = "9d2197b73bf5e66e709f9f1669467d5c84062da0"; +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 = "4cafaa9a087dae6674b0fdc11ba34d3e6a8364d2"; #[allow(clippy::module_name_repetitions)] pub struct BuildResult { diff --git a/libafl_qemu/libafl_qemu_build/src/lib.rs b/libafl_qemu/libafl_qemu_build/src/lib.rs index e8b82bca5e..b346bad0b4 100644 --- a/libafl_qemu/libafl_qemu_build/src/lib.rs +++ b/libafl_qemu/libafl_qemu_build/src/lib.rs @@ -25,6 +25,9 @@ mod build; pub use build::build; +#[rustversion::nightly] +use crate::build::QEMU_REVISION; + const LLVM_VERSION_MAX: i32 = 33; static mut CARGO_RPATH: Option> = None; @@ -257,7 +260,7 @@ pub fn store_generated_content_if_different( file_to_update: &Path, fresh_content: &[u8], content_file_to_update: Option>, - first_line_prefix: Option<&str>, + first_line_prefixes: Vec<&str>, force_regeneration: bool, ) { let mut must_rewrite_file = true; @@ -299,7 +302,12 @@ pub fn store_generated_content_if_different( }; if must_rewrite_file { - if let Some(prefix) = first_line_prefix { + println!( + "cargo::warning={} has been regenerated.", + file_to_update.file_name().unwrap().to_str().unwrap() + ); + + for prefix in first_line_prefixes { writeln!(&file_to_check, "{prefix}").expect("Could not write prefix"); } @@ -309,6 +317,79 @@ pub fn store_generated_content_if_different( } } +#[rustversion::nightly] +fn parse_stub( + stub_bindings_file: &Path, + current_rustc_version: &Version, +) -> (bool, bool, Option>) { + let semver_re = Regex::new(r"/\* (.*) \*/").unwrap(); + let qemu_hash_re = Regex::new(r"/\* qemu git hash: (.*) \*/").unwrap(); + + if let Ok(stub_file) = File::open(stub_bindings_file) { + let mut stub_rdr = BufReader::new(stub_file); + + let mut first_line = String::new(); // rustc version + let mut second_line = String::new(); // qemu hash + let mut stub_content = Vec::::new(); + + assert!( + stub_rdr + .read_line(&mut first_line) + .expect("Could not read first line") + > 0, + "Error while reading first line." + ); + + assert!( + stub_rdr + .read_line(&mut second_line) + .expect("Could not read second line") + > 0, + "Error while reading second line." + ); + + if let Some((_, [version_str])) = semver_re + .captures_iter(&first_line) + .next() + .map(|caps| caps.extract()) + { + // The first line matches the regex + + if let Some((_, [qemu_hash_str])) = qemu_hash_re + .captures_iter(&second_line) + .next() + .map(|caps| caps.extract()) + { + // The second line matches the regex + + if let Ok(version) = Version::parse(version_str) { + // The first line contains a version + + stub_rdr + .read_to_end(&mut stub_content) + .expect("could not read stub content"); + + return ( + (current_rustc_version > &version) || (qemu_hash_str != QEMU_REVISION), + false, + Some(stub_content), + ); + } + } + } + + stub_rdr.seek(SeekFrom::Start(0)).unwrap(); + stub_rdr + .read_to_end(&mut stub_content) + .expect("could not read stub content"); + + (true, true, Some(stub_content)) + } else { + // No stub file stored + (true, true, None) + } +} + #[rustversion::nightly] pub fn maybe_generate_stub_bindings( cpu_target: &str, @@ -319,58 +400,11 @@ pub fn maybe_generate_stub_bindings( if env::var("CARGO_CFG_DOC").is_ok() && cpu_target == "x86_64" && emulation_mode == "usermode" { let current_rustc_version = rustc_version::version().expect("Could not get current rustc version"); - let semver_re = Regex::new(r"/\* (.*) \*/").unwrap(); // We only try to store the stub if the current rustc version is strictly bigger than the one used to generate - // the versioned stub. - let (try_generate, force_regeneration, stub_content): (bool, bool, Option>) = - if let Ok(stub_file) = File::open(stub_bindings_file) { - let mut stub_rdr = BufReader::new(stub_file); - - let mut first_line = String::new(); - let mut stub_content = Vec::::new(); - assert!( - stub_rdr - .read_line(&mut first_line) - .expect("Could not read first line") - > 0, - "Error while reading first line." - ); - - if let Some((_, [version_str])) = semver_re - .captures_iter(&first_line) - .next() - .map(|caps| caps.extract()) - { - // The first line matches the regex - - if let Ok(version) = Version::parse(version_str) { - // The first line contains a version - - stub_rdr - .read_to_end(&mut stub_content) - .expect("could not read stub content"); - (current_rustc_version > version, false, Some(stub_content)) - } else { - stub_rdr.seek(SeekFrom::Start(0)).unwrap(); - stub_rdr - .read_to_end(&mut stub_content) - .expect("could not read stub content"); - - (true, true, Some(stub_content)) - } - } else { - stub_rdr.seek(SeekFrom::Start(0)).unwrap(); - stub_rdr - .read_to_end(&mut stub_content) - .expect("could not read stub content"); - - (true, true, Some(stub_content)) - } - } else { - // No stub file stored - (true, true, None) - }; + // the versioned stub or the qemu hash differs. + let (try_generate, force_regeneration, stub_content) = + parse_stub(stub_bindings_file, ¤t_rustc_version); let header = format!("/* {current_rustc_version} */"); @@ -381,7 +415,10 @@ pub fn maybe_generate_stub_bindings( .expect("Could not read generated bindings file") .as_slice(), stub_content, - Some(header.as_str()), + vec![ + header.as_str(), + format!("/* qemu git hash: {QEMU_REVISION} */").as_str(), + ], force_regeneration, ); } diff --git a/libafl_qemu/libafl_qemu_sys/src/lib.rs b/libafl_qemu/libafl_qemu_sys/src/lib.rs index 6ddc509ccd..069eaff8fd 100644 --- a/libafl_qemu/libafl_qemu_sys/src/lib.rs +++ b/libafl_qemu/libafl_qemu_sys/src/lib.rs @@ -21,6 +21,16 @@ use strum_macros::EnumIter; #[cfg(all(not(feature = "clippy"), target_os = "linux"))] mod bindings { + #![allow(non_upper_case_globals)] + #![allow(non_camel_case_types)] + #![allow(non_snake_case)] + #![allow(improper_ctypes)] + #![allow(unused_mut)] + #![allow(unused)] + #![allow(unused_variables)] + #![allow(clippy::all)] + #![allow(clippy::pedantic)] + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } #[cfg(all(not(feature = "clippy"), target_os = "linux"))] @@ -107,8 +117,6 @@ macro_rules! extern_c_checked { use core::ops::BitAnd; use std::ffi::c_void; -#[cfg(feature = "python")] -use pyo3::{pyclass, pymethods, IntoPy, PyObject, Python}; #[cfg(any(feature = "clippy", not(target_os = "linux")))] pub use x86_64_stub_bindings::*; @@ -125,19 +133,6 @@ pub type GuestVirtAddr = crate::vaddr; pub type GuestHwAddrInfo = crate::qemu_plugin_hwaddr; -#[derive(Debug)] -#[repr(C)] -#[cfg(target_os = "linux")] -#[cfg_attr(feature = "python", pyclass(unsendable))] -pub struct MapInfo { - start: GuestAddr, - end: GuestAddr, - offset: GuestAddr, - path: Option, - flags: i32, - is_priv: i32, -} - #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct FatPtr(pub *const c_void, pub *const c_void); @@ -214,80 +209,3 @@ extern_c_checked! { ); pub fn libafl_qemu_gdb_reply(buf: *const u8, len: usize); } - -#[cfg(target_os = "linux")] -#[cfg_attr(feature = "python", pymethods)] -impl MapInfo { - #[must_use] - pub fn start(&self) -> GuestAddr { - self.start - } - - #[must_use] - pub fn end(&self) -> GuestAddr { - self.end - } - - #[must_use] - pub fn offset(&self) -> GuestAddr { - self.offset - } - - #[must_use] - pub fn path(&self) -> Option<&String> { - self.path.as_ref() - } - - #[must_use] - pub fn flags(&self) -> MmapPerms { - MmapPerms::try_from(self.flags).unwrap() - } - - #[must_use] - pub fn is_priv(&self) -> bool { - self.is_priv != 0 - } -} - -impl MmapPerms { - #[must_use] - pub fn readable(&self) -> bool { - matches!( - self, - MmapPerms::Read - | MmapPerms::ReadWrite - | MmapPerms::ReadExecute - | MmapPerms::ReadWriteExecute - ) - } - - #[must_use] - pub fn writable(&self) -> bool { - matches!( - self, - MmapPerms::Write - | MmapPerms::ReadWrite - | MmapPerms::WriteExecute - | MmapPerms::ReadWriteExecute - ) - } - - #[must_use] - pub fn executable(&self) -> bool { - matches!( - self, - MmapPerms::Execute - | MmapPerms::ReadExecute - | MmapPerms::WriteExecute - | MmapPerms::ReadWriteExecute - ) - } -} - -#[cfg(feature = "python")] -impl IntoPy for MmapPerms { - fn into_py(self, py: Python) -> PyObject { - let n: i32 = self.into(); - n.into_py(py) - } -} diff --git a/libafl_qemu/libafl_qemu_sys/src/usermode.rs b/libafl_qemu/libafl_qemu_sys/src/usermode.rs index a378a96f5c..dfbb2d6efa 100644 --- a/libafl_qemu/libafl_qemu_sys/src/usermode.rs +++ b/libafl_qemu/libafl_qemu_sys/src/usermode.rs @@ -2,9 +2,11 @@ use core::{slice::from_raw_parts, str::from_utf8_unchecked}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use paste::paste; +#[cfg(feature = "python")] +use pyo3::{pyclass, pymethods, IntoPy, PyObject, Python}; use strum_macros::EnumIter; -use crate::{extern_c_checked, libafl_mapinfo, strlen, GuestAddr, MapInfo}; +use crate::{extern_c_checked, libafl_mapinfo, strlen, GuestAddr, MmapPerms}; extern_c_checked! { pub fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32; @@ -30,6 +32,95 @@ pub enum VerifyAccess { Write = libc::PROT_READ | libc::PROT_WRITE, } +#[derive(Debug)] +#[repr(C)] +#[cfg(target_os = "linux")] +#[cfg_attr(feature = "python", pyclass(unsendable))] +pub struct MapInfo { + start: GuestAddr, + end: GuestAddr, + offset: GuestAddr, + path: Option, + flags: i32, + is_priv: i32, +} + +#[cfg(target_os = "linux")] +#[cfg_attr(feature = "python", pymethods)] +impl MapInfo { + #[must_use] + pub fn start(&self) -> GuestAddr { + self.start + } + + #[must_use] + pub fn end(&self) -> GuestAddr { + self.end + } + + #[must_use] + pub fn offset(&self) -> GuestAddr { + self.offset + } + + #[must_use] + pub fn path(&self) -> Option<&String> { + self.path.as_ref() + } + + #[must_use] + pub fn flags(&self) -> MmapPerms { + MmapPerms::try_from(self.flags).unwrap() + } + + #[must_use] + pub fn is_priv(&self) -> bool { + self.is_priv != 0 + } +} + +impl MmapPerms { + #[must_use] + pub fn readable(&self) -> bool { + matches!( + self, + MmapPerms::Read + | MmapPerms::ReadWrite + | MmapPerms::ReadExecute + | MmapPerms::ReadWriteExecute + ) + } + + #[must_use] + pub fn writable(&self) -> bool { + matches!( + self, + MmapPerms::Write + | MmapPerms::ReadWrite + | MmapPerms::WriteExecute + | MmapPerms::ReadWriteExecute + ) + } + + #[must_use] + pub fn executable(&self) -> bool { + matches!( + self, + MmapPerms::Execute + | MmapPerms::ReadExecute + | MmapPerms::WriteExecute + | MmapPerms::ReadWriteExecute + ) + } +} + +#[cfg(feature = "python")] +impl IntoPy for MmapPerms { + fn into_py(self, py: Python) -> PyObject { + let n: i32 = self.into(); + n.into_py(py) + } +} impl From for MapInfo { fn from(map_info: libafl_mapinfo) -> Self { let path: Option = if map_info.path.is_null() { 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 7397eefcc4..807316d98d 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,4 +1,5 @@ /* 1.81.0-nightly */ +/* qemu git hash: 712661c8200804c0bb0750f237048c6c3da2d863 */ /* automatically generated by rust-bindgen 0.69.4 */ #[repr(C)] @@ -2341,7 +2342,6 @@ pub type DeviceReset = ::std::option::Option, data: u64, @@ -13630,19 +13630,19 @@ extern "C" { ) -> usize; } extern "C" { - pub fn libafl_qemu_remove_hooks_at( + pub fn libafl_qemu_remove_instruction_hooks_at( addr: target_ulong, invalidate: ::std::os::raw::c_int, ) -> usize; } extern "C" { - pub fn libafl_qemu_remove_hook( + pub fn libafl_qemu_remove_instruction_hook( num: usize, invalidate: ::std::os::raw::c_int, ) -> ::std::os::raw::c_int; } extern "C" { - pub fn libafl_search_hook(addr: target_ulong) -> *mut libafl_hook; + pub fn libafl_search_instruction_hook(addr: target_ulong) -> *mut libafl_hook; } extern "C" { pub fn libafl_add_backdoor_hook( diff --git a/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs b/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs index 8b314a610d..0918ab0b2b 100644 --- a/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs +++ b/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs @@ -1,4 +1,5 @@ /* 1.81.0-nightly */ +/* qemu git hash: 712661c8200804c0bb0750f237048c6c3da2d863 */ /* automatically generated by rust-bindgen 0.69.4 */ pub const _STDINT_H: u32 = 1; @@ -36,7 +37,7 @@ pub const __STDC_IEC_60559_COMPLEX__: u32 = 201404; pub const __STDC_ISO_10646__: u32 = 201706; pub const __GNU_LIBRARY__: u32 = 6; pub const __GLIBC__: u32 = 2; -pub const __GLIBC_MINOR__: u32 = 38; +pub const __GLIBC_MINOR__: u32 = 39; pub const _SYS_CDEFS_H: u32 = 1; pub const __glibc_c99_flexarr_available: u32 = 1; pub const __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI: u32 = 0; @@ -60,6 +61,7 @@ pub const _BITS_TIME64_H: u32 = 1; pub const _BITS_WCHAR_H: u32 = 1; pub const _BITS_STDINT_INTN_H: u32 = 1; pub const _BITS_STDINT_UINTN_H: u32 = 1; +pub const _BITS_STDINT_LEAST_H: u32 = 1; pub const INT8_MIN: i32 = -128; pub const INT16_MIN: i32 = -32768; pub const INT32_MIN: i32 = -2147483648; diff --git a/libafl_qemu/src/breakpoint.rs b/libafl_qemu/src/breakpoint.rs index 0beadb2b77..3c7459b9e8 100644 --- a/libafl_qemu/src/breakpoint.rs +++ b/libafl_qemu/src/breakpoint.rs @@ -14,7 +14,8 @@ use libafl_qemu_sys::GuestAddr; use crate::{ command::{CommandManager, IsCommand}, - EmulatorExitHandler, Qemu, QemuHelperTuple, + modules::EmulatorModuleTuple, + EmulatorExitHandler, Qemu, }; #[repr(transparent)] @@ -23,16 +24,16 @@ 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, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { id: BreakpointId, addr: GuestAddr, - cmd: Option>>, + cmd: Option>>, disable_on_trigger: bool, enabled: bool, } @@ -53,81 +54,81 @@ impl Default for BreakpointId { } } -impl Hash for Breakpoint +impl Hash for Breakpoint where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { fn hash(&self, state: &mut H) { self.id.hash(state); } } -impl PartialEq for Breakpoint +impl PartialEq for Breakpoint where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { fn eq(&self, other: &Self) -> bool { self.id == other.id } } -impl Eq for Breakpoint +impl Eq for Breakpoint where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { } -impl Display for Breakpoint +impl Display for Breakpoint where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "Breakpoint @vaddr 0x{:x}", self.addr) } } -impl Borrow for Breakpoint +impl Borrow for Breakpoint where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { fn borrow(&self) -> &BreakpointId { &self.id } } -impl Borrow for Breakpoint +impl Borrow for Breakpoint where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { fn borrow(&self) -> &GuestAddr { &self.addr } } -impl Breakpoint +impl Breakpoint where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { // Emu will return with the breakpoint as exit reason. #[must_use] @@ -143,7 +144,7 @@ where // Emu will execute the command when it meets the breakpoint. #[must_use] - pub fn with_command + 'static>( + pub fn with_command + 'static>( addr: GuestAddr, cmd: C, disable_on_trigger: bool, @@ -167,21 +168,21 @@ where self.addr } - pub fn enable(&mut self, qemu: &Qemu) { + pub fn enable(&mut self, qemu: Qemu) { if !self.enabled { qemu.set_breakpoint(self.addr); self.enabled = true; } } - pub fn disable(&mut self, qemu: &Qemu) { + pub fn disable(&mut self, qemu: Qemu) { if self.enabled { qemu.remove_breakpoint(self.addr.into()); self.enabled = false; } } - 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 2e12e848a7..2f74072a91 100644 --- a/libafl_qemu/src/command/mod.rs +++ b/libafl_qemu/src/command/mod.rs @@ -16,20 +16,21 @@ use libafl_bolts::AsSlice; use num_enum::TryFromPrimitive; #[cfg(emulation_mode = "systemmode")] -use crate::QemuInstrumentationPagingFilter; +use crate::modules::QemuInstrumentationPagingFilter; use crate::{ command::parser::{ EndCommandParser, InputPhysCommandParser, InputVirtCommandParser, LoadCommandParser, NativeCommandParser, SaveCommandParser, StartPhysCommandParser, StartVirtCommandParser, VaddrFilterAllowRangeCommandParser, VersionCommandParser, }, - executor::QemuExecutorState, get_exit_arch_regs, + modules::{ + EmulatorModuleTuple, HasInstrumentationFilter, IsFilter, + QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter, + }, sync_exit::ExitArgs, - Emulator, EmulatorExitHandler, EmulatorMemoryChunk, ExitHandlerError, ExitHandlerResult, - GuestReg, HasInstrumentationFilter, InputLocation, IsFilter, IsSnapshotManager, Qemu, - QemuHelperTuple, QemuInstrumentationAddressRangeFilter, QemuRWError, Regs, - StdEmulatorExitHandler, StdInstrumentationFilter, CPU, + Emulator, EmulatorExitHandler, ExitHandlerError, ExitHandlerResult, GuestReg, InputLocation, + IsSnapshotManager, Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorExitHandler, CPU, }; pub mod parser; @@ -52,21 +53,21 @@ 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 + pub struct $name where - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { native_command_parsers: - HashMap, QT, S>>>, + HashMap, ET, S>>>, } - impl $name + impl $name where - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -78,7 +79,7 @@ macro_rules! define_std_command_manager { dyn NativeCommandParser< Self, StdEmulatorExitHandler, - QT, + ET, S, >, >),*] @@ -87,7 +88,7 @@ macro_rules! define_std_command_manager { let mut parsers: HashMap< GuestReg, - Box, QT, S>>, + Box, ET, S>>, > = HashMap::new(); for parser in native_parsers { @@ -102,17 +103,17 @@ macro_rules! define_std_command_manager { } } - impl CommandManager, QT, S> for $name + impl CommandManager, ET, S> for $name where - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { fn parse( &self, qemu: Qemu, - ) -> Result, QT, S>>, CommandError> { + ) -> 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])?; @@ -126,10 +127,10 @@ macro_rules! define_std_command_manager { } } - impl Debug for $name + impl Debug for $name where - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -138,10 +139,10 @@ macro_rules! define_std_command_manager { } } - impl Default for $name + impl Default for $name where - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -152,6 +153,19 @@ macro_rules! define_std_command_manager { }; } +pub struct NopCommandManager; + +impl CommandManager for NopCommandManager +where + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, +{ + fn parse(&self, _qemu: Qemu) -> Result>, CommandError> { + Ok(Rc::new(NopCommand)) + } +} + define_std_command_manager!( StdCommandManager, [ @@ -167,13 +181,13 @@ define_std_command_manager!( ] ); -pub trait CommandManager: Sized +pub trait CommandManager: Sized where - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { - fn parse(&self, qemu: Qemu) -> Result>, CommandError>; + fn parse(&self, qemu: Qemu) -> Result>, CommandError>; } #[derive(Debug, Clone, Enum, TryFromPrimitive)] @@ -184,12 +198,12 @@ 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: Debug + Display where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { /// Used to know whether the command can be run during a backdoor, or if it is necessary to go out of /// the QEMU VM to run the command. @@ -202,11 +216,10 @@ where /// - `InnerHandlerResult`: How the high-level handler should behave fn run( &self, - emu: &Emulator, - qemu_executor_state: &mut QemuExecutorState, + emu: &mut Emulator, input: &S::Input, ret_reg: Option, - ) -> Result>, ExitHandlerError>; + ) -> Result>, ExitHandlerError>; } #[cfg(emulation_mode = "systemmode")] @@ -227,14 +240,44 @@ impl From for CommandError { } } +#[derive(Debug, Clone)] +pub struct NopCommand; + +impl Display for NopCommand { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "NopCommand") + } +} + +impl IsCommand for NopCommand +where + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, +{ + fn usable_at_runtime(&self) -> bool { + true + } + + fn run( + &self, + _emu: &mut Emulator, + _input: &S::Input, + _ret_reg: Option, + ) -> Result>, ExitHandlerError> { + Ok(None) + } +} + #[derive(Debug, Clone)] pub struct SaveCommand; -impl IsCommand, QT, S> for SaveCommand +impl IsCommand, ET, S> for SaveCommand where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -244,27 +287,25 @@ where fn run( &self, - emu: &Emulator, QT, S>, - #[cfg(emulation_mode = "systemmode")] qemu_executor_state: &mut QemuExecutorState, - #[cfg(not(emulation_mode = "systemmode"))] _qemu_executor_state: &mut QemuExecutorState< - QT, - S, - >, + emu: &mut Emulator, ET, S>, _input: &S::Input, _ret_reg: Option, - ) -> Result, QT, S>>, ExitHandlerError> + ) -> Result, ET, S>>, ExitHandlerError> { let qemu = emu.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)?; + { + 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)?; + } #[cfg(emulation_mode = "systemmode")] { - let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut(); + let emulator_modules = emu.modules_mut().modules_mut(); let mut allowed_paging_ids = HashSet::new(); @@ -273,7 +314,7 @@ where let paging_filter = HasInstrumentationFilter::::filter_mut( - qemu_helpers, + emulator_modules, ); *paging_filter = QemuInstrumentationPagingFilter::AllowList(allowed_paging_ids); @@ -286,11 +327,11 @@ where #[derive(Debug, Clone)] pub struct LoadCommand; -impl IsCommand, QT, S> for LoadCommand +impl IsCommand, ET, S> for LoadCommand where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -300,11 +341,10 @@ where fn run( &self, - emu: &Emulator, QT, S>, - _qemu_executor_state: &mut QemuExecutorState, + emu: &mut Emulator, ET, S>, _input: &S::Input, _ret_reg: Option, - ) -> Result, QT, S>>, ExitHandlerError> + ) -> Result, ET, S>>, ExitHandlerError> { let qemu = emu.qemu(); let emu_exit_handler = emu.exit_handler().borrow_mut(); @@ -328,15 +368,15 @@ where #[derive(Debug, Clone)] pub struct InputCommand { - location: EmulatorMemoryChunk, + location: QemuMemoryChunk, cpu: CPU, } -impl IsCommand, QT, S> for InputCommand +impl IsCommand, ET, S> for InputCommand where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -346,11 +386,10 @@ where fn run( &self, - emu: &Emulator, QT, S>, - _qemu_executor_state: &mut QemuExecutorState, + emu: &mut Emulator, ET, S>, input: &S::Input, ret_reg: Option, - ) -> Result, QT, S>>, ExitHandlerError> + ) -> Result, ET, S>>, ExitHandlerError> { let qemu = emu.qemu(); @@ -366,14 +405,14 @@ where #[derive(Debug, Clone)] pub struct StartCommand { - input_location: EmulatorMemoryChunk, + input_location: QemuMemoryChunk, } -impl IsCommand, QT, S> for StartCommand +impl IsCommand, ET, S> for StartCommand where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -383,11 +422,10 @@ where fn run( &self, - emu: &Emulator, QT, S>, - _qemu_executor_state: &mut QemuExecutorState, + emu: &mut Emulator, ET, S>, input: &S::Input, ret_reg: Option, - ) -> Result, QT, S>>, ExitHandlerError> + ) -> Result, ET, S>>, ExitHandlerError> { let emu_exit_handler = emu.exit_handler().borrow_mut(); let qemu = emu.qemu(); @@ -420,11 +458,11 @@ where #[derive(Debug, Clone)] pub struct EndCommand(Option); -impl IsCommand, QT, S> for EndCommand +impl IsCommand, ET, S> for EndCommand where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -434,11 +472,10 @@ where fn run( &self, - emu: &Emulator, QT, S>, - _qemu_executor_state: &mut QemuExecutorState, + emu: &mut Emulator, ET, S>, _input: &S::Input, _ret_reg: Option, - ) -> Result, QT, S>>, ExitHandlerError> + ) -> Result, ET, S>>, ExitHandlerError> { let emu_exit_handler = emu.exit_handler().borrow_mut(); @@ -462,11 +499,11 @@ where #[derive(Debug, Clone)] pub struct VersionCommand(u64); -impl IsCommand, QT, S> for VersionCommand +impl IsCommand, ET, S> for VersionCommand where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -476,11 +513,10 @@ where fn run( &self, - _emu: &Emulator, QT, S>, - _qemu_executor_state: &mut QemuExecutorState, + _emu: &mut Emulator, ET, S>, _input: &S::Input, _ret_reg: Option, - ) -> Result, QT, S>>, ExitHandlerError> + ) -> Result, ET, S>>, ExitHandlerError> { let guest_version = self.0; @@ -503,11 +539,11 @@ where } #[cfg(emulation_mode = "systemmode")] -impl IsCommand, QT, S> for PagingFilterCommand +impl IsCommand, ET, S> for PagingFilterCommand where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -517,16 +553,15 @@ where fn run( &self, - _emu: &Emulator, QT, S>, - qemu_executor_state: &mut QemuExecutorState, + emu: &mut Emulator, ET, S>, _input: &S::Input, _ret_reg: Option, - ) -> Result, QT, S>>, ExitHandlerError> + ) -> Result, ET, S>>, ExitHandlerError> { - let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut(); + let qemu_modules = emu.modules_mut().modules_mut(); let paging_filter = - HasInstrumentationFilter::::filter_mut(qemu_helpers); + HasInstrumentationFilter::::filter_mut(qemu_modules); *paging_filter = self.filter.clone(); @@ -534,11 +569,11 @@ where } } -impl IsCommand, QT, S> for AddressRangeFilterCommand +impl IsCommand, ET, S> for AddressRangeFilterCommand where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -549,17 +584,16 @@ where #[allow(clippy::type_complexity)] // TODO: refactor with correct type. fn run( &self, - _emu: &Emulator, QT, S>, - qemu_executor_state: &mut QemuExecutorState, + _emu: &mut Emulator, ET, S>, _input: &S::Input, _ret_reg: Option, - ) -> Result, QT, S>>, ExitHandlerError> + ) -> Result, ET, S>>, ExitHandlerError> { - let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut(); + let qemu_modules = &mut (); let addr_range_filter = HasInstrumentationFilter::::filter_mut( - qemu_helpers, + qemu_modules, ); *addr_range_filter = self.filter.clone(); @@ -639,7 +673,7 @@ impl Display for PagingFilterCommand { impl StartCommand { #[must_use] - pub fn new(input_location: EmulatorMemoryChunk) -> Self { + pub fn new(input_location: QemuMemoryChunk) -> Self { Self { input_location } } } @@ -653,7 +687,7 @@ impl EndCommand { impl InputCommand { #[must_use] - pub fn new(location: EmulatorMemoryChunk, cpu: CPU) -> Self { + pub fn new(location: QemuMemoryChunk, cpu: CPU) -> Self { Self { location, cpu } } } diff --git a/libafl_qemu/src/command/parser.rs b/libafl_qemu/src/command/parser.rs index d1b9cedfd6..6a2d695cb5 100644 --- a/libafl_qemu/src/command/parser.rs +++ b/libafl_qemu/src/command/parser.rs @@ -13,19 +13,22 @@ use crate::{ bindings, CommandError, CommandManager, EndCommand, FilterCommand, InputCommand, IsCommand, LoadCommand, NativeExitKind, SaveCommand, StartCommand, VersionCommand, }, + modules::{ + EmulatorModuleTuple, QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter, + }, sync_exit::ExitArgs, - EmulatorExitHandler, EmulatorMemoryChunk, GuestReg, IsSnapshotManager, Qemu, QemuHelperTuple, - QemuInstrumentationAddressRangeFilter, Regs, StdEmulatorExitHandler, StdInstrumentationFilter, + EmulatorExitHandler, GuestReg, IsSnapshotManager, Qemu, QemuMemoryChunk, Regs, + StdEmulatorExitHandler, }; pub static EMU_EXIT_KIND_MAP: OnceLock>> = OnceLock::new(); -pub trait NativeCommandParser +pub trait NativeCommandParser where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { fn command_id(&self) -> GuestReg; @@ -33,16 +36,16 @@ where &self, qemu: Qemu, arch_regs_map: &'static EnumMap, - ) -> Result>, CommandError>; + ) -> Result>, CommandError>; } pub struct InputPhysCommandParser; -impl NativeCommandParser, QT, S> +impl NativeCommandParser, ET, S> for InputPhysCommandParser where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -54,12 +57,12 @@ where &self, qemu: Qemu, arch_regs_map: &'static EnumMap, - ) -> Result, QT, S>>, CommandError> { + ) -> 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(InputCommand::new( - EmulatorMemoryChunk::phys( + QemuMemoryChunk::phys( input_phys_addr, max_input_size, Some(qemu.current_cpu().unwrap()), @@ -70,12 +73,12 @@ where } pub struct InputVirtCommandParser; -impl NativeCommandParser, QT, S> +impl NativeCommandParser, ET, S> for InputVirtCommandParser where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -87,24 +90,24 @@ where &self, qemu: Qemu, arch_regs_map: &'static EnumMap, - ) -> Result, QT, S>>, CommandError> { + ) -> 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(InputCommand::new( - EmulatorMemoryChunk::virt(input_virt_addr, max_input_size, qemu.current_cpu().unwrap()), + QemuMemoryChunk::virt(input_virt_addr, max_input_size, qemu.current_cpu().unwrap()), qemu.current_cpu().unwrap(), ))) } } pub struct StartPhysCommandParser; -impl NativeCommandParser, QT, S> +impl NativeCommandParser, ET, S> for StartPhysCommandParser where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -116,11 +119,11 @@ where &self, qemu: Qemu, arch_regs_map: &'static EnumMap, - ) -> Result, QT, S>>, CommandError> { + ) -> 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(EmulatorMemoryChunk::phys( + Ok(Rc::new(StartCommand::new(QemuMemoryChunk::phys( input_phys_addr, max_input_size, Some(qemu.current_cpu().unwrap()), @@ -129,12 +132,12 @@ where } pub struct StartVirtCommandParser; -impl NativeCommandParser, QT, S> +impl NativeCommandParser, ET, S> for StartVirtCommandParser where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -146,11 +149,11 @@ where &self, qemu: Qemu, arch_regs_map: &'static EnumMap, - ) -> Result, QT, S>>, CommandError> { + ) -> 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(EmulatorMemoryChunk::virt( + Ok(Rc::new(StartCommand::new(QemuMemoryChunk::virt( input_virt_addr, max_input_size, qemu.current_cpu().unwrap(), @@ -159,11 +162,11 @@ where } pub struct SaveCommandParser; -impl NativeCommandParser, QT, S> for SaveCommandParser +impl NativeCommandParser, ET, S> for SaveCommandParser where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -175,17 +178,17 @@ where &self, _qemu: Qemu, _arch_regs_map: &'static EnumMap, - ) -> Result, QT, S>>, CommandError> { + ) -> Result, ET, S>>, CommandError> { Ok(Rc::new(SaveCommand)) } } pub struct LoadCommandParser; -impl NativeCommandParser, QT, S> for LoadCommandParser +impl NativeCommandParser, ET, S> for LoadCommandParser where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -197,17 +200,17 @@ where &self, _qemu: Qemu, _arch_regs_map: &'static EnumMap, - ) -> Result, QT, S>>, CommandError> { + ) -> Result, ET, S>>, CommandError> { Ok(Rc::new(LoadCommand)) } } pub struct EndCommandParser; -impl NativeCommandParser, QT, S> for EndCommandParser +impl NativeCommandParser, ET, S> for EndCommandParser where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -219,7 +222,7 @@ where &self, qemu: Qemu, arch_regs_map: &'static EnumMap, - ) -> Result, QT, S>>, CommandError> { + ) -> Result, ET, S>>, CommandError> { 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(); @@ -238,12 +241,12 @@ where } pub struct VersionCommandParser; -impl NativeCommandParser, QT, S> +impl NativeCommandParser, ET, S> for VersionCommandParser where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -255,7 +258,7 @@ where &self, qemu: Qemu, arch_regs_map: &'static EnumMap, - ) -> Result, QT, S>>, CommandError> { + ) -> Result, ET, S>>, CommandError> { let client_version = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?; Ok(Rc::new(VersionCommand::new(client_version))) @@ -263,12 +266,12 @@ where } pub struct VaddrFilterAllowRangeCommandParser; -impl NativeCommandParser, QT, S> +impl NativeCommandParser, ET, S> for VaddrFilterAllowRangeCommandParser where - CM: CommandManager, QT, S>, - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + CM: CommandManager, ET, S>, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { @@ -280,7 +283,7 @@ where &self, qemu: Qemu, arch_regs_map: &'static EnumMap, - ) -> Result, QT, S>>, CommandError> { + ) -> Result, ET, S>>, CommandError> { let vaddr_start: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?; let vaddr_end: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?; diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs new file mode 100644 index 0000000000..a14a03bc8d --- /dev/null +++ b/libafl_qemu/src/emu/hooks.rs @@ -0,0 +1,1240 @@ +#![allow(clippy::missing_transmute_annotations)] + +#[cfg(emulation_mode = "usermode")] +use std::ptr::addr_of_mut; +use std::{fmt::Debug, marker::PhantomData, mem::transmute, pin::Pin, ptr}; + +use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple}; +use libafl_qemu_sys::{CPUArchStatePtr, FatPtr, GuestAddr, GuestUsize, TCGTemp}; + +#[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, + PostSyscallHookId, PreSyscallHook, PreSyscallHookId, SyscallHookResult, +}; +#[cfg(emulation_mode = "usermode")] +use crate::qemu::{ + CrashHookClosure, NewThreadHookClosure, PostSyscallHookClosure, PostSyscallHookFn, + PreSyscallHookClosure, PreSyscallHookFn, +}; +use crate::{ + 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, + 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, + HookState, InstructionHook, InstructionHookClosure, InstructionHookFn, InstructionHookId, + QemuHooks, ReadExecHook, ReadExecNHook, ReadGenHook, ReadHookId, WriteExecHook, + WriteExecNHook, WriteGenHook, WriteHookId, + }, + MemAccessInfo, Qemu, +}; + +macro_rules! get_raw_hook { + ($h:expr, $replacement:expr, $fntype:ty) => { + match $h { + Hook::Function(_) | Hook::Closure(_) => Some($replacement as $fntype), + Hook::Raw(r) => { + let v: $fntype = transmute(r); + Some(v) + } + Hook::Empty => None, + } + }; +} + +macro_rules! hook_to_repr { + ($h:expr) => { + match $h { + Hook::Function(f) => HookRepr::Function(f as *const libc::c_void), + Hook::Closure(c) => HookRepr::Closure(transmute(c)), + Hook::Raw(_) => HookRepr::Empty, // managed by emu + Hook::Empty => HookRepr::Empty, + } + }; +} + +static mut EMULATOR_TOOLS: *mut () = ptr::null_mut(); + +#[cfg(emulation_mode = "usermode")] +static mut CRASH_HOOKS: Vec = vec![]; + +#[cfg(emulation_mode = "usermode")] +pub extern "C" fn crash_hook_wrapper(target_sig: i32) +where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, +{ + unsafe { + let hooks = Qemu::get().unwrap().hooks(); + + for crash_hook in &mut (*addr_of_mut!(CRASH_HOOKS)) { + match crash_hook { + HookRepr::Function(ptr) => { + let func: fn(QemuHooks, i32) = transmute(*ptr); + func(hooks, target_sig); + } + HookRepr::Closure(ptr) => { + let func: &mut Box = transmute(ptr); + func(hooks, target_sig); + } + HookRepr::Empty => (), + } + } + } +} + +/// High-level `Emulator` modules, using `QemuHooks`. +#[derive(Debug)] +pub struct EmulatorModules +where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, +{ + qemu: Qemu, + modules: Pin>, + hooks: EmulatorHooks, + phantom: PhantomData, +} + +/// Hook collection, +#[derive(Debug)] +pub struct EmulatorHooks +where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, +{ + qemu_hooks: QemuHooks, + phantom: PhantomData<(ET, S)>, + + instruction_hooks: Vec>>, + backdoor_hooks: Vec>>, + edge_hooks: Vec>>>, + block_hooks: Vec>>>, + read_hooks: Vec>>>, + write_hooks: Vec>>>, + cmp_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, +} + +impl EmulatorHooks +where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, +{ + #[must_use] + pub fn new(qemu_hooks: QemuHooks) -> Self { + Self { + qemu_hooks, + phantom: PhantomData, + instruction_hooks: Vec::new(), + backdoor_hooks: Vec::new(), + edge_hooks: Vec::new(), + block_hooks: Vec::new(), + read_hooks: Vec::new(), + write_hooks: Vec::new(), + cmp_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(), + } + } + + pub fn instruction_closure( + &mut self, + addr: GuestAddr, + hook: InstructionHookClosure, + invalidate_block: bool, + ) -> InstructionHookId { + let fat: FatPtr = unsafe { transmute(hook) }; + + self.instruction_hooks + .push(Box::pin((InstructionHookId::invalid(), fat))); + + unsafe { + let hook_state = &mut self + .instruction_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .1 as *mut FatPtr; + + let id = self.qemu_hooks.add_instruction_hooks( + &mut *hook_state, + addr, + closure_instruction_hook_wrapper::, + invalidate_block, + ); + self.instruction_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .0 = id; + id + } + } + + pub fn instructions( + &mut self, + addr: GuestAddr, + hook: InstructionHook, + invalidate_block: bool, + ) -> Option { + match hook { + Hook::Function(f) => Some(self.instruction_function(addr, f, invalidate_block)), + Hook::Closure(c) => Some(self.instruction_closure(addr, c, invalidate_block)), + Hook::Raw(r) => { + let z: *const () = ptr::null::<()>(); + Some( + self.qemu_hooks + .add_instruction_hooks(z, addr, r, invalidate_block), + ) + } + Hook::Empty => None, + } + } + + pub fn instruction_function( + &mut self, + addr: GuestAddr, + hook: InstructionHookFn, + invalidate_block: bool, + ) -> InstructionHookId { + unsafe { + self.qemu_hooks.add_instruction_hooks( + transmute(hook), + addr, + func_instruction_hook_wrapper::, + invalidate_block, + ) + } + } + + pub fn edges( + &mut self, + generation_hook: EdgeGenHook, + execution_hook: EdgeExecHook, + ) -> EdgeHookId { + unsafe { + let gen = get_raw_hook!( + generation_hook, + edge_gen_hook_wrapper::, + unsafe extern "C" fn( + &mut HookState<1, EdgeHookId>, + src: GuestAddr, + dest: GuestAddr, + ) -> u64 + ); + + let exec = get_raw_hook!( + execution_hook, + edge_0_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<1, EdgeHookId>, id: u64) + ); + + self.edge_hooks.push(Box::pin(HookState::new( + EdgeHookId::invalid(), + hook_to_repr!(generation_hook), + HookRepr::Empty, + [hook_to_repr!(execution_hook)], + ))); + + let hook_state = &mut *ptr::from_mut::>( + self.edge_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut(), + ); + + let id = self.qemu_hooks.add_edge_hooks(hook_state, gen, exec); + + self.edge_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .set_id(id); + + id + } + } + + pub fn blocks( + &mut self, + generation_hook: BlockGenHook, + post_generation_hook: BlockPostGenHook, + execution_hook: BlockExecHook, + ) -> BlockHookId { + unsafe { + let gen = get_raw_hook!( + generation_hook, + block_gen_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<1, BlockHookId>, pc: GuestAddr) -> u64 + ); + + let postgen = get_raw_hook!( + post_generation_hook, + block_post_gen_hook_wrapper::, + unsafe extern "C" fn( + &mut HookState<1, BlockHookId>, + pc: GuestAddr, + block_length: GuestUsize, + ) + ); + + let exec = get_raw_hook!( + execution_hook, + block_0_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<1, BlockHookId>, id: u64) + ); + + self.block_hooks.push(Box::pin(HookState::new( + BlockHookId::invalid(), + hook_to_repr!(generation_hook), + hook_to_repr!(post_generation_hook), + [hook_to_repr!(execution_hook)], + ))); + + let hook_state = &mut *ptr::from_mut::>( + self.block_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut(), + ); + + let id = self + .qemu_hooks + .add_block_hooks(hook_state, gen, postgen, exec); + + self.block_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .set_id(id); + + id + } + } + + #[allow(clippy::similar_names)] + pub fn reads( + &mut self, + generation_hook: ReadGenHook, + execution_hook_1: ReadExecHook, + execution_hook_2: ReadExecHook, + execution_hook_4: ReadExecHook, + execution_hook_8: ReadExecHook, + execution_hook_n: ReadExecNHook, + ) -> ReadHookId { + unsafe { + let gen = get_raw_hook!( + generation_hook, + read_gen_hook_wrapper::, + unsafe extern "C" fn( + &mut HookState<5, ReadHookId>, + pc: GuestAddr, + addr: *mut TCGTemp, + info: MemAccessInfo, + ) -> u64 + ); + let exec1 = get_raw_hook!( + execution_hook_1, + read_0_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<5, ReadHookId>, id: u64, addr: GuestAddr) + ); + let exec2 = get_raw_hook!( + execution_hook_2, + read_1_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<5, ReadHookId>, id: u64, addr: GuestAddr) + ); + let exec4 = get_raw_hook!( + execution_hook_4, + read_2_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<5, ReadHookId>, id: u64, addr: GuestAddr) + ); + let exec8 = get_raw_hook!( + execution_hook_8, + read_3_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<5, ReadHookId>, id: u64, addr: GuestAddr) + ); + let execn = get_raw_hook!( + execution_hook_n, + read_4_exec_hook_wrapper::, + unsafe extern "C" fn( + &mut HookState<5, ReadHookId>, + id: u64, + addr: GuestAddr, + size: usize, + ) + ); + + self.read_hooks.push(Box::pin(HookState::new( + ReadHookId::invalid(), + hook_to_repr!(generation_hook), + HookRepr::Empty, + [ + hook_to_repr!(execution_hook_1), + hook_to_repr!(execution_hook_2), + hook_to_repr!(execution_hook_4), + hook_to_repr!(execution_hook_8), + hook_to_repr!(execution_hook_n), + ], + ))); + + let hook_state = &mut *ptr::from_mut::>( + self.read_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut(), + ); + + let id = self + .qemu_hooks + .add_read_hooks(hook_state, gen, exec1, exec2, exec4, exec8, execn); + + self.read_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .set_id(id); + + id + } + } + + #[allow(clippy::similar_names)] + pub fn writes( + &mut self, + generation_hook: WriteGenHook, + execution_hook_1: WriteExecHook, + execution_hook_2: WriteExecHook, + execution_hook_4: WriteExecHook, + execution_hook_8: WriteExecHook, + execution_hook_n: WriteExecNHook, + ) -> WriteHookId { + unsafe { + let gen = get_raw_hook!( + generation_hook, + write_gen_hook_wrapper::, + unsafe extern "C" fn( + &mut HookState<5, WriteHookId>, + pc: GuestAddr, + addr: *mut TCGTemp, + info: MemAccessInfo, + ) -> u64 + ); + let exec1 = get_raw_hook!( + execution_hook_1, + write_0_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<5, WriteHookId>, id: u64, addr: GuestAddr) + ); + let exec2 = get_raw_hook!( + execution_hook_2, + write_1_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<5, WriteHookId>, id: u64, addr: GuestAddr) + ); + let exec4 = get_raw_hook!( + execution_hook_4, + write_2_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<5, WriteHookId>, id: u64, addr: GuestAddr) + ); + let exec8 = get_raw_hook!( + execution_hook_8, + write_3_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<5, WriteHookId>, id: u64, addr: GuestAddr) + ); + let execn = get_raw_hook!( + execution_hook_n, + write_4_exec_hook_wrapper::, + unsafe extern "C" fn( + &mut HookState<5, WriteHookId>, + id: u64, + addr: GuestAddr, + size: usize, + ) + ); + + self.write_hooks.push(Box::pin(HookState::new( + WriteHookId::invalid(), + hook_to_repr!(generation_hook), + HookRepr::Empty, + [ + hook_to_repr!(execution_hook_1), + hook_to_repr!(execution_hook_2), + hook_to_repr!(execution_hook_4), + hook_to_repr!(execution_hook_8), + hook_to_repr!(execution_hook_n), + ], + ))); + + let hook_state = &mut *ptr::from_mut::>( + self.write_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut(), + ); + + let id = self + .qemu_hooks + .add_write_hooks(hook_state, gen, exec1, exec2, exec4, exec8, execn); + + self.write_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .set_id(id); + + id + } + } + + pub fn cmps( + &mut self, + generation_hook: CmpGenHook, + execution_hook_1: CmpExecHook, + execution_hook_2: CmpExecHook, + execution_hook_4: CmpExecHook, + execution_hook_8: CmpExecHook, + ) -> CmpHookId { + unsafe { + let gen = get_raw_hook!( + generation_hook, + cmp_gen_hook_wrapper::, + unsafe extern "C" fn( + &mut HookState<4, CmpHookId>, + pc: GuestAddr, + size: usize, + ) -> u64 + ); + let exec1 = get_raw_hook!( + execution_hook_1, + cmp_0_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<4, CmpHookId>, id: u64, v0: u8, v1: u8) + ); + let exec2 = get_raw_hook!( + execution_hook_2, + cmp_1_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<4, CmpHookId>, id: u64, v0: u16, v1: u16) + ); + let exec4 = get_raw_hook!( + execution_hook_4, + cmp_2_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<4, CmpHookId>, id: u64, v0: u32, v1: u32) + ); + let exec8 = get_raw_hook!( + execution_hook_8, + cmp_3_exec_hook_wrapper::, + unsafe extern "C" fn(&mut HookState<4, CmpHookId>, id: u64, v0: u64, v1: u64) + ); + + self.cmp_hooks.push(Box::pin(HookState::new( + CmpHookId::invalid(), + hook_to_repr!(generation_hook), + HookRepr::Empty, + [ + hook_to_repr!(execution_hook_1), + hook_to_repr!(execution_hook_2), + hook_to_repr!(execution_hook_4), + hook_to_repr!(execution_hook_8), + ], + ))); + + let hook_state = &mut *ptr::from_mut::>( + self.cmp_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut(), + ); + + let id = self + .qemu_hooks + .add_cmp_hooks(hook_state, gen, exec1, exec2, exec4, exec8); + + self.cmp_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .set_id(id); + + id + } + } + + pub fn backdoor_closure(&mut self, hook: BackdoorHookClosure) -> BackdoorHookId { + unsafe { + let fat: FatPtr = transmute(hook); + self.backdoor_hooks + .push(Box::pin((BackdoorHookId::invalid(), fat))); + + let hook_state = &mut self + .backdoor_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .1 as *mut FatPtr; + + let id = self + .qemu_hooks + .add_backdoor_hook(&mut *hook_state, closure_backdoor_hook_wrapper::); + + self.backdoor_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .0 = id; + + id + } + } + + pub fn backdoor_function( + &self, + hook: fn(&mut EmulatorModules, Option<&mut S>, cpu: CPUArchStatePtr, pc: GuestAddr), + ) -> BackdoorHookId { + unsafe { + self.qemu_hooks + .add_backdoor_hook(transmute(hook), func_backdoor_hook_wrapper::) + } + } + + pub fn backdoor(&mut self, hook: BackdoorHook) -> Option { + match hook { + Hook::Function(f) => Some(self.backdoor_function(f)), + Hook::Closure(c) => Some(self.backdoor_closure(c)), + Hook::Raw(r) => { + let z: *const () = ptr::null::<()>(); + Some(self.qemu_hooks.add_backdoor_hook(z, r)) + } + Hook::Empty => None, // TODO error type + } + } +} + +#[cfg(emulation_mode = "usermode")] +impl EmulatorHooks +where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, +{ + #[allow(clippy::type_complexity)] + pub fn syscalls(&mut self, hook: PreSyscallHook) -> Option { + match hook { + Hook::Function(f) => Some(self.syscalls_function(f)), + Hook::Closure(c) => Some(self.syscalls_closure(c)), + Hook::Raw(r) => { + let z: *const () = ptr::null::<()>(); + Some(self.qemu_hooks.add_pre_syscall_hook(z, r)) + } + Hook::Empty => None, // TODO error type + } + } + + #[allow(clippy::type_complexity)] + pub fn syscalls_function(&mut self, hook: PreSyscallHookFn) -> PreSyscallHookId { + unsafe { + self.qemu_hooks + .add_pre_syscall_hook(transmute(hook), func_pre_syscall_hook_wrapper::) + } + } + + #[allow(clippy::type_complexity)] + pub fn syscalls_closure(&mut self, hook: PreSyscallHookClosure) -> PreSyscallHookId { + unsafe { + let fat: FatPtr = transmute(hook); + + self.pre_syscall_hooks + .push(Box::pin((PreSyscallHookId::invalid(), fat))); + + let hook_state = &mut self + .pre_syscall_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .1 as *mut FatPtr; + + let id = self + .qemu_hooks + .add_pre_syscall_hook(&mut *hook_state, closure_pre_syscall_hook_wrapper::); + self.pre_syscall_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .0 = id; + id + } + } + + #[allow(clippy::type_complexity)] + pub fn after_syscalls(&mut self, hook: PostSyscallHook) -> Option { + match hook { + Hook::Function(f) => Some(self.after_syscalls_function(f)), + Hook::Closure(c) => Some(self.after_syscalls_closure(c)), + Hook::Raw(r) => { + let z: *const () = ptr::null::<()>(); + Some(self.qemu_hooks.add_post_syscall_hook(z, r)) + } + Hook::Empty => None, // TODO error type + } + } + + #[allow(clippy::type_complexity)] + pub fn after_syscalls_function(&mut self, hook: PostSyscallHookFn) -> PostSyscallHookId { + unsafe { + self.qemu_hooks + .add_post_syscall_hook(transmute(hook), func_post_syscall_hook_wrapper::) + } + } + + #[allow(clippy::type_complexity)] + pub fn after_syscalls_closure( + &mut self, + hook: PostSyscallHookClosure, + ) -> PostSyscallHookId { + unsafe { + let fat: FatPtr = transmute(hook); + self.post_syscall_hooks + .push(Box::pin((PostSyscallHookId::invalid(), fat))); + + let hooks_state = &mut self + .post_syscall_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .1 as *mut FatPtr; + + let id = self.qemu_hooks.add_post_syscall_hook( + &mut *hooks_state, + closure_post_syscall_hook_wrapper::, + ); + self.post_syscall_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .0 = id; + id + } + } + + 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>, 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 + .push(HookRepr::Function(hook as *const libc::c_void)); + } + + pub fn crash_closure(&mut self, hook: CrashHookClosure) { + unsafe { + self.qemu_hooks.set_crash_hook(crash_hook_wrapper::); + self.crash_hooks.push(HookRepr::Closure(transmute(hook))); + } + } +} + +impl Default for EmulatorHooks +where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, +{ + fn default() -> Self { + Self::new(QemuHooks::get().unwrap()) + } +} + +impl EmulatorModules +where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, +{ + pub(super) fn new(qemu: Qemu, modules: ET) -> Pin> { + let mut modules = Box::pin(Self { + qemu, + modules: Box::pin(modules), + hooks: EmulatorHooks::default(), + phantom: PhantomData, + }); + + // re-translate blocks with hooks + // qemu.flush_jit(); + // -> it should be useless, since EmulatorModules must be init before QEMU ever runs + + // Set global EmulatorModules pointer + unsafe { + if EMULATOR_TOOLS.is_null() { + EMULATOR_TOOLS = ptr::from_mut::(modules.as_mut().get_mut()) as *mut (); + } else { + panic!("Emulator Modules have already been set and is still active. It is not supported to have multiple instances of `EmulatorModules` at the same time yet.") + } + } + + unsafe { + // We give access to EmulatorModuleTuple during init, the compiler complains (for good reasons) + // TODO: We should find a way to be able to check for a module without giving full access to the tuple. + modules + .modules + .init_modules_all(Self::emulator_modules_mut_unchecked()); + } + + modules + } + + #[must_use] + pub fn qemu(&self) -> Qemu { + self.qemu + } + + #[must_use] + pub fn modules(&self) -> &ET { + self.modules.as_ref().get_ref() + } + + pub fn modules_mut(&mut self) -> &mut ET { + self.modules.as_mut().get_mut() + } + + pub fn hooks_mut(&mut self) -> &mut EmulatorHooks { + &mut self.hooks + } + + pub fn first_exec_all(&mut self) { + unsafe { + self.modules + .as_mut() + .get_mut() + .first_exec_all(Self::emulator_modules_mut_unchecked()); + } + } + + pub fn pre_exec_all(&mut self, input: &S::Input) { + unsafe { + self.modules + .as_mut() + .get_mut() + .pre_exec_all(Self::emulator_modules_mut_unchecked(), input); + } + } + + pub fn post_exec_all( + &mut self, + input: &S::Input, + observers: &mut OT, + exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + { + unsafe { + self.modules.as_mut().get_mut().post_exec_all( + Self::emulator_modules_mut_unchecked(), + input, + observers, + exit_kind, + ); + } + } + + /// Get a reference to the first (type) matching member of the tuple. + #[must_use] + pub fn get(&self) -> Option<&T> + where + T: EmulatorModule, + { + self.modules.match_first_type::() + } + + /// Get a mutable reference to the first (type) matching member of the tuple. + pub fn get_mut(&mut self) -> Option<&mut T> + where + T: EmulatorModule, + { + self.modules.match_first_type_mut::() + } + + /// Get a mutable reference to `EmulatorModules` (supposedly initialized beforehand). + /// + /// # Safety + /// + /// This will always return a reference, but it will be incorrect if `EmulatorModules` has not + /// been initialized previously. + /// The user should also be consistent with the generic use (it will suppose they are the same + /// as the ones used at initialization time). + #[must_use] + pub unsafe fn emulator_modules_mut_unchecked<'a>() -> &'a mut EmulatorModules { + #[cfg(debug_assertions)] + { + (EMULATOR_TOOLS as *mut EmulatorModules) + .as_mut() + .unwrap() + } + + #[cfg(not(debug_assertions))] + { + &mut *(EMULATOR_TOOLS as *mut EmulatorModules) + } + } + + /// Get a mutable reference to `EmulatorModules`. + /// This version is safer than `emulator_modules_mut_unchecked` since it will check that + /// initialization has occurred previously. + /// + /// # Safety + /// + /// This version still presents some unsafeness: The user should be consistent with the + /// generic use (it will suppose they are the same as the ones used at initialization time). + #[must_use] + pub unsafe fn emulator_modules_mut<'a>() -> Option<&'a mut EmulatorModules> { + unsafe { (EMULATOR_TOOLS as *mut EmulatorModules).as_mut() } + } + + pub fn instructions( + &mut self, + addr: GuestAddr, + hook: InstructionHook, + invalidate_block: bool, + ) -> Option { + self.hooks.instructions(addr, hook, invalidate_block) + } + + pub fn instruction_function( + &mut self, + addr: GuestAddr, + hook: fn(&mut EmulatorModules, Option<&mut S>, GuestAddr), + invalidate_block: bool, + ) -> InstructionHookId { + self.hooks + .instruction_function(addr, hook, invalidate_block) + } + + pub fn instruction_closure( + &mut self, + addr: GuestAddr, + hook: InstructionHookClosure, + invalidate_block: bool, + ) -> InstructionHookId { + self.hooks.instruction_closure(addr, hook, invalidate_block) + } + + pub fn edges( + &mut self, + generation_hook: EdgeGenHook, + execution_hook: EdgeExecHook, + ) -> EdgeHookId { + self.hooks.edges(generation_hook, execution_hook) + } + + pub fn blocks( + &mut self, + generation_hook: BlockGenHook, + post_generation_hook: BlockPostGenHook, + execution_hook: BlockExecHook, + ) -> BlockHookId { + self.hooks + .blocks(generation_hook, post_generation_hook, execution_hook) + } + + #[allow(clippy::similar_names)] + pub fn reads( + &mut self, + generation_hook: ReadGenHook, + execution_hook_1: ReadExecHook, + execution_hook_2: ReadExecHook, + execution_hook_4: ReadExecHook, + execution_hook_8: ReadExecHook, + execution_hook_n: ReadExecNHook, + ) -> ReadHookId { + self.hooks.reads( + generation_hook, + execution_hook_1, + execution_hook_2, + execution_hook_4, + execution_hook_8, + execution_hook_n, + ) + } + + #[allow(clippy::similar_names)] + pub fn writes( + &mut self, + generation_hook: WriteGenHook, + execution_hook_1: WriteExecHook, + execution_hook_2: WriteExecHook, + execution_hook_4: WriteExecHook, + execution_hook_8: WriteExecHook, + execution_hook_n: WriteExecNHook, + ) -> WriteHookId { + self.hooks.writes( + generation_hook, + execution_hook_1, + execution_hook_2, + execution_hook_4, + execution_hook_8, + execution_hook_n, + ) + } + + pub fn cmps( + &mut self, + generation_hook: CmpGenHook, + execution_hook_1: CmpExecHook, + execution_hook_2: CmpExecHook, + execution_hook_4: CmpExecHook, + execution_hook_8: CmpExecHook, + ) -> CmpHookId { + self.hooks.cmps( + generation_hook, + execution_hook_1, + execution_hook_2, + execution_hook_4, + execution_hook_8, + ) + } + + pub fn backdoor(&mut self, hook: BackdoorHook) -> Option { + self.hooks.backdoor(hook) + } + + pub fn backdoor_function(&mut self, hook: BackdoorHookFn) -> BackdoorHookId { + self.hooks.backdoor_function(hook) + } + + pub fn backdoor_closure(&mut self, hook: BackdoorHookClosure) -> BackdoorHookId { + self.hooks.backdoor_closure(hook) + } +} + +/// Usermode-only high-level functions +#[cfg(emulation_mode = "usermode")] +impl EmulatorModules +where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, +{ + #[allow(clippy::type_complexity)] + pub fn syscalls(&mut self, hook: PreSyscallHook) -> Option { + self.hooks.syscalls(hook) + } + + #[allow(clippy::type_complexity)] + pub fn syscalls_function( + &mut self, + hook: fn( + &mut EmulatorModules, + Option<&mut S>, + sys_num: i32, + a0: GuestAddr, + a1: GuestAddr, + a2: GuestAddr, + a3: GuestAddr, + a4: GuestAddr, + a5: GuestAddr, + a6: GuestAddr, + a7: GuestAddr, + ) -> SyscallHookResult, + ) -> PreSyscallHookId { + self.hooks.syscalls_function(hook) + } + + #[allow(clippy::type_complexity)] + pub fn syscalls_closure( + &mut self, + hook: Box< + dyn for<'a> FnMut( + &'a mut EmulatorModules, + Option<&'a mut S>, + i32, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + ) -> SyscallHookResult, + >, + ) -> PreSyscallHookId { + self.hooks.syscalls_closure(hook) + } + + #[allow(clippy::type_complexity)] + pub fn after_syscalls(&mut self, hook: PostSyscallHook) -> Option { + self.hooks.after_syscalls(hook) + } + + #[allow(clippy::type_complexity)] + pub fn after_syscalls_function( + &mut self, + hook: fn( + &mut EmulatorModules, + Option<&mut S>, + res: GuestAddr, + sys_num: i32, + a0: GuestAddr, + a1: GuestAddr, + a2: GuestAddr, + a3: GuestAddr, + a4: GuestAddr, + a5: GuestAddr, + a6: GuestAddr, + a7: GuestAddr, + ) -> GuestAddr, + ) -> PostSyscallHookId { + self.hooks.after_syscalls_function(hook) + } + + #[allow(clippy::type_complexity)] + pub fn after_syscalls_closure( + &mut self, + hook: Box< + dyn for<'a> FnMut( + &'a mut EmulatorModules, + Option<&mut S>, + GuestAddr, + i32, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + ) -> GuestAddr, + >, + ) -> PostSyscallHookId { + 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>, 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); + } + + pub fn crash_closure(&mut self, hook: CrashHookClosure) { + self.hooks.crash_closure(hook); + } +} + +impl Drop for EmulatorModules +where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, +{ + fn drop(&mut self) { + // Make the global pointer null at drop time + unsafe { + EMULATOR_TOOLS = ptr::null_mut(); + } + } +} diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index 601a89de60..8dfb316907 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -10,6 +10,7 @@ use std::{ cell::{OnceCell, Ref, RefCell, RefMut}, hash::Hash, ops::Add, + pin::Pin, rc::Rc, }; @@ -17,10 +18,11 @@ use hashbrown::HashMap; use libafl::{ executors::ExitKind, inputs::HasTargetBytes, + observers::ObserversTuple, state::{HasExecutions, State}, }; use libafl_bolts::os::unix_signals::Signal; -use libafl_qemu_sys::{CPUArchStatePtr, GuestUsize}; +use libafl_qemu_sys::GuestUsize; pub use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr}; #[cfg(emulation_mode = "usermode")] pub use libafl_qemu_sys::{MapInfo, MmapPerms, MmapPermsIter}; @@ -30,15 +32,14 @@ use typed_builder::TypedBuilder; use crate::{ breakpoint::Breakpoint, command::{CommandError, InputCommand, IsCommand}, - executor::QemuExecutorState, sync_exit::SyncExit, - sys::TCGTemp, - BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, EmulatorMemoryChunk, GuestReg, HookData, - HookId, InstructionHookId, MemAccessInfo, Qemu, QemuExitError, QemuExitReason, QemuHelperTuple, - QemuInitError, QemuRWError, QemuShutdownCause, QemuSnapshotCheckResult, ReadHookId, Regs, - StdInstrumentationFilter, WriteHookId, CPU, + GuestReg, Qemu, QemuExitError, QemuExitReason, QemuInitError, QemuMemoryChunk, QemuRWError, + QemuShutdownCause, QemuSnapshotCheckResult, Regs, CPU, }; +mod hooks; +pub use hooks::*; + #[cfg(emulation_mode = "usermode")] mod usermode; @@ -47,10 +48,14 @@ mod systemmode; #[cfg(emulation_mode = "systemmode")] pub use systemmode::*; -use crate::{breakpoint::BreakpointId, command::CommandManager}; +use crate::{ + breakpoint::BreakpointId, + command::CommandManager, + modules::{EmulatorModuleTuple, StdInstrumentationFilter}, +}; -type CommandRef = Rc>; -type BreakpointMutRef = Rc>>; +type CommandRef = Rc>; +type BreakpointMutRef = Rc>>; #[derive(Clone, Copy)] pub enum GuestAddrKind { @@ -59,16 +64,16 @@ pub enum GuestAddrKind { } #[derive(Debug, Clone)] -pub enum EmulatorExitResult +pub enum EmulatorExitResult where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { 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. + Breakpoint(Rc>>), // Breakpoint triggered. Contains the address of the trigger. + SyncExit(Rc>>), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL. } #[derive(Debug, Clone)] @@ -80,17 +85,34 @@ pub enum EmulatorExitError { } #[derive(Debug, Clone)] -pub enum ExitHandlerResult +pub enum ExitHandlerResult where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { - 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. + 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. } +impl ExitHandlerResult +where + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, +{ + #[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), + _ => None, + } + } +} + #[derive(Debug, Clone)] pub enum ExitHandlerError { QemuExitReasonError(EmulatorExitError), @@ -115,16 +137,16 @@ pub enum SnapshotManagerCheckError { SnapshotCheckError(QemuSnapshotCheckResult), } -impl TryFrom> for ExitKind +impl TryFrom> for ExitKind where - CM: CommandManager + Debug, - E: EmulatorExitHandler, - QT: QemuHelperTuple + Debug, - S: State + HasExecutions + Debug, + CM: CommandManager + Debug, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple + Debug, + S: Unpin + State + HasExecutions + Debug, { type Error = String; - fn try_from(value: ExitHandlerResult) -> Result { + fn try_from(value: ExitHandlerResult) -> Result { match value { ExitHandlerResult::ReturnToHarness(unhandled_qemu_exit) => { Err(format!("Unhandled QEMU exit: {:?}", &unhandled_qemu_exit)) @@ -181,22 +203,19 @@ pub struct SnapshotId { } pub trait IsSnapshotManager: Debug + Clone { - fn save(&mut self, qemu: &Qemu) -> SnapshotId; - fn restore( - &mut self, - snapshot_id: &SnapshotId, - qemu: &Qemu, - ) -> Result<(), SnapshotManagerError>; + 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, + qemu: Qemu, ) -> Result; fn check( &self, reference_snapshot_id: &SnapshotId, - qemu: &Qemu, + qemu: Qemu, ) -> Result<(), SnapshotManagerCheckError> { let check_result = self .do_check(reference_snapshot_id, qemu) @@ -210,24 +229,21 @@ pub trait IsSnapshotManager: Debug + Clone { } } -// TODO: Rework with generics for command handlers? -pub trait EmulatorExitHandler: Sized + Debug + Clone +pub trait EmulatorExitHandler: Sized + Debug + Clone where - QT: QemuHelperTuple, - S: State + HasExecutions, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { - fn qemu_pre_run>( - emu: &Emulator, - qemu_executor_state: &mut QemuExecutorState, + fn qemu_pre_exec>( + emu: &mut Emulator, input: &S::Input, ); - fn qemu_post_run>( - emu: &Emulator, - exit_reason: Result, EmulatorExitError>, - qemu_executor_state: &mut QemuExecutorState, + fn qemu_post_exec>( + emu: &mut Emulator, + exit_reason: Result, EmulatorExitError>, input: &S::Input, - ) -> Result>, ExitHandlerError>; + ) -> Result>, ExitHandlerError>; } /// Special kind of Exit handler with no data embedded. @@ -236,24 +252,22 @@ where #[derive(Clone, Debug)] pub struct NopEmulatorExitHandler; -impl EmulatorExitHandler for NopEmulatorExitHandler +impl EmulatorExitHandler for NopEmulatorExitHandler where - QT: QemuHelperTuple, - S: State + HasExecutions, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { - fn qemu_pre_run>( - _: &Emulator, - _: &mut QemuExecutorState, + fn qemu_pre_exec>( + _: &mut Emulator, _: &S::Input, ) { } - fn qemu_post_run>( - _: &Emulator, - exit_reason: Result, EmulatorExitError>, - _: &mut QemuExecutorState, + fn qemu_post_exec>( + _: &mut Emulator, + exit_reason: Result, EmulatorExitError>, _: &S::Input, - ) -> Result>, ExitHandlerError> { + ) -> Result>, ExitHandlerError> { match exit_reason { Ok(reason) => Ok(Some(ExitHandlerResult::ReturnToHarness(reason))), Err(error) => Err(error)?, @@ -263,14 +277,14 @@ where #[derive(Debug, Clone)] pub struct InputLocation { - mem_chunk: EmulatorMemoryChunk, + mem_chunk: QemuMemoryChunk, cpu: CPU, ret_register: Option, } impl InputLocation { #[must_use] - pub fn new(mem_chunk: EmulatorMemoryChunk, cpu: CPU, ret_register: Option) -> Self { + pub fn new(mem_chunk: QemuMemoryChunk, cpu: CPU, ret_register: Option) -> Self { Self { mem_chunk, cpu, @@ -326,35 +340,37 @@ where } // TODO: replace handlers with generics to permit compile-time customization of handlers -impl EmulatorExitHandler for StdEmulatorExitHandler +impl EmulatorExitHandler for StdEmulatorExitHandler where - QT: QemuHelperTuple + StdInstrumentationFilter + Debug, - S: State + HasExecutions, + ET: EmulatorModuleTuple + StdInstrumentationFilter + Debug, + S: Unpin + State + HasExecutions, S::Input: HasTargetBytes, SM: IsSnapshotManager, { - fn qemu_pre_run>( - emu: &Emulator, - qemu_executor_state: &mut QemuExecutorState, + fn qemu_pre_exec>( + emu: &mut Emulator, input: &S::Input, ) { - let exit_handler = emu.exit_handler.borrow(); + let input_location = { + let exit_handler = emu.exit_handler.borrow(); + exit_handler.input_location.get().cloned() + }; - if let Some(input_location) = exit_handler.input_location.get() { + if let Some(input_location) = input_location { let input_command = InputCommand::new(input_location.mem_chunk.clone(), input_location.cpu); + input_command - .run(emu, qemu_executor_state, input, input_location.ret_register) + .run(emu, input, input_location.ret_register) .unwrap(); } } - fn qemu_post_run>( - emu: &Emulator, - exit_reason: Result, EmulatorExitError>, - qemu_executor_state: &mut QemuExecutorState, + fn qemu_post_exec>( + emu: &mut Emulator, + exit_reason: Result, EmulatorExitError>, input: &S::Input, - ) -> Result>, ExitHandlerError> { + ) -> Result>, ExitHandlerError> { let exit_handler = emu.exit_handler().borrow_mut(); let qemu = emu.qemu(); @@ -375,7 +391,7 @@ where }; #[allow(clippy::type_complexity)] - let (command, ret_reg): (Option>, Option) = + let (command, ret_reg): (Option>, Option) = match &mut exit_reason { EmulatorExitResult::QemuExit(shutdown_cause) => match shutdown_cause { QemuShutdownCause::HostSignal(signal) => { @@ -399,7 +415,7 @@ where drop(exit_handler); if let Some(cmd) = command { - cmd.run(emu, qemu_executor_state, input, ret_reg) + cmd.run(emu, input, ret_reg) } else { Ok(Some(ExitHandlerResult::ReturnToHarness(exit_reason))) } @@ -418,12 +434,12 @@ impl From for ExitHandlerError { } } -impl Display for EmulatorExitResult +impl Display for EmulatorExitResult where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { @@ -442,50 +458,57 @@ impl From for EmulatorExitError { } } -#[derive(Clone, Debug, TypedBuilder)] -pub struct Emulator +// TODO: Replace TypedBuilder by something better, it does not work correctly with default and +// inter-dependent fields. +#[derive(Debug, TypedBuilder)] +pub struct Emulator where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { + modules: Pin>>, command_manager: CM, - exit_handler: RefCell, + exit_handler: RefCell, #[builder(default)] - breakpoints_by_addr: RefCell>>, + breakpoints_by_addr: RefCell>>, #[builder(default)] - breakpoints_by_id: RefCell>>, + breakpoints_by_id: RefCell>>, + #[builder(setter(transform = |args: &[String], env: &[(String, String)]| Qemu::init(args, env).unwrap()))] qemu: Qemu, - _phantom: PhantomData<(QT, S)>, + _phantom: PhantomData<(ET, S)>, } #[allow(clippy::unused_self)] -impl Emulator +impl Emulator where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { #[allow(clippy::must_use_candidate, clippy::similar_names)] pub fn new( args: &[String], env: &[(String, String)], - exit_handler: E, + modules: ET, + exit_handler: EH, command_manager: CM, ) -> Result { let qemu = Qemu::init(args, env)?; - Self::new_with_qemu(qemu, exit_handler, command_manager) + Self::new_with_qemu(qemu, modules, exit_handler, command_manager) } pub fn new_with_qemu( qemu: Qemu, - exit_handler: E, + modules: ET, + exit_handler: EH, command_manager: CM, ) -> Result { Ok(Emulator { + modules: EmulatorModules::new(qemu, modules), command_manager, exit_handler: RefCell::new(exit_handler), breakpoints_by_addr: RefCell::new(HashMap::new()), @@ -495,13 +518,21 @@ where }) } - #[must_use] - pub fn qemu(&self) -> &Qemu { - &self.qemu + pub fn modules(&self) -> &EmulatorModules { + &self.modules + } + + pub fn modules_mut(&mut self) -> &mut EmulatorModules { + self.modules.as_mut().get_mut() } #[must_use] - pub fn exit_handler(&self) -> &RefCell { + pub fn qemu(&self) -> Qemu { + self.qemu + } + + #[must_use] + pub fn exit_handler(&self) -> &RefCell { &self.exit_handler } @@ -589,9 +620,9 @@ where self.qemu.read_reg(reg) } - 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); + bp.enable(self.qemu); } let bp_id = bp.id(); @@ -625,7 +656,7 @@ where .get_mut(&bp_id) .expect("Did not find the breakpoint") .borrow_mut(); - bp.disable(&self.qemu); + bp.disable(self.qemu); bp.addr() }; @@ -646,12 +677,31 @@ where self.qemu.entry_break(addr); } + pub fn first_exec_all(&mut self) { + self.modules.first_exec_all(); + } + + pub fn pre_exec_all(&mut self, input: &S::Input) { + self.modules.pre_exec_all(input); + } + + pub fn post_exec_all( + &mut self, + input: &S::Input, + observers: &mut OT, + exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + { + self.modules.post_exec_all(input, observers, exit_kind); + } + /// This function will run the emulator until the next breakpoint, or until finish. /// # Safety /// /// 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. - 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) => { @@ -686,21 +736,26 @@ 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( - &self, + &mut self, input: &S::Input, - qemu_executor_state: &mut QemuExecutorState, - ) -> Result, ExitHandlerError> { + ) -> Result, ExitHandlerError> { 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 - E::qemu_pre_run(self, qemu_executor_state, input); + EH::qemu_pre_exec(self, input); // Run QEMU let exit_reason = self.run_qemu(); // Handle QEMU exit - if let Some(exit_handler_result) = - E::qemu_post_run(self, exit_reason, qemu_executor_state, input)? - { + if let Some(exit_handler_result) = EH::qemu_post_exec(self, exit_reason, input)? { return Ok(exit_handler_result); } } @@ -713,123 +768,6 @@ where self.qemu.flush_jit(); } - // TODO set T lifetime to be like Emulator - #[deprecated( - note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`." - )] - pub fn set_hook>( - &self, - data: T, - addr: GuestAddr, - callback: extern "C" fn(T, GuestAddr), - invalidate_block: bool, - ) -> InstructionHookId { - self.qemu.set_hook(data, addr, callback, invalidate_block) - } - - #[must_use] - #[deprecated( - note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`." - )] - pub fn remove_hook(&self, id: impl HookId, invalidate_block: bool) -> bool { - self.qemu.remove_hook(id, invalidate_block) - } - - #[must_use] - #[deprecated( - note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`." - )] - pub fn remove_hooks_at(&self, addr: GuestAddr, invalidate_block: bool) -> usize { - self.qemu.remove_hooks_at(addr, invalidate_block) - } - - #[deprecated( - note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`." - )] - pub fn add_edge_hooks>( - &self, - data: T, - gen: Option u64>, - exec: Option, - ) -> EdgeHookId { - self.qemu.add_edge_hooks(data, gen, exec) - } - - #[deprecated( - note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`." - )] - pub fn add_block_hooks>( - &self, - data: T, - gen: Option u64>, - post_gen: Option, - exec: Option, - ) -> BlockHookId { - self.qemu.add_block_hooks(data, gen, post_gen, exec) - } - - #[deprecated( - note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`." - )] - pub fn add_read_hooks>( - &self, - data: T, - gen: Option u64>, - exec1: Option, - exec2: Option, - exec4: Option, - exec8: Option, - exec_n: Option, - ) -> ReadHookId { - self.qemu - .add_read_hooks(data, gen, exec1, exec2, exec4, exec8, exec_n) - } - - // TODO add MemOp info - #[deprecated( - note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`." - )] - pub fn add_write_hooks>( - &self, - data: T, - gen: Option u64>, - exec1: Option, - exec2: Option, - exec4: Option, - exec8: Option, - exec_n: Option, - ) -> WriteHookId { - self.qemu - .add_write_hooks(data, gen, exec1, exec2, exec4, exec8, exec_n) - } - - #[deprecated( - note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`." - )] - pub fn add_cmp_hooks>( - &self, - data: T, - gen: Option u64>, - exec1: Option, - exec2: Option, - exec4: Option, - exec8: Option, - ) -> CmpHookId { - self.qemu - .add_cmp_hooks(data, gen, exec1, exec2, exec4, exec8) - } - - #[deprecated( - note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`." - )] - pub fn add_backdoor_hook>( - &self, - data: T, - callback: extern "C" fn(T, CPUArchStatePtr, GuestAddr), - ) -> BackdoorHookId { - self.qemu.add_backdoor_hook(data, callback) - } - #[allow(clippy::type_complexity)] #[deprecated( note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`." diff --git a/libafl_qemu/src/emu/systemmode.rs b/libafl_qemu/src/emu/systemmode.rs index 3dae121411..f831a31f42 100644 --- a/libafl_qemu/src/emu/systemmode.rs +++ b/libafl_qemu/src/emu/systemmode.rs @@ -8,8 +8,8 @@ use libafl::state::{HasExecutions, State}; use libafl_qemu_sys::GuestPhysAddr; use crate::{ - command::CommandManager, emu::IsSnapshotManager, DeviceSnapshotFilter, Emulator, - EmulatorExitHandler, Qemu, QemuHelperTuple, QemuSnapshotCheckResult, SnapshotId, + command::CommandManager, emu::IsSnapshotManager, modules::EmulatorModuleTuple, + DeviceSnapshotFilter, Emulator, EmulatorExitHandler, Qemu, QemuSnapshotCheckResult, SnapshotId, SnapshotManagerError, }; @@ -34,7 +34,7 @@ pub enum SnapshotManager { } impl IsSnapshotManager for SnapshotManager { - fn save(&mut self, qemu: &Qemu) -> SnapshotId { + fn save(&mut self, qemu: Qemu) -> SnapshotId { match self { SnapshotManager::Qemu(qemu_sm) => qemu_sm.save(qemu), SnapshotManager::Fast(fast_sm) => fast_sm.save(qemu), @@ -44,7 +44,7 @@ impl IsSnapshotManager for SnapshotManager { fn restore( &mut self, snapshot_id: &SnapshotId, - qemu: &Qemu, + qemu: Qemu, ) -> Result<(), SnapshotManagerError> { match self { SnapshotManager::Qemu(qemu_sm) => qemu_sm.restore(snapshot_id, qemu), @@ -55,7 +55,7 @@ impl IsSnapshotManager for SnapshotManager { fn do_check( &self, reference_snapshot_id: &SnapshotId, - qemu: &Qemu, + qemu: Qemu, ) -> Result { match self { SnapshotManager::Qemu(qemu_sm) => qemu_sm.do_check(reference_snapshot_id, qemu), @@ -105,7 +105,7 @@ impl QemuSnapshotManager { } impl IsSnapshotManager for QemuSnapshotManager { - fn save(&mut self, qemu: &Qemu) -> SnapshotId { + fn save(&mut self, qemu: Qemu) -> SnapshotId { let snapshot_id = SnapshotId::gen_unique_id(); qemu.save_snapshot( self.snapshot_id_to_name(&snapshot_id).as_str(), @@ -117,7 +117,7 @@ impl IsSnapshotManager for QemuSnapshotManager { fn restore( &mut self, snapshot_id: &SnapshotId, - qemu: &Qemu, + qemu: Qemu, ) -> Result<(), SnapshotManagerError> { qemu.load_snapshot(self.snapshot_id_to_name(snapshot_id).as_str(), self.is_sync); Ok(()) @@ -126,7 +126,7 @@ impl IsSnapshotManager for QemuSnapshotManager { fn do_check( &self, _reference_snapshot_id: &SnapshotId, - _qemu: &Qemu, + _qemu: Qemu, ) -> Result { // We consider the qemu implementation to be 'ideal' for now. Ok(QemuSnapshotCheckResult::default()) @@ -134,7 +134,7 @@ impl IsSnapshotManager for QemuSnapshotManager { } impl IsSnapshotManager for FastSnapshotManager { - fn save(&mut self, qemu: &Qemu) -> SnapshotId { + fn save(&mut self, qemu: Qemu) -> SnapshotId { let snapshot_id = SnapshotId::gen_unique_id(); self.snapshots .insert(snapshot_id, qemu.create_fast_snapshot(true)); @@ -144,7 +144,7 @@ impl IsSnapshotManager for FastSnapshotManager { fn restore( &mut self, snapshot_id: &SnapshotId, - qemu: &Qemu, + qemu: Qemu, ) -> Result<(), SnapshotManagerError> { let fast_snapshot_ptr = *self .snapshots @@ -161,7 +161,7 @@ impl IsSnapshotManager for FastSnapshotManager { fn do_check( &self, reference_snapshot_id: &SnapshotId, - qemu: &Qemu, + qemu: Qemu, ) -> Result { let fast_snapshot_ptr = *self.snapshots.get(reference_snapshot_id).ok_or( SnapshotManagerError::SnapshotIdNotFound(*reference_snapshot_id), @@ -171,12 +171,12 @@ impl IsSnapshotManager for FastSnapshotManager { } } -impl Emulator +impl Emulator where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { /// Write a value to a phsical guest address, including ROM areas. pub unsafe fn write_phys_mem(&self, paddr: GuestPhysAddr, buf: &[u8]) { diff --git a/libafl_qemu/src/emu/usermode.rs b/libafl_qemu/src/emu/usermode.rs index 5295a12948..e28a8eb58a 100644 --- a/libafl_qemu/src/emu/usermode.rs +++ b/libafl_qemu/src/emu/usermode.rs @@ -3,16 +3,16 @@ use libafl_qemu_sys::{GuestAddr, MmapPerms, VerifyAccess}; use crate::{ command::CommandManager, emu::{HasExecutions, State}, - Emulator, EmulatorExitHandler, GuestMaps, HookData, NewThreadHookId, PostSyscallHookId, - PreSyscallHookId, QemuHelperTuple, SyscallHookResult, + modules::EmulatorModuleTuple, + Emulator, EmulatorExitHandler, GuestMaps, }; -impl Emulator +impl Emulator where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { /// This function gets the memory mappings from the emulator. #[must_use] @@ -92,58 +92,4 @@ where pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> { self.qemu.unmap(addr, size) } - - #[allow(clippy::type_complexity)] - pub fn add_pre_syscall_hook>( - &self, - data: T, - callback: extern "C" fn( - T, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> SyscallHookResult, - ) -> PreSyscallHookId { - self.qemu.add_pre_syscall_hook(data, callback) - } - - #[allow(clippy::type_complexity)] - pub fn add_post_syscall_hook>( - &self, - data: T, - callback: extern "C" fn( - T, - GuestAddr, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> GuestAddr, - ) -> PostSyscallHookId { - self.qemu.add_post_syscall_hook(data, callback) - } - - pub fn add_new_thread_hook>( - &self, - data: T, - callback: extern "C" fn(T, tid: u32) -> bool, - ) -> NewThreadHookId { - self.qemu.add_new_thread_hook(data, callback) - } - - #[allow(clippy::type_complexity)] - pub fn set_crash_hook(&self, callback: extern "C" fn(i32)) { - self.qemu.set_crash_hook(callback); - } } diff --git a/libafl_qemu/src/executor/mod.rs b/libafl_qemu/src/executor/mod.rs index ffaf8baad6..3150235ada 100644 --- a/libafl_qemu/src/executor/mod.rs +++ b/libafl_qemu/src/executor/mod.rs @@ -31,41 +31,48 @@ use libafl_bolts::{ tuples::RefIndexable, }; -use crate::{helpers::QemuHelperTuple, hooks::QemuHooks, Qemu}; +#[cfg(emulation_mode = "usermode")] +use crate::emu::EmulatorModules; +use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorExitHandler}; /// A version of `QemuExecutor` with a state accessible from the harness. pub mod stateful; -pub struct QemuExecutorState<'a, QT, S> +pub struct QemuExecutorState<'a, CM, EH, ET, S> where - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { - hooks: &'a mut QemuHooks, - first_exec: bool, + emulator: &'a mut Emulator, } -pub struct QemuExecutor<'a, H, OT, QT, S> +pub struct QemuExecutor<'a, CM, EH, H, OT, ET, S> where + CM: CommandManager, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, - S: State + HasExecutions, OT: ObserversTuple, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { inner: InProcessExecutor<'a, H, OT, S>, - state: QemuExecutorState<'a, QT, S>, + state: QemuExecutorState<'a, CM, EH, ET, S>, } -impl<'a, H, OT, QT, S> Debug for QemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, H, OT, ET, S> Debug for QemuExecutor<'a, CM, EH, H, OT, ET, S> where + CM: CommandManager + Debug, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, - S: State + HasExecutions, + S: Unpin + State + HasExecutions + Debug, OT: ObserversTuple + Debug, - QT: QemuHelperTuple + Debug, + ET: EmulatorModuleTuple + Debug, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QemuExecutor") - .field("hooks", &self.state.hooks) + .field("emulator", &self.state.emulator) .field("inner", &self.inner) .finish() } @@ -78,7 +85,7 @@ extern "C" { } #[cfg(emulation_mode = "usermode")] -pub unsafe fn inproc_qemu_crash_handler<'a, E, EM, OF, Z, QT, S>( +pub unsafe fn inproc_qemu_crash_handler<'a, E, EM, OF, Z, ET, S>( signal: Signal, info: &'a mut siginfo_t, mut context: Option<&'a mut ucontext_t>, @@ -89,8 +96,8 @@ pub unsafe fn inproc_qemu_crash_handler<'a, E, EM, OF, Z, QT, S>( OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus, Z: HasObjective, - QT: QemuHelperTuple + Debug + 'a, - S: State + HasExecutions + 'a, + ET: EmulatorModuleTuple + Debug + 'a, + S: Unpin + State + HasExecutions + 'a, { let puc = match &mut context { Some(v) => ptr::from_mut::(*v) as *mut c_void, @@ -129,64 +136,72 @@ pub unsafe fn inproc_qemu_timeout_handler<'a, E, EM, OF, Z>( } } -impl<'a, QT, S> QemuExecutorState<'a, QT, S> +impl<'a, CM, EH, ET, S> QemuExecutorState<'a, CM, EH, ET, S> where - S: State + HasExecutions, - QT: QemuHelperTuple + Debug, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple + Debug, + S: Unpin + State + HasExecutions + HasCorpus + HasSolutions, { - pub fn new(hooks: &'a mut QemuHooks) -> Result + #[cfg(emulation_mode = "systemmode")] + pub fn new(emulator: &'a mut Emulator) -> Result + where + E: Executor + HasInProcessHooks + HasObservers, + EM: EventFirer + EventRestarter, + OF: Feedback, + OT: ObserversTuple, + Z: HasObjective, + { + Ok(QemuExecutorState { emulator }) + } + + #[cfg(emulation_mode = "usermode")] + pub fn new(emulator: &'a mut Emulator) -> Result where E: Executor + HasInProcessHooks + HasObservers, EM: EventFirer + EventRestarter, OF: Feedback, OT: ObserversTuple, - S: State + HasExecutions + HasCorpus + HasSolutions, Z: HasObjective + ExecutionProcessor + HasScheduler, { #[cfg(emulation_mode = "usermode")] { - let handler = |hooks: &mut QemuHooks, host_sig| { + let handler = |emulator_modules: &mut EmulatorModules, host_sig| { eprintln!("Crashed with signal {host_sig}"); unsafe { libafl::executors::inprocess::generic_inproc_crash_handler::(); } - if let Some(cpu) = hooks.qemu().current_cpu() { + if let Some(cpu) = emulator_modules.qemu().current_cpu() { eprint!("Context:\n{}", cpu.display_context()); } }; - hooks.crash_closure(Box::new(handler)); + emulator.modules_mut().crash_closure(Box::new(handler)); } - Ok(QemuExecutorState { - first_exec: true, - hooks, - }) + Ok(QemuExecutorState { emulator }) } #[must_use] - pub fn hooks(&self) -> &QemuHooks { - self.hooks + pub fn emulator(&self) -> &Emulator { + self.emulator } - pub fn hooks_mut(&mut self) -> &mut QemuHooks { - self.hooks - } - - #[must_use] - pub fn qemu(&self) -> &Qemu { - self.hooks.qemu() + pub fn emulator_mut(&mut self) -> &mut Emulator { + self.emulator } } -impl<'a, H, OT, QT, S> QemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, H, OT, ET, S> QemuExecutor<'a, CM, EH, H, OT, ET, S> where + CM: CommandManager, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, - S: State + HasExecutions, + S: Unpin + State + HasExecutions, OT: ObserversTuple, - QT: QemuHelperTuple + Debug, + ET: EmulatorModuleTuple + Debug, { pub fn new( - hooks: &'a mut QemuHooks, + emulator: &'a mut Emulator, harness_fn: &'a mut H, observers: OT, fuzzer: &mut Z, @@ -197,7 +212,7 @@ where where EM: EventFirer + EventRestarter, OF: Feedback, - S: State + HasExecutions + HasCorpus + HasSolutions, + S: Unpin + State + HasExecutions + HasCorpus + HasSolutions, Z: HasObjective + HasScheduler + ExecutionProcessor, { let mut inner = InProcessExecutor::with_timeout( @@ -207,7 +222,7 @@ where #[cfg(emulation_mode = "usermode")] { inner.inprocess_hooks_mut().crash_handler = - inproc_qemu_crash_handler::, EM, OF, Z, QT, S> + inproc_qemu_crash_handler::, EM, OF, Z, ET, S> as *const c_void; } @@ -219,7 +234,7 @@ where } let state = - QemuExecutorState::new::, EM, OF, OT, Z>(hooks)?; + QemuExecutorState::new::, EM, OF, OT, Z>(emulator)?; Ok(Self { inner, state }) } @@ -238,43 +253,30 @@ where pub fn inner_mut(&mut self) -> &mut InProcessExecutor<'a, H, OT, S> { &mut self.inner } - - pub fn hooks(&self) -> &QemuHooks { - self.state.hooks() - } - - pub fn hooks_mut(&mut self) -> &mut QemuHooks { - self.state.hooks_mut() - } - - pub fn emulator(&self) -> &Qemu { - self.state.qemu() - } } -impl<'a, QT, S> QemuExecutorState<'a, QT, S> +impl<'a, CM, EH, ET, S> QemuExecutorState<'a, CM, EH, ET, S> where - S: State + HasExecutions + HasCorpus + HasSolutions, - QT: QemuHelperTuple + Debug, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { - fn pre_exec(&mut self, input: &E::Input, qemu: Qemu) + fn pre_exec(&mut self, input: &E::Input) where E: Executor, EM: EventFirer + EventRestarter, OF: Feedback, Z: HasObjective, { - if self.first_exec { - self.hooks.helpers().first_exec_all(self.hooks); - self.first_exec = false; - } - self.hooks.helpers_mut().pre_exec_all(qemu, input); + self.emulator.first_exec_all(); + + self.emulator.pre_exec_all(input); } fn post_exec( &mut self, input: &E::Input, - qemu: Qemu, observers: &mut OT, exit_kind: &mut ExitKind, ) where @@ -284,20 +286,20 @@ where OF: Feedback, Z: HasObjective, { - self.hooks - .helpers_mut() - .post_exec_all(qemu, input, observers, exit_kind); + self.emulator.post_exec_all(input, observers, exit_kind); } } -impl<'a, EM, H, OT, OF, QT, S, Z> Executor for QemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, EM, H, OT, OF, ET, S, Z> Executor for QemuExecutor<'a, CM, EH, H, OT, ET, S> where + CM: CommandManager, + EH: EmulatorExitHandler, EM: EventFirer + EventRestarter, H: FnMut(&S::Input) -> ExitKind, - S: State + HasExecutions + HasCorpus + HasSolutions, + S: Unpin + State + HasExecutions + HasCorpus + HasSolutions, OT: ObserversTuple, OF: Feedback, - QT: QemuHelperTuple + Debug, + ET: EmulatorModuleTuple + Debug, Z: HasObjective, { fn run_target( @@ -307,12 +309,10 @@ where mgr: &mut EM, input: &Self::Input, ) -> Result { - let qemu = Qemu::get().unwrap(); - self.state.pre_exec::(input, qemu); + self.state.pre_exec::(input); let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?; self.state.post_exec::( input, - qemu, &mut *self.inner.observers_mut(), &mut exit_kind, ); @@ -320,32 +320,38 @@ where } } -impl<'a, H, OT, QT, S> UsesState for QemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, H, OT, ET, S> UsesState for QemuExecutor<'a, CM, EH, H, OT, ET, S> where + CM: CommandManager, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, OT: ObserversTuple, - QT: QemuHelperTuple, - S: State + HasExecutions, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { type State = S; } -impl<'a, H, OT, QT, S> UsesObservers for QemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, H, OT, ET, S> UsesObservers for QemuExecutor<'a, CM, EH, H, OT, ET, S> where + CM: CommandManager, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, OT: ObserversTuple, - QT: QemuHelperTuple, - S: State + HasExecutions, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { type Observers = OT; } -impl<'a, H, OT, QT, S> HasObservers for QemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, H, OT, ET, S> HasObservers for QemuExecutor<'a, CM, EH, H, OT, ET, S> where + CM: CommandManager, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, - S: State + HasExecutions, + S: Unpin + State + HasExecutions, OT: ObserversTuple, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { #[inline] fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> { @@ -359,63 +365,70 @@ where } #[cfg(feature = "fork")] -pub struct QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> +pub struct QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z> where + CM: CommandManager, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, - S: State + HasExecutions, + S: Unpin + State + HasExecutions, OT: ObserversTuple, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, SP: ShMemProvider, EM: UsesState, Z: UsesState, { inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>, - state: QemuExecutorState<'a, QT, S>, + state: QemuExecutorState<'a, CM, EH, ET, S>, } #[cfg(feature = "fork")] -impl<'a, H, OT, QT, S, SP, EM, Z> Debug for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> +impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> Debug + for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z> where + CM: CommandManager + Debug, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, - S: State + HasExecutions, + S: Unpin + State + HasExecutions + Debug, OT: ObserversTuple + Debug, - QT: QemuHelperTuple + Debug, + ET: EmulatorModuleTuple + Debug, SP: ShMemProvider, EM: UsesState, Z: UsesState, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QemuForkExecutor") - .field("hooks", &self.state.hooks) + .field("emulator", &self.state.emulator) .field("inner", &self.inner) .finish() } } #[cfg(feature = "fork")] -impl<'a, H, OT, QT, S, SP, EM, Z, OF> QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> +impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z, OF> QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z> where + CM: CommandManager, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, - S: State + HasExecutions, + S: Unpin + State + HasExecutions, OT: ObserversTuple, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, SP: ShMemProvider, EM: EventFirer + EventRestarter, OF: Feedback, - S: HasSolutions, + S: Unpin + HasSolutions, Z: HasObjective, { pub fn new( - hooks: &'a mut QemuHooks, + emulator: &'a mut Emulator, harness_fn: &'a mut H, observers: OT, fuzzer: &mut Z, state: &mut S, event_mgr: &mut EM, shmem_provider: SP, - timeout: core::time::Duration, + timeout: Duration, ) -> Result { - assert!(!QT::HOOKS_DO_SIDE_EFFECTS, "When using QemuForkExecutor, the hooks must not do any side effect as they will happen in the child process and then discarded"); + assert!(!ET::HOOKS_DO_SIDE_EFFECTS, "When using QemuForkExecutor, the hooks must not do any side effect as they will happen in the child process and then discarded"); Ok(Self { inner: InProcessForkExecutor::new( @@ -427,10 +440,7 @@ where timeout, shmem_provider, )?, - state: QemuExecutorState { - first_exec: true, - hooks, - }, + state: QemuExecutorState { emulator }, }) } @@ -442,28 +452,22 @@ where &mut self.inner } - pub fn hooks(&self) -> &QemuHooks { - self.state.hooks - } - - pub fn hooks_mut(&mut self) -> &mut QemuHooks { - self.state.hooks - } - - pub fn qemu(&self) -> &Qemu { - self.state.hooks.qemu() + pub fn emulator(&self) -> &Emulator { + self.state.emulator } } #[cfg(feature = "fork")] -impl<'a, EM, H, OT, QT, S, Z, SP, OF> Executor - for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> +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> where + CM: CommandManager, + EH: EmulatorExitHandler, EM: EventManager, Z, State = S>, H: FnMut(&S::Input) -> ExitKind, - S: State + HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + HasSolutions, + S: Unpin + State + HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + HasSolutions, OT: ObserversTuple + Debug, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, SP: ShMemProvider, OF: Feedback, Z: HasObjective, @@ -475,30 +479,20 @@ where mgr: &mut EM, input: &Self::Input, ) -> Result { - let qemu = *self.state.hooks.qemu(); - if self.state.first_exec { - self.state.hooks.helpers().first_exec_all(self.state.hooks); - self.state.first_exec = false; - } - self.state.hooks.helpers_mut().pre_exec_all(qemu, input); - let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?; - self.state.hooks.helpers_mut().post_exec_all( - qemu, - input, - &mut *self.inner.observers_mut(), - &mut exit_kind, - ); - Ok(exit_kind) + self.inner.run_target(fuzzer, state, mgr, input) } } #[cfg(feature = "fork")] -impl<'a, H, OT, QT, S, SP, EM, Z> UsesObservers for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> +impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> UsesObservers + for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z> where + CM: CommandManager, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, OT: ObserversTuple, - QT: QemuHelperTuple, - S: State + HasExecutions, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, SP: ShMemProvider, EM: UsesState, Z: UsesState, @@ -507,12 +501,15 @@ where } #[cfg(feature = "fork")] -impl<'a, H, OT, QT, S, SP, EM, Z> UsesState for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> +impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> UsesState + for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z> where + CM: CommandManager, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, OT: ObserversTuple, - QT: QemuHelperTuple, - S: State + HasExecutions, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, SP: ShMemProvider, EM: UsesState, Z: UsesState, @@ -521,12 +518,15 @@ where } #[cfg(feature = "fork")] -impl<'a, H, OT, QT, S, SP, EM, Z> HasObservers for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> +impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> HasObservers + for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z> where + CM: CommandManager, + EH: EmulatorExitHandler, H: FnMut(&S::Input) -> ExitKind, - S: State + HasExecutions, + S: Unpin + State + HasExecutions, OT: ObserversTuple, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, SP: ShMemProvider, EM: UsesState, Z: UsesState, diff --git a/libafl_qemu/src/executor/stateful.rs b/libafl_qemu/src/executor/stateful.rs index 49d74889e5..b257e9d8d9 100644 --- a/libafl_qemu/src/executor/stateful.rs +++ b/libafl_qemu/src/executor/stateful.rs @@ -23,41 +23,50 @@ use libafl_bolts::tuples::RefIndexable; use crate::executor::inproc_qemu_crash_handler; #[cfg(emulation_mode = "systemmode")] use crate::executor::{inproc_qemu_timeout_handler, BREAK_ON_TMOUT}; -use crate::{executor::QemuExecutorState, helpers::QemuHelperTuple, hooks::QemuHooks, Qemu}; +use crate::{ + command::CommandManager, executor::QemuExecutorState, modules::EmulatorModuleTuple, Emulator, + EmulatorExitHandler, +}; -pub struct StatefulQemuExecutor<'a, H, OT, QT, S> +pub struct StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S> where - H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind, + S: Unpin + State + HasExecutions, OT: ObserversTuple, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - inner: StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>, + inner: StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>, } -impl<'a, H, OT, QT, S> Debug for StatefulQemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, H, OT, ET, S> Debug for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S> where - H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind, + S: Unpin + State + HasExecutions, OT: ObserversTuple + Debug, - QT: QemuHelperTuple + Debug, + ET: EmulatorModuleTuple + Debug, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("QemuExecutor") + f.debug_struct("StatefulQemuExecutor") .field("inner", &self.inner) .finish() } } -impl<'a, H, OT, QT, S> StatefulQemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, H, OT, ET, S> StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S> where - H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind, + S: Unpin + State + HasExecutions, OT: ObserversTuple, - QT: QemuHelperTuple + Debug, + ET: EmulatorModuleTuple + Debug, { pub fn new( - hooks: &'a mut QemuHooks, + emulator: &'a mut Emulator, harness_fn: &'a mut H, observers: OT, fuzzer: &mut Z, @@ -68,16 +77,16 @@ where where EM: EventFirer + EventRestarter, OF: Feedback, - S: State + HasExecutions + HasCorpus + HasSolutions, + S: Unpin + State + HasExecutions + HasCorpus + HasSolutions, Z: HasObjective + HasScheduler + ExecutionProcessor, { let qemu_state = QemuExecutorState::new::< - StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>, + StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>, EM, OF, OT, Z, - >(hooks)?; + >(emulator)?; let mut inner = StatefulInProcessExecutor::with_timeout( harness_fn, qemu_state, observers, fuzzer, state, event_mgr, timeout, @@ -86,11 +95,11 @@ where #[cfg(emulation_mode = "usermode")] { inner.inprocess_hooks_mut().crash_handler = inproc_qemu_crash_handler::< - StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>, + StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>, EM, OF, Z, - QT, + ET, S, > as *const c_void; } @@ -98,7 +107,7 @@ where #[cfg(emulation_mode = "systemmode")] { inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::< - StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>, + StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>, EM, OF, Z, @@ -108,7 +117,9 @@ where Ok(Self { inner }) } - pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>> { + pub fn inner( + &self, + ) -> &StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>> { &self.inner } @@ -121,31 +132,22 @@ where pub fn inner_mut( &mut self, - ) -> &mut StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>> { + ) -> &mut StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>> { &mut self.inner } - - pub fn hooks(&self) -> &QemuHooks { - self.inner.exposed_executor_state().hooks() - } - - pub fn hooks_mut(&mut self) -> &mut QemuHooks { - self.inner.exposed_executor_state_mut().hooks_mut() - } - - pub fn emulator(&self) -> &Qemu { - self.inner.exposed_executor_state().qemu() - } } -impl<'a, EM, H, OT, OF, QT, S, Z> Executor for StatefulQemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, EM, H, OT, OF, ET, S, Z> Executor + for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S> where + CM: CommandManager, + EH: EmulatorExitHandler, EM: EventFirer + EventRestarter, - H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, - S: State + HasExecutions + HasCorpus + HasSolutions, + H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind, + S: Unpin + State + HasExecutions + HasCorpus + HasSolutions, OT: ObserversTuple, OF: Feedback, - QT: QemuHelperTuple + Debug, + ET: EmulatorModuleTuple + Debug, Z: HasObjective, { fn run_target( @@ -155,16 +157,14 @@ where mgr: &mut EM, input: &Self::Input, ) -> Result { - let qemu = Qemu::get().unwrap(); self.inner .exposed_executor_state_mut() - .pre_exec::(input, qemu); + .pre_exec::(input); let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?; self.inner .exposed_executor_state .post_exec::( input, - qemu, &mut *self.inner.inner.observers_mut(), &mut exit_kind, ); @@ -172,32 +172,38 @@ where } } -impl<'a, H, OT, QT, S> UsesState for StatefulQemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, H, OT, ET, S> UsesState for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S> where - H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, + CM: CommandManager, + EH: EmulatorExitHandler, + H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind, OT: ObserversTuple, - QT: QemuHelperTuple, - S: State + HasExecutions, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { type State = S; } -impl<'a, H, OT, QT, S> UsesObservers for StatefulQemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, H, OT, ET, S> UsesObservers for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S> where - H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, + CM: CommandManager, + EH: EmulatorExitHandler, + H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind, OT: ObserversTuple, - QT: QemuHelperTuple, - S: State + HasExecutions, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { type Observers = OT; } -impl<'a, H, OT, QT, S> HasObservers for StatefulQemuExecutor<'a, H, OT, QT, S> +impl<'a, CM, EH, H, OT, ET, S> HasObservers for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S> where - H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind, + S: Unpin + State + HasExecutions, OT: ObserversTuple, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { #[inline] fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> { diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs deleted file mode 100644 index 95a8193987..0000000000 --- a/libafl_qemu/src/hooks.rs +++ /dev/null @@ -1,1387 +0,0 @@ -//! The high-level hooks -#![allow(clippy::type_complexity, clippy::missing_transmute_annotations)] - -#[cfg(emulation_mode = "usermode")] -use core::ptr::addr_of_mut; -use core::{ - ffi::c_void, - fmt::{self, Debug, Formatter}, - marker::PhantomData, - mem::transmute, - pin::Pin, - ptr::{self, addr_of}, -}; - -use libafl::{ - executors::{hooks::inprocess::inprocess_get_state, ExitKind}, - inputs::UsesInput, - state::NopState, -}; -use libafl_qemu_sys::{CPUArchStatePtr, FatPtr, GuestAddr, GuestUsize}; - -pub use crate::qemu::SyscallHookResult; -use crate::{ - helpers::QemuHelperTuple, - qemu::{MemAccessInfo, Qemu, SKIP_EXEC_HOOK}, - sys::TCGTemp, - BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, HookId, InstructionHookId, ReadHookId, - WriteHookId, -}; -#[cfg(emulation_mode = "usermode")] -use crate::{NewThreadHookId, PostSyscallHookId, PreSyscallHookId}; - -/* -// all kinds of hooks -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub(crate) enum Hook { - Function(*const c_void), - Closure(FatPtr), - #[cfg(emulation_mode = "usermode")] - Once(Box), - Empty, -} -*/ - -// all kinds of hooks -#[derive(Clone, PartialEq, Eq, Debug)] -pub(crate) enum HookRepr { - Function(*const c_void), - Closure(FatPtr), - Empty, -} - -pub struct HookState { - id: H, - gen: HookRepr, - post_gen: HookRepr, - execs: [HookRepr; N], -} - -pub enum Hook { - Function(F), - Closure(C), - Raw(R), - Empty, -} - -impl Hook { - pub fn is_empty(&self) -> bool { - matches!(self, Hook::Empty) - } -} - -macro_rules! get_raw_hook { - ($h:expr, $replacement:expr, $fntype:ty) => { - match $h { - Hook::Function(_) | Hook::Closure(_) => Some($replacement as $fntype), - Hook::Raw(r) => { - let v: $fntype = transmute(r); - Some(v) - } - Hook::Empty => None, - } - }; -} - -macro_rules! hook_to_repr { - ($h:expr) => { - match $h { - Hook::Function(f) => HookRepr::Function(f as *const libc::c_void), - Hook::Closure(c) => HookRepr::Closure(transmute(c)), - Hook::Raw(_) => HookRepr::Empty, // managed by emu - Hook::Empty => HookRepr::Empty, - } - }; -} - -static mut QEMU_HOOKS_PTR: *const c_void = ptr::null(); - -#[must_use] -pub unsafe fn get_qemu_hooks<'a, QT, S>() -> &'a mut QemuHooks -where - S: UsesInput, - QT: QemuHelperTuple, -{ - (QEMU_HOOKS_PTR as *mut QemuHooks) - .as_mut() - .expect("A high-level hook is installed but QemuHooks is not initialized") -} - -macro_rules! create_wrapper { - ($name:ident, ($($param:ident : $param_type:ty),*)) => { - paste::paste! { - extern "C" fn [](hook: &mut c_void, $($param: $param_type),*) - where - S: UsesInput, - QT: QemuHelperTuple, - { - unsafe { - let hooks = get_qemu_hooks::(); - let func: fn(&mut QemuHooks, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::(hook)); - func(hooks, inprocess_get_state::(), $($param),*); - } - } - - extern "C" fn [](hook: &mut FatPtr, $($param: $param_type),*) - where - S: UsesInput, - QT: QemuHelperTuple, - { - unsafe { - let hooks = get_qemu_hooks::(); - let func: &mut Box, Option<&mut S>, $($param_type),*)> = transmute(hook); - func(hooks, inprocess_get_state::(), $($param),*); - } - } - } - }; - ($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty) => { - paste::paste! { - extern "C" fn [](hook: &mut c_void, $($param: $param_type),*) -> $ret_type - where - S: UsesInput, - QT: QemuHelperTuple, - { - unsafe { - let hooks = get_qemu_hooks::(); - let func: fn(&mut QemuHooks, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::(hook)); - func(hooks, inprocess_get_state::(), $($param),*) - } - } - - extern "C" fn [](hook: &mut FatPtr, $($param: $param_type),*) -> $ret_type - where - S: UsesInput, - QT: QemuHelperTuple, - { - unsafe { - let hooks = get_qemu_hooks::(); - let func: &mut Box, Option<&mut S>, $($param_type),*) -> $ret_type> = transmute(hook); - func(hooks, inprocess_get_state::(), $($param),*) - } - } - } - }; -} - -macro_rules! create_gen_wrapper { - ($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty, $execs:literal, $hook_id:ident) => { - paste::paste! { - extern "C" fn [<$name _gen_hook_wrapper>](hook: &mut HookState<{ $execs }, $hook_id>, $($param: $param_type),*) -> $ret_type - where - S: UsesInput, - QT: QemuHelperTuple, - { - unsafe { - let hooks = get_qemu_hooks::(); - match &mut hook.gen { - HookRepr::Function(ptr) => { - let func: fn(&mut QemuHooks, Option<&mut S>, $($param_type),*) -> Option<$ret_type> = - transmute(*ptr); - func(hooks, inprocess_get_state::(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id) - } - HookRepr::Closure(ptr) => { - let func: &mut Box< - dyn FnMut(&mut QemuHooks, Option<&mut S>, $($param_type),*) -> Option<$ret_type>, - > = transmute(ptr); - func(hooks, inprocess_get_state::(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id) - } - _ => 0, - } - } - } - } - } -} - -macro_rules! create_post_gen_wrapper { - ($name:ident, ($($param:ident : $param_type:ty),*), $execs:literal, $hook_id:ident) => { - paste::paste! { - extern "C" fn [<$name _post_gen_hook_wrapper>](hook: &mut HookState<{ $execs }, $hook_id>, $($param: $param_type),*) - where - S: UsesInput, - QT: QemuHelperTuple, - { - unsafe { - let hooks = get_qemu_hooks::(); - match &mut hook.post_gen { - HookRepr::Function(ptr) => { - let func: fn(&mut QemuHooks, Option<&mut S>, $($param_type),*) = - transmute(*ptr); - func(hooks, inprocess_get_state::(), $($param),*); - } - HookRepr::Closure(ptr) => { - let func: &mut Box< - dyn FnMut(&mut QemuHooks, Option<&mut S>, $($param_type),*), - > = transmute(ptr); - func(hooks, inprocess_get_state::(), $($param),*); - } - _ => (), - } - } - } - } - } -} - -macro_rules! create_exec_wrapper { - ($name:ident, ($($param:ident : $param_type:ty),*), $execidx:literal, $execs:literal, $hook_id:ident) => { - paste::paste! { - extern "C" fn [<$name _ $execidx _exec_hook_wrapper>](hook: &mut HookState<{ $execs }, $hook_id>, $($param: $param_type),*) - where - S: UsesInput, - QT: QemuHelperTuple, - { - unsafe { - let hooks = get_qemu_hooks::(); - match &mut hook.execs[$execidx] { - HookRepr::Function(ptr) => { - let func: fn(&mut QemuHooks, Option<&mut S>, $($param_type),*) = transmute(*ptr); - func(hooks, inprocess_get_state::(), $($param),*); - } - HookRepr::Closure(ptr) => { - let func: &mut Box, Option<&mut S>, $($param_type),*)> = - transmute(ptr); - func(hooks, inprocess_get_state::(), $($param),*); - } - _ => (), - } - } - } - } - } -} - -static mut GENERIC_HOOKS: Vec>> = vec![]; -create_wrapper!(generic, (pc: GuestAddr)); -static mut BACKDOOR_HOOKS: Vec>> = vec![]; -create_wrapper!(backdoor, (cpu: CPUArchStatePtr, pc: GuestAddr)); - -#[cfg(emulation_mode = "usermode")] -static mut PRE_SYSCALL_HOOKS: Vec>> = vec![]; -#[cfg(emulation_mode = "usermode")] -create_wrapper!( - pre_syscall, - ( - sys_num: i32, - a0: GuestAddr, - a1: GuestAddr, - a2: GuestAddr, - a3: GuestAddr, - a4: GuestAddr, - a5: GuestAddr, - a6: GuestAddr, - a7: GuestAddr - ), - SyscallHookResult -); -#[cfg(emulation_mode = "usermode")] -static mut POST_SYSCALL_HOOKS: Vec>> = vec![]; -#[cfg(emulation_mode = "usermode")] -create_wrapper!( - post_syscall, - ( - res: GuestAddr, - sys_num: i32, - a0: GuestAddr, - a1: GuestAddr, - a2: GuestAddr, - a3: GuestAddr, - a4: GuestAddr, - a5: GuestAddr, - a6: GuestAddr, - a7: GuestAddr - ), - GuestAddr -); -#[cfg(emulation_mode = "usermode")] -static mut NEW_THREAD_HOOKS: Vec>> = vec![]; -#[cfg(emulation_mode = "usermode")] -create_wrapper!(new_thread, (tid: u32), bool); - -static mut EDGE_HOOKS: Vec>>> = vec![]; -create_gen_wrapper!(edge, (src: GuestAddr, dest: GuestAddr), u64, 1, EdgeHookId); -create_exec_wrapper!(edge, (id: u64), 0, 1, EdgeHookId); - -static mut BLOCK_HOOKS: Vec>>> = vec![]; -create_gen_wrapper!(block, (addr: GuestAddr), u64, 1, BlockHookId); -create_post_gen_wrapper!(block, (addr: GuestAddr, len: GuestUsize), 1, BlockHookId); -create_exec_wrapper!(block, (id: u64), 0, 1, BlockHookId); - -static mut READ_HOOKS: Vec>>> = vec![]; -create_gen_wrapper!(read, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, ReadHookId); -create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 0, 5, ReadHookId); -create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 1, 5, ReadHookId); -create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 2, 5, ReadHookId); -create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 3, 5, ReadHookId); -create_exec_wrapper!( - read, - (id: u64, addr: GuestAddr, size: usize), - 4, - 5, - ReadHookId -); - -static mut WRITE_HOOKS: Vec>>> = vec![]; -create_gen_wrapper!(write, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, WriteHookId); -create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 0, 5, WriteHookId); -create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 1, 5, WriteHookId); -create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 2, 5, WriteHookId); -create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 3, 5, WriteHookId); -create_exec_wrapper!( - write, - (id: u64, addr: GuestAddr, size: usize), - 4, - 5, - WriteHookId -); - -static mut CMP_HOOKS: Vec>>> = vec![]; -create_gen_wrapper!(cmp, (pc: GuestAddr, size: usize), u64, 4, CmpHookId); -create_exec_wrapper!(cmp, (id: u64, v0: u8, v1: u8), 0, 4, CmpHookId); -create_exec_wrapper!(cmp, (id: u64, v0: u16, v1: u16), 1, 4, CmpHookId); -create_exec_wrapper!(cmp, (id: u64, v0: u32, v1: u32), 2, 4, CmpHookId); -create_exec_wrapper!(cmp, (id: u64, v0: u64, v1: u64), 3, 4, CmpHookId); - -#[cfg(emulation_mode = "usermode")] -static mut CRASH_HOOKS: Vec = vec![]; - -#[cfg(emulation_mode = "usermode")] -extern "C" fn crash_hook_wrapper(target_sig: i32) -where - S: UsesInput, - QT: QemuHelperTuple, -{ - unsafe { - let hooks = get_qemu_hooks::(); - for hook in &mut (*addr_of_mut!(CRASH_HOOKS)) { - match hook { - HookRepr::Function(ptr) => { - let func: fn(&mut QemuHooks, i32) = transmute(*ptr); - func(hooks, target_sig); - } - HookRepr::Closure(ptr) => { - let func: &mut Box, i32)> = transmute(ptr); - func(hooks, target_sig); - } - HookRepr::Empty => (), - } - } - } -} - -static mut HOOKS_IS_INITIALIZED: bool = false; -static mut FIRST_EXEC: bool = true; - -pub struct QemuHooks -where - QT: QemuHelperTuple, - S: UsesInput, -{ - helpers: QT, - qemu: Qemu, - phantom: PhantomData, -} - -impl Debug for QemuHooks -where - S: UsesInput, - QT: QemuHelperTuple + Debug, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("QemuHooks") - .field("helpers", &self.helpers) - .field("emulator", &self.qemu) - .finish() - } -} - -impl QemuHooks> -where - QT: QemuHelperTuple>, - NopState: UsesInput, -{ - pub fn reproducer(qemu: Qemu, helpers: QT) -> Box { - Self::new(qemu, helpers) - } - - pub fn repro_run(&mut self, harness: &mut H, input: &I) -> ExitKind - where - H: FnMut(&I) -> ExitKind, - { - unsafe { - if FIRST_EXEC { - self.helpers.first_exec_all(self); - FIRST_EXEC = false; - } - } - self.helpers.pre_exec_all(self.qemu, input); - - let mut exit_kind = harness(input); - - self.helpers - .post_exec_all(self.qemu, input, &mut (), &mut exit_kind); - - exit_kind - } -} - -impl QemuHooks -where - QT: QemuHelperTuple, - S: UsesInput, -{ - pub fn new(qemu: Qemu, helpers: QT) -> Box { - unsafe { - assert!( - !HOOKS_IS_INITIALIZED, - "Only an instance of QemuHooks is permitted" - ); - HOOKS_IS_INITIALIZED = true; - } - // re-translate blocks with hooks - qemu.flush_jit(); - let slf = Box::new(Self { - qemu, - helpers, - phantom: PhantomData, - }); - slf.helpers.init_hooks_all(&slf); - unsafe { - QEMU_HOOKS_PTR = addr_of!(*slf) as *const c_void; - } - slf - } - - #[must_use] - pub fn match_helper(&self) -> Option<&T> - where - T: 'static, - { - self.helpers.match_first_type::() - } - - #[must_use] - pub fn match_helper_mut(&mut self) -> Option<&mut T> - where - T: 'static, - { - self.helpers.match_first_type_mut::() - } - - pub fn qemu(&self) -> &Qemu { - &self.qemu - } - - pub fn helpers(&self) -> &QT { - &self.helpers - } - - pub fn helpers_mut(&mut self) -> &mut QT { - &mut self.helpers - } - - pub fn instruction( - &self, - addr: GuestAddr, - hook: Hook< - fn(&mut Self, Option<&mut S>, GuestAddr), - Box FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr)>, - extern "C" fn(*const (), pc: GuestAddr), - >, - invalidate_block: bool, - ) -> InstructionHookId { - match hook { - Hook::Function(f) => self.instruction_function(addr, f, invalidate_block), - Hook::Closure(c) => self.instruction_closure(addr, c, invalidate_block), - Hook::Raw(r) => { - let z: *const () = ptr::null::<()>(); - self.qemu.set_hook(z, addr, r, invalidate_block) - } - Hook::Empty => InstructionHookId(0), // TODO error type - } - } - - pub fn instruction_function( - &self, - addr: GuestAddr, - hook: fn(&mut Self, Option<&mut S>, GuestAddr), - invalidate_block: bool, - ) -> InstructionHookId { - unsafe { - self.qemu.set_hook( - transmute(hook), - addr, - func_generic_hook_wrapper::, - invalidate_block, - ) - } - } - - pub fn instruction_closure( - &self, - addr: GuestAddr, - hook: Box FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr)>, - invalidate_block: bool, - ) -> InstructionHookId { - unsafe { - let fat: FatPtr = transmute(hook); - GENERIC_HOOKS.push(Box::pin((InstructionHookId(0), fat))); - let id = self.qemu.set_hook( - &mut GENERIC_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .1, - addr, - closure_generic_hook_wrapper::, - invalidate_block, - ); - GENERIC_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .0 = id; - id - } - } - - pub fn edges( - &self, - generation_hook: Hook< - fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option, - Box< - dyn for<'a> FnMut( - &'a mut Self, - Option<&'a mut S>, - GuestAddr, - GuestAddr, - ) -> Option, - >, - extern "C" fn(*const (), src: GuestAddr, dest: GuestAddr) -> u64, - >, - execution_hook: Hook< - fn(&mut Self, Option<&mut S>, id: u64), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64)>, - extern "C" fn(*const (), id: u64), - >, - ) -> EdgeHookId { - unsafe { - let gen = get_raw_hook!( - generation_hook, - edge_gen_hook_wrapper::, - unsafe extern "C" fn( - &mut HookState<1, EdgeHookId>, - src: GuestAddr, - dest: GuestAddr, - ) -> u64 - ); - let exec = get_raw_hook!( - execution_hook, - edge_0_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<1, EdgeHookId>, id: u64) - ); - EDGE_HOOKS.push(Box::pin(HookState { - id: EdgeHookId(0), - gen: hook_to_repr!(generation_hook), - post_gen: HookRepr::Empty, - execs: [hook_to_repr!(execution_hook)], - })); - let id = self.qemu.add_edge_hooks( - EDGE_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(), - gen, - exec, - ); - EDGE_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .id = id; - id - } - } - - pub fn blocks( - &self, - generation_hook: Hook< - fn(&mut Self, Option<&mut S>, pc: GuestAddr) -> Option, - Box FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr) -> Option>, - unsafe extern "C" fn(*const (), pc: GuestAddr) -> u64, - >, - post_generation_hook: Hook< - fn(&mut Self, Option<&mut S>, pc: GuestAddr, block_length: GuestUsize), - Box FnMut(&'a mut Self, Option<&mut S>, GuestAddr, GuestUsize)>, - unsafe extern "C" fn(*const (), pc: GuestAddr, block_length: GuestUsize), - >, - execution_hook: Hook< - fn(&mut Self, Option<&mut S>, id: u64), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64)>, - unsafe extern "C" fn(*const (), id: u64), - >, - ) -> BlockHookId { - unsafe { - let gen = get_raw_hook!( - generation_hook, - block_gen_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<1, BlockHookId>, pc: GuestAddr) -> u64 - ); - let postgen = get_raw_hook!( - post_generation_hook, - block_post_gen_hook_wrapper::, - unsafe extern "C" fn( - &mut HookState<1, BlockHookId>, - pc: GuestAddr, - block_length: GuestUsize, - ) - ); - let exec = get_raw_hook!( - execution_hook, - block_0_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<1, BlockHookId>, id: u64) - ); - BLOCK_HOOKS.push(Box::pin(HookState { - id: BlockHookId(0), - gen: hook_to_repr!(generation_hook), - post_gen: hook_to_repr!(post_generation_hook), - execs: [hook_to_repr!(execution_hook)], - })); - let id = self.qemu.add_block_hooks( - BLOCK_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(), - gen, - postgen, - exec, - ); - BLOCK_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .id = id; - id - } - } - - #[allow(clippy::similar_names)] - pub fn reads( - &self, - generation_hook: Hook< - fn( - &mut Self, - Option<&mut S>, - pc: GuestAddr, - addr: *mut TCGTemp, - info: MemAccessInfo, - ) -> Option, - Box< - dyn for<'a> FnMut( - &'a mut Self, - Option<&'a mut S>, - GuestAddr, - *mut TCGTemp, - MemAccessInfo, - ) -> Option, - >, - unsafe extern "C" fn( - *const (), - pc: GuestAddr, - addr: *mut TCGTemp, - info: MemAccessInfo, - ) -> u64, - >, - execution_hook_1: Hook< - fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr), - >, - execution_hook_2: Hook< - fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr), - >, - execution_hook_4: Hook< - fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr), - >, - execution_hook_8: Hook< - fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr), - >, - execution_hook_n: Hook< - fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr, usize)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize), - >, - ) -> ReadHookId { - unsafe { - let gen = get_raw_hook!( - generation_hook, - read_gen_hook_wrapper::, - unsafe extern "C" fn( - &mut HookState<5, ReadHookId>, - pc: GuestAddr, - addr: *mut TCGTemp, - info: MemAccessInfo, - ) -> u64 - ); - let exec1 = get_raw_hook!( - execution_hook_1, - read_0_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<5, ReadHookId>, id: u64, addr: GuestAddr) - ); - let exec2 = get_raw_hook!( - execution_hook_2, - read_1_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<5, ReadHookId>, id: u64, addr: GuestAddr) - ); - let exec4 = get_raw_hook!( - execution_hook_4, - read_2_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<5, ReadHookId>, id: u64, addr: GuestAddr) - ); - let exec8 = get_raw_hook!( - execution_hook_8, - read_3_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<5, ReadHookId>, id: u64, addr: GuestAddr) - ); - let execn = get_raw_hook!( - execution_hook_n, - read_4_exec_hook_wrapper::, - unsafe extern "C" fn( - &mut HookState<5, ReadHookId>, - id: u64, - addr: GuestAddr, - size: usize, - ) - ); - READ_HOOKS.push(Box::pin(HookState { - id: ReadHookId(0), - gen: hook_to_repr!(generation_hook), - post_gen: HookRepr::Empty, - execs: [ - hook_to_repr!(execution_hook_1), - hook_to_repr!(execution_hook_2), - hook_to_repr!(execution_hook_4), - hook_to_repr!(execution_hook_8), - hook_to_repr!(execution_hook_n), - ], - })); - let id = self.qemu.add_read_hooks( - READ_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(), - gen, - exec1, - exec2, - exec4, - exec8, - execn, - ); - READ_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .id = id; - id - } - } - - #[allow(clippy::similar_names)] - pub fn writes( - &self, - generation_hook: Hook< - fn( - &mut Self, - Option<&mut S>, - pc: GuestAddr, - addr: *mut TCGTemp, - info: MemAccessInfo, - ) -> Option, - Box< - dyn for<'a> FnMut( - &'a mut Self, - Option<&'a mut S>, - GuestAddr, - *mut TCGTemp, - MemAccessInfo, - ) -> Option, - >, - unsafe extern "C" fn( - *const (), - pc: GuestAddr, - addr: *mut TCGTemp, - info: MemAccessInfo, - ) -> u64, - >, - execution_hook_1: Hook< - fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr), - >, - execution_hook_2: Hook< - fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr), - >, - execution_hook_4: Hook< - fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr), - >, - execution_hook_8: Hook< - fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr), - >, - execution_hook_n: Hook< - fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr, usize)>, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize), - >, - ) -> WriteHookId { - unsafe { - let gen = get_raw_hook!( - generation_hook, - write_gen_hook_wrapper::, - unsafe extern "C" fn( - &mut HookState<5, WriteHookId>, - pc: GuestAddr, - addr: *mut TCGTemp, - info: MemAccessInfo, - ) -> u64 - ); - let exec1 = get_raw_hook!( - execution_hook_1, - write_0_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<5, WriteHookId>, id: u64, addr: GuestAddr) - ); - let exec2 = get_raw_hook!( - execution_hook_2, - write_1_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<5, WriteHookId>, id: u64, addr: GuestAddr) - ); - let exec4 = get_raw_hook!( - execution_hook_4, - write_2_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<5, WriteHookId>, id: u64, addr: GuestAddr) - ); - let exec8 = get_raw_hook!( - execution_hook_8, - write_3_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<5, WriteHookId>, id: u64, addr: GuestAddr) - ); - let execn = get_raw_hook!( - execution_hook_n, - write_4_exec_hook_wrapper::, - unsafe extern "C" fn( - &mut HookState<5, WriteHookId>, - id: u64, - addr: GuestAddr, - size: usize, - ) - ); - WRITE_HOOKS.push(Box::pin(HookState { - id: WriteHookId(0), - gen: hook_to_repr!(generation_hook), - post_gen: HookRepr::Empty, - execs: [ - hook_to_repr!(execution_hook_1), - hook_to_repr!(execution_hook_2), - hook_to_repr!(execution_hook_4), - hook_to_repr!(execution_hook_8), - hook_to_repr!(execution_hook_n), - ], - })); - let id = self.qemu.add_write_hooks( - WRITE_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(), - gen, - exec1, - exec2, - exec4, - exec8, - execn, - ); - WRITE_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .id = id; - id - } - } - - pub fn cmps( - &self, - generation_hook: Hook< - fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option, - Box< - dyn for<'a> FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr, usize) -> Option, - >, - unsafe extern "C" fn(*const (), pc: GuestAddr, size: usize) -> u64, - >, - execution_hook_1: Hook< - fn(&mut Self, Option<&mut S>, id: u64, v0: u8, v1: u8), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, u8, u8)>, - unsafe extern "C" fn(*const (), id: u64, v0: u8, v1: u8), - >, - execution_hook_2: Hook< - fn(&mut Self, Option<&mut S>, id: u64, v0: u16, v1: u16), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, u16, u16)>, - unsafe extern "C" fn(*const (), id: u64, v0: u16, v1: u16), - >, - execution_hook_4: Hook< - fn(&mut Self, Option<&mut S>, id: u64, v0: u32, v1: u32), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, u32, u32)>, - unsafe extern "C" fn(*const (), id: u64, v0: u32, v1: u32), - >, - execution_hook_8: Hook< - fn(&mut Self, Option<&mut S>, id: u64, v0: u64, v1: u64), - Box FnMut(&'a mut Self, Option<&'a mut S>, u64, u64, u64)>, - unsafe extern "C" fn(*const (), id: u64, v0: u64, v1: u64), - >, - ) -> CmpHookId { - unsafe { - let gen = get_raw_hook!( - generation_hook, - cmp_gen_hook_wrapper::, - unsafe extern "C" fn( - &mut HookState<4, CmpHookId>, - pc: GuestAddr, - size: usize, - ) -> u64 - ); - let exec1 = get_raw_hook!( - execution_hook_1, - cmp_0_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<4, CmpHookId>, id: u64, v0: u8, v1: u8) - ); - let exec2 = get_raw_hook!( - execution_hook_2, - cmp_1_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<4, CmpHookId>, id: u64, v0: u16, v1: u16) - ); - let exec4 = get_raw_hook!( - execution_hook_4, - cmp_2_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<4, CmpHookId>, id: u64, v0: u32, v1: u32) - ); - let exec8 = get_raw_hook!( - execution_hook_8, - cmp_3_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<4, CmpHookId>, id: u64, v0: u64, v1: u64) - ); - CMP_HOOKS.push(Box::pin(HookState { - id: CmpHookId(0), - gen: hook_to_repr!(generation_hook), - post_gen: HookRepr::Empty, - execs: [ - hook_to_repr!(execution_hook_1), - hook_to_repr!(execution_hook_2), - hook_to_repr!(execution_hook_4), - hook_to_repr!(execution_hook_8), - ], - })); - let id = self.qemu.add_cmp_hooks( - CMP_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(), - gen, - exec1, - exec2, - exec4, - exec8, - ); - CMP_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .id = id; - id - } - } - - pub fn backdoor( - &self, - hook: Hook< - fn(&mut Self, Option<&mut S>, cpu: CPUArchStatePtr, GuestAddr), - Box FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr)>, - extern "C" fn(*const (), cpu: CPUArchStatePtr, pc: GuestAddr), - >, - ) -> BackdoorHookId { - match hook { - Hook::Function(f) => self.backdoor_function(f), - Hook::Closure(c) => self.backdoor_closure(c), - Hook::Raw(r) => { - let z: *const () = ptr::null::<()>(); - self.qemu.add_backdoor_hook(z, r) - } - Hook::Empty => BackdoorHookId(0), // TODO error type - } - } - - pub fn backdoor_function( - &self, - hook: fn(&mut Self, Option<&mut S>, cpu: CPUArchStatePtr, pc: GuestAddr), - ) -> BackdoorHookId { - unsafe { - self.qemu - .add_backdoor_hook(transmute(hook), func_backdoor_hook_wrapper::) - } - } - - pub fn backdoor_closure( - &self, - hook: Box FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr)>, - ) -> BackdoorHookId { - unsafe { - let fat: FatPtr = transmute(hook); - BACKDOOR_HOOKS.push(Box::pin((BackdoorHookId(0), fat))); - let id = self.qemu.add_backdoor_hook( - &mut BACKDOOR_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .1, - closure_backdoor_hook_wrapper::, - ); - BACKDOOR_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .0 = id; - id - } - } - - #[cfg(emulation_mode = "usermode")] - #[allow(clippy::type_complexity)] - pub fn syscalls( - &self, - hook: Hook< - fn( - &mut Self, - Option<&mut S>, - sys_num: i32, - a0: GuestAddr, - a1: GuestAddr, - a2: GuestAddr, - a3: GuestAddr, - a4: GuestAddr, - a5: GuestAddr, - a6: GuestAddr, - a7: GuestAddr, - ) -> SyscallHookResult, - Box< - dyn for<'a> FnMut( - &'a mut Self, - Option<&'a mut S>, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> SyscallHookResult, - >, - extern "C" fn( - *const (), - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> SyscallHookResult, - >, - ) -> PreSyscallHookId { - match hook { - Hook::Function(f) => self.syscalls_function(f), - Hook::Closure(c) => self.syscalls_closure(c), - Hook::Raw(r) => { - let z: *const () = ptr::null::<()>(); - self.qemu.add_pre_syscall_hook(z, r) - } - Hook::Empty => PreSyscallHookId(0), // TODO error type - } - } - - #[cfg(emulation_mode = "usermode")] - #[allow(clippy::type_complexity)] - pub fn syscalls_function( - &self, - hook: fn( - &mut Self, - Option<&mut S>, - sys_num: i32, - a0: GuestAddr, - a1: GuestAddr, - a2: GuestAddr, - a3: GuestAddr, - a4: GuestAddr, - a5: GuestAddr, - a6: GuestAddr, - a7: GuestAddr, - ) -> SyscallHookResult, - ) -> PreSyscallHookId { - unsafe { - self.qemu - .add_pre_syscall_hook(transmute(hook), func_pre_syscall_hook_wrapper::) - } - } - - #[cfg(emulation_mode = "usermode")] - #[allow(clippy::type_complexity)] - pub fn syscalls_closure( - &self, - hook: Box< - dyn for<'a> FnMut( - &'a mut Self, - Option<&'a mut S>, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> SyscallHookResult, - >, - ) -> PreSyscallHookId { - unsafe { - let fat: FatPtr = transmute(hook); - PRE_SYSCALL_HOOKS.push(Box::pin((PreSyscallHookId(0), fat))); - let id = self.qemu.add_pre_syscall_hook( - &mut PRE_SYSCALL_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .1, - closure_pre_syscall_hook_wrapper::, - ); - PRE_SYSCALL_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .0 = id; - id - } - } - - #[cfg(emulation_mode = "usermode")] - #[allow(clippy::type_complexity)] - pub fn after_syscalls( - &self, - hook: Hook< - fn( - &mut Self, - Option<&mut S>, - res: GuestAddr, - sys_num: i32, - a0: GuestAddr, - a1: GuestAddr, - a2: GuestAddr, - a3: GuestAddr, - a4: GuestAddr, - a5: GuestAddr, - a6: GuestAddr, - a7: GuestAddr, - ) -> GuestAddr, - Box< - dyn for<'a> FnMut( - &'a mut Self, - Option<&mut S>, - GuestAddr, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> GuestAddr, - >, - extern "C" fn( - *const (), - GuestAddr, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> GuestAddr, - >, - ) -> PostSyscallHookId { - match hook { - Hook::Function(f) => self.after_syscalls_function(f), - Hook::Closure(c) => self.after_syscalls_closure(c), - Hook::Raw(r) => { - let z: *const () = ptr::null::<()>(); - self.qemu.add_post_syscall_hook(z, r) - } - Hook::Empty => PostSyscallHookId(0), // TODO error type - } - } - - #[cfg(emulation_mode = "usermode")] - #[allow(clippy::type_complexity)] - pub fn after_syscalls_function( - &self, - hook: fn( - &mut Self, - Option<&mut S>, - res: GuestAddr, - sys_num: i32, - a0: GuestAddr, - a1: GuestAddr, - a2: GuestAddr, - a3: GuestAddr, - a4: GuestAddr, - a5: GuestAddr, - a6: GuestAddr, - a7: GuestAddr, - ) -> GuestAddr, - ) -> PostSyscallHookId { - unsafe { - self.qemu - .add_post_syscall_hook(transmute(hook), func_post_syscall_hook_wrapper::) - } - } - - #[cfg(emulation_mode = "usermode")] - #[allow(clippy::type_complexity)] - pub fn after_syscalls_closure( - &self, - hook: Box< - dyn for<'a> FnMut( - &'a mut Self, - Option<&mut S>, - GuestAddr, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> GuestAddr, - >, - ) -> PostSyscallHookId { - unsafe { - let fat: FatPtr = transmute(hook); - POST_SYSCALL_HOOKS.push(Box::pin((PostSyscallHookId(0), fat))); - let id = self.qemu.add_post_syscall_hook( - &mut POST_SYSCALL_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .1, - closure_post_syscall_hook_wrapper::, - ); - POST_SYSCALL_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .0 = id; - id - } - } - - #[cfg(emulation_mode = "usermode")] - pub fn thread_creation( - &self, - hook: Hook< - fn(&mut Self, Option<&mut S>, tid: u32) -> bool, - Box FnMut(&'a mut Self, Option<&'a mut S>, u32) -> bool>, - extern "C" fn(*const (), tid: u32) -> bool, - >, - ) -> NewThreadHookId { - match hook { - Hook::Function(f) => self.thread_creation_function(f), - Hook::Closure(c) => self.thread_creation_closure(c), - Hook::Raw(r) => { - let z: *const () = ptr::null::<()>(); - self.qemu.add_new_thread_hook(z, r) - } - Hook::Empty => NewThreadHookId(0), // TODO error type - } - } - - #[cfg(emulation_mode = "usermode")] - pub fn thread_creation_function( - &self, - hook: fn(&mut Self, Option<&mut S>, tid: u32) -> bool, - ) -> NewThreadHookId { - unsafe { - self.qemu - .add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::) - } - } - - #[cfg(emulation_mode = "usermode")] - pub fn thread_creation_closure( - &self, - hook: Box FnMut(&'a mut Self, Option<&'a mut S>, u32) -> bool>, - ) -> NewThreadHookId { - unsafe { - let fat: FatPtr = transmute(hook); - NEW_THREAD_HOOKS.push(Box::pin((NewThreadHookId(0), fat))); - let id = self.qemu.add_new_thread_hook( - &mut NEW_THREAD_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .1, - closure_new_thread_hook_wrapper::, - ); - NEW_THREAD_HOOKS - .last_mut() - .unwrap() - .as_mut() - .get_unchecked_mut() - .0 = id; - id - } - } - - #[cfg(emulation_mode = "usermode")] - pub fn crash_function(&self, hook: fn(&mut Self, target_signal: i32)) { - unsafe { - self.qemu.set_crash_hook(crash_hook_wrapper::); - CRASH_HOOKS.push(HookRepr::Function(hook as *const libc::c_void)); - } - } - - #[cfg(emulation_mode = "usermode")] - pub fn crash_closure(&self, hook: Box) { - unsafe { - self.qemu.set_crash_hook(crash_hook_wrapper::); - CRASH_HOOKS.push(HookRepr::Closure(transmute(hook))); - } - } -} diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index 71687c3c5b..67dbbf0796 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -38,11 +38,7 @@ pub use arch::*; pub mod elf; -pub mod helpers; -pub use helpers::*; - -pub mod hooks; -pub use hooks::*; +pub mod modules; pub mod executor; pub use executor::QemuExecutor; diff --git a/libafl_qemu/src/helpers/calls.rs b/libafl_qemu/src/modules/calls.rs similarity index 65% rename from libafl_qemu/src/helpers/calls.rs rename to libafl_qemu/src/modules/calls.rs index c777599297..a412abcbed 100644 --- a/libafl_qemu/src/helpers/calls.rs +++ b/libafl_qemu/src/modules/calls.rs @@ -12,37 +12,36 @@ use thread_local::ThreadLocal; use crate::{ capstone, - helpers::{ - HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple, + modules::{ + EmulatorModule, EmulatorModuleTuple, EmulatorModules, HasInstrumentationFilter, IsFilter, QemuInstrumentationAddressRangeFilter, }, - hooks::{Hook, QemuHooks}, - qemu::ArchExtras, + qemu::{ArchExtras, Hook}, Qemu, }; pub trait CallTraceCollector: 'static { - fn on_call( + fn on_call( &mut self, - hooks: &mut QemuHooks, + emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc: GuestAddr, call_len: usize, ) where - S: UsesInput, - QT: QemuHelperTuple; + S: Unpin + UsesInput, + ET: EmulatorModuleTuple; - fn on_ret( + fn on_ret( &mut self, - hooks: &mut QemuHooks, + emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc: GuestAddr, ret_addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple; + S: Unpin + UsesInput, + ET: EmulatorModuleTuple; - // Frowarded from the `QemuCallTracerHelper` + // Frowarded from the `CallTracerModule` fn pre_exec(&mut self, _qemu: Qemu, _input: &I) where I: Input, @@ -57,31 +56,31 @@ pub trait CallTraceCollector: 'static { _exit_kind: &mut ExitKind, ) where OT: ObserversTuple, - S: UsesInput, + S: Unpin + UsesInput, { } } pub trait CallTraceCollectorTuple: 'static + MatchFirstType { - fn on_call_all( + fn on_call_all( &mut self, - hooks: &mut QemuHooks, + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, call_len: usize, ) where - S: UsesInput, - QT: QemuHelperTuple; + S: Unpin + UsesInput, + ET: EmulatorModuleTuple; - fn on_ret_all( + fn on_ret_all( &mut self, - hooks: &mut QemuHooks, + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _pc: GuestAddr, ret_addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple; + S: Unpin + UsesInput, + ET: EmulatorModuleTuple; fn pre_exec_all(&mut self, _qemu: Qemu, input: &I) where @@ -95,31 +94,31 @@ pub trait CallTraceCollectorTuple: 'static + MatchFirstType { _exit_kind: &mut ExitKind, ) where OT: ObserversTuple, - S: UsesInput; + S: Unpin + UsesInput; } impl CallTraceCollectorTuple for () { - fn on_call_all( + fn on_call_all( &mut self, - _hooks: &mut QemuHooks, + _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _pc: GuestAddr, _call_len: usize, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { } - fn on_ret_all( + fn on_ret_all( &mut self, - _hooks: &mut QemuHooks, + _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _pc: GuestAddr, _ret_addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { } @@ -137,7 +136,7 @@ impl CallTraceCollectorTuple for () { _exit_kind: &mut ExitKind, ) where OT: ObserversTuple, - S: UsesInput, + S: Unpin + UsesInput, { } } @@ -147,18 +146,18 @@ where Head: CallTraceCollector, Tail: CallTraceCollectorTuple, { - fn on_call_all( + fn on_call_all( &mut self, - hooks: &mut QemuHooks, + emulator_modules: &mut EmulatorModules, mut state: Option<&mut S>, pc: GuestAddr, call_len: usize, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { self.0.on_call( - hooks, + emulator_modules, match state.as_mut() { Some(s) => Some(*s), None => None, @@ -166,21 +165,21 @@ where pc, call_len, ); - self.1.on_call_all(hooks, state, pc, call_len); + self.1.on_call_all(emulator_modules, state, pc, call_len); } - fn on_ret_all( + fn on_ret_all( &mut self, - hooks: &mut QemuHooks, + emulator_modules: &mut EmulatorModules, mut state: Option<&mut S>, pc: GuestAddr, ret_addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { self.0.on_ret( - hooks, + emulator_modules, match state.as_mut() { Some(s) => Some(*s), None => None, @@ -188,7 +187,7 @@ where pc, ret_addr, ); - self.1.on_ret_all(hooks, state, pc, ret_addr); + self.1.on_ret_all(emulator_modules, state, pc, ret_addr); } fn pre_exec_all(&mut self, qemu: Qemu, input: &I) @@ -207,7 +206,7 @@ where exit_kind: &mut ExitKind, ) where OT: ObserversTuple, - S: UsesInput, + S: Unpin + UsesInput, { self.0.post_exec(qemu, input, observers, exit_kind); self.1.post_exec_all(qemu, input, observers, exit_kind); @@ -215,7 +214,7 @@ where } #[derive(Debug)] -pub struct QemuCallTracerHelper +pub struct CallTracerModule where T: CallTraceCollectorTuple, { @@ -224,9 +223,9 @@ where collectors: Option, } -impl QemuCallTracerHelper +impl CallTracerModule where - T: CallTraceCollectorTuple, + T: CallTraceCollectorTuple + Debug, { #[must_use] pub fn new(filter: QemuInstrumentationAddressRangeFilter, collectors: T) -> Self { @@ -242,16 +241,19 @@ where self.filter.allowed(addr) } - fn on_ret(hooks: &mut QemuHooks, state: Option<&mut S>, pc: GuestAddr) - where - S: UsesInput, - QT: QemuHelperTuple, + fn on_ret( + emulator_modules: &mut EmulatorModules, + state: Option<&mut S>, + pc: GuestAddr, + ) where + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let ret_addr: GuestAddr = hooks.qemu().read_return_address().unwrap(); + let ret_addr: GuestAddr = emulator_modules.qemu().read_return_address().unwrap(); // log::info!("RET @ 0x{:#x}", ret_addr); - let mut collectors = if let Some(h) = hooks.helpers_mut().match_first_type_mut::() { + let mut collectors = if let Some(h) = emulator_modules.get_mut::() { h.collectors.take() } else { return; @@ -262,24 +264,23 @@ where collectors .as_mut() .unwrap() - .on_ret_all(hooks, state, pc, ret_addr); - hooks - .helpers_mut() - .match_first_type_mut::() + .on_ret_all(emulator_modules, state, pc, ret_addr); + emulator_modules + .get_mut::() .unwrap() .collectors = collectors; } - fn gen_blocks_calls( - hooks: &mut QemuHooks, + fn gen_blocks_calls( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, ) -> Option where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - if let Some(h) = hooks.helpers_mut().match_first_type_mut::() { + if let Some(h) = emulator_modules.get_mut::() { if !h.must_instrument(pc) { return None; } @@ -293,21 +294,24 @@ where .unwrap(); } - let emu = hooks.qemu(); + let qemu = emulator_modules.qemu(); - if let Some(h) = hooks.helpers().match_first_type::() { + let mut call_addrs: Vec<(GuestAddr, usize)> = Vec::new(); + let mut ret_addrs: Vec = Vec::new(); + + if let Some(h) = emulator_modules.modules().match_first_type::() { #[allow(unused_mut)] let mut code = { #[cfg(emulation_mode = "usermode")] unsafe { - std::slice::from_raw_parts(emu.g2h(pc), 512) + std::slice::from_raw_parts(qemu.g2h(pc), 512) } #[cfg(emulation_mode = "systemmode")] &mut [0; 512] }; #[cfg(emulation_mode = "systemmode")] unsafe { - emu.read_mem(pc, code) + qemu.read_mem(pc, code) }; // TODO handle faults let mut iaddr = pc; @@ -322,39 +326,10 @@ where match u32::from(detail.0) { capstone::InsnGroupType::CS_GRP_CALL => { let call_len = insn.bytes().len(); - // TODO do not use a closure, find a more efficient way to pass call_len - let call_cb = Box::new( - move |hooks: &mut QemuHooks, state: Option<&mut S>, pc| { - // eprintln!("CALL @ 0x{:#x}", pc + call_len); - let mut collectors = if let Some(h) = - hooks.helpers_mut().match_first_type_mut::() - { - h.collectors.take() - } else { - return; - }; - if collectors.is_none() { - return; // TODO fix this, it can be None on races ret - } - collectors - .as_mut() - .unwrap() - .on_call_all(hooks, state, pc, call_len); - hooks - .helpers_mut() - .match_first_type_mut::() - .unwrap() - .collectors = collectors; - }, - ); - hooks.instruction_closure(insn.address() as GuestAddr, call_cb, false); + call_addrs.push((insn.address() as GuestAddr, call_len)); } capstone::InsnGroupType::CS_GRP_RET => { - hooks.instruction_function( - insn.address() as GuestAddr, - Self::on_ret, - false, - ); + ret_addrs.push(insn.address() as GuestAddr); break 'disasm; } capstone::InsnGroupType::CS_GRP_INVALID @@ -371,20 +346,51 @@ where #[cfg(emulation_mode = "usermode")] unsafe { - code = std::slice::from_raw_parts(emu.g2h(iaddr), 512); + code = std::slice::from_raw_parts(qemu.g2h(iaddr), 512); } #[cfg(emulation_mode = "systemmode")] unsafe { - emu.read_mem(pc, code); + qemu.read_mem(pc, code); } // TODO handle faults } } + for (call_addr, call_len) in call_addrs { + // TODO do not use a closure, find a more efficient way to pass call_len + let call_cb = Box::new( + move |emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc| { + // eprintln!("CALL @ 0x{:#x}", pc + call_len); + let mut collectors = + if let Some(h) = emulator_modules.get_mut::() { + h.collectors.take() + } else { + return; + }; + if collectors.is_none() { + return; // TODO fix this, it can be None on races ret + } + collectors + .as_mut() + .unwrap() + .on_call_all(emulator_modules, state, pc, call_len); + emulator_modules + .get_mut::() + .unwrap() + .collectors = collectors; + }, + ); + emulator_modules.instruction_closure(call_addr, call_cb, false); + } + + for ret_addr in ret_addrs { + emulator_modules.instruction_function(ret_addr, Self::on_ret, false); + } + None } } -impl HasInstrumentationFilter for QemuCallTracerHelper +impl HasInstrumentationFilter for CallTracerModule where T: CallTraceCollectorTuple, { @@ -397,39 +403,48 @@ where } } -impl QemuHelper for QemuCallTracerHelper +impl EmulatorModule for CallTracerModule where - S: UsesInput, + S: Unpin + UsesInput, T: CallTraceCollectorTuple + Debug, { - fn init_hooks(&self, hooks: &QemuHooks) + fn init_module(&self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - hooks.blocks( - Hook::Function(Self::gen_blocks_calls::), + emulator_modules.blocks( + Hook::Function(Self::gen_blocks_calls::), Hook::Empty, Hook::Empty, ); } - fn pre_exec(&mut self, qemu: Qemu, input: &S::Input) { - self.collectors.as_mut().unwrap().pre_exec_all(qemu, input); + fn pre_exec(&mut self, emulator_modules: &mut EmulatorModules, input: &S::Input) + where + ET: EmulatorModuleTuple, + { + self.collectors + .as_mut() + .unwrap() + .pre_exec_all(emulator_modules.qemu(), input); } - fn post_exec( + fn post_exec( &mut self, - qemu: Qemu, + emulator_modules: &mut EmulatorModules, input: &S::Input, observers: &mut OT, exit_kind: &mut ExitKind, ) where OT: ObserversTuple, + ET: EmulatorModuleTuple, { - self.collectors - .as_mut() - .unwrap() - .post_exec_all(qemu, input, observers, exit_kind); + self.collectors.as_mut().unwrap().post_exec_all( + emulator_modules.qemu(), + input, + observers, + exit_kind, + ); } } @@ -464,29 +479,29 @@ where 'a: 'static, { #[allow(clippy::unnecessary_cast)] - fn on_call( + fn on_call( &mut self, - _hooks: &mut QemuHooks, + _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, call_len: usize, ) where - S: UsesInput, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, { self.callstack_hash ^= pc as u64 + call_len as u64; } #[allow(clippy::unnecessary_cast)] - fn on_ret( + fn on_ret( &mut self, - _hooks: &mut QemuHooks, + _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _pc: GuestAddr, ret_addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, { self.callstack_hash ^= ret_addr as u64; } @@ -506,7 +521,7 @@ where exit_kind: &mut ExitKind, ) where OT: ObserversTuple, - S: UsesInput, + S: Unpin + UsesInput, { let observer = observers .get_mut(&self.observer_handle) @@ -553,15 +568,15 @@ impl FullBacktraceCollector { impl CallTraceCollector for FullBacktraceCollector { #[allow(clippy::unnecessary_cast)] - fn on_call( + fn on_call( &mut self, - _hooks: &mut QemuHooks, + _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, call_len: usize, ) where - S: UsesInput, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, { // TODO handle Thumb unsafe { @@ -570,15 +585,15 @@ impl CallTraceCollector for FullBacktraceCollector { } #[allow(clippy::unnecessary_cast)] - fn on_ret( + fn on_ret( &mut self, - _hooks: &mut QemuHooks, + _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _pc: GuestAddr, ret_addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, { unsafe { let v = &mut *CALLSTACKS.as_mut().unwrap().get_or_default().get(); diff --git a/libafl_qemu/src/helpers/cmplog.rs b/libafl_qemu/src/modules/cmplog.rs similarity index 78% rename from libafl_qemu/src/helpers/cmplog.rs rename to libafl_qemu/src/modules/cmplog.rs index e637cfe6a1..4750186a2d 100644 --- a/libafl_qemu/src/helpers/cmplog.rs +++ b/libafl_qemu/src/modules/cmplog.rs @@ -14,11 +14,12 @@ use serde::{Deserialize, Serialize}; #[cfg(emulation_mode = "usermode")] use crate::{capstone, qemu::ArchExtras, CallingConvention, Qemu}; use crate::{ - helpers::{ - hash_me, HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple, + emu::EmulatorModules, + modules::{ + hash_me, EmulatorModule, EmulatorModuleTuple, HasInstrumentationFilter, IsFilter, QemuInstrumentationAddressRangeFilter, }, - hooks::{Hook, QemuHooks}, + qemu::Hook, }; #[cfg_attr( @@ -44,11 +45,11 @@ impl QemuCmpsMapMetadata { libafl_bolts::impl_serdeany!(QemuCmpsMapMetadata); #[derive(Debug)] -pub struct QemuCmpLogHelper { +pub struct CmpLogModule { filter: QemuInstrumentationAddressRangeFilter, } -impl QemuCmpLogHelper { +impl CmpLogModule { #[must_use] pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { filter } @@ -60,13 +61,13 @@ impl QemuCmpLogHelper { } } -impl Default for QemuCmpLogHelper { +impl Default for CmpLogModule { fn default() -> Self { Self::new(QemuInstrumentationAddressRangeFilter::None) } } -impl HasInstrumentationFilter for QemuCmpLogHelper { +impl HasInstrumentationFilter for CmpLogModule { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.filter } @@ -76,16 +77,16 @@ impl HasInstrumentationFilter for QemuCmp } } -impl QemuHelper for QemuCmpLogHelper +impl EmulatorModule for CmpLogModule where - S: UsesInput + HasMetadata, + S: Unpin + UsesInput + HasMetadata, { - fn first_exec(&self, hooks: &QemuHooks) + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - hooks.cmps( - Hook::Function(gen_unique_cmp_ids::), + emulator_modules.cmps( + Hook::Function(gen_unique_cmp_ids::), Hook::Raw(trace_cmp1_cmplog), Hook::Raw(trace_cmp2_cmplog), Hook::Raw(trace_cmp4_cmplog), @@ -95,11 +96,11 @@ where } #[derive(Debug)] -pub struct QemuCmpLogChildHelper { +pub struct CmpLogChildModule { filter: QemuInstrumentationAddressRangeFilter, } -impl QemuCmpLogChildHelper { +impl CmpLogChildModule { #[must_use] pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { filter } @@ -111,25 +112,24 @@ impl QemuCmpLogChildHelper { } } -impl Default for QemuCmpLogChildHelper { +impl Default for CmpLogChildModule { fn default() -> Self { Self::new(QemuInstrumentationAddressRangeFilter::None) } } -impl QemuHelper for QemuCmpLogChildHelper +impl EmulatorModule for CmpLogChildModule where - S: UsesInput, - S: HasMetadata, + S: Unpin + UsesInput + HasMetadata, { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn first_exec(&self, hooks: &QemuHooks) + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - hooks.cmps( - Hook::Function(gen_hashed_cmp_ids::), + emulator_modules.cmps( + Hook::Function(gen_hashed_cmp_ids::), Hook::Raw(trace_cmp1_cmplog), Hook::Raw(trace_cmp2_cmplog), Hook::Raw(trace_cmp4_cmplog), @@ -138,18 +138,17 @@ where } } -pub fn gen_unique_cmp_ids( - hooks: &mut QemuHooks, +pub fn gen_unique_cmp_ids( + emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc: GuestAddr, _size: usize, ) -> Option where - S: HasMetadata, - S: UsesInput, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput + HasMetadata, { - if let Some(h) = hooks.match_helper_mut::() { + if let Some(h) = emulator_modules.get::() { if !h.must_instrument(pc) { return None; } @@ -170,18 +169,17 @@ where })) } -pub fn gen_hashed_cmp_ids( - hooks: &mut QemuHooks, +pub fn gen_hashed_cmp_ids( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, _size: usize, ) -> Option where - S: HasMetadata, - S: UsesInput, - QT: QemuHelperTuple, + S: HasMetadata + Unpin + UsesInput, + ET: EmulatorModuleTuple, { - if let Some(h) = hooks.match_helper_mut::() { + if let Some(h) = emulator_modules.get::() { if !h.must_instrument(pc) { return None; } @@ -215,13 +213,13 @@ pub extern "C" fn trace_cmp8_cmplog(_: *const (), id: u64, v0: u64, v1: u64) { #[cfg(emulation_mode = "usermode")] #[derive(Debug)] -pub struct QemuCmpLogRoutinesHelper { +pub struct CmpLogRoutinesModule { filter: QemuInstrumentationAddressRangeFilter, cs: Capstone, } #[cfg(emulation_mode = "usermode")] -impl QemuCmpLogRoutinesHelper { +impl CmpLogRoutinesModule { #[must_use] pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { @@ -262,16 +260,16 @@ impl QemuCmpLogRoutinesHelper { } } - fn gen_blocks_calls( - hooks: &mut QemuHooks, + fn gen_blocks_calls( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, ) -> Option where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - if let Some(h) = hooks.helpers_mut().match_first_type_mut::() { + if let Some(h) = emulator_modules.get_mut::() { if !h.must_instrument(pc) { return None; } @@ -285,9 +283,9 @@ impl QemuCmpLogRoutinesHelper { .unwrap(); } - let qemu = hooks.qemu(); + let qemu = emulator_modules.qemu(); - if let Some(h) = hooks.helpers().match_first_type::() { + if let Some(h) = emulator_modules.get::() { #[allow(unused_mut)] let mut code = { #[cfg(emulation_mode = "usermode")] @@ -314,7 +312,12 @@ impl QemuCmpLogRoutinesHelper { match u32::from(detail.0) { capstone::InsnGroupType::CS_GRP_CALL => { let k = (hash_me(pc.into())) & (CMPLOG_MAP_W as u64 - 1); - qemu.set_hook(k, insn.address() as GuestAddr, Self::on_call, false); + qemu.hooks().add_instruction_hooks( + k, + insn.address() as GuestAddr, + Self::on_call, + false, + ); } capstone::InsnGroupType::CS_GRP_RET | capstone::InsnGroupType::CS_GRP_INVALID @@ -345,7 +348,7 @@ impl QemuCmpLogRoutinesHelper { } #[cfg(emulation_mode = "usermode")] -impl HasInstrumentationFilter for QemuCmpLogRoutinesHelper { +impl HasInstrumentationFilter for CmpLogRoutinesModule { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.filter } @@ -356,16 +359,16 @@ impl HasInstrumentationFilter for QemuCmp } #[cfg(emulation_mode = "usermode")] -impl QemuHelper for QemuCmpLogRoutinesHelper +impl EmulatorModule for CmpLogRoutinesModule where - S: UsesInput, + S: Unpin + UsesInput, { - fn first_exec(&self, hooks: &QemuHooks) + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - hooks.blocks( - Hook::Function(Self::gen_blocks_calls::), + emulator_modules.blocks( + Hook::Function(Self::gen_blocks_calls::), Hook::Empty, Hook::Empty, ); diff --git a/libafl_qemu/src/helpers/edges.rs b/libafl_qemu/src/modules/edges.rs similarity index 81% rename from libafl_qemu/src/helpers/edges.rs rename to libafl_qemu/src/modules/edges.rs index e3547609fa..a8522788cf 100644 --- a/libafl_qemu/src/helpers/edges.rs +++ b/libafl_qemu/src/modules/edges.rs @@ -12,14 +12,14 @@ pub use libafl_targets::{ use serde::{Deserialize, Serialize}; #[cfg(emulation_mode = "systemmode")] -use crate::helpers::QemuInstrumentationPagingFilter; +use crate::modules::QemuInstrumentationPagingFilter; use crate::{ - helpers::{ - hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple, + emu::EmulatorModules, + modules::{ + hash_me, EmulatorModule, EmulatorModuleTuple, HasInstrumentationFilter, IsFilter, QemuInstrumentationAddressRangeFilter, }, - hooks::{Hook, QemuHooks}, - IsFilter, + qemu::Hook, }; #[cfg_attr( @@ -46,21 +46,21 @@ libafl_bolts::impl_serdeany!(QemuEdgesMapMetadata); #[cfg(emulation_mode = "usermode")] #[derive(Debug)] -pub struct QemuEdgeCoverageHelper { +pub struct EdgeCoverageModule { address_filter: QemuInstrumentationAddressRangeFilter, use_hitcounts: bool, } #[cfg(emulation_mode = "systemmode")] #[derive(Debug)] -pub struct QemuEdgeCoverageHelper { +pub struct EdgeCoverageModule { address_filter: QemuInstrumentationAddressRangeFilter, paging_filter: QemuInstrumentationPagingFilter, use_hitcounts: bool, } #[cfg(emulation_mode = "usermode")] -impl QemuEdgeCoverageHelper { +impl EdgeCoverageModule { #[must_use] pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { @@ -84,7 +84,7 @@ impl QemuEdgeCoverageHelper { } #[cfg(emulation_mode = "systemmode")] -impl QemuEdgeCoverageHelper { +impl EdgeCoverageModule { #[must_use] pub fn new( address_filter: QemuInstrumentationAddressRangeFilter, @@ -116,14 +116,14 @@ impl QemuEdgeCoverageHelper { } #[cfg(emulation_mode = "usermode")] -impl Default for QemuEdgeCoverageHelper { +impl Default for EdgeCoverageModule { fn default() -> Self { Self::new(QemuInstrumentationAddressRangeFilter::None) } } #[cfg(emulation_mode = "systemmode")] -impl Default for QemuEdgeCoverageHelper { +impl Default for EdgeCoverageModule { fn default() -> Self { Self::new( QemuInstrumentationAddressRangeFilter::None, @@ -132,7 +132,7 @@ impl Default for QemuEdgeCoverageHelper { } } -impl HasInstrumentationFilter for QemuEdgeCoverageHelper { +impl HasInstrumentationFilter for EdgeCoverageModule { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.address_filter } @@ -143,7 +143,7 @@ impl HasInstrumentationFilter for QemuEdg } #[cfg(emulation_mode = "systemmode")] -impl HasInstrumentationFilter for QemuEdgeCoverageHelper { +impl HasInstrumentationFilter for EdgeCoverageModule { fn filter(&self) -> &QemuInstrumentationPagingFilter { &self.paging_filter } @@ -153,20 +153,21 @@ impl HasInstrumentationFilter for QemuEdgeCover } } -impl QemuHelper for QemuEdgeCoverageHelper +impl EmulatorModule for EdgeCoverageModule where - S: UsesInput + HasMetadata, + S: Unpin + UsesInput + HasMetadata, { - fn first_exec(&self, hooks: &QemuHooks) + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { if self.use_hitcounts { // hooks.edges( - // Hook::Function(gen_unique_edge_ids::), + // Hook::Function(gen_unique_edge_ids::), // Hook::Raw(trace_edge_hitcount), // ); - let hook_id = hooks.edges(Hook::Function(gen_unique_edge_ids::), Hook::Empty); + let hook_id = + emulator_modules.edges(Hook::Function(gen_unique_edge_ids::), Hook::Empty); unsafe { libafl_qemu_sys::libafl_qemu_edge_hook_set_jit( hook_id.0, @@ -175,10 +176,11 @@ where } } else { // hooks.edges( - // Hook::Function(gen_unique_edge_ids::), + // Hook::Function(gen_unique_edge_ids::), // Hook::Raw(trace_edge_single), // ); - let hook_id = hooks.edges(Hook::Function(gen_unique_edge_ids::), Hook::Empty); + let hook_id = + emulator_modules.edges(Hook::Function(gen_unique_edge_ids::), Hook::Empty); unsafe { libafl_qemu_sys::libafl_qemu_edge_hook_set_jit( hook_id.0, @@ -189,25 +191,25 @@ where } } -pub type QemuCollidingEdgeCoverageHelper = QemuEdgeCoverageChildHelper; +pub type CollidingEdgeCoverageModule = EdgeCoverageChildModule; #[cfg(emulation_mode = "usermode")] #[derive(Debug)] -pub struct QemuEdgeCoverageChildHelper { +pub struct EdgeCoverageChildModule { address_filter: QemuInstrumentationAddressRangeFilter, use_hitcounts: bool, } #[cfg(emulation_mode = "systemmode")] #[derive(Debug)] -pub struct QemuEdgeCoverageChildHelper { +pub struct EdgeCoverageChildModule { address_filter: QemuInstrumentationAddressRangeFilter, paging_filter: QemuInstrumentationPagingFilter, use_hitcounts: bool, } #[cfg(emulation_mode = "usermode")] -impl QemuEdgeCoverageChildHelper { +impl EdgeCoverageChildModule { #[must_use] pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { @@ -231,7 +233,7 @@ impl QemuEdgeCoverageChildHelper { } #[cfg(emulation_mode = "systemmode")] -impl QemuEdgeCoverageChildHelper { +impl EdgeCoverageChildModule { #[must_use] pub fn new( address_filter: QemuInstrumentationAddressRangeFilter, @@ -263,14 +265,14 @@ impl QemuEdgeCoverageChildHelper { } #[cfg(emulation_mode = "usermode")] -impl Default for QemuEdgeCoverageChildHelper { +impl Default for EdgeCoverageChildModule { fn default() -> Self { Self::new(QemuInstrumentationAddressRangeFilter::None) } } #[cfg(emulation_mode = "systemmode")] -impl Default for QemuEdgeCoverageChildHelper { +impl Default for EdgeCoverageChildModule { fn default() -> Self { Self::new( QemuInstrumentationAddressRangeFilter::None, @@ -279,9 +281,7 @@ impl Default for QemuEdgeCoverageChildHelper { } } -impl HasInstrumentationFilter - for QemuEdgeCoverageChildHelper -{ +impl HasInstrumentationFilter for EdgeCoverageChildModule { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.address_filter } @@ -292,7 +292,7 @@ impl HasInstrumentationFilter } #[cfg(emulation_mode = "systemmode")] -impl HasInstrumentationFilter for QemuEdgeCoverageChildHelper { +impl HasInstrumentationFilter for EdgeCoverageChildModule { fn filter(&self) -> &QemuInstrumentationPagingFilter { &self.paging_filter } @@ -302,24 +302,24 @@ impl HasInstrumentationFilter for QemuEdgeCover } } -impl QemuHelper for QemuEdgeCoverageChildHelper +impl EmulatorModule for EdgeCoverageChildModule where - S: UsesInput + HasMetadata, + S: Unpin + UsesInput, { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn first_exec(&self, hooks: &QemuHooks) + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { if self.use_hitcounts { - hooks.edges( - Hook::Function(gen_hashed_edge_ids::), + emulator_modules.edges( + Hook::Function(gen_hashed_edge_ids::), Hook::Raw(trace_edge_hitcount_ptr), ); } else { - hooks.edges( - Hook::Function(gen_hashed_edge_ids::), + emulator_modules.edges( + Hook::Function(gen_hashed_edge_ids::), Hook::Raw(trace_edge_single_ptr), ); } @@ -328,7 +328,7 @@ where #[cfg(emulation_mode = "usermode")] #[derive(Debug)] -pub struct QemuEdgeCoverageClassicHelper { +pub struct EdgeCoverageClassicModule { address_filter: QemuInstrumentationAddressRangeFilter, use_hitcounts: bool, use_jit: bool, @@ -336,7 +336,7 @@ pub struct QemuEdgeCoverageClassicHelper { #[cfg(emulation_mode = "systemmode")] #[derive(Debug)] -pub struct QemuEdgeCoverageClassicHelper { +pub struct EdgeCoverageClassicModule { address_filter: QemuInstrumentationAddressRangeFilter, paging_filter: QemuInstrumentationPagingFilter, use_hitcounts: bool, @@ -344,7 +344,7 @@ pub struct QemuEdgeCoverageClassicHelper { } #[cfg(emulation_mode = "usermode")] -impl QemuEdgeCoverageClassicHelper { +impl EdgeCoverageClassicModule { #[must_use] pub fn new(address_filter: QemuInstrumentationAddressRangeFilter, use_jit: bool) -> Self { Self { @@ -373,7 +373,7 @@ impl QemuEdgeCoverageClassicHelper { } #[cfg(emulation_mode = "systemmode")] -impl QemuEdgeCoverageClassicHelper { +impl EdgeCoverageClassicModule { #[must_use] pub fn new( address_filter: QemuInstrumentationAddressRangeFilter, @@ -409,14 +409,14 @@ impl QemuEdgeCoverageClassicHelper { } #[cfg(emulation_mode = "usermode")] -impl Default for QemuEdgeCoverageClassicHelper { +impl Default for EdgeCoverageClassicModule { fn default() -> Self { Self::new(QemuInstrumentationAddressRangeFilter::None, false) } } #[cfg(emulation_mode = "systemmode")] -impl Default for QemuEdgeCoverageClassicHelper { +impl Default for EdgeCoverageClassicModule { fn default() -> Self { Self::new( QemuInstrumentationAddressRangeFilter::None, @@ -426,9 +426,7 @@ impl Default for QemuEdgeCoverageClassicHelper { } } -impl HasInstrumentationFilter - for QemuEdgeCoverageClassicHelper -{ +impl HasInstrumentationFilter for EdgeCoverageClassicModule { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.address_filter } @@ -439,7 +437,7 @@ impl HasInstrumentationFilter } #[cfg(emulation_mode = "systemmode")] -impl HasInstrumentationFilter for QemuEdgeCoverageClassicHelper { +impl HasInstrumentationFilter for EdgeCoverageClassicModule { fn filter(&self) -> &QemuInstrumentationPagingFilter { &self.paging_filter } @@ -450,20 +448,20 @@ impl HasInstrumentationFilter for QemuEdgeCover } #[allow(clippy::collapsible_else_if)] -impl QemuHelper for QemuEdgeCoverageClassicHelper +impl EmulatorModule for EdgeCoverageClassicModule where - S: UsesInput + HasMetadata, + S: Unpin + UsesInput, { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn first_exec(&self, hooks: &QemuHooks) + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { if self.use_hitcounts { if self.use_jit { - let hook_id = hooks.blocks( - Hook::Function(gen_hashed_block_ids::), + let hook_id = emulator_modules.blocks( + Hook::Function(gen_hashed_block_ids::), Hook::Empty, Hook::Empty, ); @@ -475,16 +473,16 @@ where ); } } else { - hooks.blocks( - Hook::Function(gen_hashed_block_ids::), + emulator_modules.blocks( + Hook::Function(gen_hashed_block_ids::), Hook::Empty, Hook::Raw(trace_block_transition_hitcount), ); } } else { if self.use_jit { - let hook_id = hooks.blocks( - Hook::Function(gen_hashed_block_ids::), + let hook_id = emulator_modules.blocks( + Hook::Function(gen_hashed_block_ids::), Hook::Empty, Hook::Empty, ); @@ -496,8 +494,8 @@ where ); } } else { - hooks.blocks( - Hook::Function(gen_hashed_block_ids::), + emulator_modules.blocks( + Hook::Function(gen_hashed_block_ids::), Hook::Empty, Hook::Raw(trace_block_transition_single), ); @@ -508,17 +506,17 @@ where thread_local!(static PREV_LOC : UnsafeCell = const { UnsafeCell::new(0) }); -pub fn gen_unique_edge_ids( - hooks: &mut QemuHooks, +pub fn gen_unique_edge_ids( + emulator_modules: &mut EmulatorModules, state: Option<&mut S>, src: GuestAddr, dest: GuestAddr, ) -> Option where - S: UsesInput + HasMetadata, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput + HasMetadata, { - if let Some(h) = hooks.helpers().match_first_type::() { + if let Some(h) = emulator_modules.get::() { #[cfg(emulation_mode = "usermode")] { if !h.must_instrument(src) && !h.must_instrument(dest) { @@ -528,7 +526,7 @@ where #[cfg(emulation_mode = "systemmode")] { - let paging_id = hooks + let paging_id = emulator_modules .qemu() .current_cpu() .and_then(|cpu| cpu.current_paging_id()); @@ -576,20 +574,17 @@ pub extern "C" fn trace_edge_single(_: *const (), id: u64) { } } -pub fn gen_hashed_edge_ids( - hooks: &mut QemuHooks, +pub fn gen_hashed_edge_ids( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, src: GuestAddr, dest: GuestAddr, ) -> Option where - S: UsesInput, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, { - if let Some(h) = hooks - .helpers() - .match_first_type::() - { + if let Some(h) = emulator_modules.get::() { #[cfg(emulation_mode = "usermode")] if !h.must_instrument(src) && !h.must_instrument(dest) { return None; @@ -597,7 +592,7 @@ where #[cfg(emulation_mode = "systemmode")] { - let paging_id = hooks + let paging_id = emulator_modules .qemu() .current_cpu() .and_then(|cpu| cpu.current_paging_id()); @@ -626,35 +621,16 @@ pub extern "C" fn trace_edge_single_ptr(_: *const (), id: u64) { } } -/* -pub fn gen_addr_block_ids( - _hooks: &mut QemuHooks, +pub fn gen_hashed_block_ids( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, ) -> Option where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - // GuestAddress is u32 for 32 bit guests - #[allow(clippy::unnecessary_cast)] - Some(pc as u64) -} -*/ - -pub fn gen_hashed_block_ids( - hooks: &mut QemuHooks, - _state: Option<&mut S>, - pc: GuestAddr, -) -> Option -where - S: UsesInput, - QT: QemuHelperTuple, -{ - if let Some(h) = hooks - .helpers() - .match_first_type::() - { + if let Some(h) = emulator_modules.get::() { #[cfg(emulation_mode = "usermode")] { if !h.must_instrument(pc) { @@ -663,7 +639,7 @@ where } #[cfg(emulation_mode = "systemmode")] { - let paging_id = hooks + let paging_id = emulator_modules .qemu() .current_cpu() .and_then(|cpu| cpu.current_paging_id()); diff --git a/libafl_qemu/src/helpers/mod.rs b/libafl_qemu/src/modules/mod.rs similarity index 56% rename from libafl_qemu/src/helpers/mod.rs rename to libafl_qemu/src/modules/mod.rs index a45bb1bca1..f9a5157e9e 100644 --- a/libafl_qemu/src/helpers/mod.rs +++ b/libafl_qemu/src/modules/mod.rs @@ -6,138 +6,193 @@ use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple}; use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType}; use libafl_qemu_sys::{GuestAddr, GuestPhysAddr}; -use crate::{hooks::QemuHooks, Qemu}; +use crate::Qemu; + +#[cfg(emulation_mode = "usermode")] +pub mod usermode; +#[cfg(emulation_mode = "usermode")] +pub use usermode::*; + +#[cfg(emulation_mode = "systemmode")] +pub mod systemmode; +#[cfg(emulation_mode = "systemmode")] +pub use systemmode::*; pub mod edges; -pub use edges::QemuEdgeCoverageHelper; +pub use edges::EdgeCoverageModule; #[cfg(not(cpu_target = "hexagon"))] pub mod calls; #[cfg(not(cpu_target = "hexagon"))] -pub use calls::QemuCallTracerHelper; - -#[cfg(not(cpu_target = "hexagon"))] -pub mod drcov; -#[cfg(not(cpu_target = "hexagon"))] -pub use drcov::QemuDrCovHelper; +pub use calls::CallTracerModule; #[cfg(not(any(cpu_target = "mips", cpu_target = "hexagon")))] pub mod cmplog; #[cfg(not(any(cpu_target = "mips", cpu_target = "hexagon")))] -pub use cmplog::QemuCmpLogHelper; +pub use cmplog::CmpLogModule; -#[cfg(all(emulation_mode = "usermode", feature = "injections"))] -pub mod injections; -#[cfg(all(emulation_mode = "usermode", feature = "injections"))] -pub use injections::QemuInjectionHelper; +use crate::emu::EmulatorModules; -#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))] -pub mod snapshot; -#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))] -pub use snapshot::IntervalSnapshotFilter; -#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))] -pub use snapshot::QemuSnapshotHelper; - -#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))] -pub mod asan; -#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))] -pub use asan::{init_qemu_with_asan, QemuAsanHelper}; - -#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))] -pub mod asan_guest; -#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))] -pub use asan_guest::{init_qemu_with_asan_guest, QemuAsanGuestHelper}; - -/// A helper for `libafl_qemu`. +/// A module for `libafl_qemu`. // TODO remove 'static when specialization will be stable -pub trait QemuHelper: 'static + Debug +pub trait EmulatorModule: 'static + Debug where - S: UsesInput, + S: Unpin + UsesInput, { const HOOKS_DO_SIDE_EFFECTS: bool = true; - fn init_hooks(&self, _hooks: &QemuHooks) + /// Initialize the module, mostly used to install some hooks early. + fn init_module(&self, _emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { } - fn first_exec(&self, _hooks: &QemuHooks) + fn first_exec(&mut self, _emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { } - fn pre_exec(&mut self, _qemu: Qemu, _input: &S::Input) {} + fn pre_exec(&mut self, _emulator_modules: &mut EmulatorModules, _input: &S::Input) + where + ET: EmulatorModuleTuple, + { + } - fn post_exec( + fn post_exec( &mut self, - _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind, ) where OT: ObserversTuple, + ET: EmulatorModuleTuple, { } } -pub trait QemuHelperTuple: MatchFirstType + for<'a> SplitBorrowExtractFirstType<'a> +pub trait EmulatorModuleTuple: + MatchFirstType + for<'a> SplitBorrowExtractFirstType<'a> + Unpin where - S: UsesInput, + S: Unpin + UsesInput, { const HOOKS_DO_SIDE_EFFECTS: bool; - fn init_hooks_all(&self, hooks: &QemuHooks) + fn init_modules_all(&self, _emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple; + ET: EmulatorModuleTuple; - fn first_exec_all(&self, hooks: &QemuHooks) + fn first_exec_all(&mut self, _emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple; + ET: EmulatorModuleTuple; - fn pre_exec_all(&mut self, _qemu: Qemu, input: &S::Input); - - fn post_exec_all( + fn pre_exec_all( &mut self, - _qemu: Qemu, - input: &S::Input, - _observers: &mut OT, - _exit_kind: &mut ExitKind, + _emulator_modules: &mut EmulatorModules, + _input: &S::Input, ) where - OT: ObserversTuple; -} + ET: EmulatorModuleTuple; -impl QemuHelperTuple for () -where - S: UsesInput, -{ - const HOOKS_DO_SIDE_EFFECTS: bool = false; - - fn init_hooks_all(&self, _hooks: &QemuHooks) - where - QT: QemuHelperTuple, - { - } - - fn first_exec_all(&self, _hooks: &QemuHooks) - where - QT: QemuHelperTuple, - { - } - - fn pre_exec_all(&mut self, _qemu: Qemu, _input: &S::Input) {} - - fn post_exec_all( + fn post_exec_all( &mut self, - _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind, ) where OT: ObserversTuple, + ET: EmulatorModuleTuple; +} + +impl EmulatorModuleTuple for () +where + S: Unpin + UsesInput, +{ + const HOOKS_DO_SIDE_EFFECTS: bool = false; + + fn init_modules_all(&self, _emulator_modules: &mut EmulatorModules) + where + ET: EmulatorModuleTuple, { } + + fn first_exec_all(&mut self, _emulator_modules: &mut EmulatorModules) + where + ET: EmulatorModuleTuple, + { + } + + fn pre_exec_all( + &mut self, + _emulator_modules: &mut EmulatorModules, + _input: &S::Input, + ) where + ET: EmulatorModuleTuple, + { + } + + fn post_exec_all( + &mut self, + _emulator_modules: &mut EmulatorModules, + _input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + ET: EmulatorModuleTuple, + { + } +} + +impl EmulatorModuleTuple for (Head, Tail) +where + Head: EmulatorModule + Unpin, + Tail: EmulatorModuleTuple, + S: Unpin + UsesInput, +{ + const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS; + + fn init_modules_all(&self, emulator_modules: &mut EmulatorModules) + where + ET: EmulatorModuleTuple, + { + self.0.init_module(emulator_modules); + self.1.init_modules_all(emulator_modules); + } + + fn first_exec_all(&mut self, emulator_modules: &mut EmulatorModules) + where + ET: EmulatorModuleTuple, + { + self.0.first_exec(emulator_modules); + self.1.first_exec_all(emulator_modules); + } + + fn pre_exec_all(&mut self, emulator_modules: &mut EmulatorModules, input: &S::Input) + where + ET: EmulatorModuleTuple, + { + self.0.pre_exec(emulator_modules, input); + self.1.pre_exec_all(emulator_modules, input); + } + + fn post_exec_all( + &mut self, + emulator_modules: &mut EmulatorModules, + input: &S::Input, + observers: &mut OT, + exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + ET: EmulatorModuleTuple, + { + self.0 + .post_exec(emulator_modules, input, observers, exit_kind); + self.1 + .post_exec_all(emulator_modules, input, observers, exit_kind); + } } impl HasInstrumentationFilter<()> for () { @@ -164,49 +219,6 @@ where } } -impl QemuHelperTuple for (Head, Tail) -where - Head: QemuHelper, - Tail: QemuHelperTuple, - S: UsesInput, -{ - const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS; - - fn init_hooks_all(&self, hooks: &QemuHooks) - where - QT: QemuHelperTuple, - { - self.0.init_hooks(hooks); - self.1.init_hooks_all(hooks); - } - - fn first_exec_all(&self, hooks: &QemuHooks) - where - QT: QemuHelperTuple, - { - self.0.first_exec(hooks); - self.1.first_exec_all(hooks); - } - - fn pre_exec_all(&mut self, qemu: Qemu, input: &S::Input) { - self.0.pre_exec(qemu, input); - self.1.pre_exec_all(qemu, input); - } - - fn post_exec_all( - &mut self, - qemu: Qemu, - input: &S::Input, - observers: &mut OT, - exit_kind: &mut ExitKind, - ) where - OT: ObserversTuple, - { - self.0.post_exec(qemu, input, observers, exit_kind); - self.1.post_exec_all(qemu, input, observers, exit_kind); - } -} - #[derive(Debug, Clone)] pub enum QemuFilterList { AllowList(T), @@ -271,19 +283,6 @@ where } } -#[cfg(emulation_mode = "usermode")] -pub trait StdInstrumentationFilter: - HasInstrumentationFilter -{ -} - -#[cfg(emulation_mode = "systemmode")] -pub trait StdInstrumentationFilter: - HasInstrumentationFilter - + HasInstrumentationFilter -{ -} - static mut EMPTY_ADDRESS_FILTER: UnsafeCell = UnsafeCell::new(QemuFilterList::None); static mut EMPTY_PAGING_FILTER: UnsafeCell = @@ -309,25 +308,6 @@ impl HasInstrumentationFilter for () { } } -#[cfg(emulation_mode = "systemmode")] -impl StdInstrumentationFilter for (Head, ()) where - Head: HasInstrumentationFilter - + HasInstrumentationFilter -{ -} - -#[cfg(emulation_mode = "usermode")] -impl StdInstrumentationFilter for (Head, ()) where - Head: HasInstrumentationFilter -{ -} - -#[cfg(emulation_mode = "systemmode")] -impl StdInstrumentationFilter for () {} - -#[cfg(emulation_mode = "usermode")] -impl StdInstrumentationFilter for () {} - pub trait IsFilter: Debug { type FilterParameter; @@ -344,12 +324,6 @@ impl IsFilter for () { pub trait IsAddressFilter: IsFilter {} -#[cfg(emulation_mode = "systemmode")] -pub trait IsPagingFilter: IsFilter> {} - -#[cfg(emulation_mode = "systemmode")] -impl IsPagingFilter for QemuInstrumentationPagingFilter {} - impl IsAddressFilter for QemuInstrumentationAddressRangeFilter {} #[must_use] diff --git a/libafl_qemu/src/modules/systemmode/mod.rs b/libafl_qemu/src/modules/systemmode/mod.rs new file mode 100644 index 0000000000..326a26b9b3 --- /dev/null +++ b/libafl_qemu/src/modules/systemmode/mod.rs @@ -0,0 +1,24 @@ +use libafl_qemu_sys::GuestPhysAddr; + +use crate::modules::{ + HasInstrumentationFilter, IsFilter, QemuInstrumentationAddressRangeFilter, + QemuInstrumentationPagingFilter, +}; + +pub trait StdInstrumentationFilter: + HasInstrumentationFilter + + HasInstrumentationFilter +{ +} + +impl crate::modules::StdInstrumentationFilter for (Head, ()) where + Head: HasInstrumentationFilter + + HasInstrumentationFilter +{ +} + +impl StdInstrumentationFilter for () {} + +pub trait IsPagingFilter: IsFilter> {} + +impl IsPagingFilter for QemuInstrumentationPagingFilter {} diff --git a/libafl_qemu/src/helpers/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs similarity index 85% rename from libafl_qemu/src/helpers/asan.rs rename to libafl_qemu/src/modules/usermode/asan.rs index 0145626705..b48854a380 100644 --- a/libafl_qemu/src/helpers/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, env, fs, path::PathBuf, sync::Mutex}; use hashbrown::{HashMap, HashSet}; -use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple, HasMetadata}; +use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple}; use libc::{ c_void, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE, }; @@ -12,13 +12,12 @@ use num_enum::{IntoPrimitive, TryFromPrimitive}; use rangemap::RangeMap; use crate::{ - helpers::{ - calls::FullBacktraceCollector, HasInstrumentationFilter, IsFilter, QemuHelper, - QemuHelperTuple, QemuInstrumentationAddressRangeFilter, + modules::{ + calls::FullBacktraceCollector, snapshot::SnapshotModule, EmulatorModule, + EmulatorModuleTuple, HasInstrumentationFilter, IsFilter, + QemuInstrumentationAddressRangeFilter, }, - hooks::{Hook, QemuHooks}, - qemu::{MemAccessInfo, QemuInitError, SyscallHookResult}, - snapshot::QemuSnapshotHelper, + qemu::{MemAccessInfo, QemuInitError}, sys::TCGTemp, GuestAddr, Qemu, Regs, }; @@ -152,6 +151,11 @@ use std::pin::Pin; use object::{Object, ObjectSection}; +use crate::{ + emu::EmulatorModules, + qemu::{Hook, QemuHooks, SyscallHookResult}, +}; + pub struct AsanGiovese { pub alloc_tree: Mutex>, pub saved_tree: IntervalTree, @@ -205,7 +209,7 @@ impl AsanGiovese { } #[must_use] - fn new(emu: Qemu) -> Pin> { + fn new(qemu_hooks: QemuHooks) -> Pin> { let res = Self { alloc_tree: Mutex::new(IntervalTree::new()), saved_tree: IntervalTree::new(), @@ -215,7 +219,7 @@ impl AsanGiovese { snapshot_shadow: true, // By default, track the dirty shadow pages }; let mut boxed = Box::pin(res); - emu.add_pre_syscall_hook(boxed.as_mut(), Self::fake_syscall); + qemu_hooks.add_pre_syscall_hook(boxed.as_mut(), Self::fake_syscall); boxed } @@ -713,7 +717,7 @@ pub fn init_qemu_with_asan( } let qemu = Qemu::init(args, env)?; - let rt = AsanGiovese::new(qemu); + let rt = AsanGiovese::new(qemu.hooks()); Ok((qemu, rt)) } @@ -725,10 +729,10 @@ pub enum QemuAsanOptions { SnapshotDetectLeaks, } -pub type QemuAsanChildHelper = QemuAsanHelper; +pub type AsanChildModule = AsanModule; #[derive(Debug)] -pub struct QemuAsanHelper { +pub struct AsanModule { enabled: bool, detect_leaks: bool, empty: bool, @@ -736,7 +740,7 @@ pub struct QemuAsanHelper { filter: QemuInstrumentationAddressRangeFilter, } -impl QemuAsanHelper { +impl AsanModule { #[must_use] pub fn default(rt: Pin>) -> Self { Self::new( @@ -907,7 +911,7 @@ impl QemuAsanHelper { } } -impl HasInstrumentationFilter for QemuAsanHelper { +impl HasInstrumentationFilter for AsanModule { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.filter } @@ -917,103 +921,107 @@ impl HasInstrumentationFilter for QemuAsa } } -impl QemuHelper for QemuAsanHelper +impl EmulatorModule for AsanModule where - S: UsesInput + HasMetadata, + S: Unpin + UsesInput, { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn init_hooks(&self, hooks: &QemuHooks) + fn init_module(&self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - hooks.syscalls(Hook::Function(qasan_fake_syscall::)); + emulator_modules.syscalls(Hook::Function(qasan_fake_syscall::)); if self.rt.error_callback.is_some() { - hooks.crash_function(oncrash_asan::); + emulator_modules.crash_function(oncrash_asan::); } } - fn first_exec(&self, hooks: &QemuHooks) + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - hooks.reads( - Hook::Function(gen_readwrite_asan::), - Hook::Function(trace_read1_asan::), - Hook::Function(trace_read2_asan::), - Hook::Function(trace_read4_asan::), - Hook::Function(trace_read8_asan::), - Hook::Function(trace_read_n_asan::), + emulator_modules.reads( + Hook::Function(gen_readwrite_asan::), + Hook::Function(trace_read1_asan::), + Hook::Function(trace_read2_asan::), + Hook::Function(trace_read4_asan::), + Hook::Function(trace_read8_asan::), + Hook::Function(trace_read_n_asan::), ); - if hooks.match_helper::().is_none() { - hooks.writes( - Hook::Function(gen_readwrite_asan::), - Hook::Function(trace_write1_asan::), - Hook::Function(trace_write2_asan::), - Hook::Function(trace_write4_asan::), - Hook::Function(trace_write8_asan::), - Hook::Function(trace_write_n_asan::), + if emulator_modules.get::().is_none() { + emulator_modules.writes( + Hook::Function(gen_readwrite_asan::), + Hook::Function(trace_write1_asan::), + Hook::Function(trace_write2_asan::), + Hook::Function(trace_write4_asan::), + Hook::Function(trace_write8_asan::), + Hook::Function(trace_write_n_asan::), ); } else { - // track writes for both helpers as opt - hooks.writes( - Hook::Function(gen_write_asan_snapshot::), - Hook::Function(trace_write1_asan_snapshot::), - Hook::Function(trace_write2_asan_snapshot::), - Hook::Function(trace_write4_asan_snapshot::), - Hook::Function(trace_write8_asan_snapshot::), - Hook::Function(trace_write_n_asan_snapshot::), + // track writes for both modules as opt + emulator_modules.writes( + Hook::Function(gen_write_asan_snapshot::), + Hook::Function(trace_write1_asan_snapshot::), + Hook::Function(trace_write2_asan_snapshot::), + Hook::Function(trace_write4_asan_snapshot::), + Hook::Function(trace_write8_asan_snapshot::), + Hook::Function(trace_write_n_asan_snapshot::), ); } } - fn pre_exec(&mut self, qemu: Qemu, _input: &S::Input) { + fn pre_exec(&mut self, emulator_modules: &mut EmulatorModules, _input: &S::Input) + where + ET: EmulatorModuleTuple, + { if self.empty { - self.rt.snapshot(qemu); + self.rt.snapshot(emulator_modules.qemu()); self.empty = false; } } - fn post_exec( + fn post_exec( &mut self, - qemu: Qemu, + emulator_modules: &mut EmulatorModules, _input: &S::Input, _observers: &mut OT, exit_kind: &mut ExitKind, ) where OT: ObserversTuple, + ET: EmulatorModuleTuple, { - if self.reset(qemu) == AsanRollback::HasLeaks { + if self.reset(emulator_modules.qemu()) == AsanRollback::HasLeaks { *exit_kind = ExitKind::Crash; } } } -pub fn oncrash_asan(hooks: &mut QemuHooks, target_sig: i32) +pub fn oncrash_asan(emulator_modules: &mut EmulatorModules, target_sig: i32) where - S: UsesInput, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); let pc: GuestAddr = qemu.read_reg(Regs::Pc).unwrap(); h.rt.report(qemu, pc, AsanError::Signal(target_sig)); } -pub fn gen_readwrite_asan( - hooks: &mut QemuHooks, +pub fn gen_readwrite_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, _addr: *mut TCGTemp, _info: MemAccessInfo, ) -> Option where - S: UsesInput, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules.get_mut::().unwrap(); if h.must_instrument(pc) { Some(pc.into()) } else { @@ -1021,160 +1029,160 @@ where } } -pub fn trace_read1_asan( - hooks: &mut QemuHooks, +pub fn trace_read1_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.read_1(qemu, id as GuestAddr, addr); } -pub fn trace_read2_asan( - hooks: &mut QemuHooks, +pub fn trace_read2_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.read_2(qemu, id as GuestAddr, addr); } -pub fn trace_read4_asan( - hooks: &mut QemuHooks, +pub fn trace_read4_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.read_4(qemu, id as GuestAddr, addr); } -pub fn trace_read8_asan( - hooks: &mut QemuHooks, +pub fn trace_read8_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.read_8(qemu, id as GuestAddr, addr); } -pub fn trace_read_n_asan( - hooks: &mut QemuHooks, +pub fn trace_read_n_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, size: usize, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.read_n(qemu, id as GuestAddr, addr, size); } -pub fn trace_write1_asan( - hooks: &mut QemuHooks, +pub fn trace_write1_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.write_1(qemu, id as GuestAddr, addr); } -pub fn trace_write2_asan( - hooks: &mut QemuHooks, +pub fn trace_write2_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.write_2(qemu, id as GuestAddr, addr); } -pub fn trace_write4_asan( - hooks: &mut QemuHooks, +pub fn trace_write4_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.write_4(qemu, id as GuestAddr, addr); } -pub fn trace_write8_asan( - hooks: &mut QemuHooks, +pub fn trace_write8_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.write_8(qemu, id as GuestAddr, addr); } -pub fn trace_write_n_asan( - hooks: &mut QemuHooks, +pub fn trace_write_n_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, size: usize, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.read_n(qemu, id as GuestAddr, addr, size); } -pub fn gen_write_asan_snapshot( - hooks: &mut QemuHooks, +pub fn gen_write_asan_snapshot( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, _addr: *mut TCGTemp, _info: MemAccessInfo, ) -> Option where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules.get_mut::().unwrap(); if h.must_instrument(pc) { Some(pc.into()) } else { @@ -1182,100 +1190,110 @@ where } } -pub fn trace_write1_asan_snapshot( - hooks: &mut QemuHooks, +pub fn trace_write1_asan_snapshot( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { if id != 0 { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.write_1(qemu, id as GuestAddr, addr); } - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(addr, 1); } -pub fn trace_write2_asan_snapshot( - hooks: &mut QemuHooks, +pub fn trace_write2_asan_snapshot( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { if id != 0 { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.write_2(qemu, id as GuestAddr, addr); } - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(addr, 2); } -pub fn trace_write4_asan_snapshot( - hooks: &mut QemuHooks, +pub fn trace_write4_asan_snapshot( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { if id != 0 { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.write_4(qemu, id as GuestAddr, addr); } - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(addr, 4); } -pub fn trace_write8_asan_snapshot( - hooks: &mut QemuHooks, +pub fn trace_write8_asan_snapshot( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { if id != 0 { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.write_8(qemu, id as GuestAddr, addr); } - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(addr, 8); } -pub fn trace_write_n_asan_snapshot( - hooks: &mut QemuHooks, +pub fn trace_write_n_asan_snapshot( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, addr: GuestAddr, size: usize, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { if id != 0 { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); h.read_n(qemu, id as GuestAddr, addr, size); } - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(addr, size); } #[allow(clippy::too_many_arguments)] -pub fn qasan_fake_syscall( - hooks: &mut QemuHooks, +pub fn qasan_fake_syscall( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, sys_num: i32, a0: GuestAddr, @@ -1288,12 +1306,12 @@ pub fn qasan_fake_syscall( _a7: GuestAddr, ) -> SyscallHookResult where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { if sys_num == QASAN_FAKESYS_NR { - let qemu = *hooks.qemu(); - let h = hooks.match_helper_mut::().unwrap(); + let qemu = emulator_modules.qemu(); + let h = emulator_modules.get_mut::().unwrap(); match QasanAction::try_from(a0).expect("Invalid QASan action number") { QasanAction::CheckLoad => { let pc: GuestAddr = qemu.read_reg(Regs::Pc).unwrap(); diff --git a/libafl_qemu/src/helpers/asan_guest.rs b/libafl_qemu/src/modules/usermode/asan_guest.rs similarity index 77% rename from libafl_qemu/src/helpers/asan_guest.rs rename to libafl_qemu/src/modules/usermode/asan_guest.rs index c5a70886ee..65c314a6ed 100644 --- a/libafl_qemu/src/helpers/asan_guest.rs +++ b/libafl_qemu/src/modules/usermode/asan_guest.rs @@ -7,17 +7,17 @@ use std::{ path::PathBuf, }; -use libafl::{inputs::UsesInput, HasMetadata}; +use libafl::inputs::UsesInput; #[cfg(not(feature = "clippy"))] use crate::sys::libafl_tcg_gen_asan; use crate::{ - helpers::{ - HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple, + emu::EmulatorModules, + modules::{ + EmulatorModule, EmulatorModuleTuple, HasInstrumentationFilter, IsFilter, QemuInstrumentationAddressRangeFilter, }, - hooks::{Hook, QemuHooks}, - qemu::{MemAccessInfo, Qemu, QemuInitError}, + qemu::{Hook, MemAccessInfo, Qemu, QemuInitError}, sys::TCGTemp, GuestAddr, MapInfo, }; @@ -121,13 +121,13 @@ impl From<&MapInfo> for QemuAsanGuestMapping { } #[derive(Debug)] -pub struct QemuAsanGuestHelper { +pub struct AsanGuestModule { filter: QemuInstrumentationAddressRangeFilter, mappings: Vec, } #[cfg(any(cpu_target = "aarch64", cpu_target = "x86_64", feature = "clippy"))] -impl QemuAsanGuestHelper { +impl AsanGuestModule { const HIGH_SHADOW_START: GuestAddr = 0x02008fff7000; const HIGH_SHADOW_END: GuestAddr = 0x10007fff7fff; const LOW_SHADOW_START: GuestAddr = 0x00007fff8000; @@ -140,14 +140,14 @@ impl QemuAsanGuestHelper { cpu_target = "mips", cpu_target = "ppc" ))] -impl QemuAsanGuestHelper { +impl AsanGuestModule { const HIGH_SHADOW_START: GuestAddr = 0x28000000; const HIGH_SHADOW_END: GuestAddr = 0x3fffffff; const LOW_SHADOW_START: GuestAddr = 0x20000000; const LOW_SHADOW_END: GuestAddr = 0x23ffffff; } -impl QemuAsanGuestHelper { +impl AsanGuestModule { #[must_use] pub fn default(emu: &Qemu, asan: String) -> Self { Self::new(emu, asan, QemuInstrumentationAddressRangeFilter::None) @@ -197,7 +197,7 @@ impl QemuAsanGuestHelper { } } -impl HasInstrumentationFilter for QemuAsanGuestHelper { +impl HasInstrumentationFilter for AsanGuestModule { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.filter } @@ -207,18 +207,20 @@ impl HasInstrumentationFilter for QemuAsa } } -fn gen_readwrite_guest_asan( - hooks: &mut QemuHooks, +fn gen_readwrite_guest_asan( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo, ) -> Option where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); if !h.must_instrument(pc) { return None; } @@ -244,55 +246,56 @@ where #[allow(unused_variables)] unsafe fn libafl_tcg_gen_asan(addr: *mut TCGTemp, size: usize) {} -fn guest_trace_error_asan( - _hooks: &mut QemuHooks, +fn guest_trace_error_asan( + _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, _addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { panic!("I really shouldn't be here"); } -fn guest_trace_error_n_asan( - _hooks: &mut QemuHooks, +fn guest_trace_error_n_asan( + _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, _addr: GuestAddr, _n: usize, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { panic!("I really shouldn't be here either"); } -impl QemuHelper for QemuAsanGuestHelper +impl EmulatorModule for AsanGuestModule where - S: UsesInput + HasMetadata, + S: Unpin + UsesInput, { - fn first_exec(&self, hooks: &QemuHooks) + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, { - hooks.reads( - Hook::Function(gen_readwrite_guest_asan::), - Hook::Function(guest_trace_error_asan::), - Hook::Function(guest_trace_error_asan::), - Hook::Function(guest_trace_error_asan::), - Hook::Function(guest_trace_error_asan::), - Hook::Function(guest_trace_error_n_asan::), + emulator_modules.reads( + Hook::Function(gen_readwrite_guest_asan::), + Hook::Function(guest_trace_error_asan::), + Hook::Function(guest_trace_error_asan::), + Hook::Function(guest_trace_error_asan::), + Hook::Function(guest_trace_error_asan::), + Hook::Function(guest_trace_error_n_asan::), ); - hooks.writes( - Hook::Function(gen_readwrite_guest_asan::), - Hook::Function(guest_trace_error_asan::), - Hook::Function(guest_trace_error_asan::), - Hook::Function(guest_trace_error_asan::), - Hook::Function(guest_trace_error_asan::), - Hook::Function(guest_trace_error_n_asan::), + emulator_modules.writes( + Hook::Function(gen_readwrite_guest_asan::), + Hook::Function(guest_trace_error_asan::), + Hook::Function(guest_trace_error_asan::), + Hook::Function(guest_trace_error_asan::), + Hook::Function(guest_trace_error_asan::), + Hook::Function(guest_trace_error_n_asan::), ); } } diff --git a/libafl_qemu/src/helpers/drcov.rs b/libafl_qemu/src/modules/usermode/drcov.rs similarity index 74% rename from libafl_qemu/src/helpers/drcov.rs rename to libafl_qemu/src/modules/usermode/drcov.rs index 46d26c77ec..ad0e4a71bc 100644 --- a/libafl_qemu/src/helpers/drcov.rs +++ b/libafl_qemu/src/modules/usermode/drcov.rs @@ -8,12 +8,12 @@ use rangemap::RangeMap; use serde::{Deserialize, Serialize}; use crate::{ - helpers::{ - HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple, + emu::EmulatorModules, + modules::{ + EmulatorModule, EmulatorModuleTuple, HasInstrumentationFilter, IsFilter, QemuInstrumentationAddressRangeFilter, }, - hooks::{Hook, QemuHooks}, - Qemu, + qemu::Hook, }; static DRCOV_IDS: Mutex>> = Mutex::new(None); @@ -25,21 +25,21 @@ static DRCOV_LENGTHS: Mutex>> = Mutex::new allow(clippy::unsafe_derive_deserialize) )] // for SerdeAny #[derive(Debug, Default, Serialize, Deserialize)] -pub struct QemuDrCovMetadata { +pub struct DrCovMetadata { pub current_id: u64, } -impl QemuDrCovMetadata { +impl DrCovMetadata { #[must_use] pub fn new() -> Self { Self { current_id: 0 } } } -libafl_bolts::impl_serdeany!(QemuDrCovMetadata); +libafl_bolts::impl_serdeany!(DrCovMetadata); #[derive(Debug)] -pub struct QemuDrCovHelper { +pub struct DrCovModule { filter: QemuInstrumentationAddressRangeFilter, module_mapping: RangeMap, filename: PathBuf, @@ -47,12 +47,11 @@ pub struct QemuDrCovHelper { drcov_len: usize, } -impl QemuDrCovHelper { +impl DrCovModule { #[must_use] #[allow(clippy::let_underscore_untyped)] pub fn new( filter: QemuInstrumentationAddressRangeFilter, - module_mapping: RangeMap, filename: PathBuf, full_trace: bool, ) -> Self { @@ -63,7 +62,7 @@ impl QemuDrCovHelper { let _ = DRCOV_LENGTHS.lock().unwrap().insert(HashMap::new()); Self { filter, - module_mapping, + module_mapping: RangeMap::new(), filename, full_trace, drcov_len: 0, @@ -76,7 +75,7 @@ impl QemuDrCovHelper { } } -impl HasInstrumentationFilter for QemuDrCovHelper { +impl HasInstrumentationFilter for DrCovModule { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.filter } @@ -86,31 +85,49 @@ impl HasInstrumentationFilter for QemuDrC } } -impl QemuHelper for QemuDrCovHelper +impl EmulatorModule for DrCovModule where - S: UsesInput + HasMetadata, + S: Unpin + UsesInput + HasMetadata, { - fn init_hooks(&self, hooks: &QemuHooks) + fn init_module(&self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - hooks.blocks( - Hook::Function(gen_unique_block_ids::), - Hook::Function(gen_block_lengths::), - Hook::Function(exec_trace_block::), + emulator_modules.blocks( + Hook::Function(gen_unique_block_ids::), + Hook::Function(gen_block_lengths::), + Hook::Function(exec_trace_block::), ); } - fn pre_exec(&mut self, _qemu: Qemu, _input: &S::Input) {} + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) + where + ET: EmulatorModuleTuple, + { + let qemu = emulator_modules.qemu(); - fn post_exec( + for (i, (r, p)) in qemu + .mappings() + .filter_map(|m| { + m.path() + .map(|p| ((m.start() as usize)..(m.end() as usize), p.to_string())) + .filter(|(_, p)| !p.is_empty()) + }) + .enumerate() + { + self.module_mapping.insert(r, (i as u16, p)); + } + } + + fn post_exec( &mut self, - _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind, ) where OT: ObserversTuple, + ET: EmulatorModuleTuple, { let lengths_opt = DRCOV_LENGTHS.lock().unwrap(); let lengths = lengths_opt.as_ref().unwrap(); @@ -192,40 +209,34 @@ where } } -pub fn gen_unique_block_ids( - hooks: &mut QemuHooks, +pub fn gen_unique_block_ids( + emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc: GuestAddr, ) -> Option where - S: UsesInput + HasMetadata, - QT: QemuHelperTuple, + S: Unpin + UsesInput + HasMetadata, + ET: EmulatorModuleTuple, { - let drcov_helper = hooks - .helpers() - .match_first_type::() - .unwrap(); - if !drcov_helper.must_instrument(pc) { + let drcov_module = emulator_modules.get::().unwrap(); + if !drcov_module.must_instrument(pc) { return None; } let state = state.expect("The gen_unique_block_ids hook works only for in-process fuzzing"); if state .metadata_map_mut() - .get_mut::() + .get_mut::() .is_none() { - state.add_metadata(QemuDrCovMetadata::new()); + state.add_metadata(DrCovMetadata::new()); } - let meta = state - .metadata_map_mut() - .get_mut::() - .unwrap(); + let meta = state.metadata_map_mut().get_mut::().unwrap(); match DRCOV_MAP.lock().unwrap().as_mut().unwrap().entry(pc) { Entry::Occupied(e) => { let id = *e.get(); - if drcov_helper.full_trace { + if drcov_module.full_trace { Some(id) } else { None @@ -235,7 +246,7 @@ where let id = meta.current_id; e.insert(id); meta.current_id = id + 1; - if drcov_helper.full_trace { + if drcov_module.full_trace { // GuestAddress is u32 for 32 bit guests #[allow(clippy::unnecessary_cast)] Some(id as u64) @@ -246,20 +257,17 @@ where } } -pub fn gen_block_lengths( - hooks: &mut QemuHooks, +pub fn gen_block_lengths( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, block_length: GuestUsize, ) where - S: UsesInput + HasMetadata, - QT: QemuHelperTuple, + S: Unpin + UsesInput + HasMetadata, + ET: EmulatorModuleTuple, { - let drcov_helper = hooks - .helpers() - .match_first_type::() - .unwrap(); - if !drcov_helper.must_instrument(pc) { + let drcov_module = emulator_modules.get::().unwrap(); + if !drcov_module.must_instrument(pc) { return; } DRCOV_LENGTHS @@ -270,14 +278,16 @@ pub fn gen_block_lengths( .insert(pc, block_length); } -pub fn exec_trace_block(hooks: &mut QemuHooks, _state: Option<&mut S>, id: u64) -where - QT: QemuHelperTuple, - S: UsesInput + HasMetadata, +pub fn exec_trace_block( + emulator_modules: &mut EmulatorModules, + _state: Option<&mut S>, + id: u64, +) where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput + HasMetadata, { - if hooks - .helpers() - .match_first_type::() + if emulator_modules + .get::() .unwrap() .full_trace { diff --git a/libafl_qemu/src/helpers/injections.rs b/libafl_qemu/src/modules/usermode/injections.rs similarity index 91% rename from libafl_qemu/src/helpers/injections.rs rename to libafl_qemu/src/modules/usermode/injections.rs index 68b73f2022..fcbda512fe 100644 --- a/libafl_qemu/src/helpers/injections.rs +++ b/libafl_qemu/src/modules/usermode/injections.rs @@ -21,9 +21,13 @@ use serde::{Deserialize, Serialize}; #[cfg(not(cpu_target = "hexagon"))] use crate::SYS_execve; use crate::{ - elf::EasyElf, qemu::ArchExtras, CallingConvention, Hook, Qemu, QemuHelper, QemuHelperTuple, - QemuHooks, SyscallHookResult, + elf::EasyElf, + emu::EmulatorModules, + modules::{EmulatorModule, EmulatorModuleTuple}, + qemu::{ArchExtras, Hook, SyscallHookResult}, + CallingConvention, Qemu, }; + #[cfg(cpu_target = "hexagon")] /// Hexagon syscalls are not currently supported by the `syscalls` crate, so we just paste this here for now. /// @@ -146,13 +150,13 @@ pub struct Match { } #[derive(Debug)] -pub struct QemuInjectionHelper { +pub struct InjectionModule { pub tokens: Vec, definitions: HashMap, matches_list: Vec, } -impl QemuInjectionHelper { +impl InjectionModule { /// `configure_injections` is the main function to activate the injection /// vulnerability detection feature. pub fn from_yaml + Display>(yaml_file: P) -> Result { @@ -207,20 +211,20 @@ impl QemuInjectionHelper { }) } - fn on_call_check>( - hooks: &mut QemuHooks, - id: usize, - parameter: u8, - ) { - let qemu = hooks.qemu(); + fn on_call_check(emulator_modules: &mut EmulatorModules, id: usize, parameter: u8) + where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, + { + let qemu = emulator_modules.qemu(); let reg: GuestAddr = qemu .current_cpu() .unwrap() .read_function_argument(CallingConvention::Cdecl, parameter) .unwrap_or_default(); - let helper = hooks.helpers_mut().match_first_type_mut::().unwrap(); - let matches = &helper.matches_list[id]; + let module = emulator_modules.get_mut::().unwrap(); + let matches = &module.matches_list[id]; //println!("reg value = {:x}", reg); @@ -252,22 +256,22 @@ impl QemuInjectionHelper { } } -impl QemuHelper for QemuInjectionHelper +impl EmulatorModule for InjectionModule where - S: UsesInput, + S: Unpin + UsesInput, { - fn init_hooks(&self, hooks: &QemuHooks) + fn init_module(&self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - hooks.syscalls(Hook::Function(syscall_hook::)); + emulator_modules.syscalls(Hook::Function(syscall_hook::)); } - fn first_exec(&self, hooks: &QemuHooks) + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - let qemu = *hooks.qemu(); + let qemu = emulator_modules.qemu(); let mut libs: Vec = Vec::new(); for region in qemu.mappings() { @@ -316,7 +320,7 @@ where let param = func_definition.param; for hook_addr in hook_addrs { - hooks.instruction( + emulator_modules.instructions( hook_addr, Hook::Closure(Box::new(move |hooks, _state, _guest_addr| { Self::on_call_check(hooks, id, param); @@ -329,8 +333,8 @@ where } } -fn syscall_hook( - hooks: &mut QemuHooks, // our instantiated QemuHooks +fn syscall_hook( + emulator_modules: &mut EmulatorModules, // our instantiated QemuHooks _state: Option<&mut S>, syscall: i32, // syscall number x0: GuestAddr, // registers ... @@ -343,15 +347,14 @@ fn syscall_hook( _x7: GuestAddr, ) -> SyscallHookResult where - QT: QemuHelperTuple, - S: UsesInput, + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, { log::trace!("syscall_hook {syscall} {SYS_execve}"); debug_assert!(i32::try_from(SYS_execve).is_ok()); if syscall == SYS_execve as i32 { - let _helper = hooks - .helpers_mut() - .match_first_type_mut::() + let _module = emulator_modules + .get_mut::() .unwrap(); if x0 > 0 && x1 > 0 { let c_array = x1 as *const *const c_char; diff --git a/libafl_qemu/src/modules/usermode/mod.rs b/libafl_qemu/src/modules/usermode/mod.rs new file mode 100644 index 0000000000..6f4e9a2175 --- /dev/null +++ b/libafl_qemu/src/modules/usermode/mod.rs @@ -0,0 +1,40 @@ +#[cfg(not(cpu_target = "hexagon"))] +pub mod drcov; +#[cfg(not(cpu_target = "hexagon"))] +pub use drcov::DrCovModule; + +#[cfg(feature = "injections")] +pub mod injections; +#[cfg(feature = "injections")] +pub use injections::InjectionModule; + +#[cfg(not(cpu_target = "hexagon"))] +pub mod snapshot; +#[cfg(not(cpu_target = "hexagon"))] +pub use snapshot::IntervalSnapshotFilter; +#[cfg(not(cpu_target = "hexagon"))] +pub use snapshot::SnapshotModule; + +#[cfg(not(cpu_target = "hexagon"))] +pub mod asan; +#[cfg(not(cpu_target = "hexagon"))] +pub use asan::{init_qemu_with_asan, AsanModule}; + +#[cfg(not(cpu_target = "hexagon"))] +pub mod asan_guest; +#[cfg(not(cpu_target = "hexagon"))] +pub use asan_guest::{init_qemu_with_asan_guest, AsanGuestModule}; + +use crate::modules::{HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter}; + +pub trait StdInstrumentationFilter: + HasInstrumentationFilter +{ +} + +impl StdInstrumentationFilter for (Head, ()) where + Head: HasInstrumentationFilter +{ +} + +impl StdInstrumentationFilter for () {} diff --git a/libafl_qemu/src/helpers/snapshot.rs b/libafl_qemu/src/modules/usermode/snapshot.rs similarity index 87% rename from libafl_qemu/src/helpers/snapshot.rs rename to libafl_qemu/src/modules/usermode/snapshot.rs index f3ead856db..66f3dc6b46 100644 --- a/libafl_qemu/src/helpers/snapshot.rs +++ b/libafl_qemu/src/modules/usermode/snapshot.rs @@ -1,7 +1,7 @@ use std::{cell::UnsafeCell, mem::MaybeUninit, sync::Mutex}; use hashbrown::{HashMap, HashSet}; -use libafl::{inputs::UsesInput, HasMetadata}; +use libafl::inputs::UsesInput; use libafl_qemu_sys::{GuestAddr, MmapPerms}; use meminterval::{Interval, IntervalTree}; use thread_local::ThreadLocal; @@ -20,10 +20,9 @@ use crate::SYS_mmap2; )))] use crate::SYS_newfstatat; use crate::{ - asan::QemuAsanHelper, - helpers::{QemuHelper, QemuHelperTuple, Range}, - hooks::{Hook, QemuHooks}, - qemu::SyscallHookResult, + emu::EmulatorModules, + modules::{asan::AsanModule, EmulatorModule, EmulatorModuleTuple, Range}, + qemu::{Hook, SyscallHookResult}, Qemu, SYS_brk, SYS_fstat, SYS_fstatfs, SYS_futex, SYS_getrandom, SYS_mprotect, SYS_mremap, SYS_munmap, SYS_pread64, SYS_read, SYS_readlinkat, SYS_statfs, }; @@ -32,7 +31,7 @@ use crate::{ pub const SNAPSHOT_PAGE_SIZE: usize = 4096; pub const SNAPSHOT_PAGE_MASK: GuestAddr = !(SNAPSHOT_PAGE_SIZE as GuestAddr - 1); -pub type StopExecutionCallback = Box; +pub type StopExecutionCallback = Box; #[derive(Clone, Debug)] pub struct SnapshotPageInfo { @@ -80,7 +79,7 @@ pub enum IntervalSnapshotFilter { DenyList(Vec>), } -pub struct QemuSnapshotHelper { +pub struct SnapshotModule { pub accesses: ThreadLocal>, pub maps: MappingInfo, pub new_maps: Mutex, @@ -94,9 +93,9 @@ pub struct QemuSnapshotHelper { pub interval_filter: Vec, } -impl core::fmt::Debug for QemuSnapshotHelper { +impl core::fmt::Debug for SnapshotModule { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("QemuSnapshotHelper") + f.debug_struct("SnapshotModule") .field("accesses", &self.accesses) .field("new_maps", &self.new_maps) .field("pages", &self.pages) @@ -108,7 +107,7 @@ impl core::fmt::Debug for QemuSnapshotHelper { } } -impl QemuSnapshotHelper { +impl SnapshotModule { #[must_use] pub fn new() -> Self { Self { @@ -658,78 +657,85 @@ impl QemuSnapshotHelper { } } -impl Default for QemuSnapshotHelper { +impl Default for SnapshotModule { fn default() -> Self { Self::new() } } -impl QemuHelper for QemuSnapshotHelper +impl EmulatorModule for SnapshotModule where - S: UsesInput + HasMetadata, + S: Unpin + UsesInput, { - fn first_exec(&self, hooks: &QemuHooks) + fn init_module(&self, emulator_modules: &mut EmulatorModules) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { - if hooks.match_helper::().is_none() { - // The ASan helper, if present, will call the tracer hook for the snapshot helper as opt - hooks.writes( + if emulator_modules.get::().is_none() { + // The ASan module, if present, will call the tracer hook for the snapshot helper as opt + emulator_modules.writes( Hook::Empty, - Hook::Function(trace_write_snapshot::), - Hook::Function(trace_write_snapshot::), - Hook::Function(trace_write_snapshot::), - Hook::Function(trace_write_snapshot::), - Hook::Function(trace_write_n_snapshot::), + Hook::Function(trace_write_snapshot::), + Hook::Function(trace_write_snapshot::), + Hook::Function(trace_write_snapshot::), + Hook::Function(trace_write_snapshot::), + Hook::Function(trace_write_n_snapshot::), ); } if !self.accurate_unmap { - hooks.syscalls(Hook::Function(filter_mmap_snapshot::)); + emulator_modules.syscalls(Hook::Function(filter_mmap_snapshot::)); } - hooks.after_syscalls(Hook::Function(trace_mmap_snapshot::)); + emulator_modules.after_syscalls(Hook::Function(trace_mmap_snapshot::)); } - fn pre_exec(&mut self, qemu: Qemu, _input: &S::Input) { + fn pre_exec(&mut self, emulator_modules: &mut EmulatorModules, _input: &S::Input) + where + ET: EmulatorModuleTuple, + { if self.empty { - self.snapshot(qemu); + self.snapshot(emulator_modules.qemu()); } else { - self.reset(qemu); + self.reset(emulator_modules.qemu()); } } } -pub fn trace_write_snapshot( - hooks: &mut QemuHooks, +pub fn trace_write_snapshot( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, addr: GuestAddr, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(addr, SIZE); } -pub fn trace_write_n_snapshot( - hooks: &mut QemuHooks, +pub fn trace_write_n_snapshot( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, addr: GuestAddr, size: usize, ) where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(addr, size); } #[allow(clippy::too_many_arguments)] #[allow(non_upper_case_globals)] -pub fn filter_mmap_snapshot( - hooks: &mut QemuHooks, +pub fn filter_mmap_snapshot( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, sys_num: i32, a0: GuestAddr, @@ -742,11 +748,13 @@ pub fn filter_mmap_snapshot( _a7: GuestAddr, ) -> SyscallHookResult where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { if i64::from(sys_num) == SYS_munmap { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); if !h.is_unmap_allowed(a0 as GuestAddr, a1 as usize) { return SyscallHookResult::new(Some(0)); } @@ -754,10 +762,10 @@ where SyscallHookResult::new(None) } -#[allow(clippy::too_many_arguments)] +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] #[allow(non_upper_case_globals)] -pub fn trace_mmap_snapshot( - hooks: &mut QemuHooks, +pub fn trace_mmap_snapshot( + emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, result: GuestAddr, sys_num: i32, @@ -771,21 +779,27 @@ pub fn trace_mmap_snapshot( _a7: GuestAddr, ) -> GuestAddr where - S: UsesInput, - QT: QemuHelperTuple, + S: Unpin + UsesInput, + ET: EmulatorModuleTuple, { // NOT A COMPLETE LIST OF MEMORY EFFECTS match i64::from(sys_num) { SYS_read | SYS_pread64 => { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(a1, a2 as usize); } SYS_readlinkat => { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(a2, a3 as usize); } SYS_futex => { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(a0, a3 as usize); } #[cfg(not(any( @@ -796,27 +810,37 @@ where )))] SYS_newfstatat => { if a2 != 0 { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(a2, 4096); // stat is not greater than a page } } #[cfg(any(cpu_target = "arm", cpu_target = "mips", cpu_target = "i386"))] SYS_fstatat64 => { if a2 != 0 { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(a2, 4096); // stat is not greater than a page } } SYS_statfs | SYS_fstatfs | SYS_fstat => { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(a1, 4096); // stat is not greater than a page } SYS_getrandom => { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.access(a0, a1 as usize); } SYS_brk => { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); if h.brk != result && result != 0 { /* brk has changed. we change mapping from the snapshotted brk address to the new target_brk * If no brk mapping has been made until now, change_mapped won't change anything and just create a new mapping. @@ -838,7 +862,9 @@ where #[cfg(any(cpu_target = "arm", cpu_target = "mips"))] if sys_const == SYS_mmap2 { if let Ok(prot) = MmapPerms::try_from(a2 as i32) { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.add_mapped(result, a1 as usize, Some(prot)); } } @@ -846,23 +872,31 @@ where #[cfg(not(cpu_target = "arm"))] if sys_const == SYS_mmap { if let Ok(prot) = MmapPerms::try_from(a2 as i32) { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.add_mapped(result, a1 as usize, Some(prot)); } } if sys_const == SYS_mremap { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); // TODO get the old permissions from the removed mapping h.remove_mapped(a0, a1 as usize); h.add_mapped(result, a2 as usize, None); } else if sys_const == SYS_mprotect { if let Ok(prot) = MmapPerms::try_from(a2 as i32) { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); h.change_mapped(a0, a1 as usize, Some(prot)); } } else if sys_const == SYS_munmap { - let h = hooks.match_helper_mut::().unwrap(); + let h = emulator_modules + .get_mut::() + .unwrap(); if !h.accurate_unmap && !h.is_unmap_allowed(a0, a1 as usize) { h.remove_mapped(a0, a1 as usize); } diff --git a/libafl_qemu/src/qemu/hooks.rs b/libafl_qemu/src/qemu/hooks.rs new file mode 100644 index 0000000000..8053270ec3 --- /dev/null +++ b/libafl_qemu/src/qemu/hooks.rs @@ -0,0 +1,967 @@ +//! The high-level hooks +#![allow(clippy::type_complexity, clippy::missing_transmute_annotations)] + +use core::{ffi::c_void, fmt::Debug, mem::transmute, ptr}; + +use libafl::{executors::hooks::inprocess::inprocess_get_state, inputs::UsesInput}; +#[cfg(emulation_mode = "usermode")] +use libafl_qemu_sys::libafl_dump_core_hook; +use libafl_qemu_sys::{CPUArchStatePtr, FatPtr, GuestAddr, GuestUsize}; +#[cfg(feature = "python")] +use pyo3::{pyclass, pymethods, FromPyObject}; + +use crate::{ + emu::EmulatorModules, + modules::EmulatorModuleTuple, + qemu::{MemAccessInfo, Qemu}, + sys::TCGTemp, + HookData, HookId, +}; + +pub const SKIP_EXEC_HOOK: u64 = u64::MAX; + +// all kinds of hooks +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum HookRepr { + Function(*const c_void), + Closure(FatPtr), + Empty, +} + +#[derive(Debug)] +pub struct HookState { + id: H, + gen: HookRepr, + post_gen: HookRepr, + execs: [HookRepr; N], +} + +impl HookState { + pub fn new(id: H, gen: HookRepr, post_gen: HookRepr, execs: [HookRepr; N]) -> Self { + Self { + id, + gen, + post_gen, + execs, + } + } + + pub unsafe fn set_id(&mut self, id: H) { + self.id = id; + } +} + +pub enum Hook { + Function(F), + Closure(C), + Raw(R), + Empty, +} + +#[repr(C)] +#[cfg_attr(feature = "python", pyclass)] +#[cfg_attr(feature = "python", derive(FromPyObject))] +pub struct SyscallHookResult { + pub retval: GuestAddr, + pub skip_syscall: bool, +} + +impl Hook { + pub fn is_empty(&self) -> bool { + matches!(self, Hook::Empty) + } +} + +macro_rules! create_wrapper { + ($name:ident, ($($param:ident : $param_type:ty),*)) => { + paste::paste! { + pub extern "C" fn [](hook: &mut c_void, $($param: $param_type),*) + where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, + { + unsafe { + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::(hook)); + func(modules, inprocess_get_state::(), $($param),*); + } + } + + pub extern "C" fn [](hook: &mut FatPtr, $($param: $param_type),*) + where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, + { + unsafe { + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: &mut Box, Option<&mut S>, $($param_type),*)> = transmute(hook); + func(modules, inprocess_get_state::(), $($param),*); + } + } + } + }; + ($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty) => { + paste::paste! { + pub extern "C" fn [](hook: &mut c_void, $($param: $param_type),*) -> $ret_type + where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, + { + unsafe { + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::(hook)); + func(modules, inprocess_get_state::(), $($param),*) + } + } + + pub extern "C" fn [](hook: &mut FatPtr, $($param: $param_type),*) -> $ret_type + where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, + { + unsafe { + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: &mut Box, Option<&mut S>, $($param_type),*) -> $ret_type> = transmute(hook); + func(modules, inprocess_get_state::(), $($param),*) + } + } + } + }; +} + +macro_rules! create_gen_wrapper { + ($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty, $execs:literal, $hook_id:ident) => { + paste::paste! { + pub extern "C" fn [<$name _gen_hook_wrapper>](hook: &mut HookState<{ $execs }, $hook_id>, $($param: $param_type),*) -> $ret_type + where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, + { + unsafe { + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + + match &mut hook.gen { + HookRepr::Function(ptr) => { + let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) -> Option<$ret_type> = + transmute(*ptr); + func(modules, inprocess_get_state::(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id) + } + HookRepr::Closure(ptr) => { + let func: &mut Box< + dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*) -> Option<$ret_type>, + > = transmute(ptr); + func(modules, inprocess_get_state::(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id) + } + _ => 0, + } + } + } + } + } +} + +macro_rules! create_post_gen_wrapper { + ($name:ident, ($($param:ident : $param_type:ty),*), $execs:literal, $hook_id:ident) => { + paste::paste! { + pub extern "C" fn [<$name _post_gen_hook_wrapper>](hook: &mut HookState<{ $execs }, $hook_id>, $($param: $param_type),*) + where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, + { + unsafe { + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + match &mut hook.post_gen { + HookRepr::Function(ptr) => { + let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = + transmute(*ptr); + func(modules, inprocess_get_state::(), $($param),*); + } + HookRepr::Closure(ptr) => { + let func: &mut Box< + dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), + > = transmute(ptr); + func(modules, inprocess_get_state::(), $($param),*); + } + _ => (), + } + } + } + } + } +} + +macro_rules! create_exec_wrapper { + ($name:ident, ($($param:ident : $param_type:ty),*), $execidx:literal, $execs:literal, $hook_id:ident) => { + paste::paste! { + pub extern "C" fn [<$name _ $execidx _exec_hook_wrapper>](hook: &mut HookState<{ $execs }, $hook_id>, $($param: $param_type),*) + where + ET: EmulatorModuleTuple, + S: Unpin + UsesInput, + { + unsafe { + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + match &mut hook.execs[$execidx] { + HookRepr::Function(ptr) => { + let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(*ptr); + func(modules, inprocess_get_state::(), $($param),*); + } + HookRepr::Closure(ptr) => { + let func: &mut Box, Option<&mut S>, $($param_type),*)> = + transmute(ptr); + func(modules, inprocess_get_state::(), $($param),*); + } + _ => (), + } + } + } + } + } +} + +macro_rules! create_hook_id { + ($name:ident, $sys:ident, true) => { + paste::paste! { + #[derive(Clone, Copy, PartialEq, Debug)] + pub struct [<$name HookId>](pub(crate) usize); + impl [<$name HookId>] { + #[must_use] + pub fn invalid() -> Self { + Self(0) + } + } + impl HookId for [<$name HookId>] { + fn remove(&self, invalidate_block: bool) -> bool { + unsafe { libafl_qemu_sys::$sys(self.0, invalidate_block.into()) != 0 } + } + } + } + }; + ($name:ident, $sys:ident, false) => { + paste::paste! { + #[derive(Clone, Copy, PartialEq, Debug)] + pub struct [<$name HookId>](pub(crate) usize); + impl [<$name HookId>] { + #[must_use] + pub fn invalid() -> Self { + Self(0) + } + } + impl HookId for [<$name HookId>] { + fn remove(&self, _invalidate_block: bool) -> bool { + unsafe { libafl_qemu_sys::$sys(self.0) != 0 } + } + } + } + }; +} + +macro_rules! create_hook_types { + ($name:ident, $fn_type:ty, $closure_type:ty, $raw_type:ty) => { + paste::paste! { + pub type [<$name HookFn>] = $fn_type; + pub type [<$name HookClosure>] = $closure_type; + pub type [<$name HookRaw>] = $raw_type; + + pub type [<$name Hook>] = Hook< + [<$name HookFn>], + [<$name HookClosure>], + [<$name HookRaw>], + >; + } + }; +} + +#[cfg(emulation_mode = "usermode")] +create_hook_id!(PostSyscall, libafl_qemu_remove_post_syscall_hook, false); +#[cfg(emulation_mode = "usermode")] +create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false); + +// Instruction hook wrappers +create_hook_types!( + Instruction, + fn(&mut EmulatorModules, Option<&mut S>, GuestAddr), + Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, GuestAddr)>, + extern "C" fn(*const (), pc: GuestAddr) +); +create_hook_id!(Instruction, libafl_qemu_remove_instruction_hook, true); +create_wrapper!(instruction, (pc: GuestAddr)); + +// Backdoor hook wrappers +create_hook_types!( + Backdoor, + fn(&mut EmulatorModules, Option<&mut S>, cpu: CPUArchStatePtr, GuestAddr), + Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, GuestAddr)>, + extern "C" fn(*const (), cpu: CPUArchStatePtr, pc: GuestAddr) +); +create_hook_id!(Backdoor, libafl_qemu_remove_backdoor_hook, true); +create_wrapper!(backdoor, (cpu: CPUArchStatePtr, pc: GuestAddr)); + +// Pre-syscall hook wrappers +#[cfg(emulation_mode = "usermode")] +create_hook_types!( + PreSyscall, + fn( + &mut EmulatorModules, + Option<&mut S>, + sys_num: i32, + a0: GuestAddr, + a1: GuestAddr, + a2: GuestAddr, + a3: GuestAddr, + a4: GuestAddr, + a5: GuestAddr, + a6: GuestAddr, + a7: GuestAddr, + ) -> SyscallHookResult, + Box< + dyn for<'a> FnMut( + &'a mut EmulatorModules, + Option<&'a mut S>, + i32, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + ) -> SyscallHookResult, + >, + extern "C" fn( + *const (), + i32, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + ) -> SyscallHookResult +); +#[cfg(emulation_mode = "usermode")] +create_hook_id!(PreSyscall, libafl_qemu_remove_pre_syscall_hook, false); +#[cfg(emulation_mode = "usermode")] +create_wrapper!( + pre_syscall, + ( + sys_num: i32, + a0: GuestAddr, + a1: GuestAddr, + a2: GuestAddr, + a3: GuestAddr, + a4: GuestAddr, + a5: GuestAddr, + a6: GuestAddr, + a7: GuestAddr + ), + SyscallHookResult +); + +// Post-syscall hook wrappers +#[cfg(emulation_mode = "usermode")] +create_hook_types!( + PostSyscall, + fn( + &mut EmulatorModules, + Option<&mut S>, + res: GuestAddr, + sys_num: i32, + a0: GuestAddr, + a1: GuestAddr, + a2: GuestAddr, + a3: GuestAddr, + a4: GuestAddr, + a5: GuestAddr, + a6: GuestAddr, + a7: GuestAddr, + ) -> GuestAddr, + Box< + dyn for<'a> FnMut( + &'a mut EmulatorModules, + Option<&mut S>, + GuestAddr, + i32, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + ) -> GuestAddr, + >, + extern "C" fn( + *const (), + GuestAddr, + i32, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + ) -> GuestAddr +); +#[cfg(emulation_mode = "usermode")] +create_wrapper!( + post_syscall, + ( + res: GuestAddr, + sys_num: i32, + a0: GuestAddr, + a1: GuestAddr, + a2: GuestAddr, + a3: GuestAddr, + a4: GuestAddr, + a5: GuestAddr, + a6: GuestAddr, + a7: GuestAddr + ), + GuestAddr +); + +// New thread hook wrappers +#[cfg(emulation_mode = "usermode")] +create_hook_types!( + NewThread, + fn(&mut EmulatorModules, Option<&mut S>, tid: u32) -> bool, + Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u32) -> bool>, + extern "C" fn(*const (), tid: u32) -> bool +); +#[cfg(emulation_mode = "usermode")] +create_wrapper!(new_thread, (tid: u32), bool); + +// Edge hook wrappers +create_hook_types!( + EdgeGen, + fn(&mut EmulatorModules, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option, + Box< + dyn for<'a> FnMut( + &'a mut EmulatorModules, + Option<&'a mut S>, + GuestAddr, + GuestAddr, + ) -> Option, + >, + extern "C" fn(*const (), src: GuestAddr, dest: GuestAddr) -> u64 +); +create_hook_types!( + EdgeExec, + fn(&mut EmulatorModules, Option<&mut S>, id: u64), + Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64)>, + extern "C" fn(*const (), id: u64) +); +create_hook_id!(Edge, libafl_qemu_remove_edge_hook, true); +create_gen_wrapper!(edge, (src: GuestAddr, dest: GuestAddr), u64, 1, EdgeHookId); +create_exec_wrapper!(edge, (id: u64), 0, 1, EdgeHookId); + +// Block hook wrappers +create_hook_types!( + BlockGen, + fn(&mut EmulatorModules, Option<&mut S>, pc: GuestAddr) -> Option, + Box< + dyn for<'a> FnMut( + &'a mut EmulatorModules, + Option<&'a mut S>, + GuestAddr, + ) -> Option, + >, + unsafe extern "C" fn(*const (), pc: GuestAddr) -> u64 +); +create_hook_types!( + BlockPostGen, + fn(&mut EmulatorModules, Option<&mut S>, pc: GuestAddr, block_length: GuestUsize), + Box FnMut(&'a mut EmulatorModules, Option<&mut S>, GuestAddr, GuestUsize)>, + unsafe extern "C" fn(*const (), pc: GuestAddr, block_length: GuestUsize) +); +create_hook_types!( + BlockExec, + fn(&mut EmulatorModules, Option<&mut S>, id: u64), + Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64)>, + unsafe extern "C" fn(*const (), id: u64) +); + +create_hook_id!(Block, libafl_qemu_remove_block_hook, true); +create_gen_wrapper!(block, (addr: GuestAddr), u64, 1, BlockHookId); +create_post_gen_wrapper!(block, (addr: GuestAddr, len: GuestUsize), 1, BlockHookId); +create_exec_wrapper!(block, (id: u64), 0, 1, BlockHookId); + +// Read hook wrappers +create_hook_types!( + ReadGen, + fn( + qemu_modules: &mut EmulatorModules, + Option<&mut S>, + pc: GuestAddr, + addr: *mut TCGTemp, + info: MemAccessInfo, + ) -> Option, + Box< + dyn for<'a> FnMut( + &'a mut EmulatorModules, + Option<&'a mut S>, + GuestAddr, + *mut TCGTemp, + MemAccessInfo, + ) -> Option, + >, + unsafe extern "C" fn(*const (), pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo) -> u64 +); +create_hook_types!( + ReadExec, + fn(&mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr), + Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr)>, + unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr) +); +create_hook_types!( + ReadExecN, + fn(&mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), + Box< + dyn for<'a> FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr, usize), + >, + unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize) +); +create_hook_id!(Read, libafl_qemu_remove_read_hook, true); +create_gen_wrapper!(read, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, ReadHookId); +create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 0, 5, ReadHookId); +create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 1, 5, ReadHookId); +create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 2, 5, ReadHookId); +create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 3, 5, ReadHookId); +create_exec_wrapper!( + read, + (id: u64, addr: GuestAddr, size: usize), + 4, + 5, + ReadHookId +); + +// Write hook wrappers +create_hook_types!( + WriteGen, + fn( + &mut EmulatorModules, + Option<&mut S>, + pc: GuestAddr, + addr: *mut TCGTemp, + info: MemAccessInfo, + ) -> Option, + Box< + dyn for<'a> FnMut( + &'a mut EmulatorModules, + Option<&'a mut S>, + GuestAddr, + *mut TCGTemp, + MemAccessInfo, + ) -> Option, + >, + unsafe extern "C" fn(*const (), pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo) -> u64 +); +create_hook_types!( + WriteExec, + fn(&mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr), + Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr)>, + unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr) +); +create_hook_types!( + WriteExecN, + fn(&mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), + Box< + dyn for<'a> FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr, usize), + >, + unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize) +); +create_hook_id!(Write, libafl_qemu_remove_write_hook, true); +create_gen_wrapper!(write, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, WriteHookId); +create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 0, 5, WriteHookId); +create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 1, 5, WriteHookId); +create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 2, 5, WriteHookId); +create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 3, 5, WriteHookId); +create_exec_wrapper!( + write, + (id: u64, addr: GuestAddr, size: usize), + 4, + 5, + WriteHookId +); + +// Cmp hook wrappers +create_hook_types!( + CmpGen, + fn(&mut EmulatorModules, Option<&mut S>, pc: GuestAddr, size: usize) -> Option, + Box< + dyn for<'a> FnMut( + &'a mut EmulatorModules, + Option<&'a mut S>, + GuestAddr, + usize, + ) -> Option, + >, + unsafe extern "C" fn(*const (), pc: GuestAddr, size: usize) -> u64 +); +pub type CmpExecHook = Hook< + fn(&mut EmulatorModules, Option<&mut S>, id: u64, v0: SZ, v1: SZ), + Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, u64, SZ, SZ)>, + unsafe extern "C" fn(*const (), id: u64, v0: SZ, v1: SZ), +>; +create_hook_id!(Cmp, libafl_qemu_remove_cmp_hook, true); +create_gen_wrapper!(cmp, (pc: GuestAddr, size: usize), u64, 4, CmpHookId); +create_exec_wrapper!(cmp, (id: u64, v0: u8, v1: u8), 0, 4, CmpHookId); +create_exec_wrapper!(cmp, (id: u64, v0: u16, v1: u16), 1, 4, CmpHookId); +create_exec_wrapper!(cmp, (id: u64, v0: u32, v1: u32), 2, 4, CmpHookId); +create_exec_wrapper!(cmp, (id: u64, v0: u64, v1: u64), 3, 4, CmpHookId); + +// Crash hook wrappers +#[cfg(emulation_mode = "usermode")] +pub type CrashHookClosure = Box, i32)>; + +/// The thin wrapper around QEMU hooks. +/// It is considered unsafe to use it directly. +#[derive(Clone, Copy, Debug)] +pub struct QemuHooks { + _private: (), +} + +impl QemuHooks { + /// Get a `QemuHooks` object. + /// Same as `QemuHooks::get`, but without checking whether `QemuHooks` have been correctly initialized. + /// + /// # Safety + /// + /// Should not be used out of Qemu itself. + /// Prefer `Qemu::get` for a safe version of this method. + #[must_use] + pub unsafe fn get_unchecked() -> Self { + QemuHooks { _private: () } + } + + #[must_use] + pub fn get() -> Option { + // Use QEMU to check if hooks have been initialized. + Some(Qemu::get()?.hooks()) + } + + // TODO set T lifetime to be like Emulator + #[allow(clippy::missing_transmute_annotations)] + pub fn add_instruction_hooks>( + &self, + data: T, + addr: GuestAddr, + callback: extern "C" fn(T, GuestAddr), + invalidate_block: bool, + ) -> InstructionHookId { + unsafe { + let data: u64 = data.into().0; + let callback: extern "C" fn(u64, GuestAddr) = transmute(callback); + let num = libafl_qemu_sys::libafl_qemu_add_instruction_hooks( + addr.into(), + Some(callback), + data, + i32::from(invalidate_block), + ); + InstructionHookId(num) + } + } + + #[must_use] + pub fn remove_instruction_hooks_at(&self, addr: GuestAddr, invalidate_block: bool) -> usize { + unsafe { + libafl_qemu_sys::libafl_qemu_remove_instruction_hooks_at( + addr.into(), + i32::from(invalidate_block), + ) + } + } + + #[allow(clippy::missing_transmute_annotations)] + pub fn add_edge_hooks>( + &self, + data: T, + gen: Option u64>, + exec: Option, + ) -> EdgeHookId { + unsafe { + let data: u64 = data.into().0; + let gen: Option u64> = + transmute(gen); + let exec: Option = transmute(exec); + let num = libafl_qemu_sys::libafl_add_edge_hook(gen, exec, data); + EdgeHookId(num) + } + } + + #[allow(clippy::missing_transmute_annotations)] + pub fn add_block_hooks>( + &self, + data: T, + gen: Option u64>, + post_gen: Option, + exec: Option, + ) -> BlockHookId { + unsafe { + let data: u64 = data.into().0; + let gen: Option u64> = transmute(gen); + let post_gen: Option = + transmute(post_gen); + let exec: Option = transmute(exec); + let num = libafl_qemu_sys::libafl_add_block_hook(gen, post_gen, exec, data); + BlockHookId(num) + } + } + + /// `data` can be used to pass data that can be accessed as the first argument in the `gen` and the `exec` functions + /// + /// `gen` gets passed the current programm counter, mutable access to a `TCGTemp` and information about the memory + /// access being performed. + /// The `u64` return value is an id that gets passed to the `exec` functions as their second argument. + /// + /// `exec` hooks get invoked on every read performed by the guest + /// + /// `exec1`-`exec8` special case accesses of width 1-8 + /// + /// If there is no specialized hook for a given read width, the `exec_n` will be + /// called and its last argument will specify the access width + #[allow(clippy::missing_transmute_annotations)] + pub fn add_read_hooks>( + &self, + data: T, + gen: Option u64>, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + exec_n: Option, + ) -> ReadHookId { + unsafe { + let data: u64 = data.into().0; + let gen: Option< + unsafe extern "C" fn( + u64, + GuestAddr, + *mut TCGTemp, + libafl_qemu_sys::MemOpIdx, + ) -> u64, + > = transmute(gen); + let exec1: Option = transmute(exec1); + let exec2: Option = transmute(exec2); + let exec4: Option = transmute(exec4); + let exec8: Option = transmute(exec8); + let exec_n: Option = + transmute(exec_n); + let num = libafl_qemu_sys::libafl_add_read_hook( + gen, exec1, exec2, exec4, exec8, exec_n, data, + ); + ReadHookId(num) + } + } + + // TODO add MemOp info + #[allow(clippy::missing_transmute_annotations)] + pub fn add_write_hooks>( + &self, + data: T, + gen: Option u64>, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + exec_n: Option, + ) -> WriteHookId { + unsafe { + let data: u64 = data.into().0; + let gen: Option< + unsafe extern "C" fn( + u64, + GuestAddr, + *mut TCGTemp, + libafl_qemu_sys::MemOpIdx, + ) -> u64, + > = transmute(gen); + let exec1: Option = transmute(exec1); + let exec2: Option = transmute(exec2); + let exec4: Option = transmute(exec4); + let exec8: Option = transmute(exec8); + let exec_n: Option = + transmute(exec_n); + let num = libafl_qemu_sys::libafl_add_write_hook( + gen, exec1, exec2, exec4, exec8, exec_n, data, + ); + WriteHookId(num) + } + } + + #[allow(clippy::missing_transmute_annotations)] + pub fn add_cmp_hooks>( + &self, + data: T, + gen: Option u64>, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + ) -> CmpHookId { + unsafe { + let data: u64 = data.into().0; + let gen: Option u64> = transmute(gen); + let exec1: Option = transmute(exec1); + let exec2: Option = transmute(exec2); + let exec4: Option = transmute(exec4); + let exec8: Option = transmute(exec8); + let num = libafl_qemu_sys::libafl_add_cmp_hook(gen, exec1, exec2, exec4, exec8, data); + CmpHookId(num) + } + } + + #[allow(clippy::missing_transmute_annotations)] + pub fn add_backdoor_hook>( + &self, + data: T, + callback: extern "C" fn(T, CPUArchStatePtr, GuestAddr), + ) -> BackdoorHookId { + unsafe { + let data: u64 = data.into().0; + let callback: extern "C" fn(u64, CPUArchStatePtr, GuestAddr) = transmute(callback); + let num = libafl_qemu_sys::libafl_add_backdoor_hook(Some(callback), data); + BackdoorHookId(num) + } + } +} + +#[cfg(emulation_mode = "usermode")] +impl QemuHooks { + #[allow(clippy::type_complexity)] + pub fn add_pre_syscall_hook>( + &self, + data: T, + callback: extern "C" fn( + T, + i32, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + ) -> SyscallHookResult, + ) -> PreSyscallHookId { + unsafe { + let data: u64 = data.into().0; + let callback: extern "C" fn( + u64, + i32, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + ) -> libafl_qemu_sys::syshook_ret = transmute(callback); + let num = libafl_qemu_sys::libafl_add_pre_syscall_hook(Some(callback), data); + PreSyscallHookId(num) + } + } + + pub fn add_new_thread_hook>( + &self, + data: T, + callback: extern "C" fn(T, tid: u32) -> bool, + ) -> NewThreadHookId { + unsafe { + let data: u64 = data.into().0; + let callback: extern "C" fn(u64, 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, + data: T, + callback: extern "C" fn( + T, + GuestAddr, + i32, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + ) -> GuestAddr, + ) -> PostSyscallHookId { + unsafe { + let data: u64 = data.into().0; + let callback: extern "C" fn( + u64, + GuestAddr, + i32, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + GuestAddr, + ) -> GuestAddr = transmute(callback); + let num = libafl_qemu_sys::libafl_add_post_syscall_hook(Some(callback), data); + PostSyscallHookId(num) + } + } + + #[allow(clippy::type_complexity)] + #[allow(clippy::unused_self)] + pub(crate) fn set_crash_hook(self, callback: extern "C" fn(i32)) { + unsafe { + libafl_dump_core_hook = callback; + } + } +} + +#[cfg(feature = "python")] +#[pymethods] +impl SyscallHookResult { + #[new] + #[must_use] + pub fn new(value: Option) -> Self { + value.map_or( + Self { + retval: 0, + skip_syscall: false, + }, + |v| Self { + retval: v, + skip_syscall: true, + }, + ) + } +} + +#[cfg(not(feature = "python"))] +impl SyscallHookResult { + #[must_use] + pub fn new(value: Option) -> Self { + value.map_or( + Self { + retval: 0, + skip_syscall: false, + }, + |v| Self { + retval: v, + skip_syscall: true, + }, + ) + } +} diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index 8b516302c2..d9d0472c1f 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -13,7 +13,6 @@ use std::{ ops::Range, pin::Pin, ptr, - ptr::{addr_of, null}, }; use libafl_bolts::os::unix_signals::Signal; @@ -26,12 +25,10 @@ use libafl_qemu_sys::{ 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_remove_breakpoint, libafl_qemu_set_breakpoint, libafl_qemu_trigger_breakpoint, - libafl_qemu_write_reg, CPUArchState, CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, - GuestPhysAddr, GuestUsize, GuestVirtAddr, TCGTemp, + libafl_qemu_write_reg, CPUArchState, CPUStatePtr, FatPtr, GuestAddr, GuestPhysAddr, GuestUsize, + GuestVirtAddr, }; use num_traits::Num; -#[cfg(feature = "python")] -use pyo3::prelude::*; use strum::IntoEnumIterator; use crate::{GuestAddrKind, GuestReg, Regs}; @@ -47,49 +44,11 @@ mod systemmode; #[allow(unused_imports)] pub use systemmode::*; -pub const SKIP_EXEC_HOOK: u64 = u64::MAX; +mod hooks; +pub use hooks::*; + static mut QEMU_IS_INITIALIZED: bool = false; -macro_rules! create_hook_id { - ($name:ident, $sys:ident, true) => { - paste::paste! { - #[derive(Clone, Copy, PartialEq, Debug)] - pub struct [<$name HookId>](pub(crate) usize); - impl HookId for [<$name HookId>] { - fn remove(&self, invalidate_block: bool) -> bool { - unsafe { libafl_qemu_sys::$sys(self.0, invalidate_block.into()) != 0 } - } - } - } - }; - ($name:ident, $sys:ident, false) => { - paste::paste! { - #[derive(Clone, Copy, PartialEq, Debug)] - pub struct [<$name HookId>](pub(crate) usize); - impl HookId for [<$name HookId>] { - fn remove(&self, _invalidate_block: bool) -> bool { - unsafe { libafl_qemu_sys::$sys(self.0) != 0 } - } - } - } - }; -} - -create_hook_id!(Instruction, libafl_qemu_remove_hook, true); -create_hook_id!(Backdoor, libafl_qemu_remove_backdoor_hook, true); -create_hook_id!(Edge, libafl_qemu_remove_edge_hook, true); -create_hook_id!(Block, libafl_qemu_remove_block_hook, true); -create_hook_id!(Read, libafl_qemu_remove_read_hook, true); -create_hook_id!(Write, libafl_qemu_remove_write_hook, true); -create_hook_id!(Cmp, libafl_qemu_remove_cmp_hook, true); - -#[cfg(emulation_mode = "usermode")] -create_hook_id!(PreSyscall, libafl_qemu_remove_pre_syscall_hook, false); -#[cfg(emulation_mode = "usermode")] -create_hook_id!(PostSyscall, libafl_qemu_remove_post_syscall_hook, false); -#[cfg(emulation_mode = "usermode")] -create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false); - #[derive(Debug)] pub enum QemuInitError { MultipleInstances, @@ -187,17 +146,8 @@ pub struct Qemu { _private: (), } -// syshook_ret -#[repr(C)] -#[cfg_attr(feature = "python", pyclass)] -#[cfg_attr(feature = "python", derive(FromPyObject))] -pub struct SyscallHookResult { - pub retval: GuestAddr, - pub skip_syscall: bool, -} - #[derive(Debug, Clone)] -pub struct EmulatorMemoryChunk { +pub struct QemuMemoryChunk { addr: GuestAddrKind, size: GuestReg, cpu: Option, @@ -363,11 +313,6 @@ pub trait ArchExtras { #[allow(clippy::unused_self)] impl CPU { - #[must_use] - pub fn qemu(&self) -> Qemu { - unsafe { Qemu::get_unchecked() } - } - #[must_use] #[allow(clippy::cast_sign_loss)] pub fn index(&self) -> usize { @@ -421,7 +366,7 @@ impl CPU { let val = GuestReg::to_le(val.into()); let success = - unsafe { libafl_qemu_write_reg(self.ptr, reg_id, addr_of!(val) as *const u8) }; + unsafe { libafl_qemu_write_reg(self.ptr, reg_id, ptr::addr_of!(val) as *const u8) }; if success == 0 { Err(QemuRWError { kind: QemuRWErrorKind::Write, @@ -606,7 +551,7 @@ impl Qemu { .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(null()); + envp.push(ptr::null()); unsafe { #[cfg(emulation_mode = "usermode")] qemu_user_init(argc, argv.as_ptr(), envp.as_ptr()); @@ -621,8 +566,14 @@ impl Qemu { Ok(Qemu { _private: () }) } + #[must_use] + pub fn hooks(&self) -> QemuHooks { + unsafe { QemuHooks::get_unchecked() } + } + /// Get a QEMU object. /// Same as `Qemu::get`, but without checking whether QEMU has been correctly initialized. + /// Since Qemu is a ZST, this operation is free. /// /// # Safety /// @@ -644,7 +595,15 @@ impl Qemu { } } - fn post_run(&self) -> Result { + /// This function will run the emulator until the next breakpoint / sync exit, or until finish. + /// It is a low-level function and simply kicks QEMU. + /// # Safety + /// + /// 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(&self) -> Result { + self.run_inner(); + let exit_reason = unsafe { libafl_get_exit_reason() }; if exit_reason.is_null() { Err(QemuExitError::UnexpectedExit) @@ -809,193 +768,11 @@ impl Qemu { } } - // TODO set T lifetime to be like Emulator - #[allow(clippy::missing_transmute_annotations)] - pub fn set_hook>( - &self, - data: T, - addr: GuestAddr, - callback: extern "C" fn(T, GuestAddr), - invalidate_block: bool, - ) -> InstructionHookId { - unsafe { - let data: u64 = data.into().0; - let callback: extern "C" fn(u64, GuestAddr) = transmute(callback); - let num = libafl_qemu_sys::libafl_qemu_set_hook( - addr.into(), - Some(callback), - data, - i32::from(invalidate_block), - ); - InstructionHookId(num) - } - } - #[must_use] pub fn remove_hook(&self, id: impl HookId, invalidate_block: bool) -> bool { id.remove(invalidate_block) } - #[must_use] - pub fn remove_hooks_at(&self, addr: GuestAddr, invalidate_block: bool) -> usize { - unsafe { - libafl_qemu_sys::libafl_qemu_remove_hooks_at(addr.into(), i32::from(invalidate_block)) - } - } - - #[allow(clippy::missing_transmute_annotations)] - pub fn add_edge_hooks>( - &self, - data: T, - gen: Option u64>, - exec: Option, - ) -> EdgeHookId { - unsafe { - let data: u64 = data.into().0; - let gen: Option u64> = - transmute(gen); - let exec: Option = transmute(exec); - let num = libafl_qemu_sys::libafl_add_edge_hook(gen, exec, data); - EdgeHookId(num) - } - } - - #[allow(clippy::missing_transmute_annotations)] - pub fn add_block_hooks>( - &self, - data: T, - gen: Option u64>, - post_gen: Option, - exec: Option, - ) -> BlockHookId { - unsafe { - let data: u64 = data.into().0; - let gen: Option u64> = transmute(gen); - let post_gen: Option = - transmute(post_gen); - let exec: Option = transmute(exec); - let num = libafl_qemu_sys::libafl_add_block_hook(gen, post_gen, exec, data); - BlockHookId(num) - } - } - - /// `data` can be used to pass data that can be accessed as the first argument in the `gen` and the `exec` functions - /// - /// `gen` gets passed the current programm counter, mutable access to a `TCGTemp` and information about the memory - /// access being performed. - /// The `u64` return value is an id that gets passed to the `exec` functions as their second argument. - /// - /// `exec` hooks get invoked on every read performed by the guest - /// - /// `exec1`-`exec8` special case accesses of width 1-8 - /// - /// If there is no specialized hook for a given read width, the `exec_n` will be - /// called and its last argument will specify the access width - #[allow(clippy::missing_transmute_annotations)] - pub fn add_read_hooks>( - &self, - data: T, - gen: Option u64>, - exec1: Option, - exec2: Option, - exec4: Option, - exec8: Option, - exec_n: Option, - ) -> ReadHookId { - unsafe { - let data: u64 = data.into().0; - let gen: Option< - unsafe extern "C" fn( - u64, - GuestAddr, - *mut TCGTemp, - libafl_qemu_sys::MemOpIdx, - ) -> u64, - > = transmute(gen); - let exec1: Option = transmute(exec1); - let exec2: Option = transmute(exec2); - let exec4: Option = transmute(exec4); - let exec8: Option = transmute(exec8); - let exec_n: Option = - transmute(exec_n); - let num = libafl_qemu_sys::libafl_add_read_hook( - gen, exec1, exec2, exec4, exec8, exec_n, data, - ); - ReadHookId(num) - } - } - - // TODO add MemOp info - #[allow(clippy::missing_transmute_annotations)] - pub fn add_write_hooks>( - &self, - data: T, - gen: Option u64>, - exec1: Option, - exec2: Option, - exec4: Option, - exec8: Option, - exec_n: Option, - ) -> WriteHookId { - unsafe { - let data: u64 = data.into().0; - let gen: Option< - unsafe extern "C" fn( - u64, - GuestAddr, - *mut TCGTemp, - libafl_qemu_sys::MemOpIdx, - ) -> u64, - > = transmute(gen); - let exec1: Option = transmute(exec1); - let exec2: Option = transmute(exec2); - let exec4: Option = transmute(exec4); - let exec8: Option = transmute(exec8); - let exec_n: Option = - transmute(exec_n); - let num = libafl_qemu_sys::libafl_add_write_hook( - gen, exec1, exec2, exec4, exec8, exec_n, data, - ); - WriteHookId(num) - } - } - - #[allow(clippy::missing_transmute_annotations)] - pub fn add_cmp_hooks>( - &self, - data: T, - gen: Option u64>, - exec1: Option, - exec2: Option, - exec4: Option, - exec8: Option, - ) -> CmpHookId { - unsafe { - let data: u64 = data.into().0; - let gen: Option u64> = transmute(gen); - let exec1: Option = transmute(exec1); - let exec2: Option = transmute(exec2); - let exec4: Option = transmute(exec4); - let exec8: Option = transmute(exec8); - let num = libafl_qemu_sys::libafl_add_cmp_hook(gen, exec1, exec2, exec4, exec8, data); - CmpHookId(num) - } - } - - #[allow(clippy::missing_transmute_annotations)] - pub fn add_backdoor_hook>( - &self, - data: T, - callback: extern "C" fn(T, CPUArchStatePtr, GuestAddr), - ) -> BackdoorHookId { - unsafe { - let data: u64 = data.into().0; - let callback: extern "C" fn(u64, CPUArchStatePtr, GuestAddr) = transmute(callback); - let num = libafl_qemu_sys::libafl_add_backdoor_hook(Some(callback), data); - BackdoorHookId(num) - } - } - #[allow(clippy::type_complexity)] pub fn add_gdb_cmd(&self, callback: Box bool>) { unsafe { @@ -1095,7 +872,7 @@ impl PartialOrd for GuestAddrKind { } } -impl EmulatorMemoryChunk { +impl QemuMemoryChunk { #[must_use] pub fn addr(&self) -> GuestAddrKind { self.addr @@ -1125,7 +902,7 @@ impl EmulatorMemoryChunk { } #[must_use] - pub fn get_slice(&self, range: &Range) -> Option { + pub fn get_slice(&self, range: &Range) -> Option { let new_addr = self.addr + range.start; let slice_size = range.clone().count(); @@ -1142,7 +919,7 @@ impl EmulatorMemoryChunk { /// Returns the number of bytes effectively written. #[must_use] - pub fn write(&self, qemu: &Qemu, input: &[u8]) -> GuestReg { + pub fn write(&self, qemu: Qemu, input: &[u8]) -> GuestReg { let max_len: usize = self.size.try_into().unwrap(); let input_sliced = if input.len() > max_len { @@ -1175,42 +952,6 @@ impl EmulatorMemoryChunk { } } -#[cfg(feature = "python")] -#[pymethods] -impl SyscallHookResult { - #[new] - #[must_use] - pub fn new(value: Option) -> Self { - value.map_or( - Self { - retval: 0, - skip_syscall: false, - }, - |v| Self { - retval: v, - skip_syscall: true, - }, - ) - } -} - -#[cfg(not(feature = "python"))] -impl SyscallHookResult { - #[must_use] - pub fn new(value: Option) -> Self { - value.map_or( - Self { - retval: 0, - skip_syscall: false, - }, - |v| Self { - retval: v, - skip_syscall: true, - }, - ) - } -} - #[cfg(feature = "python")] pub mod pybind { use pyo3::{exceptions::PyValueError, prelude::*}; @@ -1298,8 +1039,12 @@ pub mod pybind { unsafe { let idx = PY_GENERIC_HOOKS.len(); PY_GENERIC_HOOKS.push((addr, hook)); - self.qemu - .set_hook(idx as u64, addr, py_generic_hook_wrapper, true); + self.qemu.hooks().add_instruction_hooks( + idx as u64, + addr, + py_generic_hook_wrapper, + true, + ); } } @@ -1307,7 +1052,7 @@ pub mod pybind { unsafe { PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr); } - self.qemu.remove_hooks_at(addr, true) + self.qemu.hooks().remove_instruction_hooks_at(addr, true) } } } diff --git a/libafl_qemu/src/qemu/systemmode.rs b/libafl_qemu/src/qemu/systemmode.rs index 5cdb2f8366..d7d489db95 100644 --- a/libafl_qemu/src/qemu/systemmode.rs +++ b/libafl_qemu/src/qemu/systemmode.rs @@ -15,8 +15,8 @@ use libafl_qemu_sys::{ use num_traits::Zero; use crate::{ - EmulatorMemoryChunk, FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuExitError, - QemuExitReason, QemuSnapshotCheckResult, CPU, + FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuMemoryChunk, QemuSnapshotCheckResult, + CPU, }; pub(super) extern "C" fn qemu_cleanup_atexit() { @@ -200,17 +200,9 @@ impl Qemu { ); } - /// This function will run the emulator until the next breakpoint / sync exit, or until finish. - /// It is a low-level function and simply kicks QEMU. - /// # Safety - /// - /// 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(&self) -> Result { + pub(super) unsafe fn run_inner(&self) { vm_start(); qemu_main_loop(); - - self.post_run() } pub fn save_snapshot(&self, name: &str, sync: bool) { @@ -295,7 +287,7 @@ impl Qemu { } } -impl EmulatorMemoryChunk { +impl QemuMemoryChunk { pub fn phys_iter(&self, qemu: Qemu) -> PhysMemoryIter { PhysMemoryIter { addr: self.addr, diff --git a/libafl_qemu/src/qemu/usermode.rs b/libafl_qemu/src/qemu/usermode.rs index fe5fb1eca7..763b3a9420 100644 --- a/libafl_qemu/src/qemu/usermode.rs +++ b/libafl_qemu/src/qemu/usermode.rs @@ -4,19 +4,16 @@ use std::{ }; use libafl_qemu_sys::{ - exec_path, free_self_maps, guest_base, libafl_dump_core_hook, libafl_force_dfl, libafl_get_brk, - libafl_load_addr, libafl_maps_first, libafl_maps_next, libafl_qemu_run, libafl_set_brk, - mmap_next_start, pageflags_get_root, read_self_maps, strlen, GuestAddr, GuestUsize, - IntervalTreeNode, IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess, + exec_path, free_self_maps, guest_base, libafl_force_dfl, libafl_get_brk, libafl_load_addr, + libafl_maps_first, libafl_maps_next, libafl_qemu_run, libafl_set_brk, mmap_next_start, + pageflags_get_root, read_self_maps, strlen, GuestAddr, GuestUsize, IntervalTreeNode, + IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess, }; use libc::c_int; #[cfg(feature = "python")] use pyo3::{pyclass, pymethods, IntoPy, PyObject, PyRef, PyRefMut, Python}; -use crate::{ - HookData, NewThreadHookId, PostSyscallHookId, PreSyscallHookId, Qemu, QemuExitError, - QemuExitReason, SyscallHookResult, CPU, -}; +use crate::{Qemu, CPU}; #[cfg_attr(feature = "python", pyclass(unsendable))] pub struct GuestMaps { @@ -135,15 +132,8 @@ impl Qemu { } } - /// This function will run the emulator until the next breakpoint, or until finish. - /// # Safety - /// - /// 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(&self) -> Result { + pub(super) unsafe fn run_inner(self) { libafl_qemu_run(); - - self.post_run() } #[must_use] @@ -237,100 +227,6 @@ impl Qemu { Err(format!("Failed to unmap {addr}")) } } - - #[allow(clippy::type_complexity)] - pub fn add_pre_syscall_hook>( - &self, - data: T, - callback: extern "C" fn( - T, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> SyscallHookResult, - ) -> PreSyscallHookId { - unsafe { - let data: u64 = data.into().0; - let callback: extern "C" fn( - u64, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> libafl_qemu_sys::syshook_ret = core::mem::transmute(callback); - let num = libafl_qemu_sys::libafl_add_pre_syscall_hook(Some(callback), data); - PreSyscallHookId(num) - } - } - - #[allow(clippy::type_complexity)] - pub fn add_post_syscall_hook>( - &self, - data: T, - callback: extern "C" fn( - T, - GuestAddr, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> GuestAddr, - ) -> PostSyscallHookId { - unsafe { - let data: u64 = data.into().0; - let callback: extern "C" fn( - u64, - GuestAddr, - i32, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - GuestAddr, - ) -> GuestAddr = core::mem::transmute(callback); - let num = libafl_qemu_sys::libafl_add_post_syscall_hook(Some(callback), data); - PostSyscallHookId(num) - } - } - - pub fn add_new_thread_hook>( - &self, - data: T, - callback: extern "C" fn(T, tid: u32) -> bool, - ) -> NewThreadHookId { - unsafe { - let data: u64 = data.into().0; - let callback: extern "C" fn(u64, u32) -> bool = core::mem::transmute(callback); - let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data); - NewThreadHookId(num) - } - } - - #[allow(clippy::type_complexity)] - pub fn set_crash_hook(&self, callback: extern "C" fn(i32)) { - unsafe { - libafl_dump_core_hook = callback; - } - } } #[cfg(feature = "python")] @@ -340,7 +236,7 @@ pub mod pybind { exceptions::PyValueError, pymethods, types::PyInt, FromPyObject, PyObject, PyResult, Python, }; - use crate::{pybind::Qemu, SyscallHookResult}; + use crate::{pybind::Qemu, qemu::hooks::SyscallHookResult}; static mut PY_SYSCALL_HOOK: Option = None; @@ -438,6 +334,7 @@ pub mod pybind { PY_SYSCALL_HOOK = Some(hook); } self.qemu + .hooks() .add_pre_syscall_hook(0u64, py_syscall_hook_wrapper); } } diff --git a/libafl_qemu/src/sync_exit.rs b/libafl_qemu/src/sync_exit.rs index 3d18fedd0b..5bdb9df88b 100644 --- a/libafl_qemu/src/sync_exit.rs +++ b/libafl_qemu/src/sync_exit.rs @@ -8,7 +8,9 @@ use libafl::state::{HasExecutions, State}; use crate::{ command::{CommandManager, IsCommand}, - get_exit_arch_regs, EmulatorExitHandler, GuestReg, QemuHelperTuple, Regs, CPU, + get_exit_arch_regs, + modules::EmulatorModuleTuple, + EmulatorExitHandler, GuestReg, Regs, CPU, }; #[derive(Debug, Clone, Enum)] @@ -24,30 +26,30 @@ pub enum ExitArgs { } #[derive(Debug)] -pub struct SyncExit +pub struct SyncExit where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { - command: Rc>, + command: Rc>, } -impl SyncExit +impl SyncExit where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { #[must_use] - pub fn new(command: Rc>) -> Self { + pub fn new(command: Rc>) -> Self { Self { command } } #[must_use] - pub fn command(&self) -> Rc> { + pub fn command(&self) -> Rc> { self.command.clone() } @@ -62,12 +64,12 @@ where } } -impl Display for SyncExit +impl Display for SyncExit where - CM: CommandManager, - E: EmulatorExitHandler, - QT: QemuHelperTuple, - S: State + HasExecutions, + CM: CommandManager, + EH: EmulatorExitHandler, + ET: EmulatorModuleTuple, + S: Unpin + State + HasExecutions, { 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 aa1ca8839c..4fd22bac33 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -35,10 +35,14 @@ use libafl_bolts::{ tuples::{tuple_list, Handled, Merge}, AsSlice, }; -pub use libafl_qemu::qemu::Qemu; #[cfg(not(any(feature = "mips", feature = "hexagon")))] -use libafl_qemu::QemuCmpLogHelper; -use libafl_qemu::{edges, QemuEdgeCoverageHelper, QemuExecutor, QemuHooks}; +use libafl_qemu::modules::CmpLogModule; +pub use libafl_qemu::qemu::Qemu; +use libafl_qemu::{ + command::NopCommandManager, + modules::edges::{self, EdgeCoverageModule}, + Emulator, NopEmulatorExitHandler, QemuExecutor, +}; use libafl_targets::{edges_map_mut_ptr, CmpLogObserver}; use typed_builder::TypedBuilder; @@ -119,7 +123,7 @@ where { /// Run the fuzzer #[allow(clippy::too_many_lines, clippy::similar_names)] - pub fn run(&mut self, qemu: &Qemu) { + pub fn run(&mut self, qemu: Qemu) { let conf = match self.configuration.as_ref() { Some(name) => EventConfig::from_name(name), None => EventConfig::AlwaysUnique, @@ -222,19 +226,26 @@ where }; if self.use_cmplog.unwrap_or(false) { - let mut hooks = QemuHooks::new( - *qemu, + let modules = { #[cfg(not(any(feature = "mips", feature = "hexagon")))] - tuple_list!( - QemuEdgeCoverageHelper::default(), - QemuCmpLogHelper::default(), - ), + { + tuple_list!(EdgeCoverageModule::default(), CmpLogModule::default(),) + } #[cfg(any(feature = "mips", feature = "hexagon"))] - tuple_list!(QemuEdgeCoverageHelper::default()), - ); + { + tuple_list!(EdgeCoverageModule::default()) + } + }; + + let mut emulator = Emulator::new_with_qemu( + qemu, + modules, + NopEmulatorExitHandler, + NopCommandManager, + )?; let executor = QemuExecutor::new( - &mut hooks, + &mut emulator, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, @@ -334,11 +345,17 @@ where } } } else { - let mut hooks = - QemuHooks::new(*qemu, tuple_list!(QemuEdgeCoverageHelper::default())); + let tools = tuple_list!(EdgeCoverageModule::default()); + + let mut emulator = Emulator::new_with_qemu( + qemu, + tools, + NopEmulatorExitHandler, + NopCommandManager, + )?; let mut executor = QemuExecutor::new( - &mut hooks, + &mut emulator, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, @@ -520,7 +537,7 @@ pub mod pybind { .tokens_file(self.tokens_file.clone()) .iterations(self.iterations) .build() - .run(&qemu.qemu); + .run(qemu.qemu); } } diff --git a/utils/libafl_benches/benches/hash_speeds.rs b/utils/libafl_benches/benches/hash_speeds.rs index 1ae50dbd8d..b7b6019908 100644 --- a/utils/libafl_benches/benches/hash_speeds.rs +++ b/utils/libafl_benches/benches/hash_speeds.rs @@ -22,8 +22,7 @@ fn criterion_benchmark(c: &mut Criterion) { });*/ c.bench_function("ahash", |b| { b.iter(|| { - let mut hasher = - black_box(ahash::RandomState::with_seeds(123, 456, 789, 123).build_hasher()); + let mut hasher = ahash::RandomState::with_seeds(123, 456, 789, 123).build_hasher(); hasher.write(black_box(&bench_vec)); black_box(hasher.finish()); });