Resumable stages redux (#1780)

* initial stage refactor

* repair test

* redqueen

* fixup python bindings

* docs and clippy fixes

* misc hidden changes

* Status => Progress

* fix remaining fuzzers

* fix libafl_libfuzzer

* implement resume for nested stages

* deep testing for stage resumeability

* refactor: use let-else, docfix

* refactor in fuzzbench_forkserver_cmplog

* fmt

* actually use progress

* HasStageStatus => HasCurrentStage
This commit is contained in:
Addison Crump 2024-01-09 15:18:14 +01:00 committed by GitHub
parent 3d126f21cc
commit ba8ca6723b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1287 additions and 527 deletions

View File

@ -137,13 +137,9 @@ pub fn main() -> Result<(), Error> {
let mut executor = InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut mgr)?; let mut executor = InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut mgr)?;
state.load_initial_inputs_forced(&mut fuzzer, &mut executor, &mut mgr, &[solution_dir])?; state.load_initial_inputs_forced(&mut fuzzer, &mut executor, &mut mgr, &[solution_dir])?;
stages.perform_all(
&mut fuzzer, state.set_corpus_idx(CorpusId::from(0usize))?;
&mut executor, stages.perform_all(&mut fuzzer, &mut executor, &mut state, &mut mgr)?;
&mut state,
&mut mgr,
CorpusId::from(0_usize),
)?;
Ok(()) Ok(())
} }

View File

