From ba8ca6723b7adbc46ce19b9dce4f46a41e8dd3bc Mon Sep 17 00:00:00 2001 From: Addison Crump Date: Tue, 9 Jan 2024 15:18:14 +0100 Subject: [PATCH] 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 --- fuzzers/baby_fuzzer_minimizing/src/main.rs | 10 +- .../fuzzbench_forkserver_cmplog/src/main.rs | 13 +- libafl/src/corpus/mod.rs | 12 + libafl/src/events/llmp.rs | 2 + libafl/src/events/mod.rs | 15 +- libafl/src/events/simple.rs | 2 + libafl/src/events/tcp.rs | 3 + libafl/src/executors/command.rs | 4 +- libafl/src/executors/inprocess.rs | 8 +- libafl/src/executors/mod.rs | 168 ++++--- libafl/src/feedbacks/differential.rs | 2 +- libafl/src/fuzzer/mod.rs | 115 +++-- libafl/src/mutators/mopt_mutator.rs | 4 +- libafl/src/mutators/mutations.rs | 37 +- libafl/src/mutators/scheduled.rs | 15 +- libafl/src/mutators/tuneable.rs | 12 +- libafl/src/stages/calibrate.rs | 11 +- libafl/src/stages/colorization.rs | 21 +- libafl/src/stages/concolic.rs | 28 +- libafl/src/stages/dump.rs | 3 +- libafl/src/stages/generalization.rs | 11 +- libafl/src/stages/logics.rs | 281 ++++++++++- libafl/src/stages/mod.rs | 473 ++++++++++++++---- libafl/src/stages/mutational.rs | 23 +- libafl/src/stages/owned.rs | 66 --- libafl/src/stages/power.rs | 5 +- libafl/src/stages/stats.rs | 11 +- libafl/src/stages/string.rs | 11 +- libafl/src/stages/sync.rs | 6 +- libafl/src/stages/tmin.rs | 14 +- libafl/src/stages/tracing.rs | 20 +- libafl/src/stages/tuneable.rs | 14 +- libafl/src/state/mod.rs | 296 ++++++++--- libafl_bolts/src/tuples.rs | 24 +- .../libafl_libfuzzer_runtime/src/fuzz.rs | 4 +- .../libafl_libfuzzer_runtime/src/lib.rs | 22 +- .../libafl_libfuzzer_runtime/src/report.rs | 9 +- libafl_qemu/src/hooks.rs | 31 -- .../src/cmps/stages/aflpptracing.rs | 8 +- 39 files changed, 1287 insertions(+), 527 deletions(-) delete mode 100644 libafl/src/stages/owned.rs diff --git a/fuzzers/baby_fuzzer_minimizing/src/main.rs b/fuzzers/baby_fuzzer_minimizing/src/main.rs index a1b6650e26..1f7fc51701 100644 --- a/fuzzers/baby_fuzzer_minimizing/src/main.rs +++ b/fuzzers/baby_fuzzer_minimizing/src/main.rs @@ -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(()) } diff --git a/fuzzers/fuzzbench_forkserver_cmplog/src/main.rs b/fuzzers/fuzzbench_forkserver_cmplog/src/main.rs index 862ecf4754..ec5dde303f 100644 --- a/fuzzers/fuzzbench_forkserver_cmplog/src/main.rs +++ b/fuzzers/fuzzbench_forkserver_cmplog/src/main.rs @@ -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 { + _event_manager: &mut _| + -> Result { + 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 diff --git a/libafl/src/corpus/mod.rs b/libafl/src/corpus/mod.rs index bdbc247b37..47f01d22e4 100644 --- a/libafl/src/corpus/mod.rs +++ b/libafl/src/corpus/mod.rs @@ -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, Error>; +} + /// [`Iterator`] over the ids of a [`Corpus`] #[derive(Debug)] pub struct CorpusIdIterator<'a, C> diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index c5a72f80f3..b3aa10b084 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -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(&( diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index c5c0b64434..ebce85cb2b 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -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 { phantom: PhantomData, } @@ -620,6 +623,12 @@ impl NopEventManager { } } +impl Default for NopEventManager { + fn default() -> Self { + Self::new() + } +} + impl UsesState for NopEventManager where S: State, diff --git a/libafl/src/events/simple.rs b/libafl/src/events/simple.rs index 5a4bb1fbe7..27fbc8dded 100644 --- a/libafl/src/events/simple.rs +++ b/libafl/src/events/simple.rs @@ -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(&( diff --git a/libafl/src/events/tcp.rs b/libafl/src/events/tcp.rs index 8042fd608b..252d493afa 100644 --- a/libafl/src/events/tcp.rs +++ b/libafl/src/events/tcp.rs @@ -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(()) } diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index b0a0af18d9..a37344ccfc 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -690,10 +690,10 @@ mod tests { command::{CommandExecutor, InputLocation}, Executor, }, + fuzzer::test::NopFuzzer, inputs::BytesInput, monitors::SimpleMonitor, - state::NopState, - NopFuzzer, + state::test::NopState, }; #[test] diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 6850bb1bf4..e2da742965 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -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(); diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 74953313f0..699c5654b9 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -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,58 +141,78 @@ 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 { - phantom: PhantomData, -} - -impl UsesState for NopExecutor -where - S: State, -{ - type State = S; -} - -impl Executor for NopExecutor -where - EM: UsesState, - S: State + HasExecutions, - S::Input: HasTargetBytes, - Z: UsesState, -{ - fn run_target( - &mut self, - _fuzzer: &mut Z, - state: &mut Self::State, - _mgr: &mut EM, - input: &Self::Input, - ) -> Result { - *state.executions_mut() += 1; - - if input.target_bytes().as_slice().is_empty() { - Err(Error::empty("Input Empty")) - } else { - Ok(ExitKind::Ok) - } - } -} - #[cfg(test)] -mod test { +pub mod test { use core::marker::PhantomData; - use super::{Executor, NopExecutor}; - use crate::{events::NopEventManager, inputs::BytesInput, state::NopState, NopFuzzer}; + 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 { + phantom: PhantomData, + } + + impl NopExecutor { + #[must_use] + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } + } + + impl Default for NopExecutor { + fn default() -> Self { + Self::new() + } + } + + impl UsesState for NopExecutor + where + S: State, + { + type State = S; + } + + impl Executor for NopExecutor + where + EM: UsesState, + S: State + HasExecutions, + S::Input: HasTargetBytes, + Z: UsesState, + { + fn run_target( + &mut self, + _fuzzer: &mut Z, + state: &mut Self::State, + _mgr: &mut EM, + input: &Self::Input, + ) -> Result { + *state.executions_mut() += 1; + + if input.target_bytes().as_slice().is_empty() { + Err(Error::empty("Input Empty")) + } else { + Ok(ExitKind::Ok) + } + } + } #[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(); diff --git a/libafl/src/feedbacks/differential.rs b/libafl/src/feedbacks/differential.rs index 523b6daeb7..0c3ae53e9e 100644 --- a/libafl/src/feedbacks/differential.rs +++ b/libafl/src/feedbacks/differential.rs @@ -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)] diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 3d99afbad9..83b8aed00a 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -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, F: Feedback, OF: Feedback, - CS::State: - HasExecutions + HasMetadata + HasCorpus + HasTestcase + HasImported + HasLastReportTime, + CS::State: HasExecutions + + HasMetadata + + HasCorpus + + HasTestcase + + HasImported + + HasLastReportTime + + HasCurrentCorpusIdx + + HasCurrentStage, ST: StagesTuple, { fn fuzz_one( @@ -599,7 +601,13 @@ where state.introspection_monitor_mut().start_timer(); // Get the next index from the scheduler - let idx = self.scheduler.next(state)?; + let idx = if let Some(idx) = state.current_corpus_idx()? { + idx // we are resuming + } else { + let idx = self.scheduler.next(state)?; + state.set_corpus_idx(idx)?; // set up for resume + idx + }; // Mark the elapsed time for the scheduler #[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,44 +744,62 @@ where } #[cfg(test)] -#[derive(Clone, Debug, Default)] -pub(crate) struct NopFuzzer { - phantom: PhantomData, -} +pub mod test { + use core::marker::PhantomData; -#[cfg(test)] -impl NopFuzzer { - pub fn new() -> Self { - Self { - phantom: PhantomData, + 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 { + phantom: PhantomData, + } + + impl NopFuzzer { + #[must_use] + pub fn new() -> Self { + Self { + phantom: PhantomData, + } } } -} -#[cfg(test)] -impl UsesState for NopFuzzer -where - I: Input, -{ - type State = NopState; -} + impl Default for NopFuzzer { + fn default() -> Self { + Self::new() + } + } -#[cfg(test)] -impl Fuzzer for NopFuzzer -where - E: UsesState>, - EM: ProgressReporter>, - I: Input, - ST: StagesTuple, Self>, -{ - fn fuzz_one( - &mut self, - _stages: &mut ST, - _executor: &mut E, - _state: &mut EM::State, - _manager: &mut EM, - ) -> Result { - unimplemented!() + impl UsesState for NopFuzzer + where + S: State, + { + type State = S; + } + + impl Fuzzer for NopFuzzer + where + E: UsesState, + EM: ProgressReporter, + ST: StagesTuple, + E::State: HasMetadata + HasExecutions + HasLastReportTime + HasCurrentStage, + { + fn fuzz_one( + &mut self, + _stages: &mut ST, + _executor: &mut E, + _state: &mut EM::State, + _manager: &mut EM, + ) -> Result { + unimplemented!() + } } } diff --git a/libafl/src/mutators/mopt_mutator.rs b/libafl/src/mutators/mopt_mutator.rs index 6955c5252e..25c9647c67 100644 --- a/libafl/src/mutators/mopt_mutator.rs +++ b/libafl/src/mutators/mopt_mutator.rs @@ -392,7 +392,7 @@ where write!( f, "StdMOptMutator with {} mutations for Input type {}", - self.mutations.len(), + MT::LEN, core::any::type_name::() ) } @@ -547,7 +547,7 @@ where ) -> Result { if !state.has_metadata::() { let rand_seed = state.rand_mut().next(); - state.add_metadata::(MOpt::new(mutations.len(), swarm_num, rand_seed)?); + state.add_metadata::(MOpt::new(MT::LEN, swarm_num, rand_seed)?); } Ok(Self { name: format!("StdMOptMutator[{}]", mutations.names().join(",")), diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index 935973f6a5..b7ba0c489b 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -1396,7 +1396,7 @@ pub fn str_decode(item: &str) -> Result, 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() -> impl MutatorsTuple - 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 diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index ffdab9e92d..4bbff8dd19 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -135,7 +135,7 @@ where write!( f, "StdScheduledMutator with {} mutations for Input type {}", - self.mutations.len(), + MT::LEN, core::any::type_name::() ) } @@ -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::() ) } @@ -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( diff --git a/libafl/src/mutators/tuneable.rs b/libafl/src/mutators/tuneable.rs index 057f283a3a..2ce0686c1c 100644 --- a/libafl/src/mutators/tuneable.rs +++ b/libafl/src/mutators/tuneable.rs @@ -100,7 +100,7 @@ where write!( f, "TuneableScheduledMutator with {} mutations for Input type {}", - self.mutations.len(), + MT::LEN, core::any::type_name::() ) } @@ -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] diff --git a/libafl/src/stages/calibrate.rs b/libafl/src/stages/calibrate.rs index 50fb0bd37c..6e08ff9952 100644 --- a/libafl/src/stages/calibrate.rs +++ b/libafl/src/stages/calibrate.rs @@ -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, { + 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(); diff --git a/libafl/src/stages/colorization.rs b/libafl/src/stages/colorization.rs index dfbf7350dc..6370472e9f 100644 --- a/libafl/src/stages/colorization.rs +++ b/libafl/src/stages/colorization.rs @@ -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, { + 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 { + 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(); diff --git a/libafl/src/stages/concolic.rs b/libafl/src/stages/concolic.rs index e6dba49c5c..cabd92c83f 100644 --- a/libafl/src/stages/concolic.rs +++ b/libafl/src/stages/concolic.rs @@ -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, { + 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,8 +404,7 @@ where input_copy.bytes_mut()[index] = new_byte; } // Time is measured directly the `evaluate_input` function - let _: (crate::ExecuteInputResult, Option) = - fuzzer.evaluate_input(state, executor, manager, input_copy)?; + fuzzer.evaluate_input(state, executor, manager, input_copy)?; } } Ok(()) diff --git a/libafl/src/stages/dump.rs b/libafl/src/stages/dump.rs index a007b66923..85cccdf89c 100644 --- a/libafl/src/stages/dump.rs +++ b/libafl/src/stages/dump.rs @@ -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::() { diff --git a/libafl/src/stages/generalization.rs b/libafl/src/stages/generalization.rs index eb3cfbac66..72a3f306c0 100644 --- a/libafl/src/stages/generalization.rs +++ b/libafl/src/stages/generalization.rs @@ -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, Z: UsesState, { + 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); { diff --git a/libafl/src/stages/logics.rs b/libafl/src/stages/logics.rs index a70672ff1e..da28017b1a 100644 --- a/libafl/src/stages/logics.rs +++ b/libafl/src/stages/logics.rs @@ -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 StageProgress 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 where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST: StagesTuple, @@ -26,7 +52,7 @@ where impl UsesState for WhileStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST: StagesTuple, @@ -37,31 +63,34 @@ where impl Stage for WhileStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST: StagesTuple, Z: UsesState, + 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 WhileStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST: StagesTuple, @@ -82,7 +111,7 @@ where #[derive(Debug)] pub struct IfStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST: StagesTuple, @@ -95,7 +124,7 @@ where impl UsesState for IfStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST: StagesTuple, @@ -106,23 +135,25 @@ where impl Stage for IfStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST: StagesTuple, Z: UsesState, + 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 IfStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST: StagesTuple, @@ -151,7 +182,7 @@ where #[derive(Debug)] pub struct IfElseStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST1: StagesTuple, @@ -166,7 +197,7 @@ where impl UsesState for IfElseStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST1: StagesTuple, @@ -178,35 +209,54 @@ where impl Stage for IfElseStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST1: StagesTuple, ST2: StagesTuple, Z: UsesState, + 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)? { + 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, corpus_idx)?; + .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, corpus_idx)?; + .perform_all(fuzzer, executor, state, manager)?; } + + state.exit_inner_stage()?; + state.clear_stage()?; + Ok(()) } } impl IfElseStage where - CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, CorpusId) -> Result, + CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result, E: UsesState, EM: UsesState, ST1: StagesTuple, @@ -253,17 +303,19 @@ where EM: UsesState, ST: StagesTuple, Z: UsesState, + 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::(); + + 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::(); + + 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::(); + + test_resume(&completed, &mut state, tuple_list!(ifstage)); + } + + #[derive(Debug)] + pub struct PanicStage { + phantom: PhantomData, + } + + impl PanicStage { + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } + } + + impl UsesState for PanicStage + where + S: State, + { + type State = S; + } + + impl Stage for PanicStage + where + E: UsesState, + EM: UsesState, + Z: UsesState, + { + 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::(); + + 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::(); + + 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::(); + + test_resume(&completed, &mut state, tuple_list!(ifstage)); + } +} diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index e0461469fc..b7783eeb08 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -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: UsesState @@ -87,6 +72,12 @@ where EM: UsesState, Z: UsesState, { + // 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; + /// 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, EM: UsesState, Z: UsesState, - 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, EM: UsesState, Z: UsesState, - S: UsesInput, + S: UsesInput + HasCurrentStage, { fn perform_all( &mut self, _: &mut Z, _: &mut E, - _: &mut S, + stage: &mut S, _: &mut EM, - _: CorpusId, ) -> Result<(), Error> { - Ok(()) + if stage.current_stage()?.is_some() { + Err(Error::illegal_state( + "Got to the end of the tuple without completing resume.", + )) + } else { + Ok(()) + } } } impl StagesTuple for (Head, Tail) where Head: Stage, - Tail: StagesTuple, + Tail: StagesTuple + HasConstLen, E: UsesState, EM: UsesState, Z: UsesState, + 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 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 UsesState for ClosureStage 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 Stage for ClosureStage 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, Z: UsesState, { + 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 ClosureStage 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 From for ClosureStage 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 Stage for PushStageAdapter where CS: Scheduler, - CS::State: HasExecutions + HasMetadata + HasRand + HasCorpus + HasLastReportTime, + CS::State: + HasExecutions + HasMetadata + HasRand + HasCorpus + HasLastReportTime + HasCurrentCorpusIdx, E: Executor + HasObservers, EM: EventFirer + EventRestarter @@ -270,16 +285,23 @@ where + EvaluatorObservers + HasScheduler, { + 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 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 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 { + /// 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 StageProgress 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, 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 { + phantom: PhantomData, + } + + #[derive(Debug)] + pub struct ResumeFailedStage { + completed: Rc>, + phantom: PhantomData, + } + + #[derive(Serialize, Deserialize, Debug)] + pub struct TestProgress { + count: usize, + } + + impl_serdeany!(TestProgress); + + impl StageProgress for TestProgress + where + S: HasMetadata, + { + fn initialize_progress(state: &mut S) -> Result<(), Error> { + // check if we're resuming + if !state.has_metadata::() { + state.add_metadata(Self { count: 0 }); + } + Ok(()) + } + + fn clear_progress(state: &mut S) -> Result<(), Error> { + if state.metadata_map_mut().remove::().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 UsesState for ResumeSucceededStage + where + S: State, + { + type State = S; + } + + impl Stage for ResumeSucceededStage + where + E: UsesState, + EM: UsesState, + 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 UsesState for ResumeFailedStage + where + S: State, + { + type State = S; + } + + impl Stage for ResumeFailedStage + where + E: UsesState, + EM: UsesState, + 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() -> ( + Rc>, + tuple_list_type!(ResumeSucceededStage, ResumeFailedStage), + ) { + let completed = Rc::new(RefCell::new(false)); + ( + completed.clone(), + tuple_list!( + ResumeSucceededStage { + phantom: PhantomData + }, + ResumeFailedStage { + completed, + phantom: PhantomData + }, + ), + ) + } + + pub fn test_resume(completed: &Rc>, state: &mut S, mut stages: ST) + where + ST: StagesTuple, NopEventManager, S, NopFuzzer>, + 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." + ); + } + } +} diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 56f23213fb..f5c5fda210 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -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 + 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 + 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(()); diff --git a/libafl/src/stages/owned.rs b/libafl/src/stages/owned.rs deleted file mode 100644 index 58896ad7c0..0000000000 --- a/libafl/src/stages/owned.rs +++ /dev/null @@ -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: Stage + AsAny -where - E: UsesState, - EM: UsesState, - Z: UsesState, -{ -} - -/// An owned list of `Observer` trait objects -#[derive(Default)] -#[allow(missing_debug_implementations)] -pub struct StagesOwnedList -where - E: UsesState, -{ - /// The named trait objects map - #[allow(clippy::type_complexity)] - pub list: Vec>>, -} - -impl StagesTuple for StagesOwnedList -where - E: UsesState, - EM: UsesState, - Z: UsesState, -{ - 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 StagesOwnedList -where - E: UsesState, -{ - /// Create a new instance - #[must_use] - #[allow(clippy::type_complexity)] - pub fn new(list: Vec>>) -> Self { - Self { list } - } -} diff --git a/libafl/src/stages/power.rs b/libafl/src/stages/power.rs index e9e0318638..972a321d5e 100644 --- a/libafl/src/stages/power.rs +++ b/libafl/src/stages/power.rs @@ -71,6 +71,8 @@ where Z: Evaluator, I: MutatedTransform + 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 } } diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs index b29dfa8381..4cce498969 100644 --- a/libafl/src/stages/stats.rs +++ b/libafl/src/stages/stats.rs @@ -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, 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 { diff --git a/libafl/src/stages/string.rs b/libafl/src/stages/string.rs index d487a0a10e..5e1333faa8 100644 --- a/libafl/src/stages/string.rs +++ b/libafl/src/stages/string.rs @@ -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, Z: UsesState, { + 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::() { return Ok(()); // skip recompute diff --git a/libafl/src/stages/sync.rs b/libafl/src/stages/sync.rs index d82638bc4c..081c54a433 100644 --- a/libafl/src/stages/sync.rs +++ b/libafl/src/stages/sync.rs @@ -67,6 +67,8 @@ where Z: Evaluator, 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, 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 diff --git a/libafl/src/stages/tmin.rs b/libafl/src/stages/tmin.rs index f7343aeb6f..eb603ec30b 100644 --- a/libafl/src/stages/tmin.rs +++ b/libafl/src/stages/tmin.rs @@ -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 + HasScheduler, { + 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(); diff --git a/libafl/src/stages/tracing.rs b/libafl/src/stages/tracing.rs index 6aebb41dec..f141447b5c 100644 --- a/libafl/src/stages/tracing.rs +++ b/libafl/src/stages/tracing.rs @@ -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, Z: UsesState, { + 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, 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, 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)?; diff --git a/libafl/src/stages/tuneable.rs b/libafl/src/stages/tuneable.rs index 19f8576bdb..66d1a2333f 100644 --- a/libafl/src/stages/tuneable.rs +++ b/libafl/src/stages/tuneable.rs @@ -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 + 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(); diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 3beeb54c5c..3710f356e7 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -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 { /// The last time we reported progress (if available/used). /// This information is used by fuzzer `maybe_report_progress`. last_report_time: Option, + /// The current index of the corpus; used to record for resumable fuzzing. + corpus_idx: Option, + /// The stage indexes for each nesting of stages + stage_idx_stack: Vec, + /// The current stage depth + stage_depth: usize, phantom: PhantomData, } @@ -514,6 +527,67 @@ impl HasStartTime for StdState { } } +impl HasCurrentCorpusIdx for StdState { + 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, Error> { + Ok(self.corpus_idx) + } +} + +impl HasCurrentStage for StdState { + 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, 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 HasNestedStageStatus for StdState { + 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 StdState 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,98 +1004,169 @@ impl HasScalabilityMonitor for StdState { } } -/// A very simple state without any bells or whistles, for testing. -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct NopState { - metadata: SerdeAnyMap, - execution: usize, - rand: StdRand, - phantom: PhantomData, -} +#[cfg(test)] +pub mod test { + use core::{marker::PhantomData, time::Duration}; -impl NopState { - /// Create a new State that does nothing (for tests) - #[must_use] - pub fn new() -> Self { - NopState { - metadata: SerdeAnyMap::new(), - execution: 0, - rand: StdRand::default(), - phantom: PhantomData, + 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 { + metadata: SerdeAnyMap, + execution: usize, + rand: StdRand, + phantom: PhantomData, + } + + impl NopState { + /// Create a new State that does nothing (for tests) + #[must_use] + pub fn new() -> Self { + NopState { + metadata: SerdeAnyMap::new(), + execution: 0, + rand: StdRand::default(), + phantom: PhantomData, + } } } -} -impl UsesInput for NopState -where - I: Input, -{ - type Input = I; -} - -impl HasExecutions for NopState { - fn executions(&self) -> &usize { - &self.execution + impl UsesInput for NopState + where + I: Input, + { + type Input = I; } - fn executions_mut(&mut self) -> &mut usize { - &mut self.execution - } -} + impl HasExecutions for NopState { + fn executions(&self) -> &usize { + &self.execution + } -impl HasLastReportTime for NopState { - fn last_report_time(&self) -> &Option { - unimplemented!(); + fn executions_mut(&mut self) -> &mut usize { + &mut self.execution + } } - fn last_report_time_mut(&mut self) -> &mut Option { - unimplemented!(); - } -} + impl HasLastReportTime for NopState { + fn last_report_time(&self) -> &Option { + unimplemented!(); + } -impl HasMetadata for NopState { - fn metadata_map(&self) -> &SerdeAnyMap { - &self.metadata + fn last_report_time_mut(&mut self) -> &mut Option { + unimplemented!(); + } } - fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap { - &mut self.metadata - } -} + impl HasMetadata for NopState { + fn metadata_map(&self) -> &SerdeAnyMap { + &self.metadata + } -impl HasRand for NopState { - type Rand = StdRand; - - fn rand(&self) -> &Self::Rand { - &self.rand + fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap { + &mut self.metadata + } } - fn rand_mut(&mut self) -> &mut Self::Rand { - &mut self.rand - } -} + impl HasRand for NopState { + type Rand = StdRand; -impl State for NopState where I: Input {} + fn rand(&self) -> &Self::Rand { + &self.rand + } -#[cfg(feature = "introspection")] -impl HasClientPerfMonitor for NopState { - fn introspection_monitor(&self) -> &ClientPerfMonitor { - unimplemented!(); + fn rand_mut(&mut self) -> &mut Self::Rand { + &mut self.rand + } } - fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor { - unimplemented!(); - } -} + impl State for NopState where I: Input {} -#[cfg(feature = "scalability_introspection")] -impl HasScalabilityMonitor for NopState { - fn scalability_monitor(&self) -> &ScalabilityMonitor { - unimplemented!(); + impl HasCurrentCorpusIdx for NopState { + 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, Error> { + Ok(None) + } } - fn scalability_monitor_mut(&mut self) -> &mut ScalabilityMonitor { - unimplemented!(); + impl HasCurrentStage for NopState { + fn set_stage(&mut self, _idx: usize) -> Result<(), Error> { + Ok(()) + } + + fn clear_stage(&mut self) -> Result<(), Error> { + Ok(()) + } + + fn current_stage(&self) -> Result, Error> { + Ok(None) + } + } + + #[cfg(feature = "introspection")] + impl HasClientPerfMonitor for NopState { + fn introspection_monitor(&self) -> &ClientPerfMonitor { + unimplemented!(); + } + + fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor { + unimplemented!(); + } + } + + #[cfg(feature = "scalability_introspection")] + impl HasScalabilityMonitor for NopState { + fn scalability_monitor(&self) -> &ScalabilityMonitor { + unimplemented!(); + } + + fn scalability_monitor_mut(&mut self) -> &mut ScalabilityMonitor { + unimplemented!(); + } + } + + #[must_use] + pub fn test_std_state() -> StdState, StdRand, InMemoryCorpus> + { + StdState::new( + StdRand::with_seed(0), + InMemoryCorpus::::new(), + InMemoryCorpus::new(), + &mut (), + &mut (), + ) + .expect("couldn't instantiate the test state") + } + + #[test] + fn resume_simple() { + let mut state = test_std_state::(); + let (completed, stages) = test_resume_stages(); + + test_resume(&completed, &mut state, stages); } } diff --git a/libafl_bolts/src/tuples.rs b/libafl_bolts/src/tuples.rs index 254fb888bd..c1c5c1061d 100644 --- a/libafl_bolts/src/tuples.rs +++ b/libafl_bolts/src/tuples.rs @@ -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 @@ -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 HasConstLen for (Head, Tail) @@ -114,9 +103,18 @@ where Tail: HasConstLen, { const LEN: usize = 1 + Tail::LEN; +} +impl HasLen for C +where + C: HasConstLen, +{ fn len(&self) -> usize { - 1 + self.1.len() + Self::LEN + } + + fn is_empty(&self) -> bool { + Self::LEN != 0 } } diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs index 4ca5c5c12d..07e1551794 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs @@ -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( ) -> Result<(), Error> where F: Fuzzer, - S: HasMetadata + HasExecutions + UsesInput + HasSolutions + HasLastReportTime, + S: HasMetadata + HasExecutions + UsesInput + HasSolutions + HasLastReportTime + HasCurrentStage, E: UsesState, EM: ProgressReporter, ST: StagesTuple, diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs index 11afce8242..f0d6d7fd70 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs @@ -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::() { @@ -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, diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs index da4e4a23f4..ed50a571a6 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs @@ -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( ) -> Result<(), Error> where F: Fuzzer, - S: HasMetadata + HasNamedMetadata + HasExecutions + UsesInput + HasLastReportTime, + S: HasMetadata + + HasNamedMetadata + + HasExecutions + + UsesInput + + HasLastReportTime + + HasCurrentStage, E: HasObservers, EM: ProgressReporter, ST: StagesTuple, diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index 3517280d2f..6183d55f48 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -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 QemuHooks> -where - QT: QemuHelperTuple>, - NopState: UsesInput, -{ - pub fn reproducer(emulator: Emulator, helpers: QT) -> Box { - Self::new(emulator, helpers) - } - - pub fn repro_run(&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 QemuHooks where QT: QemuHelperTuple, diff --git a/libafl_targets/src/cmps/stages/aflpptracing.rs b/libafl_targets/src/cmps/stages/aflpptracing.rs index bf094c67c4..9243e43ae0 100644 --- a/libafl_targets/src/cmps/stages/aflpptracing.rs +++ b/libafl_targets/src/cmps/stages/aflpptracing.rs @@ -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, Z: UsesState, { + 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)?;