Skip to content

Commit b519363

Browse files
Introduce FeedbackState and allow Feedbacks to process the entire State (#103)
* save work * it builds * MutationalStage builds * compile lib.rs test * libafl tests work * adapt stb_image example * change fuzzer to not hold executor and event manager as type field * libfuzzer_stb_image running example * restore ReachabilityFeedback * restore introspection * adapt fuzzers except frida_libpng * format * compile on windows * clippy * fix libafl_frida * adapt frida_libpng
1 parent 8551f09 commit b519363

File tree

25 files changed

+1491
-1129
lines changed

25 files changed

+1491
-1129
lines changed

fuzzers/baby_fuzzer/src/main.rs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ use libafl::{
55
corpus::{InMemoryCorpus, OnDiskCorpus, QueueCorpusScheduler},
66
events::SimpleEventManager,
77
executors::{inprocess::InProcessExecutor, ExitKind},
8-
feedbacks::{CrashFeedback, MaxMapFeedback},
8+
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback},
99
fuzzer::{Fuzzer, StdFuzzer},
1010
generators::RandPrintablesGenerator,
1111
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
1212
observers::StdMapObserver,
1313
stages::mutational::StdMutationalStage,
14-
state::State,
14+
state::StdState,
1515
stats::SimpleStats,
1616
utils::{current_nanos, StdRand},
1717
};
@@ -42,19 +42,27 @@ pub fn main() {
4242
// Create an observation channel using the signals map
4343
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
4444

45+
// The state of the edges feedback.
46+
let feedback_state = MapFeedbackState::with_observer(&observer);
47+
48+
// Feedback to rate the interestingness of an input
49+
let feedback = MaxMapFeedback::new(&feedback_state, &observer);
50+
51+
// A feedback to choose if an input is a solution or not
52+
let objective = CrashFeedback::new();
53+
4554
// create a State from scratch
46-
let mut state = State::new(
55+
let mut state = StdState::new(
4756
// RNG
4857
StdRand::with_seed(current_nanos()),
4958
// Corpus that will be evolved, we keep it in memory for performance
5059
InMemoryCorpus::new(),
51-
// Feedback to rate the interestingness of an input
52-
MaxMapFeedback::new_with_observer(&observer),
5360
// Corpus in which we store solutions (crashes in this example),
5461
// on disk so the user can get them after stopping the fuzzer
5562
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
56-
// Feedbacks to recognize an input as solution
57-
CrashFeedback::new(),
63+
// States of the feedbacks.
64+
// They are the data related to the feedbacks that you want to persist in the State.
65+
tuple_list!(feedback_state),
5866
);
5967

6068
// The Stats trait define how the fuzzer stats are reported to the user
@@ -67,27 +75,32 @@ pub fn main() {
6775
// A queue policy to get testcasess from the corpus
6876
let scheduler = QueueCorpusScheduler::new();
6977

78+
// A fuzzer with feedbacks and a corpus scheduler
79+
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
80+
7081
// Create the executor for an in-process function with just one observer
71-
let mut executor =
72-
InProcessExecutor::new(&mut harness, tuple_list!(observer), &mut state, &mut mgr)
73-
.expect("Failed to create the Executor".into());
82+
let mut executor = InProcessExecutor::new(
83+
&mut harness,
84+
tuple_list!(observer),
85+
&mut fuzzer,
86+
&mut state,
87+
&mut mgr,
88+
)
89+
.expect("Failed to create the Executor".into());
7490

7591
// Generator of printable bytearrays of max size 32
7692
let mut generator = RandPrintablesGenerator::new(32);
7793

7894
// Generate 8 initial inputs
7995
state
80-
.generate_initial_inputs(&mut executor, &mut generator, &mut mgr, &scheduler, 8)
96+
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
8197
.expect("Failed to generate the initial corpus".into());
8298

83-
// Setup a basic mutator with a mutational stage
99+
// Setup a mutational stage with a basic bytes mutator
84100
let mutator = StdScheduledMutator::new(havoc_mutations());
85-
let stage = StdMutationalStage::new(mutator);
86-
87-
// A fuzzer with just one stage
88-
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
101+
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
89102

90103
fuzzer
91-
.fuzz_loop(&mut state, &mut executor, &mut mgr, &scheduler)
104+
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
92105
.expect("Error in the fuzzing loop".into());
93106
}

fuzzers/frida_libpng/src/fuzzer.rs

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ use libafl::{
1313
HasExecHooksTuple, HasObservers, HasObserversHooks,
1414
},
1515
feedback_or,
16-
feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback},
16+
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
1717
fuzzer::{Fuzzer, StdFuzzer},
1818
inputs::{HasTargetBytes, Input},
1919
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
2020
mutators::token_mutations::Tokens,
21-
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver},
21+
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver, TimeObserver},
2222
stages::mutational::StdMutationalStage,
23-
state::{HasCorpus, HasMetadata, State},
23+
state::{HasCorpus, HasMetadata, StdState},
2424
stats::SimpleStats,
2525
utils::{current_nanos, StdRand},
2626
Error,
@@ -39,14 +39,14 @@ use libafl_frida::{
3939
FridaOptions,
4040
};
4141

