diff --git a/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs b/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs index 838f8d8df5..c6ec8acfd6 100644 --- a/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs +++ b/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs @@ -176,7 +176,7 @@ fn fuzz( ); let emulator = Emulator::empty() - .qemu_cli(args) + .qemu_config(|_| args) .modules(emulator_modules) .build()?; diff --git a/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs index a26a588bb4..c1ce8a7657 100644 --- a/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs @@ -55,7 +55,6 @@ use libafl_qemu::{ GuestReg, //snapshot::QemuSnapshotHelper, MmapPerms, - Qemu, QemuExecutor, QemuExitError, QemuExitReason, @@ -175,7 +174,33 @@ fn fuzz( env::remove_var("LD_LIBRARY_PATH"); let args: Vec = env::args().collect(); - let qemu = Qemu::init(&args).expect("QEMU init failed"); + + // Create an observation channel using the coverage map + let mut edges_observer = unsafe { + HitcountsMapObserver::new(VariableMapObserver::from_mut_slice( + "edges", + OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_ALLOCATED_SIZE), + &raw mut MAX_EDGES_FOUND, + )) + .track_indices() + }; + + let modules = tuple_list!( + StdEdgeCoverageModule::builder() + .map_observer(edges_observer.as_mut()) + .build() + .unwrap(), + CmpLogModule::default(), + // QemuAsanHelper::default(asan), + //QemuSnapshotHelper::new() + ); + + let emulator = Emulator::empty() + .qemu_args(|_| args) + .modules(modules) + .build()?; + let qemu = emulator.qemu(); + // let qemu = Qemu::init(&args).expect("QEMU init failed"); // let (emu, asan) = init_with_asan(&mut args, &mut env).unwrap(); let mut elf_buffer = Vec::new(); @@ -255,16 +280,6 @@ fn fuzz( }, }; - // Create an observation channel using the coverage map - let mut edges_observer = unsafe { - HitcountsMapObserver::new(VariableMapObserver::from_mut_slice( - "edges", - OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_ALLOCATED_SIZE), - &raw mut MAX_EDGES_FOUND, - )) - .track_indices() - }; - // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); @@ -364,18 +379,6 @@ fn fuzz( ExitKind::Ok }; - let modules = tuple_list!( - StdEdgeCoverageModule::builder() - .map_observer(edges_observer.as_mut()) - .build() - .unwrap(), - CmpLogModule::default(), - // QemuAsanHelper::default(asan), - //QemuSnapshotHelper::new() - ); - - let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?; - // Create the executor for an in-process function with one observer for edge coverage and one for the execution time let executor = QemuExecutor::new( emulator, diff --git a/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs b/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs index ba00041efd..5c797249a7 100644 --- a/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs @@ -28,8 +28,8 @@ use libafl_bolts::{ }; use libafl_qemu::{ elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, CallingConvention, - Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitError, QemuExitReason, - QemuForkExecutor, QemuShutdownCause, Regs, + Emulator, GuestAddr, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuForkExecutor, + QemuShutdownCause, Regs, }; use libafl_targets::{EDGES_MAP_DEFAULT_SIZE, EDGES_MAP_PTR}; @@ -113,7 +113,31 @@ pub fn fuzz() -> Result<(), Error> { log::debug!("ARGS: {:#?}", options.args); env::remove_var("LD_LIBRARY_PATH"); - let qemu = Qemu::init(&options.args).unwrap(); + + let mut shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); + + let mut edges_shmem = shmem_provider.new_shmem(EDGES_MAP_DEFAULT_SIZE).unwrap(); + let edges = edges_shmem.as_slice_mut(); + unsafe { EDGES_MAP_PTR = edges.as_mut_ptr() }; + + let mut edges_observer = unsafe { + HitcountsMapObserver::new(ConstMapObserver::from_mut_ptr( + "edges", + NonNull::new(edges.as_mut_ptr()) + .expect("The edge map pointer is null.") + .cast::<[u8; EDGES_MAP_DEFAULT_SIZE]>(), + )) + }; + + let modules = tuple_list!(StdEdgeCoverageChildModule::builder() + .const_map_observer(edges_observer.as_mut()) + .build()?); + + let emulator = Emulator::empty() + .qemu_config(|_| options.args) + .modules(modules) + .build()?; + let qemu = emulator.qemu(); let mut elf_buffer = Vec::new(); let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap(); @@ -139,8 +163,6 @@ pub fn fuzz() -> Result<(), Error> { let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap(); - let mut shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); - let monitor = SimpleMonitor::with_user_monitor(|s| { println!("{s}"); }); @@ -157,19 +179,6 @@ pub fn fuzz() -> Result<(), Error> { }, }; - let mut edges_shmem = shmem_provider.new_shmem(EDGES_MAP_DEFAULT_SIZE).unwrap(); - let edges = edges_shmem.as_slice_mut(); - unsafe { EDGES_MAP_PTR = edges.as_mut_ptr() }; - - let mut edges_observer = unsafe { - HitcountsMapObserver::new(ConstMapObserver::from_mut_ptr( - "edges", - NonNull::new(edges.as_mut_ptr()) - .expect("The edge map pointer is null.") - .cast::<[u8; EDGES_MAP_DEFAULT_SIZE]>(), - )) - }; - let mut feedback = MaxMapFeedback::new(&edges_observer); #[allow(clippy::let_unit_value)] @@ -223,12 +232,6 @@ pub fn fuzz() -> Result<(), Error> { ExitKind::Ok }; - let modules = tuple_list!(StdEdgeCoverageChildModule::builder() - .const_map_observer(edges_observer.as_mut()) - .build()?); - - let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?; - let mut executor = QemuForkExecutor::new( emulator, &mut harness, diff --git a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs index 45f80b0f47..8cd99d6c41 100644 --- a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs @@ -31,7 +31,7 @@ use libafl_bolts::{ use libafl_qemu::{ elf::EasyElf, modules::{drcov::DrCovModule, StdAddressFilter}, - ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor, + ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, QemuExecutor, QemuExitReason, QemuRWError, QemuShutdownCause, Regs, }; @@ -123,80 +123,93 @@ pub fn fuzz() { env::remove_var("LD_LIBRARY_PATH"); - let qemu = Qemu::init(&options.args).unwrap(); - - let mut elf_buffer = Vec::new(); - let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap(); - - let test_one_input_ptr = elf - .resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr()) - .expect("Symbol LLVMFuzzerTestOneInput not found"); - log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}"); + let mut run_client = |state: Option<_>, + mut mgr: LlmpRestartingEventManager<_, _, _>, + client_description: ClientDescription| { + let mut cov_path = options.coverage_path.clone(); - qemu.entry_break(test_one_input_ptr); + let emulator_modules = tuple_list!(DrCovModule::builder() + .filter(StdAddressFilter::default()) + .filename(cov_path.clone()) + .full_trace(false) + .build()); - for m in qemu.mappings() { - log::debug!( - "Mapping: 0x{:016x}-0x{:016x}, {}", - m.start(), - m.end(), - m.path().unwrap_or(&"".to_string()) - ); - } + let emulator = Emulator::empty() + .qemu_config(|_| options.args.clone()) + .modules(emulator_modules) + .build() + .expect("QEMU initialization failed"); + let qemu = emulator.qemu(); + + let mut elf_buffer = Vec::new(); + let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap(); + + let test_one_input_ptr = elf + .resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr()) + .expect("Symbol LLVMFuzzerTestOneInput not found"); + log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}"); + + qemu.entry_break(test_one_input_ptr); + + for m in qemu.mappings() { + log::debug!( + "Mapping: 0x{:016x}-0x{:016x}, {}", + m.start(), + m.end(), + m.path().unwrap_or(&"".to_string()) + ); + } - let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap(); - log::debug!("Break at {pc:#x}"); + let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap(); + log::debug!("Break at {pc:#x}"); - let ret_addr: GuestAddr = qemu.read_return_address().unwrap(); - log::debug!("Return address = {ret_addr:#x}"); + let ret_addr: GuestAddr = qemu.read_return_address().unwrap(); + log::debug!("Return address = {ret_addr:#x}"); - qemu.set_breakpoint(ret_addr); + qemu.set_breakpoint(ret_addr); - let input_addr = qemu - .map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite) - .unwrap(); - log::debug!("Placing input at {input_addr:#x}"); + let input_addr = qemu + .map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite) + .unwrap(); + log::debug!("Placing input at {input_addr:#x}"); - let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap(); + let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap(); - let reset = |buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> { - unsafe { - let _ = qemu.write_mem(input_addr, buf); - qemu.write_reg(Regs::Pc, test_one_input_ptr)?; - qemu.write_reg(Regs::Sp, stack_ptr)?; - qemu.write_return_address(ret_addr)?; - qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?; - qemu.write_function_argument(CallingConvention::Cdecl, 1, len)?; + let reset = |buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> { + unsafe { + let _ = qemu.write_mem(input_addr, buf); + qemu.write_reg(Regs::Pc, test_one_input_ptr)?; + qemu.write_reg(Regs::Sp, stack_ptr)?; + qemu.write_return_address(ret_addr)?; + qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?; + qemu.write_function_argument(CallingConvention::Cdecl, 1, len)?; - match qemu.run() { - Ok(QemuExitReason::Breakpoint(_)) => {} - Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => { - process::exit(0) + match qemu.run() { + Ok(QemuExitReason::Breakpoint(_)) => {} + Ok(QemuExitReason::End(QemuShutdownCause::HostSignal( + Signal::SigInterrupt, + ))) => process::exit(0), + _ => panic!("Unexpected QEMU exit."), } - _ => panic!("Unexpected QEMU exit."), - } - - Ok(()) - } - }; - let mut harness = - |_emulator: &mut Emulator<_, _, _, _, _>, _state: &mut _, input: &BytesInput| { - let target = input.target_bytes(); - let mut buf = target.as_slice(); - let mut len = buf.len(); - if len > MAX_INPUT_SIZE { - buf = &buf[0..MAX_INPUT_SIZE]; - len = MAX_INPUT_SIZE; + Ok(()) } - let len = len as GuestReg; - reset(buf, len).unwrap(); - ExitKind::Ok }; - let mut run_client = |state: Option<_>, - mut mgr: LlmpRestartingEventManager<_, _, _>, - client_description: ClientDescription| { + let mut harness = + |_emulator: &mut Emulator<_, _, _, _, _>, _state: &mut _, input: &BytesInput| { + let target = input.target_bytes(); + let mut buf = target.as_slice(); + let mut len = buf.len(); + if len > MAX_INPUT_SIZE { + buf = &buf[0..MAX_INPUT_SIZE]; + len = MAX_INPUT_SIZE; + } + let len = len as GuestReg; + reset(buf, len).unwrap(); + ExitKind::Ok + }; + let core_id = client_description.core_id(); let core_idx = options .cores @@ -234,23 +247,11 @@ pub fn fuzz() { let scheduler = QueueScheduler::new(); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - 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; cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}")); - let emulator_modules = tuple_list!(DrCovModule::builder() - .filter(StdAddressFilter::default()) - .filename(cov_path) - .full_trace(false) - .build()); - - let emulator = Emulator::empty() - .qemu(qemu) - .modules(emulator_modules) - .build()?; - let mut executor = QemuExecutor::new( emulator, &mut harness, diff --git a/fuzzers/binary_only/qemu_launcher/src/client.rs b/fuzzers/binary_only/qemu_launcher/src/client.rs index cf399682c2..93f112eb72 100644 --- a/fuzzers/binary_only/qemu_launcher/src/client.rs +++ b/fuzzers/binary_only/qemu_launcher/src/client.rs @@ -11,14 +11,8 @@ use libafl::{ use libafl_bolts::{rands::StdRand, tuples::tuple_list}; #[cfg(feature = "injections")] use libafl_qemu::modules::injections::InjectionModule; -use libafl_qemu::{ - modules::{ - asan::{init_qemu_with_asan, AsanModule}, - asan_guest::{init_qemu_with_asan_guest, AsanGuestModule}, - cmplog::CmpLogModule, - DrCovModule, - }, - Qemu, +use libafl_qemu::modules::{ + asan::AsanModule, asan_guest::AsanGuestModule, cmplog::CmpLogModule, DrCovModule, }; use crate::{ @@ -80,18 +74,6 @@ impl Client<'_> { Err(Error::empty_optional("Multiple ASAN modes configured"))?; } - let (qemu, mut asan, mut asan_lib) = { - if is_asan { - let (emu, asan) = init_qemu_with_asan(&mut args, &mut env)?; - (emu, Some(asan), None) - } else if is_asan_guest { - let (emu, asan_lib) = init_qemu_with_asan_guest(&mut args, &mut env)?; - (emu, None, Some(asan_lib)) - } else { - (Qemu::init(&args)?, None, None) - } - }; - #[cfg(not(feature = "injections"))] let injection_module = None; @@ -111,8 +93,6 @@ impl Client<'_> { } }); - let harness = Harness::init(qemu).expect("Error setting up harness."); - let is_cmplog = self.options.is_cmplog_core(core_id); let extra_tokens = injection_module @@ -122,8 +102,6 @@ impl Client<'_> { let instance_builder = Instance::builder() .options(self.options) - .qemu(qemu) - .harness(harness) .mgr(mgr) .client_description(client_description) .extra_tokens(extra_tokens); @@ -136,77 +114,79 @@ impl Client<'_> { .filename(drcov.clone()) .full_trace(true) .build(); - instance_builder.build().run(tuple_list!(drcov), state) + instance_builder + .build() + .run(args, tuple_list!(drcov), state) } else if is_asan && is_cmplog { if let Some(injection_module) = injection_module { instance_builder.build().run( + args, tuple_list!( CmpLogModule::default(), - AsanModule::default(asan.take().unwrap()), + AsanModule::default(&env), injection_module, ), state, ) } else { instance_builder.build().run( - tuple_list!( - CmpLogModule::default(), - AsanModule::default(asan.take().unwrap()), - ), + args, + tuple_list!(CmpLogModule::default(), AsanModule::default(&env),), state, ) } } else if is_asan_guest && is_cmplog { if let Some(injection_module) = injection_module { instance_builder.build().run( + args, tuple_list!( CmpLogModule::default(), - AsanGuestModule::default(qemu, &asan_lib.take().unwrap()), + AsanGuestModule::default(&env), injection_module ), state, ) } else { instance_builder.build().run( - tuple_list!( - CmpLogModule::default(), - AsanGuestModule::default(qemu, &asan_lib.take().unwrap()), - ), + args, + tuple_list!(CmpLogModule::default(), AsanGuestModule::default(&env),), state, ) } } else if is_asan { if let Some(injection_module) = injection_module { instance_builder.build().run( - tuple_list!(AsanModule::default(asan.take().unwrap()), injection_module), + args, + tuple_list!(AsanModule::default(&env), injection_module), state, ) } else { - instance_builder.build().run( - tuple_list!(AsanModule::default(asan.take().unwrap()),), - state, - ) + instance_builder + .build() + .run(args, tuple_list!(AsanModule::default(&env),), state) } } else if is_asan_guest { - let modules = tuple_list!(AsanGuestModule::default(qemu, &asan_lib.take().unwrap())); - instance_builder.build().run(modules, state) + instance_builder + .build() + .run(args, tuple_list!(AsanGuestModule::default(&env)), state) } else if is_cmplog { if let Some(injection_module) = injection_module { instance_builder.build().run( + args, tuple_list!(CmpLogModule::default(), injection_module), state, ) } else { instance_builder .build() - .run(tuple_list!(CmpLogModule::default()), state) + .run(args, tuple_list!(CmpLogModule::default()), state) } } else if let Some(injection_module) = injection_module { instance_builder .build() - .run(tuple_list!(injection_module), state) + .run(args, tuple_list!(injection_module), state) } else { - instance_builder.build().run(tuple_list!(), state) + instance_builder.build().run(args, tuple_list!(), state) } } } diff --git a/fuzzers/binary_only/qemu_launcher/src/instance.rs b/fuzzers/binary_only/qemu_launcher/src/instance.rs index 268aa75f88..d55cabafb2 100644 --- a/fuzzers/binary_only/qemu_launcher/src/instance.rs +++ b/fuzzers/binary_only/qemu_launcher/src/instance.rs @@ -34,12 +34,13 @@ use libafl_bolts::shmem::StdShMemProvider; use libafl_bolts::{ ownedref::OwnedMutSlice, rands::StdRand, - tuples::{tuple_list, Merge, Prepend}, + tuples::{tuple_list, MatchFirstType, Merge, Prepend}, }; use libafl_qemu::{ elf::EasyElf, modules::{ - cmplog::CmpLogObserver, EmulatorModuleTuple, StdAddressFilter, StdEdgeCoverageModule, + cmplog::CmpLogObserver, edges::EdgeCoverageFullVariant, EdgeCoverageModule, EmulatorModule, + EmulatorModuleTuple, NopPageFilter, StdAddressFilter, StdEdgeCoverageModule, }, Emulator, GuestAddr, Qemu, QemuExecutor, }; @@ -61,9 +62,6 @@ pub type ClientMgr = pub struct Instance<'a, M: Monitor> { options: &'a FuzzerOptions, /// The harness. We create it before forking, then `take()` it inside the client. - #[builder(setter(strip_option))] - harness: Option, - qemu: Qemu, mgr: ClientMgr, client_description: ClientDescription, #[builder(default)] @@ -107,7 +105,12 @@ impl Instance<'_, M> { } #[allow(clippy::too_many_lines)] - pub fn run(&mut self, modules: ET, state: Option) -> Result<(), Error> + pub fn run( + &mut self, + args: Vec, + modules: ET, + state: Option, + ) -> Result<(), Error> where ET: EmulatorModuleTuple + Debug, { @@ -123,10 +126,21 @@ impl Instance<'_, M> { let edge_coverage_module = StdEdgeCoverageModule::builder() .map_observer(edges_observer.as_mut()) - .address_filter(self.coverage_filter(self.qemu)?) .build()?; let modules = modules.prepend(edge_coverage_module); + let mut emulator = Emulator::empty() + .modules(modules) + .qemu_config(|_| args) + .build()?; + let harness = Harness::init(emulator.qemu()).expect("Error setting up harness."); + let qemu = emulator.qemu(); + + // update address filter after qemu has been initialized + as EmulatorModule>::update_address_filter(emulator.modules_mut() + .modules_mut() + .match_first_type_mut::>() + .expect("Could not find back the edge module"), qemu, self.coverage_filter(qemu)?); // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); @@ -196,10 +210,6 @@ impl Instance<'_, M> { state.add_metadata(tokens); - let harness = self - .harness - .take() - .expect("The harness can never be None here!"); harness.post_fork(); let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, @@ -209,8 +219,6 @@ impl Instance<'_, M> { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let emulator = Emulator::empty().qemu(self.qemu).modules(modules).build()?; - if let Some(rerun_input) = &self.options.rerun_input { // TODO: We might want to support non-bytes inputs at some point? let bytes = fs::read(rerun_input) diff --git a/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs b/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs index d6a91bd267..f6e5b09687 100644 --- a/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs +++ b/fuzzers/full_system/qemu_baremetal/src/fuzzer_breakpoint.rs @@ -107,14 +107,13 @@ pub fn fuzz() { // Initialize QEMU Emulator let emu = Emulator::builder() - .qemu_cli(args) - .add_module( + .qemu_config(|_| args) + .prepend_module( StdEdgeCoverageModule::builder() .map_observer(edges_observer.as_mut()) .build()?, ) - .build() - .unwrap(); + .build()?; // Set breakpoints of interest with corresponding commands. emu.add_breakpoint( diff --git a/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs b/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs index aefd60af13..5dcf810380 100644 --- a/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs +++ b/fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs @@ -30,8 +30,7 @@ use libafl_bolts::{ }; use libafl_qemu::{ config, elf::EasyElf, executor::QemuExecutor, modules::edges::StdEdgeCoverageModuleBuilder, - Emulator, GuestPhysAddr, Qemu, QemuExitError, QemuExitReason, QemuRWError, QemuShutdownCause, - Regs, + Emulator, GuestPhysAddr, QemuExitError, QemuExitReason, QemuRWError, QemuShutdownCause, Regs, }; use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND}; @@ -93,32 +92,32 @@ pub fn fuzz() { .track_indices() }; - // Initialize QEMU - let qemu = Qemu::builder() - .machine("mps2-an385") - .monitor(config::Monitor::Null) - .kernel(format!("{target_dir}/example.elf")) - .serial(config::Serial::Null) - .no_graphic(true) - .snapshot(true) - .drives([config::Drive::builder() - .interface(config::DriveInterface::None) - .format(config::DiskImageFileFormat::Qcow2) - .file(format!("{target_dir}/dummy.qcow2")) - .build()]) - .start_cpu(false) - .build() - .expect("Failed to initialized QEMU"); - let emulator_modules = tuple_list!(StdEdgeCoverageModuleBuilder::default() .map_observer(edges_observer.as_mut()) .build()?); let emulator = Emulator::empty() - .qemu(qemu) + .qemu_config(|qemu_config| { + qemu_config + .machine("mps2-an385") + .monitor(config::Monitor::Null) + .kernel(format!("{target_dir}/example.elf")) + .serial(config::Serial::Null) + .no_graphic(true) + .snapshot(true) + .drives([config::Drive::builder() + .interface(config::DriveInterface::None) + .format(config::DiskImageFileFormat::Qcow2) + .file(format!("{target_dir}/dummy.qcow2")) + .build() + .expect("Could not build drives")]) + .start_cpu(false) + }) .modules(emulator_modules) .build()?; + let qemu = emulator.qemu(); + qemu.set_breakpoint(main_addr); unsafe { diff --git a/fuzzers/full_system/qemu_baremetal/src/fuzzer_sync_exit.rs b/fuzzers/full_system/qemu_baremetal/src/fuzzer_sync_exit.rs index fe6bc314f4..411de715da 100644 --- a/fuzzers/full_system/qemu_baremetal/src/fuzzer_sync_exit.rs +++ b/fuzzers/full_system/qemu_baremetal/src/fuzzer_sync_exit.rs @@ -63,7 +63,7 @@ pub fn fuzz() { .build()?); let emu = Emulator::builder() - .qemu_cli(args) + .qemu_config(|_| args) .modules(modules) .build()?; diff --git a/fuzzers/full_system/qemu_linux_kernel/src/fuzzer.rs b/fuzzers/full_system/qemu_linux_kernel/src/fuzzer.rs index bb33be5f5f..633714ec94 100644 --- a/fuzzers/full_system/qemu_linux_kernel/src/fuzzer.rs +++ b/fuzzers/full_system/qemu_linux_kernel/src/fuzzer.rs @@ -67,14 +67,9 @@ pub fn fuzz() { CmpLogModule::default(), ); - // let driver = StdEmulatorDriver::builder() - // .print_commands(true) - // .build(); - let emu = Emulator::builder() - .qemu_cli(args) + .qemu_config(|_| args) .modules(modules) - // .driver(driver) .build()?; let devices = emu.list_devices(); diff --git a/fuzzers/full_system/qemu_linux_process/src/fuzzer.rs b/fuzzers/full_system/qemu_linux_process/src/fuzzer.rs index ec8cf7beed..b64eca7ecf 100644 --- a/fuzzers/full_system/qemu_linux_process/src/fuzzer.rs +++ b/fuzzers/full_system/qemu_linux_process/src/fuzzer.rs @@ -70,7 +70,7 @@ pub fn fuzz() { ); let emu = Emulator::builder() - .qemu_cli(args) + .qemu_config(|_| args) .modules(modules) .build()?; diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index d107dc4d0b..773a06de7f 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "libafl_qemu" version.workspace = true -authors = ["Andrea Fioraldi "] +authors = [ + "Andrea Fioraldi ", + "Romain Malmain ", +] description = "QEMU user backend library for LibAFL" documentation = "https://docs.rs/libafl_qemu" repository = "https://github.com/AFLplusplus/LibAFL/" @@ -125,6 +128,7 @@ bytes-utils = "0.1.4" typed-builder = { workspace = true } memmap2 = "0.9.5" getset = "0.1.3" +derive_builder = "0.20.2" # Document all features of this crate (for `cargo doc`) document-features = { workspace = true, optional = true } diff --git a/libafl_qemu/libafl_qemu_build/Cargo.toml b/libafl_qemu/libafl_qemu_build/Cargo.toml index 7dbca22934..8b6c31b074 100644 --- a/libafl_qemu/libafl_qemu_build/Cargo.toml +++ b/libafl_qemu/libafl_qemu_build/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "libafl_qemu_build" version.workspace = true -authors = ["Andrea Fioraldi "] +authors = [ + "Andrea Fioraldi ", + "Romain Malmain ", +] description = "Builder for LibAFL QEMU" documentation = "https://docs.rs/libafl_qemu_build" repository = "https://github.com/AFLplusplus/LibAFL/" diff --git a/libafl_qemu/libafl_qemu_sys/Cargo.toml b/libafl_qemu/libafl_qemu_sys/Cargo.toml index 0ad98885bd..b5730812d8 100644 --- a/libafl_qemu/libafl_qemu_sys/Cargo.toml +++ b/libafl_qemu/libafl_qemu_sys/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "libafl_qemu_sys" version.workspace = true -authors = ["Andrea Fioraldi "] +authors = [ + "Andrea Fioraldi ", + "Romain Malmain ", +] description = "C to Rust bindings for the LibAFL QEMU bridge" documentation = "https://docs.rs/libafl_qemu_sys" repository = "https://github.com/AFLplusplus/LibAFL/" diff --git a/libafl_qemu/src/command/mod.rs b/libafl_qemu/src/command/mod.rs index ae87cfcc3f..87402a7794 100644 --- a/libafl_qemu/src/command/mod.rs +++ b/libafl_qemu/src/command/mod.rs @@ -452,8 +452,8 @@ where // Unleash hooks if locked if emu.driver_mut().unlock_hooks() { // Prepare hooks - emu.modules_mut().first_exec_all(state); - emu.modules_mut().pre_exec_all(state, input); + emu.modules_mut().first_exec_all(qemu, state); + emu.modules_mut().pre_exec_all(qemu, state, input); } // Auto page filtering if option is enabled diff --git a/libafl_qemu/src/emu/builder.rs b/libafl_qemu/src/emu/builder.rs index 67396d2c10..65e3d51b13 100644 --- a/libafl_qemu/src/emu/builder.rs +++ b/libafl_qemu/src/emu/builder.rs @@ -1,30 +1,26 @@ -use std::{fmt::Debug, marker::PhantomData}; +use std::marker::PhantomData; use libafl::{ inputs::{HasTargetBytes, UsesInput}, state::{HasExecutions, State}, }; -use libafl_bolts::tuples::{tuple_list, Prepend}; +use libafl_bolts::tuples::{tuple_list, Append, Prepend}; #[cfg(feature = "systemmode")] use crate::FastSnapshotManager; use crate::{ command::{CommandManager, NopCommandManager, StdCommandManager}, - config::QemuConfig, + config::QemuConfigBuilder, modules::{EmulatorModule, EmulatorModuleTuple}, - Emulator, EmulatorHooks, NopEmulatorDriver, NopSnapshotManager, Qemu, QemuHooks, QemuInitError, - StdEmulatorDriver, StdSnapshotManager, + Emulator, NopEmulatorDriver, NopSnapshotManager, QemuInitError, QemuParams, StdEmulatorDriver, + StdSnapshotManager, }; -#[derive(Clone, Debug)] -enum QemuBuilder { - Qemu(Qemu), - QemuConfig(QemuConfig), - QemuString(Vec), -} - -#[derive(Clone, Debug)] -pub struct EmulatorBuilder +/// `Emulator` builder. +/// +/// The default configuration of QEMU is always empty. +#[derive(Clone)] +pub struct EmulatorBuilder where S: UsesInput, { @@ -32,11 +28,19 @@ where driver: ED, snapshot_manager: SM, command_manager: CM, - qemu_builder: Option, + qemu_config: QB, phantom: PhantomData, } -impl EmulatorBuilder +impl + EmulatorBuilder< + NopCommandManager, + NopEmulatorDriver, + (), + QemuConfigBuilder, + S, + NopSnapshotManager, + > where S: UsesInput, { @@ -47,14 +51,22 @@ where driver: NopEmulatorDriver, snapshot_manager: NopSnapshotManager, command_manager: NopCommandManager, - qemu_builder: None, + qemu_config: QemuConfigBuilder::default(), phantom: PhantomData, } } } #[cfg(feature = "usermode")] -impl EmulatorBuilder, StdEmulatorDriver, (), S, StdSnapshotManager> +impl + EmulatorBuilder< + StdCommandManager, + StdEmulatorDriver, + (), + QemuConfigBuilder, + S, + StdSnapshotManager, + > where S: State + HasExecutions + Unpin, S::Input: HasTargetBytes, @@ -67,14 +79,22 @@ where command_manager: StdCommandManager::default(), snapshot_manager: StdSnapshotManager::default(), driver: StdEmulatorDriver::builder().build(), - qemu_builder: None, + qemu_config: QemuConfigBuilder::default(), phantom: PhantomData, } } } #[cfg(feature = "systemmode")] -impl EmulatorBuilder, StdEmulatorDriver, (), S, StdSnapshotManager> +impl + EmulatorBuilder< + StdCommandManager, + StdEmulatorDriver, + (), + QemuConfigBuilder, + S, + StdSnapshotManager, + > where S: State + HasExecutions + Unpin, S::Input: HasTargetBytes, @@ -85,12 +105,12 @@ where command_manager: StdCommandManager::default(), snapshot_manager: FastSnapshotManager::default(), driver: StdEmulatorDriver::builder().build(), - qemu_builder: None, + qemu_config: QemuConfigBuilder::default(), phantom: PhantomData, } } } -impl EmulatorBuilder +impl EmulatorBuilder where S: UsesInput + Unpin, { @@ -99,114 +119,108 @@ where driver: ED, command_manager: CM, snapshot_manager: SM, - qemu_builder: Option, + qemu_config: QB, ) -> Self { Self { modules, command_manager, driver, snapshot_manager, - qemu_builder, + qemu_config, phantom: PhantomData, } } - pub fn build(self) -> Result, QemuInitError> + pub fn build(self) -> Result, QemuInitError> where CM: CommandManager, ET: EmulatorModuleTuple, + QB: TryInto, + QemuInitError: From, { - let qemu_builder = self.qemu_builder.ok_or(QemuInitError::EmptyArgs)?; - - let mut emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; - - self.modules.pre_qemu_init_all(&mut emulator_hooks); - - let qemu: Qemu = match qemu_builder { - QemuBuilder::Qemu(qemu) => qemu, - QemuBuilder::QemuConfig(qemu_config) => { - let res: Result = qemu_config.into(); - res? - } - QemuBuilder::QemuString(qemu_string) => Qemu::init(&qemu_string)?, - }; + let qemu_params: QemuParams = self.qemu_config.try_into()?; - unsafe { - Ok(Emulator::new_with_qemu( - qemu, - emulator_hooks, - self.modules, - self.driver, - self.snapshot_manager, - self.command_manager, - )) - } + Emulator::new( + qemu_params, + self.modules, + self.driver, + self.snapshot_manager, + self.command_manager, + ) } } -impl EmulatorBuilder +impl EmulatorBuilder where CM: CommandManager, S: UsesInput + Unpin, { - #[must_use] - pub fn qemu_config(self, qemu_config: QemuConfig) -> EmulatorBuilder { - EmulatorBuilder::new( - self.modules, - self.driver, - self.command_manager, - self.snapshot_manager, - Some(QemuBuilder::QemuConfig(qemu_config)), - ) - } - - #[must_use] - pub fn qemu_cli(self, qemu_cli: Vec) -> EmulatorBuilder { + /// Main QEMU config function for building `Emulator`. + /// + /// The closure takes as parameter the current qemu configuration object and must return the new + /// QEMU configurator. For now, two configurators are supported: + /// - `QemuConfigBuilder` + /// - `Vec` + /// + /// Please check the documentation of `QemuConfig` for more information. + pub fn qemu_config( + self, + qemu_config_builder: F, + ) -> EmulatorBuilder + where + F: FnOnce(QB) -> QB2, + { EmulatorBuilder::new( self.modules, self.driver, self.command_manager, self.snapshot_manager, - Some(QemuBuilder::QemuString(qemu_cli)), + qemu_config_builder(self.qemu_config), ) } - #[must_use] - pub fn qemu(self, qemu: Qemu) -> EmulatorBuilder { + pub fn prepend_module(self, module: EM) -> EmulatorBuilder + where + EM: EmulatorModule + Unpin, + ET: EmulatorModuleTuple, + { EmulatorBuilder::new( - self.modules, + self.modules.prepend(module), self.driver, self.command_manager, self.snapshot_manager, - Some(QemuBuilder::Qemu(qemu)), + self.qemu_config, ) } - pub fn add_module(self, module: EM) -> EmulatorBuilder + pub fn append_module(self, module: EM) -> EmulatorBuilder where EM: EmulatorModule + Unpin, ET: EmulatorModuleTuple, { EmulatorBuilder::new( - self.modules.prepend(module), + self.modules.append(module), self.driver, self.command_manager, self.snapshot_manager, - self.qemu_builder, + self.qemu_config, ) } - pub fn driver(self, driver: ED2) -> EmulatorBuilder { + pub fn driver(self, driver: ED2) -> EmulatorBuilder { EmulatorBuilder::new( self.modules, driver, self.command_manager, self.snapshot_manager, - self.qemu_builder, + self.qemu_config, ) } - pub fn command_manager(self, command_manager: CM2) -> EmulatorBuilder + pub fn command_manager( + self, + command_manager: CM2, + ) -> EmulatorBuilder where CM2: CommandManager, { @@ -215,30 +229,30 @@ where self.driver, command_manager, self.snapshot_manager, - self.qemu_builder, + self.qemu_config, ) } - pub fn modules(self, modules: ET2) -> EmulatorBuilder { + pub fn modules(self, modules: ET2) -> EmulatorBuilder { EmulatorBuilder::new( modules, self.driver, self.command_manager, self.snapshot_manager, - self.qemu_builder, + self.qemu_config, ) } pub fn snapshot_manager( self, snapshot_manager: SM2, - ) -> EmulatorBuilder { + ) -> EmulatorBuilder { EmulatorBuilder::new( self.modules, self.driver, self.command_manager, snapshot_manager, - self.qemu_builder, + self.qemu_config, ) } } diff --git a/libafl_qemu/src/emu/drivers.rs b/libafl_qemu/src/emu/drivers.rs index 0fe0c6f161..18d380e9a7 100644 --- a/libafl_qemu/src/emu/drivers.rs +++ b/libafl_qemu/src/emu/drivers.rs @@ -54,7 +54,7 @@ where /// Just before calling user's harness for the first time. /// Called only once fn first_harness_exec(emulator: &mut Emulator, state: &mut S) { - emulator.modules.first_exec_all(state); + emulator.modules.first_exec_all(emulator.qemu, state); } /// Just before calling user's harness @@ -63,7 +63,7 @@ where state: &mut S, input: &S::Input, ) { - emulator.modules.pre_exec_all(state, input); + emulator.modules.pre_exec_all(emulator.qemu, state, input); } /// Just after returning from user's harness @@ -78,7 +78,7 @@ where { emulator .modules - .post_exec_all(state, input, observers, exit_kind); + .post_exec_all(emulator.qemu, state, input, observers, exit_kind); } /// Just before entering QEMU @@ -169,7 +169,7 @@ where { fn first_harness_exec(emulator: &mut Emulator, state: &mut S) { if !emulator.driver.hooks_locked { - emulator.modules.first_exec_all(state); + emulator.modules.first_exec_all(emulator.qemu, state); } } @@ -179,7 +179,7 @@ where input: &S::Input, ) { if !emulator.driver.hooks_locked { - emulator.modules.pre_exec_all(state, input); + emulator.modules.pre_exec_all(emulator.qemu, state, input); } let input_location = { emulator.driver.input_location.get().cloned() }; @@ -206,7 +206,7 @@ where if !emulator.driver.hooks_locked { emulator .modules - .post_exec_all(state, input, observers, exit_kind); + .post_exec_all(emulator.qemu, state, input, observers, exit_kind); } } diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs index bb07522b60..64c035695d 100644 --- a/libafl_qemu/src/emu/hooks.rs +++ b/libafl_qemu/src/emu/hooks.rs @@ -3,13 +3,13 @@ use std::{fmt::Debug, marker::PhantomData, mem::transmute, pin::Pin, ptr}; use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple}; -use libafl_qemu_sys::{CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, GuestUsize, TCGTemp}; +use libafl_qemu_sys::{CPUStatePtr, FatPtr, GuestAddr, GuestUsize, TCGTemp}; #[cfg(feature = "usermode")] use crate::qemu::{ closure_post_syscall_hook_wrapper, closure_pre_syscall_hook_wrapper, func_post_syscall_hook_wrapper, func_pre_syscall_hook_wrapper, PostSyscallHook, - PostSyscallHookId, PreSyscallHook, PreSyscallHookId, SyscallHookResult, + PostSyscallHookId, PreSyscallHook, PreSyscallHookId, }; #[cfg(feature = "usermode")] use crate::qemu::{ @@ -37,9 +37,15 @@ use crate::{ ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook, WriteExecNHook, WriteGenHook, WriteHookId, }, - CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, Qemu, + CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, NewThreadHookFn, Qemu, }; +/// Get a C-compatible function pointer from the input hook. +/// If the hook was already a c function, nothing is done. +/// +/// h: input hook +/// replacement: C-compatible function to call when C side should call the hook +/// fntype: type used to cast h into replacement macro_rules! get_raw_hook { ($h:expr, $replacement:expr, $fntype:ty) => { match $h { @@ -74,6 +80,7 @@ where { unsafe { let emulator_modules = EmulatorModules::::emulator_modules_mut().unwrap(); + let qemu = Qemu::get_unchecked(); let crash_hooks_ptr = &raw mut emulator_modules.hooks.crash_hooks; @@ -81,12 +88,12 @@ where match crash_hook { HookRepr::Function(ptr) => { let func: CrashHookFn = transmute(*ptr); - func(emulator_modules, target_sig); + func(qemu, emulator_modules, target_sig); } HookRepr::Closure(ptr) => { let func: &mut CrashHookClosure = &mut *(ptr::from_mut::(ptr) as *mut CrashHookClosure); - func(emulator_modules, target_sig); + func(qemu, emulator_modules, target_sig); } HookRepr::Empty => (), } @@ -100,7 +107,6 @@ pub struct EmulatorModules where S: UsesInput, { - qemu: Qemu, modules: Pin>, hooks: EmulatorHooks, phantom: PhantomData, @@ -170,6 +176,11 @@ where } } + #[must_use] + pub fn qemu_hooks(&self) -> QemuHooks { + self.qemu_hooks + } + pub fn instruction_closure( &mut self, addr: GuestAddr, @@ -679,10 +690,7 @@ where } } - pub fn backdoor_function( - &self, - hook: fn(&mut EmulatorModules, Option<&mut S>, cpu: CPUArchStatePtr, pc: GuestAddr), - ) -> BackdoorHookId { + pub fn backdoor_function(&self, hook: BackdoorHookFn) -> BackdoorHookId { unsafe { self.qemu_hooks .add_backdoor_hook(transmute(hook), func_backdoor_hook_wrapper::) @@ -715,15 +723,7 @@ where } } - pub fn thread_creation_function( - &mut self, - hook: fn( - &mut EmulatorModules, - Option<&mut S>, - env: CPUArchStatePtr, - tid: u32, - ) -> bool, - ) -> NewThreadHookId { + pub fn thread_creation_function(&mut self, hook: NewThreadHookFn) -> NewThreadHookId { unsafe { self.qemu_hooks .add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::) @@ -768,10 +768,10 @@ where S: Unpin + UsesInput, { #[allow(clippy::type_complexity)] - pub fn syscalls(&mut self, hook: PreSyscallHook) -> Option { + pub fn pre_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::Function(f) => Some(self.pre_syscalls_function(f)), + Hook::Closure(c) => Some(self.pre_syscalls_closure(c)), Hook::Raw(r) => { let z: *const () = ptr::null::<()>(); Some(self.qemu_hooks.add_pre_syscall_hook(z, r)) @@ -781,7 +781,7 @@ where } #[allow(clippy::type_complexity)] - pub fn syscalls_function(&mut self, hook: PreSyscallHookFn) -> PreSyscallHookId { + pub fn pre_syscalls_function(&mut self, hook: PreSyscallHookFn) -> PreSyscallHookId { // # Safety // Will dereference the hook as [`FatPtr`]. unsafe { @@ -791,7 +791,7 @@ where } #[allow(clippy::type_complexity)] - pub fn syscalls_closure(&mut self, hook: PreSyscallHookClosure) -> PreSyscallHookId { + pub fn pre_syscalls_closure(&mut self, hook: PreSyscallHookClosure) -> PreSyscallHookId { // # Safety // Will dereference the hook as [`FatPtr`]. unsafe { @@ -822,10 +822,10 @@ where } #[allow(clippy::type_complexity)] - pub fn after_syscalls(&mut self, hook: PostSyscallHook) -> Option { + pub fn post_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::Function(f) => Some(self.post_syscalls_function(f)), + Hook::Closure(c) => Some(self.post_syscalls_closure(c)), Hook::Raw(r) => { let z: *const () = ptr::null::<()>(); Some(self.qemu_hooks.add_post_syscall_hook(z, r)) @@ -835,7 +835,7 @@ where } #[allow(clippy::type_complexity)] - pub fn after_syscalls_function(&mut self, hook: PostSyscallHookFn) -> PostSyscallHookId { + pub fn post_syscalls_function(&mut self, hook: PostSyscallHookFn) -> PostSyscallHookId { // # Safety // Will dereference the hook as [`FatPtr`]. This should be ok. unsafe { @@ -845,7 +845,7 @@ where } #[allow(clippy::type_complexity)] - pub fn after_syscalls_closure( + pub fn post_syscalls_closure( &mut self, hook: PostSyscallHookClosure, ) -> PostSyscallHookId { @@ -876,7 +876,7 @@ where } } - pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules, target_signal: i32)) { + pub fn crash_function(&mut self, hook: CrashHookFn) { // # Safety // Will cast the valid hook to a ptr. self.qemu_hooks.set_crash_hook(crash_hook_wrapper::); @@ -965,7 +965,7 @@ where pub fn instruction_function( &mut self, addr: GuestAddr, - hook: fn(&mut EmulatorModules, Option<&mut S>, GuestAddr), + hook: InstructionHookFn, invalidate_block: bool, ) -> InstructionHookId { self.hooks @@ -1076,15 +1076,7 @@ where self.hooks.thread_creation(hook) } - pub fn thread_creation_function( - &mut self, - hook: fn( - &mut EmulatorModules, - Option<&mut S>, - env: CPUArchStatePtr, - tid: u32, - ) -> bool, - ) -> NewThreadHookId { + pub fn thread_creation_function(&mut self, hook: NewThreadHookFn) -> NewThreadHookId { self.hooks.thread_creation_function(hook) } @@ -1101,13 +1093,8 @@ where ET: EmulatorModuleTuple, S: UsesInput + Unpin, { - pub(super) fn new( - qemu: Qemu, - emulator_hooks: EmulatorHooks, - modules: ET, - ) -> Pin> { + pub(super) fn new(emulator_hooks: EmulatorHooks, modules: ET) -> Pin> { let mut modules = Box::pin(Self { - qemu, modules: Box::pin(modules), hooks: emulator_hooks, phantom: PhantomData, @@ -1130,35 +1117,40 @@ where modules } - pub fn post_qemu_init_all(&mut self) { + pub fn post_qemu_init_all(&mut self, qemu: Qemu) { // 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. unsafe { self.modules_mut() - .post_qemu_init_all(Self::emulator_modules_mut_unchecked()); + .post_qemu_init_all(qemu, Self::emulator_modules_mut_unchecked()); } } - pub fn first_exec_all(&mut self, state: &mut S) { + pub fn first_exec_all(&mut self, qemu: Qemu, state: &mut S) { // # Safety // We assume that the emulator was initialized correctly unsafe { self.modules_mut() - .first_exec_all(Self::emulator_modules_mut_unchecked(), state); + .first_exec_all(qemu, Self::emulator_modules_mut_unchecked(), state); } } - pub fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) { + pub fn pre_exec_all(&mut self, qemu: Qemu, state: &mut S, input: &S::Input) { // # Safety // We assume that the emulator was initialized correctly unsafe { - self.modules_mut() - .pre_exec_all(Self::emulator_modules_mut_unchecked(), state, input); + self.modules_mut().pre_exec_all( + qemu, + Self::emulator_modules_mut_unchecked(), + state, + input, + ); } } pub fn post_exec_all( &mut self, + qemu: Qemu, state: &mut S, input: &S::Input, observers: &mut OT, @@ -1168,6 +1160,7 @@ where { unsafe { self.modules_mut().post_exec_all( + qemu, Self::emulator_modules_mut_unchecked(), state, input, @@ -1199,16 +1192,15 @@ impl EmulatorModules where S: UsesInput, { - #[must_use] - pub fn qemu(&self) -> Qemu { - self.qemu - } - #[must_use] pub fn modules(&self) -> &ET { self.modules.as_ref().get_ref() } + pub fn hooks(&mut self) -> &EmulatorHooks { + &self.hooks + } + pub fn hooks_mut(&mut self) -> &mut EmulatorHooks { &mut self.hooks } @@ -1222,108 +1214,54 @@ where S: Unpin + UsesInput, { #[allow(clippy::type_complexity)] - pub fn syscalls(&mut self, hook: PreSyscallHook) -> Option { - self.hooks.syscalls(hook) + pub fn pre_syscalls(&mut self, hook: PreSyscallHook) -> Option { + self.hooks.pre_syscalls(hook) } /// # Safety /// Calls through to the, potentially unsafe, `syscalls_function` #[allow(clippy::type_complexity)] - pub unsafe fn syscalls_function( + pub unsafe fn pre_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, + hook: PreSyscallHookFn, ) -> PreSyscallHookId { - self.hooks.syscalls_function(hook) + self.hooks.pre_syscalls_function(hook) } /// # Safety /// Calls through to the, potentially unsafe, `syscalls_closure` #[allow(clippy::type_complexity)] - pub unsafe fn syscalls_closure( + pub unsafe fn pre_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, - >, + hook: PreSyscallHookClosure, ) -> PreSyscallHookId { - self.hooks.syscalls_closure(hook) + self.hooks.pre_syscalls_closure(hook) } #[allow(clippy::type_complexity)] - pub fn after_syscalls(&mut self, hook: PostSyscallHook) -> Option { - self.hooks.after_syscalls(hook) + pub fn post_syscalls(&mut self, hook: PostSyscallHook) -> Option { + self.hooks.post_syscalls(hook) } /// # Safety /// Calls through to the, potentially unsafe, `after_syscalls_function` #[allow(clippy::type_complexity)] - pub unsafe fn after_syscalls_function( + pub unsafe fn post_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, + hook: PostSyscallHookFn, ) -> PostSyscallHookId { - self.hooks.after_syscalls_function(hook) + self.hooks.post_syscalls_function(hook) } #[allow(clippy::type_complexity)] - pub fn after_syscalls_closure( + pub fn post_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, - >, + hook: PostSyscallHookClosure, ) -> PostSyscallHookId { - self.hooks.after_syscalls_closure(hook) + self.hooks.post_syscalls_closure(hook) } - pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules, target_signal: i32)) { + pub fn crash_function(&mut self, hook: CrashHookFn) { self.hooks.crash_function(hook); } diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index 0fcf92e3f6..38728aadd9 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -19,7 +19,7 @@ use crate::{ command::{CommandError, CommandManager, NopCommandManager, StdCommandManager}, modules::EmulatorModuleTuple, sync_exit::SyncExit, - Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk, + Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk, QemuParams, QemuShutdownCause, Regs, CPU, }; @@ -45,6 +45,8 @@ mod systemmode; #[cfg(feature = "systemmode")] pub use systemmode::*; +use crate::config::QemuConfigBuilder; + #[derive(Clone, Copy)] pub enum GuestAddrKind { Physical(GuestPhysAddr), @@ -243,8 +245,14 @@ where S: UsesInput, { #[must_use] - pub fn empty( - ) -> EmulatorBuilder { + pub fn empty() -> EmulatorBuilder< + NopCommandManager, + NopEmulatorDriver, + (), + QemuConfigBuilder, + S, + NopSnapshotManager, + > { EmulatorBuilder::empty() } } @@ -255,8 +263,14 @@ where S::Input: HasTargetBytes, { #[must_use] - pub fn builder( - ) -> EmulatorBuilder, StdEmulatorDriver, (), S, StdSnapshotManager> { + pub fn builder() -> EmulatorBuilder< + StdCommandManager, + StdEmulatorDriver, + (), + QemuConfigBuilder, + S, + StdSnapshotManager, + > { EmulatorBuilder::default() } } @@ -322,24 +336,35 @@ where S: UsesInput + Unpin, { #[allow(clippy::must_use_candidate, clippy::similar_names)] - pub fn new( - qemu_args: &[String], + pub fn new( + qemu_params: T, modules: ET, driver: ED, snapshot_manager: SM, command_manager: CM, - ) -> Result { - let mut emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; + ) -> Result + where + T: Into, + { + let mut qemu_params = qemu_params.into(); + + let emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) }; + let mut emulator_modules = EmulatorModules::new(emulator_hooks, modules); - modules.pre_qemu_init_all(&mut emulator_hooks); + // TODO: fix things there properly. The biggest issue being that it creates 2 mut ref to the module with the callback being called + unsafe { + emulator_modules.modules_mut().pre_qemu_init_all( + EmulatorModules::::emulator_modules_mut_unchecked(), + &mut qemu_params, + ); + } - let qemu = Qemu::init(qemu_args)?; + let qemu = Qemu::init(qemu_params)?; unsafe { Ok(Self::new_with_qemu( qemu, - emulator_hooks, - modules, + emulator_modules, driver, snapshot_manager, command_manager, @@ -352,17 +377,16 @@ where /// /// # Safety /// - /// pre-init qemu hooks should be run by then. - pub(crate) unsafe fn new_with_qemu( + /// pre-init qemu hooks should be run before calling this. + unsafe fn new_with_qemu( qemu: Qemu, - emulator_hooks: EmulatorHooks, - modules: ET, + emulator_modules: Pin>>, driver: ED, snapshot_manager: SM, command_manager: CM, ) -> Self { let mut emulator = Emulator { - modules: EmulatorModules::new(qemu, emulator_hooks, modules), + modules: emulator_modules, command_manager, snapshot_manager, driver, @@ -371,7 +395,7 @@ where qemu, }; - emulator.modules.post_qemu_init_all(); + emulator.modules.post_qemu_init_all(qemu); emulator } diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 6a56c06c4b..c0fc3f44c3 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -9,6 +9,8 @@ use std::ptr; #[cfg(feature = "systemmode")] use std::sync::atomic::{AtomicBool, Ordering}; +#[cfg(feature = "usermode")] +use libafl::inputs::UsesInput; use libafl::{ corpus::Corpus, events::{EventFirer, EventRestarter}, @@ -20,7 +22,6 @@ use libafl::{ }, feedbacks::Feedback, fuzzer::HasObjective, - inputs::UsesInput, observers::ObserversTuple, state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState}, Error, ExecutionProcessor, HasScheduler, @@ -39,6 +40,8 @@ use libc::siginfo_t; #[cfg(feature = "usermode")] use crate::EmulatorModules; +#[cfg(feature = "usermode")] +use crate::Qemu; use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver}; pub struct QemuExecutor<'a, CM, ED, ET, H, OT, S, SM> @@ -181,12 +184,12 @@ where inner.inprocess_hooks_mut().crash_handler = inproc_qemu_crash_handler:: as *const c_void; - let handler = |emulator_modules: &mut EmulatorModules, host_sig| { + let handler = |qemu: Qemu, _emulator_modules: &mut EmulatorModules, host_sig| { eprintln!("Crashed with signal {host_sig}"); unsafe { libafl::executors::inprocess::generic_inproc_crash_handler::(); } - if let Some(cpu) = emulator_modules.qemu().current_cpu() { + if let Some(cpu) = qemu.current_cpu() { eprint!("Context:\n{}", cpu.display_context()); } }; diff --git a/libafl_qemu/src/modules/calls.rs b/libafl_qemu/src/modules/calls.rs index 1e280a0720..8088891050 100644 --- a/libafl_qemu/src/modules/calls.rs +++ b/libafl_qemu/src/modules/calls.rs @@ -243,6 +243,7 @@ where } fn on_ret( + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc: GuestAddr, @@ -250,7 +251,7 @@ where S: Unpin + UsesInput, ET: EmulatorModuleTuple, { - let ret_addr: GuestAddr = emulator_modules.qemu().read_return_address().unwrap(); + let ret_addr: GuestAddr = qemu.read_return_address().unwrap(); // log::info!("RET @ 0x{:#x}", ret_addr); @@ -270,6 +271,7 @@ where } fn gen_blocks_calls( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -292,8 +294,6 @@ where .unwrap(); } - let qemu = emulator_modules.qemu(); - let mut call_addrs: Vec<(GuestAddr, usize)> = Vec::new(); let mut ret_addrs: Vec = Vec::new(); @@ -362,7 +362,10 @@ where 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| { + move |_qemu: Qemu, + 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() @@ -399,7 +402,7 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -412,21 +415,20 @@ where fn pre_exec( &mut self, - emulator_modules: &mut EmulatorModules, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _state: &mut S, input: &S::Input, ) where ET: EmulatorModuleTuple, { - self.collectors - .as_mut() - .unwrap() - .pre_exec_all(emulator_modules.qemu(), input); + self.collectors.as_mut().unwrap().pre_exec_all(qemu, input); } fn post_exec( &mut self, - emulator_modules: &mut EmulatorModules, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _state: &mut S, input: &S::Input, observers: &mut OT, @@ -435,12 +437,10 @@ where OT: ObserversTuple, ET: EmulatorModuleTuple, { - self.collectors.as_mut().unwrap().post_exec_all( - emulator_modules.qemu(), - input, - observers, - exit_kind, - ); + self.collectors + .as_mut() + .unwrap() + .post_exec_all(qemu, input, observers, exit_kind); } fn address_filter(&self) -> &Self::ModuleAddressFilter { diff --git a/libafl_qemu/src/modules/cmplog.rs b/libafl_qemu/src/modules/cmplog.rs index b595971374..69ec5cb605 100644 --- a/libafl_qemu/src/modules/cmplog.rs +++ b/libafl_qemu/src/modules/cmplog.rs @@ -14,11 +14,12 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "systemmode")] use crate::modules::{NopPageFilter, NOP_PAGE_FILTER}; #[cfg(feature = "usermode")] -use crate::{capstone, qemu::ArchExtras, CallingConvention, Qemu}; +use crate::{capstone, qemu::ArchExtras, CallingConvention}; use crate::{ emu::EmulatorModules, modules::{hash_me, AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter}, qemu::Hook, + Qemu, }; #[cfg_attr( @@ -74,8 +75,12 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { emulator_modules.cmps( @@ -139,8 +144,12 @@ where const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { emulator_modules.cmps( @@ -172,6 +181,7 @@ where } pub fn gen_unique_cmp_ids( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc: GuestAddr, @@ -203,6 +213,7 @@ where } pub fn gen_hashed_cmp_ids( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -296,6 +307,7 @@ impl CmpLogRoutinesModule { } fn gen_blocks_calls( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -318,8 +330,6 @@ impl CmpLogRoutinesModule { .unwrap(); } - let qemu = emulator_modules.qemu(); - if let Some(h) = emulator_modules.get::() { #[allow(unused_mut)] let mut code = { @@ -391,8 +401,12 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { emulator_modules.blocks( diff --git a/libafl_qemu/src/modules/drcov.rs b/libafl_qemu/src/modules/drcov.rs index e40d3dda0e..8530575d80 100644 --- a/libafl_qemu/src/modules/drcov.rs +++ b/libafl_qemu/src/modules/drcov.rs @@ -13,6 +13,7 @@ use crate::{ emu::EmulatorModules, modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, NopAddressFilter}, qemu::Hook, + Qemu, }; static DRCOV_IDS: Mutex>> = Mutex::new(None); @@ -264,7 +265,7 @@ where #[cfg(feature = "systemmode")] type ModulePageFilter = NopPageFilter; - fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -276,15 +277,17 @@ where } #[cfg(feature = "usermode")] - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { if self.module_mapping.is_none() { log::info!("Auto-filling module mapping for DrCov module from QEMU mapping."); - let qemu = emulator_modules.qemu(); - let mut module_mapping: RangeMap = RangeMap::new(); #[allow(clippy::unnecessary_cast)] // for GuestAddr -> u64 @@ -307,8 +310,12 @@ where } #[cfg(feature = "systemmode")] - fn first_exec(&mut self, _emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { assert!( @@ -319,6 +326,7 @@ where fn post_exec( &mut self, + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, @@ -359,6 +367,7 @@ where } pub fn gen_unique_block_ids( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, state: Option<&mut S>, pc: GuestAddr, @@ -408,6 +417,7 @@ where } pub fn gen_block_lengths( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -430,6 +440,7 @@ pub fn gen_block_lengths( } pub fn exec_trace_block( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, diff --git a/libafl_qemu/src/modules/edges/helpers.rs b/libafl_qemu/src/modules/edges/helpers.rs index 760fce7272..8cb8a8109f 100644 --- a/libafl_qemu/src/modules/edges/helpers.rs +++ b/libafl_qemu/src/modules/edges/helpers.rs @@ -60,7 +60,7 @@ mod generators { }; use crate::{ modules::{hash_me, AddressFilter, EdgeCoverageModule, EmulatorModuleTuple, PageFilter}, - EmulatorModules, + EmulatorModules, Qemu, }; fn get_mask() -> usize { @@ -77,7 +77,9 @@ mod generators { } } + #[allow(unused_variables)] pub fn gen_unique_edge_ids( + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: Option<&mut S>, src: GuestAddr, @@ -108,10 +110,7 @@ mod generators { #[cfg(feature = "systemmode")] { - let paging_id = emulator_modules - .qemu() - .current_cpu() - .and_then(|cpu| cpu.current_paging_id()); + let paging_id = qemu.current_cpu().and_then(|cpu| cpu.current_paging_id()); if !module.must_instrument(src, paging_id) && !module.must_instrument(dest, paging_id) @@ -155,8 +154,9 @@ mod generators { } } - #[allow(clippy::unnecessary_cast)] + #[allow(clippy::unnecessary_cast, unused_variables)] pub fn gen_hashed_edge_ids( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, src: GuestAddr, @@ -179,10 +179,7 @@ mod generators { #[cfg(feature = "systemmode")] { - let paging_id = emulator_modules - .qemu() - .current_cpu() - .and_then(|cpu| cpu.current_paging_id()); + let paging_id = qemu.current_cpu().and_then(|cpu| cpu.current_paging_id()); if !module.must_instrument(src, paging_id) && !module.must_instrument(dest, paging_id) @@ -209,8 +206,9 @@ mod generators { } } - #[allow(clippy::unnecessary_cast)] + #[allow(clippy::unnecessary_cast, unused_variables)] pub fn gen_hashed_block_ids( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -234,10 +232,7 @@ mod generators { } #[cfg(feature = "systemmode")] { - let page_id = emulator_modules - .qemu() - .current_cpu() - .and_then(|cpu| cpu.current_paging_id()); + let page_id = qemu.current_cpu().and_then(|cpu| cpu.current_paging_id()); if !module.must_instrument(pc, page_id) { return None; diff --git a/libafl_qemu/src/modules/edges/mod.rs b/libafl_qemu/src/modules/edges/mod.rs index db2538ca7e..1b9cb4026a 100644 --- a/libafl_qemu/src/modules/edges/mod.rs +++ b/libafl_qemu/src/modules/edges/mod.rs @@ -9,6 +9,7 @@ use libafl_qemu_sys::GuestPhysAddr; use crate::{ emu::EmulatorModules, modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, PageFilter}, + Qemu, }; mod helpers; @@ -327,8 +328,12 @@ where type ModulePageFilter = PF; const HOOKS_DO_SIDE_EFFECTS: bool = V::DO_SIDE_EFFECTS; - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { if self.use_hitcounts { diff --git a/libafl_qemu/src/modules/mod.rs b/libafl_qemu/src/modules/mod.rs index 04b80a84ee..8b470b8142 100644 --- a/libafl_qemu/src/modules/mod.rs +++ b/libafl_qemu/src/modules/mod.rs @@ -40,7 +40,7 @@ pub mod drcov; #[cfg(not(cpu_target = "hexagon"))] pub use drcov::{DrCovMetadata, DrCovModule, DrCovModuleBuilder}; -use crate::{emu::EmulatorModules, EmulatorHooks, Qemu}; +use crate::{emu::EmulatorModules, Qemu, QemuParams}; /// A module for `libafl_qemu`. // TODO remove 'static when specialization will be stable @@ -58,8 +58,14 @@ where /// Hook run **before** QEMU is initialized. /// This is always run when Emulator gets initialized, in any case. /// Install here hooks that should be alive for the whole execution of the VM, even before QEMU gets initialized. - fn pre_qemu_init(&self, _emulator_hooks: &mut EmulatorHooks) - where + /// + /// It is also possible to edit QEMU parameters, just before QEMU gets initialized. + /// Thus, the module can modify options for QEMU just before it gets initialized. + fn pre_qemu_init( + &mut self, + _emulator_modules: &mut EmulatorModules, + _qemu_params: &mut QemuParams, + ) where ET: EmulatorModuleTuple, { } @@ -67,7 +73,7 @@ where /// Hook run **after** QEMU is initialized. /// This is always run when Emulator gets initialized, in any case. /// Install here hooks that should be alive for the whole execution of the VM, after QEMU gets initialized. - fn post_qemu_init(&self, _emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&mut self, _qemu: Qemu, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -77,8 +83,12 @@ where /// This call can be delayed to the point at which fuzzing is supposed to start. /// It is mostly used to avoid running hooks during VM initialization, either /// because it is useless or it would produce wrong results. - fn first_exec(&mut self, _emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { } @@ -87,6 +97,7 @@ where /// On the first run, it is executed after [`Self::first_exec`]. fn pre_exec( &mut self, + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, @@ -98,6 +109,7 @@ where /// Run after a fuzzing run ends. fn post_exec( &mut self, + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, @@ -146,20 +158,28 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool; - fn pre_qemu_init_all(&self, _emulator_hooks: &mut EmulatorHooks) - where + fn pre_qemu_init_all( + &mut self, + emulator_modules: &mut EmulatorModules, + qemu_params: &mut QemuParams, + ) where ET: EmulatorModuleTuple; - fn post_qemu_init_all(&self, _emulator_modules: &mut EmulatorModules) + fn post_qemu_init_all(&mut self, qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple; - fn first_exec_all(&mut self, emulator_modules: &mut EmulatorModules, state: &mut S) - where + fn first_exec_all( + &mut self, + qemu: Qemu, + emulator_modules: &mut EmulatorModules, + state: &mut S, + ) where ET: EmulatorModuleTuple; fn pre_exec_all( &mut self, + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: &mut S, input: &S::Input, @@ -168,6 +188,7 @@ where fn post_exec_all( &mut self, + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: &mut S, input: &S::Input, @@ -195,30 +216,41 @@ where impl EmulatorModuleTuple for () where - S: UsesInput, + S: UsesInput + Unpin, { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn pre_qemu_init_all(&self, _emulator_hooks: &mut EmulatorHooks) - where + fn pre_qemu_init_all( + &mut self, + _emulator_modules: &mut EmulatorModules, + _qemu_params: &mut QemuParams, + ) where ET: EmulatorModuleTuple, { } - fn post_qemu_init_all(&self, _emulator_modules: &mut EmulatorModules) - where + fn post_qemu_init_all( + &mut self, + _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + ) where ET: EmulatorModuleTuple, { } - fn first_exec_all(&mut self, _emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec_all( + &mut self, + _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { } fn pre_exec_all( &mut self, + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, @@ -229,6 +261,7 @@ where fn post_exec_all( &mut self, + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, @@ -258,44 +291,53 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS; - fn pre_qemu_init_all(&self, emulator_hooks: &mut EmulatorHooks) - where + fn pre_qemu_init_all( + &mut self, + emulator_modules: &mut EmulatorModules, + qemu_params: &mut QemuParams, + ) where ET: EmulatorModuleTuple, { - self.0.pre_qemu_init(emulator_hooks); - self.1.pre_qemu_init_all(emulator_hooks); + self.0.pre_qemu_init(emulator_modules, qemu_params); + self.1.pre_qemu_init_all(emulator_modules, qemu_params); } - fn post_qemu_init_all(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init_all(&mut self, qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { - self.0.post_qemu_init(emulator_modules); - self.1.post_qemu_init_all(emulator_modules); + self.0.post_qemu_init(qemu, emulator_modules); + self.1.post_qemu_init_all(qemu, emulator_modules); } - fn first_exec_all(&mut self, emulator_modules: &mut EmulatorModules, state: &mut S) - where + fn first_exec_all( + &mut self, + qemu: Qemu, + emulator_modules: &mut EmulatorModules, + state: &mut S, + ) where ET: EmulatorModuleTuple, { - self.0.first_exec(emulator_modules, state); - self.1.first_exec_all(emulator_modules, state); + self.0.first_exec(qemu, emulator_modules, state); + self.1.first_exec_all(qemu, emulator_modules, state); } fn pre_exec_all( &mut self, + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: &mut S, input: &S::Input, ) where ET: EmulatorModuleTuple, { - self.0.pre_exec(emulator_modules, state, input); - self.1.pre_exec_all(emulator_modules, state, input); + self.0.pre_exec(qemu, emulator_modules, state, input); + self.1.pre_exec_all(qemu, emulator_modules, state, input); } fn post_exec_all( &mut self, + qemu: Qemu, emulator_modules: &mut EmulatorModules, state: &mut S, input: &S::Input, @@ -306,9 +348,9 @@ where ET: EmulatorModuleTuple, { self.0 - .post_exec(emulator_modules, state, input, observers, exit_kind); + .post_exec(qemu, emulator_modules, state, input, observers, exit_kind); self.1 - .post_exec_all(emulator_modules, state, input, observers, exit_kind); + .post_exec_all(qemu, emulator_modules, state, input, observers, exit_kind); } unsafe fn on_crash_all(&mut self) { diff --git a/libafl_qemu/src/modules/usermode/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs index 45ee1e8626..a62940d925 100644 --- a/libafl_qemu/src/modules/usermode/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -16,9 +16,9 @@ use crate::{ calls::FullBacktraceCollector, snapshot::SnapshotModule, EmulatorModule, EmulatorModuleTuple, }, - qemu::{MemAccessInfo, QemuInitError}, + qemu::MemAccessInfo, sys::TCGTemp, - Qemu, Regs, + Qemu, QemuParams, Regs, }; // TODO at some point, merge parts with libafl_frida @@ -176,8 +176,8 @@ impl core::fmt::Debug for AsanGiovese { } impl AsanGiovese { - unsafe fn map_shadow() { - assert!( + unsafe fn init(self: &mut Pin>, qemu_hooks: QemuHooks) { + assert_ne!( libc::mmap( HIGH_SHADOW_ADDR, HIGH_SHADOW_SIZE, @@ -185,9 +185,10 @@ impl AsanGiovese { MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 - ) != MAP_FAILED + ), + MAP_FAILED ); - assert!( + assert_ne!( libc::mmap( LOW_SHADOW_ADDR, LOW_SHADOW_SIZE, @@ -195,9 +196,10 @@ impl AsanGiovese { MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 - ) != MAP_FAILED + ), + MAP_FAILED ); - assert!( + assert_ne!( libc::mmap( GAP_SHADOW_ADDR, GAP_SHADOW_SIZE, @@ -205,12 +207,15 @@ impl AsanGiovese { MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 - ) != MAP_FAILED + ), + MAP_FAILED ); + + qemu_hooks.add_pre_syscall_hook(self.as_mut(), Self::fake_syscall); } #[must_use] - fn new(qemu_hooks: QemuHooks) -> Pin> { + fn new() -> Pin> { let res = Self { alloc_tree: Mutex::new(IntervalTree::new()), saved_tree: IntervalTree::new(), @@ -219,9 +224,7 @@ impl AsanGiovese { saved_shadow: HashMap::default(), snapshot_shadow: true, // By default, track the dirty shadow pages }; - let mut boxed = Box::pin(res); - qemu_hooks.add_pre_syscall_hook(boxed.as_mut(), Self::fake_syscall); - boxed + Box::pin(res) } extern "C" fn fake_syscall( @@ -255,7 +258,7 @@ impl AsanGiovese { Self::unpoison(qemu, a1, a2 as usize); } QasanAction::IsPoison => { - if Self::is_invalid_access(qemu, a1, a2 as usize) { + if Self::is_invalid_access_n(qemu, a1, a2 as usize) { r = 1; } } @@ -285,51 +288,25 @@ impl AsanGiovese { #[inline] #[must_use] - pub fn is_invalid_access_1(qemu: Qemu, addr: GuestAddr) -> bool { - unsafe { - let h = qemu.g2h::<*const c_void>(addr) as isize; - let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); - let k = *shadow_addr as isize; - k != 0 && (h & 7).wrapping_add(1) > k - } - } - - #[inline] - #[must_use] - pub fn is_invalid_access_2(qemu: Qemu, addr: GuestAddr) -> bool { - unsafe { - let h = qemu.g2h::<*const c_void>(addr) as isize; - let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); - let k = *shadow_addr as isize; - k != 0 && (h & 7).wrapping_add(2) > k - } - } - - #[inline] - #[must_use] - pub fn is_invalid_access_4(qemu: Qemu, addr: GuestAddr) -> bool { - unsafe { - let h = qemu.g2h::<*const c_void>(addr) as isize; - let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); - let k = *shadow_addr as isize; - k != 0 && (h & 7).wrapping_add(4) > k - } - } + pub fn is_invalid_access(qemu: Qemu, addr: GuestAddr) -> bool { + const { assert!(N == 1 || N == 2 || N == 4 || N == 8) }; - #[inline] - #[must_use] - pub fn is_invalid_access_8(qemu: Qemu, addr: GuestAddr) -> bool { unsafe { let h = qemu.g2h::<*const c_void>(addr) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); - *shadow_addr != 0 + if N < 8 { + let k = *shadow_addr as isize; + k != 0 && (h & 7).wrapping_add(N as isize) > k + } else { + *shadow_addr != 0 + } } } #[inline] #[must_use] #[allow(clippy::cast_sign_loss)] - pub fn is_invalid_access(qemu: Qemu, addr: GuestAddr, n: usize) -> bool { + pub fn is_invalid_access_n(qemu: Qemu, addr: GuestAddr, n: usize) -> bool { unsafe { if n == 0 { return false; @@ -672,64 +649,6 @@ impl AsanGiovese { } } -static mut ASAN_INITED: bool = false; - -pub fn init_qemu_with_asan( - args: &mut Vec, - env: &mut [(String, String)], -) -> Result<(Qemu, Pin>), QemuInitError> { - let current = env::current_exe().unwrap(); - let asan_lib = fs::canonicalize(current) - .unwrap() - .parent() - .unwrap() - .join("libqasan.so"); - let asan_lib = asan_lib - .to_str() - .expect("The path to the asan lib is invalid") - .to_string(); - let add_asan = - |e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..]; - - // TODO: adapt since qemu does not take envp anymore as parameter - let mut added = false; - for (k, v) in &mut *env { - if k == "QEMU_SET_ENV" { - let mut new_v = vec![]; - for e in v.split(',') { - if e.starts_with("LD_PRELOAD=") { - added = true; - new_v.push(add_asan(e)); - } else { - new_v.push(e.to_string()); - } - } - *v = new_v.join(","); - } - } - for i in 0..args.len() { - if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") { - added = true; - args[i + 1] = add_asan(&args[i + 1]); - } - } - - if !added { - args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib); - args.insert(1, "-E".into()); - } - - unsafe { - AsanGiovese::map_shadow(); - ASAN_INITED = true; - } - - let qemu = Qemu::init(args)?; - let rt = AsanGiovese::new(qemu.hooks()); - - Ok((qemu, rt)) -} - pub enum QemuAsanOptions { None, Snapshot, @@ -741,6 +660,7 @@ pub type AsanChildModule = AsanModule; #[derive(Debug)] pub struct AsanModule { + env: Vec<(String, String)>, enabled: bool, detect_leaks: bool, empty: bool, @@ -750,25 +670,28 @@ pub struct AsanModule { impl AsanModule { #[must_use] - pub fn default(rt: Pin>) -> Self { - Self::new(rt, StdAddressFilter::default(), &QemuAsanOptions::Snapshot) + pub fn default(env: &[(String, String)]) -> Self { + Self::new(StdAddressFilter::default(), &QemuAsanOptions::Snapshot, env) } #[must_use] pub fn new( - mut rt: Pin>, filter: StdAddressFilter, options: &QemuAsanOptions, + env: &[(String, String)], ) -> Self { - assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_qemu_with_asan(...) instead of just Qemu::init(...)"); let (snapshot, detect_leaks) = match options { QemuAsanOptions::None => (false, false), QemuAsanOptions::Snapshot => (true, false), QemuAsanOptions::DetectLeaks => (false, true), QemuAsanOptions::SnapshotDetectLeaks => (true, true), }; + + let mut rt = AsanGiovese::new(); rt.set_snapshot_shadow(snapshot); + Self { + env: env.to_vec(), enabled: true, detect_leaks, empty: true, @@ -779,21 +702,24 @@ impl AsanModule { #[must_use] pub fn with_error_callback( - mut rt: Pin>, filter: StdAddressFilter, error_callback: AsanErrorCallback, options: &QemuAsanOptions, + env: &[(String, String)], ) -> Self { - assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_qemu_with_asan(...) instead of just Qemu::init(...)"); let (snapshot, detect_leaks) = match options { QemuAsanOptions::None => (false, false), QemuAsanOptions::Snapshot => (true, false), QemuAsanOptions::DetectLeaks => (false, true), QemuAsanOptions::SnapshotDetectLeaks => (true, true), }; + + let mut rt = AsanGiovese::new(); rt.set_snapshot_shadow(snapshot); rt.set_error_callback(error_callback); + Self { + env: env.to_vec(), enabled: true, detect_leaks, empty: true, @@ -806,15 +732,15 @@ impl AsanModule { /// The `ASan` error report accesses [`FullBacktraceCollector`] #[must_use] pub unsafe fn with_asan_report( - rt: Pin>, filter: StdAddressFilter, options: &QemuAsanOptions, + env: &[(String, String)], ) -> Self { Self::with_error_callback( - rt, filter, Box::new(|rt, qemu, pc, err| unsafe { asan_report(rt, qemu, pc, &err) }), options, + env, ) } @@ -843,66 +769,30 @@ impl AsanModule { #[allow(clippy::unused_self)] #[must_use] pub fn is_poisoned(&self, qemu: Qemu, addr: GuestAddr, size: usize) -> bool { - AsanGiovese::is_invalid_access(qemu, addr, size) - } - - pub fn read_1(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_1(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 1)); - } + AsanGiovese::is_invalid_access_n(qemu, addr, size) } - pub fn read_2(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_2(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 2)); - } - } - - pub fn read_4(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_4(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 4)); - } - } - - pub fn read_8(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_8(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 8)); + pub fn read(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { + if self.enabled() && AsanGiovese::is_invalid_access::(qemu, addr) { + self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, N)); } } pub fn read_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) { - if self.enabled() && AsanGiovese::is_invalid_access(qemu, addr, size) { + if self.enabled() && AsanGiovese::is_invalid_access_n(qemu, addr, size) { self.rt .report_or_crash(qemu, pc, AsanError::Read(addr, size)); } } - pub fn write_1(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_1(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 1)); - } - } - - pub fn write_2(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_2(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 2)); - } - } - - pub fn write_4(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_4(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 4)); - } - } - - pub fn write_8(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { - if self.enabled() && AsanGiovese::is_invalid_access_8(qemu, addr) { - self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 8)); + pub fn write(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) { + if self.enabled() && AsanGiovese::is_invalid_access::(qemu, addr) { + self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, N)); } } pub fn write_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) { - if self.enabled() && AsanGiovese::is_invalid_access(qemu, addr, size) { + if self.enabled() && AsanGiovese::is_invalid_access_n(qemu, addr, size) { self.rt .report_or_crash(qemu, pc, AsanError::Write(addr, size)); } @@ -929,47 +819,108 @@ where type ModuleAddressFilter = StdAddressFilter; const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) + fn pre_qemu_init( + &mut self, + emulator_modules: &mut EmulatorModules, + qemu_params: &mut QemuParams, + ) where + ET: EmulatorModuleTuple, + { + let mut args: Vec = qemu_params.to_cli(); + + let current = env::current_exe().unwrap(); + let asan_lib = fs::canonicalize(current) + .unwrap() + .parent() + .unwrap() + .join("libqasan.so"); + let asan_lib = asan_lib + .to_str() + .expect("The path to the asan lib is invalid") + .to_string(); + let add_asan = + |e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..]; + + // TODO: adapt since qemu does not take envp anymore as parameter + let mut added = false; + for (k, v) in &mut self.env { + if k == "QEMU_SET_ENV" { + let mut new_v = vec![]; + for e in v.split(',') { + if e.starts_with("LD_PRELOAD=") { + added = true; + new_v.push(add_asan(e)); + } else { + new_v.push(e.to_string()); + } + } + *v = new_v.join(","); + } + } + for i in 0..args.len() { + if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") { + added = true; + args[i + 1] = add_asan(&args[i + 1]); + } + } + + if !added { + args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib); + args.insert(1, "-E".into()); + } + + unsafe { + AsanGiovese::init(&mut self.rt, emulator_modules.hooks().qemu_hooks()); + } + + *qemu_params = QemuParams::Cli(args); + } + + fn post_qemu_init(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { - emulator_modules.syscalls(Hook::Function(qasan_fake_syscall::)); + emulator_modules.pre_syscalls(Hook::Function(qasan_fake_syscall::)); if self.rt.error_callback.is_some() { emulator_modules.crash_function(oncrash_asan::); } } - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { 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_asan::), + Hook::Function(trace_read_asan::), + Hook::Function(trace_read_asan::), + Hook::Function(trace_read_asan::), Hook::Function(trace_read_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_asan::), + Hook::Function(trace_write_asan::), + Hook::Function(trace_write_asan::), + Hook::Function(trace_write_asan::), Hook::Function(trace_write_n_asan::), ); } else { // 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_asan_snapshot::), + Hook::Function(trace_write_asan_snapshot::), + Hook::Function(trace_write_asan_snapshot::), + Hook::Function(trace_write_asan_snapshot::), Hook::Function(trace_write_n_asan_snapshot::), ); } @@ -977,21 +928,23 @@ where fn pre_exec( &mut self, - emulator_modules: &mut EmulatorModules, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, ) where ET: EmulatorModuleTuple, { if self.empty { - self.rt.snapshot(emulator_modules.qemu()); + self.rt.snapshot(qemu); self.empty = false; } } fn post_exec( &mut self, - emulator_modules: &mut EmulatorModules, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, _observers: &mut OT, @@ -1000,7 +953,7 @@ where OT: ObserversTuple, ET: EmulatorModuleTuple, { - if self.reset(emulator_modules.qemu()) == AsanRollback::HasLeaks { + if self.reset(qemu) == AsanRollback::HasLeaks { *exit_kind = ExitKind::Crash; } } @@ -1014,18 +967,21 @@ where } } -pub fn oncrash_asan(emulator_modules: &mut EmulatorModules, target_sig: i32) -where +pub fn oncrash_asan( + qemu: Qemu, + emulator_modules: &mut EmulatorModules, + target_sig: i32, +) where ET: EmulatorModuleTuple, S: Unpin + UsesInput, { - 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( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -1044,7 +1000,8 @@ where } } -pub fn trace_read1_asan( +pub fn trace_read_asan( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1053,54 +1010,12 @@ pub fn trace_read1_asan( ET: EmulatorModuleTuple, S: Unpin + UsesInput, { - 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( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - 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( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - 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( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.read_8(qemu, id as GuestAddr, addr); + h.read::(qemu, id as GuestAddr, addr); } pub fn trace_read_n_asan( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1110,54 +1025,12 @@ pub fn trace_read_n_asan( S: Unpin + UsesInput, ET: EmulatorModuleTuple, { - 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( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - 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( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - 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( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - 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( +pub fn trace_write_asan( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1166,12 +1039,12 @@ pub fn trace_write8_asan( S: Unpin + UsesInput, ET: EmulatorModuleTuple, { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); - h.write_8(qemu, id as GuestAddr, addr); + h.write::(qemu, id as GuestAddr, addr); } pub fn trace_write_n_asan( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1181,12 +1054,12 @@ pub fn trace_write_n_asan( S: Unpin + UsesInput, ET: EmulatorModuleTuple, { - 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( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -1205,61 +1078,8 @@ where } } -pub fn trace_write1_asan_snapshot( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - if id != 0 { - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.write_1(qemu, id as GuestAddr, addr); - } - let h = emulator_modules.get_mut::().unwrap(); - h.access(addr, 1); -} - -pub fn trace_write2_asan_snapshot( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - if id != 0 { - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.write_2(qemu, id as GuestAddr, addr); - } - let h = emulator_modules.get_mut::().unwrap(); - h.access(addr, 2); -} - -pub fn trace_write4_asan_snapshot( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - id: u64, - addr: GuestAddr, -) where - S: Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - if id != 0 { - let qemu = emulator_modules.qemu(); - let h = emulator_modules.get_mut::().unwrap(); - h.write_4(qemu, id as GuestAddr, addr); - } - let h = emulator_modules.get_mut::().unwrap(); - h.access(addr, 4); -} - -pub fn trace_write8_asan_snapshot( +pub fn trace_write_asan_snapshot( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1269,15 +1089,15 @@ pub fn trace_write8_asan_snapshot( ET: EmulatorModuleTuple, { if id != 0 { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); - h.write_8(qemu, id as GuestAddr, addr); + h.write::(qemu, id as GuestAddr, addr); } let h = emulator_modules.get_mut::().unwrap(); - h.access(addr, 8); + h.access(addr, N); } pub fn trace_write_n_asan_snapshot( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, @@ -1288,7 +1108,6 @@ pub fn trace_write_n_asan_snapshot( ET: EmulatorModuleTuple, { if id != 0 { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); h.read_n(qemu, id as GuestAddr, addr, size); } @@ -1298,6 +1117,7 @@ pub fn trace_write_n_asan_snapshot( #[allow(clippy::too_many_arguments)] pub fn qasan_fake_syscall( + qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, sys_num: i32, @@ -1315,7 +1135,6 @@ where ET: EmulatorModuleTuple, { if sys_num == QASAN_FAKESYS_NR { - let qemu = emulator_modules.qemu(); let h = emulator_modules.get_mut::().unwrap(); match QasanAction::try_from(a0).expect("Invalid QASan action number") { QasanAction::CheckLoad => { diff --git a/libafl_qemu/src/modules/usermode/asan_guest.rs b/libafl_qemu/src/modules/usermode/asan_guest.rs index 567d98048e..3f3c67bd31 100644 --- a/libafl_qemu/src/modules/usermode/asan_guest.rs +++ b/libafl_qemu/src/modules/usermode/asan_guest.rs @@ -15,86 +15,11 @@ use crate::sys::libafl_tcg_gen_asan; use crate::{ emu::EmulatorModules, modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter}, - qemu::{Hook, MemAccessInfo, Qemu, QemuInitError}, + qemu::{Hook, MemAccessInfo, Qemu}, sys::TCGTemp, + QemuParams, }; -static mut ASAN_GUEST_INITED: bool = false; - -pub fn init_qemu_with_asan_guest( - args: &mut Vec, - env: &mut [(String, String)], -) -> Result<(Qemu, String), QemuInitError> { - let current = env::current_exe().unwrap(); - let asan_lib = fs::canonicalize(current) - .unwrap() - .parent() - .unwrap() - .join("libgasan.so"); - - let asan_lib = env::var_os("CUSTOM_ASAN_PATH") - .map_or(asan_lib, |x| PathBuf::from(x.to_string_lossy().to_string())); - - assert!( - asan_lib.as_path().exists(), - "The ASAN library doesn't exist: {asan_lib:#?}" - ); - - let asan_lib = asan_lib - .to_str() - .expect("The path to the asan lib is invalid") - .to_string(); - - println!("Loading ASAN: {asan_lib:}"); - - let add_asan = - |e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..]; - - let mut added = false; - for (k, v) in &mut *env { - if k == "QEMU_SET_ENV" { - let mut new_v = vec![]; - for e in v.split(',') { - if e.starts_with("LD_PRELOAD=") { - added = true; - new_v.push(add_asan(e)); - } else { - new_v.push(e.to_string()); - } - } - *v = new_v.join(","); - } - } - for i in 0..args.len() { - if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") { - added = true; - args[i + 1] = add_asan(&args[i + 1]); - } - } - - if !added { - args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib); - args.insert(1, "-E".into()); - } - - if env::var("QASAN_DEBUG").is_ok() { - args.push("-E".into()); - args.push("QASAN_DEBUG=1".into()); - } - - if env::var("QASAN_LOG").is_ok() { - args.push("-E".into()); - args.push("QASAN_LOG=1".into()); - } - - unsafe { - ASAN_GUEST_INITED = true; - } - - let emu = Qemu::init(args)?; - Ok((emu, asan_lib)) -} - #[derive(Clone)] struct QemuAsanGuestMapping { start: GuestAddr, @@ -119,8 +44,10 @@ impl From<&MapInfo> for QemuAsanGuestMapping { #[derive(Debug)] pub struct AsanGuestModule { + env: Vec<(String, String)>, filter: F, - mappings: Vec, + mappings: Option>, + asan_lib: Option, } #[cfg(any( @@ -152,8 +79,8 @@ impl AsanGuestModule { impl AsanGuestModule { #[must_use] - pub fn default(qemu: Qemu, asan: &str) -> Self { - Self::new(qemu, asan, StdAddressFilter::default()) + pub fn default(env: &[(String, String)]) -> Self { + Self::new(env, StdAddressFilter::default()) } } @@ -162,41 +89,13 @@ where F: AddressFilter, { #[must_use] - pub fn new(qemu: Qemu, asan: &str, filter: F) -> Self { - for mapping in qemu.mappings() { - println!("mapping: {mapping:#?}"); + pub fn new(env: &[(String, String)], filter: F) -> Self { + Self { + env: env.to_vec(), + filter, + mappings: None, + asan_lib: None, } - - let mappings = qemu - .mappings() - .map(|m| QemuAsanGuestMapping::from(&m)) - .collect::>(); - - for mapping in &mappings { - println!("guest mapping: {mapping:#?}"); - } - - mappings - .iter() - .find(|m| m.start <= Self::HIGH_SHADOW_START && m.end > Self::HIGH_SHADOW_END) - .expect("HighShadow not found, confirm ASAN DSO is loaded in the guest"); - - mappings - .iter() - .find(|m| m.start <= Self::LOW_SHADOW_START && m.end > Self::LOW_SHADOW_END) - .expect("LowShadow not found, confirm ASAN DSO is loaded in the guest"); - - let mappings = mappings - .iter() - .filter(|m| m.path == asan) - .cloned() - .collect::>(); - - for mapping in &mappings { - println!("asan mapping: {mapping:#?}"); - } - - Self { filter, mappings } } #[must_use] @@ -206,6 +105,7 @@ where } fn gen_readwrite_guest_asan( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, @@ -223,8 +123,15 @@ where } /* Don't sanitize the sanitizer! */ - if h.mappings.iter().any(|m| m.start <= pc && pc < m.end) { - return None; + unsafe { + if h.mappings + .as_mut() + .unwrap_unchecked() + .iter() + .any(|m| m.start <= pc && pc < m.end) + { + return None; + } } let size = info.size(); @@ -244,6 +151,7 @@ where unsafe fn libafl_tcg_gen_asan(addr: *mut TCGTemp, size: usize) {} fn guest_trace_error_asan( + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, @@ -256,6 +164,7 @@ fn guest_trace_error_asan( } fn guest_trace_error_n_asan( + _qemu: Qemu, _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, @@ -275,9 +184,127 @@ where { type ModuleAddressFilter = F; - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) + fn pre_qemu_init( + &mut self, + _emulator_modules: &mut EmulatorModules, + qemu_params: &mut QemuParams, + ) where + ET: EmulatorModuleTuple, + { + let mut args = qemu_params.to_cli(); + + let current = env::current_exe().unwrap(); + let asan_lib = fs::canonicalize(current) + .unwrap() + .parent() + .unwrap() + .join("libgasan.so"); + + let asan_lib = env::var_os("CUSTOM_ASAN_PATH") + .map_or(asan_lib, |x| PathBuf::from(x.to_string_lossy().to_string())); + + assert!( + asan_lib.as_path().exists(), + "The ASAN library doesn't exist: {asan_lib:#?}" + ); + + let asan_lib = asan_lib + .to_str() + .expect("The path to the asan lib is invalid") + .to_string(); + + println!("Loading ASAN: {asan_lib:}"); + + let add_asan = + |e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..]; + + let mut added = false; + for (k, v) in &mut self.env { + if k == "QEMU_SET_ENV" { + let mut new_v = vec![]; + for e in v.split(',') { + if e.starts_with("LD_PRELOAD=") { + added = true; + new_v.push(add_asan(e)); + } else { + new_v.push(e.to_string()); + } + } + *v = new_v.join(","); + } + } + for i in 0..args.len() { + if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") { + added = true; + args[i + 1] = add_asan(&args[i + 1]); + } + } + + if !added { + args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib); + args.insert(1, "-E".into()); + } + + if env::var("QASAN_DEBUG").is_ok() { + args.push("-E".into()); + args.push("QASAN_DEBUG=1".into()); + } + + if env::var("QASAN_LOG").is_ok() { + args.push("-E".into()); + args.push("QASAN_LOG=1".into()); + } + + *qemu_params = QemuParams::Cli(args); + + self.asan_lib = Some(asan_lib); + } + + fn post_qemu_init(&mut self, qemu: Qemu, _emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, + { + for mapping in qemu.mappings() { + println!("mapping: {mapping:#?}"); + } + + let mappings = qemu + .mappings() + .map(|m| QemuAsanGuestMapping::from(&m)) + .collect::>(); + + for mapping in &mappings { + println!("guest mapping: {mapping:#?}"); + } + + mappings + .iter() + .find(|m| m.start <= Self::HIGH_SHADOW_START && m.end > Self::HIGH_SHADOW_END) + .expect("HighShadow not found, confirm ASAN DSO is loaded in the guest"); + + mappings + .iter() + .find(|m| m.start <= Self::LOW_SHADOW_START && m.end > Self::LOW_SHADOW_END) + .expect("LowShadow not found, confirm ASAN DSO is loaded in the guest"); + + let mappings = mappings + .iter() + .filter(|m| &m.path == self.asan_lib.as_ref().unwrap()) + .cloned() + .collect::>(); + + for mapping in &mappings { + println!("asan mapping: {mapping:#?}"); + } + } + + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where + ET: EmulatorModuleTuple, S: Unpin + UsesInput, { emulator_modules.reads( diff --git a/libafl_qemu/src/modules/usermode/injections.rs b/libafl_qemu/src/modules/usermode/injections.rs index 9d62e7b017..9de76866e2 100644 --- a/libafl_qemu/src/modules/usermode/injections.rs +++ b/libafl_qemu/src/modules/usermode/injections.rs @@ -211,12 +211,15 @@ impl InjectionModule { }) } - fn on_call_check(emulator_modules: &mut EmulatorModules, id: usize, parameter: u8) - where + fn on_call_check( + qemu: Qemu, + 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() @@ -262,18 +265,21 @@ where { type ModuleAddressFilter = NopAddressFilter; - fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { - emulator_modules.syscalls(Hook::Function(syscall_hook::)); + emulator_modules.pre_syscalls(Hook::Function(syscall_hook::)); } - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where + fn first_exec( + &mut self, + qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where ET: EmulatorModuleTuple, { - let qemu = emulator_modules.qemu(); let mut libs: Vec = Vec::new(); for region in qemu.mappings() { @@ -324,8 +330,8 @@ where for hook_addr in hook_addrs { emulator_modules.instructions( hook_addr, - Hook::Closure(Box::new(move |hooks, _state, _guest_addr| { - Self::on_call_check(hooks, id, param); + Hook::Closure(Box::new(move |qemu, hooks, _state, _guest_addr| { + Self::on_call_check(qemu, hooks, id, param); })), true, ); @@ -346,6 +352,7 @@ where #[allow(clippy::too_many_arguments)] fn syscall_hook( // Our instantiated [`EmulatorModules`] + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, // Syscall number diff --git a/libafl_qemu/src/modules/usermode/mod.rs b/libafl_qemu/src/modules/usermode/mod.rs index 1d9cc503d2..93a8f5247c 100644 --- a/libafl_qemu/src/modules/usermode/mod.rs +++ b/libafl_qemu/src/modules/usermode/mod.rs @@ -11,9 +11,9 @@ pub use snapshot::{IntervalSnapshotFilter, SnapshotModule}; #[cfg(not(cpu_target = "hexagon"))] pub mod asan; #[cfg(not(cpu_target = "hexagon"))] -pub use asan::{init_qemu_with_asan, AsanModule}; +pub use 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}; +pub use asan_guest::AsanGuestModule; diff --git a/libafl_qemu/src/modules/usermode/snapshot.rs b/libafl_qemu/src/modules/usermode/snapshot.rs index 6fbe33e541..27450427ac 100644 --- a/libafl_qemu/src/modules/usermode/snapshot.rs +++ b/libafl_qemu/src/modules/usermode/snapshot.rs @@ -675,7 +675,7 @@ where { type ModuleAddressFilter = NopAddressFilter; - fn post_qemu_init(&self, emulator_modules: &mut EmulatorModules) + fn post_qemu_init(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { @@ -692,23 +692,24 @@ where } if !self.accurate_unmap { - emulator_modules.syscalls(Hook::Function(filter_mmap_snapshot::)); + emulator_modules.pre_syscalls(Hook::Function(filter_mmap_snapshot::)); } - emulator_modules.after_syscalls(Hook::Function(trace_mmap_snapshot::)); + emulator_modules.post_syscalls(Hook::Function(trace_mmap_snapshot::)); } fn pre_exec( &mut self, - emulator_modules: &mut EmulatorModules, + qemu: Qemu, + _emulator_modules: &mut EmulatorModules, _state: &mut S, _input: &S::Input, ) where ET: EmulatorModuleTuple, { if self.empty { - self.snapshot(emulator_modules.qemu()); + self.snapshot(qemu); } else { - self.reset(emulator_modules.qemu()); + self.reset(qemu); } } @@ -722,6 +723,7 @@ where } pub fn trace_write_snapshot( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, @@ -735,6 +737,7 @@ pub fn trace_write_snapshot( } pub fn trace_write_n_snapshot( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, @@ -751,6 +754,7 @@ pub fn trace_write_n_snapshot( #[allow(clippy::too_many_arguments)] #[allow(non_upper_case_globals)] pub fn filter_mmap_snapshot( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, sys_num: i32, @@ -779,6 +783,7 @@ where #[allow(clippy::too_many_arguments, clippy::too_many_lines)] #[allow(non_upper_case_globals)] pub fn trace_mmap_snapshot( + _qemu: Qemu, emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, result: GuestAddr, diff --git a/libafl_qemu/src/qemu/config.rs b/libafl_qemu/src/qemu/config.rs index b17b3e5913..5fb0ed4b3d 100644 --- a/libafl_qemu/src/qemu/config.rs +++ b/libafl_qemu/src/qemu/config.rs @@ -2,19 +2,12 @@ use core::{ fmt, fmt::{Display, Formatter}, }; -use std::{ - path::{Path, PathBuf}, - sync::OnceLock, -}; +use std::path::{Path, PathBuf}; +use derive_builder::Builder; use getset::Getters; use libafl_derive; use strum_macros; -use typed_builder::TypedBuilder; - -use crate::{Qemu, QemuInitError}; - -pub(super) static QEMU_CONFIG: OnceLock = OnceLock::new(); #[cfg(feature = "systemmode")] #[derive(Debug, strum_macros::Display, Clone)] @@ -44,7 +37,7 @@ pub enum DiskImageFileFormat { Raw, } -#[derive(Debug, Clone, Default, TypedBuilder)] +#[derive(Debug, Clone, Default, Builder)] pub struct Drive { #[builder(default, setter(strip_option, into))] file: Option, @@ -54,6 +47,13 @@ pub struct Drive { interface: Option, } +impl Drive { + #[must_use] + pub fn builder() -> DriveBuilder { + DriveBuilder::default() + } +} + impl Display for Drive { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "-drive")?; @@ -303,16 +303,13 @@ impl> From for Program { } } -#[derive(Debug, Clone, libafl_derive::Display, TypedBuilder, Getters)] -#[builder(build_method(into = Result), builder_method(vis = "pub(crate)", - doc = "Since Qemu is a zero sized struct, this is not a completely standard builder pattern. \ - The Qemu configuration is not stored in the Qemu struct after build() but in QEMU_CONFIG \ - Therefore, to use the derived builder and avoid boilerplate a builder for QemuConfig is \ - derived. \ - The QemuConfig::builder is called in Qemu::builder() which is the only place where it should \ - be called, in this way the one to one matching of Qemu and QemuConfig is enforced. Therefore \ - its visibility is pub(crate)"))] +/// Programmatic configurator for QEMU. +/// +/// It is supposed to be an equivalent to QEMU's CLI usual configuration, usable in a more +/// programmatic way and following the builder pattern. +#[derive(Debug, Clone, libafl_derive::Display, Builder, Getters)] #[getset(get = "pub")] +#[builder(pattern = "owned")] pub struct QemuConfig { #[cfg(feature = "systemmode")] #[builder(default, setter(strip_option))] @@ -350,40 +347,27 @@ pub struct QemuConfig { program: Program, } // Adding something here? Please leave Program as the last field -impl From for Result { - /// This method is necessary to make the API resemble a typical builder pattern, i.e. - /// `Qemu::builder().foo(bar).build()`, while still leveraging `TypedBuilder` for this - /// non-standard use case where `Qemu` doesn't store the configuration. - /// Internally, `TypedBuilder` is used to generate a builder for `QemuConfig`. - /// This `QemuConfig.into()` method is used by the derived `QemuConfigBuilder.build()` - /// to go from `QemuConfigBuilder` to `QemuConfig`, and finally to `Qemu` in one fn. - /// - /// # Errors - /// returns `QemuInitError` if the Qemu initialization fails, including cases where Qemu has - /// already been initialized. - fn from(config: QemuConfig) -> Self { - let args = config - .to_string() - .split(' ') - .map(ToString::to_string) - .collect::>(); - let qemu = Qemu::init(&args)?; - QEMU_CONFIG - .set(config) - .map_err(|_| unreachable!("BUG: QEMU_CONFIG was already set but Qemu was not init!"))?; - Ok(qemu) +impl QemuConfig { + #[must_use] + pub fn builder() -> QemuConfigBuilder { + QemuConfigBuilder::default() } } #[cfg(test)] mod test { use super::*; + use crate::Qemu; #[test] #[cfg(feature = "usermode")] fn usermode() { let program = "/bin/pwd"; - let qemu = Qemu::builder().program("/bin/pwd").build().unwrap(); + let qemu_config = QemuConfig::builder() + .program("/bin/pwd") + .build() + .expect("QEMU config failed."); + let qemu = Qemu::init(qemu_config).unwrap(); let config = qemu.get_config().unwrap(); assert_eq!(config.to_string().trim(), program.trim()); } @@ -393,7 +377,8 @@ mod test { let drive = Drive::builder() .format(DiskImageFileFormat::Raw) .interface(DriveInterface::Ide) - .build(); + .build() + .expect("Drive builder failed."); assert_eq!(drive.to_string(), "-drive format=raw,if=ide"); } diff --git a/libafl_qemu/src/qemu/error.rs b/libafl_qemu/src/qemu/error.rs new file mode 100644 index 0000000000..5a4c240dab --- /dev/null +++ b/libafl_qemu/src/qemu/error.rs @@ -0,0 +1,161 @@ +use core::fmt; +use std::{convert::Infallible, fmt::Display}; + +use libafl_qemu_sys::{CPUStatePtr, GuestAddr}; + +use crate::{config::QemuConfigBuilderError, CallingConvention}; + +#[derive(Debug)] +pub enum QemuError { + Init(QemuInitError), + Exit(QemuExitError), + RW(QemuRWError), +} + +#[derive(Debug)] +pub enum QemuInitError { + MultipleInstances, + EmptyArgs, + ConfigurationError(QemuConfigBuilderError), + Infallible, + TooManyArgs(usize), +} + +impl From for QemuInitError { + fn from(_: Infallible) -> Self { + QemuInitError::Infallible + } +} + +#[derive(Debug, Clone)] +pub enum QemuExitError { + UnknownKind, // Exit reason was not NULL, but exit kind is unknown. Should never happen. + UnexpectedExit, // Qemu exited without going through an expected exit point. Can be caused by a crash for example. +} + +#[derive(Debug, Clone)] +pub enum QemuRWErrorKind { + Read, + Write, +} + +#[derive(Debug, Clone)] +pub enum QemuRWErrorCause { + WrongCallingConvention(CallingConvention, CallingConvention), // expected, given + WrongArgument(i32), + CurrentCpuNotFound, + Reg(i32), + WrongMemoryLocation(GuestAddr, usize), // addr, size +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct QemuRWError { + kind: QemuRWErrorKind, + cause: QemuRWErrorCause, + cpu: Option, // Only makes sense when cause != CurrentCpuNotFound +} + +impl std::error::Error for QemuInitError {} + +impl Display for QemuInitError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + QemuInitError::MultipleInstances => { + write!(f, "Only one instance of the QEMU Emulator is permitted") + } + QemuInitError::EmptyArgs => { + write!(f, "QEMU emulator args cannot be empty") + } + QemuInitError::ConfigurationError(config_error) => { + write!(f, "QEMU Configuration error: {config_error}") + } + QemuInitError::TooManyArgs(n) => { + write!( + f, + "Too many arguments passed to QEMU emulator ({n} > i32::MAX)" + ) + } + QemuInitError::Infallible => { + write!(f, "Infallible error, should never be reached.") + } + } + } +} + +impl From for libafl::Error { + fn from(err: QemuInitError) -> Self { + libafl::Error::runtime(format!("QEMU Init error: {err}")) + } +} + +impl From for libafl::Error { + fn from(err: QemuRWError) -> Self { + libafl::Error::runtime(format!("QEMU Runtime error: {err:?}")) + } +} + +impl QemuRWError { + #[must_use] + pub fn new(kind: QemuRWErrorKind, cause: QemuRWErrorCause, cpu: Option) -> Self { + Self { kind, cause, cpu } + } + + pub fn wrong_reg(kind: QemuRWErrorKind, reg: R, cpu: Option) -> Self + where + R: Into + Clone, + { + Self::new(kind, QemuRWErrorCause::Reg(reg.into()), cpu) + } + + pub fn wrong_mem_location( + kind: QemuRWErrorKind, + cpu: CPUStatePtr, + addr: GuestAddr, + size: usize, + ) -> Self { + Self::new( + kind, + QemuRWErrorCause::WrongMemoryLocation(addr, size), + Some(cpu), + ) + } + + #[must_use] + pub fn current_cpu_not_found(kind: QemuRWErrorKind) -> Self { + Self::new(kind, QemuRWErrorCause::CurrentCpuNotFound, None) + } + + #[must_use] + pub fn new_argument_error(kind: QemuRWErrorKind, reg_id: i32) -> Self { + Self::new(kind, QemuRWErrorCause::WrongArgument(reg_id), None) + } + + pub fn check_conv( + kind: QemuRWErrorKind, + expected_conv: CallingConvention, + given_conv: CallingConvention, + ) -> Result<(), Self> { + if expected_conv != given_conv { + return Err(Self::new( + kind, + QemuRWErrorCause::WrongCallingConvention(expected_conv, given_conv), + None, + )); + } + + Ok(()) + } +} + +impl From for libafl::Error { + fn from(qemu_error: QemuError) -> Self { + libafl::Error::runtime(qemu_error) + } +} + +impl From for String { + fn from(qemu_error: QemuError) -> Self { + format!("LibAFL QEMU Error: {qemu_error:?}") + } +} diff --git a/libafl_qemu/src/qemu/hooks.rs b/libafl_qemu/src/qemu/hooks.rs index 4165742d17..3f37465bf0 100644 --- a/libafl_qemu/src/qemu/hooks.rs +++ b/libafl_qemu/src/qemu/hooks.rs @@ -100,16 +100,16 @@ impl Hook { } } -macro_rules! create_wrapper { +macro_rules! create_pre_init_wrapper { ($name:ident, ($($param:ident : $param_type:ty),*)) => { paste::paste! { - pub extern "C" fn [](hook: &mut c_void, $($param: $param_type),*) + pub extern "C" fn [](hook: &mut (), $($param: $param_type),*) where S: UsesInput + Unpin, { unsafe { let modules = EmulatorModules::::emulator_modules_mut_unchecked(); - let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::(hook)); + let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::<()>(hook)); func(modules, inprocess_get_state::(), $($param),*); } } @@ -128,13 +128,13 @@ macro_rules! create_wrapper { }; ($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 + pub extern "C" fn [](hook: &mut (), $($param: $param_type),*) -> $ret_type where S: UsesInput + Unpin, { 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)); + let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::<()>(hook)); func(modules, inprocess_get_state::(), $($param),*) } } @@ -153,6 +153,63 @@ macro_rules! create_wrapper { }; } +macro_rules! create_wrapper { + ($name:ident, ($($param:ident : $param_type:ty),*)) => { + paste::paste! { + pub extern "C" fn [](hook: &mut (), $($param: $param_type),*) + where + S: UsesInput + Unpin, + { + unsafe { + let qemu = Qemu::get_unchecked(); + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::<()>(hook)); + func(qemu, modules, inprocess_get_state::(), $($param),*); + } + } + + pub extern "C" fn [](hook: &mut FatPtr, $($param: $param_type),*) + where + S: Unpin + UsesInput, + { + unsafe { + let qemu = Qemu::get_unchecked(); + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: &mut Box, Option<&mut S>, $($param_type),*)> = &mut *(ptr::from_mut::(hook) as *mut Box, Option<&mut S>, $($param_type),*)>); + func(qemu, modules, inprocess_get_state::(), $($param),*); + } + } + } + }; + ($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty) => { + paste::paste! { + pub extern "C" fn [](hook: &mut (), $($param: $param_type),*) -> $ret_type + where + S: UsesInput + Unpin, + { + unsafe { + let qemu = Qemu::get_unchecked(); + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::<()>(hook)); + func(qemu, modules, inprocess_get_state::(), $($param),*) + } + } + + pub extern "C" fn [](hook: &mut FatPtr, $($param: $param_type),*) -> $ret_type + where + S: UsesInput + Unpin, + { + unsafe { + let qemu = Qemu::get_unchecked(); + let modules = EmulatorModules::::emulator_modules_mut_unchecked(); + let func: &mut Box, Option<&mut S>, $($param_type),*) -> $ret_type> = &mut *(ptr::from_mut::(hook) as *mut Box, Option<&mut S>, $($param_type),*) -> $ret_type>); + func(qemu, modules, inprocess_get_state::(), $($param),*) + } + } + } + }; +} + macro_rules! create_pre_exec_wrapper { ($name:ident, ($($param:ident : $param_type:ty),*), $hook_id:ident) => { paste::paste! { @@ -161,21 +218,22 @@ macro_rules! create_pre_exec_wrapper { S: UsesInput + Unpin, { unsafe { + let qemu = Qemu::get_unchecked(); let modules = EmulatorModules::::emulator_modules_mut_unchecked(); match &mut hook.pre_run { HookRepr::Function(ptr) => { - let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(*ptr); - func(modules, inprocess_get_state::(), $($param),*) + func(qemu, modules, inprocess_get_state::(), $($param),*) } HookRepr::Closure(ptr) => { let func: &mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*), > = &mut *(ptr::from_mut::(ptr) as *mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*), >); - func(modules, inprocess_get_state::(), $($param),*) + func(qemu, modules, inprocess_get_state::(), $($param),*) } _ => (), } @@ -193,21 +251,22 @@ macro_rules! create_post_exec_wrapper { S: UsesInput + Unpin, { unsafe { + let qemu = Qemu::get_unchecked(); let modules = EmulatorModules::::emulator_modules_mut_unchecked(); match &mut hook.post_run { HookRepr::Function(ptr) => { - let func: fn(&mut EmulatorModules, Option<&mut S>, $($param_type),*) = + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(*ptr); - func(modules, inprocess_get_state::(), $($param),*); + func(qemu, modules, inprocess_get_state::(), $($param),*); } HookRepr::Closure(ptr) => { let func: &mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*), > = &mut *(ptr::from_mut::(ptr) as *mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*), >); - func(modules, inprocess_get_state::(), $($param),*); + func(qemu, modules, inprocess_get_state::(), $($param),*); } _ => (), } @@ -225,19 +284,20 @@ macro_rules! create_gen_wrapper { S: UsesInput + Unpin, { unsafe { + let qemu = Qemu::get_unchecked(); 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> = + let func: fn(Qemu, &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) + func(qemu, 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>, - > = &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*) -> Option<$ret_type>>); - func(modules, inprocess_get_state::(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id) + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) -> Option<$ret_type>, + > = &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*) -> Option<$ret_type>>); + func(qemu, modules, inprocess_get_state::(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id) } _ => 0, } @@ -255,18 +315,20 @@ macro_rules! create_post_gen_wrapper { S: UsesInput + Unpin, { unsafe { + let qemu = Qemu::get_unchecked(); 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),*) = + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(*ptr); - func(modules, inprocess_get_state::(), $($param),*); + func(qemu, modules, inprocess_get_state::(), $($param),*); } HookRepr::Closure(ptr) => { let func: &mut Box< - dyn FnMut(&mut EmulatorModules, Option<&mut S>, $($param_type),*), - > = &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*)>); - func(modules, inprocess_get_state::(), $($param),*); + dyn FnMut(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*), + > = &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*)>); + func(qemu, modules, inprocess_get_state::(), $($param),*); } _ => (), } @@ -284,16 +346,18 @@ macro_rules! create_exec_wrapper { S: UsesInput + Unpin, { unsafe { + let qemu = Qemu::get_unchecked(); 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),*); + let func: fn(Qemu, &mut EmulatorModules, Option<&mut S>, $($param_type),*) = transmute(*ptr); + func(qemu, modules, inprocess_get_state::(), $($param),*); } HookRepr::Closure(ptr) => { - let func: &mut Box, Option<&mut S>, $($param_type),*)> = - &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*)>); - func(modules, inprocess_get_state::(), $($param),*); + let func: &mut Box, Option<&mut S>, $($param_type),*)> = + &mut *(ptr::from_mut::(ptr) as *mut Box, Option<&mut S>, $($param_type),*)>); + func(qemu, modules, inprocess_get_state::(), $($param),*); } _ => (), } @@ -359,8 +423,8 @@ macro_rules! create_hook_types { // Instruction hook wrappers create_hook_types!( Instruction, - fn(&mut EmulatorModules, Option<&mut S>, GuestAddr), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, GuestAddr)>, + fn(Qemu, &mut EmulatorModules, Option<&mut S>, GuestAddr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr)>, extern "C" fn(*const (), pc: GuestAddr) ); create_hook_id!(Instruction, libafl_qemu_remove_instruction_hook, true); @@ -369,9 +433,9 @@ 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) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, cpu: CPUArchStatePtr, GuestAddr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr)>, + extern "C" fn(libafl_qemu_opaque: *const (), cpu: CPUArchStatePtr, pc: GuestAddr) ); create_hook_id!(Backdoor, libafl_qemu_remove_backdoor_hook, true); create_wrapper!(backdoor, (cpu: CPUArchStatePtr, pc: GuestAddr)); @@ -381,6 +445,7 @@ create_wrapper!(backdoor, (cpu: CPUArchStatePtr, pc: GuestAddr)); create_hook_types!( PreSyscall, fn( + Qemu, &mut EmulatorModules, Option<&mut S>, sys_num: i32, @@ -395,6 +460,7 @@ create_hook_types!( ) -> SyscallHookResult, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, i32, @@ -445,6 +511,7 @@ create_wrapper!( create_hook_types!( PostSyscall, fn( + Qemu, &mut EmulatorModules, Option<&mut S>, res: GuestAddr, @@ -460,6 +527,7 @@ create_hook_types!( ) -> GuestAddr, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&mut S>, GuestAddr, @@ -520,23 +588,23 @@ create_hook_types!( u32, ) -> bool, >, - extern "C" fn(*const (), env: CPUArchStatePtr, tid: u32) -> bool + extern "C" fn(libafl_qemu_opaque: *const (), env: CPUArchStatePtr, tid: u32) -> bool ); create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false); -create_wrapper!(new_thread, (env: CPUArchStatePtr, tid: u32), bool); +create_pre_init_wrapper!(new_thread, (env: CPUArchStatePtr, tid: u32), bool); // CPU Run hook wrappers create_hook_types!( CpuPreRun, - fn(&mut EmulatorModules, Option<&mut S>, cpu: CPUStatePtr), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, CPUStatePtr)>, - extern "C" fn(*const (), cpu: CPUStatePtr) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, cpu: CPUStatePtr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, CPUStatePtr)>, + extern "C" fn(libafl_qemu_opaque: *const (), cpu: CPUStatePtr) ); create_hook_types!( CpuPostRun, - fn(&mut EmulatorModules, Option<&mut S>, cpu: CPUStatePtr), - Box FnMut(&'a mut EmulatorModules, Option<&'a mut S>, CPUStatePtr)>, - extern "C" fn(*const (), cpu: CPUStatePtr) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, cpu: CPUStatePtr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, CPUStatePtr)>, + extern "C" fn(libafl_qemu_opaque: *const (), cpu: CPUStatePtr) ); create_hook_id!(CpuRun, libafl_qemu_remove_cpu_run_hook, false); create_pre_exec_wrapper!(cpu_run, (cpu: CPUStatePtr), CpuRunHookId); @@ -546,22 +614,29 @@ create_wrapper!(cpu_run, (cpu: CPUStatePtr)); // Edge hook wrappers create_hook_types!( EdgeGen, - fn(&mut EmulatorModules, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option, + fn( + Qemu, + &mut EmulatorModules, + Option<&mut S>, + src: GuestAddr, + dest: GuestAddr, + ) -> Option, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr, GuestAddr, ) -> Option, >, - extern "C" fn(*const (), src: GuestAddr, dest: GuestAddr) -> u64 + extern "C" fn(libafl_qemu_opaque: *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)>, - unsafe extern "C" fn(*const (), id: u64) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, u64)>, + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64) ); create_hook_id!(Edge, libafl_qemu_remove_edge_hook, true); create_gen_wrapper!(edge, (src: GuestAddr, dest: GuestAddr), u64, 1, EdgeHookId); @@ -570,27 +645,36 @@ 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, + fn(Qemu, &mut EmulatorModules, Option<&mut S>, pc: GuestAddr) -> Option, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr, ) -> Option, >, - unsafe extern "C" fn(*const (), pc: GuestAddr) -> u64 + unsafe extern "C" fn(libafl_qemu_opaque: *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) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, pc: GuestAddr, block_length: GuestUsize), + Box< + dyn for<'a> FnMut( + Qemu, + &'a mut EmulatorModules, + Option<&mut S>, + GuestAddr, + GuestUsize, + ), + >, + unsafe extern "C" fn(libafl_qemu_opaque: *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) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, u64)>, + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64) ); create_hook_id!(Block, libafl_qemu_remove_block_hook, true); @@ -602,7 +686,8 @@ create_exec_wrapper!(block, (id: u64), 0, 1, BlockHookId); create_hook_types!( ReadGen, fn( - qemu_modules: &mut EmulatorModules, + Qemu, + emulator_modules: &mut EmulatorModules, Option<&mut S>, pc: GuestAddr, addr: *mut TCGTemp, @@ -610,6 +695,7 @@ create_hook_types!( ) -> Option, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr, @@ -617,21 +703,33 @@ create_hook_types!( MemAccessInfo, ) -> Option, >, - unsafe extern "C" fn(*const (), pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo) -> u64 + unsafe extern "C" fn( + libafl_qemu_opaque: *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) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr)>, + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr) ); create_hook_types!( ReadExecN, - fn(&mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), + fn(Qemu, &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), + dyn for<'a> FnMut( + Qemu, + &'a mut EmulatorModules, + Option<&'a mut S>, + u64, + GuestAddr, + usize, + ), >, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize) + unsafe extern "C" fn(libafl_qemu_opaque: *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); @@ -651,6 +749,7 @@ create_exec_wrapper!( create_hook_types!( WriteGen, fn( + Qemu, &mut EmulatorModules, Option<&mut S>, pc: GuestAddr, @@ -659,6 +758,7 @@ create_hook_types!( ) -> Option, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr, @@ -666,21 +766,33 @@ create_hook_types!( MemAccessInfo, ) -> Option, >, - unsafe extern "C" fn(*const (), pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo) -> u64 + unsafe extern "C" fn( + libafl_qemu_opaque: *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) + fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr), + Box FnMut(Qemu, &'a mut EmulatorModules, Option<&'a mut S>, u64, GuestAddr)>, + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr) ); create_hook_types!( WriteExecN, - fn(&mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), + fn(Qemu, &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), + dyn for<'a> FnMut( + Qemu, + &'a mut EmulatorModules, + Option<&'a mut S>, + u64, + GuestAddr, + usize, + ), >, - unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize) + unsafe extern "C" fn(libafl_qemu_opaque: *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); @@ -699,16 +811,23 @@ create_exec_wrapper!( // Cmp hook wrappers create_hook_types!( CmpGen, - fn(&mut EmulatorModules, Option<&mut S>, pc: GuestAddr, size: usize) -> Option, + fn( + Qemu, + &mut EmulatorModules, + Option<&mut S>, + pc: GuestAddr, + size: usize, + ) -> Option, Box< dyn for<'a> FnMut( + Qemu, &'a mut EmulatorModules, Option<&'a mut S>, GuestAddr, usize, ) -> Option, >, - unsafe extern "C" fn(*const (), pc: GuestAddr, size: usize) -> u64 + unsafe extern "C" fn(libafl_qemu_opaque: *const (), pc: GuestAddr, size: usize) -> u64 ); pub type CmpExecHook = Hook< fn(&mut EmulatorModules, Option<&mut S>, id: u64, v0: SZ, v1: SZ), @@ -724,9 +843,9 @@ create_exec_wrapper!(cmp, (id: u64, v0: u64, v1: u64), 3, 4, CmpHookId); // Crash hook wrappers #[cfg(feature = "usermode")] -pub type CrashHookFn = fn(&mut EmulatorModules, i32); +pub type CrashHookFn = fn(Qemu, &mut EmulatorModules, i32); #[cfg(feature = "usermode")] -pub type CrashHookClosure = Box, i32)>; +pub type CrashHookClosure = Box, i32)>; /// The thin wrapper around QEMU hooks. /// It is considered unsafe to use it directly. diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index d9fc7ce657..28157315ca 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -14,6 +14,7 @@ use std::{ mem::MaybeUninit, ops::Range, pin::Pin, + sync::OnceLock, }; use libafl_bolts::os::unix_signals::Signal; @@ -31,7 +32,12 @@ use strum::IntoEnumIterator; use crate::{GuestAddrKind, GuestReg, Regs}; pub mod config; -use config::{QemuConfig, QemuConfigBuilder, QEMU_CONFIG}; +use config::QemuConfig; + +pub mod error; +pub use error::{ + QemuError, QemuExitError, QemuInitError, QemuRWError, QemuRWErrorCause, QemuRWErrorKind, +}; #[cfg(feature = "usermode")] mod usermode; @@ -47,32 +53,37 @@ pub use systemmode::*; mod hooks; pub use hooks::*; +use crate::config::QemuConfigBuilder; + static mut QEMU_IS_INITIALIZED: bool = false; -#[derive(Debug)] -pub enum QemuError { - Init(QemuInitError), - Exit(QemuExitError), - RW(QemuRWError), -} +pub(super) static QEMU_CONFIG: OnceLock = OnceLock::new(); -impl From for libafl::Error { - fn from(qemu_error: QemuError) -> Self { - libafl::Error::runtime(qemu_error) - } -} +#[allow(clippy::vec_box)] +static mut GDB_COMMANDS: Vec> = Vec::new(); -impl From for String { - fn from(qemu_error: QemuError) -> Self { - format!("LibAFL QEMU Error: {qemu_error:?}") - } +pub trait HookId { + fn remove(&self, invalidate_block: bool) -> bool; } -#[derive(Debug)] -pub enum QemuInitError { - MultipleInstances, - EmptyArgs, - TooManyArgs(usize), +pub trait ArchExtras { + fn read_return_address(&self) -> Result; + fn write_return_address(&self, val: T) -> Result<(), QemuRWError> + where + T: Into; + fn read_function_argument( + &self, + conv: CallingConvention, + idx: u8, + ) -> Result; + fn write_function_argument( + &self, + conv: CallingConvention, + idx: i32, + val: T, + ) -> Result<(), QemuRWError> + where + T: Into; } #[derive(Debug, Clone)] @@ -90,81 +101,6 @@ pub enum QemuExitReason { Timeout, } -#[derive(Debug, Clone)] -pub enum QemuExitError { - UnknownKind, // Exit reason was not NULL, but exit kind is unknown. Should never happen. - UnexpectedExit, // Qemu exited without going through an expected exit point. Can be caused by a crash for example. -} - -#[derive(Debug, Clone)] -pub enum QemuRWErrorKind { - Read, - Write, -} - -#[derive(Debug, Clone)] -pub enum QemuRWErrorCause { - WrongCallingConvention(CallingConvention, CallingConvention), // expected, given - WrongArgument(i32), - CurrentCpuNotFound, - Reg(i32), - WrongMemoryLocation(GuestAddr, usize), // addr, size -} - -#[derive(Debug, Clone)] -#[allow(dead_code)] -pub struct QemuRWError { - kind: QemuRWErrorKind, - cause: QemuRWErrorCause, - cpu: Option, // Only makes sense when cause != CurrentCpuNotFound -} - -impl QemuRWError { - #[must_use] - pub fn new(kind: QemuRWErrorKind, cause: QemuRWErrorCause, cpu: Option) -> Self { - Self { kind, cause, cpu } - } - - pub fn wrong_mem_location( - kind: QemuRWErrorKind, - cpu: CPUStatePtr, - addr: GuestAddr, - size: usize, - ) -> Self { - Self::new( - kind, - QemuRWErrorCause::WrongMemoryLocation(addr, size), - Some(cpu), - ) - } - - #[must_use] - pub fn current_cpu_not_found(kind: QemuRWErrorKind) -> Self { - Self::new(kind, QemuRWErrorCause::CurrentCpuNotFound, None) - } - - #[must_use] - pub fn new_argument_error(kind: QemuRWErrorKind, reg_id: i32) -> Self { - Self::new(kind, QemuRWErrorCause::WrongArgument(reg_id), None) - } - - pub fn check_conv( - kind: QemuRWErrorKind, - expected_conv: CallingConvention, - given_conv: CallingConvention, - ) -> Result<(), Self> { - if expected_conv != given_conv { - return Err(Self::new( - kind, - QemuRWErrorCause::WrongCallingConvention(expected_conv, given_conv), - None, - )); - } - - Ok(()) - } -} - /// The thin wrapper around QEMU. /// It is considered unsafe to use it directly. /// Prefer using `Emulator` instead in case of doubt. @@ -173,6 +109,12 @@ pub struct Qemu { _private: (), } +#[derive(Clone, Debug)] +pub enum QemuParams { + Config(QemuConfig), + Cli(Vec), +} + #[derive(Debug, Clone)] pub struct QemuMemoryChunk { addr: GuestAddrKind, @@ -180,18 +122,6 @@ pub struct QemuMemoryChunk { cpu: Option, } -#[allow(clippy::vec_box)] -static mut GDB_COMMANDS: Vec> = Vec::new(); - -unsafe extern "C" fn gdb_cmd(data: *mut c_void, buf: *mut u8, len: usize) -> bool { - unsafe { - let closure = &mut *(data as *mut Box FnMut(Qemu, &'r str) -> bool>); - let cmd = std::str::from_utf8_unchecked(std::slice::from_raw_parts(buf, len)); - let qemu = Qemu::get_unchecked(); - closure(qemu, cmd) - } -} - #[derive(Debug, Clone)] pub enum QemuShutdownCause { None, @@ -224,37 +154,15 @@ pub enum CallingConvention { Cdecl, } -pub trait HookId { - fn remove(&self, invalidate_block: bool) -> bool; -} - #[derive(Debug)] pub struct HookData(u64); -impl std::error::Error for QemuInitError {} - -impl Display for QemuInitError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - QemuInitError::MultipleInstances => { - write!(f, "Only one instance of the QEMU Emulator is permitted") - } - QemuInitError::EmptyArgs => { - write!(f, "QEMU emulator args cannot be empty") - } - QemuInitError::TooManyArgs(n) => { - write!( - f, - "Too many arguments passed to QEMU emulator ({n} > i32::MAX)" - ) - } - } - } -} - -impl From for libafl::Error { - fn from(err: QemuInitError) -> Self { - libafl::Error::unknown(format!("{err}")) +unsafe extern "C" fn gdb_cmd(data: *mut c_void, buf: *mut u8, len: usize) -> bool { + unsafe { + let closure = &mut *(data as *mut Box FnMut(Qemu, &'r str) -> bool>); + let cmd = std::str::from_utf8_unchecked(std::slice::from_raw_parts(buf, len)); + let qemu = Qemu::get_unchecked(); + closure(qemu, cmd) } } @@ -269,6 +177,63 @@ impl Display for QemuExitReason { } } +impl From for QemuParams { + fn from(config: QemuConfig) -> Self { + QemuParams::Config(config) + } +} + +impl TryFrom for QemuParams { + type Error = QemuInitError; + + fn try_from(config_builder: QemuConfigBuilder) -> Result { + Ok(QemuParams::Config( + config_builder + .build() + .map_err(QemuInitError::ConfigurationError)?, + )) + } +} + +impl From<&[T]> for QemuParams +where + T: AsRef, +{ + fn from(cli: &[T]) -> Self { + QemuParams::Cli(cli.iter().map(|x| x.as_ref().into()).collect()) + } +} +impl From<&Vec> for QemuParams +where + T: AsRef, +{ + fn from(cli: &Vec) -> Self { + cli.as_slice().into() + } +} + +impl From> for QemuParams +where + T: AsRef, +{ + fn from(cli: Vec) -> Self { + (&cli).into() + } +} + +impl QemuParams { + pub fn to_cli(&self) -> Vec { + match self { + QemuParams::Config(cfg) => cfg + .to_string() + .split(' ') + .map(ToString::to_string) + .collect(), + QemuParams::Cli(cli) => cli.clone(), + } + } +} + impl MemAccessInfo { #[must_use] pub fn memop(&self) -> libafl_qemu_sys::MemOp { @@ -319,26 +284,6 @@ impl From for MemAccessInfo { } } -pub trait ArchExtras { - fn read_return_address(&self) -> Result; - fn write_return_address(&self, val: T) -> Result<(), QemuRWError> - where - T: Into; - fn read_function_argument( - &self, - conv: CallingConvention, - idx: u8, - ) -> Result; - fn write_function_argument( - &self, - conv: CallingConvention, - idx: i32, - val: T, - ) -> Result<(), QemuRWError> - where - T: Into; -} - #[allow(clippy::unused_self)] impl CPU { #[must_use] @@ -369,11 +314,11 @@ impl CPU { let mut val = MaybeUninit::uninit(); let success = libafl_qemu_read_reg(self.ptr, reg_id, val.as_mut_ptr() as *mut u8); if success == 0 { - Err(QemuRWError { - kind: QemuRWErrorKind::Write, - cause: QemuRWErrorCause::Reg(reg.into()), - cpu: Some(self.ptr), - }) + Err(QemuRWError::wrong_reg( + QemuRWErrorKind::Write, + reg, + Some(self.ptr), + )) } else { #[cfg(feature = "be")] return Ok(GuestReg::from_be(val.assume_init()).into()); @@ -398,11 +343,11 @@ impl CPU { let success = unsafe { libafl_qemu_write_reg(self.ptr, reg_id, &raw const val as *mut u8) }; if success == 0 { - Err(QemuRWError { - kind: QemuRWErrorKind::Write, - cause: QemuRWErrorCause::Reg(reg.into()), - cpu: Some(self.ptr), - }) + Err(QemuRWError::wrong_reg( + QemuRWErrorKind::Write, + reg, + Some(self.ptr), + )) } else { Ok(()) } @@ -574,14 +519,25 @@ impl From for HookData { #[allow(clippy::unused_self)] impl Qemu { - /// For more details about the parameters check - /// [the QEMU documentation](https://www.qemu.org/docs/master/about/). - pub fn builder() -> QemuConfigBuilder { - QemuConfig::builder() - } - #[allow(clippy::must_use_candidate, clippy::similar_names)] - pub fn init(args: &[String]) -> Result { + pub fn init(params: T) -> Result + where + T: Into, + { + let params: QemuParams = params.into(); + + match ¶ms { + QemuParams::Config(cfg) => { + QEMU_CONFIG + .set(cfg.clone()) + .map_err(|_| unreachable!("QEMU_CONFIG was already set but Qemu was not init!")) + .unwrap(); + } + QemuParams::Cli(_) => {} + }; + + let args = params.to_cli(); + if args.is_empty() { return Err(QemuInitError::EmptyArgs); } @@ -595,6 +551,7 @@ impl Qemu { if QEMU_IS_INITIALIZED { return Err(QemuInitError::MultipleInstances); } + QEMU_IS_INITIALIZED = true; } @@ -603,7 +560,7 @@ impl Qemu { let args: Vec = args .iter() - .map(|x| CString::new(x.clone()).unwrap()) + .map(|x| CString::new(AsRef::::as_ref(x)).unwrap()) .collect(); let mut argv: Vec<*const u8> = args.iter().map(|x| x.as_ptr() as *const u8).collect(); argv.push(ptr::null()); // argv is always null terminated. @@ -907,11 +864,7 @@ impl Qemu { impl ArchExtras for Qemu { fn read_return_address(&self) -> Result { self.current_cpu() - .ok_or(QemuRWError { - kind: QemuRWErrorKind::Read, - cause: QemuRWErrorCause::CurrentCpuNotFound, - cpu: None, - })? + .ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Read))? .read_return_address() } diff --git a/libafl_qemu/src/qemu/systemmode.rs b/libafl_qemu/src/qemu/systemmode.rs index c481ecd16f..ddb2bfcf15 100644 --- a/libafl_qemu/src/qemu/systemmode.rs +++ b/libafl_qemu/src/qemu/systemmode.rs @@ -16,8 +16,8 @@ use libc::EXIT_SUCCESS; use num_traits::Zero; use crate::{ - FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuMemoryChunk, QemuRWError, - QemuRWErrorCause, QemuRWErrorKind, QemuSnapshotCheckResult, CPU, + FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuMemoryChunk, QemuSnapshotCheckResult, + CPU, }; pub(super) extern "C" fn qemu_cleanup_atexit() { @@ -163,7 +163,7 @@ impl CPU { /// # Safety /// no check is done on the correctness of the operation. /// if a problem occurred during the operation, there will be no feedback - pub fn write_mem_unchecked(&self, addr: GuestAddr, buf: &[u8]) { + pub unsafe fn write_mem_unchecked(&self, addr: GuestAddr, buf: &[u8]) { // TODO use gdbstub's target_cpu_memory_rw_debug unsafe { libafl_qemu_sys::cpu_memory_rw_debug( diff --git a/libafl_sugar/src/qemu.rs b/libafl_sugar/src/qemu.rs index e0126c3b01..bb56a00461 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -118,7 +118,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_cli: &[String]) { let conf = match self.configuration.as_ref() { Some(name) => EventConfig::from_name(name), None => EventConfig::AlwaysUnique, @@ -240,7 +240,10 @@ where ExitKind::Ok }; - let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?; + let emulator = Emulator::empty() + .qemu_config(|_| qemu_cli.to_owned()) + .modules(modules) + .build()?; let executor = QemuExecutor::new( emulator, @@ -357,7 +360,10 @@ where ExitKind::Ok }; - let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?; + let emulator = Emulator::empty() + .qemu_config(|_| qemu_cli.to_owned()) + .modules(modules) + .build()?; let mut executor = QemuExecutor::new( emulator, @@ -476,7 +482,6 @@ pub mod pybind { use std::path::PathBuf; use libafl_bolts::core_affinity::Cores; - use libafl_qemu::qemu::pybind::Qemu; use pyo3::{prelude::*, types::PyBytes}; use crate::qemu; @@ -533,7 +538,7 @@ pub mod pybind { /// Run the fuzzer #[allow(clippy::needless_pass_by_value)] - pub fn run(&self, qemu: &Qemu, harness: PyObject) { + pub fn run(&self, qemu_cli: Vec, harness: PyObject) { qemu::QemuBytesCoverageSugar::builder() .input_dirs(&self.input_dirs) .output_dir(self.output_dir.clone()) @@ -552,7 +557,7 @@ pub mod pybind { .tokens_file(self.tokens_file.clone()) .iterations(self.iterations) .build() - .run(qemu.qemu); + .run(&qemu_cli); } }