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:
parent
3d126f21cc
commit
ba8ca6723b
@ -137,13 +137,9 @@ pub fn main() -> Result<(), Error> {
|
||||
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])?;
|
||||
stages.perform_all(
|
||||
&mut fuzzer,
|
||||
&mut executor,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
CorpusId::from(0_usize),
|
||||
)?;
|
||||
|
||||
state.set_corpus_idx(CorpusId::from(0usize))?;
|
||||
stages.perform_all(&mut fuzzer, &mut executor, &mut state, &mut mgr)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use std::{
|
||||
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use libafl::{
|
||||
corpus::{Corpus, CorpusId, InMemoryOnDiskCorpus, OnDiskCorpus},
|
||||
corpus::{Corpus, HasCurrentCorpusIdx, InMemoryOnDiskCorpus, OnDiskCorpus},
|
||||
events::SimpleEventManager,
|
||||
executors::forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
|
||||
feedback_or,
|
||||
@ -374,9 +374,14 @@ fn fuzz(
|
||||
let cb = |_fuzzer: &mut _,
|
||||
_executor: &mut _,
|
||||
state: &mut StdState<_, InMemoryOnDiskCorpus<_>, _, _>,
|
||||
_event_manager: &mut _,
|
||||
corpus_id: CorpusId|
|
||||
-> Result<bool, libafl::Error> {
|
||||
_event_manager: &mut _|
|
||||
-> Result<bool, 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 res = corpus.scheduled_count() == 1; // let's try on the 2nd trial
|
||||
|
||||
|
@ -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`]
|
||||
#[derive(Debug)]
|
||||
pub struct CorpusIdIterator<'a, C>
|
||||
|
@ -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.
|
||||
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
|
||||
self.staterestorer.reset();
|
||||
self.staterestorer.save(&(
|
||||
|
@ -128,7 +128,7 @@ pub struct EventManagerId(
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
use crate::monitors::ClientPerfMonitor;
|
||||
use crate::{inputs::UsesInput, state::UsesState};
|
||||
use crate::{inputs::UsesInput, stages::HasCurrentStage, state::UsesState};
|
||||
|
||||
/// The log event severity
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||
@ -548,8 +548,11 @@ where
|
||||
/// Restartable trait
|
||||
pub trait EventRestarter: UsesState {
|
||||
/// 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]
|
||||
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();
|
||||
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.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct NopEventManager<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>
|
||||
where
|
||||
S: State,
|
||||
|
@ -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.
|
||||
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
|
||||
self.staterestorer.reset();
|
||||
self.staterestorer.save(&(
|
||||
|
@ -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.
|
||||
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
|
||||
self.staterestorer.reset();
|
||||
self.staterestorer.save(&if self.save_state {
|
||||
@ -856,6 +858,7 @@ where
|
||||
} else {
|
||||
None
|
||||
})?;
|
||||
|
||||
self.await_restart_safe();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -690,10 +690,10 @@ mod tests {
|
||||
command::{CommandExecutor, InputLocation},
|
||||
Executor,
|
||||
},
|
||||
fuzzer::test::NopFuzzer,
|
||||
inputs::BytesInput,
|
||||
monitors::SimpleMonitor,
|
||||
state::NopState,
|
||||
NopFuzzer,
|
||||
state::test::NopState,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
@ -2186,9 +2186,9 @@ mod tests {
|
||||
use crate::{
|
||||
events::NopEventManager,
|
||||
executors::{inprocess::InProcessHandlers, Executor, ExitKind, InProcessExecutor},
|
||||
fuzzer::test::NopFuzzer,
|
||||
inputs::{NopInput, UsesInput},
|
||||
state::NopState,
|
||||
NopFuzzer,
|
||||
state::test::NopState,
|
||||
};
|
||||
|
||||
impl UsesInput for () {
|
||||
@ -2226,8 +2226,8 @@ mod tests {
|
||||
use crate::{
|
||||
events::SimpleEventManager,
|
||||
executors::{inprocess::InChildProcessHandlers, InProcessForkExecutor},
|
||||
state::NopState,
|
||||
NopFuzzer,
|
||||
fuzzer::test::NopFuzzer,
|
||||
state::test::NopState,
|
||||
};
|
||||
|
||||
let provider = StdShMemProvider::new().unwrap();
|
||||
|
@ -1,49 +1,41 @@
|
||||
//! 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;
|
||||
#[cfg(all(feature = "std", feature = "fork", unix))]
|
||||
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 use differential::DiffExecutor;
|
||||
|
||||
#[cfg(all(feature = "std", feature = "fork", unix))]
|
||||
pub mod forkserver;
|
||||
pub mod inprocess;
|
||||
pub mod shadow;
|
||||
/// Timeout executor.
|
||||
/// Not possible on `no-std` Windows or `no-std`, but works for unix
|
||||
#[cfg(any(unix, feature = "std"))]
|
||||
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 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.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
@ -149,27 +141,56 @@ where
|
||||
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> {
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use libafl_bolts::{AsSlice, Error};
|
||||
|
||||
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> UsesState for NopExecutor<S>
|
||||
where
|
||||
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
|
||||
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,
|
||||
@ -185,22 +206,13 @@ where
|
||||
Ok(ExitKind::Ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use super::{Executor, NopExecutor};
|
||||
use crate::{events::NopEventManager, inputs::BytesInput, state::NopState, NopFuzzer};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nop_executor() {
|
||||
let empty_input = BytesInput::new(vec![]);
|
||||
let nonempty_input = BytesInput::new(vec![1u8]);
|
||||
let mut executor = NopExecutor {
|
||||
phantom: PhantomData,
|
||||
};
|
||||
let mut executor = NopExecutor::new();
|
||||
let mut fuzzer = NopFuzzer::new();
|
||||
|
||||
let mut state = NopState::new();
|
||||
|
@ -163,7 +163,7 @@ mod tests {
|
||||
feedbacks::{differential::DiffResult, DiffFeedback, Feedback},
|
||||
inputs::{BytesInput, UsesInput},
|
||||
observers::Observer,
|
||||
state::{NopState, State, UsesState},
|
||||
state::{test::NopState, State, UsesState},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -6,12 +6,8 @@ use core::{fmt::Debug, marker::PhantomData, time::Duration};
|
||||
use libafl_bolts::current_time;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::inputs::Input;
|
||||
#[cfg(test)]
|
||||
use crate::state::NopState;
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId, HasTestcase, Testcase},
|
||||
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, HasTestcase, Testcase},
|
||||
events::{Event, EventConfig, EventFirer, EventProcessor, ProgressReporter},
|
||||
executors::{Executor, ExitKind, HasObservers},
|
||||
feedbacks::Feedback,
|
||||
@ -19,7 +15,7 @@ use crate::{
|
||||
mark_feature_time,
|
||||
observers::ObserversTuple,
|
||||
schedulers::Scheduler,
|
||||
stages::StagesTuple,
|
||||
stages::{HasCurrentStage, StagesTuple},
|
||||
start_timer,
|
||||
state::{
|
||||
HasCorpus, HasExecutions, HasImported, HasLastReportTime, HasMetadata, HasSolutions,
|
||||
@ -583,8 +579,14 @@ where
|
||||
EM: ProgressReporter + EventProcessor<E, Self, State = CS::State>,
|
||||
F: Feedback<CS::State>,
|
||||
OF: Feedback<CS::State>,
|
||||
CS::State:
|
||||
HasExecutions + HasMetadata + HasCorpus + HasTestcase + HasImported + HasLastReportTime,
|
||||
CS::State: HasExecutions
|
||||
+ HasMetadata
|
||||
+ HasCorpus
|
||||
+ HasTestcase
|
||||
+ HasImported
|
||||
+ HasLastReportTime
|
||||
+ HasCurrentCorpusIdx
|
||||
+ HasCurrentStage,
|
||||
ST: StagesTuple<E, EM, CS::State, Self>,
|
||||
{
|
||||
fn fuzz_one(
|
||||
@ -599,7 +601,13 @@ where
|
||||
state.introspection_monitor_mut().start_timer();
|
||||
|
||||
// Get the next index from the scheduler
|
||||
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
|
||||
#[cfg(feature = "introspection")]
|
||||
@ -610,7 +618,7 @@ where
|
||||
state.introspection_monitor_mut().reset_stage_index();
|
||||
|
||||
// Execute all stages
|
||||
stages.perform_all(self, executor, state, manager, idx)?;
|
||||
stages.perform_all(self, executor, state, manager)?;
|
||||
|
||||
// Init timer for manager
|
||||
#[cfg(feature = "introspection")]
|
||||
@ -629,6 +637,9 @@ where
|
||||
// increase scheduled count, this was fuzz_level in afl
|
||||
testcase.set_scheduled_count(scheduled_count + 1);
|
||||
}
|
||||
|
||||
state.clear_corpus_idx()?;
|
||||
|
||||
Ok(idx)
|
||||
}
|
||||
}
|
||||
@ -733,36 +744,53 @@ where
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct NopFuzzer<I> {
|
||||
phantom: PhantomData<I>,
|
||||
}
|
||||
pub mod test {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
#[cfg(test)]
|
||||
impl<I> NopFuzzer<I> {
|
||||
use libafl_bolts::Error;
|
||||
|
||||
use crate::{
|
||||
corpus::CorpusId,
|
||||
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<I> UsesState for NopFuzzer<I>
|
||||
where
|
||||
I: Input,
|
||||
{
|
||||
type State = NopState<I>;
|
||||
}
|
||||
impl<S> Default for NopFuzzer<S> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<ST, E, I, EM> Fuzzer<E, EM, ST> for NopFuzzer<I>
|
||||
where
|
||||
E: UsesState<State = NopState<I>>,
|
||||
EM: ProgressReporter<State = NopState<I>>,
|
||||
I: Input,
|
||||
ST: StagesTuple<E, EM, NopState<I>, Self>,
|
||||
{
|
||||
impl<S> UsesState for NopFuzzer<S>
|
||||
where
|
||||
S: State,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<ST, E, EM> Fuzzer<E, EM, ST> for NopFuzzer<E::State>
|
||||
where
|
||||
E: UsesState,
|
||||
EM: ProgressReporter<State = E::State>,
|
||||
ST: StagesTuple<E, EM, E::State, Self>,
|
||||
E::State: HasMetadata + HasExecutions + HasLastReportTime + HasCurrentStage,
|
||||
{
|
||||
fn fuzz_one(
|
||||
&mut self,
|
||||
_stages: &mut ST,
|
||||
@ -772,6 +800,7 @@ where
|
||||
) -> Result<CorpusId, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
|
@ -392,7 +392,7 @@ where
|
||||
write!(
|
||||
f,
|
||||
"StdMOptMutator with {} mutations for Input type {}",
|
||||
self.mutations.len(),
|
||||
MT::LEN,
|
||||
core::any::type_name::<I>()
|
||||
)
|
||||
}
|
||||
@ -547,7 +547,7 @@ where
|
||||
) -> Result<Self, Error> {
|
||||
if !state.has_metadata::<MOpt>() {
|
||||
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 {
|
||||
name: format!("StdMOptMutator[{}]", mutations.names().join(",")),
|
||||
|
@ -1396,7 +1396,7 @@ pub fn str_decode(item: &str) -> Result<Vec<u8>, Error> {
|
||||
mod tests {
|
||||
use libafl_bolts::{
|
||||
rands::StdRand,
|
||||
tuples::{tuple_list, HasConstLen},
|
||||
tuples::{tuple_list, tuple_list_type, HasConstLen},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
@ -1408,11 +1408,34 @@ mod tests {
|
||||
state::{HasMetadata, StdState},
|
||||
};
|
||||
|
||||
fn test_mutations<I, S>() -> impl MutatorsTuple<I, S>
|
||||
where
|
||||
S: HasRand + HasMetadata + HasMaxSize,
|
||||
I: HasBytesVec,
|
||||
{
|
||||
type TestMutatorsTupleType = tuple_list_type!(
|
||||
BitFlipMutator,
|
||||
ByteFlipMutator,
|
||||
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!(
|
||||
BitFlipMutator::new(),
|
||||
ByteFlipMutator::new(),
|
||||
@ -1481,7 +1504,7 @@ mod tests {
|
||||
|
||||
for _ in 0..2 {
|
||||
let mut new_testcases = vec![];
|
||||
for idx in 0..(mutations.len()) {
|
||||
for idx in 0..TestMutatorsTupleType::LEN {
|
||||
for input in &inputs {
|
||||
let mut mutant = input.clone();
|
||||
match mutations
|
||||
|
@ -135,7 +135,7 @@ where
|
||||
write!(
|
||||
f,
|
||||
"StdScheduledMutator with {} mutations for Input type {}",
|
||||
self.mutations.len(),
|
||||
MT::LEN,
|
||||
core::any::type_name::<I>()
|
||||
)
|
||||
}
|
||||
@ -197,8 +197,8 @@ where
|
||||
|
||||
/// Get the next mutation to apply
|
||||
fn schedule(&self, state: &mut S, _: &I) -> MutationId {
|
||||
debug_assert!(!self.mutations().is_empty());
|
||||
state.rand_mut().below(self.mutations().len() as u64).into()
|
||||
debug_assert!(MT::LEN != 0);
|
||||
state.rand_mut().below(MT::LEN as u64).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,7 +368,7 @@ where
|
||||
write!(
|
||||
f,
|
||||
"LoggerScheduledMutator with {} mutations for Input type {}",
|
||||
self.scheduled.mutations().len(),
|
||||
MT::LEN,
|
||||
core::any::type_name::<I>()
|
||||
)
|
||||
}
|
||||
@ -452,11 +452,8 @@ where
|
||||
|
||||
/// Get the next mutation to apply
|
||||
fn schedule(&self, state: &mut S, _: &I) -> MutationId {
|
||||
debug_assert!(!self.scheduled.mutations().is_empty());
|
||||
state
|
||||
.rand_mut()
|
||||
.below(self.scheduled.mutations().len() as u64)
|
||||
.into()
|
||||
debug_assert!(MT::LEN != 0);
|
||||
state.rand_mut().below(MT::LEN as u64).into()
|
||||
}
|
||||
|
||||
fn scheduled_mutate(
|
||||
|
@ -100,7 +100,7 @@ where
|
||||
write!(
|
||||
f,
|
||||
"TuneableScheduledMutator with {} mutations for Input type {}",
|
||||
self.mutations.len(),
|
||||
MT::LEN,
|
||||
core::any::type_name::<I>()
|
||||
)
|
||||
}
|
||||
@ -186,7 +186,7 @@ where
|
||||
|
||||
/// Get the next mutation to apply
|
||||
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.
|
||||
let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap();
|
||||
|
||||
@ -199,7 +199,7 @@ where
|
||||
metadata.next_id = 0.into();
|
||||
}
|
||||
debug_assert!(
|
||||
self.mutations().len() > ret.0,
|
||||
MT::LEN > ret.0,
|
||||
"TuneableScheduler: next vec may not contain id larger than number of mutations!"
|
||||
);
|
||||
return ret;
|
||||
@ -214,7 +214,7 @@ where
|
||||
|
||||
let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap();
|
||||
debug_assert_eq!(
|
||||
self.mutations.len(),
|
||||
MT::LEN,
|
||||
metadata.mutation_probabilities_cumulative.len(),
|
||||
"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.
|
||||
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::{
|
||||
inputs::BytesInput,
|
||||
mutators::{ByteRandMutator, ScheduledMutator},
|
||||
state::NopState,
|
||||
state::test::NopState,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
@ -12,7 +12,7 @@ use num_traits::Bounded;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId, SchedulerTestcaseMetadata},
|
||||
corpus::{Corpus, HasCurrentCorpusIdx, SchedulerTestcaseMetadata},
|
||||
events::{Event, EventFirer, LogSeverity},
|
||||
executors::{Executor, ExitKind, HasObservers},
|
||||
feedbacks::{map::MapFeedbackMetadata, HasObserverName},
|
||||
@ -92,6 +92,8 @@ where
|
||||
E::State: HasCorpus + HasMetadata + HasNamedMetadata + HasExecutions,
|
||||
Z: Evaluator<E, EM, State = E::State>,
|
||||
{
|
||||
type Progress = (); // TODO stage may be resumed, but how?
|
||||
|
||||
#[inline]
|
||||
#[allow(
|
||||
clippy::let_and_return,
|
||||
@ -104,8 +106,13 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut E::State,
|
||||
mgr: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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
|
||||
{
|
||||
let corpus = state.corpus().get(corpus_idx)?.borrow();
|
||||
|
@ -10,7 +10,7 @@ use libafl_bolts::{rands::Rand, tuples::MatchName};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId},
|
||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
||||
events::EventFirer,
|
||||
executors::{Executor, HasObservers},
|
||||
inputs::HasBytesVec,
|
||||
@ -77,6 +77,8 @@ where
|
||||
O: MapObserver,
|
||||
Z: UsesState<State = E::State>,
|
||||
{
|
||||
type Progress = (); // TODO this stage needs resume
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::let_and_return)]
|
||||
fn perform(
|
||||
@ -85,17 +87,9 @@ where
|
||||
executor: &mut E, // don't need the *main* executor for tracing
|
||||
state: &mut E::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
// Run with the mutated input
|
||||
Self::colorize(
|
||||
fuzzer,
|
||||
executor,
|
||||
state,
|
||||
manager,
|
||||
corpus_idx,
|
||||
&self.map_observer_name,
|
||||
)?;
|
||||
Self::colorize(fuzzer, executor, state, manager, &self.map_observer_name)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -156,9 +150,14 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut E::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
name: &str,
|
||||
) -> 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)?;
|
||||
// The backup of the input
|
||||
let backup = input.clone();
|
||||
|
@ -13,7 +13,7 @@ use crate::state::HasClientPerfMonitor;
|
||||
#[cfg(feature = "concolic_mutation")]
|
||||
use crate::state::State;
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId},
|
||||
corpus::Corpus,
|
||||
executors::{Executor, HasObservers},
|
||||
observers::concolic::ConcolicObserver,
|
||||
state::{HasCorpus, HasExecutions, HasMetadata},
|
||||
@ -42,6 +42,8 @@ where
|
||||
TE::State: HasExecutions + HasCorpus,
|
||||
Z: UsesState<State = TE::State>,
|
||||
{
|
||||
type Progress = (); // stage cannot be resumed
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -49,10 +51,14 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut TE::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
self.inner
|
||||
.perform(fuzzer, executor, state, manager, corpus_idx)?;
|
||||
let Some(corpus_idx) = state.current_corpus_idx()? else {
|
||||
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
|
||||
.inner
|
||||
.executor()
|
||||
@ -86,7 +92,7 @@ use libafl_bolts::tuples::MatchName;
|
||||
|
||||
#[cfg(all(feature = "concolic_mutation", feature = "introspection"))]
|
||||
use crate::monitors::PerfFeature;
|
||||
use crate::state::UsesState;
|
||||
use crate::{corpus::HasCurrentCorpusIdx, state::UsesState};
|
||||
#[cfg(feature = "concolic_mutation")]
|
||||
use crate::{
|
||||
inputs::HasBytesVec,
|
||||
@ -360,6 +366,8 @@ where
|
||||
Z::Input: HasBytesVec,
|
||||
Z::State: State + HasExecutions + HasCorpus,
|
||||
{
|
||||
type Progress = (); // TODO we need a resume for this type
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -367,8 +375,13 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut Z::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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);
|
||||
let testcase = state.corpus().get(corpus_idx)?.clone();
|
||||
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
||||
@ -391,7 +404,6 @@ where
|
||||
input_copy.bytes_mut()[index] = new_byte;
|
||||
}
|
||||
// Time is measured directly the `evaluate_input` function
|
||||
let _: (crate::ExecuteInputResult, Option<CorpusId>) =
|
||||
fuzzer.evaluate_input(state, executor, manager, input_copy)?;
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ where
|
||||
Z: UsesState,
|
||||
Z::State: HasCorpus + HasSolutions + HasRand + HasMetadata,
|
||||
{
|
||||
type Progress = (); // if this fails, we have bigger problems
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -59,7 +61,6 @@ where
|
||||
_executor: &mut E,
|
||||
state: &mut Z::State,
|
||||
_manager: &mut EM,
|
||||
_corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
let (mut corpus_idx, mut solutions_idx) =
|
||||
if let Some(meta) = state.metadata_map().get::<DumpToDiskMetadata>() {
|
||||
|
@ -9,7 +9,7 @@ use core::{fmt::Debug, marker::PhantomData};
|
||||
use libafl_bolts::AsSlice;
|
||||
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId},
|
||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
||||
executors::{Executor, HasObservers},
|
||||
feedbacks::map::MapNoveltiesMetadata,
|
||||
inputs::{BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasBytesVec, UsesInput},
|
||||
@ -64,6 +64,8 @@ where
|
||||
EM: UsesState<State = E::State>,
|
||||
Z: UsesState<State = E::State>,
|
||||
{
|
||||
type Progress = (); // TODO this stage needs a resume
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn perform(
|
||||
@ -72,8 +74,13 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut E::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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) = {
|
||||
start_timer!(state);
|
||||
{
|
||||
|
@ -3,17 +3,43 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
corpus::CorpusId,
|
||||
stages::{Stage, StagesTuple},
|
||||
stages::{HasCurrentStage, HasNestedStageStatus, Stage, StageProgress, StagesTuple},
|
||||
state::UsesState,
|
||||
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)]
|
||||
/// Perform the stage while the closure evaluates to true
|
||||
pub struct WhileStage<CB, E, EM, ST, Z>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
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>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
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>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
ST: StagesTuple<E, EM, E::State, Z>,
|
||||
Z: UsesState<State = E::State>,
|
||||
E::State: HasNestedStageStatus,
|
||||
{
|
||||
type Progress = NestedStageProgress;
|
||||
|
||||
fn perform(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
executor: &mut E,
|
||||
state: &mut E::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
while (self.closure)(fuzzer, executor, state, manager, corpus_idx)? {
|
||||
self.stages
|
||||
.perform_all(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)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<CB, E, EM, ST, Z> WhileStage<CB, E, EM, ST, Z>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
ST: StagesTuple<E, EM, E::State, Z>,
|
||||
@ -82,7 +111,7 @@ where
|
||||
#[derive(Debug)]
|
||||
pub struct IfStage<CB, E, EM, ST, Z>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
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>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
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>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
ST: StagesTuple<E, EM, E::State, Z>,
|
||||
Z: UsesState<State = E::State>,
|
||||
E::State: HasNestedStageStatus,
|
||||
{
|
||||
type Progress = NestedStageProgress;
|
||||
|
||||
fn perform(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
executor: &mut E,
|
||||
state: &mut E::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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
|
||||
.perform_all(fuzzer, executor, state, manager, corpus_idx)?;
|
||||
.perform_all(fuzzer, executor, state, manager)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -130,7 +161,7 @@ where
|
||||
|
||||
impl<CB, E, EM, ST, Z> IfStage<CB, E, EM, ST, Z>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
ST: StagesTuple<E, EM, E::State, Z>,
|
||||
@ -151,7 +182,7 @@ where
|
||||
#[derive(Debug)]
|
||||
pub struct IfElseStage<CB, E, EM, ST1, ST2, Z>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
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>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
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>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
ST1: StagesTuple<E, EM, E::State, Z>,
|
||||
ST2: StagesTuple<E, EM, E::State, Z>,
|
||||
Z: UsesState<State = E::State>,
|
||||
E::State: HasNestedStageStatus,
|
||||
{
|
||||
type Progress = NestedStageProgress;
|
||||
|
||||
fn perform(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
executor: &mut E,
|
||||
state: &mut E::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
if (self.closure)(fuzzer, executor, state, manager, corpus_idx)? {
|
||||
self.if_stages
|
||||
.perform_all(fuzzer, executor, state, manager, corpus_idx)?;
|
||||
} else {
|
||||
self.else_stages
|
||||
.perform_all(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
|
||||
.perform_all(fuzzer, executor, state, manager)?;
|
||||
} else {
|
||||
if fresh {
|
||||
state.set_stage(1)?;
|
||||
}
|
||||
state.enter_inner_stage()?;
|
||||
self.else_stages
|
||||
.perform_all(fuzzer, executor, state, manager)?;
|
||||
}
|
||||
|
||||
state.exit_inner_stage()?;
|
||||
state.clear_stage()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<CB, E, EM, ST1, ST2, Z> IfElseStage<CB, E, EM, ST1, ST2, Z>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
ST1: StagesTuple<E, EM, E::State, Z>,
|
||||
@ -253,17 +303,19 @@ where
|
||||
EM: UsesState<State = E::State>,
|
||||
ST: StagesTuple<E, EM, E::State, Z>,
|
||||
Z: UsesState<State = E::State>,
|
||||
E::State: HasNestedStageStatus,
|
||||
{
|
||||
type Progress = ();
|
||||
|
||||
fn perform(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
executor: &mut E,
|
||||
state: &mut E::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(stages) = &mut self.stages {
|
||||
stages.perform_all(fuzzer, executor, state, manager, corpus_idx)
|
||||
stages.perform_all(fuzzer, executor, state, manager)
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
/// Mutational stage is the normal fuzzing stage.
|
||||
pub mod mutational;
|
||||
pub use mutational::{MutationalStage, StdMutationalStage};
|
||||
use core::{convert::From, marker::PhantomData};
|
||||
|
||||
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 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::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod concolic;
|
||||
#[cfg(feature = "std")]
|
||||
pub use concolic::ConcolicTracingStage;
|
||||
#[cfg(feature = "std")]
|
||||
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")]
|
||||
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 crate::{
|
||||
corpus::CorpusId,
|
||||
corpus::HasCurrentCorpusIdx,
|
||||
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
|
||||
executors::{Executor, HasObservers},
|
||||
inputs::UsesInput,
|
||||
@ -79,6 +42,28 @@ use crate::{
|
||||
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.
|
||||
/// Multiple stages will be scheduled one by one for each input.
|
||||
pub trait Stage<E, EM, Z>: UsesState
|
||||
@ -87,6 +72,12 @@ where
|
||||
EM: 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
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -94,7 +85,6 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut Self::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
@ -104,7 +94,7 @@ where
|
||||
E: UsesState<State = S>,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
S: UsesInput,
|
||||
S: UsesInput + HasCurrentStage,
|
||||
{
|
||||
/// Performs all `Stages` in this tuple
|
||||
fn perform_all(
|
||||
@ -113,7 +103,6 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut S,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
@ -122,27 +111,33 @@ where
|
||||
E: UsesState<State = S>,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
S: UsesInput,
|
||||
S: UsesInput + HasCurrentStage,
|
||||
{
|
||||
fn perform_all(
|
||||
&mut self,
|
||||
_: &mut Z,
|
||||
_: &mut E,
|
||||
_: &mut S,
|
||||
stage: &mut S,
|
||||
_: &mut EM,
|
||||
_: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
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)
|
||||
where
|
||||
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>,
|
||||
EM: UsesState<State = Head::State>,
|
||||
Z: UsesState<State = Head::State>,
|
||||
Head::State: HasCurrentStage,
|
||||
{
|
||||
fn perform_all(
|
||||
&mut self,
|
||||
@ -150,15 +145,33 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut Head::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
// Perform the current stage
|
||||
self.0
|
||||
.perform(fuzzer, executor, state, manager, corpus_idx)?;
|
||||
match state.current_stage()? {
|
||||
Some(idx) if idx < Self::LEN => {
|
||||
// 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
|
||||
self.1
|
||||
.perform_all(fuzzer, executor, state, manager, corpus_idx)
|
||||
self.1.perform_all(fuzzer, executor, state, manager)
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,7 +179,7 @@ where
|
||||
#[derive(Debug)]
|
||||
pub struct ClosureStage<CB, E, EM, Z>
|
||||
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,
|
||||
{
|
||||
closure: CB,
|
||||
@ -175,7 +188,7 @@ where
|
||||
|
||||
impl<CB, E, EM, Z> UsesState for ClosureStage<CB, E, EM, Z>
|
||||
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,
|
||||
{
|
||||
type State = E::State;
|
||||
@ -183,27 +196,28 @@ where
|
||||
|
||||
impl<CB, E, EM, Z> Stage<E, EM, Z> for ClosureStage<CB, E, EM, Z>
|
||||
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,
|
||||
EM: UsesState<State = E::State>,
|
||||
Z: UsesState<State = E::State>,
|
||||
{
|
||||
type Progress = ();
|
||||
|
||||
fn perform(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
executor: &mut E,
|
||||
state: &mut E::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
(self.closure)(fuzzer, executor, state, manager, corpus_idx)
|
||||
(self.closure)(fuzzer, executor, state, manager)
|
||||
}
|
||||
}
|
||||
|
||||
/// A stage that takes a closure
|
||||
impl<CB, E, EM, Z> ClosureStage<CB, E, EM, Z>
|
||||
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,
|
||||
{
|
||||
/// Create a new [`ClosureStage`]
|
||||
@ -218,7 +232,7 @@ where
|
||||
|
||||
impl<CB, E, EM, Z> From<CB> for ClosureStage<CB, E, EM, Z>
|
||||
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,
|
||||
{
|
||||
#[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>
|
||||
where
|
||||
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>,
|
||||
EM: EventFirer<State = CS::State>
|
||||
+ EventRestarter
|
||||
@ -270,16 +285,23 @@ where
|
||||
+ EvaluatorObservers<OT>
|
||||
+ HasScheduler<Scheduler = CS>,
|
||||
{
|
||||
type Progress = (); // TODO implement resume
|
||||
|
||||
fn perform(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
executor: &mut E,
|
||||
state: &mut CS::State,
|
||||
event_mgr: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
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.init(fuzzer, state, event_mgr, executor.observers_mut())?;
|
||||
@ -318,11 +340,13 @@ pub mod pybind {
|
||||
use pyo3::prelude::*;
|
||||
|
||||
use crate::{
|
||||
corpus::CorpusId,
|
||||
corpus::HasCurrentCorpusIdx,
|
||||
events::pybind::PythonEventManager,
|
||||
executors::pybind::PythonExecutor,
|
||||
fuzzer::pybind::{PythonStdFuzzer, PythonStdFuzzerWrapper},
|
||||
stages::{mutational::pybind::PythonStdMutationalStage, Stage, StagesTuple},
|
||||
stages::{
|
||||
mutational::pybind::PythonStdMutationalStage, HasCurrentStage, Stage, StagesTuple,
|
||||
},
|
||||
state::{
|
||||
pybind::{PythonStdState, PythonStdStateWrapper},
|
||||
UsesState,
|
||||
@ -347,6 +371,8 @@ pub mod pybind {
|
||||
}
|
||||
|
||||
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PyObjectStage {
|
||||
type Progress = (); // we don't support resumption in python, and maybe can't?
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -354,8 +380,13 @@ pub mod pybind {
|
||||
executor: &mut PythonExecutor,
|
||||
state: &mut PythonStdState,
|
||||
manager: &mut PythonEventManager,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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<()> {
|
||||
self.inner.call_method1(
|
||||
py,
|
||||
@ -435,6 +466,9 @@ pub mod pybind {
|
||||
}
|
||||
|
||||
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PythonStage {
|
||||
// TODO if we implement resumption for StdMutational, we need to apply it here
|
||||
type Progress = ();
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::let_and_return)]
|
||||
fn perform(
|
||||
@ -443,10 +477,9 @@ pub mod pybind {
|
||||
executor: &mut PythonExecutor,
|
||||
state: &mut PythonStdState,
|
||||
manager: &mut PythonEventManager,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
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,
|
||||
state: &mut PythonStdState,
|
||||
manager: &mut PythonEventManager,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
for s in &mut self.list {
|
||||
s.perform(fuzzer, executor, state, manager, corpus_idx)?;
|
||||
for (i, s) in self.list.iter_mut().enumerate() {
|
||||
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(())
|
||||
}
|
||||
@ -498,3 +539,265 @@ pub mod pybind {
|
||||
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."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use core::marker::PhantomData;
|
||||
use libafl_bolts::rands::Rand;
|
||||
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId, Testcase},
|
||||
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, Testcase},
|
||||
fuzzer::Evaluator,
|
||||
inputs::Input,
|
||||
mark_feature_time,
|
||||
@ -114,8 +114,13 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut Z::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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)?;
|
||||
|
||||
start_timer!(state);
|
||||
@ -211,6 +216,8 @@ where
|
||||
Z::State: HasCorpus + HasRand,
|
||||
I: MutatedTransform<Self::Input, Self::State> + Clone,
|
||||
{
|
||||
type Progress = (); // TODO should this stage be resumed?
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::let_and_return)]
|
||||
fn perform(
|
||||
@ -219,9 +226,8 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut Z::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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")]
|
||||
state.introspection_monitor_mut().finish_stage();
|
||||
@ -300,6 +306,8 @@ where
|
||||
Z::State: HasCorpus + HasRand,
|
||||
I: MutatedTransform<Self::Input, Self::State> + Clone,
|
||||
{
|
||||
type Progress = (); // TODO implement resume
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::let_and_return)]
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
@ -309,8 +317,13 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut Z::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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 Ok(input) = I::try_transform_from(&mut testcase, state, corpus_idx) else {
|
||||
return Ok(());
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
@ -71,6 +71,8 @@ where
|
||||
Z: Evaluator<E, EM, State = E::State>,
|
||||
I: MutatedTransform<E::Input, E::State> + Clone,
|
||||
{
|
||||
type Progress = (); // TODO should we resume this stage?
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::let_and_return)]
|
||||
fn perform(
|
||||
@ -79,9 +81,8 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut E::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
let ret = self.perform_mutational(fuzzer, executor, state, manager, corpus_idx);
|
||||
let ret = self.perform_mutational(fuzzer, executor, state, manager);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use libafl_bolts::current_time;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId},
|
||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
||||
events::EventFirer,
|
||||
schedulers::minimizer::IsFavoredMetadata,
|
||||
stages::Stage,
|
||||
@ -62,14 +62,21 @@ where
|
||||
Z: UsesState<State = E::State>,
|
||||
E::State: HasImported + HasCorpus + HasMetadata,
|
||||
{
|
||||
type Progress = (); // this stage does not require resume
|
||||
|
||||
fn perform(
|
||||
&mut self,
|
||||
_fuzzer: &mut Z,
|
||||
_executor: &mut E,
|
||||
state: &mut E::State,
|
||||
_manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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`
|
||||
// compute pending, pending_favored, imported, own_finds
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ use libafl_bolts::{impl_serdeany, Error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
corpus::{CorpusId, HasTestcase},
|
||||
corpus::HasTestcase,
|
||||
inputs::{BytesInput, HasBytesVec},
|
||||
stages::Stage,
|
||||
state::{HasCorpus, HasMetadata, State, UsesState},
|
||||
@ -104,14 +104,21 @@ where
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
type Progress = (); // this stage does not need to be resumed
|
||||
|
||||
fn perform(
|
||||
&mut self,
|
||||
_fuzzer: &mut Z,
|
||||
_executor: &mut E,
|
||||
state: &mut Self::State,
|
||||
_manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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)?;
|
||||
if tc.has_metadata::<StringIdentificationMetadata>() {
|
||||
return Ok(()); // skip recompute
|
||||
|
@ -67,6 +67,8 @@ where
|
||||
Z: Evaluator<E, EM>,
|
||||
Z::State: HasCorpus + HasRand + HasMetadata,
|
||||
{
|
||||
type Progress = (); // TODO load from directory should be resumed
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -74,7 +76,6 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut Z::State,
|
||||
manager: &mut EM,
|
||||
_corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
let last = state
|
||||
.metadata_map()
|
||||
@ -253,6 +254,8 @@ where
|
||||
ICB: InputConverter<From = DI, To = S::Input>,
|
||||
DI: Input,
|
||||
{
|
||||
type Progress = (); // TODO this should be resumed in the case that a testcase causes a crash
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -260,7 +263,6 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut Z::State,
|
||||
manager: &mut EM,
|
||||
_corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
if self.client.can_convert() {
|
||||
let last_id = state
|
||||
|
@ -7,7 +7,7 @@ use ahash::RandomState;
|
||||
use libafl_bolts::{HasLen, Named};
|
||||
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId, Testcase},
|
||||
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, Testcase},
|
||||
events::EventFirer,
|
||||
executors::{Executor, ExitKind, HasObservers},
|
||||
feedbacks::{Feedback, FeedbackFactory, HasObserverName},
|
||||
@ -61,8 +61,13 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut CS::State,
|
||||
manager: &mut EM,
|
||||
base_corpus_idx: CorpusId,
|
||||
) -> 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();
|
||||
// basically copy-pasted from mutational.rs
|
||||
let num = self.iterations(state, base_corpus_idx)?;
|
||||
@ -209,15 +214,16 @@ where
|
||||
+ HasFeedback<Feedback = F1>
|
||||
+ HasScheduler<Scheduler = CS>,
|
||||
{
|
||||
type Progress = (); // TODO this stage desperately needs a resume
|
||||
|
||||
fn perform(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
executor: &mut E,
|
||||
state: &mut CS::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
self.perform_minification(fuzzer, executor, state, manager, corpus_idx)?;
|
||||
self.perform_minification(fuzzer, executor, state, manager)?;
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
state.introspection_monitor_mut().finish_stage();
|
||||
|
@ -3,7 +3,7 @@
|
||||
use core::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId},
|
||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
||||
executors::{Executor, HasObservers, ShadowExecutor},
|
||||
mark_feature_time,
|
||||
observers::ObserversTuple,
|
||||
@ -38,6 +38,8 @@ where
|
||||
EM: UsesState<State = TE::State>,
|
||||
Z: UsesState<State = TE::State>,
|
||||
{
|
||||
type Progress = (); // this stage cannot be resumed
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -45,8 +47,13 @@ where
|
||||
_executor: &mut E,
|
||||
state: &mut TE::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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);
|
||||
let input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
||||
|
||||
@ -117,6 +124,8 @@ where
|
||||
Z: UsesState<State = E::State>,
|
||||
E::State: State + HasExecutions + HasCorpus + Debug,
|
||||
{
|
||||
type Progress = (); // this stage cannot be resumed
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -124,8 +133,13 @@ where
|
||||
executor: &mut ShadowExecutor<E, SOT>,
|
||||
state: &mut E::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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);
|
||||
let input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
||||
|
||||
|
@ -7,7 +7,7 @@ use libafl_bolts::{current_time, impl_serdeany, rands::Rand};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId},
|
||||
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx},
|
||||
mark_feature_time,
|
||||
mutators::{MutationResult, Mutator},
|
||||
stages::{
|
||||
@ -173,8 +173,13 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut Z::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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 iters = self.fixed_iters(state)?;
|
||||
|
||||
@ -269,6 +274,8 @@ where
|
||||
Z::State: HasCorpus + HasRand + HasNamedMetadata + HasMetadata,
|
||||
I: MutatedTransform<Z::Input, Z::State> + Clone,
|
||||
{
|
||||
type Progress = (); // TODO should this stage be resumed?
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::let_and_return)]
|
||||
fn perform(
|
||||
@ -277,9 +284,8 @@ where
|
||||
executor: &mut E,
|
||||
state: &mut Z::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> 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")]
|
||||
state.introspection_monitor_mut().finish_stage();
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! The fuzzer, and state are the core pieces of every good fuzzer
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::{
|
||||
cell::{Ref, RefMut},
|
||||
fmt::Debug,
|
||||
@ -10,11 +11,10 @@ use core::{
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use libafl_bolts::{
|
||||
rands::{Rand, StdRand},
|
||||
rands::Rand,
|
||||
serdeany::{NamedSerdeAnyMap, SerdeAny, SerdeAnyMap},
|
||||
};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
@ -24,12 +24,13 @@ use crate::monitors::ClientPerfMonitor;
|
||||
#[cfg(feature = "scalability_introspection")]
|
||||
use crate::monitors::ScalabilityMonitor;
|
||||
use crate::{
|
||||
corpus::{Corpus, CorpusId, HasTestcase, Testcase},
|
||||
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, HasTestcase, Testcase},
|
||||
events::{Event, EventFirer, LogSeverity},
|
||||
feedbacks::Feedback,
|
||||
fuzzer::{Evaluator, ExecuteInputResult},
|
||||
generators::Generator,
|
||||
inputs::{Input, UsesInput},
|
||||
stages::{HasCurrentStage, HasNestedStageStatus},
|
||||
Error,
|
||||
};
|
||||
|
||||
@ -40,7 +41,13 @@ pub const DEFAULT_MAX_SIZE: usize = 1_048_576;
|
||||
/// Contains all important information about the current run.
|
||||
/// Will be used to restart the fuzzing process at any time.
|
||||
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).
|
||||
/// This information is used by fuzzer `maybe_report_progress`.
|
||||
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>,
|
||||
}
|
||||
|
||||
@ -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")]
|
||||
impl<C, I, R, SC> StdState<I, C, R, SC>
|
||||
where
|
||||
@ -897,6 +971,9 @@ where
|
||||
#[cfg(feature = "std")]
|
||||
dont_reenter: None,
|
||||
last_report_time: None,
|
||||
corpus_idx: None,
|
||||
stage_depth: 0,
|
||||
stage_idx_stack: Vec::new(),
|
||||
phantom: PhantomData,
|
||||
};
|
||||
feedback.init_state(&mut state)?;
|
||||
@ -927,16 +1004,37 @@ impl<I, C, R, SC> HasScalabilityMonitor for StdState<I, C, R, SC> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A very simple state without any bells or whistles, for testing.
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
pub struct NopState<I> {
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use core::{marker::PhantomData, time::Duration};
|
||||
|
||||
use libafl_bolts::{rands::StdRand, serdeany::SerdeAnyMap, Error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
corpus::{CorpusId, HasCurrentCorpusIdx, InMemoryCorpus},
|
||||
inputs::{Input, NopInput, UsesInput},
|
||||
stages::test::{test_resume, test_resume_stages},
|
||||
state::{
|
||||
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> {
|
||||
impl<I> NopState<I> {
|
||||
/// Create a new State that does nothing (for tests)
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
@ -947,16 +1045,16 @@ impl<I> NopState<I> {
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> UsesInput for NopState<I>
|
||||
where
|
||||
impl<I> UsesInput for NopState<I>
|
||||
where
|
||||
I: Input,
|
||||
{
|
||||
{
|
||||
type Input = I;
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> HasExecutions for NopState<I> {
|
||||
impl<I> HasExecutions for NopState<I> {
|
||||
fn executions(&self) -> &usize {
|
||||
&self.execution
|
||||
}
|
||||
@ -964,9 +1062,9 @@ impl<I> HasExecutions for NopState<I> {
|
||||
fn executions_mut(&mut self) -> &mut usize {
|
||||
&mut self.execution
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> HasLastReportTime for NopState<I> {
|
||||
impl<I> HasLastReportTime for NopState<I> {
|
||||
fn last_report_time(&self) -> &Option<Duration> {
|
||||
unimplemented!();
|
||||
}
|
||||
@ -974,9 +1072,9 @@ impl<I> HasLastReportTime for NopState<I> {
|
||||
fn last_report_time_mut(&mut self) -> &mut Option<Duration> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> HasMetadata for NopState<I> {
|
||||
impl<I> HasMetadata for NopState<I> {
|
||||
fn metadata_map(&self) -> &SerdeAnyMap {
|
||||
&self.metadata
|
||||
}
|
||||
@ -984,9 +1082,9 @@ impl<I> HasMetadata for NopState<I> {
|
||||
fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap {
|
||||
&mut self.metadata
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> HasRand for NopState<I> {
|
||||
impl<I> HasRand for NopState<I> {
|
||||
type Rand = StdRand;
|
||||
|
||||
fn rand(&self) -> &Self::Rand {
|
||||
@ -996,12 +1094,40 @@ impl<I> HasRand for NopState<I> {
|
||||
fn rand_mut(&mut self) -> &mut Self::Rand {
|
||||
&mut self.rand
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> State for NopState<I> where I: Input {}
|
||||
impl<I> State for NopState<I> where I: Input {}
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
impl<I> HasClientPerfMonitor for NopState<I> {
|
||||
impl<I> HasCurrentCorpusIdx for NopState<I> {
|
||||
fn set_corpus_idx(&mut self, _idx: CorpusId) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_corpus_idx(&mut self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn current_corpus_idx(&self) -> Result<Option<CorpusId>, Error> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> HasCurrentStage for NopState<I> {
|
||||
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!();
|
||||
}
|
||||
@ -1009,10 +1135,10 @@ impl<I> HasClientPerfMonitor for NopState<I> {
|
||||
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "scalability_introspection")]
|
||||
impl<I> HasScalabilityMonitor for NopState<I> {
|
||||
#[cfg(feature = "scalability_introspection")]
|
||||
impl<I> HasScalabilityMonitor for NopState<I> {
|
||||
fn scalability_monitor(&self) -> &ScalabilityMonitor {
|
||||
unimplemented!();
|
||||
}
|
||||
@ -1020,6 +1146,28 @@ impl<I> HasScalabilityMonitor for NopState<I> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
|
@ -11,7 +11,7 @@ pub use tuple_list::{tuple_list, tuple_list_type, TupleList};
|
||||
|
||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
||||
use crate::hash_std;
|
||||
use crate::Named;
|
||||
use crate::{HasLen, Named};
|
||||
|
||||
/// Returns if the type `T` is equal to `U`
|
||||
/// From <https://stackoverflow.com/a/60138532/7658998>
|
||||
@ -92,21 +92,10 @@ where
|
||||
pub trait HasConstLen {
|
||||
/// The length as constant `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 () {
|
||||
const LEN: usize = 0;
|
||||
|
||||
fn len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl<Head, Tail> HasConstLen for (Head, Tail)
|
||||
@ -114,9 +103,18 @@ where
|
||||
Tail: HasConstLen,
|
||||
{
|
||||
const LEN: usize = 1 + Tail::LEN;
|
||||
}
|
||||
|
||||
impl<C> HasLen for C
|
||||
where
|
||||
C: HasConstLen,
|
||||
{
|
||||
fn len(&self) -> usize {
|
||||
1 + self.1.len()
|
||||
Self::LEN
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
Self::LEN != 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ use libafl::{
|
||||
tui::{ui::TuiUI, TuiMonitor},
|
||||
Monitor, MultiMonitor, SimpleMonitor,
|
||||
},
|
||||
stages::StagesTuple,
|
||||
stages::{HasCurrentStage, StagesTuple},
|
||||
state::{HasExecutions, HasLastReportTime, HasMetadata, HasSolutions, UsesState},
|
||||
Error, Fuzzer,
|
||||
};
|
||||
@ -66,7 +66,7 @@ fn do_fuzz<F, ST, E, S, EM>(
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
F: Fuzzer<E, EM, ST, State = S>,
|
||||
S: HasMetadata + HasExecutions + UsesInput + HasSolutions + HasLastReportTime,
|
||||
S: HasMetadata + HasExecutions + UsesInput + HasSolutions + HasLastReportTime + HasCurrentStage,
|
||||
E: UsesState<State = S>,
|
||||
EM: ProgressReporter<State = S>,
|
||||
ST: StagesTuple<E, EM, S, F>,
|
||||
|
@ -224,7 +224,7 @@ macro_rules! fuzz_with {
|
||||
|
||||
// Set up a generalization stage for grimoire
|
||||
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);
|
||||
|
||||
@ -320,7 +320,7 @@ macro_rules! fuzz_with {
|
||||
let string_replace_power = StdMutationalStage::transforming(string_replace_mutator);
|
||||
|
||||
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
|
||||
if !state.has_metadata::<Tokens>() {
|
||||
@ -342,19 +342,19 @@ macro_rules! fuzz_with {
|
||||
// Setup a randomic Input2State stage, conditionally within a custom mutator
|
||||
let i2s =
|
||||
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 {
|
||||
LLVMCustomMutator::mutate_unchecked(StdScheduledMutator::new(tuple_list!(
|
||||
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
|
||||
let std_mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
|
||||
|
||||
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
|
||||
// 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 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 =
|
||||
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
|
||||
// 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 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 =
|
||||
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(
|
||||
tuple_list!(
|
||||
@ -408,7 +408,7 @@ macro_rules! fuzz_with {
|
||||
),
|
||||
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
|
||||
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
|
||||
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,
|
||||
tuple_list!(cmplog_observer),
|
||||
&mut fuzzer,
|
||||
|
@ -6,7 +6,7 @@ use libafl::{
|
||||
feedbacks::{MapFeedbackMetadata, MAPFEEDBACK_PREFIX},
|
||||
inputs::UsesInput,
|
||||
monitors::SimpleMonitor,
|
||||
stages::StagesTuple,
|
||||
stages::{HasCurrentStage, StagesTuple},
|
||||
state::{HasExecutions, HasLastReportTime, HasMetadata, HasNamedMetadata},
|
||||
Error, Fuzzer,
|
||||
};
|
||||
@ -24,7 +24,12 @@ fn do_report<F, ST, E, S, EM>(
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
F: Fuzzer<E, EM, ST, State = S>,
|
||||
S: HasMetadata + HasNamedMetadata + HasExecutions + UsesInput + HasLastReportTime,
|
||||
S: HasMetadata
|
||||
+ HasNamedMetadata
|
||||
+ HasExecutions
|
||||
+ UsesInput
|
||||
+ HasLastReportTime
|
||||
+ HasCurrentStage,
|
||||
E: HasObservers<State = S>,
|
||||
EM: ProgressReporter<State = S>,
|
||||
ST: StagesTuple<E, EM, S, F>,
|
||||
|
@ -12,7 +12,6 @@ use core::{
|
||||
use libafl::{
|
||||
executors::{inprocess::inprocess_get_state, ExitKind},
|
||||
inputs::UsesInput,
|
||||
state::NopState,
|
||||
};
|
||||
|
||||
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>
|
||||
where
|
||||
QT: QemuHelperTuple<S>,
|
||||
|
@ -4,7 +4,7 @@ use core::marker::PhantomData;
|
||||
#[cfg(feature = "introspection")]
|
||||
use libafl::state::HasClientPerfMonitor;
|
||||
use libafl::{
|
||||
corpus::{Corpus, CorpusId},
|
||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
||||
executors::{Executor, HasObservers},
|
||||
inputs::{BytesInput, UsesInput},
|
||||
observers::ObserversTuple,
|
||||
@ -40,6 +40,8 @@ where
|
||||
EM: UsesState<State = TE::State>,
|
||||
Z: UsesState<State = TE::State>,
|
||||
{
|
||||
type Progress = (); // TODO this needs resumption
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
@ -47,9 +49,11 @@ where
|
||||
_executor: &mut E,
|
||||
state: &mut TE::State,
|
||||
manager: &mut EM,
|
||||
corpus_idx: CorpusId,
|
||||
) -> Result<(), Error> {
|
||||
// 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)?;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user