42-
struct FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
42+
struct FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
4343
where
4444
FH: FridaHelper<'b>,
4545
H: FnMut(&[u8]) -> ExitKind,
4646
I: Input + HasTargetBytes,
4747
OT: ObserversTuple,
4848
{
49-
base: TimeoutExecutor<InProcessExecutor<'a, EM, H, I, OT, S>, I>,
49+
base: TimeoutExecutor<InProcessExecutor<'a, H, I, OT, S>, I>,
5050
/// Frida's dynamic rewriting engine
5151
stalker: Stalker<'a>,
5252
/// User provided callback for instrumentation
@@ -55,8 +55,8 @@ where
5555
_phantom: PhantomData<&'b u8>,
5656
}
5757

58-
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> Executor<I>
59-
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
58+
impl<'a, 'b, 'c, FH, H, I, OT, S> Executor<I>
59+
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
6060
where
6161
FH: FridaHelper<'b>,
6262
H: FnMut(&[u8]) -> ExitKind,
@@ -91,8 +91,8 @@ where
9191
}
9292
}
9393

94-
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasExecHooks<EM, I, S>
95-
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
94+
impl<'a, 'b, 'c, EM, FH, H, I, OT, S, Z> HasExecHooks<EM, I, S, Z>
95+
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
9696
where
9797
FH: FridaHelper<'b>,
9898
H: FnMut(&[u8]) -> ExitKind,
@@ -101,21 +101,33 @@ where
101101
{
102102
/// Called right before exexution starts
103103
#[inline]
104-
fn pre_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> {
104+
fn pre_exec(
105+
&mut self,
106+
fuzzer: &mut Z,
107+
state: &mut S,
108+
event_mgr: &mut EM,
109+
input: &I,
110+
) -> Result<(), Error> {
105111
self.helper.pre_exec(input);
106-
self.base.pre_exec(state, event_mgr, input)
112+
self.base.pre_exec(fuzzer, state, event_mgr, input)
107113
}
108114

109115
/// Called right after execution finished.
110116
#[inline]
111-
fn post_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> {
117+
fn post_exec(
118+
&mut self,
119+
fuzzer: &mut Z,
120+
state: &mut S,
121+
event_mgr: &mut EM,
122+
input: &I,
123+
) -> Result<(), Error> {
112124
self.helper.post_exec(input);
113-
self.base.post_exec(state, event_mgr, input)
125+
self.base.post_exec(fuzzer, state, event_mgr, input)
114126
}
115127
}
116128

117-
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasObservers<OT>
118-
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
129+
impl<'a, 'b, 'c, FH, H, I, OT, S> HasObservers<OT>
130+
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
119131
where
120132
FH: FridaHelper<'b>,
121133
H: FnMut(&[u8]) -> ExitKind,
@@ -133,17 +145,17 @@ where
133145
}
134146
}
135147

136-
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasObserversHooks<EM, I, OT, S>
137-
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
148+
impl<'a, 'b, 'c, EM, FH, H, I, OT, S, Z> HasObserversHooks<EM, I, OT, S, Z>
149+
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
138150
where
139151
FH: FridaHelper<'b>,
140152
H: FnMut(&[u8]) -> ExitKind,
141153
I: Input + HasTargetBytes,
142-
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
154+
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
143155
{
144156
}
145157

