Skip to content

Commit d96a820

Browse files
committed
Mutations almost completely refactored
1 parent f610496 commit d96a820

File tree

3 files changed

+347
-183
lines changed

3 files changed

+347
-183
lines changed

tooling/greybox_fuzzer/src/mutation/array.rs

+104-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1+
use std::cmp::min;
2+
13
use noirc_abi::input_parser::InputValue;
24
use rand::Rng;
35
use rand_xorshift::XorShiftRng;
46

57
use crate::mutation::configurations::{SpliceMutation, BASIC_SPLICE_MUTATION_CONFIGURATION};
68

7-
use super::configurations::{SpliceCandidate, BASIC_SPLICE_CANDIDATE_PRIORITIZATION_CONFIGURATION};
8-
struct ArraySplicer<'a> {
9+
use super::configurations::{
10+
SpliceCandidate, StructuralMutation, BASIC_SPLICE_CANDIDATE_PRIORITIZATION_CONFIGURATION,
11+
BASIC_STRUCTURE_MUTATION_CONFIGURATION, BASIC_VECTOR_STRUCTURE_MUTATION_CONFIGURATION,
12+
};
13+
struct ArrayMutator<'a> {
914
prng: &'a mut XorShiftRng,
1015
}
1116

12-
impl<'a> ArraySplicer<'a> {
17+
impl<'a> ArrayMutator<'a> {
1318
pub fn new(prng: &'a mut XorShiftRng) -> Self {
1419
Self { prng }
1520
}
@@ -99,13 +104,108 @@ impl<'a> ArraySplicer<'a> {
99104
};
100105
InputValue::Vec(result)
101106
}
107+
108+
/// Perform one of structural mutations on the vector of input values
109+
pub fn perform_structure_mutation_on_vector(
110+
&mut self,
111+
input_buffer: &Vec<InputValue>,
112+
) -> Vec<InputValue> {
113+
let result = match BASIC_VECTOR_STRUCTURE_MUTATION_CONFIGURATION.select(self.prng) {
114+
StructuralMutation::ChaoticSelfSplice => {
115+
self.chaotic_splice(input_buffer, input_buffer)
116+
}
117+
StructuralMutation::ChunkDuplication => self.duplicate_chunk(input_buffer),
118+
StructuralMutation::Swap => self.swap(input_buffer),
119+
StructuralMutation::RandomValueDuplication => {
120+
panic!("Vector mutations should have a value duplication weight of zero")
121+
}
122+
};
123+
124+
result
125+
}
126+
127+
/// Perform structural mutations on a InputValue that is a vector
128+
fn perform_structure_mutation(&mut self, input_buffer: &InputValue) -> InputValue {
129+
let input_vec = match input_buffer {
130+
InputValue::Vec(internal_vector) => internal_vector,
131+
_ => panic!("Expect to get a vector input value"),
132+
};
133+
InputValue::Vec(self.perform_structure_mutation_on_vector(input_vec))
134+
}
135+
/// Swap 2 random chunks in the buffer
136+
fn swap(&mut self, buffer: &Vec<InputValue>) -> Vec<InputValue> {
137+
let mut result = Vec::new();
138+
let buffer_length = buffer.len();
139+
140+
// We need to leave at least the last byte for the second chunk
141+
let first_chunk_position = self.prng.gen_range(0..(buffer_length - 1));
142+
143+
// The second chunk starts after the first
144+
let second_chunk_position = self.prng.gen_range((first_chunk_position + 1)..buffer_length);
145+
146+
let first_chunk_end =
147+
self.prng.gen_range((first_chunk_position + 1)..=second_chunk_position);
148+
149+
let second_chunk_end = self.prng.gen_range((second_chunk_position + 1)..=buffer_length);
150+
151+
// Leave the start in place
152+
result.extend_from_slice(&buffer[0..first_chunk_position]);
153+
154+
// Insert second chunk
155+
result.extend_from_slice(&buffer[second_chunk_position..(second_chunk_end)]);
156+
157+
// Insert what's in between the chunks
158+
result.extend_from_slice(&buffer[first_chunk_end..(second_chunk_position)]);
159+
160+
// Insert first chunk
161+
result.extend_from_slice(&buffer[first_chunk_position..first_chunk_end]);
162+
163+
// Insert the tail
164+
result.extend_from_slice(&buffer[second_chunk_end..buffer_length]);
165+
166+
result
167+
}
168+
169+
/// Take a random chunk of the input and insert it several times into the input
170+
fn duplicate_chunk(&mut self, input_buffer: &Vec<InputValue>) -> Vec<InputValue> {
171+
let mut result = input_buffer.clone();
172+
let buffer_length = input_buffer.len();
173+
// The maximum length of the chunk is half the total length
174+
let maximum_chunk_length = buffer_length / 2;
175+
176+
// Get a random position for the chunk
177+
let chunk_position = self.prng.gen_range(0..=buffer_length - 1);
178+
179+
// Pick size
180+
let chunk_size =
181+
self.prng.gen_range(1..=min(buffer_length - chunk_position, maximum_chunk_length));
182+
183+
// Find an insertion position with enough space
184+
let insertion_position = self.prng.gen_range(0..(buffer_length - chunk_size));
185+
186+
// Determine how many times to repeat
187+
let maximum_insertion_count = (buffer_length - insertion_position) / chunk_size;
188+
let insertion_count = self.prng.gen_range(0..=maximum_insertion_count);
189+
for i in 0..insertion_count {
190+
result.splice(
191+
(insertion_position + i * chunk_size)..(insertion_position + (i + 1) * chunk_size),
192+
input_buffer[chunk_position..(chunk_position + chunk_size)].iter().cloned(),
193+
);
194+
}
195+
result
196+
}
102197
}
103198

