use alloc::{
borrow::{Cow, ToOwned},
boxed::Box,
string::ToString,
vec::Vec,
};
use core::{fmt, marker::PhantomData};
#[cfg(feature = "std")]
pub use afl_stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime};
pub use calibrate::{CalibrationStage, run_target_with_timing};
pub use colorization::*;
#[cfg(all(feature = "std", unix))]
pub use concolic::ConcolicTracingStage;
#[cfg(all(feature = "std", feature = "concolic_mutation", unix))]
pub use concolic::SimpleConcolicMutationalStage;
#[cfg(feature = "std")]
pub use dump::*;
pub use generalization::GeneralizationStage;
use hashbrown::HashSet;
use libafl_bolts::{
Named, impl_serdeany,
tuples::{HasConstLen, IntoVec},
};
pub use logics::*;
pub use mutational::{MutationalStage, StdMutationalStage};
pub use power::{PowerMutationalStage, StdPowerMutationalStage};
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
pub use sync::*;
#[cfg(feature = "std")]
pub use time_tracker::TimeTrackingStageWrapper;
pub use tmin::{ObserverEqualityFactory, ObserverEqualityFeedback, StdTMinMutationalStage};
pub use tracing::TracingStage;
pub use tuneable::*;
use tuple_list::NonEmptyTuple;
#[cfg(feature = "unicode")]
pub use unicode::*;
#[cfg(feature = "std")]
pub use verify_timeouts::{TimeoutsToVerify, VerifyTimeoutsStage};
use crate::{
Error, HasNamedMetadata,
corpus::{CorpusId, HasCurrentCorpusId},
events::SendExiting,
state::{HasCurrentStageId, HasExecutions, MaybeHasClientPerfMonitor, Stoppable},
};
pub mod mutational;
pub mod push;
pub mod tmin;
pub mod shadow;
pub use shadow::*;
pub mod replay;
pub use replay::*;
#[cfg(feature = "std")]
pub mod afl_stats;
pub mod calibrate;
pub mod colorization;
#[cfg(all(feature = "std", unix))]
pub mod concolic;
#[cfg(feature = "std")]
pub mod dump;
pub mod dynamic;
pub mod generalization;
pub mod generation;
pub mod logics;
pub mod nop;
pub mod power;
#[cfg(feature = "std")]
pub mod sync;
#[cfg(feature = "std")]
pub mod time_tracker;
pub mod tracing;
pub mod tuneable;
#[cfg(feature = "unicode")]
pub mod unicode;
#[cfg(feature = "std")]
pub mod verify_timeouts;
pub trait Stage<E, EM, S, Z> {
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error>;
}
pub trait Restartable<S> {
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error>;
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error>;
}
pub trait StagesTuple<E, EM, S, Z> {
fn perform_all(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error>;
}
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for ()
where
S: HasCurrentStageId,
{
fn perform_all(
&mut self,
_: &mut Z,
_: &mut E,
stage: &mut S,
_: &mut EM,
) -> Result<(), Error> {
if stage.current_stage_id()?.is_some() {
Err(Error::illegal_state(
"Got to the end of the tuple without completing resume.",
))
} else {
Ok(())
}
}
}
impl<Head, Tail, E, EM, S, Z> StagesTuple<E, EM, S, Z> for (Head, Tail)
where
Head: Stage<E, EM, S, Z> + Restartable<S>,
Tail: StagesTuple<E, EM, S, Z> + HasConstLen,
S: HasCurrentStageId + Stoppable + MaybeHasClientPerfMonitor,
EM: SendExiting,
{
fn perform_all(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
match state.current_stage_id()? {
Some(idx) if idx < StageId(Self::LEN) => {
}
Some(idx) if idx == StageId(Self::LEN) => {
let stage = &mut self.0;
stage.perform_restartable(fuzzer, executor, state, manager)?;
state.clear_stage_id()?;
}
Some(idx) if idx > StageId(Self::LEN) => {
unreachable!("We should clear the stage index before we get here...");
}
_ => {
state.set_current_stage_id(StageId(Self::LEN))?;
let stage = &mut self.0;
stage.perform_restartable(fuzzer, executor, state, manager)?;
state.clear_stage_id()?;
}
}
#[cfg(feature = "introspection")]
state.introspection_stats_mut().finish_stage();
if state.stop_requested() {
state.discard_stop_request();
manager.on_shutdown()?;
return Err(Error::shutting_down());
}
self.1.perform_all(fuzzer, executor, state, manager)
}
}
impl<Head, Tail, E, EM, S, Z> IntoVec<Box<dyn Stage<E, EM, S, Z>>> for (Head, Tail)
where
Head: Stage<E, EM, S, Z> + 'static,
Tail: StagesTuple<E, EM, S, Z> + HasConstLen + IntoVec<Box<dyn Stage<E, EM, S, Z>>>,
S: HasCurrentStageId,
{
fn into_vec_reversed(self) -> Vec<Box<dyn Stage<E, EM, S, Z>>> {
let (head, tail) = self.uncons();
let mut ret = tail.0.into_vec_reversed();
ret.push(Box::new(head));
ret
}
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, S, Z>>> {
let mut ret = self.into_vec_reversed();
ret.reverse();
ret
}
}
impl<Tail, E, EM, S, Z> IntoVec<Box<dyn Stage<E, EM, S, Z>>> for (Tail,)
where
Tail: IntoVec<Box<dyn Stage<E, EM, S, Z>>>,
{
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, S, Z>>> {
self.0.into_vec()
}
}
impl<E, EM, S, Z> IntoVec<Box<dyn Stage<E, EM, S, Z>>> for Vec<Box<dyn Stage<E, EM, S, Z>>> {
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, S, Z>>> {
self
}
}
trait RestartableStage<E, EM, S, Z>: Stage<E, EM, S, Z> + Restartable<S> {
fn perform_restartable(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error>;
}
impl<E, EM, S, ST, Z> RestartableStage<E, EM, S, Z> for ST
where
ST: Stage<E, EM, S, Z> + Restartable<S>,
{
fn perform_restartable(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
if self.should_restart(state)? {
self.perform(fuzzer, executor, state, manager)?;
}
self.clear_progress(state)
}
}
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for Vec<Box<dyn RestartableStage<E, EM, S, Z>>>
where
EM: SendExiting,
S: HasCurrentStageId + Stoppable,
{
fn perform_all(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
self.iter_mut().try_for_each(|stage| {
if state.stop_requested() {
state.discard_stop_request();
manager.on_shutdown()?;
return Err(Error::shutting_down());
}
stage.perform_restartable(fuzzer, executor, state, manager)
})
}
}
static mut CLOSURE_STAGE_ID: usize = 0;
pub static CLOSURE_STAGE_NAME: &str = "closure";
#[derive(Debug)]
pub struct ClosureStage<CB, E, EM, Z> {
name: Cow<'static, str>,
closure: CB,
phantom: PhantomData<(E, EM, Z)>,
}
impl<CB, E, EM, Z> Named for ClosureStage<CB, E, EM, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<CB, E, EM, S, Z> Stage<E, EM, S, Z> for ClosureStage<CB, E, EM, Z>
where
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result<(), Error>,
S: HasNamedMetadata + HasCurrentCorpusId,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
(self.closure)(fuzzer, executor, state, manager)
}
}
impl<CB, E, EM, S, Z> Restartable<S> for ClosureStage<CB, E, EM, Z>
where
S: HasNamedMetadata + HasCurrentCorpusId,
{
#[inline]
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
RetryCountRestartHelper::no_retry(state, &self.name)
}
#[inline]
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<CB, E, EM, Z> ClosureStage<CB, E, EM, Z> {
#[must_use]
pub fn new(closure: CB) -> Self {
let stage_id = unsafe {
let ret = CLOSURE_STAGE_ID;
CLOSURE_STAGE_ID += 1;
ret
};
Self {
name: Cow::Owned(CLOSURE_STAGE_NAME.to_owned() + ":" + stage_id.to_string().as_ref()),
closure,
phantom: PhantomData,
}
}
}
#[derive(Clone, Deserialize, Serialize, Debug)]
pub struct RetryCountRestartHelper {
tries_remaining: Option<usize>,
skipped: HashSet<CorpusId>,
}
impl_serdeany!(RetryCountRestartHelper);
impl RetryCountRestartHelper {
pub fn no_retry<S>(state: &mut S, name: &str) -> Result<bool, Error>
where
S: HasNamedMetadata + HasCurrentCorpusId,
{
Self::should_restart(state, name, 1)
}
pub fn should_restart<S>(state: &mut S, name: &str, max_retries: usize) -> Result<bool, Error>
where
S: HasNamedMetadata + HasCurrentCorpusId,
{
let corpus_id = state.current_corpus_id()?.ok_or_else(|| {
Error::illegal_state(
"No current_corpus_id set in State, but called RetryCountRestartHelper::should_skip",
)
})?;
let initial_tries_remaining = max_retries + 1;
let metadata = state.named_metadata_or_insert_with(name, || Self {
tries_remaining: Some(initial_tries_remaining),
skipped: HashSet::new(),
});
let tries_remaining = metadata
.tries_remaining
.unwrap_or(initial_tries_remaining)
.checked_sub(1)
.ok_or_else(|| {
Error::illegal_state(
"Attempted further retries after we had already gotten to none remaining.",
)
})?;
metadata.tries_remaining = Some(tries_remaining);
Ok(if tries_remaining == 0 {
metadata.skipped.insert(corpus_id);
false
} else if metadata.skipped.contains(&corpus_id) {
false
} else {
true
})
}
pub fn clear_progress<S>(state: &mut S, name: &str) -> Result<(), Error>
where
S: HasNamedMetadata,
{
state.named_metadata_mut::<Self>(name)?.tries_remaining = None;
Ok(())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[repr(transparent)]
pub struct StageId(pub(crate) usize);
impl fmt::Display for StageId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl_serdeany!(ExecutionCountRestartHelperMetadata);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionCountRestartHelperMetadata {
started_at_execs: u64,
}
#[derive(Debug, Default, Clone)]
pub struct ExecutionCountRestartHelper {
started_at_execs: Option<u64>,
}
impl ExecutionCountRestartHelper {
#[must_use]
pub fn new() -> Self {
Self {
started_at_execs: None,
}
}
pub fn execs_since_progress_start<S>(&mut self, state: &mut S, name: &str) -> Result<u64, Error>
where
S: HasNamedMetadata + HasExecutions,
{
let started_at_execs = if let Some(started_at_execs) = self.started_at_execs {
started_at_execs
} else {
state
.named_metadata::<ExecutionCountRestartHelperMetadata>(name)
.map(|x| {
self.started_at_execs = Some(x.started_at_execs);
x.started_at_execs
})
.map_err(|err| {
Error::illegal_state(format!(
"The ExecutionCountRestartHelperMetadata should have been set at this point - {err}"
))
})?
};
Ok(state.executions() - started_at_execs)
}
pub fn should_restart<S>(&mut self, state: &mut S, name: &str) -> Result<bool, Error>
where
S: HasNamedMetadata + HasExecutions,
{
let executions = *state.executions();
let metadata =
state.named_metadata_or_insert_with(name, || ExecutionCountRestartHelperMetadata {
started_at_execs: executions,
});
self.started_at_execs = Some(metadata.started_at_execs);
Ok(true)
}
pub fn clear_progress<S>(&mut self, state: &mut S, name: &str) -> Result<(), Error>
where
S: HasNamedMetadata,
{
self.started_at_execs = None;
let _metadata = state.remove_named_metadata::<ExecutionCountRestartHelperMetadata>(name);
debug_assert!(
_metadata.is_some(),
"Called clear_progress, but should_restart was not called before (or did mutational stages get nested?)"
);
Ok(())
}
}
#[cfg(test)]
mod test {
use alloc::borrow::Cow;
use libafl_bolts::{Error, Named};
use crate::{
corpus::{Corpus, HasCurrentCorpusId, Testcase},
inputs::NopInput,
stages::RetryCountRestartHelper,
state::{HasCorpus, StdState},
};
#[test]
fn test_tries_progress() -> Result<(), Error> {
struct StageWithOneTry;
impl Named for StageWithOneTry {
fn name(&self) -> &Cow<'static, str> {
static NAME: Cow<'static, str> = Cow::Borrowed("TestStage");
&NAME
}
}
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
unsafe {
RetryCountRestartHelper::register();
}
let mut state = StdState::nop()?;
let stage = StageWithOneTry;
let corpus_id = state.corpus_mut().add(Testcase::new(NopInput {}))?;
state.set_corpus_id(corpus_id)?;
for _ in 0..10 {
assert!(RetryCountRestartHelper::should_restart(
&mut state,
stage.name(),
1
)?);
RetryCountRestartHelper::clear_progress(&mut state, stage.name())?;
}
for _ in 0..10 {
assert!(RetryCountRestartHelper::should_restart(
&mut state,
stage.name(),
2
)?);
assert!(RetryCountRestartHelper::should_restart(
&mut state,
stage.name(),
2
)?);
RetryCountRestartHelper::clear_progress(&mut state, stage.name())?;
}
assert!(RetryCountRestartHelper::should_restart(
&mut state,
stage.name(),
2
)?);
assert!(RetryCountRestartHelper::should_restart(
&mut state,
stage.name(),
2
)?);
assert!(!RetryCountRestartHelper::should_restart(
&mut state,
stage.name(),
2
)?);
RetryCountRestartHelper::clear_progress(&mut state, stage.name())?;
assert!(!RetryCountRestartHelper::should_restart(
&mut state,
stage.name(),
2
)?);
RetryCountRestartHelper::clear_progress(&mut state, stage.name())?;
Ok(())
}
}