use crate::{
bolts::current_time,
corpus::{Corpus, CorpusScheduler, Testcase},
events::{Event, EventFirer, EventManager},
executors::{Executor, ExitKind, HasObservers},
feedbacks::Feedback,
inputs::Input,
mark_feature_time,
observers::ObserversTuple,
stages::StagesTuple,
start_timer,
state::{HasClientPerfStats, HasCorpus, HasExecutions, HasSolutions},
Error,
};
#[cfg(feature = "introspection")]
use crate::stats::PerfFeature;
#[cfg(feature = "introspection")]
use alloc::boxed::Box;
use alloc::string::ToString;
use core::{marker::PhantomData, time::Duration};
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(3 * 1000);
pub trait HasCorpusScheduler<CS, I, S>
where
CS: CorpusScheduler<I, S>,
I: Input,
{
fn scheduler(&self) -> &CS;
fn scheduler_mut(&mut self) -> &mut CS;
}
pub trait HasFeedback<F, I, S>
where
F: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
fn feedback(&self) -> &F;
fn feedback_mut(&mut self) -> &mut F;
}
pub trait HasObjective<I, OF, S>
where
OF: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
fn objective(&self) -> &OF;
fn objective_mut(&mut self) -> &mut OF;
}
pub trait ExecutionProcessor<I, OT, S>
where
OT: ObserversTuple<I, S>,
I: Input,
{
fn process_execution<EM>(
&mut self,
state: &mut S,
manager: &mut EM,
input: I,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error>
where
EM: EventFirer<I, S>;
}
pub trait EvaluatorObservers<I, OT, S>: Sized
where
I: Input,
OT: ObserversTuple<I, S>,
{
fn evaluate_input_with_observers<E, EM>(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error>
where
E: Executor<EM, I, S, Self> + HasObservers<I, OT, S>,
EM: EventManager<E, I, S, Self>;
}
pub trait Evaluator<E, EM, I, S> {
fn evaluate_input(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
) -> Result<(ExecuteInputResult, Option<usize>), Error> {
self.evaluate_input_events(state, executor, manager, input, true)
}
fn evaluate_input_events(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error>;
fn add_input(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
) -> Result<usize, Error>;
}
pub trait Fuzzer<E, EM, I, S, ST> {
fn fuzz_one(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<usize, Error>;
fn fuzz_loop(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<usize, Error> {
let mut last = current_time();
let stats_timeout = STATS_TIMEOUT_DEFAULT;
loop {
self.fuzz_one(stages, executor, state, manager)?;
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
}
}
fn fuzz_loop_for(
&mut self,
stages: &mut ST,
state: &mut S,
executor: &mut E,
manager: &mut EM,
iters: u64,
) -> Result<usize, Error> {
if iters == 0 {
return Err(Error::IllegalArgument(
"Cannot fuzz for 0 iterations!".to_string(),
));
}
let mut ret = 0;
let mut last = current_time();
let stats_timeout = STATS_TIMEOUT_DEFAULT;
for _ in 0..iters {
ret = self.fuzz_one(stages, executor, state, manager)?;
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
}
Ok(ret)
}
fn maybe_report_stats(
state: &mut S,
manager: &mut EM,
last: Duration,
stats_timeout: Duration,
) -> Result<Duration, Error>;
}
#[derive(Debug, PartialEq)]
pub enum ExecuteInputResult {
None,
Corpus,
Solution,
}
#[derive(Debug)]
pub struct StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfStats,
{
scheduler: CS,
feedback: F,
objective: OF,
phantom: PhantomData<(C, I, OT, S, SC)>,
}
impl<C, CS, F, I, OF, OT, S, SC> HasCorpusScheduler<CS, I, S>
for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfStats,
{
fn scheduler(&self) -> &CS {
&self.scheduler
}
fn scheduler_mut(&mut self) -> &mut CS {
&mut self.scheduler
}
}
impl<C, CS, F, I, OF, OT, S, SC> HasFeedback<F, I, S> for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfStats,
{
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>,
S: HasClientPerfStats,
{
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> ExecutionProcessor<I, OT, S>
for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
C: Corpus<I>,
SC: Corpus<I>,
CS: CorpusScheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
OT: ObserversTuple<I, S> + serde::Serialize + serde::de::DeserializeOwned,
S: HasCorpus<C, I> + HasSolutions<SC, I> + HasClientPerfStats + HasExecutions,
{
fn process_execution<EM>(
&mut self,
state: &mut S,
manager: &mut EM,
input: I,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error>
where
EM: EventFirer<I, S>,
{
let mut res = ExecuteInputResult::None;
#[cfg(not(feature = "introspection"))]
let is_solution = self
.objective_mut()
.is_interesting(state, manager, &input, observers, exit_kind)?;
#[cfg(feature = "introspection")]
let is_solution = self
.objective_mut()
.is_interesting_introspection(state, manager, &input, observers, exit_kind)?;
if is_solution {
res = ExecuteInputResult::Solution;
} else {
#[cfg(not(feature = "introspection"))]
let is_corpus = self
.feedback_mut()
.is_interesting(state, manager, &input, observers, exit_kind)?;
#[cfg(feature = "introspection")]
let is_corpus = self
.feedback_mut()
.is_interesting_introspection(state, manager, &input, observers, exit_kind)?;
if is_corpus {
res = ExecuteInputResult::Corpus;
}
}
match res {
ExecuteInputResult::None => {
self.feedback_mut().discard_metadata(state, &input)?;
self.objective_mut().discard_metadata(state, &input)?;
Ok((res, None))
}
ExecuteInputResult::Corpus => {
self.objective_mut().discard_metadata(state, &input)?;
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)?;
if send_events {
let observers_buf = manager.serialize_observers(observers)?;
manager.fire(
state,
Event::NewTestcase {
input,
observers_buf,
exit_kind: exit_kind.clone(),
corpus_size: state.corpus().count(),
client_config: manager.configuration().to_string(),
time: current_time(),
executions: *state.executions(),
},
)?;
}
Ok((res, Some(idx)))
}
ExecuteInputResult::Solution => {
self.feedback_mut().discard_metadata(state, &input)?;
let mut testcase = Testcase::new(input);
self.objective_mut().append_metadata(state, &mut testcase)?;
state.solutions_mut().add(testcase)?;
if send_events {
manager.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
},
)?;
}
Ok((res, None))
}
}
}
}
impl<C, CS, F, I, OF, OT, S, SC> EvaluatorObservers<I, OT, S>
for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
C: Corpus<I>,
CS: CorpusScheduler<I, S>,
OT: ObserversTuple<I, S> + serde::Serialize + serde::de::DeserializeOwned,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasExecutions + HasCorpus<C, I> + HasSolutions<SC, I> + HasClientPerfStats,
SC: Corpus<I>,
{
#[inline]
fn evaluate_input_with_observers<E, EM>(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error>
where
E: Executor<EM, I, S, Self> + HasObservers<I, OT, S>,
EM: EventManager<E, I, S, Self>,
{
let exit_kind = self.execute_input(state, executor, manager, &input)?;
let observers = executor.observers();
self.process_execution(state, manager, input, observers, &exit_kind, send_events)
}
}
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<EM, I, S, Self> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S> + serde::Serialize + serde::de::DeserializeOwned,
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>,
{
#[inline]
fn evaluate_input_events(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error> {
self.evaluate_input_with_observers(state, executor, manager, input, send_events)
}
fn add_input(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
) -> Result<usize, Error> {
let exit_kind = self.execute_input(state, executor, manager, &input)?;
let observers = executor.observers();
self.objective_mut().discard_metadata(state, &input)?;
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)?;
let observers_buf = manager.serialize_observers(observers)?;
manager.fire(
state,
Event::NewTestcase {
input,
observers_buf,
exit_kind,
corpus_size: state.corpus().count(),
client_config: manager.configuration().to_string(),
time: current_time(),
executions: *state.executions(),
},
)?;
Ok(idx)
}
}
impl<C, CS, E, EM, F, I, OF, OT, S, ST, SC> Fuzzer<E, EM, I, S, ST>
for StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
EM: EventManager<E, I, S, Self>,
F: Feedback<I, S>,
I: Input,
S: HasExecutions + HasClientPerfStats,
OF: Feedback<I, S>,
ST: StagesTuple<E, EM, S, Self>,
{
#[inline]
fn maybe_report_stats(
state: &mut S,
manager: &mut EM,
last: Duration,
stats_timeout: Duration,
) -> Result<Duration, Error> {
let cur = current_time();
if cur.checked_sub(last).unwrap_or_default() > stats_timeout {
#[cfg(not(feature = "introspection"))]
manager.fire(
state,
Event::UpdateStats {
executions: *state.executions(),
time: cur,
phantom: PhantomData,
},
)?;
#[cfg(feature = "introspection")]
{
state
.introspection_stats_mut()
.set_current_time(crate::bolts::cpu::read_time_counter());
manager.fire(
state,
Event::UpdatePerfStats {
executions: *state.executions(),
time: cur,
introspection_stats: Box::new(state.introspection_stats().clone()),
phantom: PhantomData,
},
)?;
}
Ok(cur)
} else {
if cur.as_millis() % 1000 == 0 {}
Ok(last)
}
}
fn fuzz_one(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<usize, Error> {
#[cfg(feature = "introspection")]
state.introspection_stats_mut().start_timer();
let idx = self.scheduler.next(state)?;
#[cfg(feature = "introspection")]
state.introspection_stats_mut().mark_scheduler_time();
#[cfg(feature = "introspection")]
state.introspection_stats_mut().reset_stage_index();
stages.perform_all(self, executor, state, manager, idx)?;
#[cfg(feature = "introspection")]
state.introspection_stats_mut().start_timer();
manager.process(self, state, executor)?;
#[cfg(feature = "introspection")]
state.introspection_stats_mut().mark_manager_time();
Ok(idx)
}
}
impl<C, CS, F, I, OF, OT, S, SC> StdFuzzer<C, CS, F, I, OF, OT, S, SC>
where
CS: CorpusScheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasExecutions + HasClientPerfStats,
{
pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self {
Self {
scheduler,
feedback,
objective,
phantom: PhantomData,
}
}
pub fn execute_input<E, EM>(
&mut self,
state: &mut S,
executor: &mut E,
event_mgr: &mut EM,
input: &I,
) -> Result<ExitKind, Error>
where
E: Executor<EM, I, S, Self> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
{
start_timer!(state);
executor.observers_mut().pre_exec_all(state, input)?;
mark_feature_time!(state, PerfFeature::PreExecObservers);
start_timer!(state);
let exit_kind = executor.run_target(self, state, event_mgr, input)?;
mark_feature_time!(state, PerfFeature::TargetExecution);
*state.executions_mut() += 1;
start_timer!(state);
executor.observers_mut().post_exec_all(state, input)?;
mark_feature_time!(state, PerfFeature::PostExecObservers);
Ok(exit_kind)
}
}