104199
pub fn splice_array_structure(
105200
first_input: &InputValue,
106201
second_input: &InputValue,
107202
prng: &mut XorShiftRng,
108203
) -> InputValue {
109-
let mut array_splicer = ArraySplicer::new(prng);
204+
let mut array_splicer = ArrayMutator::new(prng);
110205
array_splicer.splice(first_input, second_input)
111206
}
207+
208+
pub fn mutate_vector_structure(input: &Vec<InputValue>, prng: &mut XorShiftRng) -> Vec<InputValue> {
209+
let mut array_mutator = ArrayMutator::new(prng);
210+
array_mutator.perform_structure_mutation_on_vector(input)
211+
}

tooling/greybox_fuzzer/src/mutation/configurations.rs

+72-6
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,54 @@ impl TopLevelFieldElementMutationConfiguration {
505505
}
506506
}
507507

508+
pub enum TestCaseSpliceType {
509+
/// Around 50% for each top-level element
510+
BalancedTopLevel,
511+
/// 80/20 for each element at lower level
512+
UnbalancedFull,
513+
/// One element merged into the main testcase
514+
SingleElementImport,
515+
}
516+
517+
pub(crate) struct TestCaseSpliceConfiguration {
518+
balanced_top_level_weight: usize,
519+
unbalanced_full_weight: usize,
520+
#[allow(unused)]
521+
single_element_import_weight: usize,
522+
total_weight: usize,
523+
}
524+
525+
impl TestCaseSpliceConfiguration {
526+
#[allow(unused)]
527+
pub fn new(
528+
balanced_top_level_weight: usize,
529+
unbalanced_full_weight: usize,
530+
single_element_import_weight: usize,
531+
) -> Self {
532+
let total_weight =
533+
balanced_top_level_weight + unbalanced_full_weight + single_element_import_weight;
534+
Self {
535+
balanced_top_level_weight,
536+
unbalanced_full_weight,
537+
single_element_import_weight,
538+
total_weight,
539+
}
540+
}
541+
542+
/// Select a mutation according to weights
543+
pub fn select(&self, prng: &mut XorShiftRng) -> TestCaseSpliceType {
544+
let mut selector = prng.gen_range(0..self.total_weight);
545+
if selector < self.balanced_top_level_weight {
546+
return TestCaseSpliceType::BalancedTopLevel;
547+
}
548+
selector -= self.balanced_top_level_weight;
549+
if selector < self.unbalanced_full_weight {
550+
return TestCaseSpliceType::UnbalancedFull;
551+
}
552+
return TestCaseSpliceType::SingleElementImport;
553+
}
554+
}
555+
508556
/// Default configurations for all mutations that are currently used
509557
510558
pub(crate) const BASIC_SPLICE_MUTATION_CONFIGURATION: SpliceMutationConfiguration =
@@ -515,9 +563,9 @@ pub(crate) const BASIC_SPLICE_MUTATION_CONFIGURATION: SpliceMutationConfiguratio
515563
};
516564
pub(crate) const BASIC_UNBALANCED_ARRAY_SPLICE_MUTATION_CONFIGURATION:
517565
UnbalancedArraySpliceConfiguration = UnbalancedArraySpliceConfiguration {
518-
array_specific_weight: 1,
519-
recurse_weight: 1,
520-
total_weight: 1 + 1,
566+
array_specific_weight: 11,
567+
recurse_weight: 9,
568+
total_weight: 11 + 9,
521569
};
522570
pub(crate) const BASIC_BYTE_VALUE_MUTATION_CONFIGURATION: ByteValueMutationConfiguration =
523571
ByteValueMutationConfiguration {
@@ -535,9 +583,9 @@ pub(crate) const DICTIONARY_EMPTY_BYTE_VALUE_MUTATION_CONFIGURATION:
535583

536584
pub(crate) const BASIC_SPLICE_CANDIDATE_PRIORITIZATION_CONFIGURATION:
537585
SpliceCandidatePrioritizationConfiguration = SpliceCandidatePrioritizationConfiguration {
538-
first_weight: 2,
539-
second_weight: 1,
540-
total_weight: 2 + 1,
586+
first_weight: 11,
587+
second_weight: 10,
588+
total_weight: 11 + 10,
541589
};
542590

543591
pub(crate) const BASIC_STRUCTURE_MUTATION_CONFIGURATION: StructuralMutationConfiguration =
@@ -608,3 +656,21 @@ pub(crate) const BASIC_TOPLEVEL_FIELD_ELEMENT_MUTATION_CONFIGURATION:
608656
dictionary_update_weight: 10,
609657
total_weight: 10 + 1 + 5 + 10 + 10,
610658
};
659+
660+
pub(crate) const BASIC_TESTCASE_SPLICE_CONFIGURATION: TestCaseSpliceConfiguration =
661+
TestCaseSpliceConfiguration {
662+
balanced_top_level_weight: 1,
663+
unbalanced_full_weight: 1,
664+
single_element_import_weight: 2,
665+
total_weight: 1 + 1 + 2,
666+
};
667+
668+
/// Generic vector structural mutation configuration (random value duplication weight MUST stay zero)
669+
pub(crate) const BASIC_VECTOR_STRUCTURE_MUTATION_CONFIGURATION: StructuralMutationConfiguration =
670+
StructuralMutationConfiguration {
671+
chaotic_self_splice_weight: 3,
672+
chunk_duplication_weight: 2,
673+
random_value_duplication_weight: 0,
674+
swap_weight: 3,
675+
total_weight: 3 + 2 + 0 + 3,
676+
};

0 commit comments

Comments
 (0)