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:
Addison Crump 2024-02-28 14:12:28 +01:00 committed by GitHub
parent 7a4fb06d02
commit 8c773a6b85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 342 additions and 115 deletions

View File

@ -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),
} }

View File

@ -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>,

View 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>>>,

View File

@ -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,
} }

View File

@ -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,
} }

View File

@ -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,

View File

@ -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>,

View File

@ -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

View File

@ -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,

View File

@ -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)>> {

View File

@ -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")
} }
} }

View File

@ -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(())
}
} }

View File

@ -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
}
} }

View File

@ -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")]

View File

@ -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.

View File

@ -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,
} }

View File

@ -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)));
} }
} }

View File

@ -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,
} }

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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