Merge pull request #9 from AFLplusplus/sane_generics

Sane generics
This commit is contained in:
Andrea Fioraldi 2021-02-22 15:19:56 +01:00 committed by GitHub
commit 6ed19f9dc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1785 additions and 1592 deletions

View File

@ -5,10 +5,11 @@ use std::{env, path::PathBuf};
use libafl::{ use libafl::{
bolts::{shmem::UnixShMem, tuples::tuple_list}, bolts::{shmem::UnixShMem, tuples::tuple_list},
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
events::setup_restarting_mgr, events::setup_restarting_mgr,
executors::{inprocess::InProcessExecutor, Executor, ExitKind}, executors::{inprocess::InProcessExecutor, Executor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback}, feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::Input, inputs::Input,
mutators::scheduled::HavocBytesMutator, mutators::scheduled::HavocBytesMutator,
mutators::token_mutations::TokensMetadata, mutators::token_mutations::TokensMetadata,
@ -17,7 +18,7 @@ use libafl::{
state::{HasCorpus, HasMetadata, State}, state::{HasCorpus, HasMetadata, State},
stats::SimpleStats, stats::SimpleStats,
utils::{current_nanos, StdRand}, utils::{current_nanos, StdRand},
Error, Fuzzer, StdFuzzer, Error,
}; };
/// The name of the coverage map observer, to find it again in the observer list /// The name of the coverage map observer, to find it again in the observer list
@ -69,13 +70,12 @@ pub fn main() {
/// The actual fuzzer /// The actual fuzzer
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
let mut rand = StdRand::new(current_nanos());
// 'While the stats are state, they are usually used in the broker - which is likely never restarted // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s)); let stats = SimpleStats::new(|s| println!("{}", s));
// The restarting state will spawn the same process again as child, then restarted it each time it crashes. // The restarting state will spawn the same process again as child, then restarted it each time it crashes.
let (state, mut restarting_mgr) = let (state, mut restarting_mgr) =
setup_restarting_mgr::<_, _, _, _, _, _, UnixShMem, _>(stats, broker_port) setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port)
.expect("Failed to setup the restarter".into()); .expect("Failed to setup the restarter".into());
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
@ -86,6 +86,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// If not restarting, create a State from scratch // If not restarting, create a State from scratch
let mut state = state.unwrap_or(State::new( let mut state = state.unwrap_or(State::new(
StdRand::new(current_nanos()),
InMemoryCorpus::new(), InMemoryCorpus::new(),
tuple_list!(MaxMapFeedback::new_with_observer( tuple_list!(MaxMapFeedback::new_with_observer(
&NAME_COV_MAP, &NAME_COV_MAP,
@ -111,7 +112,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Setup a basic mutator with a mutational stage // Setup a basic mutator with a mutational stage
let mutator = HavocBytesMutator::default(); let mutator = HavocBytesMutator::default();
let stage = StdMutationalStage::new(mutator); let stage = StdMutationalStage::new(mutator);
let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); let fuzzer = StdFuzzer::new(RandCorpusScheduler::new(), tuple_list!(stage));
// Create the executor // Create the executor
let mut executor = InProcessExecutor::new( let mut executor = InProcessExecutor::new(
@ -141,5 +142,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
println!("We imported {} inputs from disk.", state.corpus().count()); println!("We imported {} inputs from disk.", state.corpus().count());
} }
fuzzer.fuzz_loop(&mut rand, &mut executor, &mut state, &mut restarting_mgr) fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?;
Ok(())
} }

View File

@ -6,8 +6,8 @@ use std::hash::Hasher;
use xxhash_rust::const_xxh3; use xxhash_rust::const_xxh3;
use xxhash_rust::xxh3; use xxhash_rust::xxh3;
use libafl::utils::{Rand, StdRand};
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use libafl::utils::{Rand, StdRand};
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {
let mut rand = StdRand::new(0); let mut rand = StdRand::new(0);

View File

@ -1,9 +1,9 @@
//! Compare the speed of rand implementations //! Compare the speed of rand implementations
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use libafl::utils::{ use libafl::utils::{
Lehmer64Rand, Rand, RomuDuoJrRand, RomuTrioRand, XorShift64Rand, Xoshiro256StarRand, Lehmer64Rand, Rand, RomuDuoJrRand, RomuTrioRand, XorShift64Rand, Xoshiro256StarRand,
}; };
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {
let mut xorshift = XorShift64Rand::new(1); let mut xorshift = XorShift64Rand::new(1);

View File

@ -1,11 +1,9 @@
fn main() { fn main() {
#[cfg(target_os = "windows")]
#[cfg(target_os = "windows")] windows::build!(
windows::build!( windows::win32::system_services::HANDLE,
windows::win32::system_services::HANDLE, windows::win32::windows_programming::CloseHandle,
windows::win32::windows_programming::CloseHandle, // API needed for the shared memory
// API needed for the shared memory windows::win32::system_services::{CreateFileMappingA, OpenFileMappingA, MapViewOfFile, UnmapViewOfFile},
windows::win32::system_services::{CreateFileMappingA, OpenFileMappingA, MapViewOfFile, UnmapViewOfFile}, );
);
} }

View File

@ -1,8 +1,8 @@
//! Bolts are no conceptual fuzzing elements, but they keep libafl-based fuzzers together. //! Bolts are no conceptual fuzzing elements, but they keep libafl-based fuzzers together.
pub mod bindings;
pub mod llmp; pub mod llmp;
pub mod ownedref; pub mod ownedref;
pub mod serdeany; pub mod serdeany;
pub mod shmem; pub mod shmem;
pub mod tuples; pub mod tuples;
pub mod bindings;

View File

@ -512,7 +512,7 @@ macro_rules! impl_serdeany {
self self
} }
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[ctor] #[ctor]

View File

@ -330,8 +330,8 @@ pub mod shmem {
use core::{mem::size_of, slice}; use core::{mem::size_of, slice};
use std::ffi::CStr; use std::ffi::CStr;
use crate::Error;
use super::ShMem; use super::ShMem;
use crate::Error;
/// The default Sharedmap impl for windows using shmctl & shmget /// The default Sharedmap impl for windows using shmctl & shmget
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -343,7 +343,6 @@ pub mod shmem {
} }
// TODO complete // TODO complete
} }
#[cfg(test)] #[cfg(test)]

View File

@ -190,9 +190,9 @@ macro_rules! tuple_for_each {
} }
impl ForEach for () { impl ForEach for () {
fn for_each(&self) { } fn for_each(&self) {}
} }
impl<Head, Tail> ForEach for (Head, Tail) impl<Head, Tail> ForEach for (Head, Tail)
where where
Head: $trait_name, Head: $trait_name,
@ -206,7 +206,7 @@ macro_rules! tuple_for_each {
} }
{ {
use $fn_name::*; use $fn_name::*;
$tuple_name.for_each(); $tuple_name.for_each();
}; };
}; };
@ -221,9 +221,9 @@ macro_rules! tuple_for_each_mut {
} }
impl ForEachMut for () { impl ForEachMut for () {
fn for_each_mut(&mut self) { } fn for_each_mut(&mut self) {}
} }
impl<Head, Tail> ForEachMut for (Head, Tail) impl<Head, Tail> ForEachMut for (Head, Tail)
where where
Head: $trait_name, Head: $trait_name,
@ -237,7 +237,7 @@ macro_rules! tuple_for_each_mut {
} }
{ {
use $fn_name::*; use $fn_name::*;
$tuple_name.for_each_mut(); $tuple_name.for_each_mut();
}; };
}; };
@ -245,13 +245,13 @@ macro_rules! tuple_for_each_mut {
/* /*
pub fn test_macros() { pub fn test_macros() {
let mut t = tuple_list!(1, "a"); let mut t = tuple_list!(1, "a");
tuple_for_each!(f1, std::fmt::Display, t, |x| { tuple_for_each!(f1, std::fmt::Display, t, |x| {
println!("{}", x); println!("{}", x);
}); });
tuple_for_each_mut!(f2, std::fmt::Display, t, |x| { tuple_for_each_mut!(f2, std::fmt::Display, t, |x| {
println!("{}", x); println!("{}", x);
}); });

View File

@ -1,74 +1,86 @@
//! In-memory corpus, keeps all test cases in memory at all times //! In-memory corpus, keeps all test cases in memory at all times
use alloc::{borrow::ToOwned, vec::Vec}; use alloc::vec::Vec;
use core::{cell::RefCell, marker::PhantomData}; use core::cell::RefCell;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{corpus::Corpus, corpus::Testcase, inputs::Input, Error};
corpus::Corpus, corpus::HasTestcaseVec, corpus::Testcase, inputs::Input, utils::Rand, Error,
};
/// A corpus handling all important fuzzing in memory. /// A corpus handling all in memory.
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Default, Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")] #[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct InMemoryCorpus<I, R> pub struct InMemoryCorpus<I>
where where
I: Input, I: Input,
R: Rand,
{ {
entries: Vec<RefCell<Testcase<I>>>, entries: Vec<RefCell<Testcase<I>>>,
pos: usize, current: Option<usize>,
phantom: PhantomData<R>,
} }
impl<I, R> HasTestcaseVec<I> for InMemoryCorpus<I, R> impl<I> Corpus<I> for InMemoryCorpus<I>
where where
I: Input, I: Input,
R: Rand,
{ {
fn entries(&self) -> &[RefCell<Testcase<I>>] { /// Returns the number of elements
&self.entries
}
fn entries_mut(&mut self) -> &mut Vec<RefCell<Testcase<I>>> {
&mut self.entries
}
}
impl<I, R> Corpus<I, R> for InMemoryCorpus<I, R>
where
I: Input,
R: Rand,
{
/// Gets the next entry
#[inline] #[inline]
fn next(&mut self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error> { fn count(&self) -> usize {
if self.count() == 0 { self.entries.len()
Err(Error::Empty("No entries in corpus".to_owned())) }
/// Add an entry to the corpus and return its index
#[inline]
fn add(&mut self, testcase: Testcase<I>) -> Result<usize, Error> {
self.entries.push(RefCell::new(testcase));
Ok(self.entries.len() - 1)
}
/// Replaces the testcase at the given idx
#[inline]
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<(), Error> {
if idx >= self.entries.len() {
return Err(Error::KeyNotFound(format!("Index {} out of bounds", idx)));
}
self.entries[idx] = RefCell::new(testcase);
Ok(())
}
/// Removes an entry from the corpus, returning it if it was present.
#[inline]
fn remove(&mut self, idx: usize) -> Result<Option<Testcase<I>>, Error> {
if idx >= self.entries.len() {
Ok(None)
} else { } else {
let len = { self.entries().len() }; Ok(Some(self.entries.remove(idx).into_inner()))
let id = rand.below(len as u64) as usize;
self.pos = id;
Ok((self.get(id), id))
} }
} }
/// Returns the testacase we currently use /// Get by id
#[inline] #[inline]
fn current_testcase(&self) -> (&RefCell<Testcase<I>>, usize) { fn get(&self, idx: usize) -> Result<&RefCell<Testcase<I>>, Error> {
(self.get(self.pos), self.pos) Ok(&self.entries[idx])
}
/// Current testcase scheduled
#[inline]
fn current(&self) -> &Option<usize> {
&self.current
}
/// Current testcase scheduled (mut)
#[inline]
fn current_mut(&mut self) -> &mut Option<usize> {
&mut self.current
} }
} }
impl<I, R> InMemoryCorpus<I, R> impl<I> InMemoryCorpus<I>
where where
I: Input, I: Input,
R: Rand,
{ {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
entries: vec![], entries: vec![],
pos: 0, current: None,
phantom: PhantomData,
} }
} }
} }

225
libafl/src/corpus/minset.rs Normal file
View File

@ -0,0 +1,225 @@
use alloc::{borrow::ToOwned, vec::Vec};
use core::{cell::RefCell, iter::Iterator, marker::PhantomData};
use serde::{Deserialize, Serialize};
use crate::{
corpus::Corpus, corpus::HasTestcaseVec, corpus::Testcase, inputs::{HasLen, Input}, utils::Rand, Error,
};
pub trait FavFactor: Serialize + serde::de::DeserializeOwned + 'static
{
fn compute<I>(testcase: &Testcase<I>) -> Result<u64, Error>
where
I: Input;
}
pub struct LenTimeMulFavFactor {}
// TODO time as Duration and put len into Testcase
impl FavFactor for LenTimeMulFavFactor {
fn compute<I>(entry: &Testcase<I>) -> Result<u64, Error>
where
I: Input + HasLen
{
entry.exec_time() * entry.load_input().len()
}
}
pub trait CorpusMinimizer {
fn update_score<C, I, R>(corpus: &mut C, idx: usize) -> Result<(), Error>
where
C: Corpus<I, R>,
I: Input,
R: Rand;
fn cull<C, I, R>(corpus: &mut C) -> Result<(), Error>
where
C: Corpus<I, R>,
I: Input,
R: Rand;
}
pub struct FavCorpusMinimizer<F>
where
F: FavFactor
{
phantom: PhantomData<F>
}
impl<F> CorpusMinimizer for FavCorpusMinimizer<F>
where
F: FavFactor
{
fn update_score<C, I, R>(corpus: &mut C, idx: usize) -> Result<(), Error>
where
C: Corpus<I, R>,
I: Input,
R: Rand
{
}
fn cull<C, I, R>(corpus: &mut C) -> Result<(), Error>
where
C: Corpus<I, R>,
I: Input,
R: Rand
{
}
}
#[derive(Serialize, Deserialize)]
pub struct NoveltiesMeta {
novelties: Vec<usize>,
}
// impl Iterator<Item = usize>
/// A Queue-like corpus, wrapping an existing Corpus instance
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct MinSetCorpus<C, F, I, IT, R, T>
where
C: Corpus<I, R>,
F: FavFactor,
I: Input,
IT: Iterator<Item = T>,
R: Rand,
{
corpus: C,
pos: usize,
// TODO rebase minset on remove()
minset: HashSet<usize>,
top_rated: HashMap<T, usize>,
phantom: PhantomData<(F, I, IT, R)>,
}
impl<C, I, R> HasTestcaseVec<I> for MinSetCorpus<C, I, R>
where
C: Corpus<I, R>,
I: Input,
R: Rand,
{
#[inline]
fn entries(&self) -> &[RefCell<Testcase<I>>] {
self.corpus.entries()
}
#[inline]
fn entries_mut(&mut self) -> &mut Vec<RefCell<Testcase<I>>> {
self.corpus.entries_mut()
}
}
impl<C, I, R> Corpus<I, R> for MinSetCorpus<C, I, R>
where
C: Corpus<I, R>,
I: Input,
R: Rand,
{
/// Returns the number of elements
#[inline]
fn count(&self) -> usize {
self.corpus.count()
}
// TODO change add to return Result
#[inline]
fn add(&mut self, entry: Testcase<I>) -> usize {
let idx = self.corpus.add(entry);
self.update_score(idx).unwrap();
idx
}
/// Removes an entry from the corpus, returning it if it was present.
#[inline]
fn remove(&mut self, entry: &Testcase<I>) -> Option<Testcase<I>> {
self.corpus.remove(entry)
}
/// Gets a random entry
#[inline]
fn random_entry(&self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error> {
self.corpus.random_entry(rand)
}
/// Returns the testacase we currently use
#[inline]
fn current_testcase(&self) -> (&RefCell<Testcase<I>>, usize) {
(self.get(self.pos - 1), self.pos - 1)
}
/// Gets the next entry
#[inline]
fn next(&mut self, _rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error> {
self.cull();
self.pos += 1;
if self.corpus.count() == 0 {
return Err(Error::Empty("Corpus".to_owned()));
}
if self.pos > self.corpus.count() {
// TODO: Always loop or return informational error?
self.pos = 1;
self.cycles += 1;
}
Ok((&self.corpus.entries()[self.pos - 1], self.pos - 1))
}
}
impl<C, I, R> MinSetCorpus<C, I, R>
where
C: Corpus<I, R>,
I: Input,
R: Rand,
{
pub fn new(corpus: C) -> Self {
Self {
corpus: corpus,
phantom: PhantomData,
}
}
#[inline]
pub fn corpus(&self) -> &C {
&self.corpus
}
#[inline]
pub fn corpus_mut(&mut self) -> &mut C {
&mut self.corpus
}
// TODO move this functions and top rated to another struct
// create something like MinSetCorpus<.., FavMinimizer<LenTimeMulFavFactor>>
pub fn update_score(&mut self, idx: usize) -> Result<(), Error> {
let factor = F::compute(self.entries()[idx].borrow())?;
for elem in entry.get::<IT>() {
if let val = self.top_rated.get_mut(elem) {
if factor > F::compute(self.entries()[val].borrow())? {
continue
}
}
let _ = self.top_rated.insert(elem, idx);
}
}
pub fn cull(&mut self) {
let mut acc = HashSet::new();
self.minset.clear();
for key in self.top_rated.keys() {
if !acc.contains(key) {
let idx = self.top_rated.get(key).unwrap();
let entry = self.entries()[idx].borrow();
for elem in entry.get::<IT>() {
acc.insert(elem);
}
self.minset.insert(idx);
}
}
}
}

View File

@ -0,0 +1,103 @@
//! Corpuses contain the testcases, either in mem, on disk, or somewhere else.
//! They will hand out the next fuzz target, potentially doing basic scheduling.
pub mod testcase;
pub use testcase::Testcase;
pub mod inmemory;
pub use inmemory::InMemoryCorpus;
#[cfg(feature = "std")]
pub mod ondisk;
#[cfg(feature = "std")]
pub use ondisk::OnDiskCorpus;
pub mod queue;
pub use queue::QueueCorpus;
use alloc::{borrow::ToOwned, vec::Vec};
use core::{cell::RefCell, ptr};
use crate::{inputs::Input, utils::Rand, Error};
/// A way to obtain the containing testcase entries
pub trait HasTestcaseVec<I>
where
I: Input,
{
/// Get the entries vector field
fn entries(&self) -> &[RefCell<Testcase<I>>];
/// Get the entries vector field (mutable)
fn entries_mut(&mut self) -> &mut Vec<RefCell<Testcase<I>>>;
}
/// Corpus with all current testcases
pub trait Corpus<I, R>: HasTestcaseVec<I> + serde::Serialize + serde::de::DeserializeOwned
where
I: Input,
R: Rand,
{
/// Returns the number of elements
#[inline]
fn count(&self) -> usize {
self.entries().len()
}
// TODO implement a was_fuzzed counter
/// Add an entry to the corpus and return its index
#[inline]
fn add(&mut self, testcase: Testcase<I>) -> usize {
self.entries_mut().push(RefCell::new(testcase));
self.entries().len() - 1
}
/// Replaces the testcase at the given idx
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<(), Error> {
if self.entries_mut().len() < idx {
return Err(Error::KeyNotFound(format!("Index {} out of bounds", idx)));
}
self.entries_mut()[idx] = RefCell::new(testcase);
Ok(())
}
/// Get by id
#[inline]
fn get(&self, idx: usize) -> &RefCell<Testcase<I>> {
&self.entries()[idx]
}
/// Removes an entry from the corpus, returning it if it was present.
#[inline]
fn remove(&mut self, entry: &Testcase<I>) -> Option<Testcase<I>> {
match self
.entries()
.iter()
.position(|x| ptr::eq(x.as_ptr(), entry))
{
Some(i) => Some(self.entries_mut().remove(i).into_inner()),
None => None,
}
}
/// Gets a random entry
#[inline]
fn random_entry(&self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error> {
if self.count() == 0 {
Err(Error::Empty("No entries in corpus".to_owned()))
} else {
let len = { self.entries().len() };
let id = rand.below(len as u64) as usize;
Ok((self.get(id), id))
}
}
// TODO: IntoIter
/// Gets the next entry
fn next(&mut self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error>;
/// Returns the testacase we currently use
fn current_testcase(&self) -> (&RefCell<Testcase<I>>, usize);
}

View File

@ -1,5 +1,4 @@
//! Corpuses contain the testcases, either in mem, on disk, or somewhere else. //! Corpuses contain the testcases, either in mem, on disk, or somewhere else.
//! They will hand out the next fuzz target, potentially doing basic scheduling.
pub mod testcase; pub mod testcase;
pub use testcase::Testcase; pub use testcase::Testcase;
@ -13,90 +12,121 @@ pub mod ondisk;
pub use ondisk::OnDiskCorpus; pub use ondisk::OnDiskCorpus;
pub mod queue; pub mod queue;
pub use queue::QueueCorpus; pub use queue::QueueCorpusScheduler;
use alloc::{borrow::ToOwned, vec::Vec}; use core::cell::RefCell;
use core::{cell::RefCell, ptr}; use core::marker::PhantomData;
use crate::{inputs::Input, utils::Rand, Error}; use crate::{
inputs::Input,
/// A way to obtain the containing testcase entries state::{HasCorpus, HasRand},
pub trait HasTestcaseVec<I> utils::Rand,
where Error,
I: Input, };
{
/// Get the entries vector field
fn entries(&self) -> &[RefCell<Testcase<I>>];
/// Get the entries vector field (mutable)
fn entries_mut(&mut self) -> &mut Vec<RefCell<Testcase<I>>>;
}
/// Corpus with all current testcases /// Corpus with all current testcases
pub trait Corpus<I, R>: HasTestcaseVec<I> + serde::Serialize + serde::de::DeserializeOwned pub trait Corpus<I>: serde::Serialize + serde::de::DeserializeOwned
where where
I: Input, I: Input,
R: Rand,
{ {
/// Returns the number of elements /// Returns the number of elements
#[inline] fn count(&self) -> usize;
fn count(&self) -> usize {
self.entries().len()
}
// TODO implement a was_fuzzed counter
/// Add an entry to the corpus and return its index /// Add an entry to the corpus and return its index
#[inline] fn add(&mut self, testcase: Testcase<I>) -> Result<usize, Error>;
fn add(&mut self, testcase: Testcase<I>) -> usize {
self.entries_mut().push(RefCell::new(testcase));
self.entries().len() - 1
}
/// Replaces the testcase at the given idx /// Replaces the testcase at the given idx
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<(), Error> { fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<(), Error>;
if self.entries_mut().len() < idx {
return Err(Error::KeyNotFound(format!("Index {} out of bounds", idx))); /// Removes an entry from the corpus, returning it if it was present.
} fn remove(&mut self, idx: usize) -> Result<Option<Testcase<I>>, Error>;
self.entries_mut()[idx] = RefCell::new(testcase);
/// Get by id
fn get(&self, idx: usize) -> Result<&RefCell<Testcase<I>>, Error>;
/// Current testcase scheduled
fn current(&self) -> &Option<usize>;
/// Current testcase scheduled (mut)
fn current_mut(&mut self) -> &mut Option<usize>;
}
pub trait CorpusScheduler<I, S>
where
I: Input,
{
/// Add an entry to the corpus and return its index
fn on_add(&self, _state: &mut S, _idx: usize, _testcase: &Testcase<I>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Get by id /// Replaces the testcase at the given idx
#[inline] fn on_replace(
fn get(&self, idx: usize) -> &RefCell<Testcase<I>> { &self,
&self.entries()[idx] _state: &mut S,
_idx: usize,
_testcase: &Testcase<I>,
) -> Result<(), Error> {
Ok(())
} }
/// Removes an entry from the corpus, returning it if it was present. /// Removes an entry from the corpus, returning it if it was present.
#[inline] fn on_remove(
fn remove(&mut self, entry: &Testcase<I>) -> Option<Testcase<I>> { &self,
match self _state: &mut S,
.entries() _idx: usize,
.iter() _testcase: &Option<Testcase<I>>,
.position(|x| ptr::eq(x.as_ptr(), entry)) ) -> Result<(), Error> {
{ Ok(())
Some(i) => Some(self.entries_mut().remove(i).into_inner()),
None => None,
}
}
/// Gets a random entry
#[inline]
fn random_entry(&self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error> {
if self.count() == 0 {
Err(Error::Empty("No entries in corpus".to_owned()))
} else {
let len = { self.entries().len() };
let id = rand.below(len as u64) as usize;
Ok((self.get(id), id))
}
} }
// TODO: IntoIter // TODO: IntoIter
/// Gets the next entry /// Gets the next entry
fn next(&mut self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error>; fn next(&self, state: &mut S) -> Result<usize, Error>;
/// Returns the testacase we currently use
fn current_testcase(&self) -> (&RefCell<Testcase<I>>, usize);
} }
pub struct RandCorpusScheduler<C, I, R, S>
where
S: HasCorpus<C, I> + HasRand<R>,
C: Corpus<I>,
I: Input,
R: Rand,
{
phantom: PhantomData<(C, I, R, S)>,
}
impl<C, I, R, S> CorpusScheduler<I, S> for RandCorpusScheduler<C, I, R, S>
where
S: HasCorpus<C, I> + HasRand<R>,
C: Corpus<I>,
I: Input,
R: Rand,
{
/// Gets the next entry at random
fn next(&self, state: &mut S) -> Result<usize, Error> {
if state.corpus().count() == 0 {
Err(Error::Empty("No entries in corpus".to_owned()))
} else {
let len = state.corpus().count();
let id = state.rand_mut().below(len as u64) as usize;
*state.corpus_mut().current_mut() = Some(id);
Ok(id)
}
}
}
impl<C, I, R, S> RandCorpusScheduler<C, I, R, S>
where
S: HasCorpus<C, I> + HasRand<R>,
C: Corpus<I>,
I: Input,
R: Rand,
{
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
}
}
pub type StdCorpusScheduler<C, I, R, S> = RandCorpusScheduler<C, I, R, S>;

View File

@ -1,104 +1,104 @@
//! The ondisk corpus stores unused testcases to disk. //! The ondisk corpus stores unused testcases to disk.
use alloc::vec::Vec; use alloc::vec::Vec;
use core::{cell::RefCell, marker::PhantomData}; use core::cell::RefCell;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::path::PathBuf; use std::path::PathBuf;
use crate::{ use crate::{corpus::Corpus, corpus::Testcase, inputs::Input, Error};
corpus::Corpus, corpus::HasTestcaseVec, corpus::Testcase, inputs::Input, utils::Rand, Error,
};
/// A corpus able to store testcases to disk, and load them from disk, when they are being used. /// A corpus able to store testcases to disk, and load them from disk, when they are being used.
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Default, Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")] #[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct OnDiskCorpus<I, R> pub struct OnDiskCorpus<I>
where where
I: Input, I: Input,
R: Rand,
{ {
entries: Vec<RefCell<Testcase<I>>>, entries: Vec<RefCell<Testcase<I>>>,
current: Option<usize>,
dir_path: PathBuf, dir_path: PathBuf,
pos: usize,
phantom: PhantomData<R>,
} }
#[cfg(feature = "std")] impl<I> Corpus<I> for OnDiskCorpus<I>
impl<I, R> HasTestcaseVec<I> for OnDiskCorpus<I, R>
where where
I: Input, I: Input,
R: Rand,
{ {
/// Returns the number of elements
#[inline] #[inline]
fn entries(&self) -> &[RefCell<Testcase<I>>] { fn count(&self) -> usize {
&self.entries self.entries.len()
} }
#[inline]
fn entries_mut(&mut self) -> &mut Vec<RefCell<Testcase<I>>> {
&mut self.entries
}
}
#[cfg(feature = "std")] /// Add an entry to the corpus and return its index
impl<I, R> Corpus<I, R> for OnDiskCorpus<I, R> #[inline]
where fn add(&mut self, mut testcase: Testcase<I>) -> Result<usize, Error> {
I: Input, match testcase.filename() {
R: Rand,
{
/// Add an entry and save it to disk
fn add(&mut self, mut entry: Testcase<I>) -> usize {
match entry.filename() {
None => { None => {
// TODO walk entry metadatas to ask for pices of filename (e.g. :havoc in AFL) // TODO walk entry metadatas to ask for pices of filename (e.g. :havoc in AFL)
let filename = self.dir_path.join(format!("id_{}", &self.entries.len())); let filename = self.dir_path.join(format!("id_{}", &self.entries.len()));
let filename_str = filename.to_str().expect("Invalid Path"); let filename_str = filename.to_str().expect("Invalid Path");
entry.set_filename(filename_str.into()); testcase.set_filename(filename_str.into());
} }
_ => {} _ => {}
} }
entry testcase
.store_input() .store_input()
.expect("Could not save testcase to disk".into()); .expect("Could not save testcase to disk".into());
self.entries.push(RefCell::new(entry)); self.entries.push(RefCell::new(testcase));
self.entries.len() - 1 Ok(self.entries.len() - 1)
} }
/// Replaces the testcase at the given idx
#[inline] #[inline]
fn current_testcase(&self) -> (&RefCell<Testcase<I>>, usize) { fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<(), Error> {
(self.get(self.pos), self.pos) if idx >= self.entries.len() {
return Err(Error::KeyNotFound(format!("Index {} out of bounds", idx)));
}
self.entries[idx] = RefCell::new(testcase);
Ok(())
} }
/// Gets the next entry /// Removes an entry from the corpus, returning it if it was present.
#[inline] #[inline]
fn next(&mut self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error> { fn remove(&mut self, idx: usize) -> Result<Option<Testcase<I>>, Error> {
if self.count() == 0 { if idx >= self.entries.len() {
Err(Error::Empty("No entries in corpus".to_owned())) Ok(None)
} else { } else {
let len = { self.entries().len() }; Ok(Some(self.entries.remove(idx).into_inner()))
let id = rand.below(len as u64) as usize;
self.pos = id;
Ok((self.get(id), id))
} }
} }
// TODO save and remove files, cache, etc..., ATM use just InMemoryCorpus /// Get by id
#[inline]
fn get(&self, idx: usize) -> Result<&RefCell<Testcase<I>>, Error> {
Ok(&self.entries[idx])
}
/// Current testcase scheduled
#[inline]
fn current(&self) -> &Option<usize> {
&self.current
}
/// Current testcase scheduled (mut)
#[inline]
fn current_mut(&mut self) -> &mut Option<usize> {
&mut self.current
}
} }
#[cfg(feature = "std")] impl<I> OnDiskCorpus<I>
impl<I, R> OnDiskCorpus<I, R>
where where
I: Input, I: Input,
R: Rand,
{ {
pub fn new(dir_path: PathBuf) -> Self { pub fn new(dir_path: PathBuf) -> Self {
Self { Self {
dir_path: dir_path,
entries: vec![], entries: vec![],
pos: 0, current: None,
phantom: PhantomData, dir_path: dir_path,
} }
} }
} }

View File

@ -1,132 +1,65 @@
//! The queue corpus implements an afl-like queue mechanism //! The queue corpus implements an afl-like queue mechanism
use alloc::{borrow::ToOwned, vec::Vec}; use alloc::borrow::ToOwned;
use core::{cell::RefCell, marker::PhantomData}; use core::marker::PhantomData;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
corpus::Corpus, corpus::HasTestcaseVec, corpus::Testcase, inputs::Input, utils::Rand, Error, corpus::{Corpus, CorpusScheduler},
inputs::Input,
state::HasCorpus,
Error,
}; };
/// A Queue-like corpus, wrapping an existing Corpus instance pub struct QueueCorpusScheduler<C, I, S>
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct QueueCorpus<C, I, R>
where where
C: Corpus<I, R>, S: HasCorpus<C, I>,
C: Corpus<I>,
I: Input, I: Input,
R: Rand,
{ {
corpus: C, phantom: PhantomData<(C, I, S)>,
pos: usize,
cycles: u64,
phantom: PhantomData<(I, R)>,
} }
impl<C, I, R> HasTestcaseVec<I> for QueueCorpus<C, I, R> impl<C, I, S> CorpusScheduler<I, S> for QueueCorpusScheduler<C, I, S>
where where
C: Corpus<I, R>, S: HasCorpus<C, I>,
C: Corpus<I>,
I: Input, I: Input,
R: Rand,
{ {
#[inline] /// Gets the next entry at random
fn entries(&self) -> &[RefCell<Testcase<I>>] { fn next(&self, state: &mut S) -> Result<usize, Error> {
self.corpus.entries() if state.corpus().count() == 0 {
} Err(Error::Empty("No entries in corpus".to_owned()))
#[inline] } else {
fn entries_mut(&mut self) -> &mut Vec<RefCell<Testcase<I>>> { let id = match state.corpus().current() {
self.corpus.entries_mut() Some(cur) => {
} if *cur + 1 > state.corpus().count() {
} 0
} else {
impl<C, I, R> Corpus<I, R> for QueueCorpus<C, I, R> *cur + 1
where }
C: Corpus<I, R>, }
I: Input, None => 0,
R: Rand, };
{ *state.corpus_mut().current_mut() = Some(id);
/// Returns the number of elements Ok(id)
#[inline]
fn count(&self) -> usize {
self.corpus.count()
}
#[inline]
fn add(&mut self, entry: Testcase<I>) -> usize {
self.corpus.add(entry)
}
/// Removes an entry from the corpus, returning it if it was present.
#[inline]
fn remove(&mut self, entry: &Testcase<I>) -> Option<Testcase<I>> {
self.corpus.remove(entry)
}
/// Gets a random entry
#[inline]
fn random_entry(&self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error> {
self.corpus.random_entry(rand)
}
/// Returns the testacase we currently use
#[inline]
fn current_testcase(&self) -> (&RefCell<Testcase<I>>, usize) {
(self.get(self.pos - 1), self.pos - 1)
}
/// Gets the next entry
#[inline]
fn next(&mut self, _rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error> {
self.pos += 1;
if self.corpus.count() == 0 {
return Err(Error::Empty("Corpus".to_owned()));
} }
if self.pos > self.corpus.count() {
// TODO: Always loop or return informational error?
self.pos = 1;
self.cycles += 1;
}
Ok((&self.corpus.entries()[self.pos - 1], self.pos - 1))
} }
} }
impl<C, I, R> QueueCorpus<C, I, R> impl<C, I, S> QueueCorpusScheduler<C, I, S>
where where
C: Corpus<I, R>, S: HasCorpus<C, I>,
C: Corpus<I>,
I: Input, I: Input,
R: Rand,
{ {
pub fn new(corpus: C) -> Self { pub fn new() -> Self {
Self { Self {
corpus: corpus,
phantom: PhantomData, phantom: PhantomData,
cycles: 0,
pos: 0,
} }
} }
#[inline]
pub fn cycles(&self) -> u64 {
self.cycles
}
#[inline]
pub fn pos(&self) -> usize {
self.pos
}
// TODO maybe impl HasCorpus
#[inline]
pub fn corpus(&self) -> &C {
&self.corpus
}
#[inline]
pub fn corpus_mut(&mut self) -> &mut C {
&mut self.corpus
}
} }
/*
#[cfg(test)] #[cfg(test)]
#[cfg(feature = "std")] #[cfg(feature = "std")]
mod tests { mod tests {
@ -170,3 +103,4 @@ mod tests {
assert_eq!(filename, "fancyfile"); assert_eq!(filename, "fancyfile");
} }
} }
*/

View File

@ -159,6 +159,18 @@ where
} }
} }
/// Create a new Testcase instace given an input and a fitness
#[inline]
pub fn with_fitness(input: I, fitness: u32) -> Self {
Testcase {
input: Some(input.into()),
filename: None,
fitness: fitness,
metadatas: SerdeAnyMap::new(),
exec_time: None,
}
}
#[inline] #[inline]
pub fn default() -> Self { pub fn default() -> Self {
Testcase { Testcase {

View File

@ -1,6 +1,7 @@
use crate::bolts::llmp::LlmpSender; use crate::bolts::llmp::LlmpSender;
use alloc::{string::ToString, vec::Vec}; use alloc::{string::ToString, vec::Vec};
use core::{marker::PhantomData, time::Duration}; use core::{marker::PhantomData, time::Duration};
use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use crate::bolts::llmp::LlmpReceiver; use crate::bolts::llmp::LlmpReceiver;
@ -16,16 +17,13 @@ use crate::{
llmp::{self, LlmpClient, LlmpClientDescription, Tag}, llmp::{self, LlmpClient, LlmpClientDescription, Tag},
shmem::ShMem, shmem::ShMem,
}, },
corpus::Corpus,
events::{BrokerEventResult, Event, EventManager}, events::{BrokerEventResult, Event, EventManager},
executors::ExitKind, executors::ExitKind,
executors::{Executor, HasObservers}, executors::{Executor, HasObservers},
feedbacks::FeedbacksTuple,
inputs::Input, inputs::Input,
observers::ObserversTuple, observers::ObserversTuple,
state::State, state::IfInteresting,
stats::Stats, stats::Stats,
utils::Rand,
Error, Error,
}; };
@ -41,23 +39,25 @@ const _LLMP_TAG_RESTART: llmp::Tag = 0x8357A87;
const _LLMP_TAG_NO_RESTART: llmp::Tag = 0x57A7EE71; const _LLMP_TAG_NO_RESTART: llmp::Tag = 0x57A7EE71;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct LlmpEventManager<I, SH, ST> pub struct LlmpEventManager<I, S, SH, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>,
SH: ShMem, SH: ShMem,
ST: Stats, ST: Stats,
//CE: CustomEvent<I>, //CE: CustomEvent<I>,
{ {
stats: Option<ST>, stats: Option<ST>,
llmp: llmp::LlmpConnection<SH>, llmp: llmp::LlmpConnection<SH>,
phantom: PhantomData<I>, phantom: PhantomData<(I, S)>,
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg(unix)] #[cfg(unix)]
impl<I, ST> LlmpEventManager<I, UnixShMem, ST> impl<I, S, ST> LlmpEventManager<I, S, UnixShMem, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>,
ST: Stats, ST: Stats,
{ {
/// Create llmp on a port /// Create llmp on a port
@ -80,9 +80,10 @@ where
} }
} }
impl<I, SH, ST> Drop for LlmpEventManager<I, SH, ST> impl<I, S, SH, ST> Drop for LlmpEventManager<I, S, SH, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>,
SH: ShMem, SH: ShMem,
ST: Stats, ST: Stats,
{ {
@ -92,9 +93,10 @@ where
} }
} }
impl<I, SH, ST> LlmpEventManager<I, SH, ST> impl<I, S, SH, ST> LlmpEventManager<I, S, SH, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>,
SH: ShMem, SH: ShMem,
ST: Stats, ST: Stats,
{ {
@ -250,20 +252,15 @@ where
} }
// Handle arriving events in the client // Handle arriving events in the client
fn handle_in_client<C, E, FT, OC, OFT, OT, R>( fn handle_in_client<E, OT>(
&mut self, &mut self,
state: &mut State<C, FT, I, OC, OFT, R>, state: &mut S,
sender_id: u32, sender_id: u32,
event: Event<I>, event: Event<I>,
_executor: &mut E, _executor: &mut E,
) -> Result<(), Error> ) -> Result<(), Error>
where where
C: Corpus<I, R>,
E: Executor<I> + HasObservers<OT>, E: Executor<I> + HasObservers<OT>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
match event { match event {
@ -284,7 +281,7 @@ where
// TODO include ExitKind in NewTestcase // TODO include ExitKind in NewTestcase
let fitness = state.is_interesting(&input, &observers, ExitKind::Ok)?; let fitness = state.is_interesting(&input, &observers, ExitKind::Ok)?;
if fitness > 0 { if fitness > 0 {
if !state.add_if_interesting(input, fitness)?.is_none() { if !state.add_if_interesting(&input, fitness)?.is_none() {
#[cfg(feature = "std")] #[cfg(feature = "std")]
println!("Added received Testcase"); println!("Added received Testcase");
} }
@ -299,9 +296,10 @@ where
} }
} }
impl<I, SH, ST> EventManager<I> for LlmpEventManager<I, SH, ST> impl<I, S, SH, ST> EventManager<I, S> for LlmpEventManager<I, S, SH, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>,
SH: ShMem, SH: ShMem,
ST: Stats, //CE: CustomEvent<I>, ST: Stats, //CE: CustomEvent<I>,
{ {
@ -317,18 +315,9 @@ where
} }
} }
fn process<C, E, FT, OC, OFT, OT, R>( fn process<E, OT>(&mut self, state: &mut S, executor: &mut E) -> Result<usize, Error>
&mut self,
state: &mut State<C, FT, I, OC, OFT, R>,
executor: &mut E,
) -> Result<usize, Error>
where where
C: Corpus<I, R>,
E: Executor<I> + HasObservers<OT>, E: Executor<I> + HasObservers<OT>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
// TODO: Get around local event copy by moving handle_in_client // TODO: Get around local event copy by moving handle_in_client
@ -358,19 +347,7 @@ where
Ok(count) Ok(count)
} }
fn fire<C, FT, OC, OFT, R>( fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
&mut self,
_state: &mut State<C, FT, I, OC, OFT, R>,
event: Event<I>,
) -> Result<(), Error>
where
C: Corpus<I, R>,
FT: FeedbacksTuple<I>,
I: Input,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
{
let serialized = postcard::to_allocvec(&event)?; let serialized = postcard::to_allocvec(&event)?;
self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?; self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?;
Ok(()) Ok(())
@ -380,17 +357,13 @@ where
/// Serialize the current state and corpus during an executiont to bytes. /// Serialize the current state and corpus during an executiont to bytes.
/// On top, add the current llmp event manager instance to be restored /// On top, add the current llmp event manager instance to be restored
/// This method is needed when the fuzzer run crashes and has to restart. /// This method is needed when the fuzzer run crashes and has to restart.
pub fn serialize_state_mgr<C, FT, I, OC, OFT, R, SH, ST>( pub fn serialize_state_mgr<I, S, SH, ST>(
state: &State<C, FT, I, OC, OFT, R>, state: &S,
mgr: &LlmpEventManager<I, SH, ST>, mgr: &LlmpEventManager<I, S, SH, ST>,
) -> Result<Vec<u8>, Error> ) -> Result<Vec<u8>, Error>
where where
C: Corpus<I, R>,
FT: FeedbacksTuple<I>,
I: Input, I: Input,
R: Rand, S: Serialize + IfInteresting<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
SH: ShMem, SH: ShMem,
ST: Stats, ST: Stats,
{ {
@ -398,20 +371,16 @@ where
} }
/// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)` /// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)`
pub fn deserialize_state_mgr<C, FT, I, OC, OFT, R, SH, ST>( pub fn deserialize_state_mgr<I, S, SH, ST>(
state_corpus_serialized: &[u8], state_corpus_serialized: &[u8],
) -> Result<(State<C, FT, I, OC, OFT, R>, LlmpEventManager<I, SH, ST>), Error> ) -> Result<(S, LlmpEventManager<I, S, SH, ST>), Error>
where where
C: Corpus<I, R>,
FT: FeedbacksTuple<I>,
I: Input, I: Input,
R: Rand, S: DeserializeOwned + IfInteresting<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
SH: ShMem, SH: ShMem,
ST: Stats, ST: Stats,
{ {
let tuple: (State<C, FT, I, OC, OFT, R>, _) = postcard::from_bytes(&state_corpus_serialized)?; let tuple: (S, _) = postcard::from_bytes(&state_corpus_serialized)?;
Ok(( Ok((
tuple.0, tuple.0,
LlmpEventManager::existing_client_from_description(&tuple.1)?, LlmpEventManager::existing_client_from_description(&tuple.1)?,
@ -420,22 +389,24 @@ where
/// A manager that can restart on the fly, storing states in-between (in `on_resatrt`) /// A manager that can restart on the fly, storing states in-between (in `on_resatrt`)
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct LlmpRestartingEventManager<I, SH, ST> pub struct LlmpRestartingEventManager<I, S, SH, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>,
SH: ShMem, SH: ShMem,
ST: Stats, ST: Stats,
//CE: CustomEvent<I>, //CE: CustomEvent<I>,
{ {
/// The embedded llmp event manager /// The embedded llmp event manager
llmp_mgr: LlmpEventManager<I, SH, ST>, llmp_mgr: LlmpEventManager<I, S, SH, ST>,
/// The sender to serialize the state for the next runner /// The sender to serialize the state for the next runner
sender: LlmpSender<SH>, sender: LlmpSender<SH>,
} }
impl<I, SH, ST> EventManager<I> for LlmpRestartingEventManager<I, SH, ST> impl<I, S, SH, ST> EventManager<I, S> for LlmpRestartingEventManager<I, S, SH, ST>
where where
I: Input, I: Input,
S: IfInteresting<I> + Serialize,
SH: ShMem, SH: ShMem,
ST: Stats, //CE: CustomEvent<I>, ST: Stats, //CE: CustomEvent<I>,
{ {
@ -447,17 +418,7 @@ where
} }
/// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner. /// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner.
fn on_restart<C, FT, OC, OFT, R>( fn on_restart(&mut self, state: &mut S) -> Result<(), Error> {
&mut self,
state: &mut State<C, FT, I, OC, OFT, R>,
) -> Result<(), Error>
where
C: Corpus<I, R>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
{
// First, reset the page to 0 so the next iteration can read read from the beginning of this page // First, reset the page to 0 so the next iteration can read read from the beginning of this page
unsafe { self.sender.reset() }; unsafe { self.sender.reset() };
let state_corpus_serialized = serialize_state_mgr(state, &self.llmp_mgr)?; let state_corpus_serialized = serialize_state_mgr(state, &self.llmp_mgr)?;
@ -465,35 +426,15 @@ where
.send_buf(_LLMP_TAG_RESTART, &state_corpus_serialized) .send_buf(_LLMP_TAG_RESTART, &state_corpus_serialized)
} }
fn process<C, E, FT, OC, OFT, OT, R>( fn process<E, OT>(&mut self, state: &mut S, executor: &mut E) -> Result<usize, Error>
&mut self,
state: &mut State<C, FT, I, OC, OFT, R>,
executor: &mut E,
) -> Result<usize, Error>
where where
C: Corpus<I, R>,
E: Executor<I> + HasObservers<OT>, E: Executor<I> + HasObservers<OT>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
self.llmp_mgr.process(state, executor) self.llmp_mgr.process(state, executor)
} }
fn fire<C, FT, OC, OFT, R>( fn fire(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error> {
&mut self,
state: &mut State<C, FT, I, OC, OFT, R>,
event: Event<I>,
) -> Result<(), Error>
where
C: Corpus<I, R>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
{
// Check if we are going to crash in the event, in which case we store our current state for the next runner // Check if we are going to crash in the event, in which case we store our current state for the next runner
self.llmp_mgr.fire(state, event) self.llmp_mgr.fire(state, event)
} }
@ -505,14 +446,15 @@ const _ENV_FUZZER_RECEIVER: &str = &"_AFL_ENV_FUZZER_RECEIVER";
/// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages) /// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages)
const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT"; const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT";
impl<I, SH, ST> LlmpRestartingEventManager<I, SH, ST> impl<I, S, SH, ST> LlmpRestartingEventManager<I, S, SH, ST>
where where
I: Input, I: Input,
S: IfInteresting<I>,
SH: ShMem, SH: ShMem,
ST: Stats, //CE: CustomEvent<I>, ST: Stats, //CE: CustomEvent<I>,
{ {
/// Create a new runner, the executed child doing the actual fuzzing. /// Create a new runner, the executed child doing the actual fuzzing.
pub fn new(llmp_mgr: LlmpEventManager<I, SH, ST>, sender: LlmpSender<SH>) -> Self { pub fn new(llmp_mgr: LlmpEventManager<I, S, SH, ST>, sender: LlmpSender<SH>) -> Self {
Self { llmp_mgr, sender } Self { llmp_mgr, sender }
} }
@ -530,24 +472,14 @@ where
/// A restarting state is a combination of restarter and runner, that can be used on systems without `fork`. /// A restarting state is a combination of restarter and runner, that can be used on systems without `fork`.
/// The restarter will start a new process each time the child crashes or timeouts. /// The restarter will start a new process each time the child crashes or timeouts.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn setup_restarting_mgr<I, C, FT, OC, OFT, R, SH, ST>( pub fn setup_restarting_mgr<I, S, SH, ST>(
//mgr: &mut LlmpEventManager<I, SH, ST>, //mgr: &mut LlmpEventManager<I, S, SH, ST>,
stats: ST, stats: ST,
broker_port: u16, broker_port: u16,
) -> Result< ) -> Result<(Option<S>, LlmpRestartingEventManager<I, S, SH, ST>), Error>
(
Option<State<C, FT, I, OC, OFT, R>>,
LlmpRestartingEventManager<I, SH, ST>,
),
Error,
>
where where
I: Input, I: Input,
C: Corpus<I, R>, S: DeserializeOwned + IfInteresting<I>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
SH: ShMem, SH: ShMem,
ST: Stats, ST: Stats,
{ {
@ -555,7 +487,7 @@ where
// We start ourself as child process to actually fuzz // We start ourself as child process to actually fuzz
if std::env::var(_ENV_FUZZER_SENDER).is_err() { if std::env::var(_ENV_FUZZER_SENDER).is_err() {
mgr = LlmpEventManager::<I, SH, ST>::new_on_port(stats, broker_port)?; mgr = LlmpEventManager::<I, S, SH, ST>::new_on_port(stats, broker_port)?;
if mgr.is_broker() { if mgr.is_broker() {
// Yep, broker. Just loop here. // Yep, broker. Just loop here.
println!("Doing broker things. Run this tool again to start fuzzing in a client."); println!("Doing broker things. Run this tool again to start fuzzing in a client.");
@ -598,7 +530,7 @@ where
None => { None => {
println!("First run. Let's set it all up"); println!("First run. Let's set it all up");
// Mgr to send and receive msgs from/to all other fuzzer instances // Mgr to send and receive msgs from/to all other fuzzer instances
let client_mgr = LlmpEventManager::<I, SH, ST>::existing_client_from_env( let client_mgr = LlmpEventManager::<I, S, SH, ST>::existing_client_from_env(
_ENV_FUZZER_BROKER_CLIENT_INITIAL, _ENV_FUZZER_BROKER_CLIENT_INITIAL,
)?; )?;
@ -607,8 +539,7 @@ where
// Restoring from a previous run, deserialize state and corpus. // Restoring from a previous run, deserialize state and corpus.
Some((_sender, _tag, msg)) => { Some((_sender, _tag, msg)) => {
println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len()); println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len());
let (state, mgr): (State<C, FT, I, OC, OFT, R>, LlmpEventManager<I, SH, ST>) = let (state, mgr): (S, LlmpEventManager<I, S, SH, ST>) = deserialize_state_mgr(&msg)?;
deserialize_state_mgr(&msg)?;
(Some(state), LlmpRestartingEventManager::new(mgr, sender)) (Some(state), LlmpRestartingEventManager::new(mgr, sender))
} }

View File

@ -1,23 +1,20 @@
use alloc::{string::ToString, vec::Vec}; use alloc::{string::ToString, vec::Vec};
use core::marker::PhantomData;
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg(unix)] #[cfg(unix)]
use crate::{ use crate::{
corpus::Corpus,
events::{BrokerEventResult, Event, EventManager}, events::{BrokerEventResult, Event, EventManager},
executors::{Executor, HasObservers}, executors::{Executor, HasObservers},
feedbacks::FeedbacksTuple,
inputs::Input, inputs::Input,
observers::ObserversTuple, observers::ObserversTuple,
state::State,
stats::Stats, stats::Stats,
utils::Rand,
Error, Error,
}; };
/// A simple, single-threaded event manager that just logs /// A simple, single-threaded event manager that just logs
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct LoggerEventManager<I, ST> pub struct LoggerEventManager<I, S, ST>
where where
I: Input, I: Input,
ST: Stats, //CE: CustomEvent<I, OT>, ST: Stats, //CE: CustomEvent<I, OT>,
@ -26,25 +23,17 @@ where
stats: ST, stats: ST,
/// The events that happened since the last handle_in_broker /// The events that happened since the last handle_in_broker
events: Vec<Event<I>>, events: Vec<Event<I>>,
phantom: PhantomData<S>,
} }
impl<I, ST> EventManager<I> for LoggerEventManager<I, ST> impl<I, S, ST> EventManager<I, S> for LoggerEventManager<I, S, ST>
where where
I: Input, I: Input,
ST: Stats, //CE: CustomEvent<I, OT>, ST: Stats, //CE: CustomEvent<I, OT>,
{ {
fn process<C, E, FT, OC, OFT, OT, R>( fn process<E, OT>(&mut self, state: &mut S, _executor: &mut E) -> Result<usize, Error>
&mut self,
state: &mut State<C, FT, I, OC, OFT, R>,
_executor: &mut E,
) -> Result<usize, Error>
where where
C: Corpus<I, R>,
E: Executor<I> + HasObservers<OT>, E: Executor<I> + HasObservers<OT>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
let count = self.events.len(); let count = self.events.len();
@ -55,18 +44,7 @@ where
Ok(count) Ok(count)
} }
fn fire<C, FT, OC, OFT, R>( fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
&mut self,
_state: &mut State<C, FT, I, OC, OFT, R>,
event: Event<I>,
) -> Result<(), Error>
where
C: Corpus<I, R>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
{
match Self::handle_in_broker(&mut self.stats, &event)? { match Self::handle_in_broker(&mut self.stats, &event)? {
BrokerEventResult::Forward => self.events.push(event), BrokerEventResult::Forward => self.events.push(event),
BrokerEventResult::Handled => (), BrokerEventResult::Handled => (),
@ -75,7 +53,7 @@ where
} }
} }
impl<I, ST> LoggerEventManager<I, ST> impl<I, S, ST> LoggerEventManager<I, S, ST>
where where
I: Input, I: Input,
ST: Stats, //TODO CE: CustomEvent, ST: Stats, //TODO CE: CustomEvent,
@ -84,6 +62,7 @@ where
Self { Self {
stats: stats, stats: stats,
events: vec![], events: vec![],
phantom: PhantomData,
} }
} }
@ -132,18 +111,7 @@ where
} }
// Handle arriving events in the client // Handle arriving events in the client
fn handle_in_client<C, FT, OC, OFT, R>( fn handle_in_client(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
&mut self,
_state: &mut State<C, FT, I, OC, OFT, R>,
event: Event<I>,
) -> Result<(), Error>
where
C: Corpus<I, R>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
{
match event { match event {
_ => Err(Error::Unknown(format!( _ => Err(Error::Unknown(format!(
"Received illegal message that message should not have arrived: {:?}.", "Received illegal message that message should not have arrived: {:?}.",

View File

@ -9,13 +9,9 @@ use core::{fmt, marker::PhantomData, time::Duration};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
corpus::Corpus,
executors::{Executor, HasObservers}, executors::{Executor, HasObservers},
feedbacks::FeedbacksTuple,
inputs::Input, inputs::Input,
observers::ObserversTuple, observers::ObserversTuple,
state::State,
utils::Rand,
Error, Error,
}; };
@ -153,7 +149,7 @@ where
/// EventManager is the main communications hub. /// EventManager is the main communications hub.
/// For the "normal" multi-processed mode, you may want to look into `RestartingEventManager` /// For the "normal" multi-processed mode, you may want to look into `RestartingEventManager`
pub trait EventManager<I> pub trait EventManager<I, S>
where where
I: Input, I: Input,
{ {
@ -162,18 +158,9 @@ where
/// Lookup for incoming events and process them. /// Lookup for incoming events and process them.
/// Return the number of processes events or an error /// Return the number of processes events or an error
fn process<C, E, FT, OC, OFT, OT, R>( fn process<E, OT>(&mut self, state: &mut S, executor: &mut E) -> Result<usize, Error>
&mut self,
state: &mut State<C, FT, I, OC, OFT, R>,
executor: &mut E,
) -> Result<usize, Error>
where where
C: Corpus<I, R>,
E: Executor<I> + HasObservers<OT>, E: Executor<I> + HasObservers<OT>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple; OT: ObserversTuple;
/// Serialize all observers for this type and manager /// Serialize all observers for this type and manager
@ -194,17 +181,7 @@ where
/// For restarting event managers, implement a way to forward state to their next peers. /// For restarting event managers, implement a way to forward state to their next peers.
#[inline] #[inline]
fn on_restart<C, FT, OC, OFT, R>( fn on_restart(&mut self, _state: &mut S) -> Result<(), Error> {
&mut self,
_state: &mut State<C, FT, I, OC, OFT, R>,
) -> Result<(), Error>
where
C: Corpus<I, R>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
{
Ok(()) Ok(())
} }
@ -213,57 +190,27 @@ where
fn await_restart_safe(&mut self) {} fn await_restart_safe(&mut self) {}
/// Send off an event to the broker /// Send off an event to the broker
fn fire<C, FT, OC, OFT, R>( fn fire(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error>;
&mut self,
_state: &mut State<C, FT, I, OC, OFT, R>,
event: Event<I>,
) -> Result<(), Error>
where
C: Corpus<I, R>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>;
} }
/// An eventmgr for tests, and as placeholder if you really don't need an event manager. /// An eventmgr for tests, and as placeholder if you really don't need an event manager.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct NopEventManager<I> { pub struct NopEventManager<I, S> {
phantom: PhantomData<I>, phantom: PhantomData<(I, S)>,
} }
impl<I> EventManager<I> for NopEventManager<I> impl<I, S> EventManager<I, S> for NopEventManager<I, S>
where where
I: Input, I: Input,
{ {
fn process<C, E, FT, OC, OFT, OT, R>( fn process<E, OT>(&mut self, _state: &mut S, _executor: &mut E) -> Result<usize, Error>
&mut self,
_state: &mut State<C, FT, I, OC, OFT, R>,
_executor: &mut E,
) -> Result<usize, Error>
where where
C: Corpus<I, R>,
E: Executor<I> + HasObservers<OT>, E: Executor<I> + HasObservers<OT>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
Ok(0) Ok(0)
} }
fn fire<C, FT, OC, OFT, R>( fn fire(&mut self, _state: &mut S, _event: Event<I>) -> Result<(), Error> {
&mut self,
_state: &mut State<C, FT, I, OC, OFT, R>,
_event: Event<I>,
) -> Result<(), Error>
where
C: Corpus<I, R>,
FT: FeedbacksTuple<I>,
R: Rand,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
{
Ok(()) Ok(())
} }
} }

View File

@ -14,8 +14,7 @@ use crate::{
feedbacks::FeedbacksTuple, feedbacks::FeedbacksTuple,
inputs::{HasTargetBytes, Input}, inputs::{HasTargetBytes, Input},
observers::ObserversTuple, observers::ObserversTuple,
state::State, state::{HasObjectives, HasSolutions},
utils::Rand,
Error, Error,
}; };
@ -54,52 +53,27 @@ where
OT: ObserversTuple, OT: ObserversTuple,
{ {
#[inline] #[inline]
fn pre_exec<C, EM, FT, OC, OFT, R>( fn pre_exec<EM, S>(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error>
&mut self,
_state: &mut State<C, FT, I, OC, OFT, R>,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error>
where where
R: Rand, EM: EventManager<I, S>,
FT: FeedbacksTuple<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
C: Corpus<I, R>,
EM: EventManager<I>,
{ {
#[cfg(unix)] #[cfg(unix)]
#[cfg(feature = "std")] #[cfg(feature = "std")]
unsafe { unsafe {
set_oncrash_ptrs::<C, EM, FT, I, OC, OFT, OT, R>( set_oncrash_ptrs(state, event_mgr, self.observers(), input);
_state,
_event_mgr,
self.observers(),
_input,
);
} }
Ok(()) Ok(())
} }
#[inline] #[inline]
fn post_exec<C, EM, FT, OC, OFT, R>( fn post_exec<EM, S>(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error>
&mut self,
_state: &State<C, FT, I, OC, OFT, R>,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error>
where where
R: Rand, EM: EventManager<I, S>,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
EM: EventManager<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
{ {
#[cfg(unix)] #[cfg(unix)]
#[cfg(feature = "std")] #[cfg(feature = "std")]
unsafe { unsafe {
reset_oncrash_ptrs::<C, EM, FT, I, OT, R>(); reset_oncrash_ptrs();
} }
Ok(()) Ok(())
} }
@ -148,28 +122,24 @@ where
/// depnding on different corpus or state. /// depnding on different corpus or state.
/// * `name` - the name of this executor (to address it along the way) /// * `name` - the name of this executor (to address it along the way)
/// * `harness_fn` - the harness, executiong the function /// * `harness_fn` - the harness, executiong the function
/// * `on_crash_fn` - When an in-mem harness crashes, it may safe some state to continue fuzzing later.
/// Do that that in this function. The program will crash afterwards.
/// * `observers` - the observers observing the target during execution /// * `observers` - the observers observing the target during execution
pub fn new<C, EM, FT, OC, OFT, R>( pub fn new<EM, OC, OFT, S>(
name: &'static str, name: &'static str,
harness_fn: HarnessFunction<Self>, harness_fn: HarnessFunction<Self>,
observers: OT, observers: OT,
_state: &mut State<C, FT, I, OC, OFT, R>, _state: &mut S,
_event_mgr: &mut EM, _event_mgr: &mut EM,
) -> Self ) -> Self
where where
R: Rand, EM: EventManager<I, S>,
FT: FeedbacksTuple<I>, OC: Corpus<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>, OFT: FeedbacksTuple<I>,
C: Corpus<I, R>, S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
EM: EventManager<I>,
{ {
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg(unix)] #[cfg(unix)]
unsafe { unsafe {
setup_crash_handlers::<C, EM, FT, I, OC, OFT, OT, R>(); setup_crash_handlers::<EM, I, OC, OFT, OT, S>();
} }
Self { Self {
@ -181,25 +151,6 @@ where
} }
} }
/*
unsafe fn tidy_up_on_exit<EM>(mgr: &EM)
where
EM: EventManager<I>,
I: Input,
{
match manager.llmp {
IsClient { client } => {
let map = client.out_maps.last().unwrap();
/// wait until we can drop the message safely.
map.await_save_to_unmap_blocking();
/// Make sure all pages are unmapped.
drop(manager);
}
_ => (),
}
}*/
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg(unix)] #[cfg(unix)]
pub mod unix_signals { pub mod unix_signals {
@ -219,14 +170,13 @@ pub mod unix_signals {
}; };
use crate::{ use crate::{
corpus::Corpus, corpus::{Corpus, Testcase},
events::{Event, EventManager}, events::{Event, EventManager},
executors::ExitKind, executors::ExitKind,
feedbacks::FeedbacksTuple, feedbacks::FeedbacksTuple,
inputs::Input, inputs::Input,
observers::ObserversTuple, observers::ObserversTuple,
state::State, state::{HasObjectives, HasSolutions},
utils::Rand,
}; };
/// Let's get 8 mb for now. /// Let's get 8 mb for now.
const SIGNAL_STACK_SIZE: usize = 2 << 22; const SIGNAL_STACK_SIZE: usize = 2 << 22;
@ -242,19 +192,14 @@ pub mod unix_signals {
/// This is needed for certain non-rust side effects, as well as unix signal handling. /// This is needed for certain non-rust side effects, as well as unix signal handling.
static mut CURRENT_INPUT_PTR: *const c_void = ptr::null(); static mut CURRENT_INPUT_PTR: *const c_void = ptr::null();
pub unsafe extern "C" fn libaflrs_executor_inmem_handle_crash<C, EM, FT, I, OC, OFT, OT, R>( unsafe fn inmem_handle_crash<EM, I, OC, OFT, OT, S>(_sig: c_int, info: siginfo_t, _void: c_void)
_sig: c_int, where
info: siginfo_t, EM: EventManager<I, S>,
_void: c_void,
) where
EM: EventManager<I>,
C: Corpus<I, R>,
OT: ObserversTuple, OT: ObserversTuple,
OC: Corpus<I, R>, OC: Corpus<I>,
OFT: FeedbacksTuple<I>, OFT: FeedbacksTuple<I>,
FT: FeedbacksTuple<I>, S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
I: Input, I: Input,
R: Rand,
{ {
if CURRENT_INPUT_PTR == ptr::null() { if CURRENT_INPUT_PTR == ptr::null() {
println!( println!(
@ -267,7 +212,6 @@ pub mod unix_signals {
Ok(maps) => println!("maps:\n{}", maps), Ok(maps) => println!("maps:\n{}", maps),
Err(e) => println!("Couldn't load mappings: {:?}", e), Err(e) => println!("Couldn't load mappings: {:?}", e),
}; };
#[cfg(feature = "std")] #[cfg(feature = "std")]
{ {
println!("Type QUIT to restart the child"); println!("Type QUIT to restart the child");
@ -289,30 +233,26 @@ pub mod unix_signals {
let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap(); let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap();
// Make sure we don't crash in the crash handler forever. // Make sure we don't crash in the crash handler forever.
CURRENT_INPUT_PTR = ptr::null(); CURRENT_INPUT_PTR = ptr::null();
let state = (STATE_PTR as *mut State<C, FT, I, OC, OFT, R>) let state = (STATE_PTR as *mut S).as_mut().unwrap();
.as_mut()
.unwrap();
let mgr = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap(); let mgr = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap();
let observers = (OBSERVERS_PTR as *const OT).as_ref().unwrap(); let observers = (OBSERVERS_PTR as *const OT).as_ref().unwrap();
let obj_fitness = state let obj_fitness = state
.objective_feedbacks_mut() .objectives_mut()
.is_interesting_all(&input, observers, ExitKind::Crash) .is_interesting_all(&input, observers, ExitKind::Crash)
.expect("In crash handler objective feedbacks failure.".into()); .expect("In crash handler objectives failure.".into());
if obj_fitness > 0 { if obj_fitness > 0 {
if !state state
.add_if_objective(input.clone(), obj_fitness) .solutions_mut()
.expect("In crash handler objective corpus add failure.".into()) .add(Testcase::new(input.clone()))
.is_none() .expect("In crash handler solutions failure.".into());
{ mgr.fire(
mgr.fire( state,
state, Event::Objective {
Event::Objective { objective_size: state.solutions().count(),
objective_size: state.objective_corpus().count(), },
}, )
) .expect("Could not send crashing input".into());
.expect(&format!("Could not send timeouting input {:?}", input));
}
} }
mgr.on_restart(state).unwrap(); mgr.on_restart(state).unwrap();
@ -324,19 +264,17 @@ pub mod unix_signals {
std::process::exit(1); std::process::exit(1);
} }
pub unsafe extern "C" fn libaflrs_executor_inmem_handle_timeout<C, EM, FT, I, OC, OFT, OT, R>( unsafe fn inmem_handle_timeout<EM, I, OC, OFT, OT, S>(
_sig: c_int, _sig: c_int,
_info: siginfo_t, _info: siginfo_t,
_void: c_void, _void: c_void,
) where ) where
EM: EventManager<I>, EM: EventManager<I, S>,
C: Corpus<I, R>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple, OT: ObserversTuple,
FT: FeedbacksTuple<I>, OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
I: Input, I: Input,
R: Rand,
{ {
dbg!("TIMEOUT/SIGUSR2 received"); dbg!("TIMEOUT/SIGUSR2 received");
if CURRENT_INPUT_PTR.is_null() { if CURRENT_INPUT_PTR.is_null() {
@ -350,55 +288,46 @@ pub mod unix_signals {
let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap(); let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap();
// Make sure we don't crash in the crash handler forever. // Make sure we don't crash in the crash handler forever.
CURRENT_INPUT_PTR = ptr::null(); CURRENT_INPUT_PTR = ptr::null();
let state = (STATE_PTR as *mut State<C, FT, I, OC, OFT, R>) let state = (STATE_PTR as *mut S).as_mut().unwrap();
.as_mut()
.unwrap();
let mgr = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap(); let mgr = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap();
let observers = (OBSERVERS_PTR as *const OT).as_ref().unwrap(); let observers = (OBSERVERS_PTR as *const OT).as_ref().unwrap();
let obj_fitness = state let obj_fitness = state
.objective_feedbacks_mut() .objectives_mut()
.is_interesting_all(&input, observers, ExitKind::Timeout) .is_interesting_all(&input, observers, ExitKind::Crash)
.expect("In timeout handler objective feedbacks failure.".into()); .expect("In timeout handler objectives failure.".into());
if obj_fitness > 0 { if obj_fitness > 0 {
if !state state
.add_if_objective(input.clone(), obj_fitness) .solutions_mut()
.expect("In timeout handler objective corpus add failure.".into()) .add(Testcase::new(input.clone()))
.is_none() .expect("In timeout handler solutions failure.".into());
{ mgr.fire(
mgr.fire( state,
state, Event::Objective {
Event::Objective { objective_size: state.solutions().count(),
objective_size: state.objective_corpus().count(), },
}, )
) .expect("Could not send timeouting input".into());
.expect(&format!("Could not send timeouting input {:?}", input));
}
} }
mgr.on_restart(state).unwrap(); mgr.on_restart(state).unwrap();
println!("Waiting for broker...");
mgr.await_restart_safe();
println!("Bye!");
mgr.await_restart_safe(); mgr.await_restart_safe();
std::process::exit(1); std::process::exit(1);
} }
#[inline] #[inline]
pub unsafe fn set_oncrash_ptrs<C, EM, FT, I, OC, OFT, OT, R>( pub unsafe fn set_oncrash_ptrs<EM, I, OT, S>(
state: &mut State<C, FT, I, OC, OFT, R>, state: &mut S,
event_mgr: &mut EM, event_mgr: &mut EM,
observers: &OT, observers: &OT,
input: &I, input: &I,
) where ) {
EM: EventManager<I>,
C: Corpus<I, R>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
I: Input,
R: Rand,
{
CURRENT_INPUT_PTR = input as *const _ as *const c_void; CURRENT_INPUT_PTR = input as *const _ as *const c_void;
STATE_PTR = state as *mut _ as *mut c_void; STATE_PTR = state as *mut _ as *mut c_void;
EVENT_MGR_PTR = event_mgr as *mut _ as *mut c_void; EVENT_MGR_PTR = event_mgr as *mut _ as *mut c_void;
@ -406,24 +335,21 @@ pub mod unix_signals {
} }
#[inline] #[inline]
pub unsafe fn reset_oncrash_ptrs<C, EM, FT, I, OT, R>() { pub unsafe fn reset_oncrash_ptrs() {
CURRENT_INPUT_PTR = ptr::null(); CURRENT_INPUT_PTR = ptr::null();
STATE_PTR = ptr::null_mut(); STATE_PTR = ptr::null_mut();
EVENT_MGR_PTR = ptr::null_mut(); EVENT_MGR_PTR = ptr::null_mut();
OBSERVERS_PTR = ptr::null_mut(); OBSERVERS_PTR = ptr::null();
} }
// TODO clearly state that manager should be static (maybe put the 'static lifetime?) pub unsafe fn setup_crash_handlers<EM, I, OC, OFT, OT, S>()
pub unsafe fn setup_crash_handlers<C, EM, FT, I, OC, OFT, OT, R>()
where where
EM: EventManager<I>, EM: EventManager<I, S>,
C: Corpus<I, R>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple, OT: ObserversTuple,
FT: FeedbacksTuple<I>, OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
I: Input, I: Input,
R: Rand,
{ {
// First, set up our own stack to be used during segfault handling. (and specify `SA_ONSTACK` in `sigaction`) // First, set up our own stack to be used during segfault handling. (and specify `SA_ONSTACK` in `sigaction`)
if SIGNAL_STACK_PTR.is_null() { if SIGNAL_STACK_PTR.is_null() {
@ -440,8 +366,7 @@ pub mod unix_signals {
let mut sa: sigaction = mem::zeroed(); let mut sa: sigaction = mem::zeroed();
libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t); libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK; sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
sa.sa_sigaction = sa.sa_sigaction = inmem_handle_crash::<EM, I, OC, OFT, OT, S> as usize;
libaflrs_executor_inmem_handle_crash::<C, EM, FT, I, OC, OFT, OT, R> as usize;
for (sig, msg) in &[ for (sig, msg) in &[
(SIGSEGV, "segfault"), (SIGSEGV, "segfault"),
(SIGBUS, "sigbus"), (SIGBUS, "sigbus"),
@ -455,8 +380,7 @@ pub mod unix_signals {
} }
} }
sa.sa_sigaction = sa.sa_sigaction = inmem_handle_timeout::<EM, I, OC, OFT, OT, S> as usize;
libaflrs_executor_inmem_handle_timeout::<C, EM, FT, I, OC, OFT, OT, R> as usize;
if sigaction(SIGUSR2, &mut sa as *mut sigaction, ptr::null_mut()) < 0 { if sigaction(SIGUSR2, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
panic!("Could not set up sigusr2 handler for timeouts"); panic!("Could not set up sigusr2 handler for timeouts");
} }

View File

@ -10,13 +10,9 @@ use core::marker::PhantomData;
use crate::{ use crate::{
bolts::tuples::{MatchNameAndType, MatchType, Named, TupleList}, bolts::tuples::{MatchNameAndType, MatchType, Named, TupleList},
corpus::Corpus,
events::EventManager, events::EventManager,
feedbacks::FeedbacksTuple,
inputs::{HasTargetBytes, Input}, inputs::{HasTargetBytes, Input},
observers::ObserversTuple, observers::ObserversTuple,
state::State,
utils::Rand,
Error, Error,
}; };
@ -84,38 +80,23 @@ where
{ {
#[inline] #[inline]
/// Called right before exexution starts /// Called right before exexution starts
fn pre_exec<C, EM, FT, OC, OFT, R>( fn pre_exec<EM, S>(
&mut self, &mut self,
_state: &mut State<C, FT, I, OC, OFT, R>, _state: &mut S,
_event_mgr: &mut EM, _event_mgr: &mut EM,
_input: &I, _input: &I,
) -> Result<(), Error> ) -> Result<(), Error>
where where
R: Rand, EM: EventManager<I, S>,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
EM: EventManager<I>,
{ {
Ok(()) Ok(())
} }
#[inline] #[inline]
/// Called right after execution finished. /// Called right after execution finished.
fn post_exec<C, EM, FT, OC, OFT, R>( fn post_exec<EM, S>(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error>
&mut self,
_state: &State<C, FT, I, OC, OFT, R>,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error>
where where
R: Rand, EM: EventManager<I, S>,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
EM: EventManager<I>,
{ {
Ok(()) Ok(())
} }

View File

@ -148,13 +148,6 @@ where
self.0.discard_metadata(input)?; self.0.discard_metadata(input)?;
self.1.discard_metadata_all(input) self.1.discard_metadata_all(input)
} }
/*
fn restore_state_from_all(&mut self, restore_from: &Self) -> Result<(), Error> {
self.0.restore_from(restore_from.0)?;
self.1.restore_state_from_all(restore_from.1)?;
}
*/
} }
/// Is a crash feedback /// Is a crash feedback

148
libafl/src/fuzzer.rs Normal file
View File

@ -0,0 +1,148 @@
use crate::{
corpus::CorpusScheduler,
events::{Event, EventManager},
executors::{Executor, HasObservers},
inputs::Input,
observers::ObserversTuple,
stages::StagesTuple,
state::HasExecutions,
utils::{current_milliseconds, current_time},
Error,
};
use core::marker::PhantomData;
/// Holds a set of stages
pub trait HasStages<ST, E, EM, I, S>
where
ST: StagesTuple<E, EM, I, S>,
E: Executor<I>,
EM: EventManager<I, S>,
I: Input,
{
fn stages(&self) -> &ST;
fn stages_mut(&mut self) -> &mut ST;
}
/// Holds a scheduler
pub trait HasCorpusScheduler<CS, I, S>
where
CS: CorpusScheduler<I, S>,
I: Input,
{
fn scheduler(&self) -> &CS;
fn scheduler_mut(&mut self) -> &mut CS;
}
/// The main fuzzer trait.
pub trait Fuzzer<E, EM, S> {
fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error>;
fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error>;
}
/// Your default fuzzer instance, for everyday use.
#[derive(Clone, Debug)]
pub struct StdFuzzer<CS, ST, E, EM, I, OT, S>
where
CS: CorpusScheduler<I, S>,
ST: StagesTuple<E, EM, I, S>,
E: Executor<I>,
EM: EventManager<I, S>,
I: Input,
{
scheduler: CS,
stages: ST,
phantom: PhantomData<(E, EM, I, OT, S)>,
}
impl<CS, ST, E, EM, I, OT, S> HasStages<ST, E, EM, I, S> for StdFuzzer<CS, ST, E, EM, I, OT, S>
where
CS: CorpusScheduler<I, S>,
ST: StagesTuple<E, EM, I, S>,
E: Executor<I>,
EM: EventManager<I, S>,
I: Input,
{
fn stages(&self) -> &ST {
&self.stages
}
fn stages_mut(&mut self) -> &mut ST {
&mut self.stages
}
}
impl<CS, ST, E, EM, I, OT, S> HasCorpusScheduler<CS, I, S> for StdFuzzer<CS, ST, E, EM, I, OT, S>
where
CS: CorpusScheduler<I, S>,
ST: StagesTuple<E, EM, I, S>,
E: Executor<I>,
EM: EventManager<I, S>,
I: Input,
{
fn scheduler(&self) -> &CS {
&self.scheduler
}
fn scheduler_mut(&mut self) -> &mut CS {
&mut self.scheduler
}
}
impl<CS, ST, E, EM, I, OT, S> Fuzzer<E, EM, S> for StdFuzzer<CS, ST, E, EM, I, OT, S>
where
CS: CorpusScheduler<I, S>,
S: HasExecutions,
ST: StagesTuple<E, EM, I, S>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
I: Input,
{
fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error> {
let idx = self.scheduler().next(state)?;
self.stages().perform_all(state, executor, manager, idx)?;
manager.process(state, executor)?;
Ok(idx)
}
fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error> {
let mut last = current_milliseconds();
loop {
self.fuzz_one(state, executor, manager)?;
let cur = current_milliseconds();
if cur - last > 60 * 100 {
last = cur;
manager.fire(
state,
Event::UpdateStats {
executions: *state.executions(),
time: current_time(),
phantom: PhantomData,
},
)?
}
}
}
}
impl<CS, ST, E, EM, I, OT, S> StdFuzzer<CS, ST, E, EM, I, OT, S>
where
CS: CorpusScheduler<I, S>,
ST: StagesTuple<E, EM, I, S>,
E: Executor<I>,
EM: EventManager<I, S>,
I: Input,
{
pub fn new(scheduler: CS, stages: ST) -> Self {
Self {
scheduler: scheduler,
stages: stages,
phantom: PhantomData,
}
}
}

View File

@ -5,7 +5,7 @@ use alloc::{borrow::ToOwned, rc::Rc, vec::Vec};
use core::{cell::RefCell, convert::From}; use core::{cell::RefCell, convert::From};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::inputs::{HasBytesVec, HasTargetBytes, HasLen, Input, TargetBytes}; use crate::inputs::{HasBytesVec, HasLen, HasTargetBytes, Input, TargetBytes};
/// A bytes input is the basic input /// A bytes input is the basic input
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)] #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]

View File

@ -106,4 +106,3 @@ pub trait HasLen {
/// The lenght /// The lenght
fn len(&self) -> usize; fn len(&self) -> usize;
} }

View File

@ -25,144 +25,15 @@ pub mod state;
pub mod stats; pub mod stats;
pub mod utils; pub mod utils;
pub mod fuzzer;
pub use fuzzer::*;
use alloc::string::String; use alloc::string::String;
use core::{fmt, marker::PhantomData}; use core::fmt;
use corpus::Corpus;
use events::{Event, EventManager};
use executors::{Executor, HasObservers};
use feedbacks::FeedbacksTuple;
use inputs::Input;
use observers::ObserversTuple;
use stages::StagesTuple;
use state::{HasCorpus, State};
use utils::{current_milliseconds, current_time, Rand};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::{env::VarError, io, num::ParseIntError, string::FromUtf8Error}; use std::{env::VarError, io, num::ParseIntError, string::FromUtf8Error};
/// The main fuzzer trait.
pub trait Fuzzer<C, E, EM, FT, ST, I, OC, OFT, OT, R>
where
ST: StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R>,
EM: EventManager<I>,
E: Executor<I> + HasObservers<OT>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input,
R: Rand,
{
fn stages(&self) -> &ST;
fn stages_mut(&mut self) -> &mut ST;
fn fuzz_one(
&mut self,
rand: &mut R,
executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
manager: &mut EM,
) -> Result<usize, Error> {
let (_, idx) = state.corpus_mut().next(rand)?;
self.stages_mut()
.perform_all(rand, executor, state, manager, idx)?;
manager.process(state, executor)?;
Ok(idx)
}
fn fuzz_loop(
&mut self,
rand: &mut R,
executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
manager: &mut EM,
) -> Result<(), Error> {
let mut last = current_milliseconds();
loop {
self.fuzz_one(rand, executor, state, manager)?;
let cur = current_milliseconds();
if cur - last > 60 * 100 {
last = cur;
manager.fire(
state,
Event::UpdateStats {
executions: state.executions(),
time: current_time(),
phantom: PhantomData,
},
)?
}
}
}
}
/// Your default fuzzer instance, for everyday use.
#[derive(Clone, Debug)]
pub struct StdFuzzer<C, E, EM, FT, ST, I, OC, OFT, OT, R>
where
ST: StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R>,
EM: EventManager<I>,
E: Executor<I> + HasObservers<OT>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input,
R: Rand,
{
stages: ST,
phantom: PhantomData<(EM, E, OC, OFT, OT, FT, C, I, R)>,
}
impl<C, E, EM, FT, ST, I, OC, OFT, OT, R> Fuzzer<C, E, EM, FT, ST, I, OC, OFT, OT, R>
for StdFuzzer<C, E, EM, FT, ST, I, OC, OFT, OT, R>
where
ST: StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R>,
EM: EventManager<I>,
E: Executor<I> + HasObservers<OT>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input,
R: Rand,
{
fn stages(&self) -> &ST {
&self.stages
}
fn stages_mut(&mut self) -> &mut ST {
&mut self.stages
}
}
impl<C, E, EM, FT, ST, I, OC, OFT, OT, R> StdFuzzer<C, E, EM, FT, ST, I, OC, OFT, OT, R>
where
ST: StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R>,
EM: EventManager<I>,
E: Executor<I> + HasObservers<OT>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input,
R: Rand,
{
pub fn new(stages: ST) -> Self {
Self {
stages: stages,
phantom: PhantomData,
}
}
}
/// Main error struct for AFL /// Main error struct for AFL
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -245,6 +116,7 @@ impl From<ParseIntError> for Error {
} }
} }
/*
// TODO: no_std test // TODO: no_std test
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg(test)] #[cfg(test)]
@ -327,3 +199,4 @@ mod tests {
assert_eq!(state.corpus().count(), corpus_deserialized.count()); assert_eq!(state.corpus().count(), corpus_deserialized.count());
} }
} }
*/

View File

@ -7,38 +7,23 @@ pub use mutations::*;
pub mod token_mutations; pub mod token_mutations;
pub use token_mutations::*; pub use token_mutations::*;
use crate::{ use crate::{inputs::Input, Error};
corpus::Corpus,
inputs::Input,
state::{HasCorpus, HasMetadata},
utils::Rand,
Error,
};
// TODO mutator stats method that produces something that can be sent with the NewTestcase event // TODO mutator stats method that produces something that can be sent with the NewTestcase event
// We can use it to report which mutations generated the testcase in the broker logs // We can use it to report which mutations generated the testcase in the broker logs
/// A mutator takes input, and mutates it. /// A mutator takes input, and mutates it.
/// Simple as that. /// Simple as that.
pub trait Mutator<C, I, R, S> pub trait Mutator<I, S>
where where
C: Corpus<I, R>,
I: Input, I: Input,
R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
/// Mutate a given input /// Mutate a given input
fn mutate( fn mutate(&self, state: &mut S, input: &mut I, stage_idx: i32) -> Result<(), Error>;
&mut self,
rand: &mut R,
state: &mut S,
input: &mut I,
stage_idx: i32,
) -> Result<(), Error>;
/// Post-process given the outcome of the execution /// Post-process given the outcome of the execution
fn post_exec( fn post_exec(
&mut self, &self,
_state: &mut S, _state: &mut S,
_is_interesting: u32, _is_interesting: u32,
_stage_idx: i32, _stage_idx: i32,

View File

@ -1,7 +1,8 @@
use crate::{ use crate::{
corpus::Corpus,
inputs::{HasBytesVec, Input}, inputs::{HasBytesVec, Input},
mutators::Corpus,
mutators::*, mutators::*,
state::{HasCorpus, HasRand},
utils::Rand, utils::Rand,
Error, Error,
}; };
@ -15,42 +16,6 @@ use std::{
io::{BufRead, BufReader}, io::{BufRead, BufReader},
}; };
const ARITH_MAX: u64 = 35;
const INTERESTING_8: [i8; 9] = [-128, -1, 0, 1, 16, 32, 64, 100, 127];
const INTERESTING_16: [i16; 19] = [
-128, -1, 0, 1, 16, 32, 64, 100, 127, -32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767,
];
const INTERESTING_32: [i32; 27] = [
-128,
-1,
0,
1,
16,
32,
64,
100,
127,
-32768,
-129,
128,
255,
256,
512,
1000,
1024,
4096,
32767,
-2147483648,
-100663046,
-32769,
32768,
65535,
65536,
100663045,
2147483647,
];
/// The result of a mutation. /// The result of a mutation.
/// If the mutation got skipped, the target /// If the mutation got skipped, the target
/// will not be executed with the returned input. /// will not be executed with the returned input.
@ -62,24 +27,20 @@ pub enum MutationResult {
// TODO maybe the mutator arg is not needed // TODO maybe the mutator arg is not needed
/// The generic function type that identifies mutations /// The generic function type that identifies mutations
pub type MutationFunction<I, M, R, S> = pub type MutationFunction<I, M, S> = fn(&M, &mut S, &mut I) -> Result<MutationResult, Error>;
fn(&mut M, &mut R, &mut S, &mut I) -> Result<MutationResult, Error>;
pub trait ComposedByMutations<C, I, R, S> pub trait ComposedByMutations<I, S>
where where
C: Corpus<I, R>,
I: Input, I: Input,
R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
/// Get a mutation by index /// Get a mutation by index
fn mutation_by_idx(&self, index: usize) -> MutationFunction<I, Self, R, S>; fn mutation_by_idx(&self, index: usize) -> MutationFunction<I, Self, S>;
/// Get the number of mutations /// Get the number of mutations
fn mutations_count(&self) -> usize; fn mutations_count(&self) -> usize;
/// Add a mutation /// Add a mutation
fn add_mutation(&mut self, mutation: MutationFunction<I, Self, R, S>); fn add_mutation(&mut self, mutation: MutationFunction<I, Self, S>);
} }
/// Mem move in the own vec /// Mem move in the own vec
@ -125,21 +86,58 @@ fn buffer_set(data: &mut [u8], from: usize, len: usize, val: u8) {
} }
} }
const ARITH_MAX: u64 = 35;
const INTERESTING_8: [i8; 9] = [-128, -1, 0, 1, 16, 32, 64, 100, 127];
const INTERESTING_16: [i16; 19] = [
-128, -1, 0, 1, 16, 32, 64, 100, 127, -32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767,
];
const INTERESTING_32: [i32; 27] = [
-128,
-1,
0,
1,
16,
32,
64,
100,
127,
-32768,
-129,
128,
255,
256,
512,
1000,
1024,
4096,
32767,
-2147483648,
-100663046,
-32769,
32768,
65535,
65536,
100663045,
2147483647,
];
/// Bitflip mutation for inputs with a bytes vector /// Bitflip mutation for inputs with a bytes vector
pub fn mutation_bitflip<I, M, R, S>( pub fn mutation_bitflip<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().len() == 0 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let bit = rand.below((input.bytes().len() << 3) as u64) as usize; let bit = state.rand_mut().below((input.bytes().len() << 3) as u64) as usize;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(bit >> 3) ^= (128 >> (bit & 7)) as u8; *input.bytes_mut().get_unchecked_mut(bit >> 3) ^= (128 >> (bit & 7)) as u8;
@ -149,19 +147,20 @@ where
} }
pub fn mutation_byteflip<I, M, R, S>( pub fn mutation_byteflip<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().len() == 0 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) ^= 0xff; *input.bytes_mut().get_unchecked_mut(idx) ^= 0xff;
@ -171,19 +170,20 @@ where
} }
pub fn mutation_byteinc<I, M, R, S>( pub fn mutation_byteinc<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().len() == 0 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
let ptr = input.bytes_mut().get_unchecked_mut(idx); let ptr = input.bytes_mut().get_unchecked_mut(idx);
@ -194,19 +194,20 @@ where
} }
pub fn mutation_bytedec<I, M, R, S>( pub fn mutation_bytedec<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().len() == 0 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
let ptr = input.bytes_mut().get_unchecked_mut(idx); let ptr = input.bytes_mut().get_unchecked_mut(idx);
@ -217,19 +218,20 @@ where
} }
pub fn mutation_byteneg<I, M, R, S>( pub fn mutation_byteneg<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().len() == 0 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) = !(*input.bytes().get_unchecked(idx)); *input.bytes_mut().get_unchecked_mut(idx) = !(*input.bytes().get_unchecked(idx));
@ -239,46 +241,48 @@ where
} }
pub fn mutation_byterand<I, M, R, S>( pub fn mutation_byterand<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().len() == 0 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) = rand.below(256) as u8; *input.bytes_mut().get_unchecked_mut(idx) = state.rand_mut().below(256) as u8;
} }
Ok(MutationResult::Mutated) Ok(MutationResult::Mutated)
} }
} }
pub fn mutation_byteadd<I, M, R, S>( pub fn mutation_byteadd<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().len() == 0 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut u8; let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut u8;
let num = 1 + rand.below(ARITH_MAX) as u8; let num = 1 + state.rand_mut().below(ARITH_MAX) as u8;
match rand.below(2) { match state.rand_mut().below(2) {
0 => *ptr = (*ptr).wrapping_add(num), 0 => *ptr = (*ptr).wrapping_add(num),
_ => *ptr = (*ptr).wrapping_sub(num), _ => *ptr = (*ptr).wrapping_sub(num),
}; };
@ -288,24 +292,25 @@ where
} }
pub fn mutation_wordadd<I, M, R, S>( pub fn mutation_wordadd<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() < 2 { if input.bytes().len() < 2 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64 - 1) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16; let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16;
let num = 1 + rand.below(ARITH_MAX) as u16; let num = 1 + state.rand_mut().below(ARITH_MAX) as u16;
match rand.below(4) { match state.rand_mut().below(4) {
0 => *ptr = (*ptr).wrapping_add(num), 0 => *ptr = (*ptr).wrapping_add(num),
1 => *ptr = (*ptr).wrapping_sub(num), 1 => *ptr = (*ptr).wrapping_sub(num),
2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(),
@ -317,24 +322,25 @@ where
} }
pub fn mutation_dwordadd<I, M, R, S>( pub fn mutation_dwordadd<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() < 4 { if input.bytes().len() < 4 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64 - 3) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32; let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32;
let num = 1 + rand.below(ARITH_MAX) as u32; let num = 1 + state.rand_mut().below(ARITH_MAX) as u32;
match rand.below(4) { match state.rand_mut().below(4) {
0 => *ptr = (*ptr).wrapping_add(num), 0 => *ptr = (*ptr).wrapping_add(num),
1 => *ptr = (*ptr).wrapping_sub(num), 1 => *ptr = (*ptr).wrapping_sub(num),
2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(),
@ -346,24 +352,25 @@ where
} }
pub fn mutation_qwordadd<I, M, R, S>( pub fn mutation_qwordadd<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() < 8 { if input.bytes().len() < 8 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64 - 7) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64 - 7) as usize;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u64; let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u64;
let num = 1 + rand.below(ARITH_MAX) as u64; let num = 1 + state.rand_mut().below(ARITH_MAX) as u64;
match rand.below(4) { match state.rand_mut().below(4) {
0 => *ptr = (*ptr).wrapping_add(num), 0 => *ptr = (*ptr).wrapping_add(num),
1 => *ptr = (*ptr).wrapping_sub(num), 1 => *ptr = (*ptr).wrapping_sub(num),
2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(),
@ -375,20 +382,21 @@ where
} }
pub fn mutation_byteinteresting<I, M, R, S>( pub fn mutation_byteinteresting<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().len() == 0 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
let val = INTERESTING_8[rand.below(INTERESTING_8.len() as u64) as usize] as u8; let val = INTERESTING_8[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u8;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
*input.bytes_mut().get_unchecked_mut(idx) = val; *input.bytes_mut().get_unchecked_mut(idx) = val;
@ -398,24 +406,26 @@ where
} }
pub fn mutation_wordinteresting<I, M, R, S>( pub fn mutation_wordinteresting<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() < 2 { if input.bytes().len() < 2 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64 - 1) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize;
let val = INTERESTING_16[rand.below(INTERESTING_8.len() as u64) as usize] as u16; let val =
INTERESTING_16[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u16;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16; let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16;
if rand.below(2) == 0 { if state.rand_mut().below(2) == 0 {
*ptr = val; *ptr = val;
} else { } else {
*ptr = val.swap_bytes(); *ptr = val.swap_bytes();
@ -426,24 +436,26 @@ where
} }
pub fn mutation_dwordinteresting<I, M, R, S>( pub fn mutation_dwordinteresting<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() < 4 { if input.bytes().len() < 4 {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = rand.below(input.bytes().len() as u64 - 3) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize;
let val = INTERESTING_32[rand.below(INTERESTING_8.len() as u64) as usize] as u32; let val =
INTERESTING_32[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u32;
unsafe { unsafe {
// Moar speed, no bound check // Moar speed, no bound check
let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32; let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32;
if rand.below(2) == 0 { if state.rand_mut().below(2) == 0 {
*ptr = val; *ptr = val;
} else { } else {
*ptr = val.swap_bytes(); *ptr = val.swap_bytes();
@ -454,13 +466,14 @@ where
} }
pub fn mutation_bytesdelete<I, M, R, S>( pub fn mutation_bytesdelete<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
@ -468,28 +481,28 @@ where
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let off = rand.below(size as u64) as usize; let off = state.rand_mut().below(size as u64) as usize;
let len = rand.below((size - off) as u64) as usize; let len = state.rand_mut().below((size - off) as u64) as usize;
input.bytes_mut().drain(off..off + len); input.bytes_mut().drain(off..off + len);
Ok(MutationResult::Mutated) Ok(MutationResult::Mutated)
} }
pub fn mutation_bytesexpand<I, M, R, S>( pub fn mutation_bytesexpand<I, M, R, S>(
// TODO: max_size instead of mutator? mutator: &M,
mutator: &mut M,
rand: &mut R, state: &mut S,
_: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
M: HasMaxSize, M: HasMaxSize,
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
let off = rand.below((size + 1) as u64) as usize; let off = state.rand_mut().below((size + 1) as u64) as usize;
let mut len = 1 + rand.below(16) as usize; let mut len = 1 + state.rand_mut().below(16) as usize;
if size + len > mutator.max_size() { if size + len > mutator.max_size() {
if mutator.max_size() > size { if mutator.max_size() > size {
@ -506,19 +519,20 @@ where
} }
pub fn mutation_bytesinsert<I, M, R, S>( pub fn mutation_bytesinsert<I, M, R, S>(
mutator: &mut M, mutator: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
M: HasMaxSize, M: HasMaxSize,
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
let off = rand.below((size + 1) as u64) as usize; let off = state.rand_mut().below((size + 1) as u64) as usize;
let mut len = 1 + rand.below(16) as usize; let mut len = 1 + state.rand_mut().below(16) as usize;
if size + len > mutator.max_size() { if size + len > mutator.max_size() {
if mutator.max_size() > size { if mutator.max_size() > size {
@ -528,7 +542,7 @@ where
} }
} }
let val = input.bytes()[rand.below(size as u64) as usize]; let val = input.bytes()[state.rand_mut().below(size as u64) as usize];
input.bytes_mut().resize(size + len, 0); input.bytes_mut().resize(size + len, 0);
buffer_self_copy(input.bytes_mut(), off, off + len, size - off); buffer_self_copy(input.bytes_mut(), off, off + len, size - off);
@ -538,19 +552,20 @@ where
} }
pub fn mutation_bytesrandinsert<I, M, R, S>( pub fn mutation_bytesrandinsert<I, M, R, S>(
mutator: &mut M, mutator: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
M: HasMaxSize, M: HasMaxSize,
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
let off = rand.below((size + 1) as u64) as usize; let off = state.rand_mut().below((size + 1) as u64) as usize;
let mut len = 1 + rand.below(16) as usize; let mut len = 1 + state.rand_mut().below(16) as usize;
if size + len > mutator.max_size() { if size + len > mutator.max_size() {
if mutator.max_size() > size { if mutator.max_size() > size {
@ -560,7 +575,7 @@ where
} }
} }
let val = rand.below(256) as u8; let val = state.rand_mut().below(256) as u8;
input.bytes_mut().resize(size + len, 0); input.bytes_mut().resize(size + len, 0);
buffer_self_copy(input.bytes_mut(), off, off + len, size - off); buffer_self_copy(input.bytes_mut(), off, off + len, size - off);
@ -570,23 +585,24 @@ where
} }
pub fn mutation_bytesset<I, M, R, S>( pub fn mutation_bytesset<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
if size == 0 { if size == 0 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let off = rand.below(size as u64) as usize; let off = state.rand_mut().below(size as u64) as usize;
let len = 1 + rand.below(min(16, size - off) as u64) as usize; let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize;
let val = input.bytes()[rand.below(size as u64) as usize]; let val = input.bytes()[state.rand_mut().below(size as u64) as usize];
buffer_set(input.bytes_mut(), off, len, val); buffer_set(input.bytes_mut(), off, len, val);
@ -594,23 +610,24 @@ where
} }
pub fn mutation_bytesrandset<I, M, R, S>( pub fn mutation_bytesrandset<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
if size == 0 { if size == 0 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let off = rand.below(size as u64) as usize; let off = state.rand_mut().below(size as u64) as usize;
let len = 1 + rand.below(min(16, size - off) as u64) as usize; let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize;
let val = rand.below(256) as u8; let val = state.rand_mut().below(256) as u8;
buffer_set(input.bytes_mut(), off, len, val); buffer_set(input.bytes_mut(), off, len, val);
@ -618,13 +635,14 @@ where
} }
pub fn mutation_bytescopy<I, M, R, S>( pub fn mutation_bytescopy<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
@ -632,9 +650,9 @@ where
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let from = rand.below(input.bytes().len() as u64) as usize; let from = state.rand_mut().below(input.bytes().len() as u64) as usize;
let to = rand.below(input.bytes().len() as u64) as usize; let to = state.rand_mut().below(input.bytes().len() as u64) as usize;
let len = 1 + rand.below((size - max(from, to)) as u64) as usize; let len = 1 + state.rand_mut().below((size - max(from, to)) as u64) as usize;
buffer_self_copy(input.bytes_mut(), from, to, len); buffer_self_copy(input.bytes_mut(), from, to, len);
@ -642,13 +660,14 @@ where
} }
pub fn mutation_bytesswap<I, M, R, S>( pub fn mutation_bytesswap<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
_: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R>,
R: Rand, R: Rand,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
@ -656,9 +675,9 @@ where
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let first = rand.below(input.bytes().len() as u64) as usize; let first = state.rand_mut().below(input.bytes().len() as u64) as usize;
let second = rand.below(input.bytes().len() as u64) as usize; let second = state.rand_mut().below(input.bytes().len() as u64) as usize;
let len = 1 + rand.below((size - max(first, second)) as u64) as usize; let len = 1 + state.rand_mut().below((size - max(first, second)) as u64) as usize;
let tmp = input.bytes()[first..(first + len)].to_vec(); let tmp = input.bytes()[first..(first + len)].to_vec();
buffer_self_copy(input.bytes_mut(), second, first, len); buffer_self_copy(input.bytes_mut(), second, first, len);
@ -669,37 +688,46 @@ where
/// Crossover insert mutation /// Crossover insert mutation
pub fn mutation_crossover_insert<C, I, M, R, S>( pub fn mutation_crossover_insert<C, I, M, R, S>(
mutator: &mut M, mutator: &M,
rand: &mut R,
state: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
M: HasMaxSize, M: HasMaxSize,
C: Corpus<I, R>, C: Corpus<I>,
I: Input + HasBytesVec, I: Input + HasBytesVec,
R: Rand, R: Rand,
S: HasCorpus<C, I, R>, S: HasRand<R> + HasCorpus<C, I>,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
// We don't want to use the testcase we're already using for splicing // We don't want to use the testcase we're already using for splicing
let (other_testcase, idx) = state.corpus().random_entry(rand)?; let count = state.corpus().count();
if idx == state.corpus().current_testcase().1 { let idx = state.rand_mut().below(count as u64) as usize;
return Ok(MutationResult::Skipped); if let Some(cur) = state.corpus().current() {
if idx == *cur {
return Ok(MutationResult::Skipped);
}
} }
let mut other_ref = other_testcase.borrow_mut(); let other_size = state
let other = other_ref.load_input()?; .corpus()
.get(idx)?
let other_size = other.bytes().len(); .borrow_mut()
.load_input()?
.bytes()
.len();
if other_size < 2 { if other_size < 2 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let from = rand.below(other_size as u64) as usize; let from = state.rand_mut().below(other_size as u64) as usize;
let to = rand.below(size as u64) as usize; let to = state.rand_mut().below(size as u64) as usize;
let mut len = rand.below((other_size - from) as u64) as usize; let mut len = state.rand_mut().below((other_size - from) as u64) as usize;
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?;
if size + len > mutator.max_size() { if size + len > mutator.max_size() {
if mutator.max_size() > size { if mutator.max_size() > size {
@ -718,36 +746,45 @@ where
/// Crossover replace mutation /// Crossover replace mutation
pub fn mutation_crossover_replace<C, I, M, R, S>( pub fn mutation_crossover_replace<C, I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
state: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
C: Corpus<I, R>, C: Corpus<I>,
I: Input + HasBytesVec, I: Input + HasBytesVec,
R: Rand, R: Rand,
S: HasCorpus<C, I, R>, S: HasRand<R> + HasCorpus<C, I>,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
// We don't want to use the testcase we're already using for splicing // We don't want to use the testcase we're already using for splicing
let (other_testcase, idx) = state.corpus().random_entry(rand)?; let count = state.corpus().count();
if idx == state.corpus().current_testcase().1 { let idx = state.rand_mut().below(count as u64) as usize;
return Ok(MutationResult::Skipped); if let Some(cur) = state.corpus().current() {
if idx == *cur {
return Ok(MutationResult::Skipped);
}
} }
let mut other_ref = other_testcase.borrow_mut(); let other_size = state
let other = other_ref.load_input()?; .corpus()
.get(idx)?
let other_size = other.bytes().len(); .borrow_mut()
.load_input()?
.bytes()
.len();
if other_size < 2 { if other_size < 2 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let from = rand.below(other_size as u64) as usize; let from = state.rand_mut().below(other_size as u64) as usize;
let len = rand.below(min(other_size - from, size) as u64) as usize; let len = state.rand_mut().below(min(other_size - from, size) as u64) as usize;
let to = rand.below((size - len) as u64) as usize; let to = state.rand_mut().below((size - len) as u64) as usize;
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?;
buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); buffer_copy(input.bytes_mut(), other.bytes(), from, to, len);
@ -772,40 +809,50 @@ fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) {
/// Splicing mutation from AFL /// Splicing mutation from AFL
pub fn mutation_splice<C, I, M, R, S>( pub fn mutation_splice<C, I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
state: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
C: Corpus<I, R>, C: Corpus<I>,
I: Input + HasBytesVec, I: Input + HasBytesVec,
R: Rand, R: Rand,
S: HasCorpus<C, I, R>, S: HasRand<R> + HasCorpus<C, I>,
{ {
// We don't want to use the testcase we're already using for splicing // We don't want to use the testcase we're already using for splicing
let (other_testcase, idx) = state.corpus().random_entry(rand)?; let count = state.corpus().count();
if idx == state.corpus().current_testcase().1 { let idx = state.rand_mut().below(count as u64) as usize;
return Ok(MutationResult::Skipped); if let Some(cur) = state.corpus().current() {
} if idx == *cur {
let mut other_ref = other_testcase.borrow_mut();
let other = other_ref.load_input()?;
let mut counter = 0;
let (first_diff, last_diff) = loop {
let (f, l) = locate_diffs(input.bytes(), other.bytes());
if f != l && f >= 0 && l >= 2 {
break (f, l);
}
if counter == 3 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
counter += 1; }
let (first_diff, last_diff) = {
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?;
let mut counter = 0;
loop {
let (f, l) = locate_diffs(input.bytes(), other.bytes());
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; let split_at = state
.rand_mut()
.between(first_diff as u64, last_diff as u64) as usize;
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?;
input input
.bytes_mut() .bytes_mut()
.splice(split_at.., other.bytes()[split_at..].iter().cloned()); .splice(split_at.., other.bytes()[split_at..].iter().cloned());
@ -923,6 +970,7 @@ pub fn read_tokens_file(f: &str, tokens: &mut Vec<Vec<u8>>) -> Result<u32, Error
Ok(entries) Ok(entries)
} }
/*
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -1039,3 +1087,4 @@ token2="B"
*/ */
} }
} }
*/

View File

@ -1,72 +1,58 @@
use crate::inputs::HasBytesVec;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::{fmt, default::Default, marker::PhantomData}; use core::{default::Default, fmt, marker::PhantomData};
use fmt::Debug; use fmt::Debug;
use crate::{ use crate::{
inputs::{HasBytesVec, Input}, corpus::Corpus,
mutators::{Corpus, *}, inputs::Input,
state::{HasCorpus, HasMetadata}, mutators::{HasMaxSize, Mutator, DEFAULT_MAX_SIZE},
state::{HasCorpus, HasMetadata, HasRand},
utils::Rand, utils::Rand,
Error, Error,
}; };
pub trait ScheduledMutator<C, I, R, S>: pub use crate::mutators::mutations::*;
Mutator<C, I, R, S> + ComposedByMutations<C, I, R, S> pub use crate::mutators::token_mutations::*;
pub trait ScheduledMutator<I, S>: Mutator<I, S> + ComposedByMutations<I, S>
where where
C: Corpus<I, R>,
I: Input, I: Input,
R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
/// Compute the number of iterations used to apply stacked mutations /// Compute the number of iterations used to apply stacked mutations
#[inline] fn iterations(&self, state: &mut S, input: &I) -> u64;
fn iterations(&mut self, rand: &mut R, _input: &I) -> u64 {
1 << (1 + rand.below(6))
}
/// Get the next mutation to apply /// Get the next mutation to apply
#[inline] fn schedule(&self, mutations_count: usize, state: &mut S, input: &I) -> usize;
fn schedule(&mut self, mutations_count: usize, rand: &mut R, _input: &I) -> usize {
debug_assert!(mutations_count > 0);
rand.below(mutations_count as u64) as usize
}
/// New default implementation for mutate /// New default implementation for mutate
/// Implementations must forward mutate() to this method /// Implementations must forward mutate() to this method
fn scheduled_mutate( fn scheduled_mutate(&self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<(), Error> {
&mut self, let num = self.iterations(state, input);
rand: &mut R,
state: &mut S,
input: &mut I,
_stage_idx: i32,
) -> Result<(), Error> {
let num = self.iterations(rand, input);
for _ in 0..num { for _ in 0..num {
let idx = self.schedule(self.mutations_count(), rand, input); let idx = self.schedule(self.mutations_count(), state, input);
self.mutation_by_idx(idx)(self, rand, state, input)?; self.mutation_by_idx(idx)(self, state, input)?;
} }
Ok(()) Ok(())
} }
} }
#[derive(Clone)] pub struct StdScheduledMutator<I, R, S>
pub struct StdScheduledMutator<C, I, R, S>
where where
C: Corpus<I, R>,
I: Input, I: Input,
S: HasRand<R>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
mutations: Vec<MutationFunction<I, Self, R, S>>, mutations: Vec<MutationFunction<I, Self, S>>,
max_size: usize, max_size: usize,
phantom: PhantomData<R>,
} }
impl<C, I, R, S> Debug for StdScheduledMutator<C, I, R, S> impl<I, R, S> Debug for StdScheduledMutator<I, R, S>
where where
C: Corpus<I, R>,
I: Input, I: Input,
S: HasRand<R>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
@ -79,33 +65,25 @@ where
} }
} }
impl<C, I, R, S> Mutator<C, I, R, S> for StdScheduledMutator<C, I, R, S> impl<I, R, S> Mutator<I, S> for StdScheduledMutator<I, R, S>
where where
C: Corpus<I, R>,
I: Input, I: Input,
S: HasRand<R>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
fn mutate( fn mutate(&self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<(), Error> {
&mut self, self.scheduled_mutate(state, input, _stage_idx)
rand: &mut R,
state: &mut S,
input: &mut I,
_stage_idx: i32,
) -> Result<(), Error> {
self.scheduled_mutate(rand, state, input, _stage_idx)
} }
} }
impl<C, I, R, S> ComposedByMutations<C, I, R, S> for StdScheduledMutator<C, I, R, S> impl<I, R, S> ComposedByMutations<I, S> for StdScheduledMutator<I, R, S>
where where
C: Corpus<I, R>,
I: Input, I: Input,
S: HasRand<R>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
#[inline] #[inline]
fn mutation_by_idx(&self, index: usize) -> MutationFunction<I, Self, R, S> { fn mutation_by_idx(&self, index: usize) -> MutationFunction<I, Self, S> {
self.mutations[index] self.mutations[index]
} }
@ -115,27 +93,34 @@ where
} }
#[inline] #[inline]
fn add_mutation(&mut self, mutation: MutationFunction<I, Self, R, S>) { fn add_mutation(&mut self, mutation: MutationFunction<I, Self, S>) {
self.mutations.push(mutation) self.mutations.push(mutation)
} }
} }
impl<C, I, R, S> ScheduledMutator<C, I, R, S> for StdScheduledMutator<C, I, R, S> impl<I, R, S> ScheduledMutator<I, S> for StdScheduledMutator<I, R, S>
where where
C: Corpus<I, R>,
I: Input, I: Input,
S: HasRand<R>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
// Just use the default methods /// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &I) -> u64 {
1 << (1 + state.rand_mut().below(6))
}
/// Get the next mutation to apply
fn schedule(&self, mutations_count: usize, state: &mut S, _: &I) -> usize {
debug_assert!(mutations_count > 0);
state.rand_mut().below(mutations_count as u64) as usize
}
} }
impl<C, I, R, S> HasMaxSize for StdScheduledMutator<C, I, R, S> impl<I, R, S> HasMaxSize for StdScheduledMutator<I, R, S>
where where
C: Corpus<I, R>,
I: Input, I: Input,
S: HasRand<R>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
#[inline] #[inline]
fn max_size(&self) -> usize { fn max_size(&self) -> usize {
@ -148,64 +133,59 @@ where
} }
} }
impl<C, I, R, S> StdScheduledMutator<C, I, R, S> impl<I, R, S> StdScheduledMutator<I, R, S>
where where
C: Corpus<I, R>,
I: Input, I: Input,
S: HasRand<R>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
/// 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 {
Self { Self {
mutations: vec![], mutations: vec![],
max_size: DEFAULT_MAX_SIZE, max_size: DEFAULT_MAX_SIZE,
phantom: PhantomData,
} }
} }
/// Create a new StdScheduledMutator instance specifying mutations /// Create a new StdScheduledMutator instance specifying mutations
pub fn with_mutations(mutations: Vec<MutationFunction<I, Self, R, S>>) -> Self { pub fn with_mutations(mutations: Vec<MutationFunction<I, Self, S>>) -> Self {
StdScheduledMutator { StdScheduledMutator {
mutations: mutations, mutations: mutations,
max_size: DEFAULT_MAX_SIZE, max_size: DEFAULT_MAX_SIZE,
phantom: PhantomData,
} }
} }
} }
#[derive(Clone, Debug)]
/// 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, S> #[derive(Clone, Debug)]
pub struct HavocBytesMutator<C, I, R, S, SM>
where where
SM: ScheduledMutator<C, I, R, S> + HasMaxSize, SM: ScheduledMutator<I, S> + HasMaxSize,
C: Corpus<I, R>,
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R> + HasCorpus<C, I> + HasMetadata,
C: Corpus<I>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
scheduled: SM, scheduled: SM,
phantom: PhantomData<(C, I, R, S)>, phantom: PhantomData<(C, I, R, S)>,
} }
impl<SM, C, I, R, S> Mutator<C, I, R, S> for HavocBytesMutator<SM, C, I, R, S> impl<C, I, R, S, SM> Mutator<I, S> for HavocBytesMutator<C, I, R, S, SM>
where where
SM: ScheduledMutator<C, I, R, S> + HasMaxSize, SM: ScheduledMutator<I, S> + HasMaxSize,
C: Corpus<I, R>,
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R> + HasCorpus<C, I> + HasMetadata,
C: Corpus<I>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
/// Mutate bytes /// Mutate bytes
fn mutate( fn mutate(&self, state: &mut S, input: &mut I, stage_idx: i32) -> Result<(), Error> {
&mut self, self.scheduled.mutate(state, input, stage_idx)?;
rand: &mut R, /*let num = self.scheduled.iterations(state, input);
state: &mut S,
input: &mut I,
stage_idx: i32,
) -> Result<(), Error> {
self.scheduled.mutate(rand, state, input, stage_idx)?;
/*let num = self.scheduled.iterations(rand, input);
for _ in 0..num { for _ in 0..num {
let idx = self.scheduled.schedule(14, rand, input); let idx = self.scheduled.schedule(14, state, input);
let mutation = match idx { let mutation = match idx {
0 => mutation_bitflip, 0 => mutation_bitflip,
1 => mutation_byteflip, 1 => mutation_byteflip,
@ -222,19 +202,19 @@ where
11 => mutation_dwordinteresting, 11 => mutation_dwordinteresting,
_ => mutation_splice, _ => mutation_splice,
}; };
mutation(self, rand, state, input)?; mutation(self, state, input)?;
}*/ }*/
Ok(()) Ok(())
} }
} }
impl<SM, C, I, R, S> HasMaxSize for HavocBytesMutator<SM, C, I, R, S> impl<C, I, R, S, SM> HasMaxSize for HavocBytesMutator<C, I, R, S, SM>
where where
SM: ScheduledMutator<C, I, R, S> + HasMaxSize, SM: ScheduledMutator<I, S> + HasMaxSize,
C: Corpus<I, R>,
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R> + HasCorpus<C, I> + HasMetadata,
C: Corpus<I>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
#[inline] #[inline]
fn max_size(&self) -> usize { fn max_size(&self) -> usize {
@ -247,13 +227,13 @@ where
} }
} }
impl<SM, C, I, R, S> HavocBytesMutator<SM, C, I, R, S> impl<C, I, R, S, SM> HavocBytesMutator<C, I, R, S, SM>
where where
SM: ScheduledMutator<C, I, R, S> + HasMaxSize, SM: ScheduledMutator<I, S> + HasMaxSize,
C: Corpus<I, R>,
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R> + HasCorpus<C, I> + HasMetadata,
C: Corpus<I>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
/// Create a new HavocBytesMutator instance given a ScheduledMutator to wrap /// Create a new HavocBytesMutator instance given a ScheduledMutator to wrap
pub fn new(mut scheduled: SM) -> Self { pub fn new(mut scheduled: SM) -> Self {
@ -266,16 +246,16 @@ where
} }
} }
impl<C, I, R, S> Default for HavocBytesMutator<StdScheduledMutator<C, I, R, S>, C, I, R, S> impl<C, I, R, S> Default for HavocBytesMutator<C, I, R, S, StdScheduledMutator<I, R, S>>
where where
C: Corpus<I, R>,
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasRand<R> + HasCorpus<C, I> + HasMetadata,
C: Corpus<I>,
R: Rand, R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{ {
/// Create a new HavocBytesMutator instance wrapping StdScheduledMutator /// Create a new HavocBytesMutator instance wrapping StdScheduledMutator
fn default() -> Self { fn default() -> Self {
let mut scheduled = StdScheduledMutator::<C, I, R, S>::new(); let mut scheduled = StdScheduledMutator::<I, R, S>::new();
scheduled.add_mutation(mutation_bitflip); scheduled.add_mutation(mutation_bitflip);
scheduled.add_mutation(mutation_byteflip); scheduled.add_mutation(mutation_byteflip);
scheduled.add_mutation(mutation_byteinc); scheduled.add_mutation(mutation_byteinc);
@ -317,6 +297,7 @@ where
} }
} }
/*
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
@ -390,3 +371,4 @@ mod tests {
} }
} }
} }
*/

View File

@ -4,6 +4,7 @@
use crate::{ use crate::{
inputs::{HasBytesVec, Input}, inputs::{HasBytesVec, Input},
mutators::*, mutators::*,
state::{HasMetadata, HasRand},
utils::Rand, utils::Rand,
Error, Error,
}; };
@ -30,33 +31,33 @@ impl TokensMetadata {
/// Insert a dictionary token /// Insert a dictionary token
pub fn mutation_tokeninsert<I, M, R, S>( pub fn mutation_tokeninsert<I, M, R, S>(
mutator: &mut M, mutator: &M,
rand: &mut R,
state: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
M: HasMaxSize, M: HasMaxSize,
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasMetadata + HasRand<R>,
R: Rand, R: Rand,
S: HasMetadata,
{ {
let meta; let tokens_len = {
match state.metadata().get::<TokensMetadata>() { let meta = state.metadata().get::<TokensMetadata>();
Some(t) => { if meta.is_none() {
meta = t;
}
None => {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
if meta.unwrap().tokens.len() == 0 {
return Ok(MutationResult::Skipped);
}
meta.unwrap().tokens.len()
}; };
if meta.tokens.len() == 0 { let token_idx = state.rand_mut().below(tokens_len as u64) as usize;
return Ok(MutationResult::Skipped);
}
let token = &meta.tokens[rand.below(meta.tokens.len() as u64) as usize];
let size = input.bytes().len(); let size = input.bytes().len();
let off = rand.below((size + 1) as u64) as usize; let off = state.rand_mut().below((size + 1) as u64) as usize;
let meta = state.metadata().get::<TokensMetadata>().unwrap();
let token = &meta.tokens[token_idx];
let mut len = token.len(); let mut len = token.len();
if size + len > mutator.max_size() { if size + len > mutator.max_size() {
@ -76,37 +77,37 @@ where
/// Overwrite with a dictionary token /// Overwrite with a dictionary token
pub fn mutation_tokenreplace<I, M, R, S>( pub fn mutation_tokenreplace<I, M, R, S>(
_: &mut M, _: &M,
rand: &mut R,
state: &mut S, state: &mut S,
input: &mut I, input: &mut I,
) -> Result<MutationResult, Error> ) -> Result<MutationResult, Error>
where where
M: HasMaxSize,
I: Input + HasBytesVec, I: Input + HasBytesVec,
S: HasMetadata + HasRand<R>,
R: Rand, R: Rand,
S: HasMetadata,
{ {
let size = input.bytes().len(); let size = input.bytes().len();
if size == 0 { if size == 0 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let meta; let tokens_len = {
match state.metadata().get::<TokensMetadata>() { let meta = state.metadata().get::<TokensMetadata>();
Some(t) => { if meta.is_none() {
meta = t;
}
None => {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
if meta.unwrap().tokens.len() == 0 {
return Ok(MutationResult::Skipped);
}
meta.unwrap().tokens.len()
}; };
if meta.tokens.len() == 0 { let token_idx = state.rand_mut().below(tokens_len as u64) as usize;
return Ok(MutationResult::Skipped);
}
let token = &meta.tokens[rand.below(meta.tokens.len() as u64) as usize];
let off = rand.below(size as u64) as usize; let off = state.rand_mut().below(size as u64) as usize;
let meta = state.metadata().get::<TokensMetadata>().unwrap();
let token = &meta.tokens[token_idx];
let mut len = token.len(); let mut len = token.len();
if off + len > size { if off + len > size {
len = size - off; len = size - off;

View File

@ -2,128 +2,69 @@ pub mod mutational;
pub use mutational::StdMutationalStage; pub use mutational::StdMutationalStage;
use crate::{ use crate::{
bolts::tuples::TupleList, bolts::tuples::TupleList, events::EventManager, executors::Executor, inputs::Input, Error,
corpus::Corpus,
events::EventManager,
executors::{Executor, HasObservers},
feedbacks::FeedbacksTuple,
inputs::Input,
observers::ObserversTuple,
state::State,
utils::Rand,
Error,
}; };
/// A stage is one step in the fuzzing process. /// A stage is one step in the fuzzing process.
/// Multiple stages will be scheduled one by one for each input. /// Multiple stages will be scheduled one by one for each input.
pub trait Stage<C, E, EM, FT, I, OC, OFT, OT, R> pub trait Stage<E, EM, I, S>
where where
EM: EventManager<I>, EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>, E: Executor<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input, I: Input,
R: Rand,
{ {
/// Run the stage /// Run the stage
fn perform( fn perform(
&mut self, &self,
rand: &mut R, state: &mut S,
executor: &mut E, executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
manager: &mut EM, manager: &mut EM,
corpus_idx: usize, corpus_idx: usize,
) -> Result<(), Error>; ) -> Result<(), Error>;
} }
pub trait StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R> pub trait StagesTuple<E, EM, I, S>
where where
EM: EventManager<I>, EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>, E: Executor<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input, I: Input,
R: Rand,
{ {
fn perform_all( fn perform_all(
&mut self, &self,
rand: &mut R, state: &mut S,
executor: &mut E, executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
manager: &mut EM, manager: &mut EM,
corpus_idx: usize, corpus_idx: usize,
) -> Result<(), Error>; ) -> Result<(), Error>;
fn for_each(&self, f: fn(&dyn Stage<C, E, EM, FT, I, OC, OFT, OT, R>));
fn for_each_mut(&mut self, f: fn(&mut dyn Stage<C, E, EM, FT, I, OC, OFT, OT, R>));
} }
impl<C, E, EM, FT, I, OC, OFT, OT, R> StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R> for () impl<E, EM, I, S> StagesTuple<E, EM, I, S> for ()
where where
EM: EventManager<I>, EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>, E: Executor<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input, I: Input,
R: Rand,
{ {
fn perform_all( fn perform_all(&self, _: &mut S, _: &mut E, _: &mut EM, _: usize) -> Result<(), Error> {
&mut self,
_rand: &mut R,
_executor: &mut E,
_state: &mut State<C, FT, I, OC, OFT, R>,
_manager: &mut EM,
_corpus_idx: usize,
) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn for_each(&self, _f: fn(&dyn Stage<C, E, EM, FT, I, OC, OFT, OT, R>)) {}
fn for_each_mut(&mut self, _f: fn(&mut dyn Stage<C, E, EM, FT, I, OC, OFT, OT, R>)) {}
} }
impl<Head, Tail, EM, E, OC, OFT, OT, FT, C, I, R> StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R> impl<Head, Tail, E, EM, I, S> StagesTuple<E, EM, I, S> for (Head, Tail)
for (Head, Tail)
where where
Head: Stage<C, E, EM, FT, I, OC, OFT, OT, R>, Head: Stage<E, EM, I, S>,
Tail: StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R> + TupleList, Tail: StagesTuple<E, EM, I, S> + TupleList,
EM: EventManager<I>, EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>, E: Executor<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input, I: Input,
R: Rand,
{ {
fn perform_all( fn perform_all(
&mut self, &self,
rand: &mut R, state: &mut S,
executor: &mut E, executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
manager: &mut EM, manager: &mut EM,
corpus_idx: usize, corpus_idx: usize,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.0.perform(rand, executor, state, manager, corpus_idx)?; self.0.perform(state, executor, manager, corpus_idx)?;
self.1 self.1.perform_all(state, executor, manager, corpus_idx)
.perform_all(rand, executor, state, manager, corpus_idx)
}
fn for_each(&self, f: fn(&dyn Stage<C, E, EM, FT, I, OC, OFT, OT, R>)) {
f(&self.0);
self.1.for_each(f)
}
fn for_each_mut(&mut self, f: fn(&mut dyn Stage<C, E, EM, FT, I, OC, OFT, OT, R>)) {
f(&mut self.0);
self.1.for_each_mut(f)
} }
} }

View File

@ -1,15 +1,14 @@
use core::marker::PhantomData; use core::marker::PhantomData;
use crate::{ use crate::{
corpus::Corpus,
events::EventManager, events::EventManager,
executors::{Executor, HasObservers}, executors::{Executor, HasObservers},
feedbacks::FeedbacksTuple,
inputs::Input, inputs::Input,
mutators::Mutator, mutators::Mutator,
observers::ObserversTuple, observers::ObserversTuple,
stages::Corpus,
stages::Stage, stages::Stage,
state::{HasCorpus, State}, state::{Evaluator, HasCorpus, HasRand},
utils::Rand, utils::Rand,
Error, Error,
}; };
@ -19,19 +18,15 @@ use crate::{
/// A Mutational stage is the stage in a fuzzing run that mutates inputs. /// A Mutational stage is the stage in a fuzzing run that mutates inputs.
/// Mutational stages will usually have a range of mutations that are /// Mutational stages will usually have a range of mutations that are
/// being applied to the input one by one, between executions. /// being applied to the input one by one, between executions.
pub trait MutationalStage<C, E, EM, FT, I, M, OC, OFT, OT, R>: pub trait MutationalStage<C, E, EM, I, M, OT, S>: Stage<E, EM, I, S>
Stage<C, E, EM, FT, I, OC, OFT, OT, R>
where where
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, R>>, M: Mutator<I, S>,
EM: EventManager<I>,
E: Executor<I> + HasObservers<OT>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input, I: Input,
R: Rand, S: HasCorpus<C, I> + Evaluator<I>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
{ {
/// The mutator registered for this stage /// The mutator registered for this stage
fn mutator(&self) -> &M; fn mutator(&self) -> &M;
@ -40,70 +35,62 @@ where
fn mutator_mut(&mut self) -> &mut M; fn mutator_mut(&mut self) -> &mut M;
/// Gets the number of iterations this mutator should run for. /// Gets the number of iterations this mutator should run for.
/// This call uses internal mutability, so it may change for each call fn iterations(&self, state: &mut S) -> usize;
#[inline]
fn iterations(&mut self, rand: &mut R) -> usize {
1 + rand.below(128) as usize
}
/// Runs this (mutational) stage for the given testcase /// Runs this (mutational) stage for the given testcase
fn perform_mutational( fn perform_mutational(
&mut self, &self,
rand: &mut R, state: &mut S,
executor: &mut E, executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
manager: &mut EM, manager: &mut EM,
corpus_idx: usize, corpus_idx: usize,
) -> Result<(), Error> { ) -> Result<(), Error> {
let num = self.iterations(rand); let num = self.iterations(state);
for i in 0..num { for i in 0..num {
let mut input_mut = state let mut input_mut = state
.corpus() .corpus()
.get(corpus_idx) .get(corpus_idx)?
.borrow_mut() .borrow_mut()
.load_input()? .load_input()?
.clone(); .clone();
self.mutator_mut() self.mutator().mutate(state, &mut input_mut, i as i32)?;
.mutate(rand, state, &mut input_mut, i as i32)?;
let fitness = state.process_input(input_mut, executor, manager)?; let fitness = state.evaluate_input(input_mut, executor, manager)?;
self.mutator_mut().post_exec(state, fitness, i as i32)?; self.mutator().post_exec(state, fitness, i as i32)?;
} }
Ok(()) Ok(())
} }
} }
#[derive(Clone, Debug)] pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128;
/// The default mutational stage /// The default mutational stage
pub struct StdMutationalStage<C, E, EM, FT, I, M, OC, OFT, OT, R> #[derive(Clone, Debug)]
pub struct StdMutationalStage<C, E, EM, I, M, OT, R, S>
where where
C: Corpus<I, R>, M: Mutator<I, S>,
E: Executor<I> + HasObservers<OT>,
EM: EventManager<I>,
FT: FeedbacksTuple<I>,
I: Input, I: Input,
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, R>>, S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
OC: Corpus<I, R>, C: Corpus<I>,
OFT: FeedbacksTuple<I>, EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple, OT: ObserversTuple,
R: Rand, R: Rand,
{ {
mutator: M, mutator: M,
phantom: PhantomData<(EM, E, OC, OFT, OT, FT, C, I, R)>, phantom: PhantomData<(C, E, EM, I, OT, R, S)>,
} }
impl<C, E, EM, FT, I, M, OC, OFT, OT, R> MutationalStage<C, E, EM, FT, I, M, OC, OFT, OT, R> impl<C, E, EM, I, M, OT, R, S> MutationalStage<C, E, EM, I, M, OT, S>
for StdMutationalStage<C, E, EM, FT, I, M, OC, OFT, OT, R> for StdMutationalStage<C, E, EM, I, M, OT, R, S>
where where
C: Corpus<I, R>, M: Mutator<I, S>,
E: Executor<I> + HasObservers<OT>,
EM: EventManager<I>,
FT: FeedbacksTuple<I>,
I: Input, I: Input,
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, R>>, S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
OC: Corpus<I, R>, C: Corpus<I>,
OFT: FeedbacksTuple<I>, EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple, OT: ObserversTuple,
R: Rand, R: Rand,
{ {
@ -118,46 +105,45 @@ where
fn mutator_mut(&mut self) -> &mut M { fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator &mut self.mutator
} }
/// Gets the number of iterations as a random number
fn iterations(&self, state: &mut S) -> usize {
1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS) as usize
}
} }
impl<C, E, EM, FT, I, M, OC, OFT, OT, R> Stage<C, E, EM, FT, I, OC, OFT, OT, R> impl<C, E, EM, I, M, OT, R, S> Stage<E, EM, I, S> for StdMutationalStage<C, E, EM, I, M, OT, R, S>
for StdMutationalStage<C, E, EM, FT, I, M, OC, OFT, OT, R>
where where
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, R>>, M: Mutator<I, S>,
EM: EventManager<I>,
E: Executor<I> + HasObservers<OT>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input, I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
R: Rand, R: Rand,
{ {
#[inline] #[inline]
fn perform( fn perform(
&mut self, &self,
rand: &mut R, state: &mut S,
executor: &mut E, executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
manager: &mut EM, manager: &mut EM,
corpus_idx: usize, corpus_idx: usize,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.perform_mutational(rand, executor, state, manager, corpus_idx) self.perform_mutational(state, executor, manager, corpus_idx)
} }
} }
impl<C, E, EM, FT, I, M, OC, OFT, OT, R> StdMutationalStage<C, E, EM, FT, I, M, OC, OFT, OT, R> impl<C, E, EM, I, M, OT, R, S> StdMutationalStage<C, E, EM, I, M, OT, R, S>
where where
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, R>>, M: Mutator<I, S>,
EM: EventManager<I>,
E: Executor<I> + HasObservers<OT>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
FT: FeedbacksTuple<I>,
C: Corpus<I, R>,
I: Input, I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
R: Rand, R: Rand,
{ {
/// Creates a new default mutational stage /// Creates a new default mutational stage

View File

@ -1,6 +1,6 @@
//! The fuzzer, and state are the core pieces of every good fuzzer //! The fuzzer, and state are the core pieces of every good fuzzer
use core::{fmt::Debug, marker::PhantomData}; use core::{fmt::Debug, marker::PhantomData, time::Duration};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::{ use std::{
@ -17,7 +17,7 @@ use crate::{
generators::Generator, generators::Generator,
inputs::Input, inputs::Input,
observers::ObserversTuple, observers::ObserversTuple,
utils::{current_milliseconds, Rand}, utils::Rand,
Error, Error,
}; };
@ -25,11 +25,10 @@ use crate::{
use crate::inputs::bytes::BytesInput; use crate::inputs::bytes::BytesInput;
/// Trait for elements offering a corpus /// Trait for elements offering a corpus
pub trait HasCorpus<C, I, R> pub trait HasCorpus<C, I>
where where
C: Corpus<I, R>, C: Corpus<I>,
I: Input, I: Input,
R: Rand,
{ {
/// The testcase corpus /// The testcase corpus
fn corpus(&self) -> &C; fn corpus(&self) -> &C;
@ -37,6 +36,29 @@ where
fn corpus_mut(&mut self) -> &mut C; fn corpus_mut(&mut self) -> &mut C;
} }
/// Trait for elements offering a corpus of solutions
pub trait HasSolutions<C, I>
where
C: Corpus<I>,
I: Input,
{
/// The solutions corpus
fn solutions(&self) -> &C;
/// The solutions corpus (mut)
fn solutions_mut(&mut self) -> &mut C;
}
/// Trait for elements offering a rand
pub trait HasRand<R>
where
R: Rand,
{
/// The rand instance
fn rand(&self) -> &R;
/// The rand instance (mut)
fn rand_mut(&mut self) -> &mut R;
}
/// Trait for elements offering metadata /// Trait for elements offering metadata
pub trait HasMetadata { pub trait HasMetadata {
/// A map, storing all metadata /// A map, storing all metadata
@ -54,44 +76,409 @@ pub trait HasMetadata {
} }
} }
/// Trait for elements offering a feedbacks tuple
pub trait HasFeedbacks<FT, I>
where
FT: FeedbacksTuple<I>,
I: Input,
{
/// The feedbacks tuple
fn feedbacks(&self) -> &FT;
/// The feedbacks tuple (mut)
fn feedbacks_mut(&mut self) -> &mut FT;
/// Resets all metadata holds by feedbacks
#[inline]
fn discard_feedbacks_metadata(&mut self, input: &I) -> Result<(), Error> {
// TODO: This could probably be automatic in the feedback somehow?
self.feedbacks_mut().discard_metadata_all(&input)
}
/// Creates a new testcase, appending the metadata from each feedback
#[inline]
fn testcase_with_feedbacks_metadata(
&mut self,
input: I,
fitness: u32,
) -> Result<Testcase<I>, Error> {
let mut testcase = Testcase::with_fitness(input, fitness);
self.feedbacks_mut().append_metadata_all(&mut testcase)?;
Ok(testcase)
}
}
/// Trait for elements offering an objective feedbacks tuple
pub trait HasObjectives<FT, I>
where
FT: FeedbacksTuple<I>,
I: Input,
{
/// The objective feedbacks tuple
fn objectives(&self) -> &FT;
/// The objective feedbacks tuple (mut)
fn objectives_mut(&mut self) -> &mut FT;
}
/// Trait for the execution counter
pub trait HasExecutions {
/// The executions counter
fn executions(&self) -> &usize;
/// The executions counter (mut)
fn executions_mut(&mut self) -> &mut usize;
}
/// Trait for the starting time
pub trait HasStartTime {
/// The starting time
fn start_time(&self) -> &Duration;
/// The starting time (mut)
fn start_time_mut(&mut self) -> &mut Duration;
}
/// Add to the state if interesting
pub trait IfInteresting<I>
where
I: Input,
{
/// Evaluate if a set of observation channels has an interesting state
fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: ExitKind,
) -> Result<u32, Error>
where
OT: ObserversTuple;
/// Adds this input to the corpus, if it's intersting, and return the index
fn add_if_interesting(&mut self, input: &I, fitness: u32) -> Result<Option<usize>, Error>;
}
/// Evaluate an input modyfing the state of the fuzzer and returning a fitness
pub trait Evaluator<I>: Sized
where
I: Input,
{
/// Runs the input and triggers observers and feedback
fn evaluate_input<E, EM, OT>(
&mut self,
input: I,
executor: &mut E,
event_mgr: &mut EM,
) -> Result<u32, Error>
where
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
EM: EventManager<I, Self>;
}
/// The state a fuzz run. /// The state a fuzz run.
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "FT: serde::de::DeserializeOwned")] #[serde(bound = "FT: serde::de::DeserializeOwned")]
pub struct State<C, FT, I, OC, OFT, R> pub struct State<C, FT, I, OFT, R, SC>
where where
C: Corpus<I, R>, C: Corpus<I>,
I: Input, I: Input,
R: Rand, R: Rand,
FT: FeedbacksTuple<I>, FT: FeedbacksTuple<I>,
OC: Corpus<I, R>, SC: Corpus<I>,
OFT: FeedbacksTuple<I>, OFT: FeedbacksTuple<I>,
{ {
/// RNG instance
rand: R,
/// How many times the executor ran the harness/target /// How many times the executor ran the harness/target
executions: usize, executions: usize,
/// At what time the fuzzing started
start_time: Duration,
/// The corpus /// The corpus
corpus: C, corpus: C,
// TODO use Duration
/// At what time the fuzzing started
start_time: u64,
/// Metadata stored for this state by one of the components
metadata: SerdeAnyMap,
/// Feedbacks used to evaluate an input /// Feedbacks used to evaluate an input
feedbacks: FT, feedbacks: FT,
// Objective corpus // Solutions corpus
objective_corpus: OC, solutions: SC,
/// Objective Feedbacks /// Objective Feedbacks
objective_feedbacks: OFT, objectives: OFT,
/// Metadata stored for this state by one of the components
metadata: SerdeAnyMap,
phantom: PhantomData<(R, I)>, phantom: PhantomData<I>,
}
impl<C, FT, I, OFT, R, SC> HasRand<R> for State<C, FT, I, OFT, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// The rand instance
#[inline]
fn rand(&self) -> &R {
&self.rand
}
/// The rand instance (mut)
#[inline]
fn rand_mut(&mut self) -> &mut R {
&mut self.rand
}
}
impl<C, FT, I, OFT, R, SC> HasCorpus<C, I> for State<C, FT, I, OFT, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// Returns the corpus
#[inline]
fn corpus(&self) -> &C {
&self.corpus
}
/// Returns the mutable corpus
#[inline]
fn corpus_mut(&mut self) -> &mut C {
&mut self.corpus
}
}
impl<C, FT, I, OFT, R, SC> HasSolutions<SC, I> for State<C, FT, I, OFT, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// Returns the solutions corpus
#[inline]
fn solutions(&self) -> &SC {
&self.solutions
}
/// Returns the solutions corpus (mut)
#[inline]
fn solutions_mut(&mut self) -> &mut SC {
&mut self.solutions
}
}
impl<C, FT, I, OFT, R, SC> HasMetadata for State<C, FT, I, OFT, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// Get all the metadata into an HashMap
#[inline]
fn metadata(&self) -> &SerdeAnyMap {
&self.metadata
}
/// Get all the metadata into an HashMap (mutable)
#[inline]
fn metadata_mut(&mut self) -> &mut SerdeAnyMap {
&mut self.metadata
}
}
impl<C, FT, I, OFT, R, SC> HasFeedbacks<FT, I> for State<C, FT, I, OFT, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// The feedbacks tuple
#[inline]
fn feedbacks(&self) -> &FT {
&self.feedbacks
}
/// The feedbacks tuple (mut)
#[inline]
fn feedbacks_mut(&mut self) -> &mut FT {
&mut self.feedbacks
}
}
impl<C, FT, I, OFT, R, SC> HasObjectives<OFT, I> for State<C, FT, I, OFT, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// The objective feedbacks tuple
#[inline]
fn objectives(&self) -> &OFT {
&self.objectives
}
/// The objective feedbacks tuple (mut)
#[inline]
fn objectives_mut(&mut self) -> &mut OFT {
&mut self.objectives
}
}
impl<C, FT, I, OFT, R, SC> HasExecutions for State<C, FT, I, OFT, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// The executions counter
#[inline]
fn executions(&self) -> &usize {
&self.executions
}
/// The executions counter (mut)
#[inline]
fn executions_mut(&mut self) -> &mut usize {
&mut self.executions
}
}
impl<C, FT, I, OFT, R, SC> HasStartTime for State<C, FT, I, OFT, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// The starting time
#[inline]
fn start_time(&self) -> &Duration {
&self.start_time
}
/// The starting time (mut)
#[inline]
fn start_time_mut(&mut self) -> &mut Duration {
&mut self.start_time
}
}
impl<C, FT, I, OFT, R, SC> IfInteresting<I> for State<C, FT, I, OFT, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// Evaluate if a set of observation channels has an interesting state
fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: ExitKind,
) -> Result<u32, Error>
where
OT: ObserversTuple,
{
Ok(self
.feedbacks_mut()
.is_interesting_all(input, observers, exit_kind)?)
}
/// Adds this input to the corpus, if it's intersting, and return the index
#[inline]
fn add_if_interesting(&mut self, input: &I, fitness: u32) -> Result<Option<usize>, Error> {
if fitness > 0 {
let testcase = self.testcase_with_feedbacks_metadata(input.clone(), fitness)?;
Ok(Some(self.corpus.add(testcase)?)) // TODO scheduler hook
} else {
self.discard_feedbacks_metadata(input)?;
Ok(None)
}
}
}
impl<C, FT, I, OFT, R, SC> Evaluator<I> for State<C, FT, I, OFT, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// Process one input, adding to the respective corpuses if needed and firing the right events
#[inline]
fn evaluate_input<E, EM, OT>(
&mut self,
// TODO probably we can take a ref to input and pass a cloned one to add_if_interesting
input: I,
executor: &mut E,
manager: &mut EM,
) -> Result<u32, Error>
where
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
C: Corpus<I>,
EM: EventManager<I, Self>,
{
let (fitness, is_solution) = self.execute_input(&input, executor, manager)?;
let observers = executor.observers();
if is_solution {
// If the input is a solution, add it to the respective corpus
self.solutions_mut().add(Testcase::new(input.clone()))?;
}
if !self.add_if_interesting(&input, fitness)?.is_none() {
let observers_buf = manager.serialize_observers(observers)?;
manager.fire(
self,
Event::NewTestcase {
input: input,
observers_buf,
corpus_size: self.corpus().count() + 1,
client_config: "TODO".into(),
time: crate::utils::current_time(),
executions: *self.executions(),
},
)?;
}
Ok(fitness)
}
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl<C, FT, OC, OFT, R> State<C, FT, BytesInput, OC, OFT, R> impl<C, FT, OFT, R, SC> State<C, FT, BytesInput, OFT, R, SC>
where where
C: Corpus<BytesInput, R>, C: Corpus<BytesInput>,
R: Rand, R: Rand,
FT: FeedbacksTuple<BytesInput>, FT: FeedbacksTuple<BytesInput>,
OC: Corpus<BytesInput, R>, SC: Corpus<BytesInput>,
OFT: FeedbacksTuple<BytesInput>, OFT: FeedbacksTuple<BytesInput>,
{ {
pub fn load_from_directory<E, OT, EM>( pub fn load_from_directory<E, OT, EM>(
@ -101,10 +488,10 @@ where
in_dir: &Path, in_dir: &Path,
) -> Result<(), Error> ) -> Result<(), Error>
where where
C: Corpus<BytesInput, R>, C: Corpus<BytesInput>,
E: Executor<BytesInput> + HasObservers<OT>, E: Executor<BytesInput> + HasObservers<OT>,
OT: ObserversTuple, OT: ObserversTuple,
EM: EventManager<BytesInput>, EM: EventManager<BytesInput, Self>,
{ {
for entry in fs::read_dir(in_dir)? { for entry in fs::read_dir(in_dir)? {
let entry = entry?; let entry = entry?;
@ -121,12 +508,12 @@ where
println!("Loading file {:?} ...", &path); println!("Loading file {:?} ...", &path);
let bytes = fs::read(&path)?; let bytes = fs::read(&path)?;
let input = BytesInput::new(bytes); let input = BytesInput::new(bytes);
let (fitness, obj_fitness) = self.evaluate_input(&input, executor, manager)?; let (fitness, is_solution) = self.execute_input(&input, executor, manager)?;
if self.add_if_interesting(input, fitness)?.is_none() { if self.add_if_interesting(&input, fitness)?.is_none() {
println!("File {:?} was not interesting, skipped.", &path); println!("File {:?} was not interesting, skipped.", &path);
} }
if obj_fitness > 0 { if is_solution {
println!("File {:?} is an objective, however will be not added as an initial testcase.", &path); println!("File {:?} is a solution, however will be not considered as it is an initial testcase.", &path);
} }
} else if attr.is_dir() { } else if attr.is_dir() {
self.load_from_directory(executor, manager, &path)?; self.load_from_directory(executor, manager, &path)?;
@ -143,10 +530,10 @@ where
in_dirs: &[PathBuf], in_dirs: &[PathBuf],
) -> Result<(), Error> ) -> Result<(), Error>
where where
C: Corpus<BytesInput, R>, C: Corpus<BytesInput>,
E: Executor<BytesInput> + HasObservers<OT>, E: Executor<BytesInput> + HasObservers<OT>,
OT: ObserversTuple, OT: ObserversTuple,
EM: EventManager<BytesInput>, EM: EventManager<BytesInput, Self>,
{ {
for in_dir in in_dirs { for in_dir in in_dirs {
self.load_from_directory(executor, manager, in_dir)?; self.load_from_directory(executor, manager, in_dir)?;
@ -164,270 +551,51 @@ where
} }
} }
impl<C, FT, I, OC, OFT, R> HasCorpus<C, I, R> for State<C, FT, I, OC, OFT, R> impl<C, FT, I, OFT, R, SC> State<C, FT, I, OFT, R, SC>
where where
C: Corpus<I, R>, C: Corpus<I>,
I: Input, I: Input,
R: Rand, R: Rand,
FT: FeedbacksTuple<I>, FT: FeedbacksTuple<I>,
OC: Corpus<I, R>, SC: Corpus<I>,
OFT: FeedbacksTuple<I>, OFT: FeedbacksTuple<I>,
{ {
/// Returns the corpus
fn corpus(&self) -> &C {
&self.corpus
}
/// Returns the mutable corpus
fn corpus_mut(&mut self) -> &mut C {
&mut self.corpus
}
}
/// Trait for elements offering metadata
impl<C, FT, I, OC, OFT, R> HasMetadata for State<C, FT, I, OC, OFT, R>
where
C: Corpus<I, R>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
{
/// Get all the metadata into an HashMap
#[inline]
fn metadata(&self) -> &SerdeAnyMap {
&self.metadata
}
/// Get all the metadata into an HashMap (mutable)
#[inline]
fn metadata_mut(&mut self) -> &mut SerdeAnyMap {
&mut self.metadata
}
}
impl<C, FT, I, OC, OFT, R> State<C, FT, I, OC, OFT, R>
where
C: Corpus<I, R>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
{
/// Get executions
#[inline]
pub fn executions(&self) -> usize {
self.executions
}
/// Set executions
#[inline]
pub fn set_executions(&mut self, executions: usize) {
self.executions = executions
}
#[inline]
pub fn start_time(&self) -> u64 {
self.start_time
}
#[inline]
pub fn set_start_time(&mut self, ms: u64) {
self.start_time = ms
}
/// Returns vector of feebacks
#[inline]
pub fn feedbacks(&self) -> &FT {
&self.feedbacks
}
/// Returns vector of feebacks (mutable)
#[inline]
pub fn feedbacks_mut(&mut self) -> &mut FT {
&mut self.feedbacks
}
/// Returns vector of objective feebacks
#[inline]
pub fn objective_feedbacks(&self) -> &OFT {
&self.objective_feedbacks
}
/// Returns vector of objective feebacks (mutable)
#[inline]
pub fn objective_feedbacks_mut(&mut self) -> &mut OFT {
&mut self.objective_feedbacks
}
/// Returns the objective corpus
#[inline]
pub fn objective_corpus(&self) -> &OC {
&self.objective_corpus
}
/// Returns the mutable objective corpus
#[inline]
pub fn objective_corpus_mut(&mut self) -> &mut OC {
&mut self.objective_corpus
}
// TODO move some of these, like evaluate_input, to FuzzingEngine
#[inline]
pub fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: ExitKind,
) -> Result<u32, Error>
where
OT: ObserversTuple,
{
Ok(self
.feedbacks_mut()
.is_interesting_all(input, observers, exit_kind)?)
}
/// Runs the input and triggers observers and feedback /// Runs the input and triggers observers and feedback
pub fn evaluate_input<E, EM, OT>( pub fn execute_input<E, EM, OT>(
&mut self, &mut self,
input: &I, input: &I,
executor: &mut E, executor: &mut E,
event_mgr: &mut EM, event_mgr: &mut EM,
) -> Result<(u32, u32), Error> ) -> Result<(u32, bool), Error>
where where
E: Executor<I> + HasObservers<OT>, E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple, OT: ObserversTuple,
C: Corpus<I, R>, C: Corpus<I>,
EM: EventManager<I>, EM: EventManager<I, Self>,
{ {
executor.pre_exec_observers()?; executor.pre_exec_observers()?;
executor.pre_exec(self, event_mgr, input)?; executor.pre_exec(self, event_mgr, input)?;
let exit_kind = executor.run_target(input)?; let exit_kind = executor.run_target(input)?;
executor.post_exec(&self, event_mgr, input)?; //executor.post_exec(&self, event_mgr, input)?;
self.set_executions(self.executions() + 1); *self.executions_mut() += 1;
executor.post_exec_observers()?; executor.post_exec_observers()?;
let observers = executor.observers(); let observers = executor.observers();
let objective_fitness = let fitness =
self.objective_feedbacks self.feedbacks_mut()
.is_interesting_all(&input, observers, exit_kind.clone())?; .is_interesting_all(&input, observers, exit_kind.clone())?;
let fitness = self
.feedbacks_mut()
.is_interesting_all(&input, observers, exit_kind)?;
Ok((fitness, objective_fitness))
}
/// Resets all current feedbacks let is_solution = self
#[inline] .objectives_mut()
pub fn discard_input(&mut self, input: &I) -> Result<(), Error> { .is_interesting_all(&input, observers, exit_kind)?
// TODO: This could probably be automatic in the feedback somehow? > 0;
self.feedbacks_mut().discard_metadata_all(&input) Ok((fitness, is_solution))
}
/// Creates a new testcase, appending the metadata from each feedback
#[inline]
pub fn input_to_testcase(&mut self, input: I, fitness: u32) -> Result<Testcase<I>, Error> {
let mut testcase = Testcase::new(input);
testcase.set_fitness(fitness);
self.feedbacks_mut().append_metadata_all(&mut testcase)?;
Ok(testcase)
}
/// Create a testcase from this input, if it's intersting
#[inline]
pub fn testcase_if_interesting(
&mut self,
input: I,
fitness: u32,
) -> Result<Option<Testcase<I>>, Error> {
if fitness > 0 {
Ok(Some(self.input_to_testcase(input, fitness)?))
} else {
self.discard_input(&input)?;
Ok(None)
}
}
/// Adds this input to the corpus, if it's intersting
#[inline]
pub fn add_if_interesting(&mut self, input: I, fitness: u32) -> Result<Option<usize>, Error>
where
C: Corpus<I, R>,
{
if fitness > 0 {
let testcase = self.input_to_testcase(input, fitness)?;
Ok(Some(self.corpus_mut().add(testcase)))
} else {
self.discard_input(&input)?;
Ok(None)
}
}
/// Adds this input to the objective corpus, if it's an objective
#[inline]
pub fn add_if_objective(&mut self, input: I, fitness: u32) -> Result<Option<usize>, Error>
where
C: Corpus<I, R>,
{
if fitness > 0 {
let testcase = self.input_to_testcase(input, fitness)?;
Ok(Some(self.objective_corpus.add(testcase)))
} else {
self.discard_input(&input)?;
Ok(None)
}
}
/// Process one input, adding to the respective corpuses if needed and firing the right events
#[inline]
pub fn process_input<E, EM, OT>(
&mut self,
// TODO probably we can take a ref to input and pass a cloned one to add_if_interesting
input: I,
executor: &mut E,
manager: &mut EM,
) -> Result<u32, Error>
where
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
C: Corpus<I, R>,
EM: EventManager<I>,
{
let (fitness, obj_fitness) = self.evaluate_input(&input, executor, manager)?;
let observers = executor.observers();
if obj_fitness > 0 {
self.add_if_objective(input.clone(), obj_fitness)?;
}
if fitness > 0 {
let observers_buf = manager.serialize_observers(observers)?;
manager.fire(
self,
Event::NewTestcase {
input: input.clone(),
observers_buf,
corpus_size: self.corpus().count() + 1,
client_config: "TODO".into(),
time: crate::utils::current_time(),
executions: self.executions(),
},
)?;
self.add_if_interesting(input, fitness)?;
} else {
self.discard_input(&input)?;
}
Ok(fitness)
} }
pub fn generate_initial_inputs<G, E, OT, EM>( pub fn generate_initial_inputs<G, E, OT, EM>(
&mut self, &mut self,
rand: &mut R,
executor: &mut E, executor: &mut E,
generator: &mut G, generator: &mut G,
manager: &mut EM, manager: &mut EM,
@ -435,15 +603,15 @@ where
) -> Result<(), Error> ) -> Result<(), Error>
where where
G: Generator<I, R>, G: Generator<I, R>,
C: Corpus<I, R>, C: Corpus<I>,
E: Executor<I> + HasObservers<OT>, E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple, OT: ObserversTuple,
EM: EventManager<I>, EM: EventManager<I, Self>,
{ {
let mut added = 0; let mut added = 0;
for _ in 0..num { for _ in 0..num {
let input = generator.generate(rand)?; let input = generator.generate(self.rand_mut())?;
let fitness = self.process_input(input, executor, manager)?; let fitness = self.evaluate_input(input, executor, manager)?;
if fitness > 0 { if fitness > 0 {
added += 1; added += 1;
} }
@ -460,15 +628,16 @@ where
Ok(()) Ok(())
} }
pub fn new(corpus: C, feedbacks: FT, objective_corpus: OC, objective_feedbacks: OFT) -> Self { pub fn new(rand: R, corpus: C, feedbacks: FT, solutions: SC, objectives: OFT) -> Self {
Self { Self {
corpus, rand,
executions: 0, executions: 0,
start_time: current_milliseconds(), start_time: Duration::from_millis(0),
metadata: SerdeAnyMap::default(), metadata: SerdeAnyMap::default(),
feedbacks: feedbacks, corpus,
objective_corpus: objective_corpus, feedbacks,
objective_feedbacks: objective_feedbacks, solutions,
objectives,
phantom: PhantomData, phantom: PhantomData,
} }
} }

View File

@ -1,7 +1,7 @@
//! Utility functions for AFL //! Utility functions for AFL
use core::{cell::RefCell, debug_assert, fmt::Debug, time}; use core::{cell::RefCell, debug_assert, fmt::Debug, time};
use serde::{Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use xxhash_rust::xxh3::xxh3_64_with_seed; use xxhash_rust::xxh3::xxh3_64_with_seed;
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -10,7 +10,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
pub type StdRand = RomuTrioRand; pub type StdRand = RomuTrioRand;
/// Ways to get random around here /// Ways to get random around here
pub trait Rand: Debug + Serialize { pub trait Rand: Debug + Serialize + DeserializeOwned {
// Sets the seed of this Rand // Sets the seed of this Rand
fn set_seed(&mut self, seed: u64); fn set_seed(&mut self, seed: u64);