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
This commit is contained in:
Andrea Fioraldi 2021-05-12 23:53:27 +02:00 committed by GitHub
parent 8551f093c4
commit b51936397b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1508 additions and 1146 deletions

View File

@ -5,13 +5,13 @@ use libafl::{
corpus::{InMemoryCorpus, OnDiskCorpus, QueueCorpusScheduler},
events::SimpleEventManager,
executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback},
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback},
fuzzer::{Fuzzer, StdFuzzer},
generators::RandPrintablesGenerator,
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
observers::StdMapObserver,
stages::mutational::StdMutationalStage,
state::State,
state::StdState,
stats::SimpleStats,
utils::{current_nanos, StdRand},
};
@ -42,19 +42,27 @@ pub fn main() {
// Create an observation channel using the signals map
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
// The state of the edges feedback.
let feedback_state = MapFeedbackState::with_observer(&observer);
// Feedback to rate the interestingness of an input
let feedback = MaxMapFeedback::new(&feedback_state, &observer);
// A feedback to choose if an input is a solution or not
let objective = CrashFeedback::new();
// create a State from scratch
let mut state = State::new(
let mut state = StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedback to rate the interestingness of an input
MaxMapFeedback::new_with_observer(&observer),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
// Feedbacks to recognize an input as solution
CrashFeedback::new(),
// States of the feedbacks.
// They are the data related to the feedbacks that you want to persist in the State.
tuple_list!(feedback_state),
);
// The Stats trait define how the fuzzer stats are reported to the user
@ -67,27 +75,32 @@ pub fn main() {
// A queue policy to get testcasess from the corpus
let scheduler = QueueCorpusScheduler::new();
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// Create the executor for an in-process function with just one observer
let mut executor =
InProcessExecutor::new(&mut harness, tuple_list!(observer), &mut state, &mut mgr)
.expect("Failed to create the Executor".into());
let mut executor = InProcessExecutor::new(
&mut harness,
tuple_list!(observer),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor".into());
// Generator of printable bytearrays of max size 32
let mut generator = RandPrintablesGenerator::new(32);
// Generate 8 initial inputs
state
.generate_initial_inputs(&mut executor, &mut generator, &mut mgr, &scheduler, 8)
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
.expect("Failed to generate the initial corpus".into());
// Setup a basic mutator with a mutational stage
// Setup a mutational stage with a basic bytes mutator
let mutator = StdScheduledMutator::new(havoc_mutations());
let stage = StdMutationalStage::new(mutator);
// A fuzzer with just one stage
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
fuzzer
.fuzz_loop(&mut state, &mut executor, &mut mgr, &scheduler)
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
.expect("Error in the fuzzing loop".into());
}

View File

@ -13,14 +13,14 @@ use libafl::{
HasExecHooksTuple, HasObservers, HasObserversHooks,
},
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback},
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{HasTargetBytes, Input},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::token_mutations::Tokens,
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver},
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State},
state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats,
utils::{current_nanos, StdRand},
Error,
@ -39,14 +39,14 @@ use libafl_frida::{
FridaOptions,
};
struct FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
struct FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where
FH: FridaHelper<'b>,
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
base: TimeoutExecutor<InProcessExecutor<'a, EM, H, I, OT, S>, I>,
base: TimeoutExecutor<InProcessExecutor<'a, H, I, OT, S>, I>,
/// Frida's dynamic rewriting engine
stalker: Stalker<'a>,
/// User provided callback for instrumentation
@ -55,8 +55,8 @@ where
_phantom: PhantomData<&'b u8>,
}
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> Executor<I>
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
impl<'a, 'b, 'c, FH, H, I, OT, S> Executor<I>
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where
FH: FridaHelper<'b>,
H: FnMut(&[u8]) -> ExitKind,
@ -91,8 +91,8 @@ where
}
}
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasExecHooks<EM, I, S>
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
impl<'a, 'b, 'c, EM, FH, H, I, OT, S, Z> HasExecHooks<EM, I, S, Z>
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where
FH: FridaHelper<'b>,
H: FnMut(&[u8]) -> ExitKind,
@ -101,21 +101,33 @@ where
{
/// Called right before exexution starts
#[inline]
fn pre_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> {
fn pre_exec(
&mut self,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
input: &I,
) -> Result<(), Error> {
self.helper.pre_exec(input);
self.base.pre_exec(state, event_mgr, input)
self.base.pre_exec(fuzzer, state, event_mgr, input)
}
/// Called right after execution finished.
#[inline]
fn post_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> {
fn post_exec(
&mut self,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
input: &I,
) -> Result<(), Error> {
self.helper.post_exec(input);
self.base.post_exec(state, event_mgr, input)
self.base.post_exec(fuzzer, state, event_mgr, input)
}
}
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasObservers<OT>
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
impl<'a, 'b, 'c, FH, H, I, OT, S> HasObservers<OT>
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where
FH: FridaHelper<'b>,
H: FnMut(&[u8]) -> ExitKind,
@ -133,17 +145,17 @@ where
}
}
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasObserversHooks<EM, I, OT, S>
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
impl<'a, 'b, 'c, EM, FH, H, I, OT, S, Z> HasObserversHooks<EM, I, OT, S, Z>
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where
FH: FridaHelper<'b>,
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
{
}
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
impl<'a, 'b, 'c, FH, H, I, OT, S> FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where
FH: FridaHelper<'b>,
H: FnMut(&[u8]) -> ExitKind,
@ -152,7 +164,7 @@ where
{
pub fn new(
gum: &'a Gum,
base: InProcessExecutor<'a, EM, H, I, OT, S>,
base: InProcessExecutor<'a, H, I, OT, S>,
helper: &'c mut FH,
timeout: Duration,
) -> Self {
@ -268,25 +280,45 @@ unsafe fn fuzz(
MAP_SIZE,
));
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// Create an observation channel for ASan violations
let asan_observer = AsanErrorsObserver::new(&ASAN_ERRORS);
// The state of the edges feedback.
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
// Time feedback, this one does not need a feedback state
TimeFeedback::new_with_observer(&time_observer)
);
// Feedbacks to recognize an input as solution
let objective = feedback_or!(
CrashFeedback::new(),
TimeoutFeedback::new(),
AsanErrorsFeedback::new()
);
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
State::new(
StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
MaxMapFeedback::new_tracking_with_observer(&edges_observer, true, false),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new_save_meta(objective_dir, Some(OnDiskMetadataFormat::JsonPretty))
.unwrap(),
// Feedbacks to recognize an input as solution
feedback_or!(
CrashFeedback::new(),
TimeoutFeedback::new(),
AsanErrorsFeedback::new()
),
// States of the feedbacks.
// They are the data related to the feedbacks that you want to persist in the State.
tuple_list!(feedback_state),
)
});
@ -305,11 +337,13 @@ unsafe fn fuzz(
// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations());
let stage = StdMutationalStage::new(mutator);
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
frida_helper.register_thread();
@ -318,7 +352,8 @@ unsafe fn fuzz(
&gum,
InProcessExecutor::new(
&mut frida_harness,
tuple_list!(edges_observer, AsanErrorsObserver::new(&ASAN_ERRORS)),
tuple_list!(edges_observer, time_observer, asan_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
@ -338,7 +373,12 @@ unsafe fn fuzz(
// In case the corpus is empty (on first run), reset
if state.corpus().count() < 1 {
state
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
.load_initial_inputs(
&mut fuzzer,
&mut executor,
&mut restarting_mgr,
&corpus_dirs,
)
.expect(&format!(
"Failed to load initial corpus at {:?}",
&corpus_dirs
@ -346,7 +386,7 @@ unsafe fn fuzz(
println!("We imported {} inputs from disk.", state.corpus().count());
}
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?;
// Never reached
Ok(())

View File

@ -9,13 +9,13 @@ use libafl::{
events::setup_restarting_mgr_std,
executors::{inprocess::InProcessExecutor, ExitKind},
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback},
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback},
fuzzer::{Fuzzer, StdFuzzer},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::token_mutations::Tokens,
observers::StdMapObserver,
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State},
state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats,
utils::{current_nanos, StdRand},
Error,
@ -68,24 +68,42 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Create an observation channel using the allocations map
let allocs_observer = StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_map });
// The state of the edges feedback.
let edges_feedback_state = MapFeedbackState::with_observer(&edges_observer);
// The state of the cmps feedback.
let cmps_feedback_state = MapFeedbackState::with_observer(&cmps_observer);
// The state of the allocs feedback.
let allocs_feedback_state = MapFeedbackState::with_observer(&allocs_observer);
// Feedback to rate the interestingness of an input
let feedback = feedback_or!(
MaxMapFeedback::new(&edges_feedback_state, &edges_observer),
MaxMapFeedback::new(&cmps_feedback_state, &cmps_observer),
MaxMapFeedback::new(&allocs_feedback_state, &allocs_observer)
);
// A feedback to choose if an input is a solution or not
let objective = CrashFeedback::new();
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
State::new(
StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
feedback_or!(
MaxMapFeedback::new_with_observer(&edges_observer),
MaxMapFeedback::new_with_observer(&cmps_observer),
MaxMapFeedback::new_with_observer(&allocs_observer)
),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// Feedbacks to recognize an input as solution
CrashFeedback::new(),
// States of the feedbacks.
// They are the data related to the feedbacks that you want to persist in the State.
tuple_list!(
edges_feedback_state,
cmps_feedback_state,
allocs_feedback_state
),
)
});
@ -98,12 +116,13 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations());
let stage = StdMutationalStage::new(mutator);
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
// A random policy to get testcasess from the corpus
let scheduler = RandCorpusScheduler::new();
// A fuzzer with just one stage
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |buf: &[u8]| {
@ -115,6 +134,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
let mut executor = InProcessExecutor::new(
&mut harness,
tuple_list!(edges_observer, cmps_observer, allocs_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?;
@ -129,7 +149,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// In case the corpus is empty (on first run), reset
if state.corpus().count() < 1 {
state
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
.load_initial_inputs(
&mut fuzzer,
&mut executor,
&mut restarting_mgr,
&corpus_dirs,
)
.expect(&format!(
"Failed to load initial corpus at {:?}",
&corpus_dirs
@ -137,7 +162,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
println!("We imported {} inputs from disk.", state.corpus().count());
}
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?;
// Never reached
Ok(())

View File

@ -10,16 +10,16 @@ use libafl::{
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
QueueCorpusScheduler,
},
events::{setup_restarting_mgr_std, EventManager},
events::{setup_restarting_mgr_std, EventRestarter},
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::token_mutations::Tokens,
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State},
state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats,
utils::{current_nanos, StdRand},
Error,
@ -71,23 +71,34 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// The state of the edges feedback.
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
// Time feedback, this one does not need a feedback state
TimeFeedback::new_with_observer(&time_observer)
);
// A feedback to choose if an input is a solution or not
let objective = feedback_or!(CrashFeedback::new(), TimeoutFeedback::new());
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
State::new(
StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
feedback_or!(
MaxMapFeedback::new_tracking_with_observer(&edges_observer, true, false),
TimeFeedback::new_with_observer(&time_observer)
),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// Feedbacks to recognize an input as solution
feedback_or!(CrashFeedback::new(), TimeoutFeedback::new()),
// States of the feedbacks.
// They are the data related to the feedbacks that you want to persist in the State.
tuple_list!(feedback_state),
)
});
@ -106,14 +117,14 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations());
let stage = StdMutationalStage::new(mutator);
// A fuzzer with just one stage
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |buf: &[u8]| {
libfuzzer_test_one_input(buf);
@ -125,6 +136,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
InProcessExecutor::new(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
@ -142,7 +154,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// In case the corpus is empty (on first run), reset
if state.corpus().count() < 1 {
state
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
.load_initial_inputs(
&mut fuzzer,
&mut executor,
&mut restarting_mgr,
&corpus_dirs,
)
.expect(&format!(
"Failed to load initial corpus at {:?}",
&corpus_dirs
@ -156,10 +173,10 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// However, you will lose a lot of performance that way.
let iters = 1_000_000;
fuzzer.fuzz_loop_for(
&mut stages,
&mut state,
&mut executor,
&mut restarting_mgr,
&scheduler,
iters,
)?;

View File

@ -6,15 +6,15 @@ use std::{env, path::PathBuf};
use libafl::{
bolts::tuples::tuple_list,
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
events::{setup_restarting_mgr_std, EventManager},
events::{setup_restarting_mgr_std, EventRestarter},
executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{MaxMapFeedback, ReachabilityFeedback},
feedbacks::{MapFeedbackState, MaxMapFeedback, ReachabilityFeedback},
fuzzer::{Fuzzer, StdFuzzer},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::token_mutations::Tokens,
observers::{HitcountsMapObserver, StdMapObserver},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State},
state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats,
utils::{current_nanos, StdRand},
Error,
@ -71,20 +71,28 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
let reachability_observer =
unsafe { StdMapObserver::new_from_ptr("png.c", __libafl_target_list, TARGET_SIZE) };
// The state of the edges feedback.
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
// Feedback to rate the interestingness of an input
let feedback = MaxMapFeedback::new(&feedback_state, &edges_observer);
// A feedback to choose if an input is a solution or not
let objective = ReachabilityFeedback::new(&reachability_observer);
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
State::new(
StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
MaxMapFeedback::new_tracking_with_observer(&edges_observer, true, false),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// Feedbacks to recognize an input as solution
ReachabilityFeedback::new_with_observer(&reachability_observer),
// States of the feedbacks.
// They are the data related to the feedbacks that you want to persist in the State.
tuple_list!(feedback_state),
)
});
@ -103,14 +111,14 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations());
let stage = StdMutationalStage::new(mutator);
// A fuzzer with just one stage
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
// A random policy to get testcasess from the corpus
let scheduler = RandCorpusScheduler::new();
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |buf: &[u8]| {
libfuzzer_test_one_input(buf);
@ -120,7 +128,8 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
let mut executor = InProcessExecutor::new(
&mut harness,
tuple_list!(edges_observer, reachability_observer,),
tuple_list!(edges_observer, reachability_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?;
@ -135,7 +144,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// In case the corpus is empty (on first run), reset
if state.corpus().count() < 1 {
state
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
.load_initial_inputs(
&mut fuzzer,
&mut executor,
&mut restarting_mgr,
&corpus_dirs,
)
.expect(&format!(
"Failed to load initial corpus at {:?}",
&corpus_dirs
@ -149,10 +163,10 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// However, you will lose a lot of performance that way.
let iters = 1_000_000;
fuzzer.fuzz_loop_for(
&mut stages,
&mut state,
&mut executor,
&mut restarting_mgr,
&scheduler,
iters,
)?;

View File

@ -12,13 +12,13 @@ use libafl::{
events::setup_restarting_mgr_std,
executors::{inprocess::InProcessExecutor, ExitKind},
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::token_mutations::Tokens,
observers::{StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State},
state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats,
utils::{current_nanos, StdRand},
Error,
@ -69,23 +69,34 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// The state of the edges feedback.
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
// Time feedback, this one does not need a feedback state
TimeFeedback::new_with_observer(&time_observer)
);
// A feedback to choose if an input is a solution or not
let objective = CrashFeedback::new();
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
State::new(
StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
feedback_or!(
MaxMapFeedback::new_tracking_with_observer(&edges_observer, true, false),
TimeFeedback::new_with_observer(&time_observer)
),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// Feedback to recognize an input as solution
CrashFeedback::new(),
// States of the feedbacks.
// They are the data related to the feedbacks that you want to persist in the State.
tuple_list!(feedback_state),
)
});
@ -104,14 +115,14 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations());
let stage = StdMutationalStage::new(mutator);
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |buf: &[u8]| {
libfuzzer_test_one_input(buf);
@ -122,6 +133,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
let mut executor = InProcessExecutor::new(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?;
@ -136,7 +148,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// In case the corpus is empty (on first run), reset
if state.corpus().count() < 1 {
state
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
.load_initial_inputs(
&mut fuzzer,
&mut executor,
&mut restarting_mgr,
&corpus_dirs,
)
.expect(&format!(
"Failed to load initial corpus at {:?}",
&corpus_dirs
@ -144,7 +161,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
println!("We imported {} inputs from disk.", state.corpus().count());
}
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?;
// Never reached
Ok(())

View File

@ -82,7 +82,7 @@ mod tests {
use crate::{
corpus::{Corpus, CorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, Testcase},
inputs::bytes::BytesInput,
state::{HasCorpus, State},
state::{HasCorpus, StdState},
utils::StdRand,
};
@ -103,7 +103,7 @@ mod tests {
OnDiskCorpus::<BytesInput>::new(PathBuf::from("target/.test/fancy/objective/path"))
.unwrap();
let mut state = State::new(rand, q, (), objective_q, ());
let mut state = StdState::new(rand, q, objective_q, ());
let next_idx = scheduler.next(&mut state).unwrap();
let filename = state

View File

@ -2,6 +2,7 @@
use alloc::{string::ToString, vec::Vec};
use core::{marker::PhantomData, time::Duration};
use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "std")]
@ -18,13 +19,12 @@ use crate::{
llmp::{self, Flags, LlmpClientDescription, LlmpSender, Tag},
shmem::ShMemProvider,
},
corpus::CorpusScheduler,
events::{BrokerEventResult, Event, EventManager},
events::{BrokerEventResult, Event, EventFirer, EventManager, EventProcessor, EventRestarter},
executors::Executor,
executors::ExitKind,
executors::{Executor, HasObservers},
fuzzer::{IfInteresting, IsInteresting},
inputs::Input,
observers::ObserversTuple,
state::IfInteresting,
stats::Stats,
Error,
};
@ -57,10 +57,10 @@ const _LLMP_TAG_NO_RESTART: llmp::Tag = 0x57A7EE71;
/// An [`EventManager`] that forwards all events to other attached fuzzers on shared maps or via tcp,
/// using low-level message passing, [`crate::bolts::llmp`].
#[derive(Debug)]
pub struct LlmpEventManager<I, S, SP, ST>
pub struct LlmpEventManager<I, OT, S, SP, ST>
where
I: Input,
S: IfInteresting<I>,
OT: ObserversTuple,
SP: ShMemProvider + 'static,
ST: Stats,
//CE: CustomEvent<I>,
@ -70,18 +70,18 @@ where
#[cfg(feature = "llmp_compression")]
compressor: GzipCompressor,
phantom: PhantomData<(I, S)>,
phantom: PhantomData<(I, OT, S)>,
}
/// The minimum buffer size at which to compress LLMP IPC messages.
#[cfg(feature = "llmp_compression")]
const COMPRESS_THRESHOLD: usize = 1024;
impl<I, S, SP, ST> Drop for LlmpEventManager<I, S, SP, ST>
impl<I, OT, S, SP, ST> Drop for LlmpEventManager<I, OT, S, SP, ST>
where
I: Input,
S: IfInteresting<I>,
SP: ShMemProvider,
OT: ObserversTuple,
SP: ShMemProvider + 'static,
ST: Stats,
{
/// LLMP clients will have to wait until their pages are mapped by somebody.
@ -90,11 +90,11 @@ where
}
}
impl<I, S, SP, ST> LlmpEventManager<I, S, SP, ST>
impl<I, OT, S, SP, ST> LlmpEventManager<I, OT, S, SP, ST>
where
I: Input,
S: IfInteresting<I>,
SP: ShMemProvider,
OT: ObserversTuple,
SP: ShMemProvider + 'static,
ST: Stats,
{
/// Create llmp on a port
@ -293,18 +293,16 @@ where
// Handle arriving events in the client
#[allow(clippy::unused_self)]
fn handle_in_client<CS, E, OT>(
fn handle_in_client<E, Z>(
&mut self,
fuzzer: &mut Z,
_executor: &mut E,
state: &mut S,
_sender_id: u32,
event: Event<I>,
_executor: &mut E,
scheduler: &CS,
) -> Result<(), Error>
where
CS: CorpusScheduler<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
Z: IfInteresting<I, S> + IsInteresting<I, OT, S>,
{
match event {
Event::NewTestcase {
@ -322,9 +320,10 @@ where
let observers: OT = postcard::from_bytes(&observers_buf)?;
// TODO include ExitKind in NewTestcase
let is_interesting = state.is_interesting(&input, &observers, &ExitKind::Ok)?;
if state
.add_if_interesting(&input, is_interesting, scheduler)?
let is_interesting =
fuzzer.is_interesting(state, &input, &observers, &ExitKind::Ok)?;
if fuzzer
.add_if_interesting(state, &input, is_interesting)?
.is_some()
{
#[cfg(feature = "std")]
@ -340,12 +339,49 @@ where
}
}
impl<I, S, SP, ST> EventManager<I, S> for LlmpEventManager<I, S, SP, ST>
impl<I, OT, S, SP, ST> EventFirer<I, S> for LlmpEventManager<I, OT, S, SP, ST>
where
I: Input,
S: IfInteresting<I>,
OT: ObserversTuple,
SP: ShMemProvider,
ST: Stats, //CE: CustomEvent<I>,
ST: Stats,
//CE: CustomEvent<I>,
{
#[cfg(feature = "llmp_compression")]
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
let serialized = postcard::to_allocvec(&event)?;
let flags: Flags = LLMP_FLAG_INITIALIZED;
match self.compressor.compress(&serialized)? {
Some(comp_buf) => {
self.llmp.send_buf_with_flags(
LLMP_TAG_EVENT_TO_BOTH,
&comp_buf,
flags | LLMP_FLAG_COMPRESSED,
)?;
}
None => {
self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?;
}
}
Ok(())
}
#[cfg(not(feature = "llmp_compression"))]
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
let serialized = postcard::to_allocvec(&event)?;
self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?;
Ok(())
}
}
impl<I, OT, S, SP, ST> EventRestarter<S> for LlmpEventManager<I, OT, S, SP, ST>
where
I: Input,
OT: ObserversTuple,
SP: ShMemProvider,
ST: Stats,
//CE: CustomEvent<I>,
{
/// The llmp client needs to wait until a broker mapped all pages, before shutting down.
/// Otherwise, the OS may already have removed the shared maps,
@ -355,18 +391,18 @@ where
client.await_save_to_unmap_blocking();
}
}
}
fn process<CS, E, OT>(
&mut self,
state: &mut S,
executor: &mut E,
scheduler: &CS,
) -> Result<usize, Error>
where
CS: CorpusScheduler<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
{
impl<E, I, OT, S, SP, ST, Z> EventProcessor<E, S, Z> for LlmpEventManager<I, OT, S, SP, ST>
where
SP: ShMemProvider,
ST: Stats,
E: Executor<I>,
I: Input,
OT: ObserversTuple,
Z: IfInteresting<I, S> + IsInteresting<I, OT, S>, //CE: CustomEvent<I>,
{
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
// TODO: Get around local event copy by moving handle_in_client
let mut events = vec![];
match &mut self.llmp {
@ -397,49 +433,34 @@ where
};
let count = events.len();
events.drain(..).try_for_each(|(sender_id, event)| {
self.handle_in_client(state, sender_id, event, executor, scheduler)
self.handle_in_client(fuzzer, executor, state, sender_id, event)
})?;
Ok(count)
}
}
#[cfg(feature = "llmp_compression")]
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
let serialized = postcard::to_allocvec(&event)?;
let flags: Flags = LLMP_FLAG_INITIALIZED;
match self.compressor.compress(&serialized)? {
Some(comp_buf) => {
self.llmp.send_buf_with_flags(
LLMP_TAG_EVENT_TO_BOTH,
&comp_buf,
flags | LLMP_FLAG_COMPRESSED,
)?;
}
None => {
self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?;
}
}
Ok(())
}
#[cfg(not(feature = "llmp_compression"))]
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
let serialized = postcard::to_allocvec(&event)?;
self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?;
Ok(())
}
impl<E, I, OT, S, SP, ST, Z> EventManager<E, I, S, Z> for LlmpEventManager<I, OT, S, SP, ST>
where
SP: ShMemProvider,
ST: Stats,
E: Executor<I>,
I: Input,
OT: ObserversTuple,
Z: IfInteresting<I, S> + IsInteresting<I, OT, S>, //CE: CustomEvent<I>,
{
}
/// Serialize the current state and corpus during an executiont to bytes.
/// On top, add the current llmp event manager instance to be restored
/// This method is needed when the fuzzer run crashes and has to restart.
pub fn serialize_state_mgr<I, S, SP, ST>(
pub fn serialize_state_mgr<I, OT, S, SP, ST>(
state: &S,
mgr: &LlmpEventManager<I, S, SP, ST>,
mgr: &LlmpEventManager<I, OT, S, SP, ST>,
) -> Result<Vec<u8>, Error>
where
I: Input,
S: Serialize + IfInteresting<I>,
OT: ObserversTuple,
S: Serialize,
SP: ShMemProvider,
ST: Stats,
{
@ -448,13 +469,14 @@ where
/// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)`
#[allow(clippy::type_complexity)]
pub fn deserialize_state_mgr<I, S, SP, ST>(
pub fn deserialize_state_mgr<I, OT, S, SP, ST>(
shmem_provider: SP,
state_corpus_serialized: &[u8],
) -> Result<(S, LlmpEventManager<I, S, SP, ST>), Error>
) -> Result<(S, LlmpEventManager<I, OT, S, SP, ST>), Error>
where
I: Input,
S: DeserializeOwned + IfInteresting<I>,
OT: ObserversTuple,
S: DeserializeOwned,
SP: ShMemProvider,
ST: Stats,
{
@ -467,32 +489,49 @@ where
/// A manager that can restart on the fly, storing states in-between (in `on_resatrt`)
#[derive(Debug)]
pub struct LlmpRestartingEventManager<I, S, SP, ST>
pub struct LlmpRestartingEventManager<I, OT, S, SP, ST>
where
I: Input,
S: IfInteresting<I>,
OT: ObserversTuple,
SP: ShMemProvider + 'static,
ST: Stats,
//CE: CustomEvent<I>,
{
/// The embedded llmp event manager
llmp_mgr: LlmpEventManager<I, S, SP, ST>,
llmp_mgr: LlmpEventManager<I, OT, S, SP, ST>,
/// The sender to serialize the state for the next runner
sender: LlmpSender<SP>,
}
impl<I, S, SP, ST> EventManager<I, S> for LlmpRestartingEventManager<I, S, SP, ST>
impl<I, OT, S, SP, ST> EventFirer<I, S> for LlmpRestartingEventManager<I, OT, S, SP, ST>
where
I: Input,
S: IfInteresting<I> + Serialize,
OT: ObserversTuple,
S: Serialize,
SP: ShMemProvider,
ST: Stats, //CE: CustomEvent<I>,
ST: Stats,
//CE: CustomEvent<I>,
{
fn fire(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error> {
// Check if we are going to crash in the event, in which case we store our current state for the next runner
self.llmp_mgr.fire(state, event)
}
}
impl<I, OT, S, SP, ST> EventRestarter<S> for LlmpRestartingEventManager<I, OT, S, SP, ST>
where
I: Input,
OT: ObserversTuple,
S: Serialize,
SP: ShMemProvider,
ST: Stats,
//CE: CustomEvent<I>,
{
/// The llmp client needs to wait until a broker mapped all pages, before shutting down.
/// Otherwise, the OS may already have removed the shared maps,
#[inline]
fn await_restart_safe(&mut self) {
self.llmp_mgr.await_restart_safe();
self.llmp_mgr.await_restart_safe()
}
/// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner.
@ -503,25 +542,36 @@ where
self.sender
.send_buf(_LLMP_TAG_RESTART, &state_corpus_serialized)
}
}
fn process<CS, E, OT>(
&mut self,
state: &mut S,
executor: &mut E,
scheduler: &CS,
) -> Result<usize, Error>
where
CS: CorpusScheduler<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
{
self.llmp_mgr.process(state, executor, scheduler)
impl<E, I, OT, S, SP, ST, Z> EventProcessor<E, S, Z>
for LlmpRestartingEventManager<I, OT, S, SP, ST>
where
E: Executor<I>,
I: Input,
Z: IfInteresting<I, S> + IsInteresting<I, OT, S>,
OT: ObserversTuple,
SP: ShMemProvider + 'static,
ST: Stats,
//CE: CustomEvent<I>,
{
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
self.llmp_mgr.process(fuzzer, state, executor)
}
}
fn fire(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error> {
// Check if we are going to crash in the event, in which case we store our current state for the next runner
self.llmp_mgr.fire(state, event)
}
impl<E, I, OT, S, SP, ST, Z> EventManager<E, I, S, Z>
for LlmpRestartingEventManager<I, OT, S, SP, ST>
where
E: Executor<I>,
I: Input,
S: Serialize,
Z: IfInteresting<I, S> + IsInteresting<I, OT, S>,
OT: ObserversTuple,
SP: ShMemProvider + 'static,
ST: Stats,
//CE: CustomEvent<I>,
{
}
/// The llmp connection from the actual fuzzer to the process supervising it
@ -530,15 +580,16 @@ const _ENV_FUZZER_RECEIVER: &str = &"_AFL_ENV_FUZZER_RECEIVER";
/// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages)
const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT";
impl<I, S, SP, ST> LlmpRestartingEventManager<I, S, SP, ST>
impl<I, OT, S, SP, ST> LlmpRestartingEventManager<I, OT, S, SP, ST>
where
I: Input,
S: IfInteresting<I>,
SP: ShMemProvider,
ST: Stats, //CE: CustomEvent<I>,
OT: ObserversTuple,
SP: ShMemProvider + 'static,
ST: Stats,
//CE: CustomEvent<I>,
{
/// Create a new runner, the executed child doing the actual fuzzing.
pub fn new(llmp_mgr: LlmpEventManager<I, S, SP, ST>, sender: LlmpSender<SP>) -> Self {
pub fn new(llmp_mgr: LlmpEventManager<I, OT, S, SP, ST>, sender: LlmpSender<SP>) -> Self {
Self { llmp_mgr, sender }
}
@ -558,26 +609,26 @@ where
/// The restarter will spawn a new process each time the child crashes or timeouts.
#[cfg(feature = "std")]
#[allow(clippy::type_complexity)]
pub fn setup_restarting_mgr_std<I, S, ST>(
//mgr: &mut LlmpEventManager<I, S, SH, ST>,
pub fn setup_restarting_mgr_std<I, OT, S, ST>(
stats: ST,
broker_port: u16,
) -> Result<
(
Option<S>,
LlmpRestartingEventManager<I, S, StdShMemProvider, ST>,
LlmpRestartingEventManager<I, OT, S, StdShMemProvider, ST>,
),
Error,
>
where
I: Input,
S: DeserializeOwned + IfInteresting<I>,
OT: ObserversTuple,
S: DeserializeOwned,
ST: Stats,
{
#[cfg(target_os = "android")]
AshmemService::start().expect("Error starting Ashmem Service");
setup_restarting_mgr(StdShMemProvider::new()?, stats, broker_port)
setup_restarting_mgr::<I, OT, S, _, ST>(StdShMemProvider::new()?, stats, broker_port)
}
/// A restarting state is a combination of restarter and runner, that can be used on systems with and without `fork` support.
@ -588,20 +639,25 @@ where
clippy::type_complexity,
clippy::similar_names
)] // for { mgr = LlmpEventManager... }
pub fn setup_restarting_mgr<I, S, SP, ST>(
pub fn setup_restarting_mgr<I, OT, S, SP, ST>(
mut shmem_provider: SP,
//mgr: &mut LlmpEventManager<I, S, SH, ST>,
//mgr: &mut LlmpEventManager<I, OT, S, SH, ST>,
stats: ST,
broker_port: u16,
) -> Result<(Option<S>, LlmpRestartingEventManager<I, S, SP, ST>), Error>
) -> Result<(Option<S>, LlmpRestartingEventManager<I, OT, S, SP, ST>), Error>
where
I: Input,
S: DeserializeOwned + IfInteresting<I>,
SP: ShMemProvider,
S: DeserializeOwned,
OT: ObserversTuple,
SP: ShMemProvider + 'static,
ST: Stats,
//CE: CustomEvent<I>,
{
let mut mgr =
LlmpEventManager::<I, S, SP, ST>::new_on_port(shmem_provider.clone(), stats, broker_port)?;
let mut mgr = LlmpEventManager::<I, OT, S, SP, ST>::new_on_port(
shmem_provider.clone(),
stats,
broker_port,
)?;
// We start ourself as child process to actually fuzz
let (sender, mut receiver, mut new_shmem_provider) = if std::env::var(_ENV_FUZZER_SENDER)
@ -684,7 +740,7 @@ where
None => {
println!("First run. Let's set it all up");
// Mgr to send and receive msgs from/to all other fuzzer instances
let client_mgr = LlmpEventManager::<I, S, SP, ST>::existing_client_from_env(
let client_mgr = LlmpEventManager::<I, OT, S, SP, ST>::existing_client_from_env(
new_shmem_provider,
_ENV_FUZZER_BROKER_CLIENT_INITIAL,
)?;
@ -694,7 +750,7 @@ where
// Restoring from a previous run, deserialize state and corpus.
Some((_sender, _tag, msg)) => {
println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len());
let (state, mgr): (S, LlmpEventManager<I, S, SP, ST>) =
let (state, mgr): (S, LlmpEventManager<I, OT, S, SP, ST>) =
deserialize_state_mgr(new_shmem_provider, &msg)?;
(Some(state), LlmpRestartingEventManager::new(mgr, sender))

View File

@ -9,13 +9,7 @@ use alloc::{string::String, vec::Vec};
use core::{fmt, marker::PhantomData, time::Duration};
use serde::{Deserialize, Serialize};
use crate::{
corpus::CorpusScheduler,
executors::{Executor, HasObservers},
inputs::Input,
observers::ObserversTuple,
Error,
};
use crate::{inputs::Input, observers::ObserversTuple, Error};
#[cfg(feature = "introspection")]
use crate::stats::ClientPerfStats;
@ -175,27 +169,32 @@ where
}
}
/// [`EventManager`] is the main communications hub.
/// For the "normal" multi-processed mode, you may want to look into `RestartingEventManager`
pub trait EventManager<I, S>
/// [`EventFirer`] fire an event.
pub trait EventFirer<I, S>
where
I: Input,
{
/// Fire an Event
//fn fire<'a>(&mut self, event: Event<I>) -> Result<(), Error>;
/// Send off an event to the broker
fn fire(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error>;
}
pub trait EventRestarter<S> {
/// For restarting event managers, implement a way to forward state to their next peers.
#[inline]
fn on_restart(&mut self, _state: &mut S) -> Result<(), Error> {
Ok(())
}
/// Block until we are safe to exit.
#[inline]
fn await_restart_safe(&mut self) {}
}
/// [`EventProcessor`] process all the incoming messages
pub trait EventProcessor<E, S, Z> {
/// Lookup for incoming events and process them.
/// Return the number of processes events or an error
fn process<CS, E, OT>(
&mut self,
state: &mut S,
executor: &mut E,
scheduler: &CS,
) -> Result<usize, Error>
where
CS: CorpusScheduler<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple;
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error>;
/// Serialize all observers for this type and manager
fn serialize_observers<OT>(&mut self, observers: &OT) -> Result<Vec<u8>, Error>
@ -212,49 +211,45 @@ where
{
Ok(postcard::from_bytes(observers_buf)?)
}
}
/// For restarting event managers, implement a way to forward state to their next peers.
#[inline]
fn on_restart(&mut self, _state: &mut S) -> Result<(), Error> {
Ok(())
}
/// Block until we are safe to exit.
#[inline]
fn await_restart_safe(&mut self) {}
/// Send off an event to the broker
fn fire(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error>;
/// [`EventManager`] is the main communications hub.
/// For the "normal" multi-processed mode, you may want to look into `RestartingEventManager`
pub trait EventManager<E, I, S, Z>:
EventFirer<I, S> + EventProcessor<E, S, Z> + EventRestarter<S>
where
I: Input,
{
}
/// An eventmgr for tests, and as placeholder if you really don't need an event manager.
#[derive(Copy, Clone, Debug)]
pub struct NopEventManager<I, S> {
phantom: PhantomData<(I, S)>,
}
impl<I, S> EventManager<I, S> for NopEventManager<I, S>
pub struct NopEventManager {}
impl<I, S> EventFirer<I, S> for NopEventManager
where
I: Input,
{
fn process<CS, E, OT>(
&mut self,
_state: &mut S,
_executor: &mut E,
_scheduler: &CS,
) -> Result<usize, Error>
where
CS: CorpusScheduler<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
{
Ok(0)
}
fn fire(&mut self, _state: &mut S, _event: Event<I>) -> Result<(), Error> {
Ok(())
}
}
impl<S> EventRestarter<S> for NopEventManager {}
impl<E, S, Z> EventProcessor<E, S, Z> for NopEventManager {
fn process(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_executor: &mut E,
) -> Result<usize, Error> {
Ok(0)
}
}
impl<E, I, S, Z> EventManager<E, I, S, Z> for NopEventManager where I: Input {}
#[cfg(test)]
mod tests {

View File

@ -1,20 +1,16 @@
//! A very simple event manager, that just supports log outputs, but no multiprocessing
use alloc::{string::ToString, vec::Vec};
use core::marker::PhantomData;
use crate::{
corpus::CorpusScheduler,
events::{BrokerEventResult, Event, EventManager},
executors::{Executor, HasObservers},
events::{BrokerEventResult, Event, EventFirer, EventManager, EventProcessor, EventRestarter},
inputs::Input,
observers::ObserversTuple,
stats::Stats,
Error,
};
/// A simple, single-threaded event manager that just logs
#[derive(Clone, Debug)]
pub struct SimpleEventManager<I, S, ST>
pub struct SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, //CE: CustomEvent<I, OT>,
@ -23,33 +19,13 @@ where
stats: ST,
/// The events that happened since the last handle_in_broker
events: Vec<Event<I>>,
phantom: PhantomData<S>,
}
impl<I, S, ST> EventManager<I, S> for SimpleEventManager<I, S, ST>
impl<I, S, ST> EventFirer<I, S> for SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, //CE: CustomEvent<I, OT>,
{
fn process<CS, E, OT>(
&mut self,
state: &mut S,
_executor: &mut E,
_scheduler: &CS,
) -> Result<usize, Error>
where
CS: CorpusScheduler<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
{
let count = self.events.len();
while !self.events.is_empty() {
let event = self.events.pop().unwrap();
self.handle_in_client(state, event)?;
}
Ok(count)
}
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
match Self::handle_in_broker(&mut self.stats, &event)? {
BrokerEventResult::Forward => self.events.push(event),
@ -59,7 +35,41 @@ where
}
}
impl<I, S, ST> SimpleEventManager<I, S, ST>
impl<I, S, ST> EventRestarter<S> for SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, //CE: CustomEvent<I, OT>,
{
}
impl<E, I, S, ST, Z> EventProcessor<E, S, Z> for SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, //CE: CustomEvent<I, OT>,
{
fn process(
&mut self,
_fuzzer: &mut Z,
state: &mut S,
_executor: &mut E,
) -> Result<usize, Error> {
let count = self.events.len();
while !self.events.is_empty() {
let event = self.events.pop().unwrap();
self.handle_in_client(state, event)?;
}
Ok(count)
}
}
impl<E, I, S, ST, Z> EventManager<E, I, S, Z> for SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, //CE: CustomEvent<I, OT>,
{
}
impl<I, ST> SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, //TODO CE: CustomEvent,
@ -69,7 +79,6 @@ where
Self {
stats,
events: vec![],
phantom: PhantomData,
}
}
@ -141,7 +150,7 @@ where
// Handle arriving events in the client
#[allow(clippy::needless_pass_by_value, clippy::unused_self)]
fn handle_in_client(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
fn handle_in_client<S>(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
Err(Error::Unknown(format!(
"Received illegal message that message should not have arrived: {:?}.",
event

View File

@ -15,19 +15,20 @@ use crate::bolts::os::windows_exceptions::setup_exception_handler;
use crate::{
corpus::Corpus,
events::EventManager,
events::{EventFirer, EventRestarter},
executors::{
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
state::{HasObjective, HasSolutions},
state::HasSolutions,
Error,
};
/// The inmem executor simply calls a target function, then returns afterwards.
pub struct InProcessExecutor<'a, EM, H, I, OT, S>
pub struct InProcessExecutor<'a, H, I, OT, S>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
@ -37,10 +38,10 @@ where
harness_fn: &'a mut H,
/// The observers, observing each run
observers: OT,
phantom: PhantomData<(EM, I, S)>,
phantom: PhantomData<(I, S)>,
}
impl<'a, EM, H, I, OT, S> Executor<I> for InProcessExecutor<'a, EM, H, I, OT, S>
impl<'a, H, I, OT, S> Executor<I> for InProcessExecutor<'a, H, I, OT, S>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
@ -54,14 +55,20 @@ where
}
}
impl<'a, EM, H, I, OT, S> HasExecHooks<EM, I, S> for InProcessExecutor<'a, EM, H, I, OT, S>
impl<'a, EM, H, I, OT, S, Z> HasExecHooks<EM, I, S, Z> for InProcessExecutor<'a, H, I, OT, S>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
#[inline]
fn pre_exec(&mut self, _state: &mut S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> {
fn pre_exec(
&mut self,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
#[cfg(unix)]
unsafe {
let data = &mut unix_signal_handler::GLOBAL_STATE;
@ -75,8 +82,9 @@ where
);
// Direct raw pointers access /aliasing is pretty undefined behavior.
// Since the state and event may have moved in memory, refresh them right before the signal may happen
write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void);
write_volatile(&mut data.event_mgr_ptr, _event_mgr as *mut _ as *mut c_void);
write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void);
write_volatile(&mut data.event_mgr_ptr, event_mgr as *mut _ as *mut c_void);
write_volatile(&mut data.fuzzer_ptr, fuzzer as *mut _ as *mut c_void);
compiler_fence(Ordering::SeqCst);
}
#[cfg(windows)]
@ -92,15 +100,22 @@ where
);
// Direct raw pointers access /aliasing is pretty undefined behavior.
// Since the state and event may have moved in memory, refresh them right before the signal may happen
write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void);
write_volatile(&mut data.event_mgr_ptr, _event_mgr as *mut _ as *mut c_void);
write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void);
write_volatile(&mut data.event_mgr_ptr, event_mgr as *mut _ as *mut c_void);
write_volatile(&mut data.fuzzer_ptr, fuzzer as *mut _ as *mut c_void);
compiler_fence(Ordering::SeqCst);
}
Ok(())
}
#[inline]
fn post_exec(&mut self, _state: &mut S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> {
fn post_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
#[cfg(unix)]
unsafe {
write_volatile(
@ -121,7 +136,7 @@ where
}
}
impl<'a, EM, H, I, OT, S> HasObservers<OT> for InProcessExecutor<'a, EM, H, I, OT, S>
impl<'a, H, I, OT, S> HasObservers<OT> for InProcessExecutor<'a, H, I, OT, S>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
@ -138,15 +153,16 @@ where
}
}
impl<'a, EM, H, I, OT, S> HasObserversHooks<EM, I, OT, S> for InProcessExecutor<'a, EM, H, I, OT, S>
impl<'a, EM, H, I, OT, S, Z> HasObserversHooks<EM, I, OT, S, Z>
for InProcessExecutor<'a, H, I, OT, S>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
{
}
impl<'a, EM, H, I, OT, S> InProcessExecutor<'a, EM, H, I, OT, S>
impl<'a, H, I, OT, S> InProcessExecutor<'a, H, I, OT, S>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
@ -158,28 +174,30 @@ where
/// * `harness_fn` - the harness, executiong the function
/// * `observers` - the observers observing the target during execution
/// This may return an error on unix, if signal handler setup fails
pub fn new<OC, OF>(
pub fn new<EM, OC, OF, Z>(
harness_fn: &'a mut H,
observers: OT,
_fuzzer: &mut Z,
_state: &mut S,
_event_mgr: &mut EM,
) -> Result<Self, Error>
where
EM: EventManager<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OC: Corpus<I>,
OF: Feedback<I>,
S: HasObjective<OF, I> + HasSolutions<OC, I>,
OF: Feedback<I, S>,
S: HasSolutions<OC, I>,
Z: HasObjective<I, OF, S>,
{
#[cfg(unix)]
unsafe {
let data = &mut unix_signal_handler::GLOBAL_STATE;
write_volatile(
&mut data.crash_handler,
unix_signal_handler::inproc_crash_handler::<EM, I, OC, OF, OT, S>,
unix_signal_handler::inproc_crash_handler::<EM, I, OC, OF, OT, S, Z>,
);
write_volatile(
&mut data.timeout_handler,
unix_signal_handler::inproc_timeout_handler::<EM, I, OC, OF, OT, S>,
unix_signal_handler::inproc_timeout_handler::<EM, I, OC, OF, OT, S, Z>,
);
setup_signal_handler(data)?;
@ -190,11 +208,11 @@ where
let data = &mut windows_exception_handler::GLOBAL_STATE;
write_volatile(
&mut data.crash_handler,
windows_exception_handler::inproc_crash_handler::<EM, I, OC, OF, OT, S>,
windows_exception_handler::inproc_crash_handler::<EM, I, OC, OF, OT, S, Z>,
);
//write_volatile(
// &mut data.timeout_handler,
// windows_exception_handler::inproc_timeout_handler::<EM, I, OC, OF, OT, S>,
// windows_exception_handler::inproc_timeout_handler::<EM, I, OC, OF, OT, S, Z>,
//);
setup_exception_handler(data)?;
@ -232,12 +250,13 @@ mod unix_signal_handler {
use crate::{
bolts::os::unix_signals::{Handler, Signal},
corpus::{Corpus, Testcase},
events::{Event, EventManager},
events::{Event, EventFirer, EventRestarter},
executors::ExitKind,
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
state::{HasObjective, HasSolutions},
state::HasSolutions,
};
// TODO merge GLOBAL_STATE with the Windows one
@ -248,6 +267,8 @@ mod unix_signal_handler {
state_ptr: ptr::null_mut(),
/// The event manager ptr for signal handling
event_mgr_ptr: ptr::null_mut(),
/// The fuzzer ptr for signal handling
fuzzer_ptr: ptr::null_mut(),
/// The observers ptr for signal handling
observers_ptr: ptr::null(),
/// The current input for signal handling
@ -261,6 +282,7 @@ mod unix_signal_handler {
pub struct InProcessExecutorHandlerData {
pub state_ptr: *mut c_void,
pub event_mgr_ptr: *mut c_void,
pub fuzzer_ptr: *mut c_void,
pub observers_ptr: *const c_void,
pub current_input_ptr: *const c_void,
pub crash_handler: unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut Self),
@ -308,21 +330,23 @@ mod unix_signal_handler {
}
#[cfg(unix)]
pub unsafe fn inproc_timeout_handler<EM, I, OC, OF, OT, S>(
pub unsafe fn inproc_timeout_handler<EM, I, OC, OF, OT, S, Z>(
_signal: Signal,
_info: siginfo_t,
_context: &mut ucontext_t,
data: &mut InProcessExecutorHandlerData,
) where
EM: EventManager<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OT: ObserversTuple,
OC: Corpus<I>,
OF: Feedback<I>,
S: HasObjective<OF, I> + HasSolutions<OC, I>,
OF: Feedback<I, S>,
S: HasSolutions<OC, I>,
I: Input + HasTargetBytes,
Z: HasObjective<I, OF, S>,
{
let state = (data.state_ptr as *mut S).as_mut().unwrap();
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap();
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
if data.current_input_ptr.is_null() {
@ -337,15 +361,16 @@ mod unix_signal_handler {
let input = (data.current_input_ptr as *const I).as_ref().unwrap();
data.current_input_ptr = ptr::null();
let interesting = state
let interesting = fuzzer
.objective_mut()
.is_interesting(&input, observers, &ExitKind::Timeout)
.is_interesting(state, &input, observers, &ExitKind::Timeout)
.expect("In timeout handler objective failure.");
if interesting {
let mut new_testcase = Testcase::new(input.clone());
state
fuzzer
.objective_mut()
.append_metadata(&mut new_testcase)
.append_metadata(state, &mut new_testcase)
.expect("Failed adding metadata");
state
.solutions_mut()
@ -379,18 +404,19 @@ mod unix_signal_handler {
/// Will be used for signal handling.
/// It will store the current State to shmem, then exit.
#[allow(clippy::too_many_lines)]
pub unsafe fn inproc_crash_handler<EM, I, OC, OF, OT, S>(
pub unsafe fn inproc_crash_handler<EM, I, OC, OF, OT, S, Z>(
_signal: Signal,
_info: siginfo_t,
_context: &mut ucontext_t,
data: &mut InProcessExecutorHandlerData,
) where
EM: EventManager<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OT: ObserversTuple,
OC: Corpus<I>,
OF: Feedback<I>,
S: HasObjective<OF, I> + HasSolutions<OC, I>,
OF: Feedback<I, S>,
S: HasSolutions<OC, I>,
I: Input + HasTargetBytes,
Z: HasObjective<I, OF, S>,
{
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
let _context = *(((_context as *mut _ as *mut c_void as usize) + 128) as *mut c_void
@ -433,6 +459,7 @@ mod unix_signal_handler {
} else {
let state = (data.state_ptr as *mut S).as_mut().unwrap();
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap();
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
#[cfg(feature = "std")]
@ -483,16 +510,17 @@ mod unix_signal_handler {
// Make sure we don't crash in the crash handler forever.
data.current_input_ptr = ptr::null();
let interesting = state
let interesting = fuzzer
.objective_mut()
.is_interesting(&input, observers, &ExitKind::Crash)
.is_interesting(state, &input, observers, &ExitKind::Crash)
.expect("In crash handler objective failure.");
if interesting {
let new_input = input.clone();
let mut new_testcase = Testcase::new(new_input);
state
fuzzer
.objective_mut()
.append_metadata(&mut new_testcase)
.append_metadata(state, &mut new_testcase)
.expect("Failed adding metadata");
state
.solutions_mut()
@ -536,12 +564,13 @@ mod windows_exception_handler {
},
},
corpus::{Corpus, Testcase},
events::{Event, EventManager},
events::{Event, EventFirer, EventRestarter},
executors::ExitKind,
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
state::{HasObjective, HasSolutions},
state::HasSolutions,
};
/// Signal handling on unix systems needs some nasty unsafe.
@ -550,6 +579,8 @@ mod windows_exception_handler {
state_ptr: ptr::null_mut(),
/// The event manager ptr for signal handling
event_mgr_ptr: ptr::null_mut(),
/// The fuzzer ptr for signal handling
fuzzer_ptr: ptr::null_mut(),
/// The observers ptr for signal handling
observers_ptr: ptr::null(),
/// The current input for signal handling
@ -563,6 +594,7 @@ mod windows_exception_handler {
pub struct InProcessExecutorHandlerData {
pub state_ptr: *mut c_void,
pub event_mgr_ptr: *mut c_void,
pub fuzzer_ptr: *mut c_void,
pub observers_ptr: *const c_void,
pub current_input_ptr: *const c_void,
pub crash_handler: unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut Self),
@ -592,23 +624,25 @@ mod windows_exception_handler {
}
}
pub unsafe fn inproc_crash_handler<EM, I, OC, OF, OT, S>(
pub unsafe fn inproc_crash_handler<EM, I, OC, OF, OT, S, Z>(
code: ExceptionCode,
exception_pointers: *mut EXCEPTION_POINTERS,
data: &mut InProcessExecutorHandlerData,
) where
EM: EventManager<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OT: ObserversTuple,
OC: Corpus<I>,
OF: Feedback<I>,
S: HasObjective<OF, I> + HasSolutions<OC, I>,
OF: Feedback<I, S>,
S: HasSolutions<OC, I>,
I: Input + HasTargetBytes,
Z: HasObjective<I, OF, S>,
{
#[cfg(feature = "std")]
println!("Crashed with {}", code);
if !data.current_input_ptr.is_null() {
let state = (data.state_ptr as *mut S).as_mut().unwrap();
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap();
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
#[cfg(feature = "std")]
@ -620,16 +654,17 @@ mod windows_exception_handler {
// Make sure we don't crash in the crash handler forever.
data.current_input_ptr = ptr::null();
let interesting = state
let interesting = fuzzer
.objective_mut()
.is_interesting(&input, observers, &ExitKind::Crash)
.is_interesting(state, &input, observers, &ExitKind::Crash)
.expect("In crash handler objective failure.");
if interesting {
let new_input = input.clone();
let mut new_testcase = Testcase::new(new_input);
state
fuzzer
.objective_mut()
.append_metadata(&mut new_testcase)
.append_metadata(state, &mut new_testcase)
.expect("Failed adding metadata");
state
.solutions_mut()
@ -688,7 +723,6 @@ mod windows_exception_handler {
#[cfg(test)]
mod tests {
use core::marker::PhantomData;
use crate::{
@ -701,7 +735,7 @@ mod tests {
fn test_inmem_exec() {
let mut harness = |_buf: &[u8]| ExitKind::Ok;
let mut in_process_executor = InProcessExecutor::<(), _, NopInput, (), ()> {
let mut in_process_executor = InProcessExecutor::<_, NopInput, (), ()> {
harness_fn: &mut harness,
observers: tuple_list!(),
phantom: PhantomData,

View File

@ -35,52 +35,100 @@ pub enum ExitKind {
}
/// Pre and post exec hooks
pub trait HasExecHooks<EM, I, S> {
pub trait HasExecHooks<EM, I, S, Z> {
/// Called right before exexution starts
#[inline]
fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> {
fn pre_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
Ok(())
}
/// Called right after execution finished.
#[inline]
fn post_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> {
fn post_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
Ok(())
}
}
/// A haskell-style tuple of objects that have pre and post exec hooks
pub trait HasExecHooksTuple<EM, I, S> {
pub trait HasExecHooksTuple<EM, I, S, Z> {
/// This is called right before the next execution.
fn pre_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error>;
fn pre_exec_all(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error>;
/// This is called right after the last execution
fn post_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error>;
fn post_exec_all(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error>;
}
impl<EM, I, S> HasExecHooksTuple<EM, I, S> for () {
fn pre_exec_all(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> {
impl<EM, I, S, Z> HasExecHooksTuple<EM, I, S, Z> for () {
fn pre_exec_all(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
Ok(())
}
fn post_exec_all(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> {
fn post_exec_all(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
Ok(())
}
}
impl<EM, I, S, Head, Tail> HasExecHooksTuple<EM, I, S> for (Head, Tail)
impl<EM, I, S, Z, Head, Tail> HasExecHooksTuple<EM, I, S, Z> for (Head, Tail)
where
Head: HasExecHooks<EM, I, S>,
Tail: HasExecHooksTuple<EM, I, S>,
Head: HasExecHooks<EM, I, S, Z>,
Tail: HasExecHooksTuple<EM, I, S, Z>,
{
fn pre_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
self.0.pre_exec(state, mgr, input)?;
self.1.pre_exec_all(state, mgr, input)
fn pre_exec_all(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error> {
self.0.pre_exec(fuzzer, state, mgr, input)?;
self.1.pre_exec_all(fuzzer, state, mgr, input)
}
fn post_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
self.0.post_exec(state, mgr, input)?;
self.1.post_exec_all(state, mgr, input)
fn post_exec_all(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error> {
self.0.post_exec(fuzzer, state, mgr, input)?;
self.1.post_exec_all(fuzzer, state, mgr, input)
}
}
@ -97,20 +145,33 @@ where
}
/// Execute the exec hooks of the observers if they all implement [`HasExecHooks`].
pub trait HasObserversHooks<EM, I, OT, S>: HasObservers<OT>
pub trait HasObserversHooks<EM, I, OT, S, Z>: HasObservers<OT>
where
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
{
/// Run the pre exec hook for all [`crate::observers::Observer`]`s` linked to this [`Executor`].
#[inline]
fn pre_exec_observers(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
self.observers_mut().pre_exec_all(state, mgr, input)
fn pre_exec_observers(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error> {
self.observers_mut().pre_exec_all(fuzzer, state, mgr, input)
}
/// Run the post exec hook for all the [`crate::observers::Observer`]`s` linked to this [`Executor`].
#[inline]
fn post_exec_observers(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
self.observers_mut().post_exec_all(state, mgr, input)
fn post_exec_observers(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error> {
self.observers_mut()
.post_exec_all(fuzzer, state, mgr, input)
}
}
@ -142,7 +203,8 @@ where
}
}
impl<EM, I, S> HasExecHooks<EM, I, S> for NopExecutor<EM, I, S> where I: Input + HasTargetBytes {}
impl<EM, I, S, Z> HasExecHooks<EM, I, S, Z> for NopExecutor<EM, I, S> where I: Input + HasTargetBytes
{}
#[cfg(test)]
mod test {

View File

@ -97,21 +97,27 @@ where
}
}
impl<E, EM, I, OT, S> HasObserversHooks<EM, I, OT, S> for TimeoutExecutor<E, I>
impl<E, EM, I, OT, S, Z> HasObserversHooks<EM, I, OT, S, Z> for TimeoutExecutor<E, I>
where
E: Executor<I> + HasObservers<OT>,
I: Input,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
{
}
impl<E, EM, I, S> HasExecHooks<EM, I, S> for TimeoutExecutor<E, I>
impl<E, EM, I, S, Z> HasExecHooks<EM, I, S, Z> for TimeoutExecutor<E, I>
where
E: Executor<I> + HasExecHooks<EM, I, S>,
E: Executor<I> + HasExecHooks<EM, I, S, Z>,
I: Input,
{
#[inline]
fn pre_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
fn pre_exec(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error> {
#[cfg(unix)]
unsafe {
let milli_sec = self.exec_tmout.as_millis();
@ -137,11 +143,17 @@ where
// TODO
let _ = self.exec_tmout.as_millis();
}
self.executor.pre_exec(state, mgr, input)
self.executor.pre_exec(fuzzer, state, mgr, input)
}
#[inline]
fn post_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
fn post_exec(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error> {
#[cfg(unix)]
unsafe {
let it_value = Timeval {
@ -165,6 +177,6 @@ where
{
// TODO
}
self.executor.post_exec(state, mgr, input)
self.executor.post_exec(fuzzer, state, mgr, input)
}
}

View File

@ -12,18 +12,18 @@ use crate::{
bolts::tuples::Named,
corpus::Testcase,
executors::ExitKind,
feedbacks::Feedback,
feedbacks::{Feedback, FeedbackState, FeedbackStatesTuple},
inputs::Input,
observers::{MapObserver, ObserversTuple},
state::HasMetadata,
state::{HasFeedbackStates, HasMetadata},
utils::AsSlice,
Error,
};
/// A [`MapFeedback`] that strives to maximize the map contents.
pub type MaxMapFeedback<O, T> = MapFeedback<O, MaxReducer, T>;
pub type MaxMapFeedback<FT, O, S, T> = MapFeedback<FT, O, MaxReducer, S, T>;
/// A [`MapFeedback`] that strives to minimize the map contents.
pub type MinMapFeedback<O, T> = MapFeedback<O, MinReducer, T>;
pub type MinMapFeedback<FT, O, S, T> = MapFeedback<FT, O, MinReducer, S, T>;
/// A Reducer function is used to aggregate values for the novelty search
pub trait Reducer<T>: Serialize + serde::de::DeserializeOwned + 'static
@ -118,36 +118,104 @@ impl MapNoveltiesMetadata {
}
}
/// The state of [`MapFeedback`]
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "T: serde::de::DeserializeOwned")]
pub struct MapFeedbackState<T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{
/// Contains information about untouched entries
pub history_map: Vec<T>,
/// Name identifier of this instance
pub name: String,
}
impl<T> FeedbackState for MapFeedbackState<T> where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned
{
}
impl<T> Named for MapFeedbackState<T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{
#[inline]
fn name(&self) -> &str {
self.name.as_str()
}
}
impl<T> MapFeedbackState<T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{
/// Create new `MapFeedbackState`
#[must_use]
pub fn new(name: &'static str, map_size: usize) -> Self {
Self {
history_map: vec![T::default(); map_size],
name: name.to_string(),
}
}
/// Create new `MapFeedbackState` for the observer type.
pub fn with_observer<O>(map_observer: &O) -> Self
where
O: MapObserver<T>,
{
Self {
history_map: vec![T::default(); map_observer.map().len()],
name: map_observer.name().to_string(),
}
}
/// Create new `MapFeedbackState` using a name and a map.
/// The map can be shared.
#[must_use]
pub fn with_history_map(name: &'static str, history_map: Vec<T>) -> Self {
Self {
history_map,
name: name.to_string(),
}
}
}
/// The most common AFL-like feedback type
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "T: serde::de::DeserializeOwned")]
pub struct MapFeedback<O, R, T>
pub struct MapFeedback<FT, O, R, S, T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
R: Reducer<T>,
O: MapObserver<T>,
S: HasFeedbackStates<FT>,
FT: FeedbackStatesTuple,
{
/// Contains information about untouched entries
history_map: Vec<T>,
/// Indexes used in the last observation
indexes: Option<Vec<usize>>,
/// New indexes observed in the last observation
novelties: Option<Vec<usize>>,
/// Name identifier of this instance
name: String,
/// Name identifier of the observer
observer_name: String,
/// Phantom Data of Reducer
phantom: PhantomData<(R, O)>,
phantom: PhantomData<(FT, S, R, O, T)>,
}
impl<O, R, T, I> Feedback<I> for MapFeedback<O, R, T>
impl<I, FT, O, R, S, T> Feedback<I, S> for MapFeedback<FT, O, R, S, T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
R: Reducer<T>,
O: MapObserver<T>,
I: Input,
S: HasFeedbackStates<FT>,
FT: FeedbackStatesTuple,
{
fn is_interesting<OT>(
&mut self,
state: &mut S,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
@ -157,24 +225,29 @@ where
{
let mut interesting = false;
// TODO Replace with match_name_type when stable
let observer = observers.match_name::<O>(&self.name).unwrap();
let observer = observers.match_name::<O>(&self.observer_name).unwrap();
let size = observer.usable_count();
let initial = observer.initial();
let map_state = state
.feedback_states_mut()
.match_name_mut::<MapFeedbackState<T>>(&self.name)
.unwrap();
if self.indexes.is_none() && self.novelties.is_none() {
for i in 0..size {
let history = self.history_map[i];
let history = map_state.history_map[i];
let item = observer.map()[i];
let reduced = R::reduce(history, item);
if history != reduced {
self.history_map[i] = reduced;
map_state.history_map[i] = reduced;
interesting = true;
}
}
} else if self.indexes.is_some() && self.novelties.is_none() {
for i in 0..size {
let history = self.history_map[i];
let history = map_state.history_map[i];
let item = observer.map()[i];
if item != initial {
self.indexes.as_mut().unwrap().push(i);
@ -182,25 +255,25 @@ where
let reduced = R::reduce(history, item);
if history != reduced {
self.history_map[i] = reduced;
map_state.history_map[i] = reduced;
interesting = true;
}
}
} else if self.indexes.is_none() && self.novelties.is_some() {
for i in 0..size {
let history = self.history_map[i];
let history = map_state.history_map[i];
let item = observer.map()[i];
let reduced = R::reduce(history, item);
if history != reduced {
self.history_map[i] = reduced;
map_state.history_map[i] = reduced;
interesting = true;
self.novelties.as_mut().unwrap().push(i);
}
}
} else {
for i in 0..size {
let history = self.history_map[i];
let history = map_state.history_map[i];
let item = observer.map()[i];
if item != initial {
self.indexes.as_mut().unwrap().push(i);
@ -208,7 +281,7 @@ where
let reduced = R::reduce(history, item);
if history != reduced {
self.history_map[i] = reduced;
map_state.history_map[i] = reduced;
interesting = true;
self.novelties.as_mut().unwrap().push(i);
}
@ -218,7 +291,7 @@ where
Ok(interesting)
}
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
if let Some(v) = self.indexes.as_mut() {
let meta = MapIndexesMetadata::new(core::mem::take(v));
testcase.add_metadata(meta);
@ -231,7 +304,7 @@ where
}
/// Discard the stored metadata in case that the testcase is not added to the corpus
fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> {
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
if let Some(v) = self.indexes.as_mut() {
v.clear();
}
@ -242,11 +315,13 @@ where
}
}
impl<O, R, T> Named for MapFeedback<O, R, T>
impl<FT, O, R, S, T> Named for MapFeedback<FT, O, R, S, T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
R: Reducer<T>,
O: MapObserver<T>,
S: HasFeedbackStates<FT>,
FT: FeedbackStatesTuple,
{
#[inline]
fn name(&self) -> &str {
@ -254,83 +329,68 @@ where
}
}
impl<O, R, T> MapFeedback<O, R, T>
impl<FT, O, R, S, T> MapFeedback<FT, O, R, S, T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
R: Reducer<T>,
O: MapObserver<T>,
S: HasFeedbackStates<FT>,
FT: FeedbackStatesTuple,
{
/// Create new `MapFeedback`
#[must_use]
pub fn new(name: &'static str, map_size: usize) -> Self {
pub fn new(feedback_state: &MapFeedbackState<T>, map_observer: &O) -> Self {
Self {
history_map: vec![T::default(); map_size],
phantom: PhantomData,
indexes: None,
novelties: None,
name: name.to_string(),
name: feedback_state.name().to_string(),
observer_name: map_observer.name().to_string(),
phantom: PhantomData,
}
}
/// Create new `MapFeedback` for the observer type.
pub fn new_with_observer(map_observer: &O) -> Self {
Self {
history_map: vec![T::default(); map_observer.map().len()],
phantom: PhantomData,
indexes: None,
novelties: None,
name: map_observer.name().to_string(),
}
}
/// Create new `MapFeedback` specifying if it must track indexes of novelties
/// Create new `MapFeedback` specifying if it must track indexes of used entries and/or novelties
#[must_use]
pub fn new_tracking(
name: &'static str,
map_size: usize,
track_indexes: bool,
track_novelties: bool,
) -> Self {
Self {
history_map: vec![T::default(); map_size],
phantom: PhantomData,
indexes: if track_indexes { Some(vec![]) } else { None },
novelties: if track_novelties { Some(vec![]) } else { None },
name: name.to_string(),
}
}
/// Create new `MapFeedback` for the observer type if it must track indexes of novelties
pub fn new_tracking_with_observer(
feedback_state: &MapFeedbackState<T>,
map_observer: &O,
track_indexes: bool,
track_novelties: bool,
) -> Self {
Self {
history_map: vec![T::default(); map_observer.map().len()],
phantom: PhantomData,
indexes: if track_indexes { Some(vec![]) } else { None },
novelties: if track_novelties { Some(vec![]) } else { None },
name: map_observer.name().to_string(),
name: feedback_state.name().to_string(),
observer_name: map_observer.name().to_string(),
phantom: PhantomData,
}
}
}
impl<O, R, T> MapFeedback<O, R, T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
R: Reducer<T>,
O: MapObserver<T>,
{
/// Create new `MapFeedback` using a map observer, and a map.
/// The map can be shared.
/// Create new `MapFeedback`
#[must_use]
pub fn with_history_map(name: &'static str, history_map: Vec<T>) -> Self {
pub fn with_names(name: &'static str, observer_name: &'static str) -> Self {
Self {
history_map,
name: name.to_string(),
indexes: None,
novelties: None,
name: name.to_string(),
observer_name: observer_name.to_string(),
phantom: PhantomData,
}
}
/// Create new `MapFeedback` specifying if it must track indexes of used entries and/or novelties
#[must_use]
pub fn with_names_tracking(
name: &'static str,
observer_name: &'static str,
track_indexes: bool,
track_novelties: bool,
) -> Self {
Self {
indexes: if track_indexes { Some(vec![]) } else { None },
novelties: if track_novelties { Some(vec![]) } else { None },
observer_name: observer_name.to_string(),
name: name.to_string(),
phantom: PhantomData,
}
}
@ -350,7 +410,7 @@ where
{
/// Creates a new [`ReachabilityFeedback`] for a [`MapObserver`].
#[must_use]
pub fn new_with_observer(map_observer: &O) -> Self {
pub fn new(map_observer: &O) -> Self {
Self {
name: map_observer.name().to_string(),
target_idx: vec![],
@ -360,7 +420,7 @@ where
/// Creates a new [`ReachabilityFeedback`] for a [`MapObserver`] with the given `name`.
#[must_use]
pub fn new(name: &'static str) -> Self {
pub fn with_name(name: &'static str) -> Self {
Self {
name: name.to_string(),
target_idx: vec![],
@ -369,17 +429,21 @@ where
}
}
impl<I, O> Feedback<I> for ReachabilityFeedback<O>
impl<I, O, S> Feedback<I, S> for ReachabilityFeedback<O>
where
I: Input,
O: MapObserver<usize>,
{
fn is_interesting<OT: ObserversTuple>(
fn is_interesting<OT>(
&mut self,
_state: &mut S,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error> {
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
// TODO Replace with match_name_type when stable
let observer = observers.match_name::<O>(&self.name).unwrap();
let size = observer.usable_count();
@ -398,7 +462,7 @@ where
}
}
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
if !self.target_idx.is_empty() {
let meta = MapIndexesMetadata::new(core::mem::take(self.target_idx.as_mut()));
testcase.add_metadata(meta);
@ -406,7 +470,7 @@ where
Ok(())
}
fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> {
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
self.target_idx.clear();
Ok(())
}

View File

@ -8,7 +8,7 @@ use alloc::string::{String, ToString};
use serde::{Deserialize, Serialize};
use crate::{
bolts::tuples::Named,
bolts::tuples::{MatchName, Named},
corpus::Testcase,
executors::ExitKind,
inputs::Input,
@ -24,13 +24,14 @@ use core::{marker::PhantomData, time::Duration};
/// Feedbacks evaluate the observers.
/// Basically, they reduce the information provided by an observer to a value,
/// indicating the "interestingness" of the last run.
pub trait Feedback<I>: Named + serde::Serialize + serde::de::DeserializeOwned
pub trait Feedback<I, S>: Named
where
I: Input,
{
/// `is_interesting ` should return the "Interestingness" from 0 to 255 (percent times 2.55)
/// `is_interesting ` return if an input is worth the addition to the corpus
fn is_interesting<OT>(
&mut self,
state: &mut S,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
@ -41,6 +42,7 @@ where
#[cfg(feature = "introspection")]
fn is_interesting_with_perf<OT>(
&mut self,
state: &mut S,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
@ -54,7 +56,7 @@ where
let start_time = crate::cpu::read_time_counter();
// Execute this feedback
let ret = self.is_interesting(input, observers, &exit_kind);
let ret = self.is_interesting(state, input, observers, &exit_kind);
// Get the elapsed time for checking this feedback
let elapsed = crate::cpu::read_time_counter() - start_time;
@ -69,41 +71,62 @@ where
/// Append to the testcase the generated metadata in case of a new corpus item
#[inline]
fn append_metadata(&mut self, _testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(
&mut self,
_state: &mut S,
_testcase: &mut Testcase<I>,
) -> Result<(), Error> {
Ok(())
}
/// Discard the stored metadata in case that the testcase is not added to the corpus
#[inline]
fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> {
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
Ok(())
}
}
/// Compose [`Feedback`]`s` with an `AND` operation
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct AndFeedback<A, B, I>
/// [`FeedbackState`] is the data associated with a [`Feedback`] that must persist as part
/// of the fuzzer State
pub trait FeedbackState: Named + serde::Serialize + serde::de::DeserializeOwned {}
/// A haskell-style tuple of feedback states
pub trait FeedbackStatesTuple: MatchName + serde::Serialize + serde::de::DeserializeOwned {}
impl FeedbackStatesTuple for () {}
impl<Head, Tail> FeedbackStatesTuple for (Head, Tail)
where
A: Feedback<I>,
B: Feedback<I>,
Head: FeedbackState,
Tail: FeedbackStatesTuple,
{
}
/// Compose [`Feedback`]`s` with an `AND` operation
pub struct AndFeedback<A, B, I, S>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
/// The first [`Feedback`] to `AND`.
pub first: A,
/// The second [`Feedback`] to `AND`.
pub second: B,
phantom: PhantomData<I>,
/// The name
name: String,
phantom: PhantomData<(I, S)>,
}
impl<A, B, I> Feedback<I> for AndFeedback<A, B, I>
impl<A, B, I, S> Feedback<I, S> for AndFeedback<A, B, I, S>
where
A: Feedback<I>,
B: Feedback<I>,
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
fn is_interesting<OT>(
&mut self,
state: &mut S,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
@ -111,14 +134,19 @@ where
where
OT: ObserversTuple,
{
let a = self.first.is_interesting(input, observers, exit_kind)?;
let b = self.second.is_interesting(input, observers, exit_kind)?;
let a = self
.first
.is_interesting(state, input, observers, exit_kind)?;
let b = self
.second
.is_interesting(state, input, observers, exit_kind)?;
Ok(a && b)
}
#[cfg(feature = "introspection")]
fn is_interesting_with_perf<OT>(
&mut self,
state: &mut S,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
@ -130,6 +158,7 @@ where
{
// Execute this feedback
let a = self.first.is_interesting_with_perf(
state,
input,
observers,
&exit_kind,
@ -137,6 +166,7 @@ where
feedback_index,
)?;
let b = self.second.is_interesting_with_perf(
state,
input,
observers,
&exit_kind,
@ -147,71 +177,73 @@ where
}
#[inline]
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.first.append_metadata(testcase)?;
self.second.append_metadata(testcase)
fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.first.append_metadata(state, testcase)?;
self.second.append_metadata(state, testcase)
}
#[inline]
fn discard_metadata(&mut self, input: &I) -> Result<(), Error> {
self.first.discard_metadata(input)?;
self.second.discard_metadata(input)
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
self.first.discard_metadata(state, input)?;
self.second.discard_metadata(state, input)
}
}
impl<A, B, I> Named for AndFeedback<A, B, I>
impl<A, B, I, S> Named for AndFeedback<A, B, I, S>
where
A: Feedback<I>,
B: Feedback<I>,
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
#[inline]
fn name(&self) -> &str {
//format!("And({}, {})", self.first.name(), self.second.name())
"AndFeedback"
&self.name
}
}
impl<A, B, I> AndFeedback<A, B, I>
impl<A, B, I, S> AndFeedback<A, B, I, S>
where
A: Feedback<I>,
B: Feedback<I>,
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
/// Creates a new [`AndFeedback`], resulting in the `AND` of two feedbacks.
pub fn new(first: A, second: B) -> Self {
let name = format!("And({}, {})", first.name(), second.name());
Self {
first,
second,
name,
phantom: PhantomData,
}
}
}
/// Compose feedbacks with an OR operation
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct OrFeedback<A, B, I>
pub struct OrFeedback<A, B, I, S>
where
A: Feedback<I>,
B: Feedback<I>,
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
/// The first [`Feedback`]
pub first: A,
/// The second [`Feedback`], `OR`ed with the first.
pub second: B,
phantom: PhantomData<I>,
/// The name
name: String,
phantom: PhantomData<(I, S)>,
}
impl<A, B, I> Feedback<I> for OrFeedback<A, B, I>
impl<A, B, I, S> Feedback<I, S> for OrFeedback<A, B, I, S>
where
A: Feedback<I>,
B: Feedback<I>,
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
fn is_interesting<OT>(
&mut self,
state: &mut S,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
@ -219,14 +251,19 @@ where
where
OT: ObserversTuple,
{
let a = self.first.is_interesting(input, observers, exit_kind)?;
let b = self.second.is_interesting(input, observers, exit_kind)?;
let a = self
.first
.is_interesting(state, input, observers, exit_kind)?;
let b = self
.second
.is_interesting(state, input, observers, exit_kind)?;
Ok(a || b)
}
#[cfg(feature = "introspection")]
fn is_interesting_with_perf<OT>(
&mut self,
state: &mut S,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
@ -238,6 +275,7 @@ where
{
// Execute this feedback
let a = self.first.is_interesting_with_perf(
state,
input,
observers,
&exit_kind,
@ -245,6 +283,7 @@ where
feedback_index,
)?;
let b = self.second.is_interesting_with_perf(
state,
input,
observers,
&exit_kind,
@ -255,67 +294,69 @@ where
}
#[inline]
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.first.append_metadata(testcase)?;
self.second.append_metadata(testcase)
fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.first.append_metadata(state, testcase)?;
self.second.append_metadata(state, testcase)
}
#[inline]
fn discard_metadata(&mut self, input: &I) -> Result<(), Error> {
self.first.discard_metadata(input)?;
self.second.discard_metadata(input)
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
self.first.discard_metadata(state, input)?;
self.second.discard_metadata(state, input)
}
}
impl<A, B, I> Named for OrFeedback<A, B, I>
impl<A, B, I, S> Named for OrFeedback<A, B, I, S>
where
A: Feedback<I>,
B: Feedback<I>,
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
#[inline]
fn name(&self) -> &str {
//format!("Or({}, {})", self.first.name(), self.second.name())
"OrFeedback"
&self.name
}
}
impl<A, B, I> OrFeedback<A, B, I>
impl<A, B, I, S> OrFeedback<A, B, I, S>
where
A: Feedback<I>,
B: Feedback<I>,
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
/// Creates a new [`OrFeedback`] for two feedbacks.
pub fn new(first: A, second: B) -> Self {
let name = format!("Or({}, {})", first.name(), second.name());
Self {
first,
second,
name,
phantom: PhantomData,
}
}
}
/// Compose feedbacks with an OR operation
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct NotFeedback<A, I>
pub struct NotFeedback<A, I, S>
where
A: Feedback<I>,
A: Feedback<I, S>,
I: Input,
{
/// The feedback to invert
pub first: A,
phantom: PhantomData<I>,
/// The name
name: String,
phantom: PhantomData<(I, S)>,
}
impl<A, I> Feedback<I> for NotFeedback<A, I>
impl<A, I, S> Feedback<I, S> for NotFeedback<A, I, S>
where
A: Feedback<I>,
A: Feedback<I, S>,
I: Input,
{
fn is_interesting<OT>(
&mut self,
state: &mut S,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
@ -323,41 +364,44 @@ where
where
OT: ObserversTuple,
{
Ok(!self.first.is_interesting(input, observers, exit_kind)?)
Ok(!self
.first
.is_interesting(state, input, observers, exit_kind)?)
}
#[inline]
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.first.append_metadata(testcase)
fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.first.append_metadata(state, testcase)
}
#[inline]
fn discard_metadata(&mut self, input: &I) -> Result<(), Error> {
self.first.discard_metadata(input)
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
self.first.discard_metadata(state, input)
}
}
impl<A, I> Named for NotFeedback<A, I>
impl<A, I, S> Named for NotFeedback<A, I, S>
where
A: Feedback<I>,
A: Feedback<I, S>,
I: Input,
{
#[inline]
fn name(&self) -> &str {
//format!("Not({})", self.first.name())
"NotFeedback"
&self.name
}
}
impl<A, I> NotFeedback<A, I>
impl<A, I, S> NotFeedback<A, I, S>
where
A: Feedback<I>,
A: Feedback<I, S>,
I: Input,
{
/// Creates a new [`NotFeedback`].
pub fn new(first: A) -> Self {
let name = format!("Not({})", first.name());
Self {
first,
name,
phantom: PhantomData,
}
}
@ -394,12 +438,13 @@ macro_rules! feedback_not {
}
/// Hack to use () as empty Feedback
impl<I> Feedback<I> for ()
impl<I, S> Feedback<I, S> for ()
where
I: Input,
{
fn is_interesting<OT>(
&mut self,
_state: &mut S,
_input: &I,
_observers: &OT,
_exit_kind: &ExitKind,
@ -422,12 +467,13 @@ impl Named for () {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CrashFeedback {}
impl<I> Feedback<I> for CrashFeedback
impl<I, S> Feedback<I, S> for CrashFeedback
where
I: Input,
{
fn is_interesting<OT>(
&mut self,
_state: &mut S,
_input: &I,
_observers: &OT,
exit_kind: &ExitKind,
@ -468,12 +514,13 @@ impl Default for CrashFeedback {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeoutFeedback {}
impl<I> Feedback<I> for TimeoutFeedback
impl<I, S> Feedback<I, S> for TimeoutFeedback
where
I: Input,
{
fn is_interesting<OT>(
&mut self,
_state: &mut S,
_input: &I,
_observers: &OT,
exit_kind: &ExitKind,
@ -519,12 +566,13 @@ pub struct TimeFeedback {
name: String,
}
impl<I> Feedback<I> for TimeFeedback
impl<I, S> Feedback<I, S> for TimeFeedback
where
I: Input,
{
fn is_interesting<OT>(
&mut self,
_state: &mut S,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
@ -540,7 +588,7 @@ where
/// Append to the testcase the generated metadata in case of a new corpus item
#[inline]
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
*testcase.exec_time_mut() = self.exec_time;
self.exec_time = None;
Ok(())
@ -548,7 +596,7 @@ where
/// Discard the stored metadata in case that the testcase is not added to the corpus
#[inline]
fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> {
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
self.exec_time = None;
Ok(())
}

View File

@ -1,39 +1,31 @@
//! The `Fuzzer` is the main struct for a fuzz campaign.
use crate::{
corpus::CorpusScheduler,
corpus::{Corpus, CorpusScheduler, Testcase},
events::{Event, EventManager},
executors::{Executor, HasObservers},
executors::{
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
},
feedbacks::Feedback,
inputs::Input,
mark_feature_time,
observers::ObserversTuple,
stages::StagesTuple,
state::{HasClientPerfStats, HasExecutions},
start_timer,
state::{HasClientPerfStats, HasCorpus, HasExecutions, HasSolutions},
utils::current_time,
Error,
};
#[cfg(feature = "introspection")]
use crate::stats::PerfFeature;
use alloc::string::ToString;
use core::{marker::PhantomData, time::Duration};
/// Send a stats update all 3 (or more) seconds
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(3 * 1000);
/// Holds a set of stages
pub trait HasStages<CS, E, EM, I, S, ST>
where
ST: StagesTuple<CS, E, EM, I, S>,
E: Executor<I>,
EM: EventManager<I, S>,
I: Input,
Self: Sized,
{
/// The stages
fn stages(&self) -> &ST;
/// The stages (mut)
fn stages_mut(&mut self) -> &mut ST;
}
/// Holds a scheduler
pub trait HasCorpusScheduler<CS, I, S>
where
@ -47,8 +39,73 @@ where
fn scheduler_mut(&mut self) -> &mut CS;
}
/// Holds an feedback
pub trait HasFeedback<F, I, S>
where
F: Feedback<I, S>,
I: Input,
{
/// The feedback
fn feedback(&self) -> &F;
/// The feedback (mut)
fn feedback_mut(&mut self) -> &mut F;
}
/// Holds an objective feedback
pub trait HasObjective<I, OF, S>
where
OF: Feedback<I, S>,
I: Input,
{
/// The objective feedback
fn objective(&self) -> &OF;
/// The objective feedback (mut)
fn objective_mut(&mut self) -> &mut OF;
}
/// Evaluate if an input is interesting using the feedback
pub trait IsInteresting<I, OT, S>
where
OT: ObserversTuple,
{
/// Evaluate if a set of observation channels has an interesting state
fn is_interesting(
&mut self,
state: &mut S,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>;
}
/// Add to the state if interesting
pub trait IfInteresting<I, S> {
/// Adds this input to the corpus, if it's intersting, and return the index
fn add_if_interesting(
&mut self,
state: &mut S,
input: &I,
is_interesting: bool,
) -> Result<Option<usize>, Error>;
}
/// Evaluate an input modyfing the state of the fuzzer
pub trait Evaluator<E, EM, I, S> {
/// Runs the input and triggers observers and feedback,
/// returns if is interesting an (option) the index of the new testcase in the corpus
fn evaluate_input(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
) -> Result<(bool, Option<usize>), Error>;
}
/// The main fuzzer trait.
pub trait Fuzzer<E, EM, S, CS> {
pub trait Fuzzer<E, EM, I, S, ST> {
/// Fuzz for a single iteration
/// Returns the index of the last fuzzed corpus item
///
@ -57,24 +114,24 @@ pub trait Fuzzer<E, EM, S, CS> {
/// This way, the state will be available in the next, respawned, iteration.
fn fuzz_one(
&mut self,
state: &mut S,
stages: &mut ST,
executor: &mut E,
state: &mut S,
manager: &mut EM,
scheduler: &CS,
) -> Result<usize, Error>;
/// Fuzz forever (or until stopped)
fn fuzz_loop(
&mut self,
state: &mut S,
stages: &mut ST,
executor: &mut E,
state: &mut S,
manager: &mut EM,
scheduler: &CS,
) -> Result<usize, Error> {
let mut last = current_time();
let stats_timeout = STATS_TIMEOUT_DEFAULT;
loop {
self.fuzz_one(state, executor, manager, scheduler)?;
self.fuzz_one(stages, executor, state, manager)?;
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
}
}
@ -85,18 +142,14 @@ pub trait Fuzzer<E, EM, S, CS> {
/// If you use this fn in a restarting scenario to only run for `n` iterations,
/// before exiting, make sure you call `event_mgr.on_restart(&mut state)?;`.
/// This way, the state will be available in the next, respawned, iteration.
fn fuzz_loop_for<I>(
fn fuzz_loop_for(
&mut self,
stages: &mut ST,
state: &mut S,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
iters: u64,
) -> Result<usize, Error>
where
EM: EventManager<I, S>,
I: Input,
{
) -> Result<usize, Error> {
if iters == 0 {
return Err(Error::IllegalArgument(
"Cannot fuzz for 0 iterations!".to_string(),
@ -108,7 +161,7 @@ pub trait Fuzzer<E, EM, S, CS> {
let stats_timeout = STATS_TIMEOUT_DEFAULT;
for _ in 0..iters {
ret = self.fuzz_one(state, executor, manager, scheduler)?;
ret = self.fuzz_one(stages, executor, state, manager)?;
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
}
@ -132,44 +185,27 @@ pub trait Fuzzer<E, EM, S, CS> {
}
/// Your default fuzzer instance, for everyday use.
#[derive(Clone, Debug)]
pub struct StdFuzzer<CS, ST, E, EM, I, OT, S>
#[derive(Debug)]
pub struct StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
ST: StagesTuple<CS, E, EM, I, S>,
E: Executor<I>,
EM: EventManager<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
{
stages: ST,
phantom: PhantomData<(CS, E, EM, I, OT, S)>,
scheduler: CS,
feedback: F,
objective: OF,
phantom: PhantomData<(C, I, OT, S, SC)>,
}
impl<CS, ST, E, EM, I, OT, S> HasStages<CS, E, EM, I, S, ST> for StdFuzzer<CS, ST, E, EM, I, OT, S>
impl<C, CS, F, I, OF, OT, S, SC> HasCorpusScheduler<CS, I, S>
for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
ST: StagesTuple<CS, E, EM, I, S>,
E: Executor<I>,
EM: EventManager<I, S>,
I: Input,
{
fn stages(&self) -> &ST {
&self.stages
}
fn stages_mut(&mut self) -> &mut ST {
&mut self.stages
}
}
/*
impl<CS, ST, E, EM, I, OT, S> HasCorpusScheduler<CS, I, S> for StdFuzzer<CS, ST, E, EM, I, OT, S>
where
CS: CorpusScheduler<I, S>,
ST: StagesTuple<CS, E, EM, I, S>,
E: Executor<I>,
EM: EventManager<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
{
fn scheduler(&self) -> &CS {
&self.scheduler
@ -179,17 +215,164 @@ where
&mut self.scheduler
}
}
*/
impl<CS, ST, E, EM, I, OT, S> Fuzzer<E, EM, S, CS> for StdFuzzer<CS, ST, E, EM, I, OT, S>
impl<C, CS, F, I, OF, OT, S, SC> HasFeedback<F, I, S> for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
S: HasExecutions + HasClientPerfStats,
ST: StagesTuple<CS, E, EM, I, S>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
{
fn feedback(&self) -> &F {
&self.feedback
}
fn feedback_mut(&mut self) -> &mut F {
&mut self.feedback
}
}
impl<C, CS, F, I, OF, OT, S, SC> HasObjective<I, OF, S> for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
{
fn objective(&self) -> &OF {
&self.objective
}
fn objective_mut(&mut self) -> &mut OF {
&mut self.objective
}
}
impl<C, CS, F, I, OF, OT, S, SC> IsInteresting<I, OT, S> for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
C: Corpus<I>,
CS: CorpusScheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
OT: ObserversTuple,
S: HasCorpus<C, I>,
{
/// Evaluate if a set of observation channels has an interesting state
fn is_interesting(
&mut self,
state: &mut S,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
self.feedback_mut()
.is_interesting(state, input, observers, exit_kind)
}
}
impl<C, CS, F, I, OF, OT, S, SC> IfInteresting<I, S> for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
C: Corpus<I>,
CS: CorpusScheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
OT: ObserversTuple,
S: HasCorpus<C, I>,
{
/// Adds this input to the corpus, if it's intersting, and return the index
#[inline]
fn add_if_interesting(
&mut self,
state: &mut S,
input: &I,
is_interesting: bool,
) -> Result<Option<usize>, Error> {
if is_interesting {
let mut testcase = Testcase::new(input.clone());
self.feedback_mut().append_metadata(state, &mut testcase)?;
let idx = state.corpus_mut().add(testcase)?;
self.scheduler_mut().on_add(state, idx)?;
Ok(Some(idx))
} else {
self.feedback_mut().discard_metadata(state, input)?;
Ok(None)
}
}
}
impl<C, CS, E, EM, F, I, OF, OT, S, SC> Evaluator<E, EM, I, S>
for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
C: Corpus<I>,
CS: CorpusScheduler<I, S>,
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, S, Self>
+ HasObserversHooks<EM, I, OT, S, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Self>,
EM: EventManager<E, I, S, Self>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasExecutions + HasCorpus<C, I> + HasSolutions<SC, I> + HasClientPerfStats,
SC: Corpus<I>,
{
/// Process one input, adding to the respective corpuses if needed and firing the right events
#[inline]
fn evaluate_input(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
) -> Result<(bool, Option<usize>), Error> {
let (is_interesting, is_solution) = self.execute_input(state, executor, manager, &input)?;
let observers = executor.observers();
if is_solution {
// If the input is a solution, add it to the respective corpus
let mut testcase = Testcase::new(input.clone());
self.objective_mut().append_metadata(state, &mut testcase)?;
state.solutions_mut().add(testcase)?;
} else {
self.objective_mut().discard_metadata(state, &input)?;
}
let corpus_idx = self.add_if_interesting(state, &input, is_interesting)?;
if corpus_idx.is_some() {
let observers_buf = manager.serialize_observers(observers)?;
manager.fire(
state,
Event::NewTestcase {
input,
observers_buf,
corpus_size: state.corpus().count(),
client_config: "TODO".into(),
time: crate::utils::current_time(),
executions: *state.executions(),
},
)?;
}
Ok((is_interesting, corpus_idx))
}
}
impl<C, CS, E, EM, F, I, OF, OT, S, ST, SC> Fuzzer<E, EM, I, S, ST>
for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
EM: EventManager<E, I, S, Self>,
F: Feedback<I, S>,
I: Input,
S: HasExecutions + HasClientPerfStats,
OF: Feedback<I, S>,
ST: StagesTuple<E, EM, S, Self>,
{
#[inline]
fn maybe_report_stats(
@ -240,17 +423,17 @@ where
fn fuzz_one(
&mut self,
state: &mut S,
stages: &mut ST,
executor: &mut E,
state: &mut S,
manager: &mut EM,
scheduler: &CS,
) -> Result<usize, Error> {
// Init timer for scheduler
#[cfg(feature = "introspection")]
state.introspection_stats_mut().start_timer();
// Get the next index from the scheduler
let idx = scheduler.next(state)?;
let idx = self.scheduler.next(state)?;
// Mark the elapsed time for the scheduler
#[cfg(feature = "introspection")]
@ -261,15 +444,14 @@ where
state.introspection_stats_mut().reset_stage_index();
// Execute all stages
self.stages_mut()
.perform_all(state, executor, manager, scheduler, idx)?;
stages.perform_all(self, executor, state, manager, idx)?;
// Init timer for manager
#[cfg(feature = "introspection")]
state.introspection_stats_mut().start_timer();
// Execute the manager
manager.process(state, executor, scheduler)?;
manager.process(self, state, executor)?;
// Mark the elapsed time for the manager
#[cfg(feature = "introspection")]
@ -279,19 +461,101 @@ where
}
}
impl<CS, ST, E, EM, I, OT, S> StdFuzzer<CS, ST, E, EM, I, OT, S>
impl<C, CS, F, I, OF, OT, S, SC> StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
ST: StagesTuple<CS, E, EM, I, S>,
E: Executor<I>,
EM: EventManager<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasExecutions + HasClientPerfStats,
{
/// Create a new `StdFuzzer` with standard behavior.
pub fn new(stages: ST) -> Self {
pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self {
Self {
stages,
scheduler,
feedback,
objective,
phantom: PhantomData,
}
}
/// Runs the input and triggers observers and feedback
pub fn execute_input<E, EM>(
&mut self,
state: &mut S,
executor: &mut E,
event_mgr: &mut EM,
input: &I,
) -> Result<(bool, bool), Error>
where
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, S, Self>
+ HasObserversHooks<EM, I, OT, S, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Self>,
EM: EventManager<E, I, S, Self>,
{
start_timer!(state);
executor.pre_exec_observers(self, state, event_mgr, input)?;
mark_feature_time!(state, PerfFeature::PreExecObservers);
start_timer!(state);
executor.pre_exec(self, state, event_mgr, input)?;
mark_feature_time!(state, PerfFeature::PreExec);
start_timer!(state);
let exit_kind = executor.run_target(input)?;
mark_feature_time!(state, PerfFeature::TargetExecution);
start_timer!(state);
executor.post_exec(self, state, event_mgr, input)?;
mark_feature_time!(state, PerfFeature::PostExec);
*state.executions_mut() += 1;
start_timer!(state);
executor.post_exec_observers(self, state, event_mgr, input)?;
mark_feature_time!(state, PerfFeature::PostExecObservers);
let observers = executor.observers();
#[cfg(not(feature = "introspection"))]
let is_interesting = self
.feedback_mut()
.is_interesting(state, &input, observers, &exit_kind)?;
#[cfg(feature = "introspection")]
let is_interesting = {
// Init temporary feedback stats here. We can't use the typical pattern above
// since we need a `mut self` for `feedbacks_mut`, so we can't also hand a
// new `mut self` to `is_interesting_with_perf`. We use this stack
// variable to get the stats and then update the feedbacks directly
let mut feedback_stats = [0_u64; crate::stats::NUM_FEEDBACKS];
let feedback_index = 0;
let is_interesting = self.feedback_mut().is_interesting_with_perf(
state,
&input,
observers,
&exit_kind,
&mut feedback_stats,
feedback_index,
)?;
// Update the feedback stats
state
.introspection_stats_mut()
.update_feedbacks(feedback_stats);
// Return the total fitness
is_interesting
};
start_timer!(state);
let is_solution = self
.objective_mut()
.is_interesting(state, &input, observers, &exit_kind)?;
mark_feature_time!(state, PerfFeature::GetObjectivesInterestingAll);
Ok((is_interesting, is_solution))
}
}

View File

@ -156,7 +156,6 @@ impl From<ParseIntError> for Error {
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use crate::{
bolts::tuples::tuple_list,
corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase},
@ -164,7 +163,7 @@ mod tests {
inputs::BytesInput,
mutators::{mutations::BitFlipMutator, StdScheduledMutator},
stages::StdMutationalStage,
state::{HasCorpus, State},
state::{HasCorpus, StdState},
stats::SimpleStats,
utils::StdRand,
Fuzzer, StdFuzzer,
@ -182,10 +181,9 @@ mod tests {
let testcase = Testcase::new(vec![0; 4]);
corpus.add(testcase).unwrap();
let mut state = State::new(
let mut state = StdState::new(
rand,
corpus,
tuple_list!(),
InMemoryCorpus::<BytesInput>::new(),
tuple_list!(),
);
@ -195,33 +193,33 @@ mod tests {
});
let mut event_manager = SimpleEventManager::new(stats);
let scheduler = RandCorpusScheduler::new();
let mut fuzzer = StdFuzzer::new(scheduler, (), ());
let mut harness = |_buf: &[u8]| ExitKind::Ok;
let mut executor = InProcessExecutor::new(
&mut harness,
tuple_list!(),
//Box::new(|_, _, _, _, _| ()),
&mut fuzzer,
&mut state,
&mut event_manager,
)
.unwrap();
let mutator = StdScheduledMutator::new(tuple_list!(BitFlipMutator::new()));
let stage = StdMutationalStage::new(mutator);
let scheduler = RandCorpusScheduler::new();
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
for i in 0..1000 {
fuzzer
.fuzz_one(&mut state, &mut executor, &mut event_manager, &scheduler)
.fuzz_one(&mut stages, &mut executor, &mut state, &mut event_manager)
.unwrap_or_else(|_| panic!("Error in iter {}", i));
}
let state_serialized = postcard::to_allocvec(&state).unwrap();
let state_deserialized: State<
let state_deserialized: StdState<
InMemoryCorpus<BytesInput>,
(),
BytesInput,
(),
StdRand,
InMemoryCorpus<BytesInput>,
> = postcard::from_bytes(state_serialized.as_slice()).unwrap();

View File

@ -1837,7 +1837,7 @@ mod tests {
corpus::{Corpus, InMemoryCorpus},
inputs::BytesInput,
mutators::MutatorsTuple,
state::{HasMetadata, State},
state::{HasMetadata, StdState},
utils::StdRand,
};
@ -1895,7 +1895,7 @@ mod tests {
.add(BytesInput::new(vec![0x42; 0x1337]).into())
.unwrap();
let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ());
let mut state = StdState::new(rand, corpus, InMemoryCorpus::new(), ());
let mut mutations = test_mutations();
for _ in 0..2 {

View File

@ -404,7 +404,7 @@ mod tests {
scheduled::{havoc_mutations, StdScheduledMutator},
Mutator,
},
state::State,
state::StdState,
utils::{Rand, StdRand, XkcdRand},
};
@ -419,7 +419,7 @@ mod tests {
let testcase = corpus.get(0).expect("Corpus did not contain entries");
let mut input = testcase.borrow_mut().load_input().unwrap().clone();
let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ());
let mut state = StdState::new(rand, corpus, InMemoryCorpus::new(), ());
rand.set_seed(5);
@ -446,7 +446,7 @@ mod tests {
let mut input = testcase.borrow_mut().load_input().unwrap().clone();
let input_prior = input.clone();
let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ());
let mut state = StdState::new(rand, corpus, InMemoryCorpus::new(), ());
let mut havoc = StdScheduledMutator::new(havoc_mutations());

View File

@ -75,13 +75,19 @@ impl<'a, T> Observer for StdMapObserver<'a, T> where
{
}
impl<'a, EM, I, S, T> HasExecHooks<EM, I, S> for StdMapObserver<'a, T>
impl<'a, EM, I, S, T, Z> HasExecHooks<EM, I, S, Z> for StdMapObserver<'a, T>
where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
Self: MapObserver<T>,
{
#[inline]
fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> {
fn pre_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
self.reset_map()
}
}
@ -185,12 +191,18 @@ impl<'a, T> Observer for VariableMapObserver<'a, T> where
{
}
impl<'a, EM, I, S, T> HasExecHooks<EM, I, S> for VariableMapObserver<'a, T>
impl<'a, EM, I, S, T, Z> HasExecHooks<EM, I, S, Z> for VariableMapObserver<'a, T>
where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{
#[inline]
fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> {
fn pre_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
self.reset_map()
}
}
@ -302,21 +314,33 @@ static COUNT_CLASS_LOOKUP: [u8; 256] = [
impl<M> Observer for HitcountsMapObserver<M> where M: MapObserver<u8> {}
impl<EM, I, S, M> HasExecHooks<EM, I, S> for HitcountsMapObserver<M>
impl<EM, I, S, M, Z> HasExecHooks<EM, I, S, Z> for HitcountsMapObserver<M>
where
M: MapObserver<u8> + HasExecHooks<EM, I, S>,
M: MapObserver<u8> + HasExecHooks<EM, I, S, Z>,
{
#[inline]
fn pre_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
self.base.pre_exec(state, mgr, input)
fn pre_exec(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error> {
self.base.pre_exec(fuzzer, state, mgr, input)
}
#[inline]
fn post_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
fn post_exec(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error> {
for x in self.map_mut().iter_mut() {
*x = COUNT_CLASS_LOOKUP[*x as usize];
}
self.base.post_exec(state, mgr, input)
self.base.post_exec(fuzzer, state, mgr, input)
}
}

View File

@ -65,14 +65,26 @@ impl TimeObserver {
impl Observer for TimeObserver {}
impl<EM, I, S> HasExecHooks<EM, I, S> for TimeObserver {
fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> {
impl<EM, I, S, Z> HasExecHooks<EM, I, S, Z> for TimeObserver {
fn pre_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
self.last_runtime = None;
self.start_time = current_time();
Ok(())
}
fn post_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> {
fn post_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
self.last_runtime = Some(current_time() - self.start_time);
Ok(())
}

View File

@ -11,90 +11,67 @@ pub use mutational::{MutationalStage, StdMutationalStage};
//pub mod power;
//pub use power::PowerMutationalStage;
use crate::{
bolts::tuples::TupleList, events::EventManager, executors::Executor, inputs::Input,
state::HasClientPerfStats, Error,
};
use crate::Error;
/// A stage is one step in the fuzzing process.
/// Multiple stages will be scheduled one by one for each input.
pub trait Stage<CS, E, EM, I, S>
where
EM: EventManager<I, S>,
E: Executor<I>,
I: Input,
{
pub trait Stage<E, EM, S, Z> {
/// Run the stage
fn perform(
&mut self,
state: &mut S,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
scheduler: &CS,
corpus_idx: usize,
) -> Result<(), Error>;
}
/// A tuple holding all `Stages` used for fuzzing.
pub trait StagesTuple<CS, E, EM, I, S>
where
EM: EventManager<I, S>,
E: Executor<I>,
I: Input,
{
pub trait StagesTuple<E, EM, S, Z> {
/// Performs all `Stages` in this tuple
fn perform_all(
&mut self,
state: &mut S,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
scheduler: &CS,
corpus_idx: usize,
) -> Result<(), Error>;
}
impl<CS, E, EM, I, S> StagesTuple<CS, E, EM, I, S> for ()
where
EM: EventManager<I, S>,
E: Executor<I>,
I: Input,
{
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for () {
fn perform_all(
&mut self,
_: &mut S,
_: &mut Z,
_: &mut E,
_: &mut S,
_: &mut EM,
_: &CS,
_: usize,
) -> Result<(), Error> {
Ok(())
}
}
impl<Head, Tail, CS, E, EM, I, S> StagesTuple<CS, E, EM, I, S> for (Head, Tail)
impl<Head, Tail, E, EM, S, Z> StagesTuple<E, EM, S, Z> for (Head, Tail)
where
Head: Stage<CS, E, EM, I, S>,
Tail: StagesTuple<CS, E, EM, I, S> + TupleList,
EM: EventManager<I, S>,
E: Executor<I>,
I: Input,
S: HasClientPerfStats,
Head: Stage<E, EM, S, Z>,
Tail: StagesTuple<E, EM, S, Z>,
{
fn perform_all(
&mut self,
state: &mut S,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
scheduler: &CS,
corpus_idx: usize,
) -> Result<(), Error> {
// Perform the current stage
self.0
.perform(state, executor, manager, scheduler, corpus_idx)?;
.perform(fuzzer, executor, state, manager, corpus_idx)?;
// Execute the remaining stages
self.1
.perform_all(state, executor, manager, scheduler, corpus_idx)
.perform_all(fuzzer, executor, state, manager, corpus_idx)
}
}

View File

@ -1,16 +1,14 @@
use core::marker::PhantomData;
use crate::{
corpus::{Corpus, CorpusScheduler},
events::EventManager,
executors::{Executor, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks},
corpus::Corpus,
fuzzer::Evaluator,
inputs::Input,
mark_feature_time,
mutators::Mutator,
observers::ObserversTuple,
stages::Stage,
start_timer,
state::{Evaluator, HasClientPerfStats, HasCorpus, HasRand},
state::{HasClientPerfStats, HasCorpus, HasRand},
utils::Rand,
Error,
};
@ -23,16 +21,13 @@ use crate::stats::PerfFeature;
/// A Mutational stage is the stage in a fuzzing run that mutates inputs.
/// Mutational stages will usually have a range of mutations that are
/// being applied to the input one by one, between executions.
pub trait MutationalStage<C, CS, E, EM, I, M, OT, S>: Stage<CS, E, EM, I, S>
pub trait MutationalStage<C, E, EM, I, M, S, Z>: Stage<E, EM, S, Z>
where
C: Corpus<I>,
M: Mutator<I, S>,
I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasClientPerfStats,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
CS: CorpusScheduler<I, S>,
S: HasClientPerfStats + HasCorpus<C, I>,
Z: Evaluator<E, EM, I, S>,
{
/// The mutator registered for this stage
fn mutator(&self) -> &M;
@ -47,17 +42,17 @@ where
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
fn perform_mutational(
&mut self,
state: &mut S,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
scheduler: &CS,
corpus_idx: usize,
) -> Result<(), Error> {
let num = self.iterations(state);
for i in 0..num {
start_timer!(state);
let mut input_mut = state
let mut input = state
.corpus()
.get(corpus_idx)?
.borrow_mut()
@ -66,11 +61,11 @@ where
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
start_timer!(state);
self.mutator_mut().mutate(state, &mut input_mut, i as i32)?;
self.mutator_mut().mutate(state, &mut input, i as i32)?;
mark_feature_time!(state, PerfFeature::Mutate);
// Time is measured directly the `evaluate_input` function
let (_, corpus_idx) = state.evaluate_input(input_mut, executor, manager, scheduler)?;
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, input)?;
start_timer!(state);
self.mutator_mut().post_exec(state, i as i32, corpus_idx)?;
@ -86,35 +81,29 @@ pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128;
/// The default mutational stage
#[derive(Clone, Debug)]
pub struct StdMutationalStage<C, CS, E, EM, I, M, OT, R, S>
pub struct StdMutationalStage<C, E, EM, I, M, R, S, Z>
where
C: Corpus<I>,
M: Mutator<I, S>,
I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
CS: CorpusScheduler<I, S>,
R: Rand,
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
Z: Evaluator<E, EM, I, S>,
{
mutator: M,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(C, CS, E, EM, I, OT, R, S)>,
phantom: PhantomData<(C, E, EM, I, R, S, Z)>,
}
impl<C, CS, E, EM, I, M, OT, R, S> MutationalStage<C, CS, E, EM, I, M, OT, S>
for StdMutationalStage<C, CS, E, EM, I, M, OT, R, S>
impl<C, E, EM, I, M, R, S, Z> MutationalStage<C, E, EM, I, M, S, Z>
for StdMutationalStage<C, E, EM, I, M, R, S, Z>
where
C: Corpus<I>,
M: Mutator<I, S>,
I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R> + HasClientPerfStats,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
CS: CorpusScheduler<I, S>,
R: Rand,
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
Z: Evaluator<E, EM, I, S>,
{
/// The mutator, added to this stage
#[inline]
@ -134,43 +123,36 @@ where
}
}
impl<C, CS, E, EM, I, M, OT, R, S> Stage<CS, E, EM, I, S>
for StdMutationalStage<C, CS, E, EM, I, M, OT, R, S>
impl<C, E, EM, I, M, R, S, Z> Stage<E, EM, S, Z> for StdMutationalStage<C, E, EM, I, M, R, S, Z>
where
C: Corpus<I>,
M: Mutator<I, S>,
I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R> + HasClientPerfStats,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
CS: CorpusScheduler<I, S>,
R: Rand,
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
Z: Evaluator<E, EM, I, S>,
{
#[inline]
fn perform(
&mut self,
state: &mut S,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
scheduler: &CS,
corpus_idx: usize,
) -> Result<(), Error> {
self.perform_mutational(state, executor, manager, scheduler, corpus_idx)
self.perform_mutational(fuzzer, executor, state, manager, corpus_idx)
}
}
impl<C, CS, E, EM, I, M, OT, R, S> StdMutationalStage<C, CS, E, EM, I, M, OT, R, S>
impl<C, E, EM, I, M, R, S, Z> StdMutationalStage<C, E, EM, I, M, R, S, Z>
where
C: Corpus<I>,
M: Mutator<I, S>,
I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
CS: CorpusScheduler<I, S>,
R: Rand,
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
Z: Evaluator<E, EM, I, S>,
{
/// Creates a new default mutational stage
pub fn new(mutator: M) -> Self {

View File

@ -1,7 +1,7 @@
//! The fuzzer, and state are the core pieces of every good fuzzer
use core::{fmt::Debug, marker::PhantomData, time::Duration};
use serde::{Deserialize, Serialize};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(feature = "std")]
use std::{
fs,
@ -10,31 +10,25 @@ use std::{
use crate::{
bolts::serdeany::{SerdeAny, SerdeAnyMap},
corpus::{Corpus, CorpusScheduler, Testcase},
corpus::Corpus,
events::{Event, EventManager, LogSeverity},
executors::{
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
},
feedbacks::Feedback,
feedbacks::FeedbackStatesTuple,
fuzzer::Evaluator,
generators::Generator,
inputs::Input,
mark_feature_time,
observers::ObserversTuple,
start_timer,
stats::ClientPerfStats,
utils::Rand,
Error,
};
#[cfg(feature = "introspection")]
use crate::stats::PerfFeature;
#[cfg(feature = "std")]
use crate::inputs::bytes::BytesInput;
/// The maximum size of a testcase
pub const DEFAULT_MAX_SIZE: usize = 1_048_576;
pub trait State: Serialize + DeserializeOwned {}
/// Trait for elements offering a corpus
pub trait HasCorpus<C, I>
where
@ -114,29 +108,15 @@ pub trait HasMetadata {
}
/// Trait for elements offering a feedback
pub trait HasFeedback<F, I>: Sized
pub trait HasFeedbackStates<FT>
where
F: Feedback<I>,
I: Input,
FT: FeedbackStatesTuple,
{
/// The feedback
fn feedback(&self) -> &F;
/// The feedback states
fn feedback_states(&self) -> &FT;
/// The feedback (mut)
fn feedback_mut(&mut self) -> &mut F;
}
/// Trait for elements offering an objective feedback tuple
pub trait HasObjective<OF, I>: Sized
where
OF: Feedback<I>,
I: Input,
{
/// The objective feedback
fn objective(&self) -> &OF;
/// The objective feedback (mut)
fn objective_mut(&mut self) -> &mut OF;
/// The feedback states (mut)
fn feedback_states_mut(&mut self) -> &mut FT;
}
/// Trait for the execution counter
@ -157,67 +137,16 @@ pub trait HasStartTime {
fn start_time_mut(&mut self) -> &mut Duration;
}
/// Add to the state if interesting
pub trait IfInteresting<I>: Sized
where
I: Input,
{
/// Evaluate if a set of observation channels has an interesting state
fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
OT: ObserversTuple;
/// Adds this input to the corpus, if it's intersting, and return the index
fn add_if_interesting<CS>(
&mut self,
input: &I,
is_interesting: bool,
scheduler: &CS,
) -> Result<Option<usize>, Error>
where
CS: CorpusScheduler<I, Self>,
Self: Sized;
}
/// Evaluate an input modyfing the state of the fuzzer
pub trait Evaluator<I>: Sized
where
I: Input,
{
/// Runs the input and triggers observers and feedback
fn evaluate_input<CS, E, EM, OT>(
&mut self,
input: I,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
) -> Result<(bool, Option<usize>), Error>
where
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
EM: EventManager<I, Self>,
CS: CorpusScheduler<I, Self>;
}
/// The state a fuzz run.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "F: serde::de::DeserializeOwned")]
pub struct State<C, F, I, OF, R, SC>
#[serde(bound = "FT: serde::de::DeserializeOwned")]
pub struct StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// RNG instance
rand: R,
@ -227,12 +156,10 @@ where
start_time: Duration,
/// The corpus
corpus: C,
/// Feedbacks used to evaluate an input
feedback: F,
/// States of the feedback used to evaluate an input
feedback_states: FT,
// Solutions corpus
solutions: SC,
/// Objective Feedbacks
objective: OF,
/// Metadata stored for this state by one of the components
metadata: SerdeAnyMap,
/// MaxSize testcase size for mutators that appreciate it
@ -245,14 +172,23 @@ where
phantom: PhantomData<I>,
}
impl<C, F, I, OF, R, SC> HasRand<R> for State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> State for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
}
impl<C, FT, I, R, SC> HasRand<R> for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// The rand instance
#[inline]
@ -267,14 +203,13 @@ where
}
}
impl<C, F, I, OF, R, SC> HasCorpus<C, I> for State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> HasCorpus<C, I> for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// Returns the corpus
#[inline]
@ -289,14 +224,13 @@ where
}
}
impl<C, F, I, OF, R, SC> HasSolutions<SC, I> for State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> HasSolutions<SC, I> for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// Returns the solutions corpus
#[inline]
@ -311,14 +245,13 @@ where
}
}
impl<C, F, I, OF, R, SC> HasMetadata for State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> HasMetadata for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// Get all the metadata into an [`hashbrown::HashMap`]
#[inline]
@ -333,58 +266,34 @@ where
}
}
impl<C, F, I, OF, R, SC> HasFeedback<F, I> for State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> HasFeedbackStates<FT> for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// The feedback
/// The feedback states
#[inline]
fn feedback(&self) -> &F {
&self.feedback
fn feedback_states(&self) -> &FT {
&self.feedback_states
}
/// The feedback (mut)
/// The feedback states (mut)
#[inline]
fn feedback_mut(&mut self) -> &mut F {
&mut self.feedback
fn feedback_states_mut(&mut self) -> &mut FT {
&mut self.feedback_states
}
}
impl<C, F, I, OF, R, SC> HasObjective<OF, I> for State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> HasExecutions for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// The objective feedback
#[inline]
fn objective(&self) -> &OF {
&self.objective
}
/// The objective feedback (mut)
#[inline]
fn objective_mut(&mut self) -> &mut OF {
&mut self.objective
}
}
impl<C, F, I, OF, R, SC> HasExecutions for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// The executions counter
#[inline]
@ -399,14 +308,13 @@ where
}
}
impl<C, F, I, OF, R, SC> HasMaxSize for State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> HasMaxSize for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
fn max_size(&self) -> usize {
self.max_size
@ -417,14 +325,13 @@ where
}
}
impl<C, F, I, OF, R, SC> HasStartTime for State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> HasStartTime for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// The starting time
#[inline]
@ -439,139 +346,24 @@ where
}
}
impl<C, F, I, OF, R, SC> IfInteresting<I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// Evaluate if a set of observation channels has an interesting state
fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
self.feedback_mut()
.is_interesting(input, observers, exit_kind)
}
/// Adds this input to the corpus, if it's intersting, and return the index
#[inline]
fn add_if_interesting<CS>(
&mut self,
input: &I,
is_interesting: bool,
scheduler: &CS,
) -> Result<Option<usize>, Error>
where
CS: CorpusScheduler<I, Self>,
{
if is_interesting {
let mut testcase = Testcase::new(input.clone());
self.feedback_mut().append_metadata(&mut testcase)?;
let idx = self.corpus.add(testcase)?;
scheduler.on_add(self, idx)?;
Ok(Some(idx))
} else {
self.feedback_mut().discard_metadata(&input)?;
Ok(None)
}
}
}
impl<C, F, I, OF, R, SC> Evaluator<I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// Process one input, adding to the respective corpuses if needed and firing the right events
#[inline]
fn evaluate_input<CS, E, EM, OT>(
&mut self,
// TODO probably we can take a ref to input and pass a cloned one to add_if_interesting
input: I,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
) -> Result<(bool, Option<usize>), Error>
where
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
C: Corpus<I>,
EM: EventManager<I, Self>,
CS: CorpusScheduler<I, Self>,
{
let (is_interesting, is_solution) = self.execute_input(&input, executor, manager)?;
let observers = executor.observers();
if is_solution {
// If the input is a solution, add it to the respective corpus
let mut testcase = Testcase::new(input.clone());
self.objective_mut().append_metadata(&mut testcase)?;
self.solutions_mut().add(testcase)?;
} else {
self.objective_mut().discard_metadata(&input)?;
}
let corpus_idx = self.add_if_interesting(&input, is_interesting, scheduler)?;
if corpus_idx.is_some() {
let observers_buf = manager.serialize_observers(observers)?;
manager.fire(
self,
Event::NewTestcase {
input,
observers_buf,
corpus_size: self.corpus().count() + 1,
client_config: "TODO".into(),
time: crate::utils::current_time(),
executions: *self.executions(),
},
)?;
}
Ok((is_interesting, corpus_idx))
}
}
#[cfg(feature = "std")]
impl<C, F, OF, R, SC> State<C, F, BytesInput, OF, R, SC>
impl<C, FT, R, SC> StdState<C, FT, BytesInput, R, SC>
where
C: Corpus<BytesInput>,
R: Rand,
F: Feedback<BytesInput>,
FT: FeedbackStatesTuple,
SC: Corpus<BytesInput>,
OF: Feedback<BytesInput>,
{
/// loads inputs from a directory
fn load_from_directory<CS, E, OT, EM>(
fn load_from_directory<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
in_dir: &Path,
) -> Result<(), Error>
where
E: Executor<BytesInput>
+ HasObservers<OT>
+ HasExecHooks<EM, BytesInput, Self>
+ HasObserversHooks<EM, BytesInput, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, BytesInput, Self>,
EM: EventManager<BytesInput, Self>,
CS: CorpusScheduler<BytesInput, Self>,
Z: Evaluator<E, EM, BytesInput, Self>,
{
for entry in fs::read_dir(in_dir)? {
let entry = entry?;
@ -588,19 +380,12 @@ where
println!("Loading file {:?} ...", &path);
let bytes = fs::read(&path)?;
let input = BytesInput::new(bytes);
let (is_interesting, is_solution) =
self.execute_input(&input, executor, manager)?;
if self
.add_if_interesting(&input, is_interesting, scheduler)?
.is_none()
{
let (is_interesting, _) = fuzzer.evaluate_input(self, executor, manager, input)?;
if !is_interesting {
println!("File {:?} was not interesting, skipped.", &path);
}
if is_solution {
println!("File {:?} is a solution, however will be not considered as it is an initial testcase.", &path);
}
} else if attr.is_dir() {
self.load_from_directory(executor, manager, scheduler, &path)?;
self.load_from_directory(fuzzer, executor, manager, &path)?;
}
}
@ -608,24 +393,19 @@ where
}
/// Loads initial inputs from the passed-in `in_dirs`.
pub fn load_initial_inputs<CS, E, OT, EM>(
pub fn load_initial_inputs<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
E: Executor<BytesInput>
+ HasObservers<OT>
+ HasExecHooks<EM, BytesInput, Self>
+ HasObserversHooks<EM, BytesInput, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, BytesInput, Self>,
EM: EventManager<BytesInput, Self>,
CS: CorpusScheduler<BytesInput, Self>,
Z: Evaluator<E, EM, BytesInput, Self>,
EM: EventManager<E, BytesInput, Self, Z>,
{
for in_dir in in_dirs {
self.load_from_directory(executor, manager, scheduler, in_dir)?;
self.load_from_directory(fuzzer, executor, manager, in_dir)?;
}
manager.fire(
self,
@ -635,122 +415,37 @@ where
phantom: PhantomData,
},
)?;
manager.process(self, executor, scheduler)?;
manager.process(fuzzer, self, executor)?;
Ok(())
}
}
impl<C, F, I, OF, R, SC> State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
/// Runs the input and triggers observers and feedback
pub fn execute_input<E, EM, OT>(
&mut self,
input: &I,
executor: &mut E,
event_mgr: &mut EM,
) -> Result<(bool, bool), Error>
where
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
C: Corpus<I>,
EM: EventManager<I, Self>,
{
start_timer!(self);
executor.pre_exec_observers(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PreExecObservers);
start_timer!(self);
executor.pre_exec(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PreExec);
start_timer!(self);
let exit_kind = executor.run_target(input)?;
mark_feature_time!(self, PerfFeature::TargetExecution);
start_timer!(self);
executor.post_exec(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PostExec);
*self.executions_mut() += 1;
start_timer!(self);
executor.post_exec_observers(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PostExecObservers);
let observers = executor.observers();
#[cfg(not(feature = "introspection"))]
let is_interesting = self
.feedback_mut()
.is_interesting(&input, observers, &exit_kind)?;
#[cfg(feature = "introspection")]
let is_interesting = {
// Init temporary feedback stats here. We can't use the typical pattern above
// since we need a `mut self` for `feedbacks_mut`, so we can't also hand a
// new `mut self` to `is_interesting_with_perf`. We use this stack
// variable to get the stats and then update the feedbacks directly
let mut feedback_stats = [0_u64; crate::stats::NUM_FEEDBACKS];
let feedback_index = 0;
let is_interesting = self.feedback_mut().is_interesting_with_perf(
&input,
observers,
&exit_kind,
&mut feedback_stats,
feedback_index,
)?;
// Update the feedback stats
self.introspection_stats_mut()
.update_feedbacks(feedback_stats);
// Return the total fitness
is_interesting
};
start_timer!(self);
let is_solution = self
.objective_mut()
.is_interesting(&input, observers, &exit_kind)?;
mark_feature_time!(self, PerfFeature::GetObjectivesInterestingAll);
Ok((is_interesting, is_solution))
}
/// Generate `num` initial inputs, using the passed-in generator.
pub fn generate_initial_inputs<CS, G, E, OT, EM>(
pub fn generate_initial_inputs<G, E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
generator: &mut G,
manager: &mut EM,
scheduler: &CS,
num: usize,
) -> Result<(), Error>
where
G: Generator<I, R>,
C: Corpus<I>,
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
EM: EventManager<I, Self>,
CS: CorpusScheduler<I, Self>,
Z: Evaluator<E, EM, I, Self>,
EM: EventManager<E, I, Self, Z>,
{
let mut added = 0;
for _ in 0..num {
let input = generator.generate(self.rand_mut())?;
let (is_interesting, _) = self.evaluate_input(input, executor, manager, scheduler)?;
let (is_interesting, _) = fuzzer.evaluate_input(self, executor, manager, input)?;
if is_interesting {
added += 1;
}
@ -763,21 +458,20 @@ where
phantom: PhantomData,
},
)?;
manager.process(self, executor, scheduler)?;
manager.process(fuzzer, self, executor)?;
Ok(())
}
/// Creates a new `State`, taking ownership of all of the individual components during fuzzing.
pub fn new(rand: R, corpus: C, feedback: F, solutions: SC, objective: OF) -> Self {
pub fn new(rand: R, corpus: C, solutions: SC, feedback_states: FT) -> Self {
Self {
rand,
executions: 0,
start_time: Duration::from_millis(0),
metadata: SerdeAnyMap::default(),
corpus,
feedback,
feedback_states,
solutions,
objective,
max_size: DEFAULT_MAX_SIZE,
#[cfg(feature = "introspection")]
introspection_stats: ClientPerfStats::new(),
@ -787,14 +481,13 @@ where
}
#[cfg(feature = "introspection")]
impl<C, F, I, OF, R, SC> HasClientPerfStats for State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> HasClientPerfStats for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
fn introspection_stats(&self) -> &ClientPerfStats {
&self.introspection_stats
@ -806,14 +499,13 @@ where
}
#[cfg(not(feature = "introspection"))]
impl<C, F, I, OF, R, SC> HasClientPerfStats for State<C, F, I, OF, R, SC>
impl<C, FT, I, R, SC> HasClientPerfStats for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
OF: Feedback<I>,
{
fn introspection_stats(&self) -> &ClientPerfStats {
unimplemented!()

View File

@ -1707,8 +1707,14 @@ pub struct AsanErrorsObserver {
impl Observer for AsanErrorsObserver {}
impl<EM, I, S> HasExecHooks<EM, I, S> for AsanErrorsObserver {
fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> {
impl<EM, I, S, Z> HasExecHooks<EM, I, S, Z> for AsanErrorsObserver {
fn pre_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
unsafe {
if ASAN_ERRORS.is_some() {
ASAN_ERRORS.as_mut().unwrap().clear();
@ -1767,12 +1773,13 @@ pub struct AsanErrorsFeedback {
errors: Option<AsanErrors>,
}
impl<I> Feedback<I> for AsanErrorsFeedback
impl<I, S> Feedback<I, S> for AsanErrorsFeedback
where
I: Input + HasTargetBytes,
{
fn is_interesting<OT: ObserversTuple>(
&mut self,
_state: &mut S,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
@ -1793,7 +1800,7 @@ where
}
}
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
if let Some(errors) = &self.errors {
testcase.add_metadata(errors.clone());
}
@ -1801,7 +1808,7 @@ where
Ok(())
}
fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> {
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
self.errors = None;
Ok(())
}