@ -9,7 +9,7 @@ use std::{
use clap::{Arg, ArgAction, Command}; use clap::{Arg, ArgAction, Command};
use libafl::{ use libafl::{
corpus::{Corpus, CorpusId, InMemoryOnDiskCorpus, OnDiskCorpus}, corpus::{Corpus, HasCurrentCorpusIdx, InMemoryOnDiskCorpus, OnDiskCorpus},
events::SimpleEventManager, events::SimpleEventManager,
executors::forkserver::{ForkserverExecutor, TimeoutForkserverExecutor}, executors::forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
feedback_or, feedback_or,
@ -374,9 +374,14 @@ fn fuzz(
let cb = |_fuzzer: &mut _, let cb = |_fuzzer: &mut _,
_executor: &mut _, _executor: &mut _,
state: &mut StdState<_, InMemoryOnDiskCorpus<_>, _, _>, state: &mut StdState<_, InMemoryOnDiskCorpus<_>, _, _>,
_event_manager: &mut _, _event_manager: &mut _|
corpus_id: CorpusId| -> Result<bool, Error> {
-> Result<bool, libafl::Error> { let Some(corpus_id) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
let corpus = state.corpus().get(corpus_id)?.borrow(); let corpus = state.corpus().get(corpus_id)?.borrow();
let res = corpus.scheduled_count() == 1; // let's try on the 2nd trial let res = corpus.scheduled_count() == 1; // let's try on the 2nd trial

View File

@ -148,6 +148,18 @@ pub trait Corpus: UsesInput + Serialize + for<'de> Deserialize<'de> {
} }
} }
/// Trait for types which track the current corpus index
pub trait HasCurrentCorpusIdx {
/// Set the current corpus index; we have started processing this corpus entry
fn set_corpus_idx(&mut self, idx: CorpusId) -> Result<(), Error>;
/// Clear the current corpus index; we are done with this entry
fn clear_corpus_idx(&mut self) -> Result<(), Error>;
/// Fetch the current corpus index -- typically used after a state recovery or transfer
fn current_corpus_idx(&self) -> Result<Option<CorpusId>, Error>;
}
/// [`Iterator`] over the ids of a [`Corpus`] /// [`Iterator`] over the ids of a [`Corpus`]
#[derive(Debug)] #[derive(Debug)]
pub struct CorpusIdIterator<'a, C> pub struct CorpusIdIterator<'a, C>

View File

@ -962,6 +962,8 @@ where
/// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner. /// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner.
fn on_restart(&mut self, state: &mut S) -> Result<(), Error> { fn on_restart(&mut self, state: &mut S) -> Result<(), Error> {
state.on_restart()?;
// First, reset the page to 0 so the next iteration can read read from the beginning of this page // First, reset the page to 0 so the next iteration can read read from the beginning of this page
self.staterestorer.reset(); self.staterestorer.reset();
self.staterestorer.save(&( self.staterestorer.save(&(

View File

@ -128,7 +128,7 @@ pub struct EventManagerId(
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
use crate::monitors::ClientPerfMonitor; use crate::monitors::ClientPerfMonitor;
use crate::{inputs::UsesInput, state::UsesState}; use crate::{inputs::UsesInput, stages::HasCurrentStage, state::UsesState};
/// The log event severity /// The log event severity
#[derive(Serialize, Deserialize, Debug, Clone, Copy)] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
@ -548,8 +548,11 @@ where
/// Restartable trait /// Restartable trait
pub trait EventRestarter: UsesState { pub trait EventRestarter: UsesState {
/// For restarting event managers, implement a way to forward state to their next peers. /// For restarting event managers, implement a way to forward state to their next peers.
/// You *must* ensure that [`HasCurrentStage::on_restart`] will be invoked in this method, by you
/// or an internal [`EventRestarter`], before the state is saved for recovery.
#[inline] #[inline]
fn on_restart(&mut self, _state: &mut Self::State) -> Result<(), Error> { fn on_restart(&mut self, state: &mut Self::State) -> Result<(), Error> {
state.on_restart()?;
self.await_restart_safe(); self.await_restart_safe();
Ok(()) Ok(())
} }
@ -605,7 +608,7 @@ pub trait HasCustomBufHandlers: UsesState {
} }
/// An eventmgr for tests, and as placeholder if you really don't need an event manager. /// An eventmgr for tests, and as placeholder if you really don't need an event manager.
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug)]
pub struct NopEventManager<S> { pub struct NopEventManager<S> {
phantom: PhantomData<S>, phantom: PhantomData<S>,
} }
@ -620,6 +623,12 @@ impl<S> NopEventManager<S> {
} }
} }
impl<S> Default for NopEventManager<S> {
fn default() -> Self {
Self::new()
}
}
impl<S> UsesState for NopEventManager<S> impl<S> UsesState for NopEventManager<S>
where where
S: State, S: State,

View File

@ -353,6 +353,8 @@ where
{ {
/// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner. /// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner.
fn on_restart(&mut self, state: &mut S) -> Result<(), Error> { fn on_restart(&mut self, state: &mut S) -> Result<(), Error> {
state.on_restart()?;
// First, reset the page to 0 so the next iteration can read read from the beginning of this page // First, reset the page to 0 so the next iteration can read read from the beginning of this page
self.staterestorer.reset(); self.staterestorer.reset();
self.staterestorer.save(&( self.staterestorer.save(&(

View File

@ -849,6 +849,8 @@ where
/// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner. /// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner.
fn on_restart(&mut self, state: &mut S) -> Result<(), Error> { fn on_restart(&mut self, state: &mut S) -> Result<(), Error> {
state.on_restart()?;
// First, reset the page to 0 so the next iteration can read read from the beginning of this page // First, reset the page to 0 so the next iteration can read read from the beginning of this page
self.staterestorer.reset(); self.staterestorer.reset();
self.staterestorer.save(&if self.save_state { self.staterestorer.save(&if self.save_state {
@ -856,6 +858,7 @@ where
} else { } else {
None None
})?; })?;
self.await_restart_safe(); self.await_restart_safe();
Ok(()) Ok(())
} }

View File

@ -690,10 +690,10 @@ mod tests {
command::{CommandExecutor, InputLocation}, command::{CommandExecutor, InputLocation},
Executor, Executor,
}, },
fuzzer::test::NopFuzzer,
inputs::BytesInput, inputs::BytesInput,
monitors::SimpleMonitor, monitors::SimpleMonitor,
state::NopState, state::test::NopState,
NopFuzzer,
}; };
#[test] #[test]

View File

@ -2186,9 +2186,9 @@ mod tests {
use crate::{ use crate::{
events::NopEventManager, events::NopEventManager,
executors::{inprocess::InProcessHandlers, Executor, ExitKind, InProcessExecutor}, executors::{inprocess::InProcessHandlers, Executor, ExitKind, InProcessExecutor},
fuzzer::test::NopFuzzer,
inputs::{NopInput, UsesInput}, inputs::{NopInput, UsesInput},
state::NopState, state::test::NopState,
NopFuzzer,
}; };
impl UsesInput for () { impl UsesInput for () {
@ -2226,8 +2226,8 @@ mod tests {
use crate::{ use crate::{
events::SimpleEventManager, events::SimpleEventManager,
executors::{inprocess::InChildProcessHandlers, InProcessForkExecutor}, executors::{inprocess::InChildProcessHandlers, InProcessForkExecutor},
state::NopState, fuzzer::test::NopFuzzer,
NopFuzzer, state::test::NopState,
}; };
let provider = StdShMemProvider::new().unwrap(); let provider = StdShMemProvider::new().unwrap();

View File

@ -1,49 +1,41 @@
//! Executors take input, and run it in the target. //! Executors take input, and run it in the target.
pub mod inprocess; use core::fmt::Debug;
pub use combined::CombinedExecutor;
#[cfg(all(feature = "std", any(unix, doc)))]
pub use command::CommandExecutor;
pub use differential::DiffExecutor;
#[cfg(all(feature = "std", feature = "fork", unix))]
pub use forkserver::{Forkserver, ForkserverExecutor, TimeoutForkserverExecutor};
pub use inprocess::InProcessExecutor; pub use inprocess::InProcessExecutor;
#[cfg(all(feature = "std", feature = "fork", unix))] #[cfg(all(feature = "std", feature = "fork", unix))]
pub use inprocess::InProcessForkExecutor; pub use inprocess::InProcessForkExecutor;
use serde::{Deserialize, Serialize};
pub use shadow::ShadowExecutor;
#[cfg(any(unix, feature = "std"))]
pub use timeout::TimeoutExecutor;
pub use with_observers::WithObservers;
use crate::{
observers::{ObserversTuple, UsesObservers},
state::UsesState,
Error,
};
pub mod combined;
#[cfg(all(feature = "std", any(unix, doc)))]
pub mod command;
pub mod differential; pub mod differential;
pub use differential::DiffExecutor; #[cfg(all(feature = "std", feature = "fork", unix))]
pub mod forkserver;
pub mod inprocess;
pub mod shadow;
/// Timeout executor. /// Timeout executor.
/// Not possible on `no-std` Windows or `no-std`, but works for unix /// Not possible on `no-std` Windows or `no-std`, but works for unix
#[cfg(any(unix, feature = "std"))] #[cfg(any(unix, feature = "std"))]
pub mod timeout; pub mod timeout;
#[cfg(any(unix, feature = "std"))]
pub use timeout::TimeoutExecutor;
#[cfg(all(feature = "std", feature = "fork", unix))]
pub mod forkserver;
#[cfg(all(feature = "std", feature = "fork", unix))]
pub use forkserver::{Forkserver, ForkserverExecutor, TimeoutForkserverExecutor};
pub mod combined;
pub use combined::CombinedExecutor;
pub mod shadow;
pub use shadow::ShadowExecutor;
pub mod with_observers; pub mod with_observers;
pub use with_observers::WithObservers;
#[cfg(all(feature = "std", any(unix, doc)))]
pub mod command;
use core::{fmt::Debug, marker::PhantomData};
#[cfg(all(feature = "std", any(unix, doc)))]
pub use command::CommandExecutor;
use libafl_bolts::AsSlice;
use serde::{Deserialize, Serialize};
use crate::{
inputs::HasTargetBytes,
observers::{ObserversTuple, UsesObservers},
state::{HasExecutions, State, UsesState},
Error,
};
/// How an execution finished. /// How an execution finished.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
@ -149,58 +141,78 @@ where
fn post_run_reset(&mut self) {} fn post_run_reset(&mut self) {}
} }
/// A simple executor that does nothing.
/// If intput len is 0, `run_target` will return Err
#[derive(Debug)]
struct NopExecutor<S> {
phantom: PhantomData<S>,
}
impl<S> UsesState for NopExecutor<S>
where
S: State,
{
type State = S;
}
impl<EM, S, Z> Executor<EM, Z> for NopExecutor<S>
where
EM: UsesState<State = S>,
S: State + HasExecutions,
S::Input: HasTargetBytes,
Z: UsesState<State = S>,
{
fn run_target(
&mut self,
_fuzzer: &mut Z,
state: &mut Self::State,
_mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
*state.executions_mut() += 1;
if input.target_bytes().as_slice().is_empty() {
Err(Error::empty("Input Empty"))
} else {
Ok(ExitKind::Ok)
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { pub mod test {
use core::marker::PhantomData; use core::marker::PhantomData;
use super::{Executor, NopExecutor}; use libafl_bolts::{AsSlice, Error};
use crate::{events::NopEventManager, inputs::BytesInput, state::NopState, NopFuzzer};
use crate::{
events::NopEventManager,
executors::{Executor, ExitKind},
fuzzer::test::NopFuzzer,
inputs::{BytesInput, HasTargetBytes},
state::{test::NopState, HasExecutions, State, UsesState},
};
/// A simple executor that does nothing.
/// If intput len is 0, `run_target` will return Err
#[derive(Debug)]
pub struct NopExecutor<S> {
phantom: PhantomData<S>,
}
impl<S> NopExecutor<S> {
#[must_use]
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
}
}
impl<S> Default for NopExecutor<S> {
fn default() -> Self {
Self::new()
}
}
impl<S> UsesState for NopExecutor<S>
where
S: State,
{
type State = S;
}
impl<EM, S, Z> Executor<EM, Z> for NopExecutor<S>
where
EM: UsesState<State = S>,
S: State + HasExecutions,
S::Input: HasTargetBytes,
Z: UsesState<State = S>,
{
fn run_target(
&mut self,
_fuzzer: &mut Z,
state: &mut Self::State,
_mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
*state.executions_mut() += 1;
if input.target_bytes().as_slice().is_empty() {
Err(Error::empty("Input Empty"))
} else {
Ok(ExitKind::Ok)
}
}
}
#[test] #[test]
fn nop_executor() { fn nop_executor() {
let empty_input = BytesInput::new(vec![]); let empty_input = BytesInput::new(vec![]);
let nonempty_input = BytesInput::new(vec![1u8]); let nonempty_input = BytesInput::new(vec![1u8]);
let mut executor = NopExecutor { let mut executor = NopExecutor::new();
phantom: PhantomData,
};
let mut fuzzer = NopFuzzer::new(); let mut fuzzer = NopFuzzer::new();
let mut state = NopState::new(); let mut state = NopState::new();

View File

@ -163,7 +163,7 @@ mod tests {
feedbacks::{differential::DiffResult, DiffFeedback, Feedback}, feedbacks::{differential::DiffResult, DiffFeedback, Feedback},
inputs::{BytesInput, UsesInput}, inputs::{BytesInput, UsesInput},
observers::Observer, observers::Observer,
state::{NopState, State, UsesState}, state::{test::NopState, State, UsesState},
}; };
#[derive(Debug)] #[derive(Debug)]

View File

@ -6,12 +6,8 @@ use core::{fmt::Debug, marker::PhantomData, time::Duration};
use libafl_bolts::current_time; use libafl_bolts::current_time;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
#[cfg(test)]
use crate::inputs::Input;
#[cfg(test)]
use crate::state::NopState;
use crate::{ use crate::{
corpus::{Corpus, CorpusId, HasTestcase, Testcase}, corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, HasTestcase, Testcase},
events::{Event, EventConfig, EventFirer, EventProcessor, ProgressReporter}, events::{Event, EventConfig, EventFirer, EventProcessor, ProgressReporter},
executors::{Executor, ExitKind, HasObservers}, executors::{Executor, ExitKind, HasObservers},
feedbacks::Feedback, feedbacks::Feedback,
@ -19,7 +15,7 @@ use crate::{
mark_feature_time, mark_feature_time,
observers::ObserversTuple, observers::ObserversTuple,
schedulers::Scheduler, schedulers::Scheduler,
stages::StagesTuple, stages::{HasCurrentStage, StagesTuple},
start_timer, start_timer,
state::{ state::{
HasCorpus, HasExecutions, HasImported, HasLastReportTime, HasMetadata, HasSolutions, HasCorpus, HasExecutions, HasImported, HasLastReportTime, HasMetadata, HasSolutions,
@ -583,8 +579,14 @@ where
EM: ProgressReporter + EventProcessor<E, Self, State = CS::State>, EM: ProgressReporter + EventProcessor<E, Self, State = CS::State>,
F: Feedback<CS::State>, F: Feedback<CS::State>,
OF: Feedback<CS::State>, OF: Feedback<CS::State>,
CS::State: CS::State: HasExecutions
HasExecutions + HasMetadata + HasCorpus + HasTestcase + HasImported + HasLastReportTime, + HasMetadata
+ HasCorpus
+ HasTestcase
+ HasImported
+ HasLastReportTime
+ HasCurrentCorpusIdx
+ HasCurrentStage,
ST: StagesTuple<E, EM, CS::State, Self>, ST: StagesTuple<E, EM, CS::State, Self>,
{ {
fn fuzz_one( fn fuzz_one(
@ -599,7 +601,13 @@ where
state.introspection_monitor_mut().start_timer(); state.introspection_monitor_mut().start_timer();
// Get the next index from the scheduler // Get the next index from the scheduler
let idx = self.scheduler.next(state)?; let idx = if let Some(idx) = state.current_corpus_idx()? {
idx // we are resuming
} else {
let idx = self.scheduler.next(state)?;
state.set_corpus_idx(idx)?; // set up for resume
idx
};
// Mark the elapsed time for the scheduler // Mark the elapsed time for the scheduler
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
@ -610,7 +618,7 @@ where
state.introspection_monitor_mut().reset_stage_index(); state.introspection_monitor_mut().reset_stage_index();
// Execute all stages // Execute all stages
stages.perform_all(self, executor, state, manager, idx)?; stages.perform_all(self, executor, state, manager)?;
// Init timer for manager // Init timer for manager
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
@ -629,6 +637,9 @@ where
// increase scheduled count, this was fuzz_level in afl // increase scheduled count, this was fuzz_level in afl
testcase.set_scheduled_count(scheduled_count + 1); testcase.set_scheduled_count(scheduled_count + 1);
} }
state.clear_corpus_idx()?;
Ok(idx) Ok(idx)
} }
} }
@ -733,44 +744,62 @@ where
} }
#[cfg(test)] #[cfg(test)]
#[derive(Clone, Debug, Default)] pub mod test {
pub(crate) struct NopFuzzer<I> { use core::marker::PhantomData;
phantom: PhantomData<I>,
}
#[cfg(test)] use libafl_bolts::Error;
impl<I> NopFuzzer<I> {
pub fn new() -> Self { use crate::{
Self { corpus::CorpusId,
phantom: PhantomData, events::ProgressReporter,
stages::{HasCurrentStage, StagesTuple},
state::{HasExecutions, HasLastReportTime, HasMetadata, State, UsesState},
Fuzzer,
};
#[derive(Clone, Debug)]
pub struct NopFuzzer<S> {
phantom: PhantomData<S>,
}
impl<S> NopFuzzer<S> {
#[must_use]
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
} }
} }
}
#[cfg(test)] impl<S> Default for NopFuzzer<S> {
impl<I> UsesState for NopFuzzer<I> fn default() -> Self {
where Self::new()
I: Input, }
{ }
type State = NopState<I>;
}
#[cfg(test)] impl<S> UsesState for NopFuzzer<S>
impl<ST, E, I, EM> Fuzzer<E, EM, ST> for NopFuzzer<I> where
where S: State,
E: UsesState<State = NopState<I>>, {
EM: ProgressReporter<State = NopState<I>>, type State = S;
I: Input, }
ST: StagesTuple<E, EM, NopState<I>, Self>,
{ impl<ST, E, EM> Fuzzer<E, EM, ST> for NopFuzzer<E::State>
fn fuzz_one( where
&mut self, E: UsesState,
_stages: &mut ST, EM: ProgressReporter<State = E::State>,
_executor: &mut E, ST: StagesTuple<E, EM, E::State, Self>,
_state: &mut EM::State, E::State: HasMetadata + HasExecutions + HasLastReportTime + HasCurrentStage,
_manager: &mut EM, {
) -> Result<CorpusId, Error> { fn fuzz_one(
unimplemented!() &mut self,
_stages: &mut ST,
_executor: &mut E,
_state: &mut EM::State,
_manager: &mut EM,
) -> Result<CorpusId, Error> {
unimplemented!()
}
} }
} }

View File

@ -392,7 +392,7 @@ where
write!( write!(
f, f,
"StdMOptMutator with {} mutations for Input type {}", "StdMOptMutator with {} mutations for Input type {}",
self.mutations.len(), MT::LEN,
core::any::type_name::<I>() core::any::type_name::<I>()
) )
} }
@ -547,7 +547,7 @@ where
) -> Result<Self, Error> { ) -> Result<Self, Error> {
if !state.has_metadata::<MOpt>() { if !state.has_metadata::<MOpt>() {
let rand_seed = state.rand_mut().next(); let rand_seed = state.rand_mut().next();
state.add_metadata::<MOpt>(MOpt::new(mutations.len(), swarm_num, rand_seed)?); state.add_metadata::<MOpt>(MOpt::new(MT::LEN, swarm_num, rand_seed)?);
} }
Ok(Self { Ok(Self {
name: format!("StdMOptMutator[{}]", mutations.names().join(",")), name: format!("StdMOptMutator[{}]", mutations.names().join(",")),

View File

@ -1396,7 +1396,7 @@ pub fn str_decode(item: &str) -> Result<Vec<u8>, Error> {
mod tests { mod tests {
use libafl_bolts::{ use libafl_bolts::{
rands::StdRand, rands::StdRand,
tuples::{tuple_list, HasConstLen}, tuples::{tuple_list, tuple_list_type, HasConstLen},
}; };
use super::*; use super::*;
@ -1408,11 +1408,34 @@ mod tests {
state::{HasMetadata, StdState}, state::{HasMetadata, StdState},
}; };
fn test_mutations<I, S>() -> impl MutatorsTuple<I, S> type TestMutatorsTupleType = tuple_list_type!(
where BitFlipMutator,
S: HasRand + HasMetadata + HasMaxSize, ByteFlipMutator,
I: HasBytesVec, ByteIncMutator,
{ ByteDecMutator,
ByteNegMutator,
ByteRandMutator,
ByteAddMutator,
WordAddMutator,
DwordAddMutator,
QwordAddMutator,
ByteInterestingMutator,
WordInterestingMutator,
DwordInterestingMutator,
BytesDeleteMutator,
BytesDeleteMutator,
BytesDeleteMutator,
BytesDeleteMutator,
BytesExpandMutator,
BytesInsertMutator,
BytesRandInsertMutator,
BytesSetMutator,
BytesRandSetMutator,
BytesCopyMutator,
BytesSwapMutator,
);
fn test_mutations() -> TestMutatorsTupleType {
tuple_list!( tuple_list!(
BitFlipMutator::new(), BitFlipMutator::new(),
ByteFlipMutator::new(), ByteFlipMutator::new(),
@ -1481,7 +1504,7 @@ mod tests {
for _ in 0..2 { for _ in 0..2 {
let mut new_testcases = vec![]; let mut new_testcases = vec![];
for idx in 0..(mutations.len()) { for idx in 0..TestMutatorsTupleType::LEN {
for input in &inputs { for input in &inputs {
let mut mutant = input.clone(); let mut mutant = input.clone();
match mutations match mutations

View File

@ -135,7 +135,7 @@ where
write!( write!(
f, f,
"StdScheduledMutator with {} mutations for Input type {}", "StdScheduledMutator with {} mutations for Input type {}",
self.mutations.len(), MT::LEN,
core::any::type_name::<I>() core::any::type_name::<I>()
) )
} }
@ -197,8 +197,8 @@ where
/// Get the next mutation to apply /// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &I) -> MutationId { fn schedule(&self, state: &mut S, _: &I) -> MutationId {
debug_assert!(!self.mutations().is_empty()); debug_assert!(MT::LEN != 0);
state.rand_mut().below(self.mutations().len() as u64).into() state.rand_mut().below(MT::LEN as u64).into()
} }
} }
@ -368,7 +368,7 @@ where
write!( write!(
f, f,
"LoggerScheduledMutator with {} mutations for Input type {}", "LoggerScheduledMutator with {} mutations for Input type {}",
self.scheduled.mutations().len(), MT::LEN,
core::any::type_name::<I>() core::any::type_name::<I>()
) )
} }
@ -452,11 +452,8 @@ where
/// Get the next mutation to apply /// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &I) -> MutationId { fn schedule(&self, state: &mut S, _: &I) -> MutationId {
debug_assert!(!self.scheduled.mutations().is_empty()); debug_assert!(MT::LEN != 0);
state state.rand_mut().below(MT::LEN as u64).into()
.rand_mut()
.below(self.scheduled.mutations().len() as u64)
.into()
} }
fn scheduled_mutate( fn scheduled_mutate(

View File

@ -100,7 +100,7 @@ where
write!( write!(
f, f,
"TuneableScheduledMutator with {} mutations for Input type {}", "TuneableScheduledMutator with {} mutations for Input type {}",
self.mutations.len(), MT::LEN,
core::any::type_name::<I>() core::any::type_name::<I>()
) )
} }
@ -186,7 +186,7 @@ where
/// Get the next mutation to apply /// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &I) -> MutationId { fn schedule(&self, state: &mut S, _: &I) -> MutationId {
debug_assert!(!self.mutations().is_empty()); debug_assert!(MT::LEN != 0);
// Assumption: we can not reach this code path without previously adding this metadatum. // Assumption: we can not reach this code path without previously adding this metadatum.
let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap(); let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap();
@ -199,7 +199,7 @@ where
metadata.next_id = 0.into(); metadata.next_id = 0.into();
} }
debug_assert!( debug_assert!(
self.mutations().len() > ret.0, MT::LEN > ret.0,
"TuneableScheduler: next vec may not contain id larger than number of mutations!" "TuneableScheduler: next vec may not contain id larger than number of mutations!"
); );
return ret; return ret;
@ -214,7 +214,7 @@ where
let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap(); let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap();
debug_assert_eq!( debug_assert_eq!(
self.mutations.len(), MT::LEN,
metadata.mutation_probabilities_cumulative.len(), metadata.mutation_probabilities_cumulative.len(),
"TuneableScheduler: mutation probabilities do not match with number of mutations" "TuneableScheduler: mutation probabilities do not match with number of mutations"
); );
@ -230,7 +230,7 @@ where
} }
// fall back to random if no entries in either vec, the scheduling is not tuned. // fall back to random if no entries in either vec, the scheduling is not tuned.
state.rand_mut().below(self.mutations().len() as u64).into() state.rand_mut().below(MT::LEN as u64).into()
} }
} }
@ -376,7 +376,7 @@ mod test {
use crate::{ use crate::{
inputs::BytesInput, inputs::BytesInput,
mutators::{ByteRandMutator, ScheduledMutator}, mutators::{ByteRandMutator, ScheduledMutator},
state::NopState, state::test::NopState,
}; };
#[test] #[test]

View File

@ -12,7 +12,7 @@ use num_traits::Bounded;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
corpus::{Corpus, CorpusId, SchedulerTestcaseMetadata}, corpus::{Corpus, HasCurrentCorpusIdx, SchedulerTestcaseMetadata},
events::{Event, EventFirer, LogSeverity}, events::{Event, EventFirer, LogSeverity},
executors::{Executor, ExitKind, HasObservers}, executors::{Executor, ExitKind, HasObservers},
feedbacks::{map::MapFeedbackMetadata, HasObserverName}, feedbacks::{map::MapFeedbackMetadata, HasObserverName},
@ -92,6 +92,8 @@ where
E::State: HasCorpus + HasMetadata + HasNamedMetadata + HasExecutions, E::State: HasCorpus + HasMetadata + HasNamedMetadata + HasExecutions,
Z: Evaluator<E, EM, State = E::State>, Z: Evaluator<E, EM, State = E::State>,
{ {
type Progress = (); // TODO stage may be resumed, but how?
#[inline] #[inline]
#[allow( #[allow(
clippy::let_and_return, clippy::let_and_return,
@ -104,8 +106,13 @@ where
executor: &mut E, executor: &mut E,
state: &mut E::State, state: &mut E::State,
mgr: &mut EM, mgr: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
// Run this stage only once for each corpus entry and only if we haven't already inspected it // Run this stage only once for each corpus entry and only if we haven't already inspected it
{ {
let corpus = state.corpus().get(corpus_idx)?.borrow(); let corpus = state.corpus().get(corpus_idx)?.borrow();

View File

@ -10,7 +10,7 @@ use libafl_bolts::{rands::Rand, tuples::MatchName};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
corpus::{Corpus, CorpusId}, corpus::{Corpus, HasCurrentCorpusIdx},
events::EventFirer, events::EventFirer,
executors::{Executor, HasObservers}, executors::{Executor, HasObservers},
inputs::HasBytesVec, inputs::HasBytesVec,
@ -77,6 +77,8 @@ where
O: MapObserver, O: MapObserver,
Z: UsesState<State = E::State>, Z: UsesState<State = E::State>,
{ {
type Progress = (); // TODO this stage needs resume
#[inline] #[inline]
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
fn perform( fn perform(
@ -85,17 +87,9 @@ where
executor: &mut E, // don't need the *main* executor for tracing executor: &mut E, // don't need the *main* executor for tracing
state: &mut E::State, state: &mut E::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
// Run with the mutated input // Run with the mutated input
Self::colorize( Self::colorize(fuzzer, executor, state, manager, &self.map_observer_name)?;
fuzzer,
executor,
state,
manager,
corpus_idx,
&self.map_observer_name,
)?;
Ok(()) Ok(())
} }
@ -156,9 +150,14 @@ where
executor: &mut E, executor: &mut E,
state: &mut E::State, state: &mut E::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
name: &str, name: &str,
) -> Result<E::Input, Error> { ) -> Result<E::Input, Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
let mut input = state.corpus().cloned_input_for_id(corpus_idx)?; let mut input = state.corpus().cloned_input_for_id(corpus_idx)?;
// The backup of the input // The backup of the input
let backup = input.clone(); let backup = input.clone();

View File

@ -13,7 +13,7 @@ use crate::state::HasClientPerfMonitor;
#[cfg(feature = "concolic_mutation")] #[cfg(feature = "concolic_mutation")]
use crate::state::State; use crate::state::State;
use crate::{ use crate::{
corpus::{Corpus, CorpusId}, corpus::Corpus,
executors::{Executor, HasObservers}, executors::{Executor, HasObservers},
observers::concolic::ConcolicObserver, observers::concolic::ConcolicObserver,
state::{HasCorpus, HasExecutions, HasMetadata}, state::{HasCorpus, HasExecutions, HasMetadata},
@ -42,6 +42,8 @@ where
TE::State: HasExecutions + HasCorpus, TE::State: HasExecutions + HasCorpus,
Z: UsesState<State = TE::State>, Z: UsesState<State = TE::State>,
{ {
type Progress = (); // stage cannot be resumed
#[inline] #[inline]
fn perform( fn perform(
&mut self, &mut self,
@ -49,10 +51,14 @@ where
executor: &mut E, executor: &mut E,
state: &mut TE::State, state: &mut TE::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.inner let Some(corpus_idx) = state.current_corpus_idx()? else {
.perform(fuzzer, executor, state, manager, corpus_idx)?; return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
self.inner.perform(fuzzer, executor, state, manager)?;
if let Some(observer) = self if let Some(observer) = self
.inner .inner
.executor() .executor()
@ -86,7 +92,7 @@ use libafl_bolts::tuples::MatchName;
#[cfg(all(feature = "concolic_mutation", feature = "introspection"))] #[cfg(all(feature = "concolic_mutation", feature = "introspection"))]
use crate::monitors::PerfFeature; use crate::monitors::PerfFeature;
use crate::state::UsesState; use crate::{corpus::HasCurrentCorpusIdx, state::UsesState};
#[cfg(feature = "concolic_mutation")] #[cfg(feature = "concolic_mutation")]
use crate::{ use crate::{
inputs::HasBytesVec, inputs::HasBytesVec,
@ -360,6 +366,8 @@ where
Z::Input: HasBytesVec, Z::Input: HasBytesVec,
Z::State: State + HasExecutions + HasCorpus, Z::State: State + HasExecutions + HasCorpus,
{ {
type Progress = (); // TODO we need a resume for this type
#[inline] #[inline]
fn perform( fn perform(
&mut self, &mut self,
@ -367,8 +375,13 @@ where
executor: &mut E, executor: &mut E,
state: &mut Z::State, state: &mut Z::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
start_timer!(state); start_timer!(state);
let testcase = state.corpus().get(corpus_idx)?.clone(); let testcase = state.corpus().get(corpus_idx)?.clone();
mark_feature_time!(state, PerfFeature::GetInputFromCorpus); mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
@ -391,8 +404,7 @@ where
input_copy.bytes_mut()[index] = new_byte; input_copy.bytes_mut()[index] = new_byte;
} }
// Time is measured directly the `evaluate_input` function // Time is measured directly the `evaluate_input` function
let _: (crate::ExecuteInputResult, Option<CorpusId>) = fuzzer.evaluate_input(state, executor, manager, input_copy)?;
fuzzer.evaluate_input(state, executor, manager, input_copy)?;
} }
} }
Ok(()) Ok(())

View File

@ -52,6 +52,8 @@ where
Z: UsesState, Z: UsesState,
Z::State: HasCorpus + HasSolutions + HasRand + HasMetadata, Z::State: HasCorpus + HasSolutions + HasRand + HasMetadata,
{ {
type Progress = (); // if this fails, we have bigger problems
#[inline] #[inline]
fn perform( fn perform(
&mut self, &mut self,
@ -59,7 +61,6 @@ where
_executor: &mut E, _executor: &mut E,
state: &mut Z::State, state: &mut Z::State,
_manager: &mut EM, _manager: &mut EM,
_corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let (mut corpus_idx, mut solutions_idx) = let (mut corpus_idx, mut solutions_idx) =
if let Some(meta) = state.metadata_map().get::<DumpToDiskMetadata>() { if let Some(meta) = state.metadata_map().get::<DumpToDiskMetadata>() {

View File

@ -9,7 +9,7 @@ use core::{fmt::Debug, marker::PhantomData};
use libafl_bolts::AsSlice; use libafl_bolts::AsSlice;
use crate::{ use crate::{
corpus::{Corpus, CorpusId}, corpus::{Corpus, HasCurrentCorpusIdx},
executors::{Executor, HasObservers}, executors::{Executor, HasObservers},
feedbacks::map::MapNoveltiesMetadata, feedbacks::map::MapNoveltiesMetadata,
inputs::{BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasBytesVec, UsesInput}, inputs::{BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasBytesVec, UsesInput},
@ -64,6 +64,8 @@ where
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>, Z: UsesState<State = E::State>,
{ {
type Progress = (); // TODO this stage needs a resume
#[inline] #[inline]
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn perform( fn perform(
@ -72,8 +74,13 @@ where
executor: &mut E, executor: &mut E,
state: &mut E::State, state: &mut E::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
let (mut payload, original, novelties) = { let (mut payload, original, novelties) = {
start_timer!(state); start_timer!(state);
{ {

View File

@ -3,17 +3,43 @@
use core::marker::PhantomData; use core::marker::PhantomData;
use crate::{ use crate::{
corpus::CorpusId, stages::{HasCurrentStage, HasNestedStageStatus, Stage, StageProgress, StagesTuple},
stages::{Stage, StagesTuple},
state::UsesState, state::UsesState,
Error, Error,
}; };
/// Progress for nested stages. This merely enters/exits the inner stage's scope.
#[derive(Debug)]
pub struct NestedStageProgress;
impl<S> StageProgress<S> for NestedStageProgress
where
S: HasNestedStageStatus,
{
fn initialize_progress(state: &mut S) -> Result<(), Error> {
state.enter_inner_stage()?;
Ok(())
}
fn clear_progress(state: &mut S) -> Result<(), Error> {
state.exit_inner_stage()?;
Ok(())
}
fn progress(_state: &S) -> Result<&Self, Error> {
unimplemented!("NestedStageProgress should not be queried")
}
fn progress_mut(_state: &mut S) -> Result<&mut Self, Error> {
unimplemented!("NestedStageProgress should not be queried")
}
}
#[derive(Debug)] #[derive(Debug)]
/// Perform the stage while the closure evaluates to true /// Perform the stage while the closure evaluates to true
pub struct WhileStage<CB, E, EM, ST, Z> pub struct WhileStage<CB, E, EM, ST, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST: StagesTuple<E, EM, E::State, Z>, ST: StagesTuple<E, EM, E::State, Z>,
@ -26,7 +52,7 @@ where
impl<CB, E, EM, ST, Z> UsesState for WhileStage<CB, E, EM, ST, Z> impl<CB, E, EM, ST, Z> UsesState for WhileStage<CB, E, EM, ST, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST: StagesTuple<E, EM, E::State, Z>, ST: StagesTuple<E, EM, E::State, Z>,
@ -37,31 +63,34 @@ where
impl<CB, E, EM, ST, Z> Stage<E, EM, Z> for WhileStage<CB, E, EM, ST, Z> impl<CB, E, EM, ST, Z> Stage<E, EM, Z> for WhileStage<CB, E, EM, ST, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST: StagesTuple<E, EM, E::State, Z>, ST: StagesTuple<E, EM, E::State, Z>,
Z: UsesState<State = E::State>, Z: UsesState<State = E::State>,
E::State: HasNestedStageStatus,
{ {
type Progress = NestedStageProgress;
fn perform( fn perform(
&mut self, &mut self,
fuzzer: &mut Z, fuzzer: &mut Z,
executor: &mut E, executor: &mut E,
state: &mut E::State, state: &mut E::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
while (self.closure)(fuzzer, executor, state, manager, corpus_idx)? { while state.current_stage()?.is_some() || (self.closure)(fuzzer, executor, state, manager)?
self.stages {
.perform_all(fuzzer, executor, state, manager, corpus_idx)?; self.stages.perform_all(fuzzer, executor, state, manager)?;
} }
Ok(()) Ok(())
} }
} }
impl<CB, E, EM, ST, Z> WhileStage<CB, E, EM, ST, Z> impl<CB, E, EM, ST, Z> WhileStage<CB, E, EM, ST, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST: StagesTuple<E, EM, E::State, Z>, ST: StagesTuple<E, EM, E::State, Z>,
@ -82,7 +111,7 @@ where
#[derive(Debug)] #[derive(Debug)]
pub struct IfStage<CB, E, EM, ST, Z> pub struct IfStage<CB, E, EM, ST, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST: StagesTuple<E, EM, E::State, Z>, ST: StagesTuple<E, EM, E::State, Z>,
@ -95,7 +124,7 @@ where
impl<CB, E, EM, ST, Z> UsesState for IfStage<CB, E, EM, ST, Z> impl<CB, E, EM, ST, Z> UsesState for IfStage<CB, E, EM, ST, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST: StagesTuple<E, EM, E::State, Z>, ST: StagesTuple<E, EM, E::State, Z>,
@ -106,23 +135,25 @@ where
impl<CB, E, EM, ST, Z> Stage<E, EM, Z> for IfStage<CB, E, EM, ST, Z> impl<CB, E, EM, ST, Z> Stage<E, EM, Z> for IfStage<CB, E, EM, ST, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST: StagesTuple<E, EM, E::State, Z>, ST: StagesTuple<E, EM, E::State, Z>,
Z: UsesState<State = E::State>, Z: UsesState<State = E::State>,
E::State: HasNestedStageStatus,
{ {
type Progress = NestedStageProgress;
fn perform( fn perform(
&mut self, &mut self,
fuzzer: &mut Z, fuzzer: &mut Z,
executor: &mut E, executor: &mut E,
state: &mut E::State, state: &mut E::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
if (self.closure)(fuzzer, executor, state, manager, corpus_idx)? { if state.current_stage()?.is_some() || (self.closure)(fuzzer, executor, state, manager)? {
self.if_stages self.if_stages
.perform_all(fuzzer, executor, state, manager, corpus_idx)?; .perform_all(fuzzer, executor, state, manager)?;
} }
Ok(()) Ok(())
} }
@ -130,7 +161,7 @@ where
impl<CB, E, EM, ST, Z> IfStage<CB, E, EM, ST, Z> impl<CB, E, EM, ST, Z> IfStage<CB, E, EM, ST, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST: StagesTuple<E, EM, E::State, Z>, ST: StagesTuple<E, EM, E::State, Z>,
@ -151,7 +182,7 @@ where
#[derive(Debug)] #[derive(Debug)]
pub struct IfElseStage<CB, E, EM, ST1, ST2, Z> pub struct IfElseStage<CB, E, EM, ST1, ST2, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST1: StagesTuple<E, EM, E::State, Z>, ST1: StagesTuple<E, EM, E::State, Z>,
@ -166,7 +197,7 @@ where
impl<CB, E, EM, ST1, ST2, Z> UsesState for IfElseStage<CB, E, EM, ST1, ST2, Z> impl<CB, E, EM, ST1, ST2, Z> UsesState for IfElseStage<CB, E, EM, ST1, ST2, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST1: StagesTuple<E, EM, E::State, Z>, ST1: StagesTuple<E, EM, E::State, Z>,
@ -178,35 +209,54 @@ where
impl<CB, E, EM, ST1, ST2, Z> Stage<E, EM, Z> for IfElseStage<CB, E, EM, ST1, ST2, Z> impl<CB, E, EM, ST1, ST2, Z> Stage<E, EM, Z> for IfElseStage<CB, E, EM, ST1, ST2, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST1: StagesTuple<E, EM, E::State, Z>, ST1: StagesTuple<E, EM, E::State, Z>,
ST2: StagesTuple<E, EM, E::State, Z>, ST2: StagesTuple<E, EM, E::State, Z>,
Z: UsesState<State = E::State>, Z: UsesState<State = E::State>,
E::State: HasNestedStageStatus,
{ {
type Progress = NestedStageProgress;
fn perform( fn perform(
&mut self, &mut self,
fuzzer: &mut Z, fuzzer: &mut Z,
executor: &mut E, executor: &mut E,
state: &mut E::State, state: &mut E::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
if (self.closure)(fuzzer, executor, state, manager, corpus_idx)? { let current = state.current_stage()?;
let fresh = current.is_none();
let closure_return = fresh && (self.closure)(fuzzer, executor, state, manager)?;
if current == Some(0) || closure_return {
if fresh {
state.set_stage(0)?;
}
state.enter_inner_stage()?;
self.if_stages self.if_stages
.perform_all(fuzzer, executor, state, manager, corpus_idx)?; .perform_all(fuzzer, executor, state, manager)?;
} else { } else {
if fresh {
state.set_stage(1)?;
}
state.enter_inner_stage()?;
self.else_stages self.else_stages
.perform_all(fuzzer, executor, state, manager, corpus_idx)?; .perform_all(fuzzer, executor, state, manager)?;
} }
state.exit_inner_stage()?;
state.clear_stage()?;
Ok(()) Ok(())
} }
} }
impl<CB, E, EM, ST1, ST2, Z> IfElseStage<CB, E, EM, ST1, ST2, Z> impl<CB, E, EM, ST1, ST2, Z> IfElseStage<CB, E, EM, ST1, ST2, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<bool, Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<bool, Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST1: StagesTuple<E, EM, E::State, Z>, ST1: StagesTuple<E, EM, E::State, Z>,
@ -253,17 +303,19 @@ where
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
ST: StagesTuple<E, EM, E::State, Z>, ST: StagesTuple<E, EM, E::State, Z>,
Z: UsesState<State = E::State>, Z: UsesState<State = E::State>,
E::State: HasNestedStageStatus,
{ {
type Progress = ();
fn perform( fn perform(
&mut self, &mut self,
fuzzer: &mut Z, fuzzer: &mut Z,
executor: &mut E, executor: &mut E,
state: &mut E::State, state: &mut E::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
if let Some(stages) = &mut self.stages { if let Some(stages) = &mut self.stages {
stages.perform_all(fuzzer, executor, state, manager, corpus_idx) stages.perform_all(fuzzer, executor, state, manager)
} else { } else {
Ok(()) Ok(())
} }
@ -304,3 +356,178 @@ where
} }
} }
} }
#[cfg(test)]
mod test {
use core::{cell::RefCell, marker::PhantomData};
use libafl_bolts::{tuples::tuple_list, Error};
use crate::{
inputs::NopInput,
stages::{
test::{test_resume, test_resume_stages},
ClosureStage, IfElseStage, IfStage, Stage, WhileStage,
},
state::{test::test_std_state, State, UsesState},
};
#[test]
fn check_resumability_while() {
let once = RefCell::new(true);
let (completed, stages) = test_resume_stages();
let whilestage = WhileStage::new(|_, _, _, _| Ok(once.replace(false)), stages);
let resetstage = ClosureStage::new(|_, _, _, _| {
once.replace(true);
Ok(())
});
let mut state = test_std_state::<NopInput>();
test_resume(&completed, &mut state, tuple_list!(whilestage, resetstage));
}
#[test]
fn check_resumability_if() {
let once = RefCell::new(true);
let (completed, stages) = test_resume_stages();
let ifstage = IfStage::new(|_, _, _, _| Ok(once.replace(false)), stages);
let resetstage = ClosureStage::new(|_, _, _, _| {
once.replace(true);
Ok(())
});
let mut state = test_std_state::<NopInput>();
test_resume(&completed, &mut state, tuple_list!(ifstage, resetstage));
}
#[test]
fn check_resumability_if_deep() {
let (completed, stages) = test_resume_stages();
let ifstage = IfStage::new(
|_, _, _, _| Ok(true),
tuple_list!(IfStage::new(
|_, _, _, _| Ok(true),
tuple_list!(IfStage::new(
|_, _, _, _| Ok(true),
tuple_list!(IfStage::new(
|_, _, _, _| Ok(true),
tuple_list!(IfStage::new(|_, _, _, _| Ok(true), stages),),
),),
))
)),
);
let mut state = test_std_state::<NopInput>();
test_resume(&completed, &mut state, tuple_list!(ifstage));
}
#[derive(Debug)]
pub struct PanicStage<S> {
phantom: PhantomData<S>,
}
impl<S> PanicStage<S> {
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
}
}
impl<S> UsesState for PanicStage<S>
where
S: State,
{
type State = S;
}
impl<E, EM, Z> Stage<E, EM, Z> for PanicStage<E::State>
where
E: UsesState,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
type Progress = ();
fn perform(
&mut self,
_fuzzer: &mut Z,
_executor: &mut E,
_state: &mut Self::State,
_manager: &mut EM,
) -> Result<(), Error> {
panic!("Test failed; panic stage should never be executed.");
}
}
#[test]
fn check_resumability_if_else_if() {
let once = RefCell::new(true);
let (completed, stages) = test_resume_stages();
let ifstage = IfElseStage::new(
|_, _, _, _| Ok(once.replace(false)),
stages,
tuple_list!(PanicStage::new()),
);
let resetstage = ClosureStage::new(|_, _, _, _| {
once.replace(true);
Ok(())
});
let mut state = test_std_state::<NopInput>();
test_resume(&completed, &mut state, tuple_list!(ifstage, resetstage));
}
#[test]
fn check_resumability_if_else_else() {
let once = RefCell::new(false);
let (completed, stages) = test_resume_stages();
let ifstage = IfElseStage::new(
|_, _, _, _| Ok(once.replace(true)),
tuple_list!(PanicStage::new()),
stages,
);
let resetstage = ClosureStage::new(|_, _, _, _| {
once.replace(false);
Ok(())
});
let mut state = test_std_state::<NopInput>();
test_resume(&completed, &mut state, tuple_list!(ifstage, resetstage));
}
#[test]
fn check_resumability_if_else_else_deep() {
let (completed, stages) = test_resume_stages();
let ifstage = IfElseStage::new(
|_, _, _, _| Ok(false),
tuple_list!(PanicStage::new()),
tuple_list!(IfElseStage::new(
|_, _, _, _| Ok(false),
tuple_list!(PanicStage::new()),
tuple_list!(IfElseStage::new(
|_, _, _, _| Ok(false),
tuple_list!(PanicStage::new()),
tuple_list!(IfElseStage::new(
|_, _, _, _| Ok(false),
tuple_list!(PanicStage::new()),
tuple_list!(IfElseStage::new(
|_, _, _, _| Ok(false),
tuple_list!(PanicStage::new()),
stages,
)),
)),
)),
)),
);
let mut state = test_std_state::<NopInput>();
test_resume(&completed, &mut state, tuple_list!(ifstage));
}
}

View File

@ -4,72 +4,35 @@ A well-known [`Stage`], for example, is the mutational stage, running multiple [
Other stages may enrich [`crate::corpus::Testcase`]s with metadata. Other stages may enrich [`crate::corpus::Testcase`]s with metadata.
*/ */
/// Mutational stage is the normal fuzzing stage. use core::{convert::From, marker::PhantomData};
pub mod mutational;
pub use mutational::{MutationalStage, StdMutationalStage};
pub mod tmin;
pub use tmin::{
MapEqualityFactory, MapEqualityFeedback, StdTMinMutationalStage, TMinMutationalStage,
};
pub mod push;
pub mod tracing;
pub use tracing::{ShadowTracingStage, TracingStage};
pub mod calibrate;
pub use calibrate::CalibrationStage; pub use calibrate::CalibrationStage;
pub mod power;
pub use power::{PowerMutationalStage, StdPowerMutationalStage};
pub mod generalization;
pub use generalization::GeneralizationStage;
pub mod stats;
pub use stats::AflStatsStage;
pub mod owned;
pub use owned::StagesOwnedList;
pub mod logics;
pub use logics::*;
pub mod tuneable;
pub use tuneable::*;
pub mod colorization;
pub use colorization::*; pub use colorization::*;
#[cfg(feature = "std")]
pub mod concolic;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use concolic::ConcolicTracingStage; pub use concolic::ConcolicTracingStage;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use concolic::SimpleConcolicMutationalStage; pub use concolic::SimpleConcolicMutationalStage;
#[cfg(feature = "unicode")]
pub mod string;
#[cfg(feature = "unicode")]
pub use string::*;
#[cfg(feature = "std")]
pub mod sync;
#[cfg(feature = "std")]
pub use sync::*;
#[cfg(feature = "std")]
pub mod dump;
use core::{convert::From, marker::PhantomData};
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use dump::*; pub use dump::*;
pub use generalization::GeneralizationStage;
use libafl_bolts::tuples::HasConstLen;
pub use logics::*;
pub use mutational::{MutationalStage, StdMutationalStage};
pub use power::{PowerMutationalStage, StdPowerMutationalStage};
pub use stats::AflStatsStage;
#[cfg(feature = "unicode")]
pub use string::*;
#[cfg(feature = "std")]
pub use sync::*;
pub use tmin::{
MapEqualityFactory, MapEqualityFeedback, StdTMinMutationalStage, TMinMutationalStage,
};
pub use tracing::{ShadowTracingStage, TracingStage};
pub use tuneable::*;
use self::push::PushStage; use self::push::PushStage;
use crate::{ use crate::{
corpus::CorpusId, corpus::HasCurrentCorpusIdx,
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter}, events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
executors::{Executor, HasObservers}, executors::{Executor, HasObservers},
inputs::UsesInput, inputs::UsesInput,
@ -79,6 +42,28 @@ use crate::{
Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler, Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler,
}; };
/// Mutational stage is the normal fuzzing stage.
pub mod mutational;
pub mod push;
pub mod tmin;
pub mod calibrate;
pub mod colorization;
#[cfg(feature = "std")]
pub mod concolic;
#[cfg(feature = "std")]
pub mod dump;
pub mod generalization;
pub mod logics;
pub mod power;
pub mod stats;
#[cfg(feature = "unicode")]
pub mod string;
#[cfg(feature = "std")]
pub mod sync;
pub mod tracing;
pub mod tuneable;
/// A stage is one step in the fuzzing process. /// A stage is one step in the fuzzing process.
/// Multiple stages will be scheduled one by one for each input. /// Multiple stages will be scheduled one by one for each input.
pub trait Stage<E, EM, Z>: UsesState pub trait Stage<E, EM, Z>: UsesState
@ -87,6 +72,12 @@ where
EM: UsesState<State = Self::State>, EM: UsesState<State = Self::State>,
Z: UsesState<State = Self::State>, Z: UsesState<State = Self::State>,
{ {
// TODO: default this to () when associated_type_defaults is stable
// TODO: see RFC 2532: https://github.com/rust-lang/rust/issues/29661
// type Status: ResumableStageStatus = ();
/// The resumption data for this stage. Set to () if resuming is not necessary/possible.
type Progress: StageProgress<Self::State>;
/// Run the stage /// Run the stage
fn perform( fn perform(
&mut self, &mut self,
@ -94,7 +85,6 @@ where
executor: &mut E, executor: &mut E,
state: &mut Self::State, state: &mut Self::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error>; ) -> Result<(), Error>;
} }
@ -104,7 +94,7 @@ where
E: UsesState<State = S>, E: UsesState<State = S>,
EM: UsesState<State = S>, EM: UsesState<State = S>,
Z: UsesState<State = S>, Z: UsesState<State = S>,
S: UsesInput, S: UsesInput + HasCurrentStage,
{ {
/// Performs all `Stages` in this tuple /// Performs all `Stages` in this tuple
fn perform_all( fn perform_all(
@ -113,7 +103,6 @@ where
executor: &mut E, executor: &mut E,
state: &mut S, state: &mut S,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error>; ) -> Result<(), Error>;
} }
@ -122,27 +111,33 @@ where
E: UsesState<State = S>, E: UsesState<State = S>,
EM: UsesState<State = S>, EM: UsesState<State = S>,
Z: UsesState<State = S>, Z: UsesState<State = S>,
S: UsesInput, S: UsesInput + HasCurrentStage,
{ {
fn perform_all( fn perform_all(
&mut self, &mut self,
_: &mut Z, _: &mut Z,
_: &mut E, _: &mut E,
_: &mut S, stage: &mut S,
_: &mut EM, _: &mut EM,
_: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
Ok(()) if stage.current_stage()?.is_some() {
Err(Error::illegal_state(
"Got to the end of the tuple without completing resume.",
))
} else {
Ok(())
}
} }
} }
impl<Head, Tail, E, EM, Z> StagesTuple<E, EM, Head::State, Z> for (Head, Tail) impl<Head, Tail, E, EM, Z> StagesTuple<E, EM, Head::State, Z> for (Head, Tail)
where where
Head: Stage<E, EM, Z>, Head: Stage<E, EM, Z>,
Tail: StagesTuple<E, EM, Head::State, Z>, Tail: StagesTuple<E, EM, Head::State, Z> + HasConstLen,
E: UsesState<State = Head::State>, E: UsesState<State = Head::State>,
EM: UsesState<State = Head::State>, EM: UsesState<State = Head::State>,
Z: UsesState<State = Head::State>, Z: UsesState<State = Head::State>,
Head::State: HasCurrentStage,
{ {
fn perform_all( fn perform_all(
&mut self, &mut self,
@ -150,15 +145,33 @@ where
executor: &mut E, executor: &mut E,
state: &mut Head::State, state: &mut Head::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
// Perform the current stage match state.current_stage()? {
self.0 Some(idx) if idx < Self::LEN => {
.perform(fuzzer, executor, state, manager, corpus_idx)?; // do nothing; we are resuming
}
Some(idx) if idx == Self::LEN => {
// perform the stage, but don't set it
Head::Progress::initialize_progress(state)?;
self.0.perform(fuzzer, executor, state, manager)?;
Head::Progress::clear_progress(state)?;
state.clear_stage()?;
}
Some(idx) if idx > Self::LEN => {
unreachable!("We should clear the stage index before we get here...");
}
// this is None, but the match can't deduce that
_ => {
state.set_stage(Self::LEN)?;
Head::Progress::initialize_progress(state)?;
self.0.perform(fuzzer, executor, state, manager)?;
Head::Progress::clear_progress(state)?;
state.clear_stage()?;
}
}
// Execute the remaining stages // Execute the remaining stages
self.1 self.1.perform_all(fuzzer, executor, state, manager)
.perform_all(fuzzer, executor, state, manager, corpus_idx)
} }
} }
@ -166,7 +179,7 @@ where
#[derive(Debug)] #[derive(Debug)]
pub struct ClosureStage<CB, E, EM, Z> pub struct ClosureStage<CB, E, EM, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<(), Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<(), Error>,
E: UsesState, E: UsesState,
{ {
closure: CB, closure: CB,
@ -175,7 +188,7 @@ where
impl<CB, E, EM, Z> UsesState for ClosureStage<CB, E, EM, Z> impl<CB, E, EM, Z> UsesState for ClosureStage<CB, E, EM, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<(), Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<(), Error>,
E: UsesState, E: UsesState,
{ {
type State = E::State; type State = E::State;
@ -183,27 +196,28 @@ where
impl<CB, E, EM, Z> Stage<E, EM, Z> for ClosureStage<CB, E, EM, Z> impl<CB, E, EM, Z> Stage<E, EM, Z> for ClosureStage<CB, E, EM, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<(), Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<(), Error>,
E: UsesState, E: UsesState,
EM: UsesState<State = E::State>, EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>, Z: UsesState<State = E::State>,
{ {
type Progress = ();
fn perform( fn perform(
&mut self, &mut self,
fuzzer: &mut Z, fuzzer: &mut Z,
executor: &mut E, executor: &mut E,
state: &mut E::State, state: &mut E::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
(self.closure)(fuzzer, executor, state, manager, corpus_idx) (self.closure)(fuzzer, executor, state, manager)
} }
} }
/// A stage that takes a closure /// A stage that takes a closure
impl<CB, E, EM, Z> ClosureStage<CB, E, EM, Z> impl<CB, E, EM, Z> ClosureStage<CB, E, EM, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<(), Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<(), Error>,
E: UsesState, E: UsesState,
{ {
/// Create a new [`ClosureStage`] /// Create a new [`ClosureStage`]
@ -218,7 +232,7 @@ where
impl<CB, E, EM, Z> From<CB> for ClosureStage<CB, E, EM, Z> impl<CB, E, EM, Z> From<CB> for ClosureStage<CB, E, EM, Z>
where where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result<(), Error>, CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<(), Error>,
E: UsesState, E: UsesState,
{ {
#[must_use] #[must_use]
@ -257,7 +271,8 @@ where
impl<CS, E, EM, OT, PS, Z> Stage<E, EM, Z> for PushStageAdapter<CS, EM, OT, PS, Z> impl<CS, E, EM, OT, PS, Z> Stage<E, EM, Z> for PushStageAdapter<CS, EM, OT, PS, Z>
where where
CS: Scheduler, CS: Scheduler,
CS::State: HasExecutions + HasMetadata + HasRand + HasCorpus + HasLastReportTime, CS::State:
HasExecutions + HasMetadata + HasRand + HasCorpus + HasLastReportTime + HasCurrentCorpusIdx,
E: Executor<EM, Z> + HasObservers<Observers = OT, State = CS::State>, E: Executor<EM, Z> + HasObservers<Observers = OT, State = CS::State>,
EM: EventFirer<State = CS::State> EM: EventFirer<State = CS::State>
+ EventRestarter + EventRestarter
@ -270,16 +285,23 @@ where
+ EvaluatorObservers<OT> + EvaluatorObservers<OT>
+ HasScheduler<Scheduler = CS>, + HasScheduler<Scheduler = CS>,
{ {
type Progress = (); // TODO implement resume
fn perform( fn perform(
&mut self, &mut self,
fuzzer: &mut Z, fuzzer: &mut Z,
executor: &mut E, executor: &mut E,
state: &mut CS::State, state: &mut CS::State,
event_mgr: &mut EM, event_mgr: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let push_stage = &mut self.push_stage; let push_stage = &mut self.push_stage;
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
push_stage.set_current_corpus_idx(corpus_idx); push_stage.set_current_corpus_idx(corpus_idx);
push_stage.init(fuzzer, state, event_mgr, executor.observers_mut())?; push_stage.init(fuzzer, state, event_mgr, executor.observers_mut())?;
@ -318,11 +340,13 @@ pub mod pybind {
use pyo3::prelude::*; use pyo3::prelude::*;
use crate::{ use crate::{
corpus::CorpusId, corpus::HasCurrentCorpusIdx,
events::pybind::PythonEventManager, events::pybind::PythonEventManager,
executors::pybind::PythonExecutor, executors::pybind::PythonExecutor,
fuzzer::pybind::{PythonStdFuzzer, PythonStdFuzzerWrapper}, fuzzer::pybind::{PythonStdFuzzer, PythonStdFuzzerWrapper},
stages::{mutational::pybind::PythonStdMutationalStage, Stage, StagesTuple}, stages::{
mutational::pybind::PythonStdMutationalStage, HasCurrentStage, Stage, StagesTuple,
},
state::{ state::{
pybind::{PythonStdState, PythonStdStateWrapper}, pybind::{PythonStdState, PythonStdStateWrapper},
UsesState, UsesState,
@ -347,6 +371,8 @@ pub mod pybind {
} }
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PyObjectStage { impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PyObjectStage {
type Progress = (); // we don't support resumption in python, and maybe can't?
#[inline] #[inline]
fn perform( fn perform(
&mut self, &mut self,
@ -354,8 +380,13 @@ pub mod pybind {
executor: &mut PythonExecutor, executor: &mut PythonExecutor,
state: &mut PythonStdState, state: &mut PythonStdState,
manager: &mut PythonEventManager, manager: &mut PythonEventManager,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
Python::with_gil(|py| -> PyResult<()> { Python::with_gil(|py| -> PyResult<()> {
self.inner.call_method1( self.inner.call_method1(
py, py,
@ -435,6 +466,9 @@ pub mod pybind {
} }
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PythonStage { impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PythonStage {
// TODO if we implement resumption for StdMutational, we need to apply it here
type Progress = ();
#[inline] #[inline]
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
fn perform( fn perform(
@ -443,10 +477,9 @@ pub mod pybind {
executor: &mut PythonExecutor, executor: &mut PythonExecutor,
state: &mut PythonStdState, state: &mut PythonStdState,
manager: &mut PythonEventManager, manager: &mut PythonEventManager,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
unwrap_me_mut!(self.wrapper, s, { unwrap_me_mut!(self.wrapper, s, {
s.perform(fuzzer, executor, state, manager, corpus_idx) s.perform(fuzzer, executor, state, manager)
}) })
} }
} }
@ -482,10 +515,18 @@ pub mod pybind {
executor: &mut PythonExecutor, executor: &mut PythonExecutor,
state: &mut PythonStdState, state: &mut PythonStdState,
manager: &mut PythonEventManager, manager: &mut PythonEventManager,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
for s in &mut self.list { for (i, s) in self.list.iter_mut().enumerate() {
s.perform(fuzzer, executor, state, manager, corpus_idx)?; if let Some(continued) = state.current_stage()? {
assert!(continued >= i);
if continued > i {
continue;
}
} else {
state.set_stage(i)?;
}
s.perform(fuzzer, executor, state, manager)?;
state.clear_stage()?;
} }
Ok(()) Ok(())
} }
@ -498,3 +539,265 @@ pub mod pybind {
Ok(()) Ok(())
} }
} }
/// Trait for status tracking of stages which stash data to resume
pub trait StageProgress<S> {
/// Initialize the current status tracking for this stage, if it is not yet initialised
fn initialize_progress(state: &mut S) -> Result<(), Error>;
/// Clear the current status tracking of the associated stage
fn clear_progress(state: &mut S) -> Result<(), Error>;
/// Get the current status tracking of this stage
fn progress(state: &S) -> Result<&Self, Error>;
/// Get the current status tracking of this stage, mutably
fn progress_mut(state: &mut S) -> Result<&mut Self, Error>;
}
impl<S> StageProgress<S> for () {
fn initialize_progress(_state: &mut S) -> Result<(), Error> {
Ok(())
}
fn clear_progress(_state: &mut S) -> Result<(), Error> {
Ok(())
}
fn progress(_state: &S) -> Result<&Self, Error> {
unimplemented!("The empty tuple resumable stage status should never be queried")
}
fn progress_mut(_state: &mut S) -> Result<&mut Self, Error> {
unimplemented!("The empty tuple resumable stage status should never be queried")
}
}
/// Trait for types which track the current stage
pub trait HasCurrentStage {
/// Set the current stage; we have started processing this stage
fn set_stage(&mut self, idx: usize) -> Result<(), Error>;
/// Clear the current stage; we are done processing this stage
fn clear_stage(&mut self) -> Result<(), Error>;
/// Fetch the current stage -- typically used after a state recovery or transfer
fn current_stage(&self) -> Result<Option<usize>, Error>;
/// Notify of a reset from which we may recover
fn on_restart(&mut self) -> Result<(), Error> {
Ok(())
}
}
/// Trait for types which track nested stages. Stages which themselves contain stage tuples should
/// ensure that they constrain the state with this trait accordingly.
pub trait HasNestedStageStatus: HasCurrentStage {
/// Enter a stage scope, potentially resuming to an inner stage status. Returns Ok(true) if
/// resumed.
fn enter_inner_stage(&mut self) -> Result<(), Error>;
/// Exit a stage scope
fn exit_inner_stage(&mut self) -> Result<(), Error>;
}
#[cfg(test)]
pub mod test {
use alloc::rc::Rc;
use core::{cell::RefCell, marker::PhantomData};
use libafl_bolts::{impl_serdeany, Error};
use serde::{Deserialize, Serialize};
use tuple_list::{tuple_list, tuple_list_type};
use crate::{
events::NopEventManager,
executors::test::NopExecutor,
fuzzer::test::NopFuzzer,
stages::{Stage, StageProgress, StagesTuple},
state::{HasMetadata, State, UsesState},
};
#[derive(Debug)]
pub struct ResumeSucceededStage<S> {
phantom: PhantomData<S>,
}
#[derive(Debug)]
pub struct ResumeFailedStage<S> {
completed: Rc<RefCell<bool>>,
phantom: PhantomData<S>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct TestProgress {
count: usize,
}
impl_serdeany!(TestProgress);
impl<S> StageProgress<S> for TestProgress
where
S: HasMetadata,
{
fn initialize_progress(state: &mut S) -> Result<(), Error> {
// check if we're resuming
if !state.has_metadata::<Self>() {
state.add_metadata(Self { count: 0 });
}
Ok(())
}
fn clear_progress(state: &mut S) -> Result<(), Error> {
if state.metadata_map_mut().remove::<Self>().is_none() {
return Err(Error::illegal_state(
"attempted to clear status metadata when none was present",
));
}
Ok(())
}
fn progress(state: &S) -> Result<&Self, Error> {
state.metadata()
}
fn progress_mut(state: &mut S) -> Result<&mut Self, Error> {
state.metadata_mut()
}
}
impl<S> UsesState for ResumeSucceededStage<S>
where
S: State,
{
type State = S;
}
impl<E, EM, Z> Stage<E, EM, Z> for ResumeSucceededStage<Z::State>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: UsesState,
Z::State: HasMetadata,
{
type Progress = TestProgress;
fn perform(
&mut self,
_fuzzer: &mut Z,
_executor: &mut E,
state: &mut Self::State,
_manager: &mut EM,
) -> Result<(), Error> {
// metadata is attached by the status
let meta = Self::Progress::progress_mut(state)?;
meta.count += 1;
assert!(
meta.count == 1,
"Test failed; we resumed a succeeded stage!"
);
Ok(())
}
}
impl<S> UsesState for ResumeFailedStage<S>
where
S: State,
{
type State = S;
}
impl<E, EM, Z> Stage<E, EM, Z> for ResumeFailedStage<Z::State>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: UsesState,
Z::State: HasMetadata,
{
type Progress = TestProgress;
fn perform(
&mut self,
_fuzzer: &mut Z,
_executor: &mut E,
state: &mut Self::State,
_manager: &mut EM,
) -> Result<(), Error> {
// metadata is attached by the status
let meta = Self::Progress::progress_mut(state)?;
meta.count += 1;
if meta.count == 1 {
return Err(Error::shutting_down());
} else if meta.count > 2 {
panic!("Resume was somehow corrupted?")
} else {
self.completed.replace(true);
}
Ok(())
}
}
#[must_use]
#[allow(clippy::type_complexity)]
pub fn test_resume_stages<S>() -> (
Rc<RefCell<bool>>,
tuple_list_type!(ResumeSucceededStage<S>, ResumeFailedStage<S>),
) {
let completed = Rc::new(RefCell::new(false));
(
completed.clone(),
tuple_list!(
ResumeSucceededStage {
phantom: PhantomData
},
ResumeFailedStage {
completed,
phantom: PhantomData
},
),
)
}
pub fn test_resume<ST, S>(completed: &Rc<RefCell<bool>>, state: &mut S, mut stages: ST)
where
ST: StagesTuple<NopExecutor<S>, NopEventManager<S>, S, NopFuzzer<S>>,
S: State,
{
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
unsafe {
TestProgress::register();
}
let mut fuzzer = NopFuzzer::new();
let mut executor = NopExecutor::new();
let mut manager = NopEventManager::new();
for _ in 0..2 {
completed.replace(false);
let Err(e) = stages.perform_all(&mut fuzzer, &mut executor, state, &mut manager) else {
panic!("Test failed; stages should fail the first time.")
};
assert!(
matches!(e, Error::ShuttingDown),
"Unexpected error encountered."
);
assert!(!*completed.borrow(), "Unexpectedly complete?");
state
.on_restart()
.expect("Couldn't notify state of restart.");
assert!(
stages
.perform_all(&mut fuzzer, &mut executor, state, &mut manager)
.is_ok(),
"Test failed; stages should pass the second time."
);
assert!(
*completed.borrow(),
"Test failed; we did not set completed."
);
}
}
}

View File

@ -6,7 +6,7 @@ use core::marker::PhantomData;
use libafl_bolts::rands::Rand; use libafl_bolts::rands::Rand;
use crate::{ use crate::{
corpus::{Corpus, CorpusId, Testcase}, corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, Testcase},
fuzzer::Evaluator, fuzzer::Evaluator,
inputs::Input, inputs::Input,
mark_feature_time, mark_feature_time,
@ -114,8 +114,13 @@ where
executor: &mut E, executor: &mut E,
state: &mut Z::State, state: &mut Z::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
let num = self.iterations(state, corpus_idx)?; let num = self.iterations(state, corpus_idx)?;
start_timer!(state); start_timer!(state);
@ -211,6 +216,8 @@ where
Z::State: HasCorpus + HasRand, Z::State: HasCorpus + HasRand,
I: MutatedTransform<Self::Input, Self::State> + Clone, I: MutatedTransform<Self::Input, Self::State> + Clone,
{ {
type Progress = (); // TODO should this stage be resumed?
#[inline] #[inline]
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
fn perform( fn perform(
@ -219,9 +226,8 @@ where
executor: &mut E, executor: &mut E,
state: &mut Z::State, state: &mut Z::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let ret = self.perform_mutational(fuzzer, executor, state, manager, corpus_idx); let ret = self.perform_mutational(fuzzer, executor, state, manager);
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
state.introspection_monitor_mut().finish_stage(); state.introspection_monitor_mut().finish_stage();
@ -300,6 +306,8 @@ where
Z::State: HasCorpus + HasRand, Z::State: HasCorpus + HasRand,
I: MutatedTransform<Self::Input, Self::State> + Clone, I: MutatedTransform<Self::Input, Self::State> + Clone,
{ {
type Progress = (); // TODO implement resume
#[inline] #[inline]
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
#[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_possible_wrap)]
@ -309,8 +317,13 @@ where
executor: &mut E, executor: &mut E,
state: &mut Z::State, state: &mut Z::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
let mut testcase = state.corpus().get(corpus_idx)?.borrow_mut(); let mut testcase = state.corpus().get(corpus_idx)?.borrow_mut();
let Ok(input) = I::try_transform_from(&mut testcase, state, corpus_idx) else { let Ok(input) = I::try_transform_from(&mut testcase, state, corpus_idx) else {
return Ok(()); return Ok(());

View File

@ -1,66 +0,0 @@
//! A dynamic collection of owned Stages
use alloc::{boxed::Box, vec::Vec};
use libafl_bolts::anymap::AsAny;
use crate::{
corpus::CorpusId,
stages::{Stage, StagesTuple},
state::UsesState,
Error,
};
/// Combine `Stage` and `AsAny`
pub trait AnyStage<E, EM, Z>: Stage<E, EM, Z> + AsAny
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
Z: UsesState<State = Self::State>,
{
}
/// An owned list of `Observer` trait objects
#[derive(Default)]
#[allow(missing_debug_implementations)]
pub struct StagesOwnedList<E, EM, Z>
where
E: UsesState,
{
/// The named trait objects map
#[allow(clippy::type_complexity)]
pub list: Vec<Box<dyn AnyStage<E, EM, Z, State = E::State, Input = E::Input>>>,
}
impl<E, EM, Z> StagesTuple<E, EM, E::State, Z> for StagesOwnedList<E, EM, Z>
where
E: UsesState,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
fn perform_all(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut E::State,
manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> {
for s in &mut self.list {
s.perform(fuzzer, executor, state, manager, corpus_idx)?;
}
Ok(())
}
}
impl<E, EM, Z> StagesOwnedList<E, EM, Z>
where
E: UsesState,
{
/// Create a new instance
#[must_use]
#[allow(clippy::type_complexity)]
pub fn new(list: Vec<Box<dyn AnyStage<E, EM, Z, Input = E::Input, State = E::State>>>) -> Self {
Self { list }
}
}

View File

@ -71,6 +71,8 @@ where
Z: Evaluator<E, EM, State = E::State>, Z: Evaluator<E, EM, State = E::State>,
I: MutatedTransform<E::Input, E::State> + Clone, I: MutatedTransform<E::Input, E::State> + Clone,
{ {
type Progress = (); // TODO should we resume this stage?
#[inline] #[inline]
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
fn perform( fn perform(
@ -79,9 +81,8 @@ where
executor: &mut E, executor: &mut E,
state: &mut E::State, state: &mut E::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let ret = self.perform_mutational(fuzzer, executor, state, manager, corpus_idx); let ret = self.perform_mutational(fuzzer, executor, state, manager);
ret ret
} }
} }

View File

@ -9,7 +9,7 @@ use libafl_bolts::current_time;
use serde_json::json; use serde_json::json;
use crate::{ use crate::{
corpus::{Corpus, CorpusId}, corpus::{Corpus, HasCurrentCorpusIdx},
events::EventFirer, events::EventFirer,
schedulers::minimizer::IsFavoredMetadata, schedulers::minimizer::IsFavoredMetadata,
stages::Stage, stages::Stage,
@ -62,14 +62,21 @@ where
Z: UsesState<State = E::State>, Z: UsesState<State = E::State>,
E::State: HasImported + HasCorpus + HasMetadata, E::State: HasImported + HasCorpus + HasMetadata,
{ {
type Progress = (); // this stage does not require resume
fn perform( fn perform(
&mut self, &mut self,
_fuzzer: &mut Z, _fuzzer: &mut Z,
_executor: &mut E, _executor: &mut E,
state: &mut E::State, state: &mut E::State,
_manager: &mut EM, _manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
// Report your stats every `STATS_REPORT_INTERVAL` // Report your stats every `STATS_REPORT_INTERVAL`
// compute pending, pending_favored, imported, own_finds // compute pending, pending_favored, imported, own_finds
{ {

View File

@ -8,7 +8,7 @@ use libafl_bolts::{impl_serdeany, Error};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
corpus::{CorpusId, HasTestcase}, corpus::HasTestcase,
inputs::{BytesInput, HasBytesVec}, inputs::{BytesInput, HasBytesVec},
stages::Stage, stages::Stage,
state::{HasCorpus, HasMetadata, State, UsesState}, state::{HasCorpus, HasMetadata, State, UsesState},
@ -104,14 +104,21 @@ where
EM: UsesState<State = S>, EM: UsesState<State = S>,
Z: UsesState<State = S>, Z: UsesState<State = S>,
{ {
type Progress = (); // this stage does not need to be resumed
fn perform( fn perform(
&mut self, &mut self,
_fuzzer: &mut Z, _fuzzer: &mut Z,
_executor: &mut E, _executor: &mut E,
state: &mut Self::State, state: &mut Self::State,
_manager: &mut EM, _manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
let mut tc = state.testcase_mut(corpus_idx)?; let mut tc = state.testcase_mut(corpus_idx)?;
if tc.has_metadata::<StringIdentificationMetadata>() { if tc.has_metadata::<StringIdentificationMetadata>() {
return Ok(()); // skip recompute return Ok(()); // skip recompute

View File

@ -67,6 +67,8 @@ where
Z: Evaluator<E, EM>, Z: Evaluator<E, EM>,
Z::State: HasCorpus + HasRand + HasMetadata, Z::State: HasCorpus + HasRand + HasMetadata,
{ {
type Progress = (); // TODO load from directory should be resumed
#[inline] #[inline]
fn perform( fn perform(
&mut self, &mut self,
@ -74,7 +76,6 @@ where
executor: &mut E, executor: &mut E,
state: &mut Z::State, state: &mut Z::State,
manager: &mut EM, manager: &mut EM,
_corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let last = state let last = state
.metadata_map() .metadata_map()
@ -253,6 +254,8 @@ where
ICB: InputConverter<From = DI, To = S::Input>, ICB: InputConverter<From = DI, To = S::Input>,
DI: Input, DI: Input,
{ {
type Progress = (); // TODO this should be resumed in the case that a testcase causes a crash
#[inline] #[inline]
fn perform( fn perform(
&mut self, &mut self,
@ -260,7 +263,6 @@ where
executor: &mut E, executor: &mut E,
state: &mut Z::State, state: &mut Z::State,
manager: &mut EM, manager: &mut EM,
_corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
if self.client.can_convert() { if self.client.can_convert() {
let last_id = state let last_id = state

View File

@ -7,7 +7,7 @@ use ahash::RandomState;
use libafl_bolts::{HasLen, Named}; use libafl_bolts::{HasLen, Named};
use crate::{ use crate::{
corpus::{Corpus, CorpusId, Testcase}, corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, Testcase},
events::EventFirer, events::EventFirer,
executors::{Executor, ExitKind, HasObservers}, executors::{Executor, ExitKind, HasObservers},
feedbacks::{Feedback, FeedbackFactory, HasObserverName}, feedbacks::{Feedback, FeedbackFactory, HasObserverName},
@ -61,8 +61,13 @@ where
executor: &mut E, executor: &mut E,
state: &mut CS::State, state: &mut CS::State,
manager: &mut EM, manager: &mut EM,
base_corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(base_corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
let orig_max_size = state.max_size(); let orig_max_size = state.max_size();
// basically copy-pasted from mutational.rs // basically copy-pasted from mutational.rs
let num = self.iterations(state, base_corpus_idx)?; let num = self.iterations(state, base_corpus_idx)?;
@ -209,15 +214,16 @@ where
+ HasFeedback<Feedback = F1> + HasFeedback<Feedback = F1>
+ HasScheduler<Scheduler = CS>, + HasScheduler<Scheduler = CS>,
{ {
type Progress = (); // TODO this stage desperately needs a resume
fn perform( fn perform(
&mut self, &mut self,
fuzzer: &mut Z, fuzzer: &mut Z,
executor: &mut E, executor: &mut E,
state: &mut CS::State, state: &mut CS::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.perform_minification(fuzzer, executor, state, manager, corpus_idx)?; self.perform_minification(fuzzer, executor, state, manager)?;
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
state.introspection_monitor_mut().finish_stage(); state.introspection_monitor_mut().finish_stage();

View File

@ -3,7 +3,7 @@
use core::{fmt::Debug, marker::PhantomData}; use core::{fmt::Debug, marker::PhantomData};
use crate::{ use crate::{
corpus::{Corpus, CorpusId}, corpus::{Corpus, HasCurrentCorpusIdx},
executors::{Executor, HasObservers, ShadowExecutor}, executors::{Executor, HasObservers, ShadowExecutor},
mark_feature_time, mark_feature_time,
observers::ObserversTuple, observers::ObserversTuple,
@ -38,6 +38,8 @@ where
EM: UsesState<State = TE::State>, EM: UsesState<State = TE::State>,
Z: UsesState<State = TE::State>, Z: UsesState<State = TE::State>,
{ {
type Progress = (); // this stage cannot be resumed
#[inline] #[inline]
fn perform( fn perform(
&mut self, &mut self,
@ -45,8 +47,13 @@ where
_executor: &mut E, _executor: &mut E,
state: &mut TE::State, state: &mut TE::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
start_timer!(state); start_timer!(state);
let input = state.corpus().cloned_input_for_id(corpus_idx)?; let input = state.corpus().cloned_input_for_id(corpus_idx)?;
@ -117,6 +124,8 @@ where
Z: UsesState<State = E::State>, Z: UsesState<State = E::State>,
E::State: State + HasExecutions + HasCorpus + Debug, E::State: State + HasExecutions + HasCorpus + Debug,
{ {
type Progress = (); // this stage cannot be resumed
#[inline] #[inline]
fn perform( fn perform(
&mut self, &mut self,
@ -124,8 +133,13 @@ where
executor: &mut ShadowExecutor<E, SOT>, executor: &mut ShadowExecutor<E, SOT>,
state: &mut E::State, state: &mut E::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
start_timer!(state); start_timer!(state);
let input = state.corpus().cloned_input_for_id(corpus_idx)?; let input = state.corpus().cloned_input_for_id(corpus_idx)?;

View File

@ -7,7 +7,7 @@ use libafl_bolts::{current_time, impl_serdeany, rands::Rand};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
corpus::{Corpus, CorpusId}, corpus::{Corpus, CorpusId, HasCurrentCorpusIdx},
mark_feature_time, mark_feature_time,
mutators::{MutationResult, Mutator}, mutators::{MutationResult, Mutator},
stages::{ stages::{
@ -173,8 +173,13 @@ where
executor: &mut E, executor: &mut E,
state: &mut Z::State, state: &mut Z::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_idx()? else {
return Err(Error::illegal_state(
"state is not currently processing a corpus index",
));
};
let fuzz_time = self.seed_fuzz_time(state)?; let fuzz_time = self.seed_fuzz_time(state)?;
let iters = self.fixed_iters(state)?; let iters = self.fixed_iters(state)?;
@ -269,6 +274,8 @@ where
Z::State: HasCorpus + HasRand + HasNamedMetadata + HasMetadata, Z::State: HasCorpus + HasRand + HasNamedMetadata + HasMetadata,
I: MutatedTransform<Z::Input, Z::State> + Clone, I: MutatedTransform<Z::Input, Z::State> + Clone,
{ {
type Progress = (); // TODO should this stage be resumed?
#[inline] #[inline]
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
fn perform( fn perform(
@ -277,9 +284,8 @@ where
executor: &mut E, executor: &mut E,
state: &mut Z::State, state: &mut Z::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
let ret = self.perform_mutational(fuzzer, executor, state, manager, corpus_idx); let ret = self.perform_mutational(fuzzer, executor, state, manager);
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
state.introspection_monitor_mut().finish_stage(); state.introspection_monitor_mut().finish_stage();

View File

@ -1,5 +1,6 @@
//! The fuzzer, and state are the core pieces of every good fuzzer //! The fuzzer, and state are the core pieces of every good fuzzer
use alloc::vec::Vec;
use core::{ use core::{
cell::{Ref, RefMut}, cell::{Ref, RefMut},
fmt::Debug, fmt::Debug,
@ -10,11 +11,10 @@ use core::{
use std::{ use std::{
fs, fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
vec::Vec,
}; };
use libafl_bolts::{ use libafl_bolts::{
rands::{Rand, StdRand}, rands::Rand,
serdeany::{NamedSerdeAnyMap, SerdeAny, SerdeAnyMap}, serdeany::{NamedSerdeAnyMap, SerdeAny, SerdeAnyMap},
}; };
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
@ -24,12 +24,13 @@ use crate::monitors::ClientPerfMonitor;
#[cfg(feature = "scalability_introspection")] #[cfg(feature = "scalability_introspection")]
use crate::monitors::ScalabilityMonitor; use crate::monitors::ScalabilityMonitor;
use crate::{ use crate::{
corpus::{Corpus, CorpusId, HasTestcase, Testcase}, corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, HasTestcase, Testcase},
events::{Event, EventFirer, LogSeverity}, events::{Event, EventFirer, LogSeverity},
feedbacks::Feedback, feedbacks::Feedback,
fuzzer::{Evaluator, ExecuteInputResult}, fuzzer::{Evaluator, ExecuteInputResult},
generators::Generator, generators::Generator,
inputs::{Input, UsesInput}, inputs::{Input, UsesInput},
stages::{HasCurrentStage, HasNestedStageStatus},
Error, Error,
}; };
@ -40,7 +41,13 @@ pub const DEFAULT_MAX_SIZE: usize = 1_048_576;
/// Contains all important information about the current run. /// Contains all important information about the current run.
/// Will be used to restart the fuzzing process at any time. /// Will be used to restart the fuzzing process at any time.
pub trait State: pub trait State:
UsesInput + Serialize + DeserializeOwned + MaybeHasClientPerfMonitor + MaybeHasScalabilityMonitor UsesInput
+ Serialize
+ DeserializeOwned
+ MaybeHasClientPerfMonitor
+ MaybeHasScalabilityMonitor
+ HasCurrentCorpusIdx
+ HasCurrentStage
{ {
} }
@ -321,6 +328,12 @@ pub struct StdState<I, C, R, SC> {
/// The last time we reported progress (if available/used). /// The last time we reported progress (if available/used).
/// This information is used by fuzzer `maybe_report_progress`. /// This information is used by fuzzer `maybe_report_progress`.
last_report_time: Option<Duration>, last_report_time: Option<Duration>,
/// The current index of the corpus; used to record for resumable fuzzing.
corpus_idx: Option<CorpusId>,
/// The stage indexes for each nesting of stages
stage_idx_stack: Vec<usize>,
/// The current stage depth
stage_depth: usize,
phantom: PhantomData<I>, phantom: PhantomData<I>,
} }
@ -514,6 +527,67 @@ impl<I, C, R, SC> HasStartTime for StdState<I, C, R, SC> {
} }
} }
impl<I, C, R, SC> HasCurrentCorpusIdx for StdState<I, C, R, SC> {
fn set_corpus_idx(&mut self, idx: CorpusId) -> Result<(), Error> {
self.corpus_idx = Some(idx);
Ok(())
}
fn clear_corpus_idx(&mut self) -> Result<(), Error> {
self.corpus_idx = None;
Ok(())
}
fn current_corpus_idx(&self) -> Result<Option<CorpusId>, Error> {
Ok(self.corpus_idx)
}
}
impl<I, C, R, SC> HasCurrentStage for StdState<I, C, R, SC> {
fn set_stage(&mut self, idx: usize) -> Result<(), Error> {
// ensure we are in the right frame
if self.stage_depth != self.stage_idx_stack.len() {
return Err(Error::illegal_state(
"stage not resumed before setting stage",
));
}
self.stage_idx_stack.push(idx);
Ok(())
}
fn clear_stage(&mut self) -> Result<(), Error> {
self.stage_idx_stack.pop();
// ensure we are in the right frame
if self.stage_depth != self.stage_idx_stack.len() {
return Err(Error::illegal_state(
"we somehow cleared too many or too few states!",
));
}
Ok(())
}
fn current_stage(&self) -> Result<Option<usize>, Error> {
Ok(self.stage_idx_stack.get(self.stage_depth).copied())
}
fn on_restart(&mut self) -> Result<(), Error> {
self.stage_depth = 0; // reset the stage depth so that we may resume inward
Ok(())
}
}
impl<I, C, R, SC> HasNestedStageStatus for StdState<I, C, R, SC> {
fn enter_inner_stage(&mut self) -> Result<(), Error> {
self.stage_depth += 1;
Ok(())
}
fn exit_inner_stage(&mut self) -> Result<(), Error> {
self.stage_depth -= 1;
Ok(())
}
}
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl<C, I, R, SC> StdState<I, C, R, SC> impl<C, I, R, SC> StdState<I, C, R, SC>
where where
@ -897,6 +971,9 @@ where
#[cfg(feature = "std")] #[cfg(feature = "std")]
dont_reenter: None, dont_reenter: None,
last_report_time: None, last_report_time: None,
corpus_idx: None,
stage_depth: 0,
stage_idx_stack: Vec::new(),
phantom: PhantomData, phantom: PhantomData,
}; };
feedback.init_state(&mut state)?; feedback.init_state(&mut state)?;
@ -927,98 +1004,169 @@ impl<I, C, R, SC> HasScalabilityMonitor for StdState<I, C, R, SC> {
} }
} }
/// A very simple state without any bells or whistles, for testing. #[cfg(test)]
#[derive(Debug, Serialize, Deserialize, Default)] pub mod test {
pub struct NopState<I> { use core::{marker::PhantomData, time::Duration};
metadata: SerdeAnyMap,
execution: usize,
rand: StdRand,
phantom: PhantomData<I>,
}
impl<I> NopState<I> { use libafl_bolts::{rands::StdRand, serdeany::SerdeAnyMap, Error};
/// Create a new State that does nothing (for tests) use serde::{Deserialize, Serialize};
#[must_use]
pub fn new() -> Self { use crate::{
NopState { corpus::{CorpusId, HasCurrentCorpusIdx, InMemoryCorpus},
metadata: SerdeAnyMap::new(), inputs::{Input, NopInput, UsesInput},
execution: 0, stages::test::{test_resume, test_resume_stages},
rand: StdRand::default(), state::{
phantom: PhantomData, HasCurrentStage, HasExecutions, HasLastReportTime, HasMetadata, HasRand, State,
StdState,
},
};
#[cfg(feature = "introspection")]
use crate::{monitors::ClientPerfMonitor, state::HasClientPerfMonitor};
#[cfg(feature = "scalability_introspection")]
use crate::{monitors::ScalabilityMonitor, state::HasScalabilityMonitor};
/// A very simple state without any bells or whistles, for testing.
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct NopState<I> {
metadata: SerdeAnyMap,
execution: usize,
rand: StdRand,
phantom: PhantomData<I>,
}
impl<I> NopState<I> {
/// Create a new State that does nothing (for tests)
#[must_use]
pub fn new() -> Self {
NopState {
metadata: SerdeAnyMap::new(),
execution: 0,
rand: StdRand::default(),
phantom: PhantomData,
}
} }
} }
}
impl<I> UsesInput for NopState<I> impl<I> UsesInput for NopState<I>
where where
I: Input, I: Input,
{ {
type Input = I; type Input = I;
}
impl<I> HasExecutions for NopState<I> {
fn executions(&self) -> &usize {
&self.execution
} }
fn executions_mut(&mut self) -> &mut usize { impl<I> HasExecutions for NopState<I> {
&mut self.execution fn executions(&self) -> &usize {
} &self.execution
} }
impl<I> HasLastReportTime for NopState<I> { fn executions_mut(&mut self) -> &mut usize {
fn last_report_time(&self) -> &Option<Duration> { &mut self.execution
unimplemented!(); }
} }
fn last_report_time_mut(&mut self) -> &mut Option<Duration> { impl<I> HasLastReportTime for NopState<I> {
unimplemented!(); fn last_report_time(&self) -> &Option<Duration> {
} unimplemented!();
} }
impl<I> HasMetadata for NopState<I> { fn last_report_time_mut(&mut self) -> &mut Option<Duration> {
fn metadata_map(&self) -> &SerdeAnyMap { unimplemented!();
&self.metadata }
} }
fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap { impl<I> HasMetadata for NopState<I> {
&mut self.metadata fn metadata_map(&self) -> &SerdeAnyMap {
} &self.metadata
} }
impl<I> HasRand for NopState<I> { fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap {
type Rand = StdRand; &mut self.metadata
}
fn rand(&self) -> &Self::Rand {
&self.rand
} }
fn rand_mut(&mut self) -> &mut Self::Rand { impl<I> HasRand for NopState<I> {
&mut self.rand type Rand = StdRand;
}
}
impl<I> State for NopState<I> where I: Input {} fn rand(&self) -> &Self::Rand {
&self.rand
}
#[cfg(feature = "introspection")] fn rand_mut(&mut self) -> &mut Self::Rand {
impl<I> HasClientPerfMonitor for NopState<I> { &mut self.rand
fn introspection_monitor(&self) -> &ClientPerfMonitor { }
unimplemented!();
} }
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor { impl<I> State for NopState<I> where I: Input {}
unimplemented!();
}
}
#[cfg(feature = "scalability_introspection")] impl<I> HasCurrentCorpusIdx for NopState<I> {
impl<I> HasScalabilityMonitor for NopState<I> { fn set_corpus_idx(&mut self, _idx: CorpusId) -> Result<(), Error> {
fn scalability_monitor(&self) -> &ScalabilityMonitor { Ok(())
unimplemented!(); }
fn clear_corpus_idx(&mut self) -> Result<(), Error> {
Ok(())
}
fn current_corpus_idx(&self) -> Result<Option<CorpusId>, Error> {
Ok(None)
}
} }
fn scalability_monitor_mut(&mut self) -> &mut ScalabilityMonitor { impl<I> HasCurrentStage for NopState<I> {
unimplemented!(); fn set_stage(&mut self, _idx: usize) -> Result<(), Error> {
Ok(())
}
fn clear_stage(&mut self) -> Result<(), Error> {
Ok(())
}
fn current_stage(&self) -> Result<Option<usize>, Error> {
Ok(None)
}
}
#[cfg(feature = "introspection")]
impl<I> HasClientPerfMonitor for NopState<I> {
fn introspection_monitor(&self) -> &ClientPerfMonitor {
unimplemented!();
}
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor {
unimplemented!();
}
}
#[cfg(feature = "scalability_introspection")]
impl<I> HasScalabilityMonitor for NopState<I> {
fn scalability_monitor(&self) -> &ScalabilityMonitor {
unimplemented!();
}
fn scalability_monitor_mut(&mut self) -> &mut ScalabilityMonitor {
unimplemented!();
}
}
#[must_use]
pub fn test_std_state<I: Input>() -> StdState<I, InMemoryCorpus<I>, StdRand, InMemoryCorpus<I>>
{
StdState::new(
StdRand::with_seed(0),
InMemoryCorpus::<I>::new(),
InMemoryCorpus::new(),
&mut (),
&mut (),
)
.expect("couldn't instantiate the test state")
}
#[test]
fn resume_simple() {
let mut state = test_std_state::<NopInput>();
let (completed, stages) = test_resume_stages();
test_resume(&completed, &mut state, stages);
} }
} }

View File

@ -11,7 +11,7 @@ pub use tuple_list::{tuple_list, tuple_list_type, TupleList};
#[cfg(any(feature = "xxh3", feature = "alloc"))] #[cfg(any(feature = "xxh3", feature = "alloc"))]
use crate::hash_std; use crate::hash_std;
use crate::Named; use crate::{HasLen, Named};
/// Returns if the type `T` is equal to `U` /// Returns if the type `T` is equal to `U`
/// From <https://stackoverflow.com/a/60138532/7658998> /// From <https://stackoverflow.com/a/60138532/7658998>
@ -92,21 +92,10 @@ where
pub trait HasConstLen { pub trait HasConstLen {
/// The length as constant `usize` /// The length as constant `usize`
const LEN: usize; const LEN: usize;
/// The length
fn len(&self) -> usize;
/// Returns true, if empty
fn is_empty(&self) -> bool {
self.len() == 0
}
} }
impl HasConstLen for () { impl HasConstLen for () {
const LEN: usize = 0; const LEN: usize = 0;
fn len(&self) -> usize {
0
}
} }
impl<Head, Tail> HasConstLen for (Head, Tail) impl<Head, Tail> HasConstLen for (Head, Tail)
@ -114,9 +103,18 @@ where
Tail: HasConstLen, Tail: HasConstLen,
{ {
const LEN: usize = 1 + Tail::LEN; const LEN: usize = 1 + Tail::LEN;
}
impl<C> HasLen for C
where
C: HasConstLen,
{
fn len(&self) -> usize { fn len(&self) -> usize {
1 + self.1.len() Self::LEN
}
fn is_empty(&self) -> bool {
Self::LEN != 0
} }
} }

View File

@ -22,7 +22,7 @@ use libafl::{
tui::{ui::TuiUI, TuiMonitor}, tui::{ui::TuiUI, TuiMonitor},
Monitor, MultiMonitor, SimpleMonitor, Monitor, MultiMonitor, SimpleMonitor,
}, },
stages::StagesTuple, stages::{HasCurrentStage, StagesTuple},
state::{HasExecutions, HasLastReportTime, HasMetadata, HasSolutions, UsesState}, state::{HasExecutions, HasLastReportTime, HasMetadata, HasSolutions, UsesState},
Error, Fuzzer, Error, Fuzzer,
}; };
@ -66,7 +66,7 @@ fn do_fuzz<F, ST, E, S, EM>(
) -> Result<(), Error> ) -> Result<(), Error>
where where
F: Fuzzer<E, EM, ST, State = S>, F: Fuzzer<E, EM, ST, State = S>,
S: HasMetadata + HasExecutions + UsesInput + HasSolutions + HasLastReportTime, S: HasMetadata + HasExecutions + UsesInput + HasSolutions + HasLastReportTime + HasCurrentStage,
E: UsesState<State = S>, E: UsesState<State = S>,
EM: ProgressReporter<State = S>, EM: ProgressReporter<State = S>,
ST: StagesTuple<E, EM, S, F>, ST: StagesTuple<E, EM, S, F>,

View File

@ -224,7 +224,7 @@ macro_rules! fuzz_with {
// Set up a generalization stage for grimoire // Set up a generalization stage for grimoire
let generalization = GeneralizationStage::new(&edges_observer); let generalization = GeneralizationStage::new(&edges_observer);
let generalization = IfStage::new(|_, _, _, _, _| Ok(grimoire.into()), tuple_list!(generalization)); let generalization = IfStage::new(|_, _, _, _| Ok(grimoire.into()), tuple_list!(generalization));
let calibration = CalibrationStage::new(&map_feedback); let calibration = CalibrationStage::new(&map_feedback);
@ -320,7 +320,7 @@ macro_rules! fuzz_with {
let string_replace_power = StdMutationalStage::transforming(string_replace_mutator); let string_replace_power = StdMutationalStage::transforming(string_replace_mutator);
let string_analysis = StringIdentificationStage::new(); let string_analysis = StringIdentificationStage::new();
let string_analysis = IfStage::new(|_, _, _, _, _| Ok((unicode_used && mutator_status.std_mutational).into()), tuple_list!(string_analysis, string_power, string_replace_power)); let string_analysis = IfStage::new(|_, _, _, _| Ok((unicode_used && mutator_status.std_mutational).into()), tuple_list!(string_analysis, string_power, string_replace_power));
// Attempt to use tokens from libfuzzer dicts // Attempt to use tokens from libfuzzer dicts
if !state.has_metadata::<Tokens>() { if !state.has_metadata::<Tokens>() {
@ -342,19 +342,19 @@ macro_rules! fuzz_with {
// Setup a randomic Input2State stage, conditionally within a custom mutator // Setup a randomic Input2State stage, conditionally within a custom mutator
let i2s = let i2s =
StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new()))); StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
let i2s = IfStage::new(|_, _, _, _, _| Ok((!mutator_status.custom_mutation).into()), (i2s, ())); let i2s = IfStage::new(|_, _, _, _| Ok((!mutator_status.custom_mutation).into()), (i2s, ()));
let cm_i2s = StdMutationalStage::new(unsafe { let cm_i2s = StdMutationalStage::new(unsafe {
LLVMCustomMutator::mutate_unchecked(StdScheduledMutator::new(tuple_list!( LLVMCustomMutator::mutate_unchecked(StdScheduledMutator::new(tuple_list!(
I2SRandReplace::new() I2SRandReplace::new()
))) )))
}); });
let cm_i2s = IfStage::new(|_, _, _, _, _| Ok(mutator_status.custom_mutation.into()), (cm_i2s, ())); let cm_i2s = IfStage::new(|_, _, _, _| Ok(mutator_status.custom_mutation.into()), (cm_i2s, ()));
// TODO configure with mutation stacking options from libfuzzer // TODO configure with mutation stacking options from libfuzzer
let std_mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); let std_mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let std_power = StdPowerMutationalStage::new(std_mutator); let std_power = StdPowerMutationalStage::new(std_mutator);
let std_power = IfStage::new(|_, _, _, _, _| Ok(mutator_status.std_mutational.into()), (std_power, ())); let std_power = IfStage::new(|_, _, _, _| Ok(mutator_status.std_mutational.into()), (std_power, ()));
// for custom mutator and crossover, each have access to the LLVMFuzzerMutate -- but it appears // for custom mutator and crossover, each have access to the LLVMFuzzerMutate -- but it appears
// that this method doesn't normally offer stacked mutations where one may expect them // that this method doesn't normally offer stacked mutations where one may expect them
@ -375,10 +375,10 @@ macro_rules! fuzz_with {
let std_mutator_no_mutate = StdScheduledMutator::with_max_stack_pow(havoc_crossover(), 3); let std_mutator_no_mutate = StdScheduledMutator::with_max_stack_pow(havoc_crossover(), 3);
let cm_power = StdPowerMutationalStage::new(custom_mutator); let cm_power = StdPowerMutationalStage::new(custom_mutator);
let cm_power = IfStage::new(|_, _, _, _, _| Ok(mutator_status.custom_mutation.into()), (cm_power, ())); let cm_power = IfStage::new(|_, _, _, _| Ok(mutator_status.custom_mutation.into()), (cm_power, ()));
let cm_std_power = StdMutationalStage::new(std_mutator_no_mutate); let cm_std_power = StdMutationalStage::new(std_mutator_no_mutate);
let cm_std_power = let cm_std_power =
IfStage::new(|_, _, _, _, _| Ok(mutator_status.std_no_mutate.into()), (cm_std_power, ())); IfStage::new(|_, _, _, _| Ok(mutator_status.std_no_mutate.into()), (cm_std_power, ()));
// a custom crossover is defined // a custom crossover is defined
// while the scenario that a custom crossover is defined without a custom mutator is unlikely // while the scenario that a custom crossover is defined without a custom mutator is unlikely
@ -392,10 +392,10 @@ macro_rules! fuzz_with {
let std_mutator_no_crossover = StdScheduledMutator::new(havoc_mutations_no_crossover().merge(tokens_mutations())); let std_mutator_no_crossover = StdScheduledMutator::new(havoc_mutations_no_crossover().merge(tokens_mutations()));
let cc_power = StdMutationalStage::new(custom_crossover); let cc_power = StdMutationalStage::new(custom_crossover);
let cc_power = IfStage::new(|_, _, _, _, _| Ok(mutator_status.custom_crossover.into()), (cc_power, ())); let cc_power = IfStage::new(|_, _, _, _| Ok(mutator_status.custom_crossover.into()), (cc_power, ()));
let cc_std_power = StdPowerMutationalStage::new(std_mutator_no_crossover); let cc_std_power = StdPowerMutationalStage::new(std_mutator_no_crossover);
let cc_std_power = let cc_std_power =
IfStage::new(|_, _, _, _, _| Ok(mutator_status.std_no_crossover.into()), (cc_std_power, ())); IfStage::new(|_, _, _, _| Ok(mutator_status.std_no_crossover.into()), (cc_std_power, ()));
let grimoire_mutator = StdScheduledMutator::with_max_stack_pow( let grimoire_mutator = StdScheduledMutator::with_max_stack_pow(
tuple_list!( tuple_list!(
@ -408,7 +408,7 @@ macro_rules! fuzz_with {
), ),
3, 3,
); );
let grimoire = IfStage::new(|_, _, _, _, _| Ok(grimoire.into()), (StdMutationalStage::transforming(grimoire_mutator), ())); let grimoire = IfStage::new(|_, _, _, _| Ok(grimoire.into()), (StdMutationalStage::transforming(grimoire_mutator), ()));
// A minimization+queue policy to get testcasess from the corpus // A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerScheduler::new(PowerQueueScheduler::new(&mut state, &edges_observer, PowerSchedule::FAST)); let scheduler = IndexesLenTimeMinimizerScheduler::new(PowerQueueScheduler::new(&mut state, &edges_observer, PowerSchedule::FAST));
@ -479,7 +479,7 @@ macro_rules! fuzz_with {
// Setup a tracing stage in which we log comparisons // Setup a tracing stage in which we log comparisons
let tracing = IfStage::new(|_, _, _, _, _| Ok(!$options.skip_tracing()), (TracingStage::new(InProcessExecutor::new( let tracing = IfStage::new(|_, _, _, _| Ok(!$options.skip_tracing()), (TracingStage::new(InProcessExecutor::new(
&mut tracing_harness, &mut tracing_harness,
tuple_list!(cmplog_observer), tuple_list!(cmplog_observer),
&mut fuzzer, &mut fuzzer,

View File

@ -6,7 +6,7 @@ use libafl::{
feedbacks::{MapFeedbackMetadata, MAPFEEDBACK_PREFIX}, feedbacks::{MapFeedbackMetadata, MAPFEEDBACK_PREFIX},
inputs::UsesInput, inputs::UsesInput,
monitors::SimpleMonitor, monitors::SimpleMonitor,
stages::StagesTuple, stages::{HasCurrentStage, StagesTuple},
state::{HasExecutions, HasLastReportTime, HasMetadata, HasNamedMetadata}, state::{HasExecutions, HasLastReportTime, HasMetadata, HasNamedMetadata},
Error, Fuzzer, Error, Fuzzer,
}; };
@ -24,7 +24,12 @@ fn do_report<F, ST, E, S, EM>(
) -> Result<(), Error> ) -> Result<(), Error>
where where
F: Fuzzer<E, EM, ST, State = S>, F: Fuzzer<E, EM, ST, State = S>,
S: HasMetadata + HasNamedMetadata + HasExecutions + UsesInput + HasLastReportTime, S: HasMetadata
+ HasNamedMetadata
+ HasExecutions
+ UsesInput
+ HasLastReportTime
+ HasCurrentStage,
E: HasObservers<State = S>, E: HasObservers<State = S>,
EM: ProgressReporter<State = S>, EM: ProgressReporter<State = S>,
ST: StagesTuple<E, EM, S, F>, ST: StagesTuple<E, EM, S, F>,

View File

@ -12,7 +12,6 @@ use core::{
use libafl::{ use libafl::{
executors::{inprocess::inprocess_get_state, ExitKind}, executors::{inprocess::inprocess_get_state, ExitKind},
inputs::UsesInput, inputs::UsesInput,
state::NopState,
}; };
pub use crate::emu::SyscallHookResult; pub use crate::emu::SyscallHookResult;
@ -363,36 +362,6 @@ where
} }
} }
impl<I, QT> QemuHooks<QT, NopState<I>>
where
QT: QemuHelperTuple<NopState<I>>,
NopState<I>: UsesInput<Input = I>,
{
pub fn reproducer(emulator: Emulator, helpers: QT) -> Box<Self> {
Self::new(emulator, helpers)
}
pub fn repro_run<H>(&mut self, harness: &mut H, input: &I) -> ExitKind
where
H: FnMut(&I) -> ExitKind,
{
unsafe {
if FIRST_EXEC {
self.helpers.first_exec_all(self);
FIRST_EXEC = false;
}
}
self.helpers.pre_exec_all(&self.emulator, input);
let mut exit_kind = harness(input);
self.helpers
.post_exec_all(&self.emulator, input, &mut (), &mut exit_kind);
exit_kind
}
}
impl<QT, S> QemuHooks<QT, S> impl<QT, S> QemuHooks<QT, S>
where where
QT: QemuHelperTuple<S>, QT: QemuHelperTuple<S>,

View File

@ -4,7 +4,7 @@ use core::marker::PhantomData;
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
use libafl::state::HasClientPerfMonitor; use libafl::state::HasClientPerfMonitor;
use libafl::{ use libafl::{
corpus::{Corpus, CorpusId}, corpus::{Corpus, HasCurrentCorpusIdx},
executors::{Executor, HasObservers}, executors::{Executor, HasObservers},
inputs::{BytesInput, UsesInput}, inputs::{BytesInput, UsesInput},
observers::ObserversTuple, observers::ObserversTuple,
@ -40,6 +40,8 @@ where
EM: UsesState<State = TE::State>, EM: UsesState<State = TE::State>,
Z: UsesState<State = TE::State>, Z: UsesState<State = TE::State>,
{ {
type Progress = (); // TODO this needs resumption
#[inline] #[inline]
fn perform( fn perform(
&mut self, &mut self,
@ -47,9 +49,11 @@ where
_executor: &mut E, _executor: &mut E,
state: &mut TE::State, state: &mut TE::State,
manager: &mut EM, manager: &mut EM,
corpus_idx: CorpusId,
) -> Result<(), Error> { ) -> Result<(), Error> {
// First run with the un-mutated input // First run with the un-mutated input
let corpus_idx = state.current_corpus_idx()?.ok_or_else(|| {
Error::illegal_state("state is not currently processing a corpus index")
})?;
let unmutated_input = state.corpus().cloned_input_for_id(corpus_idx)?; let unmutated_input = state.corpus().cloned_input_for_id(corpus_idx)?;