#[cfg(feature = "std")]
use alloc::borrow::Cow;
use alloc::{string::ToString, vec::Vec};
use core::{fmt::Debug, time::Duration};
#[cfg(feature = "std")]
use core::{hash::Hash, marker::PhantomData};
#[cfg(feature = "std")]
use fastbloom::BloomFilter;
#[cfg(feature = "std")]
use libafl_bolts::impl_serdeany;
use libafl_bolts::{current_time, tuples::MatchName};
#[cfg(feature = "std")]
use serde::Deserialize;
use serde::{Serialize, de::DeserializeOwned};
#[cfg(feature = "introspection")]
use crate::monitors::stats::PerfFeature;
#[cfg(feature = "std")]
use crate::monitors::stats::{AggregatorOps, UserStats, UserStatsValue};
use crate::{
Error, HasMetadata,
corpus::{Corpus, CorpusId, HasCurrentCorpusId, HasTestcase, Testcase},
events::{
Event, EventConfig, EventFirer, EventReceiver, EventWithStats, ProgressReporter,
SendExiting,
},
executors::{Executor, ExitKind, HasObservers},
feedbacks::Feedback,
inputs::{Input, NopToTargetBytes, ToTargetBytes},
mark_feature_time,
observers::ObserversTuple,
schedulers::Scheduler,
stages::StagesTuple,
start_timer,
state::{
HasCorpus, HasCurrentStageId, HasCurrentTestcase, HasExecutions, HasImported,
HasLastFoundTime, HasLastReportTime, HasSolutions, MaybeHasClientPerfMonitor, Stoppable,
},
};
pub(crate) const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15);
pub trait HasScheduler<I, S> {
type Scheduler: Scheduler<I, S>;
fn scheduler(&self) -> &Self::Scheduler;
fn scheduler_mut(&mut self) -> &mut Self::Scheduler;
}
pub trait HasFeedback {
type Feedback;
fn feedback(&self) -> &Self::Feedback;
fn feedback_mut(&mut self) -> &mut Self::Feedback;
}
pub trait HasObjective {
type Objective;
fn objective(&self) -> &Self::Objective;
fn objective_mut(&mut self) -> &mut Self::Objective;
fn share_objectives(&self) -> bool;
fn set_share_objectives(&mut self, share_objectives: bool);
}
pub trait HasTargetBytesConverter {
type Converter;
fn target_bytes_converter(&self) -> &Self::Converter;
fn target_bytes_converter_mut(&mut self) -> &mut Self::Converter;
}
impl<I, T> ToTargetBytes<I> for T
where
T: HasTargetBytesConverter,
T::Converter: ToTargetBytes<I>,
{
fn to_target_bytes<'a>(&mut self, input: &'a I) -> libafl_bolts::ownedref::OwnedSlice<'a, u8> {
self.target_bytes_converter_mut().to_target_bytes(input)
}
}
pub trait ExecutionProcessor<EM, I, OT, S> {
fn check_results(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<ExecuteInputResult, Error>;
fn process_execution(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
exec_res: &ExecuteInputResult,
exit_kind: &ExitKind,
observers: &OT,
) -> Result<Option<CorpusId>, Error>;
fn serialize_and_dispatch(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
exec_res: &ExecuteInputResult,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<(), Error>;
fn dispatch_event(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
exec_res: &ExecuteInputResult,
obs_buf: Option<Vec<u8>>,
exit_kind: &ExitKind,
) -> Result<(), Error>;
fn evaluate_execution(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>;
}
pub trait EvaluatorObservers<E, EM, I, S> {
fn evaluate_input_with_observers(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: &I,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>;
}
pub trait EventProcessor<E, EM, I, S> {
fn process_events(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
) -> Result<(), Error>;
}
pub trait Evaluator<E, EM, I, S> {
fn evaluate_filtered(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: &I,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>;
fn evaluate_input(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: &I,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>;
fn add_input(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
) -> Result<CorpusId, Error>;
fn add_disabled_input(&mut self, state: &mut S, input: I) -> Result<CorpusId, 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<CorpusId, Error>;
fn fuzz_loop(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error>;
fn fuzz_loop_for(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
manager: &mut EM,
iters: u64,
) -> Result<CorpusId, Error>;
}
#[derive(Debug, PartialEq, Eq)]
pub enum ExecuteInputResult {
None,
Corpus,
Solution,
}
#[derive(Debug)]
pub struct StdFuzzer<CS, F, IC, IF, OF> {
scheduler: CS,
feedback: F,
objective: OF,
target_bytes_converter: IC,
input_filter: IF,
share_objectives: bool,
}
impl<CS, F, I, IC, IF, OF, S> HasScheduler<I, S> for StdFuzzer<CS, F, IC, IF, OF>
where
CS: Scheduler<I, S>,
{
type Scheduler = CS;
fn scheduler(&self) -> &CS {
&self.scheduler
}
fn scheduler_mut(&mut self) -> &mut CS {
&mut self.scheduler
}
}
impl<CS, F, IC, IF, OF> HasFeedback for StdFuzzer<CS, F, IC, IF, OF> {
type Feedback = F;
fn feedback(&self) -> &Self::Feedback {
&self.feedback
}
fn feedback_mut(&mut self) -> &mut Self::Feedback {
&mut self.feedback
}
}
impl<CS, F, IC, IF, OF> HasObjective for StdFuzzer<CS, F, IC, IF, OF> {
type Objective = OF;
fn objective(&self) -> &OF {
&self.objective
}
fn objective_mut(&mut self) -> &mut OF {
&mut self.objective
}
fn set_share_objectives(&mut self, share_objectives: bool) {
self.share_objectives = share_objectives;
}
fn share_objectives(&self) -> bool {
self.share_objectives
}
}
impl<CS, EM, F, I, IC, IF, OF, OT, S> ExecutionProcessor<EM, I, OT, S>
for StdFuzzer<CS, F, IC, IF, OF>
where
CS: Scheduler<I, S>,
EM: EventFirer<I, S>,
F: Feedback<EM, I, OT, S>,
I: Input,
OF: Feedback<EM, I, OT, S>,
OT: ObserversTuple<I, S> + Serialize,
S: HasCorpus<I>
+ MaybeHasClientPerfMonitor
+ HasExecutions
+ HasCurrentTestcase<I>
+ HasSolutions<I>
+ HasLastFoundTime
+ HasExecutions,
{
fn check_results(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<ExecuteInputResult, Error> {
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 process_execution(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
exec_res: &ExecuteInputResult,
_exit_kind: &ExitKind,
observers: &OT,
) -> Result<Option<CorpusId>, Error> {
match exec_res {
ExecuteInputResult::None => Ok(None),
ExecuteInputResult::Corpus => {
let mut testcase = Testcase::from(input.clone());
#[cfg(feature = "track_hit_feedbacks")]
self.feedback_mut()
.append_hit_feedbacks(testcase.hit_feedbacks_mut())?;
self.feedback_mut()
.append_metadata(state, manager, observers, &mut testcase)?;
let id = state.corpus_mut().add(testcase)?;
self.scheduler_mut().on_add(state, id)?;
Ok(Some(id))
}
ExecuteInputResult::Solution => {
let mut testcase = Testcase::from(input.clone());
testcase.set_parent_id_optional(*state.corpus().current());
if let Ok(mut tc) = state.current_testcase_mut() {
tc.found_objective();
}
#[cfg(feature = "track_hit_feedbacks")]
self.objective_mut()
.append_hit_feedbacks(testcase.hit_objectives_mut())?;
self.objective_mut()
.append_metadata(state, manager, observers, &mut testcase)?;
state.solutions_mut().add(testcase)?;
Ok(None)
}
}
}
fn serialize_and_dispatch(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
exec_res: &ExecuteInputResult,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<(), Error> {
let observers_buf = match exec_res {
ExecuteInputResult::Corpus => {
if manager.should_send() {
if manager.configuration() == EventConfig::AlwaysUnique {
None
} else {
Some(postcard::to_allocvec(observers)?)
}
} else {
None
}
}
_ => None,
};
self.dispatch_event(state, manager, input, exec_res, observers_buf, exit_kind)?;
Ok(())
}
fn dispatch_event(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
exec_res: &ExecuteInputResult,
observers_buf: Option<Vec<u8>>,
exit_kind: &ExitKind,
) -> Result<(), Error> {
match exec_res {
ExecuteInputResult::Corpus => {
if manager.should_send() {
manager.fire(
state,
EventWithStats::with_current_time(
Event::NewTestcase {
input: input.clone(),
observers_buf,
exit_kind: *exit_kind,
corpus_size: state.corpus().count(),
client_config: manager.configuration(),
forward_id: None,
#[cfg(all(unix, feature = "std", feature = "multi_machine"))]
node_id: None,
},
*state.executions(),
),
)?;
}
}
ExecuteInputResult::Solution => {
if manager.should_send() {
manager.fire(
state,
EventWithStats::with_current_time(
Event::Objective {
input: self.share_objectives.then_some(input.clone()),
objective_size: state.solutions().count(),
},
*state.executions(),
),
)?;
}
}
ExecuteInputResult::None => (),
}
Ok(())
}
fn evaluate_execution(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
let exec_res = self.check_results(state, manager, input, observers, exit_kind)?;
let corpus_id =
self.process_execution(state, manager, input, &exec_res, exit_kind, observers)?;
if send_events {
self.serialize_and_dispatch(state, manager, input, &exec_res, observers, exit_kind)?;
}
if exec_res != ExecuteInputResult::None {
*state.last_found_time_mut() = current_time();
}
Ok((exec_res, corpus_id))
}
}
impl<CS, E, EM, F, I, IC, IF, OF, S> EvaluatorObservers<E, EM, I, S>
for StdFuzzer<CS, F, IC, IF, OF>
where
CS: Scheduler<I, S>,
E: HasObservers + Executor<EM, I, S, Self>,
E::Observers: MatchName + ObserversTuple<I, S> + Serialize,
EM: EventFirer<I, S>,
F: Feedback<EM, I, E::Observers, S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasCorpus<I>
+ HasSolutions<I>
+ MaybeHasClientPerfMonitor
+ HasCurrentTestcase<I>
+ HasExecutions
+ HasLastFoundTime,
I: Input,
{
#[inline]
fn evaluate_input_with_observers(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: &I,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
let exit_kind = self.execute_input(state, executor, manager, input)?;
let observers = executor.observers();
self.scheduler.on_evaluation(state, input, &*observers)?;
self.evaluate_execution(state, manager, input, &*observers, &exit_kind, send_events)
}
}
pub trait InputFilter<EM, I, S> {
fn should_execute(&mut self, input: &I, state: &mut S, manager: &mut EM)
-> Result<bool, Error>;
}
#[derive(Debug, Copy, Clone)]
pub struct NopInputFilter;
impl<EM, I, S> InputFilter<EM, I, S> for NopInputFilter {
#[inline]
fn should_execute(
&mut self,
_input: &I,
_state: &mut S,
_manager: &mut EM,
) -> Result<bool, Error> {
Ok(true)
}
}
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct BloomInputFilter {
bloom: BloomFilter,
}
#[cfg(feature = "std")]
impl Default for BloomInputFilter {
fn default() -> Self {
let bloom = BloomFilter::with_false_pos(1e-4).expected_items(10_000_000);
Self { bloom }
}
}
#[cfg(feature = "std")]
impl BloomInputFilter {
#[must_use]
pub fn new(items_count: usize, fp_p: f64) -> Self {
let bloom = BloomFilter::with_false_pos(fp_p).expected_items(items_count);
Self { bloom }
}
}
#[cfg(feature = "std")]
impl<EM, I: Hash, S> InputFilter<EM, I, S> for BloomInputFilter {
#[inline]
fn should_execute(
&mut self,
input: &I,
_state: &mut S,
_manager: &mut EM,
) -> Result<bool, Error> {
Ok(!self.bloom.insert(input))
}
}
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct ReportingInputFilter<F> {
inner: F,
reporting_interval: u64,
}
#[cfg(feature = "std")]
impl<F> ReportingInputFilter<F> {
pub fn new(inner: F, reporting_interval: u64) -> Self {
Self {
inner,
reporting_interval,
}
}
}
#[cfg(feature = "std")]
impl_serdeany!(ReportingInputFilterStats);
#[cfg(feature = "std")]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct ReportingInputFilterStats {
skipped: u64,
}
#[cfg(feature = "std")]
impl<EM, F, I, S> InputFilter<EM, I, S> for ReportingInputFilter<F>
where
F: InputFilter<EM, I, S>,
EM: EventFirer<I, S>,
S: HasMetadata + HasExecutions,
{
fn should_execute(
&mut self,
input: &I,
state: &mut S,
manager: &mut EM,
) -> Result<bool, Error> {
let actual_executions = *state.executions();
let should_execute = self.inner.should_execute(input, state, manager)?;
let stats = state.metadata_or_insert_with(ReportingInputFilterStats::default);
if !should_execute {
stats.skipped += 1;
}
let skipped = stats.skipped;
let attempted_executions = skipped + actual_executions;
if attempted_executions.is_multiple_of(self.reporting_interval) {
manager.fire(
state,
EventWithStats::with_current_time(
Event::UpdateUserStats {
name: Cow::Borrowed("filtered_inputs"),
value: UserStats::new(
UserStatsValue::Ratio(skipped, attempted_executions),
AggregatorOps::Avg,
),
phantom: PhantomData,
},
actual_executions,
),
)?;
}
Ok(should_execute)
}
}
impl<CS, E, EM, F, I, IC, IF, OF, S> Evaluator<E, EM, I, S> for StdFuzzer<CS, F, IC, IF, OF>
where
CS: Scheduler<I, S>,
E: HasObservers + Executor<EM, I, S, Self>,
E::Observers: MatchName + ObserversTuple<I, S> + Serialize,
EM: EventFirer<I, S>,
F: Feedback<EM, I, E::Observers, S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasCorpus<I>
+ HasSolutions<I>
+ MaybeHasClientPerfMonitor
+ HasCurrentTestcase<I>
+ HasLastFoundTime
+ HasExecutions,
I: Input,
IF: InputFilter<EM, I, S>,
{
fn evaluate_filtered(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: &I,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
if self.input_filter.should_execute(input, state, manager)? {
self.evaluate_input(state, executor, manager, input)
} else {
Ok((ExecuteInputResult::None, None))
}
}
#[inline]
fn evaluate_input(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: &I,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
self.evaluate_input_with_observers(state, executor, manager, input, true)
}
fn add_input(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
) -> Result<CorpusId, Error> {
*state.last_found_time_mut() = current_time();
let exit_kind = self.execute_input(state, executor, manager, &input)?;
let observers = executor.observers();
let mut testcase = Testcase::from(input.clone());
testcase.set_executions(*state.executions());
#[cfg(not(feature = "introspection"))]
let is_solution: bool =
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 {
#[cfg(feature = "track_hit_feedbacks")]
self.objective_mut()
.append_hit_feedbacks(testcase.hit_objectives_mut())?;
self.objective_mut()
.append_metadata(state, manager, &*observers, &mut testcase)?;
let id = state.solutions_mut().add(testcase)?;
manager.fire(
state,
EventWithStats::with_current_time(
Event::Objective {
input: self.share_objectives.then_some(input.clone()),
objective_size: state.solutions().count(),
},
*state.executions(),
),
)?;
return Ok(id);
}
#[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,
)?;
#[cfg(feature = "track_hit_feedbacks")]
self.feedback_mut()
.append_hit_feedbacks(testcase.hit_feedbacks_mut())?;
self.feedback_mut()
.append_metadata(state, manager, &*observers, &mut testcase)?;
let id = state.corpus_mut().add(testcase)?;
self.scheduler_mut().on_add(state, id)?;
let observers_buf = if manager.configuration() == EventConfig::AlwaysUnique {
None
} else {
Some(postcard::to_allocvec(&*observers)?)
};
manager.fire(
state,
EventWithStats::with_current_time(
Event::NewTestcase {
input,
observers_buf,
exit_kind,
corpus_size: state.corpus().count(),
client_config: manager.configuration(),
forward_id: None,
#[cfg(all(unix, feature = "std", feature = "multi_machine"))]
node_id: None,
},
*state.executions(),
),
)?;
Ok(id)
}
fn add_disabled_input(&mut self, state: &mut S, input: I) -> Result<CorpusId, Error> {
let mut testcase = Testcase::from(input.clone());
testcase.set_executions(*state.executions());
testcase.set_disabled(true);
let id = state.corpus_mut().add_disabled(testcase)?;
Ok(id)
}
}
impl<CS, E, EM, F, I, IC, IF, OF, S> EventProcessor<E, EM, I, S> for StdFuzzer<CS, F, IC, IF, OF>
where
CS: Scheduler<I, S>,
E: HasObservers + Executor<EM, I, S, Self>,
E::Observers: DeserializeOwned + Serialize + ObserversTuple<I, S>,
EM: EventReceiver<I, S> + EventFirer<I, S>,
F: Feedback<EM, I, E::Observers, S>,
I: Input,
OF: Feedback<EM, I, E::Observers, S>,
S: HasCorpus<I>
+ HasSolutions<I>
+ HasExecutions
+ HasLastFoundTime
+ MaybeHasClientPerfMonitor
+ HasCurrentCorpusId
+ HasImported,
{
fn process_events(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
) -> Result<(), Error> {
while let Some((event, with_observers)) = manager.try_receive(state)? {
let res = if with_observers {
match event.event() {
Event::NewTestcase {
input,
observers_buf,
exit_kind,
..
} => {
let observers: E::Observers =
postcard::from_bytes(observers_buf.as_ref().unwrap())?;
let res = self.evaluate_execution(
state, manager, input, &observers, exit_kind, false,
)?;
res.1
}
_ => None,
}
} else {
match event.event() {
Event::NewTestcase { input, .. } => {
let res = self.evaluate_input_with_observers(
state, executor, manager, input, false,
)?;
res.1
}
Event::Objective {
input: Some(unwrapped_input),
..
} => {
let res = self.evaluate_input_with_observers(
state,
executor,
manager,
unwrapped_input,
false,
)?;
res.1
}
_ => None,
}
};
if let Some(item) = res {
*state.imported_mut() += 1;
log::debug!("Added received input as item #{item}");
manager.on_interesting(state, event)?;
} else {
log::debug!("Received input was discarded");
}
}
Ok(())
}
}
impl<CS, E, EM, F, I, IC, IF, OF, S, ST> Fuzzer<E, EM, I, S, ST> for StdFuzzer<CS, F, IC, IF, OF>
where
CS: Scheduler<I, S>,
E: HasObservers + Executor<EM, I, S, Self>,
E::Observers: DeserializeOwned + Serialize + ObserversTuple<I, S>,
EM: EventFirer<I, S>,
I: Input,
F: Feedback<EM, I, E::Observers, S>,
OF: Feedback<EM, I, E::Observers, S>,
EM: ProgressReporter<S> + SendExiting + EventReceiver<I, S>,
S: HasExecutions
+ HasMetadata
+ HasCorpus<I>
+ HasSolutions<I>
+ HasLastReportTime
+ HasLastFoundTime
+ HasImported
+ HasTestcase<I>
+ HasCurrentCorpusId
+ HasCurrentStageId
+ Stoppable
+ MaybeHasClientPerfMonitor,
ST: StagesTuple<E, EM, S, Self>,
{
fn fuzz_one(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<CorpusId, Error> {
#[cfg(feature = "introspection")]
state.introspection_stats_mut().start_timer();
let id = if let Some(id) = state.current_corpus_id()? {
id } else {
let id = self.scheduler.next(state)?;
state.set_corpus_id(id)?; id
};
#[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)?;
#[cfg(feature = "introspection")]
state.introspection_stats_mut().start_timer();
self.process_events(state, executor, manager)?;
#[cfg(feature = "introspection")]
state.introspection_stats_mut().mark_manager_time();
{
if let Ok(mut testcase) = state.testcase_mut(id) {
let scheduled_count = testcase.scheduled_count();
testcase.set_scheduled_count(scheduled_count + 1);
}
}
state.clear_corpus_id()?;
if state.stop_requested() {
state.discard_stop_request();
manager.on_shutdown()?;
return Err(Error::shutting_down());
}
Ok(id)
}
fn fuzz_loop(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
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 S,
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)]
pub struct StdFuzzerBuilder<CS, F, IC, IF, OF> {
scheduler: CS,
feedback: F,
objective: OF,
target_bytes_converter: IC,
input_filter: IF,
share_objectives: bool,
}
impl StdFuzzerBuilder<(), (), NopToTargetBytes, NopInputFilter, ()> {
#[must_use]
pub fn new() -> Self {
Self {
target_bytes_converter: NopToTargetBytes,
input_filter: NopInputFilter,
scheduler: (),
feedback: (),
objective: (),
share_objectives: false,
}
}
}
impl Default for StdFuzzerBuilder<(), (), NopToTargetBytes, NopInputFilter, ()> {
fn default() -> Self {
Self::new()
}
}
impl<CS, F, IC, IF, OF> StdFuzzerBuilder<CS, F, IC, IF, OF> {
#[must_use]
pub fn target_bytes_converter<IC2>(
self,
target_bytes_converter: IC2,
) -> StdFuzzerBuilder<CS, F, IC2, IF, OF> {
StdFuzzerBuilder {
target_bytes_converter,
input_filter: self.input_filter,
scheduler: self.scheduler,
feedback: self.feedback,
objective: self.objective,
share_objectives: self.share_objectives,
}
}
}
impl<CS, F, IC, IF, OF> StdFuzzerBuilder<CS, F, IC, IF, OF> {
#[must_use]
pub fn input_filter<IF2>(self, input_filter: IF2) -> StdFuzzerBuilder<CS, F, IC, IF2, OF> {
StdFuzzerBuilder {
target_bytes_converter: self.target_bytes_converter,
input_filter,
scheduler: self.scheduler,
feedback: self.feedback,
objective: self.objective,
share_objectives: self.share_objectives,
}
}
}
impl<CS, F, IC, IF, OF> StdFuzzerBuilder<CS, F, IC, IF, OF> {
#[must_use]
pub fn scheduler<CS2>(self, scheduler: CS2) -> StdFuzzerBuilder<CS2, F, IC, IF, OF> {
StdFuzzerBuilder {
target_bytes_converter: self.target_bytes_converter,
input_filter: self.input_filter,
scheduler,
feedback: self.feedback,
objective: self.objective,
share_objectives: self.share_objectives,
}
}
}
impl<CS, F, IC, IF, OF> StdFuzzerBuilder<CS, F, IC, IF, OF> {
#[must_use]
pub fn feedback<F2>(self, feedback: F2) -> StdFuzzerBuilder<CS, F2, IC, IF, OF> {
StdFuzzerBuilder {
target_bytes_converter: self.target_bytes_converter,
input_filter: self.input_filter,
scheduler: self.scheduler,
feedback,
objective: self.objective,
share_objectives: self.share_objectives,
}
}
}
impl<CS, F, IC, IF, OF> StdFuzzerBuilder<CS, F, IC, IF, OF> {
#[must_use]
pub fn objective<OF2>(self, objective: OF2) -> StdFuzzerBuilder<CS, F, IC, IF, OF2> {
StdFuzzerBuilder {
target_bytes_converter: self.target_bytes_converter,
input_filter: self.input_filter,
scheduler: self.scheduler,
feedback: self.feedback,
objective,
share_objectives: self.share_objectives,
}
}
}
impl<CS, F, IC, IF, OF> StdFuzzerBuilder<CS, F, IC, IF, OF> {
#[must_use]
pub fn share_objectives(self, share_objectives: bool) -> StdFuzzerBuilder<CS, F, IC, IF, OF> {
StdFuzzerBuilder {
target_bytes_converter: self.target_bytes_converter,
input_filter: self.input_filter,
scheduler: self.scheduler,
feedback: self.feedback,
objective: self.objective,
share_objectives,
}
}
}
impl<CS, F, IC, IF, OF> StdFuzzerBuilder<CS, F, IC, IF, OF> {
pub fn build(self) -> StdFuzzer<CS, F, IC, IF, OF> {
StdFuzzer {
target_bytes_converter: self.target_bytes_converter,
input_filter: self.input_filter,
scheduler: self.scheduler,
feedback: self.feedback,
objective: self.objective,
share_objectives: self.share_objectives,
}
}
}
impl<CS, F, IC, IF, OF> HasTargetBytesConverter for StdFuzzer<CS, F, IC, IF, OF> {
type Converter = IC;
fn target_bytes_converter(&self) -> &Self::Converter {
&self.target_bytes_converter
}
fn target_bytes_converter_mut(&mut self) -> &mut Self::Converter {
&mut self.target_bytes_converter
}
}
impl<CS, F, OF> StdFuzzer<CS, F, NopToTargetBytes, NopInputFilter, OF> {
pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self {
StdFuzzer::builder()
.scheduler(scheduler)
.feedback(feedback)
.objective(objective)
.build()
}
}
impl StdFuzzer<(), (), NopToTargetBytes, NopInputFilter, ()> {
#[must_use]
pub fn builder() -> StdFuzzerBuilder<(), (), NopToTargetBytes, NopInputFilter, ()> {
StdFuzzerBuilder::new()
}
}
pub trait ExecutesInput<E, EM, I, S> {
fn execute_input(
&mut self,
state: &mut S,
executor: &mut E,
event_mgr: &mut EM,
input: &I,
) -> Result<ExitKind, Error>;
}
impl<CS, E, EM, F, I, IC, IF, OF, S> ExecutesInput<E, EM, I, S> for StdFuzzer<CS, F, IC, IF, OF>
where
CS: Scheduler<I, S>,
E: Executor<EM, I, S, Self> + HasObservers,
E::Observers: ObserversTuple<I, S>,
S: HasExecutions + HasCorpus<I> + MaybeHasClientPerfMonitor,
{
fn execute_input(
&mut self,
state: &mut S,
executor: &mut E,
event_mgr: &mut EM,
input: &I,
) -> 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)
}
}
#[derive(Debug, Copy, Clone)]
pub struct NopFuzzer {
converter: NopToTargetBytes,
}
impl NopFuzzer {
#[must_use]
pub fn new() -> Self {
Self {
converter: NopToTargetBytes,
}
}
}
impl Default for NopFuzzer {
fn default() -> Self {
Self::new()
}
}
impl HasTargetBytesConverter for NopFuzzer {
type Converter = NopToTargetBytes;
fn target_bytes_converter(&self) -> &Self::Converter {
&self.converter
}
fn target_bytes_converter_mut(&mut self) -> &mut Self::Converter {
&mut self.converter
}
}
impl<E, EM, I, S, ST> Fuzzer<E, EM, I, S, ST> for NopFuzzer
where
EM: ProgressReporter<S>,
ST: StagesTuple<E, EM, S, Self>,
S: HasMetadata + HasExecutions + HasLastReportTime + HasCurrentStageId,
{
fn fuzz_one(
&mut self,
_stages: &mut ST,
_executor: &mut E,
_state: &mut S,
_manager: &mut EM,
) -> Result<CorpusId, Error> {
unimplemented!("NopFuzzer cannot fuzz");
}
fn fuzz_loop(
&mut self,
_stages: &mut ST,
_executor: &mut E,
_state: &mut S,
_manager: &mut EM,
) -> Result<(), Error> {
unimplemented!("NopFuzzer cannot fuzz");
}
fn fuzz_loop_for(
&mut self,
_stages: &mut ST,
_executor: &mut E,
_state: &mut S,
_manager: &mut EM,
_iters: u64,
) -> Result<CorpusId, Error> {
unimplemented!("NopFuzzer cannot fuzz");
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use core::cell::RefCell;
use libafl_bolts::rands::StdRand;
use crate::{
StdFuzzer,
corpus::InMemoryCorpus,
events::NopEventManager,
executors::{ExitKind, InProcessExecutor},
fuzzer::{BloomInputFilter, Evaluator},
inputs::BytesInput,
schedulers::StdScheduler,
state::StdState,
};
#[test]
fn filtered_execution() {
let execution_count = RefCell::new(0);
let scheduler = StdScheduler::new();
let bloom_filter = BloomInputFilter::default();
let mut fuzzer = StdFuzzer::builder()
.input_filter(bloom_filter)
.scheduler(scheduler)
.feedback(())
.objective(())
.build();
let mut state = StdState::new(
StdRand::new(),
InMemoryCorpus::new(),
InMemoryCorpus::new(),
&mut (),
&mut (),
)
.unwrap();
let mut manager = NopEventManager::new();
let mut harness = |_input: &BytesInput| {
*execution_count.borrow_mut() += 1;
ExitKind::Ok
};
let mut executor =
InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut manager)
.unwrap();
let input = BytesInput::new(vec![1, 2, 3]);
assert!(
fuzzer
.evaluate_input(&mut state, &mut executor, &mut manager, &input)
.is_ok()
);
assert_eq!(1, *execution_count.borrow());
assert!(
fuzzer
.evaluate_filtered(&mut state, &mut executor, &mut manager, &input)
.is_ok()
);
assert_eq!(2, *execution_count.borrow());
assert!(
fuzzer
.evaluate_filtered(&mut state, &mut executor, &mut manager, &input)
.is_ok()
);
assert_eq!(2, *execution_count.borrow());
assert!(
fuzzer
.evaluate_input(&mut state, &mut executor, &mut manager, &input)
.is_ok()
);
assert_eq!(3, *execution_count.borrow()); }
}