state trait

This commit is contained in:
Andrea Fioraldi 2020-11-20 12:53:33 +01:00
parent 056f5b6689
commit c9cc66311a
6 changed files with 245 additions and 83 deletions

View File

@ -80,10 +80,7 @@ where
match self { match self {
Self::Stored { filename } => { Self::Stored { filename } => {
let input = I::from_file(&filename)?; let input = I::from_file(&filename)?;
Ok(Self::Loaded { Ok(Self::Loaded { filename, input })
filename,
input,
})
} }
Self::Loaded { Self::Loaded {
input: _, input: _,
@ -170,11 +167,13 @@ where
{ {
/// Make sure to return a valid input instance loading it from disk if not in memory /// Make sure to return a valid input instance loading it from disk if not in memory
pub fn load_input(&mut self) -> Result<&I, AflError> { pub fn load_input(&mut self) -> Result<&I, AflError> {
// TODO: Implement cache to disk if self.input.is_none() {
match self.input.as_ref() { let input = I::from_file(self.filename.as_ref().ok_or(AflError::EmptyOptional(
Some(i) => Ok(i), "filename not specified".to_string(),
None => Err(AflError::NotImplemented("load_input".into())), ))?)?;
self.input = Some(input);
} }
Ok(self.input.as_ref().unwrap())
} }
/// Get the input, if any /// Get the input, if any
@ -226,4 +225,12 @@ where
metadatas: HashMap::default(), metadatas: HashMap::default(),
} }
} }
pub fn default() -> Self {
Testcase {
input: None,
filename: None,
metadatas: HashMap::default(),
}
}
} }

View File

@ -1,58 +1,126 @@
//! The engine is the core piece of every good fuzzer //! The engine is the core piece of every good fuzzer
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::cell::RefCell;
use crate::corpus::Corpus; use crate::corpus::{Corpus, Testcase};
use crate::executors::Executor;
use crate::feedbacks::Feedback; use crate::feedbacks::Feedback;
use crate::inputs::Input; use crate::inputs::Input;
use crate::observers::Observer;
use crate::stages::Stage; use crate::stages::Stage;
use crate::AflError; use crate::AflError;
pub trait Engine<C, I> pub trait State<C, E, I>
where where
C: Corpus<I>, C: Corpus<I>,
E: Executor<I>,
I: Input, I: Input,
{ {
/// Get the linked observers
fn observers(&self) -> &[Box<dyn Observer>];
/// Get the linked observers
fn observers_mut(&mut self) -> &mut Vec<Box<dyn Observer>>;
/// Add a linked observer
fn add_observer(&mut self, observer: Box<dyn Observer>) {
self.observers_mut().push(observer);
}
/// Reset the state of all the observes linked to this executor
fn reset_observers(&mut self) -> Result<(), AflError> {
for observer in self.observers_mut() {
observer.reset()?;
}
Ok(())
}
/// Run the post exec hook for all the observes linked to this executor
fn post_exec_observers(&mut self) -> Result<(), AflError> {
self.observers_mut()
.iter_mut()
.map(|x| x.post_exec())
.fold(Ok(()), |acc, x| if x.is_err() { x } else { acc })
}
/// Returns vector of feebacks
fn feedbacks(&self) -> &[Box<dyn Feedback<I>>]; fn feedbacks(&self) -> &[Box<dyn Feedback<I>>];
/// Returns vector of feebacks (mutable)
fn feedbacks_mut(&mut self) -> &mut Vec<Box<dyn Feedback<I>>>; fn feedbacks_mut(&mut self) -> &mut Vec<Box<dyn Feedback<I>>>;
/// Adds a feedback
fn add_feedback(&mut self, feedback: Box<dyn Feedback<I>>) { fn add_feedback(&mut self, feedback: Box<dyn Feedback<I>>) {
self.feedbacks_mut().push(feedback); self.feedbacks_mut().push(feedback);
} }
fn stages(&self) -> &[Box<dyn Stage<C, I>>]; /// Return the corpus
fn corpus(&self) -> &C;
fn stages_mut(&mut self) -> &mut Vec<Box<dyn Stage<C, I>>>; /// Return the corpus (mutable)
fn corpus_mut(&mut self) -> &mut C;
fn add_stage(&mut self, stage: Box<dyn Stage<C, I>>) { /// Return the executor
self.stages_mut().push(stage); fn executor(&self) -> &E;
/// Return the executor (mutable)
fn executor_mut(&mut self) -> &mut E;
/// Runs the input and triggers observers and feedback
fn evaluate_input(&mut self, input: &I) -> Result<bool, AflError> {
self.reset_observers()?;
self.executor_mut().run_target(input)?;
self.post_exec_observers()?;
let mut rate_acc = 0;
for feedback in self.feedbacks_mut() {
rate_acc += feedback.is_interesting(input)?;
} }
fn fuzz_one(&mut self, corpus: &mut C) -> Result<(), AflError> { if rate_acc >= 25 {
let testcase = corpus.next()?; let testcase = Rc::new(RefCell::new(Testcase::new(input.clone())));
for stage in self.stages_mut() { for feedback in self.feedbacks_mut() {
stage.perform(testcase.clone(), corpus)?; feedback.append_metadata(testcase.clone())?;
}
Ok(true)
} else {
for feedback in self.feedbacks_mut() {
feedback.discard_metadata()?;
}
Ok(false)
} }
Ok(())
} }
} }
pub struct DefaultEngine<C, I> pub struct DefaultState<C, E, I>
where where
C: Corpus<I>, C: Corpus<I>,
E: Executor<I>,
I: Input, I: Input,
{ {
observers: Vec<Box<dyn Observer>>,
feedbacks: Vec<Box<dyn Feedback<I>>>, feedbacks: Vec<Box<dyn Feedback<I>>>,
stages: Vec<Box<dyn Stage<C, I>>>, corpus: C,
executor: E,
} }
impl<C, I> Engine<C, I> for DefaultEngine<C, I> impl<C, E, I> State<C, E, I> for DefaultState<C, E, I>
where where
C: Corpus<I>, C: Corpus<I>,
E: Executor<I>,
I: Input, I: Input,
{ {
fn observers(&self) -> &[Box<dyn Observer>] {
&self.observers
}
fn observers_mut(&mut self) -> &mut Vec<Box<dyn Observer>> {
&mut self.observers
}
fn feedbacks(&self) -> &[Box<dyn Feedback<I>>] { fn feedbacks(&self) -> &[Box<dyn Feedback<I>>] {
&self.feedbacks &self.feedbacks
} }
@ -61,25 +129,98 @@ where
&mut self.feedbacks &mut self.feedbacks
} }
fn stages(&self) -> &[Box<dyn Stage<C, I>>] { fn corpus(&self) -> &C {
&self.corpus
}
fn corpus_mut(&mut self) -> &mut C {
&mut self.corpus
}
fn executor(&self) -> &E {
&self.executor
}
fn executor_mut(&mut self) -> &mut E {
&mut self.executor
}
}
impl<C, E, I> DefaultState<C, E, I>
where
C: Corpus<I>,
E: Executor<I>,
I: Input,
{
pub fn new(corpus: C, executor: E) -> Self {
DefaultState {
observers: vec![],
feedbacks: vec![],
corpus: corpus,
executor: executor,
}
}
}
pub trait Engine<S, C, E, I>
where
S: State<C, E, I>,
C: Corpus<I>,
E: Executor<I>,
I: Input,
{
fn stages(&self) -> &[Box<dyn Stage<S, C, E, I>>];
fn stages_mut(&mut self) -> &mut Vec<Box<dyn Stage<S, C, E, I>>>;
fn add_stage(&mut self, stage: Box<dyn Stage<S, C, E, I>>) {
self.stages_mut().push(stage);
}
fn fuzz_one(&mut self, state: &mut S) -> Result<(), AflError> {
let testcase = state.corpus_mut().next()?;
for stage in self.stages_mut() {
stage.perform(testcase.clone(), state)?;
}
Ok(())
}
}
pub struct DefaultEngine<S, C, E, I>
where
S: State<C, E, I>,
C: Corpus<I>,
E: Executor<I>,
I: Input,
{
stages: Vec<Box<dyn Stage<S, C, E, I>>>,
}
impl<S, C, E, I> Engine<S, C, E, I> for DefaultEngine<S, C, E, I>
where
S: State<C, E, I>,
C: Corpus<I>,
E: Executor<I>,
I: Input,
{
fn stages(&self) -> &[Box<dyn Stage<S, C, E, I>>] {
&self.stages &self.stages
} }
fn stages_mut(&mut self) -> &mut Vec<Box<dyn Stage<C, I>>> { fn stages_mut(&mut self) -> &mut Vec<Box<dyn Stage<S, C, E, I>>> {
&mut self.stages &mut self.stages
} }
} }
impl<C, I> DefaultEngine<C, I> impl<S, C, E, I> DefaultEngine<S, C, E, I>
where where
S: State<C, E, I>,
C: Corpus<I>, C: Corpus<I>,
E: Executor<I>,
I: Input, I: Input,
{ {
pub fn new() -> Self { pub fn new() -> Self {
DefaultEngine { DefaultEngine { stages: vec![] }
feedbacks: vec![],
stages: vec![],
}
} }
} }
@ -89,7 +230,7 @@ mod tests {
use alloc::boxed::Box; use alloc::boxed::Box;
use crate::corpus::{Corpus, InMemoryCorpus, Testcase}; use crate::corpus::{Corpus, InMemoryCorpus, Testcase};
use crate::engines::{DefaultEngine, Engine}; use crate::engines::{DefaultEngine, DefaultState, Engine};
use crate::executors::inmemory::InMemoryExecutor; use crate::executors::inmemory::InMemoryExecutor;
use crate::executors::{Executor, ExitKind}; use crate::executors::{Executor, ExitKind};
use crate::inputs::bytes::BytesInput; use crate::inputs::bytes::BytesInput;
@ -110,18 +251,21 @@ mod tests {
let mut corpus = InMemoryCorpus::<BytesInput, _>::new(&rand); let mut corpus = InMemoryCorpus::<BytesInput, _>::new(&rand);
let testcase = Testcase::new(vec![0; 4]).into(); let testcase = Testcase::new(vec![0; 4]).into();
corpus.add(testcase); corpus.add(testcase);
let executor = InMemoryExecutor::<BytesInput>::new(harness).into();
let executor = InMemoryExecutor::<BytesInput>::new(harness);
let mut state = DefaultState::new(corpus, executor);
let mut engine = DefaultEngine::new(); let mut engine = DefaultEngine::new();
let mut mutator = DefaultScheduledMutator::new(&rand); let mut mutator = DefaultScheduledMutator::new(&rand);
mutator.add_mutation(mutation_bitflip); mutator.add_mutation(mutation_bitflip);
let stage = DefaultMutationalStage::new(&rand, &executor, mutator); let stage = DefaultMutationalStage::new(&rand, mutator);
engine.add_stage(Box::new(stage)); engine.add_stage(Box::new(stage));
// //
for i in 0..1000 { for i in 0..1000 {
engine engine
.fuzz_one(&mut corpus) .fuzz_one(&mut state)
.expect(&format!("Error in iter {}", i)); .expect(&format!("Error in iter {}", i));
} }
} }

View File

@ -3,11 +3,13 @@ pub mod inmemory;
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::vec::Vec; use alloc::vec::Vec;
use crate::corpus::TestcaseMetadata; use crate::corpus::Testcase;
use crate::feedbacks::Feedback; use crate::feedbacks::Feedback;
use crate::inputs::Input; use crate::inputs::Input;
use crate::observers::Observer; use crate::observers::Observer;
use crate::AflError; use crate::AflError;
use alloc::rc::Rc;
use core::cell::RefCell;
pub enum ExitKind { pub enum ExitKind {
Ok, Ok,
@ -53,19 +55,21 @@ where
self.run_target(input)?; self.run_target(input)?;
self.post_exec_observers()?; self.post_exec_observers()?;
let mut metadatas: Vec<Box<dyn TestcaseMetadata>> = vec![];
let mut rate_acc = 0; let mut rate_acc = 0;
for feedback in self.feedbacks_mut() { for feedback in self.feedbacks_mut() {
let (rate, meta) = feedback.is_interesting(input); rate_acc += feedback.is_interesting(input)?;
rate_acc += rate;
if let Some(m) = meta {
metadatas.push(m);
}
} }
if rate_acc >= 25 { if rate_acc >= 25 {
let testcase = Rc::new(RefCell::new(Testcase::new(input.clone())));
for feedback in self.feedbacks_mut() {
feedback.append_metadata(testcase.clone())?;
}
Ok(true) Ok(true)
} else { } else {
for feedback in self.feedbacks_mut() {
feedback.discard_metadata()?;
}
Ok(false) Ok(false)
} }
} }

View File

@ -1,21 +1,32 @@
extern crate num; extern crate num;
use alloc::boxed::Box; use alloc::rc::Rc;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::cell::RefCell; use core::cell::RefCell;
use core::marker::PhantomData; use core::marker::PhantomData;
use num::Integer; use num::Integer;
use crate::corpus::TestcaseMetadata; use crate::corpus::Testcase;
use crate::inputs::Input; use crate::inputs::Input;
use crate::observers::MapObserver; use crate::observers::MapObserver;
use crate::AflError;
pub trait Feedback<I> pub trait Feedback<I>
where where
I: Input, I: Input,
{ {
/// is_interesting should return the "Interestingness" from 0 to 255 (percent times 2.55) /// is_interesting should return the "Interestingness" from 0 to 255 (percent times 2.55)
fn is_interesting(&mut self, input: &I) -> (u32, Option<Box<dyn TestcaseMetadata>>); fn is_interesting(&mut self, input: &I) -> Result<u32, AflError>;
/// Append to the testcase the generated metadata in case of a new corpus item
fn append_metadata(&mut self, _testcase: Rc<RefCell<Testcase<I>>>) -> Result<(), AflError> {
Ok(())
}
/// Discard the stored metadata in case that the testcase is not added to the corpus
fn discard_metadata(&mut self) -> Result<(), AflError> {
Ok(())
}
} }
/// A Reducer function is used to aggregate values for the novelty search /// A Reducer function is used to aggregate values for the novelty search
@ -88,7 +99,7 @@ where
O: MapObserver<T>, O: MapObserver<T>,
I: Input, I: Input,
{ {
fn is_interesting(&mut self, _input: &I) -> (u32, Option<Box<dyn TestcaseMetadata>>) { fn is_interesting(&mut self, _input: &I) -> Result<u32, AflError> {
let mut interesting = 0; let mut interesting = 0;
// TODO: impl. correctly, optimize // TODO: impl. correctly, optimize
@ -103,11 +114,11 @@ where
*history = reduced; *history = reduced;
interesting += 25; interesting += 25;
if interesting >= 250 { if interesting >= 250 {
return (255, None); return Ok(255);
} }
} }
} }
(interesting, None) Ok(interesting)
} }
} }

View File

@ -3,20 +3,24 @@ pub use mutational::DefaultMutationalStage;
use crate::corpus::testcase::Testcase; use crate::corpus::testcase::Testcase;
use crate::corpus::Corpus; use crate::corpus::Corpus;
use crate::engines::State;
use crate::executors::Executor;
use crate::inputs::Input; use crate::inputs::Input;
use crate::AflError; use crate::AflError;
use alloc::rc::Rc; use alloc::rc::Rc;
use core::cell::RefCell; use core::cell::RefCell;
pub trait Stage<C, I> pub trait Stage<S, C, E, I>
where where
S: State<C, E, I>,
C: Corpus<I>, C: Corpus<I>,
E: Executor<I>,
I: Input, I: Input,
{ {
/// Run the stage /// Run the stage
fn perform( fn perform(
&mut self, &mut self,
testcase: Rc<RefCell<Testcase<I>>>, testcase: Rc<RefCell<Testcase<I>>>,
corpus: &mut C, state: &mut S,
) -> Result<(), AflError>; ) -> Result<(), AflError>;
} }

View File

@ -3,6 +3,7 @@ use core::cell::RefCell;
use core::marker::PhantomData; use core::marker::PhantomData;
use crate::corpus::testcase::Testcase; use crate::corpus::testcase::Testcase;
use crate::engines::State;
use crate::executors::Executor; use crate::executors::Executor;
use crate::inputs::Input; use crate::inputs::Input;
use crate::mutators::Mutator; use crate::mutators::Mutator;
@ -13,12 +14,13 @@ use crate::AflError;
// TODO multi mutators stage // TODO multi mutators stage
pub trait MutationalStage<C, I, M, E>: Stage<C, I> + HasRand pub trait MutationalStage<M, S, C, E, I>: Stage<S, C, E, I> + HasRand
where where
M: Mutator<C, I, R = Self::R>,
S: State<C, E, I>,
E: Executor<I>,
C: Corpus<I>, C: Corpus<I>,
I: Input, I: Input,
M: Mutator<C, I, R = Self::R>,
E: Executor<I>,
{ {
/// The mutator registered for this stage /// The mutator registered for this stage
fn mutator(&self) -> &M; fn mutator(&self) -> &M;
@ -26,9 +28,6 @@ where
/// The mutator registered for this stage (mutable) /// The mutator registered for this stage (mutable)
fn mutator_mut(&mut self) -> &mut M; fn mutator_mut(&mut self) -> &mut M;
/// Rc Refcell to the executor
fn executor(&self) -> &Rc<RefCell<E>>;
/// Gets the number of iterations this mutator should run for. /// Gets the number of iterations this mutator should run for.
/// This call uses internal mutability, so it may change for each call /// This call uses internal mutability, so it may change for each call
fn iterations(&mut self) -> usize { fn iterations(&mut self) -> usize {
@ -39,7 +38,7 @@ where
fn perform_mutational( fn perform_mutational(
&mut self, &mut self,
testcase: Rc<RefCell<Testcase<I>>>, testcase: Rc<RefCell<Testcase<I>>>,
corpus: &mut C, state: &mut S,
) -> Result<(), AflError> { ) -> Result<(), AflError> {
let num = self.iterations(); let num = self.iterations();
let input = testcase.borrow_mut().load_input()?.clone(); let input = testcase.borrow_mut().load_input()?.clone();
@ -47,14 +46,14 @@ where
for i in 0..num { for i in 0..num {
let mut input_tmp = input.clone(); let mut input_tmp = input.clone();
self.mutator_mut() self.mutator_mut()
.mutate(corpus, &mut input_tmp, i as i32)?; .mutate(state.corpus_mut(), &mut input_tmp, i as i32)?;
let interesting = self.executor().borrow_mut().evaluate_input(&input_tmp)?; let interesting = state.evaluate_input(&input_tmp)?;
self.mutator_mut().post_exec(interesting, i as i32)?; self.mutator_mut().post_exec(interesting, i as i32)?;
if interesting { if interesting {
corpus.add(Testcase::new(input_tmp).into()); state.corpus_mut().add(Testcase::new(input_tmp).into());
} }
} }
Ok(()) Ok(())
@ -62,28 +61,25 @@ where
} }
/// The default mutational stage /// The default mutational stage
pub struct DefaultMutationalStage<C, I, R, M, E> pub struct DefaultMutationalStage<M, C, I, R>
where where
M: Mutator<C, I, R = R>,
C: Corpus<I>, C: Corpus<I>,
I: Input, I: Input,
R: Rand, R: Rand,
M: Mutator<C, I, R = R>,
E: Executor<I>,
{ {
rand: Rc<RefCell<R>>, rand: Rc<RefCell<R>>,
executor: Rc<RefCell<E>>,
mutator: M, mutator: M,
_phantom_corpus: PhantomData<C>, _phantom_corpus: PhantomData<C>,
_phantom_input: PhantomData<I>, _phantom_input: PhantomData<I>,
} }
impl<C, I, R, M, E> HasRand for DefaultMutationalStage<C, I, R, M, E> impl<M, C, I, R> HasRand for DefaultMutationalStage<M, C, I, R>
where where
M: Mutator<C, I, R = R>,
C: Corpus<I>, C: Corpus<I>,
I: Input, I: Input,
R: Rand, R: Rand,
M: Mutator<C, I, R = R>,
E: Executor<I>,
{ {
type R = R; type R = R;
@ -92,13 +88,14 @@ where
} }
} }
impl<C, I, R, M, E> MutationalStage<C, I, M, E> for DefaultMutationalStage<C, I, R, M, E> impl<M, S, C, E, I, R> MutationalStage<M, S, C, E, I> for DefaultMutationalStage<M, C, I, R>
where where
M: Mutator<C, I, R = R>,
S: State<C, E, I>,
C: Corpus<I>, C: Corpus<I>,
E: Executor<I>,
I: Input, I: Input,
R: Rand, R: Rand,
M: Mutator<C, I, R = R>,
E: Executor<I>,
{ {
/// The mutator, added to this stage /// The mutator, added to this stage
fn mutator(&self) -> &M { fn mutator(&self) -> &M {
@ -109,42 +106,37 @@ where
fn mutator_mut(&mut self) -> &mut M { fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator &mut self.mutator
} }
fn executor(&self) -> &Rc<RefCell<E>> {
&self.executor
}
} }
impl<C, I, R, M, E> Stage<C, I> for DefaultMutationalStage<C, I, R, M, E> impl<M, S, C, E, I, R> Stage<S, C, E, I> for DefaultMutationalStage<M, C, I, R>
where where
M: Mutator<C, I, R = R>,
S: State<C, E, I>,
C: Corpus<I>, C: Corpus<I>,
E: Executor<I>,
I: Input, I: Input,
R: Rand, R: Rand,
M: Mutator<C, I, R = R>,
E: Executor<I>,
{ {
fn perform( fn perform(
&mut self, &mut self,
testcase: Rc<RefCell<Testcase<I>>>, testcase: Rc<RefCell<Testcase<I>>>,
corpus: &mut C, state: &mut S,
) -> Result<(), AflError> { ) -> Result<(), AflError> {
self.perform_mutational(testcase, corpus) self.perform_mutational(testcase, state)
} }
} }
impl<C, I, R, M, E> DefaultMutationalStage<C, I, R, M, E> impl<M, C, I, R> DefaultMutationalStage<M, C, I, R>
where where
M: Mutator<C, I, R = R>,
C: Corpus<I>, C: Corpus<I>,
I: Input, I: Input,
R: Rand, R: Rand,
M: Mutator<C, I, R = R>,
E: Executor<I>,
{ {
/// Creates a new default mutational stage /// Creates a new default mutational stage
pub fn new(rand: &Rc<RefCell<R>>, executor: &Rc<RefCell<E>>, mutator: M) -> Self { pub fn new(rand: &Rc<RefCell<R>>, mutator: M) -> Self {
DefaultMutationalStage { DefaultMutationalStage {
rand: Rc::clone(rand), rand: Rc::clone(rand),
executor: Rc::clone(executor),
mutator: mutator, mutator: mutator,
_phantom_corpus: PhantomData, _phantom_corpus: PhantomData,
_phantom_input: PhantomData, _phantom_input: PhantomData,