Remove Input associated type (#2854)

* Completely remove Input as an associated type in multiple traits

* Unify usage of Input as generic instead 

* Remove many unused bounds, in particular HasCorpus

* fix multiple generic ordering

* update and fix CONTRIBUTING.md

* update MIGRATION

* use the same generic input type for new / with_max_iterations to make typing easier in most cases.

* Restore libafl_libfuzzer test in CI
This commit is contained in:
Romain Malmain 2025-01-17 14:53:51 +01:00 committed by GitHub
parent d4add04f87
commit f8ad61e14a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
126 changed files with 1614 additions and 1994 deletions

View File

@ -290,7 +290,7 @@ jobs:
- ./fuzzers/structure_aware/forkserver_simple_nautilus
# In-process
# - ./fuzzers/fuzz_anything/cargo_fuzz # Revive after they fix rustc
- ./fuzzers/fuzz_anything/cargo_fuzz
# - ./fuzzers/inprocess/dynamic_analysis
- ./fuzzers/inprocess/fuzzbench
- ./fuzzers/inprocess/fuzzbench_text

View File

@ -27,7 +27,7 @@ Before making your pull requests, try to see if your code follows these rules.
- `PhantomData` should have the smallest set of types needed. Try not adding `PhantomData` to your struct unless it is really necessary. Also even when you really need `PhantomData`, try to keep the types `T` used in `PhantomData` as smallest as possible
- Wherever possible, trait implementations with lifetime specifiers should use '_ lifetime elision.
- Complex constructors should be replaced with `typed_builder`, or write code in the builder pattern for yourself.
- Remove generic restrictions at the definitions (e.g., we do not need to specify that types impl `Serialize`, `Deserialize`, or `Debug` anymore at the struct definitions). Therefore, try avoiding code like this unless the contraint is really necessary.
- Remove generic restrictions at the definitions (e.g., we do not need to specify that types impl `Serialize`, `Deserialize`, or `Debug` anymore at the struct definitions). Therefore, try avoiding code like this unless the constraint is really necessary.
```rust
pub struct X<A>
where
@ -35,9 +35,8 @@ pub struct X<A>
{
fn ...
}
```
- Reduce generics to the least restrictive necessary. __Never overspecify the contraints__. There's no automated tool to check the useless constraints, so you have to verify this manually.
- Reduce generics to the least restrictive necessary. __Never overspecify the constraints__. There's no automated tool to check the useless constraints, so you have to verify this manually.
```rust
pub struct X<A>
where
@ -45,17 +44,37 @@ pub struct X<A>
{
fn ...
}
```
- Traits which have an associated type should refer to the associated type, not the concrete/generic. In other words, you should only have the associated type when you can define a getter to it. For example, in the following code, you can define a associate type.
- Prefer generic to associated types in traits definition as much as possible. They are much easier to use around, and avoid tricky caveats / type repetition in the code. It is also much easier to have unconstrained struct definitions.
Try not to write this:
```rust
pub trait X
{
type A;
fn a(&self) -> Self::A;
}
```
Try to write this instead:
```rust
pub trait X<A>
{
fn a(&self) -> A;
}
```
- Traits which have an associated type (if you have made sure you cannot use a generic instead) should refer to the associated type, not the concrete/generic. In other words, you should only have the associated type when you can define a getter to it. For example, in the following code, you can define a associate type.
```rust
pub trait X
{
type A; // <- You should(can) define it as long as you have a getter to it.
fn a(&self) -> A;
}
fn a(&self) -> Self::A;
}
```
- __Ideally__ the types used in the the arguments of methods in traits should have the same as the types defined on the traits.
```rust
pub trait X<A, B, C> // <- this trait have 3 generics, A, B, and C

View File

@ -14,7 +14,9 @@
- Trait restrictions have been simplified
- The `UsesState` and `UsesInput` traits have been removed in favor of regular Generics.
- For the structs/traits that used to use `UsesState`, we bring back the generic for the state.
- For `UsesState`, you can access to the input type through `HasCorpus` and `Corpus` traits
- `Input` is now only accessible through generic. `Input` associated types have been definitely removed.
- `HasCorpus` bound has been removed in many places it was unused before.
- `StdMutationalStage::transforming` must now explicitly state the Inputs types. As a result, `StdMutationalStage::transforming` must be written `StdMutationalStage::<_, _, FirstInputType, SecondInputType, _, _, _>::transforming`.
- The `State` trait is now private in favour of individual and more specific traits
- Restrictions from certain schedulers and stages that required their inner observer to implement `MapObserver` have been lifted in favor of requiring `Hash`
- Related: removed `hash_simple` from `MapObserver`

View File

@ -47,17 +47,17 @@ impl<S> CustomExecutor<S> {
}
}
impl<EM, S, Z> Executor<EM, <S::Corpus as Corpus>::Input, S, Z> for CustomExecutor<S>
impl<EM, I, S, Z> Executor<EM, I, S, Z> for CustomExecutor<S>
where
S: HasCorpus + HasExecutions,
<S::Corpus as Corpus>::Input: HasTargetBytes,
S: HasCorpus<I> + HasExecutions,
I: HasTargetBytes,
{
fn run_target(
&mut self,
_fuzzer: &mut Z,
state: &mut S,
_mgr: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
) -> Result<ExitKind, libafl::Error> {
// We need to keep track of the exec count.
*state.executions_mut() += 1;

View File

@ -13,7 +13,10 @@ use libafl::{
feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
mutators::{StdScheduledMutator, UnicodeCategoryRandMutator, UnicodeSubcategoryRandMutator},
mutators::{
StdScheduledMutator, UnicodeCategoryRandMutator, UnicodeInput,
UnicodeSubcategoryRandMutator,
},
observers::StdMapObserver,
schedulers::QueueScheduler,
stages::{mutational::StdMutationalStage, UnicodeIdentificationStage},
@ -134,7 +137,7 @@ pub fn main() {
));
let mut stages = tuple_list!(
UnicodeIdentificationStage::new(),
StdMutationalStage::transforming(mutator)
StdMutationalStage::<_, _, UnicodeInput, BytesInput, _, _, _>::transforming(mutator)
);
fuzzer

View File

@ -1,11 +1,10 @@
use std::borrow::Cow;
use libafl::{
corpus::{Corpus, Testcase},
corpus::Testcase,
executors::ExitKind,
feedbacks::{Feedback, MapIndexesMetadata, StateInitializer},
schedulers::{MinimizerScheduler, TestcaseScore},
state::HasCorpus,
Error, HasMetadata,
};
use libafl_bolts::{Named, SerdeAny};
@ -20,14 +19,11 @@ pub struct PacketLenMetadata {
pub struct PacketLenTestcaseScore {}
impl<S> TestcaseScore<S> for PacketLenTestcaseScore
impl<I, S> TestcaseScore<I, S> for PacketLenTestcaseScore
where
S: HasMetadata + HasCorpus,
S: HasMetadata,
{
fn compute(
_state: &S,
entry: &mut Testcase<<S::Corpus as Corpus>::Input>,
) -> Result<f64, Error> {
fn compute(_state: &S, entry: &mut Testcase<I>) -> Result<f64, Error> {
Ok(entry
.metadata_map()
.get::<PacketLenMetadata>()
@ -35,8 +31,8 @@ where
}
}
pub type PacketLenMinimizerScheduler<CS, S> =
MinimizerScheduler<CS, PacketLenTestcaseScore, MapIndexesMetadata, S>;
pub type PacketLenMinimizerScheduler<CS, I, S> =
MinimizerScheduler<CS, PacketLenTestcaseScore, I, MapIndexesMetadata, S>;
#[derive(Serialize, Deserialize, Default, Clone, Debug)]
pub struct PacketLenFeedback {

View File

@ -96,7 +96,7 @@ unsafe fn fuzz(
let shmem_provider = StdShMemProvider::new()?;
let mut run_client = |state: Option<_>,
mgr: LlmpRestartingEventManager<_, _, _>,
mgr: LlmpRestartingEventManager<_, _, _, _>,
client_description: ClientDescription| {
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
@ -104,7 +104,7 @@ unsafe fn fuzz(
if options.asan && options.asan_cores.contains(client_description.core_id()) {
(|state: Option<_>,
mut mgr: LlmpRestartingEventManager<_, _, _>,
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
_client_description| {
let gum = Gum::obtain();
@ -231,7 +231,7 @@ unsafe fn fuzz(
})(state, mgr, client_description)
} else if options.cmplog && options.cmplog_cores.contains(client_description.core_id()) {
(|state: Option<_>,
mut mgr: LlmpRestartingEventManager<_, _, _>,
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
_client_description| {
let gum = Gum::obtain();
@ -367,7 +367,7 @@ unsafe fn fuzz(
})(state, mgr, client_description)
} else {
(|state: Option<_>,
mut mgr: LlmpRestartingEventManager<_, _, _>,
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
_client_description| {
let gum = Gum::obtain();

View File

@ -81,7 +81,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
};
let mut run_client = |state: Option<_>,
mgr: LlmpRestartingEventManager<_, _, _>,
mgr: LlmpRestartingEventManager<_, _, _, _>,
client_description: ClientDescription| {
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
@ -100,7 +100,9 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
};
// if options.asan && options.asan_cores.contains(client_description.core_id()) {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _>, _client_description| {
(|state: Option<_>,
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
_client_description| {
let gum = Gum::obtain();
let coverage = CoverageRuntime::new();

View File

@ -78,7 +78,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
let shmem_provider = StdShMemProvider::new()?;
let mut run_client = |state: Option<_>,
mgr: LlmpRestartingEventManager<_, _, _>,
mgr: LlmpRestartingEventManager<_, _, _, _>,
client_description: ClientDescription| {
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
@ -98,7 +98,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
if options.asan && options.asan_cores.contains(client_description.core_id()) {
(|state: Option<_>,
mut mgr: LlmpRestartingEventManager<_, _, _>,
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
_client_description| {
let gum = Gum::obtain();
@ -214,7 +214,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
})(state, mgr, client_description)
} else if options.cmplog && options.cmplog_cores.contains(client_description.core_id()) {
(|state: Option<_>,
mut mgr: LlmpRestartingEventManager<_, _, _>,
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
_client_description| {
let gum = Gum::obtain();
@ -344,7 +344,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
})(state, mgr, client_description)
} else {
(|state: Option<_>,
mut mgr: LlmpRestartingEventManager<_, _, _>,
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
_client_description| {
let gum = Gum::obtain();

View File

@ -9,8 +9,7 @@ use clap::{builder::Str, Parser};
use libafl::{
corpus::{Corpus, InMemoryCorpus},
events::{
launcher::Launcher, ClientDescription, EventConfig, EventRestarter,
LlmpRestartingEventManager, ManagerExit,
launcher::Launcher, ClientDescription, EventConfig, LlmpRestartingEventManager, ManagerExit,
},
executors::ExitKind,
fuzzer::StdFuzzer,
@ -31,7 +30,7 @@ use libafl_bolts::{
use libafl_qemu::{
elf::EasyElf,
modules::{drcov::DrCovModule, utils::filters::StdAddressFilter},
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor,
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, QemuExecutor,
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
};
@ -124,7 +123,7 @@ pub fn fuzz() {
env::remove_var("LD_LIBRARY_PATH");
let mut run_client = |state: Option<_>,
mut mgr: LlmpRestartingEventManager<_, _, _>,
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
client_description: ClientDescription| {
let mut cov_path = options.coverage_path.clone();

View File

@ -23,7 +23,7 @@ use crate::{
#[expect(clippy::module_name_repetitions)]
pub type ClientState =
StdState<BytesInput, InMemoryOnDiskCorpus<BytesInput>, StdRand, OnDiskCorpus<BytesInput>>;
StdState<InMemoryOnDiskCorpus<BytesInput>, BytesInput, StdRand, OnDiskCorpus<BytesInput>>;
pub struct Client<'a> {
options: &'a FuzzerOptions,

View File

@ -52,13 +52,15 @@ use typed_builder::TypedBuilder;
use crate::{harness::Harness, options::FuzzerOptions};
pub type ClientState =
StdState<BytesInput, InMemoryOnDiskCorpus<BytesInput>, StdRand, OnDiskCorpus<BytesInput>>;
StdState<InMemoryOnDiskCorpus<BytesInput>, BytesInput, StdRand, OnDiskCorpus<BytesInput>>;
#[cfg(feature = "simplemgr")]
pub type ClientMgr<M> = SimpleEventManager<M, ClientState>;
#[cfg(not(feature = "simplemgr"))]
pub type ClientMgr<M> =
MonitorTypedEventManager<LlmpRestartingEventManager<(), ClientState, StdShMemProvider>, M>;
pub type ClientMgr<M> = MonitorTypedEventManager<
LlmpRestartingEventManager<(), BytesInput, ClientState, StdShMemProvider>,
M,
>;
#[derive(TypedBuilder)]
pub struct Instance<'a, M: Monitor> {
@ -321,7 +323,7 @@ impl<M: Monitor> Instance<'_, M> {
stages: &mut ST,
) -> Result<(), Error>
where
Z: Fuzzer<E, ClientMgr<M>, ClientState, ST>
Z: Fuzzer<E, ClientMgr<M>, BytesInput, ClientState, ST>
+ Evaluator<E, ClientMgr<M>, BytesInput, ClientState>,
ST: StagesTuple<E, ClientMgr<M>, ClientState, Z>,
{

View File

@ -376,7 +376,7 @@ fn fuzz(
let cb = |_fuzzer: &mut _,
_executor: &mut _,
state: &mut StdState<_, InMemoryOnDiskCorpus<_>, _, _>,
state: &mut StdState<InMemoryOnDiskCorpus<_>, _, _, _>,
_event_manager: &mut _|
-> Result<bool, Error> {
let testcase = state.current_testcase()?;

View File

@ -6,7 +6,6 @@ use std::{
};
use libafl::{
corpus::Corpus,
executors::{Executor, ExitKind, HasObservers, HasTimeout},
state::HasCorpus,
Error,
@ -253,25 +252,23 @@ fn check_file_found(file: &Path, perm: u32) -> bool {
}
#[cfg(feature = "nyx")]
pub enum SupportedExecutors<S, OT, FSV, NYX> {
Forkserver(FSV, PhantomData<(S, OT, NYX)>),
pub enum SupportedExecutors<FSV, I, OT, NYX> {
Forkserver(FSV, PhantomData<(FSV, I, OT)>),
Nyx(NYX),
}
#[cfg(feature = "nyx")]
impl<S, OT, FSV, NYX, EM, Z> Executor<EM, <S::Corpus as Corpus>::Input, S, Z>
for SupportedExecutors<S, OT, FSV, NYX>
impl<S, I, OT, FSV, NYX, EM, Z> Executor<EM, I, S, Z> for SupportedExecutors<FSV, I, OT, NYX>
where
S: HasCorpus,
NYX: Executor<EM, <S::Corpus as Corpus>::Input, S, Z>,
FSV: Executor<EM, <S::Corpus as Corpus>::Input, S, Z>,
NYX: Executor<EM, I, S, Z>,
FSV: Executor<EM, I, S, Z>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
) -> Result<ExitKind, Error> {
match self {
Self::Forkserver(fsrv, _) => fsrv.run_target(fuzzer, state, mgr, input),
@ -282,7 +279,7 @@ where
}
#[cfg(feature = "nyx")]
impl<S, OT, FSV, NYX> HasObservers for SupportedExecutors<S, OT, FSV, NYX>
impl<FSV, I, OT, NYX> HasObservers for SupportedExecutors<FSV, I, OT, NYX>
where
NYX: HasObservers<Observers = OT>,
FSV: HasObservers<Observers = OT>,
@ -308,7 +305,7 @@ where
}
#[cfg(feature = "nyx")]
impl<S, OT, FSV, NYX> HasTimeout for SupportedExecutors<S, OT, FSV, NYX>
impl<FSV, I, OT, NYX> HasTimeout for SupportedExecutors<FSV, I, OT, NYX>
where
FSV: HasTimeout,
NYX: HasTimeout,
@ -330,23 +327,22 @@ where
}
#[cfg(not(feature = "nyx"))]
pub enum SupportedExecutors<S, OT, FSV> {
Forkserver(FSV, PhantomData<(S, OT)>),
pub enum SupportedExecutors<FSV, I, OT, S> {
Forkserver(FSV, PhantomData<(I, OT, S)>),
}
#[cfg(not(feature = "nyx"))]
impl<S, OT, FSV, EM, Z> Executor<EM, <S::Corpus as Corpus>::Input, S, Z>
for SupportedExecutors<S, OT, FSV>
impl<S, I, OT, FSV, EM, Z> Executor<EM, I, S, Z> for SupportedExecutors<FSV, I, OT, S>
where
S: HasCorpus,
FSV: Executor<EM, <S::Corpus as Corpus>::Input, S, Z>,
S: HasCorpus<I>,
FSV: Executor<EM, I, S, Z>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
) -> Result<ExitKind, Error> {
match self {
Self::Forkserver(fsrv, _) => fsrv.run_target(fuzzer, state, mgr, input),
@ -355,7 +351,7 @@ where
}
#[cfg(not(feature = "nyx"))]
impl<S, OT, FSV> HasObservers for SupportedExecutors<S, OT, FSV>
impl<FSV, I, OT, S> HasObservers for SupportedExecutors<FSV, I, OT, S>
where
FSV: HasObservers<Observers = OT>,
{
@ -376,7 +372,7 @@ where
}
#[cfg(not(feature = "nyx"))]
impl<S, OT, FSV> HasTimeout for SupportedExecutors<S, OT, FSV>
impl<FSV, I, OT, S> HasTimeout for SupportedExecutors<FSV, I, OT, S>
where
FSV: HasTimeout,
{

View File

@ -4,10 +4,9 @@ use std::{
};
use libafl::{
corpus::{Corpus, Testcase},
corpus::Testcase,
executors::ExitKind,
feedbacks::{Feedback, FeedbackFactory, StateInitializer},
state::HasCorpus,
};
use libafl_bolts::{Error, Named};
use serde::{Deserialize, Serialize};
@ -53,18 +52,16 @@ impl<F> Named for CustomFilepathToTestcaseFeedback<F> {
impl<F, S> StateInitializer<S> for CustomFilepathToTestcaseFeedback<F> {}
impl<F, EM, OT, S> Feedback<EM, <S::Corpus as Corpus>::Input, OT, S>
for CustomFilepathToTestcaseFeedback<F>
impl<F, EM, I, OT, S> Feedback<EM, I, OT, S> for CustomFilepathToTestcaseFeedback<F>
where
S: HasCorpus,
F: FnMut(&mut S, &mut Testcase<<S::Corpus as Corpus>::Input>, &Path) -> Result<(), Error>,
F: FnMut(&mut S, &mut Testcase<I>, &Path) -> Result<(), Error>,
{
#[inline]
fn is_interesting(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &<S::Corpus as Corpus>::Input,
_input: &I,
_observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error> {
@ -76,7 +73,7 @@ where
state: &mut S,
_manager: &mut EM,
_observers: &OT,
testcase: &mut Testcase<<S::Corpus as Corpus>::Input>,
testcase: &mut Testcase<I>,
) -> Result<(), Error> {
(self.func)(state, testcase, &self.out_dir)?;
Ok(())

View File

@ -52,8 +52,7 @@ impl<I, S> StateInitializer<S> for PersitentRecordFeedback<I> {}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for PersitentRecordFeedback<I>
where
S: HasCorpus,
S::Corpus: Corpus<Input = I>,
S: HasCorpus<I>,
I: Input,
{
#[inline]

View File

@ -73,12 +73,13 @@ use crate::{
};
pub type LibaflFuzzState =
StdState<BytesInput, CachedOnDiskCorpus<BytesInput>, StdRand, OnDiskCorpus<BytesInput>>;
StdState<CachedOnDiskCorpus<BytesInput>, BytesInput, StdRand, OnDiskCorpus<BytesInput>>;
#[cfg(not(feature = "fuzzbench"))]
type LibaflFuzzManager = CentralizedEventManager<
LlmpRestartingEventManager<(), LibaflFuzzState, StdShMemProvider>,
LlmpRestartingEventManager<(), BytesInput, LibaflFuzzState, StdShMemProvider>,
(),
BytesInput,
LibaflFuzzState,
StdShMemProvider,
>;
@ -521,7 +522,7 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, {
);
// Run our fuzzer; WITH CmpLog
run_fuzzer_with_stages(
run_fuzzer_with_stages::<_, _, BytesInput, _, _, _>(
opt,
&mut fuzzer,
&mut stages,
@ -540,7 +541,7 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, {
);
// Run our fuzzer; NO CmpLog
run_fuzzer_with_stages(
run_fuzzer_with_stages::<_, _, BytesInput, _, _, _>(
opt,
&mut fuzzer,
&mut stages,
@ -648,7 +649,7 @@ pub fn fuzzer_target_mode(opt: &Opt) -> Cow<'static, str> {
#[derive(Debug, Serialize, Deserialize, SerdeAny)]
pub struct IsInitialCorpusEntryMetadata {}
pub fn run_fuzzer_with_stages<E, EM, S, ST, Z>(
pub fn run_fuzzer_with_stages<E, EM, I, S, ST, Z>(
opt: &Opt,
fuzzer: &mut Z,
stages: &mut ST,
@ -657,7 +658,7 @@ pub fn run_fuzzer_with_stages<E, EM, S, ST, Z>(
mgr: &mut EM,
) -> Result<(), Error>
where
Z: Fuzzer<E, EM, S, ST>,
Z: Fuzzer<E, EM, I, S, ST>,
EM: ProgressReporter<S>,
ST: StagesTuple<E, EM, S, Z>,
S: HasLastReportTime + HasExecutions + HasMetadata,

View File

@ -1,7 +1,6 @@
use libafl::{
corpus::Corpus,
events::{Event, EventManagerHook},
state::{HasCorpus, Stoppable},
state::Stoppable,
Error,
};
use libafl_bolts::ClientId;
@ -11,15 +10,15 @@ pub struct LibAflFuzzEventHook {
exit_on_solution: bool,
}
impl<S> EventManagerHook<<S::Corpus as Corpus>::Input, S> for LibAflFuzzEventHook
impl<I, S> EventManagerHook<I, S> for LibAflFuzzEventHook
where
S: HasCorpus + Stoppable,
S: Stoppable,
{
fn pre_exec(
&mut self,
state: &mut S,
_client_id: ClientId,
event: &Event<<S::Corpus as Corpus>::Input>,
event: &Event<I>,
) -> Result<bool, Error> {
if self.exit_on_solution && matches!(event, Event::Objective { .. }) {
// TODO: dump state

View File

@ -13,19 +13,17 @@ pub enum SupportedSchedulers<Q, W> {
Weighted(W, PhantomData<Q>),
}
impl<Q, S, W> RemovableScheduler<<S::Corpus as Corpus>::Input, S> for SupportedSchedulers<Q, W>
impl<I, Q, S, W> RemovableScheduler<I, S> for SupportedSchedulers<Q, W>
where
Q: Scheduler<<S::Corpus as Corpus>::Input, S>
+ RemovableScheduler<<S::Corpus as Corpus>::Input, S>,
W: Scheduler<<S::Corpus as Corpus>::Input, S>
+ RemovableScheduler<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus + HasTestcase,
Q: Scheduler<I, S> + RemovableScheduler<I, S>,
W: Scheduler<I, S> + RemovableScheduler<I, S>,
S: HasTestcase<I>,
{
fn on_remove(
&mut self,
state: &mut S,
id: CorpusId,
testcase: &Option<Testcase<<S::Corpus as Corpus>::Input>>,
testcase: &Option<Testcase<I>>,
) -> Result<(), Error> {
match self {
Self::Queue(queue, _) => queue.on_remove(state, id, testcase),
@ -33,12 +31,7 @@ where
}
}
fn on_replace(
&mut self,
state: &mut S,
id: CorpusId,
prev: &Testcase<<S::Corpus as Corpus>::Input>,
) -> Result<(), Error> {
fn on_replace(&mut self, state: &mut S, id: CorpusId, prev: &Testcase<I>) -> Result<(), Error> {
match self {
Self::Queue(queue, _) => queue.on_replace(state, id, prev),
Self::Weighted(weighted, _) => weighted.on_replace(state, id, prev),
@ -46,11 +39,11 @@ where
}
}
impl<Q, S, W> Scheduler<<S::Corpus as Corpus>::Input, S> for SupportedSchedulers<Q, W>
impl<I, Q, S, W> Scheduler<I, S> for SupportedSchedulers<Q, W>
where
Q: Scheduler<<S::Corpus as Corpus>::Input, S>,
W: Scheduler<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus + HasTestcase,
Q: Scheduler<I, S>,
W: Scheduler<I, S>,
S: HasCorpus<I> + HasTestcase<I>,
{
fn on_add(&mut self, state: &mut S, id: CorpusId) -> Result<(), Error> {
match self {
@ -82,12 +75,7 @@ where
Self::Weighted(weighted, _) => weighted.next(state),
}
}
fn on_evaluation<OTB>(
&mut self,
state: &mut S,
input: &<S::Corpus as Corpus>::Input,
observers: &OTB,
) -> Result<(), Error>
fn on_evaluation<OTB>(&mut self, state: &mut S, input: &I, observers: &OTB) -> Result<(), Error>
where
OTB: MatchName,
{

View File

@ -23,7 +23,7 @@ use libafl::{
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
inputs::{BytesInput, GeneralizedInputMetadata, HasTargetBytes},
monitors::SimpleMonitor,
mutators::{
grimoire::{
@ -604,7 +604,10 @@ fn fuzz_text(
3,
);
let grimoire = StdMutationalStage::transforming(grimoire_mutator);
let grimoire =
StdMutationalStage::<_, _, GeneralizedInputMetadata, BytesInput, _, _, _>::transforming(
grimoire_mutator,
);
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerScheduler::new(

View File

@ -141,7 +141,7 @@ pub extern "C" fn libafl_main() {
let mut secondary_run_client =
|state: Option<_>,
mut mgr: CentralizedEventManager<_, _, _, _>,
mut mgr: CentralizedEventManager<_, _, _, _, _>,
_client_description: ClientDescription| {
// Create an observation channel using the coverage map
let edges_observer =

View File

@ -219,7 +219,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
let orig_size = state.corpus().count();
let msg = "Started distillation...".to_string();
<LlmpRestartingEventManager<_, _, _> as EventFirer<BytesInput, _>>::log(
<LlmpRestartingEventManager<_, _, _, _> as EventFirer<BytesInput, _>>::log(
&mut restarting_mgr,
&mut state,
LogSeverity::Info,
@ -227,7 +227,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
)?;
minimizer.minimize(&mut fuzzer, &mut executor, &mut restarting_mgr, &mut state)?;
let msg = format!("Distilled out {} cases", orig_size - state.corpus().count());
<LlmpRestartingEventManager<_, _, _> as EventFirer<BytesInput, _>>::log(
<LlmpRestartingEventManager<_, _, _, _> as EventFirer<BytesInput, _>>::log(
&mut restarting_mgr,
&mut state,
LogSeverity::Info,

View File

@ -162,7 +162,7 @@ pub extern "C" fn libafl_main() {
);
let mut run_client = |state: Option<_>,
mut restarting_mgr: LlmpRestartingEventManager<_, _, _>,
mut restarting_mgr: LlmpRestartingEventManager<_, _, _, _>,
client_description: ClientDescription| {
// Create an observation channel using the coverage map
let edges_observer =

View File

@ -8,7 +8,7 @@ use libafl::{
executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Evaluator, Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
inputs::{BytesInput, GeneralizedInputMetadata, HasTargetBytes},
monitors::SimpleMonitor,
mutators::{
havoc_mutations, scheduled::StdScheduledMutator, GrimoireExtensionMutator,
@ -157,7 +157,9 @@ pub fn main() {
let mut stages = tuple_list!(
generalization,
StdMutationalStage::new(mutator),
StdMutationalStage::transforming(grimoire_mutator)
StdMutationalStage::<_, _, GeneralizedInputMetadata, BytesInput, _, _, _>::transforming(
grimoire_mutator
)
);
for input in initial_inputs {

View File

@ -9,7 +9,7 @@ use libafl_bolts::ClientId;
pub fn main() {
let mut monitor = TuiMonitor::builder().build();
let client_stats = ClientStats {
let _client_stats = ClientStats {
corpus_size: 1024,
executions: 512,
..ClientStats::default()

View File

@ -1,7 +1,7 @@
//! The [`CachedOnDiskCorpus`] stores [`Testcase`]s to disk, keeping a subset of them in memory/cache, evicting in a FIFO manner.
use alloc::{collections::vec_deque::VecDeque, string::String};
use core::cell::RefCell;
use core::cell::{Ref, RefCell, RefMut};
use std::path::Path;
use serde::{Deserialize, Serialize};
@ -55,12 +55,11 @@ where
Ok(())
}
}
impl<I> Corpus for CachedOnDiskCorpus<I>
impl<I> Corpus<I> for CachedOnDiskCorpus<I>
where
I: Input,
{
type Input = I;
/// Returns the number of all enabled entries
#[inline]
fn count(&self) -> usize {
@ -98,7 +97,7 @@ where
}
/// Removes an entry from the corpus, returning it if it was present; considers both enabled and disabled testcases.
fn remove(&mut self, id: CorpusId) -> Result<Testcase<Self::Input>, Error> {
fn remove(&mut self, id: CorpusId) -> Result<Testcase<I>, Error> {
let testcase = self.inner.remove(id)?;
self.cached_indexes.borrow_mut().retain(|e| *e != id);
Ok(testcase)
@ -113,7 +112,7 @@ where
}
/// Get by id; considers both enabled and disabled testcases
#[inline]
fn get_from_all(&self, id: CorpusId) -> Result<&RefCell<Testcase<Self::Input>>, Error> {
fn get_from_all(&self, id: CorpusId) -> Result<&RefCell<Testcase<I>>, Error> {
let testcase = { self.inner.get_from_all(id)? };
self.cache_testcase(testcase, id)?;
Ok(testcase)
@ -169,25 +168,25 @@ where
}
#[inline]
fn load_input_into(&self, testcase: &mut Testcase<Self::Input>) -> Result<(), Error> {
fn load_input_into(&self, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.inner.load_input_into(testcase)
}
#[inline]
fn store_input_from(&self, testcase: &Testcase<Self::Input>) -> Result<(), Error> {
fn store_input_from(&self, testcase: &Testcase<I>) -> Result<(), Error> {
self.inner.store_input_from(testcase)
}
}
impl<I> HasTestcase for CachedOnDiskCorpus<I>
impl<I> HasTestcase<I> for CachedOnDiskCorpus<I>
where
I: Input,
{
fn testcase(&self, id: CorpusId) -> Result<core::cell::Ref<Testcase<I>>, Error> {
fn testcase(&self, id: CorpusId) -> Result<Ref<Testcase<I>>, Error> {
Ok(self.get(id)?.borrow())
}
fn testcase_mut(&self, id: CorpusId) -> Result<core::cell::RefMut<Testcase<I>>, Error> {
fn testcase_mut(&self, id: CorpusId) -> Result<RefMut<Testcase<I>>, Error> {
Ok(self.get(id)?.borrow_mut())
}
}

View File

@ -1,7 +1,7 @@
//! In-memory corpus, keeps all test cases in memory at all times
use alloc::vec::Vec;
use core::cell::RefCell;
use core::cell::{Ref, RefCell, RefMut};
use serde::{Deserialize, Serialize};
@ -314,9 +314,7 @@ pub struct InMemoryCorpus<I> {
current: Option<CorpusId>,
}
impl<I> Corpus for InMemoryCorpus<I> {
type Input = I;
impl<I> Corpus<I> for InMemoryCorpus<I> {
/// Returns the number of all enabled entries
#[inline]
fn count(&self) -> usize {
@ -360,7 +358,7 @@ impl<I> Corpus for InMemoryCorpus<I> {
/// Removes an entry from the corpus, returning it if it was present; considers both enabled and disabled testcases
#[inline]
fn remove(&mut self, id: CorpusId) -> Result<Testcase<Self::Input>, Error> {
fn remove(&mut self, id: CorpusId) -> Result<Testcase<I>, Error> {
let mut testcase = self.storage.enabled.remove(id);
if testcase.is_none() {
testcase = self.storage.disabled.remove(id);
@ -380,7 +378,7 @@ impl<I> Corpus for InMemoryCorpus<I> {
}
/// Get by id; considers both enabled and disabled testcases
#[inline]
fn get_from_all(&self, id: CorpusId) -> Result<&RefCell<Testcase<Self::Input>>, Error> {
fn get_from_all(&self, id: CorpusId) -> Result<&RefCell<Testcase<I>>, Error> {
let mut testcase = self.storage.enabled.get(id);
if testcase.is_none() {
testcase = self.storage.disabled.get(id);
@ -400,17 +398,17 @@ impl<I> Corpus for InMemoryCorpus<I> {
&mut self.current
}
#[inline]
fn next(&self, id: CorpusId) -> Option<CorpusId> {
self.storage.enabled.next(id)
}
/// Peek the next free corpus id
#[inline]
fn peek_free_id(&self) -> CorpusId {
self.storage.peek_free_id()
}
#[inline]
fn next(&self, id: CorpusId) -> Option<CorpusId> {
self.storage.enabled.next(id)
}
#[inline]
fn prev(&self, id: CorpusId) -> Option<CorpusId> {
self.storage.enabled.prev(id)
@ -443,29 +441,23 @@ impl<I> Corpus for InMemoryCorpus<I> {
}
#[inline]
fn load_input_into(&self, _: &mut Testcase<Self::Input>) -> Result<(), Error> {
fn load_input_into(&self, _: &mut Testcase<I>) -> Result<(), Error> {
// Inputs never get evicted, nothing to load here.
Ok(())
}
#[inline]
fn store_input_from(&self, _: &Testcase<Self::Input>) -> Result<(), Error> {
fn store_input_from(&self, _: &Testcase<I>) -> Result<(), Error> {
Ok(())
}
}
impl<I> HasTestcase for InMemoryCorpus<I> {
fn testcase(
&self,
id: CorpusId,
) -> Result<core::cell::Ref<Testcase<<Self::Corpus as Corpus>::Input>>, Error> {
impl<I> HasTestcase<I> for InMemoryCorpus<I> {
fn testcase(&self, id: CorpusId) -> Result<Ref<Testcase<I>>, Error> {
Ok(self.get(id)?.borrow())
}
fn testcase_mut(
&self,
id: CorpusId,
) -> Result<core::cell::RefMut<Testcase<<Self::Corpus as Corpus>::Input>>, Error> {
fn testcase_mut(&self, id: CorpusId) -> Result<RefMut<Testcase<I>>, Error> {
Ok(self.get(id)?.borrow_mut())
}
}

View File

@ -5,7 +5,7 @@
//! which only stores a certain number of [`Testcase`]s and removes additional ones in a FIFO manner.
use alloc::string::String;
use core::cell::RefCell;
use core::cell::{Ref, RefCell, RefMut};
use std::{
fs,
fs::{File, OpenOptions},
@ -59,12 +59,10 @@ pub struct InMemoryOnDiskCorpus<I> {
locking: bool,
}
impl<I> Corpus for InMemoryOnDiskCorpus<I>
impl<I> Corpus<I> for InMemoryOnDiskCorpus<I>
where
I: Input,
{
type Input = I;
/// Returns the number of all enabled entries
#[inline]
fn count(&self) -> usize {
@ -182,7 +180,7 @@ where
self.inner.nth_from_all(nth)
}
fn load_input_into(&self, testcase: &mut Testcase<Self::Input>) -> Result<(), Error> {
fn load_input_into(&self, testcase: &mut Testcase<I>) -> Result<(), Error> {
if testcase.input_mut().is_none() {
let Some(file_path) = testcase.file_path().as_ref() else {
return Err(Error::illegal_argument(
@ -195,7 +193,7 @@ where
Ok(())
}
fn store_input_from(&self, testcase: &Testcase<Self::Input>) -> Result<(), Error> {
fn store_input_from(&self, testcase: &Testcase<I>) -> Result<(), Error> {
// Store the input to disk
let Some(file_path) = testcase.file_path() else {
return Err(Error::illegal_argument(
@ -211,21 +209,15 @@ where
}
}
impl<I> HasTestcase for InMemoryOnDiskCorpus<I>
impl<I> HasTestcase<I> for InMemoryOnDiskCorpus<I>
where
I: Input,
{
fn testcase(
&self,
id: CorpusId,
) -> Result<core::cell::Ref<Testcase<<Self as Corpus>::Input>>, Error> {
fn testcase(&self, id: CorpusId) -> Result<Ref<Testcase<I>>, Error> {
Ok(self.get(id)?.borrow())
}
fn testcase_mut(
&self,
id: CorpusId,
) -> Result<core::cell::RefMut<Testcase<<Self as Corpus>::Input>>, Error> {
fn testcase_mut(&self, id: CorpusId) -> Result<RefMut<Testcase<I>>, Error> {
Ok(self.get(id)?.borrow_mut())
}
}
@ -242,7 +234,7 @@ impl<I> InMemoryOnDiskCorpus<I> {
/// If you don't want metadata, use [`InMemoryOnDiskCorpus::no_meta`].
/// To pick a different metadata format, use [`InMemoryOnDiskCorpus::with_meta_format`].
///
/// Will error, if [`std::fs::create_dir_all()`] failed for `dir_path`.
/// Will error, if [`fs::create_dir_all()`] failed for `dir_path`.
pub fn new<P>(dir_path: P) -> Result<Self, Error>
where
P: AsRef<Path>,
@ -257,7 +249,7 @@ impl<I> InMemoryOnDiskCorpus<I> {
/// Creates the [`InMemoryOnDiskCorpus`] specifying the format in which `Metadata` will be saved to disk.
///
/// Will error, if [`std::fs::create_dir_all()`] failed for `dir_path`.
/// Will error, if [`fs::create_dir_all()`] failed for `dir_path`.
pub fn with_meta_format<P>(
dir_path: P,
meta_format: Option<OnDiskMetadataFormat>,
@ -271,7 +263,7 @@ impl<I> InMemoryOnDiskCorpus<I> {
/// Creates the [`InMemoryOnDiskCorpus`] specifying the format in which `Metadata` will be saved to disk
/// and the prefix for the filenames.
///
/// Will error, if [`std::fs::create_dir_all()`] failed for `dir_path`.
/// Will error, if [`fs::create_dir_all()`] failed for `dir_path`.
pub fn with_meta_format_and_prefix<P>(
dir_path: P,
meta_format: Option<OnDiskMetadataFormat>,
@ -286,7 +278,7 @@ impl<I> InMemoryOnDiskCorpus<I> {
/// Creates an [`InMemoryOnDiskCorpus`] that will not store .metadata files
///
/// Will error, if [`std::fs::create_dir_all()`] failed for `dir_path`.
/// Will error, if [`fs::create_dir_all()`] failed for `dir_path`.
pub fn no_meta<P>(dir_path: P) -> Result<Self, Error>
where
P: AsRef<Path>,

View File

@ -29,16 +29,16 @@ use crate::{
///
/// Algorithm based on WMOPT: <https://hexhive.epfl.ch/publications/files/21ISSTA2.pdf>
#[derive(Debug)]
pub struct MapCorpusMinimizer<C, E, O, S, T, TS> {
pub struct MapCorpusMinimizer<C, E, I, O, S, T, TS> {
observer_handle: Handle<C>,
phantom: PhantomData<(E, O, S, T, TS)>,
phantom: PhantomData<(E, I, O, S, T, TS)>,
}
/// Standard corpus minimizer, which weights inputs by length and time.
pub type StdCorpusMinimizer<C, E, O, S, T> =
MapCorpusMinimizer<C, E, O, S, T, LenTimeMulTestcaseScore>;
pub type StdCorpusMinimizer<C, E, I, O, S, T> =
MapCorpusMinimizer<C, E, I, O, S, T, LenTimeMulTestcaseScore>;
impl<C, E, O, S, T, TS> MapCorpusMinimizer<C, E, O, S, T, TS>
impl<C, E, I, O, S, T, TS> MapCorpusMinimizer<C, E, I, O, S, T, TS>
where
C: Named,
{
@ -52,14 +52,14 @@ where
}
}
impl<C, E, O, S, T, TS> MapCorpusMinimizer<C, E, O, S, T, TS>
impl<C, E, I, O, S, T, TS> MapCorpusMinimizer<C, E, I, O, S, T, TS>
where
for<'a> O: MapObserver<Entry = T> + AsIter<'a, Item = T>,
C: AsRef<O>,
S: HasMetadata + HasCorpus + HasExecutions,
<S::Corpus as Corpus>::Input: Input,
I: Input,
S: HasMetadata + HasCorpus<I> + HasExecutions,
T: Copy + Hash + Eq,
TS: TestcaseScore<S>,
TS: TestcaseScore<I, S>,
{
/// Do the minimization
#[expect(clippy::too_many_lines)]
@ -71,12 +71,11 @@ where
state: &mut S,
) -> Result<(), Error>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
CS: Scheduler<<S::Corpus as Corpus>::Input, S>
+ RemovableScheduler<<S::Corpus as Corpus>::Input, S>,
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
Z: HasScheduler<<S::Corpus as Corpus>::Input, S, Scheduler = CS>,
E: Executor<EM, I, S, Z> + HasObservers,
E::Observers: ObserversTuple<I, S>,
CS: Scheduler<I, S> + RemovableScheduler<I, S>,
EM: EventFirer<I, S>,
Z: HasScheduler<I, S, Scheduler = CS>,
{
// don't delete this else it won't work after restart
let current = *state.corpus().current();

View File

@ -1,5 +1,11 @@
//! Corpuses contain the testcases, either in memory, on disk, or somewhere else.
use core::{cell::RefCell, fmt, marker::PhantomData};
use serde::{Deserialize, Serialize};
use crate::Error;
pub mod testcase;
pub use testcase::{HasTestcase, SchedulerTestcaseMetadata, Testcase};
@ -23,15 +29,11 @@ pub use cached::CachedOnDiskCorpus;
#[cfg(all(feature = "cmin", unix))]
pub mod minimizer;
use core::{cell::RefCell, fmt};
pub mod nop;
#[cfg(all(feature = "cmin", unix))]
pub use minimizer::*;
pub use nop::NopCorpus;
use serde::{Deserialize, Serialize};
use crate::Error;
/// An abstraction for the index that identify a testcase in the corpus
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
@ -101,10 +103,7 @@ macro_rules! random_corpus_id_with_disabled {
}
/// Corpus with all current [`Testcase`]s, or solutions
pub trait Corpus: Sized {
/// The type of input contained in this corpus
type Input;
pub trait Corpus<I>: Sized {
/// Returns the number of all enabled entries
fn count(&self) -> usize;
@ -120,26 +119,22 @@ pub trait Corpus: Sized {
}
/// Add an enabled testcase to the corpus and return its index
fn add(&mut self, testcase: Testcase<Self::Input>) -> Result<CorpusId, Error>;
fn add(&mut self, testcase: Testcase<I>) -> Result<CorpusId, Error>;
/// Add a disabled testcase to the corpus and return its index
fn add_disabled(&mut self, testcase: Testcase<Self::Input>) -> Result<CorpusId, Error>;
fn add_disabled(&mut self, testcase: Testcase<I>) -> Result<CorpusId, Error>;
/// Replaces the [`Testcase`] at the given idx, returning the existing.
fn replace(
&mut self,
id: CorpusId,
testcase: Testcase<Self::Input>,
) -> Result<Testcase<Self::Input>, Error>;
fn replace(&mut self, id: CorpusId, testcase: Testcase<I>) -> Result<Testcase<I>, Error>;
/// Removes an entry from the corpus, returning it if it was present; considers both enabled and disabled testcases
fn remove(&mut self, id: CorpusId) -> Result<Testcase<Self::Input>, Error>;
fn remove(&mut self, id: CorpusId) -> Result<Testcase<I>, Error>;
/// Get by id; considers only enabled testcases
fn get(&self, id: CorpusId) -> Result<&RefCell<Testcase<Self::Input>>, Error>;
fn get(&self, id: CorpusId) -> Result<&RefCell<Testcase<I>>, Error>;
/// Get by id; considers both enabled and disabled testcases
fn get_from_all(&self, id: CorpusId) -> Result<&RefCell<Testcase<Self::Input>>, Error>;
fn get_from_all(&self, id: CorpusId) -> Result<&RefCell<Testcase<I>>, Error>;
/// Current testcase scheduled
fn current(&self) -> &Option<CorpusId>;
@ -163,11 +158,12 @@ pub trait Corpus: Sized {
fn last(&self) -> Option<CorpusId>;
/// An iterator over very active corpus id
fn ids(&self) -> CorpusIdIterator<'_, Self> {
fn ids(&self) -> CorpusIdIterator<'_, Self, I> {
CorpusIdIterator {
corpus: self,
cur: self.first(),
cur_back: self.last(),
phantom: PhantomData,
}
}
@ -184,15 +180,15 @@ pub trait Corpus: Sized {
/// Method to load the input for this [`Testcase`] from persistent storage,
/// if necessary, and if was not already loaded (`== Some(input)`).
/// After this call, `testcase.input()` must always return `Some(input)`.
fn load_input_into(&self, testcase: &mut Testcase<Self::Input>) -> Result<(), Error>;
fn load_input_into(&self, testcase: &mut Testcase<I>) -> Result<(), Error>;
/// Method to store the input of this `Testcase` to persistent storage, if necessary.
fn store_input_from(&self, testcase: &Testcase<Self::Input>) -> Result<(), Error>;
fn store_input_from(&self, testcase: &Testcase<I>) -> Result<(), Error>;
/// Loads the `Input` for a given [`CorpusId`] from the [`Corpus`], and returns the clone.
fn cloned_input_for_id(&self, id: CorpusId) -> Result<Self::Input, Error>
fn cloned_input_for_id(&self, id: CorpusId) -> Result<I, Error>
where
Self::Input: Clone,
I: Clone,
{
let mut testcase = self.get(id)?.borrow_mut();
Ok(testcase.load_input(self)?.clone())
@ -213,18 +209,16 @@ pub trait HasCurrentCorpusId {
/// [`Iterator`] over the ids of a [`Corpus`]
#[derive(Debug)]
pub struct CorpusIdIterator<'a, C>
where
C: Corpus,
{
pub struct CorpusIdIterator<'a, C, I> {
corpus: &'a C,
cur: Option<CorpusId>,
cur_back: Option<CorpusId>,
phantom: PhantomData<I>,
}
impl<C> Iterator for CorpusIdIterator<'_, C>
impl<C, I> Iterator for CorpusIdIterator<'_, C, I>
where
C: Corpus,
C: Corpus<I>,
{
type Item = CorpusId;
@ -238,9 +232,9 @@ where
}
}
impl<C> DoubleEndedIterator for CorpusIdIterator<'_, C>
impl<C, I> DoubleEndedIterator for CorpusIdIterator<'_, C, I>
where
C: Corpus,
C: Corpus<I>,
{
fn next_back(&mut self) -> Option<Self::Item> {
if let Some(cur_back) = self.cur_back {

View File

@ -15,8 +15,7 @@ pub struct NopCorpus<I> {
phantom: PhantomData<I>,
}
impl<I> Corpus for NopCorpus<I> {
type Input = I;
impl<I> Corpus<I> for NopCorpus<I> {
/// Returns the number of all enabled entries
#[inline]
fn count(&self) -> usize {
@ -76,12 +75,6 @@ impl<I> Corpus for NopCorpus<I> {
&self.empty
}
/// Peek the next free corpus id
#[inline]
fn peek_free_id(&self) -> CorpusId {
CorpusId::from(0_usize)
}
/// Current testcase scheduled (mutable)
#[inline]
fn current_mut(&mut self) -> &mut Option<CorpusId> {
@ -93,6 +86,12 @@ impl<I> Corpus for NopCorpus<I> {
None
}
/// Peek the next free corpus id
#[inline]
fn peek_free_id(&self) -> CorpusId {
CorpusId::from(0_usize)
}
#[inline]
fn prev(&self, _id: CorpusId) -> Option<CorpusId> {
None
@ -121,12 +120,12 @@ impl<I> Corpus for NopCorpus<I> {
}
#[inline]
fn load_input_into(&self, _testcase: &mut Testcase<Self::Input>) -> Result<(), Error> {
fn load_input_into(&self, _testcase: &mut Testcase<I>) -> Result<(), Error> {
Err(Error::unsupported("Unsupported by NopCorpus"))
}
#[inline]
fn store_input_from(&self, _testcase: &Testcase<Self::Input>) -> Result<(), Error> {
fn store_input_from(&self, _testcase: &Testcase<I>) -> Result<(), Error> {
Err(Error::unsupported("Unsupported by NopCorpus"))
}
}

View File

@ -2,7 +2,7 @@
//!
//! It _never_ keeps any of them in memory.
//! This is a good solution for solutions that are never reused, or for *very* memory-constraint environments.
//! For any other occasions, consider using [`crate::corpus::CachedOnDiskCorpus`]
//! For any other occasions, consider using [`CachedOnDiskCorpus`]
//! which stores a certain number of [`Testcase`]s in memory and removes additional ones in a FIFO manner.
use alloc::string::String;
@ -56,11 +56,10 @@ pub struct OnDiskCorpus<I> {
inner: CachedOnDiskCorpus<I>,
}
impl<I> Corpus for OnDiskCorpus<I>
impl<I> Corpus<I> for OnDiskCorpus<I>
where
I: Input,
{
type Input = I;
/// Returns the number of all enabled entries
#[inline]
fn count(&self) -> usize {
@ -96,12 +95,6 @@ where
self.inner.replace(id, testcase)
}
/// Peek the next free corpus id
#[inline]
fn peek_free_id(&self) -> CorpusId {
self.inner.peek_free_id()
}
/// Removes an entry from the corpus, returning it if it was present; considers both enabled and disabled testcases
#[inline]
fn remove(&mut self, id: CorpusId) -> Result<Testcase<I>, Error> {
@ -137,6 +130,12 @@ where
self.inner.next(id)
}
/// Peek the next free corpus id
#[inline]
fn peek_free_id(&self) -> CorpusId {
self.inner.peek_free_id()
}
#[inline]
fn prev(&self, id: CorpusId) -> Option<CorpusId> {
self.inner.prev(id)
@ -164,17 +163,17 @@ where
}
#[inline]
fn load_input_into(&self, testcase: &mut Testcase<Self::Input>) -> Result<(), Error> {
fn load_input_into(&self, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.inner.load_input_into(testcase)
}
#[inline]
fn store_input_from(&self, testcase: &Testcase<Self::Input>) -> Result<(), Error> {
fn store_input_from(&self, testcase: &Testcase<I>) -> Result<(), Error> {
self.inner.store_input_from(testcase)
}
}
impl<I> HasTestcase for OnDiskCorpus<I>
impl<I> HasTestcase<I> for OnDiskCorpus<I>
where
I: Input,
{

View File

@ -15,24 +15,18 @@ use libafl_bolts::{serdeany::SerdeAnyMap, HasLen};
use serde::{Deserialize, Serialize};
use super::Corpus;
use crate::{corpus::CorpusId, state::HasCorpus, Error, HasMetadata};
use crate::{corpus::CorpusId, Error, HasMetadata};
/// Shorthand to receive a [`Ref`] or [`RefMut`] to a stored [`Testcase`], by [`CorpusId`].
/// For a normal state, this should return a [`Testcase`] in the corpus, not the objectives.
pub trait HasTestcase: HasCorpus {
pub trait HasTestcase<I> {
/// Shorthand to receive a [`Ref`] to a stored [`Testcase`], by [`CorpusId`].
/// For a normal state, this should return a [`Testcase`] in the corpus, not the objectives.
fn testcase(
&self,
id: CorpusId,
) -> Result<Ref<Testcase<<Self::Corpus as Corpus>::Input>>, Error>;
fn testcase(&self, id: CorpusId) -> Result<Ref<Testcase<I>>, Error>;
/// Shorthand to receive a [`RefMut`] to a stored [`Testcase`], by [`CorpusId`].
/// For a normal state, this should return a [`Testcase`] in the corpus, not the objectives.
fn testcase_mut(
&self,
id: CorpusId,
) -> Result<RefMut<Testcase<<Self::Corpus as Corpus>::Input>>, Error>;
fn testcase_mut(&self, id: CorpusId) -> Result<RefMut<Testcase<I>>, Error>;
}
/// An entry in the [`Testcase`] Corpus
@ -87,7 +81,7 @@ impl<I> HasMetadata for Testcase<I> {
/// Impl of a testcase
impl<I> Testcase<I> {
/// Returns this [`Testcase`] with a loaded `Input`]
pub fn load_input<C: Corpus<Input = I>>(&mut self, corpus: &C) -> Result<&I, Error> {
pub fn load_input<C: Corpus<I>>(&mut self, corpus: &C) -> Result<&I, Error> {
corpus.load_input_into(self)?;
Ok(self.input.as_ref().unwrap())
}
@ -358,7 +352,7 @@ where
}
/// Get the `len` or calculate it, if not yet calculated.
pub fn load_len<C: Corpus<Input = I>>(&mut self, corpus: &C) -> Result<usize, Error> {
pub fn load_len<C: Corpus<I>>(&mut self, corpus: &C) -> Result<usize, Error> {
match &self.input {
Some(i) => {
let l = i.len();

View File

@ -29,7 +29,6 @@ use super::{CanSerializeObserver, ManagerExit, NopEventManager};
use crate::events::llmp::COMPRESS_THRESHOLD;
use crate::{
common::HasMetadata,
corpus::Corpus,
events::{
serialize_observers_adaptive, std_maybe_report_progress, std_report_progress,
AdaptiveSerializer, Event, EventConfig, EventFirer, EventManagerHooksTuple, EventManagerId,
@ -39,9 +38,7 @@ use crate::{
fuzzer::{EvaluatorObservers, ExecutionProcessor},
inputs::{Input, NopInput},
observers::TimeObserver,
state::{
HasCorpus, HasExecutions, HasLastReportTime, MaybeHasClientPerfMonitor, NopState, Stoppable,
},
state::{HasExecutions, HasLastReportTime, MaybeHasClientPerfMonitor, NopState, Stoppable},
Error,
};
@ -49,7 +46,7 @@ pub(crate) const _LLMP_TAG_TO_MAIN: Tag = Tag(0x3453453);
/// A wrapper manager to implement a main-secondary architecture with another broker
#[derive(Debug)]
pub struct CentralizedEventManager<EM, EMH, S, SP>
pub struct CentralizedEventManager<EM, EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -61,10 +58,10 @@ where
time_ref: Option<Handle<TimeObserver>>,
hooks: EMH,
is_main: bool,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
}
impl CentralizedEventManager<NopEventManager, (), NopState<NopInput>, NopShMemProvider> {
impl CentralizedEventManager<NopEventManager, (), NopInput, NopState<NopInput>, NopShMemProvider> {
/// Creates a builder for [`CentralizedEventManager`]
#[must_use]
pub fn builder() -> CentralizedEventManagerBuilder {
@ -98,13 +95,13 @@ impl CentralizedEventManagerBuilder {
}
/// Creates a new [`CentralizedEventManager`].
pub fn build_from_client<EM, EMH, S, SP>(
pub fn build_from_client<EM, EMH, I, S, SP>(
self,
inner: EM,
hooks: EMH,
client: LlmpClient<SP>,
time_obs: Option<Handle<TimeObserver>>,
) -> Result<CentralizedEventManager<EM, EMH, S, SP>, Error>
) -> Result<CentralizedEventManager<EM, EMH, I, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -124,14 +121,14 @@ impl CentralizedEventManagerBuilder {
///
/// If the port is not yet bound, it will act as a broker; otherwise, it
/// will act as a client.
pub fn build_on_port<EM, EMH, S, SP>(
pub fn build_on_port<EM, EMH, I, S, SP>(
self,
inner: EM,
hooks: EMH,
shmem_provider: SP,
port: u16,
time_obs: Option<Handle<TimeObserver>>,
) -> Result<CentralizedEventManager<EM, EMH, S, SP>, Error>
) -> Result<CentralizedEventManager<EM, EMH, I, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -141,14 +138,14 @@ impl CentralizedEventManagerBuilder {
/// If a client respawns, it may reuse the existing connection, previously
/// stored by [`LlmpClient::to_env()`].
pub fn build_existing_client_from_env<EM, EMH, S, SP>(
pub fn build_existing_client_from_env<EM, EMH, I, S, SP>(
self,
inner: EM,
hooks: EMH,
shmem_provider: SP,
env_name: &str,
time_obs: Option<Handle<TimeObserver>>,
) -> Result<CentralizedEventManager<EM, EMH, S, SP>, Error>
) -> Result<CentralizedEventManager<EM, EMH, I, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -157,14 +154,14 @@ impl CentralizedEventManagerBuilder {
}
/// Create an existing client from description
pub fn existing_client_from_description<EM, EMH, S, SP>(
pub fn existing_client_from_description<EM, EMH, I, S, SP>(
self,
inner: EM,
hooks: EMH,
shmem_provider: SP,
description: &LlmpClientDescription,
time_obs: Option<Handle<TimeObserver>>,
) -> Result<CentralizedEventManager<EM, EMH, S, SP>, Error>
) -> Result<CentralizedEventManager<EM, EMH, I, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -173,7 +170,7 @@ impl CentralizedEventManagerBuilder {
}
}
impl<EM, EMH, S, SP> AdaptiveSerializer for CentralizedEventManager<EM, EMH, S, SP>
impl<EM, EMH, I, S, SP> AdaptiveSerializer for CentralizedEventManager<EM, EMH, I, S, SP>
where
EM: AdaptiveSerializer,
SP: ShMemProvider,
@ -209,25 +206,20 @@ where
}
}
impl<EM, EMH, S, SP> EventFirer<<S::Corpus as Corpus>::Input, S>
for CentralizedEventManager<EM, EMH, S, SP>
impl<EM, EMH, I, S, SP> EventFirer<I, S> for CentralizedEventManager<EM, EMH, I, S, SP>
where
EM: HasEventManagerId + EventFirer<<S::Corpus as Corpus>::Input, S>,
EMH: EventManagerHooksTuple<<<S as HasCorpus>::Corpus as Corpus>::Input, S>,
EM: HasEventManagerId + EventFirer<I, S>,
EMH: EventManagerHooksTuple<I, S>,
SP: ShMemProvider,
S: HasCorpus + Stoppable,
<<S as HasCorpus>::Corpus as Corpus>::Input: Input,
S: Stoppable,
I: Input,
{
fn should_send(&self) -> bool {
self.inner.should_send()
}
#[expect(clippy::match_same_arms)]
fn fire(
&mut self,
state: &mut S,
mut event: Event<<S::Corpus as Corpus>::Input>,
) -> Result<(), Error> {
fn fire(&mut self, state: &mut S, mut event: Event<I>) -> Result<(), Error> {
if !self.is_main {
// secondary node
let mut is_tc = false;
@ -270,7 +262,7 @@ where
}
}
impl<EM, EMH, S, SP> EventRestarter<S> for CentralizedEventManager<EM, EMH, S, SP>
impl<EM, EMH, I, S, SP> EventRestarter<S> for CentralizedEventManager<EM, EMH, I, S, SP>
where
SP: ShMemProvider,
EM: EventRestarter<S>,
@ -283,7 +275,7 @@ where
}
}
impl<EM, EMH, OT, S, SP> CanSerializeObserver<OT> for CentralizedEventManager<EM, EMH, S, SP>
impl<EM, EMH, I, OT, S, SP> CanSerializeObserver<OT> for CentralizedEventManager<EM, EMH, I, S, SP>
where
EM: AdaptiveSerializer,
SP: ShMemProvider,
@ -299,7 +291,7 @@ where
}
}
impl<EM, EMH, S, SP> ManagerExit for CentralizedEventManager<EM, EMH, S, SP>
impl<EM, EMH, I, S, SP> ManagerExit for CentralizedEventManager<EM, EMH, I, S, SP>
where
EM: ManagerExit,
SP: ShMemProvider,
@ -316,17 +308,16 @@ where
}
}
impl<E, EM, EMH, S, SP, Z> EventProcessor<E, S, Z> for CentralizedEventManager<EM, EMH, S, SP>
impl<E, EM, EMH, I, S, SP, Z> EventProcessor<E, S, Z> for CentralizedEventManager<EM, EMH, I, S, SP>
where
E: HasObservers,
E::Observers: DeserializeOwned,
EM: EventProcessor<E, S, Z> + HasEventManagerId + EventFirer<<S::Corpus as Corpus>::Input, S>,
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus + Stoppable,
<S::Corpus as Corpus>::Input: Input,
EM: EventProcessor<E, S, Z> + HasEventManagerId + EventFirer<I, S>,
EMH: EventManagerHooksTuple<I, S>,
S: Stoppable,
I: Input,
SP: ShMemProvider,
Z: ExecutionProcessor<Self, <S::Corpus as Corpus>::Input, E::Observers, S>
+ EvaluatorObservers<E, Self, <S::Corpus as Corpus>::Input, S>,
Z: ExecutionProcessor<Self, I, E::Observers, S> + EvaluatorObservers<E, Self, I, S>,
{
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
if self.is_main {
@ -345,17 +336,12 @@ where
}
}
impl<EM, EMH, S, SP> ProgressReporter<S> for CentralizedEventManager<EM, EMH, S, SP>
impl<EM, EMH, I, S, SP> ProgressReporter<S> for CentralizedEventManager<EM, EMH, I, S, SP>
where
EM: EventFirer<<S::Corpus as Corpus>::Input, S> + HasEventManagerId,
EMH: EventManagerHooksTuple<<<S as HasCorpus>::Corpus as Corpus>::Input, S>,
S: HasExecutions
+ HasMetadata
+ HasLastReportTime
+ Stoppable
+ HasCorpus
+ MaybeHasClientPerfMonitor,
<S::Corpus as Corpus>::Input: Input,
EM: EventFirer<I, S> + HasEventManagerId,
EMH: EventManagerHooksTuple<I, S>,
S: HasExecutions + HasMetadata + HasLastReportTime + Stoppable + MaybeHasClientPerfMonitor,
I: Input,
SP: ShMemProvider,
{
fn maybe_report_progress(
@ -371,7 +357,7 @@ where
}
}
impl<EM, EMH, S, SP> HasEventManagerId for CentralizedEventManager<EM, EMH, S, SP>
impl<EM, EMH, I, S, SP> HasEventManagerId for CentralizedEventManager<EM, EMH, I, S, SP>
where
EM: HasEventManagerId,
SP: ShMemProvider,
@ -381,7 +367,7 @@ where
}
}
impl<EM, EMH, S, SP> CentralizedEventManager<EM, EMH, S, SP>
impl<EM, EMH, I, S, SP> CentralizedEventManager<EM, EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -402,19 +388,16 @@ where
}
}
impl<EM, EMH, S, SP> CentralizedEventManager<EM, EMH, S, SP>
impl<EM, EMH, I, S, SP> CentralizedEventManager<EM, EMH, I, S, SP>
where
EM: HasEventManagerId + EventFirer<<S::Corpus as Corpus>::Input, S>,
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus + Stoppable,
<S::Corpus as Corpus>::Input: Input,
EM: HasEventManagerId + EventFirer<I, S>,
EMH: EventManagerHooksTuple<I, S>,
S: Stoppable,
I: Input,
SP: ShMemProvider,
{
#[cfg(feature = "llmp_compression")]
fn forward_to_main<I>(&mut self, event: &Event<I>) -> Result<(), Error>
where
I: Input,
{
fn forward_to_main(&mut self, event: &Event<I>) -> Result<(), Error> {
let serialized = postcard::to_allocvec(event)?;
let flags = LLMP_FLAG_INITIALIZED;
@ -434,10 +417,7 @@ where
}
#[cfg(not(feature = "llmp_compression"))]
fn forward_to_main<I>(&mut self, event: &Event<I>) -> Result<(), Error>
where
I: Input,
{
fn forward_to_main(&mut self, event: &Event<I>) -> Result<(), Error> {
let serialized = postcard::to_allocvec(event)?;
self.client.send_buf(_LLMP_TAG_TO_MAIN, &serialized)?;
Ok(())
@ -452,8 +432,7 @@ where
where
E: HasObservers,
E::Observers: DeserializeOwned,
Z: ExecutionProcessor<Self, <S::Corpus as Corpus>::Input, E::Observers, S>
+ EvaluatorObservers<E, Self, <S::Corpus as Corpus>::Input, S>,
Z: ExecutionProcessor<Self, I, E::Observers, S> + EvaluatorObservers<E, Self, I, S>,
{
// TODO: Get around local event copy by moving handle_in_client
let self_id = self.client.sender().id();
@ -478,7 +457,7 @@ where
} else {
msg
};
let event: Event<<S::Corpus as Corpus>::Input> = postcard::from_bytes(event_bytes)?;
let event: Event<I> = postcard::from_bytes(event_bytes)?;
log::debug!("Processor received message {}", event.name_detailed());
self.handle_in_main(fuzzer, executor, state, client_id, event)?;
count += 1;
@ -493,13 +472,12 @@ where
executor: &mut E,
state: &mut S,
client_id: ClientId,
event: Event<<S::Corpus as Corpus>::Input>,
event: Event<I>,
) -> Result<(), Error>
where
E: HasObservers,
E::Observers: DeserializeOwned,
Z: ExecutionProcessor<Self, <S::Corpus as Corpus>::Input, E::Observers, S>
+ EvaluatorObservers<E, Self, <S::Corpus as Corpus>::Input, S>,
Z: ExecutionProcessor<Self, I, E::Observers, S> + EvaluatorObservers<E, Self, I, S>,
{
log::debug!("handle_in_main!");
@ -594,14 +572,3 @@ where
Ok(())
}
}
/*
impl<EM, SP> Drop for CentralizedEventManager<EM, SP>
where
EM: UsesState, SP: ShMemProvider + 'static,
{
/// LLMP clients will have to wait until their pages are mapped by somebody.
fn drop(&mut self) {
self.await_restart_safe();
}
}*/

View File

@ -51,14 +51,12 @@ use {libafl_bolts::os::startable_self, std::process::Stdio};
#[cfg(all(unix, feature = "fork", feature = "multi_machine"))]
use crate::events::multi_machine::{NodeDescriptor, TcpMultiMachineHooks};
use crate::{
corpus::Corpus,
events::{
llmp::{LlmpRestartingEventManager, LlmpShouldSaveState, ManagerKind, RestartingMgr},
EventConfig, EventManagerHooksTuple,
},
monitors::Monitor,
observers::TimeObserver,
state::HasCorpus,
Error,
};
@ -214,15 +212,15 @@ where
{
/// Launch the broker and the clients and fuzz
#[cfg(any(windows, not(feature = "fork"), all(unix, feature = "fork")))]
pub fn launch<S>(&mut self) -> Result<(), Error>
pub fn launch<I, S>(&mut self) -> Result<(), Error>
where
S: DeserializeOwned + HasCorpus + Serialize,
<S::Corpus as Corpus>::Input: DeserializeOwned,
CF: FnOnce(
Option<S>,
LlmpRestartingEventManager<(), S, SP>,
LlmpRestartingEventManager<(), I, S, SP>,
ClientDescription,
) -> Result<(), Error>,
I: DeserializeOwned,
S: DeserializeOwned + Serialize,
{
Self::launch_with_hooks(self, tuple_list!())
}
@ -235,14 +233,14 @@ where
{
/// Launch the broker and the clients and fuzz with a user-supplied hook
#[cfg(all(unix, feature = "fork"))]
pub fn launch_with_hooks<EMH, S>(&mut self, hooks: EMH) -> Result<(), Error>
pub fn launch_with_hooks<EMH, I, S>(&mut self, hooks: EMH) -> Result<(), Error>
where
S: DeserializeOwned + HasCorpus + Serialize,
<S::Corpus as Corpus>::Input: DeserializeOwned,
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S> + Clone + Copy,
S: DeserializeOwned + Serialize,
I: DeserializeOwned,
EMH: EventManagerHooksTuple<I, S> + Clone + Copy,
CF: FnOnce(
Option<S>,
LlmpRestartingEventManager<EMH, S, SP>,
LlmpRestartingEventManager<EMH, I, S, SP>,
ClientDescription,
) -> Result<(), Error>,
{
@ -314,7 +312,7 @@ where
ClientDescription::new(index, overcommit_id, bind_to);
// Fuzzer client. keeps retrying the connection to broker till the broker starts
let builder = RestartingMgr::<EMH, MT, S, SP>::builder()
let builder = RestartingMgr::<EMH, I, MT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone())
.broker_port(self.broker_port)
.kind(ManagerKind::Client {
@ -341,7 +339,7 @@ where
log::info!("I am broker!!.");
// TODO we don't want always a broker here, think about using different laucher process to spawn different configurations
let builder = RestartingMgr::<EMH, MT, S, SP>::builder()
let builder = RestartingMgr::<EMH, I, MT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone())
.monitor(Some(self.monitor.clone()))
.broker_port(self.broker_port)
@ -383,18 +381,18 @@ where
/// Launch the broker and the clients and fuzz
#[cfg(any(windows, not(feature = "fork")))]
#[expect(clippy::too_many_lines, clippy::match_wild_err_arm)]
pub fn launch_with_hooks<EMH, S>(&mut self, hooks: EMH) -> Result<(), Error>
pub fn launch_with_hooks<EMH, I, S>(&mut self, hooks: EMH) -> Result<(), Error>
where
S: DeserializeOwned + HasCorpus + Serialize,
<S::Corpus as Corpus>::Input: DeserializeOwned,
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S> + Clone + Copy,
CF: FnOnce(
Option<S>,
LlmpRestartingEventManager<EMH, S, SP>,
LlmpRestartingEventManager<EMH, I, S, SP>,
ClientDescription,
) -> Result<(), Error>,
EMH: EventManagerHooksTuple<I, S> + Clone + Copy,
I: DeserializeOwned,
S: DeserializeOwned + Serialize,
{
use libafl_bolts::core_affinity;
use libafl_bolts::core_affinity::get_core_ids;
let is_client = std::env::var(_AFL_LAUNCHER_CLIENT);
@ -403,7 +401,7 @@ where
let client_description = ClientDescription::from_safe_string(&core_conf);
// the actual client. do the fuzzing
let builder = RestartingMgr::<EMH, MT, S, SP>::builder()
let builder = RestartingMgr::<EMH, I, MT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone())
.broker_port(self.broker_port)
.kind(ManagerKind::Client {
@ -423,7 +421,7 @@ where
// I am a broker
// before going to the broker loop, spawn n clients
let core_ids = core_affinity::get_core_ids().unwrap();
let core_ids = get_core_ids().unwrap();
let mut handles = vec![];
log::info!("spawning on cores: {:?}", self.cores);
@ -504,7 +502,7 @@ where
if self.spawn_broker {
log::info!("I am broker!!.");
let builder = RestartingMgr::<EMH, MT, S, SP>::builder()
let builder = RestartingMgr::<EMH, I, MT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone())
.monitor(Some(self.monitor.clone()))
.broker_port(self.broker_port)
@ -622,7 +620,7 @@ impl<CF, MF, MT, SP> Debug for CentralizedLauncher<'_, CF, MF, MT, SP> {
}
/// The standard inner manager of centralized
pub type StdCentralizedInnerMgr<S, SP> = LlmpRestartingEventManager<(), S, SP>;
pub type StdCentralizedInnerMgr<I, S, SP> = LlmpRestartingEventManager<(), I, S, SP>;
#[cfg(all(unix, feature = "fork"))]
impl<CF, MF, MT, SP> CentralizedLauncher<'_, CF, MF, MT, SP>
@ -631,25 +629,25 @@ where
SP: ShMemProvider + 'static,
{
/// Launch a standard Centralized-based fuzzer
pub fn launch<S>(&mut self) -> Result<(), Error>
pub fn launch<I, S>(&mut self) -> Result<(), Error>
where
S: DeserializeOwned + HasCorpus + Serialize,
<S::Corpus as Corpus>::Input: DeserializeOwned + Input + Send + Sync + 'static,
S: DeserializeOwned + Serialize,
I: DeserializeOwned + Input + Send + Sync + 'static,
CF: FnOnce(
Option<S>,
CentralizedEventManager<StdCentralizedInnerMgr<S, SP>, (), S, SP>,
CentralizedEventManager<StdCentralizedInnerMgr<I, S, SP>, (), I, S, SP>,
ClientDescription,
) -> Result<(), Error>,
MF: FnOnce(
Option<S>,
CentralizedEventManager<StdCentralizedInnerMgr<S, SP>, (), S, SP>,
CentralizedEventManager<StdCentralizedInnerMgr<I, S, SP>, (), I, S, SP>,
ClientDescription,
) -> Result<(), Error>,
{
let restarting_mgr_builder =
|centralized_launcher: &Self, client_description: ClientDescription| {
// Fuzzer client. keeps retrying the connection to broker till the broker starts
let builder = RestartingMgr::<(), MT, S, SP>::builder()
let builder = RestartingMgr::<(), I, MT, S, SP>::builder()
.shmem_provider(centralized_launcher.shmem_provider.clone())
.broker_port(centralized_launcher.broker_port)
.kind(ManagerKind::Client { client_description })
@ -675,23 +673,22 @@ where
/// Launch a Centralized-based fuzzer.
/// - `main_inner_mgr_builder` will be called to build the inner manager of the main node.
/// - `secondary_inner_mgr_builder` will be called to build the inner manager of the secondary nodes.
pub fn launch_generic<EM, EMB, S>(
pub fn launch_generic<EM, EMB, I, S>(
&mut self,
main_inner_mgr_builder: EMB,
secondary_inner_mgr_builder: EMB,
) -> Result<(), Error>
where
S: HasCorpus,
<S::Corpus as Corpus>::Input: Input + Send + Sync + 'static,
I: Input + Send + Sync + 'static,
CF: FnOnce(
Option<S>,
CentralizedEventManager<EM, (), S, SP>,
CentralizedEventManager<EM, (), I, S, SP>,
ClientDescription,
) -> Result<(), Error>,
EMB: FnOnce(&Self, ClientDescription) -> Result<(Option<S>, EM), Error>,
MF: FnOnce(
Option<S>,
CentralizedEventManager<EM, (), S, SP>, // No broker_hooks for centralized EM
CentralizedEventManager<EM, (), I, S, SP>, // No broker_hooks for centralized EM
ClientDescription,
) -> Result<(), Error>,
{
@ -835,7 +832,7 @@ where
} = unsafe {
TcpMultiMachineHooks::builder()
.node_descriptor(self.multi_machine_node_descriptor.clone())
.build::<<S::Corpus as Corpus>::Input>()?
.build::<I>()?
};
let mut brokers = Brokers::new();
@ -845,13 +842,12 @@ where
brokers.add(Box::new({
#[cfg(feature = "multi_machine")]
let centralized_hooks = tuple_list!(
CentralizedLlmpHook::<<S::Corpus as Corpus>::Input>::new()?,
CentralizedLlmpHook::<I>::new()?,
multi_machine_receiver_hook,
);
#[cfg(not(feature = "multi_machine"))]
let centralized_hooks =
tuple_list!(CentralizedLlmpHook::<<S::Corpus as Corpus>::Input>::new()?);
let centralized_hooks = tuple_list!(CentralizedLlmpHook::<I>::new()?);
// TODO switch to false after solving the bug
let mut broker = LlmpBroker::with_keep_pages_attach_to_tcp(
@ -875,13 +871,11 @@ where
log::info!("I am broker!!.");
#[cfg(not(feature = "multi_machine"))]
let llmp_hook = tuple_list!(StdLlmpEventHook::<<S::Corpus as Corpus>::Input, MT>::new(
self.monitor.clone()
)?);
let llmp_hook = tuple_list!(StdLlmpEventHook::<I, MT>::new(self.monitor.clone())?);
#[cfg(feature = "multi_machine")]
let llmp_hook = tuple_list!(
StdLlmpEventHook::<<S::Corpus as Corpus>::Input, MT>::new(self.monitor.clone())?,
StdLlmpEventHook::<I, MT>::new(self.monitor.clone())?,
multi_machine_sender_hook,
);

View File

@ -34,7 +34,6 @@ use crate::events::llmp::COMPRESS_THRESHOLD;
#[cfg(feature = "std")]
use crate::events::{serialize_observers_adaptive, CanSerializeObserver};
use crate::{
corpus::Corpus,
events::{
llmp::{LLMP_TAG_EVENT_TO_BOTH, _LLMP_TAG_EVENT_TO_BROKER},
std_maybe_report_progress, std_on_restart, std_report_progress, AdaptiveSerializer, Event,
@ -47,8 +46,8 @@ use crate::{
observers::TimeObserver,
stages::HasCurrentStageId,
state::{
HasCorpus, HasExecutions, HasImported, HasLastReportTime, MaybeHasClientPerfMonitor,
NopState, Stoppable,
HasExecutions, HasImported, HasLastReportTime, MaybeHasClientPerfMonitor, NopState,
Stoppable,
},
Error, HasMetadata,
};
@ -58,7 +57,7 @@ const INITIAL_EVENT_BUFFER_SIZE: usize = 1024 * 4;
/// An `EventManager` that forwards all events to other attached fuzzers on shared maps or via tcp,
/// using low-level message passing, `llmp`.
pub struct LlmpEventManager<EMH, S, SP>
pub struct LlmpEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -80,11 +79,11 @@ where
serializations_cnt: usize,
should_serialize_cnt: usize,
pub(crate) time_ref: Option<Handle<TimeObserver>>,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
event_buffer: Vec<u8>,
}
impl LlmpEventManager<(), NopState<NopInput>, NopShMemProvider> {
impl LlmpEventManager<(), NopState<NopInput>, NopInput, NopShMemProvider> {
/// Creates a builder for [`LlmpEventManager`]
#[must_use]
pub fn builder() -> LlmpEventManagerBuilder<()> {
@ -133,12 +132,12 @@ impl<EMH> LlmpEventManagerBuilder<EMH> {
}
/// Create a manager from a raw LLMP client
pub fn build_from_client<S, SP>(
pub fn build_from_client<I, S, SP>(
self,
llmp: LlmpClient<SP>,
configuration: EventConfig,
time_ref: Option<Handle<TimeObserver>>,
) -> Result<LlmpEventManager<EMH, S, SP>, Error>
) -> Result<LlmpEventManager<EMH, I, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -163,13 +162,13 @@ impl<EMH> LlmpEventManagerBuilder<EMH> {
/// Create an LLMP event manager on a port.
/// It expects a broker to exist on this port.
#[cfg(feature = "std")]
pub fn build_on_port<S, SP>(
pub fn build_on_port<I, S, SP>(
self,
shmem_provider: SP,
port: u16,
configuration: EventConfig,
time_ref: Option<Handle<TimeObserver>>,
) -> Result<LlmpEventManager<EMH, S, SP>, Error>
) -> Result<LlmpEventManager<EMH, I, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -180,13 +179,13 @@ impl<EMH> LlmpEventManagerBuilder<EMH> {
/// If a client respawns, it may reuse the existing connection, previously
/// stored by [`LlmpClient::to_env()`].
#[cfg(feature = "std")]
pub fn build_existing_client_from_env<S, SP>(
pub fn build_existing_client_from_env<I, S, SP>(
self,
shmem_provider: SP,
env_name: &str,
configuration: EventConfig,
time_ref: Option<Handle<TimeObserver>>,
) -> Result<LlmpEventManager<EMH, S, SP>, Error>
) -> Result<LlmpEventManager<EMH, I, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -195,13 +194,13 @@ impl<EMH> LlmpEventManagerBuilder<EMH> {
}
/// Create an existing client from description
pub fn build_existing_client_from_description<S, SP>(
pub fn build_existing_client_from_description<I, S, SP>(
self,
shmem_provider: SP,
description: &LlmpClientDescription,
configuration: EventConfig,
time_ref: Option<Handle<TimeObserver>>,
) -> Result<LlmpEventManager<EMH, S, SP>, Error>
) -> Result<LlmpEventManager<EMH, I, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -211,7 +210,7 @@ impl<EMH> LlmpEventManagerBuilder<EMH> {
}
#[cfg(feature = "std")]
impl<EMH, OT, S, SP> CanSerializeObserver<OT> for LlmpEventManager<EMH, S, SP>
impl<EMH, I, OT, S, SP> CanSerializeObserver<OT> for LlmpEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
OT: Serialize + MatchNameRef,
@ -221,7 +220,7 @@ where
}
}
impl<EMH, S, SP> AdaptiveSerializer for LlmpEventManager<EMH, S, SP>
impl<EMH, I, S, SP> AdaptiveSerializer for LlmpEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -256,7 +255,7 @@ where
}
}
impl<EMH, S, SP> core::fmt::Debug for LlmpEventManager<EMH, S, SP>
impl<EMH, I, S, SP> core::fmt::Debug for LlmpEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -273,7 +272,7 @@ where
}
}
impl<EMH, S, SP> Drop for LlmpEventManager<EMH, S, SP>
impl<EMH, I, S, SP> Drop for LlmpEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -283,7 +282,7 @@ where
}
}
impl<EMH, S, SP> LlmpEventManager<EMH, S, SP>
impl<EMH, I, S, SP> LlmpEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -333,7 +332,7 @@ where
}
}
impl<EMH, S, SP> LlmpEventManager<EMH, S, SP>
impl<EMH, I, S, SP> LlmpEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -344,16 +343,15 @@ where
executor: &mut E,
state: &mut S,
client_id: ClientId,
event: Event<<S::Corpus as Corpus>::Input>,
event: Event<I>,
) -> Result<(), Error>
where
S: HasCorpus + HasImported + Stoppable,
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: Input,
S: HasImported + Stoppable,
EMH: EventManagerHooksTuple<I, S>,
I: Input,
E: HasObservers,
E::Observers: DeserializeOwned,
Z: ExecutionProcessor<Self, <S::Corpus as Corpus>::Input, E::Observers, S>
+ EvaluatorObservers<E, Self, <S::Corpus as Corpus>::Input, S>,
Z: ExecutionProcessor<Self, I, E::Observers, S> + EvaluatorObservers<E, Self, I, S>,
{
log::trace!("Got event in client: {} from {client_id:?}", event.name());
if !self.hooks.pre_exec_all(state, client_id, &event)? {
@ -409,7 +407,7 @@ where
}
}
impl<EMH, S, SP: ShMemProvider> LlmpEventManager<EMH, S, SP> {
impl<EMH, I, S, SP: ShMemProvider> LlmpEventManager<EMH, I, S, SP> {
/// Send information that this client is exiting.
/// The other side may free up all allocated memory.
/// We are no longer allowed to send anything afterwards.
@ -418,7 +416,7 @@ impl<EMH, S, SP: ShMemProvider> LlmpEventManager<EMH, S, SP> {
}
}
impl<EMH, I, S, SP> EventFirer<I, S> for LlmpEventManager<EMH, S, SP>
impl<EMH, I, S, SP> EventFirer<I, S> for LlmpEventManager<EMH, I, S, SP>
where
I: Serialize,
SP: ShMemProvider,
@ -482,7 +480,7 @@ where
}
}
impl<EMH, S, SP> EventRestarter<S> for LlmpEventManager<EMH, S, SP>
impl<EMH, I, S, SP> EventRestarter<S> for LlmpEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
S: HasCurrentStageId,
@ -492,7 +490,7 @@ where
}
}
impl<EMH, S, SP> ManagerExit for LlmpEventManager<EMH, S, SP>
impl<EMH, I, S, SP> ManagerExit for LlmpEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -508,16 +506,15 @@ where
}
}
impl<E, EMH, S, SP, Z> EventProcessor<E, S, Z> for LlmpEventManager<EMH, S, SP>
impl<E, EMH, I, S, SP, Z> EventProcessor<E, S, Z> for LlmpEventManager<EMH, I, S, SP>
where
E: HasObservers,
E::Observers: DeserializeOwned,
S: HasCorpus + HasImported + Stoppable,
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: DeserializeOwned + Input,
S: HasImported + Stoppable,
EMH: EventManagerHooksTuple<I, S>,
I: DeserializeOwned + Input,
SP: ShMemProvider,
Z: ExecutionProcessor<Self, <S::Corpus as Corpus>::Input, E::Observers, S>
+ EvaluatorObservers<E, Self, <S::Corpus as Corpus>::Input, S>,
Z: ExecutionProcessor<Self, I, E::Observers, S> + EvaluatorObservers<E, Self, I, S>,
{
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
// TODO: Get around local event copy by moving handle_in_client
@ -543,7 +540,7 @@ where
} else {
msg
};
let event: Event<<S::Corpus as Corpus>::Input> = postcard::from_bytes(event_bytes)?;
let event: Event<I> = postcard::from_bytes(event_bytes)?;
log::debug!("Received event in normal llmp {}", event.name_detailed());
// If the message comes from another machine, do not
@ -563,11 +560,11 @@ where
}
}
impl<EMH, S, SP> ProgressReporter<S> for LlmpEventManager<EMH, S, SP>
impl<EMH, I, S, SP> ProgressReporter<S> for LlmpEventManager<EMH, I, S, SP>
where
S: HasExecutions + HasLastReportTime + HasMetadata + HasCorpus + MaybeHasClientPerfMonitor,
S: HasExecutions + HasLastReportTime + HasMetadata + MaybeHasClientPerfMonitor,
SP: ShMemProvider,
<S::Corpus as Corpus>::Input: Serialize,
I: Serialize,
{
fn maybe_report_progress(
&mut self,
@ -582,7 +579,7 @@ where
}
}
impl<EMH, S, SP> HasEventManagerId for LlmpEventManager<EMH, S, SP>
impl<EMH, I, S, SP> HasEventManagerId for LlmpEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{

View File

@ -15,11 +15,10 @@ use libafl_bolts::{
use serde::{de::DeserializeOwned, Serialize};
use crate::{
corpus::Corpus,
events::{Event, EventFirer},
fuzzer::EvaluatorObservers,
inputs::{Input, InputConverter, NopInput, NopInputConverter},
state::{HasCorpus, NopState},
state::NopState,
Error,
};
@ -83,7 +82,7 @@ impl LlmpShouldSaveState {
}
/// A manager-like llmp client that converts between input types
pub struct LlmpEventConverter<IC, ICB, S, SP>
pub struct LlmpEventConverter<I, IC, ICB, S, SP>
where
SP: ShMemProvider,
{
@ -94,11 +93,12 @@ where
compressor: GzipCompressor,
converter: Option<IC>,
converter_back: Option<ICB>,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
}
impl
LlmpEventConverter<
NopInput,
NopInputConverter<NopInput>,
NopInputConverter<NopInput>,
NopState<NopInput>,
@ -134,12 +134,12 @@ impl LlmpEventConverterBuilder {
}
/// Create a event converter from a raw llmp client
pub fn build_from_client<IC, ICB, S, SP>(
pub fn build_from_client<I, IC, ICB, S, SP>(
self,
llmp: LlmpClient<SP>,
converter: Option<IC>,
converter_back: Option<ICB>,
) -> Result<LlmpEventConverter<IC, ICB, S, SP>, Error>
) -> Result<LlmpEventConverter<I, IC, ICB, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -157,13 +157,13 @@ impl LlmpEventConverterBuilder {
/// Create a client from port and the input converters
#[cfg(feature = "std")]
pub fn build_on_port<IC, ICB, S, SP>(
pub fn build_on_port<I, IC, ICB, S, SP>(
self,
shmem_provider: SP,
port: u16,
converter: Option<IC>,
converter_back: Option<ICB>,
) -> Result<LlmpEventConverter<IC, ICB, S, SP>, Error>
) -> Result<LlmpEventConverter<I, IC, ICB, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -182,13 +182,13 @@ impl LlmpEventConverterBuilder {
/// If a client respawns, it may reuse the existing connection, previously stored by [`LlmpClient::to_env()`].
#[cfg(feature = "std")]
pub fn build_existing_client_from_env<IC, ICB, S, SP>(
pub fn build_existing_client_from_env<I, IC, ICB, S, SP>(
self,
shmem_provider: SP,
env_name: &str,
converter: Option<IC>,
converter_back: Option<ICB>,
) -> Result<LlmpEventConverter<IC, ICB, S, SP>, Error>
) -> Result<LlmpEventConverter<I, IC, ICB, S, SP>, Error>
where
SP: ShMemProvider,
{
@ -206,11 +206,11 @@ impl LlmpEventConverterBuilder {
}
}
impl<IC, ICB, S, SP> Debug for LlmpEventConverter<IC, ICB, S, SP>
impl<I, IC, ICB, S, SP> Debug for LlmpEventConverter<I, IC, ICB, S, SP>
where
SP: ShMemProvider,
ICB: Debug,
IC: Debug,
ICB: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug_struct = f.debug_struct("LlmpEventConverter");
@ -226,9 +226,8 @@ where
}
}
impl<IC, ICB, S, SP> LlmpEventConverter<IC, ICB, S, SP>
impl<I, IC, ICB, S, SP> LlmpEventConverter<I, IC, ICB, S, SP>
where
S: HasCorpus,
SP: ShMemProvider,
{
// TODO other new_* routines
@ -265,8 +264,8 @@ where
event: Event<DI>,
) -> Result<(), Error>
where
ICB: InputConverter<To = <S::Corpus as Corpus>::Input, From = DI>,
Z: EvaluatorObservers<E, EM, <S::Corpus as Corpus>::Input, S>,
ICB: InputConverter<To = I, From = DI>,
Z: EvaluatorObservers<E, EM, I, S>,
{
match event {
Event::NewTestcase {
@ -308,9 +307,9 @@ where
manager: &mut EM,
) -> Result<usize, Error>
where
ICB: InputConverter<To = <S::Corpus as Corpus>::Input, From = DI>,
ICB: InputConverter<To = I, From = DI>,
DI: DeserializeOwned + Input,
Z: EvaluatorObservers<E, EM, <S::Corpus as Corpus>::Input, S>,
Z: EvaluatorObservers<E, EM, I, S>,
{
// TODO: Get around local event copy by moving handle_in_client
let self_id = self.llmp.sender().id();
@ -345,11 +344,9 @@ where
}
}
impl<IC, ICB, S, SP> EventFirer<<S::Corpus as Corpus>::Input, S>
for LlmpEventConverter<IC, ICB, S, SP>
impl<I, IC, ICB, S, SP> EventFirer<I, S> for LlmpEventConverter<I, IC, ICB, S, SP>
where
IC: InputConverter<From = <S::Corpus as Corpus>::Input>,
S: HasCorpus,
IC: InputConverter<From = I>,
SP: ShMemProvider,
IC::To: Serialize,
{
@ -362,11 +359,7 @@ where
}
#[cfg(feature = "llmp_compression")]
fn fire(
&mut self,
_state: &mut S,
event: Event<<S::Corpus as Corpus>::Input>,
) -> Result<(), Error> {
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
if self.converter.is_none() {
return Ok(());
}
@ -418,11 +411,7 @@ where
}
#[cfg(not(feature = "llmp_compression"))]
fn fire(
&mut self,
_state: &mut S,
event: Event<<S::Corpus as Corpus>::Input>,
) -> Result<(), Error> {
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
if self.converter.is_none() {
return Ok(());
}

View File

@ -33,7 +33,6 @@ use typed_builder::TypedBuilder;
use crate::events::EVENTMGR_SIGHANDLER_STATE;
use crate::{
common::HasMetadata,
corpus::Corpus,
events::{
launcher::ClientDescription, serialize_observers_adaptive, std_maybe_report_progress,
std_report_progress, AdaptiveSerializer, CanSerializeObserver, Event, EventConfig,
@ -47,28 +46,25 @@ use crate::{
monitors::Monitor,
observers::TimeObserver,
stages::HasCurrentStageId,
state::{
HasCorpus, HasExecutions, HasImported, HasLastReportTime, MaybeHasClientPerfMonitor,
Stoppable,
},
state::{HasExecutions, HasImported, HasLastReportTime, MaybeHasClientPerfMonitor, Stoppable},
Error,
};
/// A manager that can restart on the fly, storing states in-between (in `on_restart`)
#[derive(Debug)]
pub struct LlmpRestartingEventManager<EMH, S, SP>
pub struct LlmpRestartingEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
/// The embedded LLMP event manager
llmp_mgr: LlmpEventManager<EMH, S, SP>,
llmp_mgr: LlmpEventManager<EMH, I, S, SP>,
/// The staterestorer to serialize the state for the next runner
staterestorer: StateRestorer<SP>,
/// Decide if the state restorer must save the serialized state
save_state: LlmpShouldSaveState,
}
impl<EMH, S, SP> AdaptiveSerializer for LlmpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> AdaptiveSerializer for LlmpRestartingEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -103,16 +99,11 @@ where
}
}
impl<EMH, S, SP> ProgressReporter<S> for LlmpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> ProgressReporter<S> for LlmpRestartingEventManager<EMH, I, S, SP>
where
S: HasExecutions
+ HasLastReportTime
+ HasMetadata
+ HasCorpus
+ Serialize
+ MaybeHasClientPerfMonitor,
S: HasExecutions + HasLastReportTime + HasMetadata + Serialize + MaybeHasClientPerfMonitor,
SP: ShMemProvider,
<S::Corpus as Corpus>::Input: Serialize,
I: Serialize,
{
fn maybe_report_progress(
&mut self,
@ -127,14 +118,14 @@ where
}
}
impl<EMH, I, S, SP> EventFirer<I, S> for LlmpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> EventFirer<I, S> for LlmpRestartingEventManager<EMH, I, S, SP>
where
I: Serialize,
S: HasCorpus + Serialize,
S: Serialize,
SP: ShMemProvider,
{
fn should_send(&self) -> bool {
<LlmpEventManager<EMH, S, SP> as EventFirer<I, S>>::should_send(&self.llmp_mgr)
<LlmpEventManager<EMH, I, S, SP> as EventFirer<I, S>>::should_send(&self.llmp_mgr)
}
fn fire(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error> {
@ -145,12 +136,12 @@ where
}
fn configuration(&self) -> EventConfig {
<LlmpEventManager<EMH, S, SP> as EventFirer<I, S>>::configuration(&self.llmp_mgr)
<LlmpEventManager<EMH, I, S, SP> as EventFirer<I, S>>::configuration(&self.llmp_mgr)
}
}
#[cfg(feature = "std")]
impl<EMH, OT, S, SP> CanSerializeObserver<OT> for LlmpRestartingEventManager<EMH, S, SP>
impl<EMH, I, OT, S, SP> CanSerializeObserver<OT> for LlmpRestartingEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
OT: Serialize + MatchNameRef,
@ -160,7 +151,7 @@ where
}
}
impl<EMH, S, SP> EventRestarter<S> for LlmpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> EventRestarter<S> for LlmpRestartingEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
S: Serialize + HasCurrentStageId,
@ -186,7 +177,7 @@ where
}
}
impl<EMH, S, SP> ManagerExit for LlmpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> ManagerExit for LlmpRestartingEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -205,21 +196,16 @@ where
}
}
impl<E, EMH, S, SP, Z> EventProcessor<E, S, Z> for LlmpRestartingEventManager<EMH, S, SP>
impl<E, EMH, I, S, SP, Z> EventProcessor<E, S, Z> for LlmpRestartingEventManager<EMH, I, S, SP>
where
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
EMH: EventManagerHooksTuple<I, S>,
E: HasObservers,
E::Observers: DeserializeOwned,
S: HasCorpus + HasImported + Stoppable + Serialize,
<S::Corpus as Corpus>::Input: DeserializeOwned + Input,
S::Corpus: Serialize,
S: HasImported + Stoppable + Serialize,
I: DeserializeOwned + Input,
SP: ShMemProvider,
Z: ExecutionProcessor<
LlmpEventManager<EMH, S, SP>,
<S::Corpus as Corpus>::Input,
E::Observers,
S,
> + EvaluatorObservers<E, LlmpEventManager<EMH, S, SP>, <S::Corpus as Corpus>::Input, S>,
Z: ExecutionProcessor<LlmpEventManager<EMH, I, S, SP>, I, E::Observers, S>
+ EvaluatorObservers<E, LlmpEventManager<EMH, I, S, SP>, I, S>,
{
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
let res = self.llmp_mgr.process(fuzzer, state, executor)?;
@ -232,7 +218,7 @@ where
}
}
impl<EMH, S, SP> HasEventManagerId for LlmpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> HasEventManagerId for LlmpRestartingEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -247,13 +233,16 @@ const _ENV_FUZZER_RECEIVER: &str = "_AFL_ENV_FUZZER_RECEIVER";
/// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages)
const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = "_AFL_ENV_FUZZER_BROKER_CLIENT";
impl<EMH, S, SP> LlmpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> LlmpRestartingEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
S: Serialize,
{
/// Create a new runner, the executed child doing the actual fuzzing.
pub fn new(llmp_mgr: LlmpEventManager<EMH, S, SP>, staterestorer: StateRestorer<SP>) -> Self {
pub fn new(
llmp_mgr: LlmpEventManager<EMH, I, S, SP>,
staterestorer: StateRestorer<SP>,
) -> Self {
Self {
llmp_mgr,
staterestorer,
@ -263,7 +252,7 @@ where
/// Create a new runner specifying if it must save the serialized state on restart.
pub fn with_save_state(
llmp_mgr: LlmpEventManager<EMH, S, SP>,
llmp_mgr: LlmpEventManager<EMH, I, S, SP>,
staterestorer: StateRestorer<SP>,
save_state: LlmpShouldSaveState,
) -> Self {
@ -315,21 +304,21 @@ pub enum ManagerKind {
/// The restarting mgr is a combination of restarter and runner, that can be used on systems with and without `fork` support.
/// The restarter will spawn a new process each time the child crashes or timeouts.
#[expect(clippy::type_complexity)]
pub fn setup_restarting_mgr_std<MT, S>(
pub fn setup_restarting_mgr_std<I, MT, S>(
monitor: MT,
broker_port: u16,
configuration: EventConfig,
) -> Result<
(
Option<S>,
LlmpRestartingEventManager<(), S, StdShMemProvider>,
LlmpRestartingEventManager<(), I, S, StdShMemProvider>,
),
Error,
>
where
MT: Monitor + Clone,
S: HasCorpus + Serialize + DeserializeOwned,
<S::Corpus as Corpus>::Input: DeserializeOwned,
S: Serialize + DeserializeOwned,
I: DeserializeOwned,
{
RestartingMgr::builder()
.shmem_provider(StdShMemProvider::new()?)
@ -347,7 +336,7 @@ where
/// The restarter will spawn a new process each time the child crashes or timeouts.
/// This one, additionally uses the timeobserver for the adaptive serialization
#[expect(clippy::type_complexity)]
pub fn setup_restarting_mgr_std_adaptive<MT, S>(
pub fn setup_restarting_mgr_std_adaptive<I, MT, S>(
monitor: MT,
broker_port: u16,
configuration: EventConfig,
@ -355,14 +344,14 @@ pub fn setup_restarting_mgr_std_adaptive<MT, S>(
) -> Result<
(
Option<S>,
LlmpRestartingEventManager<(), S, StdShMemProvider>,
LlmpRestartingEventManager<(), I, S, StdShMemProvider>,
),
Error,
>
where
MT: Monitor + Clone,
S: HasCorpus + Serialize + DeserializeOwned,
<S::Corpus as Corpus>::Input: DeserializeOwned,
S: Serialize + DeserializeOwned,
I: DeserializeOwned,
{
RestartingMgr::builder()
.shmem_provider(StdShMemProvider::new()?)
@ -381,7 +370,7 @@ where
/// `restarter` and `runner`, that can be used on systems both with and without `fork` support. The
/// `restarter` will start a new process each time the child crashes or times out.
#[derive(TypedBuilder, Debug)]
pub struct RestartingMgr<EMH, MT, S, SP> {
pub struct RestartingMgr<EMH, I, MT, S, SP> {
/// The shared memory provider to use for the broker or client spawned by the restarting
/// manager.
shmem_provider: SP,
@ -415,20 +404,22 @@ pub struct RestartingMgr<EMH, MT, S, SP> {
#[builder(default = None)]
time_ref: Option<Handle<TimeObserver>>,
#[builder(setter(skip), default = PhantomData)]
phantom_data: PhantomData<(EMH, S)>,
phantom_data: PhantomData<(EMH, I, S)>,
}
#[expect(clippy::type_complexity, clippy::too_many_lines)]
impl<EMH, MT, S, SP> RestartingMgr<EMH, MT, S, SP>
impl<EMH, I, MT, S, SP> RestartingMgr<EMH, I, MT, S, SP>
where
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S> + Copy + Clone,
EMH: EventManagerHooksTuple<I, S> + Copy + Clone,
SP: ShMemProvider,
S: HasCorpus + Serialize + DeserializeOwned,
<S::Corpus as Corpus>::Input: DeserializeOwned,
S: Serialize + DeserializeOwned,
I: DeserializeOwned,
MT: Monitor + Clone,
{
/// Launch the broker and the clients and fuzz
pub fn launch(&mut self) -> Result<(Option<S>, LlmpRestartingEventManager<EMH, S, SP>), Error> {
pub fn launch(
&mut self,
) -> Result<(Option<S>, LlmpRestartingEventManager<EMH, I, S, SP>), Error> {
// We start ourselves as child process to actually fuzz
let (staterestorer, new_shmem_provider, core_id) = if std::env::var(_ENV_FUZZER_SENDER)
.is_err()
@ -458,9 +449,7 @@ where
match connection {
LlmpConnection::IsBroker { broker } => {
let llmp_hook =
StdLlmpEventHook::<<S::Corpus as Corpus>::Input, MT>::new(
self.monitor.take().unwrap(),
)?;
StdLlmpEventHook::<I, MT>::new(self.monitor.take().unwrap())?;
// Yep, broker. Just loop here.
log::info!(
@ -475,7 +464,7 @@ where
return Err(Error::shutting_down());
}
LlmpConnection::IsClient { client } => {
let mgr: LlmpEventManager<EMH, S, SP> = LlmpEventManager::builder()
let mgr: LlmpEventManager<EMH, I, S, SP> = LlmpEventManager::builder()
.hooks(self.hooks)
.build_from_client(
client,

View File

@ -45,11 +45,10 @@ use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
corpus::Corpus,
executors::ExitKind,
inputs::Input,
monitors::UserStats,
state::{HasCorpus, HasExecutions, HasLastReportTime, MaybeHasClientPerfMonitor},
state::{HasExecutions, HasLastReportTime, MaybeHasClientPerfMonitor},
Error, HasMetadata,
};
@ -489,10 +488,10 @@ where
/// Default implementation of [`ProgressReporter::report_progress`] for implementors with the
/// given constraints
pub fn std_report_progress<EM, S>(reporter: &mut EM, state: &mut S) -> Result<(), Error>
pub fn std_report_progress<EM, I, S>(reporter: &mut EM, state: &mut S) -> Result<(), Error>
where
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions + HasLastReportTime + HasCorpus + MaybeHasClientPerfMonitor,
EM: EventFirer<I, S>,
S: HasExecutions + HasLastReportTime + MaybeHasClientPerfMonitor,
{
let executions = *state.executions();
let cur = current_time();

View File

@ -22,7 +22,6 @@ use super::{std_on_restart, ProgressReporter};
#[cfg(all(unix, feature = "std", not(miri)))]
use crate::events::EVENTMGR_SIGHANDLER_STATE;
use crate::{
corpus::Corpus,
events::{
std_maybe_report_progress, std_report_progress, BrokerEventResult, CanSerializeObserver,
Event, EventFirer, EventManagerId, EventProcessor, EventRestarter, HasEventManagerId,
@ -30,7 +29,7 @@ use crate::{
},
monitors::Monitor,
stages::HasCurrentStageId,
state::{HasCorpus, HasExecutions, HasLastReportTime, MaybeHasClientPerfMonitor, Stoppable},
state::{HasExecutions, HasLastReportTime, MaybeHasClientPerfMonitor, Stoppable},
Error, HasMetadata,
};
#[cfg(feature = "std")]
@ -141,13 +140,7 @@ impl<I, MT, S> ProgressReporter<S> for SimpleEventManager<I, MT, S>
where
I: Debug,
MT: Monitor,
S: HasMetadata
+ HasExecutions
+ HasLastReportTime
+ Stoppable
+ HasCorpus
+ MaybeHasClientPerfMonitor,
S::Corpus: Corpus<Input = I>,
S: HasMetadata + HasExecutions + HasLastReportTime + Stoppable + MaybeHasClientPerfMonitor,
{
fn maybe_report_progress(
&mut self,
@ -383,13 +376,7 @@ where
I: Debug,
MT: Monitor,
SP: ShMemProvider,
S: HasExecutions
+ HasMetadata
+ HasLastReportTime
+ Stoppable
+ HasCorpus
+ MaybeHasClientPerfMonitor,
S::Corpus: Corpus<Input = I>,
S: HasExecutions + HasMetadata + HasLastReportTime + Stoppable + MaybeHasClientPerfMonitor,
{
fn maybe_report_progress(
&mut self,
@ -435,7 +422,7 @@ where
/// but can still used shared maps to recover from crashes and timeouts.
pub fn launch(mut monitor: MT, shmem_provider: &mut SP) -> Result<(Option<S>, Self), Error>
where
S: DeserializeOwned + Serialize + HasCorpus + HasSolutions,
S: DeserializeOwned + Serialize + HasSolutions<I>,
MT: Debug,
{
// We start ourself as child process to actually fuzz

View File

@ -42,7 +42,6 @@ use super::{std_maybe_report_progress, std_report_progress, ManagerExit};
#[cfg(all(unix, not(miri)))]
use crate::events::EVENTMGR_SIGHANDLER_STATE;
use crate::{
corpus::Corpus,
events::{
std_on_restart, BrokerEventResult, Event, EventConfig, EventFirer, EventManagerHooksTuple,
EventManagerId, EventProcessor, EventRestarter, HasEventManagerId, ProgressReporter,
@ -53,10 +52,7 @@ use crate::{
monitors::Monitor,
observers::ObserversTuple,
stages::HasCurrentStageId,
state::{
HasCorpus, HasExecutions, HasImported, HasLastReportTime, MaybeHasClientPerfMonitor,
Stoppable,
},
state::{HasExecutions, HasImported, HasLastReportTime, MaybeHasClientPerfMonitor, Stoppable},
Error, HasMetadata,
};
@ -409,7 +405,7 @@ where
}
/// An `EventManager` that forwards all events to other attached via tcp.
pub struct TcpEventManager<EMH, S> {
pub struct TcpEventManager<EMH, I, S> {
/// We send message every `throttle` second
throttle: Option<Duration>,
/// When we sent the last message
@ -425,32 +421,32 @@ pub struct TcpEventManager<EMH, S> {
/// A node will not re-use the observer values sent over TCP
/// from nodes with other configurations.
configuration: EventConfig,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
}
impl<S> TcpEventManager<(), S> {
impl<I, S> TcpEventManager<(), I, S> {
/// Create a builder for [`TcpEventManager`]
#[must_use]
pub fn builder() -> TcpEventManagerBuilder<(), S> {
pub fn builder() -> TcpEventManagerBuilder<(), I, S> {
TcpEventManagerBuilder::new()
}
}
/// Builder for `TcpEventManager`
#[derive(Debug, Copy, Clone)]
pub struct TcpEventManagerBuilder<EMH, S> {
pub struct TcpEventManagerBuilder<EMH, I, S> {
throttle: Option<Duration>,
hooks: EMH,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
}
impl<S> Default for TcpEventManagerBuilder<(), S> {
impl<I, S> Default for TcpEventManagerBuilder<(), I, S> {
fn default() -> Self {
Self::new()
}
}
impl<S> TcpEventManagerBuilder<(), S> {
impl<I, S> TcpEventManagerBuilder<(), I, S> {
/// Set the constructor
#[must_use]
pub fn new() -> Self {
@ -463,7 +459,7 @@ impl<S> TcpEventManagerBuilder<(), S> {
/// Set the hooks
#[must_use]
pub fn hooks<EMH>(self, hooks: EMH) -> TcpEventManagerBuilder<EMH, S> {
pub fn hooks<EMH>(self, hooks: EMH) -> TcpEventManagerBuilder<EMH, I, S> {
TcpEventManagerBuilder {
throttle: self.throttle,
hooks,
@ -472,7 +468,7 @@ impl<S> TcpEventManagerBuilder<(), S> {
}
}
impl<EMH, S> TcpEventManagerBuilder<EMH, S> {
impl<EMH, I, S> TcpEventManagerBuilder<EMH, I, S> {
/// Set the throttle
#[must_use]
pub fn throttle(mut self, throttle: Duration) -> Self {
@ -486,7 +482,7 @@ impl<EMH, S> TcpEventManagerBuilder<EMH, S> {
addr: &A,
client_id: ClientId,
configuration: EventConfig,
) -> Result<TcpEventManager<EMH, S>, Error> {
) -> Result<TcpEventManager<EMH, I, S>, Error> {
let mut tcp = TcpStream::connect(addr)?;
let mut our_client_id_buf = client_id.0.to_le_bytes();
@ -521,7 +517,7 @@ impl<EMH, S> TcpEventManagerBuilder<EMH, S> {
port: u16,
client_id: ClientId,
configuration: EventConfig,
) -> Result<TcpEventManager<EMH, S>, Error> {
) -> Result<TcpEventManager<EMH, I, S>, Error> {
Self::build_from_client(self, &("127.0.0.1", port), client_id, configuration)
}
@ -534,13 +530,13 @@ impl<EMH, S> TcpEventManagerBuilder<EMH, S> {
addr: &A,
env_name: &str,
configuration: EventConfig,
) -> Result<TcpEventManager<EMH, S>, Error> {
) -> Result<TcpEventManager<EMH, I, S>, Error> {
let this_id = ClientId(str::parse::<u32>(&env::var(env_name)?)?);
Self::build_from_client(self, addr, this_id, configuration)
}
}
impl<EMH, S> core::fmt::Debug for TcpEventManager<EMH, S> {
impl<EMH, I, S> core::fmt::Debug for TcpEventManager<EMH, I, S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug_struct = f.debug_struct("TcpEventManager");
let debug = debug_struct.field("tcp", &self.tcp);
@ -554,17 +550,17 @@ impl<EMH, S> core::fmt::Debug for TcpEventManager<EMH, S> {
}
}
impl<EMH, S> Drop for TcpEventManager<EMH, S> {
impl<EMH, I, S> Drop for TcpEventManager<EMH, I, S> {
/// TCP clients will have to wait until their pages are mapped by somebody.
fn drop(&mut self) {
self.await_restart_safe();
}
}
impl<EMH, S> TcpEventManager<EMH, S>
impl<EMH, I, S> TcpEventManager<EMH, I, S>
where
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions + HasMetadata + HasImported + HasCorpus + Stoppable,
EMH: EventManagerHooksTuple<I, S>,
S: HasExecutions + HasMetadata + HasImported + Stoppable,
{
/// Write the client id for a client `EventManager` to env vars
pub fn to_env(&self, env_name: &str) {
@ -578,14 +574,13 @@ where
executor: &mut E,
state: &mut S,
client_id: ClientId,
event: Event<<S::Corpus as Corpus>::Input>,
event: Event<I>,
) -> Result<(), Error>
where
E: Executor<Self, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
E::Observers: Serialize + ObserversTuple<<S::Corpus as Corpus>::Input, S>,
E: Executor<Self, I, S, Z> + HasObservers,
E::Observers: Serialize + ObserversTuple<I, S>,
for<'a> E::Observers: Deserialize<'a>,
Z: ExecutionProcessor<Self, <S::Corpus as Corpus>::Input, E::Observers, S>
+ EvaluatorObservers<E, Self, <S::Corpus as Corpus>::Input, S>,
Z: ExecutionProcessor<Self, I, E::Observers, S> + EvaluatorObservers<E, Self, I, S>,
{
if !self.hooks.pre_exec_all(state, client_id, &event)? {
return Ok(());
@ -630,7 +625,7 @@ where
}
}
impl<EMH, S> TcpEventManager<EMH, S> {
impl<EMH, I, S> TcpEventManager<EMH, I, S> {
/// Send information that this client is exiting.
/// The other side may free up all allocated memory.
/// We are no longer allowed to send anything afterwards.
@ -641,11 +636,10 @@ impl<EMH, S> TcpEventManager<EMH, S> {
}
}
impl<EMH, S> EventFirer<<S::Corpus as Corpus>::Input, S> for TcpEventManager<EMH, S>
impl<EMH, I, S> EventFirer<I, S> for TcpEventManager<EMH, I, S>
where
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus,
<S::Corpus as Corpus>::Input: Serialize,
EMH: EventManagerHooksTuple<I, S>,
I: Serialize,
{
fn should_send(&self) -> bool {
if let Some(throttle) = self.throttle {
@ -655,11 +649,7 @@ where
}
}
fn fire(
&mut self,
_state: &mut S,
event: Event<<S::Corpus as Corpus>::Input>,
) -> Result<(), Error> {
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
let serialized = postcard::to_allocvec(&event)?;
#[cfg(feature = "tcp_compression")]
@ -679,7 +669,7 @@ where
}
}
impl<EMH, S> EventRestarter<S> for TcpEventManager<EMH, S>
impl<EMH, I, S> EventRestarter<S> for TcpEventManager<EMH, I, S>
where
S: HasCurrentStageId,
{
@ -688,16 +678,15 @@ where
}
}
impl<E, EMH, S, Z> EventProcessor<E, S, Z> for TcpEventManager<EMH, S>
impl<E, EMH, I, S, Z> EventProcessor<E, S, Z> for TcpEventManager<EMH, I, S>
where
E: HasObservers + Executor<Self, <S::Corpus as Corpus>::Input, S, Z>,
E::Observers: Serialize + ObserversTuple<<S::Corpus as Corpus>::Input, S>,
E: HasObservers + Executor<Self, I, S, Z>,
E::Observers: Serialize + ObserversTuple<I, S>,
for<'a> E::Observers: Deserialize<'a>,
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions + HasMetadata + HasImported + HasCorpus + Stoppable,
<S::Corpus as Corpus>::Input: DeserializeOwned,
Z: ExecutionProcessor<Self, <S::Corpus as Corpus>::Input, E::Observers, S>
+ EvaluatorObservers<E, Self, <S::Corpus as Corpus>::Input, S>,
EMH: EventManagerHooksTuple<I, S>,
S: HasExecutions + HasMetadata + HasImported + Stoppable,
I: DeserializeOwned,
Z: ExecutionProcessor<Self, I, E::Observers, S> + EvaluatorObservers<E, Self, I, S>,
{
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
// TODO: Get around local event copy by moving handle_in_client
@ -758,7 +747,7 @@ where
}
}
impl<EMH, S> ManagerExit for TcpEventManager<EMH, S> {
impl<EMH, I, S> ManagerExit for TcpEventManager<EMH, I, S> {
/// The TCP client needs to wait until a broker has mapped all pages before shutting down.
/// Otherwise, the OS may already have removed the shared maps.
fn await_restart_safe(&mut self) {
@ -773,11 +762,11 @@ impl<EMH, S> ManagerExit for TcpEventManager<EMH, S> {
}
}
impl<EMH, S> ProgressReporter<S> for TcpEventManager<EMH, S>
impl<EMH, I, S> ProgressReporter<S> for TcpEventManager<EMH, I, S>
where
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: Serialize,
S: HasExecutions + HasMetadata + HasLastReportTime + HasCorpus + MaybeHasClientPerfMonitor,
EMH: EventManagerHooksTuple<I, S>,
I: Serialize,
S: HasExecutions + HasMetadata + HasLastReportTime + MaybeHasClientPerfMonitor,
{
fn maybe_report_progress(
&mut self,
@ -792,7 +781,7 @@ where
}
}
impl<EMH, S> HasEventManagerId for TcpEventManager<EMH, S> {
impl<EMH, I, S> HasEventManagerId for TcpEventManager<EMH, I, S> {
/// Gets the id assigned to this staterestorer.
fn mgr_id(&self) -> EventManagerId {
EventManagerId(self.client_id.0 as usize)
@ -801,23 +790,23 @@ impl<EMH, S> HasEventManagerId for TcpEventManager<EMH, S> {
/// A manager that can restart on the fly, storing states in-between (in `on_restart`)
#[derive(Debug)]
pub struct TcpRestartingEventManager<EMH, S, SP>
pub struct TcpRestartingEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
/// The embedded TCP event manager
tcp_mgr: TcpEventManager<EMH, S>,
tcp_mgr: TcpEventManager<EMH, I, S>,
/// The staterestorer to serialize the state for the next runner
staterestorer: StateRestorer<SP>,
/// Decide if the state restorer must save the serialized state
save_state: bool,
}
impl<EMH, S, SP> ProgressReporter<S> for TcpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> ProgressReporter<S> for TcpRestartingEventManager<EMH, I, S, SP>
where
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
S: HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + MaybeHasClientPerfMonitor,
<S::Corpus as Corpus>::Input: Serialize,
EMH: EventManagerHooksTuple<I, S>,
S: HasMetadata + HasExecutions + HasLastReportTime + MaybeHasClientPerfMonitor,
I: Serialize,
SP: ShMemProvider,
{
fn maybe_report_progress(
@ -833,23 +822,17 @@ where
}
}
impl<EMH, S, SP> EventFirer<<S::Corpus as Corpus>::Input, S>
for TcpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> EventFirer<I, S> for TcpRestartingEventManager<EMH, I, S, SP>
where
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus,
<S::Corpus as Corpus>::Input: Serialize,
EMH: EventManagerHooksTuple<I, S>,
I: Serialize,
SP: ShMemProvider,
{
fn should_send(&self) -> bool {
self.tcp_mgr.should_send()
}
fn fire(
&mut self,
state: &mut S,
event: Event<<S::Corpus as Corpus>::Input>,
) -> Result<(), Error> {
fn fire(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error> {
// Check if we are going to crash in the event, in which case we store our current state for the next runner
self.tcp_mgr.fire(state, event)
}
@ -859,7 +842,7 @@ where
}
}
impl<EMH, S, SP> ManagerExit for TcpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> ManagerExit for TcpRestartingEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -878,10 +861,10 @@ where
}
}
impl<EMH, S, SP> EventRestarter<S> for TcpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> EventRestarter<S> for TcpRestartingEventManager<EMH, I, S, SP>
where
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus + HasExecutions + HasCurrentStageId + Serialize,
EMH: EventManagerHooksTuple<I, S>,
S: HasExecutions + HasCurrentStageId + Serialize,
SP: ShMemProvider,
{
/// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner.
@ -901,17 +884,17 @@ where
}
}
impl<E, EMH, S, SP, Z> EventProcessor<E, S, Z> for TcpRestartingEventManager<EMH, S, SP>
impl<E, EMH, I, S, SP, Z> EventProcessor<E, S, Z> for TcpRestartingEventManager<EMH, I, S, SP>
where
E: HasObservers + Executor<TcpEventManager<EMH, S>, <S::Corpus as Corpus>::Input, S, Z>,
E: HasObservers + Executor<TcpEventManager<EMH, I, S>, I, S, Z>,
for<'a> E::Observers: Deserialize<'a>,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions + HasMetadata + HasImported + HasCorpus + Stoppable,
<S::Corpus as Corpus>::Input: DeserializeOwned,
E::Observers: ObserversTuple<I, S> + Serialize,
EMH: EventManagerHooksTuple<I, S>,
I: DeserializeOwned,
S: HasExecutions + HasMetadata + HasImported + Stoppable,
SP: ShMemProvider,
Z: ExecutionProcessor<TcpEventManager<EMH, S>, <S::Corpus as Corpus>::Input, E::Observers, S>
+ EvaluatorObservers<E, TcpEventManager<EMH, S>, <S::Corpus as Corpus>::Input, S>,
Z: ExecutionProcessor<TcpEventManager<EMH, I, S>, I, E::Observers, S>
+ EvaluatorObservers<E, TcpEventManager<EMH, I, S>, I, S>,
{
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
self.tcp_mgr.process(fuzzer, state, executor)
@ -922,7 +905,7 @@ where
}
}
impl<EMH, S, SP> HasEventManagerId for TcpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> HasEventManagerId for TcpRestartingEventManager<EMH, I, S, SP>
where
SP: ShMemProvider,
{
@ -937,15 +920,13 @@ const _ENV_FUZZER_RECEIVER: &str = "_AFL_ENV_FUZZER_RECEIVER";
/// The tcp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages)
const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = "_AFL_ENV_FUZZER_BROKER_CLIENT";
impl<EMH, S, SP> TcpRestartingEventManager<EMH, S, SP>
impl<EMH, I, S, SP> TcpRestartingEventManager<EMH, I, S, SP>
where
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus,
EMH: EventManagerHooksTuple<I, S>,
SP: ShMemProvider,
//CE: CustomEvent<I>,
{
/// Create a new runner, the executed child doing the actual fuzzing.
pub fn new(tcp_mgr: TcpEventManager<EMH, S>, staterestorer: StateRestorer<SP>) -> Self {
pub fn new(tcp_mgr: TcpEventManager<EMH, I, S>, staterestorer: StateRestorer<SP>) -> Self {
Self {
tcp_mgr,
staterestorer,
@ -955,7 +936,7 @@ where
/// Create a new runner specifying if it must save the serialized state on restart.
pub fn with_save_state(
tcp_mgr: TcpEventManager<EMH, S>,
tcp_mgr: TcpEventManager<EMH, I, S>,
staterestorer: StateRestorer<SP>,
save_state: bool,
) -> Self {
@ -996,21 +977,21 @@ pub enum TcpManagerKind {
/// The [`TcpRestartingEventManager`] is a combination of restarter and runner, that can be used on systems with and without `fork` support.
/// The restarter will spawn a new process each time the child crashes or timeouts.
#[expect(clippy::type_complexity)]
pub fn setup_restarting_mgr_tcp<MT, S>(
pub fn setup_restarting_mgr_tcp<I, MT, S>(
monitor: MT,
broker_port: u16,
configuration: EventConfig,
) -> Result<
(
Option<S>,
TcpRestartingEventManager<(), S, StdShMemProvider>,
TcpRestartingEventManager<(), I, S, StdShMemProvider>,
),
Error,
>
where
MT: Monitor + Clone,
S: HasExecutions + HasMetadata + HasImported + HasCorpus + DeserializeOwned + Stoppable,
<S::Corpus as Corpus>::Input: Input,
S: HasExecutions + HasMetadata + HasImported + DeserializeOwned + Stoppable,
I: Input,
{
TcpRestartingMgr::builder()
.shmem_provider(StdShMemProvider::new()?)
@ -1028,11 +1009,11 @@ where
/// `restarter` and `runner`, that can be used on systems both with and without `fork` support. The
/// `restarter` will start a new process each time the child crashes or times out.
#[derive(TypedBuilder, Debug)]
pub struct TcpRestartingMgr<EMH, MT, S, SP>
pub struct TcpRestartingMgr<EMH, I, MT, S, SP>
where
MT: Monitor,
S: DeserializeOwned,
SP: ShMemProvider + 'static,
MT: Monitor,
{
/// The shared memory provider to use for the broker or client spawned by the restarting
/// manager.
@ -1065,25 +1046,26 @@ where
/// The hooks for `handle_in_client`
hooks: EMH,
#[builder(setter(skip), default = PhantomData)]
phantom_data: PhantomData<S>,
phantom_data: PhantomData<(I, S)>,
}
#[expect(clippy::type_complexity, clippy::too_many_lines)]
impl<EMH, MT, S, SP> TcpRestartingMgr<EMH, MT, S, SP>
impl<EMH, I, MT, S, SP> TcpRestartingMgr<EMH, I, MT, S, SP>
where
EMH: EventManagerHooksTuple<<S::Corpus as Corpus>::Input, S> + Copy + Clone,
SP: ShMemProvider,
S: HasExecutions + HasMetadata + HasImported + HasCorpus + DeserializeOwned + Stoppable,
<S::Corpus as Corpus>::Input: Input,
EMH: EventManagerHooksTuple<I, S> + Copy + Clone,
I: Input,
MT: Monitor + Clone,
SP: ShMemProvider,
S: HasExecutions + HasMetadata + HasImported + DeserializeOwned + Stoppable,
{
/// Launch the restarting manager
pub fn launch(&mut self) -> Result<(Option<S>, TcpRestartingEventManager<EMH, S, SP>), Error> {
pub fn launch(
&mut self,
) -> Result<(Option<S>, TcpRestartingEventManager<EMH, I, S, SP>), Error> {
// We start ourself as child process to actually fuzz
let (staterestorer, _new_shmem_provider, core_id) = if env::var(_ENV_FUZZER_SENDER).is_err()
{
let broker_things = |mut broker: TcpEventBroker<<S::Corpus as Corpus>::Input, MT>,
_remote_broker_addr| {
let broker_things = |mut broker: TcpEventBroker<I, MT>, _remote_broker_addr| {
if let Some(exit_cleanly_after) = self.exit_cleanly_after {
broker.set_exit_cleanly_after(exit_cleanly_after);
}
@ -1097,8 +1079,7 @@ where
let connection = create_nonblocking_listener(("127.0.0.1", self.broker_port));
match connection {
Ok(listener) => {
let event_broker =
TcpEventBroker::<<S::Corpus as Corpus>::Input, MT>::with_listener(
let event_broker = TcpEventBroker::<I, MT>::with_listener(
listener,
self.monitor.take().unwrap(),
);
@ -1129,7 +1110,7 @@ where
}
}
TcpManagerKind::Broker => {
let event_broker = TcpEventBroker::<<S::Corpus as Corpus>::Input, MT>::new(
let event_broker = TcpEventBroker::<I, MT>::new(
format!("127.0.0.1:{}", self.broker_port),
self.monitor.take().unwrap(),
)?;

View File

@ -49,11 +49,10 @@ use super::HasTimeout;
#[cfg(target_os = "linux")]
use crate::executors::hooks::ExecutorHooksTuple;
use crate::{
corpus::Corpus,
executors::{Executor, ExitKind, HasObservers},
inputs::HasTargetBytes,
observers::{ObserversTuple, StdErrObserver, StdOutObserver},
state::{HasCorpus, HasExecutions},
state::HasExecutions,
std::borrow::ToOwned,
Error,
};
@ -280,17 +279,16 @@ where
///
/// Construct a `CommandExecutor` by implementing [`CommandConfigurator`] for a type of your choice and calling [`CommandConfigurator::into_executor`] on it.
/// Instead, you can use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`].
pub struct CommandExecutor<OT, S, T, HT = (), C = Child> {
pub struct CommandExecutor<I, OT, S, T, HT = (), C = Child> {
/// The wrapped command configurer
configurer: T,
/// The observers used by this executor
observers: OT,
hooks: HT,
phantom: PhantomData<S>,
phantom_child: PhantomData<C>,
phantom: PhantomData<(C, I, S)>,
}
impl CommandExecutor<(), (), ()> {
impl CommandExecutor<(), (), (), ()> {
/// Creates a builder for a new [`CommandExecutor`],
/// backed by a [`StdCommandConfigurator`]
/// This is usually the easiest way to construct a [`CommandExecutor`].
@ -308,7 +306,7 @@ impl CommandExecutor<(), (), ()> {
}
}
impl<OT, S, T, HT, C> Debug for CommandExecutor<OT, S, T, HT, C>
impl<I, OT, S, T, HT, C> Debug for CommandExecutor<I, OT, S, T, HT, C>
where
T: Debug,
OT: Debug,
@ -323,7 +321,7 @@ where
}
}
impl<OT, S, T, HT, C> CommandExecutor<OT, S, T, HT, C> {
impl<I, OT, S, T, HT, C> CommandExecutor<I, OT, S, T, HT, C> {
/// Accesses the inner value
pub fn inner(&mut self) -> &mut T {
&mut self.configurer
@ -331,17 +329,13 @@ impl<OT, S, T, HT, C> CommandExecutor<OT, S, T, HT, C> {
}
// this only works on unix because of the reliance on checking the process signal for detecting OOM
impl<OT, S, T> CommandExecutor<OT, S, T>
impl<I, OT, S, T> CommandExecutor<I, OT, S, T>
where
S: HasExecutions + HasCorpus,
T: CommandConfigurator<<S::Corpus as Corpus>::Input> + Debug,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions,
T: CommandConfigurator<I> + Debug,
OT: ObserversTuple<I, S>,
{
fn execute_input_with_command(
&mut self,
state: &mut S,
input: &<S::Corpus as Corpus>::Input,
) -> Result<ExitKind, Error> {
fn execute_input_with_command(&mut self, state: &mut S, input: &I) -> Result<ExitKind, Error> {
use wait_timeout::ChildExt;
*state.executions_mut() += 1;
@ -391,28 +385,27 @@ where
}
}
impl<EM, OT, S, T, Z> Executor<EM, <S::Corpus as Corpus>::Input, S, Z> for CommandExecutor<OT, S, T>
impl<EM, I, OT, S, T, Z> Executor<EM, I, S, Z> for CommandExecutor<I, OT, S, T>
where
S: HasExecutions + HasCorpus,
T: CommandConfigurator<<S::Corpus as Corpus>::Input> + Debug,
OT: MatchName + ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions,
T: CommandConfigurator<I> + Debug,
OT: MatchName + ObserversTuple<I, S>,
{
fn run_target(
&mut self,
_fuzzer: &mut Z,
state: &mut S,
_mgr: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
) -> Result<ExitKind, Error> {
self.execute_input_with_command(state, input)
}
}
// this only works on unix because of the reliance on checking the process signal for detecting OOM
impl<OT, S, T> HasTimeout for CommandExecutor<OT, S, T>
impl<I, OT, S, T> HasTimeout for CommandExecutor<I, OT, S, T>
where
S: HasCorpus,
T: CommandConfigurator<<S::Corpus as Corpus>::Input>,
T: CommandConfigurator<I>,
{
#[inline]
fn timeout(&self) -> Duration {
@ -426,13 +419,12 @@ where
}
#[cfg(target_os = "linux")]
impl<EM, OT, S, T, Z, HT> Executor<EM, <S::Corpus as Corpus>::Input, S, Z>
for CommandExecutor<OT, S, T, HT, Pid>
impl<EM, I, OT, S, T, Z, HT> Executor<EM, I, S, Z> for CommandExecutor<I, OT, S, T, HT, Pid>
where
S: HasCorpus + HasExecutions,
T: CommandConfigurator<<S::Corpus as Corpus>::Input, Pid> + Debug,
OT: MatchName + ObserversTuple<<S::Corpus as Corpus>::Input, S>,
HT: ExecutorHooksTuple<<S::Corpus as Corpus>::Input, S>,
HT: ExecutorHooksTuple<I, S>,
OT: MatchName + ObserversTuple<I, S>,
S: HasExecutions,
T: CommandConfigurator<I, Pid> + Debug,
{
/// Linux specific low level implementation, to directly handle `fork`, `exec` and use linux
/// `ptrace`
@ -444,7 +436,7 @@ where
_fuzzer: &mut Z,
state: &mut S,
_mgr: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
) -> Result<ExitKind, Error> {
*state.executions_mut() += 1;
@ -502,10 +494,9 @@ where
}
}
impl<OT, S, T, HT, C> HasObservers for CommandExecutor<OT, S, T, HT, C>
impl<I, OT, S, T, HT, C> HasObservers for CommandExecutor<I, OT, S, T, HT, C>
where
S: HasCorpus,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
OT: ObserversTuple<I, S>,
{
type Observers = OT;
@ -683,14 +674,13 @@ impl CommandExecutorBuilder {
}
/// Builds the `CommandExecutor`
pub fn build<OT, S>(
pub fn build<I, OT, S>(
&self,
observers: OT,
) -> Result<CommandExecutor<OT, S, StdCommandConfigurator>, Error>
) -> Result<CommandExecutor<I, OT, S, StdCommandConfigurator>, Error>
where
S: HasCorpus,
<S::Corpus as Corpus>::Input: HasTargetBytes,
OT: MatchName + ObserversTuple<<S::Corpus as Corpus>::Input, S>,
I: HasTargetBytes,
OT: MatchName + ObserversTuple<I, S>,
{
let Some(program) = &self.program else {
return Err(Error::illegal_argument(
@ -737,9 +727,12 @@ impl CommandExecutorBuilder {
timeout: self.timeout,
command,
};
Ok(<StdCommandConfigurator as CommandConfigurator<
<S::Corpus as Corpus>::Input,
>>::into_executor::<OT, S>(configurator, observers))
Ok(
<StdCommandConfigurator as CommandConfigurator<I>>::into_executor::<OT, S>(
configurator,
observers,
),
)
}
}
@ -747,7 +740,7 @@ impl CommandExecutorBuilder {
/// # Example
/// ```
/// use std::{io::Write, process::{Stdio, Command, Child}, time::Duration};
/// use libafl::{Error, corpus::Corpus, inputs::{BytesInput, HasTargetBytes, Input}, executors::{Executor, command::CommandConfigurator}, state::{HasCorpus, HasExecutions}};
/// use libafl::{Error, corpus::Corpus, inputs::{BytesInput, HasTargetBytes, Input}, executors::{Executor, command::CommandConfigurator}, state::{HasExecutions}};
/// use libafl_bolts::AsSlice;
/// #[derive(Debug)]
/// struct MyExecutor;
@ -779,8 +772,7 @@ impl CommandExecutorBuilder {
///
/// fn make_executor<EM, S, Z>() -> impl Executor<EM, BytesInput, S, Z>
/// where
/// S: HasCorpus + HasExecutions,
/// S::Corpus: Corpus<Input = BytesInput>
/// S: HasExecutions,
/// {
/// MyExecutor.into_executor(())
/// }
@ -816,13 +808,12 @@ pub trait CommandConfigurator<I, C = Child>: Sized {
}
/// Create an `Executor` from this `CommandConfigurator`.
fn into_executor<OT, S>(self, observers: OT) -> CommandExecutor<OT, S, Self, (), C> {
fn into_executor<OT, S>(self, observers: OT) -> CommandExecutor<I, OT, S, Self, (), C> {
CommandExecutor {
configurer: self,
observers,
hooks: (),
phantom: PhantomData,
phantom_child: PhantomData,
}
}
@ -831,13 +822,12 @@ pub trait CommandConfigurator<I, C = Child>: Sized {
self,
observers: OT,
hooks: HT,
) -> CommandExecutor<OT, S, Self, HT, C> {
) -> CommandExecutor<I, OT, S, Self, HT, C> {
CommandExecutor {
configurer: self,
observers,
hooks,
phantom: PhantomData,
phantom_child: PhantomData,
}
}
}
@ -890,7 +880,7 @@ mod tests {
executor
.run_target(
&mut NopFuzzer::new(),
&mut NopState::new(),
&mut NopState::<NopInput>::new(),
&mut mgr,
&BytesInput::new(b"test".to_vec()),
)

View File

@ -19,23 +19,21 @@ use serde::{Deserialize, Serialize};
use super::HasTimeout;
use crate::{
corpus::Corpus,
executors::{Executor, ExitKind, HasObservers},
observers::{DifferentialObserversTuple, ObserversTuple},
state::HasCorpus,
Error,
};
/// A [`DiffExecutor`] wraps a primary executor, forwarding its methods, and a secondary one
#[derive(Debug)]
pub struct DiffExecutor<A, B, DOT, OTA, OTB, S> {
pub struct DiffExecutor<A, B, DOT, I, OTA, OTB, S> {
primary: A,
secondary: B,
observers: UnsafeCell<ProxyObserversTuple<OTA, OTB, DOT>>,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
}
impl<A, B, DOT, OTA, OTB, S> DiffExecutor<A, B, DOT, OTA, OTB, S> {
impl<A, B, DOT, I, OTA, OTB, S> DiffExecutor<A, B, DOT, I, OTA, OTB, S> {
/// Create a new `DiffExecutor`, wrapping the given `executor`s.
pub fn new(primary: A, secondary: B, observers: DOT) -> Self {
Self {
@ -61,23 +59,21 @@ impl<A, B, DOT, OTA, OTB, S> DiffExecutor<A, B, DOT, OTA, OTB, S> {
}
}
impl<A, B, DOT, EM, S, Z> Executor<EM, <S::Corpus as Corpus>::Input, S, Z>
for DiffExecutor<A, B, DOT, A::Observers, B::Observers, S>
impl<A, B, DOT, EM, I, S, Z> Executor<EM, I, S, Z>
for DiffExecutor<A, B, DOT, I, A::Observers, B::Observers, S>
where
A: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
B: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
<A as HasObservers>::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
<B as HasObservers>::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
DOT: DifferentialObserversTuple<A::Observers, B::Observers, <S::Corpus as Corpus>::Input, S>
+ MatchName,
S: HasCorpus,
A: Executor<EM, I, S, Z> + HasObservers,
B: Executor<EM, I, S, Z> + HasObservers,
<A as HasObservers>::Observers: ObserversTuple<I, S>,
<B as HasObservers>::Observers: ObserversTuple<I, S>,
DOT: DifferentialObserversTuple<A::Observers, B::Observers, I, S> + MatchName,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
) -> Result<ExitKind, Error> {
self.observers(); // update in advance
let observers = self.observers.get_mut();
@ -117,7 +113,7 @@ where
}
}
impl<A, B, DOT, OTA, OTB, S> HasTimeout for DiffExecutor<A, B, DOT, OTA, OTB, S>
impl<A, B, DOT, I, OTA, OTB, S> HasTimeout for DiffExecutor<A, B, DOT, I, OTA, OTB, S>
where
A: HasTimeout,
B: HasTimeout,
@ -233,14 +229,13 @@ impl<A, B, DOT> ProxyObserversTuple<A, B, DOT> {
}
}
impl<A, B, DOT, OTA, OTB, S> HasObservers for DiffExecutor<A, B, DOT, OTA, OTB, S>
impl<A, B, DOT, I, OTA, OTB, S> HasObservers for DiffExecutor<A, B, DOT, I, OTA, OTB, S>
where
A: HasObservers<Observers = OTA>,
B: HasObservers<Observers = OTB>,
DOT: DifferentialObserversTuple<OTA, OTB, <S::Corpus as Corpus>::Input, S> + MatchName,
OTA: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
OTB: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus,
DOT: DifferentialObserversTuple<OTA, OTB, I, S> + MatchName,
OTA: ObserversTuple<I, S>,
OTB: ObserversTuple<I, S>,
{
type Observers = ProxyObserversTuple<OTA, OTB, DOT>;

View File

@ -43,12 +43,11 @@ use crate::observers::{
get_asan_runtime_flags, get_asan_runtime_flags_with_log_path, AsanBacktraceObserver,
};
use crate::{
corpus::Corpus,
executors::{Executor, ExitKind, HasObservers},
inputs::{BytesInput, Input, NopTargetBytesConverter, TargetBytesConverter},
inputs::{BytesInput, HasTargetBytes, Input, NopTargetBytesConverter, TargetBytesConverter},
mutators::Tokens,
observers::{MapObserver, Observer, ObserversTuple},
state::{HasCorpus, HasExecutions},
state::HasExecutions,
Error,
};
@ -607,7 +606,7 @@ impl Forkserver {
///
/// Shared memory feature is also available, but you have to set things up in your code.
/// Please refer to AFL++'s docs. <https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md>
pub struct ForkserverExecutor<TC, OT, S, SP>
pub struct ForkserverExecutor<I, OT, S, SP, TC>
where
SP: ShMemProvider,
{
@ -619,7 +618,7 @@ where
forkserver: Forkserver,
observers: OT,
map: Option<SP::ShMem>,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
map_size: Option<usize>,
min_input_size: usize,
max_input_size: usize,
@ -629,7 +628,7 @@ where
crash_exitcode: Option<i8>,
}
impl<TC, OT, S, SP> Debug for ForkserverExecutor<TC, OT, S, SP>
impl<I, OT, S, SP, TC> Debug for ForkserverExecutor<I, OT, S, SP, TC>
where
TC: Debug,
OT: Debug,
@ -649,7 +648,7 @@ where
}
}
impl ForkserverExecutor<(), (), (), UnixShMemProvider> {
impl ForkserverExecutor<(), (), (), UnixShMemProvider, ()> {
/// Builder for `ForkserverExecutor`
#[must_use]
pub fn builder(
@ -659,12 +658,11 @@ impl ForkserverExecutor<(), (), (), UnixShMemProvider> {
}
}
impl<TC, OT, S, SP> ForkserverExecutor<TC, OT, S, SP>
impl<I, OT, S, SP, TC> ForkserverExecutor<I, OT, S, SP, TC>
where
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus,
OT: ObserversTuple<I, S>,
SP: ShMemProvider,
TC: TargetBytesConverter,
TC: TargetBytesConverter<I>,
{
/// The `target` binary that's going to run.
pub fn target(&self) -> &OsString {
@ -698,7 +696,7 @@ where
/// Execute input and increase the execution counter.
#[inline]
fn execute_input(&mut self, state: &mut S, input: &TC::Input) -> Result<ExitKind, Error>
fn execute_input(&mut self, state: &mut S, input: &I) -> Result<ExitKind, Error>
where
S: HasExecutions,
{
@ -709,7 +707,7 @@ where
/// Execute input, but side-step the execution counter.
#[inline]
fn execute_input_uncounted(&mut self, input: &TC::Input) -> Result<ExitKind, Error> {
fn execute_input_uncounted(&mut self, input: &I) -> Result<ExitKind, Error> {
let mut exit_kind = ExitKind::Ok;
let last_run_timed_out = self.forkserver.last_run_timed_out_raw();
@ -839,13 +837,14 @@ where
/// in case no input file is specified.
/// If `debug_child` is set, the child will print to `stdout`/`stderr`.
#[expect(clippy::pedantic)]
pub fn build<OT, S>(mut self, observers: OT) -> Result<ForkserverExecutor<TC, OT, S, SP>, Error>
pub fn build<I, OT, S>(
mut self,
observers: OT,
) -> Result<ForkserverExecutor<I, OT, S, SP, TC>, Error>
where
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus,
<S::Corpus as Corpus>::Input: Input,
TC: TargetBytesConverter,
OT: ObserversTuple<I, S>,
SP: ShMemProvider,
TC: TargetBytesConverter<I>,
{
let (forkserver, input_file, map) = self.build_helper()?;
@ -901,18 +900,17 @@ where
}
/// Builds `ForkserverExecutor` downsizing the coverage map to fit exaclty the AFL++ map size.
#[expect(clippy::pedantic)]
pub fn build_dynamic_map<A, MO, OT, S>(
#[expect(clippy::pedantic, clippy::type_complexity)]
pub fn build_dynamic_map<A, MO, OT, I, S>(
mut self,
mut map_observer: A,
other_observers: OT,
) -> Result<ForkserverExecutor<TC, (A, OT), S, SP>, Error>
) -> Result<ForkserverExecutor<I, (A, OT), S, SP, TC>, Error>
where
A: Observer<I, S> + AsMut<MO>,
I: Input + HasTargetBytes,
MO: MapObserver + Truncate, // TODO maybe enforce Entry = u8 for the cov map
A: Observer<<S::Corpus as Corpus>::Input, S> + AsMut<MO>,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Prepend<MO>,
<S::Corpus as Corpus>::Input: Input,
S: HasCorpus,
OT: ObserversTuple<I, S> + Prepend<MO>,
SP: ShMemProvider,
{
let (forkserver, input_file, map) = self.build_helper()?;
@ -1540,7 +1538,7 @@ impl<'a, TC> ForkserverExecutorBuilder<'a, TC, UnixShMemProvider> {
impl<'a, TC, SP> ForkserverExecutorBuilder<'a, TC, SP> {
/// Shmem provider for forkserver's shared memory testcase feature.
pub fn target_bytes_converter<TC2: TargetBytesConverter>(
pub fn target_bytes_converter<I, TC2: TargetBytesConverter<I>>(
self,
target_bytes_converter: TC2,
) -> ForkserverExecutorBuilder<'a, TC2, SP> {
@ -1579,13 +1577,12 @@ impl Default
}
}
impl<EM, TC, OT, S, SP, Z> Executor<EM, <S::Corpus as Corpus>::Input, S, Z>
for ForkserverExecutor<TC, OT, S, SP>
impl<EM, I, OT, S, SP, TC, Z> Executor<EM, I, S, Z> for ForkserverExecutor<I, OT, S, SP, TC>
where
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
OT: ObserversTuple<I, S>,
SP: ShMemProvider,
S: HasCorpus + HasExecutions,
TC: TargetBytesConverter<Input = <S::Corpus as Corpus>::Input>,
S: HasExecutions,
TC: TargetBytesConverter<I>,
{
#[inline]
fn run_target(
@ -1593,13 +1590,13 @@ where
_fuzzer: &mut Z,
state: &mut S,
_mgr: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
) -> Result<ExitKind, Error> {
self.execute_input(state, input)
}
}
impl<TC, OT, S, SP> HasTimeout for ForkserverExecutor<TC, OT, S, SP>
impl<I, OT, S, SP, TC> HasTimeout for ForkserverExecutor<I, OT, S, SP, TC>
where
SP: ShMemProvider,
{
@ -1614,10 +1611,9 @@ where
}
}
impl<TC, OT, S, SP> HasObservers for ForkserverExecutor<TC, OT, S, SP>
impl<I, OT, S, SP, TC> HasObservers for ForkserverExecutor<I, OT, S, SP, TC>
where
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus,
OT: ObserversTuple<I, S>,
SP: ShMemProvider,
{
type Observers = OT;
@ -1677,7 +1673,7 @@ mod tests {
.coverage_map_size(MAP_SIZE)
.debug_child(false)
.shmem_provider(&mut shmem_provider)
.build::<_, NopCorpus<BytesInput>>(tuple_list!(edges_observer));
.build::<BytesInput, _, NopCorpus<BytesInput>>(tuple_list!(edges_observer));
// Since /usr/bin/echo is not a instrumented binary file, the test will just check if the forkserver has failed at the initial handshake
let result = match executor {

View File

@ -23,15 +23,15 @@ use windows::Win32::System::Threading::{CRITICAL_SECTION, PTP_TIMER};
use crate::executors::hooks::timer::TimerStruct;
#[cfg(all(unix, feature = "std"))]
use crate::executors::hooks::unix::unix_signal_handler;
#[cfg(any(unix, windows))]
use crate::{corpus::Corpus, inputs::Input, observers::ObserversTuple, state::HasCurrentTestcase};
use crate::{
events::{EventFirer, EventRestarter},
executors::{hooks::ExecutorHook, inprocess::HasInProcessHooks, Executor, HasObservers},
feedbacks::Feedback,
state::{HasCorpus, HasExecutions, HasSolutions},
state::{HasExecutions, HasSolutions},
Error, HasObjective,
};
#[cfg(any(unix, windows))]
use crate::{inputs::Input, observers::ObserversTuple, state::HasCurrentTestcase};
/// The inmem executor's handlers.
#[expect(missing_debug_implementations)]
@ -185,10 +185,7 @@ impl<I, S> HasTimeout for InProcessHooks<I, S> {
}
}
impl<I, S> ExecutorHook<I, S> for InProcessHooks<I, S>
where
S: HasCorpus,
{
impl<I, S> ExecutorHook<I, S> for InProcessHooks<I, S> {
fn init(&mut self, _state: &mut S) {}
/// Call before running a target.
fn pre_exec(&mut self, _state: &mut S, _input: &I) {
@ -223,10 +220,9 @@ impl<I, S> InProcessHooks<I, S> {
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
I: Input + Clone,
S::Solutions: Corpus<Input = I>,
{
// # Safety
// We get a pointer to `GLOBAL_STATE` that will be initialized at this point in time.
@ -268,8 +264,7 @@ impl<I, S> InProcessHooks<I, S> {
EM: EventFirer<I, S> + EventRestarter<S>,
I: Input + Clone,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase,
S::Solutions: Corpus<Input = I>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
{
let ret;
@ -330,7 +325,7 @@ impl<I, S> InProcessHooks<I, S> {
E: Executor<EM, I, S, Z> + HasObservers + HasInProcessHooks<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCorpus,
S: HasExecutions + HasSolutions<I>,
Z: HasObjective<Objective = OF>,
{
#[cfg_attr(miri, allow(unused_variables))]

View File

@ -3,10 +3,10 @@ use alloc::vec::Vec;
use core::{
ffi::c_void,
marker::PhantomData,
mem::transmute,
ptr::null,
sync::atomic::{compiler_fence, Ordering},
};
use std::intrinsics::transmute;
#[cfg(not(miri))]
use libafl_bolts::os::unix_signals::setup_signal_handler;
@ -21,7 +21,6 @@ use crate::{
HasObservers,
},
observers::ObserversTuple,
state::HasCorpus,
Error,
};
@ -35,10 +34,7 @@ pub struct InChildProcessHooks<I, S> {
phantom: PhantomData<(I, S)>,
}
impl<I, S> ExecutorHook<I, S> for InChildProcessHooks<I, S>
where
S: HasCorpus,
{
impl<I, S> ExecutorHook<I, S> for InChildProcessHooks<I, S> {
/// Init this hook
fn init(&mut self, _state: &mut S) {}
@ -61,7 +57,6 @@ impl<I, S> InChildProcessHooks<I, S> {
where
E: HasObservers,
E::Observers: ObserversTuple<I, S>,
S: HasCorpus,
{
#[cfg_attr(miri, allow(unused_variables, unused_unsafe))]
unsafe {

View File

@ -10,7 +10,7 @@ use num_traits::SaturatingAdd;
use serde::Serialize;
use typed_builder::TypedBuilder;
use crate::{corpus::Corpus, executors::hooks::ExecutorHook, state::HasCorpus, Error};
use crate::{executors::hooks::ExecutorHook, Error};
/// Info of a binary's section that can be used during `Intel PT` traces decoding
#[derive(Debug, Clone, PartialEq, Eq)]
@ -36,18 +36,18 @@ pub struct IntelPTHook<T> {
map_len: usize,
}
impl<S, T> ExecutorHook<<S::Corpus as Corpus>::Input, S> for IntelPTHook<T>
impl<I, S, T> ExecutorHook<I, S> for IntelPTHook<T>
where
S: Serialize + HasCorpus,
S: Serialize,
T: SaturatingAdd + From<u8> + Debug,
{
fn init(&mut self, _state: &mut S) {}
fn pre_exec(&mut self, _state: &mut S, _input: &<S::Corpus as Corpus>::Input) {
fn pre_exec(&mut self, _state: &mut S, _input: &I) {
self.intel_pt.enable_tracing().unwrap();
}
fn post_exec(&mut self, _state: &mut S, _input: &<S::Corpus as Corpus>::Input) {
fn post_exec(&mut self, _state: &mut S, _input: &I) {
let pt = &mut self.intel_pt;
pt.disable_tracing().unwrap();

View File

@ -9,7 +9,6 @@ pub mod unix_signal_handler {
use libc::siginfo_t;
use crate::{
corpus::Corpus,
events::{EventFirer, EventRestarter},
executors::{
common_signals,
@ -21,7 +20,7 @@ pub mod unix_signal_handler {
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasSolutions},
state::{HasCurrentTestcase, HasExecutions, HasSolutions},
};
pub(crate) type HandlerFuncPtr = unsafe fn(
@ -83,8 +82,7 @@ pub mod unix_signal_handler {
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCurrentTestcase + HasCorpus,
S::Solutions: Corpus<Input = I>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
I: Input + Clone,
{
@ -133,10 +131,9 @@ pub mod unix_signal_handler {
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCurrentTestcase + HasCorpus,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
I: Input + Clone,
S::Solutions: Corpus<Input = I>,
{
// this stuff is for batch timeout
if !data.executor_ptr.is_null()
@ -190,10 +187,9 @@ pub mod unix_signal_handler {
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
I: Input + Clone,
S::Solutions: Corpus<Input = I>,
{
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
let _context = _context.map(|p| {

View File

@ -9,7 +9,6 @@ pub mod windows_asan_handler {
};
use crate::{
corpus::Corpus,
events::{EventFirer, EventRestarter},
executors::{
hooks::inprocess::GLOBAL_STATE, inprocess::run_observers_and_save_state, Executor,
@ -19,7 +18,7 @@ pub mod windows_asan_handler {
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasSolutions},
state::{HasCurrentTestcase, HasExecutions, HasSolutions},
};
/// # Safety
@ -31,8 +30,7 @@ pub mod windows_asan_handler {
EM: EventFirer<I, S> + EventRestarter<S>,
I: Input + Clone,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCurrentTestcase + HasCorpus,
S::Solutions: Corpus<Input = I>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
{
let data = &raw mut GLOBAL_STATE;
@ -127,7 +125,6 @@ pub mod windows_exception_handler {
};
use crate::{
corpus::Corpus,
events::{EventFirer, EventRestarter},
executors::{
hooks::inprocess::{HasTimeout, InProcessExecutorHandlerData, GLOBAL_STATE},
@ -138,7 +135,7 @@ pub mod windows_exception_handler {
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasSolutions},
state::{HasCurrentTestcase, HasExecutions, HasSolutions},
};
pub(crate) type HandlerFuncPtr =
@ -189,8 +186,7 @@ pub mod windows_exception_handler {
EM: EventFirer<I, S> + EventRestarter<S>,
I: Input + Clone,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCurrentTestcase + HasCorpus,
S::Solutions: Corpus<Input = I>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
{
let old_hook = panic::take_hook();
@ -252,9 +248,8 @@ pub mod windows_exception_handler {
EM: EventFirer<I, S> + EventRestarter<S>,
I: Input + Clone,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCurrentTestcase + HasCorpus,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
S::Solutions: Corpus<Input = I>,
{
let data: &mut InProcessExecutorHandlerData =
&mut *(global_state as *mut InProcessExecutorHandlerData);
@ -322,9 +317,8 @@ pub mod windows_exception_handler {
EM: EventFirer<I, S> + EventRestarter<S>,
I: Input + Clone,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
S::Solutions: Corpus<Input = I>,
{
// Have we set a timer_before?
if data.ptp_timer.is_some() {

View File

@ -15,7 +15,6 @@ use crate::executors::hooks::inprocess::HasTimeout;
#[cfg(all(windows, feature = "std"))]
use crate::executors::hooks::inprocess::HasTimeout;
use crate::{
corpus::Corpus,
events::{EventFirer, EventRestarter},
executors::{
hooks::{
@ -29,7 +28,7 @@ use crate::{
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasSolutions},
state::{HasCurrentTestcase, HasExecutions, HasSolutions},
Error,
};
@ -69,7 +68,6 @@ impl<HT, I, OT, S> HasObservers for GenericInProcessExecutorInner<HT, I, OT, S>
impl<HT, I, OT, S> GenericInProcessExecutorInner<HT, I, OT, S>
where
OT: ObserversTuple<I, S>,
S: HasCorpus,
{
/// This function marks the boundary between the fuzzer and the target
///
@ -132,7 +130,7 @@ impl<HT, I, OT, S> GenericInProcessExecutorInner<HT, I, OT, S>
where
HT: ExecutorHooksTuple<I, S>,
OT: ObserversTuple<I, S>,
S: HasCorpus + HasExecutions + HasSolutions,
S: HasExecutions + HasSolutions<I>,
{
/// Create a new in mem executor with the default timeout (5 sec)
pub fn generic<E, EM, OF, Z>(
@ -148,8 +146,7 @@ where
EM: EventFirer<I, S> + EventRestarter<S>,
I: Input + Clone,
OF: Feedback<EM, I, E::Observers, S>,
S: HasCurrentTestcase + HasCorpus + HasSolutions,
S::Solutions: Corpus<Input = I>,
S: HasCurrentTestcase<I> + HasSolutions<I>,
Z: HasObjective<Objective = OF>,
{
Self::with_timeout_generic::<E, EM, OF, Z>(
@ -178,8 +175,7 @@ where
EM: EventFirer<I, S> + EventRestarter<S>,
I: Input + Clone,
OF: Feedback<EM, I, E::Observers, S>,
S: HasCurrentTestcase + HasCorpus + HasSolutions,
S::Solutions: Corpus<Input = I>,
S: HasCurrentTestcase<I> + HasSolutions<I>,
Z: HasObjective<Objective = OF>,
{
let mut me = Self::with_timeout_generic::<E, EM, OF, Z>(
@ -210,9 +206,8 @@ where
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasCurrentTestcase + HasCorpus + HasSolutions,
S: HasCurrentTestcase<I> + HasSolutions<I>,
Z: HasObjective<Objective = OF>,
S::Solutions: Corpus<Input = I>,
I: Input + Clone,
{
let default = InProcessHooks::new::<E, EM, OF, Z>(timeout)?;

View File

@ -77,7 +77,7 @@ where
impl<EM, H, HB, HT, I, OT, S, Z> Executor<EM, I, S, Z>
for GenericInProcessExecutor<H, HB, HT, I, OT, S>
where
S: HasCorpus + HasExecutions,
S: HasExecutions,
OT: ObserversTuple<I, S>,
HT: ExecutorHooksTuple<I, S>,
HB: BorrowMut<H>,
@ -124,8 +124,7 @@ impl<'a, H, I, OT, S> InProcessExecutor<'a, H, I, OT, S>
where
H: FnMut(&I) -> ExitKind + Sized,
OT: ObserversTuple<I, S>,
S: HasCorpus + HasCurrentTestcase + HasExecutions + HasSolutions,
S::Solutions: Corpus<Input = I>,
S: HasCurrentTestcase<I> + HasExecutions + HasSolutions<I>,
I: Input,
{
/// Create a new in mem executor with the default timeout (5 sec)
@ -227,8 +226,7 @@ where
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<I, S>,
OT: ObserversTuple<I, S>,
S: HasCorpus + HasCurrentTestcase + HasExecutions + HasSolutions,
S::Solutions: Corpus<Input = I>,
S: HasCurrentTestcase<I> + HasExecutions + HasSolutions<I>,
I: Input,
{
/// Create a new in mem executor with the default timeout (5 sec)
@ -381,10 +379,9 @@ pub fn run_observers_and_save_state<E, EM, I, OF, S, Z>(
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase,
S: HasExecutions + HasSolutions<I> + HasCorpus<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
I: Input + Clone,
S::Solutions: Corpus<Input = I>,
{
let mut observers = executor.observers_mut();
@ -443,9 +440,8 @@ where
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
I: Input + Clone,
S::Solutions: Corpus<Input = I>,
Z: HasObjective<Objective = OF> + ExecutionProcessor<EM, I, E::Observers, S>,
{
let data = &raw mut GLOBAL_STATE;

View File

@ -11,7 +11,6 @@ use core::{
use libafl_bolts::tuples::{tuple_list, RefIndexable};
use crate::{
corpus::Corpus,
events::{EventFirer, EventRestarter},
executors::{
hooks::{inprocess::InProcessHooks, ExecutorHooksTuple},
@ -22,7 +21,7 @@ use crate::{
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasSolutions},
state::{HasCurrentTestcase, HasExecutions, HasSolutions},
Error,
};
@ -74,7 +73,7 @@ where
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<I, S>,
OT: ObserversTuple<I, S>,
S: HasCorpus + HasExecutions,
S: HasExecutions,
{
fn run_target(
&mut self,
@ -106,7 +105,6 @@ where
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<I, S>,
OT: ObserversTuple<I, S>,
S: HasCorpus,
{
type Observers = OT;
#[inline]
@ -124,9 +122,8 @@ impl<'a, H, I, OT, S, ES> StatefulInProcessExecutor<'a, H, I, OT, S, ES>
where
H: FnMut(&mut ES, &mut S, &I) -> ExitKind + Sized,
OT: ObserversTuple<I, S>,
S: HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
I: Clone + Input,
S::Solutions: Corpus<Input = I>,
{
/// Create a new in mem executor with the default timeout (5 sec)
pub fn new<EM, OF, Z>(
@ -246,8 +243,7 @@ where
HT: ExecutorHooksTuple<I, S>,
I: Input + Clone,
OT: ObserversTuple<I, S>,
S: HasCorpus + HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase,
S::Solutions: Corpus<Input = I>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
{
/// Create a new in mem executor with the default timeout (5 sec)
pub fn generic<EM, OF, Z>(

View File

@ -28,7 +28,6 @@ use crate::{
ExitKind, HasObservers,
},
observers::ObserversTuple,
state::HasCorpus,
Error,
};
@ -108,7 +107,6 @@ fn parse_itimerval(timeout: Duration) -> Itimerval {
impl<EM, HT, I, OT, S, SP, Z> GenericInProcessForkExecutorInner<HT, I, OT, S, SP, EM, Z>
where
HT: ExecutorHooksTuple<I, S>,
S: HasCorpus,
SP: ShMemProvider,
OT: ObserversTuple<I, S>,
{
@ -201,7 +199,6 @@ impl<HT, I, OT, S, SP, EM, Z> GenericInProcessForkExecutorInner<HT, I, OT, S, SP
where
HT: ExecutorHooksTuple<I, S>,
OT: ObserversTuple<I, S>,
S: HasCorpus,
{
#[inline]
/// This function marks the boundary between the fuzzer and the target.

View File

@ -19,7 +19,7 @@ use crate::{
inprocess_fork::inner::GenericInProcessForkExecutorInner, Executor, ExitKind, HasObservers,
},
observers::ObserversTuple,
state::{HasCorpus, HasExecutions},
state::HasExecutions,
Error,
};
@ -45,7 +45,6 @@ pub type InProcessForkExecutor<'a, H, I, OT, S, SP, EM, Z> =
impl<'a, H, I, OT, S, SP, EM, Z> InProcessForkExecutor<'a, H, I, OT, S, SP, EM, Z>
where
OT: ObserversTuple<I, S>,
S: HasCorpus,
{
/// The constructor for `InProcessForkExecutor`
pub fn new(
@ -107,7 +106,7 @@ impl<EM, H, HT, I, OT, S, SP, Z> Executor<EM, I, S, Z>
for GenericInProcessForkExecutor<'_, H, HT, I, OT, S, SP, EM, Z>
where
H: FnMut(&I) -> ExitKind + Sized,
S: HasCorpus + HasExecutions,
S: HasExecutions,
SP: ShMemProvider,
HT: ExecutorHooksTuple<I, S>,
OT: ObserversTuple<I, S>,
@ -146,7 +145,6 @@ impl<'a, H, HT, I, OT, S, SP, EM, Z> GenericInProcessForkExecutor<'a, H, HT, I,
where
HT: ExecutorHooksTuple<I, S>,
OT: ObserversTuple<I, S>,
S: HasCorpus,
{
/// Creates a new [`GenericInProcessForkExecutor`] with custom hooks
#[expect(clippy::too_many_arguments)]

View File

@ -20,7 +20,7 @@ use crate::{
ExitKind, HasObservers,
},
observers::ObserversTuple,
state::{HasCorpus, HasExecutions},
state::HasExecutions,
Error,
};
@ -31,7 +31,6 @@ pub type StatefulInProcessForkExecutor<'a, H, I, OT, S, SP, ES, EM, Z> =
impl<'a, H, I, OT, S, SP, ES, EM, Z> StatefulInProcessForkExecutor<'a, H, I, OT, S, SP, ES, EM, Z>
where
OT: ObserversTuple<I, S>,
S: HasCorpus,
{
#[expect(clippy::too_many_arguments)]
/// The constructor for `InProcessForkExecutor`
@ -99,7 +98,7 @@ impl<EM, H, HT, I, OT, S, SP, Z, ES> Executor<EM, I, S, Z>
where
H: FnMut(&mut ES, &I) -> ExitKind + Sized,
HT: ExecutorHooksTuple<I, S>,
S: HasCorpus + HasExecutions,
S: HasExecutions,
SP: ShMemProvider,
OT: ObserversTuple<I, S>,
{
@ -138,7 +137,6 @@ impl<'a, H, HT, I, OT, S, SP, ES, EM, Z>
where
HT: ExecutorHooksTuple<I, S>,
OT: ObserversTuple<I, S>,
S: HasCorpus,
{
/// Creates a new [`StatefulGenericInProcessForkExecutor`] with custom hooks
#[expect(clippy::too_many_arguments)]

View File

@ -10,23 +10,21 @@ use libafl_bolts::tuples::RefIndexable;
use super::HasTimeout;
use crate::{
corpus::Corpus,
executors::{Executor, ExitKind, HasObservers},
observers::ObserversTuple,
state::HasCorpus,
Error,
};
/// A [`ShadowExecutor`] wraps an executor and a set of shadow observers
pub struct ShadowExecutor<E, S, SOT> {
pub struct ShadowExecutor<E, I, S, SOT> {
/// The wrapped executor
executor: E,
/// The shadow observers
shadow_observers: SOT,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
}
impl<E, S, SOT> Debug for ShadowExecutor<E, S, SOT>
impl<E, I, S, SOT> Debug for ShadowExecutor<E, I, S, SOT>
where
E: Debug,
SOT: Debug,
@ -39,11 +37,10 @@ where
}
}
impl<E, S, SOT> ShadowExecutor<E, S, SOT>
impl<E, I, S, SOT> ShadowExecutor<E, I, S, SOT>
where
E: HasObservers,
S: HasCorpus,
SOT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
SOT: ObserversTuple<I, S>,
{
/// Create a new `ShadowExecutor`, wrapping the given `executor`.
pub fn new(executor: E, shadow_observers: SOT) -> Self {
@ -67,25 +64,23 @@ where
}
}
impl<E, EM, S, SOT, Z> Executor<EM, <S::Corpus as Corpus>::Input, S, Z>
for ShadowExecutor<E, S, SOT>
impl<E, EM, I, S, SOT, Z> Executor<EM, I, S, Z> for ShadowExecutor<E, I, S, SOT>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
S: HasCorpus,
SOT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
E: Executor<EM, I, S, Z> + HasObservers,
SOT: ObserversTuple<I, S>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
) -> Result<ExitKind, Error> {
self.executor.run_target(fuzzer, state, mgr, input)
}
}
impl<E, S, SOT> HasTimeout for ShadowExecutor<E, S, SOT>
impl<E, I, S, SOT> HasTimeout for ShadowExecutor<E, I, S, SOT>
where
E: HasTimeout,
{
@ -99,11 +94,10 @@ where
}
}
impl<E, S, SOT> HasObservers for ShadowExecutor<E, S, SOT>
impl<E, I, S, SOT> HasObservers for ShadowExecutor<E, I, S, SOT>
where
E: HasObservers,
S: HasCorpus,
SOT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
SOT: ObserversTuple<I, S>,
{
type Observers = E::Observers;
#[inline]

View File

@ -5,41 +5,37 @@ use core::{fmt::Debug, marker::PhantomData};
use libafl_bolts::tuples::RefIndexable;
use crate::{
corpus::Corpus,
executors::{Executor, ExitKind, HasObservers},
observers::ObserversTuple,
state::HasCorpus,
Error,
};
/// A wrapper for any [`Executor`] to make it implement [`HasObservers`] using a given [`ObserversTuple`].
#[derive(Debug)]
pub struct WithObservers<E, OT, S> {
pub struct WithObservers<E, I, OT, S> {
executor: E,
observers: OT,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
}
impl<E, EM, OT, S, Z> Executor<EM, <S::Corpus as Corpus>::Input, S, Z> for WithObservers<E, OT, S>
impl<E, EM, I, OT, S, Z> Executor<EM, I, S, Z> for WithObservers<E, I, OT, S>
where
S: HasCorpus,
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z>,
E: Executor<EM, I, S, Z>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
) -> Result<ExitKind, Error> {
self.executor.run_target(fuzzer, state, mgr, input)
}
}
impl<E, OT, S> HasObservers for WithObservers<E, OT, S>
impl<E, I, OT, S> HasObservers for WithObservers<E, I, OT, S>
where
S: HasCorpus,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
OT: ObserversTuple<I, S>,
{
type Observers = OT;
fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> {
@ -51,7 +47,7 @@ where
}
}
impl<E, OT, S> WithObservers<E, OT, S> {
impl<E, I, OT, S> WithObservers<E, I, OT, S> {
/// Wraps the given [`Executor`] with the given [`ObserversTuple`] to implement [`HasObservers`].
///
/// If the executor already implements [`HasObservers`], then the original implementation will be overshadowed by

View File

@ -38,7 +38,7 @@ impl<S> StateInitializer<S> for CaptureTimeoutFeedback {}
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for CaptureTimeoutFeedback
where
S: HasCorpus + HasMetadata,
S: HasCorpus<I> + HasMetadata,
I: Debug + Serialize + DeserializeOwned + Default + 'static + Clone,
{
#[inline]

View File

@ -21,13 +21,12 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(feature = "track_hit_feedbacks")]
use crate::feedbacks::premature_last_result_err;
use crate::{
corpus::{Corpus, Testcase},
corpus::Testcase,
events::{Event, EventFirer},
executors::ExitKind,
feedbacks::{Feedback, HasObserverHandle, StateInitializer},
monitors::{AggregatorOps, UserStats, UserStatsValue},
observers::{CanTrack, MapObserver},
state::HasCorpus,
Error, HasMetadata, HasNamedMetadata,
};
@ -395,13 +394,13 @@ where
impl<C, EM, I, N, O, OT, R, S> Feedback<EM, I, OT, S> for MapFeedback<C, N, O, R>
where
C: CanTrack + AsRef<O>,
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
EM: EventFirer<I, S>,
N: IsNovel<O::Entry>,
O: MapObserver + for<'it> AsIter<'it, Item = O::Entry>,
O::Entry: 'static + Default + Debug + DeserializeOwned + Serialize,
OT: MatchName,
R: Reducer<O::Entry>,
S: HasNamedMetadata + HasCorpus, // delete me
S: HasNamedMetadata,
{
#[rustversion::nightly]
default fn is_interesting(
@ -538,10 +537,10 @@ where
impl<C, O, EM, I, OT, S> Feedback<EM, I, OT, S> for MapFeedback<C, DifferentIsNovel, O, MaxReducer>
where
C: CanTrack + AsRef<O>,
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
EM: EventFirer<I, S>,
O: MapObserver<Entry = u8> + for<'a> AsSlice<'a, Entry = u8> + for<'a> AsIter<'a, Item = u8>,
OT: MatchName,
S: HasNamedMetadata + HasCorpus,
S: HasNamedMetadata,
{
fn is_interesting(
&mut self,

View File

@ -67,8 +67,7 @@ impl<'a> NautilusFeedback<'a> {
testcase: &mut Testcase<NautilusInput>,
) -> Result<(), Error>
where
S: HasCorpus + HasMetadata,
S::Corpus: Corpus<Input = NautilusInput>,
S: HasCorpus<NautilusInput> + HasMetadata,
{
state.corpus().load_input_into(testcase)?;
let input = testcase.input().as_ref().unwrap().clone();
@ -93,8 +92,7 @@ impl<S> StateInitializer<S> for NautilusFeedback<'_> {}
impl<EM, OT, S> Feedback<EM, NautilusInput, OT, S> for NautilusFeedback<'_>
where
S: HasMetadata + HasCorpus,
S::Corpus: Corpus<Input = NautilusInput>,
S: HasMetadata + HasCorpus<NautilusInput>,
{
fn is_interesting(
&mut self,

View File

@ -129,9 +129,9 @@ pub trait ExecutionProcessor<EM, I, OT, S> {
/// Evaluates an input modifying the state of the fuzzer
pub trait EvaluatorObservers<E, EM, I, S> {
/// Runs the input and triggers observers and feedback,
/// returns if is interesting an (option) the index of the new
/// [`crate::corpus::Testcase`] in the [`crate::corpus::Corpus`]
/// Runs the input and triggers observers and feedback.
/// if it is interesting, returns an (option) the index of the new
/// [`Testcase`] in the [`Corpus`]
fn evaluate_input_with_observers(
&mut self,
state: &mut S,
@ -145,7 +145,7 @@ pub trait EvaluatorObservers<E, EM, I, S> {
/// Evaluate an input modifying the state of the fuzzer
pub trait Evaluator<E, EM, I, S> {
/// Runs the input if it was (likely) not previously run and triggers observers and feedback and adds the input to the previously executed list
/// returns if is interesting an (option) the index of the new [`crate::corpus::Testcase`] in the corpus
/// if it is interesting, returns an (option) the index of the new [`Testcase`] in the corpus
fn evaluate_filtered(
&mut self,
state: &mut S,
@ -155,7 +155,7 @@ pub trait Evaluator<E, EM, I, S> {
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>;
/// Runs the input and triggers observers and feedback,
/// returns if is interesting an (option) the index of the new [`crate::corpus::Testcase`] in the corpus
/// returns if is interesting an (option) the index of the new [`Testcase`] in the corpus
fn evaluate_input(
&mut self,
state: &mut S,
@ -190,7 +190,7 @@ pub trait Evaluator<E, EM, I, S> {
input: I,
) -> Result<CorpusId, Error>;
/// Adds the input to the corpus as disabled a input.
/// Adds the input to the corpus as a disabled input.
/// Used during initial corpus loading.
/// Disabled testcases are only used for splicing
/// Returns the `index` of the new testcase in the corpus.
@ -199,11 +199,11 @@ pub trait Evaluator<E, EM, I, S> {
}
/// The main fuzzer trait.
pub trait Fuzzer<E, EM, S, ST> {
pub trait Fuzzer<E, EM, I, S, ST> {
/// Fuzz for a single iteration.
/// Returns the index of the last fuzzed corpus item.
/// (Note: An iteration represents a complete run of every stage.
/// Therefore it does not mean that the harness is executed for once,
/// Therefore, it does not mean that the harness is executed for once,
/// because each stage could run the harness for multiple times)
///
/// If you use this fn in a restarting scenario to only run for `n` iterations,
@ -265,10 +265,9 @@ pub struct StdFuzzer<CS, F, IF, OF> {
input_filter: IF,
}
impl<CS, F, IF, OF, S> HasScheduler<<S::Corpus as Corpus>::Input, S> for StdFuzzer<CS, F, IF, OF>
impl<CS, F, I, IF, OF, S> HasScheduler<I, S> for StdFuzzer<CS, F, IF, OF>
where
S: HasCorpus,
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
CS: Scheduler<I, S>,
{
type Scheduler = CS;
@ -305,23 +304,25 @@ impl<CS, F, IF, OF> HasObjective for StdFuzzer<CS, F, IF, OF> {
}
}
impl<CS, EM, F, IF, OF, OT, S> ExecutionProcessor<EM, <S::Corpus as Corpus>::Input, OT, S>
for StdFuzzer<CS, F, IF, OF>
impl<CS, EM, F, I, IF, OF, OT, S> ExecutionProcessor<EM, I, OT, S> for StdFuzzer<CS, F, IF, OF>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
EM: EventFirer<<S::Corpus as Corpus>::Input, S> + CanSerializeObserver<OT>,
S: HasCorpus + MaybeHasClientPerfMonitor + HasCurrentTestcase + HasSolutions + HasLastFoundTime,
F: Feedback<EM, <S::Corpus as Corpus>::Input, OT, S>,
OF: Feedback<EM, <S::Corpus as Corpus>::Input, OT, S>,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
<S::Corpus as Corpus>::Input: Input,
S::Solutions: Corpus<Input = <S::Corpus as Corpus>::Input>,
CS: Scheduler<I, S>,
EM: EventFirer<I, S> + CanSerializeObserver<OT>,
F: Feedback<EM, I, OT, S>,
I: Input,
OF: Feedback<EM, I, OT, S>,
OT: ObserversTuple<I, S> + Serialize,
S: HasCorpus<I>
+ MaybeHasClientPerfMonitor
+ HasCurrentTestcase<I>
+ HasSolutions<I>
+ HasLastFoundTime,
{
fn check_results(
&mut self,
state: &mut S,
manager: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<ExecuteInputResult, Error> {
@ -357,107 +358,12 @@ where
Ok(res)
}
fn evaluate_execution(
&mut self,
state: &mut S,
manager: &mut EM,
input: <S::Corpus as Corpus>::Input,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
let exec_res = self.check_results(state, manager, &input, observers, exit_kind)?;
let corpus_id = self.process_execution(state, manager, &input, &exec_res, observers)?;
if send_events {
self.serialize_and_dispatch(state, manager, input, &exec_res, observers, exit_kind)?;
}
if exec_res != ExecuteInputResult::None {
*state.last_found_time_mut() = current_time();
}
Ok((exec_res, corpus_id))
}
fn serialize_and_dispatch(
&mut self,
state: &mut S,
manager: &mut EM,
input: <S::Corpus as Corpus>::Input,
exec_res: &ExecuteInputResult,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<(), Error> {
// Now send off the event
let observers_buf = match exec_res {
ExecuteInputResult::Corpus => {
if manager.should_send() {
// TODO set None for fast targets
if manager.configuration() == EventConfig::AlwaysUnique {
None
} else {
manager.serialize_observers(observers)?
}
} else {
None
}
}
_ => None,
};
self.dispatch_event(state, manager, input, exec_res, observers_buf, exit_kind)?;
Ok(())
}
fn dispatch_event(
&mut self,
state: &mut S,
manager: &mut EM,
input: <S::Corpus as Corpus>::Input,
exec_res: &ExecuteInputResult,
observers_buf: Option<Vec<u8>>,
exit_kind: &ExitKind,
) -> Result<(), Error> {
// Now send off the event
match exec_res {
ExecuteInputResult::Corpus => {
if manager.should_send() {
manager.fire(
state,
Event::NewTestcase {
input,
observers_buf,
exit_kind: *exit_kind,
corpus_size: state.corpus().count(),
client_config: manager.configuration(),
time: current_time(),
forward_id: None,
#[cfg(all(unix, feature = "std", feature = "multi_machine"))]
node_id: None,
},
)?;
}
}
ExecuteInputResult::Solution => {
if manager.should_send() {
manager.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
time: current_time(),
},
)?;
}
}
ExecuteInputResult::None => (),
}
Ok(())
}
/// Evaluate if a set of observation channels has an interesting state
fn process_execution(
&mut self,
state: &mut S,
manager: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
exec_res: &ExecuteInputResult,
observers: &OT,
) -> Result<Option<CorpusId>, Error> {
@ -504,25 +410,118 @@ where
}
}
}
fn serialize_and_dispatch(
&mut self,
state: &mut S,
manager: &mut EM,
input: I,
exec_res: &ExecuteInputResult,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<(), Error> {
// Now send off the event
let observers_buf = match exec_res {
ExecuteInputResult::Corpus => {
if manager.should_send() {
// TODO set None for fast targets
if manager.configuration() == EventConfig::AlwaysUnique {
None
} else {
manager.serialize_observers(observers)?
}
} else {
None
}
}
_ => None,
};
self.dispatch_event(state, manager, input, exec_res, observers_buf, exit_kind)?;
Ok(())
}
fn dispatch_event(
&mut self,
state: &mut S,
manager: &mut EM,
input: I,
exec_res: &ExecuteInputResult,
observers_buf: Option<Vec<u8>>,
exit_kind: &ExitKind,
) -> Result<(), Error> {
// Now send off the event
match exec_res {
ExecuteInputResult::Corpus => {
if manager.should_send() {
manager.fire(
state,
Event::NewTestcase {
input,
observers_buf,
exit_kind: *exit_kind,
corpus_size: state.corpus().count(),
client_config: manager.configuration(),
time: current_time(),
forward_id: None,
#[cfg(all(unix, feature = "std", feature = "multi_machine"))]
node_id: None,
},
)?;
}
}
ExecuteInputResult::Solution => {
if manager.should_send() {
manager.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
time: current_time(),
},
)?;
}
}
ExecuteInputResult::None => (),
}
Ok(())
}
fn evaluate_execution(
&mut self,
state: &mut S,
manager: &mut EM,
input: I,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
let exec_res = self.check_results(state, manager, &input, observers, exit_kind)?;
let corpus_id = self.process_execution(state, manager, &input, &exec_res, observers)?;
if send_events {
self.serialize_and_dispatch(state, manager, input, &exec_res, observers, exit_kind)?;
}
if exec_res != ExecuteInputResult::None {
*state.last_found_time_mut() = current_time();
}
Ok((exec_res, corpus_id))
}
}
impl<CS, E, EM, F, IF, OF, S> EvaluatorObservers<E, EM, <S::Corpus as Corpus>::Input, S>
for StdFuzzer<CS, F, IF, OF>
impl<CS, E, EM, F, I, IF, OF, S> EvaluatorObservers<E, EM, I, S> for StdFuzzer<CS, F, IF, OF>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
E: HasObservers + Executor<EM, <S::Corpus as Corpus>::Input, S, Self>,
E::Observers: MatchName + ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
EM: EventFirer<<S::Corpus as Corpus>::Input, S> + CanSerializeObserver<E::Observers>,
F: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
OF: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
S: HasCorpus
+ HasSolutions
CS: Scheduler<I, S>,
E: HasObservers + Executor<EM, I, S, Self>,
E::Observers: MatchName + ObserversTuple<I, S> + Serialize,
EM: EventFirer<I, S> + CanSerializeObserver<E::Observers>,
F: Feedback<EM, I, E::Observers, S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasCorpus<I>
+ HasSolutions<I>
+ MaybeHasClientPerfMonitor
+ HasCurrentTestcase
+ HasCurrentTestcase<I>
+ HasExecutions
+ HasLastFoundTime,
<S::Corpus as Corpus>::Input: Input,
S::Solutions: Corpus<Input = <S::Corpus as Corpus>::Input>,
I: Input,
{
/// Process one input, adding to the respective corpora if needed and firing the right events
#[inline]
@ -531,7 +530,7 @@ where
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: <S::Corpus as Corpus>::Input,
input: I,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
let exit_kind = self.execute_input(state, executor, manager, &input)?;
@ -583,31 +582,29 @@ impl<I: Hash> InputFilter<I> for BloomInputFilter {
}
}
impl<CS, E, EM, F, IF, OF, S> Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>
for StdFuzzer<CS, F, IF, OF>
impl<CS, E, EM, F, I, IF, OF, S> Evaluator<E, EM, I, S> for StdFuzzer<CS, F, IF, OF>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
E: HasObservers + Executor<EM, <S::Corpus as Corpus>::Input, S, Self>,
E::Observers: MatchName + ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
EM: EventFirer<<S::Corpus as Corpus>::Input, S> + CanSerializeObserver<E::Observers>,
F: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
OF: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
S: HasCorpus
+ HasSolutions
CS: Scheduler<I, S>,
E: HasObservers + Executor<EM, I, S, Self>,
E::Observers: MatchName + ObserversTuple<I, S> + Serialize,
EM: EventFirer<I, S> + CanSerializeObserver<E::Observers>,
F: Feedback<EM, I, E::Observers, S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasCorpus<I>
+ HasSolutions<I>
+ MaybeHasClientPerfMonitor
+ HasCurrentTestcase
+ HasCurrentTestcase<I>
+ HasLastFoundTime
+ HasExecutions,
<S::Corpus as Corpus>::Input: Input,
S::Solutions: Corpus<Input = <S::Corpus as Corpus>::Input>,
IF: InputFilter<<S::Corpus as Corpus>::Input>,
I: Input,
IF: InputFilter<I>,
{
fn evaluate_filtered(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: <S::Corpus as Corpus>::Input,
input: I,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
if self.input_filter.should_execute(&input) {
self.evaluate_input(state, executor, manager, input)
@ -623,31 +620,19 @@ where
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: <S::Corpus as Corpus>::Input,
input: I,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
self.evaluate_input_with_observers(state, executor, manager, input, send_events)
}
fn add_disabled_input(
&mut self,
state: &mut S,
input: <S::Corpus as Corpus>::Input,
) -> Result<CorpusId, Error> {
let mut testcase = Testcase::from(input.clone());
testcase.set_disabled(true);
// Add the disabled input to the main corpus
let id = state.corpus_mut().add_disabled(testcase)?;
Ok(id)
}
/// Adds an input, even if it's not considered `interesting` by any of the executors
fn add_input(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: <S::Corpus as Corpus>::Input,
input: I,
) -> Result<CorpusId, Error> {
*state.last_found_time_mut() = current_time();
@ -738,17 +723,25 @@ where
)?;
Ok(id)
}
fn add_disabled_input(&mut self, state: &mut S, input: I) -> Result<CorpusId, Error> {
let mut testcase = Testcase::from(input.clone());
testcase.set_disabled(true);
// Add the disabled input to the main corpus
let id = state.corpus_mut().add_disabled(testcase)?;
Ok(id)
}
}
impl<CS, E, EM, F, IF, OF, S, ST> Fuzzer<E, EM, S, ST> for StdFuzzer<CS, F, IF, OF>
impl<CS, E, EM, F, I, IF, OF, S, ST> Fuzzer<E, EM, I, S, ST> for StdFuzzer<CS, F, IF, OF>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
CS: Scheduler<I, S>,
EM: ProgressReporter<S> + EventProcessor<E, S, Self>,
S: HasExecutions
+ HasMetadata
+ HasCorpus
+ HasCorpus<I>
+ HasLastReportTime
+ HasTestcase
+ HasTestcase<I>
+ HasCurrentCorpusId
+ HasCurrentStageId
+ Stoppable
@ -855,7 +848,7 @@ where
manager.report_progress(state)?;
// If we would assume the fuzzer loop will always exit after this, we could do this here:
// If we assumed the fuzzer loop will always exit after this, we could do this here:
// manager.on_restart(state)?;
// But as the state may grow to a few megabytes,
// for now we won't, and the user has to do it (unless we find a way to do this on `Drop`).
@ -914,13 +907,12 @@ pub trait ExecutesInput<E, EM, I, S> {
) -> Result<ExitKind, Error>;
}
impl<CS, E, EM, F, IF, OF, S> ExecutesInput<E, EM, <S::Corpus as Corpus>::Input, S>
for StdFuzzer<CS, F, IF, OF>
impl<CS, E, EM, F, I, IF, OF, S> ExecutesInput<E, EM, I, S> for StdFuzzer<CS, F, IF, OF>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Self> + HasObservers,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions + HasCorpus + MaybeHasClientPerfMonitor,
CS: Scheduler<I, S>,
E: Executor<EM, I, S, Self> + HasObservers,
E::Observers: ObserversTuple<I, S>,
S: HasExecutions + HasCorpus<I> + MaybeHasClientPerfMonitor,
{
/// Runs the input and triggers observers and feedback
fn execute_input(
@ -928,7 +920,7 @@ where
state: &mut S,
executor: &mut E,
event_mgr: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
) -> Result<ExitKind, Error> {
start_timer!(state);
executor.observers_mut().pre_exec_all(state, input)?;
@ -966,7 +958,7 @@ impl Default for NopFuzzer {
}
}
impl<E, EM, S, ST> Fuzzer<E, EM, S, ST> for NopFuzzer
impl<E, EM, I, S, ST> Fuzzer<E, EM, I, S, ST> for NopFuzzer
where
EM: ProgressReporter<S> + EventProcessor<E, S, Self>,
ST: StagesTuple<E, EM, S, Self>,

View File

@ -9,7 +9,6 @@ use crate::{
corpus::Testcase,
inputs::BytesInput,
stages::mutational::{MutatedTransform, MutatedTransformPost},
state::HasCorpus,
Error, HasMetadata,
};
@ -105,10 +104,7 @@ impl GeneralizedInputMetadata {
}
}
impl<S> MutatedTransform<BytesInput, S> for GeneralizedInputMetadata
where
S: HasCorpus,
{
impl<S> MutatedTransform<BytesInput, S> for GeneralizedInputMetadata {
type Post = Self;
fn try_transform_from(base: &mut Testcase<BytesInput>, _state: &S) -> Result<Self, Error> {
@ -130,4 +126,4 @@ where
}
}
impl<S> MutatedTransformPost<S> for GeneralizedInputMetadata where S: HasCorpus {}
impl<S> MutatedTransformPost<S> for GeneralizedInputMetadata {}

View File

@ -356,12 +356,9 @@ where
}
/// A converter that converts from `input` to target bytes
pub trait TargetBytesConverter {
/// The input
type Input;
pub trait TargetBytesConverter<I> {
/// Create target bytes
fn to_target_bytes<'a>(&mut self, input: &'a Self::Input) -> OwnedSlice<'a, u8>;
fn to_target_bytes<'a>(&mut self, input: &'a I) -> OwnedSlice<'a, u8>;
}
/// Simply gets the target bytes out from a [`HasTargetBytes`] type.
@ -386,10 +383,11 @@ impl<I> Default for NopTargetBytesConverter<I> {
}
}
impl<I: HasTargetBytes> TargetBytesConverter for NopTargetBytesConverter<I> {
type Input = I;
fn to_target_bytes<'a>(&mut self, input: &'a Self::Input) -> OwnedSlice<'a, u8> {
impl<I> TargetBytesConverter<I> for NopTargetBytesConverter<I>
where
I: HasTargetBytes,
{
fn to_target_bytes<'a>(&mut self, input: &'a I) -> OwnedSlice<'a, u8> {
input.target_bytes()
}
}

View File

@ -154,10 +154,8 @@ impl<'a> NautilusTargetBytesConverter<'a> {
}
}
impl TargetBytesConverter for NautilusTargetBytesConverter<'_> {
type Input = NautilusInput;
fn to_target_bytes<'a>(&mut self, input: &'a Self::Input) -> OwnedSlice<'a, u8> {
impl TargetBytesConverter<NautilusInput> for NautilusTargetBytesConverter<'_> {
fn to_target_bytes<'a>(&mut self, input: &'a NautilusInput) -> OwnedSlice<'a, u8> {
let mut bytes = Vec::new();
input.unparse(self.ctx, &mut bytes);
OwnedSlice::from(bytes)

View File

@ -190,14 +190,14 @@ mod tests {
let state_serialized = postcard::to_allocvec(&state).unwrap();
let state_deserialized: StdState<
_,
InMemoryCorpus<BytesInput>,
_,
StdRand,
InMemoryCorpus<BytesInput>,
> = postcard::from_bytes::<
StdState<
BytesInput,
InMemoryCorpus<BytesInput>,
BytesInput,
RomuDuoJrRand,
InMemoryCorpus<BytesInput>,
>,

View File

@ -313,8 +313,7 @@ pub struct EncodedCrossoverInsertMutator;
impl<S> Mutator<EncodedInput, S> for EncodedCrossoverInsertMutator
where
S: HasRand + HasCorpus + HasMaxSize,
S::Corpus: Corpus<Input = EncodedInput>,
S: HasRand + HasCorpus<EncodedInput> + HasMaxSize,
{
fn mutate(&mut self, state: &mut S, input: &mut EncodedInput) -> Result<MutationResult, Error> {
let size = input.codes().len();
@ -397,8 +396,7 @@ pub struct EncodedCrossoverReplaceMutator;
impl<S> Mutator<EncodedInput, S> for EncodedCrossoverReplaceMutator
where
S: HasRand + HasCorpus,
S::Corpus: Corpus<Input = EncodedInput>,
S: HasRand + HasCorpus<EncodedInput>,
{
fn mutate(&mut self, state: &mut S, input: &mut EncodedInput) -> Result<MutationResult, Error> {
let size = input.codes().len();

View File

@ -111,8 +111,7 @@ pub struct GramatronSpliceMutator;
impl<S> Mutator<GramatronInput, S> for GramatronSpliceMutator
where
S: HasRand + HasCorpus + HasMetadata,
S::Corpus: Corpus<Input = GramatronInput>,
S: HasRand + HasCorpus<GramatronInput> + HasMetadata,
{
fn mutate(
&mut self,

View File

@ -4,6 +4,7 @@
use alloc::{borrow::Cow, vec::Vec};
use core::{
cmp::{max, min},
marker::PhantomData,
num::NonZero,
};
@ -25,13 +26,13 @@ const RECURSIVE_REPLACEMENT_DEPTH: [usize; 6] = [2, 4, 8, 16, 32, 64];
const MAX_RECURSIVE_REPLACEMENT_LEN: usize = 64 << 10;
const CHOOSE_SUBINPUT_PROB: f64 = 0.5;
fn extend_with_random_generalized<S>(
fn extend_with_random_generalized<I, S>(
state: &mut S,
items: &mut Vec<GeneralizedItem>,
gap_indices: &mut Vec<usize>,
) -> Result<MutationResult, Error>
where
S: HasMetadata + HasRand + HasCorpus,
S: HasMetadata + HasRand + HasCorpus<I>,
{
let id = random_corpus_id!(state.corpus(), state.rand_mut());
@ -116,13 +117,14 @@ where
/// Extend the generalized input with another random one from the corpus
#[derive(Debug, Default)]
pub struct GrimoireExtensionMutator {
pub struct GrimoireExtensionMutator<I> {
gap_indices: Vec<usize>,
phantom: PhantomData<I>,
}
impl<S> Mutator<GeneralizedInputMetadata, S> for GrimoireExtensionMutator
impl<I, S> Mutator<GeneralizedInputMetadata, S> for GrimoireExtensionMutator<I>
where
S: HasMetadata + HasRand + HasCorpus,
S: HasMetadata + HasRand + HasCorpus<I>,
{
fn mutate(
&mut self,
@ -137,33 +139,35 @@ where
}
}
impl Named for GrimoireExtensionMutator {
impl<I> Named for GrimoireExtensionMutator<I> {
fn name(&self) -> &Cow<'static, str> {
static NAME: Cow<'static, str> = Cow::Borrowed("GrimoireExtensionMutator");
&NAME
}
}
impl GrimoireExtensionMutator {
impl<I> GrimoireExtensionMutator<I> {
/// Creates a new [`GrimoireExtensionMutator`].
#[must_use]
pub fn new() -> Self {
Self {
gap_indices: vec![],
phantom: PhantomData,
}
}
}
/// Extend the generalized input with another random one from the corpus
#[derive(Debug, Default)]
pub struct GrimoireRecursiveReplacementMutator {
pub struct GrimoireRecursiveReplacementMutator<I> {
scratch: Vec<GeneralizedItem>,
gap_indices: Vec<usize>,
phantom: PhantomData<I>,
}
impl<S> Mutator<GeneralizedInputMetadata, S> for GrimoireRecursiveReplacementMutator
impl<I, S> Mutator<GeneralizedInputMetadata, S> for GrimoireRecursiveReplacementMutator<I>
where
S: HasMetadata + HasRand + HasCorpus,
S: HasMetadata + HasRand + HasCorpus<I>,
{
fn mutate(
&mut self,
@ -215,31 +219,34 @@ where
}
}
impl Named for GrimoireRecursiveReplacementMutator {
impl<I> Named for GrimoireRecursiveReplacementMutator<I> {
fn name(&self) -> &Cow<'static, str> {
static NAME: Cow<'static, str> = Cow::Borrowed("GrimoireRecursiveReplacementMutator");
&NAME
}
}
impl GrimoireRecursiveReplacementMutator {
impl<I> GrimoireRecursiveReplacementMutator<I> {
/// Creates a new [`GrimoireRecursiveReplacementMutator`].
#[must_use]
pub fn new() -> Self {
Self {
scratch: vec![],
gap_indices: vec![],
phantom: PhantomData,
}
}
}
/// Replace matching tokens with others from the tokens metadata
#[derive(Debug, Default)]
pub struct GrimoireStringReplacementMutator {}
pub struct GrimoireStringReplacementMutator<I> {
phantom: PhantomData<I>,
}
impl<S> Mutator<GeneralizedInputMetadata, S> for GrimoireStringReplacementMutator
impl<I, S> Mutator<GeneralizedInputMetadata, S> for GrimoireStringReplacementMutator<I>
where
S: HasMetadata + HasRand + HasCorpus,
S: HasMetadata + HasRand + HasCorpus<I>,
{
fn mutate(
&mut self,
@ -336,30 +343,33 @@ where
}
}
impl Named for GrimoireStringReplacementMutator {
impl<I> Named for GrimoireStringReplacementMutator<I> {
fn name(&self) -> &Cow<'static, str> {
static NAME: Cow<'static, str> = Cow::Borrowed("GrimoireStringReplacementMutator");
&NAME
}
}
impl GrimoireStringReplacementMutator {
impl<I> GrimoireStringReplacementMutator<I> {
/// Creates a new [`GrimoireExtensionMutator`].
#[must_use]
pub fn new() -> Self {
Self::default()
Self {
phantom: PhantomData,
}
}
}
/// Randomly delete a part of the generalized input
#[derive(Debug, Default)]
pub struct GrimoireRandomDeleteMutator {
pub struct GrimoireRandomDeleteMutator<I> {
gap_indices: Vec<usize>,
phantom: PhantomData<I>,
}
impl<S> Mutator<GeneralizedInputMetadata, S> for GrimoireRandomDeleteMutator
impl<I, S> Mutator<GeneralizedInputMetadata, S> for GrimoireRandomDeleteMutator<I>
where
S: HasMetadata + HasRand + HasCorpus,
S: HasMetadata + HasRand + HasCorpus<I>,
{
fn mutate(
&mut self,
@ -400,19 +410,20 @@ where
}
}
impl Named for GrimoireRandomDeleteMutator {
impl<I> Named for GrimoireRandomDeleteMutator<I> {
fn name(&self) -> &Cow<'static, str> {
static NAME: Cow<'static, str> = Cow::Borrowed("GrimoireRandomDeleteMutator");
&NAME
}
}
impl GrimoireRandomDeleteMutator {
impl<I> GrimoireRandomDeleteMutator<I> {
/// Creates a new [`GrimoireExtensionMutator`].
#[must_use]
pub fn new() -> Self {
Self {
gap_indices: vec![],
phantom: PhantomData,
}
}
}

View File

@ -51,9 +51,9 @@ pub type HavocMutationsNoCrossoverType = tuple_list_type!(
pub type HavocCrossoverType = tuple_list_type!(CrossoverInsertMutator, CrossoverReplaceMutator);
/// Tuple type of the mutations that compose the Havoc mutator's crossover mutations for mapped input types
pub type MappedHavocCrossoverType<F, O> = tuple_list_type!(
MappedCrossoverInsertMutator<F, O>,
MappedCrossoverReplaceMutator<F, O>,
pub type MappedHavocCrossoverType<F, I, O> = tuple_list_type!(
MappedCrossoverInsertMutator<F, I, O>,
MappedCrossoverReplaceMutator<F, I, O>,
);
/// Tuple type of the mutations that compose the Havoc mutator
@ -61,15 +61,15 @@ pub type HavocMutationsType =
merge_tuple_list_type!(HavocMutationsNoCrossoverType, HavocCrossoverType);
/// Tuple type of the mutations that compose the Havoc mutator for mapped input types
pub type MappedHavocMutationsType<F1, F2, O> = map_tuple_list_type!(
merge_tuple_list_type!(HavocMutationsNoCrossoverType, MappedHavocCrossoverType<F2,O>),
pub type MappedHavocMutationsType<F1, F2, I, O> = map_tuple_list_type!(
merge_tuple_list_type!(HavocMutationsNoCrossoverType, MappedHavocCrossoverType<F2,I, O>),
ToMappingMutator<F1>
);
/// Tuple type of the mutations that compose the Havoc mutator for mapped input types, for optional byte array input parts
pub type OptionMappedHavocMutationsType<F1, F2, O> = map_tuple_list_type!(
pub type OptionMappedHavocMutationsType<F1, F2, I, O> = map_tuple_list_type!(
map_tuple_list_type!(
merge_tuple_list_type!(HavocMutationsNoCrossoverType, MappedHavocCrossoverType<F2,O>),
merge_tuple_list_type!(HavocMutationsNoCrossoverType, MappedHavocCrossoverType<F2,I, O>),
ToOptionalMutator
),
ToMappingMutator<F1>
@ -117,9 +117,9 @@ pub fn havoc_crossover() -> HavocCrossoverType {
}
/// Get the mutations that compose the Havoc mutator's crossover strategy with custom corpus extraction logic
pub fn havoc_crossover_with_corpus_mapper<F, IO, O>(
pub fn havoc_crossover_with_corpus_mapper<F, I, IO, O>(
input_mapper: F,
) -> MappedHavocCrossoverType<F, O>
) -> MappedHavocCrossoverType<F, I, O>
where
F: Clone + Fn(&IO) -> &O,
{
@ -130,9 +130,9 @@ where
}
/// Get the mutations that compose the Havoc mutator's crossover strategy with custom corpus extraction logic
pub fn havoc_crossover_with_corpus_mapper_optional<F, O>(
pub fn havoc_crossover_with_corpus_mapper_optional<F, I, O>(
input_mapper: F,
) -> MappedHavocCrossoverType<F, O>
) -> MappedHavocCrossoverType<F, I, O>
where
F: Clone,
{
@ -155,7 +155,7 @@ pub fn havoc_mutations() -> HavocMutationsType {
pub fn mapped_havoc_mutations<F1, F2, IO1, IO2, II, O>(
current_input_mapper: F1,
input_from_corpus_mapper: F2,
) -> MappedHavocMutationsType<F1, F2, O>
) -> MappedHavocMutationsType<F1, F2, IO1, O>
where
F1: Clone + FnMut(&mut IO1) -> &mut II,
F2: Clone + Fn(&IO2) -> &O,
@ -172,7 +172,7 @@ where
pub fn optional_mapped_havoc_mutations<F1, F2, IO1, IO2, II, O>(
current_input_mapper: F1,
input_from_corpus_mapper: F2,
) -> OptionMappedHavocMutationsType<F1, F2, O>
) -> OptionMappedHavocMutationsType<F1, F2, IO1, O>
where
F1: Clone + FnMut(&mut IO1) -> &mut II,
F2: Clone + Fn(&IO2) -> &O,

View File

@ -371,7 +371,7 @@ pub struct StdMOptMutator<MT> {
impl<I, MT, S> Mutator<I, S> for StdMOptMutator<MT>
where
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
{
#[inline]
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
@ -519,7 +519,7 @@ impl<MT> StdMOptMutator<MT> {
}
fn core_mutate<I, S>(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error>
where
S: HasMetadata + HasRand + HasSolutions + HasCorpus,
S: HasMetadata + HasRand + HasSolutions<I> + HasCorpus<I>,
MT: MutatorsTuple<I, S>,
{
let mut r = MutationResult::Skipped;
@ -546,7 +546,7 @@ impl<MT> StdMOptMutator<MT> {
fn pilot_mutate<I, S>(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error>
where
S: HasMetadata + HasRand + HasSolutions + HasCorpus,
S: HasMetadata + HasRand + HasSolutions<I> + HasCorpus<I>,
MT: MutatorsTuple<I, S>,
{
let mut r = MutationResult::Skipped;
@ -604,7 +604,7 @@ impl<MT> Named for StdMOptMutator<MT> {
impl<I, MT, S> ScheduledMutator<I, S> for StdMOptMutator<MT>
where
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &I) -> u64 {

View File

@ -118,9 +118,8 @@ impl_default_multipart!(
impl<I, S> Mutator<MultipartInput<I>, S> for CrossoverInsertMutator
where
S: HasCorpus + HasMaxSize + HasRand,
S: HasCorpus<MultipartInput<I>> + HasMaxSize + HasRand,
I: Input + HasMutatorResizableBytes,
S::Corpus: Corpus<Input = MultipartInput<I>>,
{
fn mutate(
&mut self,
@ -254,9 +253,8 @@ where
impl<I, S> Mutator<MultipartInput<I>, S> for CrossoverReplaceMutator
where
S: HasCorpus + HasMaxSize + HasRand,
S: HasCorpus<MultipartInput<I>> + HasMaxSize + HasRand,
I: Input + HasMutatorResizableBytes,
S::Corpus: Corpus<Input = MultipartInput<I>>,
{
fn mutate(
&mut self,

View File

@ -1125,9 +1125,8 @@ impl CrossoverInsertMutator {
impl<I, S> Mutator<I, S> for CrossoverInsertMutator
where
S: HasCorpus + HasRand + HasMaxSize,
<S::Corpus as Corpus>::Input: HasMutatorBytes,
I: HasMutatorResizableBytes,
S: HasCorpus<I> + HasRand + HasMaxSize,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -1217,9 +1216,8 @@ impl CrossoverReplaceMutator {
impl<I, S> Mutator<I, S> for CrossoverReplaceMutator
where
S: HasCorpus + HasRand,
<S::Corpus as Corpus>::Input: HasMutatorBytes,
I: HasMutatorBytes,
S: HasCorpus<I> + HasRand,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -1297,12 +1295,12 @@ impl IntoOptionBytes for Option<Vec<u8>> {
/// Crossover insert mutation for inputs mapped to a bytes vector
#[derive(Debug)]
pub struct MappedCrossoverInsertMutator<F, O> {
pub struct MappedCrossoverInsertMutator<F, I, O> {
input_mapper: F,
phantom: PhantomData<O>,
phantom: PhantomData<(I, O)>,
}
impl<F, O> MappedCrossoverInsertMutator<F, O> {
impl<F, I, O> MappedCrossoverInsertMutator<F, I, O> {
/// Creates a new [`MappedCrossoverInsertMutator`]
pub fn new(input_mapper: F) -> Self {
Self {
@ -1312,14 +1310,14 @@ impl<F, O> MappedCrossoverInsertMutator<F, O> {
}
}
impl<S, F, I, O> Mutator<I, S> for MappedCrossoverInsertMutator<F, O>
impl<S, F, I1, I2, O> Mutator<I2, S> for MappedCrossoverInsertMutator<F, I1, O>
where
S: HasCorpus + HasMaxSize + HasRand,
I: HasMutatorResizableBytes,
F: Fn(&I1) -> &O,
I2: HasMutatorResizableBytes,
O: IntoOptionBytes,
F: Fn(&<S::Corpus as Corpus>::Input) -> &O,
S: HasCorpus<I1> + HasMaxSize + HasRand,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
fn mutate(&mut self, state: &mut S, input: &mut I2) -> Result<MutationResult, Error> {
let size = input.bytes().len();
let max_size = state.max_size();
// TODO: fix bug if size is 0 (?)
@ -1377,7 +1375,7 @@ where
}
}
impl<F, O> Named for MappedCrossoverInsertMutator<F, O> {
impl<F, I, O> Named for MappedCrossoverInsertMutator<F, I, O> {
fn name(&self) -> &Cow<'static, str> {
static NAME: Cow<'static, str> = Cow::Borrowed("MappedCrossoverInsertMutator");
&NAME
@ -1386,12 +1384,12 @@ impl<F, O> Named for MappedCrossoverInsertMutator<F, O> {
/// Crossover replace mutation for inputs mapped to a bytes vector
#[derive(Debug)]
pub struct MappedCrossoverReplaceMutator<F, O> {
pub struct MappedCrossoverReplaceMutator<F, I, O> {
input_mapper: F,
phantom: PhantomData<O>,
phantom: PhantomData<(I, O)>,
}
impl<F, O> MappedCrossoverReplaceMutator<F, O> {
impl<F, I, O> MappedCrossoverReplaceMutator<F, I, O> {
/// Creates a new [`MappedCrossoverReplaceMutator`]
pub fn new(input_mapper: F) -> Self {
Self {
@ -1401,14 +1399,14 @@ impl<F, O> MappedCrossoverReplaceMutator<F, O> {
}
}
impl<S, F, I, O> Mutator<I, S> for MappedCrossoverReplaceMutator<F, O>
impl<S, F, I1, I2, O> Mutator<I2, S> for MappedCrossoverReplaceMutator<F, I1, O>
where
S: HasCorpus + HasMaxSize + HasRand,
I: HasMutatorBytes,
F: Fn(&I1) -> &O,
I2: HasMutatorBytes,
O: IntoOptionBytes,
F: Fn(&<S::Corpus as Corpus>::Input) -> &O,
S: HasCorpus<I1> + HasMaxSize + HasRand,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
fn mutate(&mut self, state: &mut S, input: &mut I2) -> Result<MutationResult, Error> {
let size = input.bytes().len();
if size == 0 {
return Ok(MutationResult::Skipped);
@ -1463,7 +1461,7 @@ where
}
}
impl<F, O> Named for MappedCrossoverReplaceMutator<F, O> {
impl<F, I, O> Named for MappedCrossoverReplaceMutator<F, I, O> {
fn name(&self) -> &Cow<'static, str> {
static NAME: Cow<'static, str> = Cow::Borrowed("MappedCrossoverReplaceMutator");
&NAME
@ -1492,8 +1490,7 @@ pub struct SpliceMutator;
impl<I, S> Mutator<I, S> for SpliceMutator
where
S: HasCorpus + HasRand,
<S::Corpus as Corpus>::Input: HasMutatorBytes,
S: HasCorpus<I> + HasRand,
I: HasMutatorResizableBytes,
{
#[expect(clippy::cast_sign_loss)]
@ -1657,7 +1654,7 @@ mod tests {
)
}
fn test_state() -> impl HasCorpus + HasMetadata + HasRand + HasMaxSize {
fn test_state() -> impl HasCorpus<BytesInput> + HasMetadata + HasRand + HasMaxSize {
let rand = StdRand::with_seed(1337);
let mut corpus = InMemoryCorpus::new();

View File

@ -162,7 +162,7 @@ impl Debug for NautilusSpliceMutator<'_> {
impl<S> Mutator<NautilusInput, S> for NautilusSpliceMutator<'_>
where
S: HasCorpus + HasMetadata + HasRand,
S: HasCorpus<NautilusInput> + HasMetadata + HasRand,
{
fn mutate(
&mut self,

View File

@ -1,6 +1,7 @@
//! Mutators for integer-style inputs
use alloc::borrow::Cow;
use core::marker::PhantomData;
use libafl_bolts::{
rands::Rand,
@ -31,7 +32,7 @@ pub type IntMutatorsType = tuple_list_type!(
pub type IntMutatorsCrossoverType = tuple_list_type!(CrossoverMutator);
/// Mapped mutators for integer-like inputs that implement some form of crossover.
pub type MappedIntMutatorsCrossoverType<F> = tuple_list_type!(MappedCrossoverMutator<F>);
pub type MappedIntMutatorsCrossoverType<F, I> = tuple_list_type!(MappedCrossoverMutator<F, I>);
/// Mutators for integer-like inputs without crossover mutations
pub type IntMutatorsNoCrossoverType = tuple_list_type!(
@ -64,7 +65,9 @@ pub fn int_mutators_crossover() -> IntMutatorsCrossoverType {
/// Mutators for integer-like inputs that implement some form of crossover with a mapper to extract the crossed over information.
#[must_use]
pub fn mapped_int_mutators_crossover<F>(input_mapper: F) -> MappedIntMutatorsCrossoverType<F> {
pub fn mapped_int_mutators_crossover<F, I>(
input_mapper: F,
) -> MappedIntMutatorsCrossoverType<F, I> {
tuple_list!(MappedCrossoverMutator::new(input_mapper))
}
@ -77,14 +80,14 @@ pub fn int_mutators() -> IntMutatorsType {
}
/// Mapped mutators for integer-like inputs
pub type MappedIntMutatorsType<F1, F2> = tuple_list_type!(
pub type MappedIntMutatorsType<F1, F2, I> = tuple_list_type!(
MappingMutator<BitFlipMutator,F1>,
MappingMutator<NegateMutator,F1>,
MappingMutator<IncMutator,F1>,
MappingMutator<DecMutator,F1>,
MappingMutator<TwosComplementMutator,F1>,
MappingMutator<RandMutator,F1>,
MappingMutator<MappedCrossoverMutator<F2>,F1>
MappingMutator<MappedCrossoverMutator<F2, I>,F1>
);
/// Mapped mutators for integer-like inputs
@ -93,7 +96,7 @@ pub type MappedIntMutatorsType<F1, F2> = tuple_list_type!(
pub fn mapped_int_mutators<F1, F2, IO, II>(
current_input_mapper: F1,
input_from_corpus_mapper: F2,
) -> MappedIntMutatorsType<F1, F2>
) -> MappedIntMutatorsType<F1, F2, IO>
where
F1: Clone + FnMut(&mut IO) -> &mut II,
{
@ -365,8 +368,7 @@ pub struct CrossoverMutator;
impl<I, S> Mutator<I, S> for CrossoverMutator
where
S: HasRand + HasCorpus,
S::Corpus: Corpus<Input = I>,
S: HasRand + HasCorpus<I>,
I: Copy,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
@ -389,24 +391,28 @@ impl Named for CrossoverMutator {
}
/// Crossover mutation for integer-like inputs with custom state extraction function
#[derive(Debug)]
pub struct MappedCrossoverMutator<F> {
pub struct MappedCrossoverMutator<F, I> {
input_mapper: F,
phantom: PhantomData<I>,
}
impl<F> MappedCrossoverMutator<F> {
impl<F, I> MappedCrossoverMutator<F, I> {
/// Create a new [`MappedCrossoverMutator`]
pub fn new(input_mapper: F) -> Self {
Self { input_mapper }
Self {
input_mapper,
phantom: PhantomData,
}
}
}
impl<I, S, F> Mutator<I, S> for MappedCrossoverMutator<F>
impl<I, O, S, F> Mutator<O, S> for MappedCrossoverMutator<F, I>
where
S: HasRand + HasCorpus,
for<'b> F: Fn(&'b <S::Corpus as Corpus>::Input) -> &'b I,
I: Clone,
S: HasRand + HasCorpus<I>,
for<'b> F: Fn(&'b I) -> &'b O,
O: Clone,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
fn mutate(&mut self, state: &mut S, input: &mut O) -> Result<MutationResult, Error> {
let id = random_corpus_id_with_disabled!(state.corpus(), state.rand_mut());
if state.corpus().current().is_some_and(|cur| cur == id) {
@ -421,7 +427,7 @@ where
}
}
impl<F> Named for MappedCrossoverMutator<F> {
impl<F, I> Named for MappedCrossoverMutator<F, I> {
fn name(&self) -> &Cow<'static, str> {
&Cow::Borrowed("MappedCrossoverMutator")
}

View File

@ -215,7 +215,7 @@ impl<SM> Named for LoggerScheduledMutator<SM> {
impl<I, S, SM> Mutator<I, S> for LoggerScheduledMutator<SM>
where
S: HasRand + HasCorpus,
S: HasRand + HasCorpus<I>,
SM: ScheduledMutator<I, S>,
SM::Mutations: MutatorsTuple<I, S> + NamedTuple,
{
@ -258,7 +258,7 @@ where
impl<I, S, SM> ScheduledMutator<I, S> for LoggerScheduledMutator<SM>
where
S: HasRand + HasCorpus,
S: HasRand + HasCorpus<I>,
SM: ScheduledMutator<I, S>,
SM::Mutations: MutatorsTuple<I, S> + NamedTuple,
{

View File

@ -31,7 +31,7 @@ use crate::{
},
observers::cmp::{AFLppCmpValuesMetadata, CmpValues, CmpValuesMetadata},
stages::TaintMetadata,
state::{HasCorpus, HasMaxSize, HasRand},
state::{HasMaxSize, HasRand},
Error, HasMetadata,
};
@ -1305,7 +1305,7 @@ impl AFLppRedQueen {
impl<I, S> MultiMutator<I, S> for AFLppRedQueen
where
S: HasMetadata + HasRand + HasMaxSize + HasCorpus + HasCurrentCorpusId,
S: HasMetadata + HasRand + HasMaxSize + HasCurrentCorpusId,
I: HasMutatorResizableBytes + From<Vec<u8>>,
{
#[expect(clippy::needless_range_loop, clippy::too_many_lines)]

View File

@ -10,7 +10,7 @@ use core::{
use libafl_bolts::{rands::Rand, Error, HasLen, Named};
use crate::{
corpus::{Corpus, CorpusId, HasTestcase, Testcase},
corpus::{CorpusId, HasTestcase, Testcase},
inputs::{BytesInput, HasMutatorBytes, HasMutatorResizableBytes},
mutators::{rand_range, MutationResult, Mutator, Tokens},
nonzero,
@ -32,8 +32,7 @@ pub type UnicodeInput = (BytesInput, UnicodeIdentificationMetadata);
impl<S> MutatedTransform<BytesInput, S> for UnicodeInput
where
S: HasCorpus + HasTestcase,
S::Corpus: Corpus<Input = BytesInput>,
S: HasCorpus<BytesInput> + HasTestcase<BytesInput>,
{
type Post = UnicodeIdentificationMetadata;
@ -50,7 +49,7 @@ where
impl<S> MutatedTransformPost<S> for UnicodeIdentificationMetadata
where
S: HasTestcase,
S: HasTestcase<BytesInput>,
{
fn post_exec(self, state: &mut S, corpus_id: Option<CorpusId>) -> Result<(), Error> {
if let Some(corpus_id) = corpus_id {

View File

@ -104,17 +104,17 @@ impl TopAccountingMetadata {
/// A minimizer scheduler using coverage accounting
#[derive(Debug)]
pub struct CoverageAccountingScheduler<'a, CS, O> {
pub struct CoverageAccountingScheduler<'a, CS, I, O> {
accounting_map: &'a [u32],
skip_non_favored_prob: f64,
inner: IndexesLenTimeMinimizerScheduler<CS, O>,
inner: IndexesLenTimeMinimizerScheduler<CS, I, O>,
}
impl<CS, O, S> Scheduler<<S::Corpus as Corpus>::Input, S> for CoverageAccountingScheduler<'_, CS, O>
impl<CS, I, O, S> Scheduler<I, S> for CoverageAccountingScheduler<'_, CS, I, O>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus + HasMetadata + HasRand,
<S::Corpus as Corpus>::Input: HasLen,
CS: Scheduler<I, S>,
S: HasCorpus<I> + HasMetadata + HasRand,
I: HasLen,
O: CanTrack,
{
fn on_add(&mut self, state: &mut S, id: CorpusId) -> Result<(), Error> {
@ -122,12 +122,7 @@ where
self.inner.on_add(state, id)
}
fn on_evaluation<OT>(
&mut self,
state: &mut S,
input: &<S::Corpus as Corpus>::Input,
observers: &OT,
) -> Result<(), Error>
fn on_evaluation<OT>(&mut self, state: &mut S, input: &I, observers: &OT) -> Result<(), Error>
where
OT: MatchName,
{
@ -173,7 +168,7 @@ where
}
}
impl<'a, CS, O> CoverageAccountingScheduler<'a, CS, O>
impl<'a, CS, I, O> CoverageAccountingScheduler<'a, CS, I, O>
where
O: CanTrack,
{
@ -181,7 +176,7 @@ where
#[expect(clippy::cast_possible_wrap)]
pub fn update_accounting_score<S>(&self, state: &mut S, id: CorpusId) -> Result<(), Error>
where
S: HasCorpus + HasMetadata,
S: HasCorpus<I> + HasMetadata,
{
let mut indexes = vec![];
let mut new_favoreds = vec![];
@ -268,7 +263,7 @@ where
/// Cull the `Corpus`
pub fn accounting_cull<S>(&self, state: &S) -> Result<(), Error>
where
S: HasCorpus + HasMetadata,
S: HasCorpus<I> + HasMetadata,
{
let Some(top_rated) = state.metadata_map().get::<TopAccountingMetadata>() else {
return Ok(());

View File

@ -72,28 +72,26 @@ impl Default for TopRatedsMetadata {
///
/// E.g., it can use all the coverage seen so far to prioritize [`Testcase`]`s` using a [`TestcaseScore`].
#[derive(Debug, Clone)]
pub struct MinimizerScheduler<CS, F, M, S> {
pub struct MinimizerScheduler<CS, F, I, M, S> {
base: CS,
skip_non_favored_prob: f64,
remove_metadata: bool,
phantom: PhantomData<(F, M, S)>,
phantom: PhantomData<(F, I, M, S)>,
}
impl<CS, F, M, O, S> RemovableScheduler<<S::Corpus as Corpus>::Input, S>
for MinimizerScheduler<CS, F, M, O>
impl<CS, F, M, I, O, S> RemovableScheduler<I, S> for MinimizerScheduler<CS, F, I, M, O>
where
CS: RemovableScheduler<<S::Corpus as Corpus>::Input, S>
+ Scheduler<<S::Corpus as Corpus>::Input, S>,
F: TestcaseScore<S>,
CS: RemovableScheduler<I, S> + Scheduler<I, S>,
F: TestcaseScore<I, S>,
M: for<'a> AsIter<'a, Item = usize> + SerdeAny + HasRefCnt,
S: HasCorpus + HasMetadata + HasRand,
S: HasCorpus<I> + HasMetadata + HasRand,
{
/// Replaces the [`Testcase`] at the given [`CorpusId`]
fn on_replace(
&mut self,
state: &mut S,
id: CorpusId,
testcase: &Testcase<<S::Corpus as Corpus>::Input>,
testcase: &Testcase<I>,
) -> Result<(), Error> {
self.base.on_replace(state, id, testcase)?;
self.update_score(state, id)
@ -104,7 +102,7 @@ where
&mut self,
state: &mut S,
id: CorpusId,
testcase: &Option<Testcase<<S::Corpus as Corpus>::Input>>,
testcase: &Option<Testcase<I>>,
) -> Result<(), Error> {
self.base.on_remove(state, id, testcase)?;
let mut entries =
@ -188,12 +186,12 @@ where
}
}
impl<CS, F, M, O, S> Scheduler<<S::Corpus as Corpus>::Input, S> for MinimizerScheduler<CS, F, M, O>
impl<CS, F, I, M, O, S> Scheduler<I, S> for MinimizerScheduler<CS, F, I, M, O>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
F: TestcaseScore<S>,
CS: Scheduler<I, S>,
F: TestcaseScore<I, S>,
M: for<'a> AsIter<'a, Item = usize> + SerdeAny + HasRefCnt,
S: HasCorpus + HasMetadata + HasRand,
S: HasCorpus<I> + HasMetadata + HasRand,
{
/// Called when a [`Testcase`] is added to the corpus
fn on_add(&mut self, state: &mut S, id: CorpusId) -> Result<(), Error> {
@ -202,12 +200,7 @@ where
}
/// An input has been evaluated
fn on_evaluation<OT>(
&mut self,
state: &mut S,
input: &<S::Corpus as Corpus>::Input,
observers: &OT,
) -> Result<(), Error>
fn on_evaluation<OT>(&mut self, state: &mut S, input: &I, observers: &OT) -> Result<(), Error>
where
OT: MatchName,
{
@ -243,7 +236,7 @@ where
}
}
impl<CS, F, M, O> MinimizerScheduler<CS, F, M, O>
impl<CS, F, I, M, O> MinimizerScheduler<CS, F, I, M, O>
where
M: for<'a> AsIter<'a, Item = usize> + SerdeAny + HasRefCnt,
{
@ -251,8 +244,8 @@ where
#[expect(clippy::cast_possible_wrap)]
pub fn update_score<S>(&self, state: &mut S, id: CorpusId) -> Result<(), Error>
where
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata,
F: TestcaseScore<I, S>,
S: HasCorpus<I> + HasMetadata,
{
// Create a new top rated meta if not existing
if state.metadata_map().get::<TopRatedsMetadata>().is_none() {
@ -328,7 +321,7 @@ where
/// Cull the [`Corpus`] using the [`MinimizerScheduler`]
pub fn cull<S>(&self, state: &S) -> Result<(), Error>
where
S: HasCorpus + HasMetadata,
S: HasCorpus<I> + HasMetadata,
{
let Some(top_rated) = state.metadata_map().get::<TopRatedsMetadata>() else {
return Ok(());
@ -356,7 +349,7 @@ where
Ok(())
}
}
impl<CS, F, M, O> HasQueueCycles for MinimizerScheduler<CS, F, M, O>
impl<CS, F, I, M, O> HasQueueCycles for MinimizerScheduler<CS, F, I, M, O>
where
CS: HasQueueCycles,
{
@ -364,7 +357,7 @@ where
self.base.queue_cycles()
}
}
impl<CS, F, M, O> MinimizerScheduler<CS, F, M, O>
impl<CS, F, I, M, O> MinimizerScheduler<CS, F, I, M, O>
where
O: CanTrack,
{
@ -425,10 +418,10 @@ where
}
/// A [`MinimizerScheduler`] with [`LenTimeMulTestcaseScore`] to prioritize quick and small [`Testcase`]`s`.
pub type LenTimeMinimizerScheduler<CS, M, O> =
MinimizerScheduler<CS, LenTimeMulTestcaseScore, M, O>;
pub type LenTimeMinimizerScheduler<CS, I, M, O> =
MinimizerScheduler<CS, LenTimeMulTestcaseScore, I, M, O>;
/// A [`MinimizerScheduler`] with [`LenTimeMulTestcaseScore`] to prioritize quick and small [`Testcase`]`s`
/// that exercise all the entries registered in the [`MapIndexesMetadata`].
pub type IndexesLenTimeMinimizerScheduler<CS, O> =
MinimizerScheduler<CS, LenTimeMulTestcaseScore, MapIndexesMetadata, O>;
pub type IndexesLenTimeMinimizerScheduler<CS, I, O> =
MinimizerScheduler<CS, LenTimeMulTestcaseScore, I, MapIndexesMetadata, O>;

View File

@ -66,14 +66,14 @@ pub trait RemovableScheduler<I, S> {
}
/// Called when a [`Testcase`] is evaluated
pub fn on_add_metadata_default<CS, S>(
pub fn on_add_metadata_default<CS, I, S>(
scheduler: &mut CS,
state: &mut S,
id: CorpusId,
) -> Result<(), Error>
where
CS: AflScheduler,
S: HasTestcase + HasCorpus,
S: HasTestcase<I> + HasCorpus<I>,
{
let current_id = *state.corpus().current();
@ -131,9 +131,9 @@ where
}
/// Called when choosing the next [`Testcase`]
pub fn on_next_metadata_default<S>(state: &mut S) -> Result<(), Error>
pub fn on_next_metadata_default<I, S>(state: &mut S) -> Result<(), Error>
where
S: HasCorpus + HasTestcase,
S: HasCorpus<I> + HasTestcase<I>,
{
let current_id = *state.corpus().current();
@ -215,7 +215,7 @@ pub struct RandScheduler<S> {
impl<I, S> Scheduler<I, S> for RandScheduler<S>
where
S: HasCorpus + HasRand,
S: HasCorpus<I> + HasRand,
{
fn on_add(&mut self, state: &mut S, id: CorpusId) -> Result<(), Error> {
// Set parent id

View File

@ -326,7 +326,7 @@ impl<C, O> HasQueueCycles for PowerQueueScheduler<C, O> {
impl<C, I, O, S> Scheduler<I, S> for PowerQueueScheduler<C, O>
where
S: HasCorpus + HasMetadata + HasTestcase,
S: HasCorpus<I> + HasMetadata + HasTestcase<I>,
O: Hash,
C: AsRef<O>,
{

View File

@ -63,10 +63,10 @@ impl<F> ProbabilitySamplingScheduler<F> {
}
/// Calculate the score and store in `ProbabilityMetadata`
pub fn store_probability<S>(&self, state: &mut S, id: CorpusId) -> Result<(), Error>
pub fn store_probability<I, S>(&self, state: &mut S, id: CorpusId) -> Result<(), Error>
where
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata + HasRand,
F: TestcaseScore<I, S>,
S: HasCorpus<I> + HasMetadata + HasRand,
{
let prob = F::compute(state, &mut *state.corpus().get(id)?.borrow_mut())?;
debug_assert!(
@ -83,16 +83,16 @@ impl<F> ProbabilitySamplingScheduler<F> {
}
}
impl<F, S> RemovableScheduler<<S::Corpus as Corpus>::Input, S> for ProbabilitySamplingScheduler<F>
impl<F, I, S> RemovableScheduler<I, S> for ProbabilitySamplingScheduler<F>
where
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata + HasRand,
F: TestcaseScore<I, S>,
S: HasCorpus<I> + HasMetadata + HasRand,
{
fn on_remove(
&mut self,
state: &mut S,
id: CorpusId,
_testcase: &Option<Testcase<<S::Corpus as Corpus>::Input>>,
_testcase: &Option<Testcase<I>>,
) -> Result<(), Error> {
let meta = state
.metadata_map_mut()
@ -108,7 +108,7 @@ where
&mut self,
state: &mut S,
id: CorpusId,
_prev: &Testcase<<S::Corpus as Corpus>::Input>,
_prev: &Testcase<I>,
) -> Result<(), Error> {
let meta = state
.metadata_map_mut()
@ -122,10 +122,10 @@ where
}
}
impl<F, S> Scheduler<<S::Corpus as Corpus>::Input, S> for ProbabilitySamplingScheduler<F>
impl<F, I, S> Scheduler<I, S> for ProbabilitySamplingScheduler<F>
where
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata + HasRand,
F: TestcaseScore<I, S>,
S: HasCorpus<I> + HasMetadata + HasRand,
{
fn on_add(&mut self, state: &mut S, id: CorpusId) -> Result<(), Error> {
let current_id = *state.corpus().current();
@ -202,14 +202,11 @@ mod tests {
#[derive(Debug, Clone)]
pub struct UniformDistribution {}
impl<S> TestcaseScore<S> for UniformDistribution
impl<I, S> TestcaseScore<I, S> for UniformDistribution
where
S: HasCorpus,
S: HasCorpus<I>,
{
fn compute(
_state: &S,
_: &mut Testcase<<S::Corpus as Corpus>::Input>,
) -> Result<f64, Error> {
fn compute(_state: &S, _: &mut Testcase<I>) -> Result<f64, Error> {
Ok(FACTOR)
}
}

View File

@ -20,7 +20,7 @@ impl<I, S> RemovableScheduler<I, S> for QueueScheduler {}
impl<I, S> Scheduler<I, S> for QueueScheduler
where
S: HasCorpus,
S: HasCorpus<I>,
{
fn on_add(&mut self, state: &mut S, id: CorpusId) -> Result<(), Error> {
// Set parent id

View File

@ -15,13 +15,9 @@ use crate::{
};
/// Compute the favor factor of a [`Testcase`]. Higher is better.
pub trait TestcaseScore<S>
where
S: HasCorpus,
{
pub trait TestcaseScore<I, S> {
/// Computes the favor factor of a [`Testcase`]. Higher is better.
fn compute(state: &S, entry: &mut Testcase<<S::Corpus as Corpus>::Input>)
-> Result<f64, Error>;
fn compute(state: &S, entry: &mut Testcase<I>) -> Result<f64, Error>;
}
/// Multiply the testcase size with the execution time.
@ -29,16 +25,13 @@ where
#[derive(Debug, Clone)]
pub struct LenTimeMulTestcaseScore {}
impl<S> TestcaseScore<S> for LenTimeMulTestcaseScore
impl<I, S> TestcaseScore<I, S> for LenTimeMulTestcaseScore
where
S: HasCorpus,
<S::Corpus as Corpus>::Input: HasLen,
S: HasCorpus<I>,
I: HasLen,
{
#[expect(clippy::cast_precision_loss)]
fn compute(
state: &S,
entry: &mut Testcase<<S::Corpus as Corpus>::Input>,
) -> Result<f64, Error> {
fn compute(state: &S, entry: &mut Testcase<I>) -> Result<f64, Error> {
// TODO maybe enforce entry.exec_time().is_some()
Ok(entry.exec_time().map_or(1, |d| d.as_millis()) as f64
* entry.load_len(state.corpus())? as f64)
@ -55,16 +48,13 @@ const HAVOC_MAX_MULT: f64 = 64.0;
#[derive(Debug, Clone)]
pub struct CorpusPowerTestcaseScore {}
impl<S> TestcaseScore<S> for CorpusPowerTestcaseScore
impl<I, S> TestcaseScore<I, S> for CorpusPowerTestcaseScore
where
S: HasCorpus + HasMetadata,
S: HasCorpus<I> + HasMetadata,
{
/// Compute the `power` we assign to each corpus entry
#[expect(clippy::cast_precision_loss, clippy::too_many_lines)]
fn compute(
state: &S,
entry: &mut Testcase<<S::Corpus as Corpus>::Input>,
) -> Result<f64, Error> {
fn compute(state: &S, entry: &mut Testcase<I>) -> Result<f64, Error> {
let psmeta = state.metadata::<SchedulerMetadata>()?;
let fuzz_mu = if let Some(strat) = psmeta.strat() {
@ -272,16 +262,13 @@ where
#[derive(Debug, Clone)]
pub struct CorpusWeightTestcaseScore {}
impl<S> TestcaseScore<S> for CorpusWeightTestcaseScore
impl<I, S> TestcaseScore<I, S> for CorpusWeightTestcaseScore
where
S: HasCorpus + HasMetadata,
S: HasCorpus<I> + HasMetadata,
{
/// Compute the `weight` used in weighted corpus entry selection algo
#[expect(clippy::cast_precision_loss)]
fn compute(
state: &S,
entry: &mut Testcase<<S::Corpus as Corpus>::Input>,
) -> Result<f64, Error> {
fn compute(state: &S, entry: &mut Testcase<I>) -> Result<f64, Error> {
let mut weight = 1.0;
let psmeta = state.metadata::<SchedulerMetadata>()?;

View File

@ -90,9 +90,9 @@ impl TuneableScheduler {
}
/// Gets the current corpus entry id
pub fn get_current<S>(state: &S) -> CorpusId
pub fn get_current<I, S>(state: &S) -> CorpusId
where
S: HasCorpus,
S: HasCorpus<I>,
{
state
.corpus()
@ -105,7 +105,7 @@ impl<I, S> RemovableScheduler<I, S> for TuneableScheduler {}
impl<I, S> Scheduler<I, S> for TuneableScheduler
where
S: HasCorpus + HasMetadata,
S: HasCorpus<I> + HasMetadata,
{
fn on_add(&mut self, state: &mut S, id: CorpusId) -> Result<(), Error> {
// Set parent id

View File

@ -155,10 +155,10 @@ where
/// Create a new alias table when the fuzzer finds a new corpus entry
#[expect(clippy::cast_precision_loss)]
pub fn create_alias_table<S>(&self, state: &mut S) -> Result<(), Error>
pub fn create_alias_table<I, S>(&self, state: &mut S) -> Result<(), Error>
where
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata,
F: TestcaseScore<I, S>,
S: HasCorpus<I> + HasMetadata,
{
let n = state.corpus().count();
@ -303,12 +303,12 @@ impl<C, F, O> HasQueueCycles for WeightedScheduler<C, F, O> {
}
}
impl<C, F, O, S> Scheduler<<S::Corpus as Corpus>::Input, S> for WeightedScheduler<C, F, O>
impl<C, F, I, O, S> Scheduler<I, S> for WeightedScheduler<C, F, O>
where
C: AsRef<O> + Named,
F: TestcaseScore<S>,
F: TestcaseScore<I, S>,
O: Hash,
S: HasCorpus + HasMetadata + HasRand + HasTestcase,
S: HasCorpus<I> + HasMetadata + HasRand + HasTestcase<I>,
{
/// Called when a [`Testcase`] is added to the corpus
fn on_add(&mut self, state: &mut S, id: CorpusId) -> Result<(), Error> {
@ -317,12 +317,7 @@ where
Ok(())
}
fn on_evaluation<OT>(
&mut self,
state: &mut S,
_input: &<S::Corpus as Corpus>::Input,
observers: &OT,
) -> Result<(), Error>
fn on_evaluation<OT>(&mut self, state: &mut S, _input: &I, observers: &OT) -> Result<(), Error>
where
OT: MatchName,
{

View File

@ -75,7 +75,7 @@ libafl_bolts::impl_serdeany!(FuzzTime);
/// The [`AflStatsStage`] is a Stage that calculates and writes
/// AFL++'s `fuzzer_stats` and `plot_data` information.
#[derive(Debug, Clone)]
pub struct AflStatsStage<C, E, EM, O, S, Z> {
pub struct AflStatsStage<C, E, EM, I, O, S, Z> {
map_observer_handle: Handle<C>,
stats_file_path: Option<PathBuf>,
plot_file_path: Option<PathBuf>,
@ -114,7 +114,7 @@ pub struct AflStatsStage<C, E, EM, O, S, Z> {
autotokens_enabled: bool,
/// The core we are bound to
core_id: CoreId,
phantom_data: PhantomData<(O, E, EM, S, Z)>,
phantom_data: PhantomData<(E, EM, I, O, S, Z)>,
}
/// AFL++'s `fuzzer_stats`
@ -236,13 +236,14 @@ pub struct AFLPlotData<'a> {
edges_found: &'a u64,
}
impl<C, E, EM, O, S, Z> Stage<E, EM, S, Z> for AflStatsStage<C, E, EM, O, S, Z>
impl<C, E, EM, I, O, S, Z> Stage<E, EM, S, Z> for AflStatsStage<C, E, EM, I, O, S, Z>
where
C: AsRef<O> + Named,
E: HasObservers,
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
Z: HasScheduler<<S::Corpus as Corpus>::Input, S>,
EM: EventFirer<I, S>,
Z: HasScheduler<I, S>,
S: HasImported
+ HasCorpus
+ HasCorpus<I>
+ HasMetadata
+ HasStartTime
+ HasExecutions
@ -441,17 +442,17 @@ where
}
}
impl<C, E, EM, O, S, Z> AflStatsStage<C, E, EM, O, S, Z>
impl<C, E, EM, I, O, S, Z> AflStatsStage<C, E, EM, I, O, S, Z>
where
E: HasObservers,
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
S: HasImported + HasCorpus + HasMetadata + HasExecutions,
EM: EventFirer<I, S>,
S: HasImported + HasMetadata + HasExecutions,
C: AsRef<O> + Named,
O: MapObserver,
{
/// Builder for `AflStatsStage`
#[must_use]
pub fn builder() -> AflStatsStageBuilder<C, E, EM, O, S, Z> {
pub fn builder() -> AflStatsStageBuilder<C, E, EM, I, O, S, Z> {
AflStatsStageBuilder::new()
}
@ -479,13 +480,13 @@ where
Ok(())
}
fn maybe_update_is_favored_size(&mut self, testcase: &Testcase<<S::Corpus as Corpus>::Input>) {
fn maybe_update_is_favored_size(&mut self, testcase: &Testcase<I>) {
if testcase.has_metadata::<IsFavoredMetadata>() {
self.is_favored_size += 1;
}
}
fn maybe_update_slowest_exec(&mut self, testcase: &Testcase<<S::Corpus as Corpus>::Input>) {
fn maybe_update_slowest_exec(&mut self, testcase: &Testcase<I>) {
if let Some(exec_time) = testcase.exec_time() {
if exec_time > &self.slowest_exec {
self.slowest_exec = *exec_time;
@ -497,7 +498,7 @@ where
self.has_fuzzed_size += 1;
}
fn maybe_update_max_depth(&mut self, testcase: &Testcase<<S::Corpus as Corpus>::Input>) {
fn maybe_update_max_depth(&mut self, testcase: &Testcase<I>) {
if let Ok(metadata) = testcase.metadata::<SchedulerTestcaseMetadata>() {
if metadata.depth() > self.max_depth {
self.max_depth = metadata.depth();
@ -510,11 +511,7 @@ where
}
#[cfg(feature = "track_hit_feedbacks")]
fn maybe_update_last_crash(
&mut self,
testcase: &Testcase<<S::Corpus as Corpus>::Input>,
state: &S,
) {
fn maybe_update_last_crash(&mut self, testcase: &Testcase<I>, state: &S) {
#[cfg(feature = "track_hit_feedbacks")]
if testcase
.hit_objectives()
@ -526,11 +523,7 @@ where
}
#[cfg(feature = "track_hit_feedbacks")]
fn maybe_update_last_hang(
&mut self,
testcase: &Testcase<<S::Corpus as Corpus>::Input>,
state: &S,
) {
fn maybe_update_last_hang(&mut self, testcase: &Testcase<I>, state: &S) {
if testcase
.hit_objectives()
.contains(&Cow::Borrowed(TIMEOUT_FEEDBACK_NAME))
@ -652,7 +645,7 @@ pub fn get_run_cmdline() -> Cow<'static, str> {
/// The Builder for `AflStatsStage`
#[derive(Debug)]
pub struct AflStatsStageBuilder<C, E, EM, O, S, Z> {
pub struct AflStatsStageBuilder<C, E, EM, I, O, S, Z> {
stats_file_path: Option<PathBuf>,
plot_file_path: Option<PathBuf>,
core_id: Option<CoreId>,
@ -664,16 +657,16 @@ pub struct AflStatsStageBuilder<C, E, EM, O, S, Z> {
banner: String,
version: String,
target_mode: String,
phantom_data: PhantomData<(O, E, EM, S, Z)>,
phantom_data: PhantomData<(E, EM, I, O, S, Z)>,
}
impl<C, E, EM, O, S, Z> AflStatsStageBuilder<C, E, EM, O, S, Z>
impl<C, E, EM, I, O, S, Z> AflStatsStageBuilder<C, E, EM, I, O, S, Z>
where
E: HasObservers,
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
S: HasImported + HasCorpus + HasMetadata + HasExecutions,
C: AsRef<O> + Named,
E: HasObservers,
EM: EventFirer<I, S>,
O: MapObserver,
S: HasImported + HasMetadata + HasExecutions,
{
fn new() -> Self {
Self {
@ -785,7 +778,8 @@ where
/// Cannot create the plot file (if provided)
/// No `MapObserver` supplied to the builder
/// No `stats_file_path` provieded
pub fn build(self) -> Result<AflStatsStage<C, E, EM, O, S, Z>, Error> {
#[allow(clippy::type_complexity)]
pub fn build(self) -> Result<AflStatsStage<C, E, EM, I, O, S, Z>, Error> {
if self.map_observer_handle.is_none() {
return Err(Error::illegal_argument("Must set `map_observer`"));
}

View File

@ -79,33 +79,33 @@ impl Default for UnstableEntriesMetadata {
/// The calibration stage will measure the average exec time and the target's stability for this input.
#[derive(Clone, Debug)]
pub struct CalibrationStage<C, E, O, OT, S> {
pub struct CalibrationStage<C, E, I, O, OT, S> {
map_observer_handle: Handle<C>,
map_name: Cow<'static, str>,
name: Cow<'static, str>,
stage_max: usize,
/// If we should track stability
track_stability: bool,
phantom: PhantomData<(E, O, OT, S)>,
phantom: PhantomData<(E, I, O, OT, S)>,
}
impl<C, E, EM, O, OT, S, Z> Stage<E, EM, S, Z> for CalibrationStage<C, E, O, OT, S>
impl<C, E, EM, I, O, OT, S, Z> Stage<E, EM, S, Z> for CalibrationStage<C, E, I, O, OT, S>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers<Observers = OT>,
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
E: Executor<EM, I, S, Z> + HasObservers<Observers = OT>,
EM: EventFirer<I, S>,
O: MapObserver,
C: AsRef<O>,
for<'de> <O as MapObserver>::Entry:
Serialize + Deserialize<'de> + 'static + Default + Debug + Bounded,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus
OT: ObserversTuple<I, S>,
S: HasCorpus<I>
+ HasMetadata
+ HasNamedMetadata
+ HasExecutions
+ HasCurrentTestcase
+ HasCurrentTestcase<I>
+ HasCurrentCorpusId,
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: Input,
Z: Evaluator<E, EM, I, S>,
I: Input,
{
#[inline]
#[expect(clippy::too_many_lines, clippy::cast_precision_loss)]
@ -380,13 +380,12 @@ where
}
}
impl<C, E, O, OT, S> CalibrationStage<C, E, O, OT, S>
impl<C, E, I, O, OT, S> CalibrationStage<C, E, I, O, OT, S>
where
C: AsRef<O>,
O: MapObserver,
for<'it> O: AsIter<'it, Item = O::Entry>,
C: AsRef<O>,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus,
OT: ObserversTuple<I, S>,
{
/// Create a new [`CalibrationStage`].
#[must_use]
@ -419,7 +418,7 @@ where
}
}
impl<C, E, O, OT, S> Named for CalibrationStage<C, E, O, OT, S> {
impl<C, E, I, O, OT, S> Named for CalibrationStage<C, E, I, O, OT, S> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}

View File

@ -15,10 +15,10 @@ use libafl_bolts::{
use serde::{Deserialize, Serialize};
use crate::{
corpus::{Corpus, HasCurrentCorpusId},
corpus::HasCurrentCorpusId,
events::EventFirer,
executors::{Executor, HasObservers},
inputs::{HasMutatorBytes, HasMutatorResizableBytes},
inputs::HasMutatorResizableBytes,
mutators::mutations::buffer_copy,
nonzero,
observers::ObserversTuple,
@ -63,25 +63,25 @@ impl Ord for Earlier {
pub const COLORIZATION_STAGE_NAME: &str = "colorization";
/// The mutational stage using power schedules
#[derive(Clone, Debug)]
pub struct ColorizationStage<C, E, EM, O, S, Z> {
pub struct ColorizationStage<C, E, EM, I, O, S, Z> {
map_observer_handle: Handle<C>,
name: Cow<'static, str>,
phantom: PhantomData<(E, EM, O, E, S, Z)>,
phantom: PhantomData<(E, EM, I, O, E, S, Z)>,
}
impl<C, E, EM, O, S, Z> Named for ColorizationStage<C, E, EM, O, S, Z> {
impl<C, E, EM, I, O, S, Z> Named for ColorizationStage<C, E, EM, I, O, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<C, E, EM, O, S, Z> Stage<E, EM, S, Z> for ColorizationStage<C, E, EM, O, S, Z>
impl<C, E, EM, I, O, S, Z> Stage<E, EM, S, Z> for ColorizationStage<C, E, EM, I, O, S, Z>
where
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
E: HasObservers + Executor<EM, <S::Corpus as Corpus>::Input, S, Z>,
S: HasCorpus + HasMetadata + HasRand + HasNamedMetadata + HasCurrentCorpusId,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: HasMutatorResizableBytes + Clone,
EM: EventFirer<I, S>,
E: HasObservers + Executor<EM, I, S, Z>,
S: HasCorpus<I> + HasMetadata + HasRand + HasNamedMetadata + HasCurrentCorpusId,
E::Observers: ObserversTuple<I, S>,
I: HasMutatorResizableBytes + Clone,
O: Hash,
C: AsRef<O> + Named,
{
@ -107,7 +107,7 @@ where
}
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
RetryCountRestartHelper::clear_progress::<S>(state, &self.name)
}
}
@ -150,15 +150,15 @@ impl TaintMetadata {
libafl_bolts::impl_serdeany!(TaintMetadata);
impl<C, E, EM, O, S, Z> ColorizationStage<C, E, EM, O, S, Z>
impl<C, E, EM, I, O, S, Z> ColorizationStage<C, E, EM, I, O, S, Z>
where
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
EM: EventFirer<I, S>,
O: Hash,
C: AsRef<O> + Named,
E: HasObservers + Executor<EM, <S::Corpus as Corpus>::Input, S, Z>,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus + HasMetadata + HasRand + HasCurrentCorpusId + HasCurrentTestcase,
<S::Corpus as Corpus>::Input: HasMutatorResizableBytes + Clone,
E: HasObservers + Executor<EM, I, S, Z>,
E::Observers: ObserversTuple<I, S>,
S: HasCorpus<I> + HasMetadata + HasRand + HasCurrentCorpusId + HasCurrentTestcase<I>,
I: HasMutatorResizableBytes + Clone,
{
#[inline]
fn colorize(
@ -167,7 +167,7 @@ where
state: &mut S,
manager: &mut EM,
observer_handle: &Handle<C>,
) -> Result<<S::Corpus as Corpus>::Input, Error> {
) -> Result<I, Error> {
let mut input = state.current_input_cloned()?;
// The backup of the input
let backup = input.clone();
@ -308,7 +308,7 @@ where
executor: &mut E,
state: &mut S,
manager: &mut EM,
input: &<S::Corpus as Corpus>::Input,
input: &I,
observer_handle: &Handle<C>,
) -> Result<usize, Error> {
executor.observers_mut().pre_exec_all(state, input)?;

View File

@ -15,7 +15,7 @@ use libafl_bolts::{
#[cfg(all(feature = "concolic_mutation", feature = "introspection"))]
use crate::monitors::PerfFeature;
use crate::{
corpus::{Corpus, HasCurrentCorpusId},
corpus::HasCurrentCorpusId,
executors::{Executor, HasObservers},
observers::{concolic::ConcolicObserver, ObserversTuple},
stages::{RetryCountRestartHelper, Stage, TracingStage},
@ -32,29 +32,29 @@ use crate::{
/// Wraps a [`TracingStage`] to add concolic observing.
#[derive(Clone, Debug)]
pub struct ConcolicTracingStage<'a, EM, TE, S, Z> {
pub struct ConcolicTracingStage<'a, EM, I, TE, S, Z> {
name: Cow<'static, str>,
inner: TracingStage<EM, TE, S, Z>,
inner: TracingStage<EM, I, TE, S, Z>,
observer_handle: Handle<ConcolicObserver<'a>>,
}
/// The name for concolic tracer
pub const CONCOLIC_TRACING_STAGE_NAME: &str = "concolictracing";
impl<EM, TE, S, Z> Named for ConcolicTracingStage<'_, EM, TE, S, Z> {
impl<EM, I, TE, S, Z> Named for ConcolicTracingStage<'_, EM, I, TE, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, EM, TE, S, Z> Stage<E, EM, S, Z> for ConcolicTracingStage<'_, EM, TE, S, Z>
impl<E, EM, I, TE, S, Z> Stage<E, EM, S, Z> for ConcolicTracingStage<'_, EM, I, TE, S, Z>
where
TE: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
TE::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
TE: Executor<EM, I, S, Z> + HasObservers,
TE::Observers: ObserversTuple<I, S>,
S: HasExecutions
+ HasCorpus
+ HasCorpus<I>
+ HasNamedMetadata
+ HasCurrentTestcase
+ HasCurrentTestcase<I>
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
{
@ -89,11 +89,11 @@ where
}
}
impl<'a, EM, TE, S, Z> ConcolicTracingStage<'a, EM, TE, S, Z> {
impl<'a, EM, I, TE, S, Z> ConcolicTracingStage<'a, EM, I, TE, S, Z> {
/// Creates a new default tracing stage using the given [`Executor`], observing traces from a
/// [`ConcolicObserver`] with the given name.
pub fn new(
inner: TracingStage<EM, TE, S, Z>,
inner: TracingStage<EM, I, TE, S, Z>,
observer_handle: Handle<ConcolicObserver<'a>>,
) -> Self {
let observer_name = observer_handle.name().clone();
@ -353,9 +353,9 @@ fn generate_mutations(iter: impl Iterator<Item = (SymExprRef, SymExpr)>) -> Vec<
/// A mutational stage that uses Z3 to solve concolic constraints attached to the [`crate::corpus::Testcase`] by the [`ConcolicTracingStage`].
#[cfg(feature = "concolic_mutation")]
#[derive(Clone, Debug, Default)]
pub struct SimpleConcolicMutationalStage<Z> {
pub struct SimpleConcolicMutationalStage<I, Z> {
name: Cow<'static, str>,
phantom: PhantomData<Z>,
phantom: PhantomData<(I, Z)>,
}
#[cfg(feature = "concolic_mutation")]
@ -367,22 +367,22 @@ static mut SIMPLE_CONCOLIC_MUTATIONAL_ID: usize = 0;
pub const SIMPLE_CONCOLIC_MUTATIONAL_NAME: &str = "concolicmutation";
#[cfg(feature = "concolic_mutation")]
impl<Z> Named for SimpleConcolicMutationalStage<Z> {
impl<I, Z> Named for SimpleConcolicMutationalStage<I, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
#[cfg(feature = "concolic_mutation")]
impl<E, EM, S, Z> Stage<E, EM, S, Z> for SimpleConcolicMutationalStage<Z>
impl<E, EM, I, S, Z> Stage<E, EM, S, Z> for SimpleConcolicMutationalStage<I, Z>
where
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: HasMutatorBytes + Clone,
Z: Evaluator<E, EM, I, S>,
I: HasMutatorBytes + Clone,
S: HasExecutions
+ HasCorpus
+ HasCorpus<I>
+ HasMetadata
+ HasNamedMetadata
+ HasCurrentTestcase
+ HasCurrentTestcase<I>
+ MaybeHasClientPerfMonitor
+ HasCurrentCorpusId,
{
@ -434,7 +434,7 @@ where
}
#[cfg(feature = "concolic_mutation")]
impl<Z> SimpleConcolicMutationalStage<Z> {
impl<I, Z> SimpleConcolicMutationalStage<I, Z> {
#[must_use]
/// Construct this stage
pub fn new() -> Self {

View File

@ -35,20 +35,19 @@ impl_serdeany!(DumpToDiskMetadata);
/// The [`DumpToDiskStage`] is a stage that dumps the corpus and the solutions to disk
#[derive(Debug)]
pub struct DumpToDiskStage<CB1, CB2, EM, S, Z> {
pub struct DumpToDiskStage<CB1, CB2, EM, I, S, Z> {
solutions_dir: PathBuf,
corpus_dir: PathBuf,
to_bytes: CB1,
generate_filename: CB2,
phantom: PhantomData<(EM, S, Z)>,
phantom: PhantomData<(EM, I, S, Z)>,
}
impl<CB1, CB2, E, EM, S, P, Z> Stage<E, EM, S, Z> for DumpToDiskStage<CB1, CB2, EM, S, Z>
impl<CB1, CB2, E, EM, I, S, P, Z> Stage<E, EM, S, Z> for DumpToDiskStage<CB1, CB2, EM, I, S, Z>
where
CB1: FnMut(&Testcase<<S::Corpus as Corpus>::Input>, &S) -> Vec<u8>,
CB2: FnMut(&Testcase<<S::Corpus as Corpus>::Input>, &CorpusId) -> P,
S: HasCorpus + HasSolutions + HasRand + HasMetadata,
S::Solutions: Corpus<Input = <S::Corpus as Corpus>::Input>,
CB1: FnMut(&Testcase<I>, &S) -> Vec<u8>,
CB2: FnMut(&Testcase<I>, &CorpusId) -> P,
S: HasCorpus<I> + HasSolutions<I> + HasRand + HasMetadata,
P: AsRef<Path>,
{
#[inline]
@ -76,12 +75,10 @@ where
}
/// Implementation for `DumpToDiskStage` with a default `generate_filename` function.
impl<CB1, EM, S, Z>
DumpToDiskStage<CB1, fn(&Testcase<<S::Corpus as Corpus>::Input>, &CorpusId) -> String, EM, S, Z>
impl<CB1, EM, I, S, Z> DumpToDiskStage<CB1, fn(&Testcase<I>, &CorpusId) -> String, EM, I, S, Z>
where
S: HasCorpus + HasSolutions + HasRand + HasMetadata,
S::Solutions: Corpus<Input = <S::Corpus as Corpus>::Input>,
<S::Corpus as Corpus>::Input: Input,
S: HasSolutions<I> + HasRand + HasMetadata,
I: Input,
{
/// Create a new [`DumpToDiskStage`] with a default `generate_filename` function.
pub fn new<A, B>(to_bytes: CB1, corpus_dir: A, solutions_dir: B) -> Result<Self, Error>
@ -99,10 +96,7 @@ where
/// Default `generate_filename` function.
#[expect(clippy::trivially_copy_pass_by_ref)]
fn generate_filename(
testcase: &Testcase<<S::Corpus as Corpus>::Input>,
id: &CorpusId,
) -> String {
fn generate_filename(testcase: &Testcase<I>, id: &CorpusId) -> String {
[
Some(id.0.to_string()),
testcase.filename().clone(),
@ -119,10 +113,9 @@ where
}
}
impl<CB1, CB2, EM, S, Z> DumpToDiskStage<CB1, CB2, EM, S, Z>
impl<CB1, CB2, EM, I, S, Z> DumpToDiskStage<CB1, CB2, EM, I, S, Z>
where
S: HasCorpus + HasMetadata + HasSolutions,
S::Solutions: Corpus<Input = <S::Corpus as Corpus>::Input>,
S: HasMetadata + HasSolutions<I>,
{
/// Create a new [`DumpToDiskStage`] with a custom `generate_filename` function.
pub fn new_with_custom_filenames<A, B>(
@ -165,8 +158,9 @@ where
#[inline]
fn dump_state_to_disk<P: AsRef<Path>>(&mut self, state: &mut S) -> Result<(), Error>
where
CB1: FnMut(&Testcase<<S::Corpus as Corpus>::Input>, &S) -> Vec<u8>,
CB2: FnMut(&Testcase<<S::Corpus as Corpus>::Input>, &CorpusId) -> P,
S: HasCorpus<I>,
CB1: FnMut(&Testcase<I>, &S) -> Vec<u8>,
CB2: FnMut(&Testcase<I>, &CorpusId) -> P,
{
let (mut corpus_id, mut solutions_id) =
if let Some(meta) = state.metadata_map().get::<DumpToDiskMetadata>() {

View File

@ -51,32 +51,44 @@ pub static GENERALIZATION_STAGE_NAME: &str = "generalization";
/// A stage that runs a tracer executor
#[derive(Clone, Debug)]
pub struct GeneralizationStage<C, EM, O, OT, S, Z> {
pub struct GeneralizationStage<C, EM, I, O, OT, S, Z> {
name: Cow<'static, str>,
map_observer_handle: Handle<C>,
phantom: PhantomData<(EM, O, OT, S, Z)>,
phantom: PhantomData<(EM, I, O, OT, S, Z)>,
}
impl<C, EM, O, OT, S, Z> Named for GeneralizationStage<C, EM, O, OT, S, Z> {
impl<C, EM, I, O, OT, S, Z> Named for GeneralizationStage<C, EM, I, O, OT, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<C, E, EM, O, S, Z> Stage<E, EM, S, Z> for GeneralizationStage<C, EM, O, E::Observers, S, Z>
impl<C, E, EM, O, S, Z> Stage<E, EM, S, Z>
for GeneralizationStage<C, EM, BytesInput, O, E::Observers, S, Z>
where
O: MapObserver,
C: CanTrack + AsRef<O> + Named,
E: Executor<EM, BytesInput, S, Z> + HasObservers,
E::Observers: ObserversTuple<BytesInput, S>,
O: MapObserver,
S: HasExecutions
+ HasMetadata
+ HasCorpus
+ HasCorpus<BytesInput>
+ HasNamedMetadata
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
S::Corpus: Corpus<Input = BytesInput>,
{
#[inline]
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// TODO: We need to be able to resume better if something crashes or times out
RetryCountRestartHelper::should_restart::<S>(state, &self.name, 3)
}
#[inline]
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
// TODO: We need to be able to resume better if something crashes or times out
RetryCountRestartHelper::clear_progress::<S>(state, &self.name)
}
#[inline]
#[expect(clippy::too_many_lines)]
fn perform(
@ -326,26 +338,13 @@ where
Ok(())
}
#[inline]
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// TODO: We need to be able to resume better if something crashes or times out
RetryCountRestartHelper::should_restart(state, &self.name, 3)
}
#[inline]
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
// TODO: We need to be able to resume better if something crashes or times out
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<C, EM, O, OT, S, Z> GeneralizationStage<C, EM, O, OT, S, Z>
impl<C, EM, O, OT, S, Z> GeneralizationStage<C, EM, BytesInput, O, OT, S, Z>
where
O: MapObserver,
C: CanTrack + AsRef<O> + Named,
S: HasExecutions + HasMetadata + HasCorpus + MaybeHasClientPerfMonitor,
S::Corpus: Corpus<Input = BytesInput>,
S: HasExecutions + HasMetadata + HasCorpus<BytesInput> + MaybeHasClientPerfMonitor,
OT: ObserversTuple<BytesInput, S>,
{
/// Create a new [`GeneralizationStage`].
@ -372,7 +371,7 @@ where
input: &BytesInput,
) -> Result<bool, Error>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
E: Executor<EM, BytesInput, S, Z> + HasObservers,
E::Observers: ObserversTuple<BytesInput, S>,
{
start_timer!(state);
@ -414,7 +413,7 @@ where
split_char: u8,
) -> Result<(), Error>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers<Observers = OT>,
E: Executor<EM, BytesInput, S, Z> + HasObservers<Observers = OT>,
{
let mut start = 0;
while start < payload.len() {
@ -452,7 +451,7 @@ where
closing_char: u8,
) -> Result<(), Error>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers<Observers = OT>,
E: Executor<EM, BytesInput, S, Z> + HasObservers<Observers = OT>,
{
let mut index = 0;
while index < payload.len() {

View File

@ -6,33 +6,27 @@
use core::marker::PhantomData;
use crate::{
corpus::Corpus,
generators::Generator,
stages::Stage,
state::{HasCorpus, HasRand},
Error, Evaluator,
};
use crate::{generators::Generator, stages::Stage, state::HasRand, Error, Evaluator};
/// A [`Stage`] that generates a single input via a [`Generator`] and evaluates
/// it using the fuzzer, possibly adding it to the corpus.
///
/// This stage can be used to construct black-box (e.g., grammar-based) fuzzers.
#[derive(Debug)]
pub struct GenStage<G, S, Z>(G, PhantomData<(S, Z)>);
pub struct GenStage<G, I, S, Z>(G, PhantomData<(I, S, Z)>);
impl<G, S, Z> GenStage<G, S, Z> {
impl<G, I, S, Z> GenStage<G, I, S, Z> {
/// Create a new [`GenStage`].
pub fn new(g: G) -> Self {
Self(g, PhantomData)
}
}
impl<E, EM, G, S, Z> Stage<E, EM, S, Z> for GenStage<G, S, Z>
impl<E, EM, G, I, S, Z> Stage<E, EM, S, Z> for GenStage<G, I, S, Z>
where
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
S: HasCorpus + HasRand,
G: Generator<<S::Corpus as Corpus>::Input, S>,
G: Generator<I, S>,
S: HasRand,
Z: Evaluator<E, EM, I, S>,
{
#[inline]
fn perform(

View File

@ -27,11 +27,10 @@ use crate::{
// TODO multi mutators stage
/// Action performed after the un-transformed input is executed (e.g., updating metadata)
#[expect(unused_variables)]
pub trait MutatedTransformPost<S>: Sized {
/// Perform any post-execution steps necessary for the transformed input (e.g., updating metadata)
#[inline]
fn post_exec(self, state: &mut S, new_corpus_id: Option<CorpusId>) -> Result<(), Error> {
fn post_exec(self, _state: &mut S, _new_corpus_id: Option<CorpusId>) -> Result<(), Error> {
Ok(())
}
}
@ -43,10 +42,7 @@ impl<S> MutatedTransformPost<S> for () {}
///
/// This trait is implemented such that all testcases inherently transform to their inputs, should
/// the input be cloneable.
pub trait MutatedTransform<I, S>: Sized
where
I: Input,
{
pub trait MutatedTransform<I, S>: Sized {
/// Type indicating actions to be taken after the post-transformation input is executed
type Post: MutatedTransformPost<S>;
@ -60,9 +56,8 @@ where
// reflexive definition
impl<I, S> MutatedTransform<I, S> for I
where
I: Input + Clone,
S: HasCorpus,
S::Corpus: Corpus<Input = I>,
I: Clone,
S: HasCorpus<I>,
{
type Post = ();
@ -101,17 +96,17 @@ pub const DEFAULT_MUTATIONAL_MAX_ITERATIONS: usize = 128;
/// The default mutational stage
#[derive(Clone, Debug)]
pub struct StdMutationalStage<E, EM, I, M, S, Z> {
pub struct StdMutationalStage<E, EM, I1, I2, M, S, Z> {
/// The name
name: Cow<'static, str>,
/// The mutator(s) to use
mutator: M,
/// The maximum amount of iterations we should do each round
max_iterations: NonZeroUsize,
phantom: PhantomData<(E, EM, I, S, Z)>,
phantom: PhantomData<(E, EM, I1, I2, S, Z)>,
}
impl<E, EM, I, M, S, Z> MutationalStage<S> for StdMutationalStage<E, EM, I, M, S, Z>
impl<E, EM, I1, I2, M, S, Z> MutationalStage<S> for StdMutationalStage<E, EM, I1, I2, M, S, Z>
where
S: HasRand,
{
@ -140,25 +135,25 @@ static mut MUTATIONAL_STAGE_ID: usize = 0;
/// The name for mutational stage
pub static MUTATIONAL_STAGE_NAME: &str = "mutational";
impl<E, EM, I, M, S, Z> Named for StdMutationalStage<E, EM, I, M, S, Z> {
impl<E, EM, I1, I2, M, S, Z> Named for StdMutationalStage<E, EM, I1, I2, M, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, EM, I, M, S, Z> Stage<E, EM, S, Z> for StdMutationalStage<E, EM, I, M, S, Z>
impl<E, EM, I1, I2, M, S, Z> Stage<E, EM, S, Z> for StdMutationalStage<E, EM, I1, I2, M, S, Z>
where
M: Mutator<I, S>,
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
S: HasCorpus
+ HasRand
I1: Clone + MutatedTransform<I2, S>,
I2: Input,
M: Mutator<I1, S>,
S: HasRand
+ HasCorpus<I2>
+ HasMetadata
+ HasExecutions
+ HasNamedMetadata
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone,
<S::Corpus as Corpus>::Input: Input,
Z: Evaluator<E, EM, I2, S>,
{
#[inline]
fn perform(
@ -185,12 +180,12 @@ where
}
}
impl<E, EM, M, S, Z> StdMutationalStage<E, EM, <S::Corpus as Corpus>::Input, M, S, Z>
impl<E, EM, I, M, S, Z> StdMutationalStage<E, EM, I, I, M, S, Z>
where
M: Mutator<<S::Corpus as Corpus>::Input, S>,
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
S: HasCorpus + HasRand + HasCurrentCorpusId + MaybeHasClientPerfMonitor,
<S::Corpus as Corpus>::Input: Input + Clone,
M: Mutator<I, S>,
I: MutatedTransform<I, S> + Input + Clone,
S: HasCorpus<I> + HasRand + HasCurrentCorpusId + MaybeHasClientPerfMonitor,
Z: Evaluator<E, EM, I, S>,
{
/// Creates a new default mutational stage
pub fn new(mutator: M) -> Self {
@ -205,13 +200,13 @@ where
}
}
impl<E, EM, I, M, S, Z> StdMutationalStage<E, EM, I, M, S, Z>
impl<E, EM, I1, I2, M, S, Z> StdMutationalStage<E, EM, I1, I2, M, S, Z>
where
M: Mutator<I, S>,
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
S: HasCorpus + HasRand + HasCurrentTestcase + MaybeHasClientPerfMonitor,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone,
<S::Corpus as Corpus>::Input: Input,
I1: MutatedTransform<I2, S> + Clone,
I2: Input,
M: Mutator<I1, S>,
S: HasCorpus<I2> + HasRand + HasCurrentCorpusId + MaybeHasClientPerfMonitor,
Z: Evaluator<E, EM, I2, S>,
{
/// Creates a new transforming mutational stage with the default max iterations
pub fn transforming(mutator: M) -> Self {
@ -236,7 +231,16 @@ where
phantom: PhantomData,
}
}
}
impl<E, EM, I1, I2, M, S, Z> StdMutationalStage<E, EM, I1, I2, M, S, Z>
where
I1: MutatedTransform<I2, S> + Clone,
I2: Input,
M: Mutator<I1, S>,
S: HasRand + HasCurrentTestcase<I2> + MaybeHasClientPerfMonitor,
Z: Evaluator<E, EM, I2, S>,
{
/// Runs this (mutational) stage for the given testcase
fn perform_mutational(
&mut self,
@ -256,7 +260,7 @@ where
let num = self.iterations(state)?;
let mut testcase = state.current_testcase_mut()?;
let Ok(input) = I::try_transform_from(&mut testcase, state) else {
let Ok(input) = I1::try_transform_from(&mut testcase, state) else {
return Ok(());
};
drop(testcase);
@ -307,11 +311,10 @@ impl<E, EM, I, M, S, Z> Named for MultiMutationalStage<E, EM, I, M, S, Z> {
impl<E, EM, I, M, S, Z> Stage<E, EM, S, Z> for MultiMutationalStage<E, EM, I, M, S, Z>
where
I: Clone + MutatedTransform<I, S>,
M: MultiMutator<I, S>,
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
S: HasCorpus + HasRand + HasNamedMetadata + HasCurrentTestcase + HasCurrentCorpusId,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone,
<S::Corpus as Corpus>::Input: Input,
S: HasRand + HasNamedMetadata + HasCurrentTestcase<I> + HasCurrentCorpusId,
Z: Evaluator<E, EM, I, S>,
{
#[inline]
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {

View File

@ -11,10 +11,9 @@ use libafl_bolts::Named;
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
corpus::{Corpus, HasCurrentCorpusId},
corpus::HasCurrentCorpusId,
executors::{Executor, HasObservers},
fuzzer::Evaluator,
inputs::Input,
mark_feature_time,
mutators::{MutationResult, Mutator},
schedulers::{testcase_score::CorpusPowerTestcaseScore, TestcaseScore},
@ -23,7 +22,7 @@ use crate::{
MutationalStage, RetryCountRestartHelper, Stage,
},
start_timer,
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, MaybeHasClientPerfMonitor},
state::{HasCurrentTestcase, HasExecutions, HasRand, MaybeHasClientPerfMonitor},
Error, HasMetadata, HasNamedMetadata,
};
@ -48,8 +47,8 @@ impl<E, F, EM, I, M, S, Z> Named for PowerMutationalStage<E, F, EM, I, M, S, Z>
impl<E, F, EM, I, M, S, Z> MutationalStage<S> for PowerMutationalStage<E, F, EM, I, M, S, Z>
where
S: HasCurrentTestcase,
F: TestcaseScore<S>,
S: HasCurrentTestcase<I>,
F: TestcaseScore<I, S>,
{
type Mutator = M;
/// The mutator, added to this stage
@ -77,20 +76,18 @@ where
impl<E, F, EM, I, M, S, Z> Stage<E, EM, S, Z> for PowerMutationalStage<E, F, EM, I, M, S, Z>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
F: TestcaseScore<S>,
E: Executor<EM, I, S, Z> + HasObservers,
F: TestcaseScore<I, S>,
M: Mutator<I, S>,
S: HasCorpus
+ HasMetadata
S: HasMetadata
+ HasRand
+ HasExecutions
+ HasNamedMetadata
+ HasCurrentTestcase
+ HasCurrentTestcase<I>
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone + Input,
<S::Corpus as Corpus>::Input: Input,
Z: Evaluator<E, EM, I, S>,
I: MutatedTransform<I, S> + Clone,
{
#[inline]
#[expect(clippy::let_and_return)]
@ -117,14 +114,12 @@ where
impl<E, F, EM, I, M, S, Z> PowerMutationalStage<E, F, EM, I, M, S, Z>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
F: TestcaseScore<S>,
I: Input,
E: Executor<EM, I, S, Z> + HasObservers,
F: TestcaseScore<I, S>,
M: Mutator<I, S>,
S: HasCorpus + HasMetadata + HasRand + HasCurrentTestcase + MaybeHasClientPerfMonitor,
I: MutatedTransform<<S::Corpus as Corpus>::Input, S> + Clone + Input,
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: Input,
S: HasMetadata + HasRand + HasCurrentTestcase<I> + MaybeHasClientPerfMonitor,
I: MutatedTransform<I, S> + Clone,
Z: Evaluator<E, EM, I, S>,
{
/// Creates a new [`PowerMutationalStage`]
pub fn new(mutator: M) -> Self {

View File

@ -23,7 +23,7 @@ pub use mutational::StdMutationalPushStage;
use crate::{
common::HasNamedMetadata,
corpus::{Corpus, CorpusId, HasCurrentCorpusId},
corpus::{CorpusId, HasCurrentCorpusId},
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
executors::{Executor, ExitKind, HasObservers},
observers::ObserversTuple,
@ -43,7 +43,7 @@ pub struct PushStageSharedState<EM, I, OT, S, Z> {
pub fuzzer: Z,
/// The event manager
pub event_mgr: EM,
/// The [`crate::observers::ObserversTuple`]
/// The [`ObserversTuple`]
pub observers: OT,
phantom: PhantomData<I>,
}
@ -206,13 +206,13 @@ pub trait PushStage<EM, I, OT, S, Z> {
/// Allows us to use a [`PushStage`] as a normal [`Stage`]
#[derive(Debug)]
pub struct PushStageAdapter<CS, EM, OT, PS, Z> {
pub struct PushStageAdapter<CS, EM, I, OT, PS, Z> {
name: Cow<'static, str>,
push_stage: PS,
phantom: PhantomData<(CS, EM, OT, Z)>,
phantom: PhantomData<(CS, EM, I, OT, Z)>,
}
impl<CS, EM, OT, PS, Z> PushStageAdapter<CS, EM, OT, PS, Z> {
impl<CS, EM, I, OT, PS, Z> PushStageAdapter<CS, EM, I, OT, PS, Z> {
/// Create a new [`PushStageAdapter`], wrapping the given [`PushStage`]
/// to be used as a normal [`Stage`]
#[must_use]
@ -237,35 +237,43 @@ static mut PUSH_STAGE_ADAPTER_ID: usize = 0;
/// The name for push stage adapter
pub static PUSH_STAGE_ADAPTER_NAME: &str = "pushstageadapter";
impl<CS, EM, OT, PS, Z> Named for PushStageAdapter<CS, EM, OT, PS, Z> {
impl<CS, EM, I, OT, PS, Z> Named for PushStageAdapter<CS, EM, I, OT, PS, Z> {
#[must_use]
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<CS, E, EM, OT, PS, S, Z> Stage<E, EM, S, Z> for PushStageAdapter<CS, EM, OT, PS, Z>
impl<CS, E, EM, I, OT, PS, S, Z> Stage<E, EM, S, Z> for PushStageAdapter<CS, EM, I, OT, PS, Z>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
CS: Scheduler<I, S>,
S: HasExecutions
+ HasRand
+ HasCorpus
+ HasCorpus<I>
+ HasLastReportTime
+ HasCurrentCorpusId
+ HasNamedMetadata
+ HasMetadata,
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers<Observers = OT>,
EM: EventFirer<<S::Corpus as Corpus>::Input, S>
+ EventRestarter<S>
+ HasEventManagerId
+ ProgressReporter<S>,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
PS: PushStage<EM, <S::Corpus as Corpus>::Input, OT, S, Z>,
Z: ExecutesInput<E, EM, <S::Corpus as Corpus>::Input, S>
+ ExecutionProcessor<EM, <S::Corpus as Corpus>::Input, OT, S>
+ EvaluatorObservers<E, EM, <S::Corpus as Corpus>::Input, OT>
+ HasScheduler<<S::Corpus as Corpus>::Input, S>,
E: Executor<EM, I, S, Z> + HasObservers<Observers = OT>,
EM: EventFirer<I, S> + EventRestarter<S> + HasEventManagerId + ProgressReporter<S>,
OT: ObserversTuple<I, S>,
PS: PushStage<EM, I, OT, S, Z>,
Z: ExecutesInput<E, EM, I, S>
+ ExecutionProcessor<EM, I, OT, S>
+ EvaluatorObservers<E, EM, I, OT>
+ HasScheduler<I, S>,
{
#[inline]
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// TODO: Proper restart handling - call post_exec at the right time, etc...
RetryCountRestartHelper::no_retry(state, &self.name)
}
#[inline]
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
fn perform(
&mut self,
fuzzer: &mut Z,
@ -309,15 +317,4 @@ where
self.push_stage
.deinit(fuzzer, state, event_mgr, &mut *executor.observers_mut())
}
#[inline]
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// TODO: Proper restart handling - call post_exec at the right time, etc...
RetryCountRestartHelper::no_retry(state, &self.name)
}
#[inline]
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}

View File

@ -43,10 +43,10 @@ pub const DEFAULT_MUTATIONAL_MAX_ITERATIONS: usize = 128;
///
/// The default mutational push stage
#[derive(Clone, Debug)]
pub struct StdMutationalPushStage<EM, M, OT, S, Z>
pub struct StdMutationalPushStage<EM, M, I, OT, S, Z>
where
S: HasCorpus,
<S::Corpus as Corpus>::Input: Clone + Debug,
S: HasCorpus<I>,
I: Clone + Debug,
{
current_corpus_id: Option<CorpusId>,
testcases_to_do: usize,
@ -54,13 +54,13 @@ where
mutator: M,
psh: PushStageHelper<EM, <S::Corpus as Corpus>::Input, OT, S, Z>,
psh: PushStageHelper<EM, I, OT, S, Z>,
}
impl<EM, M, OT, S, Z> StdMutationalPushStage<EM, M, OT, S, Z>
impl<EM, M, I, OT, S, Z> StdMutationalPushStage<EM, M, I, OT, S, Z>
where
S: HasCorpus + HasRand,
<S::Corpus as Corpus>::Input: Clone + Debug,
S: HasCorpus<I> + HasRand,
I: Clone + Debug,
{
/// Gets the number of iterations as a random number
#[expect(clippy::unused_self, clippy::unnecessary_wraps)] // TODO: we should put this function into a trait later
@ -76,26 +76,22 @@ where
}
}
impl<EM, M, OT, S, Z> PushStage<EM, <S::Corpus as Corpus>::Input, OT, S, Z>
for StdMutationalPushStage<EM, M, OT, S, Z>
impl<EM, M, I, OT, S, Z> PushStage<EM, I, OT, S, Z> for StdMutationalPushStage<EM, M, I, OT, S, Z>
where
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
Z: HasScheduler<<S::Corpus as Corpus>::Input, S>
+ ExecutionProcessor<EM, <S::Corpus as Corpus>::Input, OT, S>,
S: HasCorpus + HasRand + MaybeHasClientPerfMonitor,
M: Mutator<<S::Corpus as Corpus>::Input, S>,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
<S::Corpus as Corpus>::Input: Input + Clone,
EM: EventFirer<I, S>,
Z: HasScheduler<I, S> + ExecutionProcessor<EM, I, OT, S>,
S: HasCorpus<I> + HasRand + MaybeHasClientPerfMonitor,
M: Mutator<I, S>,
OT: ObserversTuple<I, S> + Serialize,
I: Input + Clone,
{
#[inline]
fn push_stage_helper(&self) -> &PushStageHelper<EM, <S::Corpus as Corpus>::Input, OT, S, Z> {
fn push_stage_helper(&self) -> &PushStageHelper<EM, I, OT, S, Z> {
&self.psh
}
#[inline]
fn push_stage_helper_mut(
&mut self,
) -> &mut PushStageHelper<EM, <S::Corpus as Corpus>::Input, OT, S, Z> {
fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper<EM, I, OT, S, Z> {
&mut self.psh
}
@ -125,7 +121,7 @@ where
state: &mut S,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Option<Result<<S::Corpus as Corpus>::Input, Error>> {
) -> Option<Result<I, Error>> {
if self.testcases_done >= self.testcases_to_do {
// finished with this cicle.
return None;
@ -160,7 +156,7 @@ where
state: &mut S,
event_mgr: &mut EM,
observers: &mut OT,
last_input: <S::Corpus as Corpus>::Input,
last_input: I,
exit_kind: ExitKind,
) -> Result<(), Error> {
// todo: is_interesting, etc.
@ -188,51 +184,47 @@ where
}
}
impl<EM, M, OT, S, Z> Iterator for StdMutationalPushStage<EM, M, OT, S, Z>
impl<EM, M, I, OT, S, Z> Iterator for StdMutationalPushStage<EM, M, I, OT, S, Z>
where
EM: ProgressReporter<S> + EventFirer<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus
EM: ProgressReporter<S> + EventFirer<I, S>,
S: HasCorpus<I>
+ HasMetadata
+ HasExecutions
+ HasLastReportTime
+ HasRand
+ MaybeHasClientPerfMonitor,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
M: Mutator<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: Clone + Debug + Input,
Z: HasScheduler<<S::Corpus as Corpus>::Input, S>
+ ExecutionProcessor<EM, <S::Corpus as Corpus>::Input, OT, S>,
OT: ObserversTuple<I, S> + Serialize,
M: Mutator<I, S>,
I: Clone + Debug + Input,
Z: HasScheduler<I, S> + ExecutionProcessor<EM, I, OT, S>,
{
type Item = Result<<S::Corpus as Corpus>::Input, Error>;
type Item = Result<I, Error>;
fn next(&mut self) -> Option<Result<<S::Corpus as Corpus>::Input, Error>> {
fn next(&mut self) -> Option<Result<I, Error>> {
self.next_std()
}
}
impl<EM, M, OT, S, Z> StdMutationalPushStage<EM, M, OT, S, Z>
impl<EM, M, I, OT, S, Z> StdMutationalPushStage<EM, M, I, OT, S, Z>
where
EM: ProgressReporter<S> + EventFirer<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus
EM: ProgressReporter<S> + EventFirer<I, S>,
S: HasCorpus<I>
+ HasMetadata
+ HasExecutions
+ HasLastReportTime
+ HasRand
+ MaybeHasClientPerfMonitor,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
M: Mutator<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: Clone + Debug + Input,
Z: HasScheduler<<S::Corpus as Corpus>::Input, S>
+ ExecutionProcessor<EM, <S::Corpus as Corpus>::Input, OT, S>,
OT: ObserversTuple<I, S> + Serialize,
M: Mutator<I, S>,
I: Clone + Debug + Input,
Z: HasScheduler<I, S> + ExecutionProcessor<EM, I, OT, S>,
{
/// Creates a new default mutational stage
#[must_use]
#[expect(clippy::type_complexity)]
pub fn new(
mutator: M,
shared_state: Rc<
RefCell<Option<PushStageSharedState<EM, <S::Corpus as Corpus>::Input, OT, S, Z>>>,
>,
shared_state: Rc<RefCell<Option<PushStageSharedState<EM, I, OT, S, Z>>>>,
exit_kind: Rc<Cell<Option<ExitKind>>>,
) -> Self {
Self {
@ -245,7 +237,7 @@ where
}
/// This is the implementation for `next` for this stage
pub fn next_std(&mut self) -> Option<Result<<S::Corpus as Corpus>::Input, Error>> {
pub fn next_std(&mut self) -> Option<Result<I, Error>> {
let mut shared_state = {
let shared_state_ref = &mut (*self.push_stage_helper_mut().shared_state).borrow_mut();
shared_state_ref.take().unwrap()

View File

@ -52,31 +52,43 @@ impl SyncFromDiskMetadata {
/// A stage that loads testcases from disk to sync with other fuzzers such as AFL++
#[derive(Debug)]
pub struct SyncFromDiskStage<CB, E, EM, S, Z> {
pub struct SyncFromDiskStage<CB, E, EM, I, S, Z> {
name: Cow<'static, str>,
sync_dirs: Vec<PathBuf>,
load_callback: CB,
interval: Duration,
phantom: PhantomData<(E, EM, S, Z)>,
phantom: PhantomData<(E, EM, I, S, Z)>,
}
impl<CB, E, EM, S, Z> Named for SyncFromDiskStage<CB, E, EM, S, Z> {
impl<CB, E, EM, I, S, Z> Named for SyncFromDiskStage<CB, E, EM, I, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<CB, E, EM, S, Z> Stage<E, EM, S, Z> for SyncFromDiskStage<CB, E, EM, S, Z>
impl<CB, E, EM, I, S, Z> Stage<E, EM, S, Z> for SyncFromDiskStage<CB, E, EM, I, S, Z>
where
CB: FnMut(&mut Z, &mut S, &Path) -> Result<<S::Corpus as Corpus>::Input, Error>,
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
S: HasCorpus
CB: FnMut(&mut Z, &mut S, &Path) -> Result<I, Error>,
Z: Evaluator<E, EM, I, S>,
S: HasCorpus<I>
+ HasRand
+ HasMetadata
+ HasNamedMetadata
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
{
#[inline]
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// TODO: Needs proper crash handling for when an imported testcase crashes
// For now, Make sure we don't get stuck crashing on this testcase
RetryCountRestartHelper::no_retry(state, &self.name)
}
#[inline]
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
#[inline]
fn perform(
&mut self,
@ -136,21 +148,9 @@ where
Ok(())
}
#[inline]
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
// TODO: Needs proper crash handling for when an imported testcase crashes
// For now, Make sure we don't get stuck crashing on this testcase
RetryCountRestartHelper::no_retry(state, &self.name)
}
#[inline]
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<CB, E, EM, S, Z> SyncFromDiskStage<CB, E, EM, S, Z> {
impl<CB, E, EM, I, S, Z> SyncFromDiskStage<CB, E, EM, I, S, Z> {
/// Creates a new [`SyncFromDiskStage`]
#[must_use]
pub fn new(sync_dirs: Vec<PathBuf>, load_callback: CB, interval: Duration, name: &str) -> Self {
@ -165,25 +165,21 @@ impl<CB, E, EM, S, Z> SyncFromDiskStage<CB, E, EM, S, Z> {
}
/// Function type when the callback in `SyncFromDiskStage` is not a lambda
pub type SyncFromDiskFunction<S, Z> =
fn(&mut Z, &mut S, &Path) -> Result<<<S as HasCorpus>::Corpus as Corpus>::Input, Error>;
pub type SyncFromDiskFunction<I, S, Z> = fn(&mut Z, &mut S, &Path) -> Result<I, Error>;
impl<E, EM, S, Z> SyncFromDiskStage<SyncFromDiskFunction<S, Z>, E, EM, S, Z>
impl<E, EM, I, S, Z> SyncFromDiskStage<SyncFromDiskFunction<I, S, Z>, E, EM, I, S, Z>
where
S: HasCorpus,
<S::Corpus as Corpus>::Input: Input,
Z: Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>,
I: Input,
S: HasCorpus<I>,
Z: Evaluator<E, EM, I, S>,
{
/// Creates a new [`SyncFromDiskStage`] invoking `Input::from_file` to load inputs
#[must_use]
pub fn with_from_file(sync_dirs: Vec<PathBuf>, interval: Duration) -> Self {
fn load_callback<S: HasCorpus, Z>(
_: &mut Z,
_: &mut S,
p: &Path,
) -> Result<<S::Corpus as Corpus>::Input, Error>
fn load_callback<I, S, Z>(_: &mut Z, _: &mut S, p: &Path) -> Result<I, Error>
where
<S::Corpus as Corpus>::Input: Input,
I: Input,
S: HasCorpus<I>,
{
Input::from_file(p)
}
@ -191,7 +187,7 @@ where
interval,
name: Cow::Borrowed(SYNC_FROM_DISK_STAGE_NAME),
sync_dirs,
load_callback: load_callback::<_, _>,
load_callback: load_callback::<_, _, _>,
phantom: PhantomData,
}
}
@ -220,27 +216,38 @@ impl SyncFromBrokerMetadata {
/// A stage that loads testcases from disk to sync with other fuzzers such as AFL++
#[derive(Debug)]
pub struct SyncFromBrokerStage<IC, ICB, S, SP>
pub struct SyncFromBrokerStage<I, IC, ICB, S, SP>
where
SP: ShMemProvider,
{
client: LlmpEventConverter<IC, ICB, S, SP>,
client: LlmpEventConverter<I, IC, ICB, S, SP>,
}
impl<E, EM, IC, ICB, DI, S, SP, Z> Stage<E, EM, S, Z> for SyncFromBrokerStage<IC, ICB, S, SP>
impl<E, EM, I, IC, ICB, DI, S, SP, Z> Stage<E, EM, S, Z> for SyncFromBrokerStage<I, IC, ICB, S, SP>
where
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
S: HasExecutions + HasCorpus + HasRand + HasMetadata + Stoppable + MaybeHasClientPerfMonitor,
SP: ShMemProvider,
E: HasObservers + Executor<EM, <S::Corpus as Corpus>::Input, S, Z>,
for<'a> E::Observers: Deserialize<'a>,
Z: EvaluatorObservers<E, EM, <S::Corpus as Corpus>::Input, S>
+ ExecutionProcessor<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
IC: InputConverter<From = <S::Corpus as Corpus>::Input, To = DI>,
ICB: InputConverter<From = DI, To = <S::Corpus as Corpus>::Input>,
DI: Input,
<<S as HasCorpus>::Corpus as Corpus>::Input: Input + Clone,
EM: EventFirer<I, S>,
E: HasObservers + Executor<EM, I, S, Z>,
for<'a> E::Observers: Deserialize<'a>,
I: Input + Clone,
IC: InputConverter<From = I, To = DI>,
ICB: InputConverter<From = DI, To = I>,
S: HasExecutions + HasCorpus<I> + HasRand + HasMetadata + Stoppable + MaybeHasClientPerfMonitor,
SP: ShMemProvider,
Z: EvaluatorObservers<E, EM, I, S> + ExecutionProcessor<EM, I, E::Observers, S>,
{
#[inline]
fn should_restart(&mut self, _state: &mut S) -> Result<bool, Error> {
// No restart handling needed - does not execute the target.
Ok(true)
}
#[inline]
fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> {
// Not needed - does not execute the target.
Ok(())
}
#[inline]
fn perform(
&mut self,
@ -298,27 +305,15 @@ where
state.introspection_monitor_mut().finish_stage();
Ok(())
}
#[inline]
fn should_restart(&mut self, _state: &mut S) -> Result<bool, Error> {
// No restart handling needed - does not execute the target.
Ok(true)
}
#[inline]
fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> {
// Not needed - does not execute the target.
Ok(())
}
}
impl<IC, ICB, S, SP> SyncFromBrokerStage<IC, ICB, S, SP>
impl<I, IC, ICB, S, SP> SyncFromBrokerStage<I, IC, ICB, S, SP>
where
SP: ShMemProvider,
{
/// Creates a new [`SyncFromBrokerStage`]
#[must_use]
pub fn new(client: LlmpEventConverter<IC, ICB, S, SP>) -> Self {
pub fn new(client: LlmpEventConverter<I, IC, ICB, S, SP>) -> Self {
Self { client }
}
}

View File

@ -43,7 +43,7 @@ use crate::{
/// The default corpus entry minimising mutational stage
#[derive(Clone, Debug)]
pub struct StdTMinMutationalStage<E, EM, F, FF, M, S, Z> {
pub struct StdTMinMutationalStage<E, EM, F, FF, I, M, S, Z> {
/// The name
name: Cow<'static, str>,
/// The mutator(s) this stage uses
@ -54,39 +54,40 @@ pub struct StdTMinMutationalStage<E, EM, F, FF, M, S, Z> {
runs: usize,
/// The progress helper for this stage, keeping track of resumes after timeouts/crashes
restart_helper: ExecutionCountRestartHelper,
phantom: PhantomData<(E, EM, F, S, Z)>,
phantom: PhantomData<(E, EM, F, I, S, Z)>,
}
impl<E, EM, F, FF, M, S, Z> Stage<E, EM, S, Z> for StdTMinMutationalStage<E, EM, F, FF, M, S, Z>
impl<E, EM, F, FF, I, M, S, Z> Stage<E, EM, S, Z>
for StdTMinMutationalStage<E, EM, F, FF, I, M, S, Z>
where
Z: HasScheduler<<S::Corpus as Corpus>::Input, S>
+ ExecutionProcessor<EM, <S::Corpus as Corpus>::Input, E::Observers, S>
+ ExecutesInput<E, EM, <S::Corpus as Corpus>::Input, S>
Z: HasScheduler<I, S>
+ ExecutionProcessor<EM, I, E::Observers, S>
+ ExecutesInput<E, EM, I, S>
+ HasFeedback,
Z::Scheduler: RemovableScheduler<<S::Corpus as Corpus>::Input, S>,
Z::Scheduler: RemovableScheduler<I, S>,
E: HasObservers,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
E::Observers: ObserversTuple<I, S> + Serialize,
EM: EventFirer<I, S>,
FF: FeedbackFactory<F, E::Observers>,
F: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
F: Feedback<EM, I, E::Observers, S>,
S: HasMetadata
+ HasCorpus<I>
+ HasExecutions
+ HasSolutions
+ HasCorpus
+ HasSolutions<I>
+ HasMaxSize
+ HasNamedMetadata
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
Z::Feedback: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
M: Mutator<<S::Corpus as Corpus>::Input, S>,
<<S as HasCorpus>::Corpus as Corpus>::Input: Input + Hash + HasLen,
Z::Feedback: Feedback<EM, I, E::Observers, S>,
M: Mutator<I, S>,
I: Input + Hash + HasLen,
{
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
self.restart_helper.should_restart(state, &self.name)
}
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
self.restart_helper.clear_progress(state, &self.name)
self.restart_helper.clear_progress::<S>(state, &self.name)
}
fn perform(
@ -105,8 +106,8 @@ where
}
}
impl<E, EM, F, FF, M, S, Z> FeedbackFactory<F, E::Observers>
for StdTMinMutationalStage<E, EM, F, FF, M, S, Z>
impl<E, EM, F, FF, I, M, S, Z> FeedbackFactory<F, E::Observers>
for StdTMinMutationalStage<E, EM, F, FF, I, M, S, Z>
where
E: HasObservers,
FF: FeedbackFactory<F, E::Observers>,
@ -116,7 +117,7 @@ where
}
}
impl<E, EM, F, FF, M, S, Z> Named for StdTMinMutationalStage<E, EM, F, FF, M, S, Z> {
impl<E, EM, F, FF, I, M, S, Z> Named for StdTMinMutationalStage<E, EM, F, FF, I, M, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
@ -127,30 +128,30 @@ static mut TMIN_STAGE_ID: usize = 0;
/// The name for tmin stage
pub static TMIN_STAGE_NAME: &str = "tmin";
impl<E, EM, F, FF, M, S, Z> StdTMinMutationalStage<E, EM, F, FF, M, S, Z>
impl<E, EM, F, FF, I, M, S, Z> StdTMinMutationalStage<E, EM, F, FF, I, M, S, Z>
where
Z: HasScheduler<<S::Corpus as Corpus>::Input, S>
+ ExecutionProcessor<EM, <S::Corpus as Corpus>::Input, E::Observers, S>
+ ExecutesInput<E, EM, <S::Corpus as Corpus>::Input, S>
Z: HasScheduler<I, S>
+ ExecutionProcessor<EM, I, E::Observers, S>
+ ExecutesInput<E, EM, I, S>
+ HasFeedback,
Z::Scheduler: RemovableScheduler<<S::Corpus as Corpus>::Input, S>,
Z::Scheduler: RemovableScheduler<I, S>,
E: HasObservers,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S> + Serialize,
EM: EventFirer<<S::Corpus as Corpus>::Input, S>,
E::Observers: ObserversTuple<I, S> + Serialize,
EM: EventFirer<I, S>,
FF: FeedbackFactory<F, E::Observers>,
F: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
F: Feedback<EM, I, E::Observers, S>,
S: HasMetadata
+ HasExecutions
+ HasSolutions
+ HasCorpus
+ HasSolutions<I>
+ HasCorpus<I>
+ HasMaxSize
+ HasNamedMetadata
+ HasCurrentTestcase
+ HasCurrentTestcase<I>
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
Z::Feedback: Feedback<EM, <S::Corpus as Corpus>::Input, E::Observers, S>,
M: Mutator<<S::Corpus as Corpus>::Input, S>,
<S::Corpus as Corpus>::Input: Hash + HasLen + Input,
Z::Feedback: Feedback<EM, I, E::Observers, S>,
M: Mutator<I, S>,
I: Hash + HasLen + Input,
{
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
@ -189,10 +190,7 @@ where
}
start_timer!(state);
let transformed = <S::Corpus as Corpus>::Input::try_transform_from(
state.current_testcase_mut()?.borrow_mut(),
state,
)?;
let transformed = I::try_transform_from(state.current_testcase_mut()?.borrow_mut(), state)?;
let mut base = state.current_input_cloned()?;
// potential post operation if base is replaced by a shorter input
let mut base_post = None;
@ -310,7 +308,7 @@ where
}
}
impl<E, EM, F, FF, M, S, Z> StdTMinMutationalStage<E, EM, F, FF, M, S, Z> {
impl<E, EM, F, FF, I, M, S, Z> StdTMinMutationalStage<E, EM, F, FF, I, M, S, Z> {
/// Creates a new minimizing mutational stage that will minimize provided corpus entries
pub fn new(mutator: M, factory: FF, runs: usize) -> Self {
// unsafe but impossible that you create two threads both instantiating this instance
@ -391,12 +389,12 @@ where
/// A feedback factory for ensuring that the values of the observers for minimized inputs are the same
#[derive(Debug, Clone)]
pub struct ObserverEqualityFactory<C, M, S> {
pub struct ObserverEqualityFactory<C, I, M, S> {
observer_handle: Handle<C>,
phantom: PhantomData<(C, M, S)>,
phantom: PhantomData<(C, I, M, S)>,
}
impl<C, M, S> ObserverEqualityFactory<C, M, S>
impl<C, I, M, S> ObserverEqualityFactory<C, I, M, S>
where
M: Hash,
C: AsRef<M> + Handled,
@ -410,7 +408,7 @@ where
}
}
impl<C, M, S> HasObserverHandle for ObserverEqualityFactory<C, M, S> {
impl<C, I, M, S> HasObserverHandle for ObserverEqualityFactory<C, I, M, S> {
type Observer = C;
fn observer_handle(&self) -> &Handle<C> {
@ -418,13 +416,13 @@ impl<C, M, S> HasObserverHandle for ObserverEqualityFactory<C, M, S> {
}
}
impl<C, M, OT, S> FeedbackFactory<ObserverEqualityFeedback<C, M, S>, OT>
for ObserverEqualityFactory<C, M, S>
impl<C, I, M, OT, S> FeedbackFactory<ObserverEqualityFeedback<C, M, S>, OT>
for ObserverEqualityFactory<C, I, M, S>
where
M: Hash,
C: AsRef<M> + Handled,
OT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
S: HasCorpus,
OT: ObserversTuple<I, S>,
S: HasCorpus<I>,
{
fn create_feedback(&self, observers: &OT) -> ObserverEqualityFeedback<C, M, S> {
let obs = observers

View File

@ -11,7 +11,7 @@ use libafl_bolts::Named;
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
corpus::{Corpus, HasCurrentCorpusId},
corpus::HasCurrentCorpusId,
executors::{Executor, HasObservers, ShadowExecutor},
inputs::Input,
mark_feature_time,
@ -24,20 +24,20 @@ use crate::{
/// A stage that runs a tracer executor
#[derive(Clone, Debug)]
pub struct TracingStage<EM, TE, S, Z> {
pub struct TracingStage<EM, I, TE, S, Z> {
name: Cow<'static, str>,
tracer_executor: TE,
phantom: PhantomData<(EM, TE, S, Z)>,
phantom: PhantomData<(EM, I, TE, S, Z)>,
}
impl<EM, TE, S, Z> TracingStage<EM, TE, S, Z>
impl<EM, I, TE, S, Z> TracingStage<EM, I, TE, S, Z>
where
TE: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
TE::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
TE: Executor<EM, I, S, Z> + HasObservers,
TE::Observers: ObserversTuple<I, S>,
S: HasExecutions
+ HasCorpus
+ HasCorpus<I>
+ HasNamedMetadata
+ HasCurrentTestcase
+ HasCurrentTestcase<I>
+ MaybeHasClientPerfMonitor,
{
/// Perform tracing on the given `CorpusId`. Useful for if wrapping [`TracingStage`] with your
@ -72,16 +72,16 @@ where
}
}
impl<E, EM, TE, S, Z> Stage<E, EM, S, Z> for TracingStage<EM, TE, S, Z>
impl<E, EM, I, TE, S, Z> Stage<E, EM, S, Z> for TracingStage<EM, I, TE, S, Z>
where
TE: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
TE::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
TE: Executor<EM, I, S, Z> + HasObservers,
TE::Observers: ObserversTuple<I, S>,
S: HasExecutions
+ HasCorpus
+ HasCorpus<I>
+ HasNamedMetadata
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
<S::Corpus as Corpus>::Input: Input,
I: Input,
{
#[inline]
fn perform(
@ -103,7 +103,7 @@ where
}
}
impl<EM, TE, S, Z> Named for TracingStage<EM, TE, S, Z> {
impl<EM, I, TE, S, Z> Named for TracingStage<EM, I, TE, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
@ -114,7 +114,7 @@ static mut TRACING_STAGE_ID: usize = 0;
/// The name for tracing stage
pub static TRACING_STAGE_NAME: &str = "tracing";
impl<EM, TE, S, Z> TracingStage<EM, TE, S, Z> {
impl<EM, I, TE, S, Z> TracingStage<EM, I, TE, S, Z> {
/// Creates a new default stage
pub fn new(tracer_executor: TE) -> Self {
// unsafe but impossible that you create two threads both instantiating this instance
@ -144,9 +144,9 @@ impl<EM, TE, S, Z> TracingStage<EM, TE, S, Z> {
/// A stage that runs the shadow executor using also the shadow observers
#[derive(Clone, Debug)]
pub struct ShadowTracingStage<E, EM, SOT, S, Z> {
pub struct ShadowTracingStage<E, EM, I, SOT, S, Z> {
name: Cow<'static, str>,
phantom: PhantomData<(E, EM, SOT, S, Z)>,
phantom: PhantomData<(E, EM, I, SOT, S, Z)>,
}
/// The counter for giving this stage unique id
@ -154,31 +154,39 @@ static mut SHADOW_TRACING_STAGE_ID: usize = 0;
/// Name for shadow tracing stage
pub static SHADOW_TRACING_STAGE_NAME: &str = "shadow";
impl<E, EM, SOT, S, Z> Named for ShadowTracingStage<E, EM, SOT, S, Z> {
impl<E, EM, I, SOT, S, Z> Named for ShadowTracingStage<E, EM, I, SOT, S, Z> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<E, EM, SOT, S, Z> Stage<ShadowExecutor<E, S, SOT>, EM, S, Z>
for ShadowTracingStage<E, EM, SOT, S, Z>
impl<E, EM, I, SOT, S, Z> Stage<ShadowExecutor<E, I, S, SOT>, EM, S, Z>
for ShadowTracingStage<E, EM, I, SOT, S, Z>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
E::Observers: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
SOT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
E: Executor<EM, I, S, Z> + HasObservers,
E::Observers: ObserversTuple<I, S>,
SOT: ObserversTuple<I, S>,
S: HasExecutions
+ HasCorpus
+ HasCorpus<I>
+ HasNamedMetadata
+ Debug
+ HasCurrentTestcase
+ HasCurrentTestcase<I>
+ HasCurrentCorpusId
+ MaybeHasClientPerfMonitor,
{
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
RetryCountRestartHelper::no_retry(state, &self.name)
}
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut ShadowExecutor<E, S, SOT>,
executor: &mut ShadowExecutor<E, I, S, SOT>,
state: &mut S,
manager: &mut EM,
) -> Result<(), Error> {
@ -209,24 +217,16 @@ where
Ok(())
}
fn should_restart(&mut self, state: &mut S) -> Result<bool, Error> {
RetryCountRestartHelper::no_retry(state, &self.name)
}
fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> {
RetryCountRestartHelper::clear_progress(state, &self.name)
}
}
impl<E, EM, SOT, S, Z> ShadowTracingStage<E, EM, SOT, S, Z>
impl<E, EM, I, SOT, S, Z> ShadowTracingStage<E, EM, I, SOT, S, Z>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers,
S: HasExecutions + HasCorpus,
SOT: ObserversTuple<<S::Corpus as Corpus>::Input, S>,
E: Executor<EM, I, S, Z> + HasObservers,
S: HasExecutions + HasCorpus<I>,
SOT: ObserversTuple<I, S>,
{
/// Creates a new default stage
pub fn new(_executor: &mut ShadowExecutor<E, S, SOT>) -> Self {
pub fn new(_executor: &mut ShadowExecutor<E, I, S, SOT>) -> Self {
// unsafe but impossible that you create two threads both instantiating this instance
let stage_id = unsafe {
let ret = SHADOW_TRACING_STAGE_ID;

Some files were not shown because too many files have changed in this diff Show More