use alloc::string::ToString;
use core::{marker::PhantomData, time::Duration};
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
bolts::current_time,
corpus::{Corpus, Testcase},
events::{Event, EventConfig, EventFirer, EventManager, ProgressReporter},
executors::{Executor, ExitKind, HasObservers},
feedbacks::Feedback,
inputs::Input,
mark_feature_time,
observers::ObserversTuple,
schedulers::Scheduler,
stages::StagesTuple,
start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasSolutions},
Error,
};
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15);
pub trait HasScheduler<CS, I, S>
where
CS: Scheduler<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: HasClientPerfMonitor,
{
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: HasClientPerfMonitor,
{
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>;
}
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>
where
I: Input,
EM: ProgressReporter<I>,
S: HasExecutions + HasClientPerfMonitor + HasMetadata,
ST: ?Sized,
{
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 monitor_timeout = STATS_TIMEOUT_DEFAULT;
loop {
self.fuzz_one(stages, executor, state, manager)?;
last = manager.maybe_report_progress(state, last, monitor_timeout)?;
}
}
fn fuzz_loop_for(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
manager: &mut EM,
iters: u64,
) -> Result<usize, Error> {
if iters == 0 {
return Err(Error::illegal_argument(
"Cannot fuzz for 0 iterations!".to_string(),
));
}
let mut ret = 0;
let mut last = current_time();
let monitor_timeout = STATS_TIMEOUT_DEFAULT;
for _ in 0..iters {
ret = self.fuzz_one(stages, executor, state, manager)?;
last = manager.maybe_report_progress(state, last, monitor_timeout)?;
}
Ok(ret)
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum ExecuteInputResult {
None,
Corpus,
Solution,
}
#[derive(Debug)]
pub struct StdFuzzer<CS, F, I, OF, OT, S>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfMonitor,
{
scheduler: CS,
feedback: F,
objective: OF,
phantom: PhantomData<(I, OT, S)>,
}
impl<CS, F, I, OF, OT, S> HasScheduler<CS, I, S> for StdFuzzer<CS, F, I, OF, OT, S>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfMonitor,
{
fn scheduler(&self) -> &CS {
&self.scheduler
}
fn scheduler_mut(&mut self) -> &mut CS {
&mut self.scheduler
}
}
impl<CS, F, I, OF, OT, S> HasFeedback<F, I, S> for StdFuzzer<CS, F, I, OF, OT, S>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfMonitor,
{
fn feedback(&self) -> &F {
&self.feedback
}
fn feedback_mut(&mut self) -> &mut F {
&mut self.feedback
}
}
impl<CS, F, I, OF, OT, S> HasObjective<I, OF, S> for StdFuzzer<CS, F, I, OF, OT, S>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfMonitor,
{
fn objective(&self) -> &OF {
&self.objective
}
fn objective_mut(&mut self) -> &mut OF {
&mut self.objective
}
}
impl<CS, F, I, OF, OT, S> ExecutionProcessor<I, OT, S> for StdFuzzer<CS, F, I, OF, OT, S>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
OT: ObserversTuple<I, S> + serde::Serialize + serde::de::DeserializeOwned,
S: HasCorpus<I> + HasSolutions<I> + HasClientPerfMonitor + 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>,
{
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::with_executions(input.clone(), *state.executions());
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 = if manager.configuration() == EventConfig::AlwaysUnique {
None
} else {
Some(manager.serialize_observers(observers)?)
};
manager.fire(
state,
Event::NewTestcase {
input,
observers_buf,
exit_kind: *exit_kind,
corpus_size: state.corpus().count(),
client_config: manager.configuration(),
time: current_time(),
executions: *state.executions(),
},
)?;
}
Ok((res, Some(idx)))
}
ExecuteInputResult::Solution => {
self.feedback_mut().discard_metadata(state, &input)?;
let mut testcase = Testcase::with_executions(input, *state.executions());
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<CS, F, I, OF, OT, S> EvaluatorObservers<I, OT, S> for StdFuzzer<CS, F, I, OF, OT, S>
where
CS: Scheduler<I, S>,
OT: ObserversTuple<I, S> + serde::Serialize + serde::de::DeserializeOwned,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasCorpus<I> + HasSolutions<I> + HasClientPerfMonitor + HasExecutions,
{
#[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<CS, E, EM, F, I, OF, OT, S> Evaluator<E, EM, I, S> for StdFuzzer<CS, F, I, OF, OT, S>
where
CS: Scheduler<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: HasCorpus<I> + HasSolutions<I> + HasClientPerfMonitor + HasExecutions,
{
#[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::with_executions(input.clone(), *state.executions());
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 = if manager.configuration() == EventConfig::AlwaysUnique {
None
} else {
Some(manager.serialize_observers(observers)?)
};
manager.fire(
state,
Event::NewTestcase {
input,
observers_buf,
exit_kind,
corpus_size: state.corpus().count(),
client_config: manager.configuration(),
time: current_time(),
executions: *state.executions(),
},
)?;
Ok(idx)
}
}
impl<CS, E, EM, F, I, OF, OT, S, ST> Fuzzer<E, EM, I, S, ST> for StdFuzzer<CS, F, I, OF, OT, S>
where
CS: Scheduler<I, S>,
EM: EventManager<E, I, S, Self>,
F: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor + HasExecutions + HasMetadata,
OF: Feedback<I, S>,
ST: StagesTuple<E, EM, S, Self> + ?Sized,
{
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_monitor_mut().start_timer();
let idx = self.scheduler.next(state)?;
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().mark_scheduler_time();
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().reset_stage_index();
stages.perform_all(self, executor, state, manager, idx)?;
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().start_timer();
manager.process(self, state, executor)?;
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().mark_manager_time();
Ok(idx)
}
}
impl<CS, F, I, OF, OT, S> StdFuzzer<CS, F, I, OF, OT, S>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasExecutions + HasClientPerfMonitor,
{
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);
*state.executions_mut() += 1;
start_timer!(state);
let exit_kind = executor.run_target(self, state, event_mgr, input)?;
mark_feature_time!(state, PerfFeature::TargetExecution);
start_timer!(state);
executor
.observers_mut()
.post_exec_all(state, input, &exit_kind)?;
mark_feature_time!(state, PerfFeature::PostExecObservers);
Ok(exit_kind)
}
}
pub trait ExecutesInput<I, OT, S, Z>
where
I: Input,
OT: ObserversTuple<I, S>,
{
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, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>;
}
impl<CS, F, I, OF, OT, S> ExecutesInput<I, OT, S, Self> for StdFuzzer<CS, F, I, OF, OT, S>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OT: ObserversTuple<I, S>,
OF: Feedback<I, S>,
S: HasExecutions + HasClientPerfMonitor,
{
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);
*state.executions_mut() += 1;
start_timer!(state);
let exit_kind = executor.run_target(self, state, event_mgr, input)?;
mark_feature_time!(state, PerfFeature::TargetExecution);
start_timer!(state);
executor
.observers_mut()
.post_exec_all(state, input, &exit_kind)?;
mark_feature_time!(state, PerfFeature::PostExecObservers);
Ok(exit_kind)
}
}
#[cfg(feature = "python")]
#[allow(missing_docs)]
pub mod pybind {
use alloc::{boxed::Box, vec::Vec};
use pyo3::prelude::*;
use crate::{
bolts::ownedref::OwnedPtrMut,
events::pybind::PythonEventManager,
executors::pybind::PythonExecutor,
feedbacks::pybind::PythonFeedback,
fuzzer::{Evaluator, Fuzzer, StdFuzzer},
inputs::BytesInput,
observers::pybind::PythonObserversTuple,
schedulers::QueueScheduler,
stages::pybind::PythonStagesTuple,
state::pybind::{PythonStdState, PythonStdStateWrapper},
};
pub type PythonStdFuzzer = StdFuzzer<
QueueScheduler,
PythonFeedback,
BytesInput,
PythonFeedback,
PythonObserversTuple,
PythonStdState,
>;
#[pyclass(unsendable, name = "StdFuzzer")]
#[derive(Debug)]
pub struct PythonStdFuzzerWrapper {
pub inner: OwnedPtrMut<PythonStdFuzzer>,
}
impl PythonStdFuzzerWrapper {
pub fn wrap(r: &mut PythonStdFuzzer) -> Self {
Self {
inner: OwnedPtrMut::Ptr(r),
}
}
#[must_use]
pub fn unwrap(&self) -> &PythonStdFuzzer {
self.inner.as_ref()
}
pub fn unwrap_mut(&mut self) -> &mut PythonStdFuzzer {
self.inner.as_mut()
}
}
#[pymethods]
impl PythonStdFuzzerWrapper {
#[new]
fn new(py_feedback: PythonFeedback, py_objective: PythonFeedback) -> Self {
Self {
inner: OwnedPtrMut::Owned(Box::new(StdFuzzer::new(
QueueScheduler::new(),
py_feedback,
py_objective,
))),
}
}
fn add_input(
&mut self,
py_state: &mut PythonStdStateWrapper,
py_executor: &mut PythonExecutor,
py_mgr: &mut PythonEventManager,
input: Vec<u8>,
) -> usize {
self.inner
.as_mut()
.add_input(
py_state.unwrap_mut(),
py_executor,
py_mgr,
BytesInput::new(input),
)
.expect("Failed to add input")
}
fn fuzz_loop(
&mut self,
py_executor: &mut PythonExecutor,
py_state: &mut PythonStdStateWrapper,
py_mgr: &mut PythonEventManager,
stages_tuple: &mut PythonStagesTuple,
) {
self.inner
.as_mut()
.fuzz_loop(stages_tuple, py_executor, py_state.unwrap_mut(), py_mgr)
.expect("Failed to generate the initial corpus");
}
}
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<PythonStdFuzzerWrapper>()?;
Ok(())
}
}