Add SnapshotModule to qemu_launcher (#2887)

* Add SnapshotModule to qemu_launcher

---------

Co-authored-by: Your Name <you@example.com>
This commit is contained in:
WorksButNotTested 2025-02-11 17:42:20 +00:00 committed by GitHub
parent 4cb4b6df77
commit 739156cb23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 167 additions and 27 deletions

View File

@ -6,20 +6,18 @@ use std::{
use clap::Parser; use clap::Parser;
#[cfg(feature = "simplemgr")] #[cfg(feature = "simplemgr")]
use libafl::events::SimpleEventManager; use libafl::events::{ClientDescription, SimpleEventManager};
#[cfg(not(feature = "simplemgr"))] #[cfg(not(feature = "simplemgr"))]
use libafl::events::{EventConfig, Launcher, MonitorTypedEventManager}; use libafl::events::{EventConfig, Launcher, MonitorTypedEventManager};
use libafl::{ use libafl::{
events::{ClientDescription, LlmpEventManagerBuilder},
monitors::{tui::TuiMonitor, Monitor, MultiMonitor}, monitors::{tui::TuiMonitor, Monitor, MultiMonitor},
Error, Error,
}; };
use libafl_bolts::{core_affinity::CoreId, current_time, llmp::LlmpBroker, tuples::tuple_list}; #[cfg(feature = "simplemgr")]
use libafl_bolts::core_affinity::CoreId;
use libafl_bolts::current_time;
#[cfg(not(feature = "simplemgr"))] #[cfg(not(feature = "simplemgr"))]
use libafl_bolts::{ use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider};
shmem::{ShMemProvider, StdShMemProvider},
staterestore::StateRestorer,
};
#[cfg(unix)] #[cfg(unix)]
use { use {
nix::unistd::dup, nix::unistd::dup,
@ -85,7 +83,7 @@ impl Fuzzer {
{ {
// The shared memory allocator // The shared memory allocator
#[cfg(not(feature = "simplemgr"))] #[cfg(not(feature = "simplemgr"))]
let mut shmem_provider = StdShMemProvider::new()?; let shmem_provider = StdShMemProvider::new()?;
/* If we are running in verbose, don't provide a replacement stdout, otherwise, use /dev/null */ /* If we are running in verbose, don't provide a replacement stdout, otherwise, use /dev/null */
#[cfg(not(feature = "simplemgr"))] #[cfg(not(feature = "simplemgr"))]
let stdout = if self.options.verbose { let stdout = if self.options.verbose {

View File

@ -6,19 +6,21 @@ use libafl::events::SimpleEventManager;
#[cfg(not(feature = "simplemgr"))] #[cfg(not(feature = "simplemgr"))]
use libafl::events::{LlmpRestartingEventManager, MonitorTypedEventManager}; use libafl::events::{LlmpRestartingEventManager, MonitorTypedEventManager};
use libafl::{ use libafl::{
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, corpus::{Corpus, HasCurrentCorpusId, InMemoryOnDiskCorpus, OnDiskCorpus},
events::{ClientDescription, EventRestarter}, events::{ClientDescription, EventRestarter},
executors::{Executor, ShadowExecutor}, executors::{Executor, ExitKind, ShadowExecutor},
feedback_or, feedback_or_fast, feedback_or, feedback_or_fast,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Evaluator, Fuzzer, StdFuzzer}, fuzzer::{Evaluator, Fuzzer, StdFuzzer},
inputs::BytesInput, inputs::{BytesInput, Input},
monitors::Monitor, monitors::Monitor,
mutators::{ mutators::{
havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations, StdMOptMutator, havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations, StdMOptMutator,
StdScheduledMutator, Tokens, StdScheduledMutator, Tokens,
}, },
observers::{CanTrack, HitcountsMapObserver, TimeObserver, VariableMapObserver}, observers::{
CanTrack, HitcountsMapObserver, ObserversTuple, TimeObserver, VariableMapObserver,
},
schedulers::{ schedulers::{
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler, powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler,
}, },
@ -26,7 +28,7 @@ use libafl::{
calibrate::CalibrationStage, power::StdPowerMutationalStage, AflStatsStage, IfStage, calibrate::CalibrationStage, power::StdPowerMutationalStage, AflStatsStage, IfStage,
ShadowTracingStage, StagesTuple, StdMutationalStage, ShadowTracingStage, StagesTuple, StdMutationalStage,
}, },
state::{HasCorpus, StdState}, state::{HasCorpus, HasExecutions, HasSolutions, StdState},
Error, HasMetadata, Error, HasMetadata,
}; };
#[cfg(not(feature = "simplemgr"))] #[cfg(not(feature = "simplemgr"))]
@ -42,7 +44,7 @@ use libafl_qemu::{
cmplog::CmpLogObserver, cmplog::CmpLogObserver,
edges::EdgeCoverageFullVariant, edges::EdgeCoverageFullVariant,
utils::filters::{HasAddressFilter, NopPageFilter, StdAddressFilter}, utils::filters::{HasAddressFilter, NopPageFilter, StdAddressFilter},
EdgeCoverageModule, EmulatorModuleTuple, StdEdgeCoverageModule, EdgeCoverageModule, EmulatorModuleTuple, SnapshotModule, StdEdgeCoverageModule,
}, },
Emulator, GuestAddr, Qemu, QemuExecutor, Emulator, GuestAddr, Qemu, QemuExecutor,
}; };
@ -62,6 +64,27 @@ pub type ClientMgr<M> = MonitorTypedEventManager<
M, M,
>; >;
/*
* The snapshot and iterations options interact as follows:
*
* +----------+------------+-------------------------------------------+
* | snapshot | iterations | Functionality |
* +----------+------------+-------------------------------------------+
* | N | N | We set the snapshot module into manual |
* | | | mode and never reset it. |
* +----------+------------+-------------------------------------------+
* | N | Y | We set the snapshot module into manual |
* | | | mode and never reset it. |
* +----------+------------+-------------------------------------------+
* | Y | N | We set the snapshot module into automatic |
* | | | mode so it resets after every iteration. |
* +----------+------------+-------------------------------------------+
* | Y | Y | We set the snapshot module into manual |
* | | | mode and manually reset it after the |
* | | | required number of iterations are done. |
* +----------+------------+-------------------------------------------+
*/
#[derive(TypedBuilder)] #[derive(TypedBuilder)]
pub struct Instance<'a, M: Monitor> { pub struct Instance<'a, M: Monitor> {
options: &'a FuzzerOptions, options: &'a FuzzerOptions,
@ -131,7 +154,22 @@ impl<M: Monitor> Instance<'_, M> {
.map_observer(edges_observer.as_mut()) .map_observer(edges_observer.as_mut())
.build()?; .build()?;
let modules = modules.prepend(edge_coverage_module); let mut snapshot_module = SnapshotModule::new();
/*
* Since the generics for the modules are already excessive when taking
* into accout asan, asan guest mode, cmplog, and injection, we will
* always include the SnapshotModule in all configurations, but simply
* not use it when it is not required. See the table at the top of this
* file for details.
*/
if !self.options.snapshots || self.options.iterations.is_some() {
snapshot_module.use_manual_reset();
}
let modules = modules
.prepend(edge_coverage_module)
.prepend(snapshot_module);
let mut emulator = Emulator::empty() let mut emulator = Emulator::empty()
.qemu_parameters(args) .qemu_parameters(args)
.modules(modules) .modules(modules)
@ -266,9 +304,9 @@ impl<M: Monitor> Instance<'_, M> {
// Create an observation channel using cmplog map // Create an observation channel using cmplog map
let cmplog_observer = CmpLogObserver::new("cmplog", true); let cmplog_observer = CmpLogObserver::new("cmplog", true);
let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer)); let mut shadow_executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
let tracing = ShadowTracingStage::new(&mut executor); let tracing = ShadowTracingStage::new(&mut shadow_executor);
// Setup a randomic Input2State stage // Setup a randomic Input2State stage
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!( let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(
@ -289,7 +327,14 @@ impl<M: Monitor> Instance<'_, M> {
// The order of the stages matter! // The order of the stages matter!
let mut stages = tuple_list!(calibration, tracing, i2s, power, stats_stage); let mut stages = tuple_list!(calibration, tracing, i2s, power, stats_stage);
self.fuzz(&mut state, &mut fuzzer, &mut executor, &mut stages) self.fuzz(
&mut state,
&mut fuzzer,
&mut shadow_executor,
Self::reset_shadow_executor_snapshot_module,
qemu,
&mut stages,
)
} else { } else {
// Create a QEMU in-process executor // Create a QEMU in-process executor
let mut executor = QemuExecutor::new( let mut executor = QemuExecutor::new(
@ -306,25 +351,88 @@ impl<M: Monitor> Instance<'_, M> {
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let mut stages = tuple_list!(StdMutationalStage::new(mutator)); let mut stages = tuple_list!(StdMutationalStage::new(mutator));
self.fuzz(&mut state, &mut fuzzer, &mut executor, &mut stages) self.fuzz(
&mut state,
&mut fuzzer,
&mut executor,
Self::reset_executor_snapshot_module,
qemu,
&mut stages,
)
} }
} }
fn fuzz<Z, E, ST>( fn reset_executor_snapshot_module<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, Z>(
executor: &mut QemuExecutor<'a, C, CM, ED, EM, (SnapshotModule, ET), H, I, OT, S, SM, Z>,
qemu: Qemu,
) where
I: Input + Unpin,
ET: EmulatorModuleTuple<I, S>,
S: HasCorpus<I> + HasCurrentCorpusId + HasSolutions<I> + HasExecutions + Unpin,
H: for<'e, 's, 'i> FnMut(
&'e mut Emulator<C, CM, ED, (SnapshotModule, ET), I, S, SM>,
&'s mut S,
&'i I,
) -> ExitKind,
OT: ObserversTuple<I, S>,
{
executor
.inner_mut()
.exposed_executor_state_mut()
.modules_mut()
.modules_mut()
.0
.reset(qemu);
}
fn reset_shadow_executor_snapshot_module<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, SOT, Z>(
executor: &mut ShadowExecutor<
QemuExecutor<'a, C, CM, ED, EM, (SnapshotModule, ET), H, I, OT, S, SM, Z>,
I,
S,
SOT,
>,
qemu: Qemu,
) where
I: Input + Unpin,
ET: EmulatorModuleTuple<I, S>,
S: HasCorpus<I> + HasCurrentCorpusId + HasSolutions<I> + HasExecutions + Unpin,
H: for<'e, 's, 'i> FnMut(
&'e mut Emulator<C, CM, ED, (SnapshotModule, ET), I, S, SM>,
&'s mut S,
&'i I,
) -> ExitKind,
OT: ObserversTuple<I, S>,
SOT: ObserversTuple<I, S>,
{
executor
.executor_mut()
.inner_mut()
.exposed_executor_state_mut()
.modules_mut()
.modules_mut()
.0
.reset(qemu);
}
fn fuzz<Z, E, ST, RSM>(
&mut self, &mut self,
state: &mut ClientState, state: &mut ClientState,
fuzzer: &mut Z, fuzzer: &mut Z,
executor: &mut E, executor: &mut E,
reset_snapshot_module: RSM,
qemu: Qemu,
stages: &mut ST, stages: &mut ST,
) -> Result<(), Error> ) -> Result<(), Error>
where where
Z: Fuzzer<E, ClientMgr<M>, BytesInput, ClientState, ST> Z: Fuzzer<E, ClientMgr<M>, BytesInput, ClientState, ST>
+ Evaluator<E, ClientMgr<M>, BytesInput, ClientState>, + Evaluator<E, ClientMgr<M>, BytesInput, ClientState>,
ST: StagesTuple<E, ClientMgr<M>, ClientState, Z>, ST: StagesTuple<E, ClientMgr<M>, ClientState, Z>,
RSM: Fn(&mut E, Qemu),
{ {
let corpus_dirs = [self.options.input_dir()];
if state.must_load_initial_inputs() { if state.must_load_initial_inputs() {
let corpus_dirs = [self.options.input_dir()];
state state
.load_initial_inputs(fuzzer, executor, &mut self.mgr, &corpus_dirs) .load_initial_inputs(fuzzer, executor, &mut self.mgr, &corpus_dirs)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
@ -334,12 +442,23 @@ impl<M: Monitor> Instance<'_, M> {
println!("We imported {} inputs from disk.", state.corpus().count()); println!("We imported {} inputs from disk.", state.corpus().count());
} }
/*
* See the table a the top of this file for details on how the snapshot
* and iterations options interact.
*/
if let Some(iters) = self.options.iterations { if let Some(iters) = self.options.iterations {
fuzzer.fuzz_loop_for(stages, executor, state, &mut self.mgr, iters)?; if self.options.snapshots {
loop {
reset_snapshot_module(executor, qemu);
fuzzer.fuzz_loop_for(stages, executor, state, &mut self.mgr, iters)?;
}
} else {
fuzzer.fuzz_loop_for(stages, executor, state, &mut self.mgr, iters)?;
// It's important, that we store the state before restarting! // It's important, that we store the state before restarting!
// Else, the parent will not respawn a new child and quit. // Else, the parent will not respawn a new child and quit.
self.mgr.on_restart(state)?; self.mgr.on_restart(state)?;
}
} else { } else {
fuzzer.fuzz_loop(stages, executor, state, &mut self.mgr)?; fuzzer.fuzz_loop(stages, executor, state, &mut self.mgr)?;
} }

View File

@ -62,6 +62,9 @@ pub struct FuzzerOptions {
#[clap(long, help = "Enable AFL++ style output", conflicts_with = "verbose")] #[clap(long, help = "Enable AFL++ style output", conflicts_with = "verbose")]
pub tui: bool, pub tui: bool,
#[clap(long, help = "Enable use of snapshots to restore state")]
pub snapshots: bool,
#[arg(long = "iterations", help = "Maximum number of iterations")] #[arg(long = "iterations", help = "Maximum number of iterations")]
pub iterations: Option<u64>, pub iterations: Option<u64>,

View File

@ -62,6 +62,18 @@ where
pub fn shadow_observers_mut(&mut self) -> RefIndexable<&mut SOT, SOT> { pub fn shadow_observers_mut(&mut self) -> RefIndexable<&mut SOT, SOT> {
RefIndexable::from(&mut self.shadow_observers) RefIndexable::from(&mut self.shadow_observers)
} }
/// Inner executor
#[inline]
pub fn executor(&self) -> &E {
&self.executor
}
/// Inner executor
#[inline]
pub fn executor_mut(&mut self) -> &mut E {
&mut self.executor
}
} }
impl<E, EM, I, S, SOT, Z> Executor<EM, I, S, Z> for ShadowExecutor<E, I, S, SOT> impl<E, EM, I, S, SOT, Z> Executor<EM, I, S, Z> for ShadowExecutor<E, I, S, SOT>

View File

@ -98,6 +98,7 @@ pub struct SnapshotModule {
pub empty: bool, pub empty: bool,
pub accurate_unmap: bool, pub accurate_unmap: bool,
pub interval_filter: Vec<IntervalSnapshotFilter>, pub interval_filter: Vec<IntervalSnapshotFilter>,
auto_reset: bool,
} }
impl core::fmt::Debug for SnapshotModule { impl core::fmt::Debug for SnapshotModule {
@ -130,6 +131,7 @@ impl SnapshotModule {
empty: true, empty: true,
accurate_unmap: false, accurate_unmap: false,
interval_filter: Vec::<IntervalSnapshotFilter>::new(), interval_filter: Vec::<IntervalSnapshotFilter>::new(),
auto_reset: true,
} }
} }
@ -148,6 +150,7 @@ impl SnapshotModule {
empty: true, empty: true,
accurate_unmap: false, accurate_unmap: false,
interval_filter, interval_filter,
auto_reset: true,
} }
} }
@ -166,6 +169,7 @@ impl SnapshotModule {
empty: true, empty: true,
accurate_unmap: false, accurate_unmap: false,
interval_filter: Vec::<IntervalSnapshotFilter>::new(), interval_filter: Vec::<IntervalSnapshotFilter>::new(),
auto_reset: true,
} }
} }
@ -173,6 +177,10 @@ impl SnapshotModule {
self.accurate_unmap = true; self.accurate_unmap = true;
} }
pub fn use_manual_reset(&mut self) {
self.auto_reset = false;
}
pub fn to_skip(&self, addr: GuestAddr) -> bool { pub fn to_skip(&self, addr: GuestAddr) -> bool {
for filter in &self.interval_filter { for filter in &self.interval_filter {
match filter { match filter {
@ -731,7 +739,7 @@ where
{ {
if self.empty { if self.empty {
self.snapshot(qemu); self.snapshot(qemu);
} else { } else if self.auto_reset {
self.reset(qemu); self.reset(qemu);
} }
} }