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 <you@example.com>
This commit is contained in:
WorksButNotTested 2025-02-11 17:36:17 +00:00 committed by GitHub
parent b7fcfdd192
commit 4cb4b6df77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 73 additions and 4 deletions

View File

@ -15,7 +15,9 @@ edition = "2021"
debug = true debug = true
[features] [features]
default = ["std"] default = ["std", "snapshot"]
fork = []
snapshot = []
std = [] std = []
be = ["libafl_qemu/be"] be = ["libafl_qemu/be"]
arm = ["libafl_qemu/arm"] arm = ["libafl_qemu/arm"]

View File

@ -2,12 +2,14 @@
//! //!
#[cfg(feature = "i386")] #[cfg(feature = "i386")]
use core::mem::size_of; use core::mem::size_of;
#[cfg(feature = "snapshot")]
use core::time::Duration;
use std::{env, fmt::Write, io, path::PathBuf, process, ptr::NonNull}; use std::{env, fmt::Write, io, path::PathBuf, process, ptr::NonNull};
use clap::{builder::Str, Parser}; use clap::{builder::Str, Parser};
use libafl::{ use libafl::{
corpus::{Corpus, InMemoryOnDiskCorpus, NopCorpus}, corpus::{Corpus, InMemoryOnDiskCorpus, NopCorpus},
events::{EventRestarter, SendExiting, SimpleRestartingEventManager}, events::{SendExiting, SimpleRestartingEventManager},
executors::ExitKind, executors::ExitKind,
feedbacks::MaxMapFeedback, feedbacks::MaxMapFeedback,
fuzzer::StdFuzzer, fuzzer::StdFuzzer,
@ -26,13 +28,20 @@ use libafl_bolts::{
tuples::tuple_list, tuples::tuple_list,
AsSlice, AsSliceMut, AsSlice, AsSliceMut,
}; };
#[cfg(feature = "fork")]
use libafl_qemu::QemuForkExecutor;
use libafl_qemu::{ use libafl_qemu::{
elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, CallingConvention, elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, CallingConvention,
Emulator, GuestAddr, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuForkExecutor, Emulator, GuestAddr, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuShutdownCause,
QemuShutdownCause, Regs, Regs,
}; };
#[cfg(feature = "snapshot")]
use libafl_qemu::{modules::SnapshotModule, QemuExecutor};
use libafl_targets::{EDGES_MAP_DEFAULT_SIZE, EDGES_MAP_PTR}; 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)] #[derive(Default)]
pub struct Version; pub struct Version;
@ -130,10 +139,19 @@ pub fn fuzz() -> Result<(), Error> {
)) ))
}; };
#[cfg(feature = "fork")]
let modules = tuple_list!(StdEdgeCoverageChildModule::builder() let modules = tuple_list!(StdEdgeCoverageChildModule::builder()
.const_map_observer(edges_observer.as_mut()) .const_map_observer(edges_observer.as_mut())
.build()?); .build()?);
#[cfg(feature = "snapshot")]
let modules = tuple_list!(
StdEdgeCoverageChildModule::builder()
.const_map_observer(edges_observer.as_mut())
.build()?,
SnapshotModule::new()
);
let emulator = Emulator::empty() let emulator = Emulator::empty()
.qemu_parameters(options.args) .qemu_parameters(options.args)
.modules(modules) .modules(modules)
@ -198,6 +216,7 @@ pub fn fuzz() -> Result<(), Error> {
let scheduler = QueueScheduler::new(); let scheduler = QueueScheduler::new();
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
#[cfg(feature = "fork")]
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _, _, _>, input: &BytesInput| { let mut harness = |_emulator: &mut Emulator<_, _, _, _, _, _, _>, input: &BytesInput| {
let target = input.target_bytes(); let target = input.target_bytes();
let mut buf = target.as_slice(); let mut buf = target.as_slice();
@ -232,6 +251,43 @@ pub fn fuzz() -> Result<(), Error> {
ExitKind::Ok 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( let mut executor = QemuForkExecutor::new(
emulator, emulator,
&mut harness, &mut harness,
@ -243,6 +299,17 @@ pub fn fuzz() -> Result<(), Error> {
core::time::Duration::from_millis(5000), 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()); println!("Importing {} seeds...", files.len());
if state.must_load_initial_inputs() { if state.must_load_initial_inputs() {