use alloc::string::ToString;
use core::{fmt::Debug, marker::PhantomData, time::Duration};
use libafl_bolts::current_time;
use serde::{de::DeserializeOwned, Serialize};
use crate::{
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, HasTestcase, Testcase},
events::{Event, EventConfig, EventFirer, EventProcessor, ProgressReporter},
executors::{Executor, ExitKind, HasObservers},
feedbacks::Feedback,
inputs::UsesInput,
mark_feature_time,
observers::ObserversTuple,
schedulers::Scheduler,
stages::{HasCurrentStage, StagesTuple},
start_timer,
state::{HasCorpus, HasExecutions, HasImported, HasLastReportTime, HasSolutions, UsesState},
Error, HasMetadata,
};
#[cfg(feature = "introspection")]
use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15);
pub trait HasScheduler: UsesState
where
Self::State: HasCorpus,
{
type Scheduler: Scheduler<State = Self::State>;
fn scheduler(&self) -> &Self::Scheduler;
fn scheduler_mut(&mut self) -> &mut Self::Scheduler;
}
pub trait HasFeedback: UsesState {
type Feedback: Feedback<Self::State>;
fn feedback(&self) -> &Self::Feedback;
fn feedback_mut(&mut self) -> &mut Self::Feedback;
}
pub trait HasObjective: UsesState {
type Objective: Feedback<Self::State>;
fn objective(&self) -> &Self::Objective;
fn objective_mut(&mut self) -> &mut Self::Objective;
}
pub trait ExecutionProcessor<OT>: UsesState {
fn execute_no_process<EM>(
&mut self,
state: &mut Self::State,
manager: &mut EM,
input: &<Self::State as UsesInput>::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<ExecuteInputResult, Error>
where
EM: EventFirer<State = Self::State>;
#[allow(clippy::too_many_arguments)]
fn process_execution<EM>(
&mut self,
state: &mut Self::State,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
exec_res: &ExecuteInputResult,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<Option<CorpusId>, Error>
where
EM: EventFirer<State = Self::State>;
fn execute_and_process<EM>(
&mut self,
state: &mut Self::State,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>
where
EM: EventFirer<State = Self::State>;
}
pub trait EvaluatorObservers<OT>: UsesState + Sized {
fn evaluate_input_with_observers<E, EM>(
&mut self,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>
where
E: Executor<EM, Self> + HasObservers<Observers = OT, State = Self::State>,
EM: EventFirer<State = Self::State>;
}
pub trait Evaluator<E, EM>: UsesState
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
{
fn evaluate_input(
&mut self,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
self.evaluate_input_events(state, executor, manager, input, true)
}
fn evaluate_input_events(
&mut self,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>;
fn add_input(
&mut self,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
) -> Result<CorpusId, Error>;
fn add_disabled_input(
&mut self,
state: &mut Self::State,
input: <Self::State as UsesInput>::Input,
) -> Result<CorpusId, Error>;
}
pub trait Fuzzer<E, EM, ST>: Sized + UsesState
where
Self::State: HasMetadata + HasExecutions + HasLastReportTime,
E: UsesState<State = Self::State>,
EM: ProgressReporter<State = Self::State>,
ST: StagesTuple<E, EM, Self::State, Self>,
{
fn fuzz_one(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut EM::State,
manager: &mut EM,
) -> Result<CorpusId, Error>;
fn fuzz_loop(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut EM::State,
manager: &mut EM,
) -> Result<(), Error> {
let monitor_timeout = STATS_TIMEOUT_DEFAULT;
loop {
manager.maybe_report_progress(state, monitor_timeout)?;
self.fuzz_one(stages, executor, state, manager)?;
}
}
fn fuzz_loop_for(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut EM::State,
manager: &mut EM,
iters: u64,
) -> Result<CorpusId, Error> {
if iters == 0 {
return Err(Error::illegal_argument(
"Cannot fuzz for 0 iterations!".to_string(),
));
}
let mut ret = None;
let monitor_timeout = STATS_TIMEOUT_DEFAULT;
for _ in 0..iters {
manager.maybe_report_progress(state, monitor_timeout)?;
ret = Some(self.fuzz_one(stages, executor, state, manager)?);
}
manager.report_progress(state)?;
Ok(ret.unwrap())
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum ExecuteInputResult {
None,
Corpus,
Solution,
}
#[derive(Debug)]
pub struct StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasCorpus,
{
scheduler: CS,
feedback: F,
objective: OF,
phantom: PhantomData<OT>,
}
impl<CS, F, OF, OT> UsesState for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasCorpus,
{
type State = CS::State;
}
impl<CS, F, OF, OT> HasScheduler for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasCorpus,
{
type Scheduler = CS;
fn scheduler(&self) -> &CS {
&self.scheduler
}
fn scheduler_mut(&mut self) -> &mut CS {
&mut self.scheduler
}
}
impl<CS, F, OF, OT> HasFeedback for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasCorpus,
{
type Feedback = F;
fn feedback(&self) -> &Self::Feedback {
&self.feedback
}
fn feedback_mut(&mut self) -> &mut Self::Feedback {
&mut self.feedback
}
}
impl<CS, F, OF, OT> HasObjective for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasCorpus,
{
type Objective = OF;
fn objective(&self) -> &OF {
&self.objective
}
fn objective_mut(&mut self) -> &mut OF {
&mut self.objective
}
}
impl<CS, F, OF, OT> ExecutionProcessor<OT> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
OT: ObserversTuple<CS::State> + Serialize + DeserializeOwned,
CS::State: HasCorpus + HasSolutions + HasExecutions + HasCorpus + HasImported,
{
fn execute_no_process<EM>(
&mut self,
state: &mut Self::State,
manager: &mut EM,
input: &<Self::State as UsesInput>::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<ExecuteInputResult, Error>
where
EM: EventFirer<State = Self::State>,
{
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 corpus_worthy = self
.feedback_mut()
.is_interesting(state, manager, input, observers, exit_kind)?;
#[cfg(feature = "introspection")]
let corpus_worthy = self
.feedback_mut()
.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
if corpus_worthy {
res = ExecuteInputResult::Corpus;
}
}
Ok(res)
}
fn execute_and_process<EM>(
&mut self,
state: &mut Self::State,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>
where
EM: EventFirer<State = Self::State>,
{
let exec_res = self.execute_no_process(state, manager, &input, observers, exit_kind)?;
let corpus_idx = self.process_execution(
state,
manager,
input,
&exec_res,
observers,
exit_kind,
send_events,
)?;
Ok((exec_res, corpus_idx))
}
fn process_execution<EM>(
&mut self,
state: &mut Self::State,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
exec_res: &ExecuteInputResult,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<Option<CorpusId>, Error>
where
EM: EventFirer<State = Self::State>,
{
match exec_res {
ExecuteInputResult::None => {
self.feedback_mut().discard_metadata(state, &input)?;
self.objective_mut().discard_metadata(state, &input)?;
Ok(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, manager, observers, &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 {
manager.serialize_observers::<OT>(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(),
forward_id: None,
},
)?;
} else {
*state.imported_mut() += 1;
}
Ok(Some(idx))
}
ExecuteInputResult::Solution => {
self.feedback_mut().discard_metadata(state, &input)?;
let executions = *state.executions();
let mut testcase = Testcase::with_executions(input, executions);
testcase.set_parent_id_optional(*state.corpus().current());
self.objective_mut()
.append_metadata(state, manager, observers, &mut testcase)?;
state.solutions_mut().add(testcase)?;
if send_events {
manager.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
executions,
time: current_time(),
},
)?;
}
Ok(None)
}
}
}
}
impl<CS, F, OF, OT> EvaluatorObservers<OT> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
OT: ObserversTuple<CS::State> + Serialize + DeserializeOwned,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasCorpus + HasSolutions + HasExecutions + HasImported,
{
#[inline]
fn evaluate_input_with_observers<E, EM>(
&mut self,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>
where
E: Executor<EM, Self> + HasObservers<Observers = OT, State = Self::State>,
EM: EventFirer<State = Self::State>,
{
let exit_kind = self.execute_input(state, executor, manager, &input)?;
let observers = executor.observers();
self.scheduler.on_evaluation(state, &input, observers)?;
self.execute_and_process(state, manager, input, observers, &exit_kind, send_events)
}
}
impl<CS, E, EM, F, OF, OT> Evaluator<E, EM> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
E: HasObservers<State = CS::State, Observers = OT> + Executor<EM, Self>,
EM: EventFirer<State = CS::State>,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
OT: ObserversTuple<CS::State> + Serialize + DeserializeOwned,
CS::State: HasCorpus + HasSolutions + HasExecutions + HasImported,
{
#[inline]
fn evaluate_input_events(
&mut self,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
self.evaluate_input_with_observers(state, executor, manager, input, send_events)
}
fn add_disabled_input(
&mut self,
state: &mut Self::State,
input: <Self::State as UsesInput>::Input,
) -> Result<CorpusId, Error> {
let mut testcase = Testcase::with_executions(input.clone(), *state.executions());
testcase.set_disabled(true);
let idx = state.corpus_mut().add_disabled(testcase)?;
Ok(idx)
}
fn add_input(
&mut self,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: <Self::State as UsesInput>::Input,
) -> Result<CorpusId, Error> {
let exit_kind = self.execute_input(state, executor, manager, &input)?;
let observers = executor.observers();
let mut testcase = Testcase::with_executions(input.clone(), *state.executions());
#[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 {
self.objective_mut()
.append_metadata(state, manager, observers, &mut testcase)?;
let idx = state.solutions_mut().add(testcase)?;
let executions = *state.executions();
manager.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
executions,
time: current_time(),
},
)?;
return Ok(idx);
}
self.objective_mut().discard_metadata(state, &input)?;
#[cfg(not(feature = "introspection"))]
let _corpus_worthy = self
.feedback_mut()
.is_interesting(state, manager, &input, observers, &exit_kind)?;
#[cfg(feature = "introspection")]
let _corpus_worthy = self
.feedback_mut()
.is_interesting_introspection(state, manager, &input, observers, &exit_kind)?;
self.feedback_mut()
.append_metadata(state, manager, observers, &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 {
manager.serialize_observers::<OT>(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(),
forward_id: None,
},
)?;
Ok(idx)
}
}
impl<CS, E, EM, F, OF, OT, ST> Fuzzer<E, EM, ST> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
E: UsesState<State = CS::State>,
EM: ProgressReporter + EventProcessor<E, Self, State = CS::State>,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasExecutions
+ HasMetadata
+ HasCorpus
+ HasTestcase
+ HasImported
+ HasLastReportTime
+ HasCurrentCorpusIdx
+ HasCurrentStage,
ST: StagesTuple<E, EM, CS::State, Self>,
{
fn fuzz_one(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut CS::State,
manager: &mut EM,
) -> Result<CorpusId, Error> {
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().start_timer();
let idx = if let Some(idx) = state.current_corpus_idx()? {
idx } else {
let idx = self.scheduler.next(state)?;
state.set_corpus_idx(idx)?; idx
};
#[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)?;
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().start_timer();
manager.process(self, state, executor)?;
#[cfg(feature = "introspection")]
state.introspection_monitor_mut().mark_manager_time();
{
if let Ok(mut testcase) = state.testcase_mut(idx) {
let scheduled_count = testcase.scheduled_count();
testcase.set_scheduled_count(scheduled_count + 1);
}
}
state.clear_corpus_idx()?;
Ok(idx)
}
}
impl<CS, F, OF, OT> StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: UsesInput + HasExecutions + HasCorpus,
{
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 CS::State,
executor: &mut E,
event_mgr: &mut EM,
input: &<CS::State as UsesInput>::Input,
) -> Result<ExitKind, Error>
where
E: Executor<EM, Self> + HasObservers<Observers = OT, State = CS::State>,
EM: UsesState<State = CS::State>,
OT: ObserversTuple<CS::State>,
{
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);
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<E, EM>: UsesState
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
{
fn execute_input(
&mut self,
state: &mut Self::State,
executor: &mut E,
event_mgr: &mut EM,
input: &<Self::State as UsesInput>::Input,
) -> Result<ExitKind, Error>;
}
impl<CS, E, EM, F, OF> ExecutesInput<E, EM> for StdFuzzer<CS, F, OF, E::Observers>
where
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
E: Executor<EM, Self> + HasObservers<State = CS::State>,
EM: UsesState<State = CS::State>,
CS::State: UsesInput + HasExecutions + HasCorpus,
{
fn execute_input(
&mut self,
state: &mut CS::State,
executor: &mut E,
event_mgr: &mut EM,
input: &<CS::State as UsesInput>::Input,
) -> Result<ExitKind, Error> {
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);
start_timer!(state);
executor
.observers_mut()
.post_exec_all(state, input, &exit_kind)?;
mark_feature_time!(state, PerfFeature::PostExecObservers);
Ok(exit_kind)
}
}
#[cfg(test)]
pub mod test {
use core::marker::PhantomData;
use libafl_bolts::Error;
use crate::{
corpus::CorpusId,
events::ProgressReporter,
stages::{HasCurrentStage, StagesTuple},
state::{HasExecutions, HasLastReportTime, State, UsesState},
Fuzzer, HasMetadata,
};
#[derive(Clone, Debug)]
pub struct NopFuzzer<S> {
phantom: PhantomData<S>,
}
impl<S> NopFuzzer<S> {
#[must_use]
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
}
}
impl<S> Default for NopFuzzer<S> {
fn default() -> Self {
Self::new()
}
}
impl<S> UsesState for NopFuzzer<S>
where
S: State,
{
type State = S;
}
impl<ST, E, EM> Fuzzer<E, EM, ST> for NopFuzzer<E::State>
where
E: UsesState,
EM: ProgressReporter<State = E::State>,
ST: StagesTuple<E, EM, E::State, Self>,
E::State: HasMetadata + HasExecutions + HasLastReportTime + HasCurrentStage,
{
fn fuzz_one(
&mut self,
_stages: &mut ST,
_executor: &mut E,
_state: &mut EM::State,
_manager: &mut EM,
) -> Result<CorpusId, Error> {
unimplemented!()
}
}
}