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>,
|
||||
/// Number of executions done at discovery time
|
||||
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,
|
||||
/// Parent [`CorpusId`], if known
|
||||
parent_id: Option<CorpusId>,
|
||||
@ -366,15 +366,15 @@ where
|
||||
allow(clippy::unsafe_derive_deserialize)
|
||||
)] // for SerdeAny
|
||||
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,
|
||||
/// Number of queue cycles behind
|
||||
handicap: u64,
|
||||
/// Path depth, initialized in on_add
|
||||
/// Path depth, initialized in `on_add`
|
||||
depth: u64,
|
||||
/// Offset in n_fuzz
|
||||
/// Offset in `n_fuzz`
|
||||
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),
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ where
|
||||
SP: ShMemProvider + 'static,
|
||||
S: State + 'a,
|
||||
{
|
||||
/// The ShmemProvider to use
|
||||
/// The `ShmemProvider` to use
|
||||
shmem_provider: SP,
|
||||
/// The monitor instance to use
|
||||
monitor: MT,
|
||||
@ -95,7 +95,7 @@ where
|
||||
/// A file name to write all client output to
|
||||
#[builder(default = None)]
|
||||
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"))]
|
||||
#[builder(setter(skip), default = None)]
|
||||
opened_stdout_file: Option<File>,
|
||||
@ -103,7 +103,7 @@ where
|
||||
/// `stdout_file`.
|
||||
#[builder(default = None)]
|
||||
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"))]
|
||||
#[builder(setter(skip), default = None)]
|
||||
opened_stderr_file: Option<File>,
|
||||
@ -409,7 +409,7 @@ where
|
||||
SP: ShMemProvider + 'static,
|
||||
S: State + 'a,
|
||||
{
|
||||
/// The ShmemProvider to use
|
||||
/// The `ShmemProvider` to use
|
||||
shmem_provider: SP,
|
||||
/// The monitor instance to use
|
||||
monitor: MT,
|
||||
@ -429,7 +429,7 @@ where
|
||||
/// A file name to write all client output to
|
||||
#[builder(default = None)]
|
||||
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"))]
|
||||
#[builder(setter(skip), default = None)]
|
||||
opened_stdout_file: Option<File>,
|
||||
@ -437,7 +437,7 @@ where
|
||||
/// `stdout_file`.
|
||||
#[builder(default = None)]
|
||||
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"))]
|
||||
#[builder(setter(skip), default = None)]
|
||||
opened_stderr_file: Option<File>,
|
||||
|
@ -54,7 +54,7 @@ where
|
||||
{
|
||||
/// The monitor
|
||||
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>>,
|
||||
/// The custom buf handler
|
||||
custom_buf_handlers: Vec<Box<CustomBufHandlerFn<S>>>,
|
||||
|
@ -42,7 +42,7 @@ pub struct InProcessHooks {
|
||||
/// On timeout C function pointer
|
||||
#[cfg(feature = "std")]
|
||||
pub timeout_handler: *const c_void,
|
||||
/// TImer struct
|
||||
/// `TImer` struct
|
||||
#[cfg(feature = "std")]
|
||||
pub timer: TimerStruct,
|
||||
}
|
||||
|
@ -101,9 +101,9 @@ pub(crate) struct InProcessForkExecutorGlobalData {
|
||||
pub state_ptr: *const c_void,
|
||||
/// Stores a pointer to the current input
|
||||
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,
|
||||
/// Stores a pointer to the timeout_handler function
|
||||
/// Stores a pointer to the `timeout_handler` function
|
||||
pub timeout_handler: *const c_void,
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,8 @@ Welcome to `LibAFL`
|
||||
clippy::missing_docs_in_private_items,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::ptr_cast_constness,
|
||||
clippy::unsafe_derive_deserialize
|
||||
clippy::unsafe_derive_deserialize,
|
||||
clippy::similar_names
|
||||
)]
|
||||
#![cfg_attr(not(test), warn(
|
||||
missing_debug_implementations,
|
||||
|
@ -53,9 +53,9 @@ pub struct MOpt {
|
||||
pub operator_num: usize,
|
||||
/// The number of swarms that we want to employ during the pilot fuzzing mode
|
||||
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,
|
||||
/// 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,
|
||||
/// The number of testcases generated during this pilot fuzzing mode
|
||||
pub pilot_time: usize,
|
||||
@ -77,23 +77,23 @@ pub struct MOpt {
|
||||
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
|
||||
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>>,
|
||||
/// (Pilot Mode) Finds by each operator till now.
|
||||
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>>,
|
||||
/// (Pilot Mode) The number of mutation operator used till now
|
||||
pub pilot_operator_cycles_v2: Vec<Vec<u64>>,
|
||||
/// (Pilot Mode) The number of mutation operator used till last execution
|
||||
pub pilot_operator_cycles_v3: Vec<Vec<u64>>,
|
||||
/// Vector used in pso_update
|
||||
/// Vector used in `pso_update`
|
||||
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>,
|
||||
/// (Core Mode) Finds by each operator till now.
|
||||
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>,
|
||||
/// (Core Mode) The number of mutation operator used till now
|
||||
pub core_operator_cycles_v2: Vec<u64>,
|
||||
|
@ -468,10 +468,10 @@ struct cmp_map {
|
||||
allow(clippy::unsafe_derive_deserialize)
|
||||
)] // for SerdeAny
|
||||
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)]
|
||||
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)]
|
||||
pub new_cmpvals: HashMap<usize, Vec<CmpValues>>,
|
||||
/// The list of logged idx and headers retrieved by runnning the mutated input
|
||||
|
@ -39,7 +39,7 @@ pub struct SchedulerMetadata {
|
||||
cycles: u64,
|
||||
/// Size of the observer map
|
||||
bitmap_size: u64,
|
||||
/// Sum of log(bitmap_size)
|
||||
/// Sum of `log(bitmap_size`)
|
||||
bitmap_size_log: f64,
|
||||
/// Number of filled map entries
|
||||
bitmap_entries: u64,
|
||||
|
@ -7,18 +7,29 @@ use alloc::string::String;
|
||||
use alloc::{borrow::ToOwned, string::ToString, vec::Vec};
|
||||
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"))]
|
||||
use crate::state::HasClientPerfMonitor;
|
||||
#[cfg(feature = "concolic_mutation")]
|
||||
use crate::state::State;
|
||||
use crate::{
|
||||
corpus::Corpus,
|
||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
||||
executors::{Executor, HasObservers},
|
||||
observers::concolic::ConcolicObserver,
|
||||
state::{HasCorpus, HasExecutions, HasMetadata},
|
||||
state::{HasCorpus, HasExecutions, HasMetadata, HasNamedMetadata, UsesState},
|
||||
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.
|
||||
#[derive(Clone, Debug)]
|
||||
@ -39,16 +50,16 @@ where
|
||||
E: UsesState<State = TE::State>,
|
||||
EM: UsesState<State = TE::State>,
|
||||
TE: Executor<EM, Z> + HasObservers,
|
||||
TE::State: HasExecutions + HasCorpus,
|
||||
TE::State: HasExecutions + HasCorpus + HasNamedMetadata,
|
||||
Z: UsesState<State = TE::State>,
|
||||
{
|
||||
type Progress = (); // stage cannot be resumed
|
||||
type Progress = RetryProgress;
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
executor: &mut E,
|
||||
_executor: &mut E,
|
||||
state: &mut TE::State,
|
||||
manager: &mut EM,
|
||||
) -> Result<(), Error> {
|
||||
@ -57,8 +68,11 @@ where
|
||||
"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
|
||||
.inner
|
||||
.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> {
|
||||
/// 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 {
|
||||
Self {
|
||||
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")]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn generate_mutations(iter: impl Iterator<Item = (SymExprRef, SymExpr)>) -> Vec<Vec<(usize, u8)>> {
|
||||
|
@ -12,25 +12,25 @@ use crate::{
|
||||
#[derive(Debug)]
|
||||
pub struct NestedStageProgress;
|
||||
|
||||
impl<S> StageProgress<S> for NestedStageProgress
|
||||
impl<S, ST> StageProgress<S, ST> for NestedStageProgress
|
||||
where
|
||||
S: HasNestedStageStatus,
|
||||
{
|
||||
fn initialize_progress(state: &mut S) -> Result<(), Error> {
|
||||
fn initialize_progress(state: &mut S, _stage: &ST) -> Result<(), Error> {
|
||||
state.enter_inner_stage()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_progress(state: &mut S) -> Result<(), Error> {
|
||||
fn clear_progress(state: &mut S, _stage: &ST) -> Result<(), Error> {
|
||||
state.exit_inner_stage()?;
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,12 @@ pub use concolic::SimpleConcolicMutationalStage;
|
||||
#[cfg(feature = "std")]
|
||||
pub use dump::*;
|
||||
pub use generalization::GeneralizationStage;
|
||||
use libafl_bolts::tuples::HasConstLen;
|
||||
use hashbrown::HashSet;
|
||||
use libafl_bolts::{impl_serdeany, tuples::HasConstLen};
|
||||
pub use logics::*;
|
||||
pub use mutational::{MutationalStage, StdMutationalStage};
|
||||
pub use power::{PowerMutationalStage, StdPowerMutationalStage};
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use stats::AflStatsStage;
|
||||
#[cfg(feature = "unicode")]
|
||||
pub use string::*;
|
||||
@ -32,13 +34,16 @@ pub use tuneable::*;
|
||||
|
||||
use self::push::PushStage;
|
||||
use crate::{
|
||||
corpus::HasCurrentCorpusIdx,
|
||||
corpus::{CorpusId, HasCurrentCorpusIdx},
|
||||
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
|
||||
executors::{Executor, HasObservers},
|
||||
inputs::UsesInput,
|
||||
observers::ObserversTuple,
|
||||
schedulers::Scheduler,
|
||||
state::{HasCorpus, HasExecutions, HasLastReportTime, HasMetadata, HasRand, UsesState},
|
||||
state::{
|
||||
HasCorpus, HasExecutions, HasLastReportTime, HasMetadata, HasNamedMetadata, HasRand,
|
||||
UsesState,
|
||||
},
|
||||
Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler,
|
||||
};
|
||||
|
||||
@ -76,7 +81,7 @@ where
|
||||
// TODO: see RFC 2532: https://github.com/rust-lang/rust/issues/29661
|
||||
// type Status: ResumableStageStatus = ();
|
||||
/// The resumption data for this stage. Set to () if resuming is not necessary/possible.
|
||||
type Progress: StageProgress<Self::State>;
|
||||
type Progress: StageProgress<Self::State, Self>;
|
||||
|
||||
/// Run the stage
|
||||
fn perform(
|
||||
@ -152,9 +157,10 @@ where
|
||||
}
|
||||
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)?;
|
||||
let stage = &mut self.0;
|
||||
Head::Progress::initialize_progress(state, stage)?;
|
||||
stage.perform(fuzzer, executor, state, manager)?;
|
||||
Head::Progress::clear_progress(state, stage)?;
|
||||
state.clear_stage()?;
|
||||
}
|
||||
Some(idx) if idx > Self::LEN => {
|
||||
@ -163,9 +169,10 @@ where
|
||||
// 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)?;
|
||||
let stage = &mut self.0;
|
||||
Head::Progress::initialize_progress(state, stage)?;
|
||||
stage.perform(fuzzer, executor, state, manager)?;
|
||||
Head::Progress::clear_progress(state, stage)?;
|
||||
state.clear_stage()?;
|
||||
}
|
||||
}
|
||||
@ -541,38 +548,128 @@ pub mod pybind {
|
||||
}
|
||||
|
||||
/// 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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 () {
|
||||
fn initialize_progress(_state: &mut S) -> Result<(), Error> {
|
||||
impl<S, ST> StageProgress<S, ST> for () {
|
||||
fn initialize_progress(_state: &mut S, _stage: &ST) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_progress(_state: &mut S) -> Result<(), Error> {
|
||||
fn clear_progress(_state: &mut S, _stage: &ST) -> Result<(), Error> {
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
pub trait HasCurrentStage {
|
||||
/// 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 crate::{
|
||||
corpus::{Corpus, Testcase},
|
||||
events::NopEventManager,
|
||||
executors::test::NopExecutor,
|
||||
fuzzer::test::NopFuzzer,
|
||||
stages::{Stage, StageProgress, StagesTuple},
|
||||
state::{HasMetadata, State, UsesState},
|
||||
inputs::NopInput,
|
||||
stages::{RetryProgress, RetryingStage, Stage, StageProgress, StagesTuple},
|
||||
state::{test::test_std_state, HasCorpus, HasMetadata, State, UsesState},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -636,11 +735,11 @@ pub mod test {
|
||||
|
||||
impl_serdeany!(TestProgress);
|
||||
|
||||
impl<S> StageProgress<S> for TestProgress
|
||||
impl<S, ST> StageProgress<S, ST> for TestProgress
|
||||
where
|
||||
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
|
||||
if !state.has_metadata::<Self>() {
|
||||
state.add_metadata(Self { count: 0 });
|
||||
@ -648,7 +747,7 @@ pub mod test {
|
||||
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() {
|
||||
return Err(Error::illegal_state(
|
||||
"attempted to clear status metadata when none was present",
|
||||
@ -657,11 +756,11 @@ pub mod test {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn progress(state: &S) -> Result<&Self, Error> {
|
||||
fn progress<'a>(state: &'a S, _stage: &ST) -> Result<&'a Self, Error> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -690,7 +789,7 @@ pub mod test {
|
||||
_manager: &mut EM,
|
||||
) -> Result<(), Error> {
|
||||
// metadata is attached by the status
|
||||
let meta = Self::Progress::progress_mut(state)?;
|
||||
let meta = Self::Progress::progress_mut(state, self)?;
|
||||
meta.count += 1;
|
||||
assert!(
|
||||
meta.count == 1,
|
||||
@ -725,7 +824,7 @@ pub mod test {
|
||||
_manager: &mut EM,
|
||||
) -> Result<(), Error> {
|
||||
// metadata is attached by the status
|
||||
let meta = Self::Progress::progress_mut(state)?;
|
||||
let meta = Self::Progress::progress_mut(state, self)?;
|
||||
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 crate::{
|
||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
||||
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx},
|
||||
executors::{Executor, HasObservers, ShadowExecutor},
|
||||
mark_feature_time,
|
||||
observers::ObserversTuple,
|
||||
stages::Stage,
|
||||
stages::{RetryProgress, RetryingStage, Stage},
|
||||
start_timer,
|
||||
state::{HasCorpus, HasExecutions, State, UsesState},
|
||||
state::{HasCorpus, HasExecutions, HasNamedMetadata, State, UsesState},
|
||||
Error,
|
||||
};
|
||||
#[cfg(feature = "introspection")]
|
||||
@ -19,6 +19,7 @@ use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TracingStage<EM, TE, Z> {
|
||||
tracer_executor: TE,
|
||||
max_retries: usize,
|
||||
#[allow(clippy::type_complexity)]
|
||||
phantom: PhantomData<(EM, TE, Z)>,
|
||||
}
|
||||
@ -30,30 +31,23 @@ where
|
||||
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
|
||||
E: UsesState<State = TE::State>,
|
||||
TE: Executor<EM, Z> + HasObservers,
|
||||
TE::State: HasExecutions + HasCorpus,
|
||||
TE::State: HasExecutions + HasCorpus + HasNamedMetadata,
|
||||
EM: UsesState<State = TE::State>,
|
||||
Z: UsesState<State = TE::State>,
|
||||
{
|
||||
type Progress = (); // this stage cannot be resumed
|
||||
|
||||
#[inline]
|
||||
fn perform(
|
||||
/// Perform tracing on the given [`CorpusId`]. Useful for if wrapping [`TracingStage`] with your
|
||||
/// own stage and you need to manage [`super::StageProgress`] differently; see
|
||||
/// [`super::ConcolicTracingStage`]'s implementation as an example of usage.
|
||||
pub fn trace(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
_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)?;
|
||||
|
||||
@ -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> {
|
||||
/// Creates a new default stage
|
||||
pub fn new(tracer_executor: TE) -> Self {
|
||||
Self {
|
||||
tracer_executor,
|
||||
max_retries: 10,
|
||||
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
|
||||
pub fn executor(&self) -> &TE {
|
||||
&self.tracer_executor
|
||||
@ -102,9 +144,11 @@ impl<EM, TE, Z> TracingStage<EM, TE, Z> {
|
||||
&mut self.tracer_executor
|
||||
}
|
||||
}
|
||||
|
||||
/// A stage that runs the shadow executor using also the shadow observers
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ShadowTracingStage<E, EM, SOT, Z> {
|
||||
max_retries: usize,
|
||||
#[allow(clippy::type_complexity)]
|
||||
phantom: PhantomData<(E, EM, SOT, Z)>,
|
||||
}
|
||||
@ -122,9 +166,9 @@ where
|
||||
EM: UsesState<State = E::State>,
|
||||
SOT: ObserversTuple<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]
|
||||
fn perform(
|
||||
@ -139,6 +183,9 @@ where
|
||||
"state is not currently processing a corpus index",
|
||||
));
|
||||
};
|
||||
if Self::Progress::should_skip(state, self, corpus_idx)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
start_timer!(state);
|
||||
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>
|
||||
where
|
||||
E: Executor<EM, Z> + HasObservers,
|
||||
@ -182,7 +235,16 @@ where
|
||||
/// Creates a new default stage
|
||||
pub fn new(_executor: &mut ShadowExecutor<E, SOT>) -> Self {
|
||||
Self {
|
||||
max_retries: 10,
|
||||
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 stored with names
|
||||
named_metadata: NamedSerdeAnyMap,
|
||||
/// MaxSize testcase size for mutators that appreciate it
|
||||
/// `MaxSize` testcase size for mutators that appreciate it
|
||||
max_size: usize,
|
||||
/// Performance statistics for this fuzzer
|
||||
#[cfg(feature = "introspection")]
|
||||
|
@ -324,7 +324,7 @@ pub enum TcpResponse {
|
||||
},
|
||||
/// Notify the client on the other side that it has been accepted.
|
||||
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
|
||||
client_id: ClientId,
|
||||
},
|
||||
@ -792,7 +792,7 @@ pub struct LlmpPage {
|
||||
/// (The os may have tidied up the memory when the receiver starts to map)
|
||||
pub receivers_joined_count: AtomicU16,
|
||||
/// 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,
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
/// The current message ID
|
||||
@ -1796,7 +1796,7 @@ where
|
||||
SHM: ShMem,
|
||||
{
|
||||
/// Shmem containg the actual (unsafe) page,
|
||||
/// shared between one LlmpSender and one LlmpReceiver
|
||||
/// shared between one `LlmpSender` and one `LlmpReceiver`
|
||||
shmem: SHM,
|
||||
}
|
||||
|
||||
@ -1962,9 +1962,9 @@ where
|
||||
/// The amount of total clients that should have connected and (and disconnected)
|
||||
/// after which the broker loop should quit gracefully.
|
||||
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>,
|
||||
/// The ShMemProvider to use
|
||||
/// The `ShMemProvider` to use
|
||||
shmem_provider: SP,
|
||||
#[cfg(feature = "std")]
|
||||
/// The timeout after which a client will be considered stale, and removed.
|
||||
|
@ -282,7 +282,7 @@ pub enum ServedShMemRequest {
|
||||
PreFork(),
|
||||
/// The client's child re-registers with us after it forked.
|
||||
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,
|
||||
}
|
||||
|
||||
|
@ -396,6 +396,6 @@ mod tests {
|
||||
assert_eq!(*distances.get(&((41864 >> 1) ^ 26911)).unwrap(), 1);
|
||||
assert_eq!(*distances.get(&((26911 >> 1) ^ 52706)).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)]
|
||||
pub enum LLVMPasses {
|
||||
//CmpLogIns,
|
||||
/// The CmpLog pass
|
||||
/// The `CmpLog` pass
|
||||
CmpLogRtn,
|
||||
/// The AFL coverage pass
|
||||
AFLCoverage,
|
||||
@ -39,7 +39,7 @@ pub enum LLVMPasses {
|
||||
/// The dump cfg pass
|
||||
DumpCfg,
|
||||
#[cfg(unix)]
|
||||
/// The CmpLog Instruction pass
|
||||
/// The `CmpLog` Instruction pass
|
||||
CmpLogInstructions,
|
||||
}
|
||||
|
||||
|
@ -97,14 +97,14 @@ extern "C" {
|
||||
target_family = "unix",
|
||||
// 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.
|
||||
not(feature = "cargo-clippy")
|
||||
not(clippy)
|
||||
))]
|
||||
pub const LIBAFL_LIBFUZZER_RUNTIME_LIBRARY: &'static [u8] =
|
||||
include_bytes!(env!("LIBAFL_LIBFUZZER_RUNTIME_PATH"));
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(all(feature = "embed-runtime", not(feature = "cargo-clippy")))]
|
||||
#[cfg(all(feature = "embed-runtime", not(clippy)))]
|
||||
#[test]
|
||||
fn test_embed_runtime_sized() {
|
||||
use crate::LIBAFL_LIBFUZZER_RUNTIME_LIBRARY;
|
||||
|
@ -11,7 +11,7 @@ use libnyx::{NyxProcess, NyxReturnValue};
|
||||
const INIT_TIMEOUT: Duration = Duration::new(2, 0);
|
||||
pub struct NyxHelper {
|
||||
pub nyx_process: NyxProcess,
|
||||
/// real size of trace_bits
|
||||
/// real size of `trace_bits`
|
||||
pub real_map_size: usize,
|
||||
// real size of the trace_bits
|
||||
pub map_size: usize,
|
||||
|
@ -57,7 +57,7 @@ where
|
||||
/// Dictionary
|
||||
#[builder(default = None)]
|
||||
tokens_file: Option<PathBuf>,
|
||||
/// Flag if use CmpLog
|
||||
/// Flag if use `CmpLog`
|
||||
#[builder(default = None)]
|
||||
use_cmplog: Option<bool>,
|
||||
/// The port used for communication between this fuzzer node and other fuzzer nodes
|
||||
|
@ -63,7 +63,7 @@ where
|
||||
/// Dictionary
|
||||
#[builder(default = None)]
|
||||
tokens_file: Option<PathBuf>,
|
||||
/// Flag if use CmpLog
|
||||
/// Flag if use `CmpLog`
|
||||
#[builder(default = None)]
|
||||
use_cmplog: Option<bool>,
|
||||
/// The port the fuzzing nodes communicate over
|
||||
|
Loading…
x
Reference in New Issue
Block a user