Skip to content

Commit 739156c

Browse files
WorksButNotTestedYour Name
andauthored
Add SnapshotModule to qemu_launcher (AFLplusplus#2887)
* Add SnapshotModule to qemu_launcher --------- Co-authored-by: Your Name <you@example.com>
1 parent 4cb4b6d commit 739156c

File tree

6 files changed

+168
-28
lines changed

6 files changed

+168
-28
lines changed

fuzzers/binary_only/qemu_launcher/src/fuzzer.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,18 @@ use std::{
66

77
use clap::Parser;
88
#[cfg(feature = "simplemgr")]
9-
use libafl::events::SimpleEventManager;
9+
use libafl::events::{ClientDescription, SimpleEventManager};
1010
#[cfg(not(feature = "simplemgr"))]
1111
use libafl::events::{EventConfig, Launcher, MonitorTypedEventManager};
1212
use libafl::{
13-
events::{ClientDescription, LlmpEventManagerBuilder},
1413
monitors::{tui::TuiMonitor, Monitor, MultiMonitor},
1514
Error,
1615
};
17-
use libafl_bolts::{core_affinity::CoreId, current_time, llmp::LlmpBroker, tuples::tuple_list};
16+
#[cfg(feature = "simplemgr")]
17+
use libafl_bolts::core_affinity::CoreId;
18+
use libafl_bolts::current_time;
1819
#[cfg(not(feature = "simplemgr"))]
19-
use libafl_bolts::{
20-
shmem::{ShMemProvider, StdShMemProvider},
21-
staterestore::StateRestorer,
22-
};
20+
use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider};
2321
#[cfg(unix)]
2422
use {
2523
nix::unistd::dup,
@@ -85,7 +83,7 @@ impl Fuzzer {
8583
{
8684
// The shared memory allocator
8785
#[cfg(not(feature = "simplemgr"))]
88-
let mut shmem_provider = StdShMemProvider::new()?;
86+
let shmem_provider = StdShMemProvider::new()?;
8987
/* If we are running in verbose, don't provide a replacement stdout, otherwise, use /dev/null */
9088
#[cfg(not(feature = "simplemgr"))]
9189
let stdout = if self.options.verbose {

fuzzers/binary_only/qemu_launcher/src/instance.rs

Lines changed: 138 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,29 @@ use libafl::events::SimpleEventManager;
66
#[cfg(not(feature = "simplemgr"))]
77
use libafl::events::{LlmpRestartingEventManager, MonitorTypedEventManager};
88
use libafl::{
9-
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus},
9+
corpus::{Corpus, HasCurrentCorpusId, InMemoryOnDiskCorpus, OnDiskCorpus},
1010
events::{ClientDescription, EventRestarter},
11-
executors::{Executor, ShadowExecutor},
11+
executors::{Executor, ExitKind, ShadowExecutor},
1212
feedback_or, feedback_or_fast,
1313
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
1414
fuzzer::{Evaluator, Fuzzer, StdFuzzer},
15-
inputs::BytesInput,
15+
inputs::{BytesInput, Input},
1616
monitors::Monitor,
1717
mutators::{
1818
havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations, StdMOptMutator,
1919
StdScheduledMutator, Tokens,
2020
},
21-
observers::{CanTrack, HitcountsMapObserver, TimeObserver, VariableMapObserver},
21+
observers::{
22+
CanTrack, HitcountsMapObserver, ObserversTuple, TimeObserver, VariableMapObserver,
23+
},
2224
schedulers::{
2325
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler,
2426
},
2527
stages::{
2628
calibrate::CalibrationStage, power::StdPowerMutationalStage, AflStatsStage, IfStage,
2729
ShadowTracingStage, StagesTuple, StdMutationalStage,
2830
},
29-
state::{HasCorpus, StdState},
31+
state::{HasCorpus, HasExecutions, HasSolutions, StdState},
3032
Error, HasMetadata,
3133
};
3234
#[cfg(not(feature = "simplemgr"))]
@@ -42,7 +44,7 @@ use libafl_qemu::{
4244
cmplog::CmpLogObserver,
4345
edges::EdgeCoverageFullVariant,
4446
utils::filters::{HasAddressFilter, NopPageFilter, StdAddressFilter},
45-
EdgeCoverageModule, EmulatorModuleTuple, StdEdgeCoverageModule,
47+
EdgeCoverageModule, EmulatorModuleTuple, SnapshotModule, StdEdgeCoverageModule,
4648
},
4749
Emulator, GuestAddr, Qemu, QemuExecutor,
4850
};
@@ -62,6 +64,27 @@ pub type ClientMgr<M> = MonitorTypedEventManager<
6264
M,
6365
>;
6466

67+
/*
68+
* The snapshot and iterations options interact as follows:
69+
*
70+
* +----------+------------+-------------------------------------------+
71+
* | snapshot | iterations | Functionality |
72+
* +----------+------------+-------------------------------------------+
73+
* | N | N | We set the snapshot module into manual |
74+
* | | | mode and never reset it. |
75+
* +----------+------------+-------------------------------------------+
76+
* | N | Y | We set the snapshot module into manual |
77+
* | | | mode and never reset it. |
78+
* +----------+------------+-------------------------------------------+
79+
* | Y | N | We set the snapshot module into automatic |
80+
* | | | mode so it resets after every iteration. |
81+
* +----------+------------+-------------------------------------------+
82+
* | Y | Y | We set the snapshot module into manual |
83+
* | | | mode and manually reset it after the |
84+
* | | | required number of iterations are done. |
85+
* +----------+------------+-------------------------------------------+
86+
*/
87+
6588
#[derive(TypedBuilder)]
6689
pub struct Instance<'a, M: Monitor> {
6790
options: &'a FuzzerOptions,
@@ -131,7 +154,22 @@ impl<M: Monitor> Instance<'_, M> {
131154
.map_observer(edges_observer.as_mut())
132155
.build()?;
133156

134-
let modules = modules.prepend(edge_coverage_module);
157+
let mut snapshot_module = SnapshotModule::new();
158+
159+
/*
160+
* Since the generics for the modules are already excessive when taking
161+
* into accout asan, asan guest mode, cmplog, and injection, we will
162+
* always include the SnapshotModule in all configurations, but simply
163+
* not use it when it is not required. See the table at the top of this
164+
* file for details.
165+
*/
166+
if !self.options.snapshots || self.options.iterations.is_some() {
167+
snapshot_module.use_manual_reset();
168+
}
169+
170+
let modules = modules
171+
.prepend(edge_coverage_module)
172+
.prepend(snapshot_module);
135173
let mut emulator = Emulator::empty()
136174
.qemu_parameters(args)
137175
.modules(modules)
@@ -266,9 +304,9 @@ impl<M: Monitor> Instance<'_, M> {
266304
// Create an observation channel using cmplog map
267305
let cmplog_observer = CmpLogObserver::new("cmplog", true);
268306

269-
let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
307+
let mut shadow_executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
270308

271-
let tracing = ShadowTracingStage::new(&mut executor);
309+
let tracing = ShadowTracingStage::new(&mut shadow_executor);
272310

273311
// Setup a randomic Input2State stage
274312
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(
@@ -289,7 +327,14 @@ impl<M: Monitor> Instance<'_, M> {
289327
// The order of the stages matter!
290328
let mut stages = tuple_list!(calibration, tracing, i2s, power, stats_stage);
291329

292-
self.fuzz(&mut state, &mut fuzzer, &mut executor, &mut stages)
330+
self.fuzz(
331+
&mut state,
332+
&mut fuzzer,
333+
&mut shadow_executor,
334+
Self::reset_shadow_executor_snapshot_module,
335+
qemu,
336+
&mut stages,
337+
)
293338
} else {
294339
// Create a QEMU in-process executor
295340
let mut executor = QemuExecutor::new(
@@ -306,25 +351,88 @@ impl<M: Monitor> Instance<'_, M> {
306351
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
307352
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
308353

309-
self.fuzz(&mut state, &mut fuzzer, &mut executor, &mut stages)
354+
self.fuzz(
355+
&mut state,
356+
&mut fuzzer,
357+
&mut executor,
358+
Self::reset_executor_snapshot_module,
359+
qemu,
360+
&mut stages,
361+
)
310362
}
311363
}
312364

313-
fn fuzz<Z, E, ST>(
365+
fn reset_executor_snapshot_module<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, Z>(
366+
executor: &mut QemuExecutor<'a, C, CM, ED, EM, (SnapshotModule, ET), H, I, OT, S, SM, Z>,
367+
qemu: Qemu,
368+
) where
369+
I: Input + Unpin,
370+
ET: EmulatorModuleTuple<I, S>,
371+
S: HasCorpus<I> + HasCurrentCorpusId + HasSolutions<I> + HasExecutions + Unpin,
372+
H: for<'e, 's, 'i> FnMut(
373+
&'e mut Emulator<C, CM, ED, (SnapshotModule, ET), I, S, SM>,
374+
&'s mut S,
375+
&'i I,
376+
) -> ExitKind,
377+
OT: ObserversTuple<I, S>,
378+
{
379+
executor
380+
.inner_mut()
381+
.exposed_executor_state_mut()
382+
.modules_mut()
383+
.modules_mut()
384+
.0
385+
.reset(qemu);
386+
}
387+
388+
fn reset_shadow_executor_snapshot_module<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, SOT, Z>(
389+
executor: &mut ShadowExecutor<
390+
QemuExecutor<'a, C, CM, ED, EM, (SnapshotModule, ET), H, I, OT, S, SM, Z>,
391+
I,
392+
S,
393+
SOT,
394+
>,
395+
qemu: Qemu,
396+
) where
397+
I: Input + Unpin,
398+
ET: EmulatorModuleTuple<I, S>,
399+
S: HasCorpus<I> + HasCurrentCorpusId + HasSolutions<I> + HasExecutions + Unpin,
400+
H: for<'e, 's, 'i> FnMut(
401+
&'e mut Emulator<C, CM, ED, (SnapshotModule, ET), I, S, SM>,
402+
&'s mut S,
403+
&'i I,
404+
) -> ExitKind,
405+
OT: ObserversTuple<I, S>,
406+
SOT: ObserversTuple<I, S>,
407+
{
408+
executor
409+
.executor_mut()
410+
.inner_mut()
411+
.exposed_executor_state_mut()
412+
.modules_mut()
413+
.modules_mut()
414+
.0
415+
.reset(qemu);
416+
}
417+
418+
fn fuzz<Z, E, ST, RSM>(
314419
&mut self,
315420
state: &mut ClientState,
316421
fuzzer: &mut Z,
317422
executor: &mut E,
423+
reset_snapshot_module: RSM,
424+
qemu: Qemu,
318425
stages: &mut ST,
319426
) -> Result<(), Error>
320427
where
321428
Z: Fuzzer<E, ClientMgr<M>, BytesInput, ClientState, ST>
322429
+ Evaluator<E, ClientMgr<M>, BytesInput, ClientState>,
323430
ST: StagesTuple<E, ClientMgr<M>, ClientState, Z>,
431+
RSM: Fn(&mut E, Qemu),
324432
{
325-
let corpus_dirs = [self.options.input_dir()];
326-
327433
if state.must_load_initial_inputs() {
434+
let corpus_dirs = [self.options.input_dir()];
435+
328436
state
329437
.load_initial_inputs(fuzzer, executor, &mut self.mgr, &corpus_dirs)
330438
.unwrap_or_else(|_| {
@@ -334,12 +442,23 @@ impl<M: Monitor> Instance<'_, M> {
334442
println!("We imported {} inputs from disk.", state.corpus().count());
335443
}
336444

445+
/*
446+
* See the table a the top of this file for details on how the snapshot
447+
* and iterations options interact.
448+
*/
337449
if let Some(iters) = self.options.iterations {
338-
fuzzer.fuzz_loop_for(stages, executor, state, &mut self.mgr, iters)?;
339-
340-
// It's important, that we store the state before restarting!
341-
// Else, the parent will not respawn a new child and quit.
342-
self.mgr.on_restart(state)?;
450+
if self.options.snapshots {
451+
loop {
452+
reset_snapshot_module(executor, qemu);
453+
fuzzer.fuzz_loop_for(stages, executor, state, &mut self.mgr, iters)?;
454+
}
455+
} else {
456+
fuzzer.fuzz_loop_for(stages, executor, state, &mut self.mgr, iters)?;
457+
458+
// It's important, that we store the state before restarting!
459+
// Else, the parent will not respawn a new child and quit.
460+
self.mgr.on_restart(state)?;
461+
}
343462
} else {
344463
fuzzer.fuzz_loop(stages, executor, state, &mut self.mgr)?;
345464
}

fuzzers/binary_only/qemu_launcher/src/options.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ pub struct FuzzerOptions {
6262
#[clap(long, help = "Enable AFL++ style output", conflicts_with = "verbose")]
6363
pub tui: bool,
6464

65+
#[clap(long, help = "Enable use of snapshots to restore state")]
66+
pub snapshots: bool,
67+
6568
#[arg(long = "iterations", help = "Maximum number of iterations")]
6669
pub iterations: Option<u64>,
6770

fuzzers/binary_only/qemu_launcher/stats.txt

Whitespace-only changes.

libafl/src/executors/shadow.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ where
6262
pub fn shadow_observers_mut(&mut self) -> RefIndexable<&mut SOT, SOT> {
6363
RefIndexable::from(&mut self.shadow_observers)
6464
}
65+
66+
/// Inner executor
67+
#[inline]
68+
pub fn executor(&self) -> &E {
69+
&self.executor
70+
}
71+
72+
/// Inner executor
73+
#[inline]
74+
pub fn executor_mut(&mut self) -> &mut E {
75+
&mut self.executor
76+
}
6577
}
6678

6779
impl<E, EM, I, S, SOT, Z> Executor<EM, I, S, Z> for ShadowExecutor<E, I, S, SOT>

libafl_qemu/src/modules/usermode/snapshot.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ pub struct SnapshotModule {
9898
pub empty: bool,
9999
pub accurate_unmap: bool,
100100
pub interval_filter: Vec<IntervalSnapshotFilter>,
101+
auto_reset: bool,
101102
}
102103

103104
impl core::fmt::Debug for SnapshotModule {
@@ -130,6 +131,7 @@ impl SnapshotModule {
130131
empty: true,
131132
accurate_unmap: false,
132133
interval_filter: Vec::<IntervalSnapshotFilter>::new(),
134+
auto_reset: true,
133135
}
134136
}
135137

@@ -148,6 +150,7 @@ impl SnapshotModule {
148150
empty: true,
149151
accurate_unmap: false,
150152
interval_filter,
153+
auto_reset: true,
151154
}
152155
}
153156

@@ -166,13 +169,18 @@ impl SnapshotModule {
166169
empty: true,
167170
accurate_unmap: false,
168171
interval_filter: Vec::<IntervalSnapshotFilter>::new(),
172+
auto_reset: true,
169173
}
170174
}
171175

172176
pub fn use_accurate_unmapping(&mut self) {
173177
self.accurate_unmap = true;
174178
}
175179

180+
pub fn use_manual_reset(&mut self) {
181+
self.auto_reset = false;
182+
}
183+
176184
pub fn to_skip(&self, addr: GuestAddr) -> bool {
177185
for filter in &self.interval_filter {
178186
match filter {
@@ -731,7 +739,7 @@ where
731739
{
732740
if self.empty {
733741
self.snapshot(qemu);
734-
} else {
742+
} else if self.auto_reset {
735743
self.reset(qemu);
736744
}
737745
}

0 commit comments

Comments
 (0)