146-
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
158+
impl<'a, 'b, 'c, FH, H, I, OT, S> FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
147159
where
148160
FH: FridaHelper<'b>,
149161
H: FnMut(&[u8]) -> ExitKind,
@@ -152,7 +164,7 @@ where
152164
{
153165
pub fn new(
154166
gum: &'a Gum,
155-
base: InProcessExecutor<'a, EM, H, I, OT, S>,
167+
base: InProcessExecutor<'a, H, I, OT, S>,
156168
helper: &'c mut FH,
157169
timeout: Duration,
158170
) -> Self {
@@ -268,25 +280,45 @@ unsafe fn fuzz(
268280
MAP_SIZE,
269281
));
270282

283+
// Create an observation channel to keep track of the execution time
284+
let time_observer = TimeObserver::new("time");
285+
286+
// Create an observation channel for ASan violations
287+
let asan_observer = AsanErrorsObserver::new(&ASAN_ERRORS);
288+
289+
// The state of the edges feedback.
290+
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
291+
292+
// Feedback to rate the interestingness of an input
293+
// This one is composed by two Feedbacks in OR
294+
let feedback = feedback_or!(
295+
// New maximization map feedback linked to the edges observer and the feedback state
296+
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
297+
// Time feedback, this one does not need a feedback state
298+
TimeFeedback::new_with_observer(&time_observer)
299+
);
300+
301+
// Feedbacks to recognize an input as solution
302+
let objective = feedback_or!(
303+
CrashFeedback::new(),
304+
TimeoutFeedback::new(),
305+
AsanErrorsFeedback::new()
306+
);
307+
271308
// If not restarting, create a State from scratch
272309
let mut state = state.unwrap_or_else(|| {
273-
State::new(
310+
StdState::new(
274311
// RNG
275312
StdRand::with_seed(current_nanos()),
276313
// Corpus that will be evolved, we keep it in memory for performance
277314
InMemoryCorpus::new(),
278-
// Feedbacks to rate the interestingness of an input
279-
MaxMapFeedback::new_tracking_with_observer(&edges_observer, true, false),
280315
// Corpus in which we store solutions (crashes in this example),
281316
// on disk so the user can get them after stopping the fuzzer
282317
OnDiskCorpus::new_save_meta(objective_dir, Some(OnDiskMetadataFormat::JsonPretty))
283318
.unwrap(),
284-
// Feedbacks to recognize an input as solution
285-
feedback_or!(
286-
CrashFeedback::new(),
287-
TimeoutFeedback::new(),
288-
AsanErrorsFeedback::new()
289-
),
319+
// States of the feedbacks.
320+
// They are the data related to the feedbacks that you want to persist in the State.
321+
tuple_list!(feedback_state),
290322
)
291323
});
292324

@@ -305,11 +337,13 @@ unsafe fn fuzz(
305337

306338
// Setup a basic mutator with a mutational stage
307339
let mutator = StdScheduledMutator::new(havoc_mutations());
308-
let stage = StdMutationalStage::new(mutator);
340+
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
309341

310-
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
342+
// A minimization+queue policy to get testcasess from the corpus
311343
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
312-
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
344+
345+
// A fuzzer with feedbacks and a corpus scheduler
346+
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
313347

314348
frida_helper.register_thread();
315349

@@ -318,7 +352,8 @@ unsafe fn fuzz(
318352
&gum,
319353
InProcessExecutor::new(
320354
&mut frida_harness,
321-
tuple_list!(edges_observer, AsanErrorsObserver::new(&ASAN_ERRORS)),
355+
tuple_list!(edges_observer, time_observer, asan_observer),
356+
&mut fuzzer,
322357
&mut state,
323358
&mut restarting_mgr,
324359
)?,
@@ -338,15 +373,20 @@ unsafe fn fuzz(
338373
// In case the corpus is empty (on first run), reset
339374
if state.corpus().count() < 1 {
340375
state
341-
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
376+
.load_initial_inputs(
377+
&mut fuzzer,
378+
&mut executor,
379+
&mut restarting_mgr,
380+
&corpus_dirs,
381+
)
342382
.expect(&format!(
343383
"Failed to load initial corpus at {:?}",
344384
&corpus_dirs
345385
));
346386
println!("We imported {} inputs from disk.", state.corpus().count());
347387
}
348388

349-
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
389+
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?;
350390

351391
// Never reached
352392
Ok(())

0 commit comments

Comments
 (0)