From aefb8e313c15fbadb0255c075446421ccaa979d9 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Fri, 6 Dec 2024 17:02:20 +0000 Subject: [PATCH 01/34] fixing empty multipart name --- libafl/src/inputs/multi.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/libafl/src/inputs/multi.rs b/libafl/src/inputs/multi.rs index 7ebdd0f2c5..55353faa4c 100644 --- a/libafl/src/inputs/multi.rs +++ b/libafl/src/inputs/multi.rs @@ -154,12 +154,16 @@ where I: Input, { fn generate_name(&self, id: Option) -> String { - self.names - .iter() - .cloned() - .zip(self.parts.iter().map(|i| i.generate_name(id))) - .map(|(name, generated)| format!("{name}-{generated}")) - .collect::>() - .join(",") + if self.names().len() > 0 { + self.names + .iter() + .cloned() + .zip(self.parts.iter().map(|i| i.generate_name(id))) + .map(|(name, generated)| format!("{name}-{generated}")) + .collect::>() + .join(",") + } else { + "empty_multipart".to_string() // empty strings cause issues with OnDiskCorpus + } } } From a98c981097b8e22a0affdfbbf178ef0592e0ebfe Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Fri, 6 Dec 2024 18:31:59 +0000 Subject: [PATCH 02/34] fixing clippy --- libafl/src/inputs/multi.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libafl/src/inputs/multi.rs b/libafl/src/inputs/multi.rs index 55353faa4c..ce7007de21 100644 --- a/libafl/src/inputs/multi.rs +++ b/libafl/src/inputs/multi.rs @@ -154,7 +154,9 @@ where I: Input, { fn generate_name(&self, id: Option) -> String { - if self.names().len() > 0 { + if self.names().is_empty() { + "empty_multipart_input".to_string() // empty strings cause issues with OnDiskCorpus + } else { self.names .iter() .cloned() @@ -162,8 +164,6 @@ where .map(|(name, generated)| format!("{name}-{generated}")) .collect::>() .join(",") - } else { - "empty_multipart".to_string() // empty strings cause issues with OnDiskCorpus } } } From 2da6dc5083f1d3a9cc10fd7db7b471595a0587e4 Mon Sep 17 00:00:00 2001 From: "Dongjia \"toka\" Zhang" Date: Fri, 6 Dec 2024 21:19:34 +0100 Subject: [PATCH 03/34] New rules for the contributing (#2752) * Rules * more * aa --- CONTRIBUTING.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 81f90cdc48..ac78e7d8a4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,3 +18,58 @@ Some of the parts in this list may be hard, don't be afraid to open a PR if you Some of these checks can be performed automatically during commit using [pre-commit](https://pre-commit.com/). Once the package is installed, simply run `pre-commit install` to enable the hooks, the checks will run automatically before the commit becomes effective. + +## LibAFL Code Rules + +Before making your pull requests, try to see if your code follows these rules. + +- Wherever possible, use `Cow<'static, str>` instead of String. +- `PhantomData` should have the smallest set of types needed. +- Wherever possible, trait implementations with lifetime specifiers should use '_ lifetime elision. +- Complex constructors should be replaced with `typed_builder`, or write code in the builder pattern for yourself. +- Remove generic restrictions at the definitions (e.g., we do not need to specify that types impl `Serialize`, `Deserialize`, or `Debug` anymore at the struct definitions). Therefore, try avoiding code like this unless the contraint is really necessary. +```rust +pub struct X + where + A: P // <- Do not add contraints here +{ + fn ... +} + +``` +- Reduce generics to the least restrictive necessary. __Never overspecify the contraints__. There's no automated tool to check the useless constraints, so you have to verify this manually. +```rust +pub struct X + where + A: P + Q // <- Try to use the as smallest set of constraints as possible. If the code still compiles after deleting Q, then remove it. +{ + fn ... +} + +``` +- Traits which have an associated type should refer to the associated type, not the concrete/generic. In other words, you should only have the associated type when you can define a getter to it. For example, in the following code, you can define a associate type. +```rust +pub trait X +{ + type A; // <- You should(can) define it as long as you have a getter to it. + fn a(&self) -> A; +} + +``` +- __Ideally__ the types used in the the arguments of methods in traits should have the same as the types defined on the traits. +```rust +pub trait X // <- this trait have 3 generics, A, B, and C +{ + fn do_stuff(&self, a: A, b: B, c: C); // <- this is good because it uses all A, B, and C. + + fn do_other_stuff(&self, a: A, b: B); // <- this is not ideal because it does not have C. +} +``` +- Always alphabetically order the type generics. Therefore, +```rust +pub struct X {}; // <- Generics are alphabetically ordered +``` +But not, +```rust +pub struct X {}; // <- Generics are not ordered +``` \ No newline at end of file From 1e571a0dc7b61d07db827f20645b88d201988ed0 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sun, 8 Dec 2024 21:46:38 +0100 Subject: [PATCH 04/34] Improve Flexibility of DumpToDiskStage (#2753) * fixing empty multipart name * fixing clippy * improve flexibility of DumpToDiskStage * adding note to MIGRATION.md --- MIGRATION.md | 3 +- libafl/src/stages/dump.rs | 128 +++++++++++++++++++++++++++----------- 2 files changed, 94 insertions(+), 37 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 0f591564ae..e140eab2e7 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -6,4 +6,5 @@ - `MmapShMemProvider::new_shmem_persistent` has been removed in favour of `MmapShMem::persist`. You probably want to do something like this: `let shmem = MmapShMemProvider::new()?.new_shmem(size)?.persist()?;` # 0.14.1 -> 0.14.2 -- `MmapShMem::new` and `MmapShMemProvider::new_shmem_with_id` now take `AsRef` instead of a byte array for the filename/id. \ No newline at end of file +- `MmapShMem::new` and `MmapShMemProvider::new_shmem_with_id` now take `AsRef` instead of a byte array for the filename/id. +- The closure passed to a `DumpToDiskStage` now provides the `Testcase` instead of just the `Input`. \ No newline at end of file diff --git a/libafl/src/stages/dump.rs b/libafl/src/stages/dump.rs index ecfc2c2a10..ee80fa6062 100644 --- a/libafl/src/stages/dump.rs +++ b/libafl/src/stages/dump.rs @@ -1,14 +1,20 @@ //! The [`DumpToDiskStage`] is a stage that dumps the corpus and the solutions to disk to e.g. allow AFL to sync -use alloc::{string::String, vec::Vec}; +use alloc::vec::Vec; use core::{clone::Clone, marker::PhantomData}; -use std::{fs, fs::File, io::Write, path::PathBuf}; +use std::{ + fs::{self, File}, + io::Write, + path::{Path, PathBuf}, + string::{String, ToString}, +}; use libafl_bolts::impl_serdeany; use serde::{Deserialize, Serialize}; use crate::{ - corpus::{Corpus, CorpusId}, + corpus::{Corpus, CorpusId, Testcase}, + inputs::Input, stages::Stage, state::{HasCorpus, HasRand, HasSolutions, UsesState}, Error, HasMetadata, @@ -29,23 +35,26 @@ impl_serdeany!(DumpToDiskMetadata); /// The [`DumpToDiskStage`] is a stage that dumps the corpus and the solutions to disk #[derive(Debug)] -pub struct DumpToDiskStage { +pub struct DumpToDiskStage { solutions_dir: PathBuf, corpus_dir: PathBuf, - to_bytes: CB, + to_bytes: CB1, + generate_filename: CB2, phantom: PhantomData<(EM, Z)>, } -impl UsesState for DumpToDiskStage +impl UsesState for DumpToDiskStage where EM: UsesState, { type State = EM::State; } -impl Stage for DumpToDiskStage +impl Stage for DumpToDiskStage where - CB: FnMut(&Self::Input, &Self::State) -> Vec, + CB1: FnMut(&Testcase, &Self::State) -> Vec, + CB2: FnMut(&Testcase, &CorpusId) -> P, + P: AsRef, EM: UsesState, E: UsesState, Z: UsesState, @@ -77,7 +86,49 @@ where } } -impl DumpToDiskStage +/// Implementation for `DumpToDiskStage` with a default `generate_filename` function. +impl DumpToDiskStage, &CorpusId) -> String, EM, Z> +where + EM: UsesState, + Z: UsesState, + ::State: HasCorpus + HasSolutions + HasRand + HasMetadata, + <::State as HasCorpus>::Corpus: Corpus, + <::State as HasSolutions>::Solutions: Corpus, +{ + /// Create a new [`DumpToDiskStage`] with a default `generate_filename` function. + pub fn new(to_bytes: CB1, corpus_dir: A, solutions_dir: B) -> Result + where + A: Into, + B: Into, + { + Self::new_with_custom_filenames( + to_bytes, + Self::generate_filename, // This is now of type `fn(&Testcase, &CorpusId) -> String` + corpus_dir, + solutions_dir, + ) + } + + /// Default `generate_filename` function. + #[allow(clippy::trivially_copy_pass_by_ref)] + fn generate_filename(testcase: &Testcase, id: &CorpusId) -> String { + [ + Some(id.0.to_string()), + testcase.filename().clone(), + testcase + .input() + .as_ref() + .map(|t| t.generate_name(Some(*id))), + ] + .iter() + .flatten() + .map(String::as_str) + .collect::>() + .join("-") + } +} + +impl DumpToDiskStage where EM: UsesState, Z: UsesState, @@ -85,8 +136,13 @@ where <::State as HasCorpus>::Corpus: Corpus, <::State as HasSolutions>::Solutions: Corpus, { - /// Create a new [`DumpToDiskStage`] - pub fn new(to_bytes: CB, corpus_dir: A, solutions_dir: B) -> Result + /// Create a new [`DumpToDiskStage`] with a custom `generate_filename` function. + pub fn new_with_custom_filenames( + to_bytes: CB1, + generate_filename: CB2, + corpus_dir: A, + solutions_dir: B, + ) -> Result where A: Into, B: Into, @@ -102,7 +158,7 @@ where } let solutions_dir = solutions_dir.into(); if let Err(e) = fs::create_dir(&solutions_dir) { - if !corpus_dir.is_dir() { + if !solutions_dir.is_dir() { return Err(Error::os_error( e, format!("Error creating directory {solutions_dir:?}"), @@ -111,6 +167,7 @@ where } Ok(Self { to_bytes, + generate_filename, solutions_dir, corpus_dir, phantom: PhantomData, @@ -118,12 +175,19 @@ where } #[inline] - fn dump_state_to_disk(&mut self, state: &mut ::State) -> Result<(), Error> + fn dump_state_to_disk>( + &mut self, + state: &mut ::State, + ) -> Result<(), Error> where - CB: FnMut( - &<<::State as HasCorpus>::Corpus as Corpus>::Input, + CB1: FnMut( + &Testcase<<<::State as HasCorpus>::Corpus as Corpus>::Input>, &::State, ) -> Vec, + CB2: FnMut( + &Testcase<<<::State as HasCorpus>::Corpus as Corpus>::Input>, + &CorpusId, + ) -> P, { let (mut corpus_id, mut solutions_id) = if let Some(meta) = state.metadata_map().get::() { @@ -138,37 +202,29 @@ where while let Some(i) = corpus_id { let mut testcase = state.corpus().get(i)?.borrow_mut(); state.corpus().load_input_into(&mut testcase)?; - let bytes = (self.to_bytes)(testcase.input().as_ref().unwrap(), state); - - let fname = self.corpus_dir.join(format!( - "id_{i}_{}", - testcase - .filename() - .as_ref() - .map_or_else(|| "unnamed", String::as_str) - )); + let bytes = (self.to_bytes)(&testcase, state); + + let fname = self + .corpus_dir + .join((self.generate_filename)(&testcase, &i)); let mut f = File::create(fname)?; drop(f.write_all(&bytes)); corpus_id = state.corpus().next(i); } - while let Some(current_id) = solutions_id { - let mut testcase = state.solutions().get(current_id)?.borrow_mut(); + while let Some(i) = solutions_id { + let mut testcase = state.solutions().get(i)?.borrow_mut(); state.solutions().load_input_into(&mut testcase)?; - let bytes = (self.to_bytes)(testcase.input().as_ref().unwrap(), state); - - let fname = self.solutions_dir.join(format!( - "id_{current_id}_{}", - testcase - .filename() - .as_ref() - .map_or_else(|| "unnamed", String::as_str) - )); + let bytes = (self.to_bytes)(&testcase, state); + + let fname = self + .solutions_dir + .join((self.generate_filename)(&testcase, &i)); let mut f = File::create(fname)?; drop(f.write_all(&bytes)); - solutions_id = state.solutions().next(current_id); + solutions_id = state.solutions().next(i); } state.add_metadata(DumpToDiskMetadata { From d020b9e9488b33cc4882104ce7af482bb771309b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:56:31 +0100 Subject: [PATCH 05/34] Update bindgen requirement from 0.70.1 to 0.71.1 (#2756) Updates the requirements on [bindgen](https://github.com/rust-lang/rust-bindgen) to permit the latest version. - [Release notes](https://github.com/rust-lang/rust-bindgen/releases) - [Changelog](https://github.com/rust-lang/rust-bindgen/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/rust-bindgen/compare/v0.70.1...v0.71.1) --- updated-dependencies: - dependency-name: bindgen dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.toml | 2 +- libafl_targets/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0014cb4303..2a4ece4187 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,7 @@ libafl_jumper = { path = "./utils/libafl_jumper", version = "0.14.1", default-fe ahash = { version = "0.8.11", default-features = false } # The hash function already used in hashbrown arbitrary-int = "1.2.7" # arbitrary sized integers, useful in combination with bitfields (bitbybit crate) backtrace = { version = "0.3.74", default-features = false } # Used to get the stacktrace in StacktraceObserver -bindgen = "0.70.1" +bindgen = "0.71.1" bitbybit = "1.3.2" # bitfields, use this for bit fields and bit enums clap = "4.5.18" cc = "1.1.21" diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index 8a5f741b90..3138a12fe6 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -66,7 +66,7 @@ cmplog_extended_instrumentation = [ function-logging = ["common"] track_hit_feedbacks = ["libafl/track_hit_feedbacks"] [build-dependencies] -bindgen = "0.70.1" +bindgen = "0.71.1" cc = { version = "1.1.21", features = ["parallel"] } rustversion = "1.0.17" From e1d0b92394562e91760a6934a961110d991a7139 Mon Sep 17 00:00:00 2001 From: "Dongjia \"toka\" Zhang" Date: Thu, 12 Dec 2024 16:50:17 +0100 Subject: [PATCH 06/34] No Use* from stages (#2745) * no from stage * fixer * doc fix * how was this working???? * more fixes * delete more * rq * cargo-fuzz * m * aa --- fuzzers/baby/tutorial/src/lib.rs | 2 +- .../fuzzbench_fork_qemu/src/fuzzer.rs | 2 +- .../binary_only/fuzzbench_qemu/src/fuzzer.rs | 2 +- .../binary_only/qemu_launcher/src/instance.rs | 13 +- .../fuzzbench_forkserver/src/main.rs | 2 +- .../fuzzbench_forkserver_cmplog/src/main.rs | 5 +- fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 6 +- .../src/stages/mutational_stage.rs | 57 +--- fuzzers/inprocess/dynamic_analysis/src/lib.rs | 2 +- fuzzers/inprocess/fuzzbench/src/lib.rs | 2 +- fuzzers/inprocess/fuzzbench_ctx/src/lib.rs | 2 +- fuzzers/inprocess/fuzzbench_text/src/lib.rs | 4 +- fuzzers/inprocess/libfuzzer_libpng/src/lib.rs | 2 +- .../libfuzzer_libpng_cmin/src/lib.rs | 2 +- .../libfuzzer_libpng_tcp_manager/src/lib.rs | 2 +- .../libfuzzer_windows_asan/src/lib.rs | 2 +- libafl/Cargo.toml | 1 + libafl/src/fuzzer/mod.rs | 2 +- libafl/src/stages/afl_stats.rs | 81 +++-- libafl/src/stages/calibrate.rs | 51 ++- libafl/src/stages/colorization.rs | 71 ++-- libafl/src/stages/concolic.rs | 79 +++-- libafl/src/stages/dump.rs | 73 ++--- libafl/src/stages/generalization.rs | 77 +++-- libafl/src/stages/generation.rs | 30 +- libafl/src/stages/logics.rs | 132 +++----- libafl/src/stages/mod.rs | 279 +++------------- libafl/src/stages/mutational.rs | 256 +++++++-------- libafl/src/stages/power.rs | 161 +++++++--- libafl/src/stages/push/mod.rs | 274 ++++++++-------- libafl/src/stages/push/mutational.rs | 193 +++++++---- libafl/src/stages/stats.rs | 181 ----------- libafl/src/stages/sync.rs | 86 +++-- libafl/src/stages/time_tracker.rs | 31 +- libafl/src/stages/tmin.rs | 304 ++++++++---------- libafl/src/stages/tracing.rs | 132 ++++---- libafl/src/stages/tuneable.rs | 243 +++++++------- libafl/src/stages/unicode.rs | 22 +- libafl/src/stages/verify_timeouts.rs | 32 +- libafl_libfuzzer/runtime/src/lib.rs | 6 +- .../src/cmps/stages/aflpptracing.rs | 55 ++-- 41 files changed, 1250 insertions(+), 1709 deletions(-) delete mode 100644 libafl/src/stages/stats.rs diff --git a/fuzzers/baby/tutorial/src/lib.rs b/fuzzers/baby/tutorial/src/lib.rs index 9801ee7d76..3e44abff34 100644 --- a/fuzzers/baby/tutorial/src/lib.rs +++ b/fuzzers/baby/tutorial/src/lib.rs @@ -132,7 +132,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re // Setup a lain mutator with a mutational stage let mutator = LainMutator::new(); - let power: StdPowerMutationalStage<_, _, PacketData, _, _> = + let power: StdPowerMutationalStage<_, _, PacketData, _, _, _> = StdPowerMutationalStage::new(mutator); let mut stages = tuple_list!(calibration, power); diff --git a/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs b/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs index 838f8d8df5..bce4a5c3cc 100644 --- a/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs +++ b/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs @@ -314,7 +314,7 @@ fn fuzz( 5, )?; - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus diff --git a/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs index a26a588bb4..fe82f757ff 100644 --- a/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/binary_only/fuzzbench_qemu/src/fuzzer.rs @@ -317,7 +317,7 @@ fn fuzz( 5, )?; - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus diff --git a/fuzzers/binary_only/qemu_launcher/src/instance.rs b/fuzzers/binary_only/qemu_launcher/src/instance.rs index 268aa75f88..5693c8ae2b 100644 --- a/fuzzers/binary_only/qemu_launcher/src/instance.rs +++ b/fuzzers/binary_only/qemu_launcher/src/instance.rs @@ -1,5 +1,5 @@ use core::fmt::Debug; -use std::{fs, marker::PhantomData, ops::Range, process, time::Duration}; +use std::{fs, marker::PhantomData, ops::Range, path::PathBuf, process}; #[cfg(feature = "simplemgr")] use libafl::events::SimpleEventManager; @@ -23,8 +23,8 @@ use libafl::{ powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler, }, stages::{ - calibrate::CalibrationStage, power::StdPowerMutationalStage, IfStage, ShadowTracingStage, - StagesTuple, StatsStage, StdMutationalStage, + calibrate::CalibrationStage, power::StdPowerMutationalStage, AflStatsStage, IfStage, + ShadowTracingStage, StagesTuple, StdMutationalStage, }, state::{HasCorpus, StdState, UsesState}, Error, HasMetadata, NopFuzzer, @@ -137,7 +137,10 @@ impl Instance<'_, M> { let stats_stage = IfStage::new( |_, _, _, _| Ok(self.options.tui), - tuple_list!(StatsStage::new(Duration::from_secs(5))), + tuple_list!(AflStatsStage::builder() + .map_observer(&edges_observer) + .stats_file(PathBuf::from("stats.txt")) + .build()?), ); // Feedback to rate the interestingness of an input @@ -274,7 +277,7 @@ impl Instance<'_, M> { 5, )?; - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); // The order of the stages matter! diff --git a/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs b/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs index 3690978ede..9bead73df2 100644 --- a/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs +++ b/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs @@ -298,7 +298,7 @@ fn fuzz( 5, )?; - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus diff --git a/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs b/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs index 9fab692177..4480972337 100644 --- a/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs +++ b/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs @@ -300,7 +300,7 @@ fn fuzz( 5, )?; - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus @@ -371,7 +371,8 @@ fn fuzz( let tracing = AFLppCmplogTracingStage::new(cmplog_executor, cmplog_ref); // Setup a randomic Input2State stage - let rq = MultiMutationalStage::new(AFLppRedQueen::with_cmplog_options(true, true)); + let rq: MultiMutationalStage<_, _, BytesInput, _, _, _> = + MultiMutationalStage::new(AFLppRedQueen::with_cmplog_options(true, true)); let cb = |_fuzzer: &mut _, _executor: &mut _, diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index c985a81b0f..24f4178083 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -266,7 +266,7 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, { SupportedMutationalStages::StdMutational(StdMutationalStage::new(mutation), PhantomData) } else { SupportedMutationalStages::PowerMutational( - StdPowerMutationalStage::new(mutation), + StdPowerMutationalStage::<_, _, BytesInput, _, _, _>::new(mutation), PhantomData, ) }; @@ -487,7 +487,9 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, { let tracing = AFLppCmplogTracingStage::new(cmplog_executor, cmplog_ref); // Create a randomic Input2State stage - let rq = MultiMutationalStage::new(AFLppRedQueen::with_cmplog_options(true, true)); + let rq = MultiMutationalStage::<_, _, BytesInput, _, _, _>::new( + AFLppRedQueen::with_cmplog_options(true, true), + ); // Create an IfStage and wrap the CmpLog stages in it. // We run cmplog on the second fuzz run of the testcase. diff --git a/fuzzers/forkserver/libafl-fuzz/src/stages/mutational_stage.rs b/fuzzers/forkserver/libafl-fuzz/src/stages/mutational_stage.rs index 546d2de1a6..efde3a29cb 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/stages/mutational_stage.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/stages/mutational_stage.rs @@ -1,37 +1,25 @@ use std::{borrow::Cow, marker::PhantomData}; use libafl::{ - corpus::Corpus, - inputs::Input, - mutators::Mutator, - stages::{mutational::MutatedTransform, MutationalStage, Stage}, - state::{HasCorpus, HasRand, State, UsesState}, - Error, Evaluator, HasNamedMetadata, + stages::{MutationalStage, Stage}, + Error, }; use libafl_bolts::Named; #[derive(Debug)] -pub enum SupportedMutationalStages { - StdMutational(SM, PhantomData<(S, I, M, EM, Z, E)>), - PowerMutational(P, PhantomData<(S, I, M, EM, Z, E)>), +pub enum SupportedMutationalStages { + StdMutational(SM, PhantomData

), + PowerMutational(P, PhantomData), } -impl MutationalStage - for SupportedMutationalStages +impl MutationalStage for SupportedMutationalStages where - E: UsesState, - EM: UsesState, - M: Mutator, - Z: Evaluator, - I: MutatedTransform + Clone + Input, - SM: MutationalStage, - P: MutationalStage, - S: State + HasRand + HasCorpus + HasNamedMetadata, - <::State as HasCorpus>::Corpus: Corpus, //delete me + SM: MutationalStage, + P: MutationalStage, { + type Mutator = SM::Mutator; /// The mutator, added to this stage - #[inline] - fn mutator(&self) -> &M { + fn mutator(&self) -> &Self::Mutator { match self { Self::StdMutational(m, _) => m.mutator(), Self::PowerMutational(p, _) => p.mutator(), @@ -40,7 +28,7 @@ where /// The list of mutators, added to this stage (as mutable ref) #[inline] - fn mutator_mut(&mut self) -> &mut M { + fn mutator_mut(&mut self) -> &mut Self::Mutator { match self { Self::StdMutational(m, _) => m.mutator_mut(), Self::PowerMutational(p, _) => p.mutator_mut(), @@ -56,14 +44,7 @@ where } } -impl UsesState for SupportedMutationalStages -where - S: State + HasRand, -{ - type State = S; -} - -impl Named for SupportedMutationalStages +impl Named for SupportedMutationalStages where SM: Named, P: Named, @@ -76,18 +57,10 @@ where } } -impl Stage - for SupportedMutationalStages +impl Stage for SupportedMutationalStages where - E: UsesState, - EM: UsesState, - M: Mutator, - Z: Evaluator, - I: MutatedTransform + Clone + Input, - SM: MutationalStage, - P: MutationalStage, - S: State + HasRand + HasCorpus + HasNamedMetadata, - <::State as HasCorpus>::Corpus: Corpus, //delete me + SM: Stage, + P: Stage, { #[inline] #[allow(clippy::let_and_return)] diff --git a/fuzzers/inprocess/dynamic_analysis/src/lib.rs b/fuzzers/inprocess/dynamic_analysis/src/lib.rs index 3a4d445d7a..616bb8960e 100644 --- a/fuzzers/inprocess/dynamic_analysis/src/lib.rs +++ b/fuzzers/inprocess/dynamic_analysis/src/lib.rs @@ -314,7 +314,7 @@ fn fuzz( 5, )?; - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus diff --git a/fuzzers/inprocess/fuzzbench/src/lib.rs b/fuzzers/inprocess/fuzzbench/src/lib.rs index 710148e9cc..99b6e94b79 100644 --- a/fuzzers/inprocess/fuzzbench/src/lib.rs +++ b/fuzzers/inprocess/fuzzbench/src/lib.rs @@ -307,7 +307,7 @@ fn fuzz( 5, )?; - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus diff --git a/fuzzers/inprocess/fuzzbench_ctx/src/lib.rs b/fuzzers/inprocess/fuzzbench_ctx/src/lib.rs index 5d96df01a0..1fc485c250 100644 --- a/fuzzers/inprocess/fuzzbench_ctx/src/lib.rs +++ b/fuzzers/inprocess/fuzzbench_ctx/src/lib.rs @@ -317,7 +317,7 @@ fn fuzz( 5, )?; - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus diff --git a/fuzzers/inprocess/fuzzbench_text/src/lib.rs b/fuzzers/inprocess/fuzzbench_text/src/lib.rs index 1218689df9..f717c19491 100644 --- a/fuzzers/inprocess/fuzzbench_text/src/lib.rs +++ b/fuzzers/inprocess/fuzzbench_text/src/lib.rs @@ -374,7 +374,7 @@ fn fuzz_binary( 5, )?; - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); // A minimization+queue policy to get testcasess from the corpus @@ -589,7 +589,7 @@ fn fuzz_text( 5, )?; - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); let grimoire_mutator = StdScheduledMutator::with_max_stack_pow( diff --git a/fuzzers/inprocess/libfuzzer_libpng/src/lib.rs b/fuzzers/inprocess/libfuzzer_libpng/src/lib.rs index 9718674d32..e783ed5116 100644 --- a/fuzzers/inprocess/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/inprocess/libfuzzer_libpng/src/lib.rs @@ -143,7 +143,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); let mut stages = tuple_list!(calibration, power); diff --git a/fuzzers/inprocess/libfuzzer_libpng_cmin/src/lib.rs b/fuzzers/inprocess/libfuzzer_libpng_cmin/src/lib.rs index 823b04d79d..5a456892bc 100644 --- a/fuzzers/inprocess/libfuzzer_libpng_cmin/src/lib.rs +++ b/fuzzers/inprocess/libfuzzer_libpng_cmin/src/lib.rs @@ -140,7 +140,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); let mut stages = tuple_list!(calibration, power); diff --git a/fuzzers/inprocess/libfuzzer_libpng_tcp_manager/src/lib.rs b/fuzzers/inprocess/libfuzzer_libpng_tcp_manager/src/lib.rs index e2b23d599e..033d6814cf 100644 --- a/fuzzers/inprocess/libfuzzer_libpng_tcp_manager/src/lib.rs +++ b/fuzzers/inprocess/libfuzzer_libpng_tcp_manager/src/lib.rs @@ -141,7 +141,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); let mut stages = tuple_list!(calibration, power); diff --git a/fuzzers/inprocess/libfuzzer_windows_asan/src/lib.rs b/fuzzers/inprocess/libfuzzer_windows_asan/src/lib.rs index 3ae7f82b15..89b50d70ab 100644 --- a/fuzzers/inprocess/libfuzzer_windows_asan/src/lib.rs +++ b/fuzzers/inprocess/libfuzzer_windows_asan/src/lib.rs @@ -110,7 +110,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); - let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = + let power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(mutator); let mut stages = tuple_list!(calibration, power); diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 2592a2c7c1..d21cc90264 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -28,6 +28,7 @@ rustc-args = ["--cfg", "docsrs"] [features] default = [ + "introspection", "std", "derive", "llmp_compression", diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index bd82146662..8a074282cb 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -27,7 +27,7 @@ use crate::{ }; /// Send a monitor update all 15 (or more) seconds -const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15); +pub(crate) const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15); /// Holds a scheduler pub trait HasScheduler: UsesState diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs index 7f37241fc3..6aba14ee18 100644 --- a/libafl/src/stages/afl_stats.rs +++ b/libafl/src/stages/afl_stats.rs @@ -26,11 +26,12 @@ use crate::{ corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase}, events::EventFirer, executors::HasObservers, + inputs::UsesInput, mutators::Tokens, observers::MapObserver, schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles}, stages::{calibrate::UnstableEntriesMetadata, Stage}, - state::{HasCorpus, HasExecutions, HasImported, HasStartTime, Stoppable, UsesState}, + state::{HasCorpus, HasExecutions, HasImported, HasStartTime, Stoppable}, std::string::ToString, Error, HasMetadata, HasNamedMetadata, HasScheduler, }; @@ -73,7 +74,7 @@ libafl_bolts::impl_serdeany!(FuzzTime); /// The [`AflStatsStage`] is a Stage that calculates and writes /// AFL++'s `fuzzer_stats` and `plot_data` information. #[derive(Debug, Clone)] -pub struct AflStatsStage { +pub struct AflStatsStage { map_observer_handle: Handle, stats_file_path: PathBuf, plot_file_path: Option, @@ -112,7 +113,7 @@ pub struct AflStatsStage { autotokens_enabled: bool, /// The core we are bound to core_id: CoreId, - phantom_data: PhantomData<(O, E, EM, Z)>, + phantom_data: PhantomData<(O, E, EM, S, Z)>, } /// AFL++'s `fuzzer_stats` @@ -234,39 +235,31 @@ pub struct AFLPlotData<'a> { edges_found: &'a u64, } -impl UsesState for AflStatsStage +impl Stage for AflStatsStage where - E: UsesState, - EM: EventFirer, - Z: UsesState, -{ - type State = E::State; -} - -impl Stage for AflStatsStage -where - E: UsesState + HasObservers, - EM: EventFirer, - Z: UsesState + HasScheduler, - E::State: HasImported + E: HasObservers, + EM: EventFirer, + Z: HasScheduler, + S: HasImported + HasCorpus + HasMetadata + HasStartTime + HasExecutions + HasNamedMetadata - + Stoppable, + + Stoppable + + HasCurrentCorpusId + + UsesInput, E::Observers: MatchNameRef, O: MapObserver, C: AsRef + Named, ::Scheduler: HasQueueCycles, - <::State as HasCorpus>::Corpus: Corpus, { #[allow(clippy::too_many_lines)] fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut E::State, + state: &mut S, _manager: &mut EM, ) -> Result<(), Error> { let Some(corpus_idx) = state.current_corpus_id()? else { @@ -413,27 +406,26 @@ where Ok(()) } - fn should_restart(&mut self, _state: &mut Self::State) -> Result { + fn should_restart(&mut self, _state: &mut S) -> Result { Ok(true) } - fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> { Ok(()) } } -impl AflStatsStage +impl AflStatsStage where - E: UsesState + HasObservers, - EM: EventFirer, - Z: UsesState, - E::State: HasImported + HasCorpus + HasMetadata + HasExecutions, + E: HasObservers, + EM: EventFirer, + S: HasImported + HasCorpus + HasMetadata + HasExecutions, C: AsRef + Named, O: MapObserver, { /// Builder for `AflStatsStage` #[must_use] - pub fn builder() -> AflStatsStageBuilder { + pub fn builder() -> AflStatsStageBuilder { AflStatsStageBuilder::new() } @@ -459,13 +451,13 @@ where Ok(()) } - fn maybe_update_is_favored_size(&mut self, testcase: &Testcase) { + fn maybe_update_is_favored_size(&mut self, testcase: &Testcase<::Input>) { if testcase.has_metadata::() { self.is_favored_size += 1; } } - fn maybe_update_slowest_exec(&mut self, testcase: &Testcase) { + fn maybe_update_slowest_exec(&mut self, testcase: &Testcase<::Input>) { if let Some(exec_time) = testcase.exec_time() { if exec_time > &self.slowest_exec { self.slowest_exec = *exec_time; @@ -477,7 +469,7 @@ where self.has_fuzzed_size += 1; } - fn maybe_update_max_depth(&mut self, testcase: &Testcase) { + fn maybe_update_max_depth(&mut self, testcase: &Testcase<::Input>) { if let Ok(metadata) = testcase.metadata::() { if metadata.depth() > self.max_depth { self.max_depth = metadata.depth(); @@ -490,7 +482,11 @@ where } #[cfg(feature = "track_hit_feedbacks")] - fn maybe_update_last_crash(&mut self, testcase: &Testcase, state: &E::State) { + fn maybe_update_last_crash( + &mut self, + testcase: &Testcase<::Input>, + state: &S, + ) { #[cfg(feature = "track_hit_feedbacks")] if testcase .hit_objectives() @@ -502,7 +498,11 @@ where } #[cfg(feature = "track_hit_feedbacks")] - fn maybe_update_last_hang(&mut self, testcase: &Testcase, state: &E::State) { + fn maybe_update_last_hang( + &mut self, + testcase: &Testcase<::Input>, + state: &S, + ) { if testcase .hit_objectives() .contains(&Cow::Borrowed(TIMEOUT_FEEDBACK_NAME)) @@ -624,7 +624,7 @@ pub fn get_run_cmdline() -> Cow<'static, str> { /// The Builder for `AflStatsStage` #[derive(Debug)] -pub struct AflStatsStageBuilder { +pub struct AflStatsStageBuilder { stats_file_path: Option, plot_file_path: Option, core_id: Option, @@ -636,15 +636,14 @@ pub struct AflStatsStageBuilder { banner: String, version: String, target_mode: String, - phantom_data: PhantomData<(O, E, EM, Z)>, + phantom_data: PhantomData<(O, E, EM, S, Z)>, } -impl AflStatsStageBuilder +impl AflStatsStageBuilder where - E: UsesState + HasObservers, - EM: EventFirer, - Z: UsesState, - E::State: HasImported + HasCorpus + HasMetadata + HasExecutions, + E: HasObservers, + EM: EventFirer, + S: HasImported + HasCorpus + HasMetadata + HasExecutions, C: AsRef + Named, O: MapObserver, { @@ -758,7 +757,7 @@ where /// Cannot create the plot file (if provided) /// No `MapObserver` supplied to the builder /// No `stats_file_path` provieded - pub fn build(self) -> Result, Error> { + pub fn build(self) -> Result, Error> { if self.stats_file_path.is_none() { return Err(Error::illegal_argument("Must set `stats_file_path`")); } diff --git a/libafl/src/stages/calibrate.rs b/libafl/src/stages/calibrate.rs index c138758e16..2d2cc09bd2 100644 --- a/libafl/src/stages/calibrate.rs +++ b/libafl/src/stages/calibrate.rs @@ -13,17 +13,17 @@ use num_traits::Bounded; use serde::{Deserialize, Serialize}; use crate::{ - corpus::{Corpus, SchedulerTestcaseMetadata}, + corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata}, events::{Event, EventFirer, LogSeverity}, executors::{Executor, ExitKind, HasObservers}, feedbacks::{map::MapFeedbackMetadata, HasObserverHandle}, fuzzer::Evaluator, - inputs::UsesInput, + inputs::{Input, UsesInput}, monitors::{AggregatorOps, UserStats, UserStatsValue}, observers::{MapObserver, ObserversTuple}, schedulers::powersched::SchedulerMetadata, stages::{RetryCountRestartHelper, Stage}, - state::{HasCorpus, HasCurrentTestcase, HasExecutions, UsesState}, + state::{HasCorpus, HasCurrentTestcase, HasExecutions}, Error, HasMetadata, HasNamedMetadata, }; @@ -73,38 +73,37 @@ impl Default for UnstableEntriesMetadata { pub const CALIBRATION_STAGE_NAME: &str = "calibration"; /// The calibration stage will measure the average exec time and the target's stability for this input. #[derive(Clone, Debug)] -pub struct CalibrationStage { +pub struct CalibrationStage { map_observer_handle: Handle, map_name: Cow<'static, str>, name: Cow<'static, str>, stage_max: usize, /// If we should track stability track_stability: bool, - phantom: PhantomData<(E, O, OT)>, + phantom: PhantomData<(E, 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 UsesState for CalibrationStage +impl Stage for CalibrationStage where - E: UsesState, -{ - type State = E::State; -} - -impl Stage for CalibrationStage -where - E: Executor + HasObservers, - EM: EventFirer, + E: Executor + HasObservers, + EM: EventFirer, O: MapObserver, C: AsRef, for<'de> ::Entry: Serialize + Deserialize<'de> + 'static + Default + Debug + Bounded, - OT: ObserversTuple, - E::State: HasCorpus + HasMetadata + HasNamedMetadata + HasExecutions + HasCurrentTestcase, - Z: Evaluator, - <::State as HasCorpus>::Corpus: Corpus, //delete me + OT: ObserversTuple<::Input, S>, + S: HasCorpus + + HasMetadata + + HasNamedMetadata + + HasExecutions + + HasCurrentTestcase + + HasCurrentCorpusId + + UsesInput::Input>, + Z: Evaluator, + ::Input: Input, { #[inline] #[allow( @@ -116,7 +115,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, mgr: &mut EM, ) -> Result<(), Error> { // Run this stage only once for each corpus entry and only if we haven't already inspected it @@ -368,7 +367,7 @@ where Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { // Calibration stage disallow restarts // If a testcase that causes crash/timeout in the queue, we need to remove it from the queue immediately. RetryCountRestartHelper::no_retry(state, &self.name) @@ -377,19 +376,19 @@ where // remove this guy from corpus queue } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { // TODO: Make sure this is the correct way / there may be a better way? RetryCountRestartHelper::clear_progress(state, &self.name) } } -impl CalibrationStage +impl CalibrationStage where O: MapObserver, for<'it> O: AsIter<'it, Item = O::Entry>, C: AsRef, - OT: ObserversTuple<::Input, ::State>, - E: UsesState, + OT: ObserversTuple<::Input, S>, + S: HasCorpus, { /// Create a new [`CalibrationStage`]. #[must_use] @@ -422,7 +421,7 @@ where } } -impl Named for CalibrationStage { +impl Named for CalibrationStage { fn name(&self) -> &Cow<'static, str> { &self.name } diff --git a/libafl/src/stages/colorization.rs b/libafl/src/stages/colorization.rs index a5ba2b9bc7..91023844fc 100644 --- a/libafl/src/stages/colorization.rs +++ b/libafl/src/stages/colorization.rs @@ -14,7 +14,7 @@ use libafl_bolts::{ use serde::{Deserialize, Serialize}; use crate::{ - corpus::Corpus, + corpus::{Corpus, HasCurrentCorpusId}, events::EventFirer, executors::{Executor, HasObservers}, inputs::{HasMutatorBytes, UsesInput}, @@ -62,21 +62,14 @@ impl Ord for Earlier { pub const COLORIZATION_STAGE_NAME: &str = "colorization"; /// The mutational stage using power schedules #[derive(Clone, Debug)] -pub struct ColorizationStage { +pub struct ColorizationStage { map_observer_handle: Handle, name: Cow<'static, str>, #[allow(clippy::type_complexity)] - phantom: PhantomData<(E, EM, O, E, Z)>, + phantom: PhantomData<(E, EM, O, E, S, Z)>, } -impl UsesState for ColorizationStage -where - E: UsesState, -{ - type State = E::State; -} - -impl Named for ColorizationStage +impl Named for ColorizationStage where E: UsesState, { @@ -85,17 +78,21 @@ where } } -impl Stage for ColorizationStage +impl Stage for ColorizationStage where - EM: UsesState + EventFirer, - E: HasObservers + Executor, - E::State: HasCorpus + HasMetadata + HasRand + HasNamedMetadata, - E::Observers: ObserversTuple<::Input, ::State>, - E::Input: HasMutatorBytes, + EM: EventFirer, + E: HasObservers + Executor, + S: HasCorpus + + HasMetadata + + HasRand + + HasNamedMetadata + + HasCurrentCorpusId + + UsesInput::Input>, + E::Observers: ObserversTuple<::Input, S>, + ::Input: HasMutatorBytes + Clone, O: MapObserver, C: AsRef + Named, - Z: UsesState, - <::State as HasCorpus>::Corpus: Corpus, //delete me + Z: UsesState, { #[inline] #[allow(clippy::let_and_return)] @@ -103,7 +100,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, // don't need the *main* executor for tracing - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { // Run with the mutated input @@ -112,14 +109,14 @@ where Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { // This is a deterministic stage // Once it failed, then don't retry, // It will just fail again RetryCountRestartHelper::no_retry(state, &self.name) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { RetryCountRestartHelper::clear_progress(state, &self.name) } } @@ -163,27 +160,31 @@ impl TaintMetadata { libafl_bolts::impl_serdeany!(TaintMetadata); -impl ColorizationStage +impl ColorizationStage where - EM: UsesState::State> + EventFirer, + EM: EventFirer, O: MapObserver, C: AsRef + Named, - E: HasObservers + Executor, - E::Observers: ObserversTuple<::Input, ::State>, - ::State: HasCorpus + HasMetadata + HasRand, - E::Input: HasMutatorBytes, - Z: UsesState::State>, - <::State as HasCorpus>::Corpus: Corpus, //delete me + E: HasObservers + Executor, + E::Observers: ObserversTuple<::Input, S>, + S: HasCorpus + + HasMetadata + + HasRand + + HasCurrentCorpusId + + HasCurrentTestcase + + UsesInput::Input>, + ::Input: HasMutatorBytes + Clone, + Z: UsesState, { #[inline] #[allow(clippy::let_and_return)] fn colorize( fuzzer: &mut Z, executor: &mut E, - state: &mut ::State, + state: &mut S, manager: &mut EM, observer_handle: &Handle, - ) -> Result { + ) -> Result<::Input, Error> { let mut input = state.current_input_cloned()?; // The backup of the input let backup = input.clone(); @@ -322,9 +323,9 @@ where fn get_raw_map_hash_run( fuzzer: &mut Z, executor: &mut E, - state: &mut ::State, + state: &mut S, manager: &mut EM, - input: &E::Input, + input: &::Input, observer_handle: &Handle, ) -> Result { executor.observers_mut().pre_exec_all(state, input)?; @@ -348,7 +349,7 @@ where /// Replace bytes with random values but following certain rules #[allow(clippy::needless_range_loop)] - fn type_replace(bytes: &mut [u8], state: &mut ::State) { + fn type_replace(bytes: &mut [u8], state: &mut S) { let len = bytes.len(); for idx in 0..len { let c = match bytes[idx] { diff --git a/libafl/src/stages/concolic.rs b/libafl/src/stages/concolic.rs index ccb8a4a36f..b6261a0125 100644 --- a/libafl/src/stages/concolic.rs +++ b/libafl/src/stages/concolic.rs @@ -14,14 +14,13 @@ use libafl_bolts::{ #[cfg(all(feature = "concolic_mutation", feature = "introspection"))] use crate::monitors::PerfFeature; -#[cfg(all(feature = "introspection", feature = "concolic_mutation"))] -use crate::state::HasClientPerfMonitor; use crate::{ - corpus::Corpus, + corpus::{Corpus, HasCurrentCorpusId}, executors::{Executor, HasObservers}, + inputs::UsesInput, observers::{concolic::ConcolicObserver, ObserversTuple}, stages::{RetryCountRestartHelper, Stage, TracingStage}, - state::{HasCorpus, HasCurrentTestcase, HasExecutions, UsesState}, + state::{HasCorpus, HasCurrentTestcase, HasExecutions, MaybeHasClientPerfMonitor, UsesState}, Error, HasMetadata, HasNamedMetadata, }; #[cfg(feature = "concolic_mutation")] @@ -29,51 +28,46 @@ use crate::{ inputs::HasMutatorBytes, mark_feature_time, observers::concolic::{ConcolicMetadata, SymExpr, SymExprRef}, - start_timer, - state::State, - Evaluator, + start_timer, Evaluator, }; /// Wraps a [`TracingStage`] to add concolic observing. #[derive(Clone, Debug)] -pub struct ConcolicTracingStage<'a, EM, TE, Z> { +pub struct ConcolicTracingStage<'a, EM, TE, S, Z> { name: Cow<'static, str>, - inner: TracingStage, + inner: TracingStage, observer_handle: Handle>, } -impl UsesState for ConcolicTracingStage<'_, EM, TE, Z> -where - TE: UsesState, -{ - type State = TE::State; -} - /// The name for concolic tracer pub const CONCOLIC_TRACING_STAGE_NAME: &str = "concolictracing"; -impl Named for ConcolicTracingStage<'_, EM, TE, Z> { +impl Named for ConcolicTracingStage<'_, EM, TE, S, Z> { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl Stage for ConcolicTracingStage<'_, EM, TE, Z> +impl Stage for ConcolicTracingStage<'_, EM, TE, S, Z> where - E: UsesState, - EM: UsesState, - TE: Executor + HasObservers, - TE::Observers: ObserversTuple::State>, - TE::State: HasExecutions + HasCorpus + HasNamedMetadata + HasCurrentTestcase, - Z: UsesState, - <::State as HasCorpus>::Corpus: Corpus, //delete me + TE: Executor + HasObservers, + TE::Observers: ObserversTuple<::Input, S>, + S: HasExecutions + + HasCorpus + + HasNamedMetadata + + HasCurrentTestcase + + HasCurrentCorpusId + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + EM: UsesState, + Z: UsesState, { #[inline] fn perform( &mut self, fuzzer: &mut Z, _executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { self.inner.trace(fuzzer, state, manager)?; @@ -87,23 +81,23 @@ where Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { // This is a deterministic stage // Once it failed, then don't retry, // It will just fail again RetryCountRestartHelper::no_retry(state, &self.name) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { RetryCountRestartHelper::clear_progress(state, &self.name) } } -impl<'a, EM, TE, Z> ConcolicTracingStage<'a, EM, TE, Z> { +impl<'a, EM, TE, S, Z> ConcolicTracingStage<'a, EM, TE, S, Z> { /// Creates a new default tracing stage using the given [`Executor`], observing traces from a /// [`ConcolicObserver`] with the given name. pub fn new( - inner: TracingStage, + inner: TracingStage, observer_handle: Handle>, ) -> Self { let observer_name = observer_handle.name().clone(); @@ -392,22 +386,25 @@ impl Named for SimpleConcolicMutationalStage { } #[cfg(feature = "concolic_mutation")] -impl Stage for SimpleConcolicMutationalStage +impl Stage for SimpleConcolicMutationalStage where - E: UsesState, - EM: UsesState, - Z: Evaluator, - Z::Input: HasMutatorBytes, - Z::State: - State + HasExecutions + HasCorpus + HasMetadata + HasNamedMetadata + HasCurrentTestcase, - <::State as HasCorpus>::Corpus: Corpus, //delete me + Z: Evaluator, + ::Input: HasMutatorBytes + Clone, + S: HasExecutions + + HasCorpus + + HasMetadata + + HasNamedMetadata + + HasCurrentTestcase + + MaybeHasClientPerfMonitor + + HasCurrentCorpusId + + UsesInput::Input>, { #[inline] fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { { @@ -437,7 +434,7 @@ where } #[inline] - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { // This is a deterministic stage // Once it failed, then don't retry, // It will just fail again @@ -445,7 +442,7 @@ where } #[inline] - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { RetryCountRestartHelper::clear_progress(state, &self.name) } } diff --git a/libafl/src/stages/dump.rs b/libafl/src/stages/dump.rs index ee80fa6062..ca9f133cba 100644 --- a/libafl/src/stages/dump.rs +++ b/libafl/src/stages/dump.rs @@ -16,7 +16,7 @@ use crate::{ corpus::{Corpus, CorpusId, Testcase}, inputs::Input, stages::Stage, - state::{HasCorpus, HasRand, HasSolutions, UsesState}, + state::{HasCorpus, HasRand, HasSolutions}, Error, HasMetadata, }; @@ -35,65 +35,53 @@ impl_serdeany!(DumpToDiskMetadata); /// The [`DumpToDiskStage`] is a stage that dumps the corpus and the solutions to disk #[derive(Debug)] -pub struct DumpToDiskStage { +pub struct DumpToDiskStage { solutions_dir: PathBuf, corpus_dir: PathBuf, to_bytes: CB1, generate_filename: CB2, - phantom: PhantomData<(EM, Z)>, + phantom: PhantomData<(EM, S, Z)>, } -impl UsesState for DumpToDiskStage +impl Stage for DumpToDiskStage where - EM: UsesState, -{ - type State = EM::State; -} - -impl Stage for DumpToDiskStage -where - CB1: FnMut(&Testcase, &Self::State) -> Vec, - CB2: FnMut(&Testcase, &CorpusId) -> P, + CB1: FnMut(&Testcase<::Input>, &S) -> Vec, + CB2: FnMut(&Testcase<::Input>, &CorpusId) -> P, + S: HasCorpus + HasSolutions + HasRand + HasMetadata, + S::Solutions: Corpus::Input>, P: AsRef, - EM: UsesState, - E: UsesState, - Z: UsesState, - EM::State: HasCorpus + HasSolutions + HasRand + HasMetadata, - <::State as HasCorpus>::Corpus: Corpus, //delete me - <::State as HasSolutions>::Solutions: Corpus, //delete me { #[inline] fn perform( &mut self, _fuzzer: &mut Z, _executor: &mut E, - state: &mut Self::State, + state: &mut S, _manager: &mut EM, ) -> Result<(), Error> { self.dump_state_to_disk(state) } #[inline] - fn should_restart(&mut self, _state: &mut Self::State) -> Result { + fn should_restart(&mut self, _state: &mut S) -> Result { // Not executing the target, so restart safety is not needed Ok(true) } #[inline] - fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> { // Not executing the target, so restart safety is not needed Ok(()) } } /// Implementation for `DumpToDiskStage` with a default `generate_filename` function. -impl DumpToDiskStage, &CorpusId) -> String, EM, Z> +impl + DumpToDiskStage::Input>, &CorpusId) -> String, EM, S, Z> where - EM: UsesState, - Z: UsesState, - ::State: HasCorpus + HasSolutions + HasRand + HasMetadata, - <::State as HasCorpus>::Corpus: Corpus, - <::State as HasSolutions>::Solutions: Corpus, + S: HasCorpus + HasSolutions + HasRand + HasMetadata, + S::Solutions: Corpus::Input>, + ::Input: Input, { /// Create a new [`DumpToDiskStage`] with a default `generate_filename` function. pub fn new(to_bytes: CB1, corpus_dir: A, solutions_dir: B) -> Result @@ -111,7 +99,10 @@ where /// Default `generate_filename` function. #[allow(clippy::trivially_copy_pass_by_ref)] - fn generate_filename(testcase: &Testcase, id: &CorpusId) -> String { + fn generate_filename( + testcase: &Testcase<::Input>, + id: &CorpusId, + ) -> String { [ Some(id.0.to_string()), testcase.filename().clone(), @@ -128,13 +119,10 @@ where } } -impl DumpToDiskStage +impl DumpToDiskStage where - EM: UsesState, - Z: UsesState, - ::State: HasCorpus + HasSolutions + HasRand + HasMetadata, - <::State as HasCorpus>::Corpus: Corpus, - <::State as HasSolutions>::Solutions: Corpus, + S: HasCorpus + HasMetadata + HasSolutions, + S::Solutions: Corpus::Input>, { /// Create a new [`DumpToDiskStage`] with a custom `generate_filename` function. pub fn new_with_custom_filenames( @@ -175,19 +163,10 @@ where } #[inline] - fn dump_state_to_disk>( - &mut self, - state: &mut ::State, - ) -> Result<(), Error> + fn dump_state_to_disk>(&mut self, state: &mut S) -> Result<(), Error> where - CB1: FnMut( - &Testcase<<<::State as HasCorpus>::Corpus as Corpus>::Input>, - &::State, - ) -> Vec, - CB2: FnMut( - &Testcase<<<::State as HasCorpus>::Corpus as Corpus>::Input>, - &CorpusId, - ) -> P, + CB1: FnMut(&Testcase<::Input>, &S) -> Vec, + CB2: FnMut(&Testcase<::Input>, &CorpusId) -> P, { let (mut corpus_id, mut solutions_id) = if let Some(meta) = state.metadata_map().get::() { diff --git a/libafl/src/stages/generalization.rs b/libafl/src/stages/generalization.rs index 6b2d54d897..1ae2bd28c7 100644 --- a/libafl/src/stages/generalization.rs +++ b/libafl/src/stages/generalization.rs @@ -11,6 +11,8 @@ use libafl_bolts::{ AsSlice, Named, }; +#[cfg(feature = "introspection")] +use crate::monitors::PerfFeature; use crate::{ corpus::{Corpus, HasCurrentCorpusId}, executors::{Executor, HasObservers}, @@ -21,11 +23,9 @@ use crate::{ require_novelties_tracking, stages::{RetryCountRestartHelper, Stage}, start_timer, - state::{HasCorpus, HasExecutions, UsesState}, + state::{HasCorpus, HasExecutions, MaybeHasClientPerfMonitor, UsesState}, Error, HasMetadata, HasNamedMetadata, }; -#[cfg(feature = "introspection")] -use crate::{monitors::PerfFeature, state::HasClientPerfMonitor}; const MAX_GENERALIZED_LEN: usize = 8192; @@ -48,37 +48,35 @@ pub static GENERALIZATION_STAGE_NAME: &str = "generalization"; /// A stage that runs a tracer executor #[derive(Clone, Debug)] -pub struct GeneralizationStage { +pub struct GeneralizationStage { name: Cow<'static, str>, map_observer_handle: Handle, #[allow(clippy::type_complexity)] - phantom: PhantomData<(EM, O, OT, Z)>, + phantom: PhantomData<(EM, O, OT, S, Z)>, } -impl Named for GeneralizationStage { +impl Named for GeneralizationStage { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl UsesState for GeneralizationStage -where - EM: UsesState, -{ - type State = EM::State; -} - -impl Stage for GeneralizationStage +impl Stage for GeneralizationStage where O: MapObserver, C: CanTrack + AsRef + Named, - E: Executor + HasObservers, - E::Observers: ObserversTuple::State>, - EM::State: - UsesInput + HasExecutions + HasMetadata + HasCorpus + HasNamedMetadata, - EM: UsesState, - <::State as HasCorpus>::Corpus: Corpus, //delete me - Z: UsesState, + E: Executor + HasObservers, + E::Observers: ObserversTuple, + S: HasExecutions + + HasMetadata + + HasCorpus + + HasNamedMetadata + + HasCurrentCorpusId + + MaybeHasClientPerfMonitor + + UsesInput, + S::Corpus: Corpus, + EM: UsesState, + Z: UsesState, { #[inline] #[allow(clippy::too_many_lines)] @@ -86,7 +84,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { let Some(corpus_id) = state.current_corpus_id()? else { @@ -331,26 +329,30 @@ where } #[inline] - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { // TODO: We need to be able to resume better if something crashes or times out RetryCountRestartHelper::should_restart(state, &self.name, 3) } #[inline] - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { // TODO: We need to be able to resume better if something crashes or times out RetryCountRestartHelper::clear_progress(state, &self.name) } } -impl GeneralizationStage +impl GeneralizationStage where - EM: UsesState, O: MapObserver, C: CanTrack + AsRef + Named, - ::State: - UsesInput + HasExecutions + HasMetadata + HasCorpus, - OT: ObserversTuple::State>, + S: HasExecutions + + HasMetadata + + HasCorpus + + MaybeHasClientPerfMonitor + + UsesInput, + OT: ObserversTuple, + EM: UsesState, + Z: UsesState, { /// Create a new [`GeneralizationStage`]. #[must_use] @@ -370,15 +372,14 @@ where &self, fuzzer: &mut Z, executor: &mut E, - state: &mut ::State, + state: &mut S, manager: &mut EM, novelties: &[usize], input: &BytesInput, ) -> Result where - E: Executor::State> + HasObservers, - E::Observers: ObserversTuple::State>, - Z: UsesState, + E: Executor + HasObservers, + E::Observers: ObserversTuple, { start_timer!(state); executor.observers_mut().pre_exec_all(state, input)?; @@ -411,7 +412,7 @@ where &self, fuzzer: &mut Z, executor: &mut E, - state: &mut ::State, + state: &mut S, manager: &mut EM, payload: &mut Vec>, novelties: &[usize], @@ -419,8 +420,7 @@ where split_char: u8, ) -> Result<(), Error> where - E: Executor::State> + HasObservers, - Z: UsesState, + E: Executor + HasObservers, { let mut start = 0; while start < payload.len() { @@ -450,7 +450,7 @@ where &self, fuzzer: &mut Z, executor: &mut E, - state: &mut ::State, + state: &mut S, manager: &mut EM, payload: &mut Vec>, novelties: &[usize], @@ -458,8 +458,7 @@ where closing_char: u8, ) -> Result<(), Error> where - E: Executor::State> + HasObservers, - Z: UsesState, + E: Executor + HasObservers, { let mut index = 0; while index < payload.len() { diff --git a/libafl/src/stages/generation.rs b/libafl/src/stages/generation.rs index a2fb61916f..7067d0ea62 100644 --- a/libafl/src/stages/generation.rs +++ b/libafl/src/stages/generation.rs @@ -7,10 +7,11 @@ use core::marker::PhantomData; use crate::{ + corpus::Corpus, generators::Generator, inputs::UsesInput, stages::Stage, - state::{HasCorpus, HasRand, UsesState}, + state::{HasCorpus, HasRand}, Error, Evaluator, }; @@ -19,36 +20,27 @@ use crate::{ /// /// This stage can be used to construct black-box (e.g., grammar-based) fuzzers. #[derive(Debug)] -pub struct GenStage(G, PhantomData); +pub struct GenStage(G, PhantomData<(S, Z)>); -impl GenStage { +impl GenStage { /// Create a new [`GenStage`]. pub fn new(g: G) -> Self { Self(g, PhantomData) } } -impl UsesState for GenStage +impl Stage for GenStage where - Z: UsesState, -{ - type State = Z::State; -} - -impl Stage for GenStage -where - E: UsesState, - EM: UsesState, - Z: Evaluator, - Self::State: HasCorpus + HasRand, - G: Generator<<::State as UsesInput>::Input, Self::State>, + Z: Evaluator, + S: HasCorpus + HasRand + UsesInput::Input>, + G: Generator<::Input, S>, { #[inline] fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { let input = self.0.generate(state)?; @@ -56,13 +48,13 @@ where Ok(()) } - fn should_restart(&mut self, _state: &mut Self::State) -> Result { + fn should_restart(&mut self, _state: &mut S) -> Result { // It's a random generation stage // so you can restart for whatever times you want Ok(true) } - fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> { Ok(()) } } diff --git a/libafl/src/stages/logics.rs b/libafl/src/stages/logics.rs index c05b818774..5a3497a14c 100644 --- a/libafl/src/stages/logics.rs +++ b/libafl/src/stages/logics.rs @@ -3,8 +3,7 @@ use core::marker::PhantomData; use crate::{ - stages::{HasCurrentStageId, HasNestedStageStatus, Stage, StageId, StagesTuple}, - state::UsesState, + stages::{HasNestedStageStatus, Stage, StageId, StagesTuple}, Error, }; @@ -32,33 +31,23 @@ impl NestedStageRetryCountRestartHelper { #[derive(Debug)] /// Perform the stage while the closure evaluates to true -pub struct WhileStage { +pub struct WhileStage { closure: CB, stages: ST, - phantom: PhantomData<(E, EM, Z)>, + phantom: PhantomData<(E, EM, S, Z)>, } -impl UsesState for WhileStage +impl Stage for WhileStage where - E: UsesState, -{ - type State = E::State; -} - -impl Stage for WhileStage -where - CB: FnMut(&mut Z, &mut E, &mut Self::State, &mut EM) -> Result, - E: UsesState, - EM: UsesState, - ST: StagesTuple, - Z: UsesState, - Self::State: HasNestedStageStatus, + CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result, + ST: StagesTuple, + S: HasNestedStageStatus, { fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { while state.current_stage_id()?.is_some() @@ -70,19 +59,18 @@ where Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { NestedStageRetryCountRestartHelper::should_restart(state, self) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { NestedStageRetryCountRestartHelper::clear_progress(state, self) } } -impl WhileStage +impl WhileStage where - CB: FnMut(&mut Z, &mut E, &mut ::State, &mut EM) -> Result, - E: UsesState, + CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result, { /// Constructor pub fn new(closure: CB, stages: ST) -> Self { @@ -97,33 +85,23 @@ where /// A conditionally enabled stage. /// If the closure returns true, the wrapped stage will be executed, else it will be skipped. #[derive(Debug)] -pub struct IfStage { +pub struct IfStage { closure: CB, if_stages: ST, - phantom: PhantomData<(E, EM, Z)>, -} - -impl UsesState for IfStage -where - E: UsesState, -{ - type State = E::State; + phantom: PhantomData<(E, EM, S, Z)>, } -impl Stage for IfStage +impl Stage for IfStage where - CB: FnMut(&mut Z, &mut E, &mut Self::State, &mut EM) -> Result, - E: UsesState, - EM: UsesState, - ST: StagesTuple, - Z: UsesState, - Self::State: HasNestedStageStatus, + CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result, + ST: StagesTuple, + S: HasNestedStageStatus, { fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { if state.current_stage_id()?.is_some() || (self.closure)(fuzzer, executor, state, manager)? @@ -134,19 +112,18 @@ where Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { NestedStageRetryCountRestartHelper::should_restart(state, self) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { NestedStageRetryCountRestartHelper::clear_progress(state, self) } } -impl IfStage +impl IfStage where - CB: FnMut(&mut Z, &mut E, &mut ::State, &mut EM) -> Result, - E: UsesState, + CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result, { /// Constructor for this conditionally enabled stage. /// If the closure returns true, the wrapped stage will be executed, else it will be skipped. @@ -161,35 +138,25 @@ where /// Perform the stage if closure evaluates to true #[derive(Debug)] -pub struct IfElseStage { +pub struct IfElseStage { closure: CB, if_stages: ST1, else_stages: ST2, - phantom: PhantomData<(E, EM, Z)>, + phantom: PhantomData<(E, EM, S, Z)>, } -impl UsesState for IfElseStage +impl Stage for IfElseStage where - E: UsesState, -{ - type State = E::State; -} - -impl Stage for IfElseStage -where - CB: FnMut(&mut Z, &mut E, &mut Self::State, &mut EM) -> Result, - E: UsesState, - EM: UsesState, - ST1: StagesTuple, - ST2: StagesTuple, - Z: UsesState, - Self::State: HasNestedStageStatus, + CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result, + ST1: StagesTuple, + ST2: StagesTuple, + S: HasNestedStageStatus, { fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { let current = state.current_stage_id()?; @@ -219,19 +186,18 @@ where Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { NestedStageRetryCountRestartHelper::should_restart(state, self) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { NestedStageRetryCountRestartHelper::clear_progress(state, self) } } -impl IfElseStage +impl IfElseStage where - CB: FnMut(&mut Z, &mut E, &mut ::State, &mut EM) -> Result, - E: UsesState, + CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result, { /// Constructor pub fn new(closure: CB, if_stages: ST1, else_stages: ST2) -> Self { @@ -246,31 +212,21 @@ where /// A stage wrapper where the stages do not need to be initialized, but can be [`None`]. #[derive(Debug)] -pub struct OptionalStage { +pub struct OptionalStage { stages: Option, - phantom: PhantomData<(E, EM, Z)>, -} - -impl UsesState for OptionalStage -where - E: UsesState, -{ - type State = E::State; + phantom: PhantomData<(E, EM, S, Z)>, } -impl Stage for OptionalStage +impl Stage for OptionalStage where - E: UsesState, - EM: UsesState, - ST: StagesTuple, - Z: UsesState, - Self::State: HasNestedStageStatus, + ST: StagesTuple, + S: HasNestedStageStatus, { fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { if let Some(stages) = &mut self.stages { @@ -280,16 +236,16 @@ where } } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { NestedStageRetryCountRestartHelper::should_restart(state, self) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { NestedStageRetryCountRestartHelper::clear_progress(state, self) } } -impl OptionalStage { +impl OptionalStage { /// Constructor for this conditionally enabled stage. #[must_use] pub fn new(stages: Option) -> Self { diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 5dcad0aaca..3e66848e9b 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -33,14 +33,11 @@ pub use logics::*; pub use mutational::{MutationalStage, StdMutationalStage}; pub use power::{PowerMutationalStage, StdPowerMutationalStage}; use serde::{Deserialize, Serialize}; -pub use stats::StatsStage; #[cfg(feature = "std")] pub use sync::*; #[cfg(feature = "std")] pub use time_tracker::TimeTrackingStageWrapper; -pub use tmin::{ - MapEqualityFactory, MapEqualityFeedback, StdTMinMutationalStage, TMinMutationalStage, -}; +pub use tmin::{MapEqualityFactory, MapEqualityFeedback, StdTMinMutationalStage}; pub use tracing::{ShadowTracingStage, TracingStage}; pub use tuneable::*; use tuple_list::NonEmptyTuple; @@ -51,15 +48,9 @@ pub use verify_timeouts::{TimeoutsToVerify, VerifyTimeoutsStage}; use crate::{ corpus::{CorpusId, HasCurrentCorpusId}, - events::{EventFirer, EventProcessor, EventRestarter, HasEventManagerId, ProgressReporter}, - executors::{Executor, HasObservers}, - inputs::UsesInput, - observers::ObserversTuple, - schedulers::Scheduler, - stages::push::PushStage, - state::{HasCorpus, HasExecutions, HasLastReportTime, HasRand, State, Stoppable, UsesState}, - Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasMetadata, HasNamedMetadata, - HasScheduler, + events::EventProcessor, + state::{HasExecutions, State, Stoppable}, + Error, HasNamedMetadata, }; /// Mutational stage is the normal fuzzing stage. @@ -79,7 +70,6 @@ pub mod generalization; pub mod generation; pub mod logics; pub mod power; -pub mod stats; #[cfg(feature = "std")] pub mod sync; #[cfg(feature = "std")] @@ -93,21 +83,16 @@ pub mod verify_timeouts; /// A stage is one step in the fuzzing process. /// Multiple stages will be scheduled one by one for each input. -pub trait Stage: UsesState -where - E: UsesState, - EM: UsesState, - Z: UsesState, -{ +pub trait Stage { /// This method will be called before every call to [`Stage::perform`]. /// Initialize the restart tracking for this stage, _if it is not yet initialized_. /// On restart, this will be called again. /// As long as [`Stage::clear_progress`], all subsequent calls happen on restart. /// Returns `true`, if the stage's [`Stage::perform`] method should run, else `false`. - fn should_restart(&mut self, state: &mut Self::State) -> Result; + fn should_restart(&mut self, state: &mut S) -> Result; /// Clear the current status tracking of the associated stage - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error>; + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error>; /// Run the stage. /// @@ -119,7 +104,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error>; @@ -128,7 +113,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { if self.should_restart(state)? { @@ -139,13 +124,7 @@ where } /// A tuple holding all `Stages` used for fuzzing. -pub trait StagesTuple -where - E: UsesState, - EM: UsesState, - Z: UsesState, - S: UsesInput + HasCurrentStageId, -{ +pub trait StagesTuple { /// Performs all `Stages` in this tuple. fn perform_all( &mut self, @@ -158,10 +137,7 @@ where impl StagesTuple for () where - E: UsesState, - EM: UsesState, - Z: UsesState, - S: UsesInput + HasCurrentStageId, + S: HasCurrentStageId, { fn perform_all( &mut self, @@ -180,14 +156,12 @@ where } } -impl StagesTuple for (Head, Tail) +impl StagesTuple for (Head, Tail) where - Head: Stage, - Tail: StagesTuple + HasConstLen, - E: UsesState, - EM: UsesState + EventProcessor, - Z: UsesState, - Head::State: HasCurrentStageId, + Head: Stage, + Tail: StagesTuple + HasConstLen, + S: HasCurrentStageId + Stoppable, + EM: EventProcessor, { /// Performs all stages in the tuple, /// Checks after every stage if state wants to stop @@ -196,7 +170,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Head::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { match state.current_stage_id()? { @@ -239,66 +213,45 @@ where } } -impl - IntoVec>> for (Head, Tail) +impl IntoVec>> for (Head, Tail) where - Head: Stage + 'static, - Tail: StagesTuple - + HasConstLen - + IntoVec>>, - E: UsesState, - EM: UsesState, - Z: UsesState, - Head::State: HasCurrentStageId, + Head: Stage + 'static, + Tail: StagesTuple + HasConstLen + IntoVec>>, + S: HasCurrentStageId, { - fn into_vec_reversed( - self, - ) -> Vec>> { + fn into_vec_reversed(self) -> Vec>> { let (head, tail) = self.uncons(); let mut ret = tail.0.into_vec_reversed(); ret.push(Box::new(head)); ret } - fn into_vec(self) -> Vec>> { + fn into_vec(self) -> Vec>> { let mut ret = self.into_vec_reversed(); ret.reverse(); ret } } -impl IntoVec>> - for (Tail,) +impl IntoVec>> for (Tail,) where - Tail: UsesState + IntoVec>>, - Z: UsesState, - EM: UsesState, - E: UsesState, + Tail: IntoVec>>, { - fn into_vec(self) -> Vec>> { + fn into_vec(self) -> Vec>> { self.0.into_vec() } } -impl IntoVec>> - for Vec>> -where - Z: UsesState, - EM: UsesState, - E: UsesState, -{ - fn into_vec(self) -> Vec>> { +impl IntoVec>> for Vec>> { + fn into_vec(self) -> Vec>> { self } } -impl StagesTuple - for Vec>> +impl StagesTuple for Vec>> where - E: UsesState, - EM: UsesState + EventProcessor, - Z: UsesState, - S: UsesInput + HasCurrentStageId + State, + EM: EventProcessor, + S: HasCurrentStageId + State, { /// Performs all stages in the `Vec` /// Checks after every stage if state wants to stop @@ -333,46 +286,36 @@ pub struct ClosureStage { phantom: PhantomData<(E, EM, Z)>, } -impl UsesState for ClosureStage -where - E: UsesState, -{ - type State = E::State; -} - impl Named for ClosureStage { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl Stage for ClosureStage +impl Stage for ClosureStage where - CB: FnMut(&mut Z, &mut E, &mut Self::State, &mut EM) -> Result<(), Error>, - E: UsesState, - EM: UsesState, - Z: UsesState, - Self::State: HasNamedMetadata, + CB: FnMut(&mut Z, &mut E, &mut S, &mut EM) -> Result<(), Error>, + S: HasNamedMetadata + HasCurrentCorpusId, { fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut E::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { (self.closure)(fuzzer, executor, state, manager) } #[inline] - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { // There's no restart safety in the content of the closure. // don't restart RetryCountRestartHelper::no_retry(state, &self.name) } #[inline] - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { RetryCountRestartHelper::clear_progress(state, &self.name) } } @@ -396,132 +339,6 @@ impl ClosureStage { } } -/// Allows us to use a [`push::PushStage`] as a normal [`Stage`] -#[allow(clippy::type_complexity)] -#[derive(Debug)] -pub struct PushStageAdapter { - name: Cow<'static, str>, - push_stage: PS, - phantom: PhantomData<(CS, EM, OT, Z)>, -} - -impl PushStageAdapter { - /// Create a new [`PushStageAdapter`], wrapping the given [`PushStage`] - /// to be used as a normal [`Stage`] - #[must_use] - pub fn new(push_stage: PS) -> Self { - // unsafe but impossible that you create two threads both instantiating this instance - let stage_id = unsafe { - let ret = PUSH_STAGE_ADAPTER_ID; - PUSH_STAGE_ADAPTER_ID += 1; - ret - }; - Self { - name: Cow::Owned( - PUSH_STAGE_ADAPTER_NAME.to_owned() + ":" + stage_id.to_string().as_str(), - ), - push_stage, - phantom: PhantomData, - } - } -} -/// The unique counter for this stage -static mut PUSH_STAGE_ADAPTER_ID: usize = 0; -/// The name for push stage adapter -pub static PUSH_STAGE_ADAPTER_NAME: &str = "pushstageadapter"; - -impl UsesState for PushStageAdapter -where - Z: UsesState, -{ - type State = Z::State; -} - -impl Named for PushStageAdapter { - #[must_use] - fn name(&self) -> &Cow<'static, str> { - &self.name - } -} - -impl Stage for PushStageAdapter -where - CS: Scheduler, - Self::State: HasExecutions - + HasRand - + HasCorpus - + HasLastReportTime - + HasCurrentCorpusId - + HasNamedMetadata - + HasMetadata, - E: Executor::State> + HasObservers, - EM: EventFirer - + EventRestarter - + HasEventManagerId - + ProgressReporter, - OT: ObserversTuple, - PS: PushStage, - Z: ExecutesInput - + ExecutionProcessor - + EvaluatorObservers - + HasScheduler, -{ - fn perform( - &mut self, - fuzzer: &mut Z, - executor: &mut E, - state: &mut Z::State, - event_mgr: &mut EM, - ) -> Result<(), Error> { - let push_stage = &mut self.push_stage; - - let Some(corpus_id) = state.current_corpus_id()? else { - return Err(Error::illegal_state( - "state is not currently processing a corpus index", - )); - }; - - push_stage.set_current_corpus_id(corpus_id); - - push_stage.init(fuzzer, state, event_mgr, &mut *executor.observers_mut())?; - - loop { - let input = - match push_stage.pre_exec(fuzzer, state, event_mgr, &mut *executor.observers_mut()) - { - Some(Ok(next_input)) => next_input, - Some(Err(err)) => return Err(err), - None => break, - }; - - let exit_kind = fuzzer.execute_input(state, executor, event_mgr, &input)?; - - push_stage.post_exec( - fuzzer, - state, - event_mgr, - &mut *executor.observers_mut(), - input, - exit_kind, - )?; - } - - self.push_stage - .deinit(fuzzer, state, event_mgr, &mut *executor.observers_mut()) - } - - #[inline] - fn should_restart(&mut self, state: &mut Self::State) -> Result { - // TODO: Proper restart handling - call post_exec at the right time, etc... - RetryCountRestartHelper::no_retry(state, &self.name) - } - - #[inline] - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { - RetryCountRestartHelper::clear_progress(state, &self.name) - } -} - /// Progress which permits a fixed amount of resumes per round of fuzzing. If this amount is ever /// exceeded, the input will no longer be executed by this stage. #[derive(Clone, Deserialize, Serialize, Debug)] @@ -722,7 +539,7 @@ mod test { corpus::{Corpus, HasCurrentCorpusId, Testcase}, inputs::NopInput, stages::{RetryCountRestartHelper, Stage}, - state::{HasCorpus, State, StdState, UsesState}, + state::{HasCorpus, StdState}, HasMetadata, }; @@ -771,35 +588,25 @@ mod test { } } - impl UsesState for ResumeSucceededStage - where - S: State, - { - type State = S; - } - - impl Stage for ResumeSucceededStage + impl Stage for ResumeSucceededStage where - E: UsesState, - EM: UsesState, - Z: UsesState, - Z::State: HasMetadata, + S: HasMetadata, { fn perform( &mut self, _fuzzer: &mut Z, _executor: &mut E, - _state: &mut Self::State, + _state: &mut S, _manager: &mut EM, ) -> Result<(), Error> { Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { TestProgress::should_restart(state, self) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { TestProgress::clear_progress(state, self) } } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 1f05631ea8..1ac574a489 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -9,20 +9,20 @@ use core::{marker::PhantomData, num::NonZeroUsize}; use libafl_bolts::{rands::Rand, Named}; +#[cfg(feature = "introspection")] +use crate::monitors::PerfFeature; use crate::{ - corpus::{Corpus, CorpusId, Testcase}, + corpus::{Corpus, CorpusId, HasCurrentCorpusId, Testcase}, fuzzer::Evaluator, - inputs::Input, + inputs::{Input, UsesInput}, mark_feature_time, mutators::{MultiMutator, MutationResult, Mutator}, nonzero, stages::{RetryCountRestartHelper, Stage}, start_timer, - state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, UsesState}, + state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, MaybeHasClientPerfMonitor}, Error, HasMetadata, HasNamedMetadata, }; -#[cfg(feature = "introspection")] -use crate::{monitors::PerfFeature, state::HasClientPerfMonitor}; // TODO multi mutators stage @@ -81,74 +81,18 @@ where /// 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: Stage -where - E: UsesState, - M: Mutator, - EM: UsesState, - Z: Evaluator, - Self::State: HasCorpus + HasCurrentTestcase, - I: MutatedTransform + Clone, - <::State as HasCorpus>::Corpus: Corpus, -{ +pub trait MutationalStage { + /// The mutator of this stage + type Mutator; + /// The mutator registered for this stage - fn mutator(&self) -> &M; + fn mutator(&self) -> &Self::Mutator; /// The mutator registered for this stage (mutable) - fn mutator_mut(&mut self) -> &mut M; + fn mutator_mut(&mut self) -> &mut Self::Mutator; /// Gets the number of iterations this mutator should run for. - fn iterations(&self, state: &mut Self::State) -> Result; - - /// Runs this (mutational) stage for the given testcase - #[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely... - fn perform_mutational( - &mut self, - fuzzer: &mut Z, - executor: &mut E, - state: &mut Self::State, - manager: &mut EM, - ) -> Result<(), Error> { - start_timer!(state); - - // Here saturating_sub is needed as self.iterations() might be actually smaller than the previous value before reset. - /* - let num = self - .iterations(state)? - .saturating_sub(self.execs_since_progress_start(state)?); - */ - let num = self.iterations(state)?; - let mut testcase = state.current_testcase_mut()?; - - let Ok(input) = I::try_transform_from(&mut testcase, state) else { - return Ok(()); - }; - drop(testcase); - mark_feature_time!(state, PerfFeature::GetInputFromCorpus); - - for _ in 0..num { - let mut input = input.clone(); - - start_timer!(state); - let mutated = self.mutator_mut().mutate(state, &mut input)?; - mark_feature_time!(state, PerfFeature::Mutate); - - if mutated == MutationResult::Skipped { - continue; - } - - // Time is measured directly the `evaluate_input` function - let (untransformed, post) = input.try_transform_into(state)?; - let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?; - - start_timer!(state); - self.mutator_mut().post_exec(state, corpus_id)?; - post.post_exec(state, corpus_id)?; - mark_feature_time!(state, PerfFeature::MutatePostExec); - } - - Ok(()) - } + fn iterations(&self, state: &mut S) -> Result; } /// Default value, how many iterations each stage gets, as an upper bound. @@ -157,7 +101,7 @@ pub const DEFAULT_MUTATIONAL_MAX_ITERATIONS: usize = 128; /// The default mutational stage #[derive(Clone, Debug)] -pub struct StdMutationalStage { +pub struct StdMutationalStage { /// The name name: Cow<'static, str>, /// The mutator(s) to use @@ -165,33 +109,29 @@ pub struct StdMutationalStage { /// The maximum amount of iterations we should do each round max_iterations: NonZeroUsize, #[allow(clippy::type_complexity)] - phantom: PhantomData<(E, EM, I, Z)>, + phantom: PhantomData<(E, EM, I, S, Z)>, } -impl MutationalStage for StdMutationalStage +impl MutationalStage for StdMutationalStage where - E: UsesState, - EM: UsesState, - M: Mutator, - Z: Evaluator, - Z::State: HasCorpus + HasRand + HasExecutions + HasMetadata + HasNamedMetadata, - I: MutatedTransform + Clone, - <::State as HasCorpus>::Corpus: Corpus, //delete me + S: HasRand, { + type Mutator = M; + /// The mutator, added to this stage #[inline] - fn mutator(&self) -> &M { + fn mutator(&self) -> &Self::Mutator { &self.mutator } /// The list of mutators, added to this stage (as mutable ref) #[inline] - fn mutator_mut(&mut self) -> &mut M { + fn mutator_mut(&mut self) -> &mut Self::Mutator { &mut self.mutator } /// Gets the number of iterations as a random number - fn iterations(&self, state: &mut Self::State) -> Result { + fn iterations(&self, state: &mut S) -> Result { Ok(1 + state.rand_mut().below(self.max_iterations)) } } @@ -201,28 +141,27 @@ static mut MUTATIONAL_STAGE_ID: usize = 0; /// The name for mutational stage pub static MUTATIONAL_STAGE_NAME: &str = "mutational"; -impl UsesState for StdMutationalStage -where - Z: UsesState, -{ - type State = Z::State; -} - -impl Named for StdMutationalStage { +impl Named for StdMutationalStage { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl Stage for StdMutationalStage +impl Stage for StdMutationalStage where - E: UsesState, - EM: UsesState, - M: Mutator, - Z: Evaluator, - Z::State: HasCorpus + HasRand + HasMetadata + HasExecutions + HasNamedMetadata, - I: MutatedTransform + Clone, - <::State as HasCorpus>::Corpus: Corpus, //delete me + M: Mutator, + Z: Evaluator, + S: HasCorpus + + HasRand + + HasMetadata + + HasExecutions + + HasNamedMetadata + + HasCurrentCorpusId + + MaybeHasClientPerfMonitor + + UsesInput, + I: MutatedTransform<::Input, S> + Clone, + ::Input: Input, + S::Corpus: Corpus, { #[inline] #[allow(clippy::let_and_return)] @@ -230,7 +169,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { let ret = self.perform_mutational(fuzzer, executor, state, manager); @@ -241,22 +180,22 @@ where ret } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { RetryCountRestartHelper::should_restart(state, &self.name, 3) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { RetryCountRestartHelper::clear_progress(state, &self.name) } } -impl StdMutationalStage +impl StdMutationalStage::Input, M, S, Z> where - E: UsesState::State>, - EM: UsesState::State>, - M: Mutator::State>, - Z: Evaluator, - ::State: HasCorpus + HasRand, + M: Mutator<::Input, S>, + Z: Evaluator, + S: HasCorpus + HasRand + HasCurrentCorpusId + UsesInput + MaybeHasClientPerfMonitor, + ::Input: Input + Clone, + S::Corpus: Corpus, { /// Creates a new default mutational stage pub fn new(mutator: M) -> Self { @@ -271,13 +210,14 @@ where } } -impl StdMutationalStage +impl StdMutationalStage where - E: UsesState::State>, - EM: UsesState::State>, - M: Mutator::State>, - Z: Evaluator, - ::State: HasCorpus + HasRand, + M: Mutator, + Z: Evaluator, + S: HasCorpus + HasRand + HasCurrentTestcase + MaybeHasClientPerfMonitor + UsesInput, + I: MutatedTransform<::Input, S> + Clone, + ::Input: Input, + S::Corpus: Corpus, { /// Creates a new transforming mutational stage with the default max iterations pub fn transforming(mutator: M) -> Self { @@ -305,15 +245,64 @@ where phantom: PhantomData, } } -} + /// Runs this (mutational) stage for the given testcase + #[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely... + fn perform_mutational( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut S, + manager: &mut EM, + ) -> Result<(), Error> { + start_timer!(state); + + // Here saturating_sub is needed as self.iterations() might be actually smaller than the previous value before reset. + /* + let num = self + .iterations(state)? + .saturating_sub(self.execs_since_progress_start(state)?); + */ + let num = self.iterations(state)?; + let mut testcase = state.current_testcase_mut()?; + + let Ok(input) = I::try_transform_from(&mut testcase, state) else { + return Ok(()); + }; + drop(testcase); + mark_feature_time!(state, PerfFeature::GetInputFromCorpus); + + for _ in 0..num { + let mut input = input.clone(); + + start_timer!(state); + let mutated = self.mutator_mut().mutate(state, &mut input)?; + mark_feature_time!(state, PerfFeature::Mutate); + + if mutated == MutationResult::Skipped { + continue; + } + + // Time is measured directly the `evaluate_input` function + let (untransformed, post) = input.try_transform_into(state)?; + let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?; + + start_timer!(state); + self.mutator_mut().post_exec(state, corpus_id)?; + post.post_exec(state, corpus_id)?; + mark_feature_time!(state, PerfFeature::MutatePostExec); + } + + Ok(()) + } +} /// A mutational stage that operates on multiple inputs, as returned by [`MultiMutator::multi_mutate`]. #[derive(Clone, Debug)] -pub struct MultiMutationalStage { +pub struct MultiMutationalStage { name: Cow<'static, str>, mutator: M, #[allow(clippy::type_complexity)] - phantom: PhantomData<(E, EM, I, Z)>, + phantom: PhantomData<(E, EM, I, S, Z)>, } /// The unique id for multi mutational stage @@ -321,37 +310,29 @@ static mut MULTI_MUTATIONAL_STAGE_ID: usize = 0; /// The name for multi mutational stage pub static MULTI_MUTATIONAL_STAGE_NAME: &str = "multimutational"; -impl UsesState for MultiMutationalStage -where - Z: UsesState, -{ - type State = Z::State; -} - -impl Named for MultiMutationalStage { +impl Named for MultiMutationalStage { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl Stage for MultiMutationalStage +impl Stage for MultiMutationalStage where - E: UsesState, - EM: UsesState, - M: MultiMutator, - Z: Evaluator, - Z::State: HasCorpus + HasRand + HasNamedMetadata + HasCurrentTestcase, - I: MutatedTransform + Clone, - <::State as HasCorpus>::Corpus: Corpus, //delete me + M: MultiMutator, + Z: Evaluator, + S: HasCorpus + HasRand + HasNamedMetadata + HasCurrentTestcase + HasCurrentCorpusId + UsesInput, + I: MutatedTransform<::Input, S> + Clone, + ::Input: Input, + S::Corpus: Corpus, { #[inline] - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { // Make sure we don't get stuck crashing on a single testcase RetryCountRestartHelper::should_restart(state, &self.name, 3) } #[inline] - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { RetryCountRestartHelper::clear_progress(state, &self.name) } @@ -362,7 +343,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { let mut testcase = state.current_testcase_mut()?; @@ -386,17 +367,14 @@ where } } -impl MultiMutationalStage -where - Z: UsesState, -{ +impl MultiMutationalStage { /// Creates a new [`MultiMutationalStage`] pub fn new(mutator: M) -> Self { Self::transforming(mutator) } } -impl MultiMutationalStage { +impl MultiMutationalStage { /// Creates a new transforming mutational stage pub fn transforming(mutator: M) -> Self { // unsafe but impossible that you create two threads both instantiating this instance diff --git a/libafl/src/stages/power.rs b/libafl/src/stages/power.rs index 83083a692e..adcf982ca8 100644 --- a/libafl/src/stages/power.rs +++ b/libafl/src/stages/power.rs @@ -8,15 +8,24 @@ use core::{fmt::Debug, marker::PhantomData}; use libafl_bolts::Named; +#[cfg(feature = "introspection")] +use crate::monitors::PerfFeature; use crate::{ - corpus::Corpus, + corpus::{Corpus, HasCurrentCorpusId}, executors::{Executor, HasObservers}, fuzzer::Evaluator, - inputs::Input, - mutators::Mutator, + inputs::{Input, UsesInput}, + mark_feature_time, + mutators::{MutationResult, Mutator}, schedulers::{testcase_score::CorpusPowerTestcaseScore, TestcaseScore}, - stages::{mutational::MutatedTransform, MutationalStage, RetryCountRestartHelper, Stage}, - state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, UsesState}, + stages::{ + mutational::{MutatedTransform, MutatedTransformPost}, + MutationalStage, RetryCountRestartHelper, Stage, + }, + start_timer, + state::{ + HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, MaybeHasClientPerfMonitor, UsesState, + }, Error, HasMetadata, HasNamedMetadata, }; @@ -26,55 +35,41 @@ static mut POWER_MUTATIONAL_STAGE_ID: usize = 0; pub const POWER_MUTATIONAL_STAGE_NAME: &str = "power"; /// The mutational stage using power schedules #[derive(Clone, Debug)] -pub struct PowerMutationalStage { +pub struct PowerMutationalStage { name: Cow<'static, str>, /// The mutators we use mutator: M, #[allow(clippy::type_complexity)] - phantom: PhantomData<(E, F, EM, I, Z)>, + phantom: PhantomData<(E, F, EM, I, S, Z)>, } -impl UsesState for PowerMutationalStage -where - E: UsesState, -{ - type State = E::State; -} - -impl Named for PowerMutationalStage { +impl Named for PowerMutationalStage { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl MutationalStage for PowerMutationalStage +impl MutationalStage for PowerMutationalStage where - E: Executor + HasObservers, - EM: UsesState, - F: TestcaseScore, - I: Input, - M: Mutator, - E::State: - HasCorpus + HasMetadata + HasRand + HasExecutions + HasNamedMetadata + HasCurrentTestcase, - Z: Evaluator, - I: MutatedTransform + Clone, - <::State as HasCorpus>::Corpus: Corpus, //delete me + S: HasCurrentTestcase, + F: TestcaseScore, { + type Mutator = M; /// The mutator, added to this stage #[inline] - fn mutator(&self) -> &M { + fn mutator(&self) -> &Self::Mutator { &self.mutator } /// The list of mutators, added to this stage (as mutable ref) #[inline] - fn mutator_mut(&mut self) -> &mut M { + fn mutator_mut(&mut self) -> &mut Self::Mutator { &mut self.mutator } /// Gets the number of iterations as a random number #[allow(clippy::cast_sign_loss)] - fn iterations(&self, state: &mut Self::State) -> Result { + fn iterations(&self, state: &mut S) -> Result { // Update handicap let mut testcase = state.current_testcase_mut()?; let score = F::compute(state, &mut testcase)? as usize; @@ -83,17 +78,24 @@ where } } -impl Stage for PowerMutationalStage +impl Stage for PowerMutationalStage where - E: Executor + HasObservers, - EM: UsesState, - F: TestcaseScore, - M: Mutator, - E::State: - HasCorpus + HasMetadata + HasRand + HasExecutions + HasNamedMetadata + HasCurrentTestcase, - Z: Evaluator, - I: MutatedTransform + Clone + Input, - <::State as HasCorpus>::Corpus: Corpus, //delete me + E: Executor + HasObservers, + EM: UsesState, + F: TestcaseScore, + M: Mutator, + S: HasCorpus + + HasMetadata + + HasRand + + HasExecutions + + HasNamedMetadata + + HasCurrentTestcase + + HasCurrentCorpusId + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + Z: Evaluator, + I: MutatedTransform<::Input, S> + Clone + Input, + ::Input: Input, { #[inline] #[allow(clippy::let_and_return)] @@ -101,32 +103,39 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { let ret = self.perform_mutational(fuzzer, executor, state, manager); ret } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { // Make sure we don't get stuck crashing on a single testcase RetryCountRestartHelper::should_restart(state, &self.name, 3) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { RetryCountRestartHelper::clear_progress(state, &self.name) } } -impl PowerMutationalStage +impl PowerMutationalStage where - E: Executor + HasObservers, - EM: UsesState::State>, - F: TestcaseScore<::State>, + E: Executor + HasObservers, + EM: UsesState, + F: TestcaseScore, I: Input, - M: Mutator::State>, - ::State: HasCorpus + HasMetadata + HasRand, - Z: Evaluator::State>, + M: Mutator, + S: HasCorpus + + HasMetadata + + HasRand + + HasCurrentTestcase + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + I: MutatedTransform<::Input, S> + Clone + Input, + Z: Evaluator, + ::Input: Input, { /// Creates a new [`PowerMutationalStage`] pub fn new(mutator: M) -> Self { @@ -144,8 +153,58 @@ where phantom: PhantomData, } } + + /// Runs this (mutational) stage for the given testcase + #[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely... + fn perform_mutational( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut S, + manager: &mut EM, + ) -> Result<(), Error> { + start_timer!(state); + + // Here saturating_sub is needed as self.iterations() might be actually smaller than the previous value before reset. + /* + let num = self + .iterations(state)? + .saturating_sub(self.execs_since_progress_start(state)?); + */ + let num = self.iterations(state)?; + let mut testcase = state.current_testcase_mut()?; + + let Ok(input) = I::try_transform_from(&mut testcase, state) else { + return Ok(()); + }; + drop(testcase); + mark_feature_time!(state, PerfFeature::GetInputFromCorpus); + + for _ in 0..num { + let mut input = input.clone(); + + start_timer!(state); + let mutated = self.mutator_mut().mutate(state, &mut input)?; + mark_feature_time!(state, PerfFeature::Mutate); + + if mutated == MutationResult::Skipped { + continue; + } + + // Time is measured directly the `evaluate_input` function + let (untransformed, post) = input.try_transform_into(state)?; + let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?; + + start_timer!(state); + self.mutator_mut().post_exec(state, corpus_id)?; + post.post_exec(state, corpus_id)?; + mark_feature_time!(state, PerfFeature::MutatePostExec); + } + + Ok(()) + } } /// The standard powerscheduling stage -pub type StdPowerMutationalStage = - PowerMutationalStage; +pub type StdPowerMutationalStage = + PowerMutationalStage; diff --git a/libafl/src/stages/push/mod.rs b/libafl/src/stages/push/mod.rs index a8304962e1..cbbb456eff 100644 --- a/libafl/src/stages/push/mod.rs +++ b/libafl/src/stages/push/mod.rs @@ -8,62 +8,51 @@ /// Mutational stage is the normal fuzzing stage. pub mod mutational; -use alloc::rc::Rc; +use alloc::{ + borrow::{Cow, ToOwned}, + rc::Rc, + string::ToString, +}; use core::{ cell::{Cell, RefCell}, marker::PhantomData, - time::Duration, }; +use libafl_bolts::Named; pub use mutational::StdMutationalPushStage; use crate::{ - corpus::CorpusId, + common::HasNamedMetadata, + corpus::{Corpus, CorpusId, HasCurrentCorpusId}, events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter}, - executors::ExitKind, + executors::{Executor, ExitKind, HasObservers}, inputs::UsesInput, observers::ObserversTuple, schedulers::Scheduler, + stages::{RetryCountRestartHelper, Stage}, state::{HasCorpus, HasExecutions, HasLastReportTime, HasRand}, - Error, EvaluatorObservers, ExecutionProcessor, HasMetadata, HasScheduler, + Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasMetadata, HasScheduler, }; -/// Send a monitor update all 15 (or more) seconds -const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15); - // The shared state for all [`PushStage`]s /// Should be stored inside a `[Rc>`] #[derive(Clone, Debug)] -pub struct PushStageSharedState -where - CS: Scheduler, - EM: EventFirer + EventRestarter + HasEventManagerId, - OT: ObserversTuple, - Z::State: HasRand + HasCorpus, - Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, -{ +pub struct PushStageSharedState { /// The [`crate::state::State`] - pub state: Z::State, + pub state: S, /// 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, Z)>, + phantom: PhantomData, } -impl PushStageSharedState -where - CS: Scheduler, - EM: EventFirer + EventRestarter + HasEventManagerId, - OT: ObserversTuple, - Z::State: HasRand + HasCorpus, - Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, -{ +impl PushStageSharedState { /// Create a new `PushStageSharedState` that can be used by all [`PushStage`]s #[must_use] - pub fn new(fuzzer: Z, state: Z::State, observers: OT, event_mgr: EM) -> Self { + pub fn new(fuzzer: Z, state: S, observers: OT, event_mgr: EM) -> Self { Self { state, fuzzer, @@ -76,20 +65,13 @@ where /// Helper class for the [`PushStage`] trait, taking care of borrowing the shared state #[derive(Clone, Debug)] -pub struct PushStageHelper -where - CS: Scheduler, - EM: EventFirer + EventRestarter + HasEventManagerId, - OT: ObserversTuple, - Z::State: HasRand + HasCorpus, - Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, -{ +pub struct PushStageHelper { /// If this stage has already been initalized. /// This gets reset to `false` after one iteration of the stage is done. pub initialized: bool, /// The shared state, keeping track of the corpus and the fuzzer #[allow(clippy::type_complexity)] - pub shared_state: Rc>>>, + pub shared_state: Rc>>>, /// If the last iteration failed pub errored: bool, @@ -97,32 +79,22 @@ where pub current_corpus_id: Option, /// The input we just ran - pub current_input: Option<::Input>, // Todo: Get rid of copy + pub current_input: Option, // Todo: Get rid of copy - #[allow(clippy::type_complexity)] - phantom: PhantomData<(CS, EM, OT, Z)>, exit_kind: Rc>>, } -impl PushStageHelper -where - CS: Scheduler, - EM: EventFirer + EventRestarter + HasEventManagerId, - OT: ObserversTuple, - Z::State: HasRand + HasCorpus, - Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, -{ +impl PushStageHelper { /// Create a new [`PushStageHelper`] #[must_use] #[allow(clippy::type_complexity)] pub fn new( - shared_state: Rc>>>, + shared_state: Rc>>>, exit_kind_ref: Rc>>, ) -> Self { Self { shared_state, initialized: false, - phantom: PhantomData, exit_kind: exit_kind_ref, errored: false, current_input: None, @@ -132,14 +104,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) { + pub fn set_shared_state(&mut self, shared_state: PushStageSharedState) { (*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> { + pub fn take_shared_state(&mut self) -> Option> { let shared_state_ref = &mut (*self.shared_state).borrow_mut(); shared_state_ref.take() } @@ -158,7 +130,7 @@ where } /// Resets this state after a full stage iter. - fn end_of_iter(&mut self, shared_state: PushStageSharedState, errored: bool) { + fn end_of_iter(&mut self, shared_state: PushStageSharedState, errored: bool) { self.set_shared_state(shared_state); self.errored = errored; self.current_corpus_id = None; @@ -171,18 +143,11 @@ 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: Iterator -where - CS: Scheduler, - Z::State: HasRand + HasExecutions + HasMetadata + HasCorpus + HasLastReportTime, - EM: EventFirer + EventRestarter + HasEventManagerId + ProgressReporter, - OT: ObserversTuple, - Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, -{ +pub trait PushStage { /// Gets the [`PushStageHelper`] - fn push_stage_helper(&self) -> &PushStageHelper; + fn push_stage_helper(&self) -> &PushStageHelper; /// Gets the [`PushStageHelper`] (mutable) - fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper; + fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper; /// Set the current corpus index this stage works on fn set_current_corpus_id(&mut self, corpus_id: CorpusId) { @@ -196,7 +161,7 @@ where fn init( &mut self, _fuzzer: &mut Z, - _state: &mut Z::State, + _state: &mut S, _event_mgr: &mut EM, _observers: &mut OT, ) -> Result<(), Error> { @@ -209,20 +174,20 @@ where fn pre_exec( &mut self, _fuzzer: &mut Z, - _state: &mut Z::State, + _state: &mut S, _event_mgr: &mut EM, _observers: &mut OT, - ) -> Option::Input, Error>>; + ) -> Option>; /// Called after the execution of a testcase finished. #[inline] fn post_exec( &mut self, _fuzzer: &mut Z, - _state: &mut Z::State, + _state: &mut S, _event_mgr: &mut EM, _observers: &mut OT, - _input: ::Input, + _input: I, _exit_kind: ExitKind, ) -> Result<(), Error> { Ok(()) @@ -233,80 +198,127 @@ where fn deinit( &mut self, _fuzzer: &mut Z, - _state: &mut Z::State, + _state: &mut S, _event_mgr: &mut EM, _observers: &mut OT, ) -> Result<(), Error> { Ok(()) } +} - /// This is the default implementation for `next` for this stage - fn next_std(&mut self) -> Option::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() - }; +/// Allows us to use a [`PushStage`] as a normal [`Stage`] +#[allow(clippy::type_complexity)] +#[derive(Debug)] +pub struct PushStageAdapter { + name: Cow<'static, str>, + push_stage: PS, + phantom: PhantomData<(CS, EM, OT, Z)>, +} - let step_success = if self.push_stage_helper().initialized { - // We already ran once - - let last_input = self.push_stage_helper_mut().current_input.take().unwrap(); - - self.post_exec( - &mut shared_state.fuzzer, - &mut shared_state.state, - &mut shared_state.event_mgr, - &mut shared_state.observers, - last_input, - self.push_stage_helper().exit_kind().unwrap(), - ) - } else { - self.init( - &mut shared_state.fuzzer, - &mut shared_state.state, - &mut shared_state.event_mgr, - &mut shared_state.observers, - ) +impl PushStageAdapter { + /// Create a new [`PushStageAdapter`], wrapping the given [`PushStage`] + /// to be used as a normal [`Stage`] + #[must_use] + pub fn new(push_stage: PS) -> Self { + // unsafe but impossible that you create two threads both instantiating this instance + let stage_id = unsafe { + let ret = PUSH_STAGE_ADAPTER_ID; + PUSH_STAGE_ADAPTER_ID += 1; + ret }; - if let Err(err) = step_success { - self.push_stage_helper_mut().end_of_iter(shared_state, true); - return Some(Err(err)); + Self { + name: Cow::Owned( + PUSH_STAGE_ADAPTER_NAME.to_owned() + ":" + stage_id.to_string().as_str(), + ), + push_stage, + phantom: PhantomData, } + } +} +/// The unique counter for this stage +static mut PUSH_STAGE_ADAPTER_ID: usize = 0; +/// The name for push stage adapter +pub static PUSH_STAGE_ADAPTER_NAME: &str = "pushstageadapter"; + +impl Named for PushStageAdapter { + #[must_use] + fn name(&self) -> &Cow<'static, str> { + &self.name + } +} + +impl Stage for PushStageAdapter +where + CS: Scheduler<::Input, S>, + S: HasExecutions + + HasRand + + HasCorpus + + HasLastReportTime + + HasCurrentCorpusId + + HasNamedMetadata + + HasMetadata + + UsesInput::Input>, + E: Executor + HasObservers, + EM: EventFirer + EventRestarter + HasEventManagerId + ProgressReporter, + OT: ObserversTuple<::Input, S>, + PS: PushStage::Input, OT, S, Z>, + Z: ExecutesInput + + ExecutionProcessor + + EvaluatorObservers + + HasScheduler, +{ + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut S, + event_mgr: &mut EM, + ) -> Result<(), Error> { + let push_stage = &mut self.push_stage; + + let Some(corpus_id) = state.current_corpus_id()? else { + return Err(Error::illegal_state( + "state is not currently processing a corpus index", + )); + }; - //for i in 0..num { - let ret = self.pre_exec( - &mut shared_state.fuzzer, - &mut shared_state.state, - &mut shared_state.event_mgr, - &mut shared_state.observers, - ); - if ret.is_none() { - // We're done. - drop(self.push_stage_helper_mut().current_input.take()); - self.push_stage_helper_mut().initialized = false; - - if let Err(err) = self.deinit( - &mut shared_state.fuzzer, - &mut shared_state.state, - &mut shared_state.event_mgr, - &mut shared_state.observers, - ) { - self.push_stage_helper_mut().end_of_iter(shared_state, true); - return Some(Err(err)); - }; - - if let Err(err) = shared_state - .event_mgr - .maybe_report_progress(&mut shared_state.state, STATS_TIMEOUT_DEFAULT) - { - self.push_stage_helper_mut().end_of_iter(shared_state, true); - return Some(Err(err)); - }; - } else { - self.push_stage_helper_mut().reset_exit_kind(); + push_stage.set_current_corpus_id(corpus_id); + + push_stage.init(fuzzer, state, event_mgr, &mut *executor.observers_mut())?; + + loop { + let input = + match push_stage.pre_exec(fuzzer, state, event_mgr, &mut *executor.observers_mut()) + { + Some(Ok(next_input)) => next_input, + Some(Err(err)) => return Err(err), + None => break, + }; + + let exit_kind = fuzzer.execute_input(state, executor, event_mgr, &input)?; + + push_stage.post_exec( + fuzzer, + state, + event_mgr, + &mut *executor.observers_mut(), + input, + exit_kind, + )?; } - self.push_stage_helper_mut() - .end_of_iter(shared_state, false); - ret + + self.push_stage + .deinit(fuzzer, state, event_mgr, &mut *executor.observers_mut()) + } + + #[inline] + fn should_restart(&mut self, state: &mut S) -> Result { + // TODO: Proper restart handling - call post_exec at the right time, etc... + RetryCountRestartHelper::no_retry(state, &self.name) + } + + #[inline] + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { + RetryCountRestartHelper::clear_progress(state, &self.name) } } diff --git a/libafl/src/stages/push/mutational.rs b/libafl/src/stages/push/mutational.rs index 73d73703de..d3d35f8b50 100644 --- a/libafl/src/stages/push/mutational.rs +++ b/libafl/src/stages/push/mutational.rs @@ -11,22 +11,23 @@ use libafl_bolts::rands::Rand; use serde::Serialize; use super::{PushStage, PushStageHelper, PushStageSharedState}; +#[cfg(feature = "introspection")] +use crate::monitors::PerfFeature; use crate::{ corpus::{Corpus, CorpusId}, - events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter}, + events::{EventFirer, ProgressReporter}, executors::ExitKind, - inputs::UsesInput, + fuzzer::STATS_TIMEOUT_DEFAULT, + inputs::{Input, UsesInput}, mark_feature_time, mutators::Mutator, nonzero, observers::ObserversTuple, schedulers::Scheduler, start_timer, - state::{HasCorpus, HasExecutions, HasLastReportTime, HasRand, UsesState}, - Error, EvaluatorObservers, ExecutionProcessor, HasMetadata, HasScheduler, + state::{HasCorpus, HasExecutions, HasLastReportTime, HasRand, MaybeHasClientPerfMonitor}, + Error, ExecutionProcessor, HasMetadata, HasScheduler, }; -#[cfg(feature = "introspection")] -use crate::{monitors::PerfFeature, state::HasClientPerfMonitor}; /// The default maximum number of mutations to perform per input. pub const DEFAULT_MUTATIONAL_MAX_ITERATIONS: usize = 128; @@ -42,14 +43,10 @@ pub const DEFAULT_MUTATIONAL_MAX_ITERATIONS: usize = 128; /// /// The default mutational push stage #[derive(Clone, Debug)] -pub struct StdMutationalPushStage +pub struct StdMutationalPushStage where - CS: Scheduler, - EM: EventFirer + EventRestarter + HasEventManagerId, - M: Mutator, - OT: ObserversTuple + Serialize, - Z::State: HasRand + HasCorpus + Clone + Debug, - Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, + S: HasCorpus, + ::Input: Clone + Debug, { current_corpus_id: Option, testcases_to_do: usize, @@ -57,21 +54,17 @@ where mutator: M, - psh: PushStageHelper, + psh: PushStageHelper::Input, OT, S, Z>, } -impl StdMutationalPushStage +impl StdMutationalPushStage where - CS: Scheduler, - EM: EventFirer + EventRestarter + HasEventManagerId, - M: Mutator, - OT: ObserversTuple + Serialize, - Z::State: HasCorpus + HasRand + Clone + Debug, - Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, + S: HasCorpus + HasRand, + ::Input: Clone + Debug, { /// 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 Z::State, _corpus_id: CorpusId) -> Result { + fn iterations(&self, state: &mut S, _corpus_id: CorpusId) -> Result { Ok(1 + state .rand_mut() .below(nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS))) @@ -83,23 +76,28 @@ where } } -impl PushStage for StdMutationalPushStage +impl PushStage::Input, OT, S, Z> + for StdMutationalPushStage where - CS: Scheduler, - EM: EventFirer + EventRestarter + HasEventManagerId + ProgressReporter, - M: Mutator, - OT: ObserversTuple + Serialize, - Z::State: HasCorpus + HasRand + HasExecutions + HasLastReportTime + HasMetadata + Clone + Debug, - Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, - <::State as HasCorpus>::Corpus: Corpus, //delete me + EM: EventFirer, + Z: HasScheduler + ExecutionProcessor, + S: HasCorpus + + UsesInput::Input> + + HasRand + + MaybeHasClientPerfMonitor, + M: Mutator<::Input, S>, + OT: ObserversTuple<::Input, S> + Serialize, + ::Input: Input + Clone, { #[inline] - fn push_stage_helper(&self) -> &PushStageHelper { + fn push_stage_helper(&self) -> &PushStageHelper::Input, OT, S, Z> { &self.psh } #[inline] - fn push_stage_helper_mut(&mut self) -> &mut PushStageHelper { + fn push_stage_helper_mut( + &mut self, + ) -> &mut PushStageHelper::Input, OT, S, Z> { &mut self.psh } @@ -107,7 +105,7 @@ where fn init( &mut self, fuzzer: &mut Z, - state: &mut Z::State, + state: &mut S, _event_mgr: &mut EM, _observers: &mut OT, ) -> Result<(), Error> { @@ -126,10 +124,10 @@ where fn pre_exec( &mut self, _fuzzer: &mut Z, - state: &mut Z::State, + state: &mut S, _event_mgr: &mut EM, _observers: &mut OT, - ) -> Option::Input, Error>> { + ) -> Option::Input, Error>> { if self.testcases_done >= self.testcases_to_do { // finished with this cicle. return None; @@ -161,10 +159,10 @@ where fn post_exec( &mut self, fuzzer: &mut Z, - state: &mut Z::State, + state: &mut S, event_mgr: &mut EM, observers: &mut OT, - last_input: ::Input, + last_input: ::Input, exit_kind: ExitKind, ) -> Result<(), Error> { // todo: is_interesting, etc. @@ -183,7 +181,7 @@ where fn deinit( &mut self, _fuzzer: &mut Z, - _state: &mut Z::State, + _state: &mut S, _event_mgr: &mut EM, _observers: &mut OT, ) -> Result<(), Error> { @@ -192,38 +190,51 @@ where } } -impl Iterator for StdMutationalPushStage +impl Iterator for StdMutationalPushStage where - CS: Scheduler, - EM: EventFirer + EventRestarter + HasEventManagerId + ProgressReporter, - M: Mutator, - OT: ObserversTuple + Serialize, - Z::State: HasCorpus + HasRand + HasExecutions + HasMetadata + HasLastReportTime + Clone + Debug, - Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, - <::State as HasCorpus>::Corpus: Corpus, //delete me + EM: ProgressReporter, + S: HasCorpus + + HasMetadata + + HasExecutions + + HasLastReportTime + + HasRand + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + OT: ObserversTuple<::Input, S> + Serialize, + M: Mutator<::Input, S>, + ::Input: Clone + Debug + Input, + Z: HasScheduler + ExecutionProcessor, { - type Item = Result<::Input, Error>; + type Item = Result<::Input, Error>; - fn next(&mut self) -> Option::Input, Error>> { + fn next(&mut self) -> Option::Input, Error>> { self.next_std() } } -impl StdMutationalPushStage +impl StdMutationalPushStage where - CS: Scheduler, - EM: EventFirer + EventRestarter + HasEventManagerId, - M: Mutator, - OT: ObserversTuple + Serialize, - Z::State: HasCorpus + HasRand + Clone + Debug, - Z: ExecutionProcessor + EvaluatorObservers + HasScheduler, + EM: ProgressReporter, + S: HasCorpus + + HasMetadata + + HasExecutions + + HasLastReportTime + + HasRand + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + OT: ObserversTuple<::Input, S> + Serialize, + M: Mutator<::Input, S>, + ::Input: Clone + Debug + Input, + Z: HasScheduler + ExecutionProcessor, { /// Creates a new default mutational stage #[must_use] #[allow(clippy::type_complexity)] pub fn new( mutator: M, - shared_state: Rc>>>, + shared_state: Rc< + RefCell::Input, OT, S, Z>>>, + >, exit_kind: Rc>>, ) -> Self { Self { @@ -234,4 +245,74 @@ where testcases_done: 0, } } + + /// This is the implementation for `next` for this stage + pub fn next_std(&mut self) -> Option::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() + }; + + let step_success = if self.push_stage_helper().initialized { + // We already ran once + + let last_input = self.push_stage_helper_mut().current_input.take().unwrap(); + + self.post_exec( + &mut shared_state.fuzzer, + &mut shared_state.state, + &mut shared_state.event_mgr, + &mut shared_state.observers, + last_input, + self.push_stage_helper().exit_kind().unwrap(), + ) + } else { + self.init( + &mut shared_state.fuzzer, + &mut shared_state.state, + &mut shared_state.event_mgr, + &mut shared_state.observers, + ) + }; + if let Err(err) = step_success { + self.push_stage_helper_mut().end_of_iter(shared_state, true); + return Some(Err(err)); + } + + //for i in 0..num { + let ret = self.pre_exec( + &mut shared_state.fuzzer, + &mut shared_state.state, + &mut shared_state.event_mgr, + &mut shared_state.observers, + ); + if ret.is_none() { + // We're done. + drop(self.push_stage_helper_mut().current_input.take()); + self.push_stage_helper_mut().initialized = false; + + if let Err(err) = self.deinit( + &mut shared_state.fuzzer, + &mut shared_state.state, + &mut shared_state.event_mgr, + &mut shared_state.observers, + ) { + self.push_stage_helper_mut().end_of_iter(shared_state, true); + return Some(Err(err)); + }; + + if let Err(err) = shared_state + .event_mgr + .maybe_report_progress(&mut shared_state.state, STATS_TIMEOUT_DEFAULT) + { + self.push_stage_helper_mut().end_of_iter(shared_state, true); + return Some(Err(err)); + }; + } else { + self.push_stage_helper_mut().reset_exit_kind(); + } + self.push_stage_helper_mut() + .end_of_iter(shared_state, false); + ret + } } diff --git a/libafl/src/stages/stats.rs b/libafl/src/stages/stats.rs deleted file mode 100644 index 6be3de2fd7..0000000000 --- a/libafl/src/stages/stats.rs +++ /dev/null @@ -1,181 +0,0 @@ -//! Stage to compute/report minimal AFL-like stats - -#[cfg(feature = "std")] -use alloc::{borrow::Cow, string::ToString}; -use core::{marker::PhantomData, time::Duration}; - -use libafl_bolts::current_time; -#[cfg(feature = "std")] -use serde_json::json; - -use crate::{ - corpus::{Corpus, HasCurrentCorpusId}, - events::EventFirer, - schedulers::minimizer::IsFavoredMetadata, - stages::Stage, - state::{HasCorpus, HasImported, UsesState}, - Error, HasMetadata, -}; -#[cfg(feature = "std")] -use crate::{ - events::Event, - monitors::{AggregatorOps, UserStats, UserStatsValue}, -}; - -/// The [`StatsStage`] is a simple stage that computes and reports some stats. -#[derive(Debug, Clone)] -pub struct StatsStage { - // the number of testcases that have been fuzzed - has_fuzzed_size: usize, - // the number of "favored" testcases - is_favored_size: usize, - // the number of testcases found by itself - own_finds_size: usize, - // the number of testcases imported by other fuzzers - imported_size: usize, - // the last time that we report all stats - last_report_time: Duration, - // the interval that we report all stats - stats_report_interval: Duration, - - phantom: PhantomData<(E, EM, Z)>, -} - -impl UsesState for StatsStage -where - E: UsesState, -{ - type State = E::State; -} - -impl Stage for StatsStage -where - E: UsesState, - EM: EventFirer, - Z: UsesState, - Self::State: HasImported + HasCorpus + HasMetadata, -{ - fn perform( - &mut self, - _fuzzer: &mut Z, - _executor: &mut E, - state: &mut Self::State, - _manager: &mut EM, - ) -> Result<(), Error> { - self.update_and_report_afl_stats(state, _manager) - } - - #[inline] - fn should_restart(&mut self, _state: &mut Self::State) -> Result { - // Not running the target so we wont't crash/timeout and, hence, don't need to restore anything - Ok(true) - } - - #[inline] - fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { - // Not running the target so we wont't crash/timeout and, hence, don't need to restore anything - Ok(()) - } -} - -impl StatsStage { - fn update_and_report_afl_stats( - &mut self, - state: &mut ::State, - _manager: &mut EM, - ) -> Result<(), Error> - where - E: UsesState, - EM: EventFirer, - ::State: HasCorpus + HasImported, - { - let Some(corpus_id) = state.current_corpus_id()? else { - return Err(Error::illegal_state( - "state is not currently processing a corpus index", - )); - }; - - // Report your stats every `STATS_REPORT_INTERVAL` - // compute pending, pending_favored, imported, own_finds - { - let testcase = state.corpus().get(corpus_id)?.borrow(); - if testcase.scheduled_count() == 0 { - self.has_fuzzed_size += 1; - if testcase.has_metadata::() { - self.is_favored_size += 1; - } - } else { - return Ok(()); - } - } - - let corpus_size = state.corpus().count(); - let pending_size = corpus_size - self.has_fuzzed_size; - let pend_favored_size = corpus_size - self.is_favored_size; - self.imported_size = *state.imported(); - self.own_finds_size = corpus_size - self.imported_size; - - let cur = current_time(); - - if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval { - #[cfg(feature = "std")] - { - let json = json!({ - "pending":pending_size, - "pend_fav":pend_favored_size, - "own_finds":self.own_finds_size, - "imported":self.imported_size, - }); - _manager.fire( - state, - Event::UpdateUserStats { - name: Cow::from("Stats"), - value: UserStats::new( - UserStatsValue::String(Cow::from(json.to_string())), - AggregatorOps::None, - ), - phantom: PhantomData, - }, - )?; - } - #[cfg(not(feature = "std"))] - log::info!( - "pending: {}, pend_favored: {}, own_finds: {}, imported: {}", - pending_size, - pend_favored_size, - self.own_finds_size, - self.imported_size - ); - self.last_report_time = cur; - } - - Ok(()) - } -} - -impl StatsStage { - /// create a new instance of the [`StatsStage`] - #[must_use] - pub fn new(interval: Duration) -> Self { - Self { - stats_report_interval: interval, - ..Default::default() - } - } -} - -impl Default for StatsStage { - /// the default instance of the [`StatsStage`] - #[must_use] - fn default() -> Self { - Self { - has_fuzzed_size: 0, - is_favored_size: 0, - own_finds_size: 0, - imported_size: 0, - last_report_time: current_time(), - stats_report_interval: Duration::from_secs(15), - phantom: PhantomData, - } - } -} diff --git a/libafl/src/stages/sync.rs b/libafl/src/stages/sync.rs index 3dbed175a2..b4cc5fcec0 100644 --- a/libafl/src/stages/sync.rs +++ b/libafl/src/stages/sync.rs @@ -10,16 +10,14 @@ use std::path::{Path, PathBuf}; use libafl_bolts::{current_time, fs::find_new_files_rec, shmem::ShMemProvider, Named}; use serde::{Deserialize, Serialize}; -#[cfg(feature = "introspection")] -use crate::state::HasClientPerfMonitor; use crate::{ - corpus::{Corpus, CorpusId}, + corpus::{Corpus, CorpusId, HasCurrentCorpusId}, events::{llmp::LlmpEventConverter, Event, EventConfig, EventFirer}, executors::{Executor, ExitKind, HasObservers}, fuzzer::{Evaluator, EvaluatorObservers, ExecutionProcessor}, inputs::{Input, InputConverter, UsesInput}, stages::{RetryCountRestartHelper, Stage}, - state::{HasCorpus, HasExecutions, HasRand, State, UsesState}, + state::{HasCorpus, HasExecutions, HasRand, MaybeHasClientPerfMonitor, State, Stoppable}, Error, HasMetadata, HasNamedMetadata, }; @@ -54,41 +52,38 @@ impl SyncFromDiskMetadata { /// A stage that loads testcases from disk to sync with other fuzzers such as AFL++ #[derive(Debug)] -pub struct SyncFromDiskStage { +pub struct SyncFromDiskStage { name: Cow<'static, str>, sync_dirs: Vec, load_callback: CB, interval: Duration, - phantom: PhantomData<(E, EM, Z)>, + phantom: PhantomData<(E, EM, S, Z)>, } -impl UsesState for SyncFromDiskStage -where - Z: UsesState, -{ - type State = Z::State; -} - -impl Named for SyncFromDiskStage { +impl Named for SyncFromDiskStage { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl Stage for SyncFromDiskStage +impl Stage for SyncFromDiskStage where - CB: FnMut(&mut Z, &mut Self::State, &Path) -> Result<::Input, Error>, - E: UsesState, - EM: UsesState, - Z: Evaluator, - Self::State: HasCorpus + HasRand + HasMetadata + HasNamedMetadata, + CB: FnMut(&mut Z, &mut S, &Path) -> Result<::Input, Error>, + Z: Evaluator, + S: HasCorpus + + HasRand + + HasMetadata + + HasNamedMetadata + + UsesInput::Input> + + HasCurrentCorpusId + + MaybeHasClientPerfMonitor, { #[inline] fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { let last = state @@ -144,19 +139,19 @@ where } #[inline] - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { // TODO: Needs proper crash handling for when an imported testcase crashes // For now, Make sure we don't get stuck crashing on this testcase RetryCountRestartHelper::no_retry(state, &self.name) } #[inline] - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { RetryCountRestartHelper::clear_progress(state, &self.name) } } -impl SyncFromDiskStage { +impl SyncFromDiskStage { /// Creates a new [`SyncFromDiskStage`] #[must_use] pub fn new(sync_dirs: Vec, load_callback: CB, interval: Duration, name: &str) -> Self { @@ -174,10 +169,8 @@ impl SyncFromDiskStage { pub type SyncFromDiskFunction = fn(&mut Z, &mut S, &Path) -> Result<::Input, Error>; -impl SyncFromDiskStage, E, EM, Z> +impl SyncFromDiskStage, E, EM, S, Z> where - E: UsesState::State>, - EM: UsesState::State>, Z: Evaluator, { /// Creates a new [`SyncFromDiskStage`] invoking `Input::from_file` to load inputs @@ -234,38 +227,31 @@ where client: LlmpEventConverter, } -impl UsesState for SyncFromBrokerStage +impl Stage for SyncFromBrokerStage where - SP: ShMemProvider + 'static, - S: State, - IC: InputConverter, - ICB: InputConverter, - DI: Input, -{ - type State = S; -} - -impl Stage for SyncFromBrokerStage -where - EM: UsesState + EventFirer, - S: State + HasExecutions + HasCorpus + HasRand + HasMetadata, + EM: EventFirer, + S: HasExecutions + + HasCorpus + + HasRand + + HasMetadata + + Stoppable + + UsesInput::Input> + + State, SP: ShMemProvider, E: HasObservers + Executor, for<'a> E::Observers: Deserialize<'a>, - Z: EvaluatorObservers - + ExecutionProcessor, - IC: InputConverter, - ICB: InputConverter, + Z: EvaluatorObservers + ExecutionProcessor, + IC: InputConverter::Input, To = DI>, + ICB: InputConverter::Input>, DI: Input, - <::Corpus as Corpus>::Input: Clone, - S::Corpus: Corpus, // delete me + <::Corpus as Corpus>::Input: Input + Clone, { #[inline] fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { if self.client.can_convert() { @@ -319,13 +305,13 @@ where } #[inline] - fn should_restart(&mut self, _state: &mut Self::State) -> Result { + fn should_restart(&mut self, _state: &mut S) -> Result { // No restart handling needed - does not execute the target. Ok(true) } #[inline] - fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> { // Not needed - does not execute the target. Ok(()) } diff --git a/libafl/src/stages/time_tracker.rs b/libafl/src/stages/time_tracker.rs index ee08d0dec8..b39ae2cd49 100644 --- a/libafl/src/stages/time_tracker.rs +++ b/libafl/src/stages/time_tracker.rs @@ -3,12 +3,7 @@ use std::{marker::PhantomData, time::Duration}; use libafl_bolts::{current_time, Error}; -use crate::{ - inputs::UsesInput, - stages::Stage, - state::{State, UsesState}, - HasMetadata, -}; +use crate::{stages::Stage, HasMetadata}; /// Track an inner Stage's execution time #[derive(Debug)] pub struct TimeTrackingStageWrapper { @@ -28,27 +23,17 @@ impl TimeTrackingStageWrapper { } } -impl UsesState for TimeTrackingStageWrapper +impl Stage for TimeTrackingStageWrapper where - S: State + HasMetadata, -{ - type State = S; -} - -impl Stage for TimeTrackingStageWrapper -where - S: UsesInput + State + HasMetadata, - ST: Stage, - M: UsesState, - Z: UsesState, - E: UsesState, + S: HasMetadata, + ST: Stage, T: libafl_bolts::serdeany::SerdeAny + From, { fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut M, ) -> Result<(), Error> { let before_run = current_time(); @@ -59,11 +44,11 @@ where Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { self.inner.should_restart(state) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { self.inner.clear_progress(state) } @@ -71,7 +56,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut M, ) -> Result<(), Error> { self.inner diff --git a/libafl/src/stages/tmin.rs b/libafl/src/stages/tmin.rs index d77299e73f..e7d072e486 100644 --- a/libafl/src/stages/tmin.rs +++ b/libafl/src/stages/tmin.rs @@ -1,4 +1,4 @@ -//! The [`TMinMutationalStage`] is a stage which will attempt to minimize corpus entries. +//! The [`StdTMinMutationalStage`] is a stage which will attempt to minimize corpus entries. use alloc::{ borrow::{Cow, ToOwned}, @@ -15,12 +15,14 @@ use serde::Serialize; #[cfg(feature = "track_hit_feedbacks")] use crate::feedbacks::premature_last_result_err; +#[cfg(feature = "introspection")] +use crate::monitors::PerfFeature; use crate::{ corpus::{Corpus, HasCurrentCorpusId, Testcase}, events::EventFirer, executors::{ExitKind, HasObservers}, feedbacks::{Feedback, FeedbackFactory, HasObserverHandle, StateInitializer}, - inputs::UsesInput, + inputs::{Input, UsesInput}, mark_feature_time, mutators::{MutationResult, Mutator}, observers::{MapObserver, ObserversTuple}, @@ -31,44 +33,137 @@ use crate::{ }, start_timer, state::{ - HasCorpus, HasCurrentTestcase, HasExecutions, HasMaxSize, HasSolutions, State, UsesState, + HasCorpus, HasCurrentTestcase, HasExecutions, HasMaxSize, HasSolutions, + MaybeHasClientPerfMonitor, State, UsesState, }, Error, ExecutesInput, ExecutionProcessor, HasFeedback, HasMetadata, HasNamedMetadata, HasScheduler, }; -#[cfg(feature = "introspection")] -use crate::{monitors::PerfFeature, state::HasClientPerfMonitor}; -/// Mutational stage which minimizes corpus entries. -/// -/// You must provide at least one mutator that actually reduces size. -pub trait TMinMutationalStage: - Stage + FeedbackFactory + +/// The default corpus entry minimising mutational stage +#[derive(Clone, Debug)] +pub struct StdTMinMutationalStage { + /// The name + name: Cow<'static, str>, + /// The mutator(s) this stage uses + mutator: M, + /// The factory + factory: FF, + /// The runs (=iterations) we are supposed to do + runs: usize, + /// The progress helper for this stage, keeping track of resumes after timeouts/crashes + restart_helper: ExecutionCountRestartHelper, + #[allow(clippy::type_complexity)] + phantom: PhantomData<(E, EM, F, S, Z)>, +} + +impl Stage for StdTMinMutationalStage where - E: UsesState + HasObservers, - E::Observers: ObserversTuple + Serialize, - EM: UsesState + EventFirer, - F: Feedback, - Self::State: HasMaxSize + HasCorpus + HasSolutions + HasExecutions + HasCurrentTestcase, - Self::Input: MutatedTransform + Clone + Hash + HasLen, - IP: Clone + MutatedTransformPost, - M: Mutator, - Z: UsesState - + HasScheduler - + HasFeedback + Z: HasScheduler + + ExecutionProcessor + ExecutesInput - + ExecutionProcessor, - Z::Feedback: Feedback, - Z::Scheduler: RemovableScheduler, - <::State as HasCorpus>::Corpus: Corpus, + + HasFeedback, + Z::Scheduler: RemovableScheduler<::Input, S>, + E: HasObservers + UsesState, + E::Observers: ObserversTuple<::Input, S> + Serialize, + EM: EventFirer, + FF: FeedbackFactory, + F: Feedback::Input, E::Observers, S>, + S: HasMetadata + + HasExecutions + + HasSolutions + + HasCorpus + + HasMaxSize + + HasNamedMetadata + + HasCurrentCorpusId + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + Z::Feedback: Feedback::Input, E::Observers, S>, + M: Mutator<::Input, S>, + <::Corpus as Corpus>::Input: Input + Hash + HasLen, +{ + fn should_restart(&mut self, state: &mut S) -> Result { + self.restart_helper.should_restart(state, &self.name) + } + + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { + self.restart_helper.clear_progress(state, &self.name) + } + + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut Z::State, + manager: &mut EM, + ) -> Result<(), Error> { + self.perform_minification(fuzzer, executor, state, manager)?; + + #[cfg(feature = "introspection")] + state.introspection_monitor_mut().finish_stage(); + + Ok(()) + } +} + +impl FeedbackFactory + for StdTMinMutationalStage +where + E: HasObservers, + FF: FeedbackFactory, { - /// The mutator registered for this stage - fn mutator(&self) -> &M; + fn create_feedback(&self, ctx: &E::Observers) -> F { + self.factory.create_feedback(ctx) + } +} + +impl Named for StdTMinMutationalStage { + fn name(&self) -> &Cow<'static, str> { + &self.name + } +} + +/// The counter for giving this stage unique id +static mut TMIN_STAGE_ID: usize = 0; +/// The name for tmin stage +pub static TMIN_STAGE_NAME: &str = "tmin"; - /// The mutator registered for this stage (mutable) - fn mutator_mut(&mut self) -> &mut M; +impl StdTMinMutationalStage +where + Z: HasScheduler + + ExecutionProcessor + + ExecutesInput + + HasFeedback, + Z::Scheduler: RemovableScheduler<::Input, S>, + E: HasObservers + UsesState, + E::Observers: ObserversTuple<::Input, S> + Serialize, + EM: EventFirer, + FF: FeedbackFactory, + F: Feedback::Input, E::Observers, S>, + S: HasMetadata + + HasExecutions + + HasSolutions + + HasCorpus + + HasMaxSize + + HasNamedMetadata + + HasCurrentTestcase + + HasCurrentCorpusId + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + Z::Feedback: Feedback::Input, E::Observers, S>, + M: Mutator<::Input, S>, + ::Input: Hash + HasLen + Input, +{ + /// The list of mutators, added to this stage (as mutable ref) + #[inline] + fn mutator_mut(&mut self) -> &mut M { + &mut self.mutator + } - /// Gets the number of iterations this mutator should run for. - fn iterations(&self, state: &mut Self::State) -> Result; + /// Gets the number of iterations from a fixed number of runs + fn iterations(&self, _state: &mut S) -> usize { + self.runs + } /// Runs this (mutational) stage for new objectives #[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely... @@ -76,7 +171,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { let Some(base_corpus_id) = state.current_corpus_id()? else { @@ -88,7 +183,7 @@ where let orig_max_size = state.max_size(); // basically copy-pasted from mutational.rs let num = self - .iterations(state)? + .iterations(state) .saturating_sub(usize::try_from(self.execs_since_progress_start(state)?)?); // If num is negative, then quit. @@ -97,8 +192,10 @@ where } start_timer!(state); - let transformed = - Self::Input::try_transform_from(state.current_testcase_mut()?.borrow_mut(), state)?; + let transformed = ::Input::try_transform_from( + state.current_testcase_mut()?.borrow_mut(), + state, + )?; let mut base = state.current_input_cloned()?; // potential post operation if base is replaced by a shorter input let mut base_post = None; @@ -159,7 +256,7 @@ where if feedback.is_interesting(state, manager, &input, &*observers, &exit_kind)? { // we found a reduced corpus entry! use the smaller base base = input; - base_post = Some(post.clone()); + base_post = Some(post); // do more runs! maybe we can minify further next_i = 0; @@ -210,144 +307,13 @@ where Ok(()) } - /// Gets the number of executions this mutator already did since it got first called in this fuzz round. - fn execs_since_progress_start(&mut self, state: &mut Self::State) -> Result; -} - -/// The default corpus entry minimising mutational stage -#[derive(Clone, Debug)] -pub struct StdTMinMutationalStage { - /// The name - name: Cow<'static, str>, - /// The mutator(s) this stage uses - mutator: M, - /// The factory - factory: FF, - /// The runs (=iterations) we are supposed to do - runs: usize, - /// The progress helper for this stage, keeping track of resumes after timeouts/crashes - restart_helper: ExecutionCountRestartHelper, - #[allow(clippy::type_complexity)] - phantom: PhantomData<(E, EM, F, IP, Z)>, -} - -impl UsesState for StdTMinMutationalStage -where - Z: UsesState, -{ - type State = Z::State; -} - -impl Stage for StdTMinMutationalStage -where - Z: HasScheduler + ExecutionProcessor + ExecutesInput + HasFeedback, - Z::Scheduler: RemovableScheduler, - E: HasObservers + UsesState, - E::Observers: ObserversTuple + Serialize, - EM: EventFirer, - FF: FeedbackFactory, - F: Feedback, - Self::Input: MutatedTransform + Clone + HasLen + Hash, - Z::State: - HasMetadata + HasExecutions + HasSolutions + HasCorpus + HasMaxSize + HasNamedMetadata, - Z::Feedback: Feedback, - M: Mutator, - IP: MutatedTransformPost + Clone, - <::State as HasCorpus>::Corpus: Corpus, // delete me -{ - fn should_restart(&mut self, state: &mut Self::State) -> Result { - self.restart_helper.should_restart(state, &self.name) - } - - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { - self.restart_helper.clear_progress(state, &self.name) - } - - fn perform( - &mut self, - fuzzer: &mut Z, - executor: &mut E, - state: &mut Z::State, - manager: &mut EM, - ) -> Result<(), Error> { - self.perform_minification(fuzzer, executor, state, manager)?; - - #[cfg(feature = "introspection")] - state.introspection_monitor_mut().finish_stage(); - - Ok(()) - } -} - -impl FeedbackFactory - for StdTMinMutationalStage -where - E: HasObservers, - FF: FeedbackFactory, -{ - fn create_feedback(&self, ctx: &E::Observers) -> F { - self.factory.create_feedback(ctx) - } -} - -impl Named for StdTMinMutationalStage { - fn name(&self) -> &Cow<'static, str> { - &self.name - } -} - -/// The counter for giving this stage unique id -static mut TMIN_STAGE_ID: usize = 0; -/// The name for tmin stage -pub static TMIN_STAGE_NAME: &str = "tmin"; - -impl TMinMutationalStage - for StdTMinMutationalStage -where - Z: HasScheduler + ExecutionProcessor + ExecutesInput + HasFeedback, - Z::Scheduler: RemovableScheduler, - E: HasObservers + UsesState, - E::Observers: ObserversTuple + Serialize, - EM: EventFirer, - FF: FeedbackFactory, - F: Feedback, - Self::Input: MutatedTransform + Clone + HasLen + Hash, - Z::State: HasMetadata - + HasExecutions - + HasSolutions - + HasCorpus - + HasMaxSize - + HasNamedMetadata - + HasCurrentTestcase, - Z::Feedback: Feedback, - M: Mutator, - IP: MutatedTransformPost + Clone, - <::State as HasCorpus>::Corpus: Corpus, // delete me -{ - /// The mutator, added to this stage - #[inline] - fn mutator(&self) -> &M { - &self.mutator - } - - /// The list of mutators, added to this stage (as mutable ref) - #[inline] - fn mutator_mut(&mut self) -> &mut M { - &mut self.mutator - } - - /// Gets the number of iterations from a fixed number of runs - fn iterations(&self, _state: &mut Self::State) -> Result { - Ok(self.runs) - } - - fn execs_since_progress_start(&mut self, state: &mut Self::State) -> Result { + fn execs_since_progress_start(&mut self, state: &mut S) -> Result { self.restart_helper .execs_since_progress_start(state, &self.name) } } -impl StdTMinMutationalStage { +impl StdTMinMutationalStage { /// Creates a new minimizing mutational stage that will minimize provided corpus entries pub fn new(mutator: M, factory: FF, runs: usize) -> Self { // unsafe but impossible that you create two threads both instantiating this instance diff --git a/libafl/src/stages/tracing.rs b/libafl/src/stages/tracing.rs index 6c7fa6406e..b0a0442b4f 100644 --- a/libafl/src/stages/tracing.rs +++ b/libafl/src/stages/tracing.rs @@ -8,54 +8,47 @@ use core::{fmt::Debug, marker::PhantomData}; use libafl_bolts::Named; +#[cfg(feature = "introspection")] +use crate::monitors::PerfFeature; use crate::{ - corpus::Corpus, + corpus::{Corpus, HasCurrentCorpusId}, executors::{Executor, HasObservers, ShadowExecutor}, + inputs::{Input, UsesInput}, mark_feature_time, observers::ObserversTuple, stages::{RetryCountRestartHelper, Stage}, start_timer, - state::{HasCorpus, HasCurrentTestcase, HasExecutions, State, UsesState}, + state::{HasCorpus, HasCurrentTestcase, HasExecutions, MaybeHasClientPerfMonitor, UsesState}, Error, HasNamedMetadata, }; -#[cfg(feature = "introspection")] -use crate::{monitors::PerfFeature, state::HasClientPerfMonitor}; /// A stage that runs a tracer executor #[derive(Clone, Debug)] -pub struct TracingStage { +pub struct TracingStage { name: Cow<'static, str>, tracer_executor: TE, #[allow(clippy::type_complexity)] - phantom: PhantomData<(EM, TE, Z)>, -} - -impl UsesState for TracingStage -where - TE: UsesState, -{ - type State = TE::State; + phantom: PhantomData<(EM, TE, S, Z)>, } -impl TracingStage +impl TracingStage where - TE: Executor + HasObservers, - TE::Observers: ObserversTuple::State>, - ::State: HasExecutions + HasCorpus + HasNamedMetadata + HasCurrentTestcase, - EM: UsesState::State>, - Z: UsesState::State>, - <::State as HasCorpus>::Corpus: Corpus, // delete me + TE: Executor + HasObservers, + TE::Observers: ObserversTuple<::Input, S>, + S: HasExecutions + + HasCorpus + + HasNamedMetadata + + HasCurrentTestcase + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + EM: UsesState, //delete me + Z: UsesState, //delete me { #[allow(rustdoc::broken_intra_doc_links)] /// Perform tracing on the given `CorpusId`. Useful for if wrapping [`TracingStage`] with your /// own stage and you need to manage [`super::NestedStageRetryCountRestartHelper`] differently /// see [`super::ConcolicTracingStage`]'s implementation as an example of usage. - pub fn trace( - &mut self, - fuzzer: &mut Z, - state: &mut ::State, - manager: &mut EM, - ) -> Result<(), Error> { + pub fn trace(&mut self, fuzzer: &mut Z, state: &mut S, manager: &mut EM) -> Result<(), Error> { start_timer!(state); let input = state.current_input_cloned()?; @@ -83,37 +76,41 @@ where } } -impl Stage for TracingStage +impl Stage for TracingStage where - E: UsesState::State>, - TE: Executor + HasObservers, - TE::Observers: ObserversTuple::State>, - ::State: HasExecutions + HasCorpus + HasNamedMetadata, - EM: UsesState::State>, - Z: UsesState::State>, - <::State as HasCorpus>::Corpus: Corpus, // delete me + TE: Executor + HasObservers, + TE::Observers: ObserversTuple<::Input, S>, + S: HasExecutions + + HasCorpus + + HasNamedMetadata + + HasCurrentCorpusId + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + EM: UsesState, + Z: UsesState, + ::Input: Input, { #[inline] fn perform( &mut self, fuzzer: &mut Z, _executor: &mut E, - state: &mut ::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { self.trace(fuzzer, state, manager) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { RetryCountRestartHelper::no_retry(state, &self.name) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { RetryCountRestartHelper::clear_progress(state, &self.name) } } -impl Named for TracingStage { +impl Named for TracingStage { fn name(&self) -> &Cow<'static, str> { &self.name } @@ -124,7 +121,7 @@ static mut TRACING_STAGE_ID: usize = 0; /// The name for tracing stage pub static TRACING_STAGE_NAME: &str = "tracing"; -impl TracingStage { +impl TracingStage { /// Creates a new default stage pub fn new(tracer_executor: TE) -> Self { // unsafe but impossible that you create two threads both instantiating this instance @@ -154,49 +151,46 @@ impl TracingStage { /// A stage that runs the shadow executor using also the shadow observers #[derive(Clone, Debug)] -pub struct ShadowTracingStage { +pub struct ShadowTracingStage { name: Cow<'static, str>, #[allow(clippy::type_complexity)] - phantom: PhantomData<(E, EM, SOT, Z)>, + phantom: PhantomData<(E, EM, SOT, S, Z)>, } -impl UsesState for ShadowTracingStage -where - E: UsesState, -{ - type State = E::State; -} /// The counter for giving this stage unique id static mut SHADOW_TRACING_STAGE_ID: usize = 0; /// Name for shadow tracing stage pub static SHADOW_TRACING_STAGE_NAME: &str = "shadow"; -impl Named for ShadowTracingStage -where - E: UsesState, -{ +impl Named for ShadowTracingStage { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl Stage, EM, Z> for ShadowTracingStage +impl Stage, EM, S, Z> + for ShadowTracingStage where - E: Executor + HasObservers, - E::Observers: ObserversTuple, - EM: UsesState::State>, - SOT: ObserversTuple, - Z: UsesState::State>, - ::State: - State + HasExecutions + HasCorpus + HasNamedMetadata + Debug + HasCurrentTestcase, - <::State as HasCorpus>::Corpus: Corpus, // delete me + E: Executor + HasObservers, + E::Observers: ObserversTuple<::Input, S>, + SOT: ObserversTuple<::Input, S>, + S: HasExecutions + + HasCorpus + + HasNamedMetadata + + Debug + + HasCurrentTestcase + + HasCurrentCorpusId + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + EM: UsesState, + Z: UsesState, { #[inline] fn perform( &mut self, fuzzer: &mut Z, executor: &mut ShadowExecutor, - state: &mut ::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { start_timer!(state); @@ -227,22 +221,22 @@ where Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { RetryCountRestartHelper::no_retry(state, &self.name) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { RetryCountRestartHelper::clear_progress(state, &self.name) } } -impl ShadowTracingStage +impl ShadowTracingStage where - E: Executor + HasObservers, - ::State: State + HasExecutions + HasCorpus, - EM: UsesState::State>, - SOT: ObserversTuple, - Z: UsesState::State>, + E: Executor + HasObservers, + S: HasExecutions + HasCorpus, + SOT: ObserversTuple<::Input, S>, + EM: UsesState, + Z: UsesState, { /// Creates a new default stage pub fn new(_executor: &mut ShadowExecutor) -> Self { diff --git a/libafl/src/stages/tuneable.rs b/libafl/src/stages/tuneable.rs index b293282ae3..a5db42bb87 100644 --- a/libafl/src/stages/tuneable.rs +++ b/libafl/src/stages/tuneable.rs @@ -6,8 +6,11 @@ use core::{marker::PhantomData, time::Duration}; use libafl_bolts::{current_time, impl_serdeany, rands::Rand}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "introspection")] +use crate::monitors::PerfFeature; use crate::{ corpus::Corpus, + inputs::{Input, UsesInput}, mark_feature_time, mutators::{MutationResult, Mutator}, nonzero, @@ -16,11 +19,9 @@ use crate::{ ExecutionCountRestartHelper, MutationalStage, Stage, }, start_timer, - state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, UsesState}, + state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasRand, MaybeHasClientPerfMonitor}, Error, Evaluator, HasMetadata, HasNamedMetadata, }; -#[cfg(feature = "introspection")] -use crate::{monitors::PerfFeature, state::HasClientPerfMonitor}; #[cfg_attr( any(not(feature = "serdeany_autoreg"), miri), @@ -150,26 +151,103 @@ where /// A [`crate::stages::MutationalStage`] where the mutator iteration can be tuned at runtime #[derive(Clone, Debug)] -pub struct TuneableMutationalStage { +pub struct TuneableMutationalStage { /// The mutator we use mutator: M, /// The name of this stage name: String, /// The progress helper we use to keep track of progress across restarts restart_helper: ExecutionCountRestartHelper, - phantom: PhantomData<(E, EM, I, Z)>, + phantom: PhantomData<(E, EM, I, S, Z)>, } -impl MutationalStage for TuneableMutationalStage +impl MutationalStage for TuneableMutationalStage where - E: UsesState, - EM: UsesState, - M: Mutator, + M: Mutator, Z: Evaluator, - Z::State: - HasCorpus + HasRand + HasNamedMetadata + HasMetadata + HasExecutions + HasCurrentTestcase, - I: MutatedTransform + Clone, - <::State as HasCorpus>::Corpus: Corpus, // delete me + S: HasCorpus + HasRand + HasNamedMetadata + HasMetadata + HasExecutions + HasCurrentTestcase, + I: MutatedTransform<::Input, S> + Clone, + ::Input: Input, +{ + type Mutator = M; + /// The mutator, added to this stage + #[inline] + fn mutator(&self) -> &Self::Mutator { + &self.mutator + } + + /// The list of mutators, added to this stage (as mutable ref) + #[inline] + fn mutator_mut(&mut self) -> &mut Self::Mutator { + &mut self.mutator + } + + /// Gets the number of iterations as a random number + fn iterations(&self, state: &mut S) -> Result { + Ok( + // fall back to random + 1 + state + .rand_mut() + .below(nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS)), + ) + } +} + +impl Stage for TuneableMutationalStage +where + M: Mutator, + Z: Evaluator, + S: HasCorpus + + HasRand + + HasNamedMetadata + + HasMetadata + + HasExecutions + + HasCurrentTestcase + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + I: MutatedTransform<::Input, S> + Clone, + ::Input: Input, +{ + #[inline] + #[allow(clippy::let_and_return)] + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut S, + manager: &mut EM, + ) -> Result<(), Error> { + let ret = self.perform_mutational(fuzzer, executor, state, manager); + + #[cfg(feature = "introspection")] + state.introspection_monitor_mut().finish_stage(); + + ret + } + + fn should_restart(&mut self, state: &mut S) -> Result { + self.restart_helper.should_restart(state, &self.name) + } + + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { + self.restart_helper.clear_progress(state, &self.name) + } +} + +impl TuneableMutationalStage +where + M: Mutator, + Z: Evaluator, + S: HasCorpus + + HasRand + + HasNamedMetadata + + HasExecutions + + HasMetadata + + HasCurrentTestcase + + MaybeHasClientPerfMonitor + + UsesInput::Input>, + I: MutatedTransform<::Input, S> + Clone, + ::Input: Input, { /// Runs this (mutational) stage for the given `testcase` /// Exactly the same functionality as [`MutationalStage::perform_mutational`], but with added timeout support. @@ -177,7 +255,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { let fuzz_time = self.seed_fuzz_time(state)?; @@ -233,105 +311,24 @@ where Ok(()) } - /// The mutator, added to this stage - #[inline] - fn mutator(&self) -> &M { - &self.mutator - } - - /// The list of mutators, added to this stage (as mutable ref) - #[inline] - fn mutator_mut(&mut self) -> &mut M { - &mut self.mutator - } - - /// Gets the number of iterations as a random number - fn iterations(&self, state: &mut Self::State) -> Result { - Ok( - // fall back to random - 1 + state - .rand_mut() - .below(nonzero!(DEFAULT_MUTATIONAL_MAX_ITERATIONS)), - ) - } -} - -impl UsesState for TuneableMutationalStage -where - Z: Evaluator, -{ - type State = Z::State; -} - -impl Stage for TuneableMutationalStage -where - E: UsesState, - EM: UsesState, - M: Mutator, - Z: Evaluator, - Z::State: - HasCorpus + HasRand + HasNamedMetadata + HasMetadata + HasExecutions + HasCurrentTestcase, - I: MutatedTransform + Clone, - <::State as HasCorpus>::Corpus: Corpus, // delete me -{ - #[inline] - #[allow(clippy::let_and_return)] - fn perform( - &mut self, - fuzzer: &mut Z, - executor: &mut E, - state: &mut Self::State, - manager: &mut EM, - ) -> Result<(), Error> { - let ret = self.perform_mutational(fuzzer, executor, state, manager); - - #[cfg(feature = "introspection")] - state.introspection_monitor_mut().finish_stage(); - - ret - } - - fn should_restart(&mut self, state: &mut Self::State) -> Result { - self.restart_helper.should_restart(state, &self.name) - } - - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { - self.restart_helper.clear_progress(state, &self.name) - } -} - -impl TuneableMutationalStage -where - E: UsesState::State>, - EM: UsesState::State>, - M: Mutator::State>, - Z: Evaluator, - ::State: - HasCorpus + HasRand + HasNamedMetadata + HasExecutions + HasMetadata + HasCurrentTestcase, - I: MutatedTransform::State> + Clone, - <::State as HasCorpus>::Corpus: Corpus, // delete me -{ - fn execs_since_progress_start( - &mut self, - state: &mut ::State, - ) -> Result { + fn execs_since_progress_start(&mut self, state: &mut S) -> Result { self.restart_helper .execs_since_progress_start(state, &self.name) } /// Creates a new default tuneable mutational stage #[must_use] - pub fn new(state: &mut ::State, mutator: M) -> Self { + pub fn new(state: &mut S, mutator: M) -> Self { Self::transforming(state, mutator, STD_TUNEABLE_MUTATIONAL_STAGE_NAME) } /// Crates a new tuneable mutational stage with the given name - pub fn with_name(state: &mut ::State, mutator: M, name: &str) -> Self { + pub fn with_name(state: &mut S, mutator: M, name: &str) -> Self { Self::transforming(state, mutator, name) } /// Set the number of iterations to be used by this [`TuneableMutationalStage`] - pub fn set_iters(&self, state: &mut S, iters: u64) -> Result<(), Error> + pub fn set_iters(&self, state: &mut S, iters: u64) -> Result<(), Error> where S: HasNamedMetadata, { @@ -339,12 +336,12 @@ where } /// Set the number of iterations to be used by the std [`TuneableMutationalStage`] - pub fn set_iters_std(state: &mut ::State, iters: u64) -> Result<(), Error> { + pub fn set_iters_std(state: &mut S, iters: u64) -> Result<(), Error> { set_iters_by_name(state, iters, STD_TUNEABLE_MUTATIONAL_STAGE_NAME) } /// Set the number of iterations to be used by the [`TuneableMutationalStage`] with the given name - pub fn set_iters_by_name(state: &mut S, iters: u64, name: &str) -> Result<(), Error> + pub fn set_iters_by_name(state: &mut S, iters: u64, name: &str) -> Result<(), Error> where S: HasNamedMetadata, { @@ -352,7 +349,7 @@ where } /// Get the set iterations for this [`TuneableMutationalStage`], if any - pub fn fixed_iters(&self, state: &S) -> Result, Error> + pub fn fixed_iters(&self, state: &S) -> Result, Error> where S: HasNamedMetadata, { @@ -360,12 +357,12 @@ where } /// Get the set iterations for the std [`TuneableMutationalStage`], if any - pub fn iters_std(state: &::State) -> Result, Error> { + pub fn iters_std(state: &S) -> Result, Error> { get_iters_by_name(state, STD_TUNEABLE_MUTATIONAL_STAGE_NAME) } /// Get the set iterations for the [`TuneableMutationalStage`] with the given name, if any - pub fn iters_by_name(state: &S, name: &str) -> Result, Error> + pub fn iters_by_name(state: &S, name: &str) -> Result, Error> where S: HasNamedMetadata, { @@ -373,7 +370,7 @@ where } /// Set the time to mutate a single input in this [`TuneableMutationalStage`] - pub fn set_seed_fuzz_time(&self, state: &mut S, fuzz_time: Duration) -> Result<(), Error> + pub fn set_seed_fuzz_time(&self, state: &mut S, fuzz_time: Duration) -> Result<(), Error> where S: HasNamedMetadata, { @@ -381,15 +378,12 @@ where } /// Set the time to mutate a single input in the std [`TuneableMutationalStage`] - pub fn set_seed_fuzz_time_std( - state: &mut ::State, - fuzz_time: Duration, - ) -> Result<(), Error> { + pub fn set_seed_fuzz_time_std(state: &mut S, fuzz_time: Duration) -> Result<(), Error> { set_seed_fuzz_time_by_name(state, fuzz_time, STD_TUNEABLE_MUTATIONAL_STAGE_NAME) } /// Set the time to mutate a single input in the [`TuneableMutationalStage`] with the given name - pub fn set_seed_fuzz_time_by_name( + pub fn set_seed_fuzz_time_by_name( state: &mut S, fuzz_time: Duration, name: &str, @@ -401,7 +395,7 @@ where } /// Set the time to mutate a single input in this [`TuneableMutationalStage`] - pub fn seed_fuzz_time(&self, state: &S) -> Result, Error> + pub fn seed_fuzz_time(&self, state: &S) -> Result, Error> where S: HasNamedMetadata, { @@ -409,19 +403,12 @@ where } /// Set the time to mutate a single input for the std [`TuneableMutationalStage`] - pub fn seed_fuzz_time_std( - &self, - state: &::State, - ) -> Result, Error> { + pub fn seed_fuzz_time_std(&self, state: &S) -> Result, Error> { get_seed_fuzz_time_by_name(state, STD_TUNEABLE_MUTATIONAL_STAGE_NAME) } /// Set the time to mutate a single input for the [`TuneableMutationalStage`] with a given name - pub fn seed_fuzz_time_by_name( - &self, - state: &S, - name: &str, - ) -> Result, Error> + pub fn seed_fuzz_time_by_name(&self, state: &S, name: &str) -> Result, Error> where S: HasNamedMetadata, { @@ -429,7 +416,7 @@ where } /// Reset this to a normal, randomized, stage with - pub fn reset(&self, state: &mut S) -> Result<(), Error> + pub fn reset(&self, state: &mut S) -> Result<(), Error> where S: HasNamedMetadata, { @@ -437,12 +424,12 @@ where } /// Reset the std stage to a normal, randomized, stage - pub fn reset_std(state: &mut ::State) -> Result<(), Error> { + pub fn reset_std(state: &mut S) -> Result<(), Error> { reset_by_name(state, STD_TUNEABLE_MUTATIONAL_STAGE_NAME) } /// Reset this to a normal, randomized, stage by name - pub fn reset_by_name(state: &mut S, name: &str) -> Result<(), Error> + pub fn reset_by_name(state: &mut S, name: &str) -> Result<(), Error> where S: HasNamedMetadata, { @@ -453,7 +440,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut ::State, + state: &mut S, manager: &mut EM, input: &I, ) -> Result<(), Error> { @@ -480,17 +467,15 @@ where } } -impl TuneableMutationalStage +impl TuneableMutationalStage where - E: UsesState::State>, - EM: UsesState::State>, M: Mutator, Z: Evaluator, - ::State: HasCorpus + HasRand + HasNamedMetadata, + S: HasCorpus + HasRand + HasNamedMetadata, { /// Creates a new transforming mutational stage #[must_use] - pub fn transforming(state: &mut ::State, mutator: M, name: &str) -> Self { + pub fn transforming(state: &mut S, mutator: M, name: &str) -> Self { let _ = state.named_metadata_or_insert_with(name, TuneableMutationalStageMetadata::default); Self { mutator, diff --git a/libafl/src/stages/unicode.rs b/libafl/src/stages/unicode.rs index dc636bce8d..873729230a 100644 --- a/libafl/src/stages/unicode.rs +++ b/libafl/src/stages/unicode.rs @@ -11,7 +11,7 @@ use crate::{ corpus::Corpus, inputs::{BytesInput, HasTargetBytes}, stages::Stage, - state::{HasCorpus, HasCurrentTestcase, State, UsesState}, + state::{HasCorpus, HasCurrentTestcase}, HasMetadata, }; @@ -109,39 +109,29 @@ impl UnicodeIdentificationStage { } } -impl UsesState for UnicodeIdentificationStage +impl Stage for UnicodeIdentificationStage where - S: State, -{ - type State = S; -} - -impl Stage for UnicodeIdentificationStage -where - S: HasCorpus + State + HasCurrentTestcase, + S: HasCorpus + HasCurrentTestcase, S::Corpus: Corpus, - E: UsesState, - EM: UsesState, - Z: UsesState, { fn perform( &mut self, _fuzzer: &mut Z, _executor: &mut E, - state: &mut Self::State, + state: &mut S, _manager: &mut EM, ) -> Result<(), Error> { UnicodeIdentificationStage::identify_unicode_in_current_testcase(state) } #[inline] - fn should_restart(&mut self, _state: &mut Self::State) -> Result { + fn should_restart(&mut self, _state: &mut S) -> Result { // Stage does not run the target. No reset helper needed. Ok(true) } #[inline] - fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> { // Stage does not run the target. No reset helper needed. Ok(()) } diff --git a/libafl/src/stages/verify_timeouts.rs b/libafl/src/stages/verify_timeouts.rs index 19749083c0..b6755dadff 100644 --- a/libafl/src/stages/verify_timeouts.rs +++ b/libafl/src/stages/verify_timeouts.rs @@ -15,7 +15,7 @@ use crate::{ inputs::{BytesInput, UsesInput}, observers::ObserversTuple, stages::Stage, - state::{HasCorpus, State, UsesState}, + state::{HasCorpus, UsesState}, Evaluator, HasMetadata, }; @@ -43,13 +43,6 @@ impl VerifyTimeoutsStage { } } -impl UsesState for VerifyTimeoutsStage -where - S: State, -{ - type State = S; -} - /// Timeouts that `VerifyTimeoutsStage` will read from #[derive(Default, Serialize, Deserialize, Clone, Debug)] #[serde(bound = "I: for<'a> Deserialize<'a> + Serialize")] @@ -88,21 +81,20 @@ impl TimeoutsToVerify { } } -impl Stage for VerifyTimeoutsStage +impl Stage for VerifyTimeoutsStage where - E::Observers: ObserversTuple<::Input, ::State>, + E::Observers: ObserversTuple<::Input, S>, E: Executor + HasObservers + HasTimeout, EM: UsesState, - Z: UsesState + Evaluator, - S: HasCorpus + State + HasMetadata, - Self::Input: Debug + Serialize + DeserializeOwned + Default + 'static + Clone, - <::State as HasCorpus>::Corpus: Corpus, //delete me + Z: Evaluator, + S: HasCorpus + HasMetadata + UsesInput::Input>, + ::Input: Debug + Serialize + DeserializeOwned + Default + 'static + Clone, { fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { let mut timeouts = state @@ -118,15 +110,17 @@ where } executor.set_timeout(self.original_timeout); *self.capture_timeouts.borrow_mut() = true; - let res = state.metadata_mut::>().unwrap(); - *res = TimeoutsToVerify::::new(); + let res = state + .metadata_mut::::Input>>() + .unwrap(); + *res = TimeoutsToVerify::<::Input>::new(); Ok(()) } - fn should_restart(&mut self, _state: &mut Self::State) -> Result { + fn should_restart(&mut self, _state: &mut S) -> Result { Ok(true) } - fn clear_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, _state: &mut S) -> Result<(), Error> { Ok(()) } } diff --git a/libafl_libfuzzer/runtime/src/lib.rs b/libafl_libfuzzer/runtime/src/lib.rs index 8e48992a24..36096e05df 100644 --- a/libafl_libfuzzer/runtime/src/lib.rs +++ b/libafl_libfuzzer/runtime/src/lib.rs @@ -356,7 +356,7 @@ macro_rules! fuzz_with { // TODO configure with mutation stacking options from libfuzzer let std_mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); - let std_power: StdPowerMutationalStage<_, _, BytesInput, _, _> = StdPowerMutationalStage::new(std_mutator); + let std_power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(std_mutator); let std_power = IfStage::new(|_, _, _, _| Ok(mutator_status.std_mutational.into()), (std_power, ())); // for custom mutator and crossover, each have access to the LLVMFuzzerMutate -- but it appears @@ -378,7 +378,7 @@ macro_rules! fuzz_with { // Safe to unwrap: stack pow is not 0. let std_mutator_no_mutate = StdScheduledMutator::with_max_stack_pow(havoc_crossover(),3); - let cm_power: StdPowerMutationalStage<_, _, BytesInput, _, _> = StdPowerMutationalStage::new(custom_mutator); + let cm_power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(custom_mutator); let cm_power = IfStage::new(|_, _, _, _| Ok(mutator_status.custom_mutation.into()), (cm_power, ())); let cm_std_power = StdMutationalStage::new(std_mutator_no_mutate); let cm_std_power = @@ -398,7 +398,7 @@ macro_rules! fuzz_with { let cc_power = StdMutationalStage::new(custom_crossover); let cc_power = IfStage::new(|_, _, _, _| Ok(mutator_status.custom_crossover.into()), (cc_power, ())); - let cc_std_power: StdPowerMutationalStage<_, _, BytesInput, _, _> = StdPowerMutationalStage::new(std_mutator_no_crossover); + let cc_std_power: StdPowerMutationalStage<_, _, BytesInput, _, _, _> = StdPowerMutationalStage::new(std_mutator_no_crossover); let cc_std_power = IfStage::new(|_, _, _, _| Ok(mutator_status.std_no_crossover.into()), (cc_std_power, ())); diff --git a/libafl_targets/src/cmps/stages/aflpptracing.rs b/libafl_targets/src/cmps/stages/aflpptracing.rs index d87600d56d..6749ea7feb 100644 --- a/libafl_targets/src/cmps/stages/aflpptracing.rs +++ b/libafl_targets/src/cmps/stages/aflpptracing.rs @@ -2,12 +2,12 @@ use alloc::borrow::{Cow, ToOwned}; use core::marker::PhantomData; use libafl::{ - corpus::Corpus, + corpus::{Corpus, HasCurrentCorpusId}, executors::{Executor, HasObservers}, inputs::{BytesInput, UsesInput}, observers::ObserversTuple, stages::{colorization::TaintMetadata, RetryCountRestartHelper, Stage}, - state::{HasCorpus, HasCurrentTestcase, HasExecutions, UsesState}, + state::{HasCorpus, HasCurrentTestcase, UsesState}, Error, HasMetadata, HasNamedMetadata, }; use libafl_bolts::{ @@ -19,56 +19,42 @@ use crate::cmps::observers::AFLppCmpLogObserver; /// Trace with tainted input #[derive(Clone, Debug)] -pub struct AFLppCmplogTracingStage<'a, EM, TE, Z> -where - TE: UsesState, -{ +pub struct AFLppCmplogTracingStage<'a, EM, TE, S, Z> { name: Cow<'static, str>, tracer_executor: TE, cmplog_observer_handle: Handle>, #[allow(clippy::type_complexity)] - phantom: PhantomData<(EM, TE, Z)>, + phantom: PhantomData<(EM, TE, S, Z)>, } /// The name for aflpp tracing stage pub static AFLPP_CMPLOG_TRACING_STAGE_NAME: &str = "aflpptracing"; -impl UsesState for AFLppCmplogTracingStage<'_, EM, TE, Z> -where - TE: UsesState, -{ - type State = TE::State; -} - -impl Named for AFLppCmplogTracingStage<'_, EM, TE, Z> -where - TE: UsesState, -{ +impl Named for AFLppCmplogTracingStage<'_, EM, TE, S, Z> { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl Stage for AFLppCmplogTracingStage<'_, EM, TE, Z> +impl Stage for AFLppCmplogTracingStage<'_, EM, TE, S, Z> where - E: UsesState, - TE: Executor + HasObservers, - TE::State: HasExecutions - + HasCorpus - + HasMetadata + EM: UsesState, + Z: UsesState, + TE: HasObservers + Executor, + TE::Observers: MatchNameRef + ObserversTuple, + S: HasCorpus + + HasCurrentTestcase + UsesInput + + HasMetadata + HasNamedMetadata - + HasCurrentTestcase, - TE::Observers: MatchNameRef + ObserversTuple, - EM: UsesState, - Z: UsesState, - ::Corpus: Corpus, //delete me + + HasCurrentCorpusId, + S::Corpus: Corpus, { #[inline] fn perform( &mut self, fuzzer: &mut Z, _executor: &mut E, - state: &mut TE::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { // First run with the un-mutated input @@ -131,22 +117,19 @@ where Ok(()) } - fn should_restart(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut S) -> Result { // Tracing stage is always deterministic // don't restart RetryCountRestartHelper::no_retry(state, &self.name) } - fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut S) -> Result<(), Error> { // TODO: this may need better resumption? (Or is it always used with a forkserver?) RetryCountRestartHelper::clear_progress(state, &self.name) } } -impl<'a, EM, TE, Z> AFLppCmplogTracingStage<'a, EM, TE, Z> -where - TE: UsesState, -{ +impl<'a, EM, TE, S, Z> AFLppCmplogTracingStage<'a, EM, TE, S, Z> { /// With cmplog observer pub fn new(tracer_executor: TE, observer_handle: Handle>) -> Self { let observer_name = observer_handle.name().clone(); From c842edaaea95e2b170205eb1608a5f49432ae36d Mon Sep 17 00:00:00 2001 From: "Dongjia \"toka\" Zhang" Date: Thu, 12 Dec 2024 19:20:05 +0100 Subject: [PATCH 07/34] Update CONTRIBUTING.md MIGRATION.md (#2762) --- CONTRIBUTING.md | 2 +- MIGRATION.md | 6 ++++-- libafl/Cargo.toml | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac78e7d8a4..352e5bd302 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ Once the package is installed, simply run `pre-commit install` to enable the hoo Before making your pull requests, try to see if your code follows these rules. - Wherever possible, use `Cow<'static, str>` instead of String. -- `PhantomData` should have the smallest set of types needed. +- `PhantomData` should have the smallest set of types needed. Try not adding `PhantomData` to your struct unless it is really necessary. Also even when you really need `PhantomData`, try to keep the types `T` used in `PhantomData` as smallest as possible - Wherever possible, trait implementations with lifetime specifiers should use '_ lifetime elision. - Complex constructors should be replaced with `typed_builder`, or write code in the builder pattern for yourself. - Remove generic restrictions at the definitions (e.g., we do not need to specify that types impl `Serialize`, `Deserialize`, or `Debug` anymore at the struct definitions). Therefore, try avoiding code like this unless the contraint is really necessary. diff --git a/MIGRATION.md b/MIGRATION.md index e140eab2e7..392bc577ba 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -5,6 +5,8 @@ - Removed `with_observers` from `Executor` trait. - `MmapShMemProvider::new_shmem_persistent` has been removed in favour of `MmapShMem::persist`. You probably want to do something like this: `let shmem = MmapShMemProvider::new()?.new_shmem(size)?.persist()?;` -# 0.14.1 -> 0.14.2 +# 0.14.1 -> 0.15.0 - `MmapShMem::new` and `MmapShMemProvider::new_shmem_with_id` now take `AsRef` instead of a byte array for the filename/id. -- The closure passed to a `DumpToDiskStage` now provides the `Testcase` instead of just the `Input`. \ No newline at end of file +- The closure passed to a `DumpToDiskStage` now provides the `Testcase` instead of just the `Input`. +- `StatsStage` is deleted, and it is superceded by `AflStatsStage` +- \ No newline at end of file diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index d21cc90264..2592a2c7c1 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -28,7 +28,6 @@ rustc-args = ["--cfg", "docsrs"] [features] default = [ - "introspection", "std", "derive", "llmp_compression", From 31d9b5641debe3348ca74fb1b40a50e87dee4fb7 Mon Sep 17 00:00:00 2001 From: "Dongjia \"toka\" Zhang" Date: Thu, 12 Dec 2024 20:40:28 +0100 Subject: [PATCH 08/34] No Uses* from `fuzzer` (#2761) * go * fixing stuf * hello from windows * more * lolg * lolf * fix * a --------- Co-authored-by: Your Name --- .../baby_fuzzer_custom_executor/src/main.rs | 3 +- .../binary_only/qemu_launcher/src/instance.rs | 5 +- .../forkserver/libafl-fuzz/src/executor.rs | 1 - fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs | 16 +- libafl/src/corpus/minimizer.rs | 2 +- libafl/src/events/centralized.rs | 110 ++-- libafl/src/events/llmp/mgr.rs | 34 +- libafl/src/events/llmp/mod.rs | 15 +- libafl/src/events/llmp/restarting.rs | 89 ++-- libafl/src/events/tcp.rs | 84 ++-- libafl/src/executors/combined.rs | 2 - libafl/src/executors/command.rs | 3 - libafl/src/executors/differential.rs | 1 - libafl/src/executors/forkserver.rs | 1 - libafl/src/executors/hooks/inprocess.rs | 6 +- libafl/src/executors/hooks/unix.rs | 6 +- libafl/src/executors/hooks/windows.rs | 8 +- libafl/src/executors/inprocess/inner.rs | 6 +- libafl/src/executors/inprocess/mod.rs | 31 +- libafl/src/executors/inprocess/stateful.rs | 13 +- libafl/src/executors/inprocess_fork/inner.rs | 1 - libafl/src/executors/inprocess_fork/mod.rs | 9 +- .../src/executors/inprocess_fork/stateful.rs | 2 +- libafl/src/executors/mod.rs | 2 - libafl/src/executors/shadow.rs | 1 - libafl/src/executors/with_observers.rs | 1 - libafl/src/fuzzer/mod.rs | 472 ++++++++---------- libafl/src/stages/afl_stats.rs | 4 +- libafl/src/stages/calibrate.rs | 2 +- libafl/src/stages/colorization.rs | 2 - libafl/src/stages/concolic.rs | 11 +- libafl/src/stages/generalization.rs | 2 - libafl/src/stages/generation.rs | 2 +- libafl/src/stages/mutational.rs | 8 +- libafl/src/stages/power.rs | 4 +- libafl/src/stages/push/mod.rs | 8 +- libafl/src/stages/push/mutational.rs | 9 +- libafl/src/stages/sync.rs | 20 +- libafl/src/stages/tmin.rs | 18 +- libafl/src/stages/tracing.rs | 10 +- libafl/src/stages/tuneable.rs | 10 +- libafl/src/stages/verify_timeouts.rs | 2 +- libafl/src/state/mod.rs | 24 +- libafl_frida/src/executor.rs | 1 - libafl_libfuzzer/runtime/src/fuzz.rs | 2 +- libafl_libfuzzer/runtime/src/report.rs | 2 +- libafl_libfuzzer/runtime/src/tmin.rs | 2 +- libafl_nyx/src/executor.rs | 1 - libafl_qemu/src/executor.rs | 18 +- .../src/cmps/stages/aflpptracing.rs | 1 - libafl_targets/src/windows_asan.rs | 2 +- libafl_tinyinst/src/executor.rs | 1 - 52 files changed, 495 insertions(+), 595 deletions(-) diff --git a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs index 003839f84d..f9c0b2e9ee 100644 --- a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs +++ b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs @@ -53,13 +53,12 @@ impl Executor for CustomExecutor where EM: UsesState, S: State + HasExecutions, - Z: UsesState, Self::Input: HasTargetBytes, { fn run_target( &mut self, _fuzzer: &mut Z, - state: &mut Self::State, + state: &mut S, _mgr: &mut EM, input: &Self::Input, ) -> Result { diff --git a/fuzzers/binary_only/qemu_launcher/src/instance.rs b/fuzzers/binary_only/qemu_launcher/src/instance.rs index 5693c8ae2b..317d21a36f 100644 --- a/fuzzers/binary_only/qemu_launcher/src/instance.rs +++ b/fuzzers/binary_only/qemu_launcher/src/instance.rs @@ -312,9 +312,8 @@ impl Instance<'_, M> { stages: &mut ST, ) -> Result<(), Error> where - Z: Fuzzer, ST> - + UsesState - + Evaluator, State = ClientState>, + Z: Fuzzer, ClientState, ST> + + Evaluator, BytesInput, ClientState>, E: UsesState, ST: StagesTuple, ClientState, Z>, { diff --git a/fuzzers/forkserver/libafl-fuzz/src/executor.rs b/fuzzers/forkserver/libafl-fuzz/src/executor.rs index 42db166408..06082e8a73 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/executor.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/executor.rs @@ -357,7 +357,6 @@ pub enum SupportedExecutors { impl Executor for SupportedExecutors where S: State, - Z: UsesState, EM: UsesState, FSV: Executor, { diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 24f4178083..6f7895968d 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -22,7 +22,7 @@ use libafl::{ CaptureTimeoutFeedback, ConstFeedback, CrashFeedback, MaxMapFeedback, TimeFeedback, }, fuzzer::StdFuzzer, - inputs::{BytesInput, NopTargetBytesConverter}, + inputs::{BytesInput, NopTargetBytesConverter, UsesInput}, mutators::{havoc_mutations, tokens_mutations, AFLppRedQueen, StdScheduledMutator, Tokens}, observers::{CanTrack, HitcountsMapObserver, StdMapObserver, TimeObserver}, schedulers::{ @@ -38,7 +38,6 @@ use libafl::{ }, state::{ HasCorpus, HasCurrentTestcase, HasExecutions, HasLastReportTime, HasStartTime, StdState, - UsesState, }, Error, Fuzzer, HasFeedback, HasMetadata, SerdeAny, }; @@ -649,20 +648,19 @@ pub fn fuzzer_target_mode(opt: &Opt) -> Cow<'static, str> { #[derive(Debug, Serialize, Deserialize, SerdeAny)] pub struct IsInitialCorpusEntryMetadata {} -pub fn run_fuzzer_with_stages( +pub fn run_fuzzer_with_stages( opt: &Opt, fuzzer: &mut Z, stages: &mut ST, executor: &mut E, - state: &mut ::State, + state: &mut S, mgr: &mut EM, ) -> Result<(), Error> where - Z: Fuzzer, - E: UsesState, - EM: ProgressReporter, - ST: StagesTuple, - ::State: HasLastReportTime + HasExecutions + HasMetadata, + Z: Fuzzer, + EM: ProgressReporter, + ST: StagesTuple, + S: HasLastReportTime + HasExecutions + HasMetadata + UsesInput, { if opt.bench_just_one { fuzzer.fuzz_loop_for(stages, executor, state, mgr, 1)?; diff --git a/libafl/src/corpus/minimizer.rs b/libafl/src/corpus/minimizer.rs index 84c6be3875..c6599ca75d 100644 --- a/libafl/src/corpus/minimizer.rs +++ b/libafl/src/corpus/minimizer.rs @@ -77,7 +77,7 @@ where E::Observers: ObserversTuple, CS: Scheduler + RemovableScheduler, EM: EventFirer, - Z: HasScheduler, + Z: HasScheduler, { // don't delete this else it won't work after restart let current = *state.corpus().current(); diff --git a/libafl/src/events/centralized.rs b/libafl/src/events/centralized.rs index 515c8ec5b9..e0b1a441f5 100644 --- a/libafl/src/events/centralized.rs +++ b/libafl/src/events/centralized.rs @@ -30,6 +30,7 @@ use crate::events::llmp::COMPRESS_THRESHOLD; #[cfg(feature = "scalability_introspection")] use crate::state::HasScalabilityMonitor; use crate::{ + corpus::Corpus, events::{ AdaptiveSerializer, CustomBufEventResult, Event, EventConfig, EventFirer, EventManager, EventManagerHooksTuple, EventManagerId, EventProcessor, EventRestarter, @@ -39,7 +40,7 @@ use crate::{ fuzzer::{EvaluatorObservers, ExecutionProcessor}, inputs::{Input, NopInput, UsesInput}, observers::{ObserversTuple, TimeObserver}, - state::{HasExecutions, HasLastReportTime, NopState, State, Stoppable, UsesState}, + state::{HasCorpus, HasExecutions, HasLastReportTime, NopState, State, Stoppable, UsesState}, Error, HasMetadata, }; @@ -49,8 +50,8 @@ pub(crate) const _LLMP_TAG_TO_MAIN: Tag = Tag(0x3453453); #[derive(Debug)] pub struct CentralizedEventManager where - EM: UsesState, - EMH: EventManagerHooksTuple, + EM: UsesState, + EMH: EventManagerHooksTuple, S: State, SP: ShMemProvider, { @@ -114,8 +115,8 @@ impl CentralizedEventManagerBuilder { time_obs: Option>, ) -> Result, Error> where - EM: UsesState, - EMH: EventManagerHooksTuple, + EM: UsesState, + EMH: EventManagerHooksTuple, S: State, SP: ShMemProvider, { @@ -135,7 +136,6 @@ impl CentralizedEventManagerBuilder { /// /// If the port is not yet bound, it will act as a broker; otherwise, it /// will act as a client. - #[cfg(feature = "std")] pub fn build_on_port( self, inner: EM, @@ -145,8 +145,8 @@ impl CentralizedEventManagerBuilder { time_obs: Option>, ) -> Result, Error> where - EM: UsesState, - EMH: EventManagerHooksTuple, + EM: UsesState, + EMH: EventManagerHooksTuple, S: State, SP: ShMemProvider, { @@ -165,7 +165,6 @@ impl CentralizedEventManagerBuilder { /// If a client respawns, it may reuse the existing connection, previously /// stored by [`LlmpClient::to_env()`]. - #[cfg(feature = "std")] pub fn build_existing_client_from_env( self, inner: EM, @@ -175,8 +174,8 @@ impl CentralizedEventManagerBuilder { time_obs: Option>, ) -> Result, Error> where - EM: UsesState, - EMH: EventManagerHooksTuple, + EM: UsesState, + EMH: EventManagerHooksTuple, S: State, SP: ShMemProvider, { @@ -193,7 +192,6 @@ impl CentralizedEventManagerBuilder { } /// Create an existing client from description - #[cfg(feature = "std")] pub fn existing_client_from_description( self, inner: EM, @@ -203,8 +201,8 @@ impl CentralizedEventManagerBuilder { time_obs: Option>, ) -> Result, Error> where - EM: UsesState, - EMH: EventManagerHooksTuple, + EM: UsesState, + EMH: EventManagerHooksTuple, S: State, SP: ShMemProvider, { @@ -222,8 +220,8 @@ impl CentralizedEventManagerBuilder { } impl UsesState for CentralizedEventManager where - EM: UsesState, - EMH: EventManagerHooksTuple, + EM: UsesState, + EMH: EventManagerHooksTuple, S: State, SP: ShMemProvider, { @@ -232,8 +230,8 @@ where impl AdaptiveSerializer for CentralizedEventManager where - EM: AdaptiveSerializer + UsesState, - EMH: EventManagerHooksTuple, + EM: AdaptiveSerializer + UsesState, + EMH: EventManagerHooksTuple, S: State, SP: ShMemProvider, { @@ -270,9 +268,10 @@ where impl EventFirer for CentralizedEventManager where - EM: AdaptiveSerializer + EventFirer + HasEventManagerId, - EMH: EventManagerHooksTuple, - S: State, + EM: AdaptiveSerializer + EventFirer + HasEventManagerId, + EMH: EventManagerHooksTuple, + S: State + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, { fn should_send(&self) -> bool { @@ -342,8 +341,8 @@ where impl EventRestarter for CentralizedEventManager where - EM: EventRestarter, - EMH: EventManagerHooksTuple, + EM: EventRestarter, + EMH: EventManagerHooksTuple, S: State, SP: ShMemProvider, { @@ -368,17 +367,18 @@ where impl EventProcessor for CentralizedEventManager where - EM: AdaptiveSerializer + EventProcessor + EventFirer + HasEventManagerId, - EMH: EventManagerHooksTuple, + EM: AdaptiveSerializer + EventProcessor + EventFirer + HasEventManagerId, + EMH: EventManagerHooksTuple, E: HasObservers + Executor, E::Observers: ObserversTuple<::Input, ::State> + Serialize, for<'a> E::Observers: Deserialize<'a>, - S: State, + S: State + HasCorpus, + S::Corpus: Corpus, Self::State: HasExecutions + HasMetadata, SP: ShMemProvider, - Z: EvaluatorObservers - + ExecutionProcessor, + Z: EvaluatorObservers::Input, S> + + ExecutionProcessor::Input, E::Observers, S>, { fn process( &mut self, @@ -408,20 +408,21 @@ where E::Observers: ObserversTuple<::Input, ::State> + Serialize, for<'a> E::Observers: Deserialize<'a>, - EM: AdaptiveSerializer + EventManager, + EM: AdaptiveSerializer + EventManager, EM::State: HasExecutions + HasMetadata + HasLastReportTime, - EMH: EventManagerHooksTuple, - S: State, + EMH: EventManagerHooksTuple, + S: State + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, - Z: EvaluatorObservers - + ExecutionProcessor, + Z: EvaluatorObservers::Input, S> + + ExecutionProcessor::Input, E::Observers, S>, { } impl HasCustomBufHandlers for CentralizedEventManager where - EM: HasCustomBufHandlers, - EMH: EventManagerHooksTuple, + EM: HasCustomBufHandlers, + EMH: EventManagerHooksTuple, S: State, SP: ShMemProvider, { @@ -438,19 +439,21 @@ where impl ProgressReporter for CentralizedEventManager where - EM: AdaptiveSerializer + ProgressReporter + HasEventManagerId, + EM: AdaptiveSerializer + ProgressReporter + HasEventManagerId, EM::State: HasMetadata + HasExecutions + HasLastReportTime, - EMH: EventManagerHooksTuple, - S: State, + EMH: EventManagerHooksTuple, + S: State + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, { } impl HasEventManagerId for CentralizedEventManager where - EM: HasEventManagerId + UsesState, - EMH: EventManagerHooksTuple, - S: State, + EM: HasEventManagerId + UsesState, + EMH: EventManagerHooksTuple, + S: State + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, { fn mgr_id(&self) -> EventManagerId { @@ -460,9 +463,10 @@ where impl CentralizedEventManager where - EM: UsesState, - EMH: EventManagerHooksTuple, - S: State, + EM: UsesState, + EMH: EventManagerHooksTuple, + S: State + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, { /// Describe the client event manager's LLMP parts in a restorable fashion @@ -472,7 +476,6 @@ where /// Write the config for a client [`EventManager`] to env vars, a new /// client can reattach using [`CentralizedEventManagerBuilder::build_existing_client_from_env()`]. - #[cfg(feature = "std")] pub fn to_env(&self, env_name: &str) { self.client.to_env(env_name).unwrap(); } @@ -485,9 +488,10 @@ where impl CentralizedEventManager where - EM: UsesState + EventFirer + AdaptiveSerializer + HasEventManagerId, - EMH: EventManagerHooksTuple, - S: State + Stoppable, + EM: UsesState + EventFirer + AdaptiveSerializer + HasEventManagerId, + EMH: EventManagerHooksTuple, + S: State + Stoppable + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, { #[cfg(feature = "llmp_compression")] @@ -535,8 +539,8 @@ where ObserversTuple<::Input, ::State> + Serialize, ::State: UsesInput + HasExecutions + HasMetadata, for<'a> E::Observers: Deserialize<'a>, - Z: ExecutionProcessor::State> - + EvaluatorObservers, + Z: EvaluatorObservers::Input, S> + + ExecutionProcessor::Input, E::Observers, S>, { // TODO: Get around local event copy by moving handle_in_client let self_id = self.client.sender().id(); @@ -585,8 +589,8 @@ where ObserversTuple<::Input, ::State> + Serialize, ::State: UsesInput + HasExecutions + HasMetadata, for<'a> E::Observers: Deserialize<'a> + Serialize, - Z: ExecutionProcessor::State> - + EvaluatorObservers, + Z: EvaluatorObservers::Input, S> + + ExecutionProcessor::Input, E::Observers, S>, { log::debug!("handle_in_main!"); @@ -640,7 +644,7 @@ where process::id(), event_name ); - fuzzer.evaluate_input_with_observers::( + fuzzer.evaluate_input_with_observers( state, executor, self, diff --git a/libafl/src/events/llmp/mgr.rs b/libafl/src/events/llmp/mgr.rs index 1153c30610..687d7e10d4 100644 --- a/libafl/src/events/llmp/mgr.rs +++ b/libafl/src/events/llmp/mgr.rs @@ -30,6 +30,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "llmp_compression")] use crate::events::llmp::COMPRESS_THRESHOLD; use crate::{ + corpus::Corpus, events::{ llmp::{LLMP_TAG_EVENT_TO_BOTH, _LLMP_TAG_EVENT_TO_BROKER}, AdaptiveSerializer, CustomBufEventResult, CustomBufHandlerFn, Event, EventConfig, @@ -40,7 +41,7 @@ use crate::{ fuzzer::{Evaluator, EvaluatorObservers, ExecutionProcessor}, inputs::{NopInput, UsesInput}, observers::{ObserversTuple, TimeObserver}, - state::{HasExecutions, HasImported, HasLastReportTime, NopState, State, UsesState}, + state::{HasCorpus, HasExecutions, HasImported, HasLastReportTime, NopState, State, UsesState}, Error, HasMetadata, }; @@ -387,7 +388,8 @@ where impl LlmpEventManager where EMH: EventManagerHooksTuple, - S: State + HasExecutions + HasMetadata + HasImported, + S: State + HasExecutions + HasMetadata + HasImported + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, { // Handle arriving events in the client @@ -404,9 +406,9 @@ where E: Executor + HasObservers, E::Observers: ObserversTuple + Serialize, for<'a> E::Observers: Deserialize<'a>, - Z: ExecutionProcessor - + EvaluatorObservers - + Evaluator, + Z: ExecutionProcessor::Input, E::Observers, S> + + EvaluatorObservers::Input, S> + + Evaluator::Input, S>, { if !self.hooks.pre_exec_all(state, client_id, &event)? { return Ok(()); @@ -449,9 +451,7 @@ where { state.scalability_monitor_mut().testcase_without_observers += 1; } - fuzzer.evaluate_input_with_observers::( - state, executor, self, input, false, - )? + fuzzer.evaluate_input_with_observers(state, executor, self, input, false)? }; if let Some(item) = res.1 { *state.imported_mut() += 1; @@ -585,14 +585,15 @@ where impl EventProcessor for LlmpEventManager where EMH: EventManagerHooksTuple, - S: State + HasExecutions + HasMetadata + HasImported, + S: State + HasExecutions + HasMetadata + HasImported + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, E: HasObservers + Executor, E::Observers: ObserversTuple + Serialize, for<'a> E::Observers: Deserialize<'a>, - Z: ExecutionProcessor - + EvaluatorObservers - + Evaluator, + Z: ExecutionProcessor::Input, E::Observers, S> + + EvaluatorObservers::Input, S> + + Evaluator::Input, S>, { fn process( &mut self, @@ -649,11 +650,12 @@ where E::Observers: ObserversTuple + Serialize, for<'a> E::Observers: Deserialize<'a>, EMH: EventManagerHooksTuple, - S: State + HasExecutions + HasMetadata + HasLastReportTime + HasImported, + S: State + HasExecutions + HasMetadata + HasLastReportTime + HasImported + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, - Z: ExecutionProcessor - + EvaluatorObservers - + Evaluator, + Z: ExecutionProcessor::Input, E::Observers, S> + + EvaluatorObservers::Input, S> + + Evaluator::Input, S>, { } diff --git a/libafl/src/events/llmp/mod.rs b/libafl/src/events/llmp/mod.rs index bdbe32f4ba..0175d33c6f 100644 --- a/libafl/src/events/llmp/mod.rs +++ b/libafl/src/events/llmp/mod.rs @@ -16,11 +16,12 @@ use libafl_bolts::{ use serde::Deserialize; use crate::{ + corpus::Corpus, events::{CustomBufEventResult, CustomBufHandlerFn, Event, EventFirer}, executors::{Executor, HasObservers}, fuzzer::{EvaluatorObservers, ExecutionProcessor}, inputs::{Input, InputConverter, NopInput, NopInputConverter, UsesInput}, - state::{HasExecutions, NopState, State, Stoppable, UsesState}, + state::{HasCorpus, HasExecutions, NopState, State, Stoppable, UsesState}, Error, HasMetadata, }; @@ -253,7 +254,7 @@ where impl LlmpEventConverter where - S: UsesInput + HasExecutions + HasMetadata + Stoppable, + S: UsesInput + HasExecutions + HasMetadata + Stoppable + HasCorpus, SP: ShMemProvider, IC: InputConverter, ICB: InputConverter, @@ -295,8 +296,10 @@ where where E: Executor + HasObservers, EM: UsesState + EventFirer, + S::Corpus: Corpus, for<'a> E::Observers: Deserialize<'a>, - Z: ExecutionProcessor + EvaluatorObservers, + Z: ExecutionProcessor::Input, E::Observers, S> + + EvaluatorObservers::Input, S>, { match event { Event::NewTestcase { @@ -308,7 +311,7 @@ where return Ok(()); }; - let res = fuzzer.evaluate_input_with_observers::( + let res = fuzzer.evaluate_input_with_observers( state, executor, manager, @@ -349,8 +352,10 @@ where where E: Executor + HasObservers, EM: UsesState + EventFirer, + S::Corpus: Corpus, for<'a> E::Observers: Deserialize<'a>, - Z: ExecutionProcessor + EvaluatorObservers, + Z: ExecutionProcessor::Input, E::Observers, S> + + EvaluatorObservers::Input, S>, { // TODO: Get around local event copy by moving handle_in_client let self_id = self.llmp.sender().id(); diff --git a/libafl/src/events/llmp/restarting.rs b/libafl/src/events/llmp/restarting.rs index c76d89497d..731c9e932d 100644 --- a/libafl/src/events/llmp/restarting.rs +++ b/libafl/src/events/llmp/restarting.rs @@ -4,56 +4,51 @@ //! restart/refork it. use alloc::{boxed::Box, vec::Vec}; -#[cfg(feature = "std")] -use core::sync::atomic::{compiler_fence, Ordering}; -#[cfg(feature = "std")] -use core::time::Duration; -use core::{marker::PhantomData, num::NonZeroUsize}; -#[cfg(feature = "std")] +use core::{ + marker::PhantomData, + num::NonZeroUsize, + sync::atomic::{compiler_fence, Ordering}, + time::Duration, +}; use std::net::SocketAddr; -#[cfg(feature = "std")] -use libafl_bolts::core_affinity::CoreId; -#[cfg(all(feature = "std", any(windows, not(feature = "fork"))))] +#[cfg(any(windows, not(feature = "fork")))] use libafl_bolts::os::startable_self; -#[cfg(all(unix, feature = "std", not(miri)))] +#[cfg(all(unix, not(miri)))] use libafl_bolts::os::unix_signals::setup_signal_handler; -#[cfg(all(feature = "std", feature = "fork", unix))] +#[cfg(all(feature = "fork", unix))] use libafl_bolts::os::{fork, ForkResult}; -#[cfg(feature = "std")] -use libafl_bolts::{ - llmp::LlmpConnection, os::CTRL_C_EXIT, shmem::StdShMemProvider, staterestore::StateRestorer, -}; use libafl_bolts::{ - llmp::{Broker, LlmpBroker}, - shmem::ShMemProvider, + core_affinity::CoreId, + llmp::{Broker, LlmpBroker, LlmpConnection}, + os::CTRL_C_EXIT, + shmem::{ShMemProvider, StdShMemProvider}, + staterestore::StateRestorer, tuples::{tuple_list, Handle}, }; use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] use typed_builder::TypedBuilder; -#[cfg(all(unix, feature = "std", not(miri)))] +#[cfg(all(unix, not(miri)))] use crate::events::EVENTMGR_SIGHANDLER_STATE; -#[cfg(feature = "std")] -use crate::events::{AdaptiveSerializer, CustomBufEventResult, HasCustomBufHandlers}; use crate::{ + corpus::Corpus, events::{ - launcher::ClientDescription, Event, EventConfig, EventFirer, EventManager, - EventManagerHooksTuple, EventManagerId, EventProcessor, EventRestarter, HasEventManagerId, - LlmpEventManager, LlmpShouldSaveState, ProgressReporter, StdLlmpEventHook, + launcher::ClientDescription, AdaptiveSerializer, CustomBufEventResult, Event, EventConfig, + EventFirer, EventManager, EventManagerHooksTuple, EventManagerId, EventProcessor, + EventRestarter, HasCustomBufHandlers, HasEventManagerId, LlmpEventManager, + LlmpShouldSaveState, ProgressReporter, StdLlmpEventHook, }, executors::{Executor, HasObservers}, fuzzer::{Evaluator, EvaluatorObservers, ExecutionProcessor}, inputs::UsesInput, monitors::Monitor, observers::{ObserversTuple, TimeObserver}, - state::{HasExecutions, HasImported, HasLastReportTime, State, UsesState}, + state::{HasCorpus, HasExecutions, HasImported, HasLastReportTime, State, UsesState}, Error, HasMetadata, }; /// A manager that can restart on the fly, storing states in-between (in `on_restart`) -#[cfg(feature = "std")] #[derive(Debug)] pub struct LlmpRestartingEventManager where @@ -69,7 +64,6 @@ where save_state: LlmpShouldSaveState, } -#[cfg(feature = "std")] impl AdaptiveSerializer for LlmpRestartingEventManager where SP: ShMemProvider, @@ -106,7 +100,6 @@ where } } -#[cfg(feature = "std")] impl UsesState for LlmpRestartingEventManager where S: State, @@ -115,7 +108,6 @@ where type State = S; } -#[cfg(feature = "std")] impl ProgressReporter for LlmpRestartingEventManager where S: State + HasExecutions + HasMetadata + HasLastReportTime, @@ -123,7 +115,6 @@ where { } -#[cfg(feature = "std")] impl EventFirer for LlmpRestartingEventManager where SP: ShMemProvider, @@ -157,7 +148,6 @@ where } } -#[cfg(feature = "std")] impl EventRestarter for LlmpRestartingEventManager where S: State + HasExecutions, @@ -199,18 +189,22 @@ where } } -#[cfg(feature = "std")] impl EventProcessor for LlmpRestartingEventManager where E: HasObservers + Executor, Z, State = S>, E::Observers: ObserversTuple + Serialize, for<'a> E::Observers: Deserialize<'a>, EMH: EventManagerHooksTuple, - S: State + HasExecutions + HasMetadata + HasImported, + S: State + HasExecutions + HasMetadata + HasImported + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, - Z: ExecutionProcessor, E::Observers, State = S> - + EvaluatorObservers, E::Observers> - + Evaluator>, + Z: ExecutionProcessor< + LlmpEventManager, + ::Input, + E::Observers, + S, + > + EvaluatorObservers, ::Input, S> + + Evaluator, ::Input, S>, { fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result { let res = self.llmp_mgr.process(fuzzer, state, executor)?; @@ -223,22 +217,25 @@ where } } -#[cfg(feature = "std")] impl EventManager for LlmpRestartingEventManager where E: HasObservers + Executor, Z, State = S>, E::Observers: ObserversTuple + Serialize, for<'a> E::Observers: Deserialize<'a>, EMH: EventManagerHooksTuple, - S: State + HasExecutions + HasMetadata + HasLastReportTime + HasImported, + S: State + HasExecutions + HasMetadata + HasLastReportTime + HasImported + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider, - Z: ExecutionProcessor, E::Observers, State = S> - + EvaluatorObservers, E::Observers> - + Evaluator>, + Z: ExecutionProcessor< + LlmpEventManager, + ::Input, + E::Observers, + S, + > + EvaluatorObservers, ::Input, S> + + Evaluator, ::Input, S>, { } -#[cfg(feature = "std")] impl HasEventManagerId for LlmpRestartingEventManager where S: State, @@ -249,7 +246,6 @@ where } } -#[cfg(feature = "std")] impl HasCustomBufHandlers for LlmpRestartingEventManager where S: State, @@ -269,7 +265,6 @@ const _ENV_FUZZER_RECEIVER: &str = "_AFL_ENV_FUZZER_RECEIVER"; /// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages) const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = "_AFL_ENV_FUZZER_BROKER_CLIENT"; -#[cfg(feature = "std")] impl LlmpRestartingEventManager where S: State, @@ -321,7 +316,6 @@ where } /// The kind of manager we're creating right now -#[cfg(feature = "std")] #[derive(Debug, Clone)] pub enum ManagerKind { /// Any kind will do @@ -339,7 +333,6 @@ pub enum ManagerKind { /// /// The restarting mgr is a combination of restarter and runner, that can be used on systems with and without `fork` support. /// The restarter will spawn a new process each time the child crashes or timeouts. -#[cfg(feature = "std")] #[allow(clippy::type_complexity)] pub fn setup_restarting_mgr_std( monitor: MT, @@ -371,7 +364,6 @@ where /// The restarting mgr is a combination of restarter and runner, that can be used on systems with and without `fork` support. /// The restarter will spawn a new process each time the child crashes or timeouts. /// This one, additionally uses the timeobserver for the adaptive serialization -#[cfg(feature = "std")] #[allow(clippy::type_complexity)] pub fn setup_restarting_mgr_std_adaptive( monitor: MT, @@ -405,7 +397,6 @@ where /// The [`RestartingMgr`] is is a combination of a /// `restarter` and `runner`, that can be used on systems both with and without `fork` support. The /// `restarter` will start a new process each time the child crashes or times out. -#[cfg(feature = "std")] #[allow(clippy::default_trait_access, clippy::ignored_unit_patterns)] #[derive(TypedBuilder, Debug)] pub struct RestartingMgr { @@ -448,7 +439,6 @@ pub struct RestartingMgr { phantom_data: PhantomData<(EMH, S)>, } -#[cfg(feature = "std")] #[allow(clippy::type_complexity, clippy::too_many_lines)] impl RestartingMgr where @@ -719,7 +709,6 @@ where } #[cfg(test)] -#[cfg(feature = "std")] mod tests { use core::sync::atomic::{compiler_fence, Ordering}; diff --git a/libafl/src/events/tcp.rs b/libafl/src/events/tcp.rs index 977ad9de85..5b4ac815db 100644 --- a/libafl/src/events/tcp.rs +++ b/libafl/src/events/tcp.rs @@ -16,32 +16,33 @@ use std::{ #[cfg(feature = "tcp_compression")] use libafl_bolts::compress::GzipCompressor; -#[cfg(feature = "std")] -use libafl_bolts::core_affinity::CoreId; -#[cfg(all(feature = "std", any(windows, not(feature = "fork"))))] +#[cfg(any(windows, not(feature = "fork")))] use libafl_bolts::os::startable_self; -#[cfg(all(unix, feature = "std", not(miri)))] +#[cfg(all(unix, not(miri)))] use libafl_bolts::os::unix_signals::setup_signal_handler; -#[cfg(feature = "std")] -use libafl_bolts::os::CTRL_C_EXIT; -#[cfg(all(feature = "std", feature = "fork", unix))] +#[cfg(all(feature = "fork", unix))] use libafl_bolts::os::{fork, ForkResult}; -use libafl_bolts::{shmem::ShMemProvider, tuples::tuple_list, ClientId}; -#[cfg(feature = "std")] -use libafl_bolts::{shmem::StdShMemProvider, staterestore::StateRestorer}; +use libafl_bolts::{ + core_affinity::CoreId, + os::CTRL_C_EXIT, + shmem::{ShMemProvider, StdShMemProvider}, + staterestore::StateRestorer, + tuples::tuple_list, + ClientId, +}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, sync::{broadcast, broadcast::error::RecvError, mpsc}, task::{spawn, JoinHandle}, }; -#[cfg(feature = "std")] use typed_builder::TypedBuilder; use super::{CustomBufEventResult, CustomBufHandlerFn}; -#[cfg(all(unix, feature = "std", not(miri)))] +#[cfg(all(unix, not(miri)))] use crate::events::EVENTMGR_SIGHANDLER_STATE; use crate::{ + corpus::Corpus, events::{ BrokerEventResult, Event, EventConfig, EventFirer, EventManager, EventManagerHooksTuple, EventManagerId, EventProcessor, EventRestarter, HasCustomBufHandlers, HasEventManagerId, @@ -52,7 +53,7 @@ use crate::{ inputs::{Input, UsesInput}, monitors::Monitor, observers::ObserversTuple, - state::{HasExecutions, HasImported, HasLastReportTime, State, UsesState}, + state::{HasCorpus, HasExecutions, HasImported, HasLastReportTime, State, UsesState}, Error, HasMetadata, }; @@ -583,7 +584,8 @@ where impl TcpEventManager where EMH: EventManagerHooksTuple, - S: State + HasExecutions + HasMetadata + HasImported, + S: State + HasExecutions + HasMetadata + HasImported + HasCorpus, + S::Corpus: Corpus, { /// Write the client id for a client [`EventManager`] to env vars pub fn to_env(&self, env_name: &str) { @@ -604,8 +606,8 @@ where E: Executor + HasObservers, E::Observers: Serialize + ObserversTuple, for<'a> E::Observers: Deserialize<'a>, - Z: ExecutionProcessor - + EvaluatorObservers, + Z: ExecutionProcessor::Input, E::Observers, S> + + EvaluatorObservers::Input, S>, { if !self.hooks.pre_exec_all(state, client_id, &event)? { return Ok(()); @@ -636,8 +638,7 @@ where { state.scalability_monitor_mut().testcase_without_observers += 1; } - fuzzer - .evaluate_input_with_observers::(state, executor, self, input, false)? + fuzzer.evaluate_input_with_observers(state, executor, self, input, false)? }; if let Some(item) = _res.1 { *state.imported_mut() += 1; @@ -745,9 +746,10 @@ where E::Observers: Serialize + ObserversTuple, for<'a> E::Observers: Deserialize<'a>, EMH: EventManagerHooksTuple, - S: State + HasExecutions + HasMetadata + HasImported, - Z: EvaluatorObservers - + ExecutionProcessor, + S: State + HasExecutions + HasMetadata + HasImported + HasCorpus, + S::Corpus: Corpus, + Z: ExecutionProcessor::Input, E::Observers, S> + + EvaluatorObservers::Input, S>, { fn process( &mut self, @@ -820,9 +822,10 @@ where E::Observers: Serialize + ObserversTuple, for<'a> E::Observers: Deserialize<'a>, EMH: EventManagerHooksTuple, - S: State + HasExecutions + HasMetadata + HasLastReportTime + HasImported, - Z: EvaluatorObservers - + ExecutionProcessor, + S: State + HasExecutions + HasMetadata + HasLastReportTime + HasImported + HasCorpus, + S::Corpus: Corpus, + Z: ExecutionProcessor::Input, E::Observers, S> + + EvaluatorObservers::Input, S>, { } @@ -858,7 +861,6 @@ where } /// A manager that can restart on the fly, storing states in-between (in `on_restart`) -#[cfg(feature = "std")] #[derive(Debug)] pub struct TcpRestartingEventManager where @@ -875,7 +877,6 @@ where save_state: bool, } -#[cfg(feature = "std")] impl UsesState for TcpRestartingEventManager where EMH: EventManagerHooksTuple, @@ -885,7 +886,6 @@ where type State = S; } -#[cfg(feature = "std")] impl ProgressReporter for TcpRestartingEventManager where EMH: EventManagerHooksTuple, @@ -894,7 +894,6 @@ where { } -#[cfg(feature = "std")] impl EventFirer for TcpRestartingEventManager where EMH: EventManagerHooksTuple, @@ -920,7 +919,6 @@ where } } -#[cfg(feature = "std")] impl EventRestarter for TcpRestartingEventManager where EMH: EventManagerHooksTuple, @@ -959,17 +957,17 @@ where } } -#[cfg(feature = "std")] impl EventProcessor for TcpRestartingEventManager where E: HasObservers + Executor, Z, State = S>, for<'a> E::Observers: Deserialize<'a>, E::Observers: ObserversTuple + Serialize, EMH: EventManagerHooksTuple, - S: State + HasExecutions + HasMetadata + HasImported, + S: State + HasExecutions + HasMetadata + HasImported + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider + 'static, - Z: EvaluatorObservers, E::Observers, State = S> - + ExecutionProcessor, E::Observers>, //CE: CustomEvent, + Z: ExecutionProcessor, ::Input, E::Observers, S> + + EvaluatorObservers, ::Input, S>, { fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result { self.tcp_mgr.process(fuzzer, state, executor) @@ -980,21 +978,20 @@ where } } -#[cfg(feature = "std")] impl EventManager for TcpRestartingEventManager where E: HasObservers + Executor, Z, State = S>, E::Observers: ObserversTuple + Serialize, for<'a> E::Observers: Deserialize<'a>, EMH: EventManagerHooksTuple, - S: State + HasExecutions + HasMetadata + HasLastReportTime + HasImported, + S: State + HasExecutions + HasMetadata + HasLastReportTime + HasImported + HasCorpus, + S::Corpus: Corpus, SP: ShMemProvider + 'static, - Z: EvaluatorObservers, E::Observers, State = S> - + ExecutionProcessor, E::Observers>, //CE: CustomEvent, + Z: ExecutionProcessor, ::Input, E::Observers, S> + + EvaluatorObservers, ::Input, S>, { } -#[cfg(feature = "std")] impl HasEventManagerId for TcpRestartingEventManager where EMH: EventManagerHooksTuple, @@ -1012,7 +1009,6 @@ const _ENV_FUZZER_RECEIVER: &str = "_AFL_ENV_FUZZER_RECEIVER"; /// The tcp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages) const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = "_AFL_ENV_FUZZER_BROKER_CLIENT"; -#[cfg(feature = "std")] impl TcpRestartingEventManager where EMH: EventManagerHooksTuple, @@ -1054,7 +1050,6 @@ where } /// The kind of manager we're creating right now -#[cfg(feature = "std")] #[derive(Debug, Clone, Copy)] pub enum TcpManagerKind { /// Any kind will do @@ -1072,7 +1067,6 @@ pub enum TcpManagerKind { /// /// The [`TcpRestartingEventManager`] is a combination of restarter and runner, that can be used on systems with and without `fork` support. /// The restarter will spawn a new process each time the child crashes or timeouts. -#[cfg(feature = "std")] #[allow(clippy::type_complexity)] pub fn setup_restarting_mgr_tcp( monitor: MT, @@ -1087,7 +1081,8 @@ pub fn setup_restarting_mgr_tcp( > where MT: Monitor + Clone, - S: State + HasExecutions + HasMetadata + HasImported, + S: State + HasExecutions + HasMetadata + HasImported + HasCorpus, + S::Corpus: Corpus, { TcpRestartingMgr::builder() .shmem_provider(StdShMemProvider::new()?) @@ -1104,7 +1099,6 @@ where /// The [`TcpRestartingMgr`] is a combination of a /// `restarter` and `runner`, that can be used on systems both with and without `fork` support. The /// `restarter` will start a new process each time the child crashes or times out. -#[cfg(feature = "std")] #[allow(clippy::default_trait_access, clippy::ignored_unit_patterns)] #[derive(TypedBuilder, Debug)] pub struct TcpRestartingMgr @@ -1148,13 +1142,13 @@ where phantom_data: PhantomData, } -#[cfg(feature = "std")] #[allow(clippy::type_complexity, clippy::too_many_lines)] impl TcpRestartingMgr where EMH: EventManagerHooksTuple + Copy + Clone, SP: ShMemProvider, - S: State + HasExecutions + HasMetadata + HasImported, + S: State + HasExecutions + HasMetadata + HasImported + HasCorpus, + S::Corpus: Corpus, MT: Monitor + Clone, { /// Launch the restarting manager diff --git a/libafl/src/executors/combined.rs b/libafl/src/executors/combined.rs index 639f1f1528..20c830ac39 100644 --- a/libafl/src/executors/combined.rs +++ b/libafl/src/executors/combined.rs @@ -26,7 +26,6 @@ impl CombinedExecutor { A: Executor, B: Executor::State>, EM: UsesState::State>, - Z: UsesState::State>, { Self { primary, secondary } } @@ -48,7 +47,6 @@ where B: Executor::State>, Self::State: HasExecutions, EM: UsesState::State>, - Z: UsesState::State>, { fn run_target( &mut self, diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 7bf6db343c..8471f91b03 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -396,7 +396,6 @@ where S: State + HasExecutions + UsesInput, T: CommandConfigurator + Debug, OT: Debug + MatchName + ObserversTuple, - Z: UsesState, { fn run_target( &mut self, @@ -433,7 +432,6 @@ where S: State + HasExecutions + UsesInput, T: CommandConfigurator + Debug, OT: Debug + MatchName + ObserversTuple, - Z: UsesState, HT: ExecutorHooksTuple, { /// Linux specific low level implementation, to directly handle `fork`, `exec` and use linux @@ -793,7 +791,6 @@ impl CommandExecutorBuilder { /// fn make_executor() -> impl Executor /// where /// EM: UsesState, -/// Z: UsesState, /// EM::State: UsesInput + HasExecutions, /// { /// MyExecutor.into_executor(()) diff --git a/libafl/src/executors/differential.rs b/libafl/src/executors/differential.rs index 316295be11..95b0e093cb 100644 --- a/libafl/src/executors/differential.rs +++ b/libafl/src/executors/differential.rs @@ -74,7 +74,6 @@ where ::Observers: ObserversTuple<<::State as UsesInput>::Input, ::State>, DOT: DifferentialObserversTuple + MatchName, - Z: UsesState::State>, { fn run_target( &mut self, diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index f3a403827b..2b0e8dec82 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -1588,7 +1588,6 @@ where S: State + HasExecutions, TC: TargetBytesConverter, EM: UsesState, - Z: UsesState, { #[inline] fn run_target( diff --git a/libafl/src/executors/hooks/inprocess.rs b/libafl/src/executors/hooks/inprocess.rs index f4a927c9c8..876016b972 100644 --- a/libafl/src/executors/hooks/inprocess.rs +++ b/libafl/src/executors/hooks/inprocess.rs @@ -236,7 +236,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { @@ -279,7 +279,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, E::State: State + HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { @@ -336,7 +336,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, + Z: HasObjective, { #[cfg_attr(miri, allow(unused_variables))] let ret = Self { diff --git a/libafl/src/executors/hooks/unix.rs b/libafl/src/executors/hooks/unix.rs index a63adb0563..25ece10a70 100644 --- a/libafl/src/executors/hooks/unix.rs +++ b/libafl/src/executors/hooks/unix.rs @@ -84,7 +84,7 @@ pub mod unix_signal_handler { EM: EventFirer + EventRestarter, OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { @@ -134,7 +134,7 @@ pub mod unix_signal_handler { EM: EventFirer + EventRestarter, OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { @@ -192,7 +192,7 @@ pub mod unix_signal_handler { EM: EventFirer + EventRestarter, OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { diff --git a/libafl/src/executors/hooks/windows.rs b/libafl/src/executors/hooks/windows.rs index d4ec7829ae..ec2aef5a04 100644 --- a/libafl/src/executors/hooks/windows.rs +++ b/libafl/src/executors/hooks/windows.rs @@ -31,7 +31,7 @@ pub mod windows_asan_handler { OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus, E::Observers: ObserversTuple<::Input, E::State>, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { @@ -190,7 +190,7 @@ pub mod windows_exception_handler { OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus, E::Observers: ObserversTuple<::Input, E::State>, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { @@ -253,7 +253,7 @@ pub mod windows_exception_handler { EM: EventFirer + EventRestarter, OF: Feedback, E::State: State + HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { @@ -326,7 +326,7 @@ pub mod windows_exception_handler { EM: EventFirer + EventRestarter, OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index cb6d38ae76..08c4f79e1a 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -161,7 +161,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { @@ -191,7 +191,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { @@ -224,7 +224,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index e326743cce..366532cbf7 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -106,7 +106,6 @@ where HT: ExecutorHooksTuple, OT: ObserversTuple, S: State + HasExecutions, - Z: UsesState, { fn run_target( &mut self, @@ -173,7 +172,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, { Self::with_timeout_generic( tuple_list!(), @@ -201,7 +200,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, ::Solutions: Corpus, //delete me <::Corpus as Corpus>::Input: Clone, //delete me { @@ -242,7 +241,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, ::Solutions: Corpus, //delete me <::Corpus as Corpus>::Input: Clone, //delete me { @@ -287,7 +286,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, { Self::with_timeout_generic( user_hooks, @@ -316,7 +315,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, ::Solutions: Corpus, //delete me <::Corpus as Corpus>::Input: Clone, //delete me { @@ -353,7 +352,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, ::Solutions: Corpus, //delete me <::Corpus as Corpus>::Input: Clone, //delete me { @@ -442,7 +441,7 @@ pub fn run_observers_and_save_state( EM: EventFirer + EventRestarter, OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me { let mut observers = executor.observers_mut(); @@ -503,9 +502,9 @@ where EM: EventFirer + EventRestarter, OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase, - Z: HasObjective - + HasScheduler - + ExecutionProcessor, + Z: HasObjective + + HasScheduler + + ExecutionProcessor, <::State as HasSolutions>::Solutions: Corpus, //delete me { let data = &raw mut GLOBAL_STATE; @@ -533,7 +532,7 @@ where #[cfg(test)] mod tests { - use libafl_bolts::tuples::tuple_list; + use libafl_bolts::{rands::XkcdRand, tuples::tuple_list}; use crate::{ corpus::InMemoryCorpus, @@ -542,7 +541,7 @@ mod tests { feedbacks::CrashFeedback, inputs::{NopInput, UsesInput}, schedulers::RandScheduler, - state::StdState, + state::{NopState, StdState}, StdFuzzer, }; @@ -554,16 +553,16 @@ mod tests { #[allow(clippy::let_unit_value)] fn test_inmem_exec() { let mut harness = |_buf: &NopInput| ExitKind::Ok; - let rand = libafl_bolts::rands::XkcdRand::new(); + let rand = XkcdRand::new(); let corpus = InMemoryCorpus::::new(); let solutions = InMemoryCorpus::new(); let mut objective = CrashFeedback::new(); let mut feedback = tuple_list!(); - let sche = RandScheduler::new(); + let sche: RandScheduler> = RandScheduler::new(); let mut mgr = NopEventManager::new(); let mut state = StdState::new(rand, corpus, solutions, &mut feedback, &mut objective).unwrap(); - let mut fuzzer = StdFuzzer::<_, _, _, _>::new(sche, feedback, objective); + let mut fuzzer = StdFuzzer::<_, _, _>::new(sche, feedback, objective); let mut in_process_executor = InProcessExecutor::new( &mut harness, diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs index 27bd145836..159d87fac5 100644 --- a/libafl/src/executors/inprocess/stateful.rs +++ b/libafl/src/executors/inprocess/stateful.rs @@ -98,7 +98,6 @@ where HT: ExecutorHooksTuple, OT: ObserversTuple, S: State + HasExecutions, - Z: UsesState, { fn run_target( &mut self, @@ -165,7 +164,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, { Self::with_timeout_generic( tuple_list!(), @@ -195,7 +194,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, ::Solutions: Corpus, //delete me <::Corpus as Corpus>::Input: Clone, //delete me { @@ -238,7 +237,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, ::Solutions: Corpus, //delete me <::Corpus as Corpus>::Input: Clone, //delete me { @@ -303,7 +302,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, { Self::with_timeout_generic( user_hooks, @@ -334,7 +333,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, ::Solutions: Corpus, //delete me <::Corpus as Corpus>::Input: Clone, //delete me { @@ -373,7 +372,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State, - Z: HasObjective, + Z: HasObjective, ::Solutions: Corpus, //delete me <::Corpus as Corpus>::Input: Clone, //delete me { diff --git a/libafl/src/executors/inprocess_fork/inner.rs b/libafl/src/executors/inprocess_fork/inner.rs index 9c82fbf70c..e34d1eb56b 100644 --- a/libafl/src/executors/inprocess_fork/inner.rs +++ b/libafl/src/executors/inprocess_fork/inner.rs @@ -121,7 +121,6 @@ where SP: ShMemProvider, HT: ExecutorHooksTuple, EM: EventFirer + EventRestarter, - Z: UsesState, { pub(super) unsafe fn pre_run_target_child( &mut self, diff --git a/libafl/src/executors/inprocess_fork/mod.rs b/libafl/src/executors/inprocess_fork/mod.rs index fd5e7c3f6c..b3386cc03b 100644 --- a/libafl/src/executors/inprocess_fork/mod.rs +++ b/libafl/src/executors/inprocess_fork/mod.rs @@ -55,7 +55,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: HasSolutions, - Z: HasObjective, + Z: HasObjective, { #[allow(clippy::too_many_arguments)] /// The constructor for `InProcessForkExecutor` @@ -93,7 +93,6 @@ where SP: ShMemProvider, HT: ExecutorHooksTuple, EM: UsesState, - Z: UsesState, { harness_fn: &'a mut H, inner: GenericInProcessForkExecutorInner, @@ -107,7 +106,6 @@ where SP: ShMemProvider, HT: ExecutorHooksTuple + Debug, EM: UsesState, - Z: UsesState, { #[cfg(target_os = "linux")] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -135,7 +133,6 @@ where SP: ShMemProvider, HT: ExecutorHooksTuple, EM: UsesState, - Z: UsesState, { type State = S; } @@ -149,7 +146,6 @@ where SP: ShMemProvider, HT: ExecutorHooksTuple, EM: EventFirer + EventRestarter, - Z: UsesState, { #[allow(unreachable_code)] #[inline] @@ -191,7 +187,7 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: State + HasSolutions, - Z: HasObjective, + Z: HasObjective, { /// Creates a new [`GenericInProcessForkExecutor`] with custom hooks #[allow(clippy::too_many_arguments)] @@ -242,7 +238,6 @@ where OT: ObserversTuple, SP: ShMemProvider, EM: UsesState, - Z: UsesState, { type Observers = OT; #[inline] diff --git a/libafl/src/executors/inprocess_fork/stateful.rs b/libafl/src/executors/inprocess_fork/stateful.rs index f5a5d854c2..027f272abd 100644 --- a/libafl/src/executors/inprocess_fork/stateful.rs +++ b/libafl/src/executors/inprocess_fork/stateful.rs @@ -124,7 +124,7 @@ where OT: ObserversTuple + Debug, S: State + HasExecutions, SP: ShMemProvider, - Z: HasObjective, + Z: HasObjective, { #[allow(unreachable_code)] #[inline] diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 000cb68081..a15ce0e1ef 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -120,7 +120,6 @@ pub trait HasObservers { pub trait Executor: UsesState where EM: UsesState, - Z: UsesState, { /// Instruct the target about the input and run fn run_target( @@ -209,7 +208,6 @@ mod test { EM: UsesState, S: State + HasExecutions, S::Input: HasTargetBytes, - Z: UsesState, { fn run_target( &mut self, diff --git a/libafl/src/executors/shadow.rs b/libafl/src/executors/shadow.rs index 0ec225b1ca..717822f900 100644 --- a/libafl/src/executors/shadow.rs +++ b/libafl/src/executors/shadow.rs @@ -68,7 +68,6 @@ where E: Executor + HasObservers, SOT: ObserversTuple, EM: UsesState, - Z: UsesState, { fn run_target( &mut self, diff --git a/libafl/src/executors/with_observers.rs b/libafl/src/executors/with_observers.rs index 7b161cbb97..e94a761ea2 100644 --- a/libafl/src/executors/with_observers.rs +++ b/libafl/src/executors/with_observers.rs @@ -23,7 +23,6 @@ impl Executor for WithObservers where E: Executor, EM: UsesState, - Z: UsesState, { fn run_target( &mut self, diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 8a074282cb..4ac5aeedb6 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -1,10 +1,10 @@ //! The `Fuzzer` is the main struct for a fuzz campaign. use alloc::{string::ToString, vec::Vec}; -use core::{fmt::Debug, marker::PhantomData, time::Duration}; +use core::{fmt::Debug, time::Duration}; -use libafl_bolts::current_time; -use serde::{de::DeserializeOwned, Serialize}; +use libafl_bolts::{current_time, tuples::MatchName}; +use serde::Serialize; #[cfg(feature = "introspection")] use crate::monitors::PerfFeature; @@ -13,7 +13,7 @@ use crate::{ events::{Event, EventConfig, EventFirer, EventProcessor, ProgressReporter}, executors::{Executor, ExitKind, HasObservers}, feedbacks::Feedback, - inputs::UsesInput, + inputs::{Input, UsesInput}, mark_feature_time, observers::ObserversTuple, schedulers::Scheduler, @@ -21,7 +21,7 @@ use crate::{ start_timer, state::{ HasCorpus, HasCurrentTestcase, HasExecutions, HasLastFoundTime, HasLastReportTime, - HasSolutions, State, Stoppable, UsesState, + HasSolutions, MaybeHasClientPerfMonitor, State, UsesState, }, Error, HasMetadata, }; @@ -30,12 +30,9 @@ use crate::{ pub(crate) const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_secs(15); /// Holds a scheduler -pub trait HasScheduler: UsesState -where - Self::State: HasCorpus, -{ +pub trait HasScheduler { /// The [`Scheduler`] for this fuzzer - type Scheduler: Scheduler; + type Scheduler: Scheduler; /// The scheduler fn scheduler(&self) -> &Self::Scheduler; @@ -45,7 +42,7 @@ where } /// Holds an feedback -pub trait HasFeedback: UsesState { +pub trait HasFeedback { /// The feedback type type Feedback; @@ -57,7 +54,7 @@ pub trait HasFeedback: UsesState { } /// Holds an objective feedback -pub trait HasObjective: UsesState { +pub trait HasObjective { /// The type of the [`Feedback`] used to find objectives for this fuzzer type Objective; @@ -69,104 +66,87 @@ pub trait HasObjective: UsesState { } /// Evaluates if an input is interesting using the feedback -pub trait ExecutionProcessor: UsesState { +pub trait ExecutionProcessor { /// Check the outcome of the execution, find if it is worth for corpus or objectives fn check_results( &mut self, - state: &mut Self::State, + state: &mut S, manager: &mut EM, - input: &::Input, + input: &I, observers: &OT, exit_kind: &ExitKind, - ) -> Result - where - EM: EventFirer, - OT: ObserversTuple<::Input, Self::State>; + ) -> Result; /// Process `ExecuteInputResult`. Add to corpus, solution or ignore #[allow(clippy::too_many_arguments)] fn process_execution( &mut self, - state: &mut Self::State, + state: &mut S, manager: &mut EM, - input: &::Input, + input: &I, exec_res: &ExecuteInputResult, observers: &OT, - ) -> Result, Error> - where - EM: EventFirer, - OT: ObserversTuple<::Input, Self::State>; + ) -> Result, Error>; /// serialize and send event via manager fn serialize_and_dispatch( &mut self, - state: &mut Self::State, + state: &mut S, manager: &mut EM, - input: ::Input, + input: I, exec_res: &ExecuteInputResult, observers: &OT, exit_kind: &ExitKind, - ) -> Result<(), Error> - where - EM: EventFirer, - OT: ObserversTuple<::Input, Self::State> + Serialize; + ) -> Result<(), Error>; /// send event via manager fn dispatch_event( &mut self, - state: &mut Self::State, + state: &mut S, manager: &mut EM, - input: ::Input, + input: I, exec_res: &ExecuteInputResult, obs_buf: Option>, exit_kind: &ExitKind, - ) -> Result<(), Error> - where - EM: EventFirer; + ) -> Result<(), Error>; /// Evaluate if a set of observation channels has an interesting state fn evaluate_execution( &mut self, - state: &mut Self::State, + state: &mut S, manager: &mut EM, - input: ::Input, + input: I, observers: &OT, exit_kind: &ExitKind, send_events: bool, - ) -> Result<(ExecuteInputResult, Option), Error> - where - EM: EventFirer, - OT: ObserversTuple<::Input, Self::State> + Serialize; + ) -> Result<(ExecuteInputResult, Option), Error>; } /// Evaluates an input modifying the state of the fuzzer -pub trait EvaluatorObservers: UsesState + Sized { +pub trait EvaluatorObservers { /// Runs the input and triggers observers and feedback, /// returns if is interesting an (option) the index of the new /// [`crate::corpus::Testcase`] in the [`crate::corpus::Corpus`] - fn evaluate_input_with_observers( + fn evaluate_input_with_observers( &mut self, - state: &mut Self::State, + state: &mut S, executor: &mut E, manager: &mut EM, - input: ::Input, + input: I, send_events: bool, - ) -> Result<(ExecuteInputResult, Option), Error> - where - E: Executor + HasObservers, - EM: EventFirer; + ) -> Result<(ExecuteInputResult, Option), Error>; } /// Evaluate an input modifying the state of the fuzzer -pub trait Evaluator: UsesState { +pub trait Evaluator { /// Runs the input and triggers observers and feedback, /// returns if is interesting an (option) the index of the new [`crate::corpus::Testcase`] in the corpus fn evaluate_input( &mut self, - state: &mut Self::State, + state: &mut S, executor: &mut E, manager: &mut EM, - input: ::Input, + input: I, ) -> Result<(ExecuteInputResult, Option), Error> { self.evaluate_input_events(state, executor, manager, input, true) } @@ -176,10 +156,10 @@ pub trait Evaluator: UsesState { /// This version has a boolean to decide if send events to the manager. fn evaluate_input_events( &mut self, - state: &mut Self::State, + state: &mut S, executor: &mut E, manager: &mut EM, - input: ::Input, + input: I, send_events: bool, ) -> Result<(ExecuteInputResult, Option), Error>; @@ -189,10 +169,10 @@ pub trait Evaluator: UsesState { /// Usually, you want to use [`Evaluator::evaluate_input`], unless you know what you are doing. fn add_input( &mut self, - state: &mut Self::State, + state: &mut S, executor: &mut E, manager: &mut EM, - input: ::Input, + input: I, ) -> Result; /// Adds the input to the corpus as disabled a input. @@ -200,21 +180,11 @@ pub trait Evaluator: UsesState { /// Disabled testcases are only used for splicing /// Returns the `index` of the new testcase in the corpus. /// Usually, you want to use [`Evaluator::evaluate_input`], unless you know what you are doing. - fn add_disabled_input( - &mut self, - state: &mut Self::State, - input: ::Input, - ) -> Result; + fn add_disabled_input(&mut self, state: &mut S, input: I) -> Result; } /// The main fuzzer trait. -pub trait Fuzzer: Sized + UsesState -where - Self::State: HasMetadata + HasExecutions + HasLastReportTime + Stoppable, - E: UsesState, - EM: ProgressReporter, - ST: StagesTuple, -{ +pub trait Fuzzer { /// Fuzz for a single iteration. /// Returns the index of the last fuzzed corpus item. /// (Note: An iteration represents a complete run of every stage. @@ -228,7 +198,7 @@ where &mut self, stages: &mut ST, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result; @@ -237,16 +207,9 @@ where &mut self, stages: &mut ST, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, - ) -> Result<(), Error> { - let monitor_timeout = STATS_TIMEOUT_DEFAULT; - loop { - manager.maybe_report_progress(state, monitor_timeout)?; - - self.fuzz_one(stages, executor, state, manager)?; - } - } + ) -> Result<(), Error>; /// Fuzz for n iterations. /// Returns the index of the last fuzzed corpus item. @@ -261,33 +224,10 @@ where &mut self, stages: &mut ST, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, iters: u64, - ) -> Result { - if iters == 0 { - return Err(Error::illegal_argument( - "Cannot fuzz for 0 iterations!".to_string(), - )); - } - - let mut ret = None; - let monitor_timeout = STATS_TIMEOUT_DEFAULT; - - for _ in 0..iters { - manager.maybe_report_progress(state, monitor_timeout)?; - ret = Some(self.fuzz_one(stages, executor, state, manager)?); - } - - manager.report_progress(state)?; - - // If we would assume the fuzzer loop will always exit after this, we could do this here: - // manager.on_restart(state)?; - // But as the state may grow to a few megabytes, - // for now we won't, and the user has to do it (unless we find a way to do this on `Drop`). - - Ok(ret.unwrap()) - } + ) -> Result; } /// The corpus this input should be added to @@ -303,24 +243,16 @@ pub enum ExecuteInputResult { /// Your default fuzzer instance, for everyday use. #[derive(Debug)] -pub struct StdFuzzer { +pub struct StdFuzzer { scheduler: CS, feedback: F, objective: OF, - phantom: PhantomData, } -impl UsesState for StdFuzzer +impl HasScheduler<::Input, S> for StdFuzzer where - S: State, -{ - type State = S; -} - -impl HasScheduler for StdFuzzer -where - S: State + HasCorpus, - CS: Scheduler, + S: HasCorpus, + CS: Scheduler<::Input, S>, { type Scheduler = CS; @@ -333,10 +265,7 @@ where } } -impl HasFeedback for StdFuzzer -where - S: State, -{ +impl HasFeedback for StdFuzzer { type Feedback = F; fn feedback(&self) -> &Self::Feedback { @@ -348,10 +277,7 @@ where } } -impl HasObjective for StdFuzzer -where - S: State, -{ +impl HasObjective for StdFuzzer { type Objective = OF; fn objective(&self) -> &OF { @@ -363,27 +289,30 @@ where } } -impl ExecutionProcessor for StdFuzzer +impl ExecutionProcessor::Input, OT, S> + for StdFuzzer where - CS: Scheduler, - F: Feedback, - OF: Feedback, - S: HasCorpus + HasSolutions + HasExecutions + HasCorpus + HasCurrentCorpusId + State, - S::Corpus: Corpus, //delete me - S::Solutions: Corpus, //delete me + CS: Scheduler<::Input, S>, + EM: EventFirer, + S: HasCorpus + + MaybeHasClientPerfMonitor + + UsesInput::Input> + + HasCurrentTestcase + + HasSolutions, + F: Feedback::Input, OT, S>, + OF: Feedback::Input, OT, S>, + OT: ObserversTuple<::Input, S> + Serialize, + ::Input: Input, + S::Solutions: Corpus::Input>, { fn check_results( &mut self, state: &mut S, manager: &mut EM, - input: &S::Input, + input: &::Input, observers: &OT, exit_kind: &ExitKind, - ) -> Result - where - EM: EventFirer, - OT: ObserversTuple, - { + ) -> Result { let mut res = ExecuteInputResult::None; #[cfg(not(feature = "introspection"))] @@ -418,17 +347,13 @@ where fn evaluate_execution( &mut self, - state: &mut Self::State, + state: &mut S, manager: &mut EM, - input: ::Input, + input: ::Input, observers: &OT, exit_kind: &ExitKind, send_events: bool, - ) -> Result<(ExecuteInputResult, Option), Error> - where - EM: EventFirer, - OT: ObserversTuple + Serialize, - { + ) -> Result<(ExecuteInputResult, Option), Error> { let exec_res = self.check_results(state, manager, &input, observers, exit_kind)?; let corpus_id = self.process_execution(state, manager, &input, &exec_res, observers)?; if send_events { @@ -439,17 +364,13 @@ where fn serialize_and_dispatch( &mut self, - state: &mut Self::State, + state: &mut S, manager: &mut EM, - input: ::Input, + input: ::Input, exec_res: &ExecuteInputResult, observers: &OT, exit_kind: &ExitKind, - ) -> Result<(), Error> - where - EM: EventFirer, - OT: ObserversTuple + Serialize, - { + ) -> Result<(), Error> { // Now send off the event let observers_buf = match exec_res { ExecuteInputResult::Corpus => { @@ -473,16 +394,13 @@ where fn dispatch_event( &mut self, - state: &mut Self::State, + state: &mut S, manager: &mut EM, - input: ::Input, + input: ::Input, exec_res: &ExecuteInputResult, observers_buf: Option>, exit_kind: &ExitKind, - ) -> Result<(), Error> - where - EM: EventFirer, - { + ) -> Result<(), Error> { // Now send off the event match exec_res { ExecuteInputResult::Corpus => { @@ -522,16 +440,12 @@ where /// Evaluate if a set of observation channels has an interesting state fn process_execution( &mut self, - state: &mut Self::State, + state: &mut S, manager: &mut EM, - input: &S::Input, + input: &::Input, exec_res: &ExecuteInputResult, observers: &OT, - ) -> Result, Error> - where - EM: EventFirer, - OT: ObserversTuple, - { + ) -> Result, Error> { match exec_res { ExecuteInputResult::None => { self.feedback_mut().discard_metadata(state, input)?; @@ -577,30 +491,34 @@ where } } -impl EvaluatorObservers for StdFuzzer +impl EvaluatorObservers::Input, S> + for StdFuzzer where - CS: Scheduler, - OT: ObserversTuple + Serialize + DeserializeOwned, - F: Feedback, - OF: Feedback, - S: HasCorpus + HasSolutions + HasExecutions + State, - S::Corpus: Corpus, //delete me - S::Solutions: Corpus, //delete me + CS: Scheduler<::Input, S>, + E: HasObservers + Executor, + E::Observers: MatchName + ObserversTuple<::Input, S> + Serialize, + EM: EventFirer, + F: Feedback::Input, E::Observers, S>, + OF: Feedback::Input, E::Observers, S>, + S: HasCorpus + + HasSolutions + + MaybeHasClientPerfMonitor + + HasCurrentTestcase + + UsesInput::Input> + + HasExecutions, + ::Input: Input, + S::Solutions: Corpus::Input>, { /// Process one input, adding to the respective corpora if needed and firing the right events #[inline] - fn evaluate_input_with_observers( + fn evaluate_input_with_observers( &mut self, state: &mut S, executor: &mut E, manager: &mut EM, - input: S::Input, + input: ::Input, send_events: bool, - ) -> Result<(ExecuteInputResult, Option), Error> - where - E: Executor + HasObservers, - EM: EventFirer, - { + ) -> Result<(ExecuteInputResult, Option), Error> { let exit_kind = self.execute_input(state, executor, manager, &input)?; let observers = executor.observers(); @@ -610,34 +528,40 @@ where } } -impl Evaluator for StdFuzzer +impl Evaluator::Input, S> for StdFuzzer where - CS: Scheduler, + CS: Scheduler<::Input, S>, E: HasObservers + Executor, - E::Observers: ObserversTuple + Serialize + DeserializeOwned, + E::Observers: MatchName + ObserversTuple<::Input, S> + Serialize, EM: EventFirer, - F: Feedback, - OF: Feedback, - S: HasCorpus + HasSolutions + HasExecutions + HasLastFoundTime + State, - S::Corpus: Corpus, //delete me - S::Solutions: Corpus, //delete me + F: Feedback::Input, E::Observers, S>, + OF: Feedback::Input, E::Observers, S>, + S: HasCorpus + + HasSolutions + + MaybeHasClientPerfMonitor + + HasCurrentTestcase + + HasLastFoundTime + + HasExecutions + + UsesInput::Input>, + ::Input: Input, + S::Solutions: Corpus::Input>, { /// Process one input, adding to the respective corpora if needed and firing the right events #[inline] fn evaluate_input_events( &mut self, - state: &mut Self::State, + state: &mut S, executor: &mut E, manager: &mut EM, - input: ::Input, + input: ::Input, send_events: bool, ) -> Result<(ExecuteInputResult, Option), Error> { self.evaluate_input_with_observers(state, executor, manager, input, send_events) } fn add_disabled_input( &mut self, - state: &mut Self::State, - input: ::Input, + state: &mut S, + input: ::Input, ) -> Result { let mut testcase = Testcase::from(input.clone()); testcase.set_disabled(true); @@ -648,10 +572,10 @@ where /// Adds an input, even if it's not considered `interesting` by any of the executors fn add_input( &mut self, - state: &mut Self::State, + state: &mut S, executor: &mut E, manager: &mut EM, - input: ::Input, + input: ::Input, ) -> Result { *state.last_found_time_mut() = current_time(); @@ -744,7 +668,7 @@ where } } -impl Fuzzer for StdFuzzer +impl Fuzzer for StdFuzzer where CS: Scheduler, E: UsesState, @@ -763,7 +687,7 @@ where &mut self, stages: &mut ST, executor: &mut E, - state: &mut Self::State, + state: &mut S, manager: &mut EM, ) -> Result { // Init timer for scheduler @@ -819,77 +743,89 @@ where Ok(id) } -} -impl StdFuzzer -where - CS: Scheduler, - S: UsesInput + HasExecutions + HasCorpus + State, -{ - /// Create a new `StdFuzzer` with standard behavior. - pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self { - Self { - scheduler, - feedback, - objective, - phantom: PhantomData, + fn fuzz_loop( + &mut self, + stages: &mut ST, + executor: &mut E, + state: &mut S, + manager: &mut EM, + ) -> Result<(), Error> { + let monitor_timeout = STATS_TIMEOUT_DEFAULT; + loop { + manager.maybe_report_progress(state, monitor_timeout)?; + + self.fuzz_one(stages, executor, state, manager)?; } } - /// Runs the input and triggers observers - pub fn execute_input( + fn fuzz_loop_for( &mut self, - state: &mut ::State, + stages: &mut ST, executor: &mut E, - event_mgr: &mut EM, - input: &<::State as UsesInput>::Input, - ) -> Result - where - E: Executor::State> + HasObservers, - E::Observers: ObserversTuple<::Input, ::State>, - EM: UsesState::State>, - { - start_timer!(state); - executor.observers_mut().pre_exec_all(state, input)?; - mark_feature_time!(state, PerfFeature::PreExecObservers); + state: &mut S, + manager: &mut EM, + iters: u64, + ) -> Result { + if iters == 0 { + return Err(Error::illegal_argument( + "Cannot fuzz for 0 iterations!".to_string(), + )); + } - start_timer!(state); - let exit_kind = executor.run_target(self, state, event_mgr, input)?; - mark_feature_time!(state, PerfFeature::TargetExecution); + let mut ret = None; + let monitor_timeout = STATS_TIMEOUT_DEFAULT; - start_timer!(state); - executor - .observers_mut() - .post_exec_all(state, input, &exit_kind)?; - mark_feature_time!(state, PerfFeature::PostExecObservers); + for _ in 0..iters { + manager.maybe_report_progress(state, monitor_timeout)?; + ret = Some(self.fuzz_one(stages, executor, state, manager)?); + } - Ok(exit_kind) + manager.report_progress(state)?; + + // If we would assume the fuzzer loop will always exit after this, we could do this here: + // manager.on_restart(state)?; + // But as the state may grow to a few megabytes, + // for now we won't, and the user has to do it (unless we find a way to do this on `Drop`). + + Ok(ret.unwrap()) + } +} + +impl StdFuzzer { + /// Create a new `StdFuzzer` with standard behavior. + pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self { + Self { + scheduler, + feedback, + objective, + } } } /// Structs with this trait will execute an input -pub trait ExecutesInput: UsesState -where - E: UsesState, - EM: UsesState, -{ +pub trait ExecutesInput { /// Runs the input and triggers observers and feedback fn execute_input( &mut self, - state: &mut Self::State, + state: &mut S, executor: &mut E, event_mgr: &mut EM, - input: &::Input, + input: &I, ) -> Result; } -impl ExecutesInput for StdFuzzer +impl ExecutesInput::Input, S> + for StdFuzzer where - CS: Scheduler, + CS: Scheduler<::Input, S>, E: Executor + HasObservers, - E::Observers: ObserversTuple<::Input, ::State>, - EM: UsesState, - S: UsesInput + HasExecutions + HasCorpus + State, + E::Observers: ObserversTuple<::Input, S>, + EM: UsesState, + S: UsesInput::Input> + + HasExecutions + + HasCorpus + + MaybeHasClientPerfMonitor, { /// Runs the input and triggers observers and feedback fn execute_input( @@ -897,7 +833,7 @@ where state: &mut S, executor: &mut E, event_mgr: &mut EM, - input: &S::Input, + input: &::Input, ) -> Result { start_timer!(state); executor.observers_mut().pre_exec_all(state, input)?; @@ -919,39 +855,28 @@ where /// A [`NopFuzzer`] that does nothing #[derive(Clone, Debug)] -pub struct NopFuzzer { - phantom: PhantomData, -} +pub struct NopFuzzer {} -impl NopFuzzer { +impl NopFuzzer { /// Creates a new [`NopFuzzer`] #[must_use] pub fn new() -> Self { - Self { - phantom: PhantomData, - } + Self {} } } -impl Default for NopFuzzer { +impl Default for NopFuzzer { fn default() -> Self { Self::new() } } -impl UsesState for NopFuzzer -where - S: State, -{ - type State = S; -} - -impl Fuzzer for NopFuzzer +impl Fuzzer for NopFuzzer where E: UsesState, - EM: ProgressReporter + EventProcessor, - ST: StagesTuple, - Self::State: HasMetadata + HasExecutions + HasLastReportTime + HasCurrentStageId, + EM: ProgressReporter + EventProcessor, + ST: StagesTuple, + S: HasMetadata + HasExecutions + HasLastReportTime + HasCurrentStageId + UsesInput, { fn fuzz_one( &mut self, @@ -962,4 +887,25 @@ where ) -> Result { unimplemented!("NopFuzzer cannot fuzz"); } + + fn fuzz_loop( + &mut self, + _stages: &mut ST, + _executor: &mut E, + _state: &mut S, + _manager: &mut EM, + ) -> Result<(), Error> { + unimplemented!("NopFuzzer cannot fuzz"); + } + + fn fuzz_loop_for( + &mut self, + _stages: &mut ST, + _executor: &mut E, + _state: &mut S, + _manager: &mut EM, + _iters: u64, + ) -> Result { + unimplemented!("NopFuzzer cannot fuzz"); + } } diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs index 6aba14ee18..ece9ba1b73 100644 --- a/libafl/src/stages/afl_stats.rs +++ b/libafl/src/stages/afl_stats.rs @@ -239,7 +239,7 @@ impl Stage for AflStatsStage where E: HasObservers, EM: EventFirer, - Z: HasScheduler, + Z: HasScheduler<::Input, S>, S: HasImported + HasCorpus + HasMetadata @@ -252,7 +252,7 @@ where E::Observers: MatchNameRef, O: MapObserver, C: AsRef + Named, - ::Scheduler: HasQueueCycles, + Z::Scheduler: HasQueueCycles, { #[allow(clippy::too_many_lines)] fn perform( diff --git a/libafl/src/stages/calibrate.rs b/libafl/src/stages/calibrate.rs index 2d2cc09bd2..3cdbc2fd47 100644 --- a/libafl/src/stages/calibrate.rs +++ b/libafl/src/stages/calibrate.rs @@ -102,7 +102,7 @@ where + HasCurrentTestcase + HasCurrentCorpusId + UsesInput::Input>, - Z: Evaluator, + Z: Evaluator::Input, S>, ::Input: Input, { #[inline] diff --git a/libafl/src/stages/colorization.rs b/libafl/src/stages/colorization.rs index 91023844fc..732bbce1c2 100644 --- a/libafl/src/stages/colorization.rs +++ b/libafl/src/stages/colorization.rs @@ -92,7 +92,6 @@ where ::Input: HasMutatorBytes + Clone, O: MapObserver, C: AsRef + Named, - Z: UsesState, { #[inline] #[allow(clippy::let_and_return)] @@ -174,7 +173,6 @@ where + HasCurrentTestcase + UsesInput::Input>, ::Input: HasMutatorBytes + Clone, - Z: UsesState, { #[inline] #[allow(clippy::let_and_return)] diff --git a/libafl/src/stages/concolic.rs b/libafl/src/stages/concolic.rs index b6261a0125..d4b6641f6a 100644 --- a/libafl/src/stages/concolic.rs +++ b/libafl/src/stages/concolic.rs @@ -60,7 +60,6 @@ where + MaybeHasClientPerfMonitor + UsesInput::Input>, EM: UsesState, - Z: UsesState, { #[inline] fn perform( @@ -362,14 +361,6 @@ pub struct SimpleConcolicMutationalStage { phantom: PhantomData, } -#[cfg(feature = "concolic_mutation")] -impl UsesState for SimpleConcolicMutationalStage -where - Z: UsesState, -{ - type State = Z::State; -} - #[cfg(feature = "concolic_mutation")] /// The unique id for this stage static mut SIMPLE_CONCOLIC_MUTATIONAL_ID: usize = 0; @@ -388,7 +379,7 @@ impl Named for SimpleConcolicMutationalStage { #[cfg(feature = "concolic_mutation")] impl Stage for SimpleConcolicMutationalStage where - Z: Evaluator, + Z: Evaluator::Input, S>, ::Input: HasMutatorBytes + Clone, S: HasExecutions + HasCorpus diff --git a/libafl/src/stages/generalization.rs b/libafl/src/stages/generalization.rs index 1ae2bd28c7..123c6162ac 100644 --- a/libafl/src/stages/generalization.rs +++ b/libafl/src/stages/generalization.rs @@ -76,7 +76,6 @@ where + UsesInput, S::Corpus: Corpus, EM: UsesState, - Z: UsesState, { #[inline] #[allow(clippy::too_many_lines)] @@ -352,7 +351,6 @@ where + UsesInput, OT: ObserversTuple, EM: UsesState, - Z: UsesState, { /// Create a new [`GeneralizationStage`]. #[must_use] diff --git a/libafl/src/stages/generation.rs b/libafl/src/stages/generation.rs index 7067d0ea62..8fe6ff28fa 100644 --- a/libafl/src/stages/generation.rs +++ b/libafl/src/stages/generation.rs @@ -31,7 +31,7 @@ impl GenStage { impl Stage for GenStage where - Z: Evaluator, + Z: Evaluator::Input, S>, S: HasCorpus + HasRand + UsesInput::Input>, G: Generator<::Input, S>, { diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 1ac574a489..5e67897a85 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -150,7 +150,7 @@ impl Named for StdMutationalStage { impl Stage for StdMutationalStage where M: Mutator, - Z: Evaluator, + Z: Evaluator::Input, S>, S: HasCorpus + HasRand + HasMetadata @@ -192,7 +192,7 @@ where impl StdMutationalStage::Input, M, S, Z> where M: Mutator<::Input, S>, - Z: Evaluator, + Z: Evaluator::Input, S>, S: HasCorpus + HasRand + HasCurrentCorpusId + UsesInput + MaybeHasClientPerfMonitor, ::Input: Input + Clone, S::Corpus: Corpus, @@ -213,7 +213,7 @@ where impl StdMutationalStage where M: Mutator, - Z: Evaluator, + Z: Evaluator::Input, S>, S: HasCorpus + HasRand + HasCurrentTestcase + MaybeHasClientPerfMonitor + UsesInput, I: MutatedTransform<::Input, S> + Clone, ::Input: Input, @@ -319,7 +319,7 @@ impl Named for MultiMutationalStage { impl Stage for MultiMutationalStage where M: MultiMutator, - Z: Evaluator, + Z: Evaluator::Input, S>, S: HasCorpus + HasRand + HasNamedMetadata + HasCurrentTestcase + HasCurrentCorpusId + UsesInput, I: MutatedTransform<::Input, S> + Clone, ::Input: Input, diff --git a/libafl/src/stages/power.rs b/libafl/src/stages/power.rs index adcf982ca8..abd05b0b01 100644 --- a/libafl/src/stages/power.rs +++ b/libafl/src/stages/power.rs @@ -93,7 +93,7 @@ where + HasCurrentCorpusId + MaybeHasClientPerfMonitor + UsesInput::Input>, - Z: Evaluator, + Z: Evaluator::Input, S>, I: MutatedTransform<::Input, S> + Clone + Input, ::Input: Input, { @@ -134,7 +134,7 @@ where + MaybeHasClientPerfMonitor + UsesInput::Input>, I: MutatedTransform<::Input, S> + Clone + Input, - Z: Evaluator, + Z: Evaluator::Input, S>, ::Input: Input, { /// Creates a new [`PowerMutationalStage`] diff --git a/libafl/src/stages/push/mod.rs b/libafl/src/stages/push/mod.rs index cbbb456eff..b2caf3c2ad 100644 --- a/libafl/src/stages/push/mod.rs +++ b/libafl/src/stages/push/mod.rs @@ -262,10 +262,10 @@ where EM: EventFirer + EventRestarter + HasEventManagerId + ProgressReporter, OT: ObserversTuple<::Input, S>, PS: PushStage::Input, OT, S, Z>, - Z: ExecutesInput - + ExecutionProcessor - + EvaluatorObservers - + HasScheduler, + Z: ExecutesInput::Input, S> + + ExecutionProcessor::Input, OT, S> + + EvaluatorObservers::Input, OT> + + HasScheduler<::Input, S>, { fn perform( &mut self, diff --git a/libafl/src/stages/push/mutational.rs b/libafl/src/stages/push/mutational.rs index d3d35f8b50..76d28cfb09 100644 --- a/libafl/src/stages/push/mutational.rs +++ b/libafl/src/stages/push/mutational.rs @@ -80,7 +80,8 @@ impl PushStage::Input, OT, S, Z> for StdMutationalPushStage where EM: EventFirer, - Z: HasScheduler + ExecutionProcessor, + Z: HasScheduler<::Input, S> + + ExecutionProcessor::Input, OT, S>, S: HasCorpus + UsesInput::Input> + HasRand @@ -203,7 +204,8 @@ where OT: ObserversTuple<::Input, S> + Serialize, M: Mutator<::Input, S>, ::Input: Clone + Debug + Input, - Z: HasScheduler + ExecutionProcessor, + Z: HasScheduler<::Input, S> + + ExecutionProcessor::Input, OT, S>, { type Item = Result<::Input, Error>; @@ -225,7 +227,8 @@ where OT: ObserversTuple<::Input, S> + Serialize, M: Mutator<::Input, S>, ::Input: Clone + Debug + Input, - Z: HasScheduler + ExecutionProcessor, + Z: HasScheduler<::Input, S> + + ExecutionProcessor::Input, OT, S>, { /// Creates a new default mutational stage #[must_use] diff --git a/libafl/src/stages/sync.rs b/libafl/src/stages/sync.rs index b4cc5fcec0..a6cbcd3783 100644 --- a/libafl/src/stages/sync.rs +++ b/libafl/src/stages/sync.rs @@ -69,7 +69,7 @@ impl Named for SyncFromDiskStage { impl Stage for SyncFromDiskStage where CB: FnMut(&mut Z, &mut S, &Path) -> Result<::Input, Error>, - Z: Evaluator, + Z: Evaluator::Input, S>, S: HasCorpus + HasRand + HasMetadata @@ -167,20 +167,25 @@ impl SyncFromDiskStage { /// Function type when the callback in `SyncFromDiskStage` is not a lambda pub type SyncFromDiskFunction = - fn(&mut Z, &mut S, &Path) -> Result<::Input, Error>; + fn(&mut Z, &mut S, &Path) -> Result<<::Corpus as Corpus>::Input, Error>; -impl SyncFromDiskStage, E, EM, S, Z> +impl SyncFromDiskStage, E, EM, S, Z> where - Z: Evaluator, + S: HasCorpus, + ::Input: Input, + Z: Evaluator::Input, S>, { /// Creates a new [`SyncFromDiskStage`] invoking `Input::from_file` to load inputs #[must_use] pub fn with_from_file(sync_dirs: Vec, interval: Duration) -> Self { - fn load_callback( + fn load_callback( _: &mut Z, _: &mut S, p: &Path, - ) -> Result { + ) -> Result<::Input, Error> + where + ::Input: Input, + { Input::from_file(p) } Self { @@ -240,7 +245,8 @@ where SP: ShMemProvider, E: HasObservers + Executor, for<'a> E::Observers: Deserialize<'a>, - Z: EvaluatorObservers + ExecutionProcessor, + Z: EvaluatorObservers::Input, S> + + ExecutionProcessor::Input, E::Observers, S>, IC: InputConverter::Input, To = DI>, ICB: InputConverter::Input>, DI: Input, diff --git a/libafl/src/stages/tmin.rs b/libafl/src/stages/tmin.rs index e7d072e486..ff449b19fa 100644 --- a/libafl/src/stages/tmin.rs +++ b/libafl/src/stages/tmin.rs @@ -59,9 +59,9 @@ pub struct StdTMinMutationalStage { impl Stage for StdTMinMutationalStage where - Z: HasScheduler - + ExecutionProcessor - + ExecutesInput + Z: HasScheduler<::Input, S> + + ExecutionProcessor::Input, E::Observers, S> + + ExecutesInput::Input, S> + HasFeedback, Z::Scheduler: RemovableScheduler<::Input, S>, E: HasObservers + UsesState, @@ -94,7 +94,7 @@ where &mut self, fuzzer: &mut Z, executor: &mut E, - state: &mut Z::State, + state: &mut S, manager: &mut EM, ) -> Result<(), Error> { self.perform_minification(fuzzer, executor, state, manager)?; @@ -130,14 +130,14 @@ pub static TMIN_STAGE_NAME: &str = "tmin"; impl StdTMinMutationalStage where - Z: HasScheduler - + ExecutionProcessor - + ExecutesInput + Z: HasScheduler<::Input, S> + + ExecutionProcessor::Input, E::Observers, S> + + ExecutesInput::Input, S> + HasFeedback, Z::Scheduler: RemovableScheduler<::Input, S>, - E: HasObservers + UsesState, + E: HasObservers + UsesState, E::Observers: ObserversTuple<::Input, S> + Serialize, - EM: EventFirer, + EM: EventFirer, FF: FeedbackFactory, F: Feedback::Input, E::Observers, S>, S: HasMetadata diff --git a/libafl/src/stages/tracing.rs b/libafl/src/stages/tracing.rs index b0a0442b4f..68f0fdfe76 100644 --- a/libafl/src/stages/tracing.rs +++ b/libafl/src/stages/tracing.rs @@ -42,7 +42,6 @@ where + MaybeHasClientPerfMonitor + UsesInput::Input>, EM: UsesState, //delete me - Z: UsesState, //delete me { #[allow(rustdoc::broken_intra_doc_links)] /// Perform tracing on the given `CorpusId`. Useful for if wrapping [`TracingStage`] with your @@ -87,7 +86,6 @@ where + MaybeHasClientPerfMonitor + UsesInput::Input>, EM: UsesState, - Z: UsesState, ::Input: Input, { #[inline] @@ -183,7 +181,6 @@ where + MaybeHasClientPerfMonitor + UsesInput::Input>, EM: UsesState, - Z: UsesState, { #[inline] fn perform( @@ -232,11 +229,10 @@ where impl ShadowTracingStage where - E: Executor + HasObservers, - S: HasExecutions + HasCorpus, + E: Executor + HasObservers, + S: HasExecutions + HasCorpus + UsesInput, SOT: ObserversTuple<::Input, S>, - EM: UsesState, - Z: UsesState, + EM: UsesState, { /// Creates a new default stage pub fn new(_executor: &mut ShadowExecutor) -> Self { diff --git a/libafl/src/stages/tuneable.rs b/libafl/src/stages/tuneable.rs index a5db42bb87..25a2ec82ef 100644 --- a/libafl/src/stages/tuneable.rs +++ b/libafl/src/stages/tuneable.rs @@ -164,7 +164,7 @@ pub struct TuneableMutationalStage { impl MutationalStage for TuneableMutationalStage where M: Mutator, - Z: Evaluator, + Z: Evaluator::Input, S>, S: HasCorpus + HasRand + HasNamedMetadata + HasMetadata + HasExecutions + HasCurrentTestcase, I: MutatedTransform<::Input, S> + Clone, ::Input: Input, @@ -196,7 +196,7 @@ where impl Stage for TuneableMutationalStage where M: Mutator, - Z: Evaluator, + Z: Evaluator::Input, S>, S: HasCorpus + HasRand + HasNamedMetadata @@ -237,7 +237,7 @@ where impl TuneableMutationalStage where M: Mutator, - Z: Evaluator, + Z: Evaluator::Input, S>, S: HasCorpus + HasRand + HasNamedMetadata @@ -469,8 +469,8 @@ where impl TuneableMutationalStage where - M: Mutator, - Z: Evaluator, + M: Mutator, + Z: Evaluator::Input, S>, S: HasCorpus + HasRand + HasNamedMetadata, { /// Creates a new transforming mutational stage diff --git a/libafl/src/stages/verify_timeouts.rs b/libafl/src/stages/verify_timeouts.rs index b6755dadff..9d62f61b57 100644 --- a/libafl/src/stages/verify_timeouts.rs +++ b/libafl/src/stages/verify_timeouts.rs @@ -86,7 +86,7 @@ where E::Observers: ObserversTuple<::Input, S>, E: Executor + HasObservers + HasTimeout, EM: UsesState, - Z: Evaluator, + Z: Evaluator::Input, S>, S: HasCorpus + HasMetadata + UsesInput::Input>, ::Input: Debug + Serialize + DeserializeOwned + Default + 'static + Clone, { diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 0237aa8712..86477a6aa1 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -725,7 +725,7 @@ where where E: UsesState, EM: EventFirer, - Z: Evaluator, + Z: Evaluator, { if let Some(remaining) = self.remaining_initial_files.as_ref() { // everything was loaded @@ -750,7 +750,7 @@ where where E: UsesState, EM: EventFirer, - Z: Evaluator, + Z: Evaluator, { log::info!("Loading file {:?} ...", &path); let input = (config.loader)(fuzzer, self, path)?; @@ -779,7 +779,7 @@ where where E: UsesState, EM: EventFirer, - Z: Evaluator, + Z: Evaluator, { loop { match self.next_file() { @@ -844,7 +844,7 @@ where where E: UsesState, EM: EventFirer, - Z: Evaluator, + Z: Evaluator, { self.load_initial_inputs_custom_by_filenames( fuzzer, @@ -872,7 +872,7 @@ where where E: UsesState, EM: EventFirer, - Z: Evaluator, + Z: Evaluator, { self.canonicalize_input_dirs(in_dirs)?; self.continue_loading_initial_inputs_custom( @@ -899,7 +899,7 @@ where where E: UsesState, EM: EventFirer, - Z: Evaluator, + Z: Evaluator, { self.load_initial_inputs_custom_by_filenames( fuzzer, @@ -925,7 +925,7 @@ where where E: UsesState, EM: EventFirer, - Z: Evaluator, + Z: Evaluator, { self.canonicalize_input_dirs(in_dirs)?; self.continue_loading_initial_inputs_custom( @@ -952,7 +952,7 @@ where where E: UsesState, EM: EventFirer, - Z: Evaluator, + Z: Evaluator, { self.canonicalize_input_dirs(in_dirs)?; self.continue_loading_initial_inputs_custom( @@ -994,7 +994,7 @@ where where E: UsesState, EM: EventFirer, - Z: Evaluator, + Z: Evaluator, { if self.multicore_inputs_processed.unwrap_or(false) { self.continue_loading_initial_inputs_custom( @@ -1090,7 +1090,7 @@ where E: UsesState, EM: EventFirer, G: Generator<::Input, Self>, - Z: Evaluator, + Z: Evaluator, { let mut added = 0; for _ in 0..num { @@ -1129,7 +1129,7 @@ where E: UsesState, EM: EventFirer, G: Generator<::Input, Self>, - Z: Evaluator, + Z: Evaluator, { self.generate_initial_internal(fuzzer, executor, generator, manager, num, true) } @@ -1147,7 +1147,7 @@ where E: UsesState, EM: EventFirer, G: Generator<::Input, Self>, - Z: Evaluator, + Z: Evaluator, { self.generate_initial_internal(fuzzer, executor, generator, manager, num, false) } diff --git a/libafl_frida/src/executor.rs b/libafl_frida/src/executor.rs index a7feffc839..1f80d2bac0 100644 --- a/libafl_frida/src/executor.rs +++ b/libafl_frida/src/executor.rs @@ -74,7 +74,6 @@ where TC: TargetBytesConverter, OT: ObserversTuple, RT: FridaRuntimeTuple, - Z: UsesState, { /// Instruct the target about the input and run #[inline] diff --git a/libafl_libfuzzer/runtime/src/fuzz.rs b/libafl_libfuzzer/runtime/src/fuzz.rs index 94f3954559..17827c9c14 100644 --- a/libafl_libfuzzer/runtime/src/fuzz.rs +++ b/libafl_libfuzzer/runtime/src/fuzz.rs @@ -62,7 +62,7 @@ fn do_fuzz( mgr: &mut EM, ) -> Result<(), Error> where - F: Fuzzer, + F: Fuzzer, S: HasMetadata + HasExecutions + UsesInput diff --git a/libafl_libfuzzer/runtime/src/report.rs b/libafl_libfuzzer/runtime/src/report.rs index 44ee09cad1..8c22b28bde 100644 --- a/libafl_libfuzzer/runtime/src/report.rs +++ b/libafl_libfuzzer/runtime/src/report.rs @@ -23,7 +23,7 @@ fn do_report( _mgr: &mut EM, ) -> Result<(), Error> where - F: Fuzzer, + F: Fuzzer, S: HasMetadata + HasNamedMetadata + HasExecutions diff --git a/libafl_libfuzzer/runtime/src/tmin.rs b/libafl_libfuzzer/runtime/src/tmin.rs index dab8f27433..44ff647796 100644 --- a/libafl_libfuzzer/runtime/src/tmin.rs +++ b/libafl_libfuzzer/runtime/src/tmin.rs @@ -13,7 +13,7 @@ use libafl::{ schedulers::QueueScheduler, stages::StdTMinMutationalStage, state::{HasCorpus, StdState}, - Error, Fuzzer, StdFuzzer, + Error, ExecutesInput, Fuzzer, StdFuzzer, }; use libafl_bolts::{ rands::{RomuDuoJrRand, StdRand}, diff --git a/libafl_nyx/src/executor.rs b/libafl_nyx/src/executor.rs index 5f100a5c83..c83e35ade2 100644 --- a/libafl_nyx/src/executor.rs +++ b/libafl_nyx/src/executor.rs @@ -50,7 +50,6 @@ where EM: UsesState, S: State + HasExecutions, S::Input: HasTargetBytes, - Z: UsesState, OT: ObserversTuple, { fn run_target( diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 6a56c06c4b..b5ce9791c5 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -98,7 +98,7 @@ pub unsafe fn inproc_qemu_timeout_handler( ET: EmulatorModuleTuple, OF: Feedback, S: State + Unpin, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { @@ -166,9 +166,10 @@ where EM: EventFirer + EventRestarter, OF: Feedback, S: Unpin + State + HasExecutions + HasCorpus + HasSolutions, - Z: HasObjective - + HasScheduler - + ExecutionProcessor, + S::Corpus: Corpus, + Z: HasObjective + + HasScheduler<::Input, S> + + ExecutionProcessor::Input, OT, S>, S::Solutions: Corpus, //delete me ::Input: Clone, //delete me { @@ -241,7 +242,6 @@ where H: FnMut(&mut Emulator, &mut S, &S::Input) -> ExitKind, OT: ObserversTuple, S: State + HasExecutions + Unpin, - Z: UsesState, { fn run_target( &mut self, @@ -315,7 +315,6 @@ where OT: ObserversTuple, S: UsesInput, SP: ShMemProvider, - Z: UsesState, { inner: QemuInProcessForkExecutor<'a, CM, ED, EM, ET, H, OT, S, SM, SP, Z>, } @@ -333,7 +332,6 @@ where S: UsesInput + Debug, SM: Debug, SP: ShMemProvider, - Z: UsesState, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QemuForkExecutor") @@ -354,7 +352,7 @@ where OT: ObserversTuple, S: State + HasSolutions, SP: ShMemProvider, - Z: HasObjective, + Z: HasObjective, Z::Objective: Feedback, { #[allow(clippy::too_many_arguments)] @@ -416,7 +414,7 @@ where OT: ObserversTuple + Debug, S: State + HasExecutions + Unpin, SP: ShMemProvider, - Z: HasObjective, + Z: HasObjective, { fn run_target( &mut self, @@ -452,7 +450,6 @@ where OT: ObserversTuple, S: State, SP: ShMemProvider, - Z: UsesState, { type State = S; } @@ -468,7 +465,6 @@ where OT: ObserversTuple, S: State, SP: ShMemProvider, - Z: UsesState, { type Observers = OT; #[inline] diff --git a/libafl_targets/src/cmps/stages/aflpptracing.rs b/libafl_targets/src/cmps/stages/aflpptracing.rs index 6749ea7feb..99f37ffb12 100644 --- a/libafl_targets/src/cmps/stages/aflpptracing.rs +++ b/libafl_targets/src/cmps/stages/aflpptracing.rs @@ -38,7 +38,6 @@ impl Named for AFLppCmplogTracingStage<'_, EM, TE, S, Z> { impl Stage for AFLppCmplogTracingStage<'_, EM, TE, S, Z> where EM: UsesState, - Z: UsesState, TE: HasObservers + Executor, TE::Observers: MatchNameRef + ObserversTuple, S: HasCorpus diff --git a/libafl_targets/src/windows_asan.rs b/libafl_targets/src/windows_asan.rs index 3ba804dd3e..1f23c63132 100644 --- a/libafl_targets/src/windows_asan.rs +++ b/libafl_targets/src/windows_asan.rs @@ -37,7 +37,7 @@ where OF: Feedback, E::State: HasSolutions + HasCorpus + HasExecutions, E::Observers: ObserversTuple<::Input, E::State>, - Z: HasObjective, + Z: HasObjective, <::State as HasSolutions>::Solutions: Corpus, //delete me <<::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me { diff --git a/libafl_tinyinst/src/executor.rs b/libafl_tinyinst/src/executor.rs index 6dca42c5ae..b4b22d58fe 100644 --- a/libafl_tinyinst/src/executor.rs +++ b/libafl_tinyinst/src/executor.rs @@ -54,7 +54,6 @@ where S: State + HasExecutions, S::Input: HasTargetBytes, SP: ShMemProvider, - Z: UsesState, { #[inline] fn run_target( From c9eb2a85d9e965bc63b6ede33fe260c9f01a24c0 Mon Sep 17 00:00:00 2001 From: "Dongjia \"toka\" Zhang" Date: Thu, 12 Dec 2024 22:02:34 +0100 Subject: [PATCH 09/34] Remove useless cfgs (#2764) --- libafl/src/corpus/cached.rs | 1 - libafl/src/corpus/inmemory_ondisk.rs | 7 +++---- libafl/src/corpus/ondisk.rs | 1 - libafl/src/executors/command.rs | 2 +- libafl/src/executors/hooks/timer.rs | 4 ++-- libafl/src/executors/inprocess_fork/mod.rs | 2 +- libafl/src/observers/stdio.rs | 9 +++------ libafl/src/stages/sync.rs | 2 +- 8 files changed, 11 insertions(+), 17 deletions(-) diff --git a/libafl/src/corpus/cached.rs b/libafl/src/corpus/cached.rs index dfa7c15f34..2e21fee65c 100644 --- a/libafl/src/corpus/cached.rs +++ b/libafl/src/corpus/cached.rs @@ -18,7 +18,6 @@ use crate::{ /// A corpus that keeps a maximum number of [`Testcase`]s in memory /// and load them from disk, when they are being used. /// The eviction policy is FIFO. -#[cfg(feature = "std")] #[derive(Default, Serialize, Deserialize, Clone, Debug)] pub struct CachedOnDiskCorpus { inner: InMemoryOnDiskCorpus, diff --git a/libafl/src/corpus/inmemory_ondisk.rs b/libafl/src/corpus/inmemory_ondisk.rs index 58be73441d..e879881098 100644 --- a/libafl/src/corpus/inmemory_ondisk.rs +++ b/libafl/src/corpus/inmemory_ondisk.rs @@ -6,11 +6,11 @@ use alloc::string::String; use core::cell::RefCell; -#[cfg(feature = "std")] -use std::{fs, fs::File, io::Write}; use std::{ - fs::OpenOptions, + fs, + fs::{File, OpenOptions}, io, + io::Write, path::{Path, PathBuf}, }; @@ -50,7 +50,6 @@ fn try_create_new>(path: P) -> Result, io::Error> { /// A corpus able to store [`Testcase`]s to disk, while also keeping all of them in memory. /// /// Metadata is written to a `..metadata` file in the same folder by default. -#[cfg(feature = "std")] #[derive(Default, Serialize, Deserialize, Clone, Debug)] pub struct InMemoryOnDiskCorpus { inner: InMemoryCorpus, diff --git a/libafl/src/corpus/ondisk.rs b/libafl/src/corpus/ondisk.rs index db32fd37d9..5c6569538f 100644 --- a/libafl/src/corpus/ondisk.rs +++ b/libafl/src/corpus/ondisk.rs @@ -22,7 +22,6 @@ use crate::{ }; /// Options for the the format of the on-disk metadata -#[cfg(feature = "std")] #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub enum OnDiskMetadataFormat { /// A binary-encoded postcard diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 8471f91b03..9218dfd18a 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -862,7 +862,7 @@ pub trait CommandConfigurator: Sized { } /// waitpid wrapper that ignores some signals sent by the ptraced child -#[cfg(all(feature = "std", target_os = "linux"))] +#[cfg(target_os = "linux")] fn waitpid_filtered(pid: Pid, options: Option) -> Result { loop { let wait_status = waitpid(pid, options); diff --git a/libafl/src/executors/hooks/timer.rs b/libafl/src/executors/hooks/timer.rs index 21d5541348..bf0639f389 100644 --- a/libafl/src/executors/hooks/timer.rs +++ b/libafl/src/executors/hooks/timer.rs @@ -58,7 +58,7 @@ pub(crate) struct Itimerval { pub it_value: Timeval, } -#[cfg(all(feature = "std", unix, not(target_os = "linux")))] +#[cfg(all(unix, not(target_os = "linux")))] extern "C" { pub(crate) fn setitimer( which: libc::c_int, @@ -102,7 +102,7 @@ pub struct TimerStruct { pub(crate) tmout_start_time: Duration, } -#[cfg(all(feature = "std", windows))] +#[cfg(windows)] #[allow(non_camel_case_types)] type PTP_TIMER_CALLBACK = unsafe extern "system" fn( param0: PTP_CALLBACK_INSTANCE, diff --git a/libafl/src/executors/inprocess_fork/mod.rs b/libafl/src/executors/inprocess_fork/mod.rs index b3386cc03b..e313405bdc 100644 --- a/libafl/src/executors/inprocess_fork/mod.rs +++ b/libafl/src/executors/inprocess_fork/mod.rs @@ -349,7 +349,7 @@ pub mod child_signal_handlers { } #[cfg(test)] -#[cfg(all(feature = "std", feature = "fork", unix))] +#[cfg(all(feature = "fork", unix))] mod tests { use libafl_bolts::tuples::tuple_list; use serial_test::serial; diff --git a/libafl/src/observers/stdio.rs b/libafl/src/observers/stdio.rs index bab111c9d4..266cb68110 100644 --- a/libafl/src/observers/stdio.rs +++ b/libafl/src/observers/stdio.rs @@ -3,7 +3,7 @@ //! The [`StdOutObserver`] and [`StdErrObserver`] observers look at the stdout of a program //! The executor must explicitly support these observers. #![cfg_attr( - all(feature = "std", unix), + unix, doc = r"For example, they are supported on the [`crate::executors::CommandExecutor`]." )] @@ -19,11 +19,8 @@ use crate::{observers::Observer, Error}; /// Only works for supported executors. /// /// # Example usage -#[cfg_attr(all(feature = "std", target_os = "linux", not(miri)), doc = " ```")] // miri doesn't like the Command crate, linux as a shorthand for the availability of base64 -#[cfg_attr( - not(all(feature = "std", target_os = "linux", not(miri))), - doc = " ```ignore" -)] +#[cfg_attr(all(target_os = "linux", not(miri)), doc = " ```")] // miri doesn't like the Command crate, linux as a shorthand for the availability of base64 +#[cfg_attr(not(all(target_os = "linux", not(miri))), doc = " ```ignore")] /// use std::borrow::Cow; /// /// use libafl::{ diff --git a/libafl/src/stages/sync.rs b/libafl/src/stages/sync.rs index a6cbcd3783..7f51ed6f13 100644 --- a/libafl/src/stages/sync.rs +++ b/libafl/src/stages/sync.rs @@ -282,7 +282,7 @@ where client_config: EventConfig::AlwaysUnique, time: current_time(), forward_id: None, - #[cfg(all(unix, feature = "std", feature = "multi_machine"))] + #[cfg(all(unix, feature = "multi_machine"))] node_id: None, }, )?; From 93b64f9afb101c211825a37f2172c0d611037f1b Mon Sep 17 00:00:00 2001 From: Sharad Khanna Date: Sat, 14 Dec 2024 13:54:19 -0500 Subject: [PATCH 10/34] Link libresolv on all Apple OSs (#2767) --- libafl_frida/build.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libafl_frida/build.rs b/libafl_frida/build.rs index f7e10787fd..7d0656cc4c 100644 --- a/libafl_frida/build.rs +++ b/libafl_frida/build.rs @@ -18,6 +18,9 @@ fn main() { println!("cargo:rustc-link-lib=dylib=c++"); } + #[cfg(target_vendor = "apple")] + println!("cargo:rustc-link-lib=dylib=resolv"); + println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=test_harness.cpp"); println!("cargo:rerun-if-changed=src/gettls.c"); From 294d2f16d447863634949fec2c52fcbd1683e8da Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 15 Dec 2024 14:40:58 +0100 Subject: [PATCH 11/34] Somewhat ugly CI fix... (#2768) * Maybe fix CI * does this help? * Very dirty 'fix' --- Cargo.toml | 2 +- libafl/src/observers/cmp.rs | 77 ++++++++++++++++++++----------------- libafl_intelpt/src/lib.rs | 1 + 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2a4ece4187..7f82cd0b1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,7 +84,7 @@ ahash = { version = "0.8.11", default-features = false } # The hash function alr arbitrary-int = "1.2.7" # arbitrary sized integers, useful in combination with bitfields (bitbybit crate) backtrace = { version = "0.3.74", default-features = false } # Used to get the stacktrace in StacktraceObserver bindgen = "0.71.1" -bitbybit = "1.3.2" # bitfields, use this for bit fields and bit enums +bitbybit = "1.3.3" # bitfields, use this for bit fields and bit enums clap = "4.5.18" cc = "1.1.21" cmake = "0.1.51" diff --git a/libafl/src/observers/cmp.rs b/libafl/src/observers/cmp.rs index de35f6e1f3..b139ddb3a4 100644 --- a/libafl/src/observers/cmp.rs +++ b/libafl/src/observers/cmp.rs @@ -5,8 +5,6 @@ use core::{ ops::{Deref, DerefMut}, }; -use arbitrary_int::{u1, u4, u5, u6}; -use bitbybit::bitfield; use hashbrown::HashMap; use libafl_bolts::{ownedref::OwnedRefMut, AsSlice, HasLen, Named}; use serde::{Deserialize, Serialize}; @@ -405,40 +403,47 @@ impl AFLppCmpValuesMetadata { } } -/// Comparison header, used to describe a set of comparison values efficiently. -/// -/// # Bitfields -/// -/// - hits: The number of hits of a particular comparison -/// - id: Unused by ``LibAFL``, a unique ID for a particular comparison -/// - shape: Whether a comparison is u8/u8, u16/u16, etc. -/// - type_: Whether the comparison value represents an instruction (like a `cmp`) or function -/// call arguments -/// - attribute: OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform -/// - overflow: Whether the comparison overflows -/// - reserved: Reserved for future use -#[bitfield(u16)] -#[derive(Debug)] -pub struct AFLppCmpLogHeader { - /// The number of hits of a particular comparison - /// - /// 6 bits up to 63 entries, we have CMP_MAP_H = 32 (so using half of it) - #[bits(0..=5, r)] - hits: u6, - /// Whether a comparison is u8/u8, u16/u16, etc. - /// - /// 31 + 1 bytes max - #[bits(6..=10, r)] - shape: u5, - /// Whether the comparison value represents an instruction (like a `cmp`) or function call - /// arguments +#[allow(missing_docs)] // 2024-12-15: bitfield is leading CI to fail due to missing docs. +mod aflpp_cmplog_header { + use arbitrary_int::{u1, u4, u5, u6}; + use bitbybit::bitfield; + + /// Comparison header, used to describe a set of comparison values efficiently. /// - /// 2: cmp, rtn - #[bit(11, r)] - type_: u1, - /// OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform + /// # Bitfields /// - /// 16 types for arithmetic comparison types - #[bits(12..=15, r)] - attribute: u4, + /// - hits: The number of hits of a particular comparison + /// - id: Unused by ``LibAFL``, a unique ID for a particular comparison + /// - shape: Whether a comparison is u8/u8, u16/u16, etc. + /// - type_: Whether the comparison value represents an instruction (like a `cmp`) or function + /// call arguments + /// - attribute: OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform + /// - overflow: Whether the comparison overflows + /// - reserved: Reserved for future use + #[bitfield(u16)] + #[derive(Debug)] + pub struct AFLppCmpLogHeader { + /// The number of hits of a particular comparison + /// + /// 6 bits up to 63 entries, we have CMP_MAP_H = 32 (so using half of it) + #[bits(0..=5, r)] + hits: u6, + /// Whether a comparison is u8/u8, u16/u16, etc. + /// + /// 31 + 1 bytes max + #[bits(6..=10, r)] + shape: u5, + /// Whether the comparison value represents an instruction (like a `cmp`) or function call + /// arguments + /// + /// 2: cmp, rtn + #[bit(11, r)] + type_: u1, + /// OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform + /// + /// 16 types for arithmetic comparison types + #[bits(12..=15, r)] + attribute: u4, + } } +pub use aflpp_cmplog_header::AFLppCmpLogHeader; diff --git a/libafl_intelpt/src/lib.rs b/libafl_intelpt/src/lib.rs index 0530aa33fc..c2ab683858 100644 --- a/libafl_intelpt/src/lib.rs +++ b/libafl_intelpt/src/lib.rs @@ -695,6 +695,7 @@ impl IntelPTBuilder { /// Perf event config for `IntelPT` /// /// (This is almost mapped to `IA32_RTIT_CTL MSR` by perf) +#[allow(missing_docs)] // 2024-12-15: bitfield is leading CI to fail due to missing docs. #[cfg(target_os = "linux")] #[bitfield(u64, default = 0)] struct PtConfig { From c170986fe9998452998f4ea9a6d38cc8e52a0d80 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sun, 15 Dec 2024 15:00:41 +0100 Subject: [PATCH 12/34] Add Input Types and Mutators for Numeric Types (#2760) * fixing empty multipart name * fixing clippy * New rules for the contributing (#2752) * Rules * more * aa * Improve Flexibility of DumpToDiskStage (#2753) * fixing empty multipart name * fixing clippy * improve flexibility of DumpToDiskStage * adding note to MIGRATION.md * Introduce WrappingMutator * introducing mutators for int types * fixing no_std * random fixes * Add hash derivation for WrappingInput * Revert fixes that broke things * Derive Default on WrappingInput * Add unit tests * Fixes according to code review * introduce mappable ValueInputs * remove unnecessary comments * Elide more lifetimes * remove dead code * simplify hashing * improve docs * improve randomization * rename method to align with standard library * add typedefs for int types for ValueMutRefInput * rename test * add safety notice to trait function * improve randomize performance for i128/u128 * rename macro * improve comment * actually check return values in test * make 128 bit int randomize even more efficient * shifting signed values --------- Co-authored-by: Dongjia "toka" Zhang Co-authored-by: Dominik Maier --- .../baby_fuzzer_custom_input/src/input.rs | 27 +- .../baby_fuzzer_custom_input/src/main.rs | 52 +- libafl/src/inputs/bytes.rs | 96 +--- libafl/src/inputs/mod.rs | 41 +- libafl/src/inputs/value.rs | 339 +++++++++++++ libafl/src/mutators/mod.rs | 2 + libafl/src/mutators/mutations.rs | 44 +- libafl/src/mutators/numeric.rs | 478 ++++++++++++++++++ libafl_bolts/src/lib.rs | 17 +- 9 files changed, 948 insertions(+), 148 deletions(-) create mode 100644 libafl/src/inputs/value.rs create mode 100644 libafl/src/mutators/numeric.rs diff --git a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs index 6c45c88939..557a51d1ba 100644 --- a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs +++ b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/input.rs @@ -1,18 +1,15 @@ use core::num::NonZeroUsize; -use std::{ - borrow::Cow, - hash::{DefaultHasher, Hash, Hasher}, -}; +use std::{borrow::Cow, hash::Hash}; use libafl::{ corpus::CorpusId, generators::{Generator, RandBytesGenerator}, - inputs::{BytesInput, HasTargetBytes, Input, MutVecInput}, + inputs::{value::MutI16Input, BytesInput, HasTargetBytes, Input, MutVecInput}, mutators::{MutationResult, Mutator}, state::HasRand, Error, SerdeAny, }; -use libafl_bolts::{rands::Rand, Named}; +use libafl_bolts::{generic_hash_std, rands::Rand, Named}; use serde::{Deserialize, Serialize}; /// The custom [`Input`] type used in this example, consisting of a byte array part, a byte array that is not always present, and a boolean @@ -20,20 +17,20 @@ use serde::{Deserialize, Serialize}; /// Imagine these could be used to model command line arguments for a bash command, where /// - `byte_array` is binary data that is always needed like what is passed to stdin, /// - `optional_byte_array` is binary data passed as a command line arg, and it is only passed if it is not `None` in the input, +/// - `num` is an arbitrary number (`i16` in this case) /// - `boolean` models the presence or absence of a command line flag that does not require additional data #[derive(Serialize, Deserialize, Clone, Debug, Hash, SerdeAny)] pub struct CustomInput { pub byte_array: Vec, pub optional_byte_array: Option>, + pub num: i16, pub boolean: bool, } /// Hash-based implementation impl Input for CustomInput { fn generate_name(&self, _id: Option) -> String { - let mut hasher = DefaultHasher::new(); - self.hash(&mut hasher); - format!("{:016x}", hasher.finish()) + format!("{:016x}", generic_hash_std(self)) } } @@ -57,6 +54,16 @@ impl CustomInput { pub fn optional_byte_array(&self) -> Option<&[u8]> { self.optional_byte_array.as_deref() } + + /// Returns a mutable reference to the number + pub fn num_mut(&mut self) -> MutI16Input<'_> { + (&mut self.num).into() + } + + /// Returns an immutable reference to the number + pub fn num(&self) -> &i16 { + &self.num + } } /// A generator for [`CustomInput`] used in this example @@ -86,10 +93,12 @@ where .coinflip(0.5) .then(|| generator.generate(state).unwrap().target_bytes().into()); let boolean = state.rand_mut().coinflip(0.5); + let num = state.rand_mut().next() as i16; Ok(CustomInput { byte_array, optional_byte_array, + num, boolean, }) } diff --git a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs index 873e26dea1..3102ddc0f2 100644 --- a/fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs +++ b/fuzzers/structure_aware/baby_fuzzer_custom_input/src/main.rs @@ -8,7 +8,10 @@ use input::{ CustomInput, CustomInputGenerator, ToggleBooleanMutator, ToggleOptionalByteArrayMutator, }; #[cfg(feature = "simple_interface")] -use libafl::mutators::havoc_mutations::{mapped_havoc_mutations, optional_mapped_havoc_mutations}; +use libafl::mutators::{ + havoc_mutations::{mapped_havoc_mutations, optional_mapped_havoc_mutations}, + numeric::mapped_int_mutators, +}; use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus}, events::SimpleEventManager, @@ -32,6 +35,7 @@ use { libafl::mutators::{ havoc_mutations::{havoc_crossover_with_corpus_mapper, havoc_mutations_no_crossover}, mapping::{ToMappedInputFunctionMappingMutatorMapper, ToOptionMappingMutatorMapper}, + numeric::{int_mutators_no_crossover, mapped_int_mutators_crossover}, }, libafl_bolts::tuples::Map, }; @@ -43,7 +47,7 @@ static mut SIGNALS_PTR: *mut u8 = &raw mut SIGNALS as _; /// Assign a signal to the signals map fn signals_set(idx: usize) { - if idx > 2 { + if idx > 3 { println!("Setting signal: {idx}"); } unsafe { write(SIGNALS_PTR.add(idx), 1) }; @@ -60,17 +64,21 @@ pub fn main() { signals_set(1); if input.optional_byte_array == Some(vec![b'b']) { signals_set(2); - if input.boolean { - #[cfg(unix)] - panic!("Artificial bug triggered =)"); - - // panic!() raises a STATUS_STACK_BUFFER_OVERRUN exception which cannot be caught by the exception handler. - // Here we make it raise STATUS_ACCESS_VIOLATION instead. - // Extending the windows exception handler is a TODO. Maybe we can refer to what winafl code does. - // https://github.com/googleprojectzero/winafl/blob/ea5f6b85572980bb2cf636910f622f36906940aa/winafl.c#L728 - #[cfg(windows)] - unsafe { - write_volatile(0 as *mut u32, 0); + // require input.num to be in the top 1% of possible values + if input.num > i16::MAX - i16::MAX / 50 { + signals_set(3); + if input.boolean { + #[cfg(unix)] + panic!("Artificial bug triggered =)"); + + // panic!() raises a STATUS_STACK_BUFFER_OVERRUN exception which cannot be caught by the exception handler. + // Here we make it raise STATUS_ACCESS_VIOLATION instead. + // Extending the windows exception handler is a TODO. Maybe we can refer to what winafl code does. + // https://github.com/googleprojectzero/winafl/blob/ea5f6b85572980bb2cf636910f622f36906940aa/winafl.c#L728 + #[cfg(windows)] + unsafe { + write_volatile(0 as *mut u32, 0); + } } } } @@ -136,7 +144,7 @@ pub fn main() { .expect("Failed to generate the initial corpus"); #[cfg(feature = "simple_interface")] - let (mapped_mutators, optional_mapped_mutators) = { + let (mapped_mutators, optional_mapped_mutators, int_mutators) = { // Creating mutators that will operate on input.byte_array let mapped_mutators = mapped_havoc_mutations(CustomInput::byte_array_mut, CustomInput::byte_array); @@ -146,11 +154,13 @@ pub fn main() { CustomInput::optional_byte_array_mut, CustomInput::optional_byte_array, ); - (mapped_mutators, optional_mapped_mutators) + + let int_mutators = mapped_int_mutators(CustomInput::num_mut, CustomInput::num); + (mapped_mutators, optional_mapped_mutators, int_mutators) }; #[cfg(not(feature = "simple_interface"))] - let (mapped_mutators, optional_mapped_mutators) = { + let (mapped_mutators, optional_mapped_mutators, int_mutators) = { // Creating mutators that will operate on input.byte_array let mapped_mutators = havoc_mutations_no_crossover() .merge(havoc_crossover_with_corpus_mapper(CustomInput::byte_array)) @@ -168,7 +178,13 @@ pub fn main() { CustomInput::optional_byte_array_mut, )); - (mapped_mutators, optional_mapped_mutators) + // Creating mutators that will operate on input.num + let int_mutators = int_mutators_no_crossover() + .merge(mapped_int_mutators_crossover(CustomInput::num)) + .map(ToMappedInputFunctionMappingMutatorMapper::new( + CustomInput::num_mut, + )); + (mapped_mutators, optional_mapped_mutators, int_mutators) }; // Merging multiple lists of mutators that mutate a sub-part of the custom input @@ -178,6 +194,8 @@ pub fn main() { .merge(mapped_mutators) // Then, mutators for the optional byte array, these return MutationResult::Skipped if the part is not present .merge(optional_mapped_mutators) + // Then, mutators for the number + .merge(int_mutators) // A custom mutator that sets the optional byte array to None if present, and generates a random byte array of length 1 if it is not .prepend(ToggleOptionalByteArrayMutator::new(nonzero!(1))) // Finally, a custom mutator that toggles the boolean part of the input diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index 127e672953..55bf30b35c 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -1,61 +1,20 @@ //! The `BytesInput` is the "normal" input, a map of bytes, that can be sent directly to the client //! (As opposed to other, more abstract, inputs, like an Grammar-Based AST Input) -use alloc::{borrow::ToOwned, rc::Rc, string::String, vec::Vec}; -use core::{ - cell::RefCell, - hash::{BuildHasher, Hasher}, +use alloc::{ + borrow::ToOwned, + rc::Rc, + vec::{self, Vec}, }; -#[cfg(feature = "std")] -use std::{fs::File, io::Read, path::Path}; +use core::cell::RefCell; -use ahash::RandomState; -#[cfg(feature = "std")] -use libafl_bolts::{fs::write_file_atomic, Error}; use libafl_bolts::{ownedref::OwnedSlice, HasLen}; -use serde::{Deserialize, Serialize}; -use crate::{ - corpus::CorpusId, - inputs::{HasMutatorBytes, HasTargetBytes, Input}, -}; +use super::ValueInput; +use crate::inputs::{HasMutatorBytes, HasTargetBytes}; /// A bytes input is the basic input -#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash)] -pub struct BytesInput { - /// The raw input bytes - pub(crate) bytes: Vec, -} - -impl Input for BytesInput { - #[cfg(feature = "std")] - /// Write this input to the file - fn to_file

(&self, path: P) -> Result<(), Error> - where - P: AsRef, - { - write_file_atomic(path, &self.bytes) - } - - /// Load the content of this input from a file - #[cfg(feature = "std")] - fn from_file

(path: P) -> Result - where - P: AsRef, - { - let mut file = File::open(path)?; - let mut bytes: Vec = vec![]; - file.read_to_end(&mut bytes)?; - Ok(BytesInput::new(bytes)) - } - - /// Generate a name for this input - fn generate_name(&self, _id: Option) -> String { - let mut hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher(); - hasher.write(self.bytes()); - format!("{:016x}", hasher.finish()) - } -} +pub type BytesInput = ValueInput>; /// Rc Ref-cell from Input impl From for Rc> { @@ -65,57 +24,48 @@ impl From for Rc> { } impl HasMutatorBytes for BytesInput { - #[inline] fn bytes(&self) -> &[u8] { - &self.bytes + self.as_ref() } - #[inline] fn bytes_mut(&mut self) -> &mut [u8] { - &mut self.bytes + self.as_mut() } fn resize(&mut self, new_len: usize, value: u8) { - self.bytes.resize(new_len, value); + self.as_mut().resize(new_len, value); } fn extend<'a, I: IntoIterator>(&mut self, iter: I) { - Extend::extend(&mut self.bytes, iter); + self.as_mut().extend(iter); } - fn splice(&mut self, range: R, replace_with: I) -> alloc::vec::Splice<'_, I::IntoIter> + fn splice(&mut self, range: R, replace_with: I) -> vec::Splice<'_, I::IntoIter> where R: core::ops::RangeBounds, I: IntoIterator, { - self.bytes.splice(range, replace_with) + self.as_mut().splice(range, replace_with) } - fn drain(&mut self, range: R) -> alloc::vec::Drain<'_, u8> + fn drain(&mut self, range: R) -> vec::Drain<'_, u8> where R: core::ops::RangeBounds, { - self.bytes.drain(range) + self.as_mut().drain(range) } } impl HasTargetBytes for BytesInput { #[inline] fn target_bytes(&self) -> OwnedSlice { - OwnedSlice::from(&self.bytes) + OwnedSlice::from(self.as_ref()) } } impl HasLen for BytesInput { - #[inline] fn len(&self) -> usize { - self.bytes.len() - } -} - -impl From> for BytesInput { - fn from(bytes: Vec) -> Self { - Self::new(bytes) + self.as_ref().len() } } @@ -127,14 +77,6 @@ impl From<&[u8]> for BytesInput { impl From for Vec { fn from(value: BytesInput) -> Vec { - value.bytes - } -} - -impl BytesInput { - /// Creates a new bytes input using the given bytes - #[must_use] - pub const fn new(bytes: Vec) -> Self { - Self { bytes } + value.into_inner() } } diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index 240be96917..dc7a3e5b99 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -3,6 +3,9 @@ pub mod bytes; pub use bytes::BytesInput; +pub mod value; +pub use value::ValueInput; + pub mod encoded; pub use encoded::*; @@ -28,7 +31,12 @@ use alloc::{ string::{String, ToString}, vec::{Drain, Splice, Vec}, }; -use core::{clone::Clone, fmt::Debug, marker::PhantomData, ops::RangeBounds}; +use core::{ + clone::Clone, + fmt::Debug, + marker::PhantomData, + ops::{Deref, DerefMut, RangeBounds}, +}; #[cfg(feature = "std")] use std::{fs::File, hash::Hash, io::Read, path::Path}; @@ -42,6 +50,7 @@ use libafl_bolts::{ #[cfg(feature = "nautilus")] pub use nautilus::*; use serde::{Deserialize, Serialize}; +use value::ValueMutRefInput; use crate::corpus::CorpusId; @@ -210,36 +219,29 @@ where } /// A wrapper type that allows us to use mutators for Mutators for `&mut `[`Vec`]. -#[derive(Debug)] -pub struct MutVecInput<'a>(&'a mut Vec); - -impl<'a> From<&'a mut Vec> for MutVecInput<'a> { - fn from(value: &'a mut Vec) -> Self { - Self(value) - } -} +pub type MutVecInput<'a> = ValueMutRefInput<'a, Vec>; impl HasLen for MutVecInput<'_> { fn len(&self) -> usize { - self.0.len() + self.deref().len() } } impl HasMutatorBytes for MutVecInput<'_> { fn bytes(&self) -> &[u8] { - self.0 + self } fn bytes_mut(&mut self) -> &mut [u8] { - self.0 + self } fn resize(&mut self, new_len: usize, value: u8) { - self.0.resize(new_len, value); + self.deref_mut().resize(new_len, value); } fn extend<'b, I: IntoIterator>(&mut self, iter: I) { - self.0.extend(iter); + self.deref_mut().extend(iter); } fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter> @@ -247,24 +249,17 @@ impl HasMutatorBytes for MutVecInput<'_> { R: RangeBounds, I: IntoIterator, { - self.0.splice::(range, replace_with) + self.deref_mut().splice::(range, replace_with) } fn drain(&mut self, range: R) -> Drain<'_, u8> where R: RangeBounds, { - self.0.drain(range) + self.deref_mut().drain(range) } } -impl MappedInput for MutVecInput<'_> { - type Type<'b> - = MutVecInput<'b> - where - Self: 'b; -} - /// Defines the input type shared across traits of the type. /// Needed for consistency across HasCorpus/HasSolutions and friends. pub trait UsesInput { diff --git a/libafl/src/inputs/value.rs b/libafl/src/inputs/value.rs new file mode 100644 index 0000000000..06922c5f95 --- /dev/null +++ b/libafl/src/inputs/value.rs @@ -0,0 +1,339 @@ +//! Newtype pattern style wrapper for [`super::Input`]s + +use alloc::{string::String, vec::Vec}; +use core::{ + fmt::Debug, + hash::Hash, + ops::{Deref, DerefMut}, +}; + +use libafl_bolts::{generic_hash_std, rands::Rand}; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] +use { + libafl_bolts::{fs::write_file_atomic, Error}, + std::{fs::File, io::Read, path::Path}, +}; + +use super::{Input, MappedInput}; +use crate::{corpus::CorpusId, mutators::numeric::Numeric}; + +/// Newtype pattern wrapper around an underlying structure to implement inputs +/// +/// This does not blanket implement [`super::Input`], because for certain inputs, writing them to disk does not make sense, because they don't own their data (like [`super::MutVecInput`]) +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)] +pub struct ValueInput(I); + +impl From for ValueInput { + fn from(value: I) -> Self { + Self(value) + } +} + +impl ValueInput { + /// Create a new [`ValueInput`] + pub const fn new(value: I) -> Self { + Self(value) + } + + /// Extract the inner value + pub fn into_inner(self) -> I { + self.0 + } +} + +impl AsRef for ValueInput { + fn as_ref(&self) -> &I { + &self.0 + } +} + +impl AsMut for ValueInput { + fn as_mut(&mut self) -> &mut I { + &mut self.0 + } +} + +impl Copy for ValueInput {} + +// Macro to implement the `Input` trait and create type aliases for `WrappingInput` +macro_rules! impl_input_for_value_input { + ($($t:ty => $name:ident),+ $(,)?) => { + $( + impl Input for ValueInput<$t> { + fn generate_name(&self, _id: Option) -> String { + format!("{:016x}", generic_hash_std(self)) + } + } + + /// Input wrapping a <$t> + pub type $name = ValueInput<$t>; + )* + }; +} + +// Invoke the macro with type-name pairs +impl_input_for_value_input!( + u8 => U8Input, + u16 => U16Input, + u32 => U32Input, + u64 => U64Input, + u128 => U128Input, + usize => UsizeInput, + i8 => I8Input, + i16 => I16Input, + i32 => I32Input, + i64 => I64Input, + i128 => I128Input, + isize => IsizeInput, +); + +/// manually implemented because files can be written more efficiently +impl Input for ValueInput> { + fn generate_name(&self, _id: Option) -> String { + format!("{:016x}", generic_hash_std(self)) + } + + /// Write this input to the file + #[cfg(feature = "std")] + fn to_file

(&self, path: P) -> Result<(), Error> + where + P: AsRef, + { + write_file_atomic(path, self.as_ref())?; + Ok(()) + } + + /// Load the content of this input from a file + #[cfg(feature = "std")] + fn from_file

(path: P) -> Result + where + P: AsRef, + { + let mut file = File::open(path)?; + let mut data = vec![]; + file.read_to_end(&mut data)?; + Ok(data.into()) + } +} + +impl Numeric for ValueInput +where + I: Numeric, +{ + fn flip_all_bits(&mut self) { + self.as_mut().flip_all_bits(); + } + + fn flip_bit_at(&mut self, rhs: usize) { + self.as_mut().flip_bit_at(rhs); + } + + fn wrapping_inc(&mut self) { + self.as_mut().wrapping_inc(); + } + + fn wrapping_dec(&mut self) { + self.as_mut().wrapping_dec(); + } + + fn twos_complement(&mut self) { + self.as_mut().twos_complement(); + } + + fn randomize(&mut self, rand: &mut R) { + self.as_mut().randomize(rand); + } +} + +/// Input type that holds a mutable reference to an inner value +#[derive(Debug, PartialEq)] +pub struct ValueMutRefInput<'a, I>(&'a mut I); + +// Macro to implement the `Input` trait and create type aliases for `WrappingInput` +macro_rules! impl_input_for_value_mut_ref_input { + ($($t:ty => $name:ident),+ $(,)?) => { + $( /// Input wrapping a <$t> + pub type $name<'a> = ValueMutRefInput<'a, $t>; + )* + }; +} + +// Invoke the macro with type-name pairs +impl_input_for_value_mut_ref_input!( + u8 => MutU8Input, + u16 => MutU16Input, + u32 => MutU32Input, + u64 => MutU64Input, + u128 => MutU128Input, + usize => MutUsizeInput, + i8 => MutI8Input, + i16 => MutI16Input, + i32 => MutI32Input, + i64 => MutI64Input, + i128 => MutI128Input, + isize => MutIsizeInput, +); + +impl Deref for ValueMutRefInput<'_, I> { + type Target = I; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl DerefMut for ValueMutRefInput<'_, I> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +impl<'a, I> From<&'a mut I> for ValueMutRefInput<'a, I> { + fn from(value: &'a mut I) -> Self { + Self(value) + } +} + +impl<'a, I> From<&'a mut ValueInput> for ValueMutRefInput<'a, I> { + fn from(value: &'a mut ValueInput) -> Self { + Self(value.as_mut()) + } +} + +impl MappedInput for ValueMutRefInput<'_, I> { + type Type<'a> + = ValueMutRefInput<'a, I> + where + Self: 'a; +} + +impl Numeric for ValueMutRefInput<'_, I> +where + I: Numeric, +{ + fn flip_all_bits(&mut self) { + self.deref_mut().flip_all_bits(); + } + + fn flip_bit_at(&mut self, rhs: usize) { + self.deref_mut().flip_bit_at(rhs); + } + + fn wrapping_inc(&mut self) { + self.deref_mut().wrapping_inc(); + } + + fn wrapping_dec(&mut self) { + self.deref_mut().wrapping_dec(); + } + + fn twos_complement(&mut self) { + self.deref_mut().twos_complement(); + } + + fn randomize(&mut self, rand: &mut R) { + self.deref_mut().randomize(rand); + } +} + +#[cfg(test)] +mod tests { + #[cfg(feature = "std")] + use { + super::{ValueInput, ValueMutRefInput}, + crate::mutators::numeric::Numeric, + alloc::fmt::Debug, + std::any::type_name, + }; + + #[cfg(feature = "std")] + macro_rules! apply_all_ops { + ($prep:stmt, $value:expr, $type:ty, $check_twos_complement:expr) => {{ + $prep + let mut j = $value; + j.flip_all_bits(); + $prep + assert_ne!(j, $value, "{:?}.flip_all_bits() for {}", j, type_name::<$type>()); + + $prep + let mut j = $value; + j.wrapping_inc(); + $prep + assert_ne!(j, $value, "{:?}.wrapping_inc() for {}", j, type_name::<$type>()); + + $prep + let mut j = $value; + j.wrapping_dec(); + $prep + assert_ne!(j, $value, "{:?}.wrapping_dec() for {}", j, type_name::<$type>()); + + $prep + let mut j = $value; + j.twos_complement(); + if $check_twos_complement { + $prep + assert_ne!(j, $value, "{:?}.twos_complement() for {}", j, type_name::<$type>()); + } + + $prep + let mut j = $value; + j.flip_bit_at(0); + $prep + assert_ne!(j, $value, "{:?}.flip_bit_at(0) for {}", j, type_name::<$type>()); + + $prep + let mut j = $value; + j.flip_bit_at(size_of::() * 8 - 1); + $prep + assert_ne!(j, $value, "{:?}.flip_bit_at({}) for {}", j, size_of::() * 8 - 1, type_name::<$type>()); + }}; + } + + #[cfg(feature = "std")] + fn take_numeric(i: &I, check_twos_complement: bool) { + apply_all_ops!({}, i.clone(), I, check_twos_complement); + apply_all_ops!( + {}, + ValueInput::from(i.clone()), + ValueInput, + check_twos_complement + ); + apply_all_ops!( + let mut i_clone = i.clone(), + ValueMutRefInput::from(&mut i_clone), + ValueMutRefInput<'_, I>, + check_twos_complement + ); + } + + #[test] + #[cfg(feature = "std")] // type detection for better error messages, running with std is sufficient + fn compiles() { + // twos complement doesn't change anything on the min value of numeric types + take_numeric(&u8::MIN, false); + take_numeric(&u16::MIN, false); + take_numeric(&u32::MIN, false); + take_numeric(&u64::MIN, false); + take_numeric(&u128::MIN, false); + take_numeric(&usize::MIN, false); + take_numeric(&i8::MIN, false); + take_numeric(&i16::MIN, false); + take_numeric(&i32::MIN, false); + take_numeric(&i64::MIN, false); + take_numeric(&i128::MIN, false); + take_numeric(&isize::MIN, false); + take_numeric(&u8::MAX, true); + take_numeric(&u16::MAX, true); + take_numeric(&u32::MAX, true); + take_numeric(&u64::MAX, true); + take_numeric(&u128::MAX, true); + take_numeric(&usize::MAX, true); + take_numeric(&i8::MAX, true); + take_numeric(&i16::MAX, true); + take_numeric(&i32::MAX, true); + take_numeric(&i64::MAX, true); + take_numeric(&i128::MAX, true); + take_numeric(&isize::MAX, true); + } +} diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index 73ab635a1d..f3875c7b43 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -13,6 +13,8 @@ use serde::{Deserialize, Serialize}; pub use token_mutations::*; pub mod havoc_mutations; pub use havoc_mutations::*; +pub mod numeric; +pub use numeric::{int_mutators, mapped_int_mutators}; pub mod encoded_mutations; pub use encoded_mutations::*; pub mod mopt_mutator; diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index 4c014fca9b..c44cabb2ac 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -1749,7 +1749,7 @@ mod tests { } let mut gaps = 0; let mut range = 0..10; - let mut iter = mutated.bytes.iter().copied(); + let mut iter = mutated.as_ref().iter().copied(); while let Some(expected) = range.next() { if let Some(last) = iter.next() { if expected != last { @@ -1768,9 +1768,11 @@ mod tests { } } assert_eq!( - gaps, 1, + gaps, + 1, "{:?} should have exactly one gap, found {}", - mutated.bytes, gaps + mutated.as_ref(), + gaps ); } @@ -1802,20 +1804,20 @@ mod tests { continue; } let mut expansion = 0; - let mut expansion_len = base.bytes.len(); - for (i, value) in mutated.bytes.iter().copied().enumerate() { + let mut expansion_len = base.as_ref().len(); + for (i, value) in mutated.as_ref().iter().copied().enumerate() { if i as u8 != value { expansion = value as usize; expansion_len = i - expansion; break; } } - assert_eq!(mutated.bytes.len(), base.bytes.len() + expansion_len); + assert_eq!(mutated.as_ref().len(), base.as_ref().len() + expansion_len); for (expected, value) in (0..(expansion + expansion_len)) - .chain(expansion..base.bytes.len()) - .zip(mutated.bytes) + .chain(expansion..base.as_ref().len()) + .zip(mutated.as_ref()) { - assert_eq!(expected as u8, value); + assert_eq!(expected as u8, *value); } for i in (expansion..).take(expansion_len) { counts[i] += 1; @@ -1851,19 +1853,19 @@ mod tests { continue; } let mut inserted = 0; - for (i, value) in mutated.bytes.iter().copied().enumerate() { + for (i, value) in mutated.as_ref().iter().copied().enumerate() { if i as u8 != value { inserted = value; break; } } - assert!(mutated.bytes.len() <= base.bytes.len() + 16); + assert!(mutated.as_ref().len() <= base.as_ref().len() + 16); assert_eq!( - bytecount::count(&mutated.bytes, inserted), - mutated.bytes.len() - base.bytes.len() + 1 + bytecount::count(mutated.as_ref(), inserted), + mutated.as_ref().len() - base.as_ref().len() + 1 ); counts[inserted as usize] += 1; - insertions[mutated.bytes.len() - base.bytes.len() - 1] += 1; + insertions[mutated.as_ref().len() - base.as_ref().len() - 1] += 1; } let average = counts.iter().copied().sum::() / counts.len(); @@ -1901,22 +1903,22 @@ mod tests { continue; } let mut inserted = 10; - for (i, value) in mutated.bytes.iter().copied().enumerate() { + for (i, value) in mutated.as_ref().iter().copied().enumerate() { if i as u8 != value { inserted = value; break; } } - assert!(mutated.bytes.len() <= base.bytes.len() + 16); - let offset = usize::from((inserted as usize) < base.bytes.len()); + assert!(mutated.as_ref().len() <= base.as_ref().len() + 16); + let offset = usize::from((inserted as usize) < base.as_ref().len()); assert_eq!( - bytecount::count(&mutated.bytes, inserted), - mutated.bytes.len() - base.bytes.len() + offset, + bytecount::count(mutated.as_ref(), inserted), + mutated.as_ref().len() - base.as_ref().len() + offset, "{:?}", - mutated.bytes + mutated.as_ref() ); counts[inserted as usize] += 1; - insertions[mutated.bytes.len() - base.bytes.len() - 1] += 1; + insertions[mutated.as_ref().len() - base.as_ref().len() - 1] += 1; } let average = counts.iter().copied().sum::() / counts.len(); diff --git a/libafl/src/mutators/numeric.rs b/libafl/src/mutators/numeric.rs new file mode 100644 index 0000000000..d226f2ce4b --- /dev/null +++ b/libafl/src/mutators/numeric.rs @@ -0,0 +1,478 @@ +//! Mutators for integer-style inputs + +use alloc::borrow::Cow; + +use libafl_bolts::{ + rands::Rand, + tuples::{Map as _, Merge}, + Error, Named, +}; +use tuple_list::{tuple_list, tuple_list_type}; + +use super::{ + MappedInputFunctionMappingMutator, MutationResult, Mutator, + ToMappedInputFunctionMappingMutatorMapper, +}; +use crate::{ + corpus::Corpus, + inputs::value::ValueMutRefInput, + random_corpus_id_with_disabled, + state::{HasCorpus, HasRand}, +}; + +/// All mutators for integer-like inputs +pub type IntMutatorsType = tuple_list_type!( + BitFlipMutator, + NegateMutator, + IncMutator, + DecMutator, + TwosComplementMutator, + RandMutator, + CrossoverMutator +); +type IntMutatorsCrossoverType = tuple_list_type!(CrossoverMutator); +type MappedIntMutatorsCrossoverType = tuple_list_type!(MappedCrossoverMutator); +type IntMutatorsNoCrossoverType = tuple_list_type!( + BitFlipMutator, + NegateMutator, + IncMutator, + DecMutator, + TwosComplementMutator, + RandMutator, +); + +/// Mutators for integer-like inputs without crossover mutations +#[must_use] +pub fn int_mutators_no_crossover() -> IntMutatorsNoCrossoverType { + tuple_list!( + BitFlipMutator, + NegateMutator, + IncMutator, + DecMutator, + TwosComplementMutator, + RandMutator, + ) +} + +/// Mutators for integer-like inputs that implement some form of crossover +#[must_use] +pub fn int_mutators_crossover() -> IntMutatorsCrossoverType { + tuple_list!(CrossoverMutator) +} + +/// Mutators for integer-like inputs that implement some form of crossover with a mapper to extract the crossed over information. +#[must_use] +pub fn mapped_int_mutators_crossover(input_mapper: F) -> MappedIntMutatorsCrossoverType { + tuple_list!(MappedCrossoverMutator::new(input_mapper)) +} + +/// Mutators for integer-like inputs +/// +/// Modelled after the applicable mutators from [`super::havoc_mutations::havoc_mutations`] +#[must_use] +pub fn int_mutators() -> IntMutatorsType { + int_mutators_no_crossover().merge(int_mutators_crossover()) +} + +/// Mapped mutators for integer-like inputs +pub type MappedIntMutatorsType = tuple_list_type!( + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator, + MappedInputFunctionMappingMutator,F1,I> +); + +/// Mapped mutators for integer-like inputs +/// +/// Modelled after the applicable mutators from [`super::havoc_mutations::havoc_mutations`] +pub fn mapped_int_mutators( + current_input_mapper: F1, + input_from_corpus_mapper: F2, +) -> MappedIntMutatorsType +where + F1: Clone + FnMut(IO) -> II, +{ + int_mutators_no_crossover() + .merge(mapped_int_mutators_crossover(input_from_corpus_mapper)) + .map(ToMappedInputFunctionMappingMutatorMapper::new( + current_input_mapper, + )) +} +/// Functionality required for Numeric Mutators (see [`int_mutators`]) +pub trait Numeric { + /// Flip all bits of the number. + fn flip_all_bits(&mut self); + + /// Flip the bit at the specified offset. + /// + /// # Safety + /// + /// Panics if the `offset` is out of bounds for the type + fn flip_bit_at(&mut self, offset: usize); + + /// Increment the number by one, wrapping around on overflow. + fn wrapping_inc(&mut self); + + /// Decrement the number by one, wrapping around on underflow. + fn wrapping_dec(&mut self); + + /// Compute the two's complement of the number. + fn twos_complement(&mut self); + + /// Randomizes the value using the provided random number generator. + fn randomize(&mut self, rand: &mut R); +} + +// Macro to implement the Numeric trait for multiple integer types a u64 can be cast to +macro_rules! impl_numeric_cast_randomize { + ($($t:ty)*) => ($( + impl Numeric for $t { + #[inline] + fn flip_all_bits(&mut self) { + *self = !*self; + } + + #[inline] + fn flip_bit_at(&mut self, offset: usize) { + *self ^= 1 << offset; + } + + #[inline] + fn wrapping_inc(&mut self) { + *self = self.wrapping_add(1); + } + + #[inline] + fn wrapping_dec(&mut self) { + *self = self.wrapping_sub(1); + } + + #[inline] + fn twos_complement(&mut self) { + *self = self.wrapping_neg(); + } + + #[inline] + #[allow(trivial_numeric_casts, clippy::cast_possible_wrap)] + fn randomize(&mut self, rand: &mut R) { + *self = rand.next() as $t; + } + + } + )*) +} + +impl_numeric_cast_randomize!( u8 u16 u32 u64 usize i8 i16 i32 i64 isize ); + +// Macro to implement the Numeric trait for multiple integer types a u64 cannot be cast to +macro_rules! impl_numeric_128_bits_randomize { + ($($t:ty)*) => ($( + impl Numeric for $t { + #[inline] + fn flip_all_bits(&mut self) { + *self = !*self; + } + + #[inline] + fn flip_bit_at(&mut self, offset: usize) { + *self ^= 1 << offset; + } + + #[inline] + fn wrapping_inc(&mut self) { + *self = self.wrapping_add(1); + } + + #[inline] + fn wrapping_dec(&mut self) { + *self = self.wrapping_sub(1); + } + + #[inline] + fn twos_complement(&mut self) { + *self = self.wrapping_neg(); + } + + #[inline] + #[allow(trivial_numeric_casts, clippy::cast_possible_wrap)] + fn randomize(&mut self, rand: &mut R) { + *self = (u128::from(rand.next()) << 64 | u128::from(rand.next())) as $t; + } + + } + )*) +} + +// Apply the macro to all desired integer types +impl_numeric_128_bits_randomize! { u128 i128 } + +/// Bitflip mutation for integer-like inputs +#[derive(Debug)] +pub struct BitFlipMutator; + +impl Mutator for BitFlipMutator +where + S: HasRand, + I: Numeric, +{ + fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { + let offset = state.rand_mut().choose(0..size_of::()).unwrap(); + input.flip_bit_at(offset); + Ok(MutationResult::Mutated) + } +} + +impl Named for BitFlipMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("BitFlipMutator") + } +} + +/// Negate mutation for integer-like inputs, i.e. flip all bits +#[derive(Debug)] +pub struct NegateMutator; + +impl Mutator for NegateMutator +where + I: Numeric, +{ + fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { + input.flip_all_bits(); + Ok(MutationResult::Mutated) + } +} + +impl Named for NegateMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("ByteFlipMutator") + } +} + +/// Increment mutation for integer-like inputs. Wraps on overflows. +#[derive(Debug)] +pub struct IncMutator; + +impl Mutator for IncMutator +where + I: Numeric, +{ + fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { + input.wrapping_inc(); + Ok(MutationResult::Mutated) + } +} + +impl Named for IncMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("IncMutator") + } +} + +/// Decrement mutation for integer-like inputs. Wraps on underflow. +#[derive(Debug)] +pub struct DecMutator; + +impl Mutator for DecMutator +where + I: Numeric, +{ + fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { + input.wrapping_dec(); + Ok(MutationResult::Mutated) + } +} + +impl Named for DecMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("DecMutator") + } +} + +/// Two's complement mutation for integer-like inputs +#[derive(Debug)] +pub struct TwosComplementMutator; + +impl Mutator for TwosComplementMutator +where + I: Numeric, +{ + fn mutate(&mut self, _state: &mut S, input: &mut I) -> Result { + input.twos_complement(); + Ok(MutationResult::Mutated) + } +} + +impl Named for TwosComplementMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("NegMutator") + } +} + +/// Randomize mutation for integer-like inputs +#[derive(Debug)] +pub struct RandMutator; + +impl Mutator for RandMutator +where + S: HasRand, + I: Numeric, +{ + fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { + // set to random data byte-wise since the RNGs don't work for all numeric types + input.randomize(state.rand_mut()); + Ok(MutationResult::Mutated) + } +} + +impl Named for RandMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("RandMutator") + } +} + +/// Crossover mutation for integer-like inputs +#[derive(Debug)] +pub struct CrossoverMutator; + +impl Mutator for CrossoverMutator +where + S: HasRand + HasCorpus, + S::Corpus: Corpus, + I: Copy, +{ + fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { + let id = random_corpus_id_with_disabled!(state.corpus(), state.rand_mut()); + + if state.corpus().current().is_some_and(|cur| cur == id) { + return Ok(MutationResult::Skipped); + } + + let other_testcase = state.corpus().get_from_all(id)?.borrow_mut(); + *input = *other_testcase.input().as_ref().unwrap(); + Ok(MutationResult::Mutated) + } +} + +impl Named for CrossoverMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("CrossoverMutator") + } +} +/// Crossover mutation for integer-like inputs with custom state extraction function +#[derive(Debug)] +pub struct MappedCrossoverMutator { + input_mapper: F, +} + +impl MappedCrossoverMutator { + /// Create a new [`MappedCrossoverMutator`] + pub fn new(input_mapper: F) -> Self { + Self { input_mapper } + } +} + +impl Mutator, S> for MappedCrossoverMutator +where + S: HasRand + HasCorpus, + for<'b> F: Fn(&'b ::Input) -> &'b I, + I: Clone, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut ValueMutRefInput<'_, I>, + ) -> Result { + let id = random_corpus_id_with_disabled!(state.corpus(), state.rand_mut()); + + if state.corpus().current().is_some_and(|cur| cur == id) { + return Ok(MutationResult::Skipped); + } + + let other_testcase = state.corpus().get_from_all(id)?.borrow_mut(); + let other_input = other_testcase.input().as_ref().unwrap(); + let mapped_input = (self.input_mapper)(other_input).clone(); + **input = mapped_input; + Ok(MutationResult::Mutated) + } +} + +impl Named for MappedCrossoverMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("MappedCrossoverMutator") + } +} + +#[cfg(test)] +mod tests { + + use libafl_bolts::{ + rands::{Rand, XkcdRand}, + tuples::IntoVec as _, + }; + use serde::{Deserialize, Serialize}; + + use super::{int_mutators, Numeric}; + use crate::{ + corpus::{Corpus as _, InMemoryCorpus, Testcase}, + inputs::value::I16Input, + mutators::MutationResult, + state::StdState, + }; + + #[test] + fn randomized() { + const RAND_NUM: u64 = 0xAAAAAAAAAAAAAAAA; // 0b10101010.. + #[derive(Serialize, Deserialize, Debug)] + struct FixedRand; + impl Rand for FixedRand { + fn set_seed(&mut self, _seed: u64) {} + fn next(&mut self) -> u64 { + RAND_NUM + } + } + + let rand = &mut FixedRand; + + let mut i = 0_u8; + Numeric::randomize(&mut i, rand); + assert_eq!(0xAA, i); + + let mut i = 0_u128; + Numeric::randomize(&mut i, rand); + assert_eq!(((u128::from(RAND_NUM) << 64) | u128::from(RAND_NUM)), i); + + let mut i = 0_i16; + Numeric::randomize(&mut i, rand); + assert_eq!(-0b101010101010110, i); // two's complement + } + + #[test] + fn all_mutate_owned() { + let mut corpus = InMemoryCorpus::new(); + corpus.add(Testcase::new(42_i16.into())).unwrap(); + let mut state = StdState::new( + XkcdRand::new(), + corpus, + InMemoryCorpus::new(), + &mut (), + &mut (), + ) + .unwrap(); + + let mutators = int_mutators().into_vec(); + + for mut m in mutators { + let mut input: I16Input = 1_i16.into(); + assert_eq!( + MutationResult::Mutated, + m.mutate(&mut state, &mut input).unwrap(), + "Errored with {}", + m.name() + ); + assert_ne!(1, input.into_inner(), "Errored with {}", m.name()); + } + } +} diff --git a/libafl_bolts/src/lib.rs b/libafl_bolts/src/lib.rs index 0c6af328db..b29139c0c2 100644 --- a/libafl_bolts/src/lib.rs +++ b/libafl_bolts/src/lib.rs @@ -146,7 +146,7 @@ use alloc::{borrow::Cow, vec::Vec}; #[cfg(all(not(feature = "xxh3"), feature = "alloc"))] use core::hash::BuildHasher; #[cfg(any(feature = "xxh3", feature = "alloc"))] -use core::hash::Hasher; +use core::hash::{Hash, Hasher}; #[cfg(feature = "std")] use std::time::{SystemTime, UNIX_EPOCH}; #[cfg(all(unix, feature = "std"))] @@ -265,6 +265,21 @@ pub fn hash_std(input: &[u8]) -> u64 { } } +/// Hashes the input with a given hash +/// +/// Hashes the input with a given hash, depending on features: +/// [`xxh3_64`](https://docs.rs/xxhash-rust/latest/xxhash_rust/xxh3/fn.xxh3_64.html) +/// if the `xxh3` feature is used, /// else [`ahash`](https://docs.rs/ahash/latest/ahash/). +/// +/// If you have access to a `&[u8]` directly, [`hash_std`] may provide better performance +#[cfg(any(feature = "xxh3", feature = "alloc"))] +#[must_use] +pub fn generic_hash_std(input: &I) -> u64 { + let mut hasher = hasher_std(); + input.hash(&mut hasher); + hasher.finish() +} + /// Main error struct for `LibAFL` #[derive(Debug)] pub enum Error { From bab9890520e4e4f8698d79b2ba605123faf90980 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sun, 15 Dec 2024 16:18:06 +0100 Subject: [PATCH 13/34] Add HashMutator --- libafl/src/mutators/hash.rs | 80 +++++++++++++++++++++++++++++++++++++ libafl/src/mutators/mod.rs | 16 ++++++-- 2 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 libafl/src/mutators/hash.rs diff --git a/libafl/src/mutators/hash.rs b/libafl/src/mutators/hash.rs new file mode 100644 index 0000000000..2eda9c1580 --- /dev/null +++ b/libafl/src/mutators/hash.rs @@ -0,0 +1,80 @@ +//! A wrapper around a [`Mutator`] that ensures an input really changed [`MutationResult::Mutated`] +//! by hashing pre- and post-mutation +use std::{borrow::Cow, hash::Hash}; + +use libafl_bolts::{generic_hash_std, Error, Named}; + +use super::{MutationResult, Mutator}; + +/// A wrapper around a [`Mutator`] that ensures an input really changed [`MutationResult::Mutated`] +/// by hashing pre- and post-mutation +#[derive(Debug)] +pub struct HashMutator { + inner: M, + name: Cow<'static, str>, +} + +impl HashMutator +where + M: Named, +{ + /// Create a new [`HashMutator`] + pub fn new(inner: M) -> Self { + let name = Cow::Owned(format!("HashMutator<{}>", inner.name().clone())); + Self { inner, name } + } +} + +impl Mutator for HashMutator +where + I: Hash, + M: Mutator, +{ + fn mutate(&mut self, state: &mut S, input: &mut I) -> Result { + let before = generic_hash_std(input); + self.inner.mutate(state, input)?; + if before == generic_hash_std(input) { + Ok(MutationResult::Skipped) + } else { + Ok(MutationResult::Mutated) + } + } +} + +impl Named for HashMutator { + fn name(&self) -> &Cow<'static, str> { + &self.name + } +} + +#[cfg(test)] +mod tests { + use crate::{ + inputs::BytesInput, + mutators::{BytesSetMutator, HashMutator, MutationResult, Mutator}, + state::NopState, + }; + + #[test] + fn not_mutated() { + let mut state: NopState = NopState::new(); + let mut inner = BytesSetMutator::new(); + + let mut input = BytesInput::new(vec![0; 5]); + + // nothing changed, yet `MutationResult::Mutated` was reported + assert_eq!( + MutationResult::Mutated, + inner.mutate(&mut state, &mut input).unwrap() + ); + assert_eq!(BytesInput::new(vec![0; 5]), input); + + // now it is correctly reported as `MutationResult::Skipped` + let mut hash_mutator = HashMutator::new(inner); + assert_eq!( + MutationResult::Skipped, + hash_mutator.mutate(&mut state, &mut input).unwrap() + ); + assert_eq!(BytesInput::new(vec![0; 5]), input); + } +} diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index f3875c7b43..294a713409 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -28,6 +28,11 @@ pub use mapping::*; pub mod tuneable; pub use tuneable::*; +#[cfg(feature = "std")] +pub mod hash; +#[cfg(feature = "std")] +pub use hash::*; + #[cfg(feature = "unicode")] pub mod unicode; #[cfg(feature = "unicode")] @@ -84,12 +89,15 @@ impl From for MutationId { } } -/// The result of a mutation. -/// If the mutation got skipped, the target -/// will not be executed with the returned input. +/// Result of the mutation. +/// +/// [`MutationResult::Skipped`] does not necessarily mean that the input changed, +/// just that the mutator did something. +#[cfg(feature = "std")] +/// For slow targets, consider wrapping your mutator in a [`hash::HashMutator`] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MutationResult { - /// The [`Mutator`] mutated this `Input`. + /// The [`Mutator`] executed on this `Input`. It may still be the same. Mutated, /// The [`Mutator`] did not mutate this `Input`. It was `Skipped`. Skipped, From 71fc1c6aa872dd5f016fc7708f5e7a569eb598d6 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sun, 15 Dec 2024 16:18:16 +0100 Subject: [PATCH 14/34] Fix docs --- libafl_bolts/src/shmem.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libafl_bolts/src/shmem.rs b/libafl_bolts/src/shmem.rs index 571169ed1b..2eb2b0273f 100644 --- a/libafl_bolts/src/shmem.rs +++ b/libafl_bolts/src/shmem.rs @@ -626,10 +626,7 @@ where pub mod unix_shmem { /// Mmap [`ShMem`] for Unix #[cfg(not(target_os = "android"))] - pub use default::MmapShMem; - /// Mmap [`ShMemProvider`] for Unix - #[cfg(not(target_os = "android"))] - pub use default::MmapShMemProvider; + pub use default::{MmapShMem, MmapShMemProvider, MAX_MMAP_FILENAME_LEN}; #[cfg(doc)] use crate::shmem::{ShMem, ShMemProvider}; @@ -669,7 +666,8 @@ pub mod unix_shmem { Error, }; - const MAX_MMAP_FILENAME_LEN: usize = 20; + /// The max number of bytes used when generating names for [`MmapShMem`]s. + pub const MAX_MMAP_FILENAME_LEN: usize = 20; /// Mmap-based The sharedmap impl for unix using [`shm_open`] and [`mmap`]. /// Default on `MacOS` and `iOS`, where we need a central point to unmap From 30e1db4bca424844d37a1619a0e3ffdc81082019 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sun, 15 Dec 2024 16:23:32 +0100 Subject: [PATCH 15/34] Fix docs again --- libafl/src/mutators/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index 294a713409..26b20a519d 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -92,9 +92,8 @@ impl From for MutationId { /// Result of the mutation. /// /// [`MutationResult::Skipped`] does not necessarily mean that the input changed, -/// just that the mutator did something. -#[cfg(feature = "std")] -/// For slow targets, consider wrapping your mutator in a [`hash::HashMutator`] +/// just that the mutator did something. For slow targets, consider wrapping your +/// mutator in a [`hash::HashMutator`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MutationResult { /// The [`Mutator`] executed on this `Input`. It may still be the same. From 025a56aeafbb5016692ec23d2bee09fe7e403cdb Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 17 Dec 2024 17:25:58 +0100 Subject: [PATCH 16/34] introducing bloom filter --- Cargo.toml | 15 --- .../baby_fuzzer_custom_executor/Cargo.toml | 3 +- .../baby_fuzzer_custom_executor/src/main.rs | 5 + libafl/Cargo.toml | 1 + libafl/src/fuzzer/mod.rs | 97 ++++++++++++++++--- 5 files changed, 91 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7f82cd0b1c..76271b3607 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,28 +56,13 @@ license = "MIT OR Apache-2.0" # Internal deps libafl = { path = "./libafl", version = "0.14.1", default-features = false } libafl_bolts = { path = "./libafl_bolts", version = "0.14.1", default-features = false } -libafl_cc = { path = "./libafl_cc", version = "0.14.1", default-features = false } -symcc_runtime = { path = "./libafl_concolic/symcc_runtime", version = "0.14.1", default-features = false } symcc_libafl = { path = "./libafl_concolic/symcc_libafl", version = "0.14.1", default-features = false } libafl_derive = { path = "./libafl_derive", version = "0.14.1", default-features = false } -libafl_frida = { path = "./libafl_frida", version = "0.14.1", default-features = false } libafl_intelpt = { path = "./libafl_intelpt", version = "0.14.1", default-features = false } -libafl_libfuzzer = { path = "./libafl_libfuzzer", version = "0.14.1", default-features = false } -libafl_nyx = { path = "./libafl_nyx", version = "0.14.1", default-features = false } libafl_targets = { path = "./libafl_targets", version = "0.14.1", default-features = false } -libafl_tinyinst = { path = "./libafl_tinyinst", version = "0.14.1", default-features = false } libafl_qemu = { path = "./libafl_qemu", version = "0.14.1", default-features = false } libafl_qemu_build = { path = "./libafl_qemu/libafl_qemu_build", version = "0.14.1", default-features = false } libafl_qemu_sys = { path = "./libafl_qemu/libafl_qemu_sys", version = "0.14.1", default-features = false } -libafl_sugar = { path = "./libafl_sugar", version = "0.14.1", default-features = false } -dump_constraints = { path = "./libafl_concolic/test/dump_constraints", version = "0.14.1", default-features = false } -runtime_test = { path = "./libafl_concolic/test/runtime_test", version = "0.14.1", default-features = false } -build_and_test_fuzzers = { path = "./utils/build_and_test_fuzzers", version = "0.14.1", default-features = false } -deexit = { path = "./utils/deexit", version = "0.14.1", default-features = false } -drcov_utils = { path = "./utils/drcov_utils", version = "0.14.1", default-features = false } -construct_automata = { path = "./utils/gramatron/construct_automata", version = "0.14.1", default-features = false } -libafl_benches = { path = "./utils/libafl_benches", version = "0.14.1", default-features = false } -libafl_jumper = { path = "./utils/libafl_jumper", version = "0.14.1", default-features = false } # External deps ahash = { version = "0.8.11", default-features = false } # The hash function already used in hashbrown diff --git a/fuzzers/baby/baby_fuzzer_custom_executor/Cargo.toml b/fuzzers/baby/baby_fuzzer_custom_executor/Cargo.toml index f7905ec4c2..6de3587646 100644 --- a/fuzzers/baby/baby_fuzzer_custom_executor/Cargo.toml +++ b/fuzzers/baby/baby_fuzzer_custom_executor/Cargo.toml @@ -8,8 +8,9 @@ authors = [ edition = "2021" [features] -default = ["std"] +default = ["std", "bloom_filter"] tui = ["libafl/tui_monitor"] +bloom_filter = ["std"] std = [] [profile.dev] diff --git a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs index f9c0b2e9ee..9d92b27b51 100644 --- a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs +++ b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs @@ -133,7 +133,12 @@ pub fn main() { let scheduler = QueueScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler + #[cfg(not(feature = "bloom_filter"))] let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + #[cfg(feature = "bloom_filter")] + let mut fuzzer = + StdFuzzer::new_with_bloom_filter(scheduler, feedback, objective, 10_000_000, 0.001) + .unwrap(); // Create the executor for an in-process function with just one observer let executor = CustomExecutor::new(&state); diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 2592a2c7c1..f80fe27af9 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -291,6 +291,7 @@ document-features = { workspace = true, optional = true } clap = { workspace = true, optional = true } num_enum = { workspace = true, optional = true } libipt = { workspace = true, optional = true } +bloomfilter = "3.0.1" [lints] workspace = true diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 4ac5aeedb6..25db0e96de 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -2,7 +2,10 @@ use alloc::{string::ToString, vec::Vec}; use core::{fmt::Debug, time::Duration}; +#[cfg(feature = "std")] +use std::hash::Hash; +use bloomfilter::Bloom; use libafl_bolts::{current_time, tuples::MatchName}; use serde::Serialize; @@ -243,13 +246,14 @@ pub enum ExecuteInputResult { /// Your default fuzzer instance, for everyday use. #[derive(Debug)] -pub struct StdFuzzer { +pub struct StdFuzzer { scheduler: CS, feedback: F, objective: OF, + input_filter: IF, } -impl HasScheduler<::Input, S> for StdFuzzer +impl HasScheduler<::Input, S> for StdFuzzer where S: HasCorpus, CS: Scheduler<::Input, S>, @@ -265,7 +269,7 @@ where } } -impl HasFeedback for StdFuzzer { +impl HasFeedback for StdFuzzer { type Feedback = F; fn feedback(&self) -> &Self::Feedback { @@ -277,7 +281,7 @@ impl HasFeedback for StdFuzzer { } } -impl HasObjective for StdFuzzer { +impl HasObjective for StdFuzzer { type Objective = OF; fn objective(&self) -> &OF { @@ -289,8 +293,8 @@ impl HasObjective for StdFuzzer { } } -impl ExecutionProcessor::Input, OT, S> - for StdFuzzer +impl ExecutionProcessor::Input, OT, S> + for StdFuzzer where CS: Scheduler<::Input, S>, EM: EventFirer, @@ -491,8 +495,8 @@ where } } -impl EvaluatorObservers::Input, S> - for StdFuzzer +impl EvaluatorObservers::Input, S> + for StdFuzzer where CS: Scheduler<::Input, S>, E: HasObservers + Executor, @@ -528,7 +532,43 @@ where } } -impl Evaluator::Input, S> for StdFuzzer +trait InputFilter { + fn should_execute(&mut self, input: &I) -> bool; +} + +/// A pseudo-filter that will execute each input. +#[derive(Debug)] +pub struct NopInputFilter; +impl InputFilter for NopInputFilter { + fn should_execute(&mut self, _input: &I) -> bool { + true + } +} + +/// A filter that probabilistically prevents duplicate execution of the same input based on a bloom filter. +#[cfg(feature = "std")] +#[derive(Debug)] +pub struct BloomInputFilter { + bloom: Bloom, +} + +#[cfg(feature = "std")] +impl BloomInputFilter { + fn new(items_count: usize, fp_p: f64) -> Result { + let bloom = Bloom::new_for_fp_rate(items_count, fp_p).map_err(Error::illegal_argument)?; + Ok(Self { bloom }) + } +} + +#[cfg(feature = "std")] +impl InputFilter for BloomInputFilter { + fn should_execute(&mut self, input: &I) -> bool { + !self.bloom.check_and_set(input) + } +} + +impl Evaluator::Input, S> + for StdFuzzer where CS: Scheduler<::Input, S>, E: HasObservers + Executor, @@ -545,6 +585,7 @@ where + UsesInput::Input>, ::Input: Input, S::Solutions: Corpus::Input>, + IF: InputFilter<::Input>, { /// Process one input, adding to the respective corpora if needed and firing the right events #[inline] @@ -556,7 +597,11 @@ where input: ::Input, send_events: bool, ) -> Result<(ExecuteInputResult, Option), Error> { - self.evaluate_input_with_observers(state, executor, manager, input, send_events) + if self.input_filter.should_execute(&input) { + self.evaluate_input_with_observers(state, executor, manager, input, send_events) + } else { + Ok((ExecuteInputResult::None, None)) + } } fn add_disabled_input( &mut self, @@ -668,7 +713,7 @@ where } } -impl Fuzzer for StdFuzzer +impl Fuzzer for StdFuzzer where CS: Scheduler, E: UsesState, @@ -792,16 +837,40 @@ where } } -impl StdFuzzer { +impl StdFuzzer { /// Create a new `StdFuzzer` with standard behavior. pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self { Self { scheduler, feedback, objective, + input_filter: NopInputFilter, } } } +impl StdFuzzer> { + /// Create a new [`StdFuzzer`], which, with a certain certainty, executes each input only once. + /// + /// This is achieved by hashing each input and using a bloom filter to differentiate inputs. + /// + /// Use this implementation if hashing each input is very fast compared to executing potential duplicate inputs. + pub fn new_with_bloom_filter( + scheduler: CS, + feedback: F, + objective: OF, + items_count: usize, + fp_p: f64, + ) -> Result { + let input_filter = BloomInputFilter::new(items_count, fp_p)?; + + Ok(Self { + scheduler, + feedback, + objective, + input_filter, + }) + } +} /// Structs with this trait will execute an input pub trait ExecutesInput { @@ -815,8 +884,8 @@ pub trait ExecutesInput { ) -> Result; } -impl ExecutesInput::Input, S> - for StdFuzzer +impl ExecutesInput::Input, S> + for StdFuzzer where CS: Scheduler<::Input, S>, E: Executor + HasObservers, From 63b9ac9ed1a2af0650ffddeac57c6f16a38bc581 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 17 Dec 2024 23:08:27 +0100 Subject: [PATCH 17/34] fix tests --- libafl/src/executors/inprocess/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index 366532cbf7..09e5906aa1 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -562,7 +562,7 @@ mod tests { let mut mgr = NopEventManager::new(); let mut state = StdState::new(rand, corpus, solutions, &mut feedback, &mut objective).unwrap(); - let mut fuzzer = StdFuzzer::<_, _, _>::new(sche, feedback, objective); + let mut fuzzer = StdFuzzer::new(sche, feedback, objective); let mut in_process_executor = InProcessExecutor::new( &mut harness, From ff0a25b3009f6b61176e3c62d64196e0394ce502 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 24 Dec 2024 16:53:54 +0100 Subject: [PATCH 18/34] Implement evaluate_filtered --- .../baby_fuzzer_custom_executor/Cargo.toml | 4 +- .../baby_fuzzer_custom_executor/src/main.rs | 6 +- libafl/Cargo.toml | 2 +- libafl/src/fuzzer/mod.rs | 150 ++++++++++++++---- libafl/src/mutators/mod.rs | 2 +- libafl/src/stages/concolic.rs | 4 +- libafl/src/stages/generation.rs | 2 +- libafl/src/stages/mutational.rs | 10 +- libafl/src/stages/power.rs | 5 +- libafl/src/stages/tuneable.rs | 4 +- 10 files changed, 141 insertions(+), 48 deletions(-) diff --git a/fuzzers/baby/baby_fuzzer_custom_executor/Cargo.toml b/fuzzers/baby/baby_fuzzer_custom_executor/Cargo.toml index 6de3587646..969bbf0bb1 100644 --- a/fuzzers/baby/baby_fuzzer_custom_executor/Cargo.toml +++ b/fuzzers/baby/baby_fuzzer_custom_executor/Cargo.toml @@ -8,9 +8,9 @@ authors = [ edition = "2021" [features] -default = ["std", "bloom_filter"] +default = ["std", "bloom_input_filter"] tui = ["libafl/tui_monitor"] -bloom_filter = ["std"] +bloom_input_filter = ["std"] std = [] [profile.dev] diff --git a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs index 837ca6c35f..b471ad61d1 100644 --- a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs +++ b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs @@ -134,11 +134,11 @@ pub fn main() { let scheduler = QueueScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler - #[cfg(not(feature = "bloom_filter"))] + #[cfg(not(feature = "bloom_input_filter"))] let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - #[cfg(feature = "bloom_filter")] + #[cfg(feature = "bloom_input_filter")] let mut fuzzer = - StdFuzzer::new_with_bloom_filter(scheduler, feedback, objective, 10_000_000, 0.001) + StdFuzzer::new_with_bloom_input_filter(scheduler, feedback, objective, 10_000_000, 0.001) .unwrap(); // Create the executor for an in-process function with just one observer diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index f80fe27af9..5a987c4352 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -291,7 +291,7 @@ document-features = { workspace = true, optional = true } clap = { workspace = true, optional = true } num_enum = { workspace = true, optional = true } libipt = { workspace = true, optional = true } -bloomfilter = "3.0.1" +fastbloom = "0.8.0" [lints] workspace = true diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 985ae041a8..1034d2ec21 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -5,7 +5,8 @@ use core::{fmt::Debug, time::Duration}; #[cfg(feature = "std")] use std::hash::Hash; -use bloomfilter::Bloom; +#[cfg(feature = "std")] +use fastbloom::BloomFilter; use libafl_bolts::{current_time, tuples::MatchName}; use serde::Serialize; @@ -141,6 +142,16 @@ pub trait EvaluatorObservers { /// Evaluate an input modifying the state of the fuzzer pub trait Evaluator { + /// Runs the input if it was (likely) not previously run and triggers observers and feedback and adds the input to the previously executed list + /// returns if is interesting an (option) the index of the new [`crate::corpus::Testcase`] in the corpus + fn evaluate_filtered( + &mut self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + input: I, + ) -> Result<(ExecuteInputResult, Option), Error>; + /// Runs the input and triggers observers and feedback, /// returns if is interesting an (option) the index of the new [`crate::corpus::Testcase`] in the corpus fn evaluate_input( @@ -245,14 +256,14 @@ pub enum ExecuteInputResult { /// Your default fuzzer instance, for everyday use. #[derive(Debug)] -pub struct StdFuzzer { +pub struct StdFuzzer { scheduler: CS, feedback: F, objective: OF, input_filter: IF, } -impl HasScheduler<::Input, S> for StdFuzzer +impl HasScheduler<::Input, S> for StdFuzzer where S: HasCorpus, CS: Scheduler<::Input, S>, @@ -268,7 +279,7 @@ where } } -impl HasFeedback for StdFuzzer { +impl HasFeedback for StdFuzzer { type Feedback = F; fn feedback(&self) -> &Self::Feedback { @@ -280,7 +291,7 @@ impl HasFeedback for StdFuzzer { } } -impl HasObjective for StdFuzzer { +impl HasObjective for StdFuzzer { type Objective = OF; fn objective(&self) -> &OF { @@ -293,7 +304,7 @@ impl HasObjective for StdFuzzer { } impl ExecutionProcessor::Input, OT, S> - for StdFuzzer + for StdFuzzer where CS: Scheduler<::Input, S>, EM: EventFirer, @@ -499,7 +510,7 @@ where } impl EvaluatorObservers::Input, S> - for StdFuzzer + for StdFuzzer where CS: Scheduler<::Input, S>, E: HasObservers + Executor, @@ -544,6 +555,8 @@ trait InputFilter { #[derive(Debug)] pub struct NopInputFilter; impl InputFilter for NopInputFilter { + #[inline] + #[must_use] fn should_execute(&mut self, _input: &I) -> bool { true } @@ -552,27 +565,30 @@ impl InputFilter for NopInputFilter { /// A filter that probabilistically prevents duplicate execution of the same input based on a bloom filter. #[cfg(feature = "std")] #[derive(Debug)] -pub struct BloomInputFilter { - bloom: Bloom, +pub struct BloomInputFilter { + bloom: BloomFilter, } #[cfg(feature = "std")] -impl BloomInputFilter { - fn new(items_count: usize, fp_p: f64) -> Result { - let bloom = Bloom::new_for_fp_rate(items_count, fp_p).map_err(Error::illegal_argument)?; - Ok(Self { bloom }) +impl BloomInputFilter { + #[must_use] + fn new(items_count: usize, fp_p: f64) -> Self { + let bloom = BloomFilter::with_false_pos(fp_p).expected_items(items_count); + Self { bloom } } } #[cfg(feature = "std")] -impl InputFilter for BloomInputFilter { +impl InputFilter for BloomInputFilter { + #[inline] + #[must_use] fn should_execute(&mut self, input: &I) -> bool { - !self.bloom.check_and_set(input) + !self.bloom.insert(input) } } impl Evaluator::Input, S> - for StdFuzzer + for StdFuzzer where CS: Scheduler<::Input, S>, E: HasObservers + Executor, @@ -591,22 +607,33 @@ where S::Solutions: Corpus::Input>, IF: InputFilter<::Input>, { - /// Process one input, adding to the respective corpora if needed and firing the right events - #[inline] - fn evaluate_input_events( + fn evaluate_filtered( &mut self, state: &mut S, executor: &mut E, manager: &mut EM, input: ::Input, - send_events: bool, ) -> Result<(ExecuteInputResult, Option), Error> { if self.input_filter.should_execute(&input) { - self.evaluate_input_with_observers(state, executor, manager, input, send_events) + self.evaluate_input(state, executor, manager, input) } else { Ok((ExecuteInputResult::None, None)) } } + + /// 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, + executor: &mut E, + manager: &mut EM, + input: ::Input, + send_events: bool, + ) -> Result<(ExecuteInputResult, Option), Error> { + self.evaluate_input_with_observers(state, executor, manager, input, send_events) + } + fn add_disabled_input( &mut self, state: &mut S, @@ -618,6 +645,7 @@ where let id = state.corpus_mut().add_disabled(testcase)?; Ok(id) } + /// Adds an input, even if it's not considered `interesting` by any of the executors fn add_input( &mut self, @@ -717,7 +745,7 @@ where } } -impl Fuzzer for StdFuzzer +impl Fuzzer for StdFuzzer where CS: Scheduler, E: UsesState, @@ -841,7 +869,7 @@ where } } -impl StdFuzzer { +impl StdFuzzer { /// Create a new `StdFuzzer` with standard behavior. pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self { Self { @@ -852,27 +880,29 @@ impl StdFuzzer { } } } -impl StdFuzzer> { + +#[cfg(feature = "std")] // hashing requires std +impl StdFuzzer { /// Create a new [`StdFuzzer`], which, with a certain certainty, executes each input only once. /// /// This is achieved by hashing each input and using a bloom filter to differentiate inputs. /// /// Use this implementation if hashing each input is very fast compared to executing potential duplicate inputs. - pub fn new_with_bloom_filter( + pub fn with_bloom_filter( scheduler: CS, feedback: F, objective: OF, items_count: usize, fp_p: f64, - ) -> Result { - let input_filter = BloomInputFilter::new(items_count, fp_p)?; + ) -> Self { + let input_filter = BloomInputFilter::new(items_count, fp_p); - Ok(Self { + Self { scheduler, feedback, objective, input_filter, - }) + } } } @@ -889,7 +919,7 @@ pub trait ExecutesInput { } impl ExecutesInput::Input, S> - for StdFuzzer + for StdFuzzer where CS: Scheduler<::Input, S>, E: Executor + HasObservers, @@ -982,3 +1012,63 @@ where unimplemented!("NopFuzzer cannot fuzz"); } } + +#[cfg(all(test, feature = "std"))] +mod tests { + use core::cell::RefCell; + + use libafl_bolts::rands::StdRand; + + use super::{Evaluator, StdFuzzer}; + use crate::{ + corpus::InMemoryCorpus, + events::NopEventManager, + executors::{ExitKind, InProcessExecutor}, + inputs::BytesInput, + schedulers::StdScheduler, + state::StdState, + }; + + #[test] + fn filtered_execution() { + let execution_count = RefCell::new(0); + let scheduler = StdScheduler::new(); + let mut fuzzer = StdFuzzer::with_bloom_filter(scheduler, (), (), 100, 1e-4); + let mut state = StdState::new( + StdRand::new(), + InMemoryCorpus::new(), + InMemoryCorpus::new(), + &mut (), + &mut (), + ) + .unwrap(); + let mut manager = NopEventManager::new(); + let mut harness = |_input: &BytesInput| { + *execution_count.borrow_mut() += 1; + ExitKind::Ok + }; + let mut executor = + InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut manager) + .unwrap(); + let input = BytesInput::new(vec![1, 2, 3]); + assert!(fuzzer + .evaluate_input(&mut state, &mut executor, &mut manager, input.clone()) + .is_ok()); + assert_eq!(1, *execution_count.borrow()); // evaluate_input does not add it to the filter + + assert!(fuzzer + .evaluate_filtered(&mut state, &mut executor, &mut manager, input.clone()) + .is_ok()); + assert_eq!(2, *execution_count.borrow()); // at to the filter + + assert!(fuzzer + .evaluate_filtered(&mut state, &mut executor, &mut manager, input.clone()) + .is_ok()); + assert_eq!(2, *execution_count.borrow()); // the harness is not called + + assert!(fuzzer + .evaluate_input(&mut state, &mut executor, &mut manager, input.clone()) + .is_ok()); + assert_eq!(3, *execution_count.borrow()); // evaluate_input ignores filters + } +} diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index b34e3cbc29..b87c66fb44 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -96,7 +96,7 @@ impl From for MutationId { /// mutator in a [`hash::HashMutator`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MutationResult { - /// The [`Mutator`] executed on this `Input`. It may still be the same. + /// The [`Mutator`] executed on this `Input`. It may not guarantee that the input has actually been changed. Mutated, /// The [`Mutator`] did not mutate this `Input`. It was `Skipped`. Skipped, diff --git a/libafl/src/stages/concolic.rs b/libafl/src/stages/concolic.rs index 2f2ebd0876..55ecf3518c 100644 --- a/libafl/src/stages/concolic.rs +++ b/libafl/src/stages/concolic.rs @@ -417,8 +417,8 @@ where for (index, new_byte) in mutation { input_copy.bytes_mut()[index] = new_byte; } - // Time is measured directly the `evaluate_input` function - fuzzer.evaluate_input(state, executor, manager, input_copy)?; + // Time is measured directly the `evaluate_filtered` function + fuzzer.evaluate_filtered(state, executor, manager, input_copy)?; } } Ok(()) diff --git a/libafl/src/stages/generation.rs b/libafl/src/stages/generation.rs index 8fe6ff28fa..ccf1b94f9d 100644 --- a/libafl/src/stages/generation.rs +++ b/libafl/src/stages/generation.rs @@ -44,7 +44,7 @@ where manager: &mut EM, ) -> Result<(), Error> { let input = self.0.generate(state)?; - fuzzer.evaluate_input(state, executor, manager, input)?; + fuzzer.evaluate_filtered(state, executor, manager, input)?; Ok(()) } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 61ac616eee..d147b1c1d6 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -277,9 +277,10 @@ where continue; } - // Time is measured directly the `evaluate_input` function + // Time is measured directly the `evaluate_filtered` function let (untransformed, post) = input.try_transform_into(state)?; - let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?; + let (_, corpus_id) = + fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; start_timer!(state); self.mutator_mut().post_exec(state, corpus_id)?; @@ -345,9 +346,10 @@ where let generated = self.mutator.multi_mutate(state, &input, None)?; for new_input in generated { - // Time is measured directly the `evaluate_input` function + // Time is measured directly the `evaluate_filtered` function let (untransformed, post) = new_input.try_transform_into(state)?; - let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?; + let (_, corpus_id) = + fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; self.mutator.multi_post_exec(state, corpus_id)?; post.post_exec(state, corpus_id)?; } diff --git a/libafl/src/stages/power.rs b/libafl/src/stages/power.rs index e3d64def59..3333656eba 100644 --- a/libafl/src/stages/power.rs +++ b/libafl/src/stages/power.rs @@ -189,9 +189,10 @@ where continue; } - // Time is measured directly the `evaluate_input` function + // Time is measured directly the `evaluate_filtered` function let (untransformed, post) = input.try_transform_into(state)?; - let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?; + let (_, corpus_id) = + fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; start_timer!(state); self.mutator_mut().post_exec(state, corpus_id)?; diff --git a/libafl/src/stages/tuneable.rs b/libafl/src/stages/tuneable.rs index 9c3aeab6af..a9b6d38a39 100644 --- a/libafl/src/stages/tuneable.rs +++ b/libafl/src/stages/tuneable.rs @@ -453,9 +453,9 @@ where return Ok(()); } - // Time is measured directly the `evaluate_input` function + // Time is measured directly the `evaluate_filtered` function let (untransformed, post) = input.try_transform_into(state)?; - let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?; + let (_, corpus_id) = fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; start_timer!(state); self.mutator_mut().post_exec(state, corpus_id)?; From d193c065c85845b9ba4f7b1d45722d242b2ebc58 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Mon, 23 Dec 2024 13:25:14 +0100 Subject: [PATCH 19/34] Add macros to libafl_bolts tuples for mapping and merging types (#2788) * Add macros * Use the macros for havoc_mutations * Fix docs * improve merge_tuple_list_type to accept n items --- libafl/src/mutators/havoc_mutations.rs | 105 ++++----------------- libafl_bolts/src/tuples.rs | 123 +++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 97 deletions(-) diff --git a/libafl/src/mutators/havoc_mutations.rs b/libafl/src/mutators/havoc_mutations.rs index 7399ba7930..f2d1c512e3 100644 --- a/libafl/src/mutators/havoc_mutations.rs +++ b/libafl/src/mutators/havoc_mutations.rs @@ -1,11 +1,12 @@ //! [`crate::mutators::Mutator`] collection equivalent to AFL++'s havoc mutations -use libafl_bolts::tuples::{Map, Merge}; -use tuple_list::{tuple_list, tuple_list_type}; +use libafl_bolts::{ + map_tuple_list_type, merge_tuple_list_type, + tuples::{tuple_list, tuple_list_type, Map, Merge}, +}; -use super::{MappingMutator, ToMappingMutator}; use crate::mutators::{ - mapping::{OptionalMutator, ToOptionalMutator}, + mapping::{ToMappingMutator, ToOptionalMutator}, mutations::{ BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, ByteIncMutator, ByteInterestingMutator, ByteNegMutator, ByteRandMutator, BytesCopyMutator, @@ -56,96 +57,22 @@ pub type MappedHavocCrossoverType = tuple_list_type!( ); /// Tuple type of the mutations that compose the Havoc mutator -pub type HavocMutationsType = tuple_list_type!( - BitFlipMutator, - ByteFlipMutator, - ByteIncMutator, - ByteDecMutator, - ByteNegMutator, - ByteRandMutator, - ByteAddMutator, - WordAddMutator, - DwordAddMutator, - QwordAddMutator, - ByteInterestingMutator, - WordInterestingMutator, - DwordInterestingMutator, - BytesDeleteMutator, - BytesDeleteMutator, - BytesDeleteMutator, - BytesDeleteMutator, - BytesExpandMutator, - BytesInsertMutator, - BytesRandInsertMutator, - BytesSetMutator, - BytesRandSetMutator, - BytesCopyMutator, - BytesInsertCopyMutator, - BytesSwapMutator, - CrossoverInsertMutator, - CrossoverReplaceMutator, -); +pub type HavocMutationsType = + merge_tuple_list_type!(HavocMutationsNoCrossoverType, HavocCrossoverType); /// Tuple type of the mutations that compose the Havoc mutator for mapped input types -pub type MappedHavocMutationsType = tuple_list_type!( - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, - MappingMutator, F1>, - MappingMutator, F1>, +pub type MappedHavocMutationsType = map_tuple_list_type!( + merge_tuple_list_type!(HavocMutationsNoCrossoverType, MappedHavocCrossoverType), + ToMappingMutator ); /// Tuple type of the mutations that compose the Havoc mutator for mapped input types, for optional byte array input parts -pub type OptionMappedHavocMutationsType = tuple_list_type!( - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator, F1>, - MappingMutator>, F1>, - MappingMutator>, F1>, +pub type OptionMappedHavocMutationsType = map_tuple_list_type!( + map_tuple_list_type!( + merge_tuple_list_type!(HavocMutationsNoCrossoverType, MappedHavocCrossoverType), + ToOptionalMutator + ), + ToMappingMutator ); /// Get the mutations that compose the Havoc mutator (only applied to single inputs) diff --git a/libafl_bolts/src/tuples.rs b/libafl_bolts/src/tuples.rs index f9497799a7..4c9b5c78aa 100644 --- a/libafl_bolts/src/tuples.rs +++ b/libafl_bolts/src/tuples.rs @@ -833,6 +833,81 @@ macro_rules! tuple_for_each_mut { }; } +/// Maps the types of a mapping with a [`MappingFunctor`] +/// +/// ```rust +/// use libafl_bolts::{ +/// map_tuple_list_type, +/// tuples::{MappingFunctor, Map, tuple_list, tuple_list_type} +/// }; +/// +/// struct Wrapper(T); +/// struct MyMapper; +/// +/// impl MappingFunctor for MyMapper { +/// type Output = Wrapper; +/// +/// fn apply(&mut self, from: T) -> >::Output { +/// Wrapper(from) +/// } +/// } +/// +/// struct A; +/// struct B; +/// struct C; +/// +/// type OrigType = tuple_list_type!(A, B, C); +/// type MappedType = map_tuple_list_type!(OrigType, MyMapper); +/// let orig: OrigType = tuple_list!(A, B, C); +/// let _mapped: MappedType = orig.map(MyMapper); +/// ``` +#[macro_export] +macro_rules! map_tuple_list_type { + ($Tuple:ty, $Mapper:ty) => { + <$Tuple as $crate::tuples::Map<$Mapper>>::MapResult + }; +} + +/// Merges the types of two merged [`tuple_list!`]s +/// +/// ```rust +/// use libafl_bolts::{merge_tuple_list_type, tuples::{Merge, tuple_list, tuple_list_type}}; +/// +/// struct A; +/// struct B; +/// struct C; +/// struct D; +/// struct E; +/// +/// type Lhs = tuple_list_type!(A, B, C); +/// type Rhs = tuple_list_type!(D, E); +/// type Merged = merge_tuple_list_type!(Lhs, Rhs); +/// +/// let lhs: Lhs = tuple_list!(A, B, C); +/// let rhs: Rhs = tuple_list!(D, E); +/// let _merged: Merged = lhs.merge(rhs); +/// ``` +#[macro_export] +macro_rules! merge_tuple_list_type { + // Base case: when only two types are provided, apply the Merge trait directly + ($Type1:ty) => { + $Type1 + }; + + // Base case: when only two types are provided, apply the Merge trait directly + ($Type1:ty, $Type2:ty) => { + <$Type1 as $crate::tuples::Merge<$Type2>>::MergeResult + }; + + // Recursive case: when more than two types are provided + ($Type1:ty, $Type2:ty, $( $rest:ty ),+) => { + merge_tuple_list_type!( + <$Type1 as $crate::tuples::Merge<$Type2>>::MergeResult, + $( $rest ),+ + ) + }; +} + /* // Define trait and implement it for several primitive types. @@ -864,11 +939,13 @@ impl PlusOne for (Head, Tail) where #[cfg(test)] mod test { + use core::marker::PhantomData; + use tuple_list::{tuple_list, tuple_list_type}; #[cfg(feature = "alloc")] use crate::ownedref::OwnedMutSlice; - use crate::tuples::{type_eq, Map, MappingFunctor}; + use crate::tuples::{type_eq, Map, MappingFunctor, Merge}; #[test] // for type name tests @@ -916,9 +993,11 @@ mod test { #[test] fn test_mapper() { struct W(T); - struct MyMapper; - impl MappingFunctor for MyMapper { + // PhantomData shows how to deal with mappers that have generics + struct ExampleMapper

(PhantomData

); + + impl MappingFunctor for ExampleMapper

{ type Output = W; fn apply(&mut self, from: T) -> Self::Output { @@ -930,12 +1009,40 @@ mod test { struct B; struct C; - let orig = tuple_list!(A, B, C); - let mapped = orig.map(MyMapper); + type OrigType = tuple_list_type!(A, B, C); + type MappedType = map_tuple_list_type!(OrigType, ExampleMapper); + let orig: OrigType = tuple_list!(A, B, C); + let _mapped: MappedType = orig.map(ExampleMapper(PhantomData::)); + } - // this won't compile if the mapped type is not correct - #[expect(clippy::no_effect_underscore_binding)] - let _type_assert: tuple_list_type!(W, W, W) = mapped; + #[test] + fn test_merge() { + struct A; + struct B; + struct C; + struct D; + struct E; + + type Lhs = tuple_list_type!(A, B, C); + type Rhs = tuple_list_type!(D, E); + type Merged = merge_tuple_list_type!(Lhs, Rhs); + type IndividuallyMergedPre = merge_tuple_list_type!( + tuple_list_type!(A), + tuple_list_type!(B), + tuple_list_type!(C), + Rhs + ); + type IndividuallyMergedPost = + merge_tuple_list_type!(Lhs, tuple_list_type!(D), tuple_list_type!(E)); + type MergedCloned = merge_tuple_list_type!(Merged); + + let lhs: Lhs = tuple_list!(A, B, C); + let rhs: Rhs = tuple_list!(D, E); + let merged: Merged = lhs.merge(rhs); + let merged: IndividuallyMergedPre = merged; + let merged: IndividuallyMergedPost = merged; + #[allow(clippy::no_effect_underscore_binding)] + let _merged: MergedCloned = merged; } /// Function that tests the tuple macros From d16ede37a4f947afd0c29af0ab27b5b198792a37 Mon Sep 17 00:00:00 2001 From: s1341 Date: Tue, 24 Dec 2024 11:00:44 +0200 Subject: [PATCH 20/34] libafl_cc: Automatically find llvm_ar path (#2790) --- libafl_cc/build.rs | 18 ++++++++++++++++++ libafl_cc/src/ar.rs | 8 +++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/libafl_cc/build.rs b/libafl_cc/build.rs index c602457e58..d58b8ca835 100644 --- a/libafl_cc/build.rs +++ b/libafl_cc/build.rs @@ -234,6 +234,7 @@ fn main() { println!("cargo:rerun-if-env-changed=LLVM_CONFIG"); println!("cargo:rerun-if-env-changed=LLVM_BINDIR"); + println!("cargo:rerun-if-env-changed=LLVM_AR_PATH"); println!("cargo:rerun-if-env-changed=LLVM_CXXFLAGS"); println!("cargo:rerun-if-env-changed=LLVM_LDFLAGS"); println!("cargo:rerun-if-env-changed=LLVM_VERSION"); @@ -244,6 +245,7 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); let llvm_bindir = env::var("LLVM_BINDIR"); + let llvm_ar_path = env::var("LLVM_AR_PATH"); let llvm_cxxflags = env::var("LLVM_CXXFLAGS"); let llvm_ldflags = env::var("LLVM_LDFLAGS"); let llvm_version = env::var("LLVM_VERSION"); @@ -266,6 +268,8 @@ fn main() { pub const CLANG_PATH: &str = \"clang\"; /// The path to the `clang++` executable pub const CLANGXX_PATH: &str = \"clang++\"; +/// The path to the `llvm-ar` executable +pub const LLVM_AR_PATH: &str = \"llvm-ar\"; /// The llvm version used to build llvm passes pub const LIBAFL_CC_LLVM_VERSION: Option = None; " @@ -281,16 +285,24 @@ pub const LIBAFL_CC_LLVM_VERSION: Option = None; exec_llvm_config(&["--bindir"]) }; let bindir_path = Path::new(&llvm_bindir); + let llvm_ar_path = if let Ok(ar_path) = llvm_ar_path { + ar_path + } else { + exec_llvm_config(&["--bindir"]) + }; let clang; let clangcpp; + let llvm_ar; if cfg!(windows) { clang = bindir_path.join("clang.exe"); clangcpp = bindir_path.join("clang++.exe"); + llvm_ar = Path::new(&llvm_ar_path).join("llvm-ar.exe"); } else { clang = bindir_path.join("clang"); clangcpp = bindir_path.join("clang++"); + llvm_ar = Path::new(&llvm_ar_path).join("llvm-ar"); } if !clang.exists() { @@ -302,6 +314,10 @@ pub const LIBAFL_CC_LLVM_VERSION: Option = None; println!("cargo:warning=Failed to find clang++ frontend."); return; } + if !llvm_ar.exists() { + println!("cargo:warning=Failed to find llvm-ar archiver."); + return; + } let cxxflags = if let Ok(flags) = llvm_cxxflags { flags @@ -344,6 +360,8 @@ pub const LIBAFL_CC_LLVM_VERSION: Option = None; pub const CLANG_PATH: &str = {clang:?}; /// The path to the `clang++` executable pub const CLANGXX_PATH: &str = {clangcpp:?}; + /// The path to the `llvm-ar` executable + pub const LLVM_AR_PATH: &str = {llvm_ar:?}; /// The default size of the edges map the fuzzer uses pub const EDGES_MAP_DEFAULT_SIZE: usize = {edge_map_default_size}; diff --git a/libafl_cc/src/ar.rs b/libafl_cc/src/ar.rs index 373d6b4b2c..50eeb3e68b 100644 --- a/libafl_cc/src/ar.rs +++ b/libafl_cc/src/ar.rs @@ -5,6 +5,8 @@ use std::{env, path::PathBuf, str::FromStr}; use crate::{Error, ToolWrapper, LIB_EXT, LIB_PREFIX}; +include!(concat!(env!("OUT_DIR"), "/clang_constants.rs")); + /// Wrap Clang #[expect(clippy::struct_excessive_bools)] #[derive(Debug)] @@ -184,11 +186,7 @@ impl ToolWrapper for ArWrapper { }) .collect::>(); - let Ok(ar_path) = env::var("LLVM_AR_PATH") else { - panic!("Couldn't find llvm-ar. Specify the `LLVM_AR_PATH` environment variable"); - }; - - args.push(ar_path); + args.push(LLVM_AR_PATH.to_string()); args.extend_from_slice(base_args.as_slice()); From c6f5646ffd63198dbd4b89f3574556af6c2f2ca3 Mon Sep 17 00:00:00 2001 From: s1341 Date: Tue, 24 Dec 2024 15:22:01 +0200 Subject: [PATCH 21/34] imemory_ondisk: Don't fail write under any circumstances if locking is disabled (#2791) * imemory_ondisk: Don't fail write under any circumstances if locking is disabled * fmt * inmemory_ondisk: Add a log message on failure * clippy' * micro optimization --- libafl/src/corpus/inmemory_ondisk.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libafl/src/corpus/inmemory_ondisk.rs b/libafl/src/corpus/inmemory_ondisk.rs index c2e0a5536b..8afccf93cb 100644 --- a/libafl/src/corpus/inmemory_ondisk.rs +++ b/libafl/src/corpus/inmemory_ondisk.rs @@ -442,7 +442,12 @@ impl InMemoryOnDiskCorpus { *testcase.metadata_path_mut() = Some(metafile_path); } - self.store_input_from(testcase)?; + if let Err(err) = self.store_input_from(testcase) { + if self.locking { + return Err(err); + } + log::error!("An error occurred when trying to write a testcase without locking: {err}"); + } Ok(()) } From 28b5c4af33994c615fd1bdd71340217168b21ac5 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 24 Dec 2024 17:04:50 +0100 Subject: [PATCH 22/34] Revert changes to global Cargo.toml --- Cargo.toml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 4a080f32ea..335e8eb66b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,13 +56,28 @@ license = "MIT OR Apache-2.0" # Internal deps libafl = { path = "./libafl", version = "0.14.1", default-features = false } libafl_bolts = { path = "./libafl_bolts", version = "0.14.1", default-features = false } +libafl_cc = { path = "./libafl_cc", version = "0.14.1", default-features = false } +symcc_runtime = { path = "./libafl_concolic/symcc_runtime", version = "0.14.1", default-features = false } symcc_libafl = { path = "./libafl_concolic/symcc_libafl", version = "0.14.1", default-features = false } libafl_derive = { path = "./libafl_derive", version = "0.14.1", default-features = false } +libafl_frida = { path = "./libafl_frida", version = "0.14.1", default-features = false } libafl_intelpt = { path = "./libafl_intelpt", version = "0.14.1", default-features = false } +libafl_libfuzzer = { path = "./libafl_libfuzzer", version = "0.14.1", default-features = false } +libafl_nyx = { path = "./libafl_nyx", version = "0.14.1", default-features = false } libafl_targets = { path = "./libafl_targets", version = "0.14.1", default-features = false } +libafl_tinyinst = { path = "./libafl_tinyinst", version = "0.14.1", default-features = false } libafl_qemu = { path = "./libafl_qemu", version = "0.14.1", default-features = false } libafl_qemu_build = { path = "./libafl_qemu/libafl_qemu_build", version = "0.14.1", default-features = false } libafl_qemu_sys = { path = "./libafl_qemu/libafl_qemu_sys", version = "0.14.1", default-features = false } +libafl_sugar = { path = "./libafl_sugar", version = "0.14.1", default-features = false } +dump_constraints = { path = "./libafl_concolic/test/dump_constraints", version = "0.14.1", default-features = false } +runtime_test = { path = "./libafl_concolic/test/runtime_test", version = "0.14.1", default-features = false } +build_and_test_fuzzers = { path = "./utils/build_and_test_fuzzers", version = "0.14.1", default-features = false } +deexit = { path = "./utils/deexit", version = "0.14.1", default-features = false } +drcov_utils = { path = "./utils/drcov_utils", version = "0.14.1", default-features = false } +construct_automata = { path = "./utils/gramatron/construct_automata", version = "0.14.1", default-features = false } +libafl_benches = { path = "./utils/libafl_benches", version = "0.14.1", default-features = false } +libafl_jumper = { path = "./utils/libafl_jumper", version = "0.14.1", default-features = false } # External deps ahash = { version = "0.8.11", default-features = false } # The hash function already used in hashbrown From 60e188f99a7297e9d50012d6c723824baeba2215 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 24 Dec 2024 17:15:29 +0100 Subject: [PATCH 23/34] Hide std-dependent dependency behind std feature --- libafl/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 5a987c4352..7781fce16d 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -58,6 +58,7 @@ std = [ "serial_test", "libafl_bolts/std", "typed-builder", + "fastbloom", ] ## Tracks the Feedbacks and the Objectives that were interesting for a Testcase @@ -291,7 +292,7 @@ document-features = { workspace = true, optional = true } clap = { workspace = true, optional = true } num_enum = { workspace = true, optional = true } libipt = { workspace = true, optional = true } -fastbloom = "0.8.0" +fastbloom = { version = "0.8.0", optional = true } [lints] workspace = true From 68041b900617d2ad3d850866f47a7d0331773a9e Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 24 Dec 2024 17:16:47 +0100 Subject: [PATCH 24/34] Fix example fuzzer --- fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs index b471ad61d1..c80b5ac72d 100644 --- a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs +++ b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs @@ -138,8 +138,7 @@ pub fn main() { let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); #[cfg(feature = "bloom_input_filter")] let mut fuzzer = - StdFuzzer::new_with_bloom_input_filter(scheduler, feedback, objective, 10_000_000, 0.001) - .unwrap(); + StdFuzzer::with_bloom_filter(scheduler, feedback, objective, 10_000_000, 0.001); // Create the executor for an in-process function with just one observer let executor = CustomExecutor::new(&state); From e3f530e82274731fa40578cb16f1784e4e9487f8 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 24 Dec 2024 17:27:48 +0100 Subject: [PATCH 25/34] Rename constructor for filtered fuzzer --- fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs | 2 +- libafl/src/fuzzer/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs index c80b5ac72d..adeae32832 100644 --- a/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs +++ b/fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs @@ -138,7 +138,7 @@ pub fn main() { let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); #[cfg(feature = "bloom_input_filter")] let mut fuzzer = - StdFuzzer::with_bloom_filter(scheduler, feedback, objective, 10_000_000, 0.001); + StdFuzzer::with_bloom_input_filter(scheduler, feedback, objective, 10_000_000, 0.001); // Create the executor for an in-process function with just one observer let executor = CustomExecutor::new(&state); diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 1034d2ec21..0303888c5e 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -888,7 +888,7 @@ impl StdFuzzer { /// This is achieved by hashing each input and using a bloom filter to differentiate inputs. /// /// Use this implementation if hashing each input is very fast compared to executing potential duplicate inputs. - pub fn with_bloom_filter( + pub fn with_bloom_input_filter( scheduler: CS, feedback: F, objective: OF, @@ -1033,7 +1033,7 @@ mod tests { fn filtered_execution() { let execution_count = RefCell::new(0); let scheduler = StdScheduler::new(); - let mut fuzzer = StdFuzzer::with_bloom_filter(scheduler, (), (), 100, 1e-4); + let mut fuzzer = StdFuzzer::with_bloom_input_filter(scheduler, (), (), 100, 1e-4); let mut state = StdState::new( StdRand::new(), InMemoryCorpus::new(), From db994a48f1c0c0f898fa41f89ffcacca3851d528 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 24 Dec 2024 17:28:31 +0100 Subject: [PATCH 26/34] Reorder generics alphabetically --- libafl/src/fuzzer/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 0303888c5e..26e4d04f76 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -303,7 +303,7 @@ impl HasObjective for StdFuzzer { } } -impl ExecutionProcessor::Input, OT, S> +impl ExecutionProcessor::Input, OT, S> for StdFuzzer where CS: Scheduler<::Input, S>, @@ -509,7 +509,7 @@ where } } -impl EvaluatorObservers::Input, S> +impl EvaluatorObservers::Input, S> for StdFuzzer where CS: Scheduler<::Input, S>, @@ -587,7 +587,7 @@ impl InputFilter for BloomInputFilter { } } -impl Evaluator::Input, S> +impl Evaluator::Input, S> for StdFuzzer where CS: Scheduler<::Input, S>, @@ -745,7 +745,7 @@ where } } -impl Fuzzer for StdFuzzer +impl Fuzzer for StdFuzzer where CS: Scheduler, E: UsesState, @@ -918,7 +918,7 @@ pub trait ExecutesInput { ) -> Result; } -impl ExecutesInput::Input, S> +impl ExecutesInput::Input, S> for StdFuzzer where CS: Scheduler<::Input, S>, From d2dc266a38eb5c1ec15727e81f6a7269a62abbcf Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Tue, 24 Dec 2024 17:29:05 +0100 Subject: [PATCH 27/34] Rename HashingMutator, add note to MutationResult about filtered fuzzers --- libafl/src/mutators/hash.rs | 16 ++++++++-------- libafl/src/mutators/mod.rs | 5 +++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libafl/src/mutators/hash.rs b/libafl/src/mutators/hash.rs index 2eda9c1580..92c580a4f4 100644 --- a/libafl/src/mutators/hash.rs +++ b/libafl/src/mutators/hash.rs @@ -9,23 +9,23 @@ use super::{MutationResult, Mutator}; /// A wrapper around a [`Mutator`] that ensures an input really changed [`MutationResult::Mutated`] /// by hashing pre- and post-mutation #[derive(Debug)] -pub struct HashMutator { +pub struct HashingMutator { inner: M, name: Cow<'static, str>, } -impl HashMutator +impl HashingMutator where M: Named, { - /// Create a new [`HashMutator`] + /// Create a new [`HashingMutator`] pub fn new(inner: M) -> Self { - let name = Cow::Owned(format!("HashMutator<{}>", inner.name().clone())); + let name = Cow::Owned(format!("HashingMutator<{}>", inner.name().clone())); Self { inner, name } } } -impl Mutator for HashMutator +impl Mutator for HashingMutator where I: Hash, M: Mutator, @@ -41,7 +41,7 @@ where } } -impl Named for HashMutator { +impl Named for HashingMutator { fn name(&self) -> &Cow<'static, str> { &self.name } @@ -51,7 +51,7 @@ impl Named for HashMutator { mod tests { use crate::{ inputs::BytesInput, - mutators::{BytesSetMutator, HashMutator, MutationResult, Mutator}, + mutators::{BytesSetMutator, HashingMutator, MutationResult, Mutator}, state::NopState, }; @@ -70,7 +70,7 @@ mod tests { assert_eq!(BytesInput::new(vec![0; 5]), input); // now it is correctly reported as `MutationResult::Skipped` - let mut hash_mutator = HashMutator::new(inner); + let mut hash_mutator = HashingMutator::new(inner); assert_eq!( MutationResult::Skipped, hash_mutator.mutate(&mut state, &mut input).unwrap() diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index b87c66fb44..dd1cfe4ad4 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -92,8 +92,9 @@ impl From for MutationId { /// Result of the mutation. /// /// [`MutationResult::Skipped`] does not necessarily mean that the input changed, -/// just that the mutator did something. For slow targets, consider wrapping your -/// mutator in a [`hash::HashMutator`]. +/// just that the mutator did something. For slow targets, consider using +/// a filtered fuzzer (e.g. [`crate::fuzzer::StdFuzzer::with_bloom_input_filter`]) +/// or wrapping your mutator in a [`hash::HashingMutator`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MutationResult { /// The [`Mutator`] executed on this `Input`. It may not guarantee that the input has actually been changed. From bdade95f2f02d5c3f4a3cb50ba3181aeff50c52b Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sat, 28 Dec 2024 00:38:14 +0100 Subject: [PATCH 28/34] Improve StdFuzzer according to feedback --- libafl/src/fuzzer/mod.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 26e4d04f76..1b8e41daa6 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -869,18 +869,25 @@ where } } -impl StdFuzzer { - /// Create a new `StdFuzzer` with standard behavior. - pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self { +impl StdFuzzer { + /// Create a new [`StdFuzzer`] with standard behavior and the provided duplicate input execution filter. + pub fn with_input_filter(scheduler: CS, feedback: F, objective: OF, input_filter: IF) -> Self { Self { scheduler, feedback, objective, - input_filter: NopInputFilter, + input_filter, } } } +impl StdFuzzer { + /// Create a new [`StdFuzzer`] with standard behavior and no duplicate input execution filtering. + pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self { + Self::with_input_filter(scheduler, feedback, objective, NopInputFilter) + } +} + #[cfg(feature = "std")] // hashing requires std impl StdFuzzer { /// Create a new [`StdFuzzer`], which, with a certain certainty, executes each input only once. @@ -896,13 +903,7 @@ impl StdFuzzer { fp_p: f64, ) -> Self { let input_filter = BloomInputFilter::new(items_count, fp_p); - - Self { - scheduler, - feedback, - objective, - input_filter, - } + Self::with_input_filter(scheduler, feedback, objective, input_filter) } } From d2fda6bbbb75d1a9b6c62d6d43020b2878416476 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sat, 28 Dec 2024 00:39:50 +0100 Subject: [PATCH 29/34] rename hashing mutator --- libafl/src/mutators/hash.rs | 18 +++++++++--------- libafl/src/mutators/mod.rs | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libafl/src/mutators/hash.rs b/libafl/src/mutators/hash.rs index 92c580a4f4..8b231b7d75 100644 --- a/libafl/src/mutators/hash.rs +++ b/libafl/src/mutators/hash.rs @@ -7,25 +7,25 @@ use libafl_bolts::{generic_hash_std, Error, Named}; use super::{MutationResult, Mutator}; /// A wrapper around a [`Mutator`] that ensures an input really changed [`MutationResult::Mutated`] -/// by hashing pre- and post-mutation +/// by hashing pre- and post-mutation and comparing the values #[derive(Debug)] -pub struct HashingMutator { +pub struct MutationChecker { inner: M, name: Cow<'static, str>, } -impl HashingMutator +impl MutationChecker where M: Named, { - /// Create a new [`HashingMutator`] + /// Create a new [`MutationChecker`] pub fn new(inner: M) -> Self { - let name = Cow::Owned(format!("HashingMutator<{}>", inner.name().clone())); + let name = Cow::Owned(format!("MutationChecker<{}>", inner.name().clone())); Self { inner, name } } } -impl Mutator for HashingMutator +impl Mutator for MutationChecker where I: Hash, M: Mutator, @@ -41,7 +41,7 @@ where } } -impl Named for HashingMutator { +impl Named for MutationChecker { fn name(&self) -> &Cow<'static, str> { &self.name } @@ -51,7 +51,7 @@ impl Named for HashingMutator { mod tests { use crate::{ inputs::BytesInput, - mutators::{BytesSetMutator, HashingMutator, MutationResult, Mutator}, + mutators::{BytesSetMutator, MutationChecker, MutationResult, Mutator}, state::NopState, }; @@ -70,7 +70,7 @@ mod tests { assert_eq!(BytesInput::new(vec![0; 5]), input); // now it is correctly reported as `MutationResult::Skipped` - let mut hash_mutator = HashingMutator::new(inner); + let mut hash_mutator = MutationChecker::new(inner); assert_eq!( MutationResult::Skipped, hash_mutator.mutate(&mut state, &mut input).unwrap() diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index dd1cfe4ad4..5b2de83bd4 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -93,8 +93,8 @@ impl From for MutationId { /// /// [`MutationResult::Skipped`] does not necessarily mean that the input changed, /// just that the mutator did something. For slow targets, consider using -/// a filtered fuzzer (e.g. [`crate::fuzzer::StdFuzzer::with_bloom_input_filter`]) -/// or wrapping your mutator in a [`hash::HashingMutator`]. +/// a filtered fuzzer (see [`crate::fuzzer::StdFuzzer::with_input_filter`]) +/// or wrapping your mutator in a [`hash::MutationChecker`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MutationResult { /// The [`Mutator`] executed on this `Input`. It may not guarantee that the input has actually been changed. From 01c91fe64db4f2e400b4436b67b9bbd4e075828d Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sat, 28 Dec 2024 00:44:45 +0100 Subject: [PATCH 30/34] Fix english in comment --- libafl/src/stages/concolic.rs | 2 +- libafl/src/stages/mutational.rs | 4 ++-- libafl/src/stages/power.rs | 2 +- libafl/src/stages/tuneable.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libafl/src/stages/concolic.rs b/libafl/src/stages/concolic.rs index 55ecf3518c..7eb992ee4c 100644 --- a/libafl/src/stages/concolic.rs +++ b/libafl/src/stages/concolic.rs @@ -417,7 +417,7 @@ where for (index, new_byte) in mutation { input_copy.bytes_mut()[index] = new_byte; } - // Time is measured directly the `evaluate_filtered` function + // Time is measured directly in `evaluate_filtered` fuzzer.evaluate_filtered(state, executor, manager, input_copy)?; } } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index d147b1c1d6..322abfa2b9 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -277,7 +277,7 @@ where continue; } - // Time is measured directly the `evaluate_filtered` function + // Time is measured directly in `evaluate_filtered` let (untransformed, post) = input.try_transform_into(state)?; let (_, corpus_id) = fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; @@ -346,7 +346,7 @@ where let generated = self.mutator.multi_mutate(state, &input, None)?; for new_input in generated { - // Time is measured directly the `evaluate_filtered` function + // Time is measured directly in `evaluate_filtered` let (untransformed, post) = new_input.try_transform_into(state)?; let (_, corpus_id) = fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; diff --git a/libafl/src/stages/power.rs b/libafl/src/stages/power.rs index 3333656eba..284924dc06 100644 --- a/libafl/src/stages/power.rs +++ b/libafl/src/stages/power.rs @@ -189,7 +189,7 @@ where continue; } - // Time is measured directly the `evaluate_filtered` function + // Time is measured directly in `evaluate_filtered` let (untransformed, post) = input.try_transform_into(state)?; let (_, corpus_id) = fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; diff --git a/libafl/src/stages/tuneable.rs b/libafl/src/stages/tuneable.rs index a9b6d38a39..1c42b4889c 100644 --- a/libafl/src/stages/tuneable.rs +++ b/libafl/src/stages/tuneable.rs @@ -453,7 +453,7 @@ where return Ok(()); } - // Time is measured directly the `evaluate_filtered` function + // Time is measured directly in `evaluate_filtered` let (untransformed, post) = input.try_transform_into(state)?; let (_, corpus_id) = fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; From 40aa07c2b849f22eabcf1fee38184c7ac0547e88 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sat, 28 Dec 2024 00:51:42 +0100 Subject: [PATCH 31/34] Cleanup of old PRs that break the CI --- libafl_bolts/src/llmp.rs | 6 +++++- libafl_bolts/src/rands/mod.rs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libafl_bolts/src/llmp.rs b/libafl_bolts/src/llmp.rs index 614d502557..9e65e76c02 100644 --- a/libafl_bolts/src/llmp.rs +++ b/libafl_bolts/src/llmp.rs @@ -2669,7 +2669,11 @@ where self.inner .llmp_clients .binary_search_by_key(&client_id, |x| x.id) - .expect("Fatal error, client ID {client_id} not found in llmp_clients.") + .unwrap_or_else(|_| { + panic!( + "Fatal error, client ID {client_id:?} not found in llmp_clients." + ) + }) }; let client = &mut self.inner.llmp_clients[pos]; match client.recv()? { diff --git a/libafl_bolts/src/rands/mod.rs b/libafl_bolts/src/rands/mod.rs index a6a8730c95..c55f105b2b 100644 --- a/libafl_bolts/src/rands/mod.rs +++ b/libafl_bolts/src/rands/mod.rs @@ -18,7 +18,7 @@ static SEED_COUNTER: AtomicUsize = AtomicUsize::new(0); /// Return a pseudo-random seed. For `no_std` environments, a single deterministic sequence is used. #[must_use] -#[expect(unreachable_code)] +#[allow(unreachable_code)] // cfg dependent pub fn random_seed() -> u64 { #[cfg(feature = "std")] return random_seed_from_random_state(); @@ -365,7 +365,7 @@ impl Rand for Lehmer64Rand { fn set_seed(&mut self, mut seed: u64) { let hi = splitmix64(&mut seed); let lo = splitmix64(&mut seed) | 1; - self.s = u128::from(hi) << 64 | u128::from(lo); + self.s = (u128::from(hi) << 64) | u128::from(lo); } #[inline] From 3499e640b496a7bba390cf92597efb89d27588f3 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sat, 28 Dec 2024 00:55:47 +0100 Subject: [PATCH 32/34] Fix more CI bugs --- libafl_bolts/src/llmp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl_bolts/src/llmp.rs b/libafl_bolts/src/llmp.rs index 9e65e76c02..d191e02b6c 100644 --- a/libafl_bolts/src/llmp.rs +++ b/libafl_bolts/src/llmp.rs @@ -2772,7 +2772,7 @@ where self.inner .llmp_clients .binary_search_by_key(&client_id, |x| x.id) - .expect("Fatal error, client ID {client_id} not found in llmp_clients.") + .unwrap_or_else(|_| panic!("Fatal error, client ID {client_id:?} not found in llmp_clients.")) }; let map = &mut self.inner.llmp_clients[pos].current_recv_shmem; From 06499cd43ea0630310fb07e7feae34e33ab1419f Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sat, 28 Dec 2024 00:58:17 +0100 Subject: [PATCH 33/34] Code cleanup --- libafl_qemu/src/modules/usermode/asan.rs | 3 +-- libafl_qemu/src/modules/usermode/snapshot.rs | 3 +-- libafl_targets/src/libfuzzer/mod.rs | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/libafl_qemu/src/modules/usermode/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs index 96674a899d..5ff4cd5b31 100644 --- a/libafl_qemu/src/modules/usermode/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -1532,8 +1532,7 @@ mod addr2line_legacy { /// # Safety /// Will access the global [`FullBacktraceCollector`]. /// Calling this function concurrently might be racey. -#[expect(clippy::unnecessary_cast)] -#[expect(clippy::too_many_lines)] +#[expect(clippy::too_many_lines, clippy::unnecessary_cast)] pub unsafe fn asan_report(rt: &AsanGiovese, qemu: Qemu, pc: GuestAddr, err: &AsanError) { let mut regions = HashMap::new(); for region in qemu.mappings() { diff --git a/libafl_qemu/src/modules/usermode/snapshot.rs b/libafl_qemu/src/modules/usermode/snapshot.rs index 776fc749da..e3579ed0e9 100644 --- a/libafl_qemu/src/modules/usermode/snapshot.rs +++ b/libafl_qemu/src/modules/usermode/snapshot.rs @@ -780,8 +780,7 @@ where SyscallHookResult::new(None) } -#[expect(clippy::too_many_arguments)] -#[expect(non_upper_case_globals)] +#[expect(non_upper_case_globals, clippy::too_many_arguments)] pub fn trace_mmap_snapshot( emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, diff --git a/libafl_targets/src/libfuzzer/mod.rs b/libafl_targets/src/libfuzzer/mod.rs index f5d9d41bdb..4cd967808b 100644 --- a/libafl_targets/src/libfuzzer/mod.rs +++ b/libafl_targets/src/libfuzzer/mod.rs @@ -26,8 +26,7 @@ extern "C" { /// /// # Safety /// Calls the libfuzzer-style init function which is native code. -#[expect(clippy::similar_names)] -#[expect(clippy::must_use_candidate)] // nobody uses that return code... +#[expect(clippy::must_use_candidate, clippy::similar_names)] // nobody uses that return code... pub unsafe fn libfuzzer_initialize(args: &[String]) -> i32 { let args: Vec = args.iter().map(|x| x.clone() + "\0").collect(); let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect(); From 22f98ebc18774f34ac5b96b05523f9e80ca20868 Mon Sep 17 00:00:00 2001 From: Valentin Huber Date: Sat, 28 Dec 2024 01:57:45 +0100 Subject: [PATCH 34/34] Remove unnecessary comments --- libafl/src/stages/concolic.rs | 1 - libafl/src/stages/mutational.rs | 2 -- libafl/src/stages/power.rs | 1 - libafl/src/stages/tuneable.rs | 1 - 4 files changed, 5 deletions(-) diff --git a/libafl/src/stages/concolic.rs b/libafl/src/stages/concolic.rs index 7eb992ee4c..be5765f3c6 100644 --- a/libafl/src/stages/concolic.rs +++ b/libafl/src/stages/concolic.rs @@ -417,7 +417,6 @@ where for (index, new_byte) in mutation { input_copy.bytes_mut()[index] = new_byte; } - // Time is measured directly in `evaluate_filtered` fuzzer.evaluate_filtered(state, executor, manager, input_copy)?; } } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 322abfa2b9..9b3ffdbb9f 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -277,7 +277,6 @@ where continue; } - // Time is measured directly in `evaluate_filtered` let (untransformed, post) = input.try_transform_into(state)?; let (_, corpus_id) = fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; @@ -346,7 +345,6 @@ where let generated = self.mutator.multi_mutate(state, &input, None)?; for new_input in generated { - // Time is measured directly in `evaluate_filtered` let (untransformed, post) = new_input.try_transform_into(state)?; let (_, corpus_id) = fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; diff --git a/libafl/src/stages/power.rs b/libafl/src/stages/power.rs index 284924dc06..695d2c6038 100644 --- a/libafl/src/stages/power.rs +++ b/libafl/src/stages/power.rs @@ -189,7 +189,6 @@ where continue; } - // Time is measured directly in `evaluate_filtered` let (untransformed, post) = input.try_transform_into(state)?; let (_, corpus_id) = fuzzer.evaluate_filtered(state, executor, manager, untransformed)?; diff --git a/libafl/src/stages/tuneable.rs b/libafl/src/stages/tuneable.rs index 1c42b4889c..2764291281 100644 --- a/libafl/src/stages/tuneable.rs +++ b/libafl/src/stages/tuneable.rs @@ -453,7 +453,6 @@ where return Ok(()); } - // Time is measured directly in `evaluate_filtered` let (untransformed, post) = input.try_transform_into(state)?; let (_, corpus_id) = fuzzer.evaluate_filtered(state, executor, manager, untransformed)?;