Skip to content

Commit 3412508

Browse files
grandizzyDaniPopes
andauthored
feat(forge): Remove proptest runner from fuzzed tests (#11061)
* Remove proptest from fuzzed tests * Persist and replay fuzz failure * Nits * add fuzz coverage metrics config, cleanup * rm unec RefCell, refactor * Add inline fuzz coverage metrics * Same corpus manager for fuzz and invariant tests * Fix fmt * Unify configs * Use corpus manager / abi mutation for fuzz tests - Move hit count in corpus - add mutate_abi and evict_oldes_corpus fns TODO: load persisted corpus from file on fuzz test start * Replay corpus for fuzz tests as well * Add corpus replay failures for fuzz tests too * cleanup fuzz corpus on forge clean, display errors * more abi mutations * rand bit flip mutation + add support for more types * clippy * More mutations - interesting byte and word * More mutations, traces * Review changes * add mutators traits * Simplify * Update crates/evm/fuzz/src/strategies/mod.rs Co-authored-by: DaniPopes <[email protected]> * Avoid clone when mutating array and tuples * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: DaniPopes <[email protected]> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: DaniPopes <[email protected]> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: DaniPopes <[email protected]> * Fix typo * use wrapping add / sub for inc/dec mutation * Validate inc/dec, better word mutation, tests * Remove unused validation, early return on validation * mutators to operate on &mut [u8], instrument, cleanup * Mutate fixedbytes, more address mutations * Unify mutators * Add word mutation unit test * Address nits * Update crates/evm/fuzz/src/strategies/param.rs Co-authored-by: DaniPopes <[email protected]> * Update crates/evm/fuzz/src/strategies/param.rs Co-authored-by: DaniPopes <[email protected]> * Rename mutate_random_array/tuple_value --------- Co-authored-by: DaniPopes <[email protected]>
1 parent ddc5468 commit 3412508

File tree

27 files changed

+1395
-518
lines changed

27 files changed

+1395
-518
lines changed

crates/config/src/fuzz.rs

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ pub struct FuzzConfig {
2424
pub dictionary: FuzzDictionaryConfig,
2525
/// Number of runs to execute and include in the gas report.
2626
pub gas_report_samples: u32,
27+
/// The fuzz corpus configuration.
28+
#[serde(flatten)]
29+
pub corpus: FuzzCorpusConfig,
2730
/// Path where fuzz failures are recorded and replayed.
2831
pub failure_persist_dir: Option<PathBuf>,
29-
/// Name of the file to record fuzz failures, defaults to `failures`.
30-
pub failure_persist_file: Option<String>,
3132
/// show `console.log` in fuzz test, defaults to `false`
3233
pub show_logs: bool,
3334
/// Optional timeout (in seconds) for each property test
@@ -43,8 +44,8 @@ impl Default for FuzzConfig {
4344
seed: None,
4445
dictionary: FuzzDictionaryConfig::default(),
4546
gas_report_samples: 256,
47+
corpus: FuzzCorpusConfig::default(),
4648
failure_persist_dir: None,
47-
failure_persist_file: None,
4849
show_logs: false,
4950
timeout: None,
5051
}
@@ -54,11 +55,7 @@ impl Default for FuzzConfig {
5455
impl FuzzConfig {
5556
/// Creates fuzz configuration to write failures in `{PROJECT_ROOT}/cache/fuzz` dir.
5657
pub fn new(cache_dir: PathBuf) -> Self {
57-
Self {
58-
failure_persist_dir: Some(cache_dir),
59-
failure_persist_file: Some("failures".to_string()),
60-
..Default::default()
61-
}
58+
Self { failure_persist_dir: Some(cache_dir), ..Default::default() }
6259
}
6360
}
6461

@@ -97,3 +94,49 @@ impl Default for FuzzDictionaryConfig {
9794
}
9895
}
9996
}
97+
98+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
99+
pub struct FuzzCorpusConfig {
100+
// Path to corpus directory, enabled coverage guided fuzzing mode.
101+
// If not set then sequences producing new coverage are not persisted and mutated.
102+
pub corpus_dir: Option<PathBuf>,
103+
// Whether corpus to use gzip file compression and decompression.
104+
pub corpus_gzip: bool,
105+
// Number of mutations until entry marked as eligible to be flushed from in-memory corpus.
106+
// Mutations will be performed at least `corpus_min_mutations` times.
107+
pub corpus_min_mutations: usize,
108+
// Number of corpus that won't be evicted from memory.
109+
pub corpus_min_size: usize,
110+
/// Whether to collect and display edge coverage metrics.
111+
pub show_edge_coverage: bool,
112+
}
113+
114+
impl FuzzCorpusConfig {
115+
pub fn with_test_name(&mut self, test_name: &String) {
116+
if let Some(corpus_dir) = &self.corpus_dir {
117+
self.corpus_dir = Some(corpus_dir.join(test_name));
118+
}
119+
}
120+
121+
/// Whether edge coverage should be collected and displayed.
122+
pub fn collect_edge_coverage(&self) -> bool {
123+
self.corpus_dir.is_some() || self.show_edge_coverage
124+
}
125+
126+
/// Whether coverage guided fuzzing is enabled.
127+
pub fn is_coverage_guided(&self) -> bool {
128+
self.corpus_dir.is_some()
129+
}
130+
}
131+
132+
impl Default for FuzzCorpusConfig {
133+
fn default() -> Self {
134+
Self {
135+
corpus_dir: None,
136+
corpus_gzip: true,
137+
corpus_min_mutations: 5,
138+
corpus_min_size: 0,
139+
show_edge_coverage: false,
140+
}
141+
}
142+
}

crates/config/src/invariant.rs

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Configuration for invariant testing
22
3-
use crate::fuzz::FuzzDictionaryConfig;
3+
use crate::fuzz::{FuzzCorpusConfig, FuzzDictionaryConfig};
44
use serde::{Deserialize, Serialize};
55
use std::path::PathBuf;
66

@@ -26,15 +26,9 @@ pub struct InvariantConfig {
2626
pub max_assume_rejects: u32,
2727
/// Number of runs to execute and include in the gas report.
2828
pub gas_report_samples: u32,
29-
/// Path where invariant corpus is stored, enables coverage guided fuzzing and edge coverage
30-
/// metrics.
31-
pub corpus_dir: Option<PathBuf>,
32-
/// Whether corpus to use gzip file compression and decompression.
33-
pub corpus_gzip: bool,
34-
// Number of corpus mutations until marked as eligible to be flushed from memory.
35-
pub corpus_min_mutations: usize,
36-
// Number of corpus that won't be evicted from memory.
37-
pub corpus_min_size: usize,
29+
/// The fuzz corpus configuration.
30+
#[serde(flatten)]
31+
pub corpus: FuzzCorpusConfig,
3832
/// Path where invariant failures are recorded and replayed.
3933
pub failure_persist_dir: Option<PathBuf>,
4034
/// Whether to collect and display fuzzed selectors metrics.
@@ -43,8 +37,6 @@ pub struct InvariantConfig {
4337
pub timeout: Option<u32>,
4438
/// Display counterexample as solidity calls.
4539
pub show_solidity: bool,
46-
/// Whether to collect and display edge coverage metrics.
47-
pub show_edge_coverage: bool,
4840
}
4941

5042
impl Default for InvariantConfig {
@@ -58,15 +50,11 @@ impl Default for InvariantConfig {
5850
shrink_run_limit: 5000,
5951
max_assume_rejects: 65536,
6052
gas_report_samples: 256,
61-
corpus_dir: None,
62-
corpus_gzip: true,
63-
corpus_min_mutations: 5,
64-
corpus_min_size: 0,
53+
corpus: FuzzCorpusConfig::default(),
6554
failure_persist_dir: None,
6655
show_metrics: true,
6756
timeout: None,
6857
show_solidity: false,
69-
show_edge_coverage: false,
7058
}
7159
}
7260
}
@@ -83,15 +71,11 @@ impl InvariantConfig {
8371
shrink_run_limit: 5000,
8472
max_assume_rejects: 65536,
8573
gas_report_samples: 256,
86-
corpus_dir: None,
87-
corpus_gzip: true,
88-
corpus_min_mutations: 5,
89-
corpus_min_size: 0,
74+
corpus: FuzzCorpusConfig::default(),
9075
failure_persist_dir: Some(cache_dir),
9176
show_metrics: true,
9277
timeout: None,
9378
show_solidity: false,
94-
show_edge_coverage: false,
9579
}
9680
}
9781
}

