From 4cb4b6df7748b34f233128ea54c1b1c5de4ad9e2 Mon Sep 17 00:00:00 2001 From: WorksButNotTested <62701594+WorksButNotTested@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:36:17 +0000 Subject: [PATCH] Change qemu_cmin to use snapshots (#2939) * Change qemu_cmin to use snapshots * Use features to support both fork and snapshot modes --------- Co-authored-by: Your Name --- fuzzers/binary_only/qemu_cmin/Cargo.toml | 4 +- fuzzers/binary_only/qemu_cmin/src/fuzzer.rs | 73 ++++++++++++++++++++- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/fuzzers/binary_only/qemu_cmin/Cargo.toml b/fuzzers/binary_only/qemu_cmin/Cargo.toml index f486c2337f..e208babda6 100644 --- a/fuzzers/binary_only/qemu_cmin/Cargo.toml +++ b/fuzzers/binary_only/qemu_cmin/Cargo.toml @@ -15,7 +15,9 @@ edition = "2021" debug = true [features] -default = ["std"] +default = ["std", "snapshot"] +fork = [] +snapshot = [] std = [] be = ["libafl_qemu/be"] arm = ["libafl_qemu/arm"] diff --git a/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs b/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs index 72ad553881..f9c63b5087 100644 --- a/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs @@ -2,12 +2,14 @@ //! #[cfg(feature = "i386")] use core::mem::size_of; +#[cfg(feature = "snapshot")] +use core::time::Duration; use std::{env, fmt::Write, io, path::PathBuf, process, ptr::NonNull}; use clap::{builder::Str, Parser}; use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, NopCorpus}, - events::{EventRestarter, SendExiting, SimpleRestartingEventManager}, + events::{SendExiting, SimpleRestartingEventManager}, executors::ExitKind, feedbacks::MaxMapFeedback, fuzzer::StdFuzzer, @@ -26,13 +28,20 @@ use libafl_bolts::{ tuples::tuple_list, AsSlice, AsSliceMut, }; +#[cfg(feature = "fork")] +use libafl_qemu::QemuForkExecutor; use libafl_qemu::{ elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, CallingConvention, - Emulator, GuestAddr, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuForkExecutor, - QemuShutdownCause, Regs, + Emulator, GuestAddr, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuShutdownCause, + Regs, }; +#[cfg(feature = "snapshot")] +use libafl_qemu::{modules::SnapshotModule, QemuExecutor}; use libafl_targets::{EDGES_MAP_DEFAULT_SIZE, EDGES_MAP_PTR}; +#[cfg(all(feature = "fork", feature = "snapshot"))] +compile_error!("Cannot enable both 'fork' and 'snapshot' features at the same time."); + #[derive(Default)] pub struct Version; @@ -130,10 +139,19 @@ pub fn fuzz() -> Result<(), Error> { )) }; + #[cfg(feature = "fork")] let modules = tuple_list!(StdEdgeCoverageChildModule::builder() .const_map_observer(edges_observer.as_mut()) .build()?); + #[cfg(feature = "snapshot")] + let modules = tuple_list!( + StdEdgeCoverageChildModule::builder() + .const_map_observer(edges_observer.as_mut()) + .build()?, + SnapshotModule::new() + ); + let emulator = Emulator::empty() .qemu_parameters(options.args) .modules(modules) @@ -198,6 +216,7 @@ pub fn fuzz() -> Result<(), Error> { let scheduler = QueueScheduler::new(); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + #[cfg(feature = "fork")] let mut harness = |_emulator: &mut Emulator<_, _, _, _, _, _, _>, input: &BytesInput| { let target = input.target_bytes(); let mut buf = target.as_slice(); @@ -232,6 +251,43 @@ pub fn fuzz() -> Result<(), Error> { ExitKind::Ok }; + #[cfg(feature = "snapshot")] + 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; + + unsafe { + qemu.write_mem(input_addr, buf).expect("qemu write failed."); + + qemu.write_reg(Regs::Pc, test_one_input_ptr).unwrap(); + qemu.write_reg(Regs::Sp, stack_ptr).unwrap(); + qemu.write_return_address(ret_addr).unwrap(); + qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr) + .unwrap(); + qemu.write_function_argument(CallingConvention::Cdecl, 1, len) + .unwrap(); + + match qemu.run() { + Ok(QemuExitReason::Breakpoint(_)) => {} + Ok(QemuExitReason::End(QemuShutdownCause::HostSignal( + Signal::SigInterrupt, + ))) => process::exit(0), + Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash, + _ => panic!("Unexpected QEMU exit."), + } + } + + ExitKind::Ok + }; + + #[cfg(feature = "fork")] let mut executor = QemuForkExecutor::new( emulator, &mut harness, @@ -243,6 +299,17 @@ pub fn fuzz() -> Result<(), Error> { core::time::Duration::from_millis(5000), )?; + #[cfg(feature = "snapshot")] + let mut executor = QemuExecutor::new( + emulator, + &mut harness, + tuple_list!(edges_observer), + &mut fuzzer, + &mut state, + &mut mgr, + Duration::from_millis(5000), + )?; + println!("Importing {} seeds...", files.len()); if state.must_load_initial_inputs() {