From 08fe6ab7913881a6f0ce3d438c794a8781b36577 Mon Sep 17 00:00:00 2001 From: "Dongjia \"toka\" Zhang" Date: Thu, 16 Mar 2023 23:58:03 +0900 Subject: [PATCH] Fix mutator slowdown (#1138) * perf stat * fix except swap * swap * fix * reveral-based byte swap * Revert "reveral-based byte swap" This reverts commit 2bc9609ece47fd4e8f6d96862f8ad3fb77f11aec. * no introspection * clp fmt * change rand_range to have at least 1 length * don't use modulo --------- Co-authored-by: Addison Crump Co-authored-by: Dominik Maier --- libafl/src/mutators/mutations.rs | 302 ++++++++++++++++++++++++------- 1 file changed, 232 insertions(+), 70 deletions(-) diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index 21c452e782..f086f0943c 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -63,10 +63,13 @@ pub fn buffer_set(data: &mut [T], from: usize, len: usize, val: T) { /// This problem corresponds to: #[inline] pub fn rand_range(state: &mut S, upper: usize, max_len: usize) -> Range { - let len = state.rand_mut().next() as usize % max_len + 1; - let offset2 = state.rand_mut().next() as usize % (upper + len); + let len = 1 + state.rand_mut().below(max_len as u64) as usize; + // sample from [1..upper + len] + let mut offset2 = 1 + state.rand_mut().below((upper + len - 1) as u64) as usize; let offset1 = offset2.saturating_sub(len); - let offset2 = offset2.clamp(0, upper); + if offset2 > upper { + offset2 = upper; + } offset1..offset2 } @@ -490,9 +493,6 @@ where } let range = rand_range(state, size, size); - if range.is_empty() { - return Ok(MutationResult::Skipped); - } input.bytes_mut().drain(range); @@ -535,17 +535,15 @@ where return Ok(MutationResult::Skipped); } - let mut range = rand_range(state, size, min(max_size - size, 16)); - if range.is_empty() { - return Ok(MutationResult::Skipped); - } - let new_size = range.len() + size; + let range = rand_range(state, size, min(16, max_size - size)); - let mut target = size; - core::mem::swap(&mut target, &mut range.end); - - input.bytes_mut().resize(new_size, 0); - input.bytes_mut().copy_within(range, target); + input.bytes_mut().resize(size + range.len(), 0); + buffer_self_copy( + input.bytes_mut(), + range.start, + range.start + range.len(), + size - range.start, + ); Ok(MutationResult::Mutated) } @@ -586,14 +584,22 @@ where return Ok(MutationResult::Skipped); } - let amount = 1 + state.rand_mut().below(min(max_size - size, 16) as u64) as usize; + let mut amount = 1 + state.rand_mut().below(16) as usize; let offset = state.rand_mut().below(size as u64 + 1) as usize; + if size + amount > max_size { + if max_size > size { + amount = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; - input - .bytes_mut() - .splice(offset..offset, core::iter::repeat(val).take(amount)); + input.bytes_mut().resize(size + amount, 0); + buffer_self_copy(input.bytes_mut(), offset, offset + amount, size - offset); + buffer_set(input.bytes_mut(), offset, amount, val); Ok(MutationResult::Mutated) } @@ -634,14 +640,22 @@ where return Ok(MutationResult::Skipped); } - let amount = 1 + state.rand_mut().below(min(max_size - size, 16) as u64) as usize; + let mut amount = 1 + state.rand_mut().below(16) as usize; let offset = state.rand_mut().below(size as u64 + 1) as usize; + if size + amount > max_size { + if max_size > size { + amount = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + let val = state.rand_mut().next() as u8; - input - .bytes_mut() - .splice(offset..offset, core::iter::repeat(val).take(amount)); + input.bytes_mut().resize(size + amount, 0); + buffer_self_copy(input.bytes_mut(), offset, offset + amount, size - offset); + buffer_set(input.bytes_mut(), offset, amount, val); Ok(MutationResult::Mutated) } @@ -681,15 +695,10 @@ where return Ok(MutationResult::Skipped); } let range = rand_range(state, size, min(size, 16)); - if range.is_empty() { - return Ok(MutationResult::Skipped); - } let val = *state.rand_mut().choose(input.bytes()); let quantity = range.len(); - input - .bytes_mut() - .splice(range, core::iter::repeat(val).take(quantity)); + buffer_set(input.bytes_mut(), range.start, quantity, val); Ok(MutationResult::Mutated) } @@ -729,15 +738,10 @@ where return Ok(MutationResult::Skipped); } let range = rand_range(state, size, min(size, 16)); - if range.is_empty() { - return Ok(MutationResult::Skipped); - } let val = state.rand_mut().next() as u8; let quantity = range.len(); - input - .bytes_mut() - .splice(range, core::iter::repeat(val).take(quantity)); + buffer_set(input.bytes_mut(), range.start, quantity, val); Ok(MutationResult::Mutated) } @@ -780,7 +784,7 @@ where let target = state.rand_mut().below(size as u64) as usize; let range = rand_range(state, size, size - target); - input.bytes_mut().copy_within(range, target); + buffer_self_copy(input.bytes_mut(), range.start, target, range.len()); Ok(MutationResult::Mutated) } @@ -818,22 +822,32 @@ where _stage_idx: i32, ) -> Result { let size = input.bytes().len(); - if size <= 1 || size == state.max_size() { + if size <= 1 || size >= state.max_size() { return Ok(MutationResult::Skipped); } let target = state.rand_mut().below(size as u64) as usize; // make sure that the sampled range is both in bounds and of an acceptable size let max_insert_len = min(size - target, state.max_size() - size); - let range = rand_range(state, size, max_insert_len); + let range = rand_range(state, size, min(16, max_insert_len)); - self.tmp_buf.clear(); - self.tmp_buf.extend(input.bytes()[range].iter().copied()); - - input - .bytes_mut() - .splice(target..target, self.tmp_buf.drain(..)); + input.bytes_mut().resize(size + range.len(), 0); + self.tmp_buf.resize(range.len(), 0); + buffer_copy( + &mut self.tmp_buf, + input.bytes(), + range.start, + 0, + range.len(), + ); + buffer_self_copy( + input.bytes_mut(), + target, + target + range.len(), + size - target, + ); + buffer_copy(input.bytes_mut(), &self.tmp_buf, 0, target, range.len()); Ok(MutationResult::Mutated) } } @@ -858,6 +872,7 @@ pub struct BytesSwapMutator { tmp_buf: Vec, } +#[allow(clippy::too_many_lines)] impl Mutator for BytesSwapMutator where S: HasRand, @@ -874,31 +889,166 @@ where return Ok(MutationResult::Skipped); } - self.tmp_buf.clear(); - let first = rand_range(state, size, size); if state.rand_mut().next() & 1 == 0 && first.start != 0 { + // The second range comes before first. + let second = rand_range(state, first.start, first.start); - self.tmp_buf.extend(input.bytes_mut().drain(first.clone())); - self.tmp_buf - .extend(input.bytes()[second.clone()].iter().copied()); - input - .bytes_mut() - .splice(first.start..first.start, self.tmp_buf.drain(first.len()..)); - input.bytes_mut().splice(second, self.tmp_buf.drain(..)); + self.tmp_buf.resize(first.len(), 0); + // If range first is larger + if first.len() >= second.len() { + let diff_in_size = first.len() - second.len(); + + // copy first range to tmp + buffer_copy( + &mut self.tmp_buf, + input.bytes(), + first.start, + 0, + first.len(), + ); + + // adjust second.end..first.start, move them by diff_in_size to the right + buffer_self_copy( + input.bytes_mut(), + second.end, + second.end + diff_in_size, + first.start - second.end, + ); + + // copy second to where first was + buffer_self_copy( + input.bytes_mut(), + second.start, + first.start + diff_in_size, + second.len(), + ); + + // copy first back + buffer_copy( + input.bytes_mut(), + &self.tmp_buf, + 0, + second.start, + first.len(), + ); + } else { + let diff_in_size = second.len() - first.len(); + + // copy first range to tmp + buffer_copy( + &mut self.tmp_buf, + input.bytes(), + first.start, + 0, + first.len(), + ); + + // adjust second.end..first.start, move them by diff_in_size to the left + buffer_self_copy( + input.bytes_mut(), + second.end, + second.end - diff_in_size, + first.start - second.end, + ); + + // copy second to where first was + buffer_self_copy( + input.bytes_mut(), + second.start, + first.start - diff_in_size, + second.len(), + ); + + // copy first back + buffer_copy( + input.bytes_mut(), + &self.tmp_buf, + 0, + second.start, + first.len(), + ); + } Ok(MutationResult::Mutated) } else if first.end != size { + // The first range comes before the second range let mut second = rand_range(state, size - first.end, size - first.end); second.start += first.end; second.end += first.end; - self.tmp_buf.extend(input.bytes_mut().drain(second.clone())); - self.tmp_buf - .extend(input.bytes()[first.clone()].iter().copied()); - input.bytes_mut().splice( - second.start..second.start, - self.tmp_buf.drain(second.len()..), - ); - input.bytes_mut().splice(first, self.tmp_buf.drain(..)); + + self.tmp_buf.resize(second.len(), 0); + if second.len() >= first.len() { + let diff_in_size = second.len() - first.len(); + // copy second range to tmp + buffer_copy( + &mut self.tmp_buf, + input.bytes(), + second.start, + 0, + second.len(), + ); + + // adjust first.end..second.start, move them by diff_in_size to the right + buffer_self_copy( + input.bytes_mut(), + first.end, + first.end + diff_in_size, + second.start - first.end, + ); + + // copy first to where second was + buffer_self_copy( + input.bytes_mut(), + first.start, + second.start + diff_in_size, + first.len(), + ); + + // copy second back + buffer_copy( + input.bytes_mut(), + &self.tmp_buf, + 0, + first.start, + second.len(), + ); + } else { + let diff_in_size = first.len() - second.len(); + // copy second range to tmp + buffer_copy( + &mut self.tmp_buf, + input.bytes(), + second.start, + 0, + second.len(), + ); + + // adjust first.end..second.start, move them by diff_in_size to the left + buffer_self_copy( + input.bytes_mut(), + first.end, + first.end - diff_in_size, + second.start - first.end, + ); + + // copy first to where second was + buffer_self_copy( + input.bytes_mut(), + first.start, + second.start - diff_in_size, + first.len(), + ); + + // copy second back + buffer_copy( + input.bytes_mut(), + &self.tmp_buf, + 0, + first.start, + second.len(), + ); + } + Ok(MutationResult::Mutated) } else { Ok(MutationResult::Skipped) @@ -967,10 +1117,20 @@ where let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); let other = other_testcase.load_input()?; - input - .bytes_mut() - .splice(target..target, other.bytes()[range].iter().copied()); - + input.bytes_mut().resize(size + range.len(), 0); + buffer_self_copy( + input.bytes_mut(), + target, + target + range.len(), + size - target, + ); + buffer_copy( + input.bytes_mut(), + other.bytes(), + range.start, + target, + range.len(), + ); Ok(MutationResult::Mutated) } } @@ -1034,11 +1194,13 @@ where let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); let other = other_testcase.load_input()?; - input.bytes_mut().splice( - target..(target + range.len()), - other.bytes()[range].iter().copied(), + buffer_copy( + input.bytes_mut(), + other.bytes(), + range.start, + target, + range.len(), ); - Ok(MutationResult::Mutated) } }