This commit is contained in:
Dominik Maier 2020-11-24 19:57:45 +01:00
commit a77b1cb18e
5 changed files with 369 additions and 238 deletions

View File

@ -402,7 +402,7 @@ mod tests {
use crate::executors::inmemory::InMemoryExecutor; use crate::executors::inmemory::InMemoryExecutor;
use crate::executors::{Executor, ExitKind}; use crate::executors::{Executor, ExitKind};
use crate::inputs::bytes::BytesInput; 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::stages::mutational::StdMutationalStage;
use crate::utils::StdRand; use crate::utils::StdRand;

View File

@ -8,7 +8,7 @@ pub mod shmem_translated;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use crate::events::llmp::LLMP; pub use crate::events::llmp::LLMP;
//use core::any::TypeId; use core::fmt::Formatter;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::io::Write; use std::io::Write;
@ -19,8 +19,43 @@ use crate::inputs::Input;
use crate::utils::Rand; use crate::utils::Rand;
use crate::AflError; use crate::AflError;
pub enum EventDestination {
Main,
Broker,
Clients,
}
pub trait Event { pub trait Event {
fn name(&self) -> &'static str; fn name() -> &'static str;
fn destination() -> EventDestination;
fn log<S, C, E, I, R>(&self, formatter: &mut Formatter, _state: &S) -> Result<(), AflError>
where
S: State<C, E, I, R>,
C: Corpus<I, R>,
E: Executor<I>,
I: Input,
R: Rand,
{
match write!(formatter, "[{}]", Self::name()) {
Ok(_) => Ok(()),
Err(_) => Err(AflError::Unknown("write error".to_string())),
}
}
fn on_recv<S, C, E, I, R>(&self, _state: &mut S) -> Result<(), AflError>
where
S: State<C, E, I, R>,
C: Corpus<I, R>,
E: Executor<I>,
I: Input,
R: Rand,
{
Ok(())
}
// TODO serialize and deserialize, defaults to serde
} }
pub trait EventManager<S, C, E, I, R> pub trait EventManager<S, C, E, I, R>
@ -32,6 +67,7 @@ where
R: Rand, R: Rand,
{ {
/// Check if this EventaManager support a given Event type /// Check if this EventaManager support a given Event type
/// To compare events, use Event::name().as_ptr()
fn enabled<T>(&self) -> bool fn enabled<T>(&self) -> bool
where where
T: Event; T: Event;
@ -71,9 +107,13 @@ macro_rules! fire_event {
pub struct LoadInitialEvent {} pub struct LoadInitialEvent {}
impl Event for LoadInitialEvent { impl Event for LoadInitialEvent {
fn name(&self) -> &'static str { fn name() -> &'static str {
"LOAD" "LOAD"
} }
fn destination() -> EventDestination {
EventDestination::Broker
}
} }
impl LoadInitialEvent { impl LoadInitialEvent {
pub fn new() -> Self { pub fn new() -> Self {
@ -92,9 +132,13 @@ impl<I> Event for NewTestcaseEvent<I>
where where
I: Input, I: Input,
{ {
fn name(&self) -> &'static str { fn name() -> &'static str {
"NEW" "NEW"
} }
fn destination() -> EventDestination {
EventDestination::Clients
}
} }
impl<I> NewTestcaseEvent<I> impl<I> NewTestcaseEvent<I>
@ -112,9 +156,13 @@ where
pub struct UpdateStatsEvent {} pub struct UpdateStatsEvent {}
impl Event for UpdateStatsEvent { impl Event for UpdateStatsEvent {
fn name(&self) -> &'static str { fn name() -> &'static str {
"STATS" "STATS"
} }
fn destination() -> EventDestination {
EventDestination::Broker
}
} }
impl UpdateStatsEvent { impl UpdateStatsEvent {
pub fn new() -> Self { pub fn new() -> Self {
@ -122,6 +170,22 @@ impl UpdateStatsEvent {
} }
} }
pub struct CrashEvent {}
impl Event for CrashEvent {
fn name() -> &'static str {
"CRASH"
}
fn destination() -> EventDestination {
EventDestination::Broker
}
}
impl CrashEvent {
pub fn new() -> Self {
CrashEvent {}
}
}
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub struct LoggerEventManager<W> pub struct LoggerEventManager<W>
where where
@ -146,20 +210,13 @@ where
T: Event, T: Event,
{ {
true true
/*let _load = TypeId::of::<LoadInitialEvent>();
let _new = TypeId::of::<NewTestcaseEvent>();
match TypeId::of::<T>() {
_load => true,
_new => true,
_ => false,
}*/
} }
fn fire<T>(&mut self, event: T) -> Result<(), AflError> fn fire<T>(&mut self, _event: T) -> Result<(), AflError>
where where
T: Event, T: Event,
{ {
self.events.push(event.name().to_string()); self.events.push(T::name().to_string());
Ok(()) Ok(())
} }

View File

@ -1,8 +1,7 @@
pub mod scheduled; pub mod scheduled;
pub use scheduled::ComposedByMutations; pub use scheduled::*;
pub use scheduled::HavocBytesMutator; pub mod mutations;
pub use scheduled::ScheduledMutator; pub use mutations::*;
pub use scheduled::StdScheduledMutator;
use crate::corpus::Corpus; use crate::corpus::Corpus;
use crate::inputs::Input; use crate::inputs::Input;
@ -34,3 +33,10 @@ where
Ok(()) 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,260 @@
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, &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: &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: &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: &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: &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: &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: &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: & 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: & 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_testcase, _) = corpus.random_entry(rand)?.clone();
// TODO: Load let other = Testcase::load_from_disk(other_test)?;
// println!("Input: {:?}, other input: {:?}", input.bytes(), other.bytes());
let other = match other_testcase.input() {
Some(i) => i,
None => return Ok(MutationResult::Skipped), //TODO
};
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::inputs::{HasBytesVec, Input};
use crate::mutators::Corpus; use crate::mutators::Corpus;
use crate::mutators::Mutator; use crate::mutators::*;
use crate::utils::Rand; use crate::utils::Rand;
use crate::AflError; 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, &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> pub trait ScheduledMutator<C, I, R>: Mutator<C, I, R> + ComposedByMutations<C, I, R>
where where
C: Corpus<I, R>, C: Corpus<I, R>,
@ -85,6 +59,7 @@ where
R: Rand, R: Rand,
{ {
mutations: Vec<MutationFunction<Self, C, I, R>>, mutations: Vec<MutationFunction<Self, C, I, R>>,
max_size: usize,
} }
impl<C, I, R> Mutator<C, I, R> for StdScheduledMutator<C, I, R> impl<C, I, R> Mutator<C, I, R> for StdScheduledMutator<C, I, R>
@ -132,6 +107,20 @@ where
// Just use the default methods // 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> impl<C, I, R> StdScheduledMutator<C, I, R>
where where
C: Corpus<I, R>, C: Corpus<I, R>,
@ -140,203 +129,21 @@ where
{ {
/// Create a new StdScheduledMutator instance without mutations and corpus /// Create a new StdScheduledMutator instance without mutations and corpus
pub fn new() -> Self { pub fn new() -> Self {
StdScheduledMutator { mutations: vec![] } StdScheduledMutator {
mutations: vec![],
max_size: DEFAULT_MAX_SIZE,
}
} }
/// Create a new StdScheduledMutator instance specifying mutations /// Create a new StdScheduledMutator instance specifying mutations
pub fn with_mutations(mutations: Vec<MutationFunction<Self, C, I, R>>) -> Self { pub fn with_mutations(mutations: Vec<MutationFunction<Self, C, I, R>>) -> Self {
StdScheduledMutator { StdScheduledMutator {
mutations: mutations, 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: &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: &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: &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: &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: &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: &C,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: Mutator<C, I, R>,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
{
// TODO: Don't reuse the current testcase!
// (We don't want to use the testcase we're already using for splicing)
let (other_testcase, _) = corpus.random_entry(rand)?;
// TODO: let other = other_testcase.load_input()?;
// println!("Input: {:?}, other input: {:?}", input.bytes(), other.bytes());
let other = match other_testcase.input() {
Some(i) => i,
None => return Ok(MutationResult::Skipped), // TODO!!
};
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 /// Schedule some selected byte level mutations given a ScheduledMutator type
pub struct HavocBytesMutator<SM, C, I, R> pub struct HavocBytesMutator<SM, C, I, R>
where where
@ -401,11 +208,12 @@ where
scheduled.add_mutation(mutation_bytedec); scheduled.add_mutation(mutation_bytedec);
scheduled.add_mutation(mutation_byteneg); scheduled.add_mutation(mutation_byteneg);
scheduled.add_mutation(mutation_bitflip); //scheduled.add_mutation(mutation_bytesexpand);
scheduled.add_mutation(mutation_bitflip); scheduled.add_mutation(mutation_bytesdelete);
scheduled.add_mutation(mutation_bitflip); scheduled.add_mutation(mutation_bytesdelete);
scheduled.add_mutation(mutation_bitflip); scheduled.add_mutation(mutation_bytesdelete);
scheduled.add_mutation(mutation_bitflip); scheduled.add_mutation(mutation_bytesdelete);
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_bitflip);