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 {
Self::Stored { filename } => {
let input = I::from_file(&filename)?;
Ok(Self::Loaded {
filename,
input,
})
Ok(Self::Loaded { filename, input })
}
Self::Loaded {
input: _,
@ -170,11 +167,13 @@ where
{
/// 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> {
// TODO: Implement cache to disk
match self.input.as_ref() {
Some(i) => Ok(i),
None => Err(AflError::NotImplemented("load_input".into())),
if self.input.is_none() {
let input = I::from_file(self.filename.as_ref().ok_or(AflError::EmptyOptional(
"filename not specified".to_string(),
))?)?;
self.input = Some(input);
}
Ok(self.input.as_ref().unwrap())
}
/// Get the input, if any
@ -226,4 +225,12 @@ where
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
use alloc::boxed::Box;
use alloc::rc::Rc;
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::inputs::Input;
use crate::observers::Observer;
use crate::stages::Stage;
use crate::AflError;
pub trait Engine<C, I>
pub trait State<C, E, I>
where
C: Corpus<I>,
E: Executor<I>,
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>>];
/// Returns vector of feebacks (mutable)
fn feedbacks_mut(&mut self) -> &mut Vec<Box<dyn Feedback<I>>>;
/// Adds a feedback
fn add_feedback(&mut self, feedback: Box<dyn Feedback<I>>) {
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>>) {
self.stages_mut().push(stage);
}
/// Return the executor
fn executor(&self) -> &E;
fn fuzz_one(&mut self, corpus: &mut C) -> Result<(), AflError> {
let testcase = corpus.next()?;
for stage in self.stages_mut() {
stage.perform(testcase.clone(), corpus)?;
/// 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)?;
}
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)
} 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
C: Corpus<I>,
E: Executor<I>,
I: Input,
{
observers: Vec<Box<dyn Observer>>,
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
C: Corpus<I>,
E: Executor<I>,
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>>] {
&self.feedbacks
}
@ -61,25 +129,98 @@ where
&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
}
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
}
}
impl<C, I> DefaultEngine<C, I>
impl<S, C, E, I> DefaultEngine<S, C, E, I>
where
S: State<C, E, I>,
C: Corpus<I>,
E: Executor<I>,
I: Input,
{
pub fn new() -> Self {
DefaultEngine {
feedbacks: vec![],
stages: vec![],
}
DefaultEngine { stages: vec![] }
}
}
@ -89,7 +230,7 @@ mod tests {
use alloc::boxed::Box;
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::{Executor, ExitKind};
use crate::inputs::bytes::BytesInput;
@ -110,18 +251,21 @@ mod tests {
let mut corpus = InMemoryCorpus::<BytesInput, _>::new(&rand);
let testcase = Testcase::new(vec![0; 4]).into();
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 mutator = DefaultScheduledMutator::new(&rand);
mutator.add_mutation(mutation_bitflip);
let stage = DefaultMutationalStage::new(&rand, &executor, mutator);
let stage = DefaultMutationalStage::new(&rand, mutator);
engine.add_stage(Box::new(stage));
//
for i in 0..1000 {
engine
.fuzz_one(&mut corpus)
.fuzz_one(&mut state)
.expect(&format!("Error in iter {}", i));
}
}

View File

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

View File

@ -1,21 +1,32 @@
extern crate num;
use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::vec::Vec;
use core::cell::RefCell;
use core::marker::PhantomData;
use num::Integer;
use crate::corpus::TestcaseMetadata;
use crate::corpus::Testcase;
use crate::inputs::Input;
use crate::observers::MapObserver;
use crate::AflError;
pub trait Feedback<I>
where
I: Input,
{
/// 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
@ -88,7 +99,7 @@ where
O: MapObserver<T>,
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;
// TODO: impl. correctly, optimize
@ -103,11 +114,11 @@ where
*history = reduced;
interesting += 25;
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::Corpus;
use crate::engines::State;
use crate::executors::Executor;
use crate::inputs::Input;
use crate::AflError;
use alloc::rc::Rc;
use core::cell::RefCell;
pub trait Stage<C, I>
pub trait Stage<S, C, E, I>
where
S: State<C, E, I>,
C: Corpus<I>,
E: Executor<I>,
I: Input,
{
/// Run the stage
fn perform(
&mut self,
testcase: Rc<RefCell<Testcase<I>>>,
corpus: &mut C,
state: &mut S,
) -> Result<(), AflError>;
}

View File

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