use crate::{
corpus::CorpusScheduler,
events::{Event, EventManager},
executors::{Executor, HasObservers},
inputs::Input,
observers::ObserversTuple,
stages::StagesTuple,
state::{HasClientPerfStats, HasExecutions},
utils::current_time,
Error,
};
use alloc::string::ToString;
use core::{marker::PhantomData, time::Duration};
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(3 * 1000);
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,
{
fn stages(&self) -> &ST;
fn stages_mut(&mut self) -> &mut ST;
}
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 Fuzzer<E, EM, S, CS> {
fn fuzz_one(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
) -> Result<usize, Error>;
fn fuzz_loop(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
) -> Result<usize, Error> {
let mut last = current_time();
let stats_timeout = STATS_TIMEOUT_DEFAULT;
loop {
self.fuzz_one(state, executor, manager, scheduler)?;
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
}
}
fn fuzz_loop_for<I>(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
iters: u64,
) -> Result<usize, Error>
where
EM: EventManager<I, S>,
I: Input,
{
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(state, executor, manager, scheduler)?;
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(Clone, Debug)]
pub struct 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,
{
stages: ST,
phantom: PhantomData<(CS, E, EM, I, OT, S)>,
}
impl<CS, ST, E, EM, I, OT, S> HasStages<CS, E, EM, I, S, ST> 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,
{
fn stages(&self) -> &ST {
&self.stages
}
fn stages_mut(&mut self) -> &mut ST {
&mut self.stages
}
}
impl<CS, ST, E, EM, I, OT, S> Fuzzer<E, EM, S, CS> for StdFuzzer<CS, ST, E, EM, I, OT, S>
where
CS: CorpusScheduler<I, S>,
S: HasExecutions + HasClientPerfStats,
ST: StagesTuple<CS, E, EM, I, S>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
I: Input,
{
#[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 - last > 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::cpu::read_time_counter());
manager.fire(
state,
Event::UpdatePerfStats {
executions: *state.executions(),
time: cur,
introspection_stats: Box::new(*state.introspection_stats()),
phantom: PhantomData,
},
)?;
}
Ok(cur)
} else {
if cur.as_millis() % 1000 == 0 {}
Ok(last)
}
}
fn fuzz_one(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
) -> Result<usize, Error> {
#[cfg(feature = "introspection")]
state.introspection_stats_mut().start_timer();
let idx = scheduler.next(state)?;
#[cfg(feature = "introspection")]
state.introspection_stats_mut().mark_scheduler_time();
#[cfg(feature = "introspection")]
state.introspection_stats_mut().reset_stage_index();
self.stages_mut()
.perform_all(state, executor, manager, scheduler, idx)?;
#[cfg(feature = "introspection")]
state.introspection_stats_mut().start_timer();
manager.process(state, executor, scheduler)?;
#[cfg(feature = "introspection")]
state.introspection_stats_mut().mark_manager_time();
Ok(idx)
}
}
impl<CS, ST, E, EM, I, OT, S> 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,
{
pub fn new(stages: ST) -> Self {
Self {
stages,
phantom: PhantomData,
}
}
}