Skip to content

Commit

Permalink
Merge pull request #481 from plotchy/prevent_dup_overwrite
Browse files Browse the repository at this point in the history
Prevent duplicate input overwriting
  • Loading branch information
jacob-chia authored May 28, 2024
2 parents f0195c6 + 0e84648 commit 70e430d
Showing 1 changed file with 87 additions and 5 deletions.
92 changes: 87 additions & 5 deletions src/mutation_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@ impl ConstantPoolMetadata {
}

impl_serdeany!(ConstantPoolMetadata);

/// Metadata for Mutations
///
/// This is metadata attached to the global fuzz state
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct MutatorMetadata {
/// Used to prevent more than one full overwrite during mutation
pub full_overwrite_performed: bool,
}

impl MutatorMetadata {
pub fn new() -> Self {
Default::default()
}

pub fn set_full_overwrite_performed(&mut self, full_overwrite_performed: bool) {
self.full_overwrite_performed = full_overwrite_performed;
}
}

impl_serdeany!(MutatorMetadata);

/// [`GaussianNoiseMutator`] is a mutator that adds Gaussian noise to the input
/// value.
///
Expand Down Expand Up @@ -302,7 +324,15 @@ where
I: Input + HasBytesVec,
{
/// Mutate the input to a constant in the contract
/// This always entirely overwrites the input (unless it skips mutation)
fn mutate(&mut self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<MutationResult, Error> {
// if full_overwrite_performed is true, we skip mutation
if let Some(metadata) = state.metadata_map().get::<MutatorMetadata>() {
if metadata.full_overwrite_performed {
return Ok(MutationResult::Skipped);
}
}

let idx = state.rand_mut().next() as usize;

let constant = match state.metadata_map().get::<ConstantPoolMetadata>() {
Expand All @@ -321,6 +351,16 @@ where
} else {
input_bytes.copy_from_slice(&[vec![0; input_len - constant_len], constant.clone()].concat());
}

// prevent fully overwriting the input on this mutation cycle again
if let Some(metadata) = state.metadata_map_mut().get_mut::<MutatorMetadata>() {
metadata.set_full_overwrite_performed(true);
} else {
let mut metadata = MutatorMetadata::new();
metadata.set_full_overwrite_performed(true);
state.metadata_map_mut().insert(metadata);
}

Ok(MutationResult::Mutated)
}
}
Expand Down Expand Up @@ -362,11 +402,19 @@ pub fn mutate_with_vm_slot<S: State + HasRand>(vm_slots: &HashMap<EVMU256, EVMU2

impl<'a, I, S> Mutator<I, S> for VMStateHintedMutator<'a>
where
S: State + HasRand,
S: State + HasRand + HasMetadata,
I: Input + HasBytesVec,
{
/// Mutate the input to a value in the VM state
/// This always entirely overwrites the input (unless it skips mutation)
fn mutate(&mut self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<MutationResult, Error> {
// if full_overwrite_performed is true, we skip mutation
if let Some(metadata) = state.metadata_map().get::<MutatorMetadata>() {
if metadata.full_overwrite_performed {
return Ok(MutationResult::Skipped);
}
}

let input_len = input.bytes().len();
if input_len < 8 {
return Ok(MutationResult::Skipped);
Expand All @@ -376,6 +424,15 @@ where
let data: [u8; 32] = new_val.to_be_bytes();

input.bytes_mut().copy_from_slice(&data[(32 - input_len)..]);

// prevent fully overwriting the input on this mutation cycle again
if let Some(metadata) = state.metadata_map_mut().get_mut::<MutatorMetadata>() {
metadata.set_full_overwrite_performed(true);
} else {
let mut metadata = MutatorMetadata::new();
metadata.set_full_overwrite_performed(true);
state.metadata_map_mut().insert(metadata);
}
Ok(MutationResult::Mutated)
}
}
Expand All @@ -398,16 +455,29 @@ where
IncDecValue::new(),
);

if !state.has_metadata::<MutatorMetadata>() {
state.metadata_map_mut().insert(MutatorMetadata::default());
}

let mut res = MutationResult::Skipped;
if let Some(vm_slots) = vm_slots {
let mut mutator = StdScheduledMutator::with_max_stack_pow(
(VMStateHintedMutator::new(&vm_slots), mutations),
MAX_STACK_POW as u64,
);
mutator.mutate(state, input, 0).unwrap()
res = mutator.mutate(state, input, 0).unwrap()
} else {
let mut mutator = StdScheduledMutator::with_max_stack_pow(mutations, MAX_STACK_POW as u64);
mutator.mutate(state, input, 0).unwrap()
res = mutator.mutate(state, input, 0).unwrap()
}

state
.metadata_map_mut()
.get_mut::<MutatorMetadata>()
.unwrap()
.set_full_overwrite_performed(false);

res
}

/// Mutator that mutates the `VARIABLE SIZE` input bytes (e.g., string) in
Expand Down Expand Up @@ -435,14 +505,26 @@ where
IncDecValue::new(),
);

if !state.has_metadata::<MutatorMetadata>() {
state.metadata_map_mut().insert(MutatorMetadata::default());
}

let mut res = MutationResult::Skipped;
if let Some(vm_slots) = vm_slots {
let mut mutator = StdScheduledMutator::with_max_stack_pow(
(VMStateHintedMutator::new(&vm_slots), mutations),
MAX_STACK_POW as u64,
);
mutator.mutate(state, input, 0).unwrap()
res = mutator.mutate(state, input, 0).unwrap();
} else {
let mut mutator = StdScheduledMutator::with_max_stack_pow(mutations, MAX_STACK_POW as u64);
mutator.mutate(state, input, 0).unwrap()
res = mutator.mutate(state, input, 0).unwrap();
}

state
.metadata_map_mut()
.get_mut::<MutatorMetadata>()
.unwrap()
.set_full_overwrite_performed(false);
res
}

0 comments on commit 70e430d

Please sign in to comment.