crates/config/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ pub use providers::Remappings;
107107
use providers::*;
108108

109109
mod fuzz;
110-
pub use fuzz::{FuzzConfig, FuzzDictionaryConfig};
110+
pub use fuzz::{FuzzConfig, FuzzCorpusConfig, FuzzDictionaryConfig};
111111

112112
mod invariant;
113113
pub use invariant::InvariantConfig;
@@ -1095,7 +1095,8 @@ impl Config {
10951095
}
10961096
};
10971097
remove_test_dir(&self.fuzz.failure_persist_dir);
1098-
remove_test_dir(&self.invariant.corpus_dir);
1098+
remove_test_dir(&self.fuzz.corpus.corpus_dir);
1099+
remove_test_dir(&self.invariant.corpus.corpus_dir);
10991100
remove_test_dir(&self.invariant.failure_persist_dir);
11001101

11011102
Ok(())
@@ -4614,7 +4615,6 @@ mod tests {
46144615
runs: 512,
46154616
depth: 10,
46164617
failure_persist_dir: Some(PathBuf::from("cache/invariant")),
4617-
corpus_dir: None,
46184618
..Default::default()
46194619
}
46204620
);

crates/evm/core/src/constants.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ pub const MAGIC_ASSUME: &[u8] = b"FOUNDRY::ASSUME";
3737
/// Magic return value returned by the `skip` cheatcode. Optionally appended with a reason.
3838
pub const MAGIC_SKIP: &[u8] = b"FOUNDRY::SKIP";
3939

40-
/// Test timeout return value.
41-
pub const TEST_TIMEOUT: &str = "FOUNDRY::TEST_TIMEOUT";
42-
4340
/// The address that deploys the default CREATE2 deployer contract.
4441
pub const DEFAULT_CREATE2_DEPLOYER_DEPLOYER: Address =
4542
address!("0x3fAB184622Dc19b6109349B94811493BF2a45362");

0 commit comments

Comments
 (0)