unfinished generic magics

This commit is contained in:
Andrea Fioraldi 2021-02-18 17:12:27 +01:00
parent 31d94925d0
commit 8b22a06ec9
11 changed files with 810 additions and 422 deletions

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

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

View File

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

View File

@ -1,102 +1,204 @@
//! 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;
use alloc::{vec::Vec};
use core::{cell::RefCell};
#[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>>>;
}
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>;
}
pub trait CorpusScheduler {
/// Add an entry to the corpus and return its index
fn on_add<C, I, R, S>(&self, state: &mut S, idx: usize, testcase: &Testcase<I>) -> Result<(), Error>
where
S: HasCorpus<C, I> + HasRand<R>,
C: Corpus<I>,
I: Input,
R: Rand
{
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<C, I, R, S>(&self, state: &mut S, idx: usize, testcase: &Testcase<I>) -> Result<(), Error>
where
S: HasCorpus<C, I> + HasRand<R>,
C: Corpus<I>,
I: Input,
R: Rand
{
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<C, I, R, S>(&self, state: &mut S, idx: usize, testcase: &Option<Testcase<I>>) -> Result<(), Error>
where
S: HasCorpus<C, I> + HasRand<R>,
C: Corpus<I>,
I: Input,
R: Rand
{
Ok(())
}
// TODO: IntoIter
/// Gets the next entry
fn next(&mut self, rand: &mut R) -> Result<(&RefCell<Testcase<I>>, usize), Error>;
fn next<C, I, R, S>(&self, state: &mut S) -> Result<usize, Error>
where
S: HasCorpus<C, I> + HasRand<R>,
C: Corpus<I>,
I: Input,
R: Rand;
/// Returns the testacase we currently use
fn current_testcase(&self) -> (&RefCell<Testcase<I>>, usize);
}
pub struct RandCorpusScheduler {}
impl CorpusScheduler for RandCorpusScheduler {
/// Gets the next entry at random
fn next<C, I, R, S>(state: &mut S) -> Result<usize, Error>
where
S: HasCorpus<C, I> + HasRand<R>,
C: Corpus<I>,
I: Input,
R: Rand
{
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;
Ok(id)
}
}
}
pub struct InMemoryCorpus<I>
where
I: Input,
{
entries: Vec<RefCell<Testcase<I>>>,
}
impl<I, SC> Corpus<I> for InMemoryCorpus<I>
where
I: Input,
{
/// Returns the number of elements
#[inline]
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 {
Ok(Some(self.entries.remove(idx).into_inner()))
}
}
/// Get by id
#[inline]
fn get(&self, idx: usize) -> Result<&RefCell<Testcase<I>>, Error> {
Ok(&self.entries[idx])
}
/*/// Add an entry to the corpus and return its index
#[inline]
fn add<R, S>(state: &mut S, testcase: Testcase<I>) -> Result<usize, Error>
where
S: HasCorpus<Self, I> + HasRand<R>,
R: Rand
{
state.corpus_mut().entries.push(RefCell::new(testcase));
let idx = state.corpus().entries.len() - 1;
// Scheduler hook
SC::on_add(state, idx, state.corpus().entries[idx].borrow())?;
Ok(idx)
}
/// Replaces the testcase at the given idx
#[inline]
fn replace<R, S>(state: &mut S, idx: usize, testcase: Testcase<I>) -> Result<(), Error>
where
S: HasCorpus<Self, I> + HasRand<R>,
R: Rand
{
if state.corpus().entries.len() < idx {
return Err(Error::KeyNotFound(format!("Index {} out of bounds", idx)));
}
state.corpus_mut().entries[idx] = RefCell::new(testcase);
// Scheduler hook
SC::on_replace(state, idx, state.corpus().entries[idx])?;
Ok(())
}
/// Removes an entry from the corpus, returning it if it was present.
#[inline]
fn remove<R, S>(state: &mut S, idx: usize) -> Result<Option<Testcase<I>>, Error>
where
S: HasCorpus<Self, I> + HasRand<R>,
R: Rand
{
let testcase = match state.corpus_mut()
.entries
.iter()
.position(|x| ptr::eq(x.as_ptr(), entry))
{
Some(i) => Some(state.corpus_mut().entries.remove(i).into_inner()),
None => None,
};
// Scheduler hook
SC::on_remove(state, idx, &testcase)?;
Ok(testcase)
}*/
}

View File

@ -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

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

@ -0,0 +1,142 @@
use core::{marker::PhantomData};
use crate::{
corpus::{CorpusScheduler},
events::{Event, EventManager},
executors::{Executor},
inputs::Input,
stages::StagesTuple,
utils::{current_milliseconds, current_time},
Error
};
/// Holds a set of stages
pub trait HasStages<ST>
where
ST: StagesTuple,
{
fn stages(&self) -> &ST;
fn stages_mut(&mut self) -> &mut ST;
}
/// Holds a set of stages
pub trait HasCorpusScheduler<CS>
where
CS: CorpusScheduler,
{
fn scheduler(&self) -> &CS;
fn scheduler_mut(&mut self) -> &mut CS;
}
/// The main fuzzer trait.
pub trait Fuzzer<CS, ST, I>: HasCorpusScheduler<CS> + HasStages<ST>
where
CS: CorpusScheduler,
ST: StagesTuple<I>,
I: Input
{
fn fuzz_one<E, EM, S>(
&mut self,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<usize, Error>
where
EM: EventManager<I>,
E: Executor<I>,
{
let idx = self.scheduler().next(state)?;
self.stages()
.perform_all(executor, state, manager, idx)?;
manager.process(state, executor)?;
Ok(idx)
}
fn fuzz_loop<E, EM, S>(
&mut self,
executor: &mut E,
state: &mut S,
manager: &mut EM,
) -> Result<usize, Error>
where
EM: EventManager<I>,
E: Executor<I>,
{
let mut last = current_milliseconds();
loop {
self.fuzz_one(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<CS, ST, I>
where
CS: CorpusScheduler,
ST: StagesTuple<I>,
I: Input
{
scheduler: CS,
stages: ST,
}
impl<CS, ST, I> HasStages<ST> for StdFuzzer<CS, ST, I>
where
CS: CorpusScheduler,
ST: StagesTuple<I>,
I: Input
{
fn stages(&self) -> &ST {
&self.stages
}
fn stages_mut(&mut self) -> &mut ST {
&mut self.stages
}
}
impl<CS, ST, I> HasCorpusScheduler<CS> for StdFuzzer<CS, ST, I>
where
CS: CorpusScheduler,
ST: StagesTuple<I>,
I: Input
{
fn scheduler(&self) -> &CS {
&self.scheduler
}
fn scheduler_mut(&mut self) -> &mut CS {
&mut self.scheduler
}
}
impl<CS, ST, I> StdFuzzer<CS, ST, I>
where
CS: CorpusScheduler,
ST: StagesTuple<I>,
I: Input
{
pub fn new(scheduler: CS, stages: ST) -> Self {
Self {
scheduler: scheduler,
stages: stages,
}
}
}

View File

@ -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 {

View File

@ -8,10 +8,7 @@ pub mod token_mutations;
pub use token_mutations::*;
use crate::{
corpus::Corpus,
inputs::Input,
state::{HasCorpus, HasMetadata},
utils::Rand,
Error,
};
@ -20,24 +17,20 @@ use crate::{
/// A mutator takes input, and mutates it.
/// Simple as that.
pub trait Mutator<C, I, R, S>
pub trait Mutator<I>
where
C: Corpus<I, R>,
I: Input,
R: Rand,
S: HasCorpus<C, I, R> + HasMetadata,
{
/// Mutate a given input
fn mutate(
fn mutate<S>(
&mut self,
rand: &mut R,
state: &mut S,
input: &mut I,
stage_idx: i32,
) -> Result<(), Error>;
/// Post-process given the outcome of the execution
fn post_exec(
fn post_exec<S>(
&mut self,
_state: &mut S,
_is_interesting: u32,

View File

@ -5,125 +5,83 @@ use crate::{
bolts::tuples::TupleList,
corpus::Corpus,
events::EventManager,
executors::{Executor, HasObservers},
feedbacks::FeedbacksTuple,
executors::{Executor},
inputs::Input,
observers::ObserversTuple,
state::State,
utils::Rand,
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<I>
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>,
I: Input,
R: Rand,
I: Input
{
/// Run the stage
fn perform(
&mut self,
rand: &mut R,
fn perform<E, EM, S>(
&self,
executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
state: &mut S,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error>;
) -> Result<(), Error>
where
EM: EventManager<I>,
E: Executor<I>;
}
pub trait StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R>
pub trait StagesTuple<I>
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>,
I: Input,
R: Rand,
I: Input
{
fn perform_all(
&mut self,
rand: &mut R,
fn perform_all<E, EM, S>(
&self,
executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
state: &mut S,
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>));
) -> Result<(), Error>
where
EM: EventManager<I>,
E: Executor<I>;
}
impl<C, E, EM, FT, I, OC, OFT, OT, R> StagesTuple<C, E, EM, FT, I, OC, OFT, OT, R> for ()
impl<I> StagesTuple<I> 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>,
I: Input,
R: Rand,
I: Input
{
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<E, EM, S>(
&self,
executor: &mut E,
state: &mut S,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error>
where
EM: EventManager<I>,
E: Executor<I>
{
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, I> StagesTuple<I> 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>,
I: Input,
R: Rand,
Head: Stage,
Tail: StagesTuple + TupleList,
I: Input
{
fn perform_all(
&mut self,
rand: &mut R,
fn perform_all<E, EM, S>(
&self,
executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
state: &mut S,
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)
) -> Result<(), Error>
where
EM: EventManager<I>,
E: Executor<I>
{
self.0.perform(executor, state, manager, corpus_idx)?;
self.1 .perform_all(executor, state, manager, corpus_idx)
}
}

View File

@ -2,14 +2,12 @@ use core::marker::PhantomData;
use crate::{
events::EventManager,
executors::{Executor, HasObservers},
feedbacks::FeedbacksTuple,
executors::{Executor},
inputs::Input,
mutators::Mutator,
observers::ObserversTuple,
stages::Corpus,
stages::Stage,
state::{HasCorpus, State},
state::{HasRand},
utils::Rand,
Error,
};
@ -19,19 +17,10 @@ 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<I, M>: Stage<I>
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>,
I: Input,
R: Rand,
{
/// The mutator registered for this stage
fn mutator(&self) -> &M;
@ -40,22 +29,21 @@ 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<S>(&mut self, state: &mut S) -> usize;
/// Runs this (mutational) stage for the given testcase
fn perform_mutational(
&mut self,
rand: &mut R,
fn perform_mutational<E, EM, S>(
&self,
executor: &mut E,
state: &mut State<C, FT, I, OC, OFT, R>,
state: &mut S,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
let num = self.iterations(rand);
) -> Result<(), Error>
where
EM: EventManager<I>,
E: Executor<I>
{
let num = self.iterations(state);
for i in 0..num {
let mut input_mut = state
.corpus()
@ -64,7 +52,7 @@ where
.load_input()?
.clone();
self.mutator_mut()
.mutate(rand, state, &mut input_mut, i as i32)?;
.mutate(state, &mut input_mut, i as i32)?;
let fitness = state.process_input(input_mut, executor, manager)?;
@ -74,38 +62,23 @@ where
}
}
#[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<I, M>
where
C: Corpus<I, R>,
E: Executor<I> + HasObservers<OT>,
EM: EventManager<I>,
FT: FeedbacksTuple<I>,
M: Mutator<I>,
I: Input,
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, R>>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
R: Rand,
{
mutator: M,
phantom: PhantomData<(EM, E, OC, OFT, OT, FT, C, I, R)>,
phantom: PhantomData<I>,
}
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<I, M> MutationalStage<I, M> for StdMutationalStage<I, M>
where
C: Corpus<I, R>,
E: Executor<I> + HasObservers<OT>,
EM: EventManager<I>,
FT: FeedbacksTuple<I>,
M: Mutator<I>,
I: Input,
M: Mutator<C, I, R, State<C, FT, I, OC, OFT, R>>,
OC: Corpus<I, R>,
OFT: FeedbacksTuple<I>,
OT: ObserversTuple,
R: Rand,
{
/// The mutator, added to this stage
#[inline]
@ -118,47 +91,42 @@ where
fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator
}
}
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>
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>,
I: Input,
R: Rand,
{
#[inline]
fn perform(
&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> {
self.perform_mutational(rand, executor, state, manager, corpus_idx)
/// Gets the number of iterations as a random number
fn iterations<R, S>(&mut self, state: &mut S) -> usize
where
S: HasRand<R>,
R: Rand
{
1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS) as usize
}
}
impl<C, E, EM, FT, I, M, OC, OFT, OT, R> StdMutationalStage<C, E, EM, FT, I, M, OC, OFT, OT, R>
impl<I, M> Stage<I> for StdMutationalStage<I, M>
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>,
I: Input,
{
#[inline]
fn perform<E, EM, S>(
&self,
executor: &mut E,
state: &mut S,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error>
where
EM: EventManager<I>,
E: Executor<I>
{
self.perform_mutational(executor, state, manager, corpus_idx)
}
}
impl<I, M> StdMutationalStage<I, M>
where
M: Mutator<I>,
I: Input,
R: Rand,
{
/// Creates a new default mutational stage
pub fn new(mutator: M) -> Self {

View File

@ -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,17 @@ where
fn corpus_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
@ -59,13 +69,15 @@ pub trait HasMetadata {
#[serde(bound = "FT: serde::de::DeserializeOwned")]
pub struct State<C, FT, I, OC, OFT, R>
where
C: Corpus<I, R>,
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
OC: Corpus<I, R>,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// RNG instance
rand: R,
/// How many times the executor ran the harness/target
executions: usize,
/// The corpus
@ -82,16 +94,16 @@ where
/// Objective Feedbacks
objective_feedbacks: OFT,
phantom: PhantomData<(R, I)>,
phantom: PhantomData<I>,
}
#[cfg(feature = "std")]
impl<C, FT, OC, OFT, R> State<C, FT, BytesInput, OC, OFT, R>
where
C: Corpus<BytesInput, R>,
C: Corpus<BytesInput>,
R: Rand,
FT: FeedbacksTuple<BytesInput>,
OC: Corpus<BytesInput, R>,
OC: Corpus<BytesInput>,
OFT: FeedbacksTuple<BytesInput>,
{
pub fn load_from_directory<E, OT, EM>(
@ -101,7 +113,7 @@ where
in_dir: &Path,
) -> Result<(), Error>
where
C: Corpus<BytesInput, R>,
C: Corpus<BytesInput>,
E: Executor<BytesInput> + HasObservers<OT>,
OT: ObserversTuple,
EM: EventManager<BytesInput>,
@ -143,7 +155,7 @@ where
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
C: Corpus<BytesInput, R>,
C: Corpus<BytesInput>,
E: Executor<BytesInput> + HasObservers<OT>,
OT: ObserversTuple,
EM: EventManager<BytesInput>,
@ -164,13 +176,34 @@ where
}
}
impl<C, FT, I, OC, OFT, R> HasCorpus<C, I, R> for State<C, FT, I, OC, OFT, R>
impl<C, FT, I, OC, OFT, R> HasRand<R> for State<C, FT, I, OC, OFT, R>
where
C: Corpus<I, R>,
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
OC: Corpus<I, R>,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// The rand instance
fn rand(&self) -> &R {
&self.rand
}
/// The rand instance (mut)
fn rand_mut(&mut self) -> &mut R {
&mut self.rand
}
}
impl<C, FT, I, OC, OFT, R> HasCorpus<C, I> for State<C, FT, I, OC, OFT, R>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// Returns the corpus
@ -187,11 +220,11 @@ where
/// 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>,
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
OC: Corpus<I, R>,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// Get all the metadata into an HashMap
@ -209,11 +242,11 @@ where
impl<C, FT, I, OC, OFT, R> State<C, FT, I, OC, OFT, R>
where
C: Corpus<I, R>,
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
OC: Corpus<I, R>,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
{
/// Get executions
@ -299,7 +332,7 @@ where
where
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
C: Corpus<I, R>,
C: Corpus<I>,
EM: EventManager<I>,
{
executor.pre_exec_observers()?;
@ -356,11 +389,11 @@ where
#[inline]
pub fn add_if_interesting(&mut self, input: I, fitness: u32) -> Result<Option<usize>, Error>
where
C: Corpus<I, R>,
C: Corpus<I>,
{
if fitness > 0 {
let testcase = self.input_to_testcase(input, fitness)?;
Ok(Some(self.corpus_mut().add(testcase)))
Ok(Some(C::add(self, testcase)?))
} else {
self.discard_input(&input)?;
Ok(None)
@ -371,7 +404,7 @@ where
#[inline]
pub fn add_if_objective(&mut self, input: I, fitness: u32) -> Result<Option<usize>, Error>
where
C: Corpus<I, R>,
C: Corpus<I>,
{
if fitness > 0 {
let testcase = self.input_to_testcase(input, fitness)?;
@ -394,7 +427,7 @@ where
where
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
C: Corpus<I, R>,
C: Corpus<I>,
EM: EventManager<I>,
{
let (fitness, obj_fitness) = self.evaluate_input(&input, executor, manager)?;
@ -435,7 +468,7 @@ 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>,

View File

@ -1,7 +1,7 @@
//! Utility functions for AFL
use core::{cell::RefCell, debug_assert, fmt::Debug, time};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
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);