Skip to content

Commit 40fd045

Browse files
feat(fuzz): rename mutation types to GenPrefix/GenSuffix/GenMutate
Addresses @0xalpharush's review comment on PR #13177: unify mutation strategies by renaming Prefix/Suffix/Abi to GenPrefix/GenSuffix/GenMutate. GenMutate now mutates a random number of calls (1 to all) using shuffled indices, similar to how GenPrefix generates a random prefix of new calls. This is more gradual than the previous 30% all / 70% single approach. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019bf992-367c-721c-a497-58a1a11ee2be
1 parent d452197 commit 40fd045

1 file changed

Lines changed: 34 additions & 31 deletions

File tree

crates/evm/evm/src/executors/corpus.rs

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,12 @@ enum MutationType {
8585
Repeat,
8686
/// Interleave calls from two random call sequences.
8787
Interleave,
88-
/// Replace prefix of the original call sequence with new calls.
89-
Prefix,
90-
/// Replace suffix of the original call sequence with new calls.
91-
Suffix,
92-
/// ABI mutate random args of selected call in sequence.
93-
Abi,
88+
/// Generate new calls for a random prefix of the sequence.
89+
GenPrefix,
90+
/// Generate new calls for a random suffix of the sequence.
91+
GenSuffix,
92+
/// ABI mutate a random number of calls (1 to all) in the sequence.
93+
GenMutate,
9494
}
9595

9696
/// Holds Corpus information.
@@ -286,9 +286,9 @@ impl WorkerCorpus {
286286
Just(MutationType::Splice),
287287
Just(MutationType::Repeat),
288288
Just(MutationType::Interleave),
289-
Just(MutationType::Prefix),
290-
Just(MutationType::Suffix),
291-
Just(MutationType::Abi),
289+
Just(MutationType::GenPrefix),
290+
Just(MutationType::GenSuffix),
291+
Just(MutationType::GenMutate),
292292
]
293293
.boxed();
294294

@@ -531,30 +531,33 @@ impl WorkerCorpus {
531531
new_seq.push(tx);
532532
}
533533
}
534-
MutationType::Prefix => {
534+
MutationType::GenPrefix => {
535535
let corpus = if rng.random::<bool>() { primary } else { secondary };
536-
trace!(target: "corpus", "overwrite prefix of {}", corpus.uuid);
536+
trace!(target: "corpus", "generate prefix of {}", corpus.uuid);
537537

538538
self.current_mutated = Some(corpus.uuid);
539539

540540
new_seq = corpus.tx_seq.clone();
541+
// Generate new calls for a random prefix (0 to all elements).
541542
for i in 0..rng.random_range(0..=new_seq.len()) {
542543
new_seq[i] = self.new_tx(test_runner)?;
543544
}
544545
}
545-
MutationType::Suffix => {
546+
MutationType::GenSuffix => {
546547
let corpus = if rng.random::<bool>() { primary } else { secondary };
547-
trace!(target: "corpus", "overwrite suffix of {}", corpus.uuid);
548+
trace!(target: "corpus", "generate suffix of {}", corpus.uuid);
548549

549550
self.current_mutated = Some(corpus.uuid);
550551

551552
new_seq = corpus.tx_seq.clone();
552-
for i in new_seq.len() - rng.random_range(0..new_seq.len())..corpus.tx_seq.len()
553-
{
554-
new_seq[i] = self.new_tx(test_runner)?;
553+
// Generate new calls for a random suffix (0 to all elements).
554+
let len = new_seq.len();
555+
let start = len - rng.random_range(0..len);
556+
for tx in new_seq.iter_mut().skip(start) {
557+
*tx = self.new_tx(test_runner)?;
555558
}
556559
}
557-
MutationType::Abi => {
560+
MutationType::GenMutate => {
558561
let targets = targeted_contracts.targets.lock();
559562
let corpus = if rng.random::<bool>() { primary } else { secondary };
560563
trace!(target: "corpus", "ABI mutate args of {}", corpus.uuid);
@@ -563,20 +566,20 @@ impl WorkerCorpus {
563566

564567
new_seq = corpus.tx_seq.clone();
565568

566-
// 30% chance to mutate ALL calls in the sequence.
567-
// This helps break multi-constraint bugs where any call could hit the target.
568-
if rng.random_range(0..10) < 3 {
569-
for tx in &mut new_seq {
570-
if let (_, Some(function)) = targets.fuzzed_artifacts(tx)
571-
&& !function.inputs.is_empty()
572-
{
573-
self.abi_mutate(tx, function, test_runner, fuzz_state, senders)?;
574-
}
575-
}
576-
} else {
577-
// Standard: mutate a single random call.
578-
let idx = rng.random_range(0..new_seq.len());
579-
let tx = new_seq.get_mut(idx).unwrap();
569+
let len = new_seq.len();
570+
// Mutate a random number of calls (1 to all), similar to how GenPrefix
571+
// generates a random number of new calls.
572+
let n_to_mutate = rng.random_range(1..=len);
573+
574+
// Shuffle indices to select which calls to mutate.
575+
let mut indices: Vec<usize> = (0..len).collect();
576+
for i in (1..len).rev() {
577+
let j = rng.random_range(0..=i);
578+
indices.swap(i, j);
579+
}
580+
581+
for i in indices.into_iter().take(n_to_mutate) {
582+
let tx = &mut new_seq[i];
580583
if let (_, Some(function)) = targets.fuzzed_artifacts(tx)
581584
&& !function.inputs.is_empty()
582585
{

0 commit comments

Comments
 (0)