mutations module
This commit is contained in:
parent
2b949156b9
commit
c8bad518c8
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
263
afl/src/mutators/mutations.rs
Normal file
263
afl/src/mutators/mutations.rs
Normal 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)
|
||||
}
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user