Implement RetryProgress
for limiting retry attempts in stages (#1890)
* do that again but smarter * remember to register * appease the clippy * cleanup * autofix clippy * more clippy fixes * more clippy... * small clippy fix * with_tries => with_retries * most recent suggestions * final clippy... hopefully
This commit is contained in:
parent
7a4fb06d02
commit
8c773a6b85
@ -60,7 +60,7 @@ where
|
|||||||
cached_len: Option<usize>,
|
cached_len: Option<usize>,
|
||||||
/// Number of executions done at discovery time
|
/// Number of executions done at discovery time
|
||||||
executions: usize,
|
executions: usize,
|
||||||
/// Number of fuzzing iterations of this particular input updated in perform_mutational
|
/// Number of fuzzing iterations of this particular input updated in `perform_mutational`
|
||||||
scheduled_count: usize,
|
scheduled_count: usize,
|
||||||
/// Parent [`CorpusId`], if known
|
/// Parent [`CorpusId`], if known
|
||||||
parent_id: Option<CorpusId>,
|
parent_id: Option<CorpusId>,
|
||||||
@ -366,15 +366,15 @@ where
|
|||||||
allow(clippy::unsafe_derive_deserialize)
|
allow(clippy::unsafe_derive_deserialize)
|
||||||
)] // for SerdeAny
|
)] // for SerdeAny
|
||||||
pub struct SchedulerTestcaseMetadata {
|
pub struct SchedulerTestcaseMetadata {
|
||||||
/// Number of bits set in bitmap, updated in calibrate_case
|
/// Number of bits set in bitmap, updated in `calibrate_case`
|
||||||
bitmap_size: u64,
|
bitmap_size: u64,
|
||||||
/// Number of queue cycles behind
|
/// Number of queue cycles behind
|
||||||
handicap: u64,
|
handicap: u64,
|
||||||
/// Path depth, initialized in on_add
|
/// Path depth, initialized in `on_add`
|
||||||
depth: u64,
|
depth: u64,
|
||||||
/// Offset in n_fuzz
|
/// Offset in `n_fuzz`
|
||||||
n_fuzz_entry: usize,
|
n_fuzz_entry: usize,
|
||||||
/// Cycles used to calibrate this (not really needed if it were not for on_replace and on_remove)
|
/// Cycles used to calibrate this (not really needed if it were not for `on_replace` and `on_remove`)
|
||||||
cycle_and_time: (Duration, usize),
|
cycle_and_time: (Duration, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ where
|
|||||||
SP: ShMemProvider + 'static,
|
SP: ShMemProvider + 'static,
|
||||||
S: State + 'a,
|
S: State + 'a,
|
||||||
{
|
{
|
||||||
/// The ShmemProvider to use
|
/// The `ShmemProvider` to use
|
||||||
shmem_provider: SP,
|
shmem_provider: SP,
|
||||||
/// The monitor instance to use
|
/// The monitor instance to use
|
||||||
monitor: MT,
|
monitor: MT,
|
||||||
@ -95,7 +95,7 @@ where
|
|||||||
/// A file name to write all client output to
|
/// A file name to write all client output to
|
||||||
#[builder(default = None)]
|
#[builder(default = None)]
|
||||||
stdout_file: Option<&'a str>,
|
stdout_file: Option<&'a str>,
|
||||||
/// The actual, opened, stdout_file - so that we keep it open until the end
|
/// The actual, opened, `stdout_file` - so that we keep it open until the end
|
||||||
#[cfg(all(unix, feature = "std", feature = "fork"))]
|
#[cfg(all(unix, feature = "std", feature = "fork"))]
|
||||||
#[builder(setter(skip), default = None)]
|
#[builder(setter(skip), default = None)]
|
||||||
opened_stdout_file: Option<File>,
|
opened_stdout_file: Option<File>,
|
||||||
@ -103,7 +103,7 @@ where
|
|||||||
/// `stdout_file`.
|
/// `stdout_file`.
|
||||||
#[builder(default = None)]
|
#[builder(default = None)]
|
||||||
stderr_file: Option<&'a str>,
|
stderr_file: Option<&'a str>,
|
||||||
/// The actual, opened, stdout_file - so that we keep it open until the end
|
/// The actual, opened, `stdout_file` - so that we keep it open until the end
|
||||||
#[cfg(all(unix, feature = "std", feature = "fork"))]
|
#[cfg(all(unix, feature = "std", feature = "fork"))]
|
||||||
#[builder(setter(skip), default = None)]
|
#[builder(setter(skip), default = None)]
|
||||||
opened_stderr_file: Option<File>,
|
opened_stderr_file: Option<File>,
|
||||||
@ -409,7 +409,7 @@ where
|
|||||||
SP: ShMemProvider + 'static,
|
SP: ShMemProvider + 'static,
|
||||||
S: State + 'a,
|
S: State + 'a,
|
||||||
{
|
{
|
||||||
/// The ShmemProvider to use
|
/// The `ShmemProvider` to use
|
||||||
shmem_provider: SP,
|
shmem_provider: SP,
|
||||||
/// The monitor instance to use
|
/// The monitor instance to use
|
||||||
monitor: MT,
|
monitor: MT,
|
||||||
@ -429,7 +429,7 @@ where
|
|||||||
/// A file name to write all client output to
|
/// A file name to write all client output to
|
||||||
#[builder(default = None)]
|
#[builder(default = None)]
|
||||||
stdout_file: Option<&'a str>,
|
stdout_file: Option<&'a str>,
|
||||||
/// The actual, opened, stdout_file - so that we keep it open until the end
|
/// The actual, opened, `stdout_file` - so that we keep it open until the end
|
||||||
#[cfg(all(unix, feature = "std", feature = "fork"))]
|
#[cfg(all(unix, feature = "std", feature = "fork"))]
|
||||||
#[builder(setter(skip), default = None)]
|
#[builder(setter(skip), default = None)]
|
||||||
opened_stdout_file: Option<File>,
|
opened_stdout_file: Option<File>,
|
||||||
@ -437,7 +437,7 @@ where
|
|||||||
/// `stdout_file`.
|
/// `stdout_file`.
|
||||||
#[builder(default = None)]
|
#[builder(default = None)]
|
||||||
stderr_file: Option<&'a str>,
|
stderr_file: Option<&'a str>,
|
||||||
/// The actual, opened, stdout_file - so that we keep it open until the end
|
/// The actual, opened, `stdout_file` - so that we keep it open until the end
|
||||||
#[cfg(all(unix, feature = "std", feature = "fork"))]
|
#[cfg(all(unix, feature = "std", feature = "fork"))]
|
||||||
#[builder(setter(skip), default = None)]
|
#[builder(setter(skip), default = None)]
|
||||||
opened_stderr_file: Option<File>,
|
opened_stderr_file: Option<File>,
|
||||||
|
@ -54,7 +54,7 @@ where
|
|||||||
{
|
{
|
||||||
/// The monitor
|
/// The monitor
|
||||||
monitor: MT,
|
monitor: MT,
|
||||||
/// The events that happened since the last handle_in_broker
|
/// The events that happened since the last `handle_in_broker`
|
||||||
events: Vec<Event<S::Input>>,
|
events: Vec<Event<S::Input>>,
|
||||||
/// The custom buf handler
|
/// The custom buf handler
|
||||||
custom_buf_handlers: Vec<Box<CustomBufHandlerFn<S>>>,
|
custom_buf_handlers: Vec<Box<CustomBufHandlerFn<S>>>,
|
||||||
|
@ -42,7 +42,7 @@ pub struct InProcessHooks {
|
|||||||
/// On timeout C function pointer
|
/// On timeout C function pointer
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub timeout_handler: *const c_void,
|
pub timeout_handler: *const c_void,
|
||||||
/// TImer struct
|
/// `TImer` struct
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub timer: TimerStruct,
|
pub timer: TimerStruct,
|
||||||
}
|
}
|
||||||
|
@ -101,9 +101,9 @@ pub(crate) struct InProcessForkExecutorGlobalData {
|
|||||||
pub state_ptr: *const c_void,
|
pub state_ptr: *const c_void,
|
||||||
/// Stores a pointer to the current input
|
/// Stores a pointer to the current input
|
||||||
pub current_input_ptr: *const c_void,
|
pub current_input_ptr: *const c_void,
|
||||||
/// Stores a pointer to the crash_handler function
|
/// Stores a pointer to the `crash_handler` function
|
||||||
pub crash_handler: *const c_void,
|
pub crash_handler: *const c_void,
|
||||||
/// Stores a pointer to the timeout_handler function
|
/// Stores a pointer to the `timeout_handler` function
|
||||||
pub timeout_handler: *const c_void,
|
pub timeout_handler: *const c_void,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@ Welcome to `LibAFL`
|
|||||||
clippy::missing_docs_in_private_items,
|
clippy::missing_docs_in_private_items,
|
||||||
clippy::module_name_repetitions,
|
clippy::module_name_repetitions,
|
||||||
clippy::ptr_cast_constness,
|
clippy::ptr_cast_constness,
|
||||||
clippy::unsafe_derive_deserialize
|
clippy::unsafe_derive_deserialize,
|
||||||
|
clippy::similar_names
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(not(test), warn(
|
#![cfg_attr(not(test), warn(
|
||||||
missing_debug_implementations,
|
missing_debug_implementations,
|
||||||
|
@ -53,9 +53,9 @@ pub struct MOpt {
|
|||||||
pub operator_num: usize,
|
pub operator_num: usize,
|
||||||
/// The number of swarms that we want to employ during the pilot fuzzing mode
|
/// The number of swarms that we want to employ during the pilot fuzzing mode
|
||||||
pub swarm_num: usize,
|
pub swarm_num: usize,
|
||||||
/// We'll generate inputs for `period_pilot` times before we call pso_update in pilot fuzzing module
|
/// We'll generate inputs for `period_pilot` times before we call `pso_update` in pilot fuzzing module
|
||||||
pub period_pilot: usize,
|
pub period_pilot: usize,
|
||||||
/// We'll generate inputs for `period_core` times before we call pso_update in core fuzzing module
|
/// We'll generate inputs for `period_core` times before we call `pso_update` in core fuzzing module
|
||||||
pub period_core: usize,
|
pub period_core: usize,
|
||||||
/// The number of testcases generated during this pilot fuzzing mode
|
/// The number of testcases generated during this pilot fuzzing mode
|
||||||
pub pilot_time: usize,
|
pub pilot_time: usize,
|
||||||
@ -77,23 +77,23 @@ pub struct MOpt {
|
|||||||
probability_now: Vec<Vec<f64>>,
|
probability_now: Vec<Vec<f64>>,
|
||||||
/// The fitness for each swarm, we'll calculate the fitness in the pilot fuzzing mode and use the best one in the core fuzzing mode
|
/// The fitness for each swarm, we'll calculate the fitness in the pilot fuzzing mode and use the best one in the core fuzzing mode
|
||||||
pub swarm_fitness: Vec<f64>,
|
pub swarm_fitness: Vec<f64>,
|
||||||
/// (Pilot Mode) Finds by each operators. This vector is used in pso_update
|
/// (Pilot Mode) Finds by each operators. This vector is used in `pso_update`
|
||||||
pub pilot_operator_finds: Vec<Vec<u64>>,
|
pub pilot_operator_finds: Vec<Vec<u64>>,
|
||||||
/// (Pilot Mode) Finds by each operator till now.
|
/// (Pilot Mode) Finds by each operator till now.
|
||||||
pub pilot_operator_finds_v2: Vec<Vec<u64>>,
|
pub pilot_operator_finds_v2: Vec<Vec<u64>>,
|
||||||
/// (Pilot Mode) The number of mutation operator used. This vector is used in pso_update
|
/// (Pilot Mode) The number of mutation operator used. This vector is used in `pso_update`
|
||||||
pub pilot_operator_cycles: Vec<Vec<u64>>,
|
pub pilot_operator_cycles: Vec<Vec<u64>>,
|
||||||
/// (Pilot Mode) The number of mutation operator used till now
|
/// (Pilot Mode) The number of mutation operator used till now
|
||||||
pub pilot_operator_cycles_v2: Vec<Vec<u64>>,
|
pub pilot_operator_cycles_v2: Vec<Vec<u64>>,
|
||||||
/// (Pilot Mode) The number of mutation operator used till last execution
|
/// (Pilot Mode) The number of mutation operator used till last execution
|
||||||
pub pilot_operator_cycles_v3: Vec<Vec<u64>>,
|
pub pilot_operator_cycles_v3: Vec<Vec<u64>>,
|
||||||
/// Vector used in pso_update
|
/// Vector used in `pso_update`
|
||||||
pub operator_finds_puppet: Vec<u64>,
|
pub operator_finds_puppet: Vec<u64>,
|
||||||
/// (Core Mode) Finds by each operators. This vector is used in pso_update
|
/// (Core Mode) Finds by each operators. This vector is used in `pso_update`
|
||||||
pub core_operator_finds: Vec<u64>,
|
pub core_operator_finds: Vec<u64>,
|
||||||
/// (Core Mode) Finds by each operator till now.
|
/// (Core Mode) Finds by each operator till now.
|
||||||
pub core_operator_finds_v2: Vec<u64>,
|
pub core_operator_finds_v2: Vec<u64>,
|
||||||
/// (Core Mode) The number of mutation operator used. This vector is used in pso_update
|
/// (Core Mode) The number of mutation operator used. This vector is used in `pso_update`
|
||||||
pub core_operator_cycles: Vec<u64>,
|
pub core_operator_cycles: Vec<u64>,
|
||||||
/// (Core Mode) The number of mutation operator used till now
|
/// (Core Mode) The number of mutation operator used till now
|
||||||
pub core_operator_cycles_v2: Vec<u64>,
|
pub core_operator_cycles_v2: Vec<u64>,
|
||||||
|
@ -468,10 +468,10 @@ struct cmp_map {
|
|||||||
allow(clippy::unsafe_derive_deserialize)
|
allow(clippy::unsafe_derive_deserialize)
|
||||||
)] // for SerdeAny
|
)] // for SerdeAny
|
||||||
pub struct AFLppCmpValuesMetadata {
|
pub struct AFLppCmpValuesMetadata {
|
||||||
/// The first map of AFLppCmpLogVals retrieved by running the un-mutated input
|
/// The first map of `AFLppCmpLogVals` retrieved by running the un-mutated input
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub orig_cmpvals: HashMap<usize, Vec<CmpValues>>,
|
pub orig_cmpvals: HashMap<usize, Vec<CmpValues>>,
|
||||||
/// The second map of AFLppCmpLogVals retrieved by runnning the mutated input
|
/// The second map of `AFLppCmpLogVals` retrieved by runnning the mutated input
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub new_cmpvals: HashMap<usize, Vec<CmpValues>>,
|
pub new_cmpvals: HashMap<usize, Vec<CmpValues>>,
|
||||||
/// The list of logged idx and headers retrieved by runnning the mutated input
|
/// The list of logged idx and headers retrieved by runnning the mutated input
|
||||||
|
@ -39,7 +39,7 @@ pub struct SchedulerMetadata {
|
|||||||
cycles: u64,
|
cycles: u64,
|
||||||
/// Size of the observer map
|
/// Size of the observer map
|
||||||
bitmap_size: u64,
|
bitmap_size: u64,
|
||||||
/// Sum of log(bitmap_size)
|
/// Sum of `log(bitmap_size`)
|
||||||
bitmap_size_log: f64,
|
bitmap_size_log: f64,
|
||||||
/// Number of filled map entries
|
/// Number of filled map entries
|
||||||
bitmap_entries: u64,
|
bitmap_entries: u64,
|
||||||
|
@ -7,18 +7,29 @@ use alloc::string::String;
|
|||||||
use alloc::{borrow::ToOwned, string::ToString, vec::Vec};
|
use alloc::{borrow::ToOwned, string::ToString, vec::Vec};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use super::{Stage, TracingStage};
|
use libafl_bolts::tuples::MatchName;
|
||||||
|
|
||||||
|
use super::{RetryProgress, RetryingStage, Stage, TracingStage};
|
||||||
|
#[cfg(all(feature = "concolic_mutation", feature = "introspection"))]
|
||||||
|
use crate::monitors::PerfFeature;
|
||||||
#[cfg(all(feature = "introspection", feature = "concolic_mutation"))]
|
#[cfg(all(feature = "introspection", feature = "concolic_mutation"))]
|
||||||
use crate::state::HasClientPerfMonitor;
|
use crate::state::HasClientPerfMonitor;
|
||||||
#[cfg(feature = "concolic_mutation")]
|
#[cfg(feature = "concolic_mutation")]
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::Corpus,
|
corpus::{Corpus, HasCurrentCorpusIdx},
|
||||||
executors::{Executor, HasObservers},
|
executors::{Executor, HasObservers},
|
||||||
observers::concolic::ConcolicObserver,
|
observers::concolic::ConcolicObserver,
|
||||||
state::{HasCorpus, HasExecutions, HasMetadata},
|
state::{HasCorpus, HasExecutions, HasMetadata, HasNamedMetadata, UsesState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "concolic_mutation")]
|
||||||
|
use crate::{
|
||||||
|
inputs::HasBytesVec,
|
||||||
|
mark_feature_time,
|
||||||
|
observers::concolic::{ConcolicMetadata, SymExpr, SymExprRef},
|
||||||
|
start_timer, Evaluator,
|
||||||
|
};
|
||||||
|
|
||||||
/// Wraps a [`TracingStage`] to add concolic observing.
|
/// Wraps a [`TracingStage`] to add concolic observing.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -39,16 +50,16 @@ where
|
|||||||
E: UsesState<State = TE::State>,
|
E: UsesState<State = TE::State>,
|
||||||
EM: UsesState<State = TE::State>,
|
EM: UsesState<State = TE::State>,
|
||||||
TE: Executor<EM, Z> + HasObservers,
|
TE: Executor<EM, Z> + HasObservers,
|
||||||
TE::State: HasExecutions + HasCorpus,
|
TE::State: HasExecutions + HasCorpus + HasNamedMetadata,
|
||||||
Z: UsesState<State = TE::State>,
|
Z: UsesState<State = TE::State>,
|
||||||
{
|
{
|
||||||
type Progress = (); // stage cannot be resumed
|
type Progress = RetryProgress;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
executor: &mut E,
|
_executor: &mut E,
|
||||||
state: &mut TE::State,
|
state: &mut TE::State,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
@ -57,8 +68,11 @@ where
|
|||||||
"state is not currently processing a corpus index",
|
"state is not currently processing a corpus index",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
if Self::Progress::should_skip(state, &self.inner, corpus_idx)? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
self.inner.perform(fuzzer, executor, state, manager)?;
|
self.inner.trace(fuzzer, state, manager, corpus_idx)?;
|
||||||
if let Some(observer) = self
|
if let Some(observer) = self
|
||||||
.inner
|
.inner
|
||||||
.executor()
|
.executor()
|
||||||
@ -78,8 +92,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<EM, TE, Z> RetryingStage for ConcolicTracingStage<EM, TE, Z> {
|
||||||
|
fn max_retries(&self) -> usize {
|
||||||
|
self.inner.max_retries()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<EM, TE, Z> ConcolicTracingStage<EM, TE, Z> {
|
impl<EM, TE, Z> ConcolicTracingStage<EM, TE, Z> {
|
||||||
/// Creates a new default tracing stage using the given [`Executor`], observing traces from a [`ConcolicObserver`] with the given name.
|
/// Creates a new default tracing stage using the given [`Executor`], observing traces from a
|
||||||
|
/// [`ConcolicObserver`] with the given name. The [`RetryingStage::max_retries`] is
|
||||||
|
/// used from the provided inner stage.
|
||||||
pub fn new(inner: TracingStage<EM, TE, Z>, observer_name: String) -> Self {
|
pub fn new(inner: TracingStage<EM, TE, Z>, observer_name: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner,
|
inner,
|
||||||
@ -88,19 +110,6 @@ impl<EM, TE, Z> ConcolicTracingStage<EM, TE, Z> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use libafl_bolts::tuples::MatchName;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "concolic_mutation", feature = "introspection"))]
|
|
||||||
use crate::monitors::PerfFeature;
|
|
||||||
use crate::{corpus::HasCurrentCorpusIdx, state::UsesState};
|
|
||||||
#[cfg(feature = "concolic_mutation")]
|
|
||||||
use crate::{
|
|
||||||
inputs::HasBytesVec,
|
|
||||||
mark_feature_time,
|
|
||||||
observers::concolic::{ConcolicMetadata, SymExpr, SymExprRef},
|
|
||||||
start_timer, Evaluator,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "concolic_mutation")]
|
#[cfg(feature = "concolic_mutation")]
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
fn generate_mutations(iter: impl Iterator<Item = (SymExprRef, SymExpr)>) -> Vec<Vec<(usize, u8)>> {
|
fn generate_mutations(iter: impl Iterator<Item = (SymExprRef, SymExpr)>) -> Vec<Vec<(usize, u8)>> {
|
||||||
|
@ -12,25 +12,25 @@ use crate::{
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NestedStageProgress;
|
pub struct NestedStageProgress;
|
||||||
|
|
||||||
impl<S> StageProgress<S> for NestedStageProgress
|
impl<S, ST> StageProgress<S, ST> for NestedStageProgress
|
||||||
where
|
where
|
||||||
S: HasNestedStageStatus,
|
S: HasNestedStageStatus,
|
||||||
{
|
{
|
||||||
fn initialize_progress(state: &mut S) -> Result<(), Error> {
|
fn initialize_progress(state: &mut S, _stage: &ST) -> Result<(), Error> {
|
||||||
state.enter_inner_stage()?;
|
state.enter_inner_stage()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_progress(state: &mut S) -> Result<(), Error> {
|
fn clear_progress(state: &mut S, _stage: &ST) -> Result<(), Error> {
|
||||||
state.exit_inner_stage()?;
|
state.exit_inner_stage()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress(_state: &S) -> Result<&Self, Error> {
|
fn progress<'a>(_state: &'a S, _stage: &ST) -> Result<&'a Self, Error> {
|
||||||
unimplemented!("NestedStageProgress should not be queried")
|
unimplemented!("NestedStageProgress should not be queried")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress_mut(_state: &mut S) -> Result<&mut Self, Error> {
|
fn progress_mut<'a>(_state: &'a mut S, _stage: &ST) -> Result<&'a mut Self, Error> {
|
||||||
unimplemented!("NestedStageProgress should not be queried")
|
unimplemented!("NestedStageProgress should not be queried")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,12 @@ pub use concolic::SimpleConcolicMutationalStage;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use dump::*;
|
pub use dump::*;
|
||||||
pub use generalization::GeneralizationStage;
|
pub use generalization::GeneralizationStage;
|
||||||
use libafl_bolts::tuples::HasConstLen;
|
use hashbrown::HashSet;
|
||||||
|
use libafl_bolts::{impl_serdeany, tuples::HasConstLen};
|
||||||
pub use logics::*;
|
pub use logics::*;
|
||||||
pub use mutational::{MutationalStage, StdMutationalStage};
|
pub use mutational::{MutationalStage, StdMutationalStage};
|
||||||
pub use power::{PowerMutationalStage, StdPowerMutationalStage};
|
pub use power::{PowerMutationalStage, StdPowerMutationalStage};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
pub use stats::AflStatsStage;
|
pub use stats::AflStatsStage;
|
||||||
#[cfg(feature = "unicode")]
|
#[cfg(feature = "unicode")]
|
||||||
pub use string::*;
|
pub use string::*;
|
||||||
@ -32,13 +34,16 @@ pub use tuneable::*;
|
|||||||
|
|
||||||
use self::push::PushStage;
|
use self::push::PushStage;
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::HasCurrentCorpusIdx,
|
corpus::{CorpusId, HasCurrentCorpusIdx},
|
||||||
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
|
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
|
||||||
executors::{Executor, HasObservers},
|
executors::{Executor, HasObservers},
|
||||||
inputs::UsesInput,
|
inputs::UsesInput,
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
schedulers::Scheduler,
|
schedulers::Scheduler,
|
||||||
state::{HasCorpus, HasExecutions, HasLastReportTime, HasMetadata, HasRand, UsesState},
|
state::{
|
||||||
|
HasCorpus, HasExecutions, HasLastReportTime, HasMetadata, HasNamedMetadata, HasRand,
|
||||||
|
UsesState,
|
||||||
|
},
|
||||||
Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler,
|
Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,7 +81,7 @@ where
|
|||||||
// TODO: see RFC 2532: https://github.com/rust-lang/rust/issues/29661
|
// TODO: see RFC 2532: https://github.com/rust-lang/rust/issues/29661
|
||||||
// type Status: ResumableStageStatus = ();
|
// type Status: ResumableStageStatus = ();
|
||||||
/// The resumption data for this stage. Set to () if resuming is not necessary/possible.
|
/// The resumption data for this stage. Set to () if resuming is not necessary/possible.
|
||||||
type Progress: StageProgress<Self::State>;
|
type Progress: StageProgress<Self::State, Self>;
|
||||||
|
|
||||||
/// Run the stage
|
/// Run the stage
|
||||||
fn perform(
|
fn perform(
|
||||||
@ -152,9 +157,10 @@ where
|
|||||||
}
|
}
|
||||||
Some(idx) if idx == Self::LEN => {
|
Some(idx) if idx == Self::LEN => {
|
||||||
// perform the stage, but don't set it
|
// perform the stage, but don't set it
|
||||||
Head::Progress::initialize_progress(state)?;
|
let stage = &mut self.0;
|
||||||
self.0.perform(fuzzer, executor, state, manager)?;
|
Head::Progress::initialize_progress(state, stage)?;
|
||||||
Head::Progress::clear_progress(state)?;
|
stage.perform(fuzzer, executor, state, manager)?;
|
||||||
|
Head::Progress::clear_progress(state, stage)?;
|
||||||
state.clear_stage()?;
|
state.clear_stage()?;
|
||||||
}
|
}
|
||||||
Some(idx) if idx > Self::LEN => {
|
Some(idx) if idx > Self::LEN => {
|
||||||
@ -163,9 +169,10 @@ where
|
|||||||
// this is None, but the match can't deduce that
|
// this is None, but the match can't deduce that
|
||||||
_ => {
|
_ => {
|
||||||
state.set_stage(Self::LEN)?;
|
state.set_stage(Self::LEN)?;
|
||||||
Head::Progress::initialize_progress(state)?;
|
let stage = &mut self.0;
|
||||||
self.0.perform(fuzzer, executor, state, manager)?;
|
Head::Progress::initialize_progress(state, stage)?;
|
||||||
Head::Progress::clear_progress(state)?;
|
stage.perform(fuzzer, executor, state, manager)?;
|
||||||
|
Head::Progress::clear_progress(state, stage)?;
|
||||||
state.clear_stage()?;
|
state.clear_stage()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -541,38 +548,128 @@ pub mod pybind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for status tracking of stages which stash data to resume
|
/// Trait for status tracking of stages which stash data to resume
|
||||||
pub trait StageProgress<S> {
|
pub trait StageProgress<S, ST>
|
||||||
|
where
|
||||||
|
ST: ?Sized,
|
||||||
|
{
|
||||||
/// Initialize the current status tracking for this stage, if it is not yet initialised
|
/// Initialize the current status tracking for this stage, if it is not yet initialised
|
||||||
fn initialize_progress(state: &mut S) -> Result<(), Error>;
|
fn initialize_progress(state: &mut S, stage: &ST) -> Result<(), Error>;
|
||||||
|
|
||||||
/// Clear the current status tracking of the associated stage
|
/// Clear the current status tracking of the associated stage
|
||||||
fn clear_progress(state: &mut S) -> Result<(), Error>;
|
fn clear_progress(state: &mut S, stage: &ST) -> Result<(), Error>;
|
||||||
|
|
||||||
/// Get the current status tracking of this stage
|
/// Get the current status tracking of this stage
|
||||||
fn progress(state: &S) -> Result<&Self, Error>;
|
fn progress<'a>(state: &'a S, stage: &ST) -> Result<&'a Self, Error>;
|
||||||
|
|
||||||
/// Get the current status tracking of this stage, mutably
|
/// Get the current status tracking of this stage, mutably
|
||||||
fn progress_mut(state: &mut S) -> Result<&mut Self, Error>;
|
fn progress_mut<'a>(state: &'a mut S, stage: &ST) -> Result<&'a mut Self, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> StageProgress<S> for () {
|
impl<S, ST> StageProgress<S, ST> for () {
|
||||||
fn initialize_progress(_state: &mut S) -> Result<(), Error> {
|
fn initialize_progress(_state: &mut S, _stage: &ST) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_progress(_state: &mut S) -> Result<(), Error> {
|
fn clear_progress(_state: &mut S, _stage: &ST) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress(_state: &S) -> Result<&Self, Error> {
|
fn progress<'a>(_state: &'a S, _stage: &ST) -> Result<&'a Self, Error> {
|
||||||
unimplemented!("The empty tuple resumable stage status should never be queried")
|
unimplemented!("The empty tuple resumable stage status should never be queried")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress_mut(_state: &mut S) -> Result<&mut Self, Error> {
|
fn progress_mut<'a>(_state: &'a mut S, _stage: &ST) -> Result<&'a mut Self, Error> {
|
||||||
unimplemented!("The empty tuple resumable stage status should never be queried")
|
unimplemented!("The empty tuple resumable stage status should never be queried")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Progress which permits a fixed amount of resumes per round of fuzzing. If this amount is ever
|
||||||
|
/// exceeded, the input will no longer be executed by this stage.
|
||||||
|
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||||
|
pub struct RetryProgress {
|
||||||
|
tries_remaining: Option<usize>,
|
||||||
|
skipped: HashSet<CorpusId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_serdeany!(RetryProgress);
|
||||||
|
|
||||||
|
/// Stage which specifies a certain amount of retries over which a scheduled input is attempted. To
|
||||||
|
/// be used in combination with [`RetryProgress`].
|
||||||
|
pub trait RetryingStage {
|
||||||
|
/// The number of times each testcase may be retries.
|
||||||
|
fn max_retries(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, ST> StageProgress<S, ST> for RetryProgress
|
||||||
|
where
|
||||||
|
S: HasNamedMetadata,
|
||||||
|
ST: RetryingStage,
|
||||||
|
{
|
||||||
|
fn initialize_progress(state: &mut S, stage: &ST) -> Result<(), Error> {
|
||||||
|
if let Ok(metadata) = state.named_metadata_mut::<Self>(core::any::type_name_of_val(stage)) {
|
||||||
|
if let Some(ref mut remaining) = metadata.tries_remaining {
|
||||||
|
*remaining = remaining.checked_sub(1).ok_or_else(|| {
|
||||||
|
Error::illegal_state(
|
||||||
|
"Attempted further retries after we had already gotten to none remaining.",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
metadata.tries_remaining = Some(stage.max_retries() + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.add_named_metadata(
|
||||||
|
Self {
|
||||||
|
tries_remaining: Some(stage.max_retries() + 1),
|
||||||
|
skipped: HashSet::new(),
|
||||||
|
},
|
||||||
|
core::any::type_name_of_val(stage),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_progress(state: &mut S, stage: &ST) -> Result<(), Error> {
|
||||||
|
let metadata = state.named_metadata_mut::<Self>(core::any::type_name_of_val(stage))?;
|
||||||
|
metadata.tries_remaining = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn progress<'a>(state: &'a S, stage: &ST) -> Result<&'a Self, Error> {
|
||||||
|
state.named_metadata::<Self>(core::any::type_name_of_val(stage))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn progress_mut<'a>(state: &'a mut S, stage: &ST) -> Result<&'a mut Self, Error> {
|
||||||
|
state.named_metadata_mut::<Self>(core::any::type_name_of_val(stage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RetryProgress {
|
||||||
|
/// Whether we should skip the provided corpus entry.
|
||||||
|
pub fn should_skip<S, ST>(
|
||||||
|
state: &mut S,
|
||||||
|
stage: &ST,
|
||||||
|
corpus_idx: CorpusId,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
S: HasNamedMetadata,
|
||||||
|
ST: RetryingStage,
|
||||||
|
{
|
||||||
|
let progress = Self::progress_mut(state, stage)?;
|
||||||
|
if progress.skipped.contains(&corpus_idx) {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
let remaining = progress.tries_remaining.as_mut().ok_or_else(||
|
||||||
|
Error::illegal_state(
|
||||||
|
"Attempted to check if we should skip a testcase without having initialised the number of tries remaining.",
|
||||||
|
))?;
|
||||||
|
if *remaining == 0 {
|
||||||
|
progress.skipped.insert(corpus_idx);
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Trait for types which track the current stage
|
/// Trait for types which track the current stage
|
||||||
pub trait HasCurrentStage {
|
pub trait HasCurrentStage {
|
||||||
/// Set the current stage; we have started processing this stage
|
/// Set the current stage; we have started processing this stage
|
||||||
@ -611,11 +708,13 @@ pub mod test {
|
|||||||
use tuple_list::{tuple_list, tuple_list_type};
|
use tuple_list::{tuple_list, tuple_list_type};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
corpus::{Corpus, Testcase},
|
||||||
events::NopEventManager,
|
events::NopEventManager,
|
||||||
executors::test::NopExecutor,
|
executors::test::NopExecutor,
|
||||||
fuzzer::test::NopFuzzer,
|
fuzzer::test::NopFuzzer,
|
||||||
stages::{Stage, StageProgress, StagesTuple},
|
inputs::NopInput,
|
||||||
state::{HasMetadata, State, UsesState},
|
stages::{RetryProgress, RetryingStage, Stage, StageProgress, StagesTuple},
|
||||||
|
state::{test::test_std_state, HasCorpus, HasMetadata, State, UsesState},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -636,11 +735,11 @@ pub mod test {
|
|||||||
|
|
||||||
impl_serdeany!(TestProgress);
|
impl_serdeany!(TestProgress);
|
||||||
|
|
||||||
impl<S> StageProgress<S> for TestProgress
|
impl<S, ST> StageProgress<S, ST> for TestProgress
|
||||||
where
|
where
|
||||||
S: HasMetadata,
|
S: HasMetadata,
|
||||||
{
|
{
|
||||||
fn initialize_progress(state: &mut S) -> Result<(), Error> {
|
fn initialize_progress(state: &mut S, _stage: &ST) -> Result<(), Error> {
|
||||||
// check if we're resuming
|
// check if we're resuming
|
||||||
if !state.has_metadata::<Self>() {
|
if !state.has_metadata::<Self>() {
|
||||||
state.add_metadata(Self { count: 0 });
|
state.add_metadata(Self { count: 0 });
|
||||||
@ -648,7 +747,7 @@ pub mod test {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_progress(state: &mut S) -> Result<(), Error> {
|
fn clear_progress(state: &mut S, _stage: &ST) -> Result<(), Error> {
|
||||||
if state.metadata_map_mut().remove::<Self>().is_none() {
|
if state.metadata_map_mut().remove::<Self>().is_none() {
|
||||||
return Err(Error::illegal_state(
|
return Err(Error::illegal_state(
|
||||||
"attempted to clear status metadata when none was present",
|
"attempted to clear status metadata when none was present",
|
||||||
@ -657,11 +756,11 @@ pub mod test {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress(state: &S) -> Result<&Self, Error> {
|
fn progress<'a>(state: &'a S, _stage: &ST) -> Result<&'a Self, Error> {
|
||||||
state.metadata()
|
state.metadata()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress_mut(state: &mut S) -> Result<&mut Self, Error> {
|
fn progress_mut<'a>(state: &'a mut S, _stage: &ST) -> Result<&'a mut Self, Error> {
|
||||||
state.metadata_mut()
|
state.metadata_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -690,7 +789,7 @@ pub mod test {
|
|||||||
_manager: &mut EM,
|
_manager: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// metadata is attached by the status
|
// metadata is attached by the status
|
||||||
let meta = Self::Progress::progress_mut(state)?;
|
let meta = Self::Progress::progress_mut(state, self)?;
|
||||||
meta.count += 1;
|
meta.count += 1;
|
||||||
assert!(
|
assert!(
|
||||||
meta.count == 1,
|
meta.count == 1,
|
||||||
@ -725,7 +824,7 @@ pub mod test {
|
|||||||
_manager: &mut EM,
|
_manager: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// metadata is attached by the status
|
// metadata is attached by the status
|
||||||
let meta = Self::Progress::progress_mut(state)?;
|
let meta = Self::Progress::progress_mut(state, self)?;
|
||||||
meta.count += 1;
|
meta.count += 1;
|
||||||
|
|
||||||
if meta.count == 1 {
|
if meta.count == 1 {
|
||||||
@ -800,4 +899,60 @@ pub mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tries_progress() -> Result<(), Error> {
|
||||||
|
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
|
||||||
|
unsafe {
|
||||||
|
RetryProgress::register();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StageWithOneTry;
|
||||||
|
|
||||||
|
impl RetryingStage for StageWithOneTry {
|
||||||
|
fn max_retries(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state = test_std_state();
|
||||||
|
let stage = StageWithOneTry;
|
||||||
|
|
||||||
|
let corpus_idx = state.corpus_mut().add(Testcase::new(NopInput {}))?;
|
||||||
|
|
||||||
|
for _ in 0..10 {
|
||||||
|
// used normally, no retries means we never skip
|
||||||
|
RetryProgress::initialize_progress(&mut state, &stage)?;
|
||||||
|
assert!(!RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
||||||
|
RetryProgress::clear_progress(&mut state, &stage)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..10 {
|
||||||
|
// used normally, only one retry means we never skip
|
||||||
|
RetryProgress::initialize_progress(&mut state, &stage)?;
|
||||||
|
assert!(!RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
||||||
|
RetryProgress::initialize_progress(&mut state, &stage)?;
|
||||||
|
assert!(!RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
||||||
|
RetryProgress::clear_progress(&mut state, &stage)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
RetryProgress::initialize_progress(&mut state, &stage)?;
|
||||||
|
assert!(!RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
||||||
|
// task failed, let's resume
|
||||||
|
RetryProgress::initialize_progress(&mut state, &stage)?;
|
||||||
|
// we still have one more try!
|
||||||
|
assert!(!RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
||||||
|
// task failed, let's resume
|
||||||
|
RetryProgress::initialize_progress(&mut state, &stage)?;
|
||||||
|
// out of retries, so now we skip
|
||||||
|
assert!(RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
||||||
|
RetryProgress::clear_progress(&mut state, &stage)?;
|
||||||
|
|
||||||
|
RetryProgress::initialize_progress(&mut state, &stage)?;
|
||||||
|
// we previously exhausted this testcase's retries, so we skip
|
||||||
|
assert!(RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
||||||
|
RetryProgress::clear_progress(&mut state, &stage)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
use core::{fmt::Debug, marker::PhantomData};
|
use core::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx},
|
||||||
executors::{Executor, HasObservers, ShadowExecutor},
|
executors::{Executor, HasObservers, ShadowExecutor},
|
||||||
mark_feature_time,
|
mark_feature_time,
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
stages::Stage,
|
stages::{RetryProgress, RetryingStage, Stage},
|
||||||
start_timer,
|
start_timer,
|
||||||
state::{HasCorpus, HasExecutions, State, UsesState},
|
state::{HasCorpus, HasExecutions, HasNamedMetadata, State, UsesState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
@ -19,6 +19,7 @@ use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TracingStage<EM, TE, Z> {
|
pub struct TracingStage<EM, TE, Z> {
|
||||||
tracer_executor: TE,
|
tracer_executor: TE,
|
||||||
|
max_retries: usize,
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
phantom: PhantomData<(EM, TE, Z)>,
|
phantom: PhantomData<(EM, TE, Z)>,
|
||||||
}
|
}
|
||||||
@ -30,30 +31,23 @@ where
|
|||||||
type State = TE::State;
|
type State = TE::State;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, EM, TE, Z> Stage<E, EM, Z> for TracingStage<EM, TE, Z>
|
impl<EM, TE, Z> TracingStage<EM, TE, Z>
|
||||||
where
|
where
|
||||||
E: UsesState<State = TE::State>,
|
|
||||||
TE: Executor<EM, Z> + HasObservers,
|
TE: Executor<EM, Z> + HasObservers,
|
||||||
TE::State: HasExecutions + HasCorpus,
|
TE::State: HasExecutions + HasCorpus + HasNamedMetadata,
|
||||||
EM: UsesState<State = TE::State>,
|
EM: UsesState<State = TE::State>,
|
||||||
Z: UsesState<State = TE::State>,
|
Z: UsesState<State = TE::State>,
|
||||||
{
|
{
|
||||||
type Progress = (); // this stage cannot be resumed
|
/// Perform tracing on the given [`CorpusId`]. Useful for if wrapping [`TracingStage`] with your
|
||||||
|
/// own stage and you need to manage [`super::StageProgress`] differently; see
|
||||||
#[inline]
|
/// [`super::ConcolicTracingStage`]'s implementation as an example of usage.
|
||||||
fn perform(
|
pub fn trace(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
_executor: &mut E,
|
|
||||||
state: &mut TE::State,
|
state: &mut TE::State,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
|
corpus_idx: CorpusId,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let Some(corpus_idx) = state.current_corpus_idx()? else {
|
|
||||||
return Err(Error::illegal_state(
|
|
||||||
"state is not currently processing a corpus index",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
start_timer!(state);
|
start_timer!(state);
|
||||||
let input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
let input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
||||||
|
|
||||||
@ -83,15 +77,63 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E, EM, TE, Z> Stage<E, EM, Z> for TracingStage<EM, TE, Z>
|
||||||
|
where
|
||||||
|
E: UsesState<State = TE::State>,
|
||||||
|
TE: Executor<EM, Z> + HasObservers,
|
||||||
|
TE::State: HasExecutions + HasCorpus + HasNamedMetadata,
|
||||||
|
EM: UsesState<State = TE::State>,
|
||||||
|
Z: UsesState<State = TE::State>,
|
||||||
|
{
|
||||||
|
type Progress = RetryProgress;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn perform(
|
||||||
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
|
_executor: &mut E,
|
||||||
|
state: &mut TE::State,
|
||||||
|
manager: &mut EM,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let Some(corpus_idx) = state.current_corpus_idx()? else {
|
||||||
|
return Err(Error::illegal_state(
|
||||||
|
"state is not currently processing a corpus index",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
if Self::Progress::should_skip(state, self, corpus_idx)? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.trace(fuzzer, state, manager, corpus_idx)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<EM, TE, Z> RetryingStage for TracingStage<EM, TE, Z> {
|
||||||
|
fn max_retries(&self) -> usize {
|
||||||
|
self.max_retries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<EM, TE, Z> TracingStage<EM, TE, Z> {
|
impl<EM, TE, Z> TracingStage<EM, TE, Z> {
|
||||||
/// Creates a new default stage
|
/// Creates a new default stage
|
||||||
pub fn new(tracer_executor: TE) -> Self {
|
pub fn new(tracer_executor: TE) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tracer_executor,
|
tracer_executor,
|
||||||
|
max_retries: 10,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specify how many times that this stage will try again to trace the input before giving up
|
||||||
|
/// and not processing the input again. 0 retries means that the trace will be tried only once.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_retries(mut self, retries: usize) -> Self {
|
||||||
|
self.max_retries = retries;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the underlying tracer executor
|
/// Gets the underlying tracer executor
|
||||||
pub fn executor(&self) -> &TE {
|
pub fn executor(&self) -> &TE {
|
||||||
&self.tracer_executor
|
&self.tracer_executor
|
||||||
@ -102,9 +144,11 @@ impl<EM, TE, Z> TracingStage<EM, TE, Z> {
|
|||||||
&mut self.tracer_executor
|
&mut self.tracer_executor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A stage that runs the shadow executor using also the shadow observers
|
/// A stage that runs the shadow executor using also the shadow observers
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ShadowTracingStage<E, EM, SOT, Z> {
|
pub struct ShadowTracingStage<E, EM, SOT, Z> {
|
||||||
|
max_retries: usize,
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
phantom: PhantomData<(E, EM, SOT, Z)>,
|
phantom: PhantomData<(E, EM, SOT, Z)>,
|
||||||
}
|
}
|
||||||
@ -122,9 +166,9 @@ where
|
|||||||
EM: UsesState<State = E::State>,
|
EM: UsesState<State = E::State>,
|
||||||
SOT: ObserversTuple<E::State>,
|
SOT: ObserversTuple<E::State>,
|
||||||
Z: UsesState<State = E::State>,
|
Z: UsesState<State = E::State>,
|
||||||
E::State: State + HasExecutions + HasCorpus + Debug,
|
E::State: State + HasExecutions + HasCorpus + HasNamedMetadata + Debug,
|
||||||
{
|
{
|
||||||
type Progress = (); // this stage cannot be resumed
|
type Progress = RetryProgress;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
@ -139,6 +183,9 @@ where
|
|||||||
"state is not currently processing a corpus index",
|
"state is not currently processing a corpus index",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
if Self::Progress::should_skip(state, self, corpus_idx)? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
start_timer!(state);
|
start_timer!(state);
|
||||||
let input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
let input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
||||||
@ -171,6 +218,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E, EM, SOT, Z> RetryingStage for ShadowTracingStage<E, EM, SOT, Z> {
|
||||||
|
fn max_retries(&self) -> usize {
|
||||||
|
self.max_retries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E, EM, SOT, Z> ShadowTracingStage<E, EM, SOT, Z>
|
impl<E, EM, SOT, Z> ShadowTracingStage<E, EM, SOT, Z>
|
||||||
where
|
where
|
||||||
E: Executor<EM, Z> + HasObservers,
|
E: Executor<EM, Z> + HasObservers,
|
||||||
@ -182,7 +235,16 @@ where
|
|||||||
/// Creates a new default stage
|
/// Creates a new default stage
|
||||||
pub fn new(_executor: &mut ShadowExecutor<E, SOT>) -> Self {
|
pub fn new(_executor: &mut ShadowExecutor<E, SOT>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
max_retries: 10,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specify how many times that this stage will try again to trace the input before giving up
|
||||||
|
/// and not processing the input again. 0 retries means that the trace will be tried only once.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_retries(mut self, retries: usize) -> Self {
|
||||||
|
self.max_retries = retries;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,7 +312,7 @@ pub struct StdState<I, C, R, SC> {
|
|||||||
metadata: SerdeAnyMap,
|
metadata: SerdeAnyMap,
|
||||||
/// Metadata stored with names
|
/// Metadata stored with names
|
||||||
named_metadata: NamedSerdeAnyMap,
|
named_metadata: NamedSerdeAnyMap,
|
||||||
/// MaxSize testcase size for mutators that appreciate it
|
/// `MaxSize` testcase size for mutators that appreciate it
|
||||||
max_size: usize,
|
max_size: usize,
|
||||||
/// Performance statistics for this fuzzer
|
/// Performance statistics for this fuzzer
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
|
@ -324,7 +324,7 @@ pub enum TcpResponse {
|
|||||||
},
|
},
|
||||||
/// Notify the client on the other side that it has been accepted.
|
/// Notify the client on the other side that it has been accepted.
|
||||||
LocalClientAccepted {
|
LocalClientAccepted {
|
||||||
/// The ClientId this client should send messages as.
|
/// The `ClientId` this client should send messages as.
|
||||||
/// Mainly used for client-side deduplication of incoming messages
|
/// Mainly used for client-side deduplication of incoming messages
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
},
|
},
|
||||||
@ -792,7 +792,7 @@ pub struct LlmpPage {
|
|||||||
/// (The os may have tidied up the memory when the receiver starts to map)
|
/// (The os may have tidied up the memory when the receiver starts to map)
|
||||||
pub receivers_joined_count: AtomicU16,
|
pub receivers_joined_count: AtomicU16,
|
||||||
/// Set to != 1 by the receiver, once it left again after joining.
|
/// Set to != 1 by the receiver, once it left again after joining.
|
||||||
/// It's not safe for the sender to re-map this page before this is equal to receivers_joined_count
|
/// It's not safe for the sender to re-map this page before this is equal to `receivers_joined_count`
|
||||||
pub receivers_left_count: AtomicU16,
|
pub receivers_left_count: AtomicU16,
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
/// The current message ID
|
/// The current message ID
|
||||||
@ -1796,7 +1796,7 @@ where
|
|||||||
SHM: ShMem,
|
SHM: ShMem,
|
||||||
{
|
{
|
||||||
/// Shmem containg the actual (unsafe) page,
|
/// Shmem containg the actual (unsafe) page,
|
||||||
/// shared between one LlmpSender and one LlmpReceiver
|
/// shared between one `LlmpSender` and one `LlmpReceiver`
|
||||||
shmem: SHM,
|
shmem: SHM,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1962,9 +1962,9 @@ where
|
|||||||
/// The amount of total clients that should have connected and (and disconnected)
|
/// The amount of total clients that should have connected and (and disconnected)
|
||||||
/// after which the broker loop should quit gracefully.
|
/// after which the broker loop should quit gracefully.
|
||||||
pub exit_cleanly_after: Option<NonZeroUsize>,
|
pub exit_cleanly_after: Option<NonZeroUsize>,
|
||||||
/// Clients that should be removed soon, (offset into llmp_clients)
|
/// Clients that should be removed soon, (offset into `llmp_clients`)
|
||||||
clients_to_remove: Vec<usize>,
|
clients_to_remove: Vec<usize>,
|
||||||
/// The ShMemProvider to use
|
/// The `ShMemProvider` to use
|
||||||
shmem_provider: SP,
|
shmem_provider: SP,
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
/// The timeout after which a client will be considered stale, and removed.
|
/// The timeout after which a client will be considered stale, and removed.
|
||||||
|
@ -282,7 +282,7 @@ pub enum ServedShMemRequest {
|
|||||||
PreFork(),
|
PreFork(),
|
||||||
/// The client's child re-registers with us after it forked.
|
/// The client's child re-registers with us after it forked.
|
||||||
PostForkChildHello(i32),
|
PostForkChildHello(i32),
|
||||||
/// The ShMem Service should exit. This is sually sent internally on `drop`, but feel free to do whatever with it?
|
/// The `ShMem` Service should exit. This is sually sent internally on `drop`, but feel free to do whatever with it?
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,6 +396,6 @@ mod tests {
|
|||||||
assert_eq!(*distances.get(&((41864 >> 1) ^ 26911)).unwrap(), 1);
|
assert_eq!(*distances.get(&((41864 >> 1) ^ 26911)).unwrap(), 1);
|
||||||
assert_eq!(*distances.get(&((26911 >> 1) ^ 52706)).unwrap(), 2);
|
assert_eq!(*distances.get(&((26911 >> 1) ^ 52706)).unwrap(), 2);
|
||||||
assert_eq!(*distances.get(&((26911 >> 1) ^ 41925)).unwrap(), 2);
|
assert_eq!(*distances.get(&((26911 >> 1) ^ 41925)).unwrap(), 2);
|
||||||
assert!(distances.get(&((41864 >> 1) ^ 52706)).is_none());
|
assert!(!distances.contains_key(&((41864 >> 1) ^ 52706)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ include!(concat!(env!("OUT_DIR"), "/clang_constants.rs"));
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum LLVMPasses {
|
pub enum LLVMPasses {
|
||||||
//CmpLogIns,
|
//CmpLogIns,
|
||||||
/// The CmpLog pass
|
/// The `CmpLog` pass
|
||||||
CmpLogRtn,
|
CmpLogRtn,
|
||||||
/// The AFL coverage pass
|
/// The AFL coverage pass
|
||||||
AFLCoverage,
|
AFLCoverage,
|
||||||
@ -39,7 +39,7 @@ pub enum LLVMPasses {
|
|||||||
/// The dump cfg pass
|
/// The dump cfg pass
|
||||||
DumpCfg,
|
DumpCfg,
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
/// The CmpLog Instruction pass
|
/// The `CmpLog` Instruction pass
|
||||||
CmpLogInstructions,
|
CmpLogInstructions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,14 +97,14 @@ extern "C" {
|
|||||||
target_family = "unix",
|
target_family = "unix",
|
||||||
// Disable when building with clippy, as it will complain about the missing environment
|
// Disable when building with clippy, as it will complain about the missing environment
|
||||||
// variable which is set by the build script, which is not run under clippy.
|
// variable which is set by the build script, which is not run under clippy.
|
||||||
not(feature = "cargo-clippy")
|
not(clippy)
|
||||||
))]
|
))]
|
||||||
pub const LIBAFL_LIBFUZZER_RUNTIME_LIBRARY: &'static [u8] =
|
pub const LIBAFL_LIBFUZZER_RUNTIME_LIBRARY: &'static [u8] =
|
||||||
include_bytes!(env!("LIBAFL_LIBFUZZER_RUNTIME_PATH"));
|
include_bytes!(env!("LIBAFL_LIBFUZZER_RUNTIME_PATH"));
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[cfg(all(feature = "embed-runtime", not(feature = "cargo-clippy")))]
|
#[cfg(all(feature = "embed-runtime", not(clippy)))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_embed_runtime_sized() {
|
fn test_embed_runtime_sized() {
|
||||||
use crate::LIBAFL_LIBFUZZER_RUNTIME_LIBRARY;
|
use crate::LIBAFL_LIBFUZZER_RUNTIME_LIBRARY;
|
||||||
|
@ -11,7 +11,7 @@ use libnyx::{NyxProcess, NyxReturnValue};
|
|||||||
const INIT_TIMEOUT: Duration = Duration::new(2, 0);
|
const INIT_TIMEOUT: Duration = Duration::new(2, 0);
|
||||||
pub struct NyxHelper {
|
pub struct NyxHelper {
|
||||||
pub nyx_process: NyxProcess,
|
pub nyx_process: NyxProcess,
|
||||||
/// real size of trace_bits
|
/// real size of `trace_bits`
|
||||||
pub real_map_size: usize,
|
pub real_map_size: usize,
|
||||||
// real size of the trace_bits
|
// real size of the trace_bits
|
||||||
pub map_size: usize,
|
pub map_size: usize,
|
||||||
|
@ -57,7 +57,7 @@ where
|
|||||||
/// Dictionary
|
/// Dictionary
|
||||||
#[builder(default = None)]
|
#[builder(default = None)]
|
||||||
tokens_file: Option<PathBuf>,
|
tokens_file: Option<PathBuf>,
|
||||||
/// Flag if use CmpLog
|
/// Flag if use `CmpLog`
|
||||||
#[builder(default = None)]
|
#[builder(default = None)]
|
||||||
use_cmplog: Option<bool>,
|
use_cmplog: Option<bool>,
|
||||||
/// The port used for communication between this fuzzer node and other fuzzer nodes
|
/// The port used for communication between this fuzzer node and other fuzzer nodes
|
||||||
|
@ -63,7 +63,7 @@ where
|
|||||||
/// Dictionary
|
/// Dictionary
|
||||||
#[builder(default = None)]
|
#[builder(default = None)]
|
||||||
tokens_file: Option<PathBuf>,
|
tokens_file: Option<PathBuf>,
|
||||||
/// Flag if use CmpLog
|
/// Flag if use `CmpLog`
|
||||||
#[builder(default = None)]
|
#[builder(default = None)]
|
||||||
use_cmplog: Option<bool>,
|
use_cmplog: Option<bool>,
|
||||||
/// The port the fuzzing nodes communicate over
|
/// The port the fuzzing nodes communicate over
|
||||||
|
Loading…
x
Reference in New Issue
Block a user