commit
6ed19f9dc3
@ -5,10 +5,11 @@ use std::{env, path::PathBuf};
|
||||
|
||||
use libafl::{
|
||||
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
|
||||
events::setup_restarting_mgr,
|
||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
inputs::Input,
|
||||
mutators::scheduled::HavocBytesMutator,
|
||||
mutators::token_mutations::TokensMetadata,
|
||||
@ -17,7 +18,7 @@ use libafl::{
|
||||
state::{HasCorpus, HasMetadata, State},
|
||||
stats::SimpleStats,
|
||||
utils::{current_nanos, StdRand},
|
||||
Error, Fuzzer, StdFuzzer,
|
||||
Error,
|
||||
};
|
||||
|
||||
/// 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
|
||||
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
|
||||
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.
|
||||
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());
|
||||
|
||||
// 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
|
||||
let mut state = state.unwrap_or(State::new(
|
||||
StdRand::new(current_nanos()),
|
||||
InMemoryCorpus::new(),
|
||||
tuple_list!(MaxMapFeedback::new_with_observer(
|
||||
&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
|
||||
let mutator = HavocBytesMutator::default();
|
||||
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
|
||||
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());
|
||||
}
|
||||
|
||||
fuzzer.fuzz_loop(&mut rand, &mut executor, &mut state, &mut restarting_mgr)
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ use std::hash::Hasher;
|
||||
use xxhash_rust::const_xxh3;
|
||||
use xxhash_rust::xxh3;
|
||||
|
||||
use libafl::utils::{Rand, StdRand};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use libafl::utils::{Rand, StdRand};
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut rand = StdRand::new(0);
|
||||
|
@ -1,9 +1,9 @@
|
||||
//! Compare the speed of rand implementations
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use libafl::utils::{
|
||||
Lehmer64Rand, Rand, RomuDuoJrRand, RomuTrioRand, XorShift64Rand, Xoshiro256StarRand,
|
||||
};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut xorshift = XorShift64Rand::new(1);
|
||||
|
@ -1,11 +1,9 @@
|
||||
fn main() {
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
windows::build!(
|
||||
windows::win32::system_services::HANDLE,
|
||||
windows::win32::windows_programming::CloseHandle,
|
||||
// API needed for the shared memory
|
||||
windows::win32::system_services::{CreateFileMappingA, OpenFileMappingA, MapViewOfFile, UnmapViewOfFile},
|
||||
);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
windows::build!(
|
||||
windows::win32::system_services::HANDLE,
|
||||
windows::win32::windows_programming::CloseHandle,
|
||||
// API needed for the shared memory
|
||||
windows::win32::system_services::{CreateFileMappingA, OpenFileMappingA, MapViewOfFile, UnmapViewOfFile},
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! Bolts are no conceptual fuzzing elements, but they keep libafl-based fuzzers together.
|
||||
|
||||
pub mod bindings;
|
||||
pub mod llmp;
|
||||
pub mod ownedref;
|
||||
pub mod serdeany;
|
||||
pub mod shmem;
|
||||
pub mod tuples;
|
||||
pub mod bindings;
|
||||
|
@ -512,7 +512,7 @@ macro_rules! impl_serdeany {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(feature = "std")]
|
||||
#[ctor]
|
||||
|
@ -330,8 +330,8 @@ pub mod shmem {
|
||||
use core::{mem::size_of, slice};
|
||||
use std::ffi::CStr;
|
||||
|
||||
use crate::Error;
|
||||
use super::ShMem;
|
||||
use crate::Error;
|
||||
|
||||
/// The default Sharedmap impl for windows using shmctl & shmget
|
||||
#[derive(Clone, Debug)]
|
||||
@ -343,7 +343,6 @@ pub mod shmem {
|
||||
}
|
||||
|
||||
// TODO complete
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -190,9 +190,9 @@ macro_rules! tuple_for_each {
|
||||
}
|
||||
|
||||
impl ForEach for () {
|
||||
fn for_each(&self) { }
|
||||
fn for_each(&self) {}
|
||||
}
|
||||
|
||||
|
||||
impl<Head, Tail> ForEach for (Head, Tail)
|
||||
where
|
||||
Head: $trait_name,
|
||||
@ -206,7 +206,7 @@ macro_rules! tuple_for_each {
|
||||
}
|
||||
{
|
||||
use $fn_name::*;
|
||||
|
||||
|
||||
$tuple_name.for_each();
|
||||
};
|
||||
};
|
||||
@ -221,9 +221,9 @@ macro_rules! tuple_for_each_mut {
|
||||
}
|
||||
|
||||
impl ForEachMut for () {
|
||||
fn for_each_mut(&mut self) { }
|
||||
fn for_each_mut(&mut self) {}
|
||||
}
|
||||
|
||||
|
||||
impl<Head, Tail> ForEachMut for (Head, Tail)
|
||||
where
|
||||
Head: $trait_name,
|
||||
@ -237,7 +237,7 @@ macro_rules! tuple_for_each_mut {
|
||||
}
|
||||
{
|
||||
use $fn_name::*;
|
||||
|
||||
|
||||
$tuple_name.for_each_mut();
|
||||
};
|
||||
};
|
||||
@ -245,13 +245,13 @@ macro_rules! tuple_for_each_mut {
|
||||
|
||||
/*
|
||||
pub fn test_macros() {
|
||||
|
||||
|
||||
let mut t = tuple_list!(1, "a");
|
||||
|
||||
tuple_for_each!(f1, std::fmt::Display, t, |x| {
|
||||
println!("{}", x);
|
||||
});
|
||||
|
||||
|
||||
tuple_for_each_mut!(f2, std::fmt::Display, t, |x| {
|
||||
println!("{}", x);
|
||||
});
|
||||
|
@ -1,74 +1,86 @@
|
||||
//! In-memory corpus, keeps all test cases in memory at all times
|
||||
|
||||
use alloc::{borrow::ToOwned, vec::Vec};
|
||||
use core::{cell::RefCell, marker::PhantomData};
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::RefCell;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
corpus::Corpus, corpus::HasTestcaseVec, corpus::Testcase, inputs::Input, utils::Rand, Error,
|
||||
};
|
||||
use crate::{corpus::Corpus, corpus::Testcase, inputs::Input, Error};
|
||||
|
||||
/// A corpus handling all important fuzzing in memory.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
/// A corpus handling all in memory.
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(bound = "I: serde::de::DeserializeOwned")]
|
||||
pub struct InMemoryCorpus<I, R>
|
||||
pub struct InMemoryCorpus<I>
|
||||
where
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
entries: Vec<RefCell<Testcase<I>>>,
|
||||
pos: usize,
|
||||
phantom: PhantomData<R>,
|
||||
current: Option<usize>,
|
||||
}
|
||||
|
||||
impl<I, R> HasTestcaseVec<I> for InMemoryCorpus<I, R>
|
||||
impl<I> Corpus<I> for InMemoryCorpus<I>
|
||||
where
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
fn entries(&self) -> &[RefCell<Testcase<I>>] {
|
||||
&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
|
||||
/// Returns the number of elements
|
||||
#[inline]
|
||||
fn next(&mut self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error> {
|
||||
if self.count() == 0 {
|
||||
Err(Error::Empty("No entries in corpus".to_owned()))
|
||||
fn count(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
let len = { self.entries().len() };
|
||||
let id = rand.below(len as u64) as usize;
|
||||
self.pos = id;
|
||||
Ok((self.get(id), id))
|
||||
Ok(Some(self.entries.remove(idx).into_inner()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the testacase we currently use
|
||||
/// Get by id
|
||||
#[inline]
|
||||
fn current_testcase(&self) -> (&RefCell<Testcase<I>>, usize) {
|
||||
(self.get(self.pos), self.pos)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R> InMemoryCorpus<I, R>
|
||||
impl<I> InMemoryCorpus<I>
|
||||
where
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
entries: vec![],
|
||||
pos: 0,
|
||||
phantom: PhantomData,
|
||||
current: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
225
libafl/src/corpus/minset.rs
Normal file
225
libafl/src/corpus/minset.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
103
libafl/src/corpus/mod old.rs
Normal file
103
libafl/src/corpus/mod old.rs
Normal 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);
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
//! 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;
|
||||
@ -13,90 +12,121 @@ pub mod ondisk;
|
||||
pub use ondisk::OnDiskCorpus;
|
||||
|
||||
pub mod queue;
|
||||
pub use queue::QueueCorpus;
|
||||
pub use queue::QueueCorpusScheduler;
|
||||
|
||||
use alloc::{borrow::ToOwned, vec::Vec};
|
||||
use core::{cell::RefCell, ptr};
|
||||
use core::cell::RefCell;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
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>>>;
|
||||
}
|
||||
use crate::{
|
||||
inputs::Input,
|
||||
state::{HasCorpus, HasRand},
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
|
||||
/// 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
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
/// Returns the number of elements
|
||||
#[inline]
|
||||
fn count(&self) -> usize {
|
||||
self.entries().len()
|
||||
}
|
||||
|
||||
// TODO implement a was_fuzzed counter
|
||||
fn count(&self) -> usize;
|
||||
|
||||
/// 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
|
||||
}
|
||||
fn add(&mut self, testcase: Testcase<I>) -> Result<usize, Error>;
|
||||
|
||||
/// 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);
|
||||
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<(), Error>;
|
||||
|
||||
/// Removes an entry from the corpus, returning it if it was present.
|
||||
fn remove(&mut self, idx: usize) -> Result<Option<Testcase<I>>, Error>;
|
||||
|
||||
/// 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(())
|
||||
}
|
||||
|
||||
/// Get by id
|
||||
#[inline]
|
||||
fn get(&self, idx: usize) -> &RefCell<Testcase<I>> {
|
||||
&self.entries()[idx]
|
||||
/// Replaces the testcase at the given idx
|
||||
fn on_replace(
|
||||
&self,
|
||||
_state: &mut S,
|
||||
_idx: usize,
|
||||
_testcase: &Testcase<I>,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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))
|
||||
}
|
||||
fn on_remove(
|
||||
&self,
|
||||
_state: &mut S,
|
||||
_idx: usize,
|
||||
_testcase: &Option<Testcase<I>>,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 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);
|
||||
fn next(&self, state: &mut S) -> Result<usize, Error>;
|
||||
}
|
||||
|
||||
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>;
|
||||
|
@ -1,104 +1,104 @@
|
||||
//! The ondisk corpus stores unused testcases to disk.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::{cell::RefCell, marker::PhantomData};
|
||||
use core::cell::RefCell;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{
|
||||
corpus::Corpus, corpus::HasTestcaseVec, corpus::Testcase, inputs::Input, utils::Rand, Error,
|
||||
};
|
||||
use crate::{corpus::Corpus, corpus::Testcase, inputs::Input, Error};
|
||||
|
||||
/// A corpus able to store testcases to disk, and load them from disk, when they are being used.
|
||||
#[cfg(feature = "std")]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(bound = "I: serde::de::DeserializeOwned")]
|
||||
pub struct OnDiskCorpus<I, R>
|
||||
pub struct OnDiskCorpus<I>
|
||||
where
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
entries: Vec<RefCell<Testcase<I>>>,
|
||||
current: Option<usize>,
|
||||
dir_path: PathBuf,
|
||||
pos: usize,
|
||||
phantom: PhantomData<R>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<I, R> HasTestcaseVec<I> for OnDiskCorpus<I, R>
|
||||
impl<I> Corpus<I> for OnDiskCorpus<I>
|
||||
where
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
/// Returns the number of elements
|
||||
#[inline]
|
||||
fn entries(&self) -> &[RefCell<Testcase<I>>] {
|
||||
&self.entries
|
||||
fn count(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
#[inline]
|
||||
fn entries_mut(&mut self) -> &mut Vec<RefCell<Testcase<I>>> {
|
||||
&mut self.entries
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<I, R> Corpus<I, R> for OnDiskCorpus<I, R>
|
||||
where
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
/// Add an entry and save it to disk
|
||||
fn add(&mut self, mut entry: Testcase<I>) -> usize {
|
||||
match entry.filename() {
|
||||
/// Add an entry to the corpus and return its index
|
||||
#[inline]
|
||||
fn add(&mut self, mut testcase: Testcase<I>) -> Result<usize, Error> {
|
||||
match testcase.filename() {
|
||||
None => {
|
||||
// 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_str = filename.to_str().expect("Invalid Path");
|
||||
entry.set_filename(filename_str.into());
|
||||
testcase.set_filename(filename_str.into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
entry
|
||||
testcase
|
||||
.store_input()
|
||||
.expect("Could not save testcase to disk".into());
|
||||
self.entries.push(RefCell::new(entry));
|
||||
self.entries.len() - 1
|
||||
self.entries.push(RefCell::new(testcase));
|
||||
Ok(self.entries.len() - 1)
|
||||
}
|
||||
|
||||
/// Replaces the testcase at the given idx
|
||||
#[inline]
|
||||
fn current_testcase(&self) -> (&RefCell<Testcase<I>>, usize) {
|
||||
(self.get(self.pos), self.pos)
|
||||
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(())
|
||||
}
|
||||
|
||||
/// Gets the next entry
|
||||
/// Removes an entry from the corpus, returning it if it was present.
|
||||
#[inline]
|
||||
fn next(&mut self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error> {
|
||||
if self.count() == 0 {
|
||||
Err(Error::Empty("No entries in corpus".to_owned()))
|
||||
fn remove(&mut self, idx: usize) -> Result<Option<Testcase<I>>, Error> {
|
||||
if idx >= self.entries.len() {
|
||||
Ok(None)
|
||||
} else {
|
||||
let len = { self.entries().len() };
|
||||
let id = rand.below(len as u64) as usize;
|
||||
self.pos = id;
|
||||
Ok((self.get(id), id))
|
||||
Ok(Some(self.entries.remove(idx).into_inner()))
|
||||
}
|
||||
}
|
||||
|
||||
// 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, R> OnDiskCorpus<I, R>
|
||||
impl<I> OnDiskCorpus<I>
|
||||
where
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
pub fn new(dir_path: PathBuf) -> Self {
|
||||
Self {
|
||||
dir_path: dir_path,
|
||||
entries: vec![],
|
||||
pos: 0,
|
||||
phantom: PhantomData,
|
||||
current: None,
|
||||
dir_path: dir_path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,132 +1,65 @@
|
||||
//! The queue corpus implements an afl-like queue mechanism
|
||||
|
||||
use alloc::{borrow::ToOwned, vec::Vec};
|
||||
use core::{cell::RefCell, marker::PhantomData};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use alloc::borrow::ToOwned;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
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
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(bound = "I: serde::de::DeserializeOwned")]
|
||||
pub struct QueueCorpus<C, I, R>
|
||||
pub struct QueueCorpusScheduler<C, I, S>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
S: HasCorpus<C, I>,
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
corpus: C,
|
||||
pos: usize,
|
||||
cycles: u64,
|
||||
phantom: PhantomData<(I, R)>,
|
||||
phantom: PhantomData<(C, I, S)>,
|
||||
}
|
||||
|
||||
impl<C, I, R> HasTestcaseVec<I> for QueueCorpus<C, I, R>
|
||||
impl<C, I, S> CorpusScheduler<I, S> for QueueCorpusScheduler<C, I, S>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
S: HasCorpus<C, I>,
|
||||
C: Corpus<I>,
|
||||
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 QueueCorpus<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()
|
||||
}
|
||||
|
||||
#[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()));
|
||||
/// 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 id = match state.corpus().current() {
|
||||
Some(cur) => {
|
||||
if *cur + 1 > state.corpus().count() {
|
||||
0
|
||||
} else {
|
||||
*cur + 1
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
*state.corpus_mut().current_mut() = Some(id);
|
||||
Ok(id)
|
||||
}
|
||||
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
|
||||
C: Corpus<I, R>,
|
||||
S: HasCorpus<C, I>,
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
pub fn new(corpus: C) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
corpus: corpus,
|
||||
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(feature = "std")]
|
||||
mod tests {
|
||||
@ -170,3 +103,4 @@ mod tests {
|
||||
assert_eq!(filename, "fancyfile");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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]
|
||||
pub fn default() -> Self {
|
||||
Testcase {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::bolts::llmp::LlmpSender;
|
||||
use alloc::{string::ToString, vec::Vec};
|
||||
use core::{marker::PhantomData, time::Duration};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use crate::bolts::llmp::LlmpReceiver;
|
||||
@ -16,16 +17,13 @@ use crate::{
|
||||
llmp::{self, LlmpClient, LlmpClientDescription, Tag},
|
||||
shmem::ShMem,
|
||||
},
|
||||
corpus::Corpus,
|
||||
events::{BrokerEventResult, Event, EventManager},
|
||||
executors::ExitKind,
|
||||
executors::{Executor, HasObservers},
|
||||
feedbacks::FeedbacksTuple,
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
state::State,
|
||||
state::IfInteresting,
|
||||
stats::Stats,
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
|
||||
@ -41,23 +39,25 @@ const _LLMP_TAG_RESTART: llmp::Tag = 0x8357A87;
|
||||
const _LLMP_TAG_NO_RESTART: llmp::Tag = 0x57A7EE71;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LlmpEventManager<I, SH, ST>
|
||||
pub struct LlmpEventManager<I, S, SH, ST>
|
||||
where
|
||||
I: Input,
|
||||
S: IfInteresting<I>,
|
||||
SH: ShMem,
|
||||
ST: Stats,
|
||||
//CE: CustomEvent<I>,
|
||||
{
|
||||
stats: Option<ST>,
|
||||
llmp: llmp::LlmpConnection<SH>,
|
||||
phantom: PhantomData<I>,
|
||||
phantom: PhantomData<(I, S)>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(unix)]
|
||||
impl<I, ST> LlmpEventManager<I, UnixShMem, ST>
|
||||
impl<I, S, ST> LlmpEventManager<I, S, UnixShMem, ST>
|
||||
where
|
||||
I: Input,
|
||||
S: IfInteresting<I>,
|
||||
ST: Stats,
|
||||
{
|
||||
/// 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
|
||||
I: Input,
|
||||
S: IfInteresting<I>,
|
||||
SH: ShMem,
|
||||
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
|
||||
I: Input,
|
||||
S: IfInteresting<I>,
|
||||
SH: ShMem,
|
||||
ST: Stats,
|
||||
{
|
||||
@ -250,20 +252,15 @@ where
|
||||
}
|
||||
|
||||
// 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,
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
state: &mut S,
|
||||
sender_id: u32,
|
||||
event: Event<I>,
|
||||
_executor: &mut E,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
R: Rand,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
match event {
|
||||
@ -284,7 +281,7 @@ where
|
||||
// TODO include ExitKind in NewTestcase
|
||||
let fitness = state.is_interesting(&input, &observers, ExitKind::Ok)?;
|
||||
if fitness > 0 {
|
||||
if !state.add_if_interesting(input, fitness)?.is_none() {
|
||||
if !state.add_if_interesting(&input, fitness)?.is_none() {
|
||||
#[cfg(feature = "std")]
|
||||
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
|
||||
I: Input,
|
||||
S: IfInteresting<I>,
|
||||
SH: ShMem,
|
||||
ST: Stats, //CE: CustomEvent<I>,
|
||||
{
|
||||
@ -317,18 +315,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn process<C, E, FT, OC, OFT, OT, R>(
|
||||
&mut self,
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
executor: &mut E,
|
||||
) -> Result<usize, Error>
|
||||
fn process<E, OT>(&mut self, state: &mut S, executor: &mut E) -> Result<usize, Error>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
R: Rand,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
// TODO: Get around local event copy by moving handle_in_client
|
||||
@ -358,19 +347,7 @@ where
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn fire<C, FT, OC, OFT, R>(
|
||||
&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>,
|
||||
{
|
||||
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
|
||||
let serialized = postcard::to_allocvec(&event)?;
|
||||
self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?;
|
||||
Ok(())
|
||||
@ -380,17 +357,13 @@ where
|
||||
/// Serialize the current state and corpus during an executiont to bytes.
|
||||
/// 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.
|
||||
pub fn serialize_state_mgr<C, FT, I, OC, OFT, R, SH, ST>(
|
||||
state: &State<C, FT, I, OC, OFT, R>,
|
||||
mgr: &LlmpEventManager<I, SH, ST>,
|
||||
pub fn serialize_state_mgr<I, S, SH, ST>(
|
||||
state: &S,
|
||||
mgr: &LlmpEventManager<I, S, SH, ST>,
|
||||
) -> Result<Vec<u8>, Error>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
S: Serialize + IfInteresting<I>,
|
||||
SH: ShMem,
|
||||
ST: Stats,
|
||||
{
|
||||
@ -398,20 +371,16 @@ where
|
||||
}
|
||||
|
||||
/// 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],
|
||||
) -> Result<(State<C, FT, I, OC, OFT, R>, LlmpEventManager<I, SH, ST>), Error>
|
||||
) -> Result<(S, LlmpEventManager<I, S, SH, ST>), Error>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
S: DeserializeOwned + IfInteresting<I>,
|
||||
SH: ShMem,
|
||||
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((
|
||||
tuple.0,
|
||||
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`)
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LlmpRestartingEventManager<I, SH, ST>
|
||||
pub struct LlmpRestartingEventManager<I, S, SH, ST>
|
||||
where
|
||||
I: Input,
|
||||
S: IfInteresting<I>,
|
||||
SH: ShMem,
|
||||
ST: Stats,
|
||||
//CE: CustomEvent<I>,
|
||||
{
|
||||
/// 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
|
||||
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
|
||||
I: Input,
|
||||
S: IfInteresting<I> + Serialize,
|
||||
SH: ShMem,
|
||||
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.
|
||||
fn on_restart<C, FT, OC, OFT, R>(
|
||||
&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>,
|
||||
{
|
||||
fn on_restart(&mut self, state: &mut S) -> Result<(), Error> {
|
||||
// First, reset the page to 0 so the next iteration can read read from the beginning of this page
|
||||
unsafe { self.sender.reset() };
|
||||
let state_corpus_serialized = serialize_state_mgr(state, &self.llmp_mgr)?;
|
||||
@ -465,35 +426,15 @@ where
|
||||
.send_buf(_LLMP_TAG_RESTART, &state_corpus_serialized)
|
||||
}
|
||||
|
||||
fn process<C, E, FT, OC, OFT, OT, R>(
|
||||
&mut self,
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
executor: &mut E,
|
||||
) -> Result<usize, Error>
|
||||
fn process<E, OT>(&mut self, state: &mut S, executor: &mut E) -> Result<usize, Error>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
R: Rand,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
self.llmp_mgr.process(state, executor)
|
||||
}
|
||||
|
||||
fn fire<C, FT, OC, OFT, R>(
|
||||
&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>,
|
||||
{
|
||||
fn fire(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error> {
|
||||
// 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)
|
||||
}
|
||||
@ -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)
|
||||
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
|
||||
I: Input,
|
||||
S: IfInteresting<I>,
|
||||
SH: ShMem,
|
||||
ST: Stats, //CE: CustomEvent<I>,
|
||||
{
|
||||
/// 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 }
|
||||
}
|
||||
|
||||
@ -530,24 +472,14 @@ where
|
||||
/// 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.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn setup_restarting_mgr<I, C, FT, OC, OFT, R, SH, ST>(
|
||||
//mgr: &mut LlmpEventManager<I, SH, ST>,
|
||||
pub fn setup_restarting_mgr<I, S, SH, ST>(
|
||||
//mgr: &mut LlmpEventManager<I, S, SH, ST>,
|
||||
stats: ST,
|
||||
broker_port: u16,
|
||||
) -> Result<
|
||||
(
|
||||
Option<State<C, FT, I, OC, OFT, R>>,
|
||||
LlmpRestartingEventManager<I, SH, ST>,
|
||||
),
|
||||
Error,
|
||||
>
|
||||
) -> Result<(Option<S>, LlmpRestartingEventManager<I, S, SH, ST>), Error>
|
||||
where
|
||||
I: Input,
|
||||
C: Corpus<I, R>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
R: Rand,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
S: DeserializeOwned + IfInteresting<I>,
|
||||
SH: ShMem,
|
||||
ST: Stats,
|
||||
{
|
||||
@ -555,7 +487,7 @@ where
|
||||
|
||||
// We start ourself as child process to actually fuzz
|
||||
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() {
|
||||
// Yep, broker. Just loop here.
|
||||
println!("Doing broker things. Run this tool again to start fuzzing in a client.");
|
||||
@ -598,7 +530,7 @@ where
|
||||
None => {
|
||||
println!("First run. Let's set it all up");
|
||||
// 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,
|
||||
)?;
|
||||
|
||||
@ -607,8 +539,7 @@ where
|
||||
// Restoring from a previous run, deserialize state and corpus.
|
||||
Some((_sender, _tag, msg)) => {
|
||||
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>) =
|
||||
deserialize_state_mgr(&msg)?;
|
||||
let (state, mgr): (S, LlmpEventManager<I, S, SH, ST>) = deserialize_state_mgr(&msg)?;
|
||||
|
||||
(Some(state), LlmpRestartingEventManager::new(mgr, sender))
|
||||
}
|
||||
|
@ -1,23 +1,20 @@
|
||||
use alloc::{string::ToString, vec::Vec};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(unix)]
|
||||
use crate::{
|
||||
corpus::Corpus,
|
||||
events::{BrokerEventResult, Event, EventManager},
|
||||
executors::{Executor, HasObservers},
|
||||
feedbacks::FeedbacksTuple,
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
state::State,
|
||||
stats::Stats,
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
|
||||
/// A simple, single-threaded event manager that just logs
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LoggerEventManager<I, ST>
|
||||
pub struct LoggerEventManager<I, S, ST>
|
||||
where
|
||||
I: Input,
|
||||
ST: Stats, //CE: CustomEvent<I, OT>,
|
||||
@ -26,25 +23,17 @@ where
|
||||
stats: ST,
|
||||
/// The events that happened since the last handle_in_broker
|
||||
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
|
||||
I: Input,
|
||||
ST: Stats, //CE: CustomEvent<I, OT>,
|
||||
{
|
||||
fn process<C, E, FT, OC, OFT, OT, R>(
|
||||
&mut self,
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
_executor: &mut E,
|
||||
) -> Result<usize, Error>
|
||||
fn process<E, OT>(&mut self, state: &mut S, _executor: &mut E) -> Result<usize, Error>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
R: Rand,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
let count = self.events.len();
|
||||
@ -55,18 +44,7 @@ where
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn fire<C, FT, OC, OFT, R>(
|
||||
&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>,
|
||||
{
|
||||
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
|
||||
match Self::handle_in_broker(&mut self.stats, &event)? {
|
||||
BrokerEventResult::Forward => self.events.push(event),
|
||||
BrokerEventResult::Handled => (),
|
||||
@ -75,7 +53,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, ST> LoggerEventManager<I, ST>
|
||||
impl<I, S, ST> LoggerEventManager<I, S, ST>
|
||||
where
|
||||
I: Input,
|
||||
ST: Stats, //TODO CE: CustomEvent,
|
||||
@ -84,6 +62,7 @@ where
|
||||
Self {
|
||||
stats: stats,
|
||||
events: vec![],
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,18 +111,7 @@ where
|
||||
}
|
||||
|
||||
// Handle arriving events in the client
|
||||
fn handle_in_client<C, FT, OC, OFT, R>(
|
||||
&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>,
|
||||
{
|
||||
fn handle_in_client(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
|
||||
match event {
|
||||
_ => Err(Error::Unknown(format!(
|
||||
"Received illegal message that message should not have arrived: {:?}.",
|
||||
|
@ -9,13 +9,9 @@ use core::{fmt, marker::PhantomData, time::Duration};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
corpus::Corpus,
|
||||
executors::{Executor, HasObservers},
|
||||
feedbacks::FeedbacksTuple,
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
state::State,
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
|
||||
@ -153,7 +149,7 @@ where
|
||||
|
||||
/// EventManager is the main communications hub.
|
||||
/// For the "normal" multi-processed mode, you may want to look into `RestartingEventManager`
|
||||
pub trait EventManager<I>
|
||||
pub trait EventManager<I, S>
|
||||
where
|
||||
I: Input,
|
||||
{
|
||||
@ -162,18 +158,9 @@ where
|
||||
|
||||
/// Lookup for incoming events and process them.
|
||||
/// Return the number of processes events or an error
|
||||
fn process<C, E, FT, OC, OFT, OT, R>(
|
||||
&mut self,
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
executor: &mut E,
|
||||
) -> Result<usize, Error>
|
||||
fn process<E, OT>(&mut self, state: &mut S, executor: &mut E) -> Result<usize, Error>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
R: Rand,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
OT: ObserversTuple;
|
||||
|
||||
/// 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.
|
||||
#[inline]
|
||||
fn on_restart<C, FT, OC, OFT, R>(
|
||||
&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>,
|
||||
{
|
||||
fn on_restart(&mut self, _state: &mut S) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -213,57 +190,27 @@ where
|
||||
fn await_restart_safe(&mut self) {}
|
||||
|
||||
/// Send off an event to the broker
|
||||
fn fire<C, FT, OC, OFT, R>(
|
||||
&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>;
|
||||
fn fire(&mut self, state: &mut S, event: Event<I>) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// An eventmgr for tests, and as placeholder if you really don't need an event manager.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct NopEventManager<I> {
|
||||
phantom: PhantomData<I>,
|
||||
pub struct NopEventManager<I, S> {
|
||||
phantom: PhantomData<(I, S)>,
|
||||
}
|
||||
impl<I> EventManager<I> for NopEventManager<I>
|
||||
impl<I, S> EventManager<I, S> for NopEventManager<I, S>
|
||||
where
|
||||
I: Input,
|
||||
{
|
||||
fn process<C, E, FT, OC, OFT, OT, R>(
|
||||
&mut self,
|
||||
_state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
_executor: &mut E,
|
||||
) -> Result<usize, Error>
|
||||
fn process<E, OT>(&mut self, _state: &mut S, _executor: &mut E) -> Result<usize, Error>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
R: Rand,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fire<C, FT, OC, OFT, R>(
|
||||
&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>,
|
||||
{
|
||||
fn fire(&mut self, _state: &mut S, _event: Event<I>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,7 @@ use crate::{
|
||||
feedbacks::FeedbacksTuple,
|
||||
inputs::{HasTargetBytes, Input},
|
||||
observers::ObserversTuple,
|
||||
state::State,
|
||||
utils::Rand,
|
||||
state::{HasObjectives, HasSolutions},
|
||||
Error,
|
||||
};
|
||||
|
||||
@ -54,52 +53,27 @@ where
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
#[inline]
|
||||
fn pre_exec<C, EM, FT, OC, OFT, R>(
|
||||
&mut self,
|
||||
_state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
_event_mgr: &mut EM,
|
||||
_input: &I,
|
||||
) -> Result<(), Error>
|
||||
fn pre_exec<EM, S>(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error>
|
||||
where
|
||||
R: Rand,
|
||||
FT: FeedbacksTuple<I>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
C: Corpus<I, R>,
|
||||
EM: EventManager<I>,
|
||||
EM: EventManager<I, S>,
|
||||
{
|
||||
#[cfg(unix)]
|
||||
#[cfg(feature = "std")]
|
||||
unsafe {
|
||||
set_oncrash_ptrs::<C, EM, FT, I, OC, OFT, OT, R>(
|
||||
_state,
|
||||
_event_mgr,
|
||||
self.observers(),
|
||||
_input,
|
||||
);
|
||||
set_oncrash_ptrs(state, event_mgr, self.observers(), input);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn post_exec<C, EM, FT, OC, OFT, R>(
|
||||
&mut self,
|
||||
_state: &State<C, FT, I, OC, OFT, R>,
|
||||
_event_mgr: &mut EM,
|
||||
_input: &I,
|
||||
) -> Result<(), Error>
|
||||
fn post_exec<EM, S>(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error>
|
||||
where
|
||||
R: Rand,
|
||||
FT: FeedbacksTuple<I>,
|
||||
C: Corpus<I, R>,
|
||||
EM: EventManager<I>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
EM: EventManager<I, S>,
|
||||
{
|
||||
#[cfg(unix)]
|
||||
#[cfg(feature = "std")]
|
||||
unsafe {
|
||||
reset_oncrash_ptrs::<C, EM, FT, I, OT, R>();
|
||||
reset_oncrash_ptrs();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -148,28 +122,24 @@ where
|
||||
/// depnding on different corpus or state.
|
||||
/// * `name` - the name of this executor (to address it along the way)
|
||||
/// * `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
|
||||
pub fn new<C, EM, FT, OC, OFT, R>(
|
||||
pub fn new<EM, OC, OFT, S>(
|
||||
name: &'static str,
|
||||
harness_fn: HarnessFunction<Self>,
|
||||
observers: OT,
|
||||
_state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
_state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
) -> Self
|
||||
where
|
||||
R: Rand,
|
||||
FT: FeedbacksTuple<I>,
|
||||
OC: Corpus<I, R>,
|
||||
EM: EventManager<I, S>,
|
||||
OC: Corpus<I>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
C: Corpus<I, R>,
|
||||
EM: EventManager<I>,
|
||||
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
||||
{
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(unix)]
|
||||
unsafe {
|
||||
setup_crash_handlers::<C, EM, FT, I, OC, OFT, OT, R>();
|
||||
setup_crash_handlers::<EM, I, OC, OFT, OT, S>();
|
||||
}
|
||||
|
||||
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(unix)]
|
||||
pub mod unix_signals {
|
||||
@ -219,14 +170,13 @@ pub mod unix_signals {
|
||||
};
|
||||
|
||||
use crate::{
|
||||
corpus::Corpus,
|
||||
corpus::{Corpus, Testcase},
|
||||
events::{Event, EventManager},
|
||||
executors::ExitKind,
|
||||
feedbacks::FeedbacksTuple,
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
state::State,
|
||||
utils::Rand,
|
||||
state::{HasObjectives, HasSolutions},
|
||||
};
|
||||
/// Let's get 8 mb for now.
|
||||
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.
|
||||
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>(
|
||||
_sig: c_int,
|
||||
info: siginfo_t,
|
||||
_void: c_void,
|
||||
) where
|
||||
EM: EventManager<I>,
|
||||
C: Corpus<I, R>,
|
||||
unsafe fn inmem_handle_crash<EM, I, OC, OFT, OT, S>(_sig: c_int, info: siginfo_t, _void: c_void)
|
||||
where
|
||||
EM: EventManager<I, S>,
|
||||
OT: ObserversTuple,
|
||||
OC: Corpus<I, R>,
|
||||
OC: Corpus<I>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
if CURRENT_INPUT_PTR == ptr::null() {
|
||||
println!(
|
||||
@ -267,7 +212,6 @@ pub mod unix_signals {
|
||||
Ok(maps) => println!("maps:\n{}", maps),
|
||||
Err(e) => println!("Couldn't load mappings: {:?}", e),
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
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();
|
||||
// Make sure we don't crash in the crash handler forever.
|
||||
CURRENT_INPUT_PTR = ptr::null();
|
||||
let state = (STATE_PTR as *mut State<C, FT, I, OC, OFT, R>)
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let state = (STATE_PTR as *mut S).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 obj_fitness = state
|
||||
.objective_feedbacks_mut()
|
||||
.objectives_mut()
|
||||
.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 !state
|
||||
.add_if_objective(input.clone(), obj_fitness)
|
||||
.expect("In crash handler objective corpus add failure.".into())
|
||||
.is_none()
|
||||
{
|
||||
mgr.fire(
|
||||
state,
|
||||
Event::Objective {
|
||||
objective_size: state.objective_corpus().count(),
|
||||
},
|
||||
)
|
||||
.expect(&format!("Could not send timeouting input {:?}", input));
|
||||
}
|
||||
state
|
||||
.solutions_mut()
|
||||
.add(Testcase::new(input.clone()))
|
||||
.expect("In crash handler solutions failure.".into());
|
||||
mgr.fire(
|
||||
state,
|
||||
Event::Objective {
|
||||
objective_size: state.solutions().count(),
|
||||
},
|
||||
)
|
||||
.expect("Could not send crashing input".into());
|
||||
}
|
||||
|
||||
mgr.on_restart(state).unwrap();
|
||||
@ -324,19 +264,17 @@ pub mod unix_signals {
|
||||
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,
|
||||
_info: siginfo_t,
|
||||
_void: c_void,
|
||||
) where
|
||||
EM: EventManager<I>,
|
||||
C: Corpus<I, R>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
EM: EventManager<I, S>,
|
||||
OT: ObserversTuple,
|
||||
FT: FeedbacksTuple<I>,
|
||||
OC: Corpus<I>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
dbg!("TIMEOUT/SIGUSR2 received");
|
||||
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();
|
||||
// Make sure we don't crash in the crash handler forever.
|
||||
CURRENT_INPUT_PTR = ptr::null();
|
||||
let state = (STATE_PTR as *mut State<C, FT, I, OC, OFT, R>)
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let state = (STATE_PTR as *mut S).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 obj_fitness = state
|
||||
.objective_feedbacks_mut()
|
||||
.is_interesting_all(&input, observers, ExitKind::Timeout)
|
||||
.expect("In timeout handler objective feedbacks failure.".into());
|
||||
.objectives_mut()
|
||||
.is_interesting_all(&input, observers, ExitKind::Crash)
|
||||
.expect("In timeout handler objectives failure.".into());
|
||||
if obj_fitness > 0 {
|
||||
if !state
|
||||
.add_if_objective(input.clone(), obj_fitness)
|
||||
.expect("In timeout handler objective corpus add failure.".into())
|
||||
.is_none()
|
||||
{
|
||||
mgr.fire(
|
||||
state,
|
||||
Event::Objective {
|
||||
objective_size: state.objective_corpus().count(),
|
||||
},
|
||||
)
|
||||
.expect(&format!("Could not send timeouting input {:?}", input));
|
||||
}
|
||||
state
|
||||
.solutions_mut()
|
||||
.add(Testcase::new(input.clone()))
|
||||
.expect("In timeout handler solutions failure.".into());
|
||||
mgr.fire(
|
||||
state,
|
||||
Event::Objective {
|
||||
objective_size: state.solutions().count(),
|
||||
},
|
||||
)
|
||||
.expect("Could not send timeouting input".into());
|
||||
}
|
||||
|
||||
mgr.on_restart(state).unwrap();
|
||||
|
||||
println!("Waiting for broker...");
|
||||
mgr.await_restart_safe();
|
||||
println!("Bye!");
|
||||
|
||||
mgr.await_restart_safe();
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn set_oncrash_ptrs<C, EM, FT, I, OC, OFT, OT, R>(
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
pub unsafe fn set_oncrash_ptrs<EM, I, OT, S>(
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
observers: &OT,
|
||||
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;
|
||||
STATE_PTR = state 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]
|
||||
pub unsafe fn reset_oncrash_ptrs<C, EM, FT, I, OT, R>() {
|
||||
pub unsafe fn reset_oncrash_ptrs() {
|
||||
CURRENT_INPUT_PTR = ptr::null();
|
||||
STATE_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<C, EM, FT, I, OC, OFT, OT, R>()
|
||||
pub unsafe fn setup_crash_handlers<EM, I, OC, OFT, OT, S>()
|
||||
where
|
||||
EM: EventManager<I>,
|
||||
C: Corpus<I, R>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
EM: EventManager<I, S>,
|
||||
OT: ObserversTuple,
|
||||
FT: FeedbacksTuple<I>,
|
||||
OC: Corpus<I>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
// First, set up our own stack to be used during segfault handling. (and specify `SA_ONSTACK` in `sigaction`)
|
||||
if SIGNAL_STACK_PTR.is_null() {
|
||||
@ -440,8 +366,7 @@ pub mod unix_signals {
|
||||
let mut sa: sigaction = mem::zeroed();
|
||||
libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
|
||||
sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
|
||||
sa.sa_sigaction =
|
||||
libaflrs_executor_inmem_handle_crash::<C, EM, FT, I, OC, OFT, OT, R> as usize;
|
||||
sa.sa_sigaction = inmem_handle_crash::<EM, I, OC, OFT, OT, S> as usize;
|
||||
for (sig, msg) in &[
|
||||
(SIGSEGV, "segfault"),
|
||||
(SIGBUS, "sigbus"),
|
||||
@ -455,8 +380,7 @@ pub mod unix_signals {
|
||||
}
|
||||
}
|
||||
|
||||
sa.sa_sigaction =
|
||||
libaflrs_executor_inmem_handle_timeout::<C, EM, FT, I, OC, OFT, OT, R> as usize;
|
||||
sa.sa_sigaction = inmem_handle_timeout::<EM, I, OC, OFT, OT, S> as usize;
|
||||
if sigaction(SIGUSR2, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
|
||||
panic!("Could not set up sigusr2 handler for timeouts");
|
||||
}
|
||||
|
@ -10,13 +10,9 @@ use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
bolts::tuples::{MatchNameAndType, MatchType, Named, TupleList},
|
||||
corpus::Corpus,
|
||||
events::EventManager,
|
||||
feedbacks::FeedbacksTuple,
|
||||
inputs::{HasTargetBytes, Input},
|
||||
observers::ObserversTuple,
|
||||
state::State,
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
|
||||
@ -84,38 +80,23 @@ where
|
||||
{
|
||||
#[inline]
|
||||
/// Called right before exexution starts
|
||||
fn pre_exec<C, EM, FT, OC, OFT, R>(
|
||||
fn pre_exec<EM, S>(
|
||||
&mut self,
|
||||
_state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
_state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
_input: &I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
R: Rand,
|
||||
FT: FeedbacksTuple<I>,
|
||||
C: Corpus<I, R>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
EM: EventManager<I>,
|
||||
EM: EventManager<I, S>,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Called right after execution finished.
|
||||
fn post_exec<C, EM, FT, OC, OFT, R>(
|
||||
&mut self,
|
||||
_state: &State<C, FT, I, OC, OFT, R>,
|
||||
_event_mgr: &mut EM,
|
||||
_input: &I,
|
||||
) -> Result<(), Error>
|
||||
fn post_exec<EM, S>(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error>
|
||||
where
|
||||
R: Rand,
|
||||
FT: FeedbacksTuple<I>,
|
||||
C: Corpus<I, R>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
EM: EventManager<I>,
|
||||
EM: EventManager<I, S>,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
@ -148,13 +148,6 @@ where
|
||||
self.0.discard_metadata(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
|
||||
|
148
libafl/src/fuzzer.rs
Normal file
148
libafl/src/fuzzer.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ use alloc::{borrow::ToOwned, rc::Rc, vec::Vec};
|
||||
use core::{cell::RefCell, convert::From};
|
||||
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
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
|
||||
|
@ -106,4 +106,3 @@ pub trait HasLen {
|
||||
/// The lenght
|
||||
fn len(&self) -> usize;
|
||||
}
|
||||
|
||||
|
@ -25,144 +25,15 @@ pub mod state;
|
||||
pub mod stats;
|
||||
pub mod utils;
|
||||
|
||||
pub mod fuzzer;
|
||||
pub use fuzzer::*;
|
||||
|
||||
use alloc::string::String;
|
||||
use core::{fmt, marker::PhantomData};
|
||||
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};
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
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
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@ -245,6 +116,7 @@ impl From<ParseIntError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: no_std test
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(test)]
|
||||
@ -327,3 +199,4 @@ mod tests {
|
||||
assert_eq!(state.corpus().count(), corpus_deserialized.count());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -7,38 +7,23 @@ pub use mutations::*;
|
||||
pub mod token_mutations;
|
||||
pub use token_mutations::*;
|
||||
|
||||
use crate::{
|
||||
corpus::Corpus,
|
||||
inputs::Input,
|
||||
state::{HasCorpus, HasMetadata},
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
use crate::{inputs::Input, Error};
|
||||
|
||||
// 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
|
||||
|
||||
/// A mutator takes input, and mutates it.
|
||||
/// Simple as that.
|
||||
pub trait Mutator<C, I, R, S>
|
||||
pub trait Mutator<I, S>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
/// Mutate a given input
|
||||
fn mutate(
|
||||
&mut self,
|
||||
rand: &mut R,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
stage_idx: i32,
|
||||
) -> Result<(), Error>;
|
||||
fn mutate(&self, state: &mut S, input: &mut I, stage_idx: i32) -> Result<(), Error>;
|
||||
|
||||
/// Post-process given the outcome of the execution
|
||||
fn post_exec(
|
||||
&mut self,
|
||||
&self,
|
||||
_state: &mut S,
|
||||
_is_interesting: u32,
|
||||
_stage_idx: i32,
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::{
|
||||
corpus::Corpus,
|
||||
inputs::{HasBytesVec, Input},
|
||||
mutators::Corpus,
|
||||
mutators::*,
|
||||
state::{HasCorpus, HasRand},
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
@ -15,42 +16,6 @@ use std::{
|
||||
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.
|
||||
/// If the mutation got skipped, the target
|
||||
/// will not be executed with the returned input.
|
||||
@ -62,24 +27,20 @@ pub enum MutationResult {
|
||||
|
||||
// TODO maybe the mutator arg is not needed
|
||||
/// The generic function type that identifies mutations
|
||||
pub type MutationFunction<I, M, R, S> =
|
||||
fn(&mut M, &mut R, &mut S, &mut I) -> Result<MutationResult, Error>;
|
||||
pub type MutationFunction<I, M, S> = fn(&M, &mut S, &mut I) -> Result<MutationResult, Error>;
|
||||
|
||||
pub trait ComposedByMutations<C, I, R, S>
|
||||
pub trait ComposedByMutations<I, S>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
/// 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
|
||||
fn mutations_count(&self) -> usize;
|
||||
|
||||
/// 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
|
||||
@ -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
|
||||
pub fn mutation_bitflip<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() == 0 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} 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 {
|
||||
// Moar speed, no bound check
|
||||
*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>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() == 0 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} 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 {
|
||||
// Moar speed, no bound check
|
||||
*input.bytes_mut().get_unchecked_mut(idx) ^= 0xff;
|
||||
@ -171,19 +170,20 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_byteinc<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() == 0 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} 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 {
|
||||
// Moar speed, no bound check
|
||||
let ptr = input.bytes_mut().get_unchecked_mut(idx);
|
||||
@ -194,19 +194,20 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_bytedec<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() == 0 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} 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 {
|
||||
// Moar speed, no bound check
|
||||
let ptr = input.bytes_mut().get_unchecked_mut(idx);
|
||||
@ -217,19 +218,20 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_byteneg<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() == 0 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} 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 {
|
||||
// Moar speed, no bound check
|
||||
*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>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() == 0 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} 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 {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mutation_byteadd<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() == 0 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} 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 {
|
||||
// Moar speed, no bound check
|
||||
let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut u8;
|
||||
let num = 1 + rand.below(ARITH_MAX) as u8;
|
||||
match rand.below(2) {
|
||||
let num = 1 + state.rand_mut().below(ARITH_MAX) as u8;
|
||||
match state.rand_mut().below(2) {
|
||||
0 => *ptr = (*ptr).wrapping_add(num),
|
||||
_ => *ptr = (*ptr).wrapping_sub(num),
|
||||
};
|
||||
@ -288,24 +292,25 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_wordadd<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() < 2 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} 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 {
|
||||
// Moar speed, no bound check
|
||||
let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16;
|
||||
let num = 1 + rand.below(ARITH_MAX) as u16;
|
||||
match rand.below(4) {
|
||||
let num = 1 + state.rand_mut().below(ARITH_MAX) as u16;
|
||||
match state.rand_mut().below(4) {
|
||||
0 => *ptr = (*ptr).wrapping_add(num),
|
||||
1 => *ptr = (*ptr).wrapping_sub(num),
|
||||
2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(),
|
||||
@ -317,24 +322,25 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_dwordadd<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() < 4 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} 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 {
|
||||
// Moar speed, no bound check
|
||||
let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32;
|
||||
let num = 1 + rand.below(ARITH_MAX) as u32;
|
||||
match rand.below(4) {
|
||||
let num = 1 + state.rand_mut().below(ARITH_MAX) as u32;
|
||||
match state.rand_mut().below(4) {
|
||||
0 => *ptr = (*ptr).wrapping_add(num),
|
||||
1 => *ptr = (*ptr).wrapping_sub(num),
|
||||
2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(),
|
||||
@ -346,24 +352,25 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_qwordadd<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() < 8 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} 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 {
|
||||
// Moar speed, no bound check
|
||||
let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u64;
|
||||
let num = 1 + rand.below(ARITH_MAX) as u64;
|
||||
match rand.below(4) {
|
||||
let num = 1 + state.rand_mut().below(ARITH_MAX) as u64;
|
||||
match state.rand_mut().below(4) {
|
||||
0 => *ptr = (*ptr).wrapping_add(num),
|
||||
1 => *ptr = (*ptr).wrapping_sub(num),
|
||||
2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(),
|
||||
@ -375,20 +382,21 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_byteinteresting<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() == 0 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} else {
|
||||
let idx = rand.below(input.bytes().len() as u64) as usize;
|
||||
let val = INTERESTING_8[rand.below(INTERESTING_8.len() as u64) as usize] as u8;
|
||||
let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
|
||||
let val = INTERESTING_8[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u8;
|
||||
unsafe {
|
||||
// Moar speed, no bound check
|
||||
*input.bytes_mut().get_unchecked_mut(idx) = val;
|
||||
@ -398,24 +406,26 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_wordinteresting<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() < 2 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} else {
|
||||
let idx = rand.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 idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize;
|
||||
let val =
|
||||
INTERESTING_16[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u16;
|
||||
unsafe {
|
||||
// Moar speed, no bound check
|
||||
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;
|
||||
} else {
|
||||
*ptr = val.swap_bytes();
|
||||
@ -426,24 +436,26 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_dwordinteresting<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
if input.bytes().len() < 4 {
|
||||
Ok(MutationResult::Skipped)
|
||||
} else {
|
||||
let idx = rand.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 idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize;
|
||||
let val =
|
||||
INTERESTING_32[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u32;
|
||||
unsafe {
|
||||
// Moar speed, no bound check
|
||||
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;
|
||||
} else {
|
||||
*ptr = val.swap_bytes();
|
||||
@ -454,13 +466,14 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_bytesdelete<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
@ -468,28 +481,28 @@ where
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let off = rand.below(size as u64) as usize;
|
||||
let len = rand.below((size - off) as u64) as usize;
|
||||
let off = state.rand_mut().below(size as u64) as usize;
|
||||
let len = state.rand_mut().below((size - off) as u64) as usize;
|
||||
input.bytes_mut().drain(off..off + len);
|
||||
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
|
||||
pub fn mutation_bytesexpand<I, M, R, S>(
|
||||
// TODO: max_size instead of mutator?
|
||||
mutator: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
mutator: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
M: HasMaxSize,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
let off = rand.below((size + 1) as u64) as usize;
|
||||
let mut len = 1 + rand.below(16) as usize;
|
||||
let off = state.rand_mut().below((size + 1) as u64) as usize;
|
||||
let mut len = 1 + state.rand_mut().below(16) as usize;
|
||||
|
||||
if size + len > mutator.max_size() {
|
||||
if mutator.max_size() > size {
|
||||
@ -506,19 +519,20 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_bytesinsert<I, M, R, S>(
|
||||
mutator: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
mutator: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
M: HasMaxSize,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
let off = rand.below((size + 1) as u64) as usize;
|
||||
let mut len = 1 + rand.below(16) as usize;
|
||||
let off = state.rand_mut().below((size + 1) as u64) as usize;
|
||||
let mut len = 1 + state.rand_mut().below(16) as usize;
|
||||
|
||||
if size + len > mutator.max_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);
|
||||
buffer_self_copy(input.bytes_mut(), off, off + len, size - off);
|
||||
@ -538,19 +552,20 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_bytesrandinsert<I, M, R, S>(
|
||||
mutator: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
mutator: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
M: HasMaxSize,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
let off = rand.below((size + 1) as u64) as usize;
|
||||
let mut len = 1 + rand.below(16) as usize;
|
||||
let off = state.rand_mut().below((size + 1) as u64) as usize;
|
||||
let mut len = 1 + state.rand_mut().below(16) as usize;
|
||||
|
||||
if size + len > mutator.max_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);
|
||||
buffer_self_copy(input.bytes_mut(), off, off + len, size - off);
|
||||
@ -570,23 +585,24 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_bytesset<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
if size == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
let off = rand.below(size as u64) as usize;
|
||||
let len = 1 + rand.below(min(16, size - off) as u64) as usize;
|
||||
let off = state.rand_mut().below(size 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);
|
||||
|
||||
@ -594,23 +610,24 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_bytesrandset<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
if size == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
let off = rand.below(size as u64) as usize;
|
||||
let len = 1 + rand.below(min(16, size - off) as u64) as usize;
|
||||
let off = state.rand_mut().below(size 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);
|
||||
|
||||
@ -618,13 +635,14 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_bytescopy<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
@ -632,9 +650,9 @@ where
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let from = rand.below(input.bytes().len() as u64) as usize;
|
||||
let to = rand.below(input.bytes().len() as u64) as usize;
|
||||
let len = 1 + rand.below((size - max(from, to)) as u64) as usize;
|
||||
let from = state.rand_mut().below(input.bytes().len() as u64) as usize;
|
||||
let to = state.rand_mut().below(input.bytes().len() 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);
|
||||
|
||||
@ -642,13 +660,14 @@ where
|
||||
}
|
||||
|
||||
pub fn mutation_bytesswap<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &mut S,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
@ -656,9 +675,9 @@ where
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let first = rand.below(input.bytes().len() as u64) as usize;
|
||||
let second = rand.below(input.bytes().len() as u64) as usize;
|
||||
let len = 1 + rand.below((size - max(first, second)) as u64) as usize;
|
||||
let first = state.rand_mut().below(input.bytes().len() as u64) as usize;
|
||||
let second = state.rand_mut().below(input.bytes().len() 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();
|
||||
buffer_self_copy(input.bytes_mut(), second, first, len);
|
||||
@ -669,37 +688,46 @@ where
|
||||
|
||||
/// Crossover insert mutation
|
||||
pub fn mutation_crossover_insert<C, I, M, R, S>(
|
||||
mutator: &mut M,
|
||||
rand: &mut R,
|
||||
mutator: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
M: HasMaxSize,
|
||||
C: Corpus<I, R>,
|
||||
C: Corpus<I>,
|
||||
I: Input + HasBytesVec,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R>,
|
||||
S: HasRand<R> + HasCorpus<C, I>,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
|
||||
// We don't want to use the testcase we're already using for splicing
|
||||
let (other_testcase, idx) = state.corpus().random_entry(rand)?;
|
||||
if idx == state.corpus().current_testcase().1 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
let count = state.corpus().count();
|
||||
let idx = state.rand_mut().below(count as u64) as usize;
|
||||
if let Some(cur) = state.corpus().current() {
|
||||
if idx == *cur {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
}
|
||||
|
||||
let mut other_ref = other_testcase.borrow_mut();
|
||||
let other = other_ref.load_input()?;
|
||||
|
||||
let other_size = other.bytes().len();
|
||||
let other_size = state
|
||||
.corpus()
|
||||
.get(idx)?
|
||||
.borrow_mut()
|
||||
.load_input()?
|
||||
.bytes()
|
||||
.len();
|
||||
if other_size < 2 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let from = rand.below(other_size as u64) as usize;
|
||||
let to = rand.below(size as u64) as usize;
|
||||
let mut len = rand.below((other_size - from) as u64) as usize;
|
||||
let from = state.rand_mut().below(other_size as u64) as usize;
|
||||
let to = state.rand_mut().below(size 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 mutator.max_size() > size {
|
||||
@ -718,36 +746,45 @@ where
|
||||
|
||||
/// Crossover replace mutation
|
||||
pub fn mutation_crossover_replace<C, I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
C: Corpus<I>,
|
||||
I: Input + HasBytesVec,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R>,
|
||||
S: HasRand<R> + HasCorpus<C, I>,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
|
||||
// We don't want to use the testcase we're already using for splicing
|
||||
let (other_testcase, idx) = state.corpus().random_entry(rand)?;
|
||||
if idx == state.corpus().current_testcase().1 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
let count = state.corpus().count();
|
||||
let idx = state.rand_mut().below(count as u64) as usize;
|
||||
if let Some(cur) = state.corpus().current() {
|
||||
if idx == *cur {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
}
|
||||
|
||||
let mut other_ref = other_testcase.borrow_mut();
|
||||
let other = other_ref.load_input()?;
|
||||
|
||||
let other_size = other.bytes().len();
|
||||
let other_size = state
|
||||
.corpus()
|
||||
.get(idx)?
|
||||
.borrow_mut()
|
||||
.load_input()?
|
||||
.bytes()
|
||||
.len();
|
||||
if other_size < 2 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let from = rand.below(other_size as u64) as usize;
|
||||
let len = rand.below(min(other_size - from, size) as u64) as usize;
|
||||
let to = rand.below((size - len) as u64) as usize;
|
||||
let from = state.rand_mut().below(other_size as u64) as usize;
|
||||
let len = state.rand_mut().below(min(other_size - from, size) 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);
|
||||
|
||||
@ -772,40 +809,50 @@ fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) {
|
||||
|
||||
/// Splicing mutation from AFL
|
||||
pub fn mutation_splice<C, I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &M,
|
||||
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
C: Corpus<I>,
|
||||
I: Input + HasBytesVec,
|
||||
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
|
||||
let (other_testcase, idx) = state.corpus().random_entry(rand)?;
|
||||
if idx == state.corpus().current_testcase().1 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
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 {
|
||||
let count = state.corpus().count();
|
||||
let idx = state.rand_mut().below(count as u64) as usize;
|
||||
if let Some(cur) = state.corpus().current() {
|
||||
if idx == *cur {
|
||||
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
|
||||
.bytes_mut()
|
||||
.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)
|
||||
}
|
||||
|
||||
/*
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(feature = "std")]
|
||||
@ -1039,3 +1087,4 @@ token2="B"
|
||||
*/
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -1,72 +1,58 @@
|
||||
use crate::inputs::HasBytesVec;
|
||||
use alloc::vec::Vec;
|
||||
use core::{fmt, default::Default, marker::PhantomData};
|
||||
use core::{default::Default, fmt, marker::PhantomData};
|
||||
use fmt::Debug;
|
||||
|
||||
use crate::{
|
||||
inputs::{HasBytesVec, Input},
|
||||
mutators::{Corpus, *},
|
||||
state::{HasCorpus, HasMetadata},
|
||||
corpus::Corpus,
|
||||
inputs::Input,
|
||||
mutators::{HasMaxSize, Mutator, DEFAULT_MAX_SIZE},
|
||||
state::{HasCorpus, HasMetadata, HasRand},
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
|
||||
pub trait ScheduledMutator<C, I, R, S>:
|
||||
Mutator<C, I, R, S> + ComposedByMutations<C, I, R, S>
|
||||
pub use crate::mutators::mutations::*;
|
||||
pub use crate::mutators::token_mutations::*;
|
||||
|
||||
pub trait ScheduledMutator<I, S>: Mutator<I, S> + ComposedByMutations<I, S>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
/// Compute the number of iterations used to apply stacked mutations
|
||||
#[inline]
|
||||
fn iterations(&mut self, rand: &mut R, _input: &I) -> u64 {
|
||||
1 << (1 + rand.below(6))
|
||||
}
|
||||
fn iterations(&self, state: &mut S, input: &I) -> u64;
|
||||
|
||||
/// Get the next mutation to apply
|
||||
#[inline]
|
||||
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
|
||||
}
|
||||
fn schedule(&self, mutations_count: usize, state: &mut S, input: &I) -> usize;
|
||||
|
||||
/// New default implementation for mutate
|
||||
/// Implementations must forward mutate() to this method
|
||||
fn scheduled_mutate(
|
||||
&mut self,
|
||||
rand: &mut R,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
_stage_idx: i32,
|
||||
) -> Result<(), Error> {
|
||||
let num = self.iterations(rand, input);
|
||||
fn scheduled_mutate(&self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<(), Error> {
|
||||
let num = self.iterations(state, input);
|
||||
for _ in 0..num {
|
||||
let idx = self.schedule(self.mutations_count(), rand, input);
|
||||
self.mutation_by_idx(idx)(self, rand, state, input)?;
|
||||
let idx = self.schedule(self.mutations_count(), state, input);
|
||||
self.mutation_by_idx(idx)(self, state, input)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StdScheduledMutator<C, I, R, S>
|
||||
pub struct StdScheduledMutator<I, R, S>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
mutations: Vec<MutationFunction<I, Self, R, S>>,
|
||||
mutations: Vec<MutationFunction<I, Self, S>>,
|
||||
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
|
||||
C: Corpus<I, R>,
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
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
|
||||
C: Corpus<I, R>,
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
rand: &mut R,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
_stage_idx: i32,
|
||||
) -> Result<(), Error> {
|
||||
self.scheduled_mutate(rand, state, input, _stage_idx)
|
||||
fn mutate(&self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<(), Error> {
|
||||
self.scheduled_mutate(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
|
||||
C: Corpus<I, R>,
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
#[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]
|
||||
}
|
||||
|
||||
@ -115,27 +93,34 @@ where
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
C: Corpus<I, R>,
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
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
|
||||
C: Corpus<I, R>,
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
#[inline]
|
||||
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
|
||||
C: Corpus<I, R>,
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
/// Create a new StdScheduledMutator instance without mutations and corpus
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mutations: vec![],
|
||||
max_size: DEFAULT_MAX_SIZE,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
mutations: mutations,
|
||||
max_size: DEFAULT_MAX_SIZE,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// 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
|
||||
SM: ScheduledMutator<C, I, R, S> + HasMaxSize,
|
||||
C: Corpus<I, R>,
|
||||
SM: ScheduledMutator<I, S> + HasMaxSize,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata,
|
||||
C: Corpus<I>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
scheduled: SM,
|
||||
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
|
||||
SM: ScheduledMutator<C, I, R, S> + HasMaxSize,
|
||||
C: Corpus<I, R>,
|
||||
SM: ScheduledMutator<I, S> + HasMaxSize,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata,
|
||||
C: Corpus<I>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
/// Mutate bytes
|
||||
fn mutate(
|
||||
&mut self,
|
||||
rand: &mut R,
|
||||
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);
|
||||
fn mutate(&self, state: &mut S, input: &mut I, stage_idx: i32) -> Result<(), Error> {
|
||||
self.scheduled.mutate(state, input, stage_idx)?;
|
||||
/*let num = self.scheduled.iterations(state, input);
|
||||
for _ in 0..num {
|
||||
let idx = self.scheduled.schedule(14, rand, input);
|
||||
let idx = self.scheduled.schedule(14, state, input);
|
||||
let mutation = match idx {
|
||||
0 => mutation_bitflip,
|
||||
1 => mutation_byteflip,
|
||||
@ -222,19 +202,19 @@ where
|
||||
11 => mutation_dwordinteresting,
|
||||
_ => mutation_splice,
|
||||
};
|
||||
mutation(self, rand, state, input)?;
|
||||
mutation(self, state, input)?;
|
||||
}*/
|
||||
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
|
||||
SM: ScheduledMutator<C, I, R, S> + HasMaxSize,
|
||||
C: Corpus<I, R>,
|
||||
SM: ScheduledMutator<I, S> + HasMaxSize,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata,
|
||||
C: Corpus<I>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
#[inline]
|
||||
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
|
||||
SM: ScheduledMutator<C, I, R, S> + HasMaxSize,
|
||||
C: Corpus<I, R>,
|
||||
SM: ScheduledMutator<I, S> + HasMaxSize,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata,
|
||||
C: Corpus<I>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
/// Create a new HavocBytesMutator instance given a ScheduledMutator to wrap
|
||||
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
|
||||
C: Corpus<I, R>,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata,
|
||||
C: Corpus<I>,
|
||||
R: Rand,
|
||||
S: HasCorpus<C, I, R> + HasMetadata,
|
||||
{
|
||||
/// Create a new HavocBytesMutator instance wrapping StdScheduledMutator
|
||||
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_byteflip);
|
||||
scheduled.add_mutation(mutation_byteinc);
|
||||
@ -317,6 +297,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
@ -390,3 +371,4 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -4,6 +4,7 @@
|
||||
use crate::{
|
||||
inputs::{HasBytesVec, Input},
|
||||
mutators::*,
|
||||
state::{HasMetadata, HasRand},
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
@ -30,33 +31,33 @@ impl TokensMetadata {
|
||||
|
||||
/// Insert a dictionary token
|
||||
pub fn mutation_tokeninsert<I, M, R, S>(
|
||||
mutator: &mut M,
|
||||
rand: &mut R,
|
||||
mutator: &M,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
M: HasMaxSize,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R>,
|
||||
R: Rand,
|
||||
S: HasMetadata,
|
||||
{
|
||||
let meta;
|
||||
match state.metadata().get::<TokensMetadata>() {
|
||||
Some(t) => {
|
||||
meta = t;
|
||||
}
|
||||
None => {
|
||||
let tokens_len = {
|
||||
let meta = state.metadata().get::<TokensMetadata>();
|
||||
if meta.is_none() {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
if meta.unwrap().tokens.len() == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
meta.unwrap().tokens.len()
|
||||
};
|
||||
if meta.tokens.len() == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
let token = &meta.tokens[rand.below(meta.tokens.len() as u64) as usize];
|
||||
let token_idx = state.rand_mut().below(tokens_len as u64) as usize;
|
||||
|
||||
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();
|
||||
|
||||
if size + len > mutator.max_size() {
|
||||
@ -76,37 +77,37 @@ where
|
||||
|
||||
/// Overwrite with a dictionary token
|
||||
pub fn mutation_tokenreplace<I, M, R, S>(
|
||||
_: &mut M,
|
||||
rand: &mut R,
|
||||
_: &M,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
) -> Result<MutationResult, Error>
|
||||
where
|
||||
M: HasMaxSize,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R>,
|
||||
R: Rand,
|
||||
S: HasMetadata,
|
||||
{
|
||||
let size = input.bytes().len();
|
||||
if size == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let meta;
|
||||
match state.metadata().get::<TokensMetadata>() {
|
||||
Some(t) => {
|
||||
meta = t;
|
||||
}
|
||||
None => {
|
||||
let tokens_len = {
|
||||
let meta = state.metadata().get::<TokensMetadata>();
|
||||
if meta.is_none() {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
if meta.unwrap().tokens.len() == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
meta.unwrap().tokens.len()
|
||||
};
|
||||
if meta.tokens.len() == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
let token = &meta.tokens[rand.below(meta.tokens.len() as u64) as usize];
|
||||
let token_idx = state.rand_mut().below(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();
|
||||
if off + len > size {
|
||||
len = size - off;
|
||||
|
@ -2,128 +2,69 @@ pub mod mutational;
|
||||
pub use mutational::StdMutationalStage;
|
||||
|
||||
use crate::{
|
||||
bolts::tuples::TupleList,
|
||||
corpus::Corpus,
|
||||
events::EventManager,
|
||||
executors::{Executor, HasObservers},
|
||||
feedbacks::FeedbacksTuple,
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
state::State,
|
||||
utils::Rand,
|
||||
Error,
|
||||
bolts::tuples::TupleList, events::EventManager, executors::Executor, inputs::Input, Error,
|
||||
};
|
||||
|
||||
/// A stage is one step in the fuzzing process.
|
||||
/// 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
|
||||
EM: EventManager<I>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
OT: ObserversTuple,
|
||||
FT: FeedbacksTuple<I>,
|
||||
C: Corpus<I, R>,
|
||||
EM: EventManager<I, S>,
|
||||
E: Executor<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
/// Run the stage
|
||||
fn perform(
|
||||
&mut self,
|
||||
rand: &mut R,
|
||||
&self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
manager: &mut EM,
|
||||
corpus_idx: usize,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub trait StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R>
|
||||
pub trait StagesTuple<E, EM, I, S>
|
||||
where
|
||||
EM: EventManager<I>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
OT: ObserversTuple,
|
||||
FT: FeedbacksTuple<I>,
|
||||
C: Corpus<I, R>,
|
||||
EM: EventManager<I, S>,
|
||||
E: Executor<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
fn perform_all(
|
||||
&mut self,
|
||||
rand: &mut R,
|
||||
&self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
manager: &mut EM,
|
||||
corpus_idx: usize,
|
||||
) -> 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
|
||||
EM: EventManager<I>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
OT: ObserversTuple,
|
||||
FT: FeedbacksTuple<I>,
|
||||
C: Corpus<I, R>,
|
||||
EM: EventManager<I, S>,
|
||||
E: Executor<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
fn perform_all(
|
||||
&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> {
|
||||
fn perform_all(&self, _: &mut S, _: &mut E, _: &mut EM, _: usize) -> Result<(), Error> {
|
||||
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>
|
||||
for (Head, Tail)
|
||||
impl<Head, Tail, E, EM, I, S> StagesTuple<E, EM, I, S> for (Head, Tail)
|
||||
where
|
||||
Head: Stage<C, E, EM, FT, I, OC, OFT, OT, R>,
|
||||
Tail: StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R> + TupleList,
|
||||
EM: EventManager<I>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
OT: ObserversTuple,
|
||||
FT: FeedbacksTuple<I>,
|
||||
C: Corpus<I, R>,
|
||||
Head: Stage<E, EM, I, S>,
|
||||
Tail: StagesTuple<E, EM, I, S> + TupleList,
|
||||
EM: EventManager<I, S>,
|
||||
E: Executor<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
fn perform_all(
|
||||
&mut self,
|
||||
rand: &mut R,
|
||||
&self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
manager: &mut EM,
|
||||
corpus_idx: usize,
|
||||
) -> Result<(), Error> {
|
||||
self.0.perform(rand, executor, state, manager, corpus_idx)?;
|
||||
self.1
|
||||
.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)
|
||||
self.0.perform(state, executor, manager, corpus_idx)?;
|
||||
self.1.perform_all(state, executor, manager, corpus_idx)
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
corpus::Corpus,
|
||||
events::EventManager,
|
||||
executors::{Executor, HasObservers},
|
||||
feedbacks::FeedbacksTuple,
|
||||
inputs::Input,
|
||||
mutators::Mutator,
|
||||
observers::ObserversTuple,
|
||||
stages::Corpus,
|
||||
stages::Stage,
|
||||
state::{HasCorpus, State},
|
||||
state::{Evaluator, HasCorpus, HasRand},
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
@ -19,19 +18,15 @@ use crate::{
|
||||
/// A Mutational stage is the stage in a fuzzing run that mutates inputs.
|
||||
/// Mutational stages will usually have a range of mutations that are
|
||||
/// being applied to the input one by one, between executions.
|
||||
pub trait MutationalStage<C, E, EM, FT, I, M, OC, OFT, OT, R>:
|
||||
Stage<C, E, EM, FT, I, OC, OFT, OT, R>
|
||||
pub trait MutationalStage<C, E, EM, I, M, OT, S>: Stage<E, EM, I, S>
|
||||
where
|
||||
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, 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>,
|
||||
M: Mutator<I, S>,
|
||||
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
|
||||
fn mutator(&self) -> &M;
|
||||
@ -40,70 +35,62 @@ where
|
||||
fn mutator_mut(&mut self) -> &mut M;
|
||||
|
||||
/// Gets the number of iterations this mutator should run for.
|
||||
/// This call uses internal mutability, so it may change for each call
|
||||
#[inline]
|
||||
fn iterations(&mut self, rand: &mut R) -> usize {
|
||||
1 + rand.below(128) as usize
|
||||
}
|
||||
fn iterations(&self, state: &mut S) -> usize;
|
||||
|
||||
/// Runs this (mutational) stage for the given testcase
|
||||
fn perform_mutational(
|
||||
&mut self,
|
||||
rand: &mut R,
|
||||
&self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
manager: &mut EM,
|
||||
corpus_idx: usize,
|
||||
) -> Result<(), Error> {
|
||||
let num = self.iterations(rand);
|
||||
let num = self.iterations(state);
|
||||
for i in 0..num {
|
||||
let mut input_mut = state
|
||||
.corpus()
|
||||
.get(corpus_idx)
|
||||
.get(corpus_idx)?
|
||||
.borrow_mut()
|
||||
.load_input()?
|
||||
.clone();
|
||||
self.mutator_mut()
|
||||
.mutate(rand, state, &mut input_mut, i as i32)?;
|
||||
self.mutator().mutate(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(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128;
|
||||
|
||||
/// 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
|
||||
C: Corpus<I, R>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
EM: EventManager<I>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
M: Mutator<I, S>,
|
||||
I: Input,
|
||||
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, R>>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
|
||||
C: Corpus<I>,
|
||||
EM: EventManager<I, S>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
OT: ObserversTuple,
|
||||
R: Rand,
|
||||
{
|
||||
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>
|
||||
for StdMutationalStage<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, I, M, OT, R, S>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
EM: EventManager<I>,
|
||||
FT: FeedbacksTuple<I>,
|
||||
M: Mutator<I, S>,
|
||||
I: Input,
|
||||
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, R>>,
|
||||
OC: Corpus<I, R>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
|
||||
C: Corpus<I>,
|
||||
EM: EventManager<I, S>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
OT: ObserversTuple,
|
||||
R: Rand,
|
||||
{
|
||||
@ -118,46 +105,45 @@ where
|
||||
fn mutator_mut(&mut self) -> &mut M {
|
||||
&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>
|
||||
for StdMutationalStage<C, E, EM, FT, I, M, 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>
|
||||
where
|
||||
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, 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>,
|
||||
M: Mutator<I, S>,
|
||||
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,
|
||||
{
|
||||
#[inline]
|
||||
fn perform(
|
||||
&mut self,
|
||||
rand: &mut R,
|
||||
&self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
state: &mut State<C, FT, I, OC, OFT, R>,
|
||||
manager: &mut EM,
|
||||
corpus_idx: usize,
|
||||
) -> 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
|
||||
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, 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>,
|
||||
M: Mutator<I, S>,
|
||||
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,
|
||||
{
|
||||
/// Creates a new default mutational stage
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! 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};
|
||||
#[cfg(feature = "std")]
|
||||
use std::{
|
||||
@ -17,7 +17,7 @@ use crate::{
|
||||
generators::Generator,
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
utils::{current_milliseconds, Rand},
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
|
||||
@ -25,11 +25,10 @@ use crate::{
|
||||
use crate::inputs::bytes::BytesInput;
|
||||
|
||||
/// Trait for elements offering a corpus
|
||||
pub trait HasCorpus<C, I, R>
|
||||
pub trait HasCorpus<C, I>
|
||||
where
|
||||
C: Corpus<I, R>,
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
{
|
||||
/// The testcase corpus
|
||||
fn corpus(&self) -> &C;
|
||||
@ -37,6 +36,29 @@ where
|
||||
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
|
||||
pub trait HasMetadata {
|
||||
/// 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.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[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
|
||||
C: Corpus<I, R>,
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
FT: FeedbacksTuple<I>,
|
||||
OC: Corpus<I, R>,
|
||||
SC: Corpus<I>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
{
|
||||
/// RNG instance
|
||||
rand: R,
|
||||
/// How many times the executor ran the harness/target
|
||||
executions: usize,
|
||||
/// At what time the fuzzing started
|
||||
start_time: Duration,
|
||||
/// The corpus
|
||||
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: FT,
|
||||
// Objective corpus
|
||||
objective_corpus: OC,
|
||||
// Solutions corpus
|
||||
solutions: SC,
|
||||
/// 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")]
|
||||
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
|
||||
C: Corpus<BytesInput, R>,
|
||||
C: Corpus<BytesInput>,
|
||||
R: Rand,
|
||||
FT: FeedbacksTuple<BytesInput>,
|
||||
OC: Corpus<BytesInput, R>,
|
||||
SC: Corpus<BytesInput>,
|
||||
OFT: FeedbacksTuple<BytesInput>,
|
||||
{
|
||||
pub fn load_from_directory<E, OT, EM>(
|
||||
@ -101,10 +488,10 @@ where
|
||||
in_dir: &Path,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
C: Corpus<BytesInput, R>,
|
||||
C: Corpus<BytesInput>,
|
||||
E: Executor<BytesInput> + HasObservers<OT>,
|
||||
OT: ObserversTuple,
|
||||
EM: EventManager<BytesInput>,
|
||||
EM: EventManager<BytesInput, Self>,
|
||||
{
|
||||
for entry in fs::read_dir(in_dir)? {
|
||||
let entry = entry?;
|
||||
@ -121,12 +508,12 @@ where
|
||||
println!("Loading file {:?} ...", &path);
|
||||
let bytes = fs::read(&path)?;
|
||||
let input = BytesInput::new(bytes);
|
||||
let (fitness, obj_fitness) = self.evaluate_input(&input, executor, manager)?;
|
||||
if self.add_if_interesting(input, fitness)?.is_none() {
|
||||
let (fitness, is_solution) = self.execute_input(&input, executor, manager)?;
|
||||
if self.add_if_interesting(&input, fitness)?.is_none() {
|
||||
println!("File {:?} was not interesting, skipped.", &path);
|
||||
}
|
||||
if obj_fitness > 0 {
|
||||
println!("File {:?} is an objective, however will be not added as an initial testcase.", &path);
|
||||
if is_solution {
|
||||
println!("File {:?} is a solution, however will be not considered as it is an initial testcase.", &path);
|
||||
}
|
||||
} else if attr.is_dir() {
|
||||
self.load_from_directory(executor, manager, &path)?;
|
||||
@ -143,10 +530,10 @@ where
|
||||
in_dirs: &[PathBuf],
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
C: Corpus<BytesInput, R>,
|
||||
C: Corpus<BytesInput>,
|
||||
E: Executor<BytesInput> + HasObservers<OT>,
|
||||
OT: ObserversTuple,
|
||||
EM: EventManager<BytesInput>,
|
||||
EM: EventManager<BytesInput, Self>,
|
||||
{
|
||||
for in_dir in in_dirs {
|
||||
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
|
||||
C: Corpus<I, R>,
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
R: Rand,
|
||||
FT: FeedbacksTuple<I>,
|
||||
OC: Corpus<I, R>,
|
||||
SC: Corpus<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
|
||||
pub fn evaluate_input<E, EM, OT>(
|
||||
pub fn execute_input<E, EM, OT>(
|
||||
&mut self,
|
||||
input: &I,
|
||||
executor: &mut E,
|
||||
event_mgr: &mut EM,
|
||||
) -> Result<(u32, u32), Error>
|
||||
) -> Result<(u32, bool), Error>
|
||||
where
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
OT: ObserversTuple,
|
||||
C: Corpus<I, R>,
|
||||
EM: EventManager<I>,
|
||||
C: Corpus<I>,
|
||||
EM: EventManager<I, Self>,
|
||||
{
|
||||
executor.pre_exec_observers()?;
|
||||
|
||||
executor.pre_exec(self, event_mgr, 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()?;
|
||||
|
||||
let observers = executor.observers();
|
||||
let objective_fitness =
|
||||
self.objective_feedbacks
|
||||
let fitness =
|
||||
self.feedbacks_mut()
|
||||
.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
|
||||
#[inline]
|
||||
pub fn discard_input(&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]
|
||||
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)
|
||||
let is_solution = self
|
||||
.objectives_mut()
|
||||
.is_interesting_all(&input, observers, exit_kind)?
|
||||
> 0;
|
||||
Ok((fitness, is_solution))
|
||||
}
|
||||
|
||||
pub fn generate_initial_inputs<G, E, OT, EM>(
|
||||
&mut self,
|
||||
rand: &mut R,
|
||||
executor: &mut E,
|
||||
generator: &mut G,
|
||||
manager: &mut EM,
|
||||
@ -435,15 +603,15 @@ where
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
G: Generator<I, R>,
|
||||
C: Corpus<I, R>,
|
||||
C: Corpus<I>,
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
OT: ObserversTuple,
|
||||
EM: EventManager<I>,
|
||||
EM: EventManager<I, Self>,
|
||||
{
|
||||
let mut added = 0;
|
||||
for _ in 0..num {
|
||||
let input = generator.generate(rand)?;
|
||||
let fitness = self.process_input(input, executor, manager)?;
|
||||
let input = generator.generate(self.rand_mut())?;
|
||||
let fitness = self.evaluate_input(input, executor, manager)?;
|
||||
if fitness > 0 {
|
||||
added += 1;
|
||||
}
|
||||
@ -460,15 +628,16 @@ where
|
||||
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 {
|
||||
corpus,
|
||||
rand,
|
||||
executions: 0,
|
||||
start_time: current_milliseconds(),
|
||||
start_time: Duration::from_millis(0),
|
||||
metadata: SerdeAnyMap::default(),
|
||||
feedbacks: feedbacks,
|
||||
objective_corpus: objective_corpus,
|
||||
objective_feedbacks: objective_feedbacks,
|
||||
corpus,
|
||||
feedbacks,
|
||||
solutions,
|
||||
objectives,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Utility functions for AFL
|
||||
|
||||
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;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@ -10,7 +10,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
pub type StdRand = RomuTrioRand;
|
||||
|
||||
/// Ways to get random around here
|
||||
pub trait Rand: Debug + Serialize {
|
||||
pub trait Rand: Debug + Serialize + DeserializeOwned {
|
||||
// Sets the seed of this Rand
|
||||
fn set_seed(&mut self, seed: u64);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user