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:
parent
8551f093c4
commit
b51936397b
@ -5,13 +5,13 @@ use libafl::{
|
|||||||
corpus::{InMemoryCorpus, OnDiskCorpus, QueueCorpusScheduler},
|
corpus::{InMemoryCorpus, OnDiskCorpus, QueueCorpusScheduler},
|
||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
generators::RandPrintablesGenerator,
|
generators::RandPrintablesGenerator,
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
observers::StdMapObserver,
|
observers::StdMapObserver,
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::State,
|
state::StdState,
|
||||||
stats::SimpleStats,
|
stats::SimpleStats,
|
||||||
utils::{current_nanos, StdRand},
|
utils::{current_nanos, StdRand},
|
||||||
};
|
};
|
||||||
@ -42,19 +42,27 @@ pub fn main() {
|
|||||||
// Create an observation channel using the signals map
|
// Create an observation channel using the signals map
|
||||||
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
|
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
|
// create a State from scratch
|
||||||
let mut state = State::new(
|
let mut state = StdState::new(
|
||||||
// RNG
|
// RNG
|
||||||
StdRand::with_seed(current_nanos()),
|
StdRand::with_seed(current_nanos()),
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
InMemoryCorpus::new(),
|
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),
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
||||||
// Feedbacks to recognize an input as solution
|
// States of the feedbacks.
|
||||||
CrashFeedback::new(),
|
// 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
|
// The Stats trait define how the fuzzer stats are reported to the user
|
||||||
@ -67,9 +75,17 @@ pub fn main() {
|
|||||||
// A queue policy to get testcasess from the corpus
|
// A queue policy to get testcasess from the corpus
|
||||||
let scheduler = QueueCorpusScheduler::new();
|
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
|
// Create the executor for an in-process function with just one observer
|
||||||
let mut executor =
|
let mut executor = InProcessExecutor::new(
|
||||||
InProcessExecutor::new(&mut harness, tuple_list!(observer), &mut state, &mut mgr)
|
&mut harness,
|
||||||
|
tuple_list!(observer),
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
)
|
||||||
.expect("Failed to create the Executor".into());
|
.expect("Failed to create the Executor".into());
|
||||||
|
|
||||||
// Generator of printable bytearrays of max size 32
|
// Generator of printable bytearrays of max size 32
|
||||||
@ -77,17 +93,14 @@ pub fn main() {
|
|||||||
|
|
||||||
// Generate 8 initial inputs
|
// Generate 8 initial inputs
|
||||||
state
|
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());
|
.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 mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
let stage = StdMutationalStage::new(mutator);
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
|
|
||||||
// A fuzzer with just one stage
|
|
||||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
|
||||||
|
|
||||||
fuzzer
|
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());
|
.expect("Error in the fuzzing loop".into());
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,14 @@ use libafl::{
|
|||||||
HasExecHooksTuple, HasObservers, HasObserversHooks,
|
HasExecHooksTuple, HasObservers, HasObserversHooks,
|
||||||
},
|
},
|
||||||
feedback_or,
|
feedback_or,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
mutators::token_mutations::Tokens,
|
mutators::token_mutations::Tokens,
|
||||||
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver},
|
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver, TimeObserver},
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::{HasCorpus, HasMetadata, State},
|
state::{HasCorpus, HasMetadata, StdState},
|
||||||
stats::SimpleStats,
|
stats::SimpleStats,
|
||||||
utils::{current_nanos, StdRand},
|
utils::{current_nanos, StdRand},
|
||||||
Error,
|
Error,
|
||||||
@ -39,14 +39,14 @@ use libafl_frida::{
|
|||||||
FridaOptions,
|
FridaOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
|
struct FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
|
||||||
where
|
where
|
||||||
FH: FridaHelper<'b>,
|
FH: FridaHelper<'b>,
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
I: Input + HasTargetBytes,
|
I: Input + HasTargetBytes,
|
||||||
OT: ObserversTuple,
|
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
|
/// Frida's dynamic rewriting engine
|
||||||
stalker: Stalker<'a>,
|
stalker: Stalker<'a>,
|
||||||
/// User provided callback for instrumentation
|
/// User provided callback for instrumentation
|
||||||
@ -55,8 +55,8 @@ where
|
|||||||
_phantom: PhantomData<&'b u8>,
|
_phantom: PhantomData<&'b u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> Executor<I>
|
impl<'a, 'b, 'c, FH, H, I, OT, S> Executor<I>
|
||||||
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
|
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
|
||||||
where
|
where
|
||||||
FH: FridaHelper<'b>,
|
FH: FridaHelper<'b>,
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
@ -91,8 +91,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasExecHooks<EM, I, S>
|
impl<'a, 'b, 'c, EM, FH, H, I, OT, S, Z> HasExecHooks<EM, I, S, Z>
|
||||||
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
|
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
|
||||||
where
|
where
|
||||||
FH: FridaHelper<'b>,
|
FH: FridaHelper<'b>,
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
@ -101,21 +101,33 @@ where
|
|||||||
{
|
{
|
||||||
/// Called right before exexution starts
|
/// Called right before exexution starts
|
||||||
#[inline]
|
#[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.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.
|
/// Called right after execution finished.
|
||||||
#[inline]
|
#[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.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>
|
impl<'a, 'b, 'c, FH, H, I, OT, S> HasObservers<OT>
|
||||||
for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S>
|
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
|
||||||
where
|
where
|
||||||
FH: FridaHelper<'b>,
|
FH: FridaHelper<'b>,
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
@ -133,17 +145,17 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasObserversHooks<EM, 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, EM, FH, H, I, OT, S>
|
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
|
||||||
where
|
where
|
||||||
FH: FridaHelper<'b>,
|
FH: FridaHelper<'b>,
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
I: Input + HasTargetBytes,
|
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
|
where
|
||||||
FH: FridaHelper<'b>,
|
FH: FridaHelper<'b>,
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
@ -152,7 +164,7 @@ where
|
|||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
gum: &'a Gum,
|
gum: &'a Gum,
|
||||||
base: InProcessExecutor<'a, EM, H, I, OT, S>,
|
base: InProcessExecutor<'a, H, I, OT, S>,
|
||||||
helper: &'c mut FH,
|
helper: &'c mut FH,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -268,25 +280,45 @@ unsafe fn fuzz(
|
|||||||
MAP_SIZE,
|
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
|
// If not restarting, create a State from scratch
|
||||||
let mut state = state.unwrap_or_else(|| {
|
let mut state = state.unwrap_or_else(|| {
|
||||||
State::new(
|
StdState::new(
|
||||||
// RNG
|
// RNG
|
||||||
StdRand::with_seed(current_nanos()),
|
StdRand::with_seed(current_nanos()),
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
InMemoryCorpus::new(),
|
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),
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
OnDiskCorpus::new_save_meta(objective_dir, Some(OnDiskMetadataFormat::JsonPretty))
|
OnDiskCorpus::new_save_meta(objective_dir, Some(OnDiskMetadataFormat::JsonPretty))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
// Feedbacks to recognize an input as solution
|
// States of the feedbacks.
|
||||||
feedback_or!(
|
// They are the data related to the feedbacks that you want to persist in the State.
|
||||||
CrashFeedback::new(),
|
tuple_list!(feedback_state),
|
||||||
TimeoutFeedback::new(),
|
|
||||||
AsanErrorsFeedback::new()
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -305,11 +337,13 @@ unsafe fn fuzz(
|
|||||||
|
|
||||||
// Setup a basic mutator with a mutational stage
|
// Setup a basic mutator with a mutational stage
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
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 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();
|
frida_helper.register_thread();
|
||||||
|
|
||||||
@ -318,7 +352,8 @@ unsafe fn fuzz(
|
|||||||
&gum,
|
&gum,
|
||||||
InProcessExecutor::new(
|
InProcessExecutor::new(
|
||||||
&mut frida_harness,
|
&mut frida_harness,
|
||||||
tuple_list!(edges_observer, AsanErrorsObserver::new(&ASAN_ERRORS)),
|
tuple_list!(edges_observer, time_observer, asan_observer),
|
||||||
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut restarting_mgr,
|
&mut restarting_mgr,
|
||||||
)?,
|
)?,
|
||||||
@ -338,7 +373,12 @@ unsafe fn fuzz(
|
|||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
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!(
|
.expect(&format!(
|
||||||
"Failed to load initial corpus at {:?}",
|
"Failed to load initial corpus at {:?}",
|
||||||
&corpus_dirs
|
&corpus_dirs
|
||||||
@ -346,7 +386,7 @@ unsafe fn fuzz(
|
|||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
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
|
// Never reached
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -9,13 +9,13 @@ use libafl::{
|
|||||||
events::setup_restarting_mgr_std,
|
events::setup_restarting_mgr_std,
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||||
feedback_or,
|
feedback_or,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
mutators::token_mutations::Tokens,
|
mutators::token_mutations::Tokens,
|
||||||
observers::StdMapObserver,
|
observers::StdMapObserver,
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::{HasCorpus, HasMetadata, State},
|
state::{HasCorpus, HasMetadata, StdState},
|
||||||
stats::SimpleStats,
|
stats::SimpleStats,
|
||||||
utils::{current_nanos, StdRand},
|
utils::{current_nanos, StdRand},
|
||||||
Error,
|
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
|
// Create an observation channel using the allocations map
|
||||||
let allocs_observer = StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_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
|
// If not restarting, create a State from scratch
|
||||||
let mut state = state.unwrap_or_else(|| {
|
let mut state = state.unwrap_or_else(|| {
|
||||||
State::new(
|
StdState::new(
|
||||||
// RNG
|
// RNG
|
||||||
StdRand::with_seed(current_nanos()),
|
StdRand::with_seed(current_nanos()),
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
InMemoryCorpus::new(),
|
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),
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
OnDiskCorpus::new(objective_dir).unwrap(),
|
OnDiskCorpus::new(objective_dir).unwrap(),
|
||||||
// Feedbacks to recognize an input as solution
|
// States of the feedbacks.
|
||||||
CrashFeedback::new(),
|
// 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
|
// Setup a basic mutator with a mutational stage
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
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
|
// A random policy to get testcasess from the corpus
|
||||||
let scheduler = RandCorpusScheduler::new();
|
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
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |buf: &[u8]| {
|
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(
|
let mut executor = InProcessExecutor::new(
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, cmps_observer, allocs_observer),
|
tuple_list!(edges_observer, cmps_observer, allocs_observer),
|
||||||
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut restarting_mgr,
|
&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
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
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!(
|
.expect(&format!(
|
||||||
"Failed to load initial corpus at {:?}",
|
"Failed to load initial corpus at {:?}",
|
||||||
&corpus_dirs
|
&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());
|
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
|
// Never reached
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -10,16 +10,16 @@ use libafl::{
|
|||||||
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
||||||
QueueCorpusScheduler,
|
QueueCorpusScheduler,
|
||||||
},
|
},
|
||||||
events::{setup_restarting_mgr_std, EventManager},
|
events::{setup_restarting_mgr_std, EventRestarter},
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
||||||
feedback_or,
|
feedback_or,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
mutators::token_mutations::Tokens,
|
mutators::token_mutations::Tokens,
|
||||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::{HasCorpus, HasMetadata, State},
|
state::{HasCorpus, HasMetadata, StdState},
|
||||||
stats::SimpleStats,
|
stats::SimpleStats,
|
||||||
utils::{current_nanos, StdRand},
|
utils::{current_nanos, StdRand},
|
||||||
Error,
|
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
|
// Create an observation channel to keep track of the execution time
|
||||||
let time_observer = TimeObserver::new("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
|
// If not restarting, create a State from scratch
|
||||||
let mut state = state.unwrap_or_else(|| {
|
let mut state = state.unwrap_or_else(|| {
|
||||||
State::new(
|
StdState::new(
|
||||||
// RNG
|
// RNG
|
||||||
StdRand::with_seed(current_nanos()),
|
StdRand::with_seed(current_nanos()),
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
InMemoryCorpus::new(),
|
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),
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
OnDiskCorpus::new(objective_dir).unwrap(),
|
OnDiskCorpus::new(objective_dir).unwrap(),
|
||||||
// Feedbacks to recognize an input as solution
|
// States of the feedbacks.
|
||||||
feedback_or!(CrashFeedback::new(), TimeoutFeedback::new()),
|
// 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
|
// Setup a basic mutator with a mutational stage
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
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
|
|
||||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
|
||||||
|
|
||||||
// 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 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
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |buf: &[u8]| {
|
let mut harness = |buf: &[u8]| {
|
||||||
libfuzzer_test_one_input(buf);
|
libfuzzer_test_one_input(buf);
|
||||||
@ -125,6 +136,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
|||||||
InProcessExecutor::new(
|
InProcessExecutor::new(
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, time_observer),
|
tuple_list!(edges_observer, time_observer),
|
||||||
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut restarting_mgr,
|
&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
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
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!(
|
.expect(&format!(
|
||||||
"Failed to load initial corpus at {:?}",
|
"Failed to load initial corpus at {:?}",
|
||||||
&corpus_dirs
|
&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.
|
// However, you will lose a lot of performance that way.
|
||||||
let iters = 1_000_000;
|
let iters = 1_000_000;
|
||||||
fuzzer.fuzz_loop_for(
|
fuzzer.fuzz_loop_for(
|
||||||
|
&mut stages,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut executor,
|
&mut executor,
|
||||||
&mut restarting_mgr,
|
&mut restarting_mgr,
|
||||||
&scheduler,
|
|
||||||
iters,
|
iters,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -6,15 +6,15 @@ use std::{env, path::PathBuf};
|
|||||||
use libafl::{
|
use libafl::{
|
||||||
bolts::tuples::tuple_list,
|
bolts::tuples::tuple_list,
|
||||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
|
||||||
events::{setup_restarting_mgr_std, EventManager},
|
events::{setup_restarting_mgr_std, EventRestarter},
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||||
feedbacks::{MaxMapFeedback, ReachabilityFeedback},
|
feedbacks::{MapFeedbackState, MaxMapFeedback, ReachabilityFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
mutators::token_mutations::Tokens,
|
mutators::token_mutations::Tokens,
|
||||||
observers::{HitcountsMapObserver, StdMapObserver},
|
observers::{HitcountsMapObserver, StdMapObserver},
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::{HasCorpus, HasMetadata, State},
|
state::{HasCorpus, HasMetadata, StdState},
|
||||||
stats::SimpleStats,
|
stats::SimpleStats,
|
||||||
utils::{current_nanos, StdRand},
|
utils::{current_nanos, StdRand},
|
||||||
Error,
|
Error,
|
||||||
@ -71,20 +71,28 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
|||||||
let reachability_observer =
|
let reachability_observer =
|
||||||
unsafe { StdMapObserver::new_from_ptr("png.c", __libafl_target_list, TARGET_SIZE) };
|
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
|
// If not restarting, create a State from scratch
|
||||||
let mut state = state.unwrap_or_else(|| {
|
let mut state = state.unwrap_or_else(|| {
|
||||||
State::new(
|
StdState::new(
|
||||||
// RNG
|
// RNG
|
||||||
StdRand::with_seed(current_nanos()),
|
StdRand::with_seed(current_nanos()),
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
InMemoryCorpus::new(),
|
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),
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
OnDiskCorpus::new(objective_dir).unwrap(),
|
OnDiskCorpus::new(objective_dir).unwrap(),
|
||||||
// Feedbacks to recognize an input as solution
|
// States of the feedbacks.
|
||||||
ReachabilityFeedback::new_with_observer(&reachability_observer),
|
// 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
|
// Setup a basic mutator with a mutational stage
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
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
|
|
||||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
|
||||||
|
|
||||||
// A random policy to get testcasess from the corpus
|
// A random policy to get testcasess from the corpus
|
||||||
let scheduler = RandCorpusScheduler::new();
|
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
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |buf: &[u8]| {
|
let mut harness = |buf: &[u8]| {
|
||||||
libfuzzer_test_one_input(buf);
|
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
|
// 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(
|
let mut executor = InProcessExecutor::new(
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, reachability_observer,),
|
tuple_list!(edges_observer, reachability_observer),
|
||||||
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut restarting_mgr,
|
&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
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
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!(
|
.expect(&format!(
|
||||||
"Failed to load initial corpus at {:?}",
|
"Failed to load initial corpus at {:?}",
|
||||||
&corpus_dirs
|
&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.
|
// However, you will lose a lot of performance that way.
|
||||||
let iters = 1_000_000;
|
let iters = 1_000_000;
|
||||||
fuzzer.fuzz_loop_for(
|
fuzzer.fuzz_loop_for(
|
||||||
|
&mut stages,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut executor,
|
&mut executor,
|
||||||
&mut restarting_mgr,
|
&mut restarting_mgr,
|
||||||
&scheduler,
|
|
||||||
iters,
|
iters,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -12,13 +12,13 @@ use libafl::{
|
|||||||
events::setup_restarting_mgr_std,
|
events::setup_restarting_mgr_std,
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||||
feedback_or,
|
feedback_or,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
mutators::token_mutations::Tokens,
|
mutators::token_mutations::Tokens,
|
||||||
observers::{StdMapObserver, TimeObserver},
|
observers::{StdMapObserver, TimeObserver},
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::{HasCorpus, HasMetadata, State},
|
state::{HasCorpus, HasMetadata, StdState},
|
||||||
stats::SimpleStats,
|
stats::SimpleStats,
|
||||||
utils::{current_nanos, StdRand},
|
utils::{current_nanos, StdRand},
|
||||||
Error,
|
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
|
// Create an observation channel to keep track of the execution time
|
||||||
let time_observer = TimeObserver::new("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
|
// If not restarting, create a State from scratch
|
||||||
let mut state = state.unwrap_or_else(|| {
|
let mut state = state.unwrap_or_else(|| {
|
||||||
State::new(
|
StdState::new(
|
||||||
// RNG
|
// RNG
|
||||||
StdRand::with_seed(current_nanos()),
|
StdRand::with_seed(current_nanos()),
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
InMemoryCorpus::new(),
|
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),
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
OnDiskCorpus::new(objective_dir).unwrap(),
|
OnDiskCorpus::new(objective_dir).unwrap(),
|
||||||
// Feedback to recognize an input as solution
|
// States of the feedbacks.
|
||||||
CrashFeedback::new(),
|
// 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
|
// Setup a basic mutator with a mutational stage
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
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
|
|
||||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
|
||||||
|
|
||||||
// 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 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
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |buf: &[u8]| {
|
let mut harness = |buf: &[u8]| {
|
||||||
libfuzzer_test_one_input(buf);
|
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(
|
let mut executor = InProcessExecutor::new(
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, time_observer),
|
tuple_list!(edges_observer, time_observer),
|
||||||
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut restarting_mgr,
|
&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
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
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!(
|
.expect(&format!(
|
||||||
"Failed to load initial corpus at {:?}",
|
"Failed to load initial corpus at {:?}",
|
||||||
&corpus_dirs
|
&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());
|
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
|
// Never reached
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -82,7 +82,7 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, CorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, Testcase},
|
corpus::{Corpus, CorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, Testcase},
|
||||||
inputs::bytes::BytesInput,
|
inputs::bytes::BytesInput,
|
||||||
state::{HasCorpus, State},
|
state::{HasCorpus, StdState},
|
||||||
utils::StdRand,
|
utils::StdRand,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ mod tests {
|
|||||||
OnDiskCorpus::<BytesInput>::new(PathBuf::from("target/.test/fancy/objective/path"))
|
OnDiskCorpus::<BytesInput>::new(PathBuf::from("target/.test/fancy/objective/path"))
|
||||||
.unwrap();
|
.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 next_idx = scheduler.next(&mut state).unwrap();
|
||||||
let filename = state
|
let filename = state
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use alloc::{string::ToString, vec::Vec};
|
use alloc::{string::ToString, vec::Vec};
|
||||||
use core::{marker::PhantomData, time::Duration};
|
use core::{marker::PhantomData, time::Duration};
|
||||||
|
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -18,13 +19,12 @@ use crate::{
|
|||||||
llmp::{self, Flags, LlmpClientDescription, LlmpSender, Tag},
|
llmp::{self, Flags, LlmpClientDescription, LlmpSender, Tag},
|
||||||
shmem::ShMemProvider,
|
shmem::ShMemProvider,
|
||||||
},
|
},
|
||||||
corpus::CorpusScheduler,
|
events::{BrokerEventResult, Event, EventFirer, EventManager, EventProcessor, EventRestarter},
|
||||||
events::{BrokerEventResult, Event, EventManager},
|
executors::Executor,
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
executors::{Executor, HasObservers},
|
fuzzer::{IfInteresting, IsInteresting},
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
state::IfInteresting,
|
|
||||||
stats::Stats,
|
stats::Stats,
|
||||||
Error,
|
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,
|
/// An [`EventManager`] that forwards all events to other attached fuzzers on shared maps or via tcp,
|
||||||
/// using low-level message passing, [`crate::bolts::llmp`].
|
/// using low-level message passing, [`crate::bolts::llmp`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LlmpEventManager<I, S, SP, ST>
|
pub struct LlmpEventManager<I, OT, S, SP, ST>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: IfInteresting<I>,
|
OT: ObserversTuple,
|
||||||
SP: ShMemProvider + 'static,
|
SP: ShMemProvider + 'static,
|
||||||
ST: Stats,
|
ST: Stats,
|
||||||
//CE: CustomEvent<I>,
|
//CE: CustomEvent<I>,
|
||||||
@ -70,18 +70,18 @@ where
|
|||||||
#[cfg(feature = "llmp_compression")]
|
#[cfg(feature = "llmp_compression")]
|
||||||
compressor: GzipCompressor,
|
compressor: GzipCompressor,
|
||||||
|
|
||||||
phantom: PhantomData<(I, S)>,
|
phantom: PhantomData<(I, OT, S)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The minimum buffer size at which to compress LLMP IPC messages.
|
/// The minimum buffer size at which to compress LLMP IPC messages.
|
||||||
#[cfg(feature = "llmp_compression")]
|
#[cfg(feature = "llmp_compression")]
|
||||||
const COMPRESS_THRESHOLD: usize = 1024;
|
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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: IfInteresting<I>,
|
OT: ObserversTuple,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider + 'static,
|
||||||
ST: Stats,
|
ST: Stats,
|
||||||
{
|
{
|
||||||
/// LLMP clients will have to wait until their pages are mapped by somebody.
|
/// 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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: IfInteresting<I>,
|
OT: ObserversTuple,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider + 'static,
|
||||||
ST: Stats,
|
ST: Stats,
|
||||||
{
|
{
|
||||||
/// Create llmp on a port
|
/// Create llmp on a port
|
||||||
@ -293,18 +293,16 @@ where
|
|||||||
|
|
||||||
// Handle arriving events in the client
|
// Handle arriving events in the client
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
fn handle_in_client<CS, E, OT>(
|
fn handle_in_client<E, Z>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
|
_executor: &mut E,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
_sender_id: u32,
|
_sender_id: u32,
|
||||||
event: Event<I>,
|
event: Event<I>,
|
||||||
_executor: &mut E,
|
|
||||||
scheduler: &CS,
|
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
CS: CorpusScheduler<I, S>,
|
Z: IfInteresting<I, S> + IsInteresting<I, OT, S>,
|
||||||
E: Executor<I> + HasObservers<OT>,
|
|
||||||
OT: ObserversTuple,
|
|
||||||
{
|
{
|
||||||
match event {
|
match event {
|
||||||
Event::NewTestcase {
|
Event::NewTestcase {
|
||||||
@ -322,9 +320,10 @@ where
|
|||||||
|
|
||||||
let observers: OT = postcard::from_bytes(&observers_buf)?;
|
let observers: OT = postcard::from_bytes(&observers_buf)?;
|
||||||
// TODO include ExitKind in NewTestcase
|
// TODO include ExitKind in NewTestcase
|
||||||
let is_interesting = state.is_interesting(&input, &observers, &ExitKind::Ok)?;
|
let is_interesting =
|
||||||
if state
|
fuzzer.is_interesting(state, &input, &observers, &ExitKind::Ok)?;
|
||||||
.add_if_interesting(&input, is_interesting, scheduler)?
|
if fuzzer
|
||||||
|
.add_if_interesting(state, &input, is_interesting)?
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
#[cfg(feature = "std")]
|
#[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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: IfInteresting<I>,
|
OT: ObserversTuple,
|
||||||
SP: ShMemProvider,
|
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.
|
/// 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,
|
/// Otherwise, the OS may already have removed the shared maps,
|
||||||
@ -355,18 +391,18 @@ where
|
|||||||
client.await_save_to_unmap_blocking();
|
client.await_save_to_unmap_blocking();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn process<CS, E, OT>(
|
impl<E, I, OT, S, SP, ST, Z> EventProcessor<E, S, Z> for LlmpEventManager<I, OT, S, SP, ST>
|
||||||
&mut self,
|
where
|
||||||
state: &mut S,
|
SP: ShMemProvider,
|
||||||
executor: &mut E,
|
ST: Stats,
|
||||||
scheduler: &CS,
|
E: Executor<I>,
|
||||||
) -> Result<usize, Error>
|
I: Input,
|
||||||
where
|
|
||||||
CS: CorpusScheduler<I, S>,
|
|
||||||
E: Executor<I> + HasObservers<OT>,
|
|
||||||
OT: ObserversTuple,
|
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
|
// TODO: Get around local event copy by moving handle_in_client
|
||||||
let mut events = vec![];
|
let mut events = vec![];
|
||||||
match &mut self.llmp {
|
match &mut self.llmp {
|
||||||
@ -397,49 +433,34 @@ where
|
|||||||
};
|
};
|
||||||
let count = events.len();
|
let count = events.len();
|
||||||
events.drain(..).try_for_each(|(sender_id, event)| {
|
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)
|
Ok(count)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "llmp_compression")]
|
impl<E, I, OT, S, SP, ST, Z> EventManager<E, I, S, Z> for LlmpEventManager<I, OT, S, SP, ST>
|
||||||
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
|
where
|
||||||
let serialized = postcard::to_allocvec(&event)?;
|
SP: ShMemProvider,
|
||||||
let flags: Flags = LLMP_FLAG_INITIALIZED;
|
ST: Stats,
|
||||||
|
E: Executor<I>,
|
||||||
match self.compressor.compress(&serialized)? {
|
I: Input,
|
||||||
Some(comp_buf) => {
|
OT: ObserversTuple,
|
||||||
self.llmp.send_buf_with_flags(
|
Z: IfInteresting<I, S> + IsInteresting<I, OT, S>, //CE: CustomEvent<I>,
|
||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize the current state and corpus during an executiont to bytes.
|
/// Serialize the current state and corpus during an executiont to bytes.
|
||||||
/// On top, add the current llmp event manager instance to be restored
|
/// 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.
|
/// 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,
|
state: &S,
|
||||||
mgr: &LlmpEventManager<I, S, SP, ST>,
|
mgr: &LlmpEventManager<I, OT, S, SP, ST>,
|
||||||
) -> Result<Vec<u8>, Error>
|
) -> Result<Vec<u8>, Error>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: Serialize + IfInteresting<I>,
|
OT: ObserversTuple,
|
||||||
|
S: Serialize,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
ST: Stats,
|
ST: Stats,
|
||||||
{
|
{
|
||||||
@ -448,13 +469,14 @@ where
|
|||||||
|
|
||||||
/// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)`
|
/// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)`
|
||||||
#[allow(clippy::type_complexity)]
|
#[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,
|
shmem_provider: SP,
|
||||||
state_corpus_serialized: &[u8],
|
state_corpus_serialized: &[u8],
|
||||||
) -> Result<(S, LlmpEventManager<I, S, SP, ST>), Error>
|
) -> Result<(S, LlmpEventManager<I, OT, S, SP, ST>), Error>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: DeserializeOwned + IfInteresting<I>,
|
OT: ObserversTuple,
|
||||||
|
S: DeserializeOwned,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
ST: Stats,
|
ST: Stats,
|
||||||
{
|
{
|
||||||
@ -467,32 +489,49 @@ where
|
|||||||
|
|
||||||
/// A manager that can restart on the fly, storing states in-between (in `on_resatrt`)
|
/// A manager that can restart on the fly, storing states in-between (in `on_resatrt`)
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LlmpRestartingEventManager<I, S, SP, ST>
|
pub struct LlmpRestartingEventManager<I, OT, S, SP, ST>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: IfInteresting<I>,
|
OT: ObserversTuple,
|
||||||
SP: ShMemProvider + 'static,
|
SP: ShMemProvider + 'static,
|
||||||
ST: Stats,
|
ST: Stats,
|
||||||
//CE: CustomEvent<I>,
|
//CE: CustomEvent<I>,
|
||||||
{
|
{
|
||||||
/// The embedded llmp event manager
|
/// 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
|
/// The sender to serialize the state for the next runner
|
||||||
sender: LlmpSender<SP>,
|
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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: IfInteresting<I> + Serialize,
|
OT: ObserversTuple,
|
||||||
|
S: Serialize,
|
||||||
SP: ShMemProvider,
|
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.
|
/// 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,
|
/// Otherwise, the OS may already have removed the shared maps,
|
||||||
#[inline]
|
#[inline]
|
||||||
fn await_restart_safe(&mut self) {
|
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.
|
/// 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
|
self.sender
|
||||||
.send_buf(_LLMP_TAG_RESTART, &state_corpus_serialized)
|
.send_buf(_LLMP_TAG_RESTART, &state_corpus_serialized)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn process<CS, E, OT>(
|
impl<E, I, OT, S, SP, ST, Z> EventProcessor<E, S, Z>
|
||||||
&mut self,
|
for LlmpRestartingEventManager<I, OT, S, SP, ST>
|
||||||
state: &mut S,
|
where
|
||||||
executor: &mut E,
|
E: Executor<I>,
|
||||||
scheduler: &CS,
|
I: Input,
|
||||||
) -> Result<usize, Error>
|
Z: IfInteresting<I, S> + IsInteresting<I, OT, S>,
|
||||||
where
|
|
||||||
CS: CorpusScheduler<I, S>,
|
|
||||||
E: Executor<I> + HasObservers<OT>,
|
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
{
|
SP: ShMemProvider + 'static,
|
||||||
self.llmp_mgr.process(state, executor, scheduler)
|
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> {
|
impl<E, I, OT, S, SP, ST, Z> EventManager<E, I, S, Z>
|
||||||
// Check if we are going to crash in the event, in which case we store our current state for the next runner
|
for LlmpRestartingEventManager<I, OT, S, SP, ST>
|
||||||
self.llmp_mgr.fire(state, event)
|
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
|
/// 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)
|
/// 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";
|
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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: IfInteresting<I>,
|
OT: ObserversTuple,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider + 'static,
|
||||||
ST: Stats, //CE: CustomEvent<I>,
|
ST: Stats,
|
||||||
|
//CE: CustomEvent<I>,
|
||||||
{
|
{
|
||||||
/// Create a new runner, the executed child doing the actual fuzzing.
|
/// 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 }
|
Self { llmp_mgr, sender }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,26 +609,26 @@ where
|
|||||||
/// The restarter will spawn a new process each time the child crashes or timeouts.
|
/// The restarter will spawn a new process each time the child crashes or timeouts.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn setup_restarting_mgr_std<I, S, ST>(
|
pub fn setup_restarting_mgr_std<I, OT, S, ST>(
|
||||||
//mgr: &mut LlmpEventManager<I, S, SH, ST>,
|
|
||||||
stats: ST,
|
stats: ST,
|
||||||
broker_port: u16,
|
broker_port: u16,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
Option<S>,
|
Option<S>,
|
||||||
LlmpRestartingEventManager<I, S, StdShMemProvider, ST>,
|
LlmpRestartingEventManager<I, OT, S, StdShMemProvider, ST>,
|
||||||
),
|
),
|
||||||
Error,
|
Error,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: DeserializeOwned + IfInteresting<I>,
|
OT: ObserversTuple,
|
||||||
|
S: DeserializeOwned,
|
||||||
ST: Stats,
|
ST: Stats,
|
||||||
{
|
{
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
AshmemService::start().expect("Error starting Ashmem Service");
|
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.
|
/// 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::type_complexity,
|
||||||
clippy::similar_names
|
clippy::similar_names
|
||||||
)] // for { mgr = LlmpEventManager... }
|
)] // 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,
|
mut shmem_provider: SP,
|
||||||
//mgr: &mut LlmpEventManager<I, S, SH, ST>,
|
//mgr: &mut LlmpEventManager<I, OT, S, SH, ST>,
|
||||||
stats: ST,
|
stats: ST,
|
||||||
broker_port: u16,
|
broker_port: u16,
|
||||||
) -> Result<(Option<S>, LlmpRestartingEventManager<I, S, SP, ST>), Error>
|
) -> Result<(Option<S>, LlmpRestartingEventManager<I, OT, S, SP, ST>), Error>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: DeserializeOwned + IfInteresting<I>,
|
S: DeserializeOwned,
|
||||||
SP: ShMemProvider,
|
OT: ObserversTuple,
|
||||||
|
SP: ShMemProvider + 'static,
|
||||||
ST: Stats,
|
ST: Stats,
|
||||||
|
//CE: CustomEvent<I>,
|
||||||
{
|
{
|
||||||
let mut mgr =
|
let mut mgr = LlmpEventManager::<I, OT, S, SP, ST>::new_on_port(
|
||||||
LlmpEventManager::<I, S, SP, ST>::new_on_port(shmem_provider.clone(), stats, broker_port)?;
|
shmem_provider.clone(),
|
||||||
|
stats,
|
||||||
|
broker_port,
|
||||||
|
)?;
|
||||||
|
|
||||||
// We start ourself as child process to actually fuzz
|
// We start ourself as child process to actually fuzz
|
||||||
let (sender, mut receiver, mut new_shmem_provider) = if std::env::var(_ENV_FUZZER_SENDER)
|
let (sender, mut receiver, mut new_shmem_provider) = if std::env::var(_ENV_FUZZER_SENDER)
|
||||||
@ -684,7 +740,7 @@ where
|
|||||||
None => {
|
None => {
|
||||||
println!("First run. Let's set it all up");
|
println!("First run. Let's set it all up");
|
||||||
// Mgr to send and receive msgs from/to all other fuzzer instances
|
// 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,
|
new_shmem_provider,
|
||||||
_ENV_FUZZER_BROKER_CLIENT_INITIAL,
|
_ENV_FUZZER_BROKER_CLIENT_INITIAL,
|
||||||
)?;
|
)?;
|
||||||
@ -694,7 +750,7 @@ where
|
|||||||
// Restoring from a previous run, deserialize state and corpus.
|
// Restoring from a previous run, deserialize state and corpus.
|
||||||
Some((_sender, _tag, msg)) => {
|
Some((_sender, _tag, msg)) => {
|
||||||
println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len());
|
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)?;
|
deserialize_state_mgr(new_shmem_provider, &msg)?;
|
||||||
|
|
||||||
(Some(state), LlmpRestartingEventManager::new(mgr, sender))
|
(Some(state), LlmpRestartingEventManager::new(mgr, sender))
|
||||||
|
@ -9,13 +9,7 @@ use alloc::{string::String, vec::Vec};
|
|||||||
use core::{fmt, marker::PhantomData, time::Duration};
|
use core::{fmt, marker::PhantomData, time::Duration};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{inputs::Input, observers::ObserversTuple, Error};
|
||||||
corpus::CorpusScheduler,
|
|
||||||
executors::{Executor, HasObservers},
|
|
||||||
inputs::Input,
|
|
||||||
observers::ObserversTuple,
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
use crate::stats::ClientPerfStats;
|
use crate::stats::ClientPerfStats;
|
||||||
@ -175,27 +169,32 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`EventManager`] is the main communications hub.
|
/// [`EventFirer`] fire an event.
|
||||||
/// For the "normal" multi-processed mode, you may want to look into `RestartingEventManager`
|
pub trait EventFirer<I, S>
|
||||||
pub trait EventManager<I, S>
|
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
/// Fire an Event
|
/// Send off an event to the broker
|
||||||
//fn fire<'a>(&mut self, event: Event<I>) -> Result<(), Error>;
|
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.
|
/// Lookup for incoming events and process them.
|
||||||
/// Return the number of processes events or an error
|
/// Return the number of processes events or an error
|
||||||
fn process<CS, E, OT>(
|
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error>;
|
||||||
&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;
|
|
||||||
|
|
||||||
/// Serialize all observers for this type and manager
|
/// Serialize all observers for this type and manager
|
||||||
fn serialize_observers<OT>(&mut self, observers: &OT) -> Result<Vec<u8>, Error>
|
fn serialize_observers<OT>(&mut self, observers: &OT) -> Result<Vec<u8>, Error>
|
||||||
@ -212,49 +211,45 @@ where
|
|||||||
{
|
{
|
||||||
Ok(postcard::from_bytes(observers_buf)?)
|
Ok(postcard::from_bytes(observers_buf)?)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// For restarting event managers, implement a way to forward state to their next peers.
|
/// [`EventManager`] is the main communications hub.
|
||||||
#[inline]
|
/// For the "normal" multi-processed mode, you may want to look into `RestartingEventManager`
|
||||||
fn on_restart(&mut self, _state: &mut S) -> Result<(), Error> {
|
pub trait EventManager<E, I, S, Z>:
|
||||||
Ok(())
|
EventFirer<I, S> + EventProcessor<E, S, Z> + EventRestarter<S>
|
||||||
}
|
where
|
||||||
|
I: Input,
|
||||||
/// 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>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An eventmgr for tests, and as placeholder if you really don't need an event manager.
|
/// An eventmgr for tests, and as placeholder if you really don't need an event manager.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct NopEventManager<I, S> {
|
pub struct NopEventManager {}
|
||||||
phantom: PhantomData<(I, S)>,
|
|
||||||
}
|
impl<I, S> EventFirer<I, S> for NopEventManager
|
||||||
impl<I, S> EventManager<I, S> for NopEventManager<I, S>
|
|
||||||
where
|
where
|
||||||
I: Input,
|
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> {
|
fn fire(&mut self, _state: &mut S, _event: Event<I>) -> Result<(), Error> {
|
||||||
Ok(())
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
//! A very simple event manager, that just supports log outputs, but no multiprocessing
|
//! A very simple event manager, that just supports log outputs, but no multiprocessing
|
||||||
use alloc::{string::ToString, vec::Vec};
|
use alloc::{string::ToString, vec::Vec};
|
||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::CorpusScheduler,
|
events::{BrokerEventResult, Event, EventFirer, EventManager, EventProcessor, EventRestarter},
|
||||||
events::{BrokerEventResult, Event, EventManager},
|
|
||||||
executors::{Executor, HasObservers},
|
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
observers::ObserversTuple,
|
|
||||||
stats::Stats,
|
stats::Stats,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A simple, single-threaded event manager that just logs
|
/// A simple, single-threaded event manager that just logs
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SimpleEventManager<I, S, ST>
|
pub struct SimpleEventManager<I, ST>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
ST: Stats, //CE: CustomEvent<I, OT>,
|
ST: Stats, //CE: CustomEvent<I, OT>,
|
||||||
@ -23,33 +19,13 @@ where
|
|||||||
stats: ST,
|
stats: ST,
|
||||||
/// The events that happened since the last handle_in_broker
|
/// The events that happened since the last handle_in_broker
|
||||||
events: Vec<Event<I>>,
|
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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
ST: Stats, //CE: CustomEvent<I, OT>,
|
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> {
|
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
|
||||||
match Self::handle_in_broker(&mut self.stats, &event)? {
|
match Self::handle_in_broker(&mut self.stats, &event)? {
|
||||||
BrokerEventResult::Forward => self.events.push(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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
ST: Stats, //TODO CE: CustomEvent,
|
ST: Stats, //TODO CE: CustomEvent,
|
||||||
@ -69,7 +79,6 @@ where
|
|||||||
Self {
|
Self {
|
||||||
stats,
|
stats,
|
||||||
events: vec![],
|
events: vec![],
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +150,7 @@ where
|
|||||||
|
|
||||||
// Handle arriving events in the client
|
// Handle arriving events in the client
|
||||||
#[allow(clippy::needless_pass_by_value, clippy::unused_self)]
|
#[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!(
|
Err(Error::Unknown(format!(
|
||||||
"Received illegal message that message should not have arrived: {:?}.",
|
"Received illegal message that message should not have arrived: {:?}.",
|
||||||
event
|
event
|
||||||
|
@ -15,19 +15,20 @@ use crate::bolts::os::windows_exceptions::setup_exception_handler;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::Corpus,
|
corpus::Corpus,
|
||||||
events::EventManager,
|
events::{EventFirer, EventRestarter},
|
||||||
executors::{
|
executors::{
|
||||||
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
|
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
|
||||||
},
|
},
|
||||||
feedbacks::Feedback,
|
feedbacks::Feedback,
|
||||||
|
fuzzer::HasObjective,
|
||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
state::{HasObjective, HasSolutions},
|
state::HasSolutions,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The inmem executor simply calls a target function, then returns afterwards.
|
/// 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
|
where
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
I: Input + HasTargetBytes,
|
I: Input + HasTargetBytes,
|
||||||
@ -37,10 +38,10 @@ where
|
|||||||
harness_fn: &'a mut H,
|
harness_fn: &'a mut H,
|
||||||
/// The observers, observing each run
|
/// The observers, observing each run
|
||||||
observers: OT,
|
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
|
where
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
I: Input + HasTargetBytes,
|
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
|
where
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
I: Input + HasTargetBytes,
|
I: Input + HasTargetBytes,
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[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)]
|
#[cfg(unix)]
|
||||||
unsafe {
|
unsafe {
|
||||||
let data = &mut unix_signal_handler::GLOBAL_STATE;
|
let data = &mut unix_signal_handler::GLOBAL_STATE;
|
||||||
@ -75,8 +82,9 @@ where
|
|||||||
);
|
);
|
||||||
// Direct raw pointers access /aliasing is pretty undefined behavior.
|
// 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
|
// 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.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.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);
|
compiler_fence(Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -92,15 +100,22 @@ where
|
|||||||
);
|
);
|
||||||
// Direct raw pointers access /aliasing is pretty undefined behavior.
|
// 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
|
// 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.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.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);
|
compiler_fence(Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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)]
|
#[cfg(unix)]
|
||||||
unsafe {
|
unsafe {
|
||||||
write_volatile(
|
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
|
where
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
I: Input + HasTargetBytes,
|
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
|
where
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
I: Input + HasTargetBytes,
|
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
|
where
|
||||||
H: FnMut(&[u8]) -> ExitKind,
|
H: FnMut(&[u8]) -> ExitKind,
|
||||||
I: Input + HasTargetBytes,
|
I: Input + HasTargetBytes,
|
||||||
@ -158,28 +174,30 @@ where
|
|||||||
/// * `harness_fn` - the harness, executiong the function
|
/// * `harness_fn` - the harness, executiong the function
|
||||||
/// * `observers` - the observers observing the target during execution
|
/// * `observers` - the observers observing the target during execution
|
||||||
/// This may return an error on unix, if signal handler setup fails
|
/// 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,
|
harness_fn: &'a mut H,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
|
_fuzzer: &mut Z,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_event_mgr: &mut EM,
|
_event_mgr: &mut EM,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
EM: EventManager<I, S>,
|
EM: EventFirer<I, S> + EventRestarter<S>,
|
||||||
OC: Corpus<I>,
|
OC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
OF: Feedback<I, S>,
|
||||||
S: HasObjective<OF, I> + HasSolutions<OC, I>,
|
S: HasSolutions<OC, I>,
|
||||||
|
Z: HasObjective<I, OF, S>,
|
||||||
{
|
{
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
unsafe {
|
unsafe {
|
||||||
let data = &mut unix_signal_handler::GLOBAL_STATE;
|
let data = &mut unix_signal_handler::GLOBAL_STATE;
|
||||||
write_volatile(
|
write_volatile(
|
||||||
&mut data.crash_handler,
|
&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(
|
write_volatile(
|
||||||
&mut data.timeout_handler,
|
&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)?;
|
setup_signal_handler(data)?;
|
||||||
@ -190,11 +208,11 @@ where
|
|||||||
let data = &mut windows_exception_handler::GLOBAL_STATE;
|
let data = &mut windows_exception_handler::GLOBAL_STATE;
|
||||||
write_volatile(
|
write_volatile(
|
||||||
&mut data.crash_handler,
|
&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(
|
//write_volatile(
|
||||||
// &mut data.timeout_handler,
|
// &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)?;
|
setup_exception_handler(data)?;
|
||||||
@ -232,12 +250,13 @@ mod unix_signal_handler {
|
|||||||
use crate::{
|
use crate::{
|
||||||
bolts::os::unix_signals::{Handler, Signal},
|
bolts::os::unix_signals::{Handler, Signal},
|
||||||
corpus::{Corpus, Testcase},
|
corpus::{Corpus, Testcase},
|
||||||
events::{Event, EventManager},
|
events::{Event, EventFirer, EventRestarter},
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
feedbacks::Feedback,
|
feedbacks::Feedback,
|
||||||
|
fuzzer::HasObjective,
|
||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
state::{HasObjective, HasSolutions},
|
state::HasSolutions,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO merge GLOBAL_STATE with the Windows one
|
// TODO merge GLOBAL_STATE with the Windows one
|
||||||
@ -248,6 +267,8 @@ mod unix_signal_handler {
|
|||||||
state_ptr: ptr::null_mut(),
|
state_ptr: ptr::null_mut(),
|
||||||
/// The event manager ptr for signal handling
|
/// The event manager ptr for signal handling
|
||||||
event_mgr_ptr: ptr::null_mut(),
|
event_mgr_ptr: ptr::null_mut(),
|
||||||
|
/// The fuzzer ptr for signal handling
|
||||||
|
fuzzer_ptr: ptr::null_mut(),
|
||||||
/// The observers ptr for signal handling
|
/// The observers ptr for signal handling
|
||||||
observers_ptr: ptr::null(),
|
observers_ptr: ptr::null(),
|
||||||
/// The current input for signal handling
|
/// The current input for signal handling
|
||||||
@ -261,6 +282,7 @@ mod unix_signal_handler {
|
|||||||
pub struct InProcessExecutorHandlerData {
|
pub struct InProcessExecutorHandlerData {
|
||||||
pub state_ptr: *mut c_void,
|
pub state_ptr: *mut c_void,
|
||||||
pub event_mgr_ptr: *mut c_void,
|
pub event_mgr_ptr: *mut c_void,
|
||||||
|
pub fuzzer_ptr: *mut c_void,
|
||||||
pub observers_ptr: *const c_void,
|
pub observers_ptr: *const c_void,
|
||||||
pub current_input_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),
|
pub crash_handler: unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut Self),
|
||||||
@ -308,21 +330,23 @@ mod unix_signal_handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[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,
|
_signal: Signal,
|
||||||
_info: siginfo_t,
|
_info: siginfo_t,
|
||||||
_context: &mut ucontext_t,
|
_context: &mut ucontext_t,
|
||||||
data: &mut InProcessExecutorHandlerData,
|
data: &mut InProcessExecutorHandlerData,
|
||||||
) where
|
) where
|
||||||
EM: EventManager<I, S>,
|
EM: EventFirer<I, S> + EventRestarter<S>,
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
OC: Corpus<I>,
|
OC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
OF: Feedback<I, S>,
|
||||||
S: HasObjective<OF, I> + HasSolutions<OC, I>,
|
S: HasSolutions<OC, I>,
|
||||||
I: Input + HasTargetBytes,
|
I: Input + HasTargetBytes,
|
||||||
|
Z: HasObjective<I, OF, S>,
|
||||||
{
|
{
|
||||||
let state = (data.state_ptr as *mut S).as_mut().unwrap();
|
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 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();
|
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
|
||||||
|
|
||||||
if data.current_input_ptr.is_null() {
|
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();
|
let input = (data.current_input_ptr as *const I).as_ref().unwrap();
|
||||||
data.current_input_ptr = ptr::null();
|
data.current_input_ptr = ptr::null();
|
||||||
|
|
||||||
let interesting = state
|
let interesting = fuzzer
|
||||||
.objective_mut()
|
.objective_mut()
|
||||||
.is_interesting(&input, observers, &ExitKind::Timeout)
|
.is_interesting(state, &input, observers, &ExitKind::Timeout)
|
||||||
.expect("In timeout handler objective failure.");
|
.expect("In timeout handler objective failure.");
|
||||||
|
|
||||||
if interesting {
|
if interesting {
|
||||||
let mut new_testcase = Testcase::new(input.clone());
|
let mut new_testcase = Testcase::new(input.clone());
|
||||||
state
|
fuzzer
|
||||||
.objective_mut()
|
.objective_mut()
|
||||||
.append_metadata(&mut new_testcase)
|
.append_metadata(state, &mut new_testcase)
|
||||||
.expect("Failed adding metadata");
|
.expect("Failed adding metadata");
|
||||||
state
|
state
|
||||||
.solutions_mut()
|
.solutions_mut()
|
||||||
@ -379,18 +404,19 @@ mod unix_signal_handler {
|
|||||||
/// Will be used for signal handling.
|
/// Will be used for signal handling.
|
||||||
/// It will store the current State to shmem, then exit.
|
/// It will store the current State to shmem, then exit.
|
||||||
#[allow(clippy::too_many_lines)]
|
#[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,
|
_signal: Signal,
|
||||||
_info: siginfo_t,
|
_info: siginfo_t,
|
||||||
_context: &mut ucontext_t,
|
_context: &mut ucontext_t,
|
||||||
data: &mut InProcessExecutorHandlerData,
|
data: &mut InProcessExecutorHandlerData,
|
||||||
) where
|
) where
|
||||||
EM: EventManager<I, S>,
|
EM: EventFirer<I, S> + EventRestarter<S>,
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
OC: Corpus<I>,
|
OC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
OF: Feedback<I, S>,
|
||||||
S: HasObjective<OF, I> + HasSolutions<OC, I>,
|
S: HasSolutions<OC, I>,
|
||||||
I: Input + HasTargetBytes,
|
I: Input + HasTargetBytes,
|
||||||
|
Z: HasObjective<I, OF, S>,
|
||||||
{
|
{
|
||||||
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
|
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
|
||||||
let _context = *(((_context as *mut _ as *mut c_void as usize) + 128) as *mut c_void
|
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 {
|
} else {
|
||||||
let state = (data.state_ptr as *mut S).as_mut().unwrap();
|
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 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();
|
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -483,16 +510,17 @@ mod unix_signal_handler {
|
|||||||
// Make sure we don't crash in the crash handler forever.
|
// Make sure we don't crash in the crash handler forever.
|
||||||
data.current_input_ptr = ptr::null();
|
data.current_input_ptr = ptr::null();
|
||||||
|
|
||||||
let interesting = state
|
let interesting = fuzzer
|
||||||
.objective_mut()
|
.objective_mut()
|
||||||
.is_interesting(&input, observers, &ExitKind::Crash)
|
.is_interesting(state, &input, observers, &ExitKind::Crash)
|
||||||
.expect("In crash handler objective failure.");
|
.expect("In crash handler objective failure.");
|
||||||
|
|
||||||
if interesting {
|
if interesting {
|
||||||
let new_input = input.clone();
|
let new_input = input.clone();
|
||||||
let mut new_testcase = Testcase::new(new_input);
|
let mut new_testcase = Testcase::new(new_input);
|
||||||
state
|
fuzzer
|
||||||
.objective_mut()
|
.objective_mut()
|
||||||
.append_metadata(&mut new_testcase)
|
.append_metadata(state, &mut new_testcase)
|
||||||
.expect("Failed adding metadata");
|
.expect("Failed adding metadata");
|
||||||
state
|
state
|
||||||
.solutions_mut()
|
.solutions_mut()
|
||||||
@ -536,12 +564,13 @@ mod windows_exception_handler {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
corpus::{Corpus, Testcase},
|
corpus::{Corpus, Testcase},
|
||||||
events::{Event, EventManager},
|
events::{Event, EventFirer, EventRestarter},
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
feedbacks::Feedback,
|
feedbacks::Feedback,
|
||||||
|
fuzzer::HasObjective,
|
||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
state::{HasObjective, HasSolutions},
|
state::HasSolutions,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Signal handling on unix systems needs some nasty unsafe.
|
/// Signal handling on unix systems needs some nasty unsafe.
|
||||||
@ -550,6 +579,8 @@ mod windows_exception_handler {
|
|||||||
state_ptr: ptr::null_mut(),
|
state_ptr: ptr::null_mut(),
|
||||||
/// The event manager ptr for signal handling
|
/// The event manager ptr for signal handling
|
||||||
event_mgr_ptr: ptr::null_mut(),
|
event_mgr_ptr: ptr::null_mut(),
|
||||||
|
/// The fuzzer ptr for signal handling
|
||||||
|
fuzzer_ptr: ptr::null_mut(),
|
||||||
/// The observers ptr for signal handling
|
/// The observers ptr for signal handling
|
||||||
observers_ptr: ptr::null(),
|
observers_ptr: ptr::null(),
|
||||||
/// The current input for signal handling
|
/// The current input for signal handling
|
||||||
@ -563,6 +594,7 @@ mod windows_exception_handler {
|
|||||||
pub struct InProcessExecutorHandlerData {
|
pub struct InProcessExecutorHandlerData {
|
||||||
pub state_ptr: *mut c_void,
|
pub state_ptr: *mut c_void,
|
||||||
pub event_mgr_ptr: *mut c_void,
|
pub event_mgr_ptr: *mut c_void,
|
||||||
|
pub fuzzer_ptr: *mut c_void,
|
||||||
pub observers_ptr: *const c_void,
|
pub observers_ptr: *const c_void,
|
||||||
pub current_input_ptr: *const c_void,
|
pub current_input_ptr: *const c_void,
|
||||||
pub crash_handler: unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut Self),
|
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,
|
code: ExceptionCode,
|
||||||
exception_pointers: *mut EXCEPTION_POINTERS,
|
exception_pointers: *mut EXCEPTION_POINTERS,
|
||||||
data: &mut InProcessExecutorHandlerData,
|
data: &mut InProcessExecutorHandlerData,
|
||||||
) where
|
) where
|
||||||
EM: EventManager<I, S>,
|
EM: EventFirer<I, S> + EventRestarter<S>,
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
OC: Corpus<I>,
|
OC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
OF: Feedback<I, S>,
|
||||||
S: HasObjective<OF, I> + HasSolutions<OC, I>,
|
S: HasSolutions<OC, I>,
|
||||||
I: Input + HasTargetBytes,
|
I: Input + HasTargetBytes,
|
||||||
|
Z: HasObjective<I, OF, S>,
|
||||||
{
|
{
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
println!("Crashed with {}", code);
|
println!("Crashed with {}", code);
|
||||||
if !data.current_input_ptr.is_null() {
|
if !data.current_input_ptr.is_null() {
|
||||||
let state = (data.state_ptr as *mut S).as_mut().unwrap();
|
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 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();
|
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -620,16 +654,17 @@ mod windows_exception_handler {
|
|||||||
// Make sure we don't crash in the crash handler forever.
|
// Make sure we don't crash in the crash handler forever.
|
||||||
data.current_input_ptr = ptr::null();
|
data.current_input_ptr = ptr::null();
|
||||||
|
|
||||||
let interesting = state
|
let interesting = fuzzer
|
||||||
.objective_mut()
|
.objective_mut()
|
||||||
.is_interesting(&input, observers, &ExitKind::Crash)
|
.is_interesting(state, &input, observers, &ExitKind::Crash)
|
||||||
.expect("In crash handler objective failure.");
|
.expect("In crash handler objective failure.");
|
||||||
|
|
||||||
if interesting {
|
if interesting {
|
||||||
let new_input = input.clone();
|
let new_input = input.clone();
|
||||||
let mut new_testcase = Testcase::new(new_input);
|
let mut new_testcase = Testcase::new(new_input);
|
||||||
state
|
fuzzer
|
||||||
.objective_mut()
|
.objective_mut()
|
||||||
.append_metadata(&mut new_testcase)
|
.append_metadata(state, &mut new_testcase)
|
||||||
.expect("Failed adding metadata");
|
.expect("Failed adding metadata");
|
||||||
state
|
state
|
||||||
.solutions_mut()
|
.solutions_mut()
|
||||||
@ -688,7 +723,6 @@ mod windows_exception_handler {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -701,7 +735,7 @@ mod tests {
|
|||||||
fn test_inmem_exec() {
|
fn test_inmem_exec() {
|
||||||
let mut harness = |_buf: &[u8]| ExitKind::Ok;
|
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,
|
harness_fn: &mut harness,
|
||||||
observers: tuple_list!(),
|
observers: tuple_list!(),
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
|
@ -35,52 +35,100 @@ pub enum ExitKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pre and post exec hooks
|
/// Pre and post exec hooks
|
||||||
pub trait HasExecHooks<EM, I, S> {
|
pub trait HasExecHooks<EM, I, S, Z> {
|
||||||
/// Called right before exexution starts
|
/// Called right before exexution starts
|
||||||
#[inline]
|
#[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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called right after execution finished.
|
/// Called right after execution finished.
|
||||||
#[inline]
|
#[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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A haskell-style tuple of objects that have pre and post exec hooks
|
/// 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.
|
/// 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
|
/// 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 () {
|
impl<EM, I, S, Z> HasExecHooksTuple<EM, I, S, Z> for () {
|
||||||
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> {
|
||||||
Ok(())
|
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(())
|
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
|
where
|
||||||
Head: HasExecHooks<EM, I, S>,
|
Head: HasExecHooks<EM, I, S, Z>,
|
||||||
Tail: HasExecHooksTuple<EM, I, S>,
|
Tail: HasExecHooksTuple<EM, I, S, Z>,
|
||||||
{
|
{
|
||||||
fn pre_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
|
fn pre_exec_all(
|
||||||
self.0.pre_exec(state, mgr, input)?;
|
&mut self,
|
||||||
self.1.pre_exec_all(state, mgr, input)
|
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> {
|
fn post_exec_all(
|
||||||
self.0.post_exec(state, mgr, input)?;
|
&mut self,
|
||||||
self.1.post_exec_all(state, mgr, input)
|
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`].
|
/// 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
|
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`].
|
/// Run the pre exec hook for all [`crate::observers::Observer`]`s` linked to this [`Executor`].
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pre_exec_observers(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
|
fn pre_exec_observers(
|
||||||
self.observers_mut().pre_exec_all(state, mgr, input)
|
&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`].
|
/// Run the post exec hook for all the [`crate::observers::Observer`]`s` linked to this [`Executor`].
|
||||||
#[inline]
|
#[inline]
|
||||||
fn post_exec_observers(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
|
fn post_exec_observers(
|
||||||
self.observers_mut().post_exec_all(state, mgr, input)
|
&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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
@ -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
|
where
|
||||||
E: Executor<I> + HasObservers<OT>,
|
E: Executor<I> + HasObservers<OT>,
|
||||||
I: Input,
|
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
|
where
|
||||||
E: Executor<I> + HasExecHooks<EM, I, S>,
|
E: Executor<I> + HasExecHooks<EM, I, S, Z>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[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)]
|
#[cfg(unix)]
|
||||||
unsafe {
|
unsafe {
|
||||||
let milli_sec = self.exec_tmout.as_millis();
|
let milli_sec = self.exec_tmout.as_millis();
|
||||||
@ -137,11 +143,17 @@ where
|
|||||||
// TODO
|
// TODO
|
||||||
let _ = self.exec_tmout.as_millis();
|
let _ = self.exec_tmout.as_millis();
|
||||||
}
|
}
|
||||||
self.executor.pre_exec(state, mgr, input)
|
self.executor.pre_exec(fuzzer, state, mgr, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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)]
|
#[cfg(unix)]
|
||||||
unsafe {
|
unsafe {
|
||||||
let it_value = Timeval {
|
let it_value = Timeval {
|
||||||
@ -165,6 +177,6 @@ where
|
|||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
self.executor.post_exec(state, mgr, input)
|
self.executor.post_exec(fuzzer, state, mgr, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,18 +12,18 @@ use crate::{
|
|||||||
bolts::tuples::Named,
|
bolts::tuples::Named,
|
||||||
corpus::Testcase,
|
corpus::Testcase,
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
feedbacks::Feedback,
|
feedbacks::{Feedback, FeedbackState, FeedbackStatesTuple},
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
observers::{MapObserver, ObserversTuple},
|
observers::{MapObserver, ObserversTuple},
|
||||||
state::HasMetadata,
|
state::{HasFeedbackStates, HasMetadata},
|
||||||
utils::AsSlice,
|
utils::AsSlice,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A [`MapFeedback`] that strives to maximize the map contents.
|
/// 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.
|
/// 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
|
/// A Reducer function is used to aggregate values for the novelty search
|
||||||
pub trait Reducer<T>: Serialize + serde::de::DeserializeOwned + 'static
|
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
|
/// The most common AFL-like feedback type
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||||
pub struct MapFeedback<O, R, T>
|
pub struct MapFeedback<FT, O, R, S, T>
|
||||||
where
|
where
|
||||||
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
R: Reducer<T>,
|
R: Reducer<T>,
|
||||||
O: MapObserver<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 used in the last observation
|
||||||
indexes: Option<Vec<usize>>,
|
indexes: Option<Vec<usize>>,
|
||||||
/// New indexes observed in the last observation
|
/// New indexes observed in the last observation
|
||||||
novelties: Option<Vec<usize>>,
|
novelties: Option<Vec<usize>>,
|
||||||
/// Name identifier of this instance
|
/// Name identifier of this instance
|
||||||
name: String,
|
name: String,
|
||||||
|
/// Name identifier of the observer
|
||||||
|
observer_name: String,
|
||||||
/// Phantom Data of Reducer
|
/// 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
|
where
|
||||||
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
R: Reducer<T>,
|
R: Reducer<T>,
|
||||||
O: MapObserver<T>,
|
O: MapObserver<T>,
|
||||||
I: Input,
|
I: Input,
|
||||||
|
S: HasFeedbackStates<FT>,
|
||||||
|
FT: FeedbackStatesTuple,
|
||||||
{
|
{
|
||||||
fn is_interesting<OT>(
|
fn is_interesting<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
_input: &I,
|
_input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
_exit_kind: &ExitKind,
|
_exit_kind: &ExitKind,
|
||||||
@ -157,24 +225,29 @@ where
|
|||||||
{
|
{
|
||||||
let mut interesting = false;
|
let mut interesting = false;
|
||||||
// TODO Replace with match_name_type when stable
|
// 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 size = observer.usable_count();
|
||||||
let initial = observer.initial();
|
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() {
|
if self.indexes.is_none() && self.novelties.is_none() {
|
||||||
for i in 0..size {
|
for i in 0..size {
|
||||||
let history = self.history_map[i];
|
let history = map_state.history_map[i];
|
||||||
let item = observer.map()[i];
|
let item = observer.map()[i];
|
||||||
|
|
||||||
let reduced = R::reduce(history, item);
|
let reduced = R::reduce(history, item);
|
||||||
if history != reduced {
|
if history != reduced {
|
||||||
self.history_map[i] = reduced;
|
map_state.history_map[i] = reduced;
|
||||||
interesting = true;
|
interesting = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if self.indexes.is_some() && self.novelties.is_none() {
|
} else if self.indexes.is_some() && self.novelties.is_none() {
|
||||||
for i in 0..size {
|
for i in 0..size {
|
||||||
let history = self.history_map[i];
|
let history = map_state.history_map[i];
|
||||||
let item = observer.map()[i];
|
let item = observer.map()[i];
|
||||||
if item != initial {
|
if item != initial {
|
||||||
self.indexes.as_mut().unwrap().push(i);
|
self.indexes.as_mut().unwrap().push(i);
|
||||||
@ -182,25 +255,25 @@ where
|
|||||||
|
|
||||||
let reduced = R::reduce(history, item);
|
let reduced = R::reduce(history, item);
|
||||||
if history != reduced {
|
if history != reduced {
|
||||||
self.history_map[i] = reduced;
|
map_state.history_map[i] = reduced;
|
||||||
interesting = true;
|
interesting = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if self.indexes.is_none() && self.novelties.is_some() {
|
} else if self.indexes.is_none() && self.novelties.is_some() {
|
||||||
for i in 0..size {
|
for i in 0..size {
|
||||||
let history = self.history_map[i];
|
let history = map_state.history_map[i];
|
||||||
let item = observer.map()[i];
|
let item = observer.map()[i];
|
||||||
|
|
||||||
let reduced = R::reduce(history, item);
|
let reduced = R::reduce(history, item);
|
||||||
if history != reduced {
|
if history != reduced {
|
||||||
self.history_map[i] = reduced;
|
map_state.history_map[i] = reduced;
|
||||||
interesting = true;
|
interesting = true;
|
||||||
self.novelties.as_mut().unwrap().push(i);
|
self.novelties.as_mut().unwrap().push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i in 0..size {
|
for i in 0..size {
|
||||||
let history = self.history_map[i];
|
let history = map_state.history_map[i];
|
||||||
let item = observer.map()[i];
|
let item = observer.map()[i];
|
||||||
if item != initial {
|
if item != initial {
|
||||||
self.indexes.as_mut().unwrap().push(i);
|
self.indexes.as_mut().unwrap().push(i);
|
||||||
@ -208,7 +281,7 @@ where
|
|||||||
|
|
||||||
let reduced = R::reduce(history, item);
|
let reduced = R::reduce(history, item);
|
||||||
if history != reduced {
|
if history != reduced {
|
||||||
self.history_map[i] = reduced;
|
map_state.history_map[i] = reduced;
|
||||||
interesting = true;
|
interesting = true;
|
||||||
self.novelties.as_mut().unwrap().push(i);
|
self.novelties.as_mut().unwrap().push(i);
|
||||||
}
|
}
|
||||||
@ -218,7 +291,7 @@ where
|
|||||||
Ok(interesting)
|
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() {
|
if let Some(v) = self.indexes.as_mut() {
|
||||||
let meta = MapIndexesMetadata::new(core::mem::take(v));
|
let meta = MapIndexesMetadata::new(core::mem::take(v));
|
||||||
testcase.add_metadata(meta);
|
testcase.add_metadata(meta);
|
||||||
@ -231,7 +304,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
/// 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() {
|
if let Some(v) = self.indexes.as_mut() {
|
||||||
v.clear();
|
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
|
where
|
||||||
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
R: Reducer<T>,
|
R: Reducer<T>,
|
||||||
O: MapObserver<T>,
|
O: MapObserver<T>,
|
||||||
|
S: HasFeedbackStates<FT>,
|
||||||
|
FT: FeedbackStatesTuple,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn name(&self) -> &str {
|
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
|
where
|
||||||
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
R: Reducer<T>,
|
R: Reducer<T>,
|
||||||
O: MapObserver<T>,
|
O: MapObserver<T>,
|
||||||
|
S: HasFeedbackStates<FT>,
|
||||||
|
FT: FeedbackStatesTuple,
|
||||||
{
|
{
|
||||||
/// Create new `MapFeedback`
|
/// Create new `MapFeedback`
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(name: &'static str, map_size: usize) -> Self {
|
pub fn new(feedback_state: &MapFeedbackState<T>, map_observer: &O) -> Self {
|
||||||
Self {
|
Self {
|
||||||
history_map: vec![T::default(); map_size],
|
|
||||||
phantom: PhantomData,
|
|
||||||
indexes: None,
|
indexes: None,
|
||||||
novelties: 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.
|
/// Create new `MapFeedback` specifying if it must track indexes of used entries and/or novelties
|
||||||
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
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_tracking(
|
pub fn new_tracking(
|
||||||
name: &'static str,
|
feedback_state: &MapFeedbackState<T>,
|
||||||
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(
|
|
||||||
map_observer: &O,
|
map_observer: &O,
|
||||||
track_indexes: bool,
|
track_indexes: bool,
|
||||||
track_novelties: bool,
|
track_novelties: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
history_map: vec![T::default(); map_observer.map().len()],
|
|
||||||
phantom: PhantomData,
|
|
||||||
indexes: if track_indexes { Some(vec![]) } else { None },
|
indexes: if track_indexes { Some(vec![]) } else { None },
|
||||||
novelties: if track_novelties { 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>
|
/// Create new `MapFeedback`
|
||||||
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.
|
|
||||||
#[must_use]
|
#[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 {
|
Self {
|
||||||
history_map,
|
|
||||||
name: name.to_string(),
|
|
||||||
indexes: None,
|
indexes: None,
|
||||||
novelties: 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,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,7 +410,7 @@ where
|
|||||||
{
|
{
|
||||||
/// Creates a new [`ReachabilityFeedback`] for a [`MapObserver`].
|
/// Creates a new [`ReachabilityFeedback`] for a [`MapObserver`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_with_observer(map_observer: &O) -> Self {
|
pub fn new(map_observer: &O) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: map_observer.name().to_string(),
|
name: map_observer.name().to_string(),
|
||||||
target_idx: vec![],
|
target_idx: vec![],
|
||||||
@ -360,7 +420,7 @@ where
|
|||||||
|
|
||||||
/// Creates a new [`ReachabilityFeedback`] for a [`MapObserver`] with the given `name`.
|
/// Creates a new [`ReachabilityFeedback`] for a [`MapObserver`] with the given `name`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(name: &'static str) -> Self {
|
pub fn with_name(name: &'static str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
target_idx: vec![],
|
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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
O: MapObserver<usize>,
|
O: MapObserver<usize>,
|
||||||
{
|
{
|
||||||
fn is_interesting<OT: ObserversTuple>(
|
fn is_interesting<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
_input: &I,
|
_input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
_exit_kind: &ExitKind,
|
_exit_kind: &ExitKind,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
OT: ObserversTuple,
|
||||||
|
{
|
||||||
// TODO Replace with match_name_type when stable
|
// TODO Replace with match_name_type when stable
|
||||||
let observer = observers.match_name::<O>(&self.name).unwrap();
|
let observer = observers.match_name::<O>(&self.name).unwrap();
|
||||||
let size = observer.usable_count();
|
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() {
|
if !self.target_idx.is_empty() {
|
||||||
let meta = MapIndexesMetadata::new(core::mem::take(self.target_idx.as_mut()));
|
let meta = MapIndexesMetadata::new(core::mem::take(self.target_idx.as_mut()));
|
||||||
testcase.add_metadata(meta);
|
testcase.add_metadata(meta);
|
||||||
@ -406,7 +470,7 @@ where
|
|||||||
Ok(())
|
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();
|
self.target_idx.clear();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use alloc::string::{String, ToString};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::tuples::Named,
|
bolts::tuples::{MatchName, Named},
|
||||||
corpus::Testcase,
|
corpus::Testcase,
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
@ -24,13 +24,14 @@ use core::{marker::PhantomData, time::Duration};
|
|||||||
/// Feedbacks evaluate the observers.
|
/// Feedbacks evaluate the observers.
|
||||||
/// Basically, they reduce the information provided by an observer to a value,
|
/// Basically, they reduce the information provided by an observer to a value,
|
||||||
/// indicating the "interestingness" of the last run.
|
/// indicating the "interestingness" of the last run.
|
||||||
pub trait Feedback<I>: Named + serde::Serialize + serde::de::DeserializeOwned
|
pub trait Feedback<I, S>: Named
|
||||||
where
|
where
|
||||||
I: Input,
|
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>(
|
fn is_interesting<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
input: &I,
|
input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
@ -41,6 +42,7 @@ where
|
|||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
fn is_interesting_with_perf<OT>(
|
fn is_interesting_with_perf<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
input: &I,
|
input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
@ -54,7 +56,7 @@ where
|
|||||||
let start_time = crate::cpu::read_time_counter();
|
let start_time = crate::cpu::read_time_counter();
|
||||||
|
|
||||||
// Execute this feedback
|
// 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
|
// Get the elapsed time for checking this feedback
|
||||||
let elapsed = crate::cpu::read_time_counter() - start_time;
|
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
|
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||||
#[inline]
|
#[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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
||||||
#[inline]
|
#[inline]
|
||||||
fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> {
|
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compose [`Feedback`]`s` with an `AND` operation
|
/// [`FeedbackState`] is the data associated with a [`Feedback`] that must persist as part
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
/// of the fuzzer State
|
||||||
#[serde(bound = "I: serde::de::DeserializeOwned")]
|
pub trait FeedbackState: Named + serde::Serialize + serde::de::DeserializeOwned {}
|
||||||
pub struct AndFeedback<A, B, I>
|
|
||||||
|
/// 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
|
where
|
||||||
A: Feedback<I>,
|
Head: FeedbackState,
|
||||||
B: Feedback<I>,
|
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,
|
I: Input,
|
||||||
{
|
{
|
||||||
/// The first [`Feedback`] to `AND`.
|
/// The first [`Feedback`] to `AND`.
|
||||||
pub first: A,
|
pub first: A,
|
||||||
/// The second [`Feedback`] to `AND`.
|
/// The second [`Feedback`] to `AND`.
|
||||||
pub second: B,
|
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
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
B: Feedback<I>,
|
B: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
fn is_interesting<OT>(
|
fn is_interesting<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
input: &I,
|
input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
@ -111,14 +134,19 @@ where
|
|||||||
where
|
where
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
{
|
{
|
||||||
let a = self.first.is_interesting(input, observers, exit_kind)?;
|
let a = self
|
||||||
let b = self.second.is_interesting(input, observers, exit_kind)?;
|
.first
|
||||||
|
.is_interesting(state, input, observers, exit_kind)?;
|
||||||
|
let b = self
|
||||||
|
.second
|
||||||
|
.is_interesting(state, input, observers, exit_kind)?;
|
||||||
Ok(a && b)
|
Ok(a && b)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
fn is_interesting_with_perf<OT>(
|
fn is_interesting_with_perf<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
input: &I,
|
input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
@ -130,6 +158,7 @@ where
|
|||||||
{
|
{
|
||||||
// Execute this feedback
|
// Execute this feedback
|
||||||
let a = self.first.is_interesting_with_perf(
|
let a = self.first.is_interesting_with_perf(
|
||||||
|
state,
|
||||||
input,
|
input,
|
||||||
observers,
|
observers,
|
||||||
&exit_kind,
|
&exit_kind,
|
||||||
@ -137,6 +166,7 @@ where
|
|||||||
feedback_index,
|
feedback_index,
|
||||||
)?;
|
)?;
|
||||||
let b = self.second.is_interesting_with_perf(
|
let b = self.second.is_interesting_with_perf(
|
||||||
|
state,
|
||||||
input,
|
input,
|
||||||
observers,
|
observers,
|
||||||
&exit_kind,
|
&exit_kind,
|
||||||
@ -147,71 +177,73 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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> {
|
||||||
self.first.append_metadata(testcase)?;
|
self.first.append_metadata(state, testcase)?;
|
||||||
self.second.append_metadata(testcase)
|
self.second.append_metadata(state, testcase)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn discard_metadata(&mut self, input: &I) -> Result<(), Error> {
|
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
|
||||||
self.first.discard_metadata(input)?;
|
self.first.discard_metadata(state, input)?;
|
||||||
self.second.discard_metadata(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
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
B: Feedback<I>,
|
B: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
//format!("And({}, {})", self.first.name(), self.second.name())
|
&self.name
|
||||||
"AndFeedback"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B, I> AndFeedback<A, B, I>
|
impl<A, B, I, S> AndFeedback<A, B, I, S>
|
||||||
where
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
B: Feedback<I>,
|
B: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
/// Creates a new [`AndFeedback`], resulting in the `AND` of two feedbacks.
|
/// Creates a new [`AndFeedback`], resulting in the `AND` of two feedbacks.
|
||||||
pub fn new(first: A, second: B) -> Self {
|
pub fn new(first: A, second: B) -> Self {
|
||||||
|
let name = format!("And({}, {})", first.name(), second.name());
|
||||||
Self {
|
Self {
|
||||||
first,
|
first,
|
||||||
second,
|
second,
|
||||||
|
name,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compose feedbacks with an OR operation
|
/// Compose feedbacks with an OR operation
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
pub struct OrFeedback<A, B, I, S>
|
||||||
#[serde(bound = "I: serde::de::DeserializeOwned")]
|
|
||||||
pub struct OrFeedback<A, B, I>
|
|
||||||
where
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
B: Feedback<I>,
|
B: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
/// The first [`Feedback`]
|
/// The first [`Feedback`]
|
||||||
pub first: A,
|
pub first: A,
|
||||||
/// The second [`Feedback`], `OR`ed with the first.
|
/// The second [`Feedback`], `OR`ed with the first.
|
||||||
pub second: B,
|
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
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
B: Feedback<I>,
|
B: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
fn is_interesting<OT>(
|
fn is_interesting<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
input: &I,
|
input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
@ -219,14 +251,19 @@ where
|
|||||||
where
|
where
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
{
|
{
|
||||||
let a = self.first.is_interesting(input, observers, exit_kind)?;
|
let a = self
|
||||||
let b = self.second.is_interesting(input, observers, exit_kind)?;
|
.first
|
||||||
|
.is_interesting(state, input, observers, exit_kind)?;
|
||||||
|
let b = self
|
||||||
|
.second
|
||||||
|
.is_interesting(state, input, observers, exit_kind)?;
|
||||||
Ok(a || b)
|
Ok(a || b)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
fn is_interesting_with_perf<OT>(
|
fn is_interesting_with_perf<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
input: &I,
|
input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
@ -238,6 +275,7 @@ where
|
|||||||
{
|
{
|
||||||
// Execute this feedback
|
// Execute this feedback
|
||||||
let a = self.first.is_interesting_with_perf(
|
let a = self.first.is_interesting_with_perf(
|
||||||
|
state,
|
||||||
input,
|
input,
|
||||||
observers,
|
observers,
|
||||||
&exit_kind,
|
&exit_kind,
|
||||||
@ -245,6 +283,7 @@ where
|
|||||||
feedback_index,
|
feedback_index,
|
||||||
)?;
|
)?;
|
||||||
let b = self.second.is_interesting_with_perf(
|
let b = self.second.is_interesting_with_perf(
|
||||||
|
state,
|
||||||
input,
|
input,
|
||||||
observers,
|
observers,
|
||||||
&exit_kind,
|
&exit_kind,
|
||||||
@ -255,67 +294,69 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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> {
|
||||||
self.first.append_metadata(testcase)?;
|
self.first.append_metadata(state, testcase)?;
|
||||||
self.second.append_metadata(testcase)
|
self.second.append_metadata(state, testcase)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn discard_metadata(&mut self, input: &I) -> Result<(), Error> {
|
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
|
||||||
self.first.discard_metadata(input)?;
|
self.first.discard_metadata(state, input)?;
|
||||||
self.second.discard_metadata(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
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
B: Feedback<I>,
|
B: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
//format!("Or({}, {})", self.first.name(), self.second.name())
|
&self.name
|
||||||
"OrFeedback"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B, I> OrFeedback<A, B, I>
|
impl<A, B, I, S> OrFeedback<A, B, I, S>
|
||||||
where
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
B: Feedback<I>,
|
B: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
/// Creates a new [`OrFeedback`] for two feedbacks.
|
/// Creates a new [`OrFeedback`] for two feedbacks.
|
||||||
pub fn new(first: A, second: B) -> Self {
|
pub fn new(first: A, second: B) -> Self {
|
||||||
|
let name = format!("Or({}, {})", first.name(), second.name());
|
||||||
Self {
|
Self {
|
||||||
first,
|
first,
|
||||||
second,
|
second,
|
||||||
|
name,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compose feedbacks with an OR operation
|
/// Compose feedbacks with an OR operation
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
pub struct NotFeedback<A, I, S>
|
||||||
#[serde(bound = "I: serde::de::DeserializeOwned")]
|
|
||||||
pub struct NotFeedback<A, I>
|
|
||||||
where
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
/// The feedback to invert
|
/// The feedback to invert
|
||||||
pub first: A,
|
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
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
fn is_interesting<OT>(
|
fn is_interesting<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
input: &I,
|
input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
@ -323,41 +364,44 @@ where
|
|||||||
where
|
where
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
{
|
{
|
||||||
Ok(!self.first.is_interesting(input, observers, exit_kind)?)
|
Ok(!self
|
||||||
|
.first
|
||||||
|
.is_interesting(state, input, observers, exit_kind)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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> {
|
||||||
self.first.append_metadata(testcase)
|
self.first.append_metadata(state, testcase)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn discard_metadata(&mut self, input: &I) -> Result<(), Error> {
|
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
|
||||||
self.first.discard_metadata(input)
|
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
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
//format!("Not({})", self.first.name())
|
&self.name
|
||||||
"NotFeedback"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, I> NotFeedback<A, I>
|
impl<A, I, S> NotFeedback<A, I, S>
|
||||||
where
|
where
|
||||||
A: Feedback<I>,
|
A: Feedback<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
/// Creates a new [`NotFeedback`].
|
/// Creates a new [`NotFeedback`].
|
||||||
pub fn new(first: A) -> Self {
|
pub fn new(first: A) -> Self {
|
||||||
|
let name = format!("Not({})", first.name());
|
||||||
Self {
|
Self {
|
||||||
first,
|
first,
|
||||||
|
name,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -394,12 +438,13 @@ macro_rules! feedback_not {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Hack to use () as empty Feedback
|
/// Hack to use () as empty Feedback
|
||||||
impl<I> Feedback<I> for ()
|
impl<I, S> Feedback<I, S> for ()
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
fn is_interesting<OT>(
|
fn is_interesting<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
_input: &I,
|
_input: &I,
|
||||||
_observers: &OT,
|
_observers: &OT,
|
||||||
_exit_kind: &ExitKind,
|
_exit_kind: &ExitKind,
|
||||||
@ -422,12 +467,13 @@ impl Named for () {
|
|||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct CrashFeedback {}
|
pub struct CrashFeedback {}
|
||||||
|
|
||||||
impl<I> Feedback<I> for CrashFeedback
|
impl<I, S> Feedback<I, S> for CrashFeedback
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
fn is_interesting<OT>(
|
fn is_interesting<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
_input: &I,
|
_input: &I,
|
||||||
_observers: &OT,
|
_observers: &OT,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
@ -468,12 +514,13 @@ impl Default for CrashFeedback {
|
|||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct TimeoutFeedback {}
|
pub struct TimeoutFeedback {}
|
||||||
|
|
||||||
impl<I> Feedback<I> for TimeoutFeedback
|
impl<I, S> Feedback<I, S> for TimeoutFeedback
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
fn is_interesting<OT>(
|
fn is_interesting<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
_input: &I,
|
_input: &I,
|
||||||
_observers: &OT,
|
_observers: &OT,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
@ -519,12 +566,13 @@ pub struct TimeFeedback {
|
|||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> Feedback<I> for TimeFeedback
|
impl<I, S> Feedback<I, S> for TimeFeedback
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
fn is_interesting<OT>(
|
fn is_interesting<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
_input: &I,
|
_input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
_exit_kind: &ExitKind,
|
_exit_kind: &ExitKind,
|
||||||
@ -540,7 +588,7 @@ where
|
|||||||
|
|
||||||
/// Append to the testcase the generated metadata in case of a new corpus item
|
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||||
#[inline]
|
#[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;
|
*testcase.exec_time_mut() = self.exec_time;
|
||||||
self.exec_time = None;
|
self.exec_time = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -548,7 +596,7 @@ where
|
|||||||
|
|
||||||
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
||||||
#[inline]
|
#[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;
|
self.exec_time = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,31 @@
|
|||||||
//! The `Fuzzer` is the main struct for a fuzz campaign.
|
//! The `Fuzzer` is the main struct for a fuzz campaign.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::CorpusScheduler,
|
corpus::{Corpus, CorpusScheduler, Testcase},
|
||||||
events::{Event, EventManager},
|
events::{Event, EventManager},
|
||||||
executors::{Executor, HasObservers},
|
executors::{
|
||||||
|
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
|
||||||
|
},
|
||||||
|
feedbacks::Feedback,
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
|
mark_feature_time,
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
stages::StagesTuple,
|
stages::StagesTuple,
|
||||||
state::{HasClientPerfStats, HasExecutions},
|
start_timer,
|
||||||
|
state::{HasClientPerfStats, HasCorpus, HasExecutions, HasSolutions},
|
||||||
utils::current_time,
|
utils::current_time,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "introspection")]
|
||||||
|
use crate::stats::PerfFeature;
|
||||||
|
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
use core::{marker::PhantomData, time::Duration};
|
use core::{marker::PhantomData, time::Duration};
|
||||||
|
|
||||||
/// Send a stats update all 3 (or more) seconds
|
/// Send a stats update all 3 (or more) seconds
|
||||||
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(3 * 1000);
|
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
|
/// Holds a scheduler
|
||||||
pub trait HasCorpusScheduler<CS, I, S>
|
pub trait HasCorpusScheduler<CS, I, S>
|
||||||
where
|
where
|
||||||
@ -47,8 +39,73 @@ where
|
|||||||
fn scheduler_mut(&mut self) -> &mut CS;
|
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.
|
/// The main fuzzer trait.
|
||||||
pub trait Fuzzer<E, EM, S, CS> {
|
pub trait Fuzzer<E, EM, I, S, ST> {
|
||||||
/// Fuzz for a single iteration
|
/// Fuzz for a single iteration
|
||||||
/// Returns the index of the last fuzzed corpus item
|
/// 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.
|
/// This way, the state will be available in the next, respawned, iteration.
|
||||||
fn fuzz_one(
|
fn fuzz_one(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
stages: &mut ST,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
|
state: &mut S,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
) -> Result<usize, Error>;
|
) -> Result<usize, Error>;
|
||||||
|
|
||||||
/// Fuzz forever (or until stopped)
|
/// Fuzz forever (or until stopped)
|
||||||
fn fuzz_loop(
|
fn fuzz_loop(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
stages: &mut ST,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
|
state: &mut S,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let mut last = current_time();
|
let mut last = current_time();
|
||||||
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
||||||
loop {
|
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)?;
|
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,
|
/// 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)?;`.
|
/// before exiting, make sure you call `event_mgr.on_restart(&mut state)?;`.
|
||||||
/// This way, the state will be available in the next, respawned, iteration.
|
/// This way, the state will be available in the next, respawned, iteration.
|
||||||
fn fuzz_loop_for<I>(
|
fn fuzz_loop_for(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
stages: &mut ST,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
iters: u64,
|
iters: u64,
|
||||||
) -> Result<usize, Error>
|
) -> Result<usize, Error> {
|
||||||
where
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
I: Input,
|
|
||||||
{
|
|
||||||
if iters == 0 {
|
if iters == 0 {
|
||||||
return Err(Error::IllegalArgument(
|
return Err(Error::IllegalArgument(
|
||||||
"Cannot fuzz for 0 iterations!".to_string(),
|
"Cannot fuzz for 0 iterations!".to_string(),
|
||||||
@ -108,7 +161,7 @@ pub trait Fuzzer<E, EM, S, CS> {
|
|||||||
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
||||||
|
|
||||||
for _ in 0..iters {
|
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)?;
|
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.
|
/// Your default fuzzer instance, for everyday use.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StdFuzzer<CS, ST, E, EM, I, OT, S>
|
pub struct StdFuzzer<C, CS, F, I, OF, OT, S, SC>
|
||||||
where
|
where
|
||||||
CS: CorpusScheduler<I, S>,
|
CS: CorpusScheduler<I, S>,
|
||||||
ST: StagesTuple<CS, E, EM, I, S>,
|
F: Feedback<I, S>,
|
||||||
E: Executor<I>,
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
I: Input,
|
I: Input,
|
||||||
|
OF: Feedback<I, S>,
|
||||||
{
|
{
|
||||||
stages: ST,
|
scheduler: CS,
|
||||||
phantom: PhantomData<(CS, E, EM, I, OT, S)>,
|
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
|
where
|
||||||
CS: CorpusScheduler<I, S>,
|
CS: CorpusScheduler<I, S>,
|
||||||
ST: StagesTuple<CS, E, EM, I, S>,
|
F: Feedback<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>,
|
|
||||||
I: Input,
|
I: Input,
|
||||||
|
OF: Feedback<I, S>,
|
||||||
{
|
{
|
||||||
fn scheduler(&self) -> &CS {
|
fn scheduler(&self) -> &CS {
|
||||||
&self.scheduler
|
&self.scheduler
|
||||||
@ -179,17 +215,164 @@ where
|
|||||||
&mut self.scheduler
|
&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
|
where
|
||||||
CS: CorpusScheduler<I, S>,
|
CS: CorpusScheduler<I, S>,
|
||||||
S: HasExecutions + HasClientPerfStats,
|
F: Feedback<I, S>,
|
||||||
ST: StagesTuple<CS, E, EM, I, S>,
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
E: Executor<I> + HasObservers<OT>,
|
|
||||||
OT: ObserversTuple,
|
|
||||||
I: Input,
|
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]
|
#[inline]
|
||||||
fn maybe_report_stats(
|
fn maybe_report_stats(
|
||||||
@ -240,17 +423,17 @@ where
|
|||||||
|
|
||||||
fn fuzz_one(
|
fn fuzz_one(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
stages: &mut ST,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
|
state: &mut S,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
// Init timer for scheduler
|
// Init timer for scheduler
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
state.introspection_stats_mut().start_timer();
|
state.introspection_stats_mut().start_timer();
|
||||||
|
|
||||||
// Get the next index from the scheduler
|
// 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
|
// Mark the elapsed time for the scheduler
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
@ -261,15 +444,14 @@ where
|
|||||||
state.introspection_stats_mut().reset_stage_index();
|
state.introspection_stats_mut().reset_stage_index();
|
||||||
|
|
||||||
// Execute all stages
|
// Execute all stages
|
||||||
self.stages_mut()
|
stages.perform_all(self, executor, state, manager, idx)?;
|
||||||
.perform_all(state, executor, manager, scheduler, idx)?;
|
|
||||||
|
|
||||||
// Init timer for manager
|
// Init timer for manager
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
state.introspection_stats_mut().start_timer();
|
state.introspection_stats_mut().start_timer();
|
||||||
|
|
||||||
// Execute the manager
|
// Execute the manager
|
||||||
manager.process(state, executor, scheduler)?;
|
manager.process(self, state, executor)?;
|
||||||
|
|
||||||
// Mark the elapsed time for the manager
|
// Mark the elapsed time for the manager
|
||||||
#[cfg(feature = "introspection")]
|
#[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
|
where
|
||||||
CS: CorpusScheduler<I, S>,
|
CS: CorpusScheduler<I, S>,
|
||||||
ST: StagesTuple<CS, E, EM, I, S>,
|
F: Feedback<I, S>,
|
||||||
E: Executor<I>,
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
I: Input,
|
I: Input,
|
||||||
|
OF: Feedback<I, S>,
|
||||||
|
S: HasExecutions + HasClientPerfStats,
|
||||||
{
|
{
|
||||||
/// Create a new `StdFuzzer` with standard behavior.
|
/// Create a new `StdFuzzer` with standard behavior.
|
||||||
pub fn new(stages: ST) -> Self {
|
pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self {
|
||||||
Self {
|
Self {
|
||||||
stages,
|
scheduler,
|
||||||
|
feedback,
|
||||||
|
objective,
|
||||||
phantom: PhantomData,
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,6 @@ impl From<ParseIntError> for Error {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::tuples::tuple_list,
|
bolts::tuples::tuple_list,
|
||||||
corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase},
|
corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase},
|
||||||
@ -164,7 +163,7 @@ mod tests {
|
|||||||
inputs::BytesInput,
|
inputs::BytesInput,
|
||||||
mutators::{mutations::BitFlipMutator, StdScheduledMutator},
|
mutators::{mutations::BitFlipMutator, StdScheduledMutator},
|
||||||
stages::StdMutationalStage,
|
stages::StdMutationalStage,
|
||||||
state::{HasCorpus, State},
|
state::{HasCorpus, StdState},
|
||||||
stats::SimpleStats,
|
stats::SimpleStats,
|
||||||
utils::StdRand,
|
utils::StdRand,
|
||||||
Fuzzer, StdFuzzer,
|
Fuzzer, StdFuzzer,
|
||||||
@ -182,10 +181,9 @@ mod tests {
|
|||||||
let testcase = Testcase::new(vec![0; 4]);
|
let testcase = Testcase::new(vec![0; 4]);
|
||||||
corpus.add(testcase).unwrap();
|
corpus.add(testcase).unwrap();
|
||||||
|
|
||||||
let mut state = State::new(
|
let mut state = StdState::new(
|
||||||
rand,
|
rand,
|
||||||
corpus,
|
corpus,
|
||||||
tuple_list!(),
|
|
||||||
InMemoryCorpus::<BytesInput>::new(),
|
InMemoryCorpus::<BytesInput>::new(),
|
||||||
tuple_list!(),
|
tuple_list!(),
|
||||||
);
|
);
|
||||||
@ -195,33 +193,33 @@ mod tests {
|
|||||||
});
|
});
|
||||||
let mut event_manager = SimpleEventManager::new(stats);
|
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 harness = |_buf: &[u8]| ExitKind::Ok;
|
||||||
let mut executor = InProcessExecutor::new(
|
let mut executor = InProcessExecutor::new(
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(),
|
tuple_list!(),
|
||||||
//Box::new(|_, _, _, _, _| ()),
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut event_manager,
|
&mut event_manager,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mutator = StdScheduledMutator::new(tuple_list!(BitFlipMutator::new()));
|
let mutator = StdScheduledMutator::new(tuple_list!(BitFlipMutator::new()));
|
||||||
let stage = StdMutationalStage::new(mutator);
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
let scheduler = RandCorpusScheduler::new();
|
|
||||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
|
||||||
|
|
||||||
for i in 0..1000 {
|
for i in 0..1000 {
|
||||||
fuzzer
|
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));
|
.unwrap_or_else(|_| panic!("Error in iter {}", i));
|
||||||
}
|
}
|
||||||
|
|
||||||
let state_serialized = postcard::to_allocvec(&state).unwrap();
|
let state_serialized = postcard::to_allocvec(&state).unwrap();
|
||||||
let state_deserialized: State<
|
let state_deserialized: StdState<
|
||||||
InMemoryCorpus<BytesInput>,
|
InMemoryCorpus<BytesInput>,
|
||||||
(),
|
(),
|
||||||
BytesInput,
|
BytesInput,
|
||||||
(),
|
|
||||||
StdRand,
|
StdRand,
|
||||||
InMemoryCorpus<BytesInput>,
|
InMemoryCorpus<BytesInput>,
|
||||||
> = postcard::from_bytes(state_serialized.as_slice()).unwrap();
|
> = postcard::from_bytes(state_serialized.as_slice()).unwrap();
|
||||||
|
@ -1837,7 +1837,7 @@ mod tests {
|
|||||||
corpus::{Corpus, InMemoryCorpus},
|
corpus::{Corpus, InMemoryCorpus},
|
||||||
inputs::BytesInput,
|
inputs::BytesInput,
|
||||||
mutators::MutatorsTuple,
|
mutators::MutatorsTuple,
|
||||||
state::{HasMetadata, State},
|
state::{HasMetadata, StdState},
|
||||||
utils::StdRand,
|
utils::StdRand,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1895,7 +1895,7 @@ mod tests {
|
|||||||
.add(BytesInput::new(vec![0x42; 0x1337]).into())
|
.add(BytesInput::new(vec![0x42; 0x1337]).into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ());
|
let mut state = StdState::new(rand, corpus, InMemoryCorpus::new(), ());
|
||||||
|
|
||||||
let mut mutations = test_mutations();
|
let mut mutations = test_mutations();
|
||||||
for _ in 0..2 {
|
for _ in 0..2 {
|
||||||
|
@ -404,7 +404,7 @@ mod tests {
|
|||||||
scheduled::{havoc_mutations, StdScheduledMutator},
|
scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
Mutator,
|
Mutator,
|
||||||
},
|
},
|
||||||
state::State,
|
state::StdState,
|
||||||
utils::{Rand, StdRand, XkcdRand},
|
utils::{Rand, StdRand, XkcdRand},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -419,7 +419,7 @@ mod tests {
|
|||||||
let testcase = corpus.get(0).expect("Corpus did not contain entries");
|
let testcase = corpus.get(0).expect("Corpus did not contain entries");
|
||||||
let mut input = testcase.borrow_mut().load_input().unwrap().clone();
|
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);
|
rand.set_seed(5);
|
||||||
|
|
||||||
@ -446,7 +446,7 @@ mod tests {
|
|||||||
let mut input = testcase.borrow_mut().load_input().unwrap().clone();
|
let mut input = testcase.borrow_mut().load_input().unwrap().clone();
|
||||||
let input_prior = input.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());
|
let mut havoc = StdScheduledMutator::new(havoc_mutations());
|
||||||
|
|
||||||
|
@ -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
|
where
|
||||||
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
Self: MapObserver<T>,
|
Self: MapObserver<T>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[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()
|
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
|
where
|
||||||
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[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()
|
self.reset_map()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,21 +314,33 @@ static COUNT_CLASS_LOOKUP: [u8; 256] = [
|
|||||||
|
|
||||||
impl<M> Observer for HitcountsMapObserver<M> where M: MapObserver<u8> {}
|
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
|
where
|
||||||
M: MapObserver<u8> + HasExecHooks<EM, I, S>,
|
M: MapObserver<u8> + HasExecHooks<EM, I, S, Z>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pre_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> {
|
fn pre_exec(
|
||||||
self.base.pre_exec(state, mgr, input)
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
|
state: &mut S,
|
||||||
|
mgr: &mut EM,
|
||||||
|
input: &I,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.base.pre_exec(fuzzer, state, mgr, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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() {
|
for x in self.map_mut().iter_mut() {
|
||||||
*x = COUNT_CLASS_LOOKUP[*x as usize];
|
*x = COUNT_CLASS_LOOKUP[*x as usize];
|
||||||
}
|
}
|
||||||
self.base.post_exec(state, mgr, input)
|
self.base.post_exec(fuzzer, state, mgr, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,14 +65,26 @@ impl TimeObserver {
|
|||||||
|
|
||||||
impl Observer for TimeObserver {}
|
impl Observer for TimeObserver {}
|
||||||
|
|
||||||
impl<EM, I, S> HasExecHooks<EM, I, S> for TimeObserver {
|
impl<EM, I, S, Z> HasExecHooks<EM, I, S, Z> for TimeObserver {
|
||||||
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.last_runtime = None;
|
self.last_runtime = None;
|
||||||
self.start_time = current_time();
|
self.start_time = current_time();
|
||||||
Ok(())
|
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);
|
self.last_runtime = Some(current_time() - self.start_time);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -11,90 +11,67 @@ pub use mutational::{MutationalStage, StdMutationalStage};
|
|||||||
|
|
||||||
//pub mod power;
|
//pub mod power;
|
||||||
//pub use power::PowerMutationalStage;
|
//pub use power::PowerMutationalStage;
|
||||||
|
use crate::Error;
|
||||||
use crate::{
|
|
||||||
bolts::tuples::TupleList, events::EventManager, executors::Executor, inputs::Input,
|
|
||||||
state::HasClientPerfStats, Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A stage is one step in the fuzzing process.
|
/// A stage is one step in the fuzzing process.
|
||||||
/// Multiple stages will be scheduled one by one for each input.
|
/// Multiple stages will be scheduled one by one for each input.
|
||||||
pub trait Stage<CS, E, EM, I, S>
|
pub trait Stage<E, EM, S, Z> {
|
||||||
where
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
E: Executor<I>,
|
|
||||||
I: Input,
|
|
||||||
{
|
|
||||||
/// Run the stage
|
/// Run the stage
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
fuzzer: &mut Z,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
|
state: &mut S,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
corpus_idx: usize,
|
corpus_idx: usize,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A tuple holding all `Stages` used for fuzzing.
|
/// A tuple holding all `Stages` used for fuzzing.
|
||||||
pub trait StagesTuple<CS, E, EM, I, S>
|
pub trait StagesTuple<E, EM, S, Z> {
|
||||||
where
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
E: Executor<I>,
|
|
||||||
I: Input,
|
|
||||||
{
|
|
||||||
/// Performs all `Stages` in this tuple
|
/// Performs all `Stages` in this tuple
|
||||||
fn perform_all(
|
fn perform_all(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
fuzzer: &mut Z,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
|
state: &mut S,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
corpus_idx: usize,
|
corpus_idx: usize,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS, E, EM, I, S> StagesTuple<CS, E, EM, I, S> for ()
|
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for () {
|
||||||
where
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
E: Executor<I>,
|
|
||||||
I: Input,
|
|
||||||
{
|
|
||||||
fn perform_all(
|
fn perform_all(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &mut S,
|
_: &mut Z,
|
||||||
_: &mut E,
|
_: &mut E,
|
||||||
|
_: &mut S,
|
||||||
_: &mut EM,
|
_: &mut EM,
|
||||||
_: &CS,
|
|
||||||
_: usize,
|
_: usize,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Ok(())
|
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
|
where
|
||||||
Head: Stage<CS, E, EM, I, S>,
|
Head: Stage<E, EM, S, Z>,
|
||||||
Tail: StagesTuple<CS, E, EM, I, S> + TupleList,
|
Tail: StagesTuple<E, EM, S, Z>,
|
||||||
EM: EventManager<I, S>,
|
|
||||||
E: Executor<I>,
|
|
||||||
I: Input,
|
|
||||||
S: HasClientPerfStats,
|
|
||||||
{
|
{
|
||||||
fn perform_all(
|
fn perform_all(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
fuzzer: &mut Z,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
|
state: &mut S,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
corpus_idx: usize,
|
corpus_idx: usize,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// Perform the current stage
|
// Perform the current stage
|
||||||
self.0
|
self.0
|
||||||
.perform(state, executor, manager, scheduler, corpus_idx)?;
|
.perform(fuzzer, executor, state, manager, corpus_idx)?;
|
||||||
|
|
||||||
// Execute the remaining stages
|
// Execute the remaining stages
|
||||||
self.1
|
self.1
|
||||||
.perform_all(state, executor, manager, scheduler, corpus_idx)
|
.perform_all(fuzzer, executor, state, manager, corpus_idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, CorpusScheduler},
|
corpus::Corpus,
|
||||||
events::EventManager,
|
fuzzer::Evaluator,
|
||||||
executors::{Executor, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks},
|
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
mark_feature_time,
|
mark_feature_time,
|
||||||
mutators::Mutator,
|
mutators::Mutator,
|
||||||
observers::ObserversTuple,
|
|
||||||
stages::Stage,
|
stages::Stage,
|
||||||
start_timer,
|
start_timer,
|
||||||
state::{Evaluator, HasClientPerfStats, HasCorpus, HasRand},
|
state::{HasClientPerfStats, HasCorpus, HasRand},
|
||||||
utils::Rand,
|
utils::Rand,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
@ -23,16 +21,13 @@ use crate::stats::PerfFeature;
|
|||||||
/// A Mutational stage is the stage in a fuzzing run that mutates inputs.
|
/// A Mutational stage is the stage in a fuzzing run that mutates inputs.
|
||||||
/// Mutational stages will usually have a range of mutations that are
|
/// Mutational stages will usually have a range of mutations that are
|
||||||
/// being applied to the input one by one, between executions.
|
/// 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
|
where
|
||||||
|
C: Corpus<I>,
|
||||||
M: Mutator<I, S>,
|
M: Mutator<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
S: HasCorpus<C, I> + Evaluator<I> + HasClientPerfStats,
|
S: HasClientPerfStats + HasCorpus<C, I>,
|
||||||
C: Corpus<I>,
|
Z: Evaluator<E, EM, I, S>,
|
||||||
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>,
|
|
||||||
{
|
{
|
||||||
/// The mutator registered for this stage
|
/// The mutator registered for this stage
|
||||||
fn mutator(&self) -> &M;
|
fn mutator(&self) -> &M;
|
||||||
@ -47,17 +42,17 @@ where
|
|||||||
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
|
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
|
||||||
fn perform_mutational(
|
fn perform_mutational(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
fuzzer: &mut Z,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
|
state: &mut S,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
corpus_idx: usize,
|
corpus_idx: usize,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let num = self.iterations(state);
|
let num = self.iterations(state);
|
||||||
|
|
||||||
for i in 0..num {
|
for i in 0..num {
|
||||||
start_timer!(state);
|
start_timer!(state);
|
||||||
let mut input_mut = state
|
let mut input = state
|
||||||
.corpus()
|
.corpus()
|
||||||
.get(corpus_idx)?
|
.get(corpus_idx)?
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
@ -66,11 +61,11 @@ where
|
|||||||
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
||||||
|
|
||||||
start_timer!(state);
|
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);
|
mark_feature_time!(state, PerfFeature::Mutate);
|
||||||
|
|
||||||
// Time is measured directly the `evaluate_input` function
|
// 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);
|
start_timer!(state);
|
||||||
self.mutator_mut().post_exec(state, i as i32, corpus_idx)?;
|
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
|
/// The default mutational stage
|
||||||
#[derive(Clone, Debug)]
|
#[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
|
where
|
||||||
|
C: Corpus<I>,
|
||||||
M: Mutator<I, S>,
|
M: Mutator<I, S>,
|
||||||
I: Input,
|
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,
|
R: Rand,
|
||||||
|
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
|
||||||
|
Z: Evaluator<E, EM, I, S>,
|
||||||
{
|
{
|
||||||
mutator: M,
|
mutator: M,
|
||||||
#[allow(clippy::type_complexity)]
|
#[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>
|
impl<C, E, EM, I, M, R, S, Z> MutationalStage<C, E, EM, I, M, S, Z>
|
||||||
for StdMutationalStage<C, CS, E, EM, I, M, OT, R, S>
|
for StdMutationalStage<C, E, EM, I, M, R, S, Z>
|
||||||
where
|
where
|
||||||
|
C: Corpus<I>,
|
||||||
M: Mutator<I, S>,
|
M: Mutator<I, S>,
|
||||||
I: Input,
|
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,
|
R: Rand,
|
||||||
|
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
|
||||||
|
Z: Evaluator<E, EM, I, S>,
|
||||||
{
|
{
|
||||||
/// The mutator, added to this stage
|
/// The mutator, added to this stage
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -134,43 +123,36 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, CS, E, EM, I, M, OT, R, S> Stage<CS, E, EM, I, 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>
|
||||||
for StdMutationalStage<C, CS, E, EM, I, M, OT, R, S>
|
|
||||||
where
|
where
|
||||||
|
C: Corpus<I>,
|
||||||
M: Mutator<I, S>,
|
M: Mutator<I, S>,
|
||||||
I: Input,
|
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,
|
R: Rand,
|
||||||
|
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
|
||||||
|
Z: Evaluator<E, EM, I, S>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
fuzzer: &mut Z,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
|
state: &mut S,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
corpus_idx: usize,
|
corpus_idx: usize,
|
||||||
) -> Result<(), Error> {
|
) -> 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
|
where
|
||||||
|
C: Corpus<I>,
|
||||||
M: Mutator<I, S>,
|
M: Mutator<I, S>,
|
||||||
I: Input,
|
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,
|
R: Rand,
|
||||||
|
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
|
||||||
|
Z: Evaluator<E, EM, I, S>,
|
||||||
{
|
{
|
||||||
/// Creates a new default mutational stage
|
/// Creates a new default mutational stage
|
||||||
pub fn new(mutator: M) -> Self {
|
pub fn new(mutator: M) -> Self {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! The fuzzer, and state are the core pieces of every good fuzzer
|
//! The fuzzer, and state are the core pieces of every good fuzzer
|
||||||
|
|
||||||
use core::{fmt::Debug, marker::PhantomData, time::Duration};
|
use core::{fmt::Debug, marker::PhantomData, time::Duration};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
@ -10,31 +10,25 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::serdeany::{SerdeAny, SerdeAnyMap},
|
bolts::serdeany::{SerdeAny, SerdeAnyMap},
|
||||||
corpus::{Corpus, CorpusScheduler, Testcase},
|
corpus::Corpus,
|
||||||
events::{Event, EventManager, LogSeverity},
|
events::{Event, EventManager, LogSeverity},
|
||||||
executors::{
|
feedbacks::FeedbackStatesTuple,
|
||||||
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
|
fuzzer::Evaluator,
|
||||||
},
|
|
||||||
feedbacks::Feedback,
|
|
||||||
generators::Generator,
|
generators::Generator,
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
mark_feature_time,
|
|
||||||
observers::ObserversTuple,
|
|
||||||
start_timer,
|
|
||||||
stats::ClientPerfStats,
|
stats::ClientPerfStats,
|
||||||
utils::Rand,
|
utils::Rand,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "introspection")]
|
|
||||||
use crate::stats::PerfFeature;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use crate::inputs::bytes::BytesInput;
|
use crate::inputs::bytes::BytesInput;
|
||||||
|
|
||||||
/// The maximum size of a testcase
|
/// The maximum size of a testcase
|
||||||
pub const DEFAULT_MAX_SIZE: usize = 1_048_576;
|
pub const DEFAULT_MAX_SIZE: usize = 1_048_576;
|
||||||
|
|
||||||
|
pub trait State: Serialize + DeserializeOwned {}
|
||||||
|
|
||||||
/// Trait for elements offering a corpus
|
/// Trait for elements offering a corpus
|
||||||
pub trait HasCorpus<C, I>
|
pub trait HasCorpus<C, I>
|
||||||
where
|
where
|
||||||
@ -114,29 +108,15 @@ pub trait HasMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for elements offering a feedback
|
/// Trait for elements offering a feedback
|
||||||
pub trait HasFeedback<F, I>: Sized
|
pub trait HasFeedbackStates<FT>
|
||||||
where
|
where
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
I: Input,
|
|
||||||
{
|
{
|
||||||
/// The feedback
|
/// The feedback states
|
||||||
fn feedback(&self) -> &F;
|
fn feedback_states(&self) -> &FT;
|
||||||
|
|
||||||
/// The feedback (mut)
|
/// The feedback states (mut)
|
||||||
fn feedback_mut(&mut self) -> &mut F;
|
fn feedback_states_mut(&mut self) -> &mut FT;
|
||||||
}
|
|
||||||
|
|
||||||
/// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for the execution counter
|
/// Trait for the execution counter
|
||||||
@ -157,67 +137,16 @@ pub trait HasStartTime {
|
|||||||
fn start_time_mut(&mut self) -> &mut Duration;
|
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.
|
/// The state a fuzz run.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[serde(bound = "F: serde::de::DeserializeOwned")]
|
#[serde(bound = "FT: serde::de::DeserializeOwned")]
|
||||||
pub struct State<C, F, I, OF, R, SC>
|
pub struct StdState<C, FT, I, R, SC>
|
||||||
where
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
SC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
|
||||||
{
|
{
|
||||||
/// RNG instance
|
/// RNG instance
|
||||||
rand: R,
|
rand: R,
|
||||||
@ -227,12 +156,10 @@ where
|
|||||||
start_time: Duration,
|
start_time: Duration,
|
||||||
/// The corpus
|
/// The corpus
|
||||||
corpus: C,
|
corpus: C,
|
||||||
/// Feedbacks used to evaluate an input
|
/// States of the feedback used to evaluate an input
|
||||||
feedback: F,
|
feedback_states: FT,
|
||||||
// Solutions corpus
|
// Solutions corpus
|
||||||
solutions: SC,
|
solutions: SC,
|
||||||
/// Objective Feedbacks
|
|
||||||
objective: OF,
|
|
||||||
/// Metadata stored for this state by one of the components
|
/// Metadata stored for this state by one of the components
|
||||||
metadata: SerdeAnyMap,
|
metadata: SerdeAnyMap,
|
||||||
/// MaxSize testcase size for mutators that appreciate it
|
/// MaxSize testcase size for mutators that appreciate it
|
||||||
@ -245,14 +172,23 @@ where
|
|||||||
phantom: PhantomData<I>,
|
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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
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>,
|
SC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
|
||||||
{
|
{
|
||||||
/// The rand instance
|
/// The rand instance
|
||||||
#[inline]
|
#[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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
SC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
|
||||||
{
|
{
|
||||||
/// Returns the corpus
|
/// Returns the corpus
|
||||||
#[inline]
|
#[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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
SC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
|
||||||
{
|
{
|
||||||
/// Returns the solutions corpus
|
/// Returns the solutions corpus
|
||||||
#[inline]
|
#[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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
SC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
|
||||||
{
|
{
|
||||||
/// Get all the metadata into an [`hashbrown::HashMap`]
|
/// Get all the metadata into an [`hashbrown::HashMap`]
|
||||||
#[inline]
|
#[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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
SC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
|
||||||
{
|
{
|
||||||
/// The feedback
|
/// The feedback states
|
||||||
#[inline]
|
#[inline]
|
||||||
fn feedback(&self) -> &F {
|
fn feedback_states(&self) -> &FT {
|
||||||
&self.feedback
|
&self.feedback_states
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The feedback (mut)
|
/// The feedback states (mut)
|
||||||
#[inline]
|
#[inline]
|
||||||
fn feedback_mut(&mut self) -> &mut F {
|
fn feedback_states_mut(&mut self) -> &mut FT {
|
||||||
&mut self.feedback
|
&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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
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
|
/// The executions counter
|
||||||
#[inline]
|
#[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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
SC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
|
||||||
{
|
{
|
||||||
fn max_size(&self) -> usize {
|
fn max_size(&self) -> usize {
|
||||||
self.max_size
|
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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
SC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
|
||||||
{
|
{
|
||||||
/// The starting time
|
/// The starting time
|
||||||
#[inline]
|
#[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")]
|
#[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
|
where
|
||||||
C: Corpus<BytesInput>,
|
C: Corpus<BytesInput>,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<BytesInput>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<BytesInput>,
|
SC: Corpus<BytesInput>,
|
||||||
OF: Feedback<BytesInput>,
|
|
||||||
{
|
{
|
||||||
/// loads inputs from a directory
|
/// loads inputs from a directory
|
||||||
fn load_from_directory<CS, E, OT, EM>(
|
fn load_from_directory<E, EM, Z>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
in_dir: &Path,
|
in_dir: &Path,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
E: Executor<BytesInput>
|
Z: Evaluator<E, EM, BytesInput, Self>,
|
||||||
+ 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>,
|
|
||||||
{
|
{
|
||||||
for entry in fs::read_dir(in_dir)? {
|
for entry in fs::read_dir(in_dir)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
@ -588,19 +380,12 @@ where
|
|||||||
println!("Loading file {:?} ...", &path);
|
println!("Loading file {:?} ...", &path);
|
||||||
let bytes = fs::read(&path)?;
|
let bytes = fs::read(&path)?;
|
||||||
let input = BytesInput::new(bytes);
|
let input = BytesInput::new(bytes);
|
||||||
let (is_interesting, is_solution) =
|
let (is_interesting, _) = fuzzer.evaluate_input(self, executor, manager, input)?;
|
||||||
self.execute_input(&input, executor, manager)?;
|
if !is_interesting {
|
||||||
if self
|
|
||||||
.add_if_interesting(&input, is_interesting, scheduler)?
|
|
||||||
.is_none()
|
|
||||||
{
|
|
||||||
println!("File {:?} was not interesting, skipped.", &path);
|
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() {
|
} 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`.
|
/// 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,
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
in_dirs: &[PathBuf],
|
in_dirs: &[PathBuf],
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
E: Executor<BytesInput>
|
Z: Evaluator<E, EM, BytesInput, Self>,
|
||||||
+ HasObservers<OT>
|
EM: EventManager<E, BytesInput, Self, Z>,
|
||||||
+ HasExecHooks<EM, BytesInput, Self>
|
|
||||||
+ HasObserversHooks<EM, BytesInput, OT, Self>,
|
|
||||||
OT: ObserversTuple + HasExecHooksTuple<EM, BytesInput, Self>,
|
|
||||||
EM: EventManager<BytesInput, Self>,
|
|
||||||
CS: CorpusScheduler<BytesInput, Self>,
|
|
||||||
{
|
{
|
||||||
for in_dir in in_dirs {
|
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(
|
manager.fire(
|
||||||
self,
|
self,
|
||||||
@ -635,122 +415,37 @@ where
|
|||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
manager.process(self, executor, scheduler)?;
|
manager.process(fuzzer, self, executor)?;
|
||||||
Ok(())
|
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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
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.
|
/// 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,
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
|
||||||
num: usize,
|
num: usize,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
G: Generator<I, R>,
|
G: Generator<I, R>,
|
||||||
C: Corpus<I>,
|
Z: Evaluator<E, EM, I, Self>,
|
||||||
E: Executor<I>
|
EM: EventManager<E, I, Self, Z>,
|
||||||
+ 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>,
|
|
||||||
{
|
{
|
||||||
let mut added = 0;
|
let mut added = 0;
|
||||||
for _ in 0..num {
|
for _ in 0..num {
|
||||||
let input = generator.generate(self.rand_mut())?;
|
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 {
|
if is_interesting {
|
||||||
added += 1;
|
added += 1;
|
||||||
}
|
}
|
||||||
@ -763,21 +458,20 @@ where
|
|||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
manager.process(self, executor, scheduler)?;
|
manager.process(fuzzer, self, executor)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `State`, taking ownership of all of the individual components during fuzzing.
|
/// 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 {
|
Self {
|
||||||
rand,
|
rand,
|
||||||
executions: 0,
|
executions: 0,
|
||||||
start_time: Duration::from_millis(0),
|
start_time: Duration::from_millis(0),
|
||||||
metadata: SerdeAnyMap::default(),
|
metadata: SerdeAnyMap::default(),
|
||||||
corpus,
|
corpus,
|
||||||
feedback,
|
feedback_states,
|
||||||
solutions,
|
solutions,
|
||||||
objective,
|
|
||||||
max_size: DEFAULT_MAX_SIZE,
|
max_size: DEFAULT_MAX_SIZE,
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
introspection_stats: ClientPerfStats::new(),
|
introspection_stats: ClientPerfStats::new(),
|
||||||
@ -787,14 +481,13 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "introspection")]
|
#[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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
SC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
|
||||||
{
|
{
|
||||||
fn introspection_stats(&self) -> &ClientPerfStats {
|
fn introspection_stats(&self) -> &ClientPerfStats {
|
||||||
&self.introspection_stats
|
&self.introspection_stats
|
||||||
@ -806,14 +499,13 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "introspection"))]
|
#[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
|
where
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
F: Feedback<I>,
|
FT: FeedbackStatesTuple,
|
||||||
SC: Corpus<I>,
|
SC: Corpus<I>,
|
||||||
OF: Feedback<I>,
|
|
||||||
{
|
{
|
||||||
fn introspection_stats(&self) -> &ClientPerfStats {
|
fn introspection_stats(&self) -> &ClientPerfStats {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
@ -1707,8 +1707,14 @@ pub struct AsanErrorsObserver {
|
|||||||
|
|
||||||
impl Observer for AsanErrorsObserver {}
|
impl Observer for AsanErrorsObserver {}
|
||||||
|
|
||||||
impl<EM, I, S> HasExecHooks<EM, I, S> for AsanErrorsObserver {
|
impl<EM, I, S, Z> HasExecHooks<EM, I, S, Z> for AsanErrorsObserver {
|
||||||
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> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if ASAN_ERRORS.is_some() {
|
if ASAN_ERRORS.is_some() {
|
||||||
ASAN_ERRORS.as_mut().unwrap().clear();
|
ASAN_ERRORS.as_mut().unwrap().clear();
|
||||||
@ -1767,12 +1773,13 @@ pub struct AsanErrorsFeedback {
|
|||||||
errors: Option<AsanErrors>,
|
errors: Option<AsanErrors>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> Feedback<I> for AsanErrorsFeedback
|
impl<I, S> Feedback<I, S> for AsanErrorsFeedback
|
||||||
where
|
where
|
||||||
I: Input + HasTargetBytes,
|
I: Input + HasTargetBytes,
|
||||||
{
|
{
|
||||||
fn is_interesting<OT: ObserversTuple>(
|
fn is_interesting<OT: ObserversTuple>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
_input: &I,
|
_input: &I,
|
||||||
observers: &OT,
|
observers: &OT,
|
||||||
_exit_kind: &ExitKind,
|
_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 {
|
if let Some(errors) = &self.errors {
|
||||||
testcase.add_metadata(errors.clone());
|
testcase.add_metadata(errors.clone());
|
||||||
}
|
}
|
||||||
@ -1801,7 +1808,7 @@ where
|
|||||||
Ok(())
|
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;
|
self.errors = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user