mutations module

This commit is contained in:
Andrea Fioraldi 2020-11-24 14:31:50 +01:00
parent 2b949156b9
commit c8bad518c8
4 changed files with 301 additions and 226 deletions

View File

@ -397,7 +397,7 @@ mod tests {
use crate::executors::inmemory::InMemoryExecutor;
use crate::executors::{Executor, ExitKind};
use crate::inputs::bytes::BytesInput;
use crate::mutators::scheduled::{mutation_bitflip, ComposedByMutations, StdScheduledMutator};
use crate::mutators::{mutation_bitflip, ComposedByMutations, StdScheduledMutator};
use crate::stages::mutational::StdMutationalStage;
use crate::utils::StdRand;

View File

@ -1,8 +1,7 @@
pub mod scheduled;
pub use scheduled::ComposedByMutations;
pub use scheduled::HavocBytesMutator;
pub use scheduled::ScheduledMutator;
pub use scheduled::StdScheduledMutator;
pub use scheduled::*;
pub mod mutations;
pub use mutations::*;
use alloc::rc::Rc;
use core::cell::RefCell;
@ -38,3 +37,10 @@ where
Ok(())
}
}
pub const DEFAULT_MAX_SIZE: usize = 1048576;
pub trait HasMaxSize {
fn max_size(&self) -> usize;
fn set_max_size(&mut self, max_size: usize);
}

View File

@ -0,0 +1,263 @@
use crate::inputs::{HasBytesVec, Input};
use crate::mutators::Corpus;
use crate::mutators::*;
use crate::utils::Rand;
use crate::AflError;
pub enum MutationResult {
Mutated,
Skipped,
}
// TODO maybe the mutator arg is not needed
/// The generic function type that identifies mutations
pub type MutationFunction<M, C, I, R> =
fn(&mut M, &mut R, &mut C, &mut I) -> Result<MutationResult, AflError>;
pub trait ComposedByMutations<C, I, R>
where
C: Corpus<I, R>,
I: Input,
R: Rand,
{
/// Get a mutation by index
fn mutation_by_idx(&self, index: usize) -> MutationFunction<Self, C, I, R>;
/// Get the number of mutations
fn mutations_count(&self) -> usize;
/// Add a mutation
fn add_mutation(&mut self, mutation: MutationFunction<Self, C, I, R>);
}
/// Bitflip mutation for inputs with a bytes vector
pub fn mutation_bitflip<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
if input.bytes().len() == 0 {
Ok(MutationResult::Skipped)
} else {
let bit = rand.below((input.bytes().len() << 3) as u64) as usize;
unsafe {
// moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(bit >> 3) ^= (128 >> (bit & 7)) as u8;
}
Ok(MutationResult::Mutated)
}
}
pub fn mutation_byteflip<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
if input.bytes().len() == 0 {
Ok(MutationResult::Skipped)
} else {
let idx = rand.below(input.bytes().len() as u64) as usize;
unsafe {
// moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) ^= 0xff;
}
Ok(MutationResult::Mutated)
}
}
pub fn mutation_byteinc<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
if input.bytes().len() == 0 {
Ok(MutationResult::Skipped)
} else {
let idx = rand.below(input.bytes().len() as u64) as usize;
unsafe {
// moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) += 1;
}
Ok(MutationResult::Mutated)
}
}
pub fn mutation_bytedec<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
if input.bytes().len() == 0 {
Ok(MutationResult::Skipped)
} else {
let idx = rand.below(input.bytes().len() as u64) as usize;
unsafe {
// moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) -= 1;
}
Ok(MutationResult::Mutated)
}
}
pub fn mutation_byteneg<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
if input.bytes().len() == 0 {
Ok(MutationResult::Skipped)
} else {
let idx = rand.below(input.bytes().len() as u64) as usize;
unsafe {
// moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) = !(*input.bytes().get_unchecked(idx));
}
Ok(MutationResult::Mutated)
}
}
/*
pub fn mutation_bytesexpand<M, C, I, R>(
mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R> + HasMaxSize,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
let len = rand.below(mutator.max_size() as u64) as usize;
Ok(MutationResult::Mutated)
}
*/
pub fn mutation_bytesdelete<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
let size = input.bytes().len();
if size <= 2 {
return Ok(MutationResult::Skipped);
}
let off = rand.below(size as u64) as usize;
let len = rand.below((size - off) as u64) as usize;
input.bytes_mut().drain(off..len);
Ok(MutationResult::Mutated)
}
/// Returns the first and last diff position between the given vectors, stopping at the min len
fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) {
let mut first_diff: i64 = -1;
let mut last_diff: i64 = -1;
for (i, (this_el, other_el)) in this.iter().zip(other.iter()).enumerate() {
if this_el != other_el {
if first_diff < 0 {
first_diff = i as i64;
}
last_diff = i as i64;
}
}
(first_diff, last_diff)
}
/// Splicing mutator
pub fn mutation_splice<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
// We don't want to use the testcase we're already using for splicing
let (other_rr, _) = corpus.random_entry(rand)?.clone();
let mut other_testcase = match other_rr.try_borrow_mut() {
Ok(x) => x,
Err(_) => {
return Ok(MutationResult::Skipped);
}
};
let other = other_testcase.load_input()?;
// println!("Input: {:?}, other input: {:?}", input.bytes(), other.bytes());
let mut counter = 0;
let (first_diff, last_diff) = loop {
let (f, l) = locate_diffs(input.bytes(), other.bytes());
// println!("Diffs were between {} and {}", f, l);
if f != l && f >= 0 && l >= 2 {
break (f, l);
}
if counter == 3 {
return Ok(MutationResult::Skipped);
}
counter += 1;
};
let split_at = rand.between(first_diff as u64, last_diff as u64) as usize;
// println!("Splicing at {}", split_at);
input
.bytes_mut()
.splice(split_at.., other.bytes()[split_at..].iter().cloned());
// println!("Splice result: {:?}, input is now: {:?}", split_result, input.bytes());
Ok(MutationResult::Mutated)
}

View File

@ -3,36 +3,10 @@ use core::marker::PhantomData;
use crate::inputs::{HasBytesVec, Input};
use crate::mutators::Corpus;
use crate::mutators::Mutator;
use crate::mutators::*;
use crate::utils::Rand;
use crate::AflError;
pub enum MutationResult {
Mutated,
Skipped,
}
// TODO maybe the mutator arg is not needed
/// The generic function type that identifies mutations
type MutationFunction<M, C, I, R> =
fn(&mut M, &mut R, &mut C, &mut I) -> Result<MutationResult, AflError>;
pub trait ComposedByMutations<C, I, R>
where
C: Corpus<I, R>,
I: Input,
R: Rand,
{
/// Get a mutation by index
fn mutation_by_idx(&self, index: usize) -> MutationFunction<Self, C, I, R>;
/// Get the number of mutations
fn mutations_count(&self) -> usize;
/// Add a mutation
fn add_mutation(&mut self, mutation: MutationFunction<Self, C, I, R>);
}
pub trait ScheduledMutator<C, I, R>: Mutator<C, I, R> + ComposedByMutations<C, I, R>
where
C: Corpus<I, R>,
@ -85,6 +59,7 @@ where
R: Rand,
{
mutations: Vec<MutationFunction<Self, C, I, R>>,
max_size: usize,
}
impl<C, I, R> Mutator<C, I, R> for StdScheduledMutator<C, I, R>
@ -132,6 +107,20 @@ where
// Just use the default methods
}
impl<C, I, R> HasMaxSize for StdScheduledMutator<C, I, R>
where
C: Corpus<I, R>,
I: Input,
R: Rand,
{
fn max_size(&self) -> usize {
self.max_size
}
fn set_max_size(&mut self, max_size: usize) {
self.max_size = max_size;
}
}
impl<C, I, R> StdScheduledMutator<C, I, R>
where
C: Corpus<I, R>,
@ -140,205 +129,21 @@ where
{
/// Create a new StdScheduledMutator instance without mutations and corpus
pub fn new() -> Self {
StdScheduledMutator { mutations: vec![] }
StdScheduledMutator {
mutations: vec![],
max_size: DEFAULT_MAX_SIZE,
}
}
/// Create a new StdScheduledMutator instance specifying mutations
pub fn with_mutations(mutations: Vec<MutationFunction<Self, C, I, R>>) -> Self {
StdScheduledMutator {
mutations: mutations,
max_size: DEFAULT_MAX_SIZE,
}
}
}
/// Bitflip mutation for inputs with a bytes vector
pub fn mutation_bitflip<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
if input.bytes().len() == 0 {
Ok(MutationResult::Skipped)
} else {
let bit = rand.below((input.bytes().len() << 3) as u64) as usize;
unsafe {
// moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(bit >> 3) ^= (128 >> (bit & 7)) as u8;
}
Ok(MutationResult::Mutated)
}
}
pub fn mutation_byteflip<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
if input.bytes().len() == 0 {
Ok(MutationResult::Skipped)
} else {
let idx = rand.below(input.bytes().len() as u64) as usize;
unsafe {
// moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) ^= 0xff;
}
Ok(MutationResult::Mutated)
}
}
pub fn mutation_byteinc<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
if input.bytes().len() == 0 {
Ok(MutationResult::Skipped)
} else {
let idx = rand.below(input.bytes().len() as u64) as usize;
unsafe {
// moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) += 1;
}
Ok(MutationResult::Mutated)
}
}
pub fn mutation_bytedec<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
if input.bytes().len() == 0 {
Ok(MutationResult::Skipped)
} else {
let idx = rand.below(input.bytes().len() as u64) as usize;
unsafe {
// moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) -= 1;
}
Ok(MutationResult::Mutated)
}
}
pub fn mutation_byteneg<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
_corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
if input.bytes().len() == 0 {
Ok(MutationResult::Skipped)
} else {
let idx = rand.below(input.bytes().len() as u64) as usize;
unsafe {
// moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) = !(*input.bytes().get_unchecked(idx));
}
Ok(MutationResult::Mutated)
}
}
/// Returns the first and last diff position between the given vectors, stopping at the min len
fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) {
let mut first_diff: i64 = -1;
let mut last_diff: i64 = -1;
for (i, (this_el, other_el)) in this.iter().zip(other.iter()).enumerate() {
if this_el != other_el {
if first_diff < 0 {
first_diff = i as i64;
}
last_diff = i as i64;
}
}
(first_diff, last_diff)
}
/// Splicing mutator
pub fn mutation_splice<M, C, I, R>(
_mutator: &mut M,
rand: &mut R,
corpus: &mut C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
// We don't want to use the testcase we're already using for splicing
let (other_rr, _) = corpus.random_entry(rand)?.clone();
let mut other_testcase = match other_rr.try_borrow_mut() {
Ok(x) => x,
Err(_) => {
return Ok(MutationResult::Skipped);
}
};
let other = other_testcase.load_input()?;
// println!("Input: {:?}, other input: {:?}", input.bytes(), other.bytes());
let mut counter = 0;
let (first_diff, last_diff) = loop {
let (f, l) = locate_diffs(input.bytes(), other.bytes());
// println!("Diffs were between {} and {}", f, l);
if f != l && f >= 0 && l >= 2 {
break (f, l);
}
if counter == 3 {
return Ok(MutationResult::Skipped);
}
counter += 1;
};
let split_at = rand.between(first_diff as u64, last_diff as u64) as usize;
// println!("Splicing at {}", split_at);
input
.bytes_mut()
.splice(split_at.., other.bytes()[split_at..].iter().cloned());
// println!("Splice result: {:?}, input is now: {:?}", split_result, input.bytes());
Ok(MutationResult::Mutated)
}
/// Schedule some selected byte level mutations given a ScheduledMutator type
pub struct HavocBytesMutator<SM, C, I, R>
where
@ -403,11 +208,12 @@ where
scheduled.add_mutation(mutation_bytedec);
scheduled.add_mutation(mutation_byteneg);
scheduled.add_mutation(mutation_bitflip);
scheduled.add_mutation(mutation_bitflip);
scheduled.add_mutation(mutation_bitflip);
scheduled.add_mutation(mutation_bitflip);
scheduled.add_mutation(mutation_bitflip);
//scheduled.add_mutation(mutation_bytesexpand);
scheduled.add_mutation(mutation_bytesdelete);
scheduled.add_mutation(mutation_bytesdelete);
scheduled.add_mutation(mutation_bytesdelete);
scheduled.add_mutation(mutation_bytesdelete);
scheduled.add_mutation(mutation_bitflip);
scheduled.add_mutation(mutation_bitflip);
scheduled.add_mutation(mutation_bitflip);