Associated types for Corpus, State (#767)

* Associated types for Corpus, State

* cleanup

* fix no_std

* drop unused clauses

* Corpus

* cleanup

* adding things

* fixed fuzzer

* remove phantom data

* python

* progress?

* more more

* oof

* wow it builds?

* python fixes, tests

* fix python fun

* black fmt for python

* clippy, added Nop things

* fixes

* fix merge

* make it compile (#836)

* doc-test fixes, prelude-b-gone for cargo-hack compat

* fixes for windows, concolic

* really fix windows, maybe

* imagine using windows

* ...

* elide I generic when used with S: State

* Elide many, many generics, but at what cost?

* progress on push

* Constraint HasCorpus, HasSolutions at trait definition

* remove unused feature

* remove unstable usage since we constrained HasCorpus at definition

* compiled, but still no type inference for MaxMapFeedback

* cleanup inprocess

* resolve some std conflicts

* simplify map

* undo unnecessary cfg specification

* fix breaking test case for CI on no-std

* fix concolic build failures

* fix macos build

* fixes for windows build

* timeout fixes for windows build

* fix pybindings issues

* fixup qemu

* fix outstanding local build issues

* maybe fix windows inprocess

* doc fixes

* unbridled fury

* de-associate State from Feedback, replace with generic as AT inference is not sufficient to derive specialisation for MapFeedback

* merge update

* refactor + speed up fuzzer builds by sharing build work

* cleanup lingering compiler errors

* lol missed one

* revert QEMU-Nyx change, not sure how I did that

* move HasInput to inputs

* HasInput => KnowsInput

* update bounds to enforce via associated types

* disentangle observers with fuzzer

* revert --target; update some fuzzers to match new API

* resolve outstanding fuzzer build blockers (that I can run on my system)

* fixes for non-linux unixes

* fix for windows

* Knows => Uses, final fixes for windows

* <guttural screaming>

* fixes for concolic

* loosen bound for frida executor so windows builds correctly

* cleanup generics for eventmanager/eventprocessor to drop observers requirement

* improve inference over fuzz_one and friends

* update migration notes

* fixes for python bindings

* fixes for generic counts in event managers

* finish migration notes

* post-merge fix

Co-authored-by: Addison Crump <addison.crump@cispa.de>
This commit is contained in:
Dominik Maier 2022-10-24 03:22:26 +02:00 committed by GitHub
parent 9695ce0029
commit 663a33168e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 4079 additions and 3135 deletions

View File

@ -2,8 +2,7 @@ use libafl;
#[cfg(target_os = "linux")]
use libafl_qemu;
use libafl_sugar;
use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::{prelude::*, types::PyDict};
const LIBAFL_CODE: &str = r#"
class BaseObserver:

View File

@ -1,30 +1,38 @@
from pylibafl.libafl import *
import ctypes
class FooObserver(BaseObserver):
def __init__(self):
self.n = 0
def name(self):
return "Foo"
def pre_exec(self, state, input):
if self.n % 10000 == 0:
print("FOO!", self.n, input)
self.n += 1
class FooFeedback(BaseFeedback):
def is_interesting(self, state, mgr, input, observers, exit_kind):
ob = observers.match_name("Foo").unwrap_py()
return ob.n % 10000 == 0
class FooExecutor(BaseExecutor):
def __init__(self, harness, observers: ObserversTuple):
self.h = harness
self.o = observers
def observers(self):
return self.o
def run_target(self, fuzzer, state, mgr, input) -> ExitKind:
return (self.h)(input)
libc = ctypes.cdll.LoadLibrary("libc.so.6")
area_ptr = libc.calloc(1, 4096)
@ -33,34 +41,46 @@ observer = StdMapObserverI8("mymap", area_ptr, 4096)
m = observer.as_map_observer()
observers = ObserversTuple([observer.as_map_observer().as_observer(), FooObserver().as_observer()])
observers = ObserversTuple(
[observer.as_map_observer().as_observer(), FooObserver().as_observer()]
)
feedback = feedback_or(MaxMapFeedbackI8(m).as_feedback(), FooFeedback().as_feedback())
objective = feedback_and_fast(CrashFeedback().as_feedback(), MaxMapFeedbackI8(m).as_feedback())
objective = feedback_and_fast(
CrashFeedback().as_feedback(), MaxMapFeedbackI8(m).as_feedback()
)
fuzzer = StdFuzzer(feedback, objective)
rand = StdRand.with_current_nanos()
state = StdState(rand.as_rand(), InMemoryCorpus().as_corpus(), InMemoryCorpus().as_corpus(), feedback, objective)
state = StdState(
rand.as_rand(),
InMemoryCorpus().as_corpus(),
InMemoryCorpus().as_corpus(),
feedback,
objective,
)
monitor = SimpleMonitor(lambda s: print(s))
mgr = SimpleEventManager(monitor.as_monitor())
def harness(buf) -> ExitKind:
#print(buf)
# print(buf)
m[0] = 1
if len(buf) > 0 and buf[0] == ord('a'):
if len(buf) > 0 and buf[0] == ord("a"):
m[1] = 1
if len(buf) > 1 and buf[1] == ord('b'):
if len(buf) > 1 and buf[1] == ord("b"):
m[2] = 1
if len(buf) > 2 and buf[2] == ord('c'):
if len(buf) > 2 and buf[2] == ord("c"):
m[3] = 1
return ExitKind.crash()
return ExitKind.ok()
# executor = InProcessExecutor(harness, observers, fuzzer, state, mgr.as_manager())
executor = FooExecutor(harness, observers)
@ -69,6 +89,6 @@ stage = StdMutationalStage(StdHavocMutator().as_mutator())
stage_tuple_list = StagesTuple([stage.as_stage()])
fuzzer.add_input(state, executor.as_executor(), mgr.as_manager(), b'\0\0')
fuzzer.add_input(state, executor.as_executor(), mgr.as_manager(), b"\0\0")
fuzzer.fuzz_loop(executor.as_executor(), state, mgr.as_manager(), stage_tuple_list)

View File

@ -0,0 +1,161 @@
# Migrating from libafl <0.9 to 0.9
Internal APIs of libafl have changed in version 0.9 to prefer associated types in cases where components were "fixed" to
particular versions of other components. As a result, many existing custom components will not be compatible between
versions prior to 0.9 and version 0.9.
## Reasons for this change
When implementing a trait with a generic, it is possible to have more than one instantiation of that generic trait. As a
result, everywhere where consistency across generic types was required to implement a trait, it needed to be properly
and explicitly constrained at every point. This led to `impl`s which were at best difficult to debug and, at worst,
incorrect and caused confusing bugs for users.
For example, consider the MapCorpusMinimizer implementation (from <0.9) below:
```rust
impl<E, I, O, S, TS> CorpusMinimizer<I, S> for MapCorpusMinimizer<E, I, O, S, TS>
where
E: Copy + Hash + Eq,
I: Input,
for<'a> O: MapObserver<Entry = E> + AsIter<'a, Item = E>,
S: HasMetadata + HasCorpus<I>,
TS: TestcaseScore<I, S>,
{
fn minimize<CS, EX, EM, OT, Z>(
&self,
fuzzer: &mut Z,
executor: &mut EX,
manager: &mut EM,
state: &mut S,
) -> Result<(), Error>
where
CS: Scheduler<I, S>,
EX: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
EM: EventManager<EX, I, S, Z>,
OT: ObserversTuple<S>,
Z: Evaluator<EX, EM, I, S> + HasScheduler<CS, I, S>,
{
// --- SNIP ---
}
}
```
It was previously necessary to constrain every generic using a slew of other generics; above, it is necessary to
constrain the input type (`I`) for every generic, despite the fact that this was already made clear by the state (`S`)
and that the input will necessarily be the same over every implementation for that type.
Below is the same code, but with the associated types changes (note that some generic names have changed):
```rust
impl<E, O, T, TS> CorpusMinimizer<E> for MapCorpusMinimizer<E, O, T, TS>
where
E: UsesState,
for<'a> O: MapObserver<Entry = T> + AsIter<'a, Item = T>,
E::State: HasMetadata + HasCorpus,
T: Copy + Hash + Eq,
TS: TestcaseScore<E::State>,
{
fn minimize<CS, EM, Z>(
&self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
state: &mut E::State,
) -> Result<(), Error>
where
E: Executor<EM, Z> + HasObservers,
CS: Scheduler<State=E::State>,
EM: UsesState<State=E::State>,
Z: HasScheduler<CS, State=E::State>,
{
// --- SNIP ---
}
}
```
The executor is constrained to `EM` and `Z`, with each of their respective states being constrained to `E`'s state. It
is no longer necessary to explicitly defined a generic for the input type, the state type, or the generic type, as these
are all present as associated types for `E`. Additionally, we don't even need to specify any details about the observers
(`OT` in the previous version) as the type does not need to be constrained and is not shared by other types.
## Scope
You are affected by this change if:
- You specified explicit generics for a type (e.g., `MaxMapFeedback::<_, (), _>::new(...)`)
- You implemented a custom component (e.g., `Mutator`, `Executor`, `State`, `Fuzzer`, `Feedback`, `Observer`, etc.)
If you did neither of these, congrats! You are likely unaffected by these changes.
### Migrating explicit generics
Migrating specific generics should be a quite simple process; you should review the API documentation for details on the
order of generics and replace them accordingly. Generally speaking, it should no longer be necessary to specify these
generics.
See `fuzzers/` for examples of these changes.
### Migrating component types
If you implemented a Mutator, Executor, State, or another kind of component, you must update your implementation. The
main changes to the API are in the use of "Uses*" for associated types.
In many scenarios, Input, Observers, and State generics have been moved into traits with associated types (namely,
"UsesInput", "UsesObservers", and "UsesState". These traits are required for many existing traits now and are very
straightforward to implement. In a majority of cases, you will have generics on your custom implementation or a fixed
type to implement this with. Thankfully, Rust will let you know when you need to implement this type.
As an example, InMemoryCorpus before 0.9 looked like this:
```rust
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct InMemoryCorpus<I>
where
I: Input,
{
entries: Vec<RefCell<Testcase<I>>>,
current: Option<usize>,
}
impl<I> Corpus<I> for InMemoryCorpus<I>
where
I: Input,
{
// --- SNIP ---
}
```
After 0.9, all `Corpus` implementations are required to implement `UsesInput` and `Corpus` no longer has a generic for
the input type (as it is now provided by the UsesInput impl). The migrated implementation is shown below:
```rust
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct InMemoryCorpus<I>
where
I: Input,
{
entries: Vec<RefCell<Testcase<I>>>,
current: Option<usize>,
}
impl<I> UsesInput for InMemoryCorpus<I>
where
I: Input,
{
type Input = I;
}
impl<I> Corpus for InMemoryCorpus<I>
where
I: Input,
{
// --- SNIP ---
}
```
Now, `Corpus` cannot be accidentally implemented for another type other than that specified by `InMemoryCorpus`, as it
is fixed to the associated type for `UsesInput`.
A more complex example of migration can be found in the "Reasons for this change" section of this document.

View File

@ -1,6 +1,6 @@
# Backtrace baby fuzzers
The projects contained in this directory are simple fuzzers derived from the original baby_fuzzer examples, whose perpose is to show how to use a `BacktraceObserver` or an `ASANObserver` to dedupe crashes and other necessary component for this feature.
The projects contained in this directory are simple fuzzers derived from the original baby_fuzzer examples, whose purpose is to show how to use a `BacktraceObserver` or an `ASANObserver` to dedupe crashes and other necessary components for this feature.
The examples cover:

View File

@ -58,10 +58,7 @@ pub fn main() {
let mut feedback = MaxMapFeedback::new(&observer);
// A feedback to choose if an input is a solution or not
let mut objective = feedback_and!(
CrashFeedback::new(),
NewHashFeedback::<BacktraceObserver>::new(&bt_observer)
);
let mut objective = feedback_and!(CrashFeedback::new(), NewHashFeedback::new(&bt_observer));
// create a State from scratch
let mut state = StdState::new(

View File

@ -48,10 +48,7 @@ pub fn main() {
let mut feedback = MaxMapFeedback::new(&observer);
// A feedback to choose if an input is a solution or not
let mut objective = feedback_and!(
CrashFeedback::new(),
NewHashFeedback::<BacktraceObserver>::new(&bt_observer)
);
let mut objective = feedback_and!(CrashFeedback::new(), NewHashFeedback::new(&bt_observer));
// create a State from scratch
let mut state = StdState::new(

View File

@ -3,7 +3,7 @@ use std::env;
fn main() {
let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
let mut cmd = cc::Build::new().get_compiler().to_command();
cmd.args(&["src/test_command.c", "-o"])
cmd.args(["src/test_command.c", "-o"])
.arg(&format!("{}/test_command", &cwd))
.arg("-fsanitize=address")
.status()

View File

@ -46,10 +46,7 @@ pub fn main() {
let mut feedback = MaxMapFeedback::new(&observer);
// A feedback to choose if an input is a solution or not
let mut objective = feedback_and!(
CrashFeedback::new(),
NewHashFeedback::<AsanBacktraceObserver>::new(&bt_observer)
);
let mut objective = feedback_and!(CrashFeedback::new(), NewHashFeedback::new(&bt_observer));
// let mut objective = CrashFeedback::new();
// create a State from scratch
@ -93,7 +90,7 @@ pub fn main() {
let mut command = Command::new("./test_command");
let command = command
.args(&[self.shmem_id.as_str()])
.args([self.shmem_id.as_str()])
.env("ASAN_OPTIONS", get_asan_runtime_flags());
command

View File

@ -32,13 +32,13 @@ fn main() {
if !afl_gcc_path.is_file() {
Command::new("make")
.arg("all")
.current_dir(&afl_path)
.current_dir(afl_path)
.status()
.unwrap();
}
Command::new(afl_gcc_path)
.args(&["src/program.c", "-o"])
.args(["src/program.c", "-o"])
.arg(&format!("{}/target/release/program", &cwd))
.arg("-fsanitize=address")
.status()

View File

@ -77,10 +77,7 @@ pub fn main() {
let mut feedback = MaxMapFeedback::new(&observer);
// A feedback to choose if an input is a solution or not
let mut objective = feedback_and!(
CrashFeedback::new(),
NewHashFeedback::<BacktraceObserver>::new(&bt_observer)
);
let mut objective = feedback_and!(CrashFeedback::new(), NewHashFeedback::new(&bt_observer));
// create a State from scratch
let mut state = StdState::new(

View File

@ -71,10 +71,7 @@ pub fn main() {
let mut feedback = MaxMapFeedback::new(&observer);
// A feedback to choose if an input is a solution or not
let mut objective = feedback_and!(
CrashFeedback::new(),
NewHashFeedback::<BacktraceObserver>::new(&bt_observer)
);
let mut objective = feedback_and!(CrashFeedback::new(), NewHashFeedback::new(&bt_observer));
// create a State from scratch
let mut state = StdState::new(

View File

@ -117,7 +117,7 @@ pub fn main() {
// Must be a crash
CrashFeedback::new(),
// Take it onlt if trigger new coverage over crashes
MaxMapFeedback::<_, _, _, u8>::new(&edges_observer)
MaxMapFeedback::new(&edges_observer)
);
// create a State from scratch

View File

@ -75,9 +75,7 @@ unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
let shmem_provider = StdShMemProvider::new()?;
let mut run_client = |state: Option<_>,
mgr: LlmpRestartingEventManager<_, _, _, _>,
core_id| {
let mut run_client = |state: Option<_>, mgr: LlmpRestartingEventManager<_, _>, core_id| {
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
// println!("{:?}", mgr.mgr_id());
@ -95,7 +93,7 @@ unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
};
if options.asan && options.asan_cores.contains(core_id) {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _, _>, _core_id| {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, _core_id| {
let gum = Gum::obtain();
let coverage = CoverageRuntime::new();
@ -220,7 +218,7 @@ unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
Ok(())
})(state, mgr, core_id)
} else if options.cmplog && options.cmplog_cores.contains(core_id) {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _, _>, _core_id| {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, _core_id| {
let gum = Gum::obtain();
let coverage = CoverageRuntime::new();
@ -354,7 +352,7 @@ unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
Ok(())
})(state, mgr, core_id)
} else {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _, _>, _core_id| {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, _core_id| {
let gum = Gum::obtain();
let coverage = CoverageRuntime::new();

View File

@ -69,9 +69,7 @@ unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
let shmem_provider = StdShMemProvider::new()?;
let mut run_client = |state: Option<_>,
mgr: LlmpRestartingEventManager<_, _, _, _>,
core_id| {
let mut run_client = |state: Option<_>, mgr: LlmpRestartingEventManager<_, _>, core_id| {
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
// println!("{:?}", mgr.mgr_id());
@ -89,7 +87,7 @@ unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
};
if options.asan && options.asan_cores.contains(core_id) {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _, _>, _core_id| {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, _core_id| {
let gum = Gum::obtain();
let coverage = CoverageRuntime::new();
@ -214,7 +212,7 @@ unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
Ok(())
})(state, mgr, core_id)
} else if options.cmplog && options.cmplog_cores.contains(core_id) {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _, _>, _core_id| {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, _core_id| {
let gum = Gum::obtain();
let coverage = CoverageRuntime::new();
@ -348,7 +346,7 @@ unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
Ok(())
})(state, mgr, core_id)
} else {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _, _>, _core_id| {
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, _core_id| {
let gum = Gum::obtain();
let coverage = CoverageRuntime::new();

View File

@ -4,13 +4,13 @@ use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
use clap::{self, Parser};
use std::{
env,
path::PathBuf,
process::{Child, Command, Stdio},
};
use clap::{self, Parser};
use libafl::{
bolts::{
current_nanos,
@ -48,7 +48,6 @@ use libafl::{
state::{HasCorpus, StdState},
Error,
};
use libafl_targets::{
libfuzzer_initialize, libfuzzer_test_one_input, CmpLogObserver, CMPLOG_MAP, EDGES_MAP,
MAX_EDGES_NUM,
@ -116,7 +115,6 @@ fn fuzz(
let cmplog = unsafe { &mut CMPLOG_MAP };
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!(
@ -139,12 +137,13 @@ fn fuzz(
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// States of the feedbacks.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
// Same for objective feedbacks
&mut objective,
).unwrap()
// States of the feedbacks.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
// Same for objective feedbacks
&mut objective,
)
.unwrap()
});
println!("We're a client, let's fuzz :)");

View File

@ -60,7 +60,7 @@ fn main() {
corpus
.add(Testcase::new(input))
.expect("error in adding corpus");
let solutions = OnDiskCorpus::<BytesInput>::new(PathBuf::from("./crashes")).unwrap();
let solutions = OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap();
// libafl stuff
let mut feedback = MaxMapFeedback::new(&observer);

View File

@ -48,7 +48,7 @@ fn main() {
// let monitor = SimpleMonitor::new(|x|-> () {println!("{}",x)});
let monitor = TuiMonitor::new("test_fuzz".to_string(), true);
let mut mgr: SimpleEventManager<BytesInput, _, _> = SimpleEventManager::new(monitor);
let mut mgr = SimpleEventManager::new(monitor);
let mut executor = NyxExecutor::new(&mut helper, tuple_list!(observer)).unwrap();
let mutator = StdScheduledMutator::new(havoc_mutations());
let mut stages = tuple_list!(StdMutationalStage::new(mutator));

View File

@ -37,7 +37,7 @@ pub fn main() {
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
// Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::<BytesInput, _, _, _>::new(&observer);
let mut feedback = MaxMapFeedback::new(&observer);
// A feedback to choose if an input is a solution or not
let mut objective = CrashFeedback::new();
@ -47,7 +47,7 @@ pub fn main() {
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
InMemoryCorpus::<BytesInput>::new(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),

View File

@ -4,6 +4,7 @@ use libafl::{
events::EventFirer,
executors::ExitKind,
feedbacks::{Feedback, MapIndexesMetadata},
inputs::UsesInput,
observers::ObserversTuple,
schedulers::{MinimizerScheduler, TestcaseScore},
state::{HasClientPerfMonitor, HasCorpus, HasMetadata},
@ -20,9 +21,9 @@ pub struct PacketLenMetadata {
pub struct PacketLenTestcaseScore {}
impl<S> TestcaseScore<PacketData, S> for PacketLenTestcaseScore
impl<S> TestcaseScore<S> for PacketLenTestcaseScore
where
S: HasCorpus<PacketData> + HasMetadata,
S: HasCorpus<Input = PacketData> + HasMetadata,
{
fn compute(entry: &mut Testcase<PacketData>, _state: &S) -> Result<f64, Error> {
Ok(entry
@ -32,17 +33,17 @@ where
}
}
pub type PacketLenMinimizerScheduler<CS, S> =
MinimizerScheduler<CS, PacketLenTestcaseScore, PacketData, MapIndexesMetadata, S>;
pub type PacketLenMinimizerScheduler<CS> =
MinimizerScheduler<CS, PacketLenTestcaseScore, MapIndexesMetadata>;
#[derive(Serialize, Deserialize, Default, Clone, Debug)]
pub struct PacketLenFeedback {
len: u64,
}
impl<S> Feedback<PacketData, S> for PacketLenFeedback
impl<S> Feedback<S> for PacketLenFeedback
where
S: HasClientPerfMonitor,
S: UsesInput<Input = PacketData> + HasClientPerfMonitor,
{
#[inline]
fn is_interesting<EM, OT>(
@ -54,8 +55,8 @@ where
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<PacketData>,
OT: ObserversTuple<PacketData, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
self.len = input.length;
Ok(false)

View File

@ -4,6 +4,7 @@ use libafl::{
rands::{Rand, StdRand},
tuples::Named,
},
inputs::UsesInput,
mutators::{MutationResult, Mutator},
state::HasRand,
Error,
@ -15,7 +16,10 @@ pub struct LainMutator {
inner: lain::mutator::Mutator<StdRand>,
}
impl<S: HasRand> Mutator<PacketData, S> for LainMutator {
impl<S> Mutator<S> for LainMutator
where
S: UsesInput<Input = PacketData> + HasRand,
{
fn mutate(
&mut self,
state: &mut S,

View File

@ -33,13 +33,13 @@ use crate::bolts::core_affinity::CoreId;
use crate::bolts::os::startable_self;
#[cfg(all(unix, feature = "std", feature = "fork"))]
use crate::bolts::os::{dup2, fork, ForkResult};
use crate::inputs::UsesInput;
#[cfg(feature = "std")]
use crate::{
bolts::{core_affinity::Cores, shmem::ShMemProvider},
events::{EventConfig, LlmpRestartingEventManager, ManagerKind, RestartingMgr},
inputs::Input,
monitors::Monitor,
observers::ObserversTuple,
state::{HasClientPerfMonitor, HasExecutions},
Error,
};
@ -49,14 +49,13 @@ const _AFL_LAUNCHER_CLIENT: &str = "AFL_LAUNCHER_CLIENT";
#[cfg(feature = "std")]
#[derive(TypedBuilder)]
#[allow(clippy::type_complexity, missing_debug_implementations)]
pub struct Launcher<'a, CF, I, MT, OT, S, SP>
pub struct Launcher<'a, CF, MT, S, SP>
where
CF: FnOnce(Option<S>, LlmpRestartingEventManager<I, OT, S, SP>, usize) -> Result<(), Error>,
I: Input + 'a,
CF: FnOnce(Option<S>, LlmpRestartingEventManager<S, SP>, usize) -> Result<(), Error>,
S::Input: 'a,
MT: Monitor,
SP: ShMemProvider + 'static,
OT: ObserversTuple<I, S> + 'a,
S: DeserializeOwned + 'a,
S: DeserializeOwned + UsesInput + 'a,
{
/// The ShmemProvider to use
shmem_provider: SP,
@ -86,17 +85,15 @@ where
#[builder(default = true)]
spawn_broker: bool,
#[builder(setter(skip), default = PhantomData)]
phantom_data: PhantomData<(&'a I, &'a OT, &'a S, &'a SP)>,
phantom_data: PhantomData<(&'a S, &'a SP)>,
}
impl<CF, I, MT, OT, S, SP> Debug for Launcher<'_, CF, I, MT, OT, S, SP>
impl<CF, MT, S, SP> Debug for Launcher<'_, CF, MT, S, SP>
where
CF: FnOnce(Option<S>, LlmpRestartingEventManager<I, OT, S, SP>, usize) -> Result<(), Error>,
I: Input,
OT: ObserversTuple<I, S> + DeserializeOwned,
CF: FnOnce(Option<S>, LlmpRestartingEventManager<S, SP>, usize) -> Result<(), Error>,
MT: Monitor + Clone,
SP: ShMemProvider + 'static,
S: DeserializeOwned,
S: DeserializeOwned + UsesInput,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Launcher")
@ -111,14 +108,12 @@ where
}
#[cfg(feature = "std")]
impl<'a, CF, I, MT, OT, S, SP> Launcher<'a, CF, I, MT, OT, S, SP>
impl<'a, CF, MT, S, SP> Launcher<'a, CF, MT, S, SP>
where
CF: FnOnce(Option<S>, LlmpRestartingEventManager<I, OT, S, SP>, usize) -> Result<(), Error>,
I: Input,
OT: ObserversTuple<I, S> + DeserializeOwned,
CF: FnOnce(Option<S>, LlmpRestartingEventManager<S, SP>, usize) -> Result<(), Error>,
MT: Monitor + Clone,
S: DeserializeOwned + UsesInput + HasExecutions + HasClientPerfMonitor,
SP: ShMemProvider + 'static,
S: DeserializeOwned,
{
/// Launch the broker and the clients and fuzz
#[cfg(all(unix, feature = "std", feature = "fork"))]
@ -175,7 +170,7 @@ where
}
// Fuzzer client. keeps retrying the connection to broker till the broker starts
let (state, mgr) = RestartingMgr::<I, MT, OT, S, SP>::builder()
let (state, mgr) = RestartingMgr::<MT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone())
.broker_port(self.broker_port)
.kind(ManagerKind::Client {
@ -198,7 +193,7 @@ where
println!("I am broker!!.");
// TODO we don't want always a broker here, think about using different laucher process to spawn different configurations
RestartingMgr::<I, MT, OT, S, SP>::builder()
RestartingMgr::<MT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone())
.monitor(Some(self.monitor.clone()))
.broker_port(self.broker_port)
@ -245,7 +240,7 @@ where
//todo: silence stdout and stderr for clients
// the actual client. do the fuzzing
let (state, mgr) = RestartingMgr::<I, MT, OT, S, SP>::builder()
let (state, mgr) = RestartingMgr::<MT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone())
.broker_port(self.broker_port)
.kind(ManagerKind::Client {
@ -299,7 +294,7 @@ where
#[cfg(feature = "std")]
println!("I am broker!!.");
RestartingMgr::<I, MT, OT, S, SP>::builder()
RestartingMgr::<MT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone())
.monitor(Some(self.monitor.clone()))
.broker_port(self.broker_port)

View File

@ -153,6 +153,7 @@ pub fn format_duration_hms(duration: &time::Duration) -> String {
}
/// The purpose of this module is to alleviate imports of the bolts by adding a glob import.
#[cfg(feature = "prelude")]
pub mod bolts_prelude {
#[cfg(feature = "std")]
pub use super::build_id::*;

View File

@ -11,7 +11,7 @@ use crate::{
ondisk::{OnDiskCorpus, OnDiskMetadataFormat},
Corpus, Testcase,
},
inputs::Input,
inputs::{Input, UsesInput},
Error,
};
@ -28,7 +28,14 @@ where
cache_max_len: usize,
}
impl<I> Corpus<I> for CachedOnDiskCorpus<I>
impl<I> UsesInput for CachedOnDiskCorpus<I>
where
I: Input,
{
type Input = I;
}
impl<I> Corpus for CachedOnDiskCorpus<I>
where
I: Input,
{

View File

@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use crate::{
corpus::{Corpus, Testcase},
inputs::Input,
inputs::{Input, UsesInput},
Error,
};
@ -22,7 +22,14 @@ where
current: Option<usize>,
}
impl<I> Corpus<I> for InMemoryCorpus<I>
impl<I> UsesInput for InMemoryCorpus<I>
where
I: Input,
{
type Input = I;
}
impl<I> Corpus for InMemoryCorpus<I>
where
I: Input,
{

View File

@ -12,71 +12,70 @@ use num_traits::ToPrimitive;
use z3::{ast::Bool, Config, Context, Optimize};
use crate::{
bolts::AsIter,
bolts::{
tuples::{MatchName, Named},
AsIter,
},
corpus::Corpus,
events::EventManager,
executors::{Executor, HasObservers},
inputs::Input,
observers::{MapObserver, ObserversTuple},
schedulers::{LenTimeMulTestcaseScore, Scheduler, TestcaseScore},
state::{HasCorpus, HasMetadata},
Error, Evaluator, HasScheduler,
state::{HasCorpus, HasMetadata, UsesState},
Error, HasScheduler,
};
/// `CorpusMinimizers` minimize corpora according to internal logic. See various implementations for
/// details.
pub trait CorpusMinimizer<I, S>
pub trait CorpusMinimizer<E>
where
I: Input,
S: HasCorpus<I>,
E: UsesState,
E::State: HasCorpus,
{
/// Minimize the corpus of the provided state.
fn minimize<CS, EX, EM, OT, Z>(
fn minimize<CS, EM, Z>(
&self,
fuzzer: &mut Z,
executor: &mut EX,
executor: &mut E,
manager: &mut EM,
state: &mut S,
state: &mut E::State,
) -> Result<(), Error>
where
CS: Scheduler<I, S>,
EX: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
EM: EventManager<EX, I, S, Z>,
OT: ObserversTuple<I, S>,
Z: Evaluator<EX, EM, I, S> + HasScheduler<CS, I, S>;
E: Executor<EM, Z> + HasObservers,
CS: Scheduler<State = E::State>,
EM: UsesState<State = E::State>,
Z: HasScheduler<CS, State = E::State>;
}
/// Minimizes a corpus according to coverage maps, weighting by the specified `TestcaseScore`.
///
/// Algorithm based on WMOPT: <https://hexhive.epfl.ch/publications/files/21ISSTA2.pdf>
#[derive(Debug)]
pub struct MapCorpusMinimizer<E, I, O, S, TS>
pub struct MapCorpusMinimizer<E, O, T, TS>
where
E: Copy + Hash + Eq,
I: Input,
for<'a> O: MapObserver<Entry = E> + AsIter<'a, Item = E>,
S: HasMetadata + HasCorpus<I>,
TS: TestcaseScore<I, S>,
E: UsesState,
E::State: HasCorpus + HasMetadata,
TS: TestcaseScore<E::State>,
{
obs_name: String,
phantom: PhantomData<(E, I, O, S, TS)>,
phantom: PhantomData<(E, O, T, TS)>,
}
/// Standard corpus minimizer, which weights inputs by length and time.
pub type StdCorpusMinimizer<E, I, O, S> =
MapCorpusMinimizer<E, I, O, S, LenTimeMulTestcaseScore<I, S>>;
pub type StdCorpusMinimizer<E, O, T> =
MapCorpusMinimizer<E, O, T, LenTimeMulTestcaseScore<<E as UsesState>::State>>;
impl<E, I, O, S, TS> MapCorpusMinimizer<E, I, O, S, TS>
impl<E, O, T, TS> MapCorpusMinimizer<E, O, T, TS>
where
E: Copy + Hash + Eq,
I: Input,
for<'a> O: MapObserver<Entry = E> + AsIter<'a, Item = E>,
S: HasMetadata + HasCorpus<I>,
TS: TestcaseScore<I, S>,
E: UsesState,
E::State: HasCorpus + HasMetadata,
TS: TestcaseScore<E::State>,
{
/// Constructs a new `MapCorpusMinimizer` from a provided observer. This observer will be used
/// in the future to get observed maps from an executed input.
pub fn new(obs: &O) -> Self {
pub fn new(obs: &O) -> Self
where
O: Named,
{
Self {
obs_name: obs.name().to_string(),
phantom: PhantomData,
@ -84,27 +83,26 @@ where
}
}
impl<E, I, O, S, TS> CorpusMinimizer<I, S> for MapCorpusMinimizer<E, I, O, S, TS>
impl<E, O, T, TS> CorpusMinimizer<E> for MapCorpusMinimizer<E, O, T, TS>
where
E: Copy + Hash + Eq,
I: Input,
for<'a> O: MapObserver<Entry = E> + AsIter<'a, Item = E>,
S: HasMetadata + HasCorpus<I>,
TS: TestcaseScore<I, S>,
E: UsesState,
for<'a> O: MapObserver<Entry = T> + AsIter<'a, Item = T>,
E::State: HasMetadata + HasCorpus,
T: Copy + Hash + Eq,
TS: TestcaseScore<E::State>,
{
fn minimize<CS, EX, EM, OT, Z>(
fn minimize<CS, EM, Z>(
&self,
fuzzer: &mut Z,
executor: &mut EX,
executor: &mut E,
manager: &mut EM,
state: &mut S,
state: &mut E::State,
) -> Result<(), Error>
where
CS: Scheduler<I, S>,
EX: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
EM: EventManager<EX, I, S, Z>,
OT: ObserversTuple<I, S>,
Z: Evaluator<EX, EM, I, S> + HasScheduler<CS, I, S>,
E: Executor<EM, Z> + HasObservers,
CS: Scheduler<State = E::State>,
EM: UsesState<State = E::State>,
Z: HasScheduler<CS, State = E::State>,
{
let cfg = Config::default();
let ctx = Context::new(&cfg);

View File

@ -23,13 +23,10 @@ use core::cell::RefCell;
#[cfg(feature = "cmin")]
pub use minimizer::*;
use crate::{inputs::Input, Error};
use crate::{inputs::UsesInput, Error};
/// Corpus with all current testcases
pub trait Corpus<I>: serde::Serialize + for<'de> serde::Deserialize<'de>
where
I: Input,
{
pub trait Corpus: UsesInput + serde::Serialize + for<'de> serde::Deserialize<'de> {
/// Returns the number of elements
fn count(&self) -> usize;
@ -39,16 +36,20 @@ where
}
/// Add an entry to the corpus and return its index
fn add(&mut self, testcase: Testcase<I>) -> Result<usize, Error>;
fn add(&mut self, testcase: Testcase<Self::Input>) -> Result<usize, Error>;
/// Replaces the testcase at the given idx, returning the existing.
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<Testcase<I>, Error>;
fn replace(
&mut self,
idx: usize,
testcase: Testcase<Self::Input>,
) -> Result<Testcase<Self::Input>, Error>;
/// Removes an entry from the corpus, returning it if it was present.
fn remove(&mut self, idx: usize) -> Result<Option<Testcase<I>>, Error>;
fn remove(&mut self, idx: usize) -> Result<Option<Testcase<Self::Input>>, Error>;
/// Get by id
fn get(&self, idx: usize) -> Result<&RefCell<Testcase<I>>, Error>;
fn get(&self, idx: usize) -> Result<&RefCell<Testcase<Self::Input>>, Error>;
/// Current testcase scheduled
fn current(&self) -> &Option<usize>;
@ -72,7 +73,7 @@ pub mod pybind {
ondisk::pybind::PythonOnDiskCorpus, testcase::pybind::PythonTestcaseWrapper, Corpus,
Testcase,
},
inputs::BytesInput,
inputs::{BytesInput, UsesInput},
Error,
};
@ -170,7 +171,11 @@ pub mod pybind {
}
}
impl Corpus<BytesInput> for PythonCorpus {
impl UsesInput for PythonCorpus {
type Input = BytesInput;
}
impl Corpus for PythonCorpus {
#[inline]
fn count(&self) -> usize {
unwrap_me!(self.wrapper, c, { c.count() })

View File

@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize};
use crate::{
bolts::serdeany::SerdeAnyMap,
corpus::{Corpus, Testcase},
inputs::Input,
inputs::{Input, UsesInput},
state::HasMetadata,
Error,
};
@ -54,7 +54,14 @@ where
meta_format: Option<OnDiskMetadataFormat>,
}
impl<I> Corpus<I> for OnDiskCorpus<I>
impl<I> UsesInput for OnDiskCorpus<I>
where
I: Input,
{
type Input = I;
}
impl<I> Corpus for OnDiskCorpus<I>
where
I: Input,
{

View File

@ -2,7 +2,7 @@
//! It will contain a respective input, and metadata.
use alloc::string::String;
use core::{convert::Into, default::Default, option::Option, time::Duration};
use core::{default::Default, option::Option, time::Duration};
use serde::{Deserialize, Serialize};
@ -181,14 +181,11 @@ where
self.fuzzed = fuzzed;
}
/// Create a new Testcase instace given an input
/// Create a new Testcase instance given an input
#[inline]
pub fn new<T>(input: T) -> Self
where
T: Into<I>,
{
pub fn new(input: I) -> Self {
let mut slf = Testcase {
input: Some(input.into()),
input: Some(input),
..Testcase::default()
};
slf.input.as_mut().unwrap().wrapped_as_testcase();

View File

@ -11,9 +11,9 @@ use core::{marker::PhantomData, time::Duration};
#[cfg(feature = "std")]
use std::net::{SocketAddr, ToSocketAddrs};
use serde::de::DeserializeOwned;
use serde::Deserialize;
#[cfg(feature = "std")]
use serde::Serialize;
use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "std")]
use typed_builder::TypedBuilder;
@ -42,9 +42,9 @@ use crate::{
},
executors::{Executor, HasObservers},
fuzzer::{EvaluatorObservers, ExecutionProcessor},
inputs::Input,
inputs::{Input, UsesInput},
monitors::Monitor,
observers::ObserversTuple,
state::{HasClientPerfMonitor, HasExecutions, HasMetadata, UsesState},
Error,
};
@ -244,12 +244,10 @@ where
/// An [`EventManager`] that forwards all events to other attached fuzzers on shared maps or via tcp,
/// using low-level message passing, [`crate::bolts::llmp`].
pub struct LlmpEventManager<I, OT, S, SP>
pub struct LlmpEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
S: UsesInput,
SP: ShMemProvider + 'static,
//CE: CustomEvent<I>,
{
llmp: LlmpClient<SP>,
/// The custom buf handler
@ -257,14 +255,13 @@ where
#[cfg(feature = "llmp_compression")]
compressor: GzipCompressor,
configuration: EventConfig,
phantom: PhantomData<(I, OT, S)>,
phantom: PhantomData<S>,
}
impl<I, OT, S, SP> core::fmt::Debug for LlmpEventManager<I, OT, S, SP>
impl<S, SP> core::fmt::Debug for LlmpEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
SP: ShMemProvider + 'static,
S: UsesInput,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug_struct = f.debug_struct("LlmpEventManager");
@ -279,11 +276,10 @@ where
}
}
impl<I, OT, S, SP> Drop for LlmpEventManager<I, OT, S, SP>
impl<S, SP> Drop for LlmpEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
SP: ShMemProvider + 'static,
S: UsesInput,
{
/// LLMP clients will have to wait until their pages are mapped by somebody.
fn drop(&mut self) {
@ -291,10 +287,9 @@ where
}
}
impl<I, OT, S, SP> LlmpEventManager<I, OT, S, SP>
impl<S, SP> LlmpEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
S: UsesInput + HasExecutions + HasClientPerfMonitor,
SP: ShMemProvider + 'static,
{
/// Create a manager from a raw llmp client
@ -319,7 +314,7 @@ where
configuration: EventConfig,
) -> Result<Self, Error> {
Ok(Self {
llmp: llmp::LlmpClient::create_attach_to_tcp(shmem_provider, port)?,
llmp: LlmpClient::create_attach_to_tcp(shmem_provider, port)?,
#[cfg(feature = "llmp_compression")]
compressor: GzipCompressor::new(COMPRESS_THRESHOLD),
configuration,
@ -357,7 +352,7 @@ where
configuration: EventConfig,
) -> Result<Self, Error> {
Ok(Self {
llmp: llmp::LlmpClient::existing_client_from_description(shmem_provider, description)?,
llmp: LlmpClient::existing_client_from_description(shmem_provider, description)?,
#[cfg(feature = "llmp_compression")]
compressor: GzipCompressor::new(COMPRESS_THRESHOLD),
configuration,
@ -380,12 +375,12 @@ where
executor: &mut E,
state: &mut S,
_client_id: u32,
event: Event<I>,
event: Event<S::Input>,
) -> Result<(), Error>
where
OT: ObserversTuple<I, S> + DeserializeOwned,
E: Executor<Self, I, S, Z> + HasObservers<I, OT, S>,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S>,
E: Executor<Self, Z> + HasObservers<State = S>,
for<'a> E::Observers: Deserialize<'a>,
Z: ExecutionProcessor<E::Observers, State = S> + EvaluatorObservers<E::Observers>,
{
match event {
Event::NewTestcase {
@ -406,10 +401,13 @@ where
let _res = if client_config.match_with(&self.configuration)
&& observers_buf.is_some()
{
let observers: OT = postcard::from_bytes(observers_buf.as_ref().unwrap())?;
let observers: E::Observers =
postcard::from_bytes(observers_buf.as_ref().unwrap())?;
fuzzer.process_execution(state, self, input, &observers, &exit_kind, false)?
} else {
fuzzer.evaluate_input_with_observers(state, executor, self, input, false)?
fuzzer.evaluate_input_with_observers::<E, Self>(
state, executor, self, input, false,
)?
};
#[cfg(feature = "std")]
if let Some(item) = _res.1 {
@ -433,15 +431,25 @@ where
}
}
impl<I, OT, S, SP> EventFirer<I> for LlmpEventManager<I, OT, S, SP>
impl<S, SP> UsesState for LlmpEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
S: UsesInput,
SP: ShMemProvider,
{
type State = S;
}
impl<S, SP> EventFirer for LlmpEventManager<S, SP>
where
S: UsesInput,
SP: ShMemProvider,
//CE: CustomEvent<I>,
{
#[cfg(feature = "llmp_compression")]
fn fire<S2>(&mut self, _state: &mut S2, event: Event<I>) -> Result<(), Error> {
fn fire(
&mut self,
_state: &mut Self::State,
event: Event<<Self::State as UsesInput>::Input>,
) -> Result<(), Error> {
let serialized = postcard::to_allocvec(&event)?;
let flags: Flags = LLMP_FLAG_INITIALIZED;
@ -461,7 +469,11 @@ where
}
#[cfg(not(feature = "llmp_compression"))]
fn fire<S2>(&mut self, _state: &mut S2, event: Event<I>) -> Result<(), Error> {
fn fire(
&mut self,
_state: &mut Self::State,
event: Event<<Self::State as UsesInput>::Input>,
) -> Result<(), Error> {
let serialized = postcard::to_allocvec(&event)?;
self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?;
Ok(())
@ -472,12 +484,10 @@ where
}
}
impl<I, OT, S, SP> EventRestarter<S> for LlmpEventManager<I, OT, S, SP>
impl<S, SP> EventRestarter for LlmpEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
S: UsesInput,
SP: ShMemProvider,
//CE: CustomEvent<I>,
{
/// The llmp client needs to wait until a broker mapped all pages, before shutting down.
/// Otherwise, the OS may already have removed the shared maps,
@ -487,15 +497,20 @@ where
}
}
impl<E, I, OT, S, SP, Z> EventProcessor<E, I, S, Z> for LlmpEventManager<I, OT, S, SP>
impl<E, S, SP, Z> EventProcessor<E, Z> for LlmpEventManager<S, SP>
where
S: UsesInput + HasClientPerfMonitor + HasExecutions,
SP: ShMemProvider,
E: Executor<Self, I, S, Z> + HasObservers<I, OT, S>,
I: Input,
OT: ObserversTuple<I, S> + DeserializeOwned,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S>, //CE: CustomEvent<I>,
E: HasObservers<State = S> + Executor<Self, Z>,
for<'a> E::Observers: Deserialize<'a>,
Z: EvaluatorObservers<E::Observers, State = S> + ExecutionProcessor<E::Observers, State = S>,
{
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
fn process(
&mut self,
fuzzer: &mut Z,
state: &mut Self::State,
executor: &mut E,
) -> Result<usize, Error> {
// TODO: Get around local event copy by moving handle_in_client
let self_id = self.llmp.sender.id;
let mut count = 0;
@ -519,7 +534,7 @@ where
} else {
msg
};
let event: Event<I> = postcard::from_bytes(event_bytes)?;
let event: Event<S::Input> = postcard::from_bytes(event_bytes)?;
self.handle_in_client(fuzzer, executor, state, client_id, event)?;
count += 1;
}
@ -527,20 +542,19 @@ where
}
}
impl<E, I, OT, S, SP, Z> EventManager<E, I, S, Z> for LlmpEventManager<I, OT, S, SP>
impl<E, S, SP, Z> EventManager<E, Z> for LlmpEventManager<S, SP>
where
E: Executor<Self, I, S, Z> + HasObservers<I, OT, S>,
I: Input,
OT: ObserversTuple<I, S> + DeserializeOwned,
E: HasObservers<State = S> + Executor<Self, Z>,
for<'a> E::Observers: Deserialize<'a>,
S: UsesInput + HasExecutions + HasClientPerfMonitor + HasMetadata,
SP: ShMemProvider,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S>, //CE: CustomEvent<I>,
Z: EvaluatorObservers<E::Observers, State = S> + ExecutionProcessor<E::Observers, State = S>,
{
}
impl<I, OT, S, SP> HasCustomBufHandlers<S> for LlmpEventManager<I, OT, S, SP>
impl<S, SP> HasCustomBufHandlers<S> for LlmpEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
S: UsesInput,
SP: ShMemProvider,
{
fn add_custom_buf_handler(
@ -551,18 +565,16 @@ where
}
}
impl<I, OT, S, SP> ProgressReporter<I> for LlmpEventManager<I, OT, S, SP>
impl<S, SP> ProgressReporter for LlmpEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S> + DeserializeOwned,
S: UsesInput + HasExecutions + HasClientPerfMonitor + HasMetadata,
SP: ShMemProvider,
{
}
impl<I, OT, S, SP> HasEventManagerId for LlmpEventManager<I, OT, S, SP>
impl<S, SP> HasEventManagerId for LlmpEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S> + DeserializeOwned,
S: UsesInput,
SP: ShMemProvider,
{
/// Gets the id assigned to this staterestorer.
@ -576,38 +588,47 @@ where
/// A manager that can restart on the fly, storing states in-between (in `on_restart`)
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct LlmpRestartingEventManager<I, OT, S, SP>
pub struct LlmpRestartingEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
S: UsesInput,
SP: ShMemProvider + 'static,
//CE: CustomEvent<I>,
{
/// The embedded llmp event manager
llmp_mgr: LlmpEventManager<I, OT, S, SP>,
llmp_mgr: LlmpEventManager<S, SP>,
/// The staterestorer to serialize the state for the next runner
staterestorer: StateRestorer<SP>,
}
#[cfg(feature = "std")]
impl<I, OT, S, SP> ProgressReporter<I> for LlmpRestartingEventManager<I, OT, S, SP>
impl<S, SP> UsesState for LlmpRestartingEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
S: Serialize,
S: UsesInput,
SP: ShMemProvider + 'static,
{
type State = S;
}
#[cfg(feature = "std")]
impl<S, SP> ProgressReporter for LlmpRestartingEventManager<S, SP>
where
S: UsesInput + HasExecutions + HasClientPerfMonitor + HasMetadata + Serialize,
SP: ShMemProvider,
{
}
#[cfg(feature = "std")]
impl<I, OT, S, SP> EventFirer<I> for LlmpRestartingEventManager<I, OT, S, SP>
impl<S, SP> EventFirer for LlmpRestartingEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
SP: ShMemProvider,
S: UsesInput,
//CE: CustomEvent<I>,
{
fn fire<S2>(&mut self, state: &mut S2, event: Event<I>) -> Result<(), Error> {
fn fire(
&mut self,
state: &mut Self::State,
event: Event<<Self::State as UsesInput>::Input>,
) -> 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.llmp_mgr.fire(state, event)
}
@ -618,11 +639,9 @@ where
}
#[cfg(feature = "std")]
impl<I, OT, S, SP> EventRestarter<S> for LlmpRestartingEventManager<I, OT, S, SP>
impl<S, SP> EventRestarter for LlmpRestartingEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S>,
S: Serialize,
S: UsesInput + HasExecutions + HasClientPerfMonitor + Serialize,
SP: ShMemProvider,
//CE: CustomEvent<I>,
{
@ -643,14 +662,13 @@ where
}
#[cfg(feature = "std")]
impl<E, I, OT, S, SP, Z> EventProcessor<E, I, S, Z> for LlmpRestartingEventManager<I, OT, S, SP>
impl<E, S, SP, Z> EventProcessor<E, Z> for LlmpRestartingEventManager<S, SP>
where
E: Executor<LlmpEventManager<I, OT, S, SP>, I, S, Z> + HasObservers<I, OT, S>,
I: Input,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S>,
OT: ObserversTuple<I, S> + DeserializeOwned,
E: HasObservers<State = S> + Executor<LlmpEventManager<S, SP>, Z>,
for<'a> E::Observers: Deserialize<'a>,
S: UsesInput + HasExecutions + HasClientPerfMonitor,
SP: ShMemProvider + 'static,
//CE: CustomEvent<I>,
Z: EvaluatorObservers<E::Observers, State = S> + ExecutionProcessor<E::Observers>, //CE: CustomEvent<I>,
{
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
self.llmp_mgr.process(fuzzer, state, executor)
@ -658,24 +676,20 @@ where
}
#[cfg(feature = "std")]
impl<E, I, OT, S, SP, Z> EventManager<E, I, S, Z> for LlmpRestartingEventManager<I, OT, S, SP>
impl<E, S, SP, Z> EventManager<E, Z> for LlmpRestartingEventManager<S, SP>
where
E: Executor<LlmpEventManager<I, OT, S, SP>, I, S, Z> + HasObservers<I, OT, S>,
I: Input,
S: Serialize,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S>,
OT: ObserversTuple<I, S> + DeserializeOwned,
E: HasObservers<State = S> + Executor<LlmpEventManager<S, SP>, Z>,
for<'a> E::Observers: Deserialize<'a>,
S: UsesInput + HasExecutions + HasClientPerfMonitor + HasMetadata + Serialize,
SP: ShMemProvider + 'static,
//CE: CustomEvent<I>,
Z: EvaluatorObservers<E::Observers, State = S> + ExecutionProcessor<E::Observers>, //CE: CustomEvent<I>,
{
}
#[cfg(feature = "std")]
impl<I, OT, S, SP> HasEventManagerId for LlmpRestartingEventManager<I, OT, S, SP>
impl<S, SP> HasEventManagerId for LlmpRestartingEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S> + DeserializeOwned,
S: Serialize,
S: UsesInput + Serialize,
SP: ShMemProvider + 'static,
{
fn mgr_id(&self) -> EventManagerId {
@ -690,15 +704,14 @@ const _ENV_FUZZER_RECEIVER: &str = "_AFL_ENV_FUZZER_RECEIVER";
const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = "_AFL_ENV_FUZZER_BROKER_CLIENT";
#[cfg(feature = "std")]
impl<I, OT, S, SP> LlmpRestartingEventManager<I, OT, S, SP>
impl<S, SP> LlmpRestartingEventManager<S, SP>
where
I: Input,
OT: ObserversTuple<I, S> + DeserializeOwned,
S: UsesInput,
SP: ShMemProvider + 'static,
//CE: CustomEvent<I>,
{
/// Create a new runner, the executed child doing the actual fuzzing.
pub fn new(llmp_mgr: LlmpEventManager<I, OT, S, SP>, staterestorer: StateRestorer<SP>) -> Self {
pub fn new(llmp_mgr: LlmpEventManager<S, SP>, staterestorer: StateRestorer<SP>) -> Self {
Self {
llmp_mgr,
staterestorer,
@ -736,22 +749,14 @@ pub enum ManagerKind {
/// The restarter will spawn a new process each time the child crashes or timeouts.
#[cfg(feature = "std")]
#[allow(clippy::type_complexity)]
pub fn setup_restarting_mgr_std<I, MT, OT, S>(
pub fn setup_restarting_mgr_std<MT, S>(
monitor: MT,
broker_port: u16,
configuration: EventConfig,
) -> Result<
(
Option<S>,
LlmpRestartingEventManager<I, OT, S, StdShMemProvider>,
),
Error,
>
) -> Result<(Option<S>, LlmpRestartingEventManager<S, StdShMemProvider>), Error>
where
I: Input,
MT: Monitor + Clone,
OT: ObserversTuple<I, S> + DeserializeOwned,
S: DeserializeOwned,
S: DeserializeOwned + UsesInput + HasClientPerfMonitor + HasExecutions,
{
RestartingMgr::builder()
.shmem_provider(StdShMemProvider::new()?)
@ -768,11 +773,9 @@ where
#[cfg(feature = "std")]
#[allow(clippy::default_trait_access)]
#[derive(TypedBuilder, Debug)]
pub struct RestartingMgr<I, MT, OT, S, SP>
pub struct RestartingMgr<MT, S, SP>
where
I: Input,
OT: ObserversTuple<I, S> + DeserializeOwned,
S: DeserializeOwned,
S: UsesInput + DeserializeOwned,
SP: ShMemProvider + 'static,
MT: Monitor,
//CE: CustomEvent<I>,
@ -795,28 +798,25 @@ where
#[builder(default = ManagerKind::Any)]
kind: ManagerKind,
#[builder(setter(skip), default = PhantomData)]
phantom_data: PhantomData<(I, OT, S)>,
phantom_data: PhantomData<S>,
}
#[cfg(feature = "std")]
#[allow(clippy::type_complexity, clippy::too_many_lines)]
impl<I, MT, OT, S, SP> RestartingMgr<I, MT, OT, S, SP>
impl<MT, S, SP> RestartingMgr<MT, S, SP>
where
I: Input,
OT: ObserversTuple<I, S> + DeserializeOwned,
S: DeserializeOwned,
SP: ShMemProvider,
S: UsesInput + HasExecutions + HasClientPerfMonitor + DeserializeOwned,
MT: Monitor + Clone,
{
/// Launch the restarting manager
pub fn launch(
&mut self,
) -> Result<(Option<S>, LlmpRestartingEventManager<I, OT, S, SP>), Error> {
pub fn launch(&mut self) -> Result<(Option<S>, LlmpRestartingEventManager<S, SP>), Error> {
// We start ourself as child process to actually fuzz
let (staterestorer, new_shmem_provider, core_id) = if std::env::var(_ENV_FUZZER_SENDER)
.is_err()
{
let broker_things = |mut broker: LlmpEventBroker<I, MT, SP>, remote_broker_addr| {
let broker_things = |mut broker: LlmpEventBroker<S::Input, MT, SP>,
remote_broker_addr| {
if let Some(remote_broker_addr) = remote_broker_addr {
println!("B2b: Connecting to {:?}", &remote_broker_addr);
broker.connect_b2b(remote_broker_addr)?;
@ -832,7 +832,7 @@ where
LlmpConnection::on_port(self.shmem_provider.clone(), self.broker_port)?;
match connection {
LlmpConnection::IsBroker { broker } => {
let event_broker = LlmpEventBroker::<I, MT, SP>::new(
let event_broker = LlmpEventBroker::<S::Input, MT, SP>::new(
broker,
self.monitor.take().unwrap(),
)?;
@ -847,14 +847,13 @@ where
return Err(Error::shutting_down());
}
LlmpConnection::IsClient { client } => {
let mgr =
LlmpEventManager::<I, OT, S, SP>::new(client, self.configuration)?;
let mgr = LlmpEventManager::<S, SP>::new(client, self.configuration)?;
(mgr, None)
}
}
}
ManagerKind::Broker => {
let event_broker = LlmpEventBroker::<I, MT, SP>::new_on_port(
let event_broker = LlmpEventBroker::<S::Input, MT, SP>::new_on_port(
self.shmem_provider.clone(),
self.monitor.take().unwrap(),
self.broker_port,
@ -866,7 +865,7 @@ where
}
ManagerKind::Client { cpu_core } => {
// We are a client
let mgr = LlmpEventManager::<I, OT, S, SP>::new_on_port(
let mgr = LlmpEventManager::<S, SP>::new_on_port(
self.shmem_provider.clone(),
self.broker_port,
self.configuration,
@ -967,7 +966,7 @@ where
} else {
println!("First run. Let's set it all up");
// Mgr to send and receive msgs from/to all other fuzzer instances
let mgr = LlmpEventManager::<I, OT, S, SP>::existing_client_from_env(
let mgr = LlmpEventManager::<S, SP>::existing_client_from_env(
new_shmem_provider,
_ENV_FUZZER_BROKER_CLIENT_INITIAL,
self.configuration,
@ -1006,12 +1005,14 @@ mod tests {
corpus::{Corpus, InMemoryCorpus, Testcase},
events::{llmp::_ENV_FUZZER_SENDER, LlmpEventManager},
executors::{ExitKind, InProcessExecutor},
feedbacks::ConstFeedback,
fuzzer::Fuzzer,
inputs::BytesInput,
mutators::BitFlipMutator,
schedulers::RandScheduler,
stages::StdMutationalStage,
state::StdState,
Fuzzer, StdFuzzer,
StdFuzzer,
};
#[test]
@ -1020,12 +1021,16 @@ mod tests {
let rand = StdRand::with_seed(0);
let mut corpus = InMemoryCorpus::<BytesInput>::new();
let testcase = Testcase::new(vec![0; 4]);
let testcase = Testcase::new(vec![0; 4].into());
corpus.add(testcase).unwrap();
let solutions = InMemoryCorpus::<BytesInput>::new();
let mut state = StdState::new(rand, corpus, solutions, &mut (), &mut ()).unwrap();
let mut feedback = ConstFeedback::new(false);
let mut objective = ConstFeedback::new(false);
let mut state =
StdState::new(rand, corpus, solutions, &mut feedback, &mut objective).unwrap();
let mut shmem_provider = StdShMemProvider::new().unwrap();
@ -1041,12 +1046,14 @@ mod tests {
llmp_client.mark_safe_to_unmap();
}
let mut llmp_mgr =
LlmpEventManager::<BytesInput, (), _, _>::new(llmp_client, "fuzzer".into()).unwrap();
let mut llmp_mgr = LlmpEventManager::new(llmp_client, "fuzzer".into()).unwrap();
let scheduler = RandScheduler::new();
let mut fuzzer = StdFuzzer::new(scheduler, (), ());
let feedback = ConstFeedback::new(true);
let objective = ConstFeedback::new(false);
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let mut harness = |_buf: &BytesInput| ExitKind::Ok;
let mut executor = InProcessExecutor::new(

View File

@ -37,6 +37,7 @@ pub struct EventManagerId {
#[cfg(feature = "introspection")]
use crate::monitors::ClientPerfMonitor;
use crate::{inputs::UsesInput, state::UsesState};
/// The log event severity
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
@ -300,10 +301,7 @@ where
}
/// [`EventFirer`] fire an event.
pub trait EventFirer<I>
where
I: Input,
{
pub trait EventFirer: UsesState {
/// Send off an [`Event`] to the broker
///
/// For multi-processed managers, such as [`llmp::LlmpEventManager`],
@ -312,13 +310,17 @@ where
/// (for example for each [`Input`], on multiple cores)
/// the [`llmp`] shared map may fill up and the client will eventually OOM or [`panic`].
/// This should not happen for a normal use-case.
fn fire<S>(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error>;
fn fire(
&mut self,
state: &mut Self::State,
event: Event<<Self::State as UsesInput>::Input>,
) -> Result<(), Error>;
/// Send off an [`Event::Log`] event to the broker.
/// This is a shortcut for [`EventFirer::fire`] with [`Event::Log`] as argument.
fn log<S>(
fn log(
&mut self,
state: &mut S,
state: &mut Self::State,
severity_level: LogSeverity,
message: String,
) -> Result<(), Error> {
@ -333,9 +335,9 @@ where
}
/// Serialize all observers for this type and manager
fn serialize_observers<OT, S>(&mut self, observers: &OT) -> Result<Vec<u8>, Error>
fn serialize_observers<OT>(&mut self, observers: &OT) -> Result<Vec<u8>, Error>
where
OT: ObserversTuple<I, S> + Serialize,
OT: ObserversTuple<Self::State> + Serialize,
{
Ok(postcard::to_allocvec(observers)?)
}
@ -347,22 +349,19 @@ where
}
/// [`ProgressReporter`] report progress to the broker.
pub trait ProgressReporter<I>: EventFirer<I>
pub trait ProgressReporter: EventFirer
where
I: Input,
Self::State: HasClientPerfMonitor + HasMetadata + HasExecutions,
{
/// Given the last time, if `monitor_timeout` seconds passed, send off an info/monitor/heartbeat message to the broker.
/// Returns the new `last` time (so the old one, unless `monitor_timeout` time has passed and monitor have been sent)
/// Will return an [`crate::Error`], if the stats could not be sent.
fn maybe_report_progress<S>(
fn maybe_report_progress(
&mut self,
state: &mut S,
state: &mut Self::State,
last_report_time: Duration,
monitor_timeout: Duration,
) -> Result<Duration, Error>
where
S: HasExecutions + HasClientPerfMonitor + HasMetadata,
{
) -> Result<Duration, Error> {
let executions = *state.executions();
let cur = current_time();
// default to 0 here to avoid crashes on clock skew
@ -421,10 +420,10 @@ where
}
/// Restartable trait
pub trait EventRestarter<S> {
pub trait EventRestarter: UsesState {
/// For restarting event managers, implement a way to forward state to their next peers.
#[inline]
fn on_restart(&mut self, _state: &mut S) -> Result<(), Error> {
fn on_restart(&mut self, _state: &mut Self::State) -> Result<(), Error> {
Ok(())
}
@ -434,18 +433,15 @@ pub trait EventRestarter<S> {
}
/// [`EventProcessor`] process all the incoming messages
pub trait EventProcessor<E, I, S, Z> {
pub trait EventProcessor<E, Z>: UsesState {
/// Lookup for incoming events and process them.
/// Return the number of processes events or an error
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error>;
/// Deserialize all observers for this type and manager
fn deserialize_observers<OT>(&mut self, observers_buf: &[u8]) -> Result<OT, Error>
where
OT: ObserversTuple<I, S> + serde::de::DeserializeOwned,
{
Ok(postcard::from_bytes(observers_buf)?)
}
fn process(
&mut self,
fuzzer: &mut Z,
state: &mut Self::State,
executor: &mut E,
) -> Result<usize, Error>;
}
/// The id of this [`EventManager`].
/// For multi processed [`EventManager`]s,
@ -458,14 +454,10 @@ pub trait HasEventManagerId {
/// [`EventManager`] is the main communications hub.
/// For the "normal" multi-processed mode, you may want to look into [`LlmpRestartingEventManager`]
pub trait EventManager<E, I, S, Z>:
EventFirer<I>
+ EventProcessor<E, I, S, Z>
+ EventRestarter<S>
+ HasEventManagerId
+ ProgressReporter<I>
pub trait EventManager<E, Z>:
EventFirer + EventProcessor<E, Z> + EventRestarter + HasEventManagerId + ProgressReporter
where
I: Input,
Self::State: HasClientPerfMonitor + HasMetadata + HasExecutions,
{
}
@ -480,34 +472,63 @@ pub trait HasCustomBufHandlers<S> {
}
/// An eventmgr for tests, and as placeholder if you really don't need an event manager.
#[derive(Copy, Clone, Debug)]
pub struct NopEventManager {}
#[derive(Copy, Clone, Debug, Default)]
pub struct NopEventManager<S> {
phantom: PhantomData<S>,
}
impl<I> EventFirer<I> for NopEventManager
impl<S> NopEventManager<S> {
/// Creates a new [`NopEventManager`]
#[must_use]
pub fn new() -> Self {
NopEventManager {
phantom: PhantomData,
}
}
}
impl<S> UsesState for NopEventManager<S>
where
I: Input,
S: UsesInput,
{
fn fire<S>(&mut self, _state: &mut S, _event: Event<I>) -> Result<(), Error> {
type State = S;
}
impl<S> EventFirer for NopEventManager<S>
where
S: UsesInput,
{
fn fire(
&mut self,
_state: &mut Self::State,
_event: Event<<Self::State as UsesInput>::Input>,
) -> Result<(), Error> {
Ok(())
}
}
impl<S> EventRestarter<S> for NopEventManager {}
impl<S> EventRestarter for NopEventManager<S> where S: UsesInput {}
impl<E, I, S, Z> EventProcessor<E, I, S, Z> for NopEventManager {
impl<E, S, Z> EventProcessor<E, Z> for NopEventManager<S>
where
S: UsesInput + HasClientPerfMonitor + HasExecutions,
{
fn process(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_state: &mut Self::State,
_executor: &mut E,
) -> Result<usize, Error> {
Ok(0)
}
}
impl<E, I, S, Z> EventManager<E, I, S, Z> for NopEventManager where I: Input {}
impl<E, S, Z> EventManager<E, Z> for NopEventManager<S> where
S: UsesInput + HasClientPerfMonitor + HasExecutions + HasMetadata
{
}
impl<S> HasCustomBufHandlers<S> for NopEventManager {
impl<S> HasCustomBufHandlers<S> for NopEventManager<S> {
fn add_custom_buf_handler(
&mut self,
_handler: Box<dyn FnMut(&mut S, &String, &[u8]) -> Result<CustomBufEventResult, Error>>,
@ -515,9 +536,12 @@ impl<S> HasCustomBufHandlers<S> for NopEventManager {
}
}
impl<I> ProgressReporter<I> for NopEventManager where I: Input {}
impl<S> ProgressReporter for NopEventManager<S> where
S: UsesInput + HasClientPerfMonitor + HasExecutions + HasMetadata
{
}
impl HasEventManagerId for NopEventManager {
impl<S> HasEventManagerId for NopEventManager<S> {
fn mgr_id(&self) -> EventManagerId {
EventManagerId { id: 0 }
}
@ -594,7 +618,7 @@ pub mod pybind {
executors::pybind::PythonExecutor,
fuzzer::pybind::PythonStdFuzzer,
inputs::BytesInput,
state::pybind::PythonStdState,
state::{pybind::PythonStdState, UsesState},
Error,
};
@ -637,17 +661,19 @@ pub mod pybind {
}
}
impl EventFirer<BytesInput> for PythonEventManager {
fn fire<S>(&mut self, state: &mut S, event: Event<BytesInput>) -> Result<(), Error> {
impl UsesState for PythonEventManager {
type State = PythonStdState;
}
impl EventFirer for PythonEventManager {
fn fire(&mut self, state: &mut Self::State, event: Event<BytesInput>) -> Result<(), Error> {
unwrap_me_mut!(self.wrapper, e, { e.fire(state, event) })
}
}
impl<S> EventRestarter<S> for PythonEventManager {}
impl EventRestarter for PythonEventManager {}
impl EventProcessor<PythonExecutor, BytesInput, PythonStdState, PythonStdFuzzer>
for PythonEventManager
{
impl EventProcessor<PythonExecutor, PythonStdFuzzer> for PythonEventManager {
fn process(
&mut self,
fuzzer: &mut PythonStdFuzzer,
@ -658,7 +684,7 @@ pub mod pybind {
}
}
impl ProgressReporter<BytesInput> for PythonEventManager {}
impl ProgressReporter for PythonEventManager {}
impl HasEventManagerId for PythonEventManager {
fn mgr_id(&self) -> EventManagerId {
@ -666,10 +692,7 @@ pub mod pybind {
}
}
impl EventManager<PythonExecutor, BytesInput, PythonStdState, PythonStdFuzzer>
for PythonEventManager
{
}
impl EventManager<PythonExecutor, PythonStdFuzzer> for PythonEventManager {}
/// Register the classes to the python module
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {

View File

@ -21,7 +21,7 @@ use crate::bolts::os::{fork, ForkResult};
use crate::{
bolts::{shmem::ShMemProvider, staterestore::StateRestorer},
corpus::Corpus,
executors::Executor,
monitors::SimplePrintingMonitor,
state::{HasCorpus, HasSolutions},
};
use crate::{
@ -29,8 +29,9 @@ use crate::{
BrokerEventResult, Event, EventFirer, EventManager, EventManagerId, EventProcessor,
EventRestarter, HasEventManagerId,
},
inputs::Input,
inputs::UsesInput,
monitors::Monitor,
state::{HasClientPerfMonitor, HasExecutions, HasMetadata, UsesState},
Error,
};
@ -41,24 +42,23 @@ const _ENV_FUZZER_RECEIVER: &str = "_AFL_ENV_FUZZER_RECEIVER";
const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = "_AFL_ENV_FUZZER_BROKER_CLIENT";
/// A simple, single-threaded event manager that just logs
pub struct SimpleEventManager<I, MT, S>
pub struct SimpleEventManager<MT, S>
where
I: Input,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
S: UsesInput,
{
/// The monitor
monitor: MT,
/// The events that happened since the last handle_in_broker
events: Vec<Event<I>>,
events: Vec<Event<S::Input>>,
/// The custom buf handler
custom_buf_handlers: Vec<Box<CustomBufHandlerFn<S>>>,
phantom: PhantomData<S>,
}
impl<I, MT, S> Debug for SimpleEventManager<I, MT, S>
impl<MT, S> Debug for SimpleEventManager<MT, S>
where
I: Input,
MT: Monitor + Debug,
MT: Debug,
S: UsesInput,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SimpleEventManager")
@ -69,12 +69,23 @@ where
}
}
impl<I, MT, S> EventFirer<I> for SimpleEventManager<I, MT, S>
impl<MT, S> UsesState for SimpleEventManager<MT, S>
where
I: Input,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
S: UsesInput,
{
fn fire<S2>(&mut self, _state: &mut S2, event: Event<I>) -> Result<(), Error> {
type State = S;
}
impl<MT, S> EventFirer for SimpleEventManager<MT, S>
where
MT: Monitor,
S: UsesInput,
{
fn fire(
&mut self,
_state: &mut Self::State,
event: Event<<Self::State as UsesInput>::Input>,
) -> Result<(), Error> {
match Self::handle_in_broker(&mut self.monitor, &event)? {
BrokerEventResult::Forward => self.events.push(event),
BrokerEventResult::Handled => (),
@ -83,17 +94,17 @@ where
}
}
impl<I, MT, S> EventRestarter<S> for SimpleEventManager<I, MT, S>
impl<MT, S> EventRestarter for SimpleEventManager<MT, S>
where
I: Input,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
MT: Monitor,
S: UsesInput,
{
}
impl<E, I, MT, S, Z> EventProcessor<E, I, S, Z> for SimpleEventManager<I, MT, S>
impl<E, MT, S, Z> EventProcessor<E, Z> for SimpleEventManager<MT, S>
where
I: Input,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
MT: Monitor,
S: UsesInput,
{
fn process(
&mut self,
@ -110,17 +121,17 @@ where
}
}
impl<E, I, MT, S, Z> EventManager<E, I, S, Z> for SimpleEventManager<I, MT, S>
impl<E, MT, S, Z> EventManager<E, Z> for SimpleEventManager<MT, S>
where
I: Input,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
MT: Monitor,
S: UsesInput + HasClientPerfMonitor + HasExecutions + HasMetadata,
{
}
impl<I, MT, S> HasCustomBufHandlers<S> for SimpleEventManager<I, MT, S>
impl<MT, S> HasCustomBufHandlers<S> for SimpleEventManager<MT, S>
where
I: Input,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
MT: Monitor, //CE: CustomEvent<I, OT>,
S: UsesInput,
{
/// Adds a custom buffer handler that will run for each incoming `CustomBuf` event.
fn add_custom_buf_handler(
@ -131,27 +142,39 @@ where
}
}
impl<I, MT, S> ProgressReporter<I> for SimpleEventManager<I, MT, S>
impl<MT, S> ProgressReporter for SimpleEventManager<MT, S>
where
I: Input,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
MT: Monitor,
S: UsesInput + HasExecutions + HasClientPerfMonitor + HasMetadata,
{
}
impl<I, MT, S> HasEventManagerId for SimpleEventManager<I, MT, S>
impl<MT, S> HasEventManagerId for SimpleEventManager<MT, S>
where
I: Input,
MT: Monitor + Debug,
MT: Monitor,
S: UsesInput,
{
fn mgr_id(&self) -> EventManagerId {
EventManagerId { id: 0 }
}
}
impl<I, MT, S> SimpleEventManager<I, MT, S>
#[cfg(feature = "std")]
impl<S> SimpleEventManager<SimplePrintingMonitor, S>
where
I: Input,
MT: Monitor + Debug, //TODO CE: CustomEvent,
S: UsesInput,
{
/// Creates a [`SimpleEventManager`] that just prints to `stdout`.
#[must_use]
pub fn printing() -> Self {
Self::new(SimplePrintingMonitor::new())
}
}
impl<MT, S> SimpleEventManager<MT, S>
where
MT: Monitor, //TODO CE: CustomEvent,
S: UsesInput,
{
/// Creates a new [`SimpleEventManager`].
pub fn new(monitor: MT) -> Self {
@ -165,7 +188,10 @@ where
/// Handle arriving events in the broker
#[allow(clippy::unnecessary_wraps)]
fn handle_in_broker(monitor: &mut MT, event: &Event<I>) -> Result<BrokerEventResult, Error> {
fn handle_in_broker(
monitor: &mut MT,
event: &Event<S::Input>,
) -> Result<BrokerEventResult, Error> {
match event {
Event::NewTestcase {
input: _,
@ -247,7 +273,7 @@ where
// Handle arriving events in the client
#[allow(clippy::needless_pass_by_value, clippy::unused_self)]
fn handle_in_client(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error> {
fn handle_in_client(&mut self, state: &mut S, event: Event<S::Input>) -> Result<(), Error> {
if let Event::CustomBuf { tag, buf } = &event {
for handler in &mut self.custom_buf_handlers {
handler(state, tag, buf)?;
@ -268,37 +294,47 @@ where
#[cfg(feature = "std")]
#[allow(clippy::default_trait_access)]
#[derive(Debug)]
pub struct SimpleRestartingEventManager<I, MT, S, SP>
pub struct SimpleRestartingEventManager<MT, S, SP>
where
I: Input,
SP: ShMemProvider,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
S: UsesInput,
SP: ShMemProvider, //CE: CustomEvent<I, OT>,
{
/// The actual simple event mgr
simple_event_mgr: SimpleEventManager<I, MT, S>,
simple_event_mgr: SimpleEventManager<MT, S>,
/// [`StateRestorer`] for restarts
staterestorer: StateRestorer<SP>,
}
#[cfg(feature = "std")]
impl<I, MT, S, SP> EventFirer<I> for SimpleRestartingEventManager<I, MT, S, SP>
impl<MT, S, SP> UsesState for SimpleRestartingEventManager<MT, S, SP>
where
I: Input,
S: UsesInput,
SP: ShMemProvider,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
{
fn fire<S2>(&mut self, _state: &mut S2, event: Event<I>) -> Result<(), Error> {
type State = S;
}
#[cfg(feature = "std")]
impl<MT, S, SP> EventFirer for SimpleRestartingEventManager<MT, S, SP>
where
MT: Monitor,
S: UsesInput,
SP: ShMemProvider,
{
fn fire(
&mut self,
_state: &mut Self::State,
event: Event<<Self::State as UsesInput>::Input>,
) -> Result<(), Error> {
self.simple_event_mgr.fire(_state, event)
}
}
#[cfg(feature = "std")]
impl<I, MT, S, SP> EventRestarter<S> for SimpleRestartingEventManager<I, MT, S, SP>
impl<MT, S, SP> EventRestarter for SimpleRestartingEventManager<MT, S, SP>
where
I: Input,
S: Serialize,
S: UsesInput + Serialize,
SP: ShMemProvider,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
{
/// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner.
fn on_restart(&mut self, state: &mut S) -> Result<(), Error> {
@ -309,35 +345,37 @@ where
}
#[cfg(feature = "std")]
impl<E, I, S, SP, MT, Z> EventProcessor<E, I, S, Z> for SimpleRestartingEventManager<I, MT, S, SP>
impl<E, MT, S, SP, Z> EventProcessor<E, Z> for SimpleRestartingEventManager<MT, S, SP>
where
I: Input,
S: Serialize,
MT: Monitor,
S: UsesInput + HasClientPerfMonitor + HasExecutions + Serialize,
SP: ShMemProvider,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
{
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
fn process(
&mut self,
fuzzer: &mut Z,
state: &mut Self::State,
executor: &mut E,
) -> Result<usize, Error> {
self.simple_event_mgr.process(fuzzer, state, executor)
}
}
#[cfg(feature = "std")]
impl<E, I, S, SP, MT, Z> EventManager<E, I, S, Z> for SimpleRestartingEventManager<I, MT, S, SP>
impl<E, MT, S, SP, Z> EventManager<E, Z> for SimpleRestartingEventManager<MT, S, SP>
where
E: Executor<Self, I, S, Z>,
I: Input,
S: Serialize,
MT: Monitor,
S: UsesInput + HasExecutions + HasClientPerfMonitor + HasMetadata + Serialize,
SP: ShMemProvider,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
{
}
#[cfg(feature = "std")]
impl<I, MT, S, SP> HasCustomBufHandlers<S> for SimpleRestartingEventManager<I, MT, S, SP>
impl<MT, S, SP> HasCustomBufHandlers<S> for SimpleRestartingEventManager<MT, S, SP>
where
I: Input,
MT: Monitor,
S: UsesInput,
SP: ShMemProvider,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
{
fn add_custom_buf_handler(
&mut self,
@ -348,20 +386,20 @@ where
}
#[cfg(feature = "std")]
impl<I, MT, S, SP> ProgressReporter<I> for SimpleRestartingEventManager<I, MT, S, SP>
impl<MT, S, SP> ProgressReporter for SimpleRestartingEventManager<MT, S, SP>
where
I: Input,
MT: Monitor,
S: UsesInput + HasExecutions + HasClientPerfMonitor + HasMetadata,
SP: ShMemProvider,
MT: Monitor + Debug, //CE: CustomEvent<I, OT>,
{
}
#[cfg(feature = "std")]
impl<I, MT, S, SP> HasEventManagerId for SimpleRestartingEventManager<I, MT, S, SP>
impl<MT, S, SP> HasEventManagerId for SimpleRestartingEventManager<MT, S, SP>
where
I: Input,
MT: Monitor,
S: UsesInput,
SP: ShMemProvider,
MT: Monitor + Debug,
{
fn mgr_id(&self) -> EventManagerId {
self.simple_event_mgr.mgr_id()
@ -370,11 +408,11 @@ where
#[cfg(feature = "std")]
#[allow(clippy::type_complexity, clippy::too_many_lines)]
impl<I, MT, S, SP> SimpleRestartingEventManager<I, MT, S, SP>
impl<MT, S, SP> SimpleRestartingEventManager<MT, S, SP>
where
I: Input,
S: UsesInput,
SP: ShMemProvider,
MT: Monitor + Debug, //TODO CE: CustomEvent,
MT: Monitor, //TODO CE: CustomEvent,
{
/// Creates a new [`SimpleEventManager`].
fn new_launched(monitor: MT, staterestorer: StateRestorer<SP>) -> Self {
@ -390,7 +428,7 @@ where
#[allow(clippy::similar_names)]
pub fn launch(mut monitor: MT, shmem_provider: &mut SP) -> Result<(Option<S>, Self), Error>
where
S: DeserializeOwned + Serialize + HasCorpus<I> + HasSolutions<I>,
S: DeserializeOwned + Serialize + HasCorpus + HasSolutions,
MT: Debug,
{
// We start ourself as child process to actually fuzz
@ -498,7 +536,6 @@ pub mod pybind {
use crate::{
events::{pybind::PythonEventManager, SimpleEventManager},
inputs::BytesInput,
monitors::pybind::PythonMonitor,
state::pybind::PythonStdState,
};
@ -508,7 +545,7 @@ pub mod pybind {
/// Python class for SimpleEventManager
pub struct PythonSimpleEventManager {
/// Rust wrapped SimpleEventManager object
pub inner: SimpleEventManager<BytesInput, PythonMonitor, PythonStdState>,
pub inner: SimpleEventManager<PythonMonitor, PythonStdState>,
}
#[pymethods]

View File

@ -5,25 +5,26 @@ use core::fmt::Debug;
use crate::{
executors::{Executor, ExitKind, HasObservers},
inputs::Input,
observers::ObserversTuple,
observers::UsesObservers,
state::UsesState,
Error,
};
/// A [`CombinedExecutor`] wraps a primary executor, forwarding its methods, and a secondary one
#[derive(Debug)]
pub struct CombinedExecutor<A: Debug, B: Debug> {
pub struct CombinedExecutor<A, B> {
primary: A,
secondary: B,
}
impl<A: Debug, B: Debug> CombinedExecutor<A, B> {
impl<A, B> CombinedExecutor<A, B> {
/// Create a new `CombinedExecutor`, wrapping the given `executor`s.
pub fn new<EM, I, S, Z>(primary: A, secondary: B) -> Self
pub fn new<EM, Z>(primary: A, secondary: B) -> Self
where
A: Executor<EM, I, S, Z>,
B: Executor<EM, I, S, Z>,
I: Input,
A: Executor<EM, Z>,
B: Executor<EM, Z, State = A::State>,
EM: UsesState<State = A::State>,
Z: UsesState<State = A::State>,
{
Self { primary, secondary }
}
@ -39,18 +40,19 @@ impl<A: Debug, B: Debug> CombinedExecutor<A, B> {
}
}
impl<A, B, EM, I, S, Z> Executor<EM, I, S, Z> for CombinedExecutor<A, B>
impl<A, B, EM, Z> Executor<EM, Z> for CombinedExecutor<A, B>
where
A: Executor<EM, I, S, Z>,
B: Executor<EM, I, S, Z>,
I: Input,
A: Executor<EM, Z>,
B: Executor<EM, Z, State = A::State>,
EM: UsesState<State = A::State>,
Z: UsesState<State = A::State>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let ret = self.primary.run_target(fuzzer, state, mgr, input);
self.primary.post_run_reset();
@ -59,19 +61,31 @@ where
}
}
impl<A, B, I, OT, S> HasObservers<I, OT, S> for CombinedExecutor<A, B>
impl<A, B> UsesState for CombinedExecutor<A, B>
where
A: HasObservers<I, OT, S>,
B: Debug,
OT: ObserversTuple<I, S>,
A: UsesState,
{
type State = A::State;
}
impl<A, B> UsesObservers for CombinedExecutor<A, B>
where
A: UsesObservers,
{
type Observers = A::Observers;
}
impl<A, B> HasObservers for CombinedExecutor<A, B>
where
A: HasObservers,
{
#[inline]
fn observers(&self) -> &OT {
fn observers(&self) -> &Self::Observers {
self.primary.observers()
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
fn observers_mut(&mut self) -> &mut Self::Observers {
self.primary.observers_mut()
}
}

View File

@ -26,8 +26,9 @@ use crate::{
tuples::MatchName,
AsSlice,
},
inputs::HasTargetBytes,
observers::ObserversTuple,
inputs::{HasTargetBytes, UsesInput},
observers::{ObserversTuple, UsesObservers},
state::UsesState,
std::borrow::ToOwned,
};
#[cfg(feature = "std")]
@ -149,20 +150,16 @@ impl CommandConfigurator for StdCommandConfigurator {
/// A `CommandExecutor` is a wrapper around [`std::process::Command`] to execute a target as a child process.
/// Construct a `CommandExecutor` by implementing [`CommandConfigurator`] for a type of your choice and calling [`CommandConfigurator::into_executor`] on it.
/// Instead, you can also use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`].
pub struct CommandExecutor<EM, I, OT, S, T, Z>
where
T: Debug,
OT: Debug,
{
/// The wrapped command configurer
/// Instead, you can use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`].
pub struct CommandExecutor<EM, OT, S, T, Z> {
/// The wrapped comand configurer
configurer: T,
/// The obsevers used by this executor
/// The observers used by this executor
observers: OT,
phantom: PhantomData<(EM, I, S, Z)>,
phantom: PhantomData<(EM, S, Z)>,
}
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`].
@ -181,7 +178,7 @@ impl CommandExecutor<(), (), (), (), (), ()> {
}
}
impl<EM, I, OT, S, T, Z> Debug for CommandExecutor<EM, I, OT, S, T, Z>
impl<EM, OT, S, T, Z> Debug for CommandExecutor<EM, OT, S, T, Z>
where
T: Debug,
OT: Debug,
@ -194,7 +191,7 @@ where
}
}
impl<EM, I, OT, S, T, Z> CommandExecutor<EM, I, OT, S, T, Z>
impl<EM, OT, S, T, Z> CommandExecutor<EM, OT, S, T, Z>
where
T: Debug,
OT: Debug,
@ -205,9 +202,10 @@ where
}
}
impl<EM, I, OT, S, Z> CommandExecutor<EM, I, OT, S, StdCommandConfigurator, Z>
impl<EM, OT, S, Z> CommandExecutor<EM, OT, S, StdCommandConfigurator, Z>
where
OT: MatchName + Debug + ObserversTuple<I, S>,
OT: MatchName + Debug + ObserversTuple<S>,
S: UsesInput,
{
/// Creates a new `CommandExecutor`.
/// Instead of parsing the Command for `@@`, it will
@ -295,19 +293,21 @@ where
// this only works on unix because of the reliance on checking the process signal for detecting OOM
#[cfg(all(feature = "std", unix))]
impl<EM, I, OT, S, T, Z> Executor<EM, I, S, Z> for CommandExecutor<EM, I, OT, S, T, Z>
impl<EM, OT, S, T, Z> Executor<EM, Z> for CommandExecutor<EM, OT, S, T, Z>
where
I: Input + HasTargetBytes,
T: CommandConfigurator,
OT: Debug + MatchName + ObserversTuple<I, S>,
T: Debug,
EM: UsesState<State = S>,
S: UsesInput,
S::Input: HasTargetBytes,
T: CommandConfigurator + Debug,
OT: Debug + MatchName + ObserversTuple<S>,
Z: UsesState<State = S>,
{
fn run_target(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_state: &mut Self::State,
_mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
use std::os::unix::prelude::ExitStatusExt;
@ -357,8 +357,26 @@ where
}
}
impl<EM, I, OT: ObserversTuple<I, S>, S, T: Debug, Z> HasObservers<I, OT, S>
for CommandExecutor<EM, I, OT, S, T, Z>
impl<EM, OT, S, T, Z> UsesState for CommandExecutor<EM, OT, S, T, Z>
where
S: UsesInput,
{
type State = S;
}
impl<EM, OT, S, T, Z> UsesObservers for CommandExecutor<EM, OT, S, T, Z>
where
OT: ObserversTuple<S>,
S: UsesInput,
{
type Observers = OT;
}
impl<EM, OT, S, T, Z> HasObservers for CommandExecutor<EM, OT, S, T, Z>
where
S: UsesInput,
T: Debug,
OT: ObserversTuple<S>,
{
fn observers(&self) -> &OT {
&self.observers
@ -508,13 +526,14 @@ impl CommandExecutorBuilder {
self
}
/// Builds the `CommandExecutor`.
pub fn build<EM, I, OT, S, Z>(
/// Builds the `ComandExecutor`
pub fn build<EM, OT, S, Z>(
&self,
mut observers: OT,
) -> Result<CommandExecutor<EM, I, OT, S, StdCommandConfigurator, Z>, Error>
) -> Result<CommandExecutor<EM, OT, S, StdCommandConfigurator, Z>, Error>
where
OT: Debug + MatchName + ObserversTuple<I, S>,
OT: Debug + MatchName + ObserversTuple<S>,
S: UsesInput,
{
let program = if let Some(program) = &self.program {
program
@ -560,7 +579,7 @@ impl CommandExecutorBuilder {
input_location: self.input_location.clone(),
command,
};
Ok(configurator.into_executor::<EM, I, OT, S, Z>(observers))
Ok(configurator.into_executor::<EM, OT, S, Z>(observers))
}
}
@ -569,7 +588,7 @@ impl CommandExecutorBuilder {
#[cfg_attr(all(feature = "std", unix), doc = " ```")]
#[cfg_attr(not(all(feature = "std", unix)), doc = " ```ignore")]
/// use std::{io::Write, process::{Stdio, Command, Child}};
/// use libafl::{Error, bolts::AsSlice, inputs::{Input, HasTargetBytes}, executors::{Executor, command::CommandConfigurator}};
/// use libafl::{Error, bolts::AsSlice, inputs::{HasTargetBytes, Input, UsesInput}, executors::{Executor, command::CommandConfigurator}, state::UsesState};
/// #[derive(Debug)]
/// struct MyExecutor;
///
@ -591,8 +610,14 @@ impl CommandExecutorBuilder {
/// }
/// }
///
/// fn make_executor<EM, I: Input + HasTargetBytes, S, Z>() -> impl Executor<EM, I, S, Z> {
/// MyExecutor.into_executor(())
/// fn make_executor<EM, Z>() -> impl Executor<EM, Z>
/// where
/// EM: UsesState,
/// Z: UsesState<State = EM::State>,
/// EM::State: UsesInput,
/// EM::Input: HasTargetBytes
/// {
/// MyExecutor.into_executor((), None, None, None)
/// }
/// ```
@ -604,8 +629,7 @@ pub trait CommandConfigurator: Sized + Debug {
I: Input + HasTargetBytes;
/// Create an `Executor` from this `CommandConfigurator`.
/// It will observe the outputs with the respective given observer name.
fn into_executor<EM, I, OT, S, Z>(self, observers: OT) -> CommandExecutor<EM, I, OT, S, Self, Z>
fn into_executor<EM, OT, S, Z>(self, observers: OT) -> CommandExecutor<EM, OT, S, Self, Z>
where
OT: Debug + MatchName,
{
@ -627,12 +651,14 @@ mod tests {
},
inputs::BytesInput,
monitors::SimpleMonitor,
state::NopState,
NopFuzzer,
};
#[test]
#[cfg(unix)]
fn test_builder() {
let mut mgr = SimpleEventManager::<BytesInput, _, ()>::new(SimpleMonitor::new(|status| {
let mut mgr = SimpleEventManager::new(SimpleMonitor::new(|status| {
println!("{status}");
}));
@ -645,8 +671,8 @@ mod tests {
executor
.run_target(
&mut (),
&mut (),
&mut NopFuzzer::new(),
&mut NopState::new(),
&mut mgr,
&BytesInput::new(b"test".to_vec()),
)
@ -657,7 +683,8 @@ mod tests {
#[cfg(unix)]
fn test_parse_afl_cmdline() {
use alloc::string::ToString;
let mut mgr = SimpleEventManager::<BytesInput, _, ()>::new(SimpleMonitor::new(|status| {
let mut mgr = SimpleEventManager::new(SimpleMonitor::new(|status| {
println!("{status}");
}));
@ -666,8 +693,8 @@ mod tests {
.unwrap();
executor
.run_target(
&mut (),
&mut (),
&mut NopFuzzer::new(),
&mut NopState::new(),
&mut mgr,
&BytesInput::new(b"test".to_vec()),
)

View File

@ -1,5 +1,5 @@
//! Executor for differential fuzzing.
//! It wraps two exeutors that will be run after each other with the same input.
//! It wraps two executors that will be run after each other with the same input.
//! In comparison to the [`crate::executors::CombinedExecutor`] it also runs the secondary executor in `run_target`.
//!
use core::{cell::UnsafeCell, fmt::Debug};
@ -9,38 +9,28 @@ use serde::{Deserialize, Serialize};
use crate::{
bolts::{ownedref::OwnedPtrMut, tuples::MatchName},
executors::{Executor, ExitKind, HasObservers},
inputs::Input,
observers::ObserversTuple,
inputs::UsesInput,
observers::{ObserversTuple, UsesObservers},
state::UsesState,
Error,
};
/// A [`DiffExecutor`] wraps a primary executor, forwarding its methods, and a secondary one
#[derive(Debug)]
pub struct DiffExecutor<A, B, OTA, OTB>
where
A: Debug,
B: Debug,
OTA: Debug,
OTB: Debug,
{
pub struct DiffExecutor<A, B, OTA, OTB> {
primary: A,
secondary: B,
observers: UnsafeCell<ProxyObserversTuple<OTA, OTB>>,
}
impl<A, B, OTA, OTB> DiffExecutor<A, B, OTA, OTB>
where
A: Debug,
B: Debug,
OTA: Debug,
OTB: Debug,
{
impl<A, B, OTA, OTB> DiffExecutor<A, B, OTA, OTB> {
/// Create a new `DiffExecutor`, wrapping the given `executor`s.
pub fn new<EM, I, S, Z>(primary: A, secondary: B) -> Self
pub fn new<EM, Z>(primary: A, secondary: B) -> Self
where
A: Executor<EM, I, S, Z>,
B: Executor<EM, I, S, Z>,
I: Input,
A: Executor<EM, Z>,
B: Executor<EM, Z, State = A::State>,
EM: UsesState<State = A::State>,
Z: UsesState<State = A::State>,
{
Self {
primary,
@ -63,20 +53,21 @@ where
}
}
impl<A, B, EM, I, OTA, OTB, S, Z> Executor<EM, I, S, Z> for DiffExecutor<A, B, OTA, OTB>
impl<A, B, EM, OTA, OTB, Z> Executor<EM, Z> for DiffExecutor<A, B, OTA, OTB>
where
A: Executor<EM, I, S, Z>,
B: Executor<EM, I, S, Z>,
I: Input,
A: Executor<EM, Z>,
B: Executor<EM, Z, State = A::State>,
EM: UsesState<State = A::State>,
OTA: Debug,
OTB: Debug,
Z: UsesState<State = A::State>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let ret1 = self.primary.run_target(fuzzer, state, mgr, input)?;
self.primary.post_run_reset();
@ -104,12 +95,13 @@ pub struct ProxyObserversTuple<A, B> {
secondary: OwnedPtrMut<B>,
}
impl<A, B, I, S> ObserversTuple<I, S> for ProxyObserversTuple<A, B>
impl<A, B, S> ObserversTuple<S> for ProxyObserversTuple<A, B>
where
A: ObserversTuple<I, S>,
B: ObserversTuple<I, S>,
A: ObserversTuple<S>,
B: ObserversTuple<S>,
S: UsesInput,
{
fn pre_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.primary.as_mut().pre_exec_all(state, input)?;
self.secondary.as_mut().pre_exec_all(state, input)
}
@ -117,7 +109,7 @@ where
fn post_exec_all(
&mut self,
state: &mut S,
input: &I,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error> {
self.primary
@ -128,7 +120,7 @@ where
.post_exec_all(state, input, exit_kind)
}
fn pre_exec_child_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
fn pre_exec_child_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.primary.as_mut().pre_exec_child_all(state, input)?;
self.secondary.as_mut().pre_exec_child_all(state, input)
}
@ -136,7 +128,7 @@ where
fn post_exec_child_all(
&mut self,
state: &mut S,
input: &I,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error> {
self.primary
@ -197,13 +189,30 @@ impl<A, B> ProxyObserversTuple<A, B> {
}
}
impl<A, B, I, OTA, OTB, S> HasObservers<I, ProxyObserversTuple<OTA, OTB>, S>
for DiffExecutor<A, B, OTA, OTB>
impl<A, B, OTA, OTB> UsesObservers for DiffExecutor<A, B, OTA, OTB>
where
A: HasObservers<I, OTA, S>,
B: HasObservers<I, OTB, S>,
OTA: ObserversTuple<I, S>,
OTB: ObserversTuple<I, S>,
A: HasObservers<Observers = OTA>,
B: HasObservers<Observers = OTB, State = A::State>,
OTA: ObserversTuple<A::State>,
OTB: ObserversTuple<A::State>,
{
type Observers = ProxyObserversTuple<OTA, OTB>;
}
impl<A, B, OTA, OTB> UsesState for DiffExecutor<A, B, OTA, OTB>
where
A: UsesState,
B: UsesState<State = A::State>,
{
type State = A::State;
}
impl<A, B, OTA, OTB> HasObservers for DiffExecutor<A, B, OTA, OTB>
where
A: HasObservers<Observers = OTA>,
B: HasObservers<Observers = OTB, State = A::State>,
OTA: ObserversTuple<A::State>,
OTB: ObserversTuple<A::State>,
{
#[inline]
fn observers(&self) -> &ProxyObserversTuple<OTA, OTB> {

View File

@ -31,9 +31,12 @@ use crate::{
AsMutSlice, AsSlice,
},
executors::{Executor, ExitKind, HasObservers},
inputs::{HasTargetBytes, Input},
inputs::{HasTargetBytes, Input, UsesInput},
mutators::Tokens,
observers::{get_asan_runtime_flags_with_log_path, AsanBacktraceObserver, ObserversTuple},
observers::{
get_asan_runtime_flags_with_log_path, AsanBacktraceObserver, ObserversTuple, UsesObservers,
},
state::UsesState,
Error,
};
@ -370,13 +373,13 @@ pub trait HasForkserver {
/// The timeout forkserver executor that wraps around the standard forkserver executor and sets a timeout before each run.
#[derive(Debug)]
pub struct TimeoutForkserverExecutor<E: Debug> {
pub struct TimeoutForkserverExecutor<E> {
executor: E,
timeout: TimeSpec,
signal: Signal,
}
impl<E: Debug> TimeoutForkserverExecutor<E> {
impl<E> TimeoutForkserverExecutor<E> {
/// Create a new [`TimeoutForkserverExecutor`]
pub fn new(executor: E, exec_tmout: Duration) -> Result<Self, Error> {
let signal = Signal::SIGKILL;
@ -395,18 +398,20 @@ impl<E: Debug> TimeoutForkserverExecutor<E> {
}
}
impl<E: Debug, EM, I, S, Z> Executor<EM, I, S, Z> for TimeoutForkserverExecutor<E>
impl<E, EM, Z> Executor<EM, Z> for TimeoutForkserverExecutor<E>
where
I: Input + HasTargetBytes,
E: Executor<EM, I, S, Z> + HasForkserver,
E: Executor<EM, Z> + HasForkserver + Debug,
E::Input: HasTargetBytes,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
#[inline]
fn run_target(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_state: &mut Self::State,
_mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let mut exit_kind = ExitKind::Ok;
@ -491,9 +496,8 @@ where
/// This [`Executor`] can run binaries compiled for AFL/AFL++ that make use of a 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<I, OT, S, SP>
pub struct ForkserverExecutor<OT, S, SP>
where
OT: Debug,
SP: ShMemProvider,
{
target: OsString,
@ -502,12 +506,12 @@ where
forkserver: Forkserver,
observers: OT,
map: Option<SP::ShMem>,
phantom: PhantomData<(I, S)>,
phantom: PhantomData<S>,
/// Cache that indicates if we have a `ASan` observer registered.
has_asan_observer: Option<bool>,
}
impl<I, OT, S, SP> Debug for ForkserverExecutor<I, OT, S, SP>
impl<OT, S, SP> Debug for ForkserverExecutor<OT, S, SP>
where
OT: Debug,
SP: ShMemProvider,
@ -524,7 +528,7 @@ where
}
}
impl ForkserverExecutor<(), (), (), StdShMemProvider> {
impl ForkserverExecutor<(), (), StdShMemProvider> {
/// Builder for `ForkserverExecutor`
#[must_use]
pub fn builder() -> ForkserverExecutorBuilder<'static, StdShMemProvider> {
@ -532,10 +536,10 @@ impl ForkserverExecutor<(), (), (), StdShMemProvider> {
}
}
impl<I, OT, S, SP> ForkserverExecutor<I, OT, S, SP>
impl<OT, S, SP> ForkserverExecutor<OT, S, SP>
where
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
OT: ObserversTuple<S>,
S: UsesState,
SP: ShMemProvider,
{
/// The `target` binary that's going to run.
@ -578,13 +582,11 @@ pub struct ForkserverExecutorBuilder<'a, SP> {
impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
/// Builds `ForkserverExecutor`.
#[allow(clippy::pedantic)]
pub fn build<I, OT, S>(
&mut self,
observers: OT,
) -> Result<ForkserverExecutor<I, OT, S, SP>, Error>
pub fn build<OT, S>(&mut self, observers: OT) -> Result<ForkserverExecutor<OT, S, SP>, Error>
where
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
OT: ObserversTuple<S>,
S: UsesInput,
S::Input: Input + HasTargetBytes,
SP: ShMemProvider,
{
let input_filename = match &self.input_filename {
@ -895,19 +897,22 @@ impl<'a> Default for ForkserverExecutorBuilder<'a, StdShMemProvider> {
}
}
impl<EM, I, OT, S, SP, Z> Executor<EM, I, S, Z> for ForkserverExecutor<I, OT, S, SP>
impl<EM, OT, S, SP, Z> Executor<EM, Z> for ForkserverExecutor<OT, S, SP>
where
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
OT: ObserversTuple<S>,
SP: ShMemProvider,
S: UsesInput,
S::Input: HasTargetBytes,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
#[inline]
fn run_target(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_state: &mut Self::State,
_mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let mut exit_kind = ExitKind::Ok;
@ -984,10 +989,27 @@ where
}
}
impl<I, OT, S, SP> HasObservers<I, OT, S> for ForkserverExecutor<I, OT, S, SP>
impl<OT, S, SP> UsesState for ForkserverExecutor<OT, S, SP>
where
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
S: UsesInput,
SP: ShMemProvider,
{
type State = S;
}
impl<OT, S, SP> UsesObservers for ForkserverExecutor<OT, S, SP>
where
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type Observers = OT;
}
impl<OT, S, SP> HasObservers for ForkserverExecutor<OT, S, SP>
where
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
#[inline]
@ -1001,10 +1023,11 @@ where
}
}
impl<I, OT, S, SP> HasForkserver for ForkserverExecutor<I, OT, S, SP>
impl<OT, S, SP> HasForkserver for ForkserverExecutor<OT, S, SP>
where
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
OT: ObserversTuple<S>,
S: UsesInput,
S::Input: Input + HasTargetBytes,
SP: ShMemProvider,
{
type SP = SP;
@ -1040,18 +1063,31 @@ where
}
}
impl<E, I, OT, S> HasObservers<I, OT, S> for TimeoutForkserverExecutor<E>
impl<E> UsesState for TimeoutForkserverExecutor<E>
where
E: HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
E: UsesState,
{
type State = E::State;
}
impl<E> UsesObservers for TimeoutForkserverExecutor<E>
where
E: UsesObservers,
{
type Observers = E::Observers;
}
impl<E> HasObservers for TimeoutForkserverExecutor<E>
where
E: HasObservers,
{
#[inline]
fn observers(&self) -> &OT {
fn observers(&self) -> &Self::Observers {
self.executor.observers()
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
fn observers_mut(&mut self) -> &mut Self::Observers {
self.executor.observers_mut()
}
}
@ -1069,10 +1105,10 @@ mod tests {
AsMutSlice,
},
executors::forkserver::ForkserverExecutorBuilder,
inputs::NopInput,
observers::{ConstMapObserver, HitcountsMapObserver},
Error,
};
#[test]
#[serial]
fn test_forkserver() {
@ -1096,7 +1132,7 @@ mod tests {
.args(&args)
.debug_child(false)
.shmem_provider(&mut shmem_provider)
.build::<NopInput, _, ()>(tuple_list!(edges_observer));
.build::<_, ()>(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

@ -46,27 +46,31 @@ use crate::{
executors::{Executor, ExitKind, HasObservers},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasClientPerfMonitor, HasSolutions},
inputs::UsesInput,
observers::{ObserversTuple, UsesObservers},
state::{HasClientPerfMonitor, HasSolutions, UsesState},
Error,
};
/// The process executor simply calls a target function, as mutable reference to a closure
pub type InProcessExecutor<'a, H, I, OT, S> = GenericInProcessExecutor<H, &'a mut H, I, OT, S>;
pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor<H, &'a mut H, OT, S>;
/// The process executor simply calls a target function, as boxed `FnMut` trait object
pub type OwnedInProcessExecutor<I, OT, S> =
GenericInProcessExecutor<dyn FnMut(&I) -> ExitKind, Box<dyn FnMut(&I) -> ExitKind>, I, OT, S>;
pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor<
dyn FnMut(&<S as UsesInput>::Input) -> ExitKind,
Box<dyn FnMut(&<S as UsesInput>::Input) -> ExitKind>,
OT,
S,
>;
/// The inmem executor simply calls a target function, then returns afterwards.
#[allow(dead_code)]
pub struct GenericInProcessExecutor<H, HB, I, OT, S>
pub struct GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&I) -> ExitKind + ?Sized,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
I: Input,
OT: ObserversTuple<I, S>,
OT: ObserversTuple<S>,
S: UsesInput,
{
/// The harness function, being executed for each fuzzing loop execution
harness_fn: HB,
@ -74,15 +78,15 @@ where
observers: OT,
// Crash and timeout hah
handlers: InProcessHandlers,
phantom: PhantomData<(I, S, *const H)>,
phantom: PhantomData<(S, *const H)>,
}
impl<H, HB, I, OT, S> Debug for GenericInProcessExecutor<H, HB, I, OT, S>
impl<H, HB, OT, S> Debug for GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&I) -> ExitKind + ?Sized,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
I: Input,
OT: ObserversTuple<I, S>,
OT: ObserversTuple<S>,
S: UsesInput,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("GenericInProcessExecutor")
@ -92,19 +96,41 @@ where
}
}
impl<EM, H, HB, I, OT, S, Z> Executor<EM, I, S, Z> for GenericInProcessExecutor<H, HB, I, OT, S>
impl<H, HB, OT, S> UsesState for GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&I) -> ExitKind + ?Sized,
H: ?Sized + FnMut(&S::Input) -> ExitKind,
HB: BorrowMut<H>,
I: Input,
OT: ObserversTuple<I, S>,
OT: ObserversTuple<S>,
S: UsesInput,
{
type State = S;
}
impl<H, HB, OT, S> UsesObservers for GenericInProcessExecutor<H, HB, OT, S>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
HB: BorrowMut<H>,
OT: ObserversTuple<S>,
S: UsesInput,
{
type Observers = OT;
}
impl<EM, H, HB, OT, S, Z> Executor<EM, Z> for GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
EM: UsesState<State = S>,
OT: ObserversTuple<S>,
S: UsesInput,
Z: UsesState<State = S>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
self.handlers
.pre_run_target(self, fuzzer, state, mgr, input);
@ -116,12 +142,12 @@ where
}
}
impl<H, HB, I, OT, S> HasObservers<I, OT, S> for GenericInProcessExecutor<H, HB, I, OT, S>
impl<H, HB, OT, S> HasObservers for GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&I) -> ExitKind + ?Sized,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
I: Input,
OT: ObserversTuple<I, S>,
OT: ObserversTuple<S>,
S: UsesInput,
{
#[inline]
fn observers(&self) -> &OT {
@ -134,12 +160,12 @@ where
}
}
impl<H, HB, I, OT, S> GenericInProcessExecutor<H, HB, I, OT, S>
impl<H, HB, OT, S> GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&I) -> ExitKind + ?Sized,
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
I: Input,
OT: ObserversTuple<I, S>,
OT: ObserversTuple<S>,
S: HasSolutions + HasClientPerfMonitor,
{
/// Create a new in mem executor.
/// Caution: crash and restart in one of them will lead to odd behavior if multiple are used,
@ -155,12 +181,12 @@ where
_event_mgr: &mut EM,
) -> Result<Self, Error>
where
EM: EventFirer<I> + EventRestarter<S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
Z: HasObjective<I, OF, S>,
Self: Executor<EM, Z, State = S>,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
Z: HasObjective<OF, State = S>,
{
let handlers = InProcessHandlers::new::<Self, EM, I, OF, OT, S, Z, H>()?;
let handlers = InProcessHandlers::new::<Self, EM, OF, Z, H>()?;
#[cfg(windows)]
unsafe {
/*
@ -217,11 +243,11 @@ pub trait HasInProcessHandlers {
}
#[cfg(windows)]
impl<'a, H, I, OT, S> HasInProcessHandlers for InProcessExecutor<'a, H, I, OT, S>
impl<'a, H, OT, S> HasInProcessHandlers for InProcessExecutor<'a, H, OT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
{
/// the timeout handler
#[inline]
@ -310,28 +336,26 @@ impl InProcessHandlers {
}
/// Create new [`InProcessHandlers`].
pub fn new<E, EM, I, OF, OT, S, Z, H>() -> Result<Self, Error>
pub fn new<E, EM, OF, Z, H>() -> Result<Self, Error>
where
I: Input,
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
EM: EventFirer<I> + EventRestarter<S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
Z: HasObjective<I, OF, S>,
H: FnMut(&I) -> ExitKind + ?Sized,
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = E::State>,
H: FnMut(&<E::State as UsesInput>::Input) -> ExitKind + ?Sized,
{
#[cfg(unix)]
unsafe {
let data = &mut GLOBAL_STATE;
#[cfg(feature = "std")]
unix_signal_handler::setup_panic_hook::<E, EM, I, OF, OT, S, Z>();
unix_signal_handler::setup_panic_hook::<E, EM, OF, Z>();
setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst);
Ok(Self {
crash_handler: unix_signal_handler::inproc_crash_handler::<E, EM, I, OF, OT, S, Z>
crash_handler: unix_signal_handler::inproc_crash_handler::<E, EM, OF, Z>
as *const c_void,
timeout_handler: unix_signal_handler::inproc_timeout_handler::<E, EM, I, OF, OT, S, Z>
timeout_handler: unix_signal_handler::inproc_timeout_handler::<E, EM, OF, Z>
as *const _,
})
}
@ -339,29 +363,15 @@ impl InProcessHandlers {
unsafe {
let data = &mut GLOBAL_STATE;
#[cfg(feature = "std")]
windows_exception_handler::setup_panic_hook::<E, EM, I, OF, OT, S, Z>();
windows_exception_handler::setup_panic_hook::<E, EM, OF, Z>();
setup_exception_handler(data)?;
compiler_fence(Ordering::SeqCst);
Ok(Self {
crash_handler: windows_exception_handler::inproc_crash_handler::<
E,
EM,
I,
OF,
OT,
S,
Z,
> as *const _,
timeout_handler: windows_exception_handler::inproc_timeout_handler::<
E,
EM,
I,
OF,
OT,
S,
Z,
> as *const c_void,
crash_handler: windows_exception_handler::inproc_crash_handler::<E, EM, OF, Z>
as *const _,
timeout_handler: windows_exception_handler::inproc_timeout_handler::<E, EM, OF, Z>
as *const c_void,
})
}
#[cfg(not(any(unix, feature = "std")))]
@ -510,7 +520,7 @@ pub fn inprocess_get_executor<'a, E>() -> Option<&'a mut E> {
unsafe { (GLOBAL_STATE.executor_ptr as *mut E).as_mut() }
}
/// Gets the inprocess [`Input`]
/// Gets the inprocess input
#[must_use]
pub fn inprocess_get_input<'a, I>() -> Option<&'a I> {
unsafe { (GLOBAL_STATE.current_input_ptr as *const I).as_ref() }
@ -530,6 +540,8 @@ mod unix_signal_handler {
use libc::siginfo_t;
#[cfg(feature = "std")]
use crate::inputs::Input;
use crate::{
bolts::os::unix_signals::{ucontext_t, Handler, Signal},
corpus::{Corpus, Testcase},
@ -540,7 +552,7 @@ mod unix_signal_handler {
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::Input,
inputs::UsesInput,
observers::ObserversTuple,
state::{HasClientPerfMonitor, HasMetadata, HasSolutions},
};
@ -596,15 +608,13 @@ mod unix_signal_handler {
/// invokes the `post_exec` hook on all observer in case of panic
#[cfg(feature = "std")]
pub fn setup_panic_hook<E, EM, I, OF, OT, S, Z>()
pub fn setup_panic_hook<E, EM, OF, Z>()
where
E: HasObservers<I, OT, S>,
EM: EventFirer<I> + EventRestarter<S>,
OT: ObserversTuple<I, S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
I: Input,
Z: HasObjective<I, OF, S>,
E: HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = E::State>,
{
let old_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
@ -614,8 +624,8 @@ mod unix_signal_handler {
// We are fuzzing!
let executor = data.executor_mut::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<S>();
let input = data.current_input::<I>();
let state = data.state_mut::<E::State>();
let input = data.current_input::<<E::State as UsesInput>::Input>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
@ -667,19 +677,17 @@ mod unix_signal_handler {
}
#[cfg(unix)]
pub(crate) unsafe fn inproc_timeout_handler<E, EM, I, OF, OT, S, Z>(
pub(crate) unsafe fn inproc_timeout_handler<E, EM, OF, Z>(
_signal: Signal,
_info: siginfo_t,
_context: &mut ucontext_t,
data: &mut InProcessExecutorHandlerData,
) where
E: HasObservers<I, OT, S>,
EM: EventFirer<I> + EventRestarter<S>,
OT: ObserversTuple<I, S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
I: Input,
Z: HasObjective<I, OF, S>,
E: HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = E::State>,
{
if !data.is_valid() {
#[cfg(feature = "std")]
@ -689,11 +697,11 @@ mod unix_signal_handler {
let executor = data.executor_mut::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<S>();
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
let input = data.take_current_input::<I>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
#[cfg(feature = "std")]
println!("Timeout in fuzz run.");
@ -747,19 +755,17 @@ mod unix_signal_handler {
/// Will be used for signal handling.
/// It will store the current State to shmem, then exit.
#[allow(clippy::too_many_lines)]
pub(crate) unsafe fn inproc_crash_handler<E, EM, I, OF, OT, S, Z>(
pub(crate) unsafe fn inproc_crash_handler<E, EM, OF, Z>(
signal: Signal,
_info: siginfo_t,
_context: &mut ucontext_t,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
EM: EventFirer<I> + EventRestarter<S>,
OT: ObserversTuple<I, S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
I: Input,
Z: HasObjective<I, OF, S>,
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = E::State>,
{
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
let _context = &mut *(((_context as *mut _ as *mut libc::c_void as usize) + 128)
@ -772,11 +778,11 @@ mod unix_signal_handler {
// disarms timeout in case of TimeoutExecutor
executor.post_run_reset();
let observers = executor.observers_mut();
let state = data.state_mut::<S>();
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
let input = data.take_current_input::<I>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
observers
.post_exec_all(state, input, &ExitKind::Crash)
@ -898,7 +904,6 @@ mod windows_exception_handler {
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasClientPerfMonitor, HasMetadata, HasSolutions},
};
@ -934,17 +939,17 @@ mod windows_exception_handler {
EnterCriticalSection, LeaveCriticalSection, RTL_CRITICAL_SECTION,
};
use crate::inputs::UsesInput;
/// invokes the `post_exec` hook on all observer in case of panic
#[cfg(feature = "std")]
pub fn setup_panic_hook<E, EM, I, OF, OT, S, Z>()
pub fn setup_panic_hook<E, EM, OF, Z>()
where
E: HasObservers<I, OT, S>,
EM: EventFirer<I> + EventRestarter<S>,
OT: ObserversTuple<I, S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
I: Input,
Z: HasObjective<I, OF, S>,
E: HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = E::State>,
{
let old_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
@ -971,11 +976,11 @@ mod windows_exception_handler {
// We are fuzzing!
let executor = data.executor_mut::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<S>();
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
let input = data.take_current_input::<I>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
observers
.post_exec_all(state, input, &ExitKind::Crash)
@ -1025,18 +1030,16 @@ mod windows_exception_handler {
}));
}
pub unsafe extern "system" fn inproc_timeout_handler<E, EM, I, OF, OT, S, Z>(
pub unsafe extern "system" fn inproc_timeout_handler<E, EM, OF, Z>(
_p0: *mut u8,
global_state: *mut c_void,
_p1: *mut u8,
) where
E: HasObservers<I, OT, S>,
EM: EventFirer<I> + EventRestarter<S>,
OT: ObserversTuple<I, S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
I: Input,
Z: HasObjective<I, OF, S>,
E: HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = E::State>,
{
let data: &mut InProcessExecutorHandlerData =
&mut *(global_state as *mut InProcessExecutorHandlerData);
@ -1050,7 +1053,7 @@ mod windows_exception_handler {
if data.in_target == 1 {
let executor = data.executor_mut::<E>();
let state = data.state_mut::<S>();
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
let observers = executor.observers_mut();
@ -1064,7 +1067,9 @@ mod windows_exception_handler {
#[cfg(feature = "std")]
let _res = stdout().flush();
let input = (data.timeout_input_ptr as *const I).as_ref().unwrap();
let input = (data.timeout_input_ptr as *const <E::State as UsesInput>::Input)
.as_ref()
.unwrap();
data.timeout_input_ptr = ptr::null_mut();
observers
@ -1122,17 +1127,15 @@ mod windows_exception_handler {
}
#[allow(clippy::too_many_lines)]
pub(crate) unsafe fn inproc_crash_handler<E, EM, I, OF, OT, S, Z>(
pub(crate) unsafe fn inproc_crash_handler<E, EM, OF, Z>(
exception_pointers: *mut EXCEPTION_POINTERS,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
EM: EventFirer<I> + EventRestarter<S>,
OT: ObserversTuple<I, S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
I: Input,
Z: HasObjective<I, OF, S>,
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = E::State>,
{
// Have we set a timer_before?
if !(data.tp_timer as *mut windows::Win32::System::Threading::TP_TIMER).is_null() {
@ -1199,7 +1202,7 @@ mod windows_exception_handler {
data.tp_timer = ptr::null_mut();
}
let state = data.state_mut::<S>();
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
let observers = executor.observers_mut();
@ -1210,7 +1213,7 @@ mod windows_exception_handler {
drop(stdout().flush());
// Make sure we don't crash in the crash handler forever.
let input = data.take_current_input::<I>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
#[cfg(feature = "std")]
eprintln!("Child crashed!");
@ -1297,11 +1300,9 @@ impl InChildProcessHandlers {
}
/// Create new [`InChildProcessHandlers`].
pub fn new<E, I, OT, S>() -> Result<Self, Error>
pub fn new<E>() -> Result<Self, Error>
where
I: Input,
E: HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
E: HasObservers,
{
unsafe {
let data = &mut FORK_EXECUTOR_GLOBAL_DATA;
@ -1309,19 +1310,16 @@ impl InChildProcessHandlers {
setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst);
Ok(Self {
crash_handler: child_signal_handlers::child_crash_handler::<E, I, OT, S>
as *const c_void,
crash_handler: child_signal_handlers::child_crash_handler::<E> as *const c_void,
timeout_handler: ptr::null(),
})
}
}
/// Create new [`InChildProcessHandlers`].
pub fn with_timeout<E, I, OT, S>() -> Result<Self, Error>
pub fn with_timeout<E>() -> Result<Self, Error>
where
I: Input,
E: HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
E: HasObservers,
{
unsafe {
let data = &mut FORK_EXECUTOR_GLOBAL_DATA;
@ -1329,10 +1327,8 @@ impl InChildProcessHandlers {
setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst);
Ok(Self {
crash_handler: child_signal_handlers::child_crash_handler::<E, I, OT, S>
as *const c_void,
timeout_handler: child_signal_handlers::child_timeout_handler::<E, I, OT, S>
as *const c_void,
crash_handler: child_signal_handlers::child_crash_handler::<E> as *const c_void,
timeout_handler: child_signal_handlers::child_timeout_handler::<E> as *const c_void,
})
}
}
@ -1442,27 +1438,27 @@ impl Handler for InProcessForkExecutorGlobalData {
/// [`InProcessForkExecutor`] is an executor that forks the current process before each execution.
#[cfg(all(feature = "std", unix))]
pub struct InProcessForkExecutor<'a, H, I, OT, S, SP>
pub struct InProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&I) -> ExitKind + ?Sized,
I: Input,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
harness_fn: &'a mut H,
shmem_provider: SP,
observers: OT,
handlers: InChildProcessHandlers,
phantom: PhantomData<(I, S)>,
phantom: PhantomData<S>,
}
/// Timeout executor for [`InProcessForkExecutor`]
#[cfg(all(feature = "std", target_os = "linux"))]
pub struct TimeoutInProcessForkExecutor<'a, H, I, OT, S, SP>
pub struct TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&I) -> ExitKind + ?Sized,
I: Input,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
harness_fn: &'a mut H,
@ -1470,15 +1466,15 @@ where
observers: OT,
handlers: InChildProcessHandlers,
itimerspec: libc::itimerspec,
phantom: PhantomData<(I, S)>,
phantom: PhantomData<S>,
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, I, OT, S, SP> Debug for InProcessForkExecutor<'a, H, I, OT, S, SP>
impl<'a, H, OT, S, SP> Debug for InProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&I) -> ExitKind + ?Sized,
I: Input,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@ -1490,11 +1486,11 @@ where
}
#[cfg(all(feature = "std", target_os = "linux"))]
impl<'a, H, I, OT, S, SP> Debug for TimeoutInProcessForkExecutor<'a, H, I, OT, S, SP>
impl<'a, H, OT, S, SP> Debug for TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&I) -> ExitKind + ?Sized,
I: Input,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@ -1507,22 +1503,45 @@ where
}
#[cfg(all(feature = "std", unix))]
impl<'a, EM, H, I, OT, S, SP, Z> Executor<EM, I, S, Z>
for InProcessForkExecutor<'a, H, I, OT, S, SP>
impl<'a, H, OT, S, SP> UsesState for InProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&I) -> ExitKind + ?Sized,
I: Input,
OT: ObserversTuple<I, S>,
H: ?Sized + FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type State = S;
}
#[cfg(all(feature = "std", target_os = "linux"))]
impl<'a, H, OT, S, SP> UsesState for TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type State = S;
}
#[cfg(all(feature = "std", unix))]
impl<'a, EM, H, OT, S, SP, Z> Executor<EM, Z> for InProcessForkExecutor<'a, H, OT, S, SP>
where
EM: UsesState<State = S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
Z: UsesState<State = S>,
{
#[allow(unreachable_code)]
#[inline]
fn run_target(
&mut self,
_fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
_mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
unsafe {
self.shmem_provider.pre_fork()?;
@ -1574,22 +1593,23 @@ where
}
#[cfg(all(feature = "std", target_os = "linux"))]
impl<'a, EM, H, I, OT, S, SP, Z> Executor<EM, I, S, Z>
for TimeoutInProcessForkExecutor<'a, H, I, OT, S, SP>
impl<'a, EM, H, OT, S, SP, Z> Executor<EM, Z> for TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&I) -> ExitKind + ?Sized,
I: Input,
OT: ObserversTuple<I, S>,
EM: UsesState<State = S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
Z: UsesState<State = S>,
{
#[allow(unreachable_code)]
#[inline]
fn run_target(
&mut self,
_fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
_mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
unsafe {
self.shmem_provider.pre_fork()?;
@ -1661,11 +1681,11 @@ where
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, I, OT, S, SP> InProcessForkExecutor<'a, H, I, OT, S, SP>
impl<'a, H, OT, S, SP> InProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&I) -> ExitKind + ?Sized,
I: Input,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
/// Creates a new [`InProcessForkExecutor`]
@ -1678,12 +1698,12 @@ where
shmem_provider: SP,
) -> Result<Self, Error>
where
EM: EventFirer<I> + EventRestarter<S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
Z: HasObjective<I, OF, S>,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = S>,
{
let handlers = InChildProcessHandlers::new::<Self, I, OT, S>()?;
let handlers = InChildProcessHandlers::new::<Self>()?;
Ok(Self {
harness_fn,
shmem_provider,
@ -1707,11 +1727,11 @@ where
}
#[cfg(all(feature = "std", target_os = "linux"))]
impl<'a, H, I, OT, S, SP> TimeoutInProcessForkExecutor<'a, H, I, OT, S, SP>
impl<'a, H, OT, S, SP> TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&I) -> ExitKind + ?Sized,
I: Input,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
S: UsesInput,
OT: ObserversTuple<S>,
SP: ShMemProvider,
{
/// Creates a new [`TimeoutInProcessForkExecutor`]
@ -1725,12 +1745,12 @@ where
shmem_provider: SP,
) -> Result<Self, Error>
where
EM: EventFirer<I> + EventRestarter<S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
Z: HasObjective<I, OF, S>,
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = S>,
{
let handlers = InChildProcessHandlers::with_timeout::<Self, I, OT, S>()?;
let handlers = InChildProcessHandlers::with_timeout::<Self>()?;
let milli_sec = timeout.as_millis();
let it_value = libc::timespec {
tv_sec: (milli_sec / 1000) as _,
@ -1769,11 +1789,33 @@ where
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, I, OT, S, SP> HasObservers<I, OT, S> for InProcessForkExecutor<'a, H, I, OT, S, SP>
impl<'a, H, OT, S, SP> UsesObservers for InProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&I) -> ExitKind + ?Sized,
I: Input,
OT: ObserversTuple<I, S>,
H: ?Sized + FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type Observers = OT;
}
#[cfg(all(feature = "std", target_os = "linux"))]
impl<'a, H, OT, S, SP> UsesObservers for TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type Observers = OT;
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> HasObservers for InProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
S: UsesInput,
OT: ObserversTuple<S>,
SP: ShMemProvider,
{
#[inline]
@ -1788,12 +1830,11 @@ where
}
#[cfg(all(feature = "std", target_os = "linux"))]
impl<'a, H, I, OT, S, SP> HasObservers<I, OT, S>
for TimeoutInProcessForkExecutor<'a, H, I, OT, S, SP>
impl<'a, H, OT, S, SP> HasObservers for TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&I) -> ExitKind + ?Sized,
I: Input,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
S: UsesInput,
OT: ObserversTuple<S>,
SP: ShMemProvider,
{
#[inline]
@ -1819,16 +1860,14 @@ pub mod child_signal_handlers {
use crate::{
bolts::os::unix_signals::{ucontext_t, Signal},
executors::{ExitKind, HasObservers},
inputs::Input,
inputs::UsesInput,
observers::ObserversTuple,
};
/// invokes the `post_exec_child` hook on all observer in case the child process panics
pub fn setup_child_panic_hook<E, I, OT, S>()
pub fn setup_child_panic_hook<E>()
where
E: HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
I: Input,
E: HasObservers,
{
let old_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
@ -1837,9 +1876,9 @@ pub mod child_signal_handlers {
if data.is_valid() {
let executor = data.executor_mut::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<S>();
let state = data.state_mut::<E::State>();
// Invalidate data to not execute again the observer hooks in the crash handler
let input = data.take_current_input::<I>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
observers
.post_exec_child_all(state, input, &ExitKind::Crash)
.expect("Failed to run post_exec on observers");
@ -1856,21 +1895,19 @@ pub mod child_signal_handlers {
/// The function should only be called from a child crash handler.
/// It will dereference the `data` pointer and assume it's valid.
#[cfg(unix)]
pub(crate) unsafe fn child_crash_handler<E, I, OT, S>(
pub(crate) unsafe fn child_crash_handler<E>(
_signal: Signal,
_info: siginfo_t,
_context: &mut ucontext_t,
data: &mut InProcessForkExecutorGlobalData,
) where
E: HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
I: Input,
E: HasObservers,
{
if data.is_valid() {
let executor = data.executor_mut::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<S>();
let input = data.take_current_input::<I>();
let state = data.state_mut::<E::State>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
observers
.post_exec_child_all(state, input, &ExitKind::Crash)
.expect("Failed to run post_exec on observers");
@ -1880,21 +1917,19 @@ pub mod child_signal_handlers {
}
#[cfg(unix)]
pub(crate) unsafe fn child_timeout_handler<E, I, OT, S>(
pub(crate) unsafe fn child_timeout_handler<E>(
_signal: Signal,
_info: siginfo_t,
_context: &mut ucontext_t,
data: &mut InProcessForkExecutorGlobalData,
) where
E: HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
I: Input,
E: HasObservers,
{
if data.is_valid() {
let executor = data.executor_mut::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<S>();
let input = data.take_current_input::<I>();
let state = data.state_mut::<E::State>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
observers
.post_exec_child_all(state, input, &ExitKind::Timeout)
.expect("Failed to run post_exec on observers");
@ -1910,22 +1945,24 @@ mod tests {
#[cfg(all(feature = "std", feature = "fork", unix))]
use serial_test::serial;
#[cfg(all(feature = "std", feature = "fork", unix))]
use crate::{
bolts::shmem::{ShMemProvider, StdShMemProvider},
executors::InProcessForkExecutor,
};
use crate::{
bolts::tuples::tuple_list,
events::NopEventManager,
executors::{inprocess::InProcessHandlers, Executor, ExitKind, InProcessExecutor},
inputs::NopInput,
inputs::{NopInput, UsesInput},
state::NopState,
NopFuzzer,
};
impl UsesInput for () {
type Input = NopInput;
}
#[test]
fn test_inmem_exec() {
let mut harness = |_buf: &NopInput| ExitKind::Ok;
let mut in_process_executor = InProcessExecutor::<_, NopInput, (), ()> {
let mut in_process_executor = InProcessExecutor::<_, _, _> {
harness_fn: &mut harness,
observers: tuple_list!(),
handlers: InProcessHandlers::nop(),
@ -1933,7 +1970,12 @@ mod tests {
};
let input = NopInput {};
in_process_executor
.run_target(&mut (), &mut (), &mut (), &input)
.run_target(
&mut NopFuzzer::new(),
&mut NopState::new(),
&mut NopEventManager::new(),
&input,
)
.unwrap();
}
@ -1941,12 +1983,18 @@ mod tests {
#[serial]
#[cfg(all(feature = "std", feature = "fork", unix))]
fn test_inprocessfork_exec() {
use crate::executors::inprocess::InChildProcessHandlers;
use crate::{
bolts::shmem::{ShMemProvider, StdShMemProvider},
events::SimpleEventManager,
executors::{inprocess::InChildProcessHandlers, InProcessForkExecutor},
state::NopState,
NopFuzzer,
};
let provider = StdShMemProvider::new().unwrap();
let mut harness = |_buf: &NopInput| ExitKind::Ok;
let mut in_process_fork_executor = InProcessForkExecutor::<_, NopInput, (), (), _> {
let mut in_process_fork_executor = InProcessForkExecutor::<_, (), _, _> {
harness_fn: &mut harness,
shmem_provider: provider,
observers: tuple_list!(),
@ -1954,8 +2002,11 @@ mod tests {
phantom: PhantomData,
};
let input = NopInput {};
let mut fuzzer = NopFuzzer::new();
let mut state = NopState::new();
let mut mgr = SimpleEventManager::printing();
in_process_fork_executor
.run_target(&mut (), &mut (), &mut (), &input)
.run_target(&mut fuzzer, &mut state, &mut mgr, &input)
.unwrap();
}
}
@ -1982,7 +2033,7 @@ pub mod pybind {
/// Python class for OwnedInProcessExecutor (i.e. InProcessExecutor with owned harness)
pub struct PythonOwnedInProcessExecutor {
/// Rust wrapped OwnedInProcessExecutor object
pub inner: OwnedInProcessExecutor<BytesInput, PythonObserversTuple, PythonStdState>,
pub inner: OwnedInProcessExecutor<PythonObserversTuple, PythonStdState>,
}
#[pymethods]

View File

@ -31,7 +31,7 @@ pub use with_observers::WithObservers;
#[cfg(all(feature = "std", any(unix, doc)))]
pub mod command;
use core::fmt::Debug;
use core::{fmt::Debug, marker::PhantomData};
#[cfg(all(feature = "std", any(unix, doc)))]
pub use command::CommandExecutor;
@ -39,8 +39,9 @@ use serde::{Deserialize, Serialize};
use crate::{
bolts::AsSlice,
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
inputs::{HasTargetBytes, UsesInput},
observers::{ObserversTuple, UsesObservers},
state::UsesState,
Error,
};
@ -100,29 +101,27 @@ impl From<ExitKind> for DiffExitKind {
crate::impl_serdeany!(DiffExitKind);
/// Holds a tuple of Observers
pub trait HasObservers<I, OT, S>: Debug
where
OT: ObserversTuple<I, S>,
{
pub trait HasObservers: UsesObservers {
/// Get the linked observers
fn observers(&self) -> &OT;
fn observers(&self) -> &Self::Observers;
/// Get the linked observers (mutable)
fn observers_mut(&mut self) -> &mut OT;
fn observers_mut(&mut self) -> &mut Self::Observers;
}
/// An executor takes the given inputs, and runs the harness/target.
pub trait Executor<EM, I, S, Z>: Debug
pub trait Executor<EM, Z>: UsesState + Debug
where
I: Input,
EM: UsesState<State = Self::State>,
Z: UsesState<State = Self::State>,
{
/// Instruct the target about the input and run
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error>;
/// Wraps this Executor with the given [`ObserversTuple`] to implement [`HasObservers`].
@ -132,7 +131,7 @@ where
fn with_observers<OT>(self, observers: OT) -> WithObservers<Self, OT>
where
Self: Sized,
OT: ObserversTuple<I, S>,
OT: ObserversTuple<Self::State>,
{
WithObservers::new(self, observers)
}
@ -145,18 +144,30 @@ where
/// A simple executor that does nothing.
/// If intput len is 0, `run_target` will return Err
#[derive(Debug)]
struct NopExecutor {}
struct NopExecutor<S> {
phantom: PhantomData<S>,
}
impl<EM, I, S, Z> Executor<EM, I, S, Z> for NopExecutor
impl<S> UsesState for NopExecutor<S>
where
I: Input + HasTargetBytes,
S: UsesInput,
{
type State = S;
}
impl<EM, S, Z> Executor<EM, Z> for NopExecutor<S>
where
EM: UsesState<State = S>,
S: UsesInput + Debug,
S::Input: HasTargetBytes,
Z: UsesState<State = S>,
{
fn run_target(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_state: &mut Self::State,
_mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
if input.target_bytes().as_slice().is_empty() {
Err(Error::empty("Input Empty"))
@ -168,19 +179,37 @@ where
#[cfg(test)]
mod test {
use core::marker::PhantomData;
use super::{Executor, NopExecutor};
use crate::inputs::BytesInput;
use crate::{events::NopEventManager, inputs::BytesInput, state::NopState, NopFuzzer};
#[test]
fn nop_executor() {
let empty_input = BytesInput::new(vec![]);
let nonempty_input = BytesInput::new(vec![1u8]);
let mut executor = NopExecutor {};
let mut executor = NopExecutor {
phantom: PhantomData,
};
let mut fuzzer = NopFuzzer::new();
let mut state = NopState::new();
executor
.run_target(&mut (), &mut (), &mut (), &empty_input)
.run_target(
&mut fuzzer,
&mut state,
&mut NopEventManager::new(),
&empty_input,
)
.unwrap_err();
executor
.run_target(&mut (), &mut (), &mut (), &nonempty_input)
.run_target(
&mut fuzzer,
&mut state,
&mut NopEventManager::new(),
&nonempty_input,
)
.unwrap();
}
}
@ -198,9 +227,12 @@ pub mod pybind {
inprocess::pybind::PythonOwnedInProcessExecutor, Executor, ExitKind, HasObservers,
},
fuzzer::pybind::{PythonStdFuzzer, PythonStdFuzzerWrapper},
inputs::{BytesInput, HasBytesVec},
observers::pybind::PythonObserversTuple,
state::pybind::{PythonStdState, PythonStdStateWrapper},
inputs::HasBytesVec,
observers::{pybind::PythonObserversTuple, UsesObservers},
state::{
pybind::{PythonStdState, PythonStdStateWrapper},
UsesState,
},
Error,
};
@ -293,7 +325,15 @@ pub mod pybind {
}
}
impl HasObservers<BytesInput, PythonObserversTuple, PythonStdState> for PyObjectExecutor {
impl UsesState for PyObjectExecutor {
type State = PythonStdState;
}
impl UsesObservers for PyObjectExecutor {
type Observers = PythonObserversTuple;
}
impl HasObservers for PyObjectExecutor {
#[inline]
fn observers(&self) -> &PythonObserversTuple {
&self.tuple
@ -305,16 +345,14 @@ pub mod pybind {
}
}
impl Executor<PythonEventManager, BytesInput, PythonStdState, PythonStdFuzzer>
for PyObjectExecutor
{
impl Executor<PythonEventManager, PythonStdFuzzer> for PyObjectExecutor {
#[inline]
fn run_target(
&mut self,
fuzzer: &mut PythonStdFuzzer,
state: &mut PythonStdState,
state: &mut Self::State,
mgr: &mut PythonEventManager,
input: &BytesInput,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let ek = Python::with_gil(|py| -> PyResult<_> {
let ek: PythonExitKind = self
@ -344,7 +382,7 @@ pub mod pybind {
#[pyclass(unsendable, name = "Executor")]
#[derive(Clone, Debug)]
/// Executor + HasObservers Trait binding
/// Executor<Input = I> + HasObservers Trait binding
pub struct PythonExecutor {
wrapper: PythonExecutorWrapper,
}
@ -404,7 +442,15 @@ pub mod pybind {
}
}
impl HasObservers<BytesInput, PythonObserversTuple, PythonStdState> for PythonExecutor {
impl UsesState for PythonExecutor {
type State = PythonStdState;
}
impl UsesObservers for PythonExecutor {
type Observers = PythonObserversTuple;
}
impl HasObservers for PythonExecutor {
#[inline]
fn observers(&self) -> &PythonObserversTuple {
let ptr = unwrap_me!(self.wrapper, e, {
@ -422,14 +468,14 @@ pub mod pybind {
}
}
impl Executor<PythonEventManager, BytesInput, PythonStdState, PythonStdFuzzer> for PythonExecutor {
impl Executor<PythonEventManager, PythonStdFuzzer> for PythonExecutor {
#[inline]
fn run_target(
&mut self,
fuzzer: &mut PythonStdFuzzer,
state: &mut PythonStdState,
state: &mut Self::State,
mgr: &mut PythonEventManager,
input: &BytesInput,
input: &Self::Input,
) -> Result<ExitKind, Error> {
unwrap_me_mut!(self.wrapper, e, { e.run_target(fuzzer, state, mgr, input) })
}

View File

@ -1,28 +1,27 @@
//! A `ShadowExecutor` wraps an executor to have shadow observer that will not be considered by the feedbacks and the manager
use core::{
fmt::{self, Debug, Formatter},
marker::PhantomData,
};
use core::fmt::{self, Debug, Formatter};
use crate::{
executors::{Executor, ExitKind, HasObservers},
inputs::Input,
observers::ObserversTuple,
observers::{ObserversTuple, UsesObservers},
state::UsesState,
Error,
};
/// A [`ShadowExecutor`] wraps an executor and a set of shadow observers
pub struct ShadowExecutor<E: Debug, I: Debug, S, SOT: Debug> {
pub struct ShadowExecutor<E, SOT> {
/// The wrapped executor
executor: E,
/// The shadow observers
shadow_observers: SOT,
/// phantom data
phantom: PhantomData<(I, S)>,
}
impl<E: Debug, I: Debug, S, SOT: Debug> Debug for ShadowExecutor<E, I, S, SOT> {
impl<E, SOT> Debug for ShadowExecutor<E, SOT>
where
E: UsesState + Debug,
SOT: ObserversTuple<E::State> + Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("ShadowExecutor")
.field("executor", &self.executor)
@ -31,16 +30,16 @@ impl<E: Debug, I: Debug, S, SOT: Debug> Debug for ShadowExecutor<E, I, S, SOT> {
}
}
impl<E: Debug, I: Debug, S, SOT: Debug> ShadowExecutor<E, I, S, SOT>
impl<E, SOT> ShadowExecutor<E, SOT>
where
SOT: ObserversTuple<I, S>,
E: HasObservers + Debug,
SOT: ObserversTuple<E::State> + Debug,
{
/// Create a new `ShadowExecutor`, wrapping the given `executor`.
pub fn new(executor: E, shadow_observers: SOT) -> Self {
Self {
executor,
shadow_observers,
phantom: PhantomData,
}
}
@ -57,38 +56,50 @@ where
}
}
impl<E, EM, I, S, SOT, Z> Executor<EM, I, S, Z> for ShadowExecutor<E, I, S, SOT>
impl<E, EM, SOT, Z> Executor<EM, Z> for ShadowExecutor<E, SOT>
where
E: Executor<EM, I, S, Z>,
I: Input,
SOT: ObserversTuple<I, S>,
E: Executor<EM, Z> + HasObservers,
SOT: ObserversTuple<E::State>,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
self.executor.run_target(fuzzer, state, mgr, input)
}
}
impl<E, I, OT, S, SOT> HasObservers<I, OT, S> for ShadowExecutor<E, I, S, SOT>
impl<E, SOT> UsesState for ShadowExecutor<E, SOT>
where
I: Debug,
S: Debug,
E: HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
SOT: ObserversTuple<I, S>,
E: UsesState,
{
type State = E::State;
}
impl<E, SOT> UsesObservers for ShadowExecutor<E, SOT>
where
E: UsesObservers,
{
type Observers = E::Observers;
}
impl<E, SOT> HasObservers for ShadowExecutor<E, SOT>
where
E: HasObservers,
SOT: ObserversTuple<E::State>,
{
#[inline]
fn observers(&self) -> &OT {
fn observers(&self) -> &Self::Observers {
self.executor.observers()
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
fn observers_mut(&mut self) -> &mut Self::Observers {
self.executor.observers_mut()
}
}

View File

@ -33,8 +33,8 @@ use windows::Win32::{
use crate::executors::inprocess::{HasInProcessHandlers, GLOBAL_STATE};
use crate::{
executors::{Executor, ExitKind, HasObservers},
inputs::Input,
observers::ObserversTuple,
observers::UsesObservers,
state::UsesState,
Error,
};
@ -77,6 +77,7 @@ const ITIMER_REAL: c_int = 0;
/// The timeout executor is a wrapper that sets a timeout before each run
pub struct TimeoutExecutor<E> {
/// The wrapped [`Executor`]
executor: E,
#[cfg(target_os = "linux")]
itimerspec: libc::itimerspec,
@ -263,18 +264,19 @@ impl<E: HasInProcessHandlers> TimeoutExecutor<E> {
}
#[cfg(windows)]
impl<E, EM, I, S, Z> Executor<EM, I, S, Z> for TimeoutExecutor<E>
impl<E, EM, Z> Executor<EM, Z> for TimeoutExecutor<E>
where
E: Executor<EM, I, S, Z> + HasInProcessHandlers,
I: Input,
E: Executor<EM, Z> + HasInProcessHandlers,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
#[allow(clippy::cast_sign_loss)]
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
unsafe {
let data = &mut GLOBAL_STATE;
@ -333,17 +335,18 @@ where
}
#[cfg(target_os = "linux")]
impl<E, EM, I, S, Z> Executor<EM, I, S, Z> for TimeoutExecutor<E>
impl<E, EM, Z> Executor<EM, Z> for TimeoutExecutor<E>
where
E: Executor<EM, I, S, Z>,
I: Input,
E: Executor<EM, Z>,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
unsafe {
libc::timer_settime(self.timerid, 0, addr_of_mut!(self.itimerspec), null_mut());
@ -364,17 +367,18 @@ where
}
#[cfg(all(unix, not(target_os = "linux")))]
impl<E, EM, I, S, Z> Executor<EM, I, S, Z> for TimeoutExecutor<E>
impl<E, EM, Z> Executor<EM, Z> for TimeoutExecutor<E>
where
E: Executor<EM, I, S, Z>,
I: Input,
E: Executor<EM, Z>,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
unsafe {
setitimer(ITIMER_REAL, &mut self.itimerval, null_mut());
@ -393,18 +397,31 @@ where
}
}
impl<E, I, OT, S> HasObservers<I, OT, S> for TimeoutExecutor<E>
impl<E> UsesState for TimeoutExecutor<E>
where
E: HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
E: UsesState,
{
type State = E::State;
}
impl<E> UsesObservers for TimeoutExecutor<E>
where
E: UsesObservers,
{
type Observers = E::Observers;
}
impl<E> HasObservers for TimeoutExecutor<E>
where
E: HasObservers,
{
#[inline]
fn observers(&self) -> &OT {
fn observers(&self) -> &Self::Observers {
self.executor.observers()
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
fn observers_mut(&mut self) -> &mut Self::Observers {
self.executor.observers_mut()
}
}

View File

@ -4,39 +4,55 @@ use core::fmt::Debug;
use crate::{
executors::{Executor, ExitKind, HasObservers},
inputs::Input,
observers::ObserversTuple,
observers::{ObserversTuple, UsesObservers},
state::UsesState,
Error,
};
/// A wrapper for any [`Executor`] to make it implement [`HasObservers`] using a given [`ObserversTuple`].
#[derive(Debug)]
pub struct WithObservers<E: Debug, OT: Debug> {
pub struct WithObservers<E, OT> {
executor: E,
observers: OT,
}
impl<E, EM, I, OT, S, Z> Executor<EM, I, S, Z> for WithObservers<E, OT>
impl<E, EM, OT, Z> Executor<EM, Z> for WithObservers<E, OT>
where
I: Input,
E: Executor<EM, I, S, Z>,
E: Executor<EM, Z> + Debug,
OT: Debug,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
self.executor.run_target(fuzzer, state, mgr, input)
}
}
impl<I, E: Debug, OT: Debug, S> HasObservers<I, OT, S> for WithObservers<E, OT>
impl<E, OT> UsesState for WithObservers<E, OT>
where
I: Input,
OT: ObserversTuple<I, S>,
E: UsesState,
{
type State = E::State;
}
impl<E, OT> UsesObservers for WithObservers<E, OT>
where
E: UsesState,
OT: ObserversTuple<E::State>,
{
type Observers = OT;
}
impl<E, OT> HasObservers for WithObservers<E, OT>
where
E: HasObservers + Debug,
OT: ObserversTuple<E::State> + Debug,
{
fn observers(&self) -> &OT {
&self.observers

View File

@ -4,6 +4,7 @@
//! to be not interesting.
//! Requires a [`ConcolicObserver`] to observe the concolic trace.
use alloc::{borrow::ToOwned, string::String};
use core::{fmt::Debug, marker::PhantomData};
use crate::{
bolts::tuples::Named,
@ -11,7 +12,7 @@ use crate::{
events::EventFirer,
executors::ExitKind,
feedbacks::Feedback,
inputs::Input,
inputs::UsesInput,
observers::{
concolic::{ConcolicMetadata, ConcolicObserver},
ObserversTuple,
@ -25,12 +26,13 @@ use crate::{
/// to be not interesting.
/// Requires a [`ConcolicObserver`] to observe the concolic trace.
#[derive(Debug)]
pub struct ConcolicFeedback {
pub struct ConcolicFeedback<S> {
name: String,
metadata: Option<ConcolicMetadata>,
phantom: PhantomData<S>,
}
impl ConcolicFeedback {
impl<S> ConcolicFeedback<S> {
/// Creates a concolic feedback from an observer
#[allow(unused)]
#[must_use]
@ -38,33 +40,33 @@ impl ConcolicFeedback {
Self {
name: observer.name().to_owned(),
metadata: None,
phantom: PhantomData,
}
}
}
impl Named for ConcolicFeedback {
impl<S> Named for ConcolicFeedback<S> {
fn name(&self) -> &str {
&self.name
}
}
impl<I, S> Feedback<I, S> for ConcolicFeedback
impl<S> Feedback<S> for ConcolicFeedback<S>
where
I: Input,
S: HasClientPerfMonitor,
S: UsesInput + Debug + HasClientPerfMonitor,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &<S as UsesInput>::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
self.metadata = observers
.match_name::<ConcolicObserver>(&self.name)
@ -75,7 +77,7 @@ where
fn append_metadata(
&mut self,
_state: &mut S,
_testcase: &mut Testcase<I>,
_testcase: &mut Testcase<<S as UsesInput>::Input>,
) -> Result<(), Error> {
if let Some(metadata) = self.metadata.take() {
_testcase.metadata_mut().insert(metadata);
@ -83,7 +85,11 @@ where
Ok(())
}
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn discard_metadata(
&mut self,
_state: &mut S,
_input: &<S as UsesInput>::Input,
) -> Result<(), Error> {
Ok(())
}
}

View File

@ -16,7 +16,7 @@ use crate::{
feedbacks::Feedback,
inputs::Input,
observers::{Observer, ObserversTuple},
state::{HasClientPerfMonitor, HasMetadata},
state::{HasClientPerfMonitor, HasMetadata, State},
Error,
};
@ -48,7 +48,7 @@ impl DiffResult {
/// A [`DiffFeedback`] compares the content of two [`Observer`]s using the given compare function.
#[derive(Serialize, Deserialize)]
pub struct DiffFeedback<F, O1, O2>
pub struct DiffFeedback<F, I, O1, O2, S>
where
F: FnMut(&O1, &O2) -> DiffResult,
{
@ -60,10 +60,10 @@ where
o2_name: String,
/// The function used to compare the two observers
compare_fn: F,
phantomm: PhantomData<(O1, O2)>,
phantomm: PhantomData<(O1, O2, I, S)>,
}
impl<F, O1, O2> DiffFeedback<F, O1, O2>
impl<F, I, O1, O2, S> DiffFeedback<F, I, O1, O2, S>
where
F: FnMut(&O1, &O2) -> DiffResult,
O1: Named,
@ -90,7 +90,7 @@ where
}
}
impl<F, O1, O2> Named for DiffFeedback<F, O1, O2>
impl<F, I, O1, O2, S> Named for DiffFeedback<F, I, O1, O2, S>
where
F: FnMut(&O1, &O2) -> DiffResult,
O1: Named,
@ -101,7 +101,7 @@ where
}
}
impl<F, O1, O2> Debug for DiffFeedback<F, O1, O2>
impl<F, I, O1, O2, S> Debug for DiffFeedback<F, I, O1, O2, S>
where
F: FnMut(&O1, &O2) -> DiffResult,
O1: Named,
@ -116,13 +116,13 @@ where
}
}
impl<F, I, O1, O2, S> Feedback<I, S> for DiffFeedback<F, O1, O2>
impl<F, I, O1, O2, S> Feedback<S> for DiffFeedback<F, I, O1, O2, S>
where
F: FnMut(&O1, &O2) -> DiffResult,
I: Input,
S: HasMetadata + HasClientPerfMonitor,
O1: Observer<I, S> + PartialEq<O2>,
O2: Observer<I, S>,
S: HasMetadata + HasClientPerfMonitor + State<Input = I>,
O1: Observer<S> + PartialEq<O2>,
O2: Observer<S>,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
@ -134,8 +134,8 @@ where
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S> + MatchName,
EM: EventFirer<State = S>,
OT: ObserversTuple<S> + MatchName,
{
fn err(name: &str) -> Error {
Error::illegal_argument(format!("DiffFeedback: observer {name} not found"))
@ -154,19 +154,16 @@ where
#[cfg(test)]
mod tests {
use alloc::string::{String, ToString};
use core::marker::PhantomData;
use crate::{
bolts::{
serdeany::SerdeAnyMap,
tuples::{tuple_list, Named},
},
bolts::tuples::{tuple_list, Named},
events::EventFirer,
executors::ExitKind,
feedbacks::{differential::DiffResult, DiffFeedback, Feedback},
inputs::{BytesInput, Input},
monitors::ClientPerfMonitor,
inputs::{BytesInput, UsesInput},
observers::Observer,
state::{HasClientPerfMonitor, HasMetadata},
state::{NopState, UsesState},
};
#[derive(Debug)]
@ -182,7 +179,7 @@ mod tests {
}
}
}
impl<I, S> Observer<I, S> for NopObserver {}
impl<S> Observer<S> for NopObserver where S: UsesInput {}
impl PartialEq for NopObserver {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
@ -194,39 +191,30 @@ mod tests {
}
}
struct NopEventFirer;
impl<I: Input> EventFirer<I> for NopEventFirer {
fn fire<S>(
struct NopEventFirer<S> {
phantom: PhantomData<S>,
}
impl<S> UsesState for NopEventFirer<S>
where
S: UsesInput,
{
type State = S;
}
impl<S> EventFirer for NopEventFirer<S>
where
S: UsesInput,
{
fn fire(
&mut self,
_state: &mut S,
_event: crate::events::Event<I>,
_event: crate::events::Event<S::Input>,
) -> Result<(), crate::Error> {
Ok(())
}
}
struct NopState;
impl HasMetadata for NopState {
fn metadata(&self) -> &SerdeAnyMap {
unimplemented!()
}
fn metadata_mut(&mut self) -> &mut SerdeAnyMap {
unimplemented!()
}
}
impl HasClientPerfMonitor for NopState {
fn introspection_monitor(&self) -> &ClientPerfMonitor {
unimplemented!()
}
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor {
unimplemented!()
}
}
fn test_diff(should_equal: bool) {
let mut nop_state = NopState;
let mut nop_state = NopState::new();
let o1 = NopObserver::new("o1", true);
let o2 = NopObserver::new("o2", should_equal);
@ -245,7 +233,9 @@ mod tests {
diff_feedback
.is_interesting(
&mut nop_state,
&mut NopEventFirer {},
&mut NopEventFirer {
phantom: PhantomData
},
&BytesInput::new(vec![0]),
&observers,
&ExitKind::Ok

View File

@ -21,7 +21,7 @@ use crate::{
events::{Event, EventFirer},
executors::ExitKind,
feedbacks::{Feedback, HasObserverName},
inputs::Input,
inputs::UsesInput,
monitors::UserStats,
observers::{MapObserver, ObserversTuple},
state::{HasClientPerfMonitor, HasMetadata, HasNamedMetadata},
@ -32,20 +32,19 @@ use crate::{
pub const MAPFEEDBACK_PREFIX: &str = "mapfeedback_metadata_";
/// A [`MapFeedback`] that implements the AFL algorithm using an [`OrReducer`] combining the bits for the history map and the bit from ``HitcountsMapObserver``.
pub type AflMapFeedback<I, O, S, T> = MapFeedback<I, DifferentIsNovel, O, OrReducer, S, T>;
pub type AflMapFeedback<O, S, T> = MapFeedback<DifferentIsNovel, O, OrReducer, S, T>;
/// A [`MapFeedback`] that strives to maximize the map contents.
pub type MaxMapFeedback<I, O, S, T> = MapFeedback<I, DifferentIsNovel, O, MaxReducer, S, T>;
pub type MaxMapFeedback<O, S, T> = MapFeedback<DifferentIsNovel, O, MaxReducer, S, T>;
/// A [`MapFeedback`] that strives to minimize the map contents.
pub type MinMapFeedback<I, O, S, T> = MapFeedback<I, DifferentIsNovel, O, MinReducer, S, T>;
pub type MinMapFeedback<O, S, T> = MapFeedback<DifferentIsNovel, O, MinReducer, S, T>;
/// A [`MapFeedback`] that strives to maximize the map contents,
/// but only, if a value is larger than `pow2` of the previous.
pub type MaxMapPow2Feedback<I, O, S, T> = MapFeedback<I, NextPow2IsNovel, O, MaxReducer, S, T>;
pub type MaxMapPow2Feedback<O, S, T> = MapFeedback<NextPow2IsNovel, O, MaxReducer, S, T>;
/// A [`MapFeedback`] that strives to maximize the map contents,
/// but only, if a value is larger than `pow2` of the previous.
pub type MaxMapOneOrFilledFeedback<I, O, S, T> =
MapFeedback<I, OneOrFilledIsNovel, O, MaxReducer, S, T>;
pub type MaxMapOneOrFilledFeedback<O, S, T> = MapFeedback<OneOrFilledIsNovel, O, MaxReducer, S, T>;
/// A `Reducer` function is used to aggregate values for the novelty search
pub trait Reducer<T>: 'static + Debug
@ -333,15 +332,7 @@ where
/// The most common AFL-like feedback type
#[derive(Clone, Debug)]
pub struct MapFeedback<I, N, O, R, S, T>
where
T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug,
R: Reducer<T>,
O: MapObserver<Entry = T>,
for<'it> O: AsIter<'it, Item = T>,
N: IsNovel<T>,
S: HasNamedMetadata,
{
pub struct MapFeedback<N, O, R, S, T> {
/// Indexes used in the last observation
indexes: Option<Vec<usize>>,
/// New indexes observed in the last observation
@ -353,18 +344,16 @@ where
/// Name of the feedback as shown in the `UserStats`
stats_name: String,
/// Phantom Data of Reducer
phantom: PhantomData<(I, N, S, R, O, T)>,
phantom: PhantomData<(N, O, R, S, T)>,
}
impl<I, N, O, R, S, T> Feedback<I, S> for MapFeedback<I, N, O, R, S, T>
impl<N, O, R, S, T> Feedback<S> for MapFeedback<N, O, R, S, T>
where
T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug,
R: Reducer<T>,
O: MapObserver<Entry = T>,
for<'it> O: AsIter<'it, Item = T>,
N: IsNovel<T>,
I: Input,
S: HasNamedMetadata + HasClientPerfMonitor + Debug,
N: IsNovel<T> + Debug,
O: MapObserver<Entry = T> + for<'it> AsIter<'it, Item = T> + Debug,
R: Reducer<T> + Debug,
S: UsesInput + HasClientPerfMonitor + HasNamedMetadata + Debug,
T: Default + Copy + Serialize + for<'de> Deserialize<'de> + PartialEq + Debug + 'static,
{
fn init_state(&mut self, state: &mut S) -> Result<(), Error> {
// Initialize `MapFeedbackMetadata` with an empty vector and add it to the state.
@ -378,13 +367,13 @@ where
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
input: &<S as UsesInput>::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
self.is_interesting_default(state, manager, input, observers, exit_kind)
}
@ -394,18 +383,22 @@ where
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
input: &<S as UsesInput>::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
self.is_interesting_default(state, manager, input, observers, exit_kind)
}
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(
&mut self,
_state: &mut S,
testcase: &mut Testcase<<S as UsesInput>::Input>,
) -> Result<(), Error> {
if let Some(v) = self.indexes.as_mut() {
let meta = MapIndexesMetadata::new(core::mem::take(v));
testcase.add_metadata(meta);
@ -418,7 +411,11 @@ where
}
/// Discard the stored metadata in case that the testcase is not added to the corpus
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn discard_metadata(
&mut self,
_state: &mut S,
_input: &<S as UsesInput>::Input,
) -> Result<(), Error> {
if let Some(v) = self.indexes.as_mut() {
v.clear();
}
@ -431,12 +428,11 @@ where
/// Specialize for the common coverage map size, maximization of u8s
#[rustversion::nightly]
impl<I, O, S> Feedback<I, S> for MapFeedback<I, DifferentIsNovel, O, MaxReducer, S, u8>
impl<O, S> Feedback<S> for MapFeedback<DifferentIsNovel, O, MaxReducer, S, u8>
where
O: MapObserver<Entry = u8> + AsSlice<u8>,
for<'it> O: AsIter<'it, Item = u8>,
I: Input,
S: HasNamedMetadata + HasClientPerfMonitor + Debug,
S: UsesInput + HasNamedMetadata + HasClientPerfMonitor + Debug,
{
#[allow(clippy::wrong_self_convention)]
#[allow(clippy::needless_range_loop)]
@ -444,13 +440,13 @@ where
&mut self,
state: &mut S,
manager: &mut EM,
_input: &I,
_input: &<S as UsesInput>::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
// 128 bits vectors
type VectorType = core::simd::u8x16;
@ -550,22 +546,14 @@ where
}
}
impl<I, N, O, R, S, T> Named for MapFeedback<I, N, O, R, S, T>
where
T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug,
R: Reducer<T>,
N: IsNovel<T>,
O: MapObserver<Entry = T>,
for<'it> O: AsIter<'it, Item = T>,
S: HasNamedMetadata,
{
impl<N, O, R, S, T> Named for MapFeedback<N, O, R, S, T> {
#[inline]
fn name(&self) -> &str {
self.name.as_str()
}
}
impl<I, N, O, R, S, T> HasObserverName for MapFeedback<I, N, O, R, S, T>
impl<N, O, R, S, T> HasObserverName for MapFeedback<N, O, R, S, T>
where
T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug,
R: Reducer<T>,
@ -584,15 +572,14 @@ fn create_stats_name(name: &str) -> String {
name.to_lowercase()
}
impl<I, N, O, R, S, T> MapFeedback<I, N, O, R, S, T>
impl<N, O, R, S, T> MapFeedback<N, O, R, S, T>
where
T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug,
R: Reducer<T>,
O: MapObserver<Entry = T>,
for<'it> O: AsIter<'it, Item = T>,
N: IsNovel<T>,
I: Input,
S: HasNamedMetadata + HasClientPerfMonitor + Debug,
S: UsesInput + HasNamedMetadata + HasClientPerfMonitor + Debug,
{
/// Create new `MapFeedback`
#[must_use]
@ -673,13 +660,13 @@ where
&mut self,
state: &mut S,
manager: &mut EM,
_input: &I,
_input: &S::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
let mut interesting = false;
// TODO Replace with match_name_type when stable
@ -735,13 +722,13 @@ where
/// A [`ReachabilityFeedback`] reports if a target has been reached.
#[derive(Clone, Debug)]
pub struct ReachabilityFeedback<O> {
pub struct ReachabilityFeedback<O, S> {
name: String,
target_idx: Vec<usize>,
phantom: PhantomData<O>,
phantom: PhantomData<(O, S)>,
}
impl<O> ReachabilityFeedback<O>
impl<O, S> ReachabilityFeedback<O, S>
where
O: MapObserver<Entry = usize>,
for<'it> O: AsIter<'it, Item = usize>,
@ -767,25 +754,24 @@ where
}
}
impl<I, O, S> Feedback<I, S> for ReachabilityFeedback<O>
impl<O, S> Feedback<S> for ReachabilityFeedback<O, S>
where
I: Input,
S: UsesInput + Debug + HasClientPerfMonitor,
O: MapObserver<Entry = usize>,
for<'it> O: AsIter<'it, Item = usize>,
S: HasClientPerfMonitor,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &<S as UsesInput>::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
// TODO Replace with match_name_type when stable
let observer = observers.match_name::<O>(&self.name).unwrap();
@ -804,7 +790,11 @@ where
}
}
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(
&mut self,
_state: &mut S,
testcase: &mut Testcase<<S as UsesInput>::Input>,
) -> Result<(), Error> {
if !self.target_idx.is_empty() {
let meta = MapIndexesMetadata::new(core::mem::take(self.target_idx.as_mut()));
testcase.add_metadata(meta);
@ -812,13 +802,17 @@ where
Ok(())
}
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn discard_metadata(
&mut self,
_state: &mut S,
_input: &<S as UsesInput>::Input,
) -> Result<(), Error> {
self.target_idx.clear();
Ok(())
}
}
impl<O> Named for ReachabilityFeedback<O>
impl<O, S> Named for ReachabilityFeedback<O, S>
where
O: MapObserver<Entry = usize>,
for<'it> O: AsIter<'it, Item = usize>,
@ -862,9 +856,7 @@ pub mod pybind {
use pyo3::prelude::*;
use super::{Debug, HasObserverName, MaxMapFeedback};
use crate::{
feedbacks::pybind::PythonFeedback, inputs::BytesInput, state::pybind::PythonStdState,
};
use crate::{feedbacks::pybind::PythonFeedback, state::pybind::PythonStdState};
macro_rules! define_python_map_feedback {
($struct_name:ident, $py_name:tt, $datatype:ty, $map_observer_type_name: ident, $my_std_state_type_name: ident) => {
@ -876,7 +868,6 @@ pub mod pybind {
pub struct $struct_name {
/// Rust wrapped MaxMapFeedback object
pub inner: MaxMapFeedback<
BytesInput,
$map_observer_type_name, /* PythonMapObserverI8 */
$my_std_state_type_name,
$datatype,

View File

@ -1,5 +1,7 @@
//! The feedbacks reduce observer state after each run to a single `is_interesting`-value.
//! If a testcase is interesting, it may be added to a Corpus.
//!
//! TODO: make S of Feedback<S> an associated type when specialisation + AT is stable
pub mod map;
pub use map::*;
@ -36,7 +38,7 @@ use crate::{
corpus::Testcase,
events::EventFirer,
executors::ExitKind,
inputs::Input,
inputs::UsesInput,
observers::{ListObserver, ObserversTuple, TimeObserver},
state::HasClientPerfMonitor,
Error,
@ -45,10 +47,9 @@ use crate::{
/// Feedbacks evaluate the observers.
/// Basically, they reduce the information provided by an observer to a value,
/// indicating the "interestingness" of the last run.
pub trait Feedback<I, S>: Named + Debug
pub trait Feedback<S>: Named + Debug
where
I: Input,
S: HasClientPerfMonitor,
S: UsesInput + HasClientPerfMonitor,
{
/// Initializes the feedback state.
/// This method is called after that the `State` is created.
@ -62,13 +63,13 @@ where
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>;
EM: EventFirer<State = S>,
OT: ObserversTuple<S>;
/// Returns if the result of a run is interesting and the value input should be stored in a corpus.
/// It also keeps track of introspection stats.
@ -79,13 +80,13 @@ where
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
// Start a timer for this feedback
let start_time = crate::bolts::cpu::read_time_counter();
@ -109,14 +110,14 @@ where
fn append_metadata(
&mut self,
_state: &mut S,
_testcase: &mut Testcase<I>,
_testcase: &mut Testcase<S::Input>,
) -> Result<(), Error> {
Ok(())
}
/// Discard the stored metadata in case that the testcase is not added to the corpus
#[inline]
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
}
@ -129,42 +130,39 @@ pub trait HasObserverName {
/// A combined feedback consisting of multiple [`Feedback`]s
#[derive(Debug)]
pub struct CombinedFeedback<A, B, FL, I, S>
pub struct CombinedFeedback<A, B, FL, S>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
B: Feedback<S>,
FL: FeedbackLogic<A, B, S>,
S: UsesInput + HasClientPerfMonitor,
{
/// First [`Feedback`]
pub first: A,
/// Second [`Feedback`]
pub second: B,
name: String,
phantom: PhantomData<(I, S, FL)>,
phantom: PhantomData<(S, FL)>,
}
impl<A, B, FL, I, S> Named for CombinedFeedback<A, B, FL, I, S>
impl<A, B, FL, S> Named for CombinedFeedback<A, B, FL, S>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
B: Feedback<S>,
FL: FeedbackLogic<A, B, S>,
S: UsesInput + HasClientPerfMonitor,
{
fn name(&self) -> &str {
self.name.as_ref()
}
}
impl<A, B, FL, I, S> CombinedFeedback<A, B, FL, I, S>
impl<A, B, FL, S> CombinedFeedback<A, B, FL, S>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
B: Feedback<S>,
FL: FeedbackLogic<A, B, S>,
S: UsesInput + HasClientPerfMonitor,
{
/// Create a new combined feedback
pub fn new(first: A, second: B) -> Self {
@ -178,13 +176,12 @@ where
}
}
impl<A, B, FL, I, S> Feedback<I, S> for CombinedFeedback<A, B, FL, I, S>
impl<A, B, FL, S> Feedback<S> for CombinedFeedback<A, B, FL, S>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input,
S: HasClientPerfMonitor + Debug,
A: Feedback<S>,
B: Feedback<S>,
FL: FeedbackLogic<A, B, S>,
S: UsesInput + HasClientPerfMonitor + Debug,
{
fn init_state(&mut self, state: &mut S) -> Result<(), Error> {
self.first.init_state(state)?;
@ -197,13 +194,13 @@ where
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
FL::is_pair_interesting(
&mut self.first,
@ -222,13 +219,13 @@ where
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
FL::is_pair_interesting_introspection(
&mut self.first,
@ -242,25 +239,28 @@ where
}
#[inline]
fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(
&mut self,
state: &mut S,
testcase: &mut Testcase<S::Input>,
) -> Result<(), Error> {
self.first.append_metadata(state, testcase)?;
self.second.append_metadata(state, testcase)
}
#[inline]
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
fn discard_metadata(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.first.discard_metadata(state, input)?;
self.second.discard_metadata(state, input)
}
}
/// Logical combination of two feedbacks
pub trait FeedbackLogic<A, B, I, S>: 'static + Debug
pub trait FeedbackLogic<A, B, S>: 'static + Debug
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
B: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
/// The name of this combination
fn name() -> &'static str;
@ -271,13 +271,13 @@ where
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>;
EM: EventFirer<State = S>,
OT: ObserversTuple<S>;
/// If this pair is interesting (with introspection features enabled)
#[cfg(feature = "introspection")]
@ -287,33 +287,31 @@ where
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>;
EM: EventFirer<State = S>,
OT: ObserversTuple<S>;
}
/// Factory for feedbacks which should be sensitive to an existing context, e.g. observer(s) from a
/// specific execution
pub trait FeedbackFactory<F, I, S, T>
pub trait FeedbackFactory<F, S, T>
where
F: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
F: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
/// Create the feedback from the provided context
fn create_feedback(&self, ctx: &T) -> F;
}
impl<FE, FU, I, S, T> FeedbackFactory<FE, I, S, T> for FU
impl<FE, FU, S, T> FeedbackFactory<FE, S, T> for FU
where
FU: Fn(&T) -> FE,
FE: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
FE: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
fn create_feedback(&self, ctx: &T) -> FE {
self(ctx)
@ -340,11 +338,10 @@ where
}
}
impl<F, I, S, T> FeedbackFactory<F, I, S, T> for DefaultFeedbackFactory<F>
impl<F, S, T> FeedbackFactory<F, S, T> for DefaultFeedbackFactory<F>
where
F: Feedback<I, S> + Default,
I: Input,
S: HasClientPerfMonitor,
F: Feedback<S> + Default,
S: UsesInput + HasClientPerfMonitor,
{
fn create_feedback(&self, _ctx: &T) -> F {
F::default()
@ -367,12 +364,11 @@ pub struct LogicEagerAnd {}
#[derive(Debug, Clone)]
pub struct LogicFastAnd {}
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicEagerOr
impl<A, B, S> FeedbackLogic<A, B, S> for LogicEagerOr
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
B: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
fn name() -> &'static str {
"Eager OR"
@ -383,13 +379,13 @@ where
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
let b = second.is_interesting(state, manager, input, observers, exit_kind)?;
@ -402,13 +398,13 @@ where
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
// Execute this feedback
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
@ -418,12 +414,11 @@ where
}
}
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicFastOr
impl<A, B, S> FeedbackLogic<A, B, S> for LogicFastOr
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
B: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
fn name() -> &'static str {
"Fast OR"
@ -434,13 +429,13 @@ where
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
if a {
@ -456,13 +451,13 @@ where
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
// Execute this feedback
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
@ -475,12 +470,11 @@ where
}
}
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicEagerAnd
impl<A, B, S> FeedbackLogic<A, B, S> for LogicEagerAnd
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
B: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
fn name() -> &'static str {
"Eager AND"
@ -491,13 +485,13 @@ where
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
let b = second.is_interesting(state, manager, input, observers, exit_kind)?;
@ -510,13 +504,13 @@ where
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
// Execute this feedback
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
@ -526,12 +520,11 @@ where
}
}
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicFastAnd
impl<A, B, S> FeedbackLogic<A, B, S> for LogicFastAnd
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
B: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
fn name() -> &'static str {
"Fast AND"
@ -542,13 +535,13 @@ where
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
if !a {
@ -564,13 +557,13 @@ where
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
// Execute this feedback
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
@ -585,42 +578,40 @@ where
/// Combine two feedbacks with an eager AND operation,
/// will call all feedbacks functions even if not necessary to conclude the result
pub type EagerAndFeedback<A, B, I, S> = CombinedFeedback<A, B, LogicEagerAnd, I, S>;
pub type EagerAndFeedback<A, B, S> = CombinedFeedback<A, B, LogicEagerAnd, S>;
/// Combine two feedbacks with an fast AND operation,
/// might skip calling feedbacks functions if not necessary to conclude the result
pub type FastAndFeedback<A, B, I, S> = CombinedFeedback<A, B, LogicFastAnd, I, S>;
pub type FastAndFeedback<A, B, S> = CombinedFeedback<A, B, LogicFastAnd, S>;
/// Combine two feedbacks with an eager OR operation,
/// will call all feedbacks functions even if not necessary to conclude the result
pub type EagerOrFeedback<A, B, I, S> = CombinedFeedback<A, B, LogicEagerOr, I, S>;
pub type EagerOrFeedback<A, B, S> = CombinedFeedback<A, B, LogicEagerOr, S>;
/// Combine two feedbacks with an fast OR operation,
/// might skip calling feedbacks functions if not necessary to conclude the result.
/// This means any feedback that is not first might be skipped, use caution when using with
/// `TimeFeedback`
pub type FastOrFeedback<A, B, I, S> = CombinedFeedback<A, B, LogicFastOr, I, S>;
pub type FastOrFeedback<A, B, S> = CombinedFeedback<A, B, LogicFastOr, S>;
/// Compose feedbacks with an `NOT` operation
#[derive(Clone)]
pub struct NotFeedback<A, I, S>
pub struct NotFeedback<A, S>
where
A: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
/// The feedback to invert
pub first: A,
/// The name
name: String,
phantom: PhantomData<(I, S)>,
phantom: PhantomData<S>,
}
impl<A, I, S> Debug for NotFeedback<A, I, S>
impl<A, S> Debug for NotFeedback<A, S>
where
A: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("NotFeedback")
@ -630,11 +621,10 @@ where
}
}
impl<A, I, S> Feedback<I, S> for NotFeedback<A, I, S>
impl<A, S> Feedback<S> for NotFeedback<A, S>
where
A: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
fn init_state(&mut self, state: &mut S) -> Result<(), Error> {
self.first.init_state(state)
@ -645,13 +635,13 @@ where
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
input: &S::Input,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
Ok(!self
.first
@ -659,21 +649,24 @@ where
}
#[inline]
fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(
&mut self,
state: &mut S,
testcase: &mut Testcase<S::Input>,
) -> Result<(), Error> {
self.first.append_metadata(state, testcase)
}
#[inline]
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
fn discard_metadata(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.first.discard_metadata(state, input)
}
}
impl<A, I, S> Named for NotFeedback<A, I, S>
impl<A, S> Named for NotFeedback<A, S>
where
A: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
#[inline]
fn name(&self) -> &str {
@ -681,11 +674,10 @@ where
}
}
impl<A, I, S> NotFeedback<A, I, S>
impl<A, S> NotFeedback<A, S>
where
A: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
A: Feedback<S>,
S: UsesInput + HasClientPerfMonitor,
{
/// Creates a new [`NotFeedback`].
pub fn new(first: A) -> Self {
@ -751,23 +743,22 @@ macro_rules! feedback_not {
}
/// Hack to use () as empty Feedback
impl<I, S> Feedback<I, S> for ()
impl<S> Feedback<S> for ()
where
I: Input,
S: HasClientPerfMonitor,
S: UsesInput + HasClientPerfMonitor,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &S::Input,
_observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
Ok(false)
}
@ -784,23 +775,22 @@ impl Named for () {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CrashFeedback {}
impl<I, S> Feedback<I, S> for CrashFeedback
impl<S> Feedback<S> for CrashFeedback
where
I: Input,
S: HasClientPerfMonitor,
S: UsesInput + HasClientPerfMonitor,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &S::Input,
_observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
if let ExitKind::Crash = exit_kind {
Ok(true)
@ -838,23 +828,22 @@ pub type CrashFeedbackFactory = DefaultFeedbackFactory<CrashFeedback>;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeoutFeedback {}
impl<I, S> Feedback<I, S> for TimeoutFeedback
impl<S> Feedback<S> for TimeoutFeedback
where
I: Input,
S: HasClientPerfMonitor,
S: UsesInput + HasClientPerfMonitor,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &S::Input,
_observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
if let ExitKind::Timeout = exit_kind {
Ok(true)
@ -897,23 +886,22 @@ pub struct TimeFeedback {
name: String,
}
impl<I, S> Feedback<I, S> for TimeFeedback
impl<S> Feedback<S> for TimeFeedback
where
I: Input,
S: HasClientPerfMonitor,
S: UsesInput + HasClientPerfMonitor,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &S::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
// TODO Replace with match_name_type when stable
let observer = observers.match_name::<TimeObserver>(self.name()).unwrap();
@ -923,7 +911,11 @@ where
/// Append to the testcase the generated metadata in case of a new corpus item
#[inline]
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(
&mut self,
_state: &mut S,
testcase: &mut Testcase<S::Input>,
) -> Result<(), Error> {
*testcase.exec_time_mut() = self.exec_time;
self.exec_time = None;
Ok(())
@ -931,7 +923,7 @@ where
/// Discard the stored metadata in case that the testcase is not added to the corpus
#[inline]
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.exec_time = None;
Ok(())
}
@ -974,10 +966,9 @@ where
phantom: PhantomData<T>,
}
impl<I, S, T> Feedback<I, S> for ListFeedback<T>
impl<S, T> Feedback<S> for ListFeedback<T>
where
I: Input,
S: HasClientPerfMonitor,
S: UsesInput + HasClientPerfMonitor,
T: Debug + Serialize + serde::de::DeserializeOwned,
{
#[allow(clippy::wrong_self_convention)]
@ -985,13 +976,13 @@ where
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &S::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
// TODO Replace with match_name_type when stable
let observer = observers
@ -1045,10 +1036,9 @@ pub enum ConstFeedback {
False,
}
impl<I, S> Feedback<I, S> for ConstFeedback
impl<S> Feedback<S> for ConstFeedback
where
I: Input,
S: HasClientPerfMonitor,
S: UsesInput + HasClientPerfMonitor,
{
#[inline]
#[allow(clippy::wrong_self_convention)]
@ -1056,13 +1046,13 @@ where
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &S::Input,
_observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
Ok(match self {
ConstFeedback::True => true,
@ -1168,7 +1158,7 @@ pub mod pybind {
}
}
impl Feedback<BytesInput, PythonStdState> for PyObjectFeedback {
impl Feedback<PythonStdState> for PyObjectFeedback {
fn init_state(&mut self, state: &mut PythonStdState) -> Result<(), Error> {
Python::with_gil(|py| -> PyResult<()> {
self.inner
@ -1187,8 +1177,8 @@ pub mod pybind {
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<BytesInput>,
OT: ObserversTuple<BytesInput, PythonStdState>,
EM: EventFirer<State = PythonStdState>,
OT: ObserversTuple<PythonStdState>,
{
// SAFETY: We use this observer in Python ony when the ObserverTuple is PythonObserversTuple
let dont_look_at_this: &PythonObserversTuple =
@ -1295,7 +1285,7 @@ pub mod pybind {
#[derive(Debug)]
#[pyclass(unsendable, name = "NotFeedback")]
pub struct PythonNotFeedback {
pub inner: NotFeedback<PythonFeedback, BytesInput, PythonStdState>,
pub inner: NotFeedback<PythonFeedback, PythonStdState>,
}
#[pymethods]
@ -1318,7 +1308,7 @@ pub mod pybind {
#[derive(Debug)]
#[pyclass(unsendable, name = $pystring)]
pub struct $pyname {
pub inner: $feed<PythonFeedback, PythonFeedback, BytesInput, PythonStdState>,
pub inner: $feed<PythonFeedback, PythonFeedback, PythonStdState>,
}
#[pymethods]
@ -1624,10 +1614,10 @@ pub mod pybind {
}
}
impl Feedback<BytesInput, PythonStdState> for PythonFeedback {
impl Feedback<PythonStdState> for PythonFeedback {
fn init_state(&mut self, state: &mut PythonStdState) -> Result<(), Error> {
unwrap_me_mut!(self.wrapper, f, {
Feedback::<BytesInput, PythonStdState>::init_state(f, state)
Feedback::<PythonStdState>::init_state(f, state)
})
}
@ -1640,8 +1630,8 @@ pub mod pybind {
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<BytesInput>,
OT: ObserversTuple<BytesInput, PythonStdState>,
EM: EventFirer<State = PythonStdState>,
OT: ObserversTuple<PythonStdState>,
{
unwrap_me_mut!(self.wrapper, f, {
f.is_interesting(state, manager, input, observers, exit_kind)

View File

@ -1,6 +1,6 @@
//! Nautilus grammar mutator, see <https://github.com/nautilus-fuzz/nautilus>
use alloc::string::String;
use core::fmt::Debug;
use core::{fmt::Debug, marker::PhantomData};
use std::fs::create_dir_all;
use grammartec::{chunkstore::ChunkStore, context::Context};
@ -16,6 +16,7 @@ use crate::{
generators::NautilusContext,
inputs::NautilusInput,
observers::ObserversTuple,
prelude::UsesInput,
state::{HasClientPerfMonitor, HasMetadata},
Error,
};
@ -52,33 +53,37 @@ impl NautilusChunksMetadata {
}
/// A nautilus feedback for grammar fuzzing
pub struct NautilusFeedback<'a> {
pub struct NautilusFeedback<'a, S> {
ctx: &'a Context,
phantom: PhantomData<S>,
}
impl Debug for NautilusFeedback<'_> {
impl<S> Debug for NautilusFeedback<'_, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "NautilusFeedback {{}}")
}
}
impl<'a> NautilusFeedback<'a> {
impl<'a, S> NautilusFeedback<'a, S> {
/// Create a new [`NautilusFeedback`]
#[must_use]
pub fn new(context: &'a NautilusContext) -> Self {
Self { ctx: &context.ctx }
Self {
ctx: &context.ctx,
phantom: PhantomData,
}
}
}
impl<'a> Named for NautilusFeedback<'a> {
impl<'a, S> Named for NautilusFeedback<'a, S> {
fn name(&self) -> &str {
"NautilusFeedback"
}
}
impl<'a, S> Feedback<NautilusInput, S> for NautilusFeedback<'a>
impl<'a, S> Feedback<S> for NautilusFeedback<'a, S>
where
S: HasMetadata + HasClientPerfMonitor,
S: HasMetadata + HasClientPerfMonitor + UsesInput<Input = NautilusInput>,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
@ -90,8 +95,8 @@ where
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<NautilusInput>,
OT: ObserversTuple<NautilusInput, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
Ok(false)
}

View File

@ -11,7 +11,7 @@ use crate::{
events::EventFirer,
executors::ExitKind,
feedbacks::{Feedback, HasObserverName},
inputs::Input,
inputs::UsesInput,
observers::{ObserverWithHashField, ObserversTuple},
state::{HasClientPerfMonitor, HasNamedMetadata},
Error,
@ -68,17 +68,16 @@ impl HashSetState<u64> for NewHashFeedbackMetadata {
/// A [`NewHashFeedback`] maintains a hashset of already seen stacktraces and considers interesting unseen ones
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NewHashFeedback<O> {
pub struct NewHashFeedback<O, S> {
name: String,
observer_name: String,
o_type: PhantomData<O>,
o_type: PhantomData<(O, S)>,
}
impl<I, S, O> Feedback<I, S> for NewHashFeedback<O>
impl<O, S> Feedback<S> for NewHashFeedback<O, S>
where
I: Input,
S: HasClientPerfMonitor + HasNamedMetadata,
O: ObserverWithHashField + Named + Debug,
S: UsesInput + Debug + HasNamedMetadata + HasClientPerfMonitor,
{
fn init_state(&mut self, state: &mut S) -> Result<(), Error> {
state.add_named_metadata(NewHashFeedbackMetadata::default(), &self.name);
@ -90,13 +89,13 @@ where
&mut self,
state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &<S as UsesInput>::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
let observer = observers
.match_name::<O>(&self.observer_name)
@ -122,21 +121,21 @@ where
}
}
impl<O> Named for NewHashFeedback<O> {
impl<O, S> Named for NewHashFeedback<O, S> {
#[inline]
fn name(&self) -> &str {
&self.name
}
}
impl<O> HasObserverName for NewHashFeedback<O> {
impl<O, S> HasObserverName for NewHashFeedback<O, S> {
#[inline]
fn observer_name(&self) -> &str {
&self.observer_name
}
}
impl<O> NewHashFeedback<O>
impl<O, S> NewHashFeedback<O, S>
where
O: ObserverWithHashField + Named + Debug,
{

View File

@ -1,23 +1,29 @@
//! The `Fuzzer` is the main struct for a fuzz campaign.
use alloc::string::ToString;
use core::{marker::PhantomData, time::Duration};
use core::{fmt::Debug, marker::PhantomData, time::Duration};
use serde::{de::DeserializeOwned, Serialize};
#[cfg(test)]
use crate::inputs::Input;
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
#[cfg(test)]
use crate::state::NopState;
use crate::{
bolts::current_time,
corpus::{Corpus, Testcase},
events::{Event, EventConfig, EventFirer, EventManager, ProgressReporter},
events::{Event, EventConfig, EventFirer, EventProcessor, ProgressReporter},
executors::{Executor, ExitKind, HasObservers},
feedbacks::Feedback,
inputs::Input,
inputs::UsesInput,
mark_feature_time,
observers::ObserversTuple,
schedulers::Scheduler,
stages::StagesTuple,
start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasSolutions},
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasSolutions, UsesState},
Error,
};
@ -25,10 +31,9 @@ use crate::{
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15);
/// Holds a scheduler
pub trait HasScheduler<CS, I, S>
pub trait HasScheduler<CS>: UsesState
where
CS: Scheduler<I, S>,
I: Input,
CS: Scheduler<State = Self::State>,
{
/// The scheduler
fn scheduler(&self) -> &CS;
@ -38,11 +43,10 @@ where
}
/// Holds an feedback
pub trait HasFeedback<F, I, S>
pub trait HasFeedback<F>: UsesState
where
F: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
F: Feedback<Self::State>,
Self::State: HasClientPerfMonitor,
{
/// The feedback
fn feedback(&self) -> &F;
@ -52,11 +56,10 @@ where
}
/// Holds an objective feedback
pub trait HasObjective<I, OF, S>
pub trait HasObjective<OF>: UsesState
where
OF: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor,
OF: Feedback<Self::State>,
Self::State: HasClientPerfMonitor,
{
/// The objective feedback
fn objective(&self) -> &OF;
@ -66,56 +69,53 @@ where
}
/// Evaluate if an input is interesting using the feedback
pub trait ExecutionProcessor<I, OT, S>
where
OT: ObserversTuple<I, S>,
I: Input,
{
pub trait ExecutionProcessor<OT>: UsesState {
/// Evaluate if a set of observation channels has an interesting state
fn process_execution<EM>(
&mut self,
state: &mut S,
state: &mut Self::State,
manager: &mut EM,
input: I,
input: <Self::State as UsesInput>::Input,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error>
where
EM: EventFirer<I>;
EM: EventFirer<State = Self::State>;
}
/// Evaluate an input modifying the state of the fuzzer
pub trait EvaluatorObservers<I, OT, S>: Sized
where
I: Input,
OT: ObserversTuple<I, S>,
{
pub trait EvaluatorObservers<OT>: UsesState + Sized {
/// Runs the input and triggers observers and feedback,
/// returns if is interesting an (option) the index of the new testcase in the corpus
/// returns if is interesting an (option) the index of the new
/// [`crate::corpus::Testcase`] in the [`crate::corpus::Corpus`]
fn evaluate_input_with_observers<E, EM>(
&mut self,
state: &mut S,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: I,
input: <Self::State as UsesInput>::Input,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error>
where
E: Executor<EM, I, S, Self> + HasObservers<I, OT, S>,
EM: EventManager<E, I, S, Self>;
E: Executor<EM, Self> + HasObservers<Observers = OT, State = Self::State>,
EM: EventFirer<State = Self::State>;
}
/// Evaluate an input modifying the state of the fuzzer
pub trait Evaluator<E, EM, I, S> {
pub trait Evaluator<E, EM>: UsesState
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
{
/// Runs the input and triggers observers and feedback,
/// returns if is interesting an (option) the index of the new testcase in the corpus
/// returns if is interesting an (option) the index of the new [`crate::corpus::Testcase`] in the corpus
fn evaluate_input(
&mut self,
state: &mut S,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: I,
input: <Self::State as UsesInput>::Input,
) -> Result<(ExecuteInputResult, Option<usize>), Error> {
self.evaluate_input_events(state, executor, manager, input, true)
}
@ -125,10 +125,10 @@ pub trait Evaluator<E, EM, I, S> {
/// This version has a boolean to decide if send events to the manager.
fn evaluate_input_events(
&mut self,
state: &mut S,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: I,
input: <Self::State as UsesInput>::Input,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error>;
@ -138,20 +138,20 @@ pub trait Evaluator<E, EM, I, S> {
/// Usually, you want to use [`Evaluator::evaluate_input`], unless you know what you are doing.
fn add_input(
&mut self,
state: &mut S,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: I,
input: <Self::State as UsesInput>::Input,
) -> Result<usize, Error>;
}
/// The main fuzzer trait.
pub trait Fuzzer<E, EM, I, S, ST>
pub trait Fuzzer<E, EM, ST>: Sized + UsesState
where
I: Input,
EM: ProgressReporter<I>,
S: HasExecutions + HasClientPerfMonitor + HasMetadata,
ST: ?Sized,
Self::State: HasClientPerfMonitor + HasMetadata + HasExecutions,
E: UsesState<State = Self::State>,
EM: ProgressReporter<State = Self::State>,
ST: StagesTuple<E, EM, Self::State, Self>,
{
/// Fuzz for a single iteration.
/// Returns the index of the last fuzzed corpus item.
@ -166,7 +166,7 @@ where
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
state: &mut EM::State,
manager: &mut EM,
) -> Result<usize, Error>;
@ -175,7 +175,7 @@ where
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
state: &mut EM::State,
manager: &mut EM,
) -> Result<usize, Error> {
let mut last = current_time();
@ -199,7 +199,7 @@ where
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
state: &mut EM::State,
manager: &mut EM,
iters: u64,
) -> Result<usize, Error> {
@ -240,27 +240,35 @@ pub enum ExecuteInputResult {
/// Your default fuzzer instance, for everyday use.
#[derive(Debug)]
pub struct StdFuzzer<CS, F, I, OF, OT, S>
pub struct StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfMonitor,
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasClientPerfMonitor,
{
scheduler: CS,
feedback: F,
objective: OF,
phantom: PhantomData<(I, OT, S)>,
phantom: PhantomData<OT>,
}
impl<CS, F, I, OF, OT, S> HasScheduler<CS, I, S> for StdFuzzer<CS, F, I, OF, OT, S>
impl<CS, F, OF, OT> UsesState for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfMonitor,
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasClientPerfMonitor,
{
type State = CS::State;
}
impl<CS, F, OF, OT> HasScheduler<CS> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasClientPerfMonitor,
{
fn scheduler(&self) -> &CS {
&self.scheduler
@ -271,13 +279,12 @@ where
}
}
impl<CS, F, I, OF, OT, S> HasFeedback<F, I, S> for StdFuzzer<CS, F, I, OF, OT, S>
impl<CS, F, OF, OT> HasFeedback<F> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfMonitor,
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasClientPerfMonitor,
{
fn feedback(&self) -> &F {
&self.feedback
@ -288,13 +295,12 @@ where
}
}
impl<CS, F, I, OF, OT, S> HasObjective<I, OF, S> for StdFuzzer<CS, F, I, OF, OT, S>
impl<CS, F, OF, OT> HasObjective<OF> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasClientPerfMonitor,
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasClientPerfMonitor,
{
fn objective(&self) -> &OF {
&self.objective
@ -305,27 +311,26 @@ where
}
}
impl<CS, F, I, OF, OT, S> ExecutionProcessor<I, OT, S> for StdFuzzer<CS, F, I, OF, OT, S>
impl<CS, F, OF, OT> ExecutionProcessor<OT> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
OT: ObserversTuple<I, S> + serde::Serialize + serde::de::DeserializeOwned,
S: HasCorpus<I> + HasSolutions<I> + HasClientPerfMonitor + HasExecutions,
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
OT: ObserversTuple<CS::State> + Serialize + DeserializeOwned,
CS::State: HasCorpus + HasSolutions + HasClientPerfMonitor + HasExecutions,
{
/// Evaluate if a set of observation channels has an interesting state
fn process_execution<EM>(
&mut self,
state: &mut S,
state: &mut CS::State,
manager: &mut EM,
input: I,
input: <CS::State as UsesInput>::Input,
observers: &OT,
exit_kind: &ExitKind,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error>
where
EM: EventFirer<I>,
EM: EventFirer<State = Self::State>,
{
let mut res = ExecuteInputResult::None;
@ -378,7 +383,7 @@ where
let observers_buf = if manager.configuration() == EventConfig::AlwaysUnique {
None
} else {
Some(manager.serialize_observers(observers)?)
Some(manager.serialize_observers::<OT>(observers)?)
};
manager.fire(
state,
@ -419,28 +424,27 @@ where
}
}
impl<CS, F, I, OF, OT, S> EvaluatorObservers<I, OT, S> for StdFuzzer<CS, F, I, OF, OT, S>
impl<CS, F, OF, OT> EvaluatorObservers<OT> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler<I, S>,
OT: ObserversTuple<I, S> + serde::Serialize + serde::de::DeserializeOwned,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasCorpus<I> + HasSolutions<I> + HasClientPerfMonitor + HasExecutions,
CS: Scheduler,
OT: ObserversTuple<CS::State> + Serialize + DeserializeOwned,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasCorpus + HasSolutions + HasClientPerfMonitor + HasExecutions,
{
/// Process one input, adding to the respective corpuses if needed and firing the right events
/// Process one input, adding to the respective corpora if needed and firing the right events
#[inline]
fn evaluate_input_with_observers<E, EM>(
&mut self,
state: &mut S,
state: &mut Self::State,
executor: &mut E,
manager: &mut EM,
input: I,
input: <Self::State as UsesInput>::Input,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error>
where
E: Executor<EM, I, S, Self> + HasObservers<I, OT, S>,
EM: EventManager<E, I, S, Self>,
E: Executor<EM, Self> + HasObservers<Observers = OT, State = Self::State>,
EM: EventFirer<State = Self::State>,
{
let exit_kind = self.execute_input(state, executor, manager, &input)?;
let observers = executor.observers();
@ -448,37 +452,36 @@ where
}
}
impl<CS, E, EM, F, I, OF, OT, S> Evaluator<E, EM, I, S> for StdFuzzer<CS, F, I, OF, OT, S>
impl<CS, E, EM, F, OF, OT> Evaluator<E, EM> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler<I, S>,
E: Executor<EM, I, S, Self> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S> + serde::Serialize + serde::de::DeserializeOwned,
EM: EventManager<E, I, S, Self>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasCorpus<I> + HasSolutions<I> + HasClientPerfMonitor + HasExecutions,
CS: Scheduler,
E: HasObservers<State = CS::State, Observers = OT> + Executor<EM, Self>,
EM: EventFirer<State = CS::State>,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
OT: ObserversTuple<CS::State> + Serialize + DeserializeOwned,
CS::State: HasCorpus + HasSolutions + HasClientPerfMonitor + HasExecutions,
{
/// Process one input, adding to the respective corpuses if needed and firing the right events
/// Process one input, adding to the respective corpora if needed and firing the right events
#[inline]
fn evaluate_input_events(
&mut self,
state: &mut S,
state: &mut CS::State,
executor: &mut E,
manager: &mut EM,
input: I,
input: <CS::State as UsesInput>::Input,
send_events: bool,
) -> Result<(ExecuteInputResult, Option<usize>), Error> {
self.evaluate_input_with_observers(state, executor, manager, input, send_events)
}
/// Adds an input, even if it's not conisered `interesting` by any of the executors
/// Adds an input, even if it's not considered `interesting` by any of the executors
fn add_input(
&mut self,
state: &mut S,
state: &mut CS::State,
executor: &mut E,
manager: &mut EM,
input: I,
input: <CS::State as UsesInput>::Input,
) -> Result<usize, Error> {
let exit_kind = self.execute_input(state, executor, manager, &input)?;
let observers = executor.observers();
@ -496,7 +499,7 @@ where
let observers_buf = if manager.configuration() == EventConfig::AlwaysUnique {
None
} else {
Some(manager.serialize_observers(observers)?)
Some(manager.serialize_observers::<OT>(observers)?)
};
manager.fire(
state,
@ -514,21 +517,21 @@ where
}
}
impl<CS, E, EM, F, I, OF, OT, S, ST> Fuzzer<E, EM, I, S, ST> for StdFuzzer<CS, F, I, OF, OT, S>
impl<CS, E, EM, F, OF, OT, ST> Fuzzer<E, EM, ST> for StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler<I, S>,
EM: EventManager<E, I, S, Self>,
F: Feedback<I, S>,
I: Input,
S: HasClientPerfMonitor + HasExecutions + HasMetadata,
OF: Feedback<I, S>,
ST: StagesTuple<E, EM, S, Self> + ?Sized,
CS: Scheduler,
E: UsesState<State = CS::State>,
EM: ProgressReporter + EventProcessor<E, Self, State = CS::State>,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: HasClientPerfMonitor + HasExecutions + HasMetadata,
ST: StagesTuple<E, EM, CS::State, Self>,
{
fn fuzz_one(
&mut self,
stages: &mut ST,
executor: &mut E,
state: &mut S,
state: &mut CS::State,
manager: &mut EM,
) -> Result<usize, Error> {
// Init timer for scheduler
@ -564,13 +567,12 @@ where
}
}
impl<CS, F, I, OF, OT, S> StdFuzzer<CS, F, I, OF, OT, S>
impl<CS, F, OF, OT> StdFuzzer<CS, F, OF, OT>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OF: Feedback<I, S>,
S: HasExecutions + HasClientPerfMonitor,
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
CS::State: UsesInput + HasExecutions + HasClientPerfMonitor,
{
/// Create a new `StdFuzzer` with standard behavior.
pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self {
@ -585,14 +587,15 @@ where
/// Runs the input and triggers observers and feedback
pub fn execute_input<E, EM>(
&mut self,
state: &mut S,
state: &mut CS::State,
executor: &mut E,
event_mgr: &mut EM,
input: &I,
input: &<CS::State as UsesInput>::Input,
) -> Result<ExitKind, Error>
where
E: Executor<EM, I, S, Self> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
E: Executor<EM, Self> + HasObservers<Observers = OT, State = CS::State>,
EM: UsesState<State = CS::State>,
OT: ObserversTuple<CS::State>,
{
start_timer!(state);
executor.observers_mut().pre_exec_all(state, input)?;
@ -614,46 +617,39 @@ where
}
}
/// Structs with this trait will execute an [`Input`]
pub trait ExecutesInput<I, OT, S, Z>
/// Structs with this trait will execute an input
pub trait ExecutesInput<E, EM>: UsesState
where
I: Input,
OT: ObserversTuple<I, S>,
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
{
/// Runs the input and triggers observers and feedback
fn execute_input<E, EM>(
fn execute_input(
&mut self,
state: &mut S,
state: &mut Self::State,
executor: &mut E,
event_mgr: &mut EM,
input: &I,
) -> Result<ExitKind, Error>
where
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>;
input: &<Self::State as UsesInput>::Input,
) -> Result<ExitKind, Error>;
}
impl<CS, F, I, OF, OT, S> ExecutesInput<I, OT, S, Self> for StdFuzzer<CS, F, I, OF, OT, S>
impl<CS, E, EM, F, OF> ExecutesInput<E, EM> for StdFuzzer<CS, F, OF, E::Observers>
where
CS: Scheduler<I, S>,
F: Feedback<I, S>,
I: Input,
OT: ObserversTuple<I, S>,
OF: Feedback<I, S>,
S: HasExecutions + HasClientPerfMonitor,
CS: Scheduler,
F: Feedback<CS::State>,
OF: Feedback<CS::State>,
E: Executor<EM, Self> + HasObservers<State = CS::State>,
EM: UsesState<State = CS::State>,
CS::State: UsesInput + HasExecutions + HasClientPerfMonitor,
{
/// Runs the input and triggers observers and feedback
fn execute_input<E, EM>(
fn execute_input(
&mut self,
state: &mut S,
state: &mut CS::State,
executor: &mut E,
event_mgr: &mut EM,
input: &I,
) -> Result<ExitKind, Error>
where
E: Executor<EM, I, S, Self> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
{
input: &<CS::State as UsesInput>::Input,
) -> Result<ExitKind, Error> {
start_timer!(state);
executor.observers_mut().pre_exec_all(state, input)?;
mark_feature_time!(state, PerfFeature::PreExecObservers);
@ -674,6 +670,48 @@ where
}
}
#[cfg(test)]
#[derive(Clone, Debug, Default)]
pub(crate) struct NopFuzzer<I> {
phantom: PhantomData<I>,
}
#[cfg(test)]
impl<I> NopFuzzer<I> {
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
}
}
#[cfg(test)]
impl<I> UsesState for NopFuzzer<I>
where
I: Input,
{
type State = NopState<I>;
}
#[cfg(test)]
impl<ST, E, I, EM> Fuzzer<E, EM, ST> for NopFuzzer<I>
where
E: UsesState<State = NopState<I>>,
EM: ProgressReporter<State = NopState<I>>,
I: Input,
ST: StagesTuple<E, EM, NopState<I>, Self>,
{
fn fuzz_one(
&mut self,
_stages: &mut ST,
_executor: &mut E,
_state: &mut EM::State,
_manager: &mut EM,
) -> Result<usize, Error> {
unimplemented!()
}
}
#[cfg(feature = "python")]
#[allow(missing_docs)]
/// `Fuzzer` Python bindings
@ -697,12 +735,10 @@ pub mod pybind {
/// `StdFuzzer` with fixed generics
pub type PythonStdFuzzer = StdFuzzer<
QueueScheduler,
QueueScheduler<PythonStdState>,
PythonFeedback,
BytesInput,
PythonFeedback,
PythonObserversTuple,
PythonStdState,
>;
/// Python class for StdFuzzer

View File

@ -109,3 +109,10 @@ pub trait HasBytesVec {
/// The internal bytes map (as mutable borrow)
fn bytes_mut(&mut self) -> &mut Vec<u8>;
}
/// Defines the input type shared across traits of the type.
/// Needed for consistency across HasCorpus/HasSolutions and friends.
pub trait UsesInput {
/// Type which will be used throughout this state.
type Input: Input;
}

View File

@ -453,19 +453,21 @@ pub mod prelude {
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
#[cfg(feature = "std")]
use crate::events::SimpleEventManager;
use crate::{
bolts::{rands::StdRand, tuples::tuple_list},
corpus::{Corpus, InMemoryCorpus, Testcase},
events::NopEventManager,
executors::{ExitKind, InProcessExecutor},
feedbacks::ConstFeedback,
fuzzer::Fuzzer,
inputs::BytesInput,
monitors::SimpleMonitor,
mutators::{mutations::BitFlipMutator, StdScheduledMutator},
schedulers::RandScheduler,
stages::StdMutationalStage,
state::{HasCorpus, StdState},
Fuzzer, StdFuzzer,
StdFuzzer,
};
#[test]
@ -474,25 +476,31 @@ mod tests {
let rand = StdRand::with_seed(0);
let mut corpus = InMemoryCorpus::<BytesInput>::new();
let testcase = Testcase::new(vec![0; 4]);
let testcase = Testcase::new(vec![0; 4].into());
corpus.add(testcase).unwrap();
let mut feedback = ConstFeedback::new(false);
let mut objective = ConstFeedback::new(false);
let mut state = StdState::new(
rand,
corpus,
InMemoryCorpus::<BytesInput>::new(),
&mut (),
&mut (),
&mut feedback,
&mut objective,
)
.unwrap();
let monitor = SimpleMonitor::new(|s| {
let _monitor = SimpleMonitor::new(|s| {
println!("{s}");
});
let mut event_manager = SimpleEventManager::new(monitor);
let mut event_manager = NopEventManager::new();
let feedback = ConstFeedback::new(false);
let objective = ConstFeedback::new(false);
let scheduler = RandScheduler::new();
let mut fuzzer = StdFuzzer::new(scheduler, (), ());
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let mut harness = |_buf: &BytesInput| ExitKind::Ok;
let mut executor = InProcessExecutor::new(
@ -515,8 +523,8 @@ mod tests {
let state_serialized = postcard::to_allocvec(&state).unwrap();
let state_deserialized: StdState<
_,
InMemoryCorpus<BytesInput>,
BytesInput,
StdRand,
InMemoryCorpus<BytesInput>,
> = postcard::from_bytes(state_serialized.as_slice()).unwrap();

View File

@ -277,6 +277,67 @@ impl Default for NopMonitor {
}
}
#[cfg(feature = "std")]
/// Tracking monitor during fuzzing that just prints to `stdout`.
#[derive(Debug, Clone, Default)]
pub struct SimplePrintingMonitor {
start_time: Duration,
client_stats: Vec<ClientStats>,
}
#[cfg(feature = "std")]
impl SimplePrintingMonitor {
/// Create a new [`SimplePrintingMonitor`]
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[cfg(feature = "std")]
impl Monitor for SimplePrintingMonitor {
/// the client monitor, mutable
fn client_stats_mut(&mut self) -> &mut Vec<ClientStats> {
&mut self.client_stats
}
/// the client monitor
fn client_stats(&self) -> &[ClientStats] {
&self.client_stats
}
/// Time this fuzzing run stated
fn start_time(&mut self) -> Duration {
self.start_time
}
fn display(&mut self, event_msg: String, sender_id: u32) {
println!(
"[{} #{}] run time: {}, clients: {}, corpus: {}, objectives: {}, executions: {}, exec/sec: {}",
event_msg,
sender_id,
format_duration_hms(&(current_time() - self.start_time)),
self.client_stats().len(),
self.corpus_size(),
self.objective_size(),
self.total_execs(),
self.execs_per_sec()
);
// Only print perf monitor if the feature is enabled
#[cfg(feature = "introspection")]
{
// Print the client performance monitor.
println!(
"Client {:03}:\n{}",
sender_id, self.client_stats[sender_id as usize].introspection_monitor
);
// Separate the spacing just a bit
println!();
}
}
}
/// Tracking monitor during fuzzing.
#[derive(Clone)]
pub struct SimpleMonitor<F>

View File

@ -9,7 +9,7 @@ use crate::{
tuples::{tuple_list, tuple_list_type},
},
corpus::Corpus,
inputs::EncodedInput,
inputs::{EncodedInput, UsesInput},
mutators::{
mutations::{buffer_copy, buffer_self_copy, ARITH_MAX},
MutationResult, Mutator, Named,
@ -22,7 +22,7 @@ use crate::{
#[derive(Debug, Default)]
pub struct EncodedRandMutator;
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedRandMutator {
impl<S: HasRand + UsesInput<Input = EncodedInput>> Mutator<S> for EncodedRandMutator {
fn mutate(
&mut self,
state: &mut S,
@ -57,7 +57,7 @@ impl EncodedRandMutator {
#[derive(Debug, Default)]
pub struct EncodedIncMutator;
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedIncMutator {
impl<S: HasRand + UsesInput<Input = EncodedInput>> Mutator<S> for EncodedIncMutator {
fn mutate(
&mut self,
state: &mut S,
@ -92,7 +92,7 @@ impl EncodedIncMutator {
#[derive(Debug, Default)]
pub struct EncodedDecMutator;
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedDecMutator {
impl<S: HasRand + UsesInput<Input = EncodedInput>> Mutator<S> for EncodedDecMutator {
fn mutate(
&mut self,
state: &mut S,
@ -127,7 +127,7 @@ impl EncodedDecMutator {
#[derive(Debug, Default)]
pub struct EncodedAddMutator;
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedAddMutator {
impl<S: HasRand + UsesInput<Input = EncodedInput>> Mutator<S> for EncodedAddMutator {
fn mutate(
&mut self,
state: &mut S,
@ -166,7 +166,7 @@ impl EncodedAddMutator {
#[derive(Debug, Default)]
pub struct EncodedDeleteMutator;
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedDeleteMutator {
impl<S: HasRand + UsesInput<Input = EncodedInput>> Mutator<S> for EncodedDeleteMutator {
fn mutate(
&mut self,
state: &mut S,
@ -206,9 +206,9 @@ pub struct EncodedInsertCopyMutator {
tmp_buf: Vec<u32>,
}
impl<S> Mutator<EncodedInput, S> for EncodedInsertCopyMutator
impl<S> Mutator<S> for EncodedInsertCopyMutator
where
S: HasRand + HasMaxSize,
S: UsesInput<Input = EncodedInput> + HasRand + HasMaxSize,
{
fn mutate(
&mut self,
@ -267,7 +267,7 @@ impl EncodedInsertCopyMutator {
#[derive(Debug, Default)]
pub struct EncodedCopyMutator;
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedCopyMutator {
impl<S: UsesInput<Input = EncodedInput> + HasRand> Mutator<S> for EncodedCopyMutator {
fn mutate(
&mut self,
state: &mut S,
@ -307,9 +307,9 @@ impl EncodedCopyMutator {
#[derive(Debug, Default)]
pub struct EncodedCrossoverInsertMutator;
impl<S> Mutator<EncodedInput, S> for EncodedCrossoverInsertMutator
impl<S> Mutator<S> for EncodedCrossoverInsertMutator
where
S: HasRand + HasCorpus<EncodedInput> + HasMaxSize,
S: UsesInput<Input = EncodedInput> + HasRand + HasCorpus + HasMaxSize,
{
fn mutate(
&mut self,
@ -381,9 +381,9 @@ impl EncodedCrossoverInsertMutator {
#[derive(Debug, Default)]
pub struct EncodedCrossoverReplaceMutator;
impl<S> Mutator<EncodedInput, S> for EncodedCrossoverReplaceMutator
impl<S> Mutator<S> for EncodedCrossoverReplaceMutator
where
S: HasRand + HasCorpus<EncodedInput>,
S: UsesInput<Input = EncodedInput> + HasRand + HasCorpus,
{
fn mutate(
&mut self,

View File

@ -10,7 +10,7 @@ use crate::{
bolts::{rands::Rand, tuples::Named},
corpus::Corpus,
generators::GramatronGenerator,
inputs::{GramatronInput, Terminal},
inputs::{GramatronInput, Terminal, UsesInput},
mutators::{MutationResult, Mutator},
state::{HasCorpus, HasMetadata, HasRand},
Error,
@ -27,9 +27,9 @@ where
generator: &'a GramatronGenerator<'a, S>,
}
impl<'a, S> Mutator<GramatronInput, S> for GramatronRandomMutator<'a, S>
impl<'a, S> Mutator<S> for GramatronRandomMutator<'a, S>
where
S: HasRand + HasMetadata,
S: UsesInput<Input = GramatronInput> + HasRand + HasMetadata,
{
fn mutate(
&mut self,
@ -96,9 +96,9 @@ impl GramatronIdxMapMetadata {
#[derive(Default, Debug)]
pub struct GramatronSpliceMutator;
impl<S> Mutator<GramatronInput, S> for GramatronSpliceMutator
impl<S> Mutator<S> for GramatronSpliceMutator
where
S: HasRand + HasCorpus<GramatronInput> + HasMetadata,
S: UsesInput<Input = GramatronInput> + HasRand + HasCorpus + HasMetadata,
{
fn mutate(
&mut self,
@ -169,9 +169,9 @@ pub struct GramatronRecursionMutator {
feature: Vec<Terminal>,
}
impl<S> Mutator<GramatronInput, S> for GramatronRecursionMutator
impl<S> Mutator<S> for GramatronRecursionMutator
where
S: HasRand + HasMetadata,
S: UsesInput<Input = GramatronInput> + HasRand + HasMetadata,
{
fn mutate(
&mut self,

View File

@ -7,7 +7,7 @@ use core::cmp::{max, min};
use crate::{
bolts::{rands::Rand, tuples::Named},
corpus::Corpus,
inputs::{GeneralizedInput, GeneralizedItem},
inputs::{GeneralizedInput, GeneralizedItem, UsesInput},
mutators::{token_mutations::Tokens, MutationResult, Mutator},
stages::generalization::GeneralizedIndexesMetadata,
state::{HasCorpus, HasMetadata, HasRand},
@ -24,7 +24,7 @@ fn extend_with_random_generalized<S>(
gap_indices: &mut Vec<usize>,
) -> Result<(), Error>
where
S: HasMetadata + HasRand + HasCorpus<GeneralizedInput>,
S: HasMetadata + HasRand + HasCorpus<Input = GeneralizedInput>,
{
let rand_idx = state.rand_mut().next() as usize;
@ -128,9 +128,9 @@ pub struct GrimoireExtensionMutator {
gap_indices: Vec<usize>,
}
impl<S> Mutator<GeneralizedInput, S> for GrimoireExtensionMutator
impl<S> Mutator<S> for GrimoireExtensionMutator
where
S: HasMetadata + HasRand + HasCorpus<GeneralizedInput>,
S: UsesInput<Input = GeneralizedInput> + HasMetadata + HasRand + HasCorpus,
{
fn mutate(
&mut self,
@ -176,9 +176,9 @@ pub struct GrimoireRecursiveReplacementMutator {
gap_indices: Vec<usize>,
}
impl<S> Mutator<GeneralizedInput, S> for GrimoireRecursiveReplacementMutator
impl<S> Mutator<S> for GrimoireRecursiveReplacementMutator
where
S: HasMetadata + HasRand + HasCorpus<GeneralizedInput>,
S: UsesInput<Input = GeneralizedInput> + HasMetadata + HasRand + HasCorpus,
{
fn mutate(
&mut self,
@ -247,9 +247,9 @@ impl GrimoireRecursiveReplacementMutator {
#[derive(Debug, Default)]
pub struct GrimoireStringReplacementMutator {}
impl<S> Mutator<GeneralizedInput, S> for GrimoireStringReplacementMutator
impl<S> Mutator<S> for GrimoireStringReplacementMutator
where
S: HasMetadata + HasRand,
S: UsesInput<Input = GeneralizedInput> + HasMetadata + HasRand,
{
fn mutate(
&mut self,
@ -355,9 +355,9 @@ pub struct GrimoireRandomDeleteMutator {
gap_indices: Vec<usize>,
}
impl<S> Mutator<GeneralizedInput, S> for GrimoireRandomDeleteMutator
impl<S> Mutator<S> for GrimoireRandomDeleteMutator
where
S: HasMetadata + HasRand + HasCorpus<GeneralizedInput>,
S: UsesInput<Input = GeneralizedInput> + HasMetadata + HasRand + HasCorpus,
{
fn mutate(
&mut self,

View File

@ -22,7 +22,7 @@ pub use nautilus::*;
use crate::{
bolts::tuples::{HasConstLen, Named},
inputs::Input,
inputs::UsesInput,
Error,
};
@ -42,15 +42,15 @@ pub enum MutationResult {
/// A mutator takes input, and mutates it.
/// Simple as that.
pub trait Mutator<I, S>
pub trait Mutator<S>
where
I: Input,
S: UsesInput,
{
/// Mutate a given input
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error>;
@ -66,15 +66,15 @@ where
}
/// A `Tuple` of `Mutators` that can execute multiple `Mutators` in a row.
pub trait MutatorsTuple<I, S>: HasConstLen
pub trait MutatorsTuple<S>: HasConstLen
where
I: Input,
S: UsesInput,
{
/// Runs the `mutate` function on all `Mutators` in this `Tuple`.
fn mutate_all(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error>;
@ -91,7 +91,7 @@ where
&mut self,
index: usize,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error>;
@ -105,14 +105,14 @@ where
) -> Result<(), Error>;
}
impl<I, S> MutatorsTuple<I, S> for ()
impl<S> MutatorsTuple<S> for ()
where
I: Input,
S: UsesInput,
{
fn mutate_all(
&mut self,
_state: &mut S,
_input: &mut I,
_input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
Ok(MutationResult::Skipped)
@ -131,7 +131,7 @@ where
&mut self,
_index: usize,
_state: &mut S,
_input: &mut I,
_input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
Ok(MutationResult::Skipped)
@ -148,16 +148,16 @@ where
}
}
impl<Head, Tail, I, S> MutatorsTuple<I, S> for (Head, Tail)
impl<Head, Tail, S> MutatorsTuple<S> for (Head, Tail)
where
Head: Mutator<I, S> + Named,
Tail: MutatorsTuple<I, S>,
I: Input,
Head: Mutator<S> + Named,
Tail: MutatorsTuple<S>,
S: UsesInput,
{
fn mutate_all(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let r = self.0.mutate(state, input, stage_idx)?;
@ -182,7 +182,7 @@ where
&mut self,
index: usize,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error> {
if index == 0 {
@ -234,7 +234,7 @@ pub mod pybind {
}
}
impl Mutator<BytesInput, PythonStdState> for PyObjectMutator {
impl Mutator<PythonStdState> for PyObjectMutator {
fn mutate(
&mut self,
state: &mut PythonStdState,
@ -329,7 +329,7 @@ pub mod pybind {
}
}
impl Mutator<BytesInput, PythonStdState> for PythonMutator {
impl Mutator<PythonStdState> for PythonMutator {
fn mutate(
&mut self,
state: &mut PythonStdState,

View File

@ -13,7 +13,6 @@ use crate::{
rands::{Rand, StdRand},
},
corpus::Corpus,
inputs::Input,
mutators::{ComposedByMutations, MutationResult, Mutator, MutatorsTuple, ScheduledMutator},
state::{HasCorpus, HasMetadata, HasRand, HasSolutions},
Error,
@ -363,46 +362,43 @@ pub enum MOptMode {
/// This is the main struct of `MOpt`, an `AFL` mutator.
/// See the original `MOpt` implementation in <https://github.com/puppet-meteor/MOpt-AFL>
pub struct StdMOptMutator<I, MT, S>
pub struct StdMOptMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
MT: MutatorsTuple<S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
mode: MOptMode,
finds_before: usize,
mutations: MT,
max_stack_pow: u64,
phantom: PhantomData<(I, S)>,
phantom: PhantomData<S>,
}
impl<I, MT, S> Debug for StdMOptMutator<I, MT, S>
impl<MT, S> Debug for StdMOptMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
MT: MutatorsTuple<S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"StdMOptMutator with {} mutations for Input type {}",
self.mutations.len(),
core::any::type_name::<I>()
core::any::type_name::<S::Input>()
)
}
}
impl<I, MT, S> Mutator<I, S> for StdMOptMutator<I, MT, S>
impl<MT, S> Mutator<S> for StdMOptMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
MT: MutatorsTuple<S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
#[inline]
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error> {
self.finds_before = state.corpus().count() + state.solutions().count();
@ -528,11 +524,10 @@ where
}
}
impl<I, MT, S> StdMOptMutator<I, MT, S>
impl<MT, S> StdMOptMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
MT: MutatorsTuple<S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
/// Create a new [`StdMOptMutator`].
pub fn new(
@ -555,7 +550,7 @@ where
fn core_mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mut r = MutationResult::Skipped;
@ -585,7 +580,7 @@ where
fn pilot_mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mut r = MutationResult::Skipped;
@ -620,11 +615,10 @@ where
}
}
impl<I, MT, S> ComposedByMutations<I, MT, S> for StdMOptMutator<I, MT, S>
impl<MT, S> ComposedByMutations<MT, S> for StdMOptMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
MT: MutatorsTuple<S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
/// Get the mutations
#[inline]
@ -639,19 +633,18 @@ where
}
}
impl<I, MT, S> ScheduledMutator<I, MT, S> for StdMOptMutator<I, MT, S>
impl<MT, S> ScheduledMutator<MT, S> for StdMOptMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
MT: MutatorsTuple<S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &I) -> u64 {
fn iterations(&self, state: &mut S, _: &S::Input) -> u64 {
1 << (1 + state.rand_mut().below(self.max_stack_pow))
}
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &I) -> usize {
fn schedule(&self, state: &mut S, _: &S::Input) -> usize {
state
.metadata_mut()
.get_mut::<MOpt>()
@ -663,7 +656,7 @@ where
fn scheduled_mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mode = self.mode;

View File

@ -9,7 +9,7 @@ use core::{
use crate::{
bolts::{rands::Rand, tuples::Named},
corpus::Corpus,
inputs::{HasBytesVec, Input},
inputs::{HasBytesVec, UsesInput},
mutators::{MutationResult, Mutator},
state::{HasCorpus, HasMaxSize, HasRand},
Error,
@ -100,15 +100,15 @@ pub const INTERESTING_32: [i32; 27] = [
#[derive(Default, Debug)]
pub struct BitFlipMutator;
impl<I, S> Mutator<I, S> for BitFlipMutator
impl<S> Mutator<S> for BitFlipMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut <S as UsesInput>::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -140,15 +140,15 @@ impl BitFlipMutator {
#[derive(Default, Debug)]
pub struct ByteFlipMutator;
impl<I, S> Mutator<I, S> for ByteFlipMutator
impl<S> Mutator<S> for ByteFlipMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -178,15 +178,15 @@ impl ByteFlipMutator {
#[derive(Default, Debug)]
pub struct ByteIncMutator;
impl<I, S> Mutator<I, S> for ByteIncMutator
impl<S> Mutator<S> for ByteIncMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -217,15 +217,15 @@ impl ByteIncMutator {
#[derive(Default, Debug)]
pub struct ByteDecMutator;
impl<I, S> Mutator<I, S> for ByteDecMutator
impl<S> Mutator<S> for ByteDecMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -256,15 +256,15 @@ impl ByteDecMutator {
#[derive(Default, Debug)]
pub struct ByteNegMutator;
impl<I, S> Mutator<I, S> for ByteNegMutator
impl<S> Mutator<S> for ByteNegMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -295,15 +295,15 @@ impl ByteNegMutator {
#[derive(Default, Debug)]
pub struct ByteRandMutator;
impl<I, S> Mutator<I, S> for ByteRandMutator
impl<S> Mutator<S> for ByteRandMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -339,15 +339,15 @@ macro_rules! add_mutator_impl {
pub struct $name;
#[allow(trivial_numeric_casts)]
impl<I, S> Mutator<I, S> for $name
impl<S> Mutator<S> for $name
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().len() < size_of::<$size>() {
@ -405,16 +405,16 @@ macro_rules! interesting_mutator_impl {
#[derive(Default, Debug)]
pub struct $name;
impl<I, S> Mutator<I, S> for $name
impl<S> Mutator<S> for $name
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
#[allow(clippy::cast_sign_loss)]
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().len() < size_of::<$size>() {
@ -458,15 +458,15 @@ interesting_mutator_impl!(DwordInterestingMutator, u32, INTERESTING_32);
#[derive(Default, Debug)]
pub struct BytesDeleteMutator;
impl<I, S> Mutator<I, S> for BytesDeleteMutator
impl<S> Mutator<S> for BytesDeleteMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -500,15 +500,15 @@ impl BytesDeleteMutator {
#[derive(Default, Debug)]
pub struct BytesExpandMutator;
impl<I, S> Mutator<I, S> for BytesExpandMutator
impl<S> Mutator<S> for BytesExpandMutator
where
I: Input + HasBytesVec,
S: HasRand + HasMaxSize,
S: UsesInput + HasRand + HasMaxSize,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -549,15 +549,15 @@ impl BytesExpandMutator {
#[derive(Default, Debug)]
pub struct BytesInsertMutator;
impl<I, S> Mutator<I, S> for BytesInsertMutator
impl<S> Mutator<S> for BytesInsertMutator
where
I: Input + HasBytesVec,
S: HasRand + HasMaxSize,
S: UsesInput + HasRand + HasMaxSize,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -604,15 +604,15 @@ impl BytesInsertMutator {
#[derive(Default, Debug)]
pub struct BytesRandInsertMutator;
impl<I, S> Mutator<I, S> for BytesRandInsertMutator
impl<S> Mutator<S> for BytesRandInsertMutator
where
I: Input + HasBytesVec,
S: HasRand + HasMaxSize,
S: UsesInput + HasRand + HasMaxSize,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -656,15 +656,15 @@ impl BytesRandInsertMutator {
#[derive(Default, Debug)]
pub struct BytesSetMutator;
impl<I, S> Mutator<I, S> for BytesSetMutator
impl<S> Mutator<S> for BytesSetMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -700,15 +700,15 @@ impl BytesSetMutator {
#[derive(Default, Debug)]
pub struct BytesRandSetMutator;
impl<I, S> Mutator<I, S> for BytesRandSetMutator
impl<S> Mutator<S> for BytesRandSetMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -744,15 +744,15 @@ impl BytesRandSetMutator {
#[derive(Default, Debug)]
pub struct BytesCopyMutator;
impl<I, S> Mutator<I, S> for BytesCopyMutator
impl<S> Mutator<S> for BytesCopyMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -790,15 +790,15 @@ pub struct BytesInsertCopyMutator {
tmp_buf: Vec<u8>,
}
impl<I, S> Mutator<I, S> for BytesInsertCopyMutator
impl<S> Mutator<S> for BytesInsertCopyMutator
where
I: Input + HasBytesVec,
S: HasRand + HasMaxSize,
S: UsesInput + HasRand + HasMaxSize,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -852,15 +852,15 @@ impl BytesInsertCopyMutator {
#[derive(Debug, Default)]
pub struct BytesSwapMutator;
impl<I, S> Mutator<I, S> for BytesSwapMutator
impl<S> Mutator<S> for BytesSwapMutator
where
I: Input + HasBytesVec,
S: HasRand,
S: UsesInput + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -898,15 +898,15 @@ impl BytesSwapMutator {
#[derive(Debug, Default)]
pub struct CrossoverInsertMutator;
impl<I, S> Mutator<I, S> for CrossoverInsertMutator
impl<S> Mutator<S> for CrossoverInsertMutator
where
I: Input + HasBytesVec,
S: HasRand + HasCorpus<I> + HasMaxSize,
S: HasCorpus + HasRand + HasMaxSize,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -973,15 +973,15 @@ impl CrossoverInsertMutator {
#[derive(Debug, Default)]
pub struct CrossoverReplaceMutator;
impl<I, S> Mutator<I, S> for CrossoverReplaceMutator
impl<S> Mutator<S> for CrossoverReplaceMutator
where
I: Input + HasBytesVec,
S: HasRand + HasCorpus<I>,
S: HasCorpus + HasRand,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -1056,16 +1056,16 @@ fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) {
#[derive(Debug, Default)]
pub struct SpliceMutator;
impl<I, S> Mutator<I, S> for SpliceMutator
impl<S> Mutator<S> for SpliceMutator
where
I: Input + HasBytesVec,
S: HasRand + HasCorpus<I>,
S: HasCorpus + HasRand,
S::Input: HasBytesVec,
{
#[allow(clippy::cast_sign_loss)]
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
// We don't want to use the testcase we're already using for splicing
@ -1173,15 +1173,16 @@ mod tests {
tuples::{tuple_list, HasConstLen},
},
corpus::{Corpus, InMemoryCorpus},
feedbacks::ConstFeedback,
inputs::BytesInput,
mutators::MutatorsTuple,
state::{HasMetadata, StdState},
};
fn test_mutations<I, S>() -> impl MutatorsTuple<I, S>
fn test_mutations<S>() -> impl MutatorsTuple<S>
where
I: Input + HasBytesVec,
S: HasRand + HasCorpus<I> + HasMetadata + HasMaxSize,
S: HasRand + HasCorpus + HasMetadata + HasMaxSize,
S::Input: HasBytesVec,
{
tuple_list!(
BitFlipMutator::new(),
@ -1226,12 +1227,21 @@ mod tests {
let rand = StdRand::with_seed(1337);
let mut corpus = InMemoryCorpus::new();
let mut feedback = ConstFeedback::new(false);
let mut objective = ConstFeedback::new(false);
corpus
.add(BytesInput::new(vec![0x42; 0x1337]).into())
.unwrap();
let mut state =
StdState::new(rand, corpus, InMemoryCorpus::new(), &mut (), &mut ()).unwrap();
let mut state = StdState::new(
rand,
corpus,
InMemoryCorpus::new(),
&mut feedback,
&mut objective,
)
.unwrap();
let mut mutations = test_mutations();
for _ in 0..2 {

View File

@ -14,6 +14,7 @@ use crate::{
generators::nautilus::NautilusContext,
inputs::nautilus::NautilusInput,
mutators::{MutationResult, Mutator},
prelude::UsesInput,
state::{HasCorpus, HasMetadata},
Error,
};
@ -30,7 +31,10 @@ impl Debug for NautilusRandomMutator<'_> {
}
}
impl<S> Mutator<NautilusInput, S> for NautilusRandomMutator<'_> {
impl<S> Mutator<S> for NautilusRandomMutator<'_>
where
S: UsesInput<Input = NautilusInput>,
{
fn mutate(
&mut self,
_state: &mut S,
@ -91,7 +95,10 @@ impl Debug for NautilusRecursionMutator<'_> {
}
}
impl<S> Mutator<NautilusInput, S> for NautilusRecursionMutator<'_> {
impl<S> Mutator<S> for NautilusRecursionMutator<'_>
where
S: UsesInput<Input = NautilusInput>,
{
fn mutate(
&mut self,
_state: &mut S,
@ -154,9 +161,9 @@ impl Debug for NautilusSpliceMutator<'_> {
}
}
impl<S> Mutator<NautilusInput, S> for NautilusSpliceMutator<'_>
impl<S> Mutator<S> for NautilusSpliceMutator<'_>
where
S: HasCorpus<NautilusInput> + HasMetadata,
S: HasCorpus + HasMetadata + UsesInput<Input = NautilusInput>,
{
fn mutate(
&mut self,

View File

@ -16,9 +16,9 @@ use crate::{
AsMutSlice, AsSlice,
},
corpus::Corpus,
inputs::Input,
inputs::UsesInput,
mutators::{MutationResult, Mutator, MutatorsTuple},
state::{HasCorpus, HasMetadata, HasRand},
state::{HasCorpus, HasMetadata, HasRand, State},
Error,
};
@ -53,10 +53,10 @@ impl LogMutationMetadata {
}
/// A [`Mutator`] that composes multiple mutations into one.
pub trait ComposedByMutations<I, MT, S>
pub trait ComposedByMutations<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
MT: MutatorsTuple<S>,
S: UsesInput,
{
/// Get the mutations
fn mutations(&self) -> &MT;
@ -66,23 +66,23 @@ where
}
/// A [`Mutator`] scheduling multiple [`Mutator`]s for an input.
pub trait ScheduledMutator<I, MT, S>: ComposedByMutations<I, MT, S> + Mutator<I, S>
pub trait ScheduledMutator<MT, S>: ComposedByMutations<MT, S> + Mutator<S>
where
I: Input,
MT: MutatorsTuple<I, S>,
MT: MutatorsTuple<S>,
S: UsesInput,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, input: &I) -> u64;
fn iterations(&self, state: &mut S, input: &S::Input) -> u64;
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, input: &I) -> usize;
fn schedule(&self, state: &mut S, input: &S::Input) -> usize;
/// New default implementation for mutate.
/// Implementations must forward mutate() to this method
fn scheduled_mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mut r = MutationResult::Skipped;
@ -101,55 +101,51 @@ where
}
/// A [`Mutator`] that schedules one of the embedded mutations on each call.
pub struct StdScheduledMutator<I, MT, S>
pub struct StdScheduledMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand,
MT: MutatorsTuple<S>,
S: State + HasRand,
{
mutations: MT,
max_stack_pow: u64,
phantom: PhantomData<(I, S)>,
phantom: PhantomData<S>,
}
impl<I, MT, S> Debug for StdScheduledMutator<I, MT, S>
impl<MT, S> Debug for StdScheduledMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand,
MT: MutatorsTuple<S>,
S: State + HasRand,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"StdScheduledMutator with {} mutations for Input type {}",
self.mutations.len(),
core::any::type_name::<I>()
core::any::type_name::<S::Input>()
)
}
}
impl<I, MT, S> Mutator<I, S> for StdScheduledMutator<I, MT, S>
impl<MT, S> Mutator<S> for StdScheduledMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand,
MT: MutatorsTuple<S>,
S: State + HasRand,
{
#[inline]
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
stage_idx: i32,
) -> Result<MutationResult, Error> {
self.scheduled_mutate(state, input, stage_idx)
}
}
impl<I, MT, S> ComposedByMutations<I, MT, S> for StdScheduledMutator<I, MT, S>
impl<MT, S> ComposedByMutations<MT, S> for StdScheduledMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand,
MT: MutatorsTuple<S>,
S: State + HasRand,
{
/// Get the mutations
#[inline]
@ -164,29 +160,27 @@ where
}
}
impl<I, MT, S> ScheduledMutator<I, MT, S> for StdScheduledMutator<I, MT, S>
impl<MT, S> ScheduledMutator<MT, S> for StdScheduledMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand,
MT: MutatorsTuple<S>,
S: State + HasRand,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &I) -> u64 {
fn iterations(&self, state: &mut S, _: &S::Input) -> u64 {
1 << (1 + state.rand_mut().below(self.max_stack_pow))
}
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &I) -> usize {
fn schedule(&self, state: &mut S, _: &S::Input) -> usize {
debug_assert!(!self.mutations().is_empty());
state.rand_mut().below(self.mutations().len() as u64) as usize
}
}
impl<I, MT, S> StdScheduledMutator<I, MT, S>
impl<MT, S> StdScheduledMutator<MT, S>
where
I: Input,
MT: MutatorsTuple<I, S>,
S: HasRand,
MT: MutatorsTuple<S>,
S: State + HasRand,
{
/// Create a new [`StdScheduledMutator`] instance specifying mutations
pub fn new(mutations: MT) -> Self {
@ -279,46 +273,43 @@ pub fn tokens_mutations() -> tuple_list_type!(TokenInsert, TokenReplace) {
}
/// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`].
pub struct LoggerScheduledMutator<I, MT, S, SM>
pub struct LoggerScheduledMutator<MT, S, SM>
where
I: Input,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus<I>,
SM: ScheduledMutator<I, MT, S>,
MT: MutatorsTuple<S> + NamedTuple,
S: UsesInput + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
scheduled: SM,
mutation_log: Vec<usize>,
phantom: PhantomData<(I, MT, S)>,
phantom: PhantomData<(MT, S)>,
}
impl<I, MT, S, SM> Debug for LoggerScheduledMutator<I, MT, S, SM>
impl<MT, S, SM> Debug for LoggerScheduledMutator<MT, S, SM>
where
I: Input,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus<I>,
SM: ScheduledMutator<I, MT, S>,
MT: MutatorsTuple<S> + NamedTuple,
S: UsesInput + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"LoggerScheduledMutator with {} mutations for Input type {}",
self.scheduled.mutations().len(),
core::any::type_name::<I>()
core::any::type_name::<<S as UsesInput>::Input>()
)
}
}
impl<I, MT, S, SM> Mutator<I, S> for LoggerScheduledMutator<I, MT, S, SM>
impl<MT, S, SM> Mutator<S> for LoggerScheduledMutator<MT, S, SM>
where
I: Input,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus<I>,
SM: ScheduledMutator<I, MT, S>,
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut <S as UsesInput>::Input,
stage_idx: i32,
) -> Result<MutationResult, Error> {
self.scheduled_mutate(state, input, stage_idx)
@ -346,12 +337,11 @@ where
}
}
impl<I, MT, S, SM> ComposedByMutations<I, MT, S> for LoggerScheduledMutator<I, MT, S, SM>
impl<MT, S, SM> ComposedByMutations<MT, S> for LoggerScheduledMutator<MT, S, SM>
where
I: Input,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus<I>,
SM: ScheduledMutator<I, MT, S>,
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
#[inline]
fn mutations(&self) -> &MT {
@ -364,20 +354,19 @@ where
}
}
impl<I, MT, S, SM> ScheduledMutator<I, MT, S> for LoggerScheduledMutator<I, MT, S, SM>
impl<MT, S, SM> ScheduledMutator<MT, S> for LoggerScheduledMutator<MT, S, SM>
where
I: Input,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus<I>,
SM: ScheduledMutator<I, MT, S>,
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &I) -> u64 {
fn iterations(&self, state: &mut S, _: &<S as UsesInput>::Input) -> u64 {
1 << (1 + state.rand_mut().below(6))
}
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &I) -> usize {
fn schedule(&self, state: &mut S, _: &<S as UsesInput>::Input) -> usize {
debug_assert!(!self.scheduled.mutations().is_empty());
state
.rand_mut()
@ -387,7 +376,7 @@ where
fn scheduled_mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut <S as UsesInput>::Input,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mut r = MutationResult::Skipped;
@ -407,12 +396,11 @@ where
}
}
impl<I, MT, S, SM> LoggerScheduledMutator<I, MT, S, SM>
impl<MT, S, SM> LoggerScheduledMutator<MT, S, SM>
where
I: Input,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus<I>,
SM: ScheduledMutator<I, MT, S>,
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
/// Create a new [`StdScheduledMutator`] instance without mutations and corpus
pub fn new(scheduled: SM) -> Self {
@ -429,6 +417,7 @@ mod tests {
use crate::{
bolts::rands::{Rand, StdRand, XkcdRand},
corpus::{Corpus, InMemoryCorpus, Testcase},
feedbacks::ConstFeedback,
inputs::{BytesInput, HasBytesVec},
mutators::{
mutations::SpliceMutator,
@ -443,14 +432,27 @@ mod tests {
// With the current impl, seed of 1 will result in a split at pos 2.
let mut rand = XkcdRand::with_seed(5);
let mut corpus: InMemoryCorpus<BytesInput> = InMemoryCorpus::new();
corpus.add(Testcase::new(vec![b'a', b'b', b'c'])).unwrap();
corpus.add(Testcase::new(vec![b'd', b'e', b'f'])).unwrap();
corpus
.add(Testcase::new(vec![b'a', b'b', b'c'].into()))
.unwrap();
corpus
.add(Testcase::new(vec![b'd', b'e', b'f'].into()))
.unwrap();
let testcase = corpus.get(0).expect("Corpus did not contain entries");
let mut input = testcase.borrow_mut().load_input().unwrap().clone();
let mut state =
StdState::new(rand, corpus, InMemoryCorpus::new(), &mut (), &mut ()).unwrap();
let mut feedback = ConstFeedback::new(false);
let mut objective = ConstFeedback::new(false);
let mut state = StdState::new(
rand,
corpus,
InMemoryCorpus::new(),
&mut feedback,
&mut objective,
)
.unwrap();
rand.set_seed(5);
@ -470,15 +472,28 @@ mod tests {
// With the current impl, seed of 1 will result in a split at pos 2.
let rand = StdRand::with_seed(0x1337);
let mut corpus: InMemoryCorpus<BytesInput> = InMemoryCorpus::new();
corpus.add(Testcase::new(vec![b'a', b'b', b'c'])).unwrap();
corpus.add(Testcase::new(vec![b'd', b'e', b'f'])).unwrap();
corpus
.add(Testcase::new(vec![b'a', b'b', b'c'].into()))
.unwrap();
corpus
.add(Testcase::new(vec![b'd', b'e', b'f'].into()))
.unwrap();
let testcase = corpus.get(0).expect("Corpus did not contain entries");
let mut input = testcase.borrow_mut().load_input().unwrap().clone();
let input_prior = input.clone();
let mut state =
StdState::new(rand, corpus, InMemoryCorpus::new(), &mut (), &mut ()).unwrap();
let mut feedback = ConstFeedback::new(false);
let mut objective = ConstFeedback::new(false);
let mut state = StdState::new(
rand,
corpus,
InMemoryCorpus::new(),
&mut feedback,
&mut objective,
)
.unwrap();
let mut havoc = StdScheduledMutator::new(havoc_mutations());
@ -507,16 +522,14 @@ pub mod pybind {
use pyo3::prelude::*;
use super::{havoc_mutations, Debug, HavocMutationsType, StdScheduledMutator};
use crate::{
inputs::BytesInput, mutators::pybind::PythonMutator, state::pybind::PythonStdState,
};
use crate::{mutators::pybind::PythonMutator, state::pybind::PythonStdState};
#[pyclass(unsendable, name = "StdHavocMutator")]
#[derive(Debug)]
/// Python class for StdHavocMutator
pub struct PythonStdHavocMutator {
/// Rust wrapped StdHavocMutator object
pub inner: StdScheduledMutator<BytesInput, HavocMutationsType, PythonStdState>,
pub inner: StdScheduledMutator<HavocMutationsType, PythonStdState>,
}
#[pymethods]

View File

@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
use crate::mutators::str_decode;
use crate::{
bolts::{rands::Rand, AsSlice},
inputs::{HasBytesVec, Input},
inputs::{HasBytesVec, UsesInput},
mutators::{buffer_self_copy, mutations::buffer_copy, MutationResult, Mutator, Named},
observers::cmp::{CmpValues, CmpValuesMetadata},
state::{HasMaxSize, HasMetadata, HasRand},
@ -295,15 +295,15 @@ impl<'it> IntoIterator for &'it Tokens {
#[derive(Debug, Default)]
pub struct TokenInsert;
impl<I, S> Mutator<I, S> for TokenInsert
impl<S> Mutator<S> for TokenInsert
where
I: Input + HasBytesVec,
S: HasMetadata + HasRand + HasMaxSize,
S: UsesInput + HasMetadata + HasRand + HasMaxSize,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -361,15 +361,15 @@ impl TokenInsert {
#[derive(Debug, Default)]
pub struct TokenReplace;
impl<I, S> Mutator<I, S> for TokenReplace
impl<S> Mutator<S> for TokenReplace
where
I: Input + HasBytesVec,
S: HasMetadata + HasRand + HasMaxSize,
S: UsesInput + HasMetadata + HasRand + HasMaxSize,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -423,16 +423,16 @@ impl TokenReplace {
#[derive(Debug, Default)]
pub struct I2SRandReplace;
impl<I, S> Mutator<I, S> for I2SRandReplace
impl<S> Mutator<S> for I2SRandReplace
where
I: Input + HasBytesVec,
S: HasMetadata + HasRand + HasMaxSize,
S: UsesInput + HasMetadata + HasRand + HasMaxSize,
S::Input: HasBytesVec,
{
#[allow(clippy::too_many_lines)]
fn mutate(
&mut self,
state: &mut S,
input: &mut I,
input: &mut S::Input,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();

View File

@ -4,12 +4,13 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::fmt::Debug;
use core::{fmt::Debug, marker::PhantomData};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::{
bolts::{ownedref::OwnedRefMut, tuples::Named, AsMutSlice, AsSlice},
inputs::UsesInput,
observers::Observer,
state::HasMetadata,
Error,
@ -111,9 +112,10 @@ pub trait CmpMap: Debug {
}
/// A [`CmpObserver`] observes the traced comparisons during the current execution using a [`CmpMap`]
pub trait CmpObserver<CM, I, S>: Observer<I, S>
pub trait CmpObserver<CM, S>: Observer<S>
where
CM: CmpMap,
S: UsesInput,
{
/// Get the number of usable cmps (all by default)
fn usable_count(&self) -> usize;
@ -194,18 +196,21 @@ where
/// A standard [`CmpObserver`] observer
#[derive(Serialize, Deserialize, Debug)]
#[serde(bound = "CM: serde::de::DeserializeOwned")]
pub struct StdCmpObserver<'a, CM>
pub struct StdCmpObserver<'a, CM, S>
where
CM: CmpMap + Serialize,
S: UsesInput,
{
cmp_map: OwnedRefMut<'a, CM>,
size: Option<OwnedRefMut<'a, usize>>,
name: String,
phantom: PhantomData<S>,
}
impl<'a, CM, I, S> CmpObserver<CM, I, S> for StdCmpObserver<'a, CM>
impl<'a, CM, S> CmpObserver<CM, S> for StdCmpObserver<'a, CM, S>
where
CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput + Debug,
{
/// Get the number of usable cmps (all by default)
fn usable_count(&self) -> usize {
@ -224,28 +229,31 @@ where
}
}
impl<'a, CM, I, S> Observer<I, S> for StdCmpObserver<'a, CM>
impl<'a, CM, S> Observer<S> for StdCmpObserver<'a, CM, S>
where
CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput + Debug,
{
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.cmp_map.as_mut().reset()?;
Ok(())
}
}
impl<'a, CM> Named for StdCmpObserver<'a, CM>
impl<'a, CM, S> Named for StdCmpObserver<'a, CM, S>
where
CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput,
{
fn name(&self) -> &str {
&self.name
}
}
impl<'a, CM> StdCmpObserver<'a, CM>
impl<'a, CM, S> StdCmpObserver<'a, CM, S>
where
CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput,
{
/// Creates a new [`StdCmpObserver`] with the given name and map.
#[must_use]
@ -254,6 +262,7 @@ where
name: name.to_string(),
size: None,
cmp_map: OwnedRefMut::Ref(map),
phantom: PhantomData,
}
}
@ -264,6 +273,7 @@ where
name: name.to_string(),
size: Some(OwnedRefMut::Ref(size)),
cmp_map: OwnedRefMut::Ref(map),
phantom: PhantomData,
}
}
}

View File

@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{
bolts::tuples::Named,
inputs::UsesInput,
observers::{
concolic::{serialization_format::MessageFileReader, ConcolicMetadata},
Observer,
@ -18,7 +19,7 @@ pub struct ConcolicObserver<'map> {
name: String,
}
impl<'map, I, S> Observer<I, S> for ConcolicObserver<'map> {}
impl<'map, S> Observer<S> for ConcolicObserver<'map> where S: UsesInput {}
impl<'map> ConcolicObserver<'map> {
/// Create the concolic observer metadata for this run

View File

@ -24,6 +24,7 @@ use crate::{
AsIter, AsIterMut, AsMutSlice, AsSlice, HasLen,
},
executors::ExitKind,
inputs::UsesInput,
observers::Observer,
Error,
};
@ -198,8 +199,9 @@ where
name: String,
}
impl<'a, I, S, T> Observer<I, S> for StdMapObserver<'a, T>
impl<'a, S, T> Observer<S> for StdMapObserver<'a, T>
where
S: UsesInput,
T: Bounded
+ PartialEq
+ Default
@ -210,7 +212,7 @@ where
+ Debug,
{
#[inline]
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.reset_map()
}
}
@ -499,13 +501,14 @@ where
name: String,
}
impl<'a, I, S, T, const N: usize> Observer<I, S> for ConstMapObserver<'a, T, N>
impl<'a, S, T, const N: usize> Observer<S> for ConstMapObserver<'a, T, N>
where
S: UsesInput,
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
Self: MapObserver,
{
#[inline]
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.reset_map()
}
}
@ -770,13 +773,14 @@ where
name: String,
}
impl<'a, I, S, T> Observer<I, S> for VariableMapObserver<'a, T>
impl<'a, S, T> Observer<S> for VariableMapObserver<'a, T>
where
S: UsesInput,
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
Self: MapObserver,
{
#[inline]
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.reset_map()
}
}
@ -1038,18 +1042,24 @@ where
base: M,
}
impl<I, S, M> Observer<I, S> for HitcountsMapObserver<M>
impl<S, M> Observer<S> for HitcountsMapObserver<M>
where
M: MapObserver<Entry = u8> + Observer<I, S> + AsMutSlice<u8>,
M: MapObserver<Entry = u8> + Observer<S> + AsMutSlice<u8>,
S: UsesInput,
{
#[inline]
fn pre_exec(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.base.pre_exec(state, input)
}
#[inline]
#[allow(clippy::cast_ptr_alignment)]
fn post_exec(&mut self, state: &mut S, input: &I, exit_kind: &ExitKind) -> Result<(), Error> {
fn post_exec(
&mut self,
state: &mut S,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error> {
let map = self.as_mut_slice();
let len = map.len();
if (len & 1) != 0 {
@ -1237,19 +1247,25 @@ where
base: M,
}
impl<I, S, M> Observer<I, S> for HitcountsIterableMapObserver<M>
impl<S, M> Observer<S> for HitcountsIterableMapObserver<M>
where
M: MapObserver<Entry = u8> + Observer<I, S>,
M: MapObserver<Entry = u8> + Observer<S>,
for<'it> M: AsIterMut<'it, Item = u8>,
S: UsesInput,
{
#[inline]
fn pre_exec(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.base.pre_exec(state, input)
}
#[inline]
#[allow(clippy::cast_ptr_alignment)]
fn post_exec(&mut self, state: &mut S, input: &I, exit_kind: &ExitKind) -> Result<(), Error> {
fn post_exec(
&mut self,
state: &mut S,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error> {
for item in self.as_iter_mut() {
*item = unsafe { *COUNT_CLASS_LOOKUP.get_unchecked((*item) as usize) };
}
@ -1429,13 +1445,14 @@ where
iter_idx: usize,
}
impl<'a, I, S, T> Observer<I, S> for MultiMapObserver<'a, T>
impl<'a, S, T> Observer<S> for MultiMapObserver<'a, T>
where
S: UsesInput,
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
Self: MapObserver,
{
#[inline]
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.reset_map()
}
}
@ -1685,13 +1702,14 @@ where
name: String,
}
impl<I, S, T> Observer<I, S> for OwnedMapObserver<T>
impl<S, T> Observer<S> for OwnedMapObserver<T>
where
S: UsesInput,
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
Self: MapObserver,
{
#[inline]
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.reset_map()
}
}
@ -1904,7 +1922,7 @@ pub mod pybind {
AsIter, AsIterMut, AsMutSlice, AsSlice, Debug, Error, HasLen, Iter, IterMut, MapObserver,
Named, Observer, OwnedMapObserver, StdMapObserver, String, Vec,
};
use crate::observers::pybind::PythonObserver;
use crate::{inputs::UsesInput, observers::pybind::PythonObserver};
#[macro_export]
macro_rules! mapob_unwrap_me {
@ -2240,12 +2258,13 @@ pub mod pybind {
}
}
impl<I, S> Observer<I, S> for $struct_name_trait
impl<S> Observer<S> for $struct_name_trait
where
Self: MapObserver,
S: UsesInput,
{
#[inline]
fn pre_exec(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.pre_exec(state, input) })
}
}

View File

@ -39,12 +39,17 @@ use crate::{
tuples::{MatchName, Named},
},
executors::ExitKind,
inputs::UsesInput,
state::UsesState,
Error,
};
/// Observers observe different information about the target.
/// They can then be used by various sorts of feedback.
pub trait Observer<I, S>: Named + Debug {
pub trait Observer<S>: Named + Debug
where
S: UsesInput,
{
/// The testcase finished execution, calculate any changes.
/// Reserved for future use.
#[inline]
@ -54,7 +59,7 @@ pub trait Observer<I, S>: Named + Debug {
/// Called right before execution starts.
#[inline]
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
@ -63,7 +68,7 @@ pub trait Observer<I, S>: Named + Debug {
fn post_exec(
&mut self,
_state: &mut S,
_input: &I,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
Ok(())
@ -71,7 +76,7 @@ pub trait Observer<I, S>: Named + Debug {
/// Called right before execution starts in the child process, if any.
#[inline]
fn pre_exec_child(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec_child(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
@ -80,7 +85,7 @@ pub trait Observer<I, S>: Named + Debug {
fn post_exec_child(
&mut self,
_state: &mut S,
_input: &I,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
Ok(())
@ -109,27 +114,37 @@ pub trait Observer<I, S>: Named + Debug {
fn observe_stderr(&mut self, stderr: &str) {}
}
/// Defines the observer type shared across traits of the type.
/// Needed for consistency across HasCorpus/HasSolutions and friends.
pub trait UsesObservers: UsesState {
/// The observers type
type Observers: ObserversTuple<Self::State>;
}
/// A haskell-style tuple of observers
pub trait ObserversTuple<I, S>: MatchName + Debug {
pub trait ObserversTuple<S>: MatchName + Debug
where
S: UsesInput,
{
/// This is called right before the next execution.
fn pre_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error>;
fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error>;
/// This is called right after the last execution
fn post_exec_all(
&mut self,
state: &mut S,
input: &I,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error>;
/// This is called right before the next execution in the child process, if any.
fn pre_exec_child_all(&mut self, state: &mut S, input: &I) -> Result<(), Error>;
fn pre_exec_child_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error>;
/// This is called right after the last execution in the child process, if any.
fn post_exec_child_all(
&mut self,
state: &mut S,
input: &I,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error>;
@ -144,28 +159,31 @@ pub trait ObserversTuple<I, S>: MatchName + Debug {
fn observe_stderr(&mut self, stderr: &str);
}
impl<I, S> ObserversTuple<I, S> for () {
fn pre_exec_all(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
impl<S> ObserversTuple<S> for ()
where
S: UsesInput,
{
fn pre_exec_all(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
fn post_exec_all(
&mut self,
_state: &mut S,
_input: &I,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
Ok(())
}
fn pre_exec_child_all(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec_child_all(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
fn post_exec_child_all(
&mut self,
_state: &mut S,
_input: &I,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
Ok(())
@ -192,12 +210,13 @@ impl<I, S> ObserversTuple<I, S> for () {
fn observe_stderr(&mut self, stderr: &str) {}
}
impl<Head, Tail, I, S> ObserversTuple<I, S> for (Head, Tail)
impl<Head, Tail, S> ObserversTuple<S> for (Head, Tail)
where
Head: Observer<I, S>,
Tail: ObserversTuple<I, S>,
Head: Observer<S>,
Tail: ObserversTuple<S>,
S: UsesInput,
{
fn pre_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.0.pre_exec(state, input)?;
self.1.pre_exec_all(state, input)
}
@ -205,14 +224,14 @@ where
fn post_exec_all(
&mut self,
state: &mut S,
input: &I,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error> {
self.0.post_exec(state, input, exit_kind)?;
self.1.post_exec_all(state, input, exit_kind)
}
fn pre_exec_child_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
fn pre_exec_child_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.0.pre_exec_child(state, input)?;
self.1.pre_exec_child_all(state, input)
}
@ -220,7 +239,7 @@ where
fn post_exec_child_all(
&mut self,
state: &mut S,
input: &I,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error> {
self.0.post_exec_child(state, input, exit_kind)?;
@ -286,8 +305,11 @@ impl TimeObserver {
}
}
impl<I, S> Observer<I, S> for TimeObserver {
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
impl<S> Observer<S> for TimeObserver
where
S: UsesInput,
{
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.last_runtime = None;
self.start_time = current_time();
Ok(())
@ -296,7 +318,7 @@ impl<I, S> Observer<I, S> for TimeObserver {
fn post_exec(
&mut self,
_state: &mut S,
_input: &I,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
self.last_runtime = current_time().checked_sub(self.start_time);
@ -348,11 +370,12 @@ where
}
}
impl<'a, I, S, T> Observer<I, S> for ListObserver<'a, T>
impl<'a, S, T> Observer<S> for ListObserver<'a, T>
where
S: UsesInput,
T: Debug + Serialize + serde::de::DeserializeOwned,
{
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.list.as_mut().clear();
Ok(())
}
@ -433,7 +456,7 @@ pub mod pybind {
}
}
impl Observer<BytesInput, PythonStdState> for PyObjectObserver {
impl Observer<PythonStdState> for PyObjectObserver {
fn flush(&mut self) -> Result<(), Error> {
Python::with_gil(|py| -> PyResult<()> {
self.inner.call_method0(py, "flush")?;
@ -827,11 +850,9 @@ pub mod pybind {
}
}
impl Observer<BytesInput, PythonStdState> for PythonObserver {
impl Observer<PythonStdState> for PythonObserver {
fn flush(&mut self) -> Result<(), Error> {
unwrap_me_mut!(self.wrapper, o, {
Observer::<BytesInput, PythonStdState>::flush(o)
})
unwrap_me_mut!(self.wrapper, o, { Observer::<PythonStdState>::flush(o) })
}
fn pre_exec(
@ -904,7 +925,7 @@ pub mod pybind {
}
}
impl ObserversTuple<BytesInput, PythonStdState> for PythonObserversTuple {
impl ObserversTuple<PythonStdState> for PythonObserversTuple {
fn pre_exec_all(
&mut self,
state: &mut PythonStdState,

View File

@ -67,7 +67,7 @@ impl<'de, I: 'static + Debug, S: 'static + Debug> Deserialize<'de> for Observers
}
}
impl<I: 'static + Debug, S: 'static + Debug> ObserversTuple<I, S> for ObserversOwnedMap<I, S> {
impl<I: 'static + Debug, S: 'static + Debug> ObserversTuple<S> for ObserversOwnedMap<I, S> {
fn pre_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
self.map
.for_each_mut(&mut |_, ob| ob.pre_exec(state, input))

View File

@ -17,7 +17,7 @@ use super::ObserverWithHashField;
use crate::{
bolts::{ownedref::OwnedRefMut, tuples::Named},
executors::ExitKind,
inputs::Input,
inputs::UsesInput,
observers::Observer,
Error,
};
@ -97,11 +97,16 @@ impl<'a> ObserverWithHashField for BacktraceObserver<'a> {
}
}
impl<'a, I, S> Observer<I, S> for BacktraceObserver<'a>
impl<'a, S> Observer<S> for BacktraceObserver<'a>
where
I: Input + Debug,
S: UsesInput,
{
fn post_exec(&mut self, _state: &mut S, _input: &I, exit_kind: &ExitKind) -> Result<(), Error> {
fn post_exec(
&mut self,
_state: &mut S,
_input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error> {
if self.harness_type == HarnessType::InProcess {
if exit_kind == &ExitKind::Crash {
self.update_hash(collect_backtrace());
@ -115,7 +120,7 @@ where
fn post_exec_child(
&mut self,
_state: &mut S,
_input: &I,
_input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error> {
if self.harness_type == HarnessType::Child {
@ -240,18 +245,18 @@ impl Default for AsanBacktraceObserver {
}
}
impl<I, S> Observer<I, S> for AsanBacktraceObserver
impl<S> Observer<S> for AsanBacktraceObserver
where
I: Debug,
S: UsesInput,
{
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
fn post_exec(
&mut self,
_state: &mut S,
_input: &I,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
Ok(())

View File

@ -6,7 +6,7 @@ use alloc::string::String;
use serde::{Deserialize, Serialize};
use crate::{bolts::tuples::Named, observers::Observer};
use crate::{bolts::tuples::Named, inputs::UsesInput, observers::Observer};
/// An observer that captures stdout of a target.
/// Only works for supported executors.
@ -27,7 +27,10 @@ impl StdOutObserver {
}
}
impl<I, S> Observer<I, S> for StdOutObserver {
impl<S> Observer<S> for StdOutObserver
where
S: UsesInput,
{
#[inline]
fn observes_stdout(&mut self) -> bool {
true
@ -63,7 +66,10 @@ impl StdErrObserver {
}
}
impl<I, S> Observer<I, S> for StdErrObserver {
impl<S> Observer<S> for StdErrObserver
where
S: UsesInput,
{
#[inline]
fn observes_stderr(&mut self) -> bool {
true

View File

@ -1,6 +1,7 @@
//! Coverage accounting corpus scheduler, more details at <https://www.ndss-symposium.org/wp-content/uploads/2020/02/24422-paper.pdf>
use alloc::vec::Vec;
use core::fmt::Debug;
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
@ -9,12 +10,12 @@ use crate::{
bolts::{rands::Rand, AsMutSlice, AsSlice, HasLen, HasRefCnt},
corpus::{Corpus, Testcase},
feedbacks::MapIndexesMetadata,
inputs::Input,
inputs::UsesInput,
schedulers::{
minimizer::{IsFavoredMetadata, MinimizerScheduler, DEFAULT_SKIP_NON_FAVORED_PROB},
LenTimeMulTestcaseScore, Scheduler,
},
state::{HasCorpus, HasMetadata, HasRand},
state::{HasCorpus, HasMetadata, HasRand, UsesState},
Error,
};
@ -93,42 +94,58 @@ impl TopAccountingMetadata {
/// A minimizer scheduler using coverage accounting
#[derive(Debug)]
pub struct CoverageAccountingScheduler<'a, CS, I, S>
pub struct CoverageAccountingScheduler<'a, CS>
where
CS: Scheduler<I, S>,
I: Input + HasLen,
S: HasCorpus<I> + HasMetadata + HasRand,
CS: UsesState,
CS::State: Debug,
{
accounting_map: &'a [u32],
skip_non_favored_prob: u64,
inner: MinimizerScheduler<CS, LenTimeMulTestcaseScore<I, S>, I, MapIndexesMetadata, S>,
inner: MinimizerScheduler<
CS,
LenTimeMulTestcaseScore<<CS as UsesState>::State>,
MapIndexesMetadata,
>,
}
impl<'a, CS, I, S> Scheduler<I, S> for CoverageAccountingScheduler<'a, CS, I, S>
impl<'a, CS> UsesState for CoverageAccountingScheduler<'a, CS>
where
CS: Scheduler<I, S>,
I: Input + HasLen,
S: HasCorpus<I> + HasMetadata + HasRand,
CS: UsesState,
CS::State: Debug,
{
fn on_add(&self, state: &mut S, idx: usize) -> Result<(), Error> {
type State = CS::State;
}
impl<'a, CS> Scheduler for CoverageAccountingScheduler<'a, CS>
where
CS: Scheduler,
CS::State: HasCorpus + HasMetadata + HasRand + Debug,
<CS::State as UsesInput>::Input: HasLen,
{
fn on_add(&self, state: &mut Self::State, idx: usize) -> Result<(), Error> {
self.update_accounting_score(state, idx)?;
self.inner.on_add(state, idx)
}
fn on_replace(&self, state: &mut S, idx: usize, testcase: &Testcase<I>) -> Result<(), Error> {
fn on_replace(
&self,
state: &mut Self::State,
idx: usize,
testcase: &Testcase<<Self::State as UsesInput>::Input>,
) -> Result<(), Error> {
self.inner.on_replace(state, idx, testcase)
}
fn on_remove(
&self,
state: &mut S,
state: &mut Self::State,
idx: usize,
testcase: &Option<Testcase<I>>,
testcase: &Option<Testcase<<Self::State as UsesInput>::Input>>,
) -> Result<(), Error> {
self.inner.on_remove(state, idx, testcase)
}
fn next(&self, state: &mut S) -> Result<usize, Error> {
fn next(&self, state: &mut Self::State) -> Result<usize, Error> {
if state
.metadata()
.get::<TopAccountingMetadata>()
@ -154,16 +171,16 @@ where
}
}
impl<'a, CS, I, S> CoverageAccountingScheduler<'a, CS, I, S>
impl<'a, CS> CoverageAccountingScheduler<'a, CS>
where
CS: Scheduler<I, S>,
I: Input + HasLen,
S: HasCorpus<I> + HasMetadata + HasRand,
CS: Scheduler,
CS::State: HasCorpus + HasMetadata + HasRand + Debug,
<CS::State as UsesInput>::Input: HasLen,
{
/// Update the `Corpus` score
#[allow(clippy::unused_self)]
#[allow(clippy::cast_possible_wrap)]
pub fn update_accounting_score(&self, state: &mut S, idx: usize) -> Result<(), Error> {
pub fn update_accounting_score(&self, state: &mut CS::State, idx: usize) -> Result<(), Error> {
let mut indexes = vec![];
let mut new_favoreds = vec![];
{
@ -243,7 +260,7 @@ where
/// Cull the `Corpus`
#[allow(clippy::unused_self)]
pub fn accounting_cull(&self, state: &mut S) -> Result<(), Error> {
pub fn accounting_cull(&self, state: &mut CS::State) -> Result<(), Error> {
let top_rated = match state.metadata().get::<TopAccountingMetadata>() {
None => return Ok(()),
Some(val) => val,
@ -263,7 +280,7 @@ where
/// Creates a new [`CoverageAccountingScheduler`] that wraps a `base` [`Scheduler`]
/// and has a default probability to skip non-faved [`Testcase`]s of [`DEFAULT_SKIP_NON_FAVORED_PROB`].
pub fn new(state: &mut S, base: CS, accounting_map: &'a [u32]) -> Self {
pub fn new(state: &mut CS::State, base: CS, accounting_map: &'a [u32]) -> Self {
match state.metadata().get::<TopAccountingMetadata>() {
Some(meta) => {
if meta.max_accounting.len() != accounting_map.len() {
@ -284,7 +301,7 @@ where
/// Creates a new [`CoverageAccountingScheduler`] that wraps a `base` [`Scheduler`]
/// and has a non-default probability to skip non-faved [`Testcase`]s using (`skip_non_favored_prob`).
pub fn with_skip_prob(
state: &mut S,
state: &mut CS::State,
base: CS,
skip_non_favored_prob: u64,
accounting_map: &'a [u32],

View File

@ -11,9 +11,9 @@ use crate::{
bolts::{rands::Rand, serdeany::SerdeAny, AsSlice, HasRefCnt},
corpus::{Corpus, Testcase},
feedbacks::MapIndexesMetadata,
inputs::Input,
inputs::UsesInput,
schedulers::{LenTimeMulTestcaseScore, Scheduler, TestcaseScore},
state::{HasCorpus, HasMetadata, HasRand},
state::{HasCorpus, HasMetadata, HasRand, UsesState},
Error,
};
@ -61,44 +61,48 @@ impl Default for TopRatedsMetadata {
/// corpus that exercise all the requested features (e.g. all the coverage seen so far)
/// prioritizing [`Testcase`]`s` using [`TestcaseScore`]
#[derive(Debug, Clone)]
pub struct MinimizerScheduler<CS, F, I, M, S>
where
CS: Scheduler<I, S>,
F: TestcaseScore<I, S>,
I: Input,
M: AsSlice<usize> + SerdeAny + HasRefCnt,
S: HasCorpus<I> + HasMetadata,
{
pub struct MinimizerScheduler<CS, F, M> {
base: CS,
skip_non_favored_prob: u64,
phantom: PhantomData<(F, I, M, S)>,
phantom: PhantomData<(F, M)>,
}
impl<CS, F, I, M, S> Scheduler<I, S> for MinimizerScheduler<CS, F, I, M, S>
impl<CS, F, M> UsesState for MinimizerScheduler<CS, F, M>
where
CS: Scheduler<I, S>,
F: TestcaseScore<I, S>,
I: Input,
CS: UsesState,
{
type State = CS::State;
}
impl<CS, F, M> Scheduler for MinimizerScheduler<CS, F, M>
where
CS: Scheduler,
F: TestcaseScore<CS::State>,
M: AsSlice<usize> + SerdeAny + HasRefCnt,
S: HasCorpus<I> + HasMetadata + HasRand,
CS::State: HasCorpus + HasMetadata + HasRand,
{
/// Add an entry to the corpus and return its index
fn on_add(&self, state: &mut S, idx: usize) -> Result<(), Error> {
fn on_add(&self, state: &mut CS::State, idx: usize) -> Result<(), Error> {
self.update_score(state, idx)?;
self.base.on_add(state, idx)
}
/// Replaces the testcase at the given idx
fn on_replace(&self, state: &mut S, idx: usize, testcase: &Testcase<I>) -> Result<(), Error> {
fn on_replace(
&self,
state: &mut CS::State,
idx: usize,
testcase: &Testcase<<CS::State as UsesInput>::Input>,
) -> Result<(), Error> {
self.base.on_replace(state, idx, testcase)
}
/// Removes an entry from the corpus, returning M if M was present.
fn on_remove(
&self,
state: &mut S,
state: &mut CS::State,
idx: usize,
testcase: &Option<Testcase<I>>,
testcase: &Option<Testcase<<CS::State as UsesInput>::Input>>,
) -> Result<(), Error> {
self.base.on_remove(state, idx, testcase)?;
let mut entries = if let Some(meta) = state.metadata_mut().get_mut::<TopRatedsMetadata>() {
@ -164,7 +168,7 @@ where
}
/// Gets the next entry
fn next(&self, state: &mut S) -> Result<usize, Error> {
fn next(&self, state: &mut CS::State) -> Result<usize, Error> {
self.cull(state)?;
let mut idx = self.base.next(state)?;
while {
@ -182,18 +186,17 @@ where
}
}
impl<CS, F, I, M, S> MinimizerScheduler<CS, F, I, M, S>
impl<CS, F, M> MinimizerScheduler<CS, F, M>
where
CS: Scheduler<I, S>,
F: TestcaseScore<I, S>,
I: Input,
CS: Scheduler,
F: TestcaseScore<CS::State>,
M: AsSlice<usize> + SerdeAny + HasRefCnt,
S: HasCorpus<I> + HasMetadata + HasRand,
CS::State: HasCorpus + HasMetadata + HasRand,
{
/// Update the `Corpus` score using the `MinimizerScheduler`
#[allow(clippy::unused_self)]
#[allow(clippy::cast_possible_wrap)]
pub fn update_score(&self, state: &mut S, idx: usize) -> Result<(), Error> {
pub fn update_score(&self, state: &mut CS::State, idx: usize) -> Result<(), Error> {
// Create a new top rated meta if not existing
if state.metadata().get::<TopRatedsMetadata>().is_none() {
state.add_metadata(TopRatedsMetadata::new());
@ -269,7 +272,7 @@ where
/// Cull the `Corpus` using the `MinimizerScheduler`
#[allow(clippy::unused_self)]
pub fn cull(&self, state: &mut S) -> Result<(), Error> {
pub fn cull(&self, state: &mut CS::State) -> Result<(), Error> {
let top_rated = match state.metadata().get::<TopRatedsMetadata>() {
None => return Ok(()),
Some(val) => val,
@ -324,10 +327,10 @@ where
}
/// A [`MinimizerScheduler`] with [`LenTimeMulTestcaseScore`] to prioritize quick and small [`Testcase`]`s`.
pub type LenTimeMinimizerScheduler<CS, I, M, S> =
MinimizerScheduler<CS, LenTimeMulTestcaseScore<I, S>, I, M, S>;
pub type LenTimeMinimizerScheduler<CS, M> =
MinimizerScheduler<CS, LenTimeMulTestcaseScore<<CS as UsesState>::State>, M>;
/// A [`MinimizerScheduler`] with [`LenTimeMulTestcaseScore`] to prioritize quick and small [`Testcase`]`s`
/// that exercise all the entries registered in the [`MapIndexesMetadata`].
pub type IndexesLenTimeMinimizerScheduler<CS, I, S> =
MinimizerScheduler<CS, LenTimeMulTestcaseScore<I, S>, I, MapIndexesMetadata, S>;
pub type IndexesLenTimeMinimizerScheduler<CS> =
MinimizerScheduler<CS, LenTimeMulTestcaseScore<<CS as UsesState>::State>, MapIndexesMetadata>;

View File

@ -1,6 +1,8 @@
//! Schedule the access to the Corpus.
pub mod queue;
use core::marker::PhantomData;
pub use queue::QueueScheduler;
pub mod probabilistic_sampling;
@ -28,52 +30,62 @@ pub use powersched::PowerQueueScheduler;
use crate::{
bolts::rands::Rand,
corpus::{Corpus, Testcase},
inputs::Input,
state::{HasCorpus, HasRand},
inputs::UsesInput,
state::{HasCorpus, HasRand, UsesState},
Error,
};
/// The scheduler define how the fuzzer requests a testcase from the corpus.
/// It has hooks to corpus add/replace/remove to allow complex scheduling algorithms to collect data.
pub trait Scheduler<I, S>
where
I: Input,
{
pub trait Scheduler: UsesState {
/// Added an entry to the corpus at the given index
fn on_add(&self, _state: &mut S, _idx: usize) -> Result<(), Error> {
fn on_add(&self, _state: &mut Self::State, _idx: usize) -> Result<(), Error> {
Ok(())
}
/// Replaced the given testcase at the given idx
fn on_replace(&self, _state: &mut S, _idx: usize, _prev: &Testcase<I>) -> Result<(), Error> {
fn on_replace(
&self,
_state: &mut Self::State,
_idx: usize,
_prev: &Testcase<<Self::State as UsesInput>::Input>,
) -> Result<(), Error> {
Ok(())
}
/// Removed the given entry from the corpus at the given index
fn on_remove(
&self,
_state: &mut S,
_state: &mut Self::State,
_idx: usize,
_testcase: &Option<Testcase<I>>,
_testcase: &Option<Testcase<<Self::State as UsesInput>::Input>>,
) -> Result<(), Error> {
Ok(())
}
/// Gets the next entry
fn next(&self, state: &mut S) -> Result<usize, Error>;
fn next(&self, state: &mut Self::State) -> Result<usize, Error>;
}
/// Feed the fuzzer simpply with a random testcase on request
/// Feed the fuzzer simply with a random testcase on request
#[derive(Debug, Clone)]
pub struct RandScheduler;
pub struct RandScheduler<S> {
phantom: PhantomData<S>,
}
impl<I, S> Scheduler<I, S> for RandScheduler
impl<S> UsesState for RandScheduler<S>
where
S: HasCorpus<I> + HasRand,
I: Input,
S: UsesInput,
{
type State = S;
}
impl<S> Scheduler for RandScheduler<S>
where
S: HasCorpus + HasRand,
{
/// Gets the next entry at random
fn next(&self, state: &mut S) -> Result<usize, Error> {
fn next(&self, state: &mut Self::State) -> Result<usize, Error> {
if state.corpus().count() == 0 {
Err(Error::empty("No entries in corpus".to_owned()))
} else {
@ -85,15 +97,17 @@ where
}
}
impl RandScheduler {
impl<S> RandScheduler<S> {
/// Create a new [`RandScheduler`] that just schedules randomly.
#[must_use]
pub fn new() -> Self {
Self
Self {
phantom: PhantomData,
}
}
}
impl Default for RandScheduler {
impl<S> Default for RandScheduler<S> {
fn default() -> Self {
Self::new()
}
@ -101,4 +115,4 @@ impl Default for RandScheduler {
/// A [`StdScheduler`] uses the default scheduler in `LibAFL` to schedule [`Testcase`]s.
/// The current `Std` is a [`RandScheduler`], although this may change in the future, if another [`Scheduler`] delivers better results.
pub type StdScheduler = RandScheduler;
pub type StdScheduler<S> = RandScheduler<S>;

View File

@ -4,17 +4,18 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::time::Duration;
use core::{marker::PhantomData, time::Duration};
use serde::{Deserialize, Serialize};
use crate::{
corpus::{Corpus, SchedulerTestcaseMetaData},
inputs::Input,
inputs::UsesInput,
schedulers::Scheduler,
state::{HasCorpus, HasMetadata},
state::{HasCorpus, HasMetadata, UsesState},
Error,
};
/// The n fuzz size
pub const N_FUZZ_SIZE: usize = 1 << 21;
@ -148,17 +149,24 @@ pub enum PowerSchedule {
/// A corpus scheduler using power schedules
#[derive(Clone, Debug)]
pub struct PowerQueueScheduler {
pub struct PowerQueueScheduler<S> {
strat: PowerSchedule,
phantom: PhantomData<S>,
}
impl<I, S> Scheduler<I, S> for PowerQueueScheduler
impl<S> UsesState for PowerQueueScheduler<S>
where
S: HasCorpus<I> + HasMetadata,
I: Input,
S: UsesInput,
{
type State = S;
}
impl<S> Scheduler for PowerQueueScheduler<S>
where
S: HasCorpus + HasMetadata,
{
/// Add an entry to the corpus and return its index
fn on_add(&self, state: &mut S, idx: usize) -> Result<(), Error> {
fn on_add(&self, state: &mut Self::State, idx: usize) -> Result<(), Error> {
if !state.has_metadata::<SchedulerMetadata>() {
state.add_metadata::<SchedulerMetadata>(SchedulerMetadata::new(Some(self.strat)));
}
@ -189,7 +197,7 @@ where
Ok(())
}
fn next(&self, state: &mut S) -> Result<usize, Error> {
fn next(&self, state: &mut Self::State) -> Result<usize, Error> {
if state.corpus().count() == 0 {
Err(Error::empty(String::from("No entries in corpus")))
} else {
@ -232,10 +240,13 @@ where
}
}
impl PowerQueueScheduler {
impl<S> PowerQueueScheduler<S> {
/// Create a new [`PowerQueueScheduler`]
#[must_use]
pub fn new(strat: PowerSchedule) -> Self {
PowerQueueScheduler { strat }
PowerQueueScheduler {
strat,
phantom: PhantomData,
}
}
}

View File

@ -10,21 +10,19 @@ use serde::{Deserialize, Serialize};
use crate::{
bolts::rands::Rand,
corpus::Corpus,
inputs::Input,
inputs::UsesInput,
schedulers::{Scheduler, TestcaseScore},
state::{HasCorpus, HasMetadata, HasRand},
state::{HasCorpus, HasMetadata, HasRand, UsesState},
Error,
};
/// Conduct reservoir sampling (probabilistic sampling) over all corpus elements.
#[derive(Debug, Clone)]
pub struct ProbabilitySamplingScheduler<F, I, S>
pub struct ProbabilitySamplingScheduler<F, S>
where
F: TestcaseScore<I, S>,
I: Input,
S: HasCorpus<I> + HasMetadata + HasRand,
S: UsesInput,
{
phantom: PhantomData<(F, I, S)>,
phantom: PhantomData<(F, S)>,
}
/// A state metadata holding a map of probability of corpus elements.
@ -55,11 +53,10 @@ impl Default for ProbabilityMetadata {
}
}
impl<F, I, S> ProbabilitySamplingScheduler<F, I, S>
impl<F, S> ProbabilitySamplingScheduler<F, S>
where
F: TestcaseScore<I, S>,
I: Input,
S: HasCorpus<I> + HasMetadata + HasRand,
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata + HasRand,
{
/// Creates a new [`struct@ProbabilitySamplingScheduler`]
#[must_use]
@ -90,13 +87,19 @@ where
}
}
impl<F, I, S> Scheduler<I, S> for ProbabilitySamplingScheduler<F, I, S>
impl<F, S> UsesState for ProbabilitySamplingScheduler<F, S>
where
F: TestcaseScore<I, S>,
I: Input,
S: HasCorpus<I> + HasMetadata + HasRand,
S: UsesInput,
{
fn on_add(&self, state: &mut S, idx: usize) -> Result<(), Error> {
type State = S;
}
impl<F, S> Scheduler for ProbabilitySamplingScheduler<F, S>
where
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata + HasRand,
{
fn on_add(&self, state: &mut Self::State, idx: usize) -> Result<(), Error> {
if state.metadata().get::<ProbabilityMetadata>().is_none() {
state.add_metadata(ProbabilityMetadata::new());
}
@ -105,7 +108,7 @@ where
/// Gets the next entry
#[allow(clippy::cast_precision_loss)]
fn next(&self, state: &mut S) -> Result<usize, Error> {
fn next(&self, state: &mut Self::State) -> Result<usize, Error> {
if state.corpus().count() == 0 {
Err(Error::empty(String::from("No entries in corpus")))
} else {
@ -127,11 +130,10 @@ where
}
}
impl<F, I, S> Default for ProbabilitySamplingScheduler<F, I, S>
impl<F, S> Default for ProbabilitySamplingScheduler<F, S>
where
F: TestcaseScore<I, S>,
I: Input,
S: HasCorpus<I> + HasMetadata + HasRand,
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata + HasRand,
{
fn default() -> Self {
Self::new()
@ -146,7 +148,8 @@ mod tests {
use crate::{
bolts::rands::StdRand,
corpus::{Corpus, InMemoryCorpus, Testcase},
inputs::{bytes::BytesInput, Input},
feedbacks::ConstFeedback,
inputs::{bytes::BytesInput, Input, UsesInput},
schedulers::{ProbabilitySamplingScheduler, Scheduler, TestcaseScore},
state::{HasCorpus, HasMetadata, StdState},
Error,
@ -162,18 +165,17 @@ mod tests {
phantom: PhantomData<I>,
}
impl<I, S> TestcaseScore<I, S> for UniformDistribution<I>
impl<S> TestcaseScore<S> for UniformDistribution<S::Input>
where
I: Input,
S: HasMetadata + HasCorpus<I>,
S: HasMetadata + HasCorpus,
{
fn compute(_: &mut Testcase<I>, _state: &S) -> Result<f64, Error> {
fn compute(_: &mut Testcase<S::Input>, _state: &S) -> Result<f64, Error> {
Ok(FACTOR)
}
}
pub type UniformProbabilitySamplingScheduler<I, S> =
ProbabilitySamplingScheduler<UniformDistribution<I>, I, S>;
pub type UniformProbabilitySamplingScheduler<S> =
ProbabilitySamplingScheduler<UniformDistribution<<S as UsesInput>::Input>, S>;
#[test]
fn test_prob_sampling() {
@ -182,6 +184,9 @@ mod tests {
let scheduler = UniformProbabilitySamplingScheduler::new();
let mut feedback = ConstFeedback::new(false);
let mut objective = ConstFeedback::new(false);
let mut corpus = InMemoryCorpus::new();
let t1 = Testcase::with_filename(BytesInput::new(vec![0_u8; 4]), "1".into());
let t2 = Testcase::with_filename(BytesInput::new(vec![1_u8; 4]), "2".into());
@ -189,8 +194,14 @@ mod tests {
let idx1 = corpus.add(t1).unwrap();
let idx2 = corpus.add(t2).unwrap();
let mut state =
StdState::new(rand, corpus, InMemoryCorpus::new(), &mut (), &mut ()).unwrap();
let mut state = StdState::new(
rand,
corpus,
InMemoryCorpus::new(),
&mut feedback,
&mut objective,
)
.unwrap();
scheduler.on_add(state.borrow_mut(), idx1).unwrap();
scheduler.on_add(state.borrow_mut(), idx2).unwrap();
let next_idx1 = scheduler.next(&mut state).unwrap();

View File

@ -1,20 +1,35 @@
//! The queue corpus scheduler implements an AFL-like queue mechanism
use alloc::borrow::ToOwned;
use core::marker::PhantomData;
use crate::{corpus::Corpus, inputs::Input, schedulers::Scheduler, state::HasCorpus, Error};
use crate::{
corpus::Corpus,
inputs::UsesInput,
schedulers::Scheduler,
state::{HasCorpus, UsesState},
Error,
};
/// Walk the corpus in a queue-like fashion
#[derive(Debug, Clone)]
pub struct QueueScheduler;
pub struct QueueScheduler<S> {
phantom: PhantomData<S>,
}
impl<I, S> Scheduler<I, S> for QueueScheduler
impl<S> UsesState for QueueScheduler<S>
where
S: HasCorpus<I>,
I: Input,
S: UsesInput,
{
type State = S;
}
impl<S> Scheduler for QueueScheduler<S>
where
S: HasCorpus,
{
/// Gets the next entry in the queue
fn next(&self, state: &mut S) -> Result<usize, Error> {
fn next(&self, state: &mut Self::State) -> Result<usize, Error> {
if state.corpus().count() == 0 {
Err(Error::empty("No entries in corpus".to_owned()))
} else {
@ -34,15 +49,17 @@ where
}
}
impl QueueScheduler {
impl<S> QueueScheduler<S> {
/// Creates a new `QueueScheduler`
#[must_use]
pub fn new() -> Self {
Self
Self {
phantom: PhantomData,
}
}
}
impl Default for QueueScheduler {
impl<S> Default for QueueScheduler<S> {
fn default() -> Self {
Self::new()
}
@ -57,6 +74,7 @@ mod tests {
use crate::{
bolts::rands::StdRand,
corpus::{Corpus, OnDiskCorpus, Testcase},
feedbacks::ConstFeedback,
inputs::bytes::BytesInput,
schedulers::{QueueScheduler, Scheduler},
state::{HasCorpus, StdState},
@ -79,7 +97,10 @@ mod tests {
OnDiskCorpus::<BytesInput>::new(PathBuf::from("target/.test/fancy/objective/path"))
.unwrap();
let mut state = StdState::new(rand, q, objective_q, &mut (), &mut ()).unwrap();
let mut feedback = ConstFeedback::new(false);
let mut objective = ConstFeedback::new(false);
let mut state = StdState::new(rand, q, objective_q, &mut feedback, &mut objective).unwrap();
let next_idx = scheduler.next(&mut state).unwrap();
let filename = state

View File

@ -6,7 +6,6 @@ use crate::{
bolts::{HasLen, HasRefCnt},
corpus::{Corpus, SchedulerTestcaseMetaData, Testcase},
feedbacks::MapIndexesMetadata,
inputs::Input,
schedulers::{
minimizer::{IsFavoredMetadata, TopRatedsMetadata},
powersched::{PowerSchedule, SchedulerMetadata},
@ -16,33 +15,28 @@ use crate::{
};
/// Compute the favor factor of a [`Testcase`]. Lower is better.
pub trait TestcaseScore<I, S>
pub trait TestcaseScore<S>
where
I: Input,
S: HasMetadata + HasCorpus<I>,
S: HasMetadata + HasCorpus,
{
/// Computes the favor factor of a [`Testcase`]. Lower is better.
fn compute(entry: &mut Testcase<I>, state: &S) -> Result<f64, Error>;
fn compute(entry: &mut Testcase<S::Input>, state: &S) -> Result<f64, Error>;
}
/// Multiply the testcase size with the execution time.
/// This favors small and quick testcases.
#[derive(Debug, Clone)]
pub struct LenTimeMulTestcaseScore<I, S>
where
I: Input + HasLen,
S: HasMetadata + HasCorpus<I>,
{
phantom: PhantomData<(I, S)>,
pub struct LenTimeMulTestcaseScore<S> {
phantom: PhantomData<S>,
}
impl<I, S> TestcaseScore<I, S> for LenTimeMulTestcaseScore<I, S>
impl<S> TestcaseScore<S> for LenTimeMulTestcaseScore<S>
where
I: Input + HasLen,
S: HasMetadata + HasCorpus<I>,
S: HasCorpus + HasMetadata,
S::Input: HasLen,
{
#[allow(clippy::cast_precision_loss, clippy::cast_lossless)]
fn compute(entry: &mut Testcase<I>, _state: &S) -> Result<f64, Error> {
fn compute(entry: &mut Testcase<S::Input>, _state: &S) -> 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.cached_len()? as f64)
}
@ -56,18 +50,13 @@ const HAVOC_MAX_MULT: f64 = 64.0;
/// The power assigned to each corpus entry
/// This result is used for power scheduling
#[derive(Debug, Clone)]
pub struct CorpusPowerTestcaseScore<I, S>
where
I: Input + HasLen,
S: HasMetadata + HasCorpus<I>,
{
phantom: PhantomData<(I, S)>,
pub struct CorpusPowerTestcaseScore<S> {
phantom: PhantomData<S>,
}
impl<I, S> TestcaseScore<I, S> for CorpusPowerTestcaseScore<I, S>
impl<S> TestcaseScore<S> for CorpusPowerTestcaseScore<S>
where
I: Input + HasLen,
S: HasMetadata + HasCorpus<I>,
S: HasCorpus + HasMetadata,
{
/// Compute the `power` we assign to each corpus entry
#[allow(
@ -76,7 +65,7 @@ where
clippy::cast_sign_loss,
clippy::cast_lossless
)]
fn compute(entry: &mut Testcase<I>, state: &S) -> Result<f64, Error> {
fn compute(entry: &mut Testcase<S::Input>, state: &S) -> Result<f64, Error> {
let psmeta = state
.metadata()
.get::<SchedulerMetadata>()
@ -294,22 +283,17 @@ where
/// The weight for each corpus entry
/// This result is used for corpus scheduling
#[derive(Debug, Clone)]
pub struct CorpusWeightTestcaseScore<I, S>
where
I: Input + HasLen,
S: HasMetadata + HasCorpus<I>,
{
phantom: PhantomData<(I, S)>,
pub struct CorpusWeightTestcaseScore<S> {
phantom: PhantomData<S>,
}
impl<I, S> TestcaseScore<I, S> for CorpusWeightTestcaseScore<I, S>
impl<S> TestcaseScore<S> for CorpusWeightTestcaseScore<S>
where
I: Input + HasLen,
S: HasMetadata + HasCorpus<I>,
S: HasCorpus + HasMetadata,
{
/// Compute the `weight` used in weighted corpus entry selection algo
#[allow(clippy::cast_precision_loss, clippy::cast_lossless)]
fn compute(entry: &mut Testcase<I>, state: &S) -> Result<f64, Error> {
fn compute(entry: &mut Testcase<S::Input>, state: &S) -> Result<f64, Error> {
let mut weight = 1.0;
let psmeta = state
.metadata()

View File

@ -12,13 +12,13 @@ use serde::{Deserialize, Serialize};
use crate::{
bolts::rands::Rand,
corpus::{Corpus, SchedulerTestcaseMetaData, Testcase},
inputs::Input,
inputs::UsesInput,
schedulers::{
powersched::{PowerSchedule, SchedulerMetadata},
testcase_score::{CorpusWeightTestcaseScore, TestcaseScore},
Scheduler,
},
state::{HasCorpus, HasMetadata, HasRand},
state::{HasCorpus, HasMetadata, HasRand, UsesState},
Error,
};
@ -89,27 +89,25 @@ crate::impl_serdeany!(WeightedScheduleMetadata);
/// A corpus scheduler using power schedules with weighted queue item selection algo.
#[derive(Clone, Debug)]
pub struct WeightedScheduler<F, I, S> {
pub struct WeightedScheduler<F, S> {
strat: Option<PowerSchedule>,
phantom: PhantomData<(F, I, S)>,
phantom: PhantomData<(F, S)>,
}
impl<F, I, S> Default for WeightedScheduler<F, I, S>
impl<F, S> Default for WeightedScheduler<F, S>
where
F: TestcaseScore<I, S>,
I: Input,
S: HasCorpus<I> + HasMetadata + HasRand,
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata + HasRand,
{
fn default() -> Self {
Self::new()
}
}
impl<F, I, S> WeightedScheduler<F, I, S>
impl<F, S> WeightedScheduler<F, S>
where
F: TestcaseScore<I, S>,
I: Input,
S: HasCorpus<I> + HasMetadata + HasRand,
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata + HasRand,
{
/// Create a new [`WeightedScheduler`] without any scheduling strategy
#[must_use]
@ -219,11 +217,17 @@ where
}
}
impl<F, I, S> Scheduler<I, S> for WeightedScheduler<F, I, S>
impl<F, S> UsesState for WeightedScheduler<F, S>
where
F: TestcaseScore<I, S>,
S: HasCorpus<I> + HasMetadata + HasRand,
I: Input,
S: UsesInput,
{
type State = S;
}
impl<F, S> Scheduler for WeightedScheduler<F, S>
where
F: TestcaseScore<S>,
S: HasCorpus + HasMetadata + HasRand,
{
/// Add an entry to the corpus and return its index
fn on_add(&self, state: &mut S, idx: usize) -> Result<(), Error> {
@ -264,7 +268,12 @@ where
Ok(())
}
fn on_replace(&self, state: &mut S, idx: usize, _testcase: &Testcase<I>) -> Result<(), Error> {
fn on_replace(
&self,
state: &mut S,
idx: usize,
_testcase: &Testcase<S::Input>,
) -> Result<(), Error> {
// Recreate the alias table
self.on_add(state, idx)
}
@ -273,7 +282,7 @@ where
&self,
state: &mut S,
_idx: usize,
_testcase: &Option<Testcase<I>>,
_testcase: &Option<Testcase<S::Input>>,
) -> Result<(), Error> {
// Recreate the alias table
self.create_alias_table(state)?;
@ -343,4 +352,4 @@ where
}
/// The standard corpus weight, same as aflpp
pub type StdWeightedScheduler<I, S> = WeightedScheduler<CorpusWeightTestcaseScore<I, S>, I, S>;
pub type StdWeightedScheduler<S> = WeightedScheduler<CorpusWeightTestcaseScore<S>, S>;

View File

@ -20,11 +20,11 @@ use crate::{
HasObserverName,
},
fuzzer::Evaluator,
inputs::Input,
inputs::UsesInput,
observers::{MapObserver, ObserversTuple},
schedulers::powersched::SchedulerMetadata,
stages::Stage,
state::{HasClientPerfMonitor, HasCorpus, HasMetadata, HasNamedMetadata},
state::{HasClientPerfMonitor, HasCorpus, HasMetadata, HasNamedMetadata, UsesState},
Error,
};
@ -63,33 +63,33 @@ impl UnstableEntriesMetadata {
/// The calibration stage will measure the average exec time and the target's stability for this input.
#[derive(Clone, Debug)]
pub struct CalibrationStage<I, O, OT, S>
where
I: Input,
O: MapObserver,
OT: ObserversTuple<I, S>,
S: HasCorpus<I> + HasMetadata + HasNamedMetadata,
{
pub struct CalibrationStage<O, OT, S> {
map_observer_name: String,
map_name: String,
stage_max: usize,
track_stability: bool,
phantom: PhantomData<(I, O, OT, S)>,
phantom: PhantomData<(O, OT, S)>,
}
const CAL_STAGE_START: usize = 4; // AFL++'s CAL_CYCLES_FAST + 1
const CAL_STAGE_MAX: usize = 8; // AFL++'s CAL_CYCLES + 1
impl<E, EM, I, O, OT, S, Z> Stage<E, EM, S, Z> for CalibrationStage<I, O, OT, S>
impl<O, OT, S> UsesState for CalibrationStage<O, OT, S>
where
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
EM: EventFirer<I>,
I: Input,
S: UsesInput,
{
type State = S;
}
impl<E, EM, O, OT, Z> Stage<E, EM, Z> for CalibrationStage<O, OT, E::State>
where
E: Executor<EM, Z> + HasObservers<Observers = OT>,
EM: EventFirer<State = E::State>,
O: MapObserver,
for<'de> <O as MapObserver>::Entry: Serialize + Deserialize<'de> + 'static,
OT: ObserversTuple<I, S>,
S: HasCorpus<I> + HasMetadata + HasClientPerfMonitor + HasNamedMetadata,
Z: Evaluator<E, EM, I, S>,
OT: ObserversTuple<E::State>,
E::State: HasCorpus + HasMetadata + HasClientPerfMonitor + HasNamedMetadata,
Z: Evaluator<E, EM, State = E::State>,
{
#[inline]
#[allow(clippy::let_and_return, clippy::too_many_lines)]
@ -97,7 +97,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut E::State,
mgr: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -285,16 +285,15 @@ where
}
}
impl<I, O, OT, S> CalibrationStage<I, O, OT, S>
impl<O, OT, S> CalibrationStage<O, OT, S>
where
I: Input,
O: MapObserver,
OT: ObserversTuple<I, S>,
S: HasCorpus<I> + HasMetadata + HasNamedMetadata,
OT: ObserversTuple<S>,
S: HasCorpus + HasMetadata + HasNamedMetadata,
{
/// Create a new [`CalibrationStage`].
#[must_use]
pub fn new<N, R>(map_feedback: &MapFeedback<I, N, O, R, S, O::Entry>) -> Self
pub fn new<N, R>(map_feedback: &MapFeedback<N, O, R, S, O::Entry>) -> Self
where
O::Entry:
PartialEq + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
@ -313,7 +312,7 @@ where
/// Create a new [`CalibrationStage`], but without checking stability.
#[must_use]
pub fn ignore_stability<N, R>(map_feedback: &MapFeedback<I, N, O, R, S, O::Entry>) -> Self
pub fn ignore_stability<N, R>(map_feedback: &MapFeedback<N, O, R, S, O::Entry>) -> Self
where
O::Entry:
PartialEq + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,

View File

@ -11,38 +11,39 @@ use super::{Stage, TracingStage};
use crate::{
corpus::Corpus,
executors::{Executor, HasObservers},
inputs::Input,
observers::{concolic::ConcolicObserver, ObserversTuple},
observers::concolic::ConcolicObserver,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata},
Error,
};
/// Wraps a [`TracingStage`] to add concolic observing.
#[derive(Clone, Debug)]
pub struct ConcolicTracingStage<EM, I, OT, S, TE, Z>
where
I: Input,
TE: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I>,
{
inner: TracingStage<EM, I, OT, S, TE, Z>,
pub struct ConcolicTracingStage<EM, TE, Z> {
inner: TracingStage<EM, TE, Z>,
observer_name: String,
}
impl<E, EM, I, OT, S, TE, Z> Stage<E, EM, S, Z> for ConcolicTracingStage<EM, I, OT, S, TE, Z>
impl<EM, TE, Z> UsesState for ConcolicTracingStage<EM, TE, Z>
where
I: Input,
TE: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I>,
TE: UsesState,
{
type State = TE::State;
}
impl<E, EM, TE, Z> Stage<E, EM, Z> for ConcolicTracingStage<EM, TE, Z>
where
E: UsesState<State = TE::State>,
EM: UsesState<State = TE::State>,
TE: Executor<EM, Z> + HasObservers,
TE::State: HasClientPerfMonitor + HasExecutions + HasCorpus,
Z: UsesState<State = TE::State>,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut TE::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -67,15 +68,9 @@ where
}
}
impl<EM, I, OT, S, TE, Z> ConcolicTracingStage<EM, I, OT, S, TE, Z>
where
I: Input,
TE: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I>,
{
impl<EM, TE, Z> ConcolicTracingStage<EM, TE, Z> {
/// Creates a new default tracing stage using the given [`Executor`], observing traces from a [`ConcolicObserver`] with the given name.
pub fn new(inner: TracingStage<EM, I, OT, S, TE, Z>, observer_name: String) -> Self {
pub fn new(inner: TracingStage<EM, TE, Z>, observer_name: String) -> Self {
Self {
inner,
observer_name,
@ -85,6 +80,7 @@ where
#[cfg(all(feature = "concolic_mutation", feature = "introspection"))]
use crate::monitors::PerfFeature;
use crate::{bolts::tuples::MatchName, state::UsesState};
#[cfg(feature = "concolic_mutation")]
use crate::{
inputs::HasBytesVec,
@ -340,27 +336,33 @@ 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`].
#[derive(Clone, Debug)]
pub struct SimpleConcolicMutationalStage<EM, I, S, Z>
where
I: Input,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I>,
{
_phantom: PhantomData<(EM, I, S, Z)>,
pub struct SimpleConcolicMutationalStage<Z> {
_phantom: PhantomData<Z>,
}
#[cfg(feature = "concolic_mutation")]
impl<E, EM, I, S, Z> Stage<E, EM, S, Z> for SimpleConcolicMutationalStage<EM, I, S, Z>
impl<Z> UsesState for SimpleConcolicMutationalStage<Z>
where
I: Input + HasBytesVec,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I>,
Z: Evaluator<E, EM, I, S>,
Z: UsesState,
{
type State = Z::State;
}
#[cfg(feature = "concolic_mutation")]
impl<E, EM, Z> Stage<E, EM, Z> for SimpleConcolicMutationalStage<Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: Evaluator<E, EM>,
Z::Input: HasBytesVec,
Z::State: HasClientPerfMonitor + HasExecutions + HasCorpus,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut Z::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -392,11 +394,7 @@ where
}
}
impl<EM, I, S, Z> Default for SimpleConcolicMutationalStage<EM, I, S, Z>
where
I: Input,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I>,
{
impl<Z> Default for SimpleConcolicMutationalStage<Z> {
fn default() -> Self {
Self {
_phantom: PhantomData,

View File

@ -1,4 +1,4 @@
//! The tracing stage can trace the target and enrich a testcase with metadata, for example for `CmpLog`.
//! The tracing stage can trace the target and enrich a [`crate::corpus::Testcase`] with metadata, for example for `CmpLog`.
use alloc::{
string::{String, ToString},
@ -16,12 +16,12 @@ use crate::{
corpus::Corpus,
executors::{Executor, HasObservers},
feedbacks::map::MapNoveltiesMetadata,
inputs::{GeneralizedInput, GeneralizedItem, HasBytesVec},
inputs::{GeneralizedInput, GeneralizedItem, HasBytesVec, UsesInput},
mark_feature_time,
observers::{MapObserver, ObserversTuple},
stages::Stage,
start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata},
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, UsesState},
Error,
};
@ -60,23 +60,32 @@ fn find_next_char(list: &[Option<u8>], mut idx: usize, ch: u8) -> usize {
/// A stage that runs a tracer executor
#[derive(Clone, Debug)]
pub struct GeneralizationStage<EM, O, OT, S, Z>
where
O: MapObserver,
OT: ObserversTuple<GeneralizedInput, S>,
S: HasClientPerfMonitor + HasExecutions + HasMetadata + HasCorpus<GeneralizedInput>,
{
pub struct GeneralizationStage<EM, O, OT, Z> {
map_observer_name: String,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(EM, O, OT, S, Z)>,
phantom: PhantomData<(EM, O, OT, Z)>,
}
impl<E, EM, O, OT, S, Z> Stage<E, EM, S, Z> for GeneralizationStage<EM, O, OT, S, Z>
impl<EM, O, OT, Z> UsesState for GeneralizationStage<EM, O, OT, Z>
where
EM: UsesState,
EM::State: UsesInput<Input = GeneralizedInput>,
{
type State = EM::State;
}
impl<E, EM, O, Z> Stage<E, EM, Z> for GeneralizationStage<EM, O, E::Observers, Z>
where
O: MapObserver,
E: Executor<EM, GeneralizedInput, S, Z> + HasObservers<GeneralizedInput, OT, S>,
OT: ObserversTuple<GeneralizedInput, S>,
S: HasClientPerfMonitor + HasExecutions + HasMetadata + HasCorpus<GeneralizedInput>,
E: Executor<EM, Z> + HasObservers,
E::Observers: ObserversTuple<E::State>,
E::State: UsesInput<Input = GeneralizedInput>
+ HasClientPerfMonitor
+ HasExecutions
+ HasMetadata
+ HasCorpus,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
#[inline]
#[allow(clippy::too_many_lines)]
@ -84,7 +93,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut E::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -347,11 +356,16 @@ where
}
}
impl<EM, O, OT, S, Z> GeneralizationStage<EM, O, OT, S, Z>
impl<EM, O, OT, Z> GeneralizationStage<EM, O, OT, Z>
where
EM: UsesState,
O: MapObserver,
OT: ObserversTuple<GeneralizedInput, S>,
S: HasClientPerfMonitor + HasExecutions + HasMetadata + HasCorpus<GeneralizedInput>,
OT: ObserversTuple<EM::State>,
EM::State: UsesInput<Input = GeneralizedInput>
+ HasClientPerfMonitor
+ HasExecutions
+ HasMetadata
+ HasCorpus,
{
/// Create a new [`GeneralizationStage`].
#[must_use]
@ -375,13 +389,14 @@ where
&self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut EM::State,
manager: &mut EM,
novelties: &[usize],
input: &GeneralizedInput,
) -> Result<bool, Error>
where
E: Executor<EM, GeneralizedInput, S, Z> + HasObservers<GeneralizedInput, OT, S>,
E: Executor<EM, Z> + HasObservers<Observers = OT, State = EM::State>,
Z: UsesState<State = EM::State>,
{
start_timer!(state);
executor.observers_mut().pre_exec_all(state, input)?;
@ -418,7 +433,7 @@ where
&self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut EM::State,
manager: &mut EM,
payload: &mut Vec<Option<u8>>,
novelties: &[usize],
@ -426,7 +441,8 @@ where
split_char: u8,
) -> Result<(), Error>
where
E: Executor<EM, GeneralizedInput, S, Z> + HasObservers<GeneralizedInput, OT, S>,
E: Executor<EM, Z> + HasObservers<Observers = OT, State = EM::State>,
Z: UsesState<State = EM::State>,
{
let mut start = 0;
while start < payload.len() {
@ -460,7 +476,7 @@ where
&self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut EM::State,
manager: &mut EM,
payload: &mut Vec<Option<u8>>,
novelties: &[usize],
@ -468,7 +484,8 @@ where
closing_char: u8,
) -> Result<(), Error>
where
E: Executor<EM, GeneralizedInput, S, Z> + HasObservers<GeneralizedInput, OT, S>,
E: Executor<EM, Z> + HasObservers<Observers = OT, State = EM::State>,
Z: UsesState<State = EM::State>,
{
let mut index = 0;
while index < payload.len() {

View File

@ -48,29 +48,40 @@ use self::push::PushStage;
use crate::{
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
executors::{Executor, HasObservers},
inputs::Input,
inputs::UsesInput,
observers::ObserversTuple,
schedulers::Scheduler,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasRand},
state::{HasClientPerfMonitor, HasExecutions, HasMetadata, HasRand, UsesState},
Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler,
};
/// A stage is one step in the fuzzing process.
/// Multiple stages will be scheduled one by one for each input.
pub trait Stage<E, EM, S, Z> {
pub trait Stage<E, EM, Z>: UsesState
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
Z: UsesState<State = Self::State>,
{
/// Run the stage
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut Self::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error>;
}
/// A tuple holding all `Stages` used for fuzzing.
pub trait StagesTuple<E, EM, S, Z> {
pub trait StagesTuple<E, EM, S, Z>
where
E: UsesState<State = S>,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
S: UsesInput,
{
/// Performs all `Stages` in this tuple
fn perform_all(
&mut self,
@ -82,7 +93,13 @@ pub trait StagesTuple<E, EM, S, Z> {
) -> Result<(), Error>;
}
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for () {
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for ()
where
E: UsesState<State = S>,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
S: UsesInput,
{
fn perform_all(
&mut self,
_: &mut Z,
@ -95,16 +112,19 @@ impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for () {
}
}
impl<Head, Tail, E, EM, S, Z> StagesTuple<E, EM, S, Z> for (Head, Tail)
impl<Head, Tail, E, EM, Z> StagesTuple<E, EM, Head::State, Z> for (Head, Tail)
where
Head: Stage<E, EM, S, Z>,
Tail: StagesTuple<E, EM, S, Z>,
Head: Stage<E, EM, Z>,
Tail: StagesTuple<E, EM, Head::State, Z>,
E: UsesState<State = Head::State>,
EM: UsesState<State = Head::State>,
Z: UsesState<State = Head::State>,
{
fn perform_all(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut Head::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -120,23 +140,35 @@ where
/// A [`Stage`] that will call a closure
#[derive(Debug)]
pub struct ClosureStage<CB, E, EM, S, Z>
pub struct ClosureStage<CB, E, EM, Z>
where
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM, usize) -> Result<(), Error>,
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, usize) -> Result<(), Error>,
E: UsesState,
{
closure: CB,
phantom: PhantomData<(E, EM, S, Z)>,
phantom: PhantomData<(E, EM, Z)>,
}
impl<CB, E, EM, S, Z> Stage<E, EM, S, Z> for ClosureStage<CB, E, EM, S, Z>
impl<CB, E, EM, Z> UsesState for ClosureStage<CB, E, EM, Z>
where
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM, usize) -> Result<(), Error>,
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, usize) -> Result<(), Error>,
E: UsesState,
{
type State = E::State;
}
impl<CB, E, EM, Z> Stage<E, EM, Z> for ClosureStage<CB, E, EM, Z>
where
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, usize) -> Result<(), Error>,
E: UsesState,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut E::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -145,9 +177,10 @@ where
}
/// A stage that takes a closure
impl<CB, E, EM, S, Z> ClosureStage<CB, E, EM, S, Z>
impl<CB, E, EM, Z> ClosureStage<CB, E, EM, Z>
where
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM, usize) -> Result<(), Error>,
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, usize) -> Result<(), Error>,
E: UsesState,
{
/// Create a new [`ClosureStage`]
#[must_use]
@ -159,9 +192,10 @@ where
}
}
impl<CB, E, EM, S, Z> From<CB> for ClosureStage<CB, E, EM, S, Z>
impl<CB, E, EM, Z> From<CB> for ClosureStage<CB, E, EM, Z>
where
CB: FnMut(&mut Z, &mut E, &mut S, &mut EM, usize) -> Result<(), Error>,
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM, usize) -> Result<(), Error>,
E: UsesState,
{
#[must_use]
fn from(closure: CB) -> Self {
@ -172,30 +206,12 @@ where
/// Allows us to use a [`push::PushStage`] as a normal [`Stage`]
#[allow(clippy::type_complexity)]
#[derive(Debug)]
pub struct PushStageAdapter<CS, EM, I, OT, PS, S, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId + ProgressReporter<I>,
I: Input,
OT: ObserversTuple<I, S>,
PS: PushStage<CS, EM, I, OT, S, Z>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
{
pub struct PushStageAdapter<CS, EM, OT, PS, Z> {
push_stage: PS,
phantom: PhantomData<(CS, EM, I, OT, S, Z)>,
phantom: PhantomData<(CS, EM, OT, Z)>,
}
impl<CS, EM, I, OT, PS, S, Z> PushStageAdapter<CS, EM, I, OT, PS, S, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId + ProgressReporter<I>,
I: Input,
OT: ObserversTuple<I, S>,
PS: PushStage<CS, EM, I, OT, S, Z>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
{
impl<CS, EM, OT, PS, Z> PushStageAdapter<CS, EM, OT, PS, Z> {
/// Create a new [`PushStageAdapter`], wrapping the given [`PushStage`]
/// to be used as a normal [`Stage`]
#[must_use]
@ -207,25 +223,34 @@ where
}
}
impl<CS, E, EM, I, OT, PS, S, Z> Stage<E, EM, S, Z> for PushStageAdapter<CS, EM, I, OT, PS, S, Z>
impl<CS, EM, OT, PS, Z> UsesState for PushStageAdapter<CS, EM, OT, PS, Z>
where
CS: Scheduler<I, S>,
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId + ProgressReporter<I>,
I: Input,
OT: ObserversTuple<I, S>,
PS: PushStage<CS, EM, I, OT, S, Z>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutesInput<I, OT, S, Z>
+ ExecutionProcessor<I, OT, S>
+ EvaluatorObservers<I, OT, S>
+ HasScheduler<CS, I, S>,
CS: UsesState,
{
type State = CS::State;
}
impl<CS, E, EM, OT, PS, Z> Stage<E, EM, Z> for PushStageAdapter<CS, EM, OT, PS, Z>
where
CS: Scheduler,
CS::State: HasClientPerfMonitor + HasExecutions + HasMetadata + HasRand,
E: Executor<EM, Z> + HasObservers<Observers = OT, State = CS::State>,
EM: EventFirer<State = CS::State>
+ EventRestarter
+ HasEventManagerId
+ ProgressReporter<State = CS::State>,
OT: ObserversTuple<CS::State>,
PS: PushStage<CS, EM, OT, Z>,
Z: ExecutesInput<E, EM, State = CS::State>
+ ExecutionProcessor<OT, State = CS::State>
+ EvaluatorObservers<OT>
+ HasScheduler<CS>,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut CS::State,
event_mgr: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -281,20 +306,19 @@ impl From<bool> for SkippableStageDecision {
/// The [`SkippableStage`] wraps any [`Stage`] so that it can be skipped, according to a condition.
#[derive(Debug, Clone)]
pub struct SkippableStage<CD, E, EM, S, ST, Z>
where
CD: FnMut(&mut S) -> SkippableStageDecision,
ST: Stage<E, EM, S, Z>,
{
pub struct SkippableStage<CD, E, EM, ST, Z> {
wrapped_stage: ST,
condition: CD,
phantom: PhantomData<(E, EM, S, Z)>,
phantom: PhantomData<(E, EM, Z)>,
}
impl<CD, E, EM, S, ST, Z> SkippableStage<CD, E, EM, S, ST, Z>
impl<CD, E, EM, ST, Z> SkippableStage<CD, E, EM, ST, Z>
where
CD: FnMut(&mut S) -> SkippableStageDecision,
ST: Stage<E, EM, S, Z>,
CD: FnMut(&mut ST::State) -> SkippableStageDecision,
ST: Stage<E, EM, Z>,
E: UsesState<State = ST::State>,
EM: UsesState<State = ST::State>,
Z: UsesState<State = ST::State>,
{
/// Create a new [`SkippableStage`]
pub fn new(wrapped_stage: ST, condition: CD) -> Self {
@ -306,10 +330,24 @@ where
}
}
impl<CD, E, EM, S, ST, Z> Stage<E, EM, S, Z> for SkippableStage<CD, E, EM, S, ST, Z>
impl<CD, E, EM, ST, Z> UsesState for SkippableStage<CD, E, EM, ST, Z>
where
CD: FnMut(&mut S) -> SkippableStageDecision,
ST: Stage<E, EM, S, Z>,
CD: FnMut(&mut ST::State) -> SkippableStageDecision,
ST: Stage<E, EM, Z>,
E: UsesState<State = ST::State>,
EM: UsesState<State = ST::State>,
Z: UsesState<State = ST::State>,
{
type State = ST::State;
}
impl<CD, E, EM, ST, Z> Stage<E, EM, Z> for SkippableStage<CD, E, EM, ST, Z>
where
CD: FnMut(&mut ST::State) -> SkippableStageDecision,
ST: Stage<E, EM, Z>,
E: UsesState<State = ST::State>,
EM: UsesState<State = ST::State>,
Z: UsesState<State = ST::State>,
{
/// Run the stage
#[inline]
@ -317,7 +355,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut ST::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -344,7 +382,10 @@ pub mod pybind {
executors::pybind::PythonExecutor,
fuzzer::pybind::{PythonStdFuzzer, PythonStdFuzzerWrapper},
stages::{mutational::pybind::PythonStdMutationalStage, Stage, StagesTuple},
state::pybind::{PythonStdState, PythonStdStateWrapper},
state::{
pybind::{PythonStdState, PythonStdStateWrapper},
UsesState,
},
Error,
};
@ -360,7 +401,11 @@ pub mod pybind {
}
}
impl Stage<PythonExecutor, PythonEventManager, PythonStdState, PythonStdFuzzer> for PyObjectStage {
impl UsesState for PyObjectStage {
type State = PythonStdState;
}
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PyObjectStage {
#[inline]
fn perform(
&mut self,
@ -444,7 +489,11 @@ pub mod pybind {
}
}
impl Stage<PythonExecutor, PythonEventManager, PythonStdState, PythonStdFuzzer> for PythonStage {
impl UsesState for PythonStage {
type State = PythonStdState;
}
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PythonStage {
#[inline]
#[allow(clippy::let_and_return)]
fn perform(

View File

@ -9,12 +9,11 @@ use crate::{
bolts::rands::Rand,
corpus::Corpus,
fuzzer::Evaluator,
inputs::Input,
mark_feature_time,
mutators::Mutator,
stages::Stage,
start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasRand},
state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState},
Error,
};
@ -23,12 +22,13 @@ use crate::{
/// A Mutational stage is the stage in a fuzzing run that mutates inputs.
/// Mutational stages will usually have a range of mutations that are
/// being applied to the input one by one, between executions.
pub trait MutationalStage<E, EM, I, M, S, Z>: Stage<E, EM, S, Z>
pub trait MutationalStage<E, EM, M, Z>: Stage<E, EM, Z>
where
M: Mutator<I, S>,
I: Input,
S: HasClientPerfMonitor + HasCorpus<I>,
Z: Evaluator<E, EM, I, S>,
E: UsesState<State = Self::State>,
M: Mutator<Self::State>,
EM: UsesState<State = Self::State>,
Z: Evaluator<E, EM, State = Self::State>,
Self::State: HasClientPerfMonitor + HasCorpus,
{
/// The mutator registered for this stage
fn mutator(&self) -> &M;
@ -37,7 +37,7 @@ where
fn mutator_mut(&mut self) -> &mut M;
/// Gets the number of iterations this mutator should run for.
fn iterations(&self, state: &mut S, corpus_idx: usize) -> Result<usize, Error>;
fn iterations(&self, state: &mut Z::State, corpus_idx: usize) -> Result<usize, Error>;
/// Runs this (mutational) stage for the given testcase
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
@ -45,7 +45,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut Z::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -82,24 +82,19 @@ pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128;
/// The default mutational stage
#[derive(Clone, Debug)]
pub struct StdMutationalStage<E, EM, I, M, S, Z>
where
M: Mutator<I, S>,
I: Input,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: Evaluator<E, EM, I, S>,
{
pub struct StdMutationalStage<E, EM, M, Z> {
mutator: M,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, I, S, Z)>,
phantom: PhantomData<(E, EM, Z)>,
}
impl<E, EM, I, M, S, Z> MutationalStage<E, EM, I, M, S, Z> for StdMutationalStage<E, EM, I, M, S, Z>
impl<E, EM, M, Z> MutationalStage<E, EM, M, Z> for StdMutationalStage<E, EM, M, Z>
where
M: Mutator<I, S>,
I: Input,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: Evaluator<E, EM, I, S>,
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
/// The mutator, added to this stage
#[inline]
@ -114,17 +109,29 @@ where
}
/// Gets the number of iterations as a random number
fn iterations(&self, state: &mut S, _corpus_idx: usize) -> Result<usize, Error> {
fn iterations(&self, state: &mut Z::State, _corpus_idx: usize) -> Result<usize, Error> {
Ok(1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS) as usize)
}
}
impl<E, EM, I, M, S, Z> Stage<E, EM, S, Z> for StdMutationalStage<E, EM, I, M, S, Z>
impl<E, EM, M, Z> UsesState for StdMutationalStage<E, EM, M, Z>
where
M: Mutator<I, S>,
I: Input,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: Evaluator<E, EM, I, S>,
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
type State = Z::State;
}
impl<E, EM, M, Z> Stage<E, EM, Z> for StdMutationalStage<E, EM, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
#[inline]
#[allow(clippy::let_and_return)]
@ -132,7 +139,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut Z::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -145,12 +152,13 @@ where
}
}
impl<E, EM, I, M, S, Z> StdMutationalStage<E, EM, I, M, S, Z>
impl<E, EM, M, Z> StdMutationalStage<E, EM, M, Z>
where
M: Mutator<I, S>,
I: Input,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: Evaluator<E, EM, I, S>,
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
/// Creates a new default mutational stage
pub fn new(mutator: M) -> Self {
@ -171,10 +179,8 @@ pub mod pybind {
events::pybind::PythonEventManager,
executors::pybind::PythonExecutor,
fuzzer::pybind::PythonStdFuzzer,
inputs::BytesInput,
mutators::pybind::PythonMutator,
stages::{pybind::PythonStage, StdMutationalStage},
state::pybind::PythonStdState,
};
#[pyclass(unsendable, name = "StdMutationalStage")]
@ -182,14 +188,8 @@ pub mod pybind {
/// Python class for StdMutationalStage
pub struct PythonStdMutationalStage {
/// Rust wrapped StdMutationalStage object
pub inner: StdMutationalStage<
PythonExecutor,
PythonEventManager,
BytesInput,
PythonMutator,
PythonStdState,
PythonStdFuzzer,
>,
pub inner:
StdMutationalStage<PythonExecutor, PythonEventManager, PythonMutator, PythonStdFuzzer>,
}
#[pymethods]

View File

@ -5,26 +5,42 @@ use alloc::{boxed::Box, vec::Vec};
use crate::{
bolts::anymap::AsAny,
stages::{Stage, StagesTuple},
state::UsesState,
Error,
};
/// Combine `Stage` and `AsAny`
pub trait AnyStage<E, EM, S, Z>: Stage<E, EM, S, Z> + AsAny {}
pub trait AnyStage<E, EM, Z>: Stage<E, EM, Z> + AsAny
where
E: UsesState<State = Self::State>,
EM: UsesState<State = Self::State>,
Z: UsesState<State = Self::State>,
{
}
/// An owned list of `Observer` trait objects
#[derive(Default)]
#[allow(missing_debug_implementations)]
pub struct StagesOwnedList<E, EM, S, Z> {
pub struct StagesOwnedList<E, EM, Z>
where
E: UsesState,
{
/// The named trait objects map
pub list: Vec<Box<dyn AnyStage<E, EM, S, Z>>>,
#[allow(clippy::type_complexity)]
pub list: Vec<Box<dyn AnyStage<E, EM, Z, State = E::State, Input = E::Input>>>,
}
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for StagesOwnedList<E, EM, S, Z> {
impl<E, EM, Z> StagesTuple<E, EM, E::State, Z> for StagesOwnedList<E, EM, Z>
where
E: UsesState,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
fn perform_all(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut E::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -35,10 +51,14 @@ impl<E, EM, S, Z> StagesTuple<E, EM, S, Z> for StagesOwnedList<E, EM, S, Z> {
}
}
impl<E, EM, S, Z> StagesOwnedList<E, EM, S, Z> {
impl<E, EM, Z> StagesOwnedList<E, EM, Z>
where
E: UsesState,
{
/// Create a new instance
#[must_use]
pub fn new(list: Vec<Box<dyn AnyStage<E, EM, S, Z>>>) -> Self {
#[allow(clippy::type_complexity)]
pub fn new(list: Vec<Box<dyn AnyStage<E, EM, Z, Input = E::Input, State = E::State>>>) -> Self {
Self { list }
}
}

View File

@ -4,49 +4,45 @@ use alloc::string::{String, ToString};
use core::{fmt::Debug, marker::PhantomData};
use crate::{
bolts::tuples::MatchName,
corpus::{Corpus, SchedulerTestcaseMetaData},
executors::{Executor, HasObservers},
fuzzer::Evaluator,
inputs::Input,
mutators::Mutator,
observers::{MapObserver, ObserversTuple},
observers::MapObserver,
schedulers::{
powersched::SchedulerMetadata, testcase_score::CorpusPowerTestcaseScore, TestcaseScore,
},
stages::{MutationalStage, Stage},
state::{HasClientPerfMonitor, HasCorpus, HasMetadata, HasRand},
state::{HasClientPerfMonitor, HasCorpus, HasMetadata, HasRand, UsesState},
Error,
};
/// The mutational stage using power schedules
#[derive(Clone, Debug)]
pub struct PowerMutationalStage<E, F, EM, I, M, O, OT, S, Z>
where
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
F: TestcaseScore<I, S>,
I: Input,
M: Mutator<I, S>,
O: MapObserver,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasMetadata,
Z: Evaluator<E, EM, I, S>,
{
pub struct PowerMutationalStage<E, F, EM, M, O, Z> {
map_observer_name: String,
mutator: M,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, F, EM, I, O, OT, S, Z)>,
phantom: PhantomData<(E, F, EM, O, Z)>,
}
impl<E, F, EM, I, M, O, OT, S, Z> MutationalStage<E, EM, I, M, S, Z>
for PowerMutationalStage<E, F, EM, I, M, O, OT, S, Z>
impl<E, F, EM, M, O, Z> UsesState for PowerMutationalStage<E, F, EM, M, O, Z>
where
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
F: TestcaseScore<I, S>,
I: Input,
M: Mutator<I, S>,
E: UsesState,
{
type State = E::State;
}
impl<E, F, EM, M, O, Z> MutationalStage<E, EM, M, Z> for PowerMutationalStage<E, F, EM, M, O, Z>
where
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = E::State>,
F: TestcaseScore<E::State>,
M: Mutator<E::State>,
O: MapObserver,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasMetadata + HasRand,
Z: Evaluator<E, EM, I, S>,
E::State: HasClientPerfMonitor + HasCorpus + HasMetadata + HasRand,
Z: Evaluator<E, EM, State = E::State>,
{
/// The mutator, added to this stage
#[inline]
@ -62,7 +58,7 @@ where
/// Gets the number of iterations as a random number
#[allow(clippy::cast_sign_loss)]
fn iterations(&self, state: &mut S, corpus_idx: usize) -> Result<usize, Error> {
fn iterations(&self, state: &mut E::State, corpus_idx: usize) -> Result<usize, Error> {
// Update handicap
let mut testcase = state.corpus().get(corpus_idx)?.borrow_mut();
let score = F::compute(&mut *testcase, state)? as usize;
@ -75,7 +71,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut E::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -129,17 +125,15 @@ where
}
}
impl<E, F, EM, I, M, O, OT, S, Z> Stage<E, EM, S, Z>
for PowerMutationalStage<E, F, EM, I, M, O, OT, S, Z>
impl<E, F, EM, M, O, Z> Stage<E, EM, Z> for PowerMutationalStage<E, F, EM, M, O, Z>
where
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
F: TestcaseScore<I, S>,
I: Input,
M: Mutator<I, S>,
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = E::State>,
F: TestcaseScore<E::State>,
M: Mutator<E::State>,
O: MapObserver,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasMetadata + HasRand,
Z: Evaluator<E, EM, I, S>,
E::State: HasClientPerfMonitor + HasCorpus + HasMetadata + HasRand,
Z: Evaluator<E, EM, State = E::State>,
{
#[inline]
#[allow(clippy::let_and_return)]
@ -147,7 +141,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut E::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -156,16 +150,15 @@ where
}
}
impl<E, F, EM, I, M, O, OT, S, Z> PowerMutationalStage<E, F, EM, I, M, O, OT, S, Z>
impl<E, F, EM, M, O, Z> PowerMutationalStage<E, F, EM, M, O, Z>
where
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
F: TestcaseScore<I, S>,
I: Input,
M: Mutator<I, S>,
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = E::State>,
F: TestcaseScore<E::State>,
M: Mutator<E::State>,
O: MapObserver,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasMetadata,
Z: Evaluator<E, EM, I, S>,
E::State: HasClientPerfMonitor + HasCorpus + HasMetadata + HasRand,
Z: Evaluator<E, EM, State = E::State>,
{
/// Creates a new [`PowerMutationalStage`]
pub fn new(mutator: M, map_observer_name: &O) -> Self {
@ -178,5 +171,5 @@ where
}
/// The standard powerscheduling stage
pub type StdPowerMutationalStage<E, EM, I, M, O, OT, S, Z> =
PowerMutationalStage<E, CorpusPowerTestcaseScore<I, S>, EM, I, M, O, OT, S, Z>;
pub type StdPowerMutationalStage<E, EM, M, O, Z> =
PowerMutationalStage<E, CorpusPowerTestcaseScore<<E as UsesState>::State>, EM, M, O, Z>;

View File

@ -19,10 +19,10 @@ use crate::{
bolts::current_time,
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
executors::ExitKind,
inputs::Input,
inputs::UsesInput,
observers::ObserversTuple,
schedulers::Scheduler,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasRand},
state::{HasClientPerfMonitor, HasExecutions, HasMetadata, HasRand},
Error, EvaluatorObservers, ExecutionProcessor, HasScheduler,
};
@ -32,38 +32,36 @@ const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15);
// The shared state for all [`PushStage`]s
/// Should be stored inside a `[Rc<RefCell<_>>`]
#[derive(Clone, Debug)]
pub struct PushStageSharedState<CS, EM, I, OT, S, Z>
pub struct PushStageSharedState<CS, EM, OT, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId,
I: Input,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasRand,
Z: ExecutionProcessor<OT, State = CS::State> + EvaluatorObservers<OT> + HasScheduler<CS>,
{
/// The [`crate::state::State`]
pub state: S,
pub state: CS::State,
/// The [`crate::fuzzer::Fuzzer`] instance
pub fuzzer: Z,
/// The [`crate::events::EventManager`]
pub event_mgr: EM,
/// The [`crate::observers::ObserversTuple`]
pub observers: OT,
phantom: PhantomData<(CS, I, OT, S, Z)>,
phantom: PhantomData<(CS, Z)>,
}
impl<CS, EM, I, OT, S, Z> PushStageSharedState<CS, EM, I, OT, S, Z>
impl<CS, EM, OT, Z> PushStageSharedState<CS, EM, OT, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId,
I: Input,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasRand,
Z: ExecutionProcessor<OT, State = CS::State> + EvaluatorObservers<OT> + HasScheduler<CS>,
{
/// Create a new `PushStageSharedState` that can be used by all [`PushStage`]s
#[must_use]
pub fn new(fuzzer: Z, state: S, observers: OT, event_mgr: EM) -> Self {
pub fn new(fuzzer: Z, state: CS::State, observers: OT, event_mgr: EM) -> Self {
Self {
state,
fuzzer,
@ -76,14 +74,13 @@ where
/// Helper class for the [`PushStage`] trait, taking care of borrowing the shared state
#[derive(Clone, Debug)]
pub struct PushStageHelper<CS, EM, I, OT, S, Z>
pub struct PushStageHelper<CS, EM, OT, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId,
I: Input,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasRand,
Z: ExecutionProcessor<OT, State = CS::State> + EvaluatorObservers<OT> + HasScheduler<CS>,
{
/// If this stage has already been initalized.
/// This gets reset to `false` after one iteration of the stage is done.
@ -92,7 +89,7 @@ where
pub last_monitor_time: Duration,
/// The shared state, keeping track of the corpus and the fuzzer
#[allow(clippy::type_complexity)]
pub shared_state: Rc<RefCell<Option<PushStageSharedState<CS, EM, I, OT, S, Z>>>>,
pub shared_state: Rc<RefCell<Option<PushStageSharedState<CS, EM, OT, Z>>>>,
/// If the last iteration failed
pub errored: bool,
@ -100,27 +97,26 @@ where
pub current_corpus_idx: Option<usize>,
/// The input we just ran
pub current_input: Option<I>, // Todo: Get rid of copy
pub current_input: Option<<CS::State as UsesInput>::Input>, // Todo: Get rid of copy
#[allow(clippy::type_complexity)]
phantom: PhantomData<(CS, (), EM, I, OT, S, Z)>,
phantom: PhantomData<(CS, EM, OT, Z)>,
exit_kind: Rc<Cell<Option<ExitKind>>>,
}
impl<CS, EM, I, OT, S, Z> PushStageHelper<CS, EM, I, OT, S, Z>
impl<CS, EM, OT, Z> PushStageHelper<CS, EM, OT, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId,
I: Input,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasRand,
Z: ExecutionProcessor<OT, State = CS::State> + EvaluatorObservers<OT> + HasScheduler<CS>,
{
/// Create a new [`PushStageHelper`]
#[must_use]
#[allow(clippy::type_complexity)]
pub fn new(
shared_state: Rc<RefCell<Option<PushStageSharedState<CS, EM, I, OT, S, Z>>>>,
shared_state: Rc<RefCell<Option<PushStageSharedState<CS, EM, OT, Z>>>>,
exit_kind_ref: Rc<Cell<Option<ExitKind>>>,
) -> Self {
Self {
@ -137,14 +133,14 @@ where
/// Sets the shared state for this helper (and all other helpers owning the same [`RefCell`])
#[inline]
pub fn set_shared_state(&mut self, shared_state: PushStageSharedState<CS, EM, I, OT, S, Z>) {
pub fn set_shared_state(&mut self, shared_state: PushStageSharedState<CS, EM, OT, Z>) {
(*self.shared_state.borrow_mut()).replace(shared_state);
}
/// Takes the shared state from this helper, replacing it with `None`
#[inline]
#[allow(clippy::type_complexity)]
pub fn take_shared_state(&mut self) -> Option<PushStageSharedState<CS, EM, I, OT, S, Z>> {
pub fn take_shared_state(&mut self) -> Option<PushStageSharedState<CS, EM, OT, Z>> {
let shared_state_ref = &mut (*self.shared_state).borrow_mut();
shared_state_ref.take()
}
@ -163,11 +159,7 @@ where
}
/// Resets this state after a full stage iter.
fn end_of_iter(
&mut self,
shared_state: PushStageSharedState<CS, EM, I, OT, S, Z>,
errored: bool,
) {
fn end_of_iter(&mut self, shared_state: PushStageSharedState<CS, EM, OT, Z>, errored: bool) {
self.set_shared_state(shared_state);
self.errored = errored;
self.current_corpus_idx = None;
@ -180,19 +172,18 @@ where
/// A push stage is a generator that returns a single testcase for each call.
/// It's an iterator so we can chain it.
/// After it has finished once, we will call it agan for the next fuzzer round.
pub trait PushStage<CS, EM, I, OT, S, Z>: Iterator
pub trait PushStage<CS, EM, OT, Z>: Iterator
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId + ProgressReporter<I>,
I: Input,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
CS: Scheduler,
CS::State: HasClientPerfMonitor + HasRand + HasExecutions + HasMetadata,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId + ProgressReporter,
OT: ObserversTuple<CS::State>,
Z: ExecutionProcessor<OT, State = CS::State> + EvaluatorObservers<OT> + HasScheduler<CS>,
{
/// Gets the [`PushStageHelper`]
fn push_stage_helper(&self) -> &PushStageHelper<CS, EM, I, OT, S, Z>;
fn push_stage_helper(&self) -> &PushStageHelper<CS, EM, OT, Z>;
/// Gets the [`PushStageHelper`] (mutable)
fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper<CS, EM, I, OT, S, Z>;
fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper<CS, EM, OT, Z>;
/// Set the current corpus index this stage works on
fn set_current_corpus_idx(&mut self, corpus_idx: usize) {
@ -206,7 +197,7 @@ where
fn init(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_state: &mut CS::State,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Result<(), Error> {
@ -219,20 +210,20 @@ where
fn pre_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_state: &mut CS::State,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Option<Result<I, Error>>;
) -> Option<Result<<CS::State as UsesInput>::Input, Error>>;
/// Called after the execution of a testcase finished.
#[inline]
fn post_exec(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_state: &mut CS::State,
_event_mgr: &mut EM,
_observers: &mut OT,
_input: I,
_input: <CS::State as UsesInput>::Input,
_exit_kind: ExitKind,
) -> Result<(), Error> {
Ok(())
@ -243,7 +234,7 @@ where
fn deinit(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_state: &mut CS::State,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Result<(), Error> {
@ -251,7 +242,7 @@ where
}
/// This is the default implementation for `next` for this stage
fn next_std(&mut self) -> Option<Result<I, Error>> {
fn next_std(&mut self) -> Option<Result<<CS::State as UsesInput>::Input, 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

@ -2,7 +2,10 @@
//! For the current input, it will perform a range of random mutations, and then run them in the executor.
use alloc::rc::Rc;
use core::cell::{Cell, RefCell};
use core::{
cell::{Cell, RefCell},
fmt::Debug,
};
use super::{PushStage, PushStageHelper, PushStageSharedState};
#[cfg(feature = "introspection")]
@ -12,7 +15,7 @@ use crate::{
corpus::Corpus,
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
executors::ExitKind,
inputs::Input,
inputs::UsesInput,
mark_feature_time,
mutators::Mutator,
observers::ObserversTuple,
@ -34,15 +37,14 @@ pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128;
///
/// The default mutational push stage
#[derive(Clone, Debug)]
pub struct StdMutationalPushStage<CS, EM, I, M, OT, S, Z>
pub struct StdMutationalPushStage<CS, EM, M, OT, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId,
I: Input,
M: Mutator<I, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId,
M: Mutator<CS::State>,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasRand + Clone + Debug,
Z: ExecutionProcessor<OT, State = CS::State> + EvaluatorObservers<OT> + HasScheduler<CS>,
{
current_corpus_idx: Option<usize>,
testcases_to_do: usize,
@ -52,22 +54,21 @@ where
mutator: M,
psh: PushStageHelper<CS, EM, I, OT, S, Z>,
psh: PushStageHelper<CS, EM, OT, Z>,
}
impl<CS, EM, I, M, OT, S, Z> StdMutationalPushStage<CS, EM, I, M, OT, S, Z>
impl<CS, EM, M, OT, Z> StdMutationalPushStage<CS, EM, M, OT, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId,
I: Input,
M: Mutator<I, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId,
M: Mutator<CS::State>,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasCorpus + HasRand + Clone + Debug,
Z: ExecutionProcessor<OT, State = CS::State> + EvaluatorObservers<OT> + HasScheduler<CS>,
{
/// Gets the number of iterations as a random number
#[allow(clippy::unused_self, clippy::unnecessary_wraps)] // TODO: we should put this function into a trait later
fn iterations(&self, state: &mut S, _corpus_idx: usize) -> Result<usize, Error> {
fn iterations(&self, state: &mut CS::State, _corpus_idx: usize) -> Result<usize, Error> {
Ok(1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS) as usize)
}
@ -77,22 +78,31 @@ where
}
}
impl<CS, EM, I, M, OT, S, Z> PushStage<CS, EM, I, OT, S, Z>
for StdMutationalPushStage<CS, EM, I, M, OT, S, Z>
impl<CS, EM, M, OT, Z> PushStage<CS, EM, OT, Z> for StdMutationalPushStage<CS, EM, M, OT, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId + ProgressReporter<I>,
I: Input,
M: Mutator<I, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId + ProgressReporter,
M: Mutator<CS::State>,
OT: ObserversTuple<CS::State>,
CS::State:
HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions + HasMetadata + Clone + Debug,
Z: ExecutionProcessor<OT, State = CS::State> + EvaluatorObservers<OT> + HasScheduler<CS>,
{
#[inline]
fn push_stage_helper(&self) -> &PushStageHelper<CS, EM, OT, Z> {
&self.psh
}
#[inline]
fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper<CS, EM, OT, Z> {
&mut self.psh
}
/// Creates a new default mutational stage
fn init(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut CS::State,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Result<(), Error> {
@ -108,25 +118,13 @@ where
Ok(())
}
#[inline]
fn deinit(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Result<(), Error> {
self.current_corpus_idx = None;
Ok(())
}
fn pre_exec(
&mut self,
_fuzzer: &mut Z,
state: &mut S,
state: &mut CS::State,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Option<Result<I, Error>> {
) -> Option<Result<<CS::State as UsesInput>::Input, Error>> {
if self.testcases_done >= self.testcases_to_do {
// finished with this cicle.
return None;
@ -159,10 +157,10 @@ where
fn post_exec(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut CS::State,
event_mgr: &mut EM,
observers: &mut OT,
last_input: I,
last_input: <CS::State as UsesInput>::Input,
exit_kind: ExitKind,
) -> Result<(), Error> {
// todo: isintersting, etc.
@ -179,49 +177,50 @@ where
}
#[inline]
fn push_stage_helper(&self) -> &PushStageHelper<CS, EM, I, OT, S, Z> {
&self.psh
}
#[inline]
fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper<CS, EM, I, OT, S, Z> {
&mut self.psh
fn deinit(
&mut self,
_fuzzer: &mut Z,
_state: &mut CS::State,
_event_mgr: &mut EM,
_observers: &mut OT,
) -> Result<(), Error> {
self.current_corpus_idx = None;
Ok(())
}
}
impl<CS, EM, I, M, OT, S, Z> Iterator for StdMutationalPushStage<CS, EM, I, M, OT, S, Z>
impl<CS, EM, M, OT, Z> Iterator for StdMutationalPushStage<CS, EM, M, OT, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId + ProgressReporter<I>,
I: Input,
M: Mutator<I, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasExecutions + HasMetadata,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
CS: Scheduler,
EM: EventFirer + EventRestarter + HasEventManagerId + ProgressReporter<State = CS::State>,
M: Mutator<CS::State>,
OT: ObserversTuple<CS::State>,
CS::State:
HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions + HasMetadata + Clone + Debug,
Z: ExecutionProcessor<OT, State = CS::State> + EvaluatorObservers<OT> + HasScheduler<CS>,
{
type Item = Result<I, Error>;
type Item = Result<<CS::State as UsesInput>::Input, Error>;
fn next(&mut self) -> Option<Result<I, Error>> {
fn next(&mut self) -> Option<Result<<CS::State as UsesInput>::Input, Error>> {
self.next_std()
}
}
impl<CS, EM, I, M, OT, S, Z> StdMutationalPushStage<CS, EM, I, M, OT, S, Z>
impl<CS, EM, M, OT, Z> StdMutationalPushStage<CS, EM, M, OT, Z>
where
CS: Scheduler<I, S>,
EM: EventFirer<I> + EventRestarter<S> + HasEventManagerId,
I: Input,
M: Mutator<I, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand,
Z: ExecutionProcessor<I, OT, S> + EvaluatorObservers<I, OT, S> + HasScheduler<CS, I, S>,
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId,
M: Mutator<CS::State>,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasCorpus + HasRand + Clone + Debug,
Z: ExecutionProcessor<OT, State = CS::State> + EvaluatorObservers<OT> + HasScheduler<CS>,
{
/// Creates a new default mutational stage
#[must_use]
#[allow(clippy::type_complexity)]
pub fn new(
mutator: M,
shared_state: Rc<RefCell<Option<PushStageSharedState<CS, EM, I, OT, S, Z>>>>,
shared_state: Rc<RefCell<Option<PushStageSharedState<CS, EM, OT, Z>>>>,
exit_kind: Rc<Cell<Option<ExitKind>>>,
stage_idx: i32,
) -> Self {

View File

@ -12,9 +12,9 @@ use serde::{Deserialize, Serialize};
use crate::{
fuzzer::Evaluator,
inputs::Input,
inputs::{Input, UsesInput},
stages::Stage,
state::{HasClientPerfMonitor, HasCorpus, HasMetadata, HasRand},
state::{HasClientPerfMonitor, HasCorpus, HasMetadata, HasRand, UsesState},
Error,
};
@ -37,32 +37,33 @@ 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, I, S, Z>
where
CB: FnMut(&mut Z, &mut S, &Path) -> Result<I, Error>,
I: Input,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasMetadata,
Z: Evaluator<E, EM, I, S>,
{
pub struct SyncFromDiskStage<CB, E, EM, Z> {
sync_dir: PathBuf,
load_callback: CB,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, I, S, Z)>,
phantom: PhantomData<(E, EM, Z)>,
}
impl<CB, E, EM, I, S, Z> Stage<E, EM, S, Z> for SyncFromDiskStage<CB, E, EM, I, S, Z>
impl<CB, E, EM, Z> UsesState for SyncFromDiskStage<CB, E, EM, Z>
where
CB: FnMut(&mut Z, &mut S, &Path) -> Result<I, Error>,
I: Input,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasMetadata,
Z: Evaluator<E, EM, I, S>,
E: UsesState,
{
type State = E::State;
}
impl<CB, E, EM, Z> Stage<E, EM, Z> for SyncFromDiskStage<CB, E, EM, Z>
where
CB: FnMut(&mut Z, &mut Z::State, &Path) -> Result<<Z::State as UsesInput>::Input, Error>,
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut Z::State,
manager: &mut EM,
_corpus_idx: usize,
) -> Result<(), Error> {
@ -94,12 +95,13 @@ where
}
}
impl<CB, E, EM, I, S, Z> SyncFromDiskStage<CB, E, EM, I, S, Z>
impl<CB, E, EM, Z> SyncFromDiskStage<CB, E, EM, Z>
where
CB: FnMut(&mut Z, &mut S, &Path) -> Result<I, Error>,
I: Input,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasMetadata,
Z: Evaluator<E, EM, I, S>,
CB: FnMut(&mut Z, &mut Z::State, &Path) -> Result<<Z::State as UsesInput>::Input, Error>,
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
{
/// Creates a new [`SyncFromDiskStage`]
#[must_use]
@ -117,7 +119,7 @@ where
last: &Option<SystemTime>,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut Z::State,
manager: &mut EM,
) -> Result<Option<SystemTime>, Error> {
let mut max_time = None;
@ -157,23 +159,29 @@ where
}
/// Function type when the callback in `SyncFromDiskStage` is not a lambda
pub type SyncFromDiskFunction<I, S, Z> = fn(&mut Z, &mut S, &Path) -> Result<I, Error>;
pub type SyncFromDiskFunction<S, Z> =
fn(&mut Z, &mut S, &Path) -> Result<<S as UsesInput>::Input, Error>;
impl<E, EM, I, S, Z> SyncFromDiskStage<SyncFromDiskFunction<I, S, Z>, E, EM, I, S, Z>
impl<E, EM, Z> SyncFromDiskStage<SyncFromDiskFunction<Z::State, Z>, E, EM, Z>
where
I: Input,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasMetadata,
Z: Evaluator<E, EM, I, S>,
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
{
/// Creates a new [`SyncFromDiskStage`] invoking `Input::from_file` to load inputs
#[must_use]
pub fn with_from_file(sync_dir: PathBuf) -> Self {
fn load_callback<Z, S, I: Input>(_: &mut Z, _: &mut S, p: &Path) -> Result<I, Error> {
I::from_file(p)
fn load_callback<S: UsesInput, Z>(
_: &mut Z,
_: &mut S,
p: &Path,
) -> Result<S::Input, Error> {
Input::from_file(p)
}
Self {
sync_dir,
load_callback: load_callback::<_, _, I>,
load_callback: load_callback::<_, _>,
phantom: PhantomData,
}
}

View File

@ -17,36 +17,36 @@ use crate::{
events::EventFirer,
executors::{Executor, ExitKind, HasObservers},
feedbacks::{Feedback, FeedbackFactory, HasObserverName},
inputs::Input,
inputs::UsesInput,
mark_feature_time,
mutators::Mutator,
observers::{MapObserver, ObserversTuple},
schedulers::Scheduler,
stages::Stage,
start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMaxSize},
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMaxSize, UsesState},
Error, ExecutesInput, ExecutionProcessor, HasFeedback, HasScheduler,
};
/// Mutational stage which minimizes corpus entries.
///
/// You must provide at least one mutator that actually reduces size.
pub trait TMinMutationalStage<CS, E, EM, F1, F2, I, M, OT, S, Z>:
Stage<E, EM, S, Z> + FeedbackFactory<F2, I, S, OT>
pub trait TMinMutationalStage<CS, E, EM, F1, F2, M, OT, Z>:
Stage<E, EM, Z> + FeedbackFactory<F2, CS::State, OT>
where
CS: Scheduler<I, S>,
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
EM: EventFirer<I>,
F1: Feedback<I, S>,
F2: Feedback<I, S>,
I: Input + Hash + HasLen,
M: Mutator<I, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasExecutions + HasMaxSize,
Z: ExecutionProcessor<I, OT, S>
+ ExecutesInput<I, OT, S, Z>
+ HasFeedback<F1, I, S>
+ HasScheduler<CS, I, S>,
Self::State: HasCorpus + HasExecutions + HasMaxSize + HasClientPerfMonitor,
<Self::State as UsesInput>::Input: HasLen + Hash,
CS: Scheduler<State = Self::State>,
E: Executor<EM, Z> + HasObservers<Observers = OT, State = Self::State>,
EM: EventFirer<State = Self::State>,
F1: Feedback<Self::State>,
F2: Feedback<Self::State>,
M: Mutator<Self::State>,
OT: ObserversTuple<CS::State>,
Z: ExecutionProcessor<OT, State = Self::State>
+ ExecutesInput<E, EM>
+ HasFeedback<F1>
+ HasScheduler<CS>,
{
/// The mutator registered for this stage
fn mutator(&self) -> &M;
@ -55,7 +55,7 @@ where
fn mutator_mut(&mut self) -> &mut M;
/// Gets the number of iterations this mutator should run for.
fn iterations(&self, state: &mut S, corpus_idx: usize) -> Result<usize, Error>;
fn iterations(&self, state: &mut CS::State, corpus_idx: usize) -> Result<usize, Error>;
/// Runs this (mutational) stage for new objectives
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
@ -63,7 +63,7 @@ where
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut CS::State,
manager: &mut EM,
base_corpus_idx: usize,
) -> Result<(), Error> {
@ -165,41 +165,47 @@ where
/// The default corpus entry minimising mutational stage
#[derive(Clone, Debug)]
pub struct StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, M, S, T, Z>
where
I: Input + HasLen,
M: Mutator<I, S>,
{
pub struct StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> {
mutator: M,
factory: FF,
runs: usize,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(CS, E, EM, F1, F2, I, S, T, Z)>,
phantom: PhantomData<(CS, E, EM, F1, F2, OT, Z)>,
}
impl<CS, E, EM, F1, F2, FF, I, M, OT, S, Z> Stage<E, EM, S, Z>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, M, S, OT, Z>
impl<CS, E, EM, F1, F2, FF, M, OT, Z> UsesState
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
CS: Scheduler<I, S>,
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
EM: EventFirer<I>,
F1: Feedback<I, S>,
F2: Feedback<I, S>,
FF: FeedbackFactory<F2, I, S, OT>,
I: Input + Hash + HasLen,
M: Mutator<I, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasExecutions + HasMaxSize,
Z: ExecutionProcessor<I, OT, S>
+ ExecutesInput<I, OT, S, Z>
+ HasFeedback<F1, I, S>
+ HasScheduler<CS, I, S>,
CS: Scheduler,
M: Mutator<CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>,
{
type State = CS::State;
}
impl<CS, E, EM, F1, F2, FF, M, OT, Z> Stage<E, EM, Z>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
CS: Scheduler,
CS::State: HasCorpus + HasExecutions + HasMaxSize + HasClientPerfMonitor,
<CS::State as UsesInput>::Input: HasLen + Hash,
E: Executor<EM, Z> + HasObservers<Observers = OT, State = CS::State>,
EM: EventFirer<State = CS::State>,
F1: Feedback<CS::State>,
F2: Feedback<CS::State>,
FF: FeedbackFactory<F2, CS::State, OT>,
M: Mutator<CS::State>,
OT: ObserversTuple<CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>
+ ExecutesInput<E, EM>
+ HasFeedback<F1>
+ HasScheduler<CS>,
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
state: &mut CS::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -212,37 +218,36 @@ where
}
}
impl<CS, E, EM, F1, F2, FF, I, M, S, T, Z> FeedbackFactory<F2, I, S, T>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, M, S, T, Z>
impl<CS, E, EM, F1, F2, FF, M, OT, Z> FeedbackFactory<F2, Z::State, OT>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
F2: Feedback<I, S>,
FF: FeedbackFactory<F2, I, S, T>,
I: Input + HasLen,
M: Mutator<I, S>,
S: HasClientPerfMonitor,
F2: Feedback<Z::State>,
FF: FeedbackFactory<F2, Z::State, OT>,
Z: UsesState,
Z::State: HasClientPerfMonitor,
{
fn create_feedback(&self, ctx: &T) -> F2 {
fn create_feedback(&self, ctx: &OT) -> F2 {
self.factory.create_feedback(ctx)
}
}
impl<CS, E, EM, F1, F2, FF, I, M, OT, S, Z> TMinMutationalStage<CS, E, EM, F1, F2, I, M, OT, S, Z>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, M, S, OT, Z>
impl<CS, E, EM, F1, F2, FF, M, OT, Z> TMinMutationalStage<CS, E, EM, F1, F2, M, OT, Z>
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
CS: Scheduler<I, S>,
E: HasObservers<I, OT, S> + Executor<EM, I, S, Z>,
EM: EventFirer<I>,
F1: Feedback<I, S>,
F2: Feedback<I, S>,
FF: FeedbackFactory<F2, I, S, OT>,
I: Input + HasLen + Hash,
M: Mutator<I, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasCorpus<I> + HasExecutions + HasMaxSize,
Z: ExecutionProcessor<I, OT, S>
+ ExecutesInput<I, OT, S, Z>
+ HasFeedback<F1, I, S>
+ HasScheduler<CS, I, S>,
CS: Scheduler,
E: HasObservers<Observers = OT, State = CS::State> + Executor<EM, Z>,
EM: EventFirer<State = CS::State>,
F1: Feedback<CS::State>,
F2: Feedback<CS::State>,
FF: FeedbackFactory<F2, CS::State, OT>,
<CS::State as UsesInput>::Input: HasLen + Hash,
M: Mutator<CS::State>,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasCorpus + HasExecutions + HasMaxSize,
Z: ExecutionProcessor<OT, State = CS::State>
+ ExecutesInput<E, EM>
+ HasFeedback<F1>
+ HasScheduler<CS>,
{
/// The mutator, added to this stage
#[inline]
@ -257,16 +262,16 @@ where
}
/// Gets the number of iterations from a fixed number of runs
fn iterations(&self, _state: &mut S, _corpus_idx: usize) -> Result<usize, Error> {
fn iterations(&self, _state: &mut CS::State, _corpus_idx: usize) -> Result<usize, Error> {
Ok(self.runs)
}
}
impl<CS, E, EM, F1, F2, FF, I, M, S, T, Z>
StdTMinMutationalStage<CS, E, EM, F1, F2, FF, I, M, S, T, Z>
impl<CS, E, EM, F1, F2, FF, M, OT, Z> StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
I: Input + HasLen,
M: Mutator<I, S>,
CS: Scheduler,
M: Mutator<CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>,
{
/// Creates a new minimising mutational stage that will minimize provided corpus entries
pub fn new(mutator: M, factory: FF, runs: usize) -> Self {
@ -282,14 +287,14 @@ where
/// A feedback which checks if the hash of the currently observed map is equal to the original hash
/// provided
#[derive(Clone, Debug)]
pub struct MapEqualityFeedback<M> {
pub struct MapEqualityFeedback<M, S> {
name: String,
obs_name: String,
orig_hash: u64,
phantom: PhantomData<M>,
phantom: PhantomData<(M, S)>,
}
impl<M> MapEqualityFeedback<M> {
impl<M, S> MapEqualityFeedback<M, S> {
/// Create a new map equality feedback -- can be used with feedback logic
#[must_use]
pub fn new(name: &str, obs_name: &str, orig_hash: u64) -> Self {
@ -302,35 +307,34 @@ impl<M> MapEqualityFeedback<M> {
}
}
impl<M> Named for MapEqualityFeedback<M> {
impl<M, S> Named for MapEqualityFeedback<M, S> {
fn name(&self) -> &str {
&self.name
}
}
impl<M> HasObserverName for MapEqualityFeedback<M> {
impl<M, S> HasObserverName for MapEqualityFeedback<M, S> {
fn observer_name(&self) -> &str {
&self.obs_name
}
}
impl<I, M, S> Feedback<I, S> for MapEqualityFeedback<M>
impl<M, S> Feedback<S> for MapEqualityFeedback<M, S>
where
I: Input,
M: MapObserver,
S: HasClientPerfMonitor,
M: MapObserver + Debug,
S: UsesInput + HasClientPerfMonitor + Debug,
{
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &<S as UsesInput>::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
let obs = observers
.match_name::<M>(self.observer_name())
@ -341,12 +345,12 @@ where
/// A feedback factory for ensuring that the maps for minimized inputs are the same
#[derive(Debug, Clone)]
pub struct MapEqualityFactory<M> {
pub struct MapEqualityFactory<M, S> {
obs_name: String,
phantom: PhantomData<M>,
phantom: PhantomData<(M, S)>,
}
impl<M> MapEqualityFactory<M>
impl<M, S> MapEqualityFactory<M, S>
where
M: MapObserver,
{
@ -359,20 +363,19 @@ where
}
}
impl<M> HasObserverName for MapEqualityFactory<M> {
impl<M, S> HasObserverName for MapEqualityFactory<M, S> {
fn observer_name(&self) -> &str {
&self.obs_name
}
}
impl<I, M, OT, S> FeedbackFactory<MapEqualityFeedback<M>, I, S, OT> for MapEqualityFactory<M>
impl<M, OT, S> FeedbackFactory<MapEqualityFeedback<M, S>, S, OT> for MapEqualityFactory<M, S>
where
I: Input,
M: MapObserver,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor,
OT: ObserversTuple<S>,
S: UsesInput + HasClientPerfMonitor + Debug,
{
fn create_feedback(&self, observers: &OT) -> MapEqualityFeedback<M> {
fn create_feedback(&self, observers: &OT) -> MapEqualityFeedback<M, S> {
let obs = observers
.match_name::<M>(self.observer_name())
.expect("Should have been provided valid observer name.");

View File

@ -7,42 +7,43 @@ use crate::monitors::PerfFeature;
use crate::{
corpus::Corpus,
executors::{Executor, HasObservers, ShadowExecutor},
inputs::Input,
mark_feature_time,
observers::ObserversTuple,
stages::Stage,
start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions},
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, State, UsesState},
Error,
};
/// A stage that runs a tracer executor
#[derive(Clone, Debug)]
pub struct TracingStage<EM, I, OT, S, TE, Z>
where
I: Input,
TE: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I>,
{
pub struct TracingStage<EM, TE, Z> {
tracer_executor: TE,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(EM, I, OT, S, TE, Z)>,
phantom: PhantomData<(EM, TE, Z)>,
}
impl<E, EM, I, OT, S, TE, Z> Stage<E, EM, S, Z> for TracingStage<EM, I, OT, S, TE, Z>
impl<EM, TE, Z> UsesState for TracingStage<EM, TE, Z>
where
I: Input,
TE: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I>,
TE: UsesState,
{
type State = TE::State;
}
impl<E, EM, TE, Z> Stage<E, EM, Z> for TracingStage<EM, TE, Z>
where
E: UsesState<State = TE::State>,
TE: Executor<EM, Z> + HasObservers,
TE::State: HasClientPerfMonitor + HasExecutions + HasCorpus,
EM: UsesState<State = TE::State>,
Z: UsesState<State = TE::State>,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
_executor: &mut E,
state: &mut S,
state: &mut TE::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -79,13 +80,7 @@ where
}
}
impl<EM, I, OT, S, TE, Z> TracingStage<EM, I, OT, S, TE, Z>
where
I: Input,
TE: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I>,
{
impl<EM, TE, Z> TracingStage<EM, TE, Z> {
/// Creates a new default stage
pub fn new(tracer_executor: TE) -> Self {
Self {
@ -102,26 +97,32 @@ where
/// A stage that runs the shadow executor using also the shadow observers
#[derive(Clone, Debug)]
pub struct ShadowTracingStage<E, EM, I, OT, S, SOT, Z> {
pub struct ShadowTracingStage<E, EM, SOT, Z> {
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, I, OT, S, SOT, Z)>,
phantom: PhantomData<(E, EM, SOT, Z)>,
}
impl<E, EM, I, OT, S, SOT, Z> Stage<ShadowExecutor<E, I, S, SOT>, EM, S, Z>
for ShadowTracingStage<E, EM, I, OT, S, SOT, Z>
impl<E, EM, SOT, Z> UsesState for ShadowTracingStage<E, EM, SOT, Z>
where
I: Input,
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
SOT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I> + Debug,
E: UsesState,
{
type State = E::State;
}
impl<E, EM, SOT, Z> Stage<ShadowExecutor<E, SOT>, EM, Z> for ShadowTracingStage<E, EM, SOT, Z>
where
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = E::State>,
SOT: ObserversTuple<E::State>,
Z: UsesState<State = E::State>,
E::State: State + HasClientPerfMonitor + HasExecutions + HasCorpus + Debug,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut ShadowExecutor<E, I, S, SOT>,
state: &mut S,
executor: &mut ShadowExecutor<E, SOT>,
state: &mut E::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
@ -160,16 +161,16 @@ where
}
}
impl<E, EM, I, OT, S, SOT, Z> ShadowTracingStage<E, EM, I, OT, S, SOT, Z>
impl<E, EM, SOT, Z> ShadowTracingStage<E, EM, SOT, Z>
where
I: Input,
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>,
SOT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<I>,
E: Executor<EM, Z> + HasObservers,
E::State: State + HasClientPerfMonitor + HasExecutions + HasCorpus,
EM: UsesState<State = E::State>,
SOT: ObserversTuple<E::State>,
Z: UsesState<State = E::State>,
{
/// Creates a new default stage
pub fn new(_executor: &mut ShadowExecutor<E, I, S, SOT>) -> Self {
pub fn new(_executor: &mut ShadowExecutor<E, SOT>) -> Self {
Self {
phantom: PhantomData,
}

View File

@ -19,7 +19,7 @@ use crate::{
feedbacks::Feedback,
fuzzer::{Evaluator, ExecuteInputResult},
generators::Generator,
inputs::Input,
inputs::{Input, UsesInput},
monitors::ClientPerfMonitor,
Error,
};
@ -29,13 +29,28 @@ pub const DEFAULT_MAX_SIZE: usize = 1_048_576;
/// The [`State`] of the fuzzer.
/// Contains all important information about the current run.
/// Will be used to restart the fuzzing process at any timme.
pub trait State: Serialize + DeserializeOwned {}
/// Will be used to restart the fuzzing process at any time.
pub trait State: UsesInput + Serialize + DeserializeOwned {}
/// Structs which implement this trait are aware of the state. This is used for type enforcement.
pub trait UsesState: UsesInput<Input = <Self::State as UsesInput>::Input> {
/// The state known by this type.
type State: UsesInput;
}
// blanket impl which automatically defines UsesInput for anything that implements UsesState
impl<KS> UsesInput for KS
where
KS: UsesState,
{
type Input = <KS::State as UsesInput>::Input;
}
/// Trait for elements offering a corpus
pub trait HasCorpus<I: Input> {
pub trait HasCorpus: UsesInput {
/// The associated type implementing [`Corpus`].
type Corpus: Corpus<I>;
type Corpus: Corpus<Input = <Self as UsesInput>::Input>;
/// The testcase corpus
fn corpus(&self) -> &Self::Corpus;
/// The testcase corpus (mutable)
@ -51,9 +66,10 @@ pub trait HasMaxSize {
}
/// Trait for elements offering a corpus of solutions
pub trait HasSolutions<I: Input> {
pub trait HasSolutions: UsesInput {
/// The associated type implementing [`Corpus`] for solutions
type Solutions: Corpus<I>;
type Solutions: Corpus<Input = <Self as UsesInput>::Input>;
/// The solutions corpus
fn solutions(&self) -> &Self::Solutions;
/// The solutions corpus (mutable)
@ -151,14 +167,12 @@ pub trait HasStartTime {
/// The state a fuzz run.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "C: serde::Serialize + for<'a> serde::Deserialize<'a>")]
pub struct StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
#[serde(bound = "
C: serde::Serialize + for<'a> serde::Deserialize<'a>,
SC: serde::Serialize + for<'a> serde::Deserialize<'a>,
R: serde::Serialize + for<'a> serde::Deserialize<'a>
")]
pub struct StdState<I, C, R, SC> {
/// RNG instance
rand: R,
/// How many times the executor ran the harness/target
@ -178,25 +192,28 @@ where
/// Performance statistics for this fuzzer
#[cfg(feature = "introspection")]
introspection_monitor: ClientPerfMonitor,
phantom: PhantomData<I>,
}
impl<C, I, R, SC> State for StdState<C, I, R, SC>
impl<I, C, R, SC> UsesInput for StdState<I, C, R, SC>
where
C: Corpus<I>,
I: Input,
{
type Input = I;
}
impl<I, C, R, SC> State for StdState<I, C, R, SC>
where
C: Corpus<Input = Self::Input>,
R: Rand,
SC: Corpus<I>,
SC: Corpus<Input = Self::Input>,
Self: UsesInput,
{
}
impl<C, I, R, SC> HasRand for StdState<C, I, R, SC>
impl<I, C, R, SC> HasRand for StdState<I, C, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
type Rand = R;
@ -213,34 +230,31 @@ where
}
}
impl<C, I, R, SC> HasCorpus<I> for StdState<C, I, R, SC>
impl<I, C, R, SC> HasCorpus for StdState<I, C, R, SC>
where
C: Corpus<I>,
I: Input,
C: Corpus<Input = <Self as UsesInput>::Input>,
R: Rand,
SC: Corpus<I>,
{
type Corpus = C;
/// Returns the corpus
#[inline]
fn corpus(&self) -> &C {
fn corpus(&self) -> &Self::Corpus {
&self.corpus
}
/// Returns the mutable corpus
#[inline]
fn corpus_mut(&mut self) -> &mut C {
fn corpus_mut(&mut self) -> &mut Self::Corpus {
&mut self.corpus
}
}
impl<C, I, R, SC> HasSolutions<I> for StdState<C, I, R, SC>
impl<I, C, R, SC> HasSolutions for StdState<I, C, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
SC: Corpus<Input = <Self as UsesInput>::Input>,
{
type Solutions = SC;
@ -257,13 +271,7 @@ where
}
}
impl<C, I, R, SC> HasMetadata for StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
impl<I, C, R, SC> HasMetadata for StdState<I, C, R, SC> {
/// Get all the metadata into an [`hashbrown::HashMap`]
#[inline]
fn metadata(&self) -> &SerdeAnyMap {
@ -277,13 +285,7 @@ where
}
}
impl<C, I, R, SC> HasNamedMetadata for StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
impl<I, C, R, SC> HasNamedMetadata for StdState<I, C, R, SC> {
/// Get all the metadata into an [`hashbrown::HashMap`]
#[inline]
fn named_metadata(&self) -> &NamedSerdeAnyMap {
@ -297,13 +299,7 @@ where
}
}
impl<C, I, R, SC> HasExecutions for StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
impl<I, C, R, SC> HasExecutions for StdState<I, C, R, SC> {
/// The executions counter
#[inline]
fn executions(&self) -> &usize {
@ -317,13 +313,7 @@ where
}
}
impl<C, I, R, SC> HasMaxSize for StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
impl<I, C, R, SC> HasMaxSize for StdState<I, C, R, SC> {
fn max_size(&self) -> usize {
self.max_size
}
@ -333,13 +323,7 @@ where
}
}
impl<C, I, R, SC> HasStartTime for StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
impl<I, C, R, SC> HasStartTime for StdState<I, C, R, SC> {
/// The starting time
#[inline]
fn start_time(&self) -> &Duration {
@ -354,12 +338,12 @@ where
}
#[cfg(feature = "std")]
impl<C, I, R, SC> StdState<C, I, R, SC>
impl<C, I, R, SC> StdState<I, C, R, SC>
where
C: Corpus<I>,
I: Input,
C: Corpus<Input = <Self as UsesInput>::Input>,
R: Rand,
SC: Corpus<I>,
SC: Corpus<Input = <Self as UsesInput>::Input>,
{
/// Loads inputs from a directory.
/// If `forced` is `true`, the value will be loaded,
@ -374,7 +358,9 @@ where
loader: &mut dyn FnMut(&mut Z, &mut Self, &Path) -> Result<I, Error>,
) -> Result<(), Error>
where
Z: Evaluator<E, EM, I, Self>,
E: UsesState<State = Self>,
EM: UsesState<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
for entry in fs::read_dir(in_dir)? {
let entry = entry?;
@ -417,8 +403,9 @@ where
forced: bool,
) -> Result<(), Error>
where
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I>,
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
for in_dir in in_dirs {
self.load_from_directory(
@ -435,7 +422,7 @@ where
Event::Log {
severity_level: LogSeverity::Debug,
message: format!("Loaded {} initial testcases.", self.corpus().count()), // get corpus count
phantom: PhantomData,
phantom: PhantomData::<I>,
},
)?;
Ok(())
@ -452,8 +439,9 @@ where
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I>,
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
self.load_initial_inputs_internal(fuzzer, executor, manager, in_dirs, true)
}
@ -467,19 +455,20 @@ where
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I>,
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
self.load_initial_inputs_internal(fuzzer, executor, manager, in_dirs, false)
}
}
impl<C, I, R, SC> StdState<C, I, R, SC>
impl<C, I, R, SC> StdState<I, C, R, SC>
where
C: Corpus<I>,
I: Input,
C: Corpus<Input = <Self as UsesInput>::Input>,
R: Rand,
SC: Corpus<I>,
SC: Corpus<Input = <Self as UsesInput>::Input>,
{
fn generate_initial_internal<G, E, EM, Z>(
&mut self,
@ -491,9 +480,10 @@ where
forced: bool,
) -> Result<(), Error>
where
G: Generator<I, Self>,
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I>,
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
G: Generator<<Self as UsesInput>::Input, Self>,
Z: Evaluator<E, EM, State = Self>,
{
let mut added = 0;
for _ in 0..num {
@ -529,9 +519,10 @@ where
num: usize,
) -> Result<(), Error>
where
G: Generator<I, Self>,
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I>,
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
G: Generator<<Self as UsesInput>::Input, Self>,
Z: Evaluator<E, EM, State = Self>,
{
self.generate_initial_internal(fuzzer, executor, generator, manager, num, true)
}
@ -546,9 +537,10 @@ where
num: usize,
) -> Result<(), Error>
where
G: Generator<I, Self>,
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I>,
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
G: Generator<<Self as UsesInput>::Input, Self>,
Z: Evaluator<E, EM, State = Self>,
{
self.generate_initial_internal(fuzzer, executor, generator, manager, num, false)
}
@ -562,8 +554,8 @@ where
objective: &mut O,
) -> Result<Self, Error>
where
F: Feedback<I, Self>,
O: Feedback<I, Self>,
F: Feedback<Self>,
O: Feedback<Self>,
{
let mut state = Self {
rand,
@ -585,13 +577,7 @@ where
}
#[cfg(feature = "introspection")]
impl<C, I, R, SC> HasClientPerfMonitor for StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
impl<I, C, R, SC> HasClientPerfMonitor for StdState<I, C, R, SC> {
fn introspection_monitor(&self) -> &ClientPerfMonitor {
&self.introspection_monitor
}
@ -602,13 +588,7 @@ where
}
#[cfg(not(feature = "introspection"))]
impl<C, I, R, SC> HasClientPerfMonitor for StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
impl<I, C, R, SC> HasClientPerfMonitor for StdState<I, C, R, SC> {
fn introspection_monitor(&self) -> &ClientPerfMonitor {
unimplemented!()
}
@ -618,6 +598,67 @@ where
}
}
#[cfg(test)]
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct NopState<I> {
phantom: PhantomData<I>,
}
#[cfg(test)]
impl<I> NopState<I> {
/// Create a new State that does nothing (for tests)
#[must_use]
pub fn new() -> Self {
NopState {
phantom: PhantomData,
}
}
}
#[cfg(test)]
impl<I> UsesInput for NopState<I>
where
I: Input,
{
type Input = I;
}
#[cfg(test)]
impl<I> HasExecutions for NopState<I> {
fn executions(&self) -> &usize {
unimplemented!()
}
fn executions_mut(&mut self) -> &mut usize {
unimplemented!()
}
}
#[cfg(test)]
impl<I> HasMetadata for NopState<I> {
fn metadata(&self) -> &SerdeAnyMap {
unimplemented!()
}
fn metadata_mut(&mut self) -> &mut SerdeAnyMap {
unimplemented!()
}
}
#[cfg(test)]
impl<I> HasClientPerfMonitor for NopState<I> {
fn introspection_monitor(&self) -> &ClientPerfMonitor {
unimplemented!()
}
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor {
unimplemented!()
}
}
#[cfg(test)]
impl<I> State for NopState<I> where I: Input {}
#[cfg(feature = "python")]
#[allow(missing_docs)]
/// `State` Python bindings
@ -643,7 +684,7 @@ pub mod pybind {
};
/// `StdState` with fixed generics
pub type PythonStdState = StdState<PythonCorpus, BytesInput, PythonRand, PythonCorpus>;
pub type PythonStdState = StdState<BytesInput, PythonCorpus, PythonRand, PythonCorpus>;
#[pyclass(unsendable, name = "StdState")]
#[derive(Debug)]

View File

@ -1,5 +1,5 @@
//! Errors that can be caught by the `libafl_frida` address sanitizer.
use std::io::Write;
use std::{fmt::Debug, io::Write, marker::PhantomData};
use backtrace::Backtrace;
use capstone::{arch::BuildsCapstone, Capstone};
@ -13,7 +13,7 @@ use libafl::{
events::EventFirer,
executors::ExitKind,
feedbacks::Feedback,
inputs::{HasTargetBytes, Input},
inputs::{HasTargetBytes, UsesInput},
observers::{Observer, ObserversTuple},
state::{HasClientPerfMonitor, HasMetadata},
Error, SerdeAny,
@ -551,8 +551,11 @@ pub struct AsanErrorsObserver {
errors: OwnedPtr<Option<AsanErrors>>,
}
impl<I, S> Observer<I, S> for AsanErrorsObserver {
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
impl<S> Observer<S> for AsanErrorsObserver
where
S: UsesInput,
{
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
unsafe {
if ASAN_ERRORS.is_some() {
ASAN_ERRORS.as_mut().unwrap().clear();
@ -607,27 +610,28 @@ impl AsanErrorsObserver {
/// A feedback reporting potential [`struct@AsanErrors`] from an `AsanErrorsObserver`
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AsanErrorsFeedback {
pub struct AsanErrorsFeedback<S> {
errors: Option<AsanErrors>,
phantom: PhantomData<S>,
}
impl<I, S> Feedback<I, S> for AsanErrorsFeedback
impl<S> Feedback<S> for AsanErrorsFeedback<S>
where
I: Input + HasTargetBytes,
S: HasClientPerfMonitor,
S: UsesInput + Debug + HasClientPerfMonitor,
S::Input: HasTargetBytes,
{
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_input: &S::Input,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
let observer = observers
.match_name::<AsanErrorsObserver>("AsanErrors")
@ -645,7 +649,11 @@ where
}
}
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
fn append_metadata(
&mut self,
_state: &mut S,
testcase: &mut Testcase<S::Input>,
) -> Result<(), Error> {
if let Some(errors) = &self.errors {
testcase.add_metadata(errors.clone());
}
@ -653,28 +661,31 @@ where
Ok(())
}
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.errors = None;
Ok(())
}
}
impl Named for AsanErrorsFeedback {
impl<S> Named for AsanErrorsFeedback<S> {
#[inline]
fn name(&self) -> &str {
"AsanErrors"
}
}
impl AsanErrorsFeedback {
impl<S> AsanErrorsFeedback<S> {
/// Create a new `AsanErrorsFeedback`
#[must_use]
pub fn new() -> Self {
Self { errors: None }
Self {
errors: None,
phantom: PhantomData,
}
}
}
impl Default for AsanErrorsFeedback {
impl<S> Default for AsanErrorsFeedback<S> {
fn default() -> Self {
Self::new()
}

View File

@ -6,11 +6,15 @@ use frida_gum::{
Gum, MemoryRange, NativePointer,
};
#[cfg(windows)]
use libafl::executors::inprocess::{HasInProcessHandlers, InProcessHandlers};
use libafl::{
executors::inprocess::{HasInProcessHandlers, InProcessHandlers},
state::{HasClientPerfMonitor, HasSolutions},
};
use libafl::{
executors::{Executor, ExitKind, HasObservers, InProcessExecutor},
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
inputs::{HasTargetBytes, UsesInput},
observers::{ObserversTuple, UsesObservers},
state::UsesState,
Error,
};
@ -21,13 +25,14 @@ use crate::helper::{FridaInstrumentationHelper, FridaRuntimeTuple};
use crate::windows_hooks::initialize;
/// The [`FridaInProcessExecutor`] is an [`Executor`] that executes the target in the same process, usinig [`frida`](https://frida.re/) for binary-only instrumentation.
pub struct FridaInProcessExecutor<'a, 'b, 'c, H, I, OT, RT, S>
pub struct FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
S::Input: HasTargetBytes,
S: UsesInput,
OT: ObserversTuple<S>,
{
base: InProcessExecutor<'a, H, I, OT, S>,
base: InProcessExecutor<'a, H, OT, S>,
/// Frida's dynamic rewriting engine
stalker: Stalker<'a>,
/// User provided callback for instrumentation
@ -36,11 +41,12 @@ where
_phantom: PhantomData<&'b u8>,
}
impl<'a, 'b, 'c, H, I, OT, RT, S> Debug for FridaInProcessExecutor<'a, 'b, 'c, H, I, OT, RT, S>
impl<'a, 'b, 'c, H, OT, RT, S> Debug for FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
S::Input: HasTargetBytes,
OT: ObserversTuple<S>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("FridaInProcessExecutor")
@ -51,22 +57,25 @@ where
}
}
impl<'a, 'b, 'c, EM, H, I, OT, RT, S, Z> Executor<EM, I, S, Z>
for FridaInProcessExecutor<'a, 'b, 'c, H, I, OT, RT, S>
impl<'a, 'b, 'c, EM, H, OT, RT, S, Z> Executor<EM, Z>
for FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
EM: UsesState<State = S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
S::Input: HasTargetBytes,
OT: ObserversTuple<S>,
RT: FridaRuntimeTuple,
Z: UsesState<State = S>,
{
/// Instruct the target about the input and run
#[inline]
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
self.helper.pre_exec(input)?;
if self.helper.stalker_enabled() {
@ -94,12 +103,32 @@ where
}
}
impl<'a, 'b, 'c, H, I, OT, RT, S> HasObservers<I, OT, S>
for FridaInProcessExecutor<'a, 'b, 'c, H, I, OT, RT, S>
impl<'a, 'b, 'c, H, OT, RT, S> UsesObservers for FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
S::Input: HasTargetBytes,
{
type Observers = OT;
}
impl<'a, 'b, 'c, H, OT, RT, S> UsesState for FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
where
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
S::Input: HasTargetBytes,
{
type State = S;
}
impl<'a, 'b, 'c, H, OT, RT, S> HasObservers for FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
where
H: FnMut(&S::Input) -> ExitKind,
S::Input: HasTargetBytes,
S: UsesInput,
OT: ObserversTuple<S>,
{
#[inline]
fn observers(&self) -> &OT {
@ -112,17 +141,18 @@ where
}
}
impl<'a, 'b, 'c, H, I, OT, S, RT> FridaInProcessExecutor<'a, 'b, 'c, H, I, OT, RT, S>
impl<'a, 'b, 'c, H, OT, S, RT> FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
S::Input: HasTargetBytes,
OT: ObserversTuple<S>,
RT: FridaRuntimeTuple,
{
/// Creates a new [`FridaInProcessExecutor`]
pub fn new(
gum: &'a Gum,
base: InProcessExecutor<'a, H, I, OT, S>,
base: InProcessExecutor<'a, H, OT, S>,
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
) -> Self {
let mut stalker = Stalker::new(gum);
@ -162,12 +192,13 @@ where
}
#[cfg(windows)]
impl<'a, 'b, 'c, H, I, OT, RT, S> HasInProcessHandlers
for FridaInProcessExecutor<'a, 'b, 'c, H, I, OT, RT, S>
impl<'a, 'b, 'c, H, OT, RT, S> HasInProcessHandlers
for FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput + HasClientPerfMonitor + HasSolutions,
S::Input: HasTargetBytes,
OT: ObserversTuple<S>,
RT: FridaRuntimeTuple,
{
/// the timeout handler

View File

@ -3,8 +3,9 @@ use std::{fmt::Debug, marker::PhantomData};
use libafl::{
bolts::AsSlice,
executors::{Executor, ExitKind, HasObservers},
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
inputs::{HasTargetBytes, UsesInput},
observers::{ObserversTuple, UsesObservers},
state::{State, UsesState},
Error,
};
use libnyx::NyxReturnValue;
@ -12,16 +13,16 @@ use libnyx::NyxReturnValue;
use crate::helper::NyxHelper;
/// executor for nyx standalone mode
pub struct NyxExecutor<'a, I, S, OT> {
pub struct NyxExecutor<'a, S, OT> {
/// implement nyx function
pub helper: &'a mut NyxHelper,
/// observers
observers: OT,
/// phantom data to keep generic type <I,S>
phantom: PhantomData<(I, S)>,
phantom: PhantomData<S>,
}
impl<'a, I, S, OT> Debug for NyxExecutor<'a, I, S, OT> {
impl<'a, S, OT> Debug for NyxExecutor<'a, S, OT> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NyxInprocessExecutor")
.field("helper", &self.helper)
@ -29,17 +30,35 @@ impl<'a, I, S, OT> Debug for NyxExecutor<'a, I, S, OT> {
}
}
impl<'a, EM, I, S, Z, OT> Executor<EM, I, S, Z> for NyxExecutor<'a, I, S, OT>
impl<'a, S, OT> UsesState for NyxExecutor<'a, S, OT>
where
I: Input + HasTargetBytes,
S: UsesInput,
{
type State = S;
}
impl<'a, S, OT> UsesObservers for NyxExecutor<'a, S, OT>
where
OT: ObserversTuple<S>,
S: UsesInput,
{
type Observers = OT;
}
impl<'a, EM, S, Z, OT> Executor<EM, Z> for NyxExecutor<'a, S, OT>
where
EM: UsesState<State = S>,
S: UsesInput,
S::Input: HasTargetBytes,
Z: UsesState<State = S>,
{
fn run_target(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_state: &mut Self::State,
_mgr: &mut EM,
input: &I,
) -> Result<libafl::executors::ExitKind, libafl::Error> {
input: &Self::Input,
) -> Result<ExitKind, Error> {
let input_owned = input.target_bytes();
let input = input_owned.as_slice();
self.helper.nyx_process.set_input(input, input.len() as u32);
@ -68,7 +87,7 @@ where
}
}
impl<'a, I, S, OT> NyxExecutor<'a, I, S, OT> {
impl<'a, S, OT> NyxExecutor<'a, S, OT> {
pub fn new(helper: &'a mut NyxHelper, observers: OT) -> Result<Self, Error> {
Ok(Self {
helper,
@ -83,10 +102,10 @@ impl<'a, I, S, OT> NyxExecutor<'a, I, S, OT> {
}
}
impl<'a, I, S, OT> HasObservers<I, OT, S> for NyxExecutor<'a, I, S, OT>
impl<'a, S, OT> HasObservers for NyxExecutor<'a, S, OT>
where
I: Input,
OT: ObserversTuple<I, S>,
S: State,
OT: ObserversTuple<S>,
{
fn observers(&self) -> &OT {
&self.observers

View File

@ -1,6 +1,6 @@
use std::{env, fs, ptr};
use libafl::{inputs::Input, state::HasMetadata};
use libafl::{inputs::UsesInput, state::HasMetadata};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::{
@ -407,52 +407,51 @@ impl Default for QemuAsanHelper {
}
}
impl<I, S> QemuHelper<I, S> for QemuAsanHelper
impl<S> QemuHelper<S> for QemuAsanHelper
where
I: Input,
S: HasMetadata,
S: UsesInput + HasMetadata,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
where
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
hooks.reads(
Some(gen_readwrite_asan::<I, QT, S>),
Some(trace_read1_asan::<I, QT, S>),
Some(trace_read2_asan::<I, QT, S>),
Some(trace_read4_asan::<I, QT, S>),
Some(trace_read8_asan::<I, QT, S>),
Some(trace_read_n_asan::<I, QT, S>),
Some(gen_readwrite_asan::<QT, S>),
Some(trace_read1_asan::<QT, S>),
Some(trace_read2_asan::<QT, S>),
Some(trace_read4_asan::<QT, S>),
Some(trace_read8_asan::<QT, S>),
Some(trace_read_n_asan::<QT, S>),
);
hooks.writes(
Some(gen_readwrite_asan::<I, QT, S>),
Some(trace_write1_asan::<I, QT, S>),
Some(trace_write2_asan::<I, QT, S>),
Some(trace_write4_asan::<I, QT, S>),
Some(trace_write8_asan::<I, QT, S>),
Some(trace_write_n_asan::<I, QT, S>),
Some(gen_readwrite_asan::<QT, S>),
Some(trace_write1_asan::<QT, S>),
Some(trace_write2_asan::<QT, S>),
Some(trace_write4_asan::<QT, S>),
Some(trace_write8_asan::<QT, S>),
Some(trace_write_n_asan::<QT, S>),
);
hooks.syscalls(qasan_fake_syscall::<I, QT, S>);
hooks.syscalls(qasan_fake_syscall::<QT, S>);
}
fn post_exec(&mut self, _emulator: &Emulator, _input: &I) {
fn post_exec(&mut self, _emulator: &Emulator, _input: &S::Input) {
self.reset();
}
}
pub fn gen_readwrite_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn gen_readwrite_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
_size: usize,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
if h.must_instrument(pc.into()) {
@ -461,142 +460,142 @@ where
None
}
}
pub fn trace_read1_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_read1_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_1(&emulator, addr);
}
pub fn trace_read2_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_read2_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_2(&emulator, addr);
}
pub fn trace_read4_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_read4_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_4(&emulator, addr);
}
pub fn trace_read8_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_read8_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_8(&emulator, addr);
}
pub fn trace_read_n_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_read_n_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
size: usize,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_n(&emulator, addr, size);
}
pub fn trace_write1_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_write1_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.write_1(&emulator, addr);
}
pub fn trace_write2_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_write2_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.write_2(&emulator, addr);
}
pub fn trace_write4_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_write4_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.write_4(&emulator, addr);
}
pub fn trace_write8_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_write8_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.write_8(&emulator, addr);
}
pub fn trace_write_n_asan<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_write_n_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
size: usize,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
@ -604,8 +603,8 @@ pub fn trace_write_n_asan<I, QT, S>(
}
#[allow(clippy::too_many_arguments)]
pub fn qasan_fake_syscall<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn qasan_fake_syscall<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
sys_num: i32,
a0: u64,
@ -618,8 +617,8 @@ pub fn qasan_fake_syscall<I, QT, S>(
_a7: u64,
) -> SyscallHookResult
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
if sys_num == QASAN_FAKESYS_NR {
let emulator = hooks.emulator().clone();

View File

@ -1,5 +1,5 @@
use capstone::prelude::*;
use libafl::inputs::Input;
use libafl::inputs::UsesInput;
use crate::{
capstone,
@ -46,33 +46,33 @@ impl Default for QemuCallTracerHelper {
}
}
impl<I, S> QemuHelper<I, S> for QemuCallTracerHelper
impl<S> QemuHelper<S> for QemuCallTracerHelper
where
I: Input,
S: UsesInput,
{
fn init_hooks<'a, QT>(&self, hooks: &QemuHooks<'a, I, QT, S>)
fn init_hooks<'a, QT>(&self, hooks: &QemuHooks<'a, QT, S>)
where
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
hooks.blocks(Some(gen_blocks_calls::<I, QT, S>), None);
hooks.blocks(Some(gen_blocks_calls::<QT, S>), None);
}
fn pre_exec(&mut self, _emulator: &Emulator, _input: &I) {
fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) {
self.reset();
}
}
/*pub fn on_call<I, QT, S>(hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, pc: GuestAddr)
/*pub fn on_call<QT, S>(hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, pc: GuestAddr)
where
I: Input,
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
}*/
pub fn on_ret<I, QT, S>(hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _pc: GuestAddr)
pub fn on_ret<QT, S>(hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _pc: GuestAddr)
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
#[cfg(cpu_target = "x86_64")]
let ret_addr = {
@ -113,14 +113,14 @@ where
}
}
pub fn gen_blocks_calls<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn gen_blocks_calls<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let emu = hooks.emulator();
if let Some(h) = hooks.helpers().match_first_type::<QemuCallTracerHelper>() {
@ -155,7 +155,7 @@ where
capstone::InsnGroupType::CS_GRP_CALL => {
// hooks.instruction_closure(insn.address() as GuestAddr, on_call, false);
let call_len = insn.bytes().len() as GuestAddr;
let call_cb = move |hooks: &mut QemuHooks<'_, I, QT, S>, _, pc| {
let call_cb = move |hooks: &mut QemuHooks<'_, QT, S>, _, pc| {
// eprintln!("CALL @ 0x{:#x}", pc + call_len);
if let Some(h) = hooks
.helpers_mut()

View File

@ -1,5 +1,5 @@
use hashbrown::HashMap;
use libafl::{inputs::Input, state::HasMetadata};
use libafl::{inputs::UsesInput, state::HasMetadata};
pub use libafl_targets::{
cmplog::__libafl_targets_cmplog_instructions, CmpLogMap, CmpLogObserver, CMPLOG_MAP,
CMPLOG_MAP_H, CMPLOG_MAP_PTR, CMPLOG_MAP_SIZE, CMPLOG_MAP_W,
@ -53,17 +53,16 @@ impl Default for QemuCmpLogHelper {
}
}
impl<I, S> QemuHelper<I, S> for QemuCmpLogHelper
impl<S> QemuHelper<S> for QemuCmpLogHelper
where
I: Input,
S: HasMetadata,
S: UsesInput + HasMetadata,
{
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
where
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
hooks.cmps_raw(
Some(gen_unique_cmp_ids::<I, QT, S>),
Some(gen_unique_cmp_ids::<QT, S>),
Some(trace_cmp1_cmplog),
Some(trace_cmp2_cmplog),
Some(trace_cmp4_cmplog),
@ -95,19 +94,19 @@ impl Default for QemuCmpLogChildHelper {
}
}
impl<I, S> QemuHelper<I, S> for QemuCmpLogChildHelper
impl<S> QemuHelper<S> for QemuCmpLogChildHelper
where
I: Input,
S: UsesInput,
S: HasMetadata,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
where
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
hooks.cmps_raw(
Some(gen_hashed_cmp_ids::<I, QT, S>),
Some(gen_hashed_cmp_ids::<QT, S>),
Some(trace_cmp1_cmplog),
Some(trace_cmp2_cmplog),
Some(trace_cmp4_cmplog),
@ -116,16 +115,16 @@ where
}
}
pub fn gen_unique_cmp_ids<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn gen_unique_cmp_ids<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
state: Option<&mut S>,
pc: GuestAddr,
_size: usize,
) -> Option<u64>
where
S: HasMetadata,
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
if let Some(h) = hooks.match_helper_mut::<QemuCmpLogHelper>() {
if !h.must_instrument(pc.into()) {
@ -148,16 +147,16 @@ where
}))
}
pub fn gen_hashed_cmp_ids<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn gen_hashed_cmp_ids<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
_size: usize,
) -> Option<u64>
where
S: HasMetadata,
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
if let Some(h) = hooks.match_helper_mut::<QemuCmpLogChildHelper>() {
if !h.must_instrument(pc.into()) {

View File

@ -1,7 +1,7 @@
use std::{cell::UnsafeCell, cmp::max};
use hashbrown::{hash_map::Entry, HashMap};
use libafl::{inputs::Input, state::HasMetadata};
use libafl::{inputs::UsesInput, state::HasMetadata};
pub use libafl_targets::{
edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE, EDGES_MAP_SIZE, MAX_EDGES_NUM,
};
@ -66,25 +66,21 @@ impl Default for QemuEdgeCoverageHelper {
}
}
impl<I, S> QemuHelper<I, S> for QemuEdgeCoverageHelper
impl<S> QemuHelper<S> for QemuEdgeCoverageHelper
where
I: Input,
S: HasMetadata,
S: UsesInput + HasMetadata,
{
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
where
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
if self.use_hitcounts {
hooks.edges_raw(
Some(gen_unique_edge_ids::<I, QT, S>),
Some(gen_unique_edge_ids::<QT, S>),
Some(trace_edge_hitcount),
);
} else {
hooks.edges_raw(
Some(gen_unique_edge_ids::<I, QT, S>),
Some(trace_edge_single),
);
hooks.edges_raw(Some(gen_unique_edge_ids::<QT, S>), Some(trace_edge_single));
}
}
}
@ -126,25 +122,25 @@ impl Default for QemuEdgeCoverageChildHelper {
}
}
impl<I, S> QemuHelper<I, S> for QemuEdgeCoverageChildHelper
impl<S> QemuHelper<S> for QemuEdgeCoverageChildHelper
where
I: Input,
S: UsesInput,
S: HasMetadata,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
where
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
if self.use_hitcounts {
hooks.edges_raw(
Some(gen_hashed_edge_ids::<I, QT, S>),
Some(gen_hashed_edge_ids::<QT, S>),
Some(trace_edge_hitcount_ptr),
);
} else {
hooks.edges_raw(
Some(gen_hashed_edge_ids::<I, QT, S>),
Some(gen_hashed_edge_ids::<QT, S>),
Some(trace_edge_single_ptr),
);
}
@ -153,16 +149,16 @@ where
thread_local!(static PREV_LOC : UnsafeCell<u64> = UnsafeCell::new(0));
pub fn gen_unique_edge_ids<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn gen_unique_edge_ids<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
state: Option<&mut S>,
src: GuestAddr,
dest: GuestAddr,
) -> Option<u64>
where
S: HasMetadata,
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
if let Some(h) = hooks.helpers().match_first_type::<QemuEdgeCoverageHelper>() {
if !h.must_instrument(src.into()) && !h.must_instrument(dest.into()) {
@ -213,15 +209,15 @@ pub extern "C" fn trace_edge_single(id: u64, _data: u64) {
}
}
pub fn gen_hashed_edge_ids<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn gen_hashed_edge_ids<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
src: GuestAddr,
dest: GuestAddr,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
if let Some(h) = hooks
.helpers()
@ -250,28 +246,28 @@ pub extern "C" fn trace_edge_single_ptr(id: u64, _data: u64) {
}
}
pub fn gen_addr_block_ids<I, QT, S>(
_hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn gen_addr_block_ids<QT, S>(
_hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
// GuestAddress is u32 for 32 bit guests
#[allow(clippy::unnecessary_cast)]
Some(pc as u64)
}
pub fn gen_hashed_block_ids<I, QT, S>(
_hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn gen_hashed_block_ids<QT, S>(
_hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
// GuestAddress is u32 for 32 bit guests
#[allow(clippy::unnecessary_cast)]

View File

@ -244,8 +244,10 @@ extern "C" {
static mut libafl_start_vcpu: extern "C" fn(cpu: CPUStatePtr);
/*
fn libafl_save_qemu_snapshot(name: *const u8);
fn libafl_load_qemu_snapshot(name: *const u8);
*/
}
#[cfg(not(feature = "usermode"))]

View File

@ -2,40 +2,41 @@
use core::fmt::{self, Debug, Formatter};
#[cfg(feature = "fork")]
use libafl::bolts::shmem::ShMemProvider;
#[cfg(feature = "fork")]
use libafl::executors::InProcessForkExecutor;
use libafl::{
bolts::shmem::ShMemProvider, events::EventManager, executors::InProcessForkExecutor,
state::HasMetadata,
};
use libafl::{
events::{EventFirer, EventRestarter},
executors::{Executor, ExitKind, HasObservers, InProcessExecutor},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasClientPerfMonitor, HasSolutions},
inputs::UsesInput,
observers::{ObserversTuple, UsesObservers},
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasSolutions, State, UsesState},
Error,
};
pub use crate::emu::SyscallHookResult;
use crate::{emu::Emulator, helper::QemuHelperTuple, hooks::QemuHooks};
pub struct QemuExecutor<'a, H, I, OT, QT, S>
pub struct QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
{
hooks: &'a mut QemuHooks<'a, I, QT, S>,
inner: InProcessExecutor<'a, H, I, OT, S>,
hooks: &'a mut QemuHooks<'a, QT, S>,
inner: InProcessExecutor<'a, H, OT, S>,
}
impl<'a, H, I, OT, QT, S> Debug for QemuExecutor<'a, H, I, OT, QT, S>
impl<'a, H, OT, QT, S> Debug for QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuExecutor")
@ -45,15 +46,15 @@ where
}
}
impl<'a, H, I, OT, QT, S> QemuExecutor<'a, H, I, OT, QT, S>
impl<'a, H, OT, QT, S> QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
{
pub fn new<EM, OF, Z>(
hooks: &'a mut QemuHooks<'a, I, QT, S>,
hooks: &'a mut QemuHooks<'a, QT, S>,
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
@ -61,10 +62,10 @@ where
event_mgr: &mut EM,
) -> Result<Self, Error>
where
EM: EventFirer<I> + EventRestarter<S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
Z: HasObjective<I, OF, S>,
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: State + HasExecutions + HasCorpus + HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = S>,
{
Ok(Self {
hooks,
@ -72,19 +73,19 @@ where
})
}
pub fn inner(&self) -> &InProcessExecutor<'a, H, I, OT, S> {
pub fn inner(&self) -> &InProcessExecutor<'a, H, OT, S> {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut InProcessExecutor<'a, H, I, OT, S> {
pub fn inner_mut(&mut self) -> &mut InProcessExecutor<'a, H, OT, S> {
&mut self.inner
}
pub fn hooks(&self) -> &QemuHooks<'a, I, QT, S> {
pub fn hooks(&self) -> &QemuHooks<'a, QT, S> {
self.hooks
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<'a, I, QT, S> {
pub fn hooks_mut(&mut self) -> &mut QemuHooks<'a, QT, S> {
self.hooks
}
@ -93,19 +94,21 @@ where
}
}
impl<'a, EM, H, I, OT, QT, S, Z> Executor<EM, I, S, Z> for QemuExecutor<'a, H, I, OT, QT, S>
impl<'a, EM, H, OT, QT, S, Z> Executor<EM, Z> for QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
EM: UsesState<State = S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
Z: UsesState<State = S>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let emu = Emulator::new_empty();
self.hooks.helpers_mut().pre_exec_all(&emu, input);
@ -115,12 +118,32 @@ where
}
}
impl<'a, H, I, OT, QT, S> HasObservers<I, OT, S> for QemuExecutor<'a, H, I, OT, QT, S>
impl<'a, H, OT, QT, S> UsesState for QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: UsesInput,
{
type State = S;
}
impl<'a, H, OT, QT, S> UsesObservers for QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: UsesInput,
{
type Observers = OT;
}
impl<'a, H, OT, QT, S> HasObservers for QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
{
#[inline]
fn observers(&self) -> &OT {
@ -134,25 +157,25 @@ where
}
#[cfg(feature = "fork")]
pub struct QemuForkExecutor<'a, H, I, OT, QT, S, SP>
pub struct QemuForkExecutor<'a, H, OT, QT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
SP: ShMemProvider,
{
hooks: &'a mut QemuHooks<'a, I, QT, S>,
inner: InProcessForkExecutor<'a, H, I, OT, S, SP>,
hooks: &'a mut QemuHooks<'a, QT, S>,
inner: InProcessForkExecutor<'a, H, OT, S, SP>,
}
#[cfg(feature = "fork")]
impl<'a, H, I, OT, QT, S, SP> Debug for QemuForkExecutor<'a, H, I, OT, QT, S, SP>
impl<'a, H, OT, QT, S, SP> Debug for QemuForkExecutor<'a, H, OT, QT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
SP: ShMemProvider,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@ -164,16 +187,16 @@ where
}
#[cfg(feature = "fork")]
impl<'a, H, I, OT, QT, S, SP> QemuForkExecutor<'a, H, I, OT, QT, S, SP>
impl<'a, H, OT, QT, S, SP> QemuForkExecutor<'a, H, OT, QT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
SP: ShMemProvider,
{
pub fn new<EM, OF, Z>(
hooks: &'a mut QemuHooks<'a, I, QT, S>,
hooks: &'a mut QemuHooks<'a, QT, S>,
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
@ -182,10 +205,10 @@ where
shmem_provider: SP,
) -> Result<Self, Error>
where
EM: EventFirer<I> + EventRestarter<S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
Z: HasObjective<I, OF, S>,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<OF, State = S>,
{
assert!(!QT::HOOKS_DO_SIDE_EFFECTS, "When using QemuForkExecutor, the hooks must not do any side effect as they will happen in the child process and then discarded");
@ -202,19 +225,19 @@ where
})
}
pub fn inner(&self) -> &InProcessForkExecutor<'a, H, I, OT, S, SP> {
pub fn inner(&self) -> &InProcessForkExecutor<'a, H, OT, S, SP> {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut InProcessForkExecutor<'a, H, I, OT, S, SP> {
pub fn inner_mut(&mut self) -> &mut InProcessForkExecutor<'a, H, OT, S, SP> {
&mut self.inner
}
pub fn hooks(&self) -> &QemuHooks<'a, I, QT, S> {
pub fn hooks(&self) -> &QemuHooks<'a, QT, S> {
self.hooks
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<'a, I, QT, S> {
pub fn hooks_mut(&mut self) -> &mut QemuHooks<'a, QT, S> {
self.hooks
}
@ -224,21 +247,22 @@ where
}
#[cfg(feature = "fork")]
impl<'a, EM, H, I, OT, QT, S, Z, SP> Executor<EM, I, S, Z>
for QemuForkExecutor<'a, H, I, OT, QT, S, SP>
impl<'a, EM, H, OT, QT, S, Z, SP> Executor<EM, Z> for QemuForkExecutor<'a, H, OT, QT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
EM: EventManager<InProcessForkExecutor<'a, H, OT, S, SP>, Z, State = S>,
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput + HasClientPerfMonitor + HasMetadata + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
SP: ShMemProvider,
Z: UsesState<State = S>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
state: &mut Self::State,
mgr: &mut EM,
input: &I,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let emu = Emulator::new_empty();
self.hooks.helpers_mut().pre_exec_all(&emu, input);
@ -249,12 +273,36 @@ where
}
#[cfg(feature = "fork")]
impl<'a, H, I, OT, QT, S, SP> HasObservers<I, OT, S> for QemuForkExecutor<'a, H, I, OT, QT, S, SP>
impl<'a, H, OT, QT, S, SP> UsesObservers for QemuForkExecutor<'a, H, OT, QT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type Observers = OT;
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP> UsesState for QemuForkExecutor<'a, H, OT, QT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type State = S;
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP> HasObservers for QemuForkExecutor<'a, H, OT, QT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
SP: ShMemProvider,
{
#[inline]

View File

@ -1,82 +1,82 @@
use core::{fmt::Debug, ops::Range};
use libafl::{bolts::tuples::MatchFirstType, inputs::Input};
use libafl::{bolts::tuples::MatchFirstType, inputs::UsesInput};
use crate::{emu::Emulator, hooks::QemuHooks};
/// A helper for `libafl_qemu`.
// TODO remove 'static when specialization will be stable
pub trait QemuHelper<I, S>: 'static + Debug
pub trait QemuHelper<S>: 'static + Debug
where
I: Input,
S: UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = true;
fn init_hooks<QT>(&self, _hooks: &QemuHooks<'_, I, QT, S>)
fn init_hooks<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
where
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
}
fn pre_exec(&mut self, _emulator: &Emulator, _input: &I) {}
fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) {}
fn post_exec(&mut self, _emulator: &Emulator, _input: &I) {}
fn post_exec(&mut self, _emulator: &Emulator, _input: &S::Input) {}
}
pub trait QemuHelperTuple<I, S>: MatchFirstType + Debug
pub trait QemuHelperTuple<S>: MatchFirstType + Debug
where
I: Input,
S: UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool;
fn init_hooks_all<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
fn init_hooks_all<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
where
QT: QemuHelperTuple<I, S>;
QT: QemuHelperTuple<S>;
fn pre_exec_all(&mut self, _emulator: &Emulator, input: &I);
fn pre_exec_all(&mut self, _emulator: &Emulator, input: &S::Input);
fn post_exec_all(&mut self, _emulator: &Emulator, input: &I);
fn post_exec_all(&mut self, _emulator: &Emulator, input: &S::Input);
}
impl<I, S> QemuHelperTuple<I, S> for ()
impl<S> QemuHelperTuple<S> for ()
where
I: Input,
S: UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init_hooks_all<QT>(&self, _hooks: &QemuHooks<'_, I, QT, S>)
fn init_hooks_all<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
where
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
}
fn pre_exec_all(&mut self, _emulator: &Emulator, _input: &I) {}
fn pre_exec_all(&mut self, _emulator: &Emulator, _input: &S::Input) {}
fn post_exec_all(&mut self, _emulator: &Emulator, _input: &I) {}
fn post_exec_all(&mut self, _emulator: &Emulator, _input: &S::Input) {}
}
impl<Head, Tail, I, S> QemuHelperTuple<I, S> for (Head, Tail)
impl<Head, Tail, S> QemuHelperTuple<S> for (Head, Tail)
where
Head: QemuHelper<I, S>,
Tail: QemuHelperTuple<I, S>,
I: Input,
Head: QemuHelper<S>,
Tail: QemuHelperTuple<S>,
S: UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS;
fn init_hooks_all<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
fn init_hooks_all<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
where
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
self.0.init_hooks(hooks);
self.1.init_hooks_all(hooks);
}
fn pre_exec_all(&mut self, emulator: &Emulator, input: &I) {
fn pre_exec_all(&mut self, emulator: &Emulator, input: &S::Input) {
self.0.pre_exec(emulator, input);
self.1.pre_exec_all(emulator, input);
}
fn post_exec_all(&mut self, emulator: &Emulator, input: &I) {
fn post_exec_all(&mut self, emulator: &Emulator, input: &S::Input) {
self.0.post_exec(emulator, input);
self.1.post_exec_all(emulator, input);
}

View File

@ -9,7 +9,7 @@ use core::{
ptr::{self, addr_of},
};
use libafl::{executors::inprocess::inprocess_get_state, inputs::Input};
use libafl::{executors::inprocess::inprocess_get_state, inputs::UsesInput};
pub use crate::emu::SyscallHookResult;
use crate::{
@ -40,35 +40,35 @@ type DynamicLenHookCl<QT, S> =
*/
static mut QEMU_HOOKS_PTR: *const c_void = ptr::null();
unsafe fn get_qemu_hooks<'a, I, QT, S>() -> &'a mut QemuHooks<'a, I, QT, S>
unsafe fn get_qemu_hooks<'a, QT, S>() -> &'a mut QemuHooks<'a, QT, S>
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
(QEMU_HOOKS_PTR as *mut QemuHooks<'a, I, QT, S>)
(QEMU_HOOKS_PTR as *mut QemuHooks<'a, QT, S>)
.as_mut()
.expect("A high-level hook is installed but QemuHooks is not initialized")
}
static mut GENERIC_HOOKS: Vec<Hook> = vec![];
extern "C" fn generic_hook_wrapper<I, QT, S>(pc: GuestAddr, index: u64)
extern "C" fn generic_hook_wrapper<QT, S>(pc: GuestAddr, index: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let hook = &mut GENERIC_HOOKS[index as usize];
match hook {
Hook::Function(ptr) => {
let func: fn(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, GuestAddr) =
let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, GuestAddr) =
transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), pc);
}
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, GuestAddr),
dyn FnMut(&mut QemuHooks<'_, QT, S>, Option<&mut S>, GuestAddr),
> = transmute(ptr);
(func)(hooks, inprocess_get_state::<S>(), pc);
}
@ -79,18 +79,18 @@ where
static mut EDGE_HOOKS: Vec<(Hook, Hook)> = vec![];
extern "C" fn gen_edge_hook_wrapper<I, QT, S>(src: GuestAddr, dst: GuestAddr, index: u64) -> u64
extern "C" fn gen_edge_hook_wrapper<QT, S>(src: GuestAddr, dst: GuestAddr, index: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let (gen, _) = &mut EDGE_HOOKS[index as usize];
match gen {
Hook::Function(ptr) => {
let func: fn(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
GuestAddr,
@ -100,7 +100,7 @@ where
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
GuestAddr,
@ -113,21 +113,21 @@ where
}
}
extern "C" fn exec_edge_hook_wrapper<I, QT, S>(id: u64, index: u64)
extern "C" fn exec_edge_hook_wrapper<QT, S>(id: u64, index: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let (_, exec) = &mut EDGE_HOOKS[index as usize];
match exec {
Hook::Function(ptr) => {
let func: fn(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64) = transmute(*ptr);
let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u64) = transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), id);
}
Hook::Closure(ptr) => {
let func: &mut Box<dyn FnMut(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64)> =
let func: &mut Box<dyn FnMut(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u64)> =
transmute(ptr);
(func)(hooks, inprocess_get_state::<S>(), id);
}
@ -138,30 +138,23 @@ where
static mut BLOCK_HOOKS: Vec<(Hook, Hook)> = vec![];
extern "C" fn gen_block_hook_wrapper<I, QT, S>(pc: GuestAddr, index: u64) -> u64
extern "C" fn gen_block_hook_wrapper<QT, S>(pc: GuestAddr, index: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let (gen, _) = &mut BLOCK_HOOKS[index as usize];
match gen {
Hook::Function(ptr) => {
let func: fn(
&mut QemuHooks<'_, I, QT, S>,
Option<&mut S>,
GuestAddr,
) -> Option<u64> = transmute(*ptr);
let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, GuestAddr) -> Option<u64> =
transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), pc).map_or(SKIP_EXEC_HOOK, |id| id)
}
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(
&mut QemuHooks<'_, I, QT, S>,
Option<&mut S>,
GuestAddr,
) -> Option<u64>,
dyn FnMut(&mut QemuHooks<'_, QT, S>, Option<&mut S>, GuestAddr) -> Option<u64>,
> = transmute(ptr);
(func)(hooks, inprocess_get_state::<S>(), pc).map_or(SKIP_EXEC_HOOK, |id| id)
}
@ -170,21 +163,21 @@ where
}
}
extern "C" fn exec_block_hook_wrapper<I, QT, S>(id: u64, index: u64)
extern "C" fn exec_block_hook_wrapper<QT, S>(id: u64, index: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let (_, exec) = &mut BLOCK_HOOKS[index as usize];
match exec {
Hook::Function(ptr) => {
let func: fn(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64) = transmute(*ptr);
let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u64) = transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), id);
}
Hook::Closure(ptr) => {
let func: &mut Box<dyn FnMut(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64)> =
let func: &mut Box<dyn FnMut(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u64)> =
transmute(ptr);
(func)(hooks, inprocess_get_state::<S>(), id);
}
@ -196,18 +189,18 @@ where
static mut READ_HOOKS: Vec<(Hook, Hook, Hook, Hook, Hook, Hook)> = vec![];
static mut WRITE_HOOKS: Vec<(Hook, Hook, Hook, Hook, Hook, Hook)> = vec![];
extern "C" fn gen_read_hook_wrapper<I, QT, S>(pc: GuestAddr, size: usize, index: u64) -> u64
extern "C" fn gen_read_hook_wrapper<QT, S>(pc: GuestAddr, size: usize, index: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let (gen, _, _, _, _, _) = &mut READ_HOOKS[index as usize];
match gen {
Hook::Function(ptr) => {
let func: fn(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
usize,
@ -217,7 +210,7 @@ where
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
usize,
@ -230,18 +223,18 @@ where
}
}
extern "C" fn gen_write_hook_wrapper<I, QT, S>(pc: GuestAddr, size: usize, index: u64) -> u64
extern "C" fn gen_write_hook_wrapper<QT, S>(pc: GuestAddr, size: usize, index: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let (gen, _, _, _, _, _) = &mut WRITE_HOOKS[index as usize];
match gen {
Hook::Function(ptr) => {
let func: fn(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
usize,
@ -251,7 +244,7 @@ where
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
usize,
@ -266,23 +259,23 @@ where
macro_rules! define_rw_exec_hook {
($name:ident, $field:tt, $global:ident) => {
extern "C" fn $name<I, QT, S>(id: u64, addr: GuestAddr, index: u64)
extern "C" fn $name<QT, S>(id: u64, addr: GuestAddr, index: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let exec = &mut $global[index as usize].$field;
match exec {
Hook::Function(ptr) => {
let func: fn(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64, GuestAddr) =
let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u64, GuestAddr) =
transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), id, addr);
}
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64, GuestAddr),
dyn FnMut(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u64, GuestAddr),
> = transmute(ptr);
(func)(hooks, inprocess_get_state::<S>(), id, addr);
}
@ -295,18 +288,18 @@ macro_rules! define_rw_exec_hook {
macro_rules! define_rw_exec_hook_n {
($name:ident, $field:tt, $global:ident) => {
extern "C" fn $name<I, QT, S>(id: u64, addr: GuestAddr, size: usize, index: u64)
extern "C" fn $name<QT, S>(id: u64, addr: GuestAddr, size: usize, index: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let exec = &mut $global[index as usize].$field;
match exec {
Hook::Function(ptr) => {
let func: fn(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
u64,
GuestAddr,
@ -317,7 +310,7 @@ macro_rules! define_rw_exec_hook_n {
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
u64,
GuestAddr,
@ -347,18 +340,18 @@ define_rw_exec_hook_n!(exec_write_n_hook_wrapper, 5, WRITE_HOOKS);
static mut CMP_HOOKS: Vec<(Hook, Hook, Hook, Hook, Hook)> = vec![];
extern "C" fn gen_cmp_hook_wrapper<I, QT, S>(pc: GuestAddr, size: usize, index: u64) -> u64
extern "C" fn gen_cmp_hook_wrapper<QT, S>(pc: GuestAddr, size: usize, index: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let (gen, _, _, _, _) = &mut CMP_HOOKS[index as usize];
match gen {
Hook::Function(ptr) => {
let func: fn(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
usize,
@ -368,7 +361,7 @@ where
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
usize,
@ -383,18 +376,18 @@ where
macro_rules! define_cmp_exec_hook {
($name:ident, $field:tt, $itype:ty) => {
extern "C" fn $name<I, QT, S>(id: u64, v0: $itype, v1: $itype, index: u64)
extern "C" fn $name<QT, S>(id: u64, v0: $itype, v1: $itype, index: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let exec = &mut CMP_HOOKS[index as usize].$field;
match exec {
Hook::Function(ptr) => {
let func: fn(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
u64,
$itype,
@ -405,7 +398,7 @@ macro_rules! define_cmp_exec_hook {
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
u64,
$itype,
@ -429,31 +422,29 @@ define_cmp_exec_hook!(exec_cmp8_hook_wrapper, 4, u64);
#[cfg(feature = "usermode")]
static mut ON_THREAD_HOOKS: Vec<Hook> = vec![];
#[cfg(feature = "usermode")]
extern "C" fn on_thread_hooks_wrapper<I, QT, S>(tid: u32)
extern "C" fn on_thread_hooks_wrapper<QT, S>(tid: u32)
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
for hook in &mut ON_THREAD_HOOKS {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
match hook {
Hook::Function(ptr) => {
let func: fn(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u32) =
transmute(*ptr);
let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u32) = transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), tid);
}
Hook::Closure(ptr) => {
let mut func: Box<
dyn FnMut(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u32),
> = transmute(*ptr);
let mut func: Box<dyn FnMut(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u32)> =
transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), tid);
// Forget the closure so that drop is not called on captured variables.
core::mem::forget(func);
}
Hook::Once(ptr) => {
let func: Box<dyn FnOnce(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u32)> =
let func: Box<dyn FnOnce(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u32)> =
transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), tid);
*hook = Hook::Empty;
@ -467,7 +458,7 @@ where
#[cfg(feature = "usermode")]
static mut SYSCALL_HOOKS: Vec<Hook> = vec![];
#[cfg(feature = "usermode")]
extern "C" fn syscall_hooks_wrapper<I, QT, S>(
extern "C" fn syscall_hooks_wrapper<QT, S>(
sys_num: i32,
a0: u64,
a1: u64,
@ -479,18 +470,18 @@ extern "C" fn syscall_hooks_wrapper<I, QT, S>(
a7: u64,
) -> SyscallHookResult
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let mut res = SyscallHookResult::new(None);
for hook in &SYSCALL_HOOKS {
match hook {
Hook::Function(ptr) => {
#[allow(clippy::type_complexity)]
let func: fn(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
i32,
u64,
@ -524,7 +515,7 @@ where
#[allow(clippy::type_complexity)]
let mut func: Box<
dyn FnMut(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
i32,
u64,
@ -569,7 +560,7 @@ where
#[cfg(feature = "usermode")]
static mut SYSCALL_POST_HOOKS: Vec<Hook> = vec![];
#[cfg(feature = "usermode")]
extern "C" fn syscall_after_hooks_wrapper<I, QT, S>(
extern "C" fn syscall_after_hooks_wrapper<QT, S>(
result: u64,
sys_num: i32,
a0: u64,
@ -582,18 +573,18 @@ extern "C" fn syscall_after_hooks_wrapper<I, QT, S>(
a7: u64,
) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<I, QT, S>();
let hooks = get_qemu_hooks::<QT, S>();
let mut res = result;
for hook in &SYSCALL_POST_HOOKS {
match hook {
Hook::Function(ptr) => {
#[allow(clippy::type_complexity)]
let func: fn(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
u64,
i32,
@ -625,7 +616,7 @@ where
#[allow(clippy::type_complexity)]
let mut func: Box<
dyn FnMut(
&mut QemuHooks<'_, I, QT, S>,
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
u64,
i32,
@ -666,20 +657,20 @@ where
static mut HOOKS_IS_INITIALIZED: bool = false;
pub struct QemuHooks<'a, I, QT, S>
pub struct QemuHooks<'a, QT, S>
where
QT: QemuHelperTuple<I, S>,
I: Input,
QT: QemuHelperTuple<S>,
S: UsesInput,
{
helpers: QT,
emulator: &'a Emulator,
phantom: PhantomData<(I, S)>,
phantom: PhantomData<S>,
}
impl<'a, I, QT, S> Debug for QemuHooks<'a, I, QT, S>
impl<'a, QT, S> Debug for QemuHooks<'a, QT, S>
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuHooks")
@ -689,10 +680,10 @@ where
}
}
impl<'a, I, QT, S> QemuHooks<'a, I, QT, S>
impl<'a, QT, S> QemuHooks<'a, QT, S>
where
QT: QemuHelperTuple<I, S>,
I: Input,
QT: QemuHelperTuple<S>,
S: UsesInput,
{
pub fn new(emulator: &'a Emulator, helpers: QT) -> Box<Self> {
unsafe {
@ -754,7 +745,7 @@ where
let index = GENERIC_HOOKS.len();
self.emulator.set_hook(
addr,
generic_hook_wrapper::<I, QT, S>,
generic_hook_wrapper::<QT, S>,
index as u64,
invalidate_block,
);
@ -771,7 +762,7 @@ where
let index = GENERIC_HOOKS.len();
self.emulator.set_hook(
addr,
generic_hook_wrapper::<I, QT, S>,
generic_hook_wrapper::<QT, S>,
index as u64,
invalidate_block,
);
@ -791,12 +782,12 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_edge_hook_wrapper::<I, QT, S>)
Some(gen_edge_hook_wrapper::<QT, S>)
},
if execution_hook.is_none() {
None
} else {
Some(exec_edge_hook_wrapper::<I, QT, S>)
Some(exec_edge_hook_wrapper::<QT, S>)
},
index as u64,
);
@ -823,12 +814,12 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_edge_hook_wrapper::<I, QT, S>)
Some(gen_edge_hook_wrapper::<QT, S>)
},
if execution_hook.is_none() {
None
} else {
Some(exec_edge_hook_wrapper::<I, QT, S>)
Some(exec_edge_hook_wrapper::<QT, S>)
},
index as u64,
);
@ -851,7 +842,7 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_edge_hook_wrapper::<I, QT, S>)
Some(gen_edge_hook_wrapper::<QT, S>)
},
execution_hook,
index as u64,
@ -876,12 +867,12 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_block_hook_wrapper::<I, QT, S>)
Some(gen_block_hook_wrapper::<QT, S>)
},
if execution_hook.is_none() {
None
} else {
Some(exec_block_hook_wrapper::<I, QT, S>)
Some(exec_block_hook_wrapper::<QT, S>)
},
index as u64,
);
@ -908,12 +899,12 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_block_hook_wrapper::<I, QT, S>)
Some(gen_block_hook_wrapper::<QT, S>)
},
if execution_hook.is_none() {
None
} else {
Some(exec_block_hook_wrapper::<I, QT, S>)
Some(exec_block_hook_wrapper::<QT, S>)
},
index as u64,
);
@ -934,7 +925,7 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_block_hook_wrapper::<I, QT, S>)
Some(gen_block_hook_wrapper::<QT, S>)
},
execution_hook,
index as u64,
@ -967,32 +958,32 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_read_hook_wrapper::<I, QT, S>)
Some(gen_read_hook_wrapper::<QT, S>)
},
if execution_hook1.is_none() {
None
} else {
Some(exec_read1_hook_wrapper::<I, QT, S>)
Some(exec_read1_hook_wrapper::<QT, S>)
},
if execution_hook2.is_none() {
None
} else {
Some(exec_read2_hook_wrapper::<I, QT, S>)
Some(exec_read2_hook_wrapper::<QT, S>)
},
if execution_hook4.is_none() {
None
} else {
Some(exec_read4_hook_wrapper::<I, QT, S>)
Some(exec_read4_hook_wrapper::<QT, S>)
},
if execution_hook8.is_none() {
None
} else {
Some(exec_read8_hook_wrapper::<I, QT, S>)
Some(exec_read8_hook_wrapper::<QT, S>)
},
if execution_hook_n.is_none() {
None
} else {
Some(exec_read_n_hook_wrapper::<I, QT, S>)
Some(exec_read_n_hook_wrapper::<QT, S>)
},
index as u64,
);
@ -1037,32 +1028,32 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_read_hook_wrapper::<I, QT, S>)
Some(gen_read_hook_wrapper::<QT, S>)
},
if execution_hook1.is_none() {
None
} else {
Some(exec_read1_hook_wrapper::<I, QT, S>)
Some(exec_read1_hook_wrapper::<QT, S>)
},
if execution_hook2.is_none() {
None
} else {
Some(exec_read2_hook_wrapper::<I, QT, S>)
Some(exec_read2_hook_wrapper::<QT, S>)
},
if execution_hook4.is_none() {
None
} else {
Some(exec_read4_hook_wrapper::<I, QT, S>)
Some(exec_read4_hook_wrapper::<QT, S>)
},
if execution_hook8.is_none() {
None
} else {
Some(exec_read8_hook_wrapper::<I, QT, S>)
Some(exec_read8_hook_wrapper::<QT, S>)
},
if execution_hook_n.is_none() {
None
} else {
Some(exec_read_n_hook_wrapper::<I, QT, S>)
Some(exec_read_n_hook_wrapper::<QT, S>)
},
index as u64,
);
@ -1093,7 +1084,7 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_read_hook_wrapper::<I, QT, S>)
Some(gen_read_hook_wrapper::<QT, S>)
},
execution_hook1,
execution_hook2,
@ -1134,32 +1125,32 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_write_hook_wrapper::<I, QT, S>)
Some(gen_write_hook_wrapper::<QT, S>)
},
if execution_hook1.is_none() {
None
} else {
Some(exec_write1_hook_wrapper::<I, QT, S>)
Some(exec_write1_hook_wrapper::<QT, S>)
},
if execution_hook2.is_none() {
None
} else {
Some(exec_write2_hook_wrapper::<I, QT, S>)
Some(exec_write2_hook_wrapper::<QT, S>)
},
if execution_hook4.is_none() {
None
} else {
Some(exec_write4_hook_wrapper::<I, QT, S>)
Some(exec_write4_hook_wrapper::<QT, S>)
},
if execution_hook8.is_none() {
None
} else {
Some(exec_write8_hook_wrapper::<I, QT, S>)
Some(exec_write8_hook_wrapper::<QT, S>)
},
if execution_hook_n.is_none() {
None
} else {
Some(exec_write_n_hook_wrapper::<I, QT, S>)
Some(exec_write_n_hook_wrapper::<QT, S>)
},
index as u64,
);
@ -1204,32 +1195,32 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_write_hook_wrapper::<I, QT, S>)
Some(gen_write_hook_wrapper::<QT, S>)
},
if execution_hook1.is_none() {
None
} else {
Some(exec_write1_hook_wrapper::<I, QT, S>)
Some(exec_write1_hook_wrapper::<QT, S>)
},
if execution_hook2.is_none() {
None
} else {
Some(exec_write2_hook_wrapper::<I, QT, S>)
Some(exec_write2_hook_wrapper::<QT, S>)
},
if execution_hook4.is_none() {
None
} else {
Some(exec_write4_hook_wrapper::<I, QT, S>)
Some(exec_write4_hook_wrapper::<QT, S>)
},
if execution_hook8.is_none() {
None
} else {
Some(exec_write8_hook_wrapper::<I, QT, S>)
Some(exec_write8_hook_wrapper::<QT, S>)
},
if execution_hook_n.is_none() {
None
} else {
Some(exec_write_n_hook_wrapper::<I, QT, S>)
Some(exec_write_n_hook_wrapper::<QT, S>)
},
index as u64,
);
@ -1260,7 +1251,7 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_write_hook_wrapper::<I, QT, S>)
Some(gen_write_hook_wrapper::<QT, S>)
},
execution_hook1,
execution_hook2,
@ -1298,27 +1289,27 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_cmp_hook_wrapper::<I, QT, S>)
Some(gen_cmp_hook_wrapper::<QT, S>)
},
if execution_hook1.is_none() {
None
} else {
Some(exec_cmp1_hook_wrapper::<I, QT, S>)
Some(exec_cmp1_hook_wrapper::<QT, S>)
},
if execution_hook2.is_none() {
None
} else {
Some(exec_cmp2_hook_wrapper::<I, QT, S>)
Some(exec_cmp2_hook_wrapper::<QT, S>)
},
if execution_hook4.is_none() {
None
} else {
Some(exec_cmp4_hook_wrapper::<I, QT, S>)
Some(exec_cmp4_hook_wrapper::<QT, S>)
},
if execution_hook8.is_none() {
None
} else {
Some(exec_cmp8_hook_wrapper::<I, QT, S>)
Some(exec_cmp8_hook_wrapper::<QT, S>)
},
index as u64,
);
@ -1357,27 +1348,27 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_cmp_hook_wrapper::<I, QT, S>)
Some(gen_cmp_hook_wrapper::<QT, S>)
},
if execution_hook1.is_none() {
None
} else {
Some(exec_cmp1_hook_wrapper::<I, QT, S>)
Some(exec_cmp1_hook_wrapper::<QT, S>)
},
if execution_hook2.is_none() {
None
} else {
Some(exec_cmp2_hook_wrapper::<I, QT, S>)
Some(exec_cmp2_hook_wrapper::<QT, S>)
},
if execution_hook4.is_none() {
None
} else {
Some(exec_cmp4_hook_wrapper::<I, QT, S>)
Some(exec_cmp4_hook_wrapper::<QT, S>)
},
if execution_hook8.is_none() {
None
} else {
Some(exec_cmp8_hook_wrapper::<I, QT, S>)
Some(exec_cmp8_hook_wrapper::<QT, S>)
},
index as u64,
);
@ -1406,7 +1397,7 @@ where
if generation_hook.is_none() {
None
} else {
Some(gen_cmp_hook_wrapper::<I, QT, S>)
Some(gen_cmp_hook_wrapper::<QT, S>)
},
execution_hook1,
execution_hook2,
@ -1432,7 +1423,7 @@ where
ON_THREAD_HOOKS.push(Hook::Function(hook as *const libc::c_void));
}
self.emulator
.set_on_thread_hook(on_thread_hooks_wrapper::<I, QT, S>);
.set_on_thread_hook(on_thread_hooks_wrapper::<QT, S>);
}
#[cfg(feature = "usermode")]
@ -1444,7 +1435,7 @@ where
ON_THREAD_HOOKS.push(Hook::Closure(transmute(hook)));
}
self.emulator
.set_on_thread_hook(on_thread_hooks_wrapper::<I, QT, S>);
.set_on_thread_hook(on_thread_hooks_wrapper::<QT, S>);
}
#[cfg(feature = "usermode")]
@ -1453,7 +1444,7 @@ where
ON_THREAD_HOOKS.push(Hook::Once(transmute(hook)));
}
self.emulator
.set_on_thread_hook(on_thread_hooks_wrapper::<I, QT, S>);
.set_on_thread_hook(on_thread_hooks_wrapper::<QT, S>);
}
#[cfg(feature = "usermode")]
@ -1478,7 +1469,7 @@ where
SYSCALL_HOOKS.push(Hook::Function(hook as *const libc::c_void));
}
self.emulator
.set_pre_syscall_hook(syscall_hooks_wrapper::<I, QT, S>);
.set_pre_syscall_hook(syscall_hooks_wrapper::<QT, S>);
}
#[cfg(feature = "usermode")]
@ -1505,7 +1496,7 @@ where
SYSCALL_HOOKS.push(Hook::Closure(transmute(hook)));
}
self.emulator
.set_pre_syscall_hook(syscall_hooks_wrapper::<I, QT, S>);
.set_pre_syscall_hook(syscall_hooks_wrapper::<QT, S>);
}
#[cfg(feature = "usermode")]
@ -1531,7 +1522,7 @@ where
SYSCALL_POST_HOOKS.push(Hook::Function(hook as *const libc::c_void));
}
self.emulator
.set_post_syscall_hook(syscall_after_hooks_wrapper::<I, QT, S>);
.set_post_syscall_hook(syscall_after_hooks_wrapper::<QT, S>);
}
#[cfg(feature = "usermode")]
@ -1559,6 +1550,6 @@ where
SYSCALL_POST_HOOKS.push(Hook::Closure(transmute(hook)));
}
self.emulator
.set_post_syscall_hook(syscall_after_hooks_wrapper::<I, QT, S>);
.set_post_syscall_hook(syscall_after_hooks_wrapper::<QT, S>);
}
}

View File

@ -4,7 +4,7 @@ use std::{
sync::Mutex,
};
use libafl::{inputs::Input, state::HasMetadata};
use libafl::{inputs::UsesInput, state::HasMetadata};
use meminterval::{Interval, IntervalTree};
use thread_local::ThreadLocal;
@ -468,31 +468,30 @@ impl Default for QemuSnapshotHelper {
}
}
impl<I, S> QemuHelper<I, S> for QemuSnapshotHelper
impl<S> QemuHelper<S> for QemuSnapshotHelper
where
I: Input,
S: HasMetadata,
S: UsesInput + HasMetadata,
{
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
where
QT: QemuHelperTuple<I, S>,
QT: QemuHelperTuple<S>,
{
hooks.writes(
None,
Some(trace_write1_snapshot::<I, QT, S>),
Some(trace_write2_snapshot::<I, QT, S>),
Some(trace_write4_snapshot::<I, QT, S>),
Some(trace_write8_snapshot::<I, QT, S>),
Some(trace_write_n_snapshot::<I, QT, S>),
Some(trace_write1_snapshot::<QT, S>),
Some(trace_write2_snapshot::<QT, S>),
Some(trace_write4_snapshot::<QT, S>),
Some(trace_write8_snapshot::<QT, S>),
Some(trace_write_n_snapshot::<QT, S>),
);
if !self.accurate_unmap {
hooks.syscalls(filter_mmap_snapshot::<I, QT, S>);
hooks.syscalls(filter_mmap_snapshot::<QT, S>);
}
hooks.after_syscalls(trace_mmap_snapshot::<I, QT, S>);
hooks.after_syscalls(trace_mmap_snapshot::<QT, S>);
}
fn pre_exec(&mut self, emulator: &Emulator, _input: &I) {
fn pre_exec(&mut self, emulator: &Emulator, _input: &S::Input) {
if self.empty {
self.snapshot(emulator);
} else {
@ -501,67 +500,67 @@ where
}
}
pub fn trace_write1_snapshot<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_write1_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(addr, 1);
}
pub fn trace_write2_snapshot<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_write2_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(addr, 2);
}
pub fn trace_write4_snapshot<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_write4_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(addr, 4);
}
pub fn trace_write8_snapshot<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_write8_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(addr, 8);
}
pub fn trace_write_n_snapshot<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_write_n_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
size: usize,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(addr, size);
@ -569,8 +568,8 @@ pub fn trace_write_n_snapshot<I, QT, S>(
#[allow(clippy::too_many_arguments)]
#[allow(non_upper_case_globals)]
pub fn filter_mmap_snapshot<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn filter_mmap_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
sys_num: i32,
a0: u64,
@ -583,8 +582,8 @@ pub fn filter_mmap_snapshot<I, QT, S>(
_a7: u64,
) -> SyscallHookResult
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
if i64::from(sys_num) == SYS_munmap {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
@ -597,8 +596,8 @@ where
#[allow(clippy::too_many_arguments)]
#[allow(non_upper_case_globals)]
pub fn trace_mmap_snapshot<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
pub fn trace_mmap_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
result: u64,
sys_num: i32,
@ -612,8 +611,8 @@ pub fn trace_mmap_snapshot<I, QT, S>(
_a7: u64,
) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
// NOT A COMPLETE LIST OF MEMORY EFFECTS
match i64::from(sys_num) {

View File

@ -114,7 +114,7 @@ impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> {
let monitor = MultiMonitor::new(|s| println!("{s}"));
let mut run_client = |state: Option<_>,
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
mut mgr: LlmpRestartingEventManager<_, _>,
_core_id| {
// Coverage map shared between target and fuzzer
let mut shmem = shmem_provider_client.new_shmem(MAP_SIZE).unwrap();

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