diff --git a/fuzzers/libfuzzer_libpng/src/mod.rs b/fuzzers/libfuzzer_libpng/src/mod.rs index 50a02df796..66097da495 100644 --- a/fuzzers/libfuzzer_libpng/src/mod.rs +++ b/fuzzers/libfuzzer_libpng/src/mod.rs @@ -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, 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, 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, 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, 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(()) } diff --git a/libafl/benches/hash_speeds.rs b/libafl/benches/hash_speeds.rs index 20d22f5748..1e2b016c9f 100644 --- a/libafl/benches/hash_speeds.rs +++ b/libafl/benches/hash_speeds.rs @@ -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); diff --git a/libafl/benches/rand_speeds.rs b/libafl/benches/rand_speeds.rs index ad97bd747b..486343d0cc 100644 --- a/libafl/benches/rand_speeds.rs +++ b/libafl/benches/rand_speeds.rs @@ -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); diff --git a/libafl/build.rs b/libafl/build.rs index f77872e9a6..1c6e87c8ce 100644 --- a/libafl/build.rs +++ b/libafl/build.rs @@ -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}, + ); } diff --git a/libafl/src/bolts/mod.rs b/libafl/src/bolts/mod.rs index 099a20e799..f73268ce7c 100644 --- a/libafl/src/bolts/mod.rs +++ b/libafl/src/bolts/mod.rs @@ -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; diff --git a/libafl/src/bolts/serdeany.rs b/libafl/src/bolts/serdeany.rs index d7cf5dc7e9..8fcd3ae208 100644 --- a/libafl/src/bolts/serdeany.rs +++ b/libafl/src/bolts/serdeany.rs @@ -512,7 +512,7 @@ macro_rules! impl_serdeany { self } } - + #[allow(non_snake_case)] #[cfg(feature = "std")] #[ctor] diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index 324f6d512a..cc4d4f962f 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -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)] diff --git a/libafl/src/bolts/tuples.rs b/libafl/src/bolts/tuples.rs index 2cd2d51d9e..ba20a0f29a 100644 --- a/libafl/src/bolts/tuples.rs +++ b/libafl/src/bolts/tuples.rs @@ -190,9 +190,9 @@ macro_rules! tuple_for_each { } impl ForEach for () { - fn for_each(&self) { } + fn for_each(&self) {} } - + impl 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 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); }); diff --git a/libafl/src/corpus/inmemory.rs b/libafl/src/corpus/inmemory.rs index 83b63edfa1..72ede51924 100644 --- a/libafl/src/corpus/inmemory.rs +++ b/libafl/src/corpus/inmemory.rs @@ -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 +pub struct InMemoryCorpus where I: Input, - R: Rand, { entries: Vec>>, - pos: usize, - phantom: PhantomData, + current: Option, } -impl HasTestcaseVec for InMemoryCorpus +impl Corpus for InMemoryCorpus where I: Input, - R: Rand, { - fn entries(&self) -> &[RefCell>] { - &self.entries - } - fn entries_mut(&mut self) -> &mut Vec>> { - &mut self.entries - } -} - -impl Corpus for InMemoryCorpus -where - I: Input, - R: Rand, -{ - /// Gets the next entry + /// Returns the number of elements #[inline] - fn next(&mut self, rand: &mut R) -> Result<(&RefCell>, 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) -> Result { + 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) -> 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>, 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>, usize) { - (self.get(self.pos), self.pos) + fn get(&self, idx: usize) -> Result<&RefCell>, Error> { + Ok(&self.entries[idx]) + } + + /// Current testcase scheduled + #[inline] + fn current(&self) -> &Option { + &self.current + } + + /// Current testcase scheduled (mut) + #[inline] + fn current_mut(&mut self) -> &mut Option { + &mut self.current } } -impl InMemoryCorpus +impl InMemoryCorpus where I: Input, - R: Rand, { pub fn new() -> Self { Self { entries: vec![], - pos: 0, - phantom: PhantomData, + current: None, } } } diff --git a/libafl/src/corpus/minset.rs b/libafl/src/corpus/minset.rs new file mode 100644 index 0000000000..713573ceb6 --- /dev/null +++ b/libafl/src/corpus/minset.rs @@ -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(testcase: &Testcase) -> Result + where + I: Input; +} + +pub struct LenTimeMulFavFactor {} + +// TODO time as Duration and put len into Testcase +impl FavFactor for LenTimeMulFavFactor { + fn compute(entry: &Testcase) -> Result + where + I: Input + HasLen + { + entry.exec_time() * entry.load_input().len() + } +} + +pub trait CorpusMinimizer { + fn update_score(corpus: &mut C, idx: usize) -> Result<(), Error> + where + C: Corpus, + I: Input, + R: Rand; + + fn cull(corpus: &mut C) -> Result<(), Error> + where + C: Corpus, + I: Input, + R: Rand; +} + +pub struct FavCorpusMinimizer +where + F: FavFactor +{ + phantom: PhantomData +} + +impl CorpusMinimizer for FavCorpusMinimizer +where + F: FavFactor +{ + fn update_score(corpus: &mut C, idx: usize) -> Result<(), Error> + where + C: Corpus, + I: Input, + R: Rand + { + + } + + fn cull(corpus: &mut C) -> Result<(), Error> + where + C: Corpus, + I: Input, + R: Rand + { + + } +} + + +#[derive(Serialize, Deserialize)] +pub struct NoveltiesMeta { + novelties: Vec, +} +// impl Iterator + +/// A Queue-like corpus, wrapping an existing Corpus instance +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "I: serde::de::DeserializeOwned")] +pub struct MinSetCorpus +where + C: Corpus, + F: FavFactor, + I: Input, + IT: Iterator, + R: Rand, +{ + corpus: C, + pos: usize, + // TODO rebase minset on remove() + minset: HashSet, + top_rated: HashMap, + phantom: PhantomData<(F, I, IT, R)>, +} + +impl HasTestcaseVec for MinSetCorpus +where + C: Corpus, + I: Input, + R: Rand, +{ + #[inline] + fn entries(&self) -> &[RefCell>] { + self.corpus.entries() + } + #[inline] + fn entries_mut(&mut self) -> &mut Vec>> { + self.corpus.entries_mut() + } +} + +impl Corpus for MinSetCorpus +where + C: Corpus, + 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) -> 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) -> Option> { + self.corpus.remove(entry) + } + + /// Gets a random entry + #[inline] + fn random_entry(&self, rand: &mut R) -> Result<(&RefCell>, usize), Error> { + self.corpus.random_entry(rand) + } + + /// Returns the testacase we currently use + #[inline] + fn current_testcase(&self) -> (&RefCell>, usize) { + (self.get(self.pos - 1), self.pos - 1) + } + + /// Gets the next entry + #[inline] + fn next(&mut self, _rand: &mut R) -> Result<(&RefCell>, 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 MinSetCorpus +where + C: Corpus, + 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> + + pub fn update_score(&mut self, idx: usize) -> Result<(), Error> { + let factor = F::compute(self.entries()[idx].borrow())?; + for elem in entry.get::() { + 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::() { + acc.insert(elem); + } + + self.minset.insert(idx); + } + } + } +} + diff --git a/libafl/src/corpus/mod old.rs b/libafl/src/corpus/mod old.rs new file mode 100644 index 0000000000..86eeaf961f --- /dev/null +++ b/libafl/src/corpus/mod old.rs @@ -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 +where + I: Input, +{ + /// Get the entries vector field + fn entries(&self) -> &[RefCell>]; + + /// Get the entries vector field (mutable) + fn entries_mut(&mut self) -> &mut Vec>>; +} + +/// Corpus with all current testcases +pub trait Corpus: HasTestcaseVec + 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) -> 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) -> 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> { + &self.entries()[idx] + } + + /// Removes an entry from the corpus, returning it if it was present. + #[inline] + fn remove(&mut self, entry: &Testcase) -> Option> { + 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>, 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>, usize), Error>; + + /// Returns the testacase we currently use + fn current_testcase(&self) -> (&RefCell>, usize); +} + diff --git a/libafl/src/corpus/mod.rs b/libafl/src/corpus/mod.rs index 14b87341bd..cd194bcfdb 100644 --- a/libafl/src/corpus/mod.rs +++ b/libafl/src/corpus/mod.rs @@ -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 -where - I: Input, -{ - /// Get the entries vector field - fn entries(&self) -> &[RefCell>]; - - /// Get the entries vector field (mutable) - fn entries_mut(&mut self) -> &mut Vec>>; -} +use crate::{ + inputs::Input, + state::{HasCorpus, HasRand}, + utils::Rand, + Error, +}; /// Corpus with all current testcases -pub trait Corpus: HasTestcaseVec + serde::Serialize + serde::de::DeserializeOwned +pub trait Corpus: 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) -> usize { - self.entries_mut().push(RefCell::new(testcase)); - self.entries().len() - 1 - } + fn add(&mut self, testcase: Testcase) -> Result; /// Replaces the testcase at the given idx - fn replace(&mut self, idx: usize, testcase: Testcase) -> 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) -> Result<(), Error>; + + /// Removes an entry from the corpus, returning it if it was present. + fn remove(&mut self, idx: usize) -> Result>, Error>; + + /// Get by id + fn get(&self, idx: usize) -> Result<&RefCell>, Error>; + + /// Current testcase scheduled + fn current(&self) -> &Option; + + /// Current testcase scheduled (mut) + fn current_mut(&mut self) -> &mut Option; +} + +pub trait CorpusScheduler +where + I: Input, +{ + /// Add an entry to the corpus and return its index + fn on_add(&self, _state: &mut S, _idx: usize, _testcase: &Testcase) -> Result<(), Error> { Ok(()) } - /// Get by id - #[inline] - fn get(&self, idx: usize) -> &RefCell> { - &self.entries()[idx] + /// Replaces the testcase at the given idx + fn on_replace( + &self, + _state: &mut S, + _idx: usize, + _testcase: &Testcase, + ) -> Result<(), Error> { + Ok(()) } /// Removes an entry from the corpus, returning it if it was present. - #[inline] - fn remove(&mut self, entry: &Testcase) -> Option> { - 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>, 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>, + ) -> Result<(), Error> { + Ok(()) } // TODO: IntoIter /// Gets the next entry - fn next(&mut self, rand: &mut R) -> Result<(&RefCell>, usize), Error>; - - /// Returns the testacase we currently use - fn current_testcase(&self) -> (&RefCell>, usize); + fn next(&self, state: &mut S) -> Result; } + +pub struct RandCorpusScheduler +where + S: HasCorpus + HasRand, + C: Corpus, + I: Input, + R: Rand, +{ + phantom: PhantomData<(C, I, R, S)>, +} + +impl CorpusScheduler for RandCorpusScheduler +where + S: HasCorpus + HasRand, + C: Corpus, + I: Input, + R: Rand, +{ + /// Gets the next entry at random + fn next(&self, state: &mut S) -> Result { + 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 RandCorpusScheduler +where + S: HasCorpus + HasRand, + C: Corpus, + I: Input, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +pub type StdCorpusScheduler = RandCorpusScheduler; diff --git a/libafl/src/corpus/ondisk.rs b/libafl/src/corpus/ondisk.rs index 00dd84e852..d4d8706f92 100644 --- a/libafl/src/corpus/ondisk.rs +++ b/libafl/src/corpus/ondisk.rs @@ -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 +pub struct OnDiskCorpus where I: Input, - R: Rand, { entries: Vec>>, + current: Option, dir_path: PathBuf, - pos: usize, - phantom: PhantomData, } -#[cfg(feature = "std")] -impl HasTestcaseVec for OnDiskCorpus +impl Corpus for OnDiskCorpus where I: Input, - R: Rand, { + /// Returns the number of elements #[inline] - fn entries(&self) -> &[RefCell>] { - &self.entries + fn count(&self) -> usize { + self.entries.len() } - #[inline] - fn entries_mut(&mut self) -> &mut Vec>> { - &mut self.entries - } -} -#[cfg(feature = "std")] -impl Corpus for OnDiskCorpus -where - I: Input, - R: Rand, -{ - /// Add an entry and save it to disk - fn add(&mut self, mut entry: Testcase) -> usize { - match entry.filename() { + /// Add an entry to the corpus and return its index + #[inline] + fn add(&mut self, mut testcase: Testcase) -> Result { + 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>, usize) { - (self.get(self.pos), self.pos) + fn replace(&mut self, idx: usize, testcase: Testcase) -> 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>, usize), Error> { - if self.count() == 0 { - Err(Error::Empty("No entries in corpus".to_owned())) + fn remove(&mut self, idx: usize) -> Result>, 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>, Error> { + Ok(&self.entries[idx]) + } + + /// Current testcase scheduled + #[inline] + fn current(&self) -> &Option { + &self.current + } + + /// Current testcase scheduled (mut) + #[inline] + fn current_mut(&mut self) -> &mut Option { + &mut self.current + } } -#[cfg(feature = "std")] -impl OnDiskCorpus +impl OnDiskCorpus 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, } } } diff --git a/libafl/src/corpus/queue.rs b/libafl/src/corpus/queue.rs index 1e90a6e0ce..2d7abdf3b2 100644 --- a/libafl/src/corpus/queue.rs +++ b/libafl/src/corpus/queue.rs @@ -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 +pub struct QueueCorpusScheduler where - C: Corpus, + S: HasCorpus, + C: Corpus, I: Input, - R: Rand, { - corpus: C, - pos: usize, - cycles: u64, - phantom: PhantomData<(I, R)>, + phantom: PhantomData<(C, I, S)>, } -impl HasTestcaseVec for QueueCorpus +impl CorpusScheduler for QueueCorpusScheduler where - C: Corpus, + S: HasCorpus, + C: Corpus, I: Input, - R: Rand, { - #[inline] - fn entries(&self) -> &[RefCell>] { - self.corpus.entries() - } - #[inline] - fn entries_mut(&mut self) -> &mut Vec>> { - self.corpus.entries_mut() - } -} - -impl Corpus for QueueCorpus -where - C: Corpus, - I: Input, - R: Rand, -{ - /// Returns the number of elements - #[inline] - fn count(&self) -> usize { - self.corpus.count() - } - - #[inline] - fn add(&mut self, entry: Testcase) -> usize { - self.corpus.add(entry) - } - - /// Removes an entry from the corpus, returning it if it was present. - #[inline] - fn remove(&mut self, entry: &Testcase) -> Option> { - self.corpus.remove(entry) - } - - /// Gets a random entry - #[inline] - fn random_entry(&self, rand: &mut R) -> Result<(&RefCell>, usize), Error> { - self.corpus.random_entry(rand) - } - - /// Returns the testacase we currently use - #[inline] - fn current_testcase(&self) -> (&RefCell>, usize) { - (self.get(self.pos - 1), self.pos - 1) - } - - /// Gets the next entry - #[inline] - fn next(&mut self, _rand: &mut R) -> Result<(&RefCell>, 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 { + 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 QueueCorpus +impl QueueCorpusScheduler where - C: Corpus, + S: HasCorpus, + C: Corpus, 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"); } } +*/ diff --git a/libafl/src/corpus/testcase.rs b/libafl/src/corpus/testcase.rs index d43812d8b3..984f5530e2 100644 --- a/libafl/src/corpus/testcase.rs +++ b/libafl/src/corpus/testcase.rs @@ -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 { diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 867eaadb31..2d7e82aaa8 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -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 +pub struct LlmpEventManager where I: Input, + S: IfInteresting, SH: ShMem, ST: Stats, //CE: CustomEvent, { stats: Option, llmp: llmp::LlmpConnection, - phantom: PhantomData, + phantom: PhantomData<(I, S)>, } #[cfg(feature = "std")] #[cfg(unix)] -impl LlmpEventManager +impl LlmpEventManager where I: Input, + S: IfInteresting, ST: Stats, { /// Create llmp on a port @@ -80,9 +80,10 @@ where } } -impl Drop for LlmpEventManager +impl Drop for LlmpEventManager where I: Input, + S: IfInteresting, SH: ShMem, ST: Stats, { @@ -92,9 +93,10 @@ where } } -impl LlmpEventManager +impl LlmpEventManager where I: Input, + S: IfInteresting, SH: ShMem, ST: Stats, { @@ -250,20 +252,15 @@ where } // Handle arriving events in the client - fn handle_in_client( + fn handle_in_client( &mut self, - state: &mut State, + state: &mut S, sender_id: u32, event: Event, _executor: &mut E, ) -> Result<(), Error> where - C: Corpus, E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, 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 EventManager for LlmpEventManager +impl EventManager for LlmpEventManager where I: Input, + S: IfInteresting, SH: ShMem, ST: Stats, //CE: CustomEvent, { @@ -317,18 +315,9 @@ where } } - fn process( - &mut self, - state: &mut State, - executor: &mut E, - ) -> Result + fn process(&mut self, state: &mut S, executor: &mut E) -> Result where - C: Corpus, E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, OT: ObserversTuple, { // TODO: Get around local event copy by moving handle_in_client @@ -358,19 +347,7 @@ where Ok(count) } - fn fire( - &mut self, - _state: &mut State, - event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - I: Input, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + fn fire(&mut self, _state: &mut S, event: Event) -> 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( - state: &State, - mgr: &LlmpEventManager, +pub fn serialize_state_mgr( + state: &S, + mgr: &LlmpEventManager, ) -> Result, Error> where - C: Corpus, - FT: FeedbacksTuple, I: Input, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, + S: Serialize + IfInteresting, 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( +pub fn deserialize_state_mgr( state_corpus_serialized: &[u8], -) -> Result<(State, LlmpEventManager), Error> +) -> Result<(S, LlmpEventManager), Error> where - C: Corpus, - FT: FeedbacksTuple, I: Input, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, + S: DeserializeOwned + IfInteresting, SH: ShMem, ST: Stats, { - let tuple: (State, _) = 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 +pub struct LlmpRestartingEventManager where I: Input, + S: IfInteresting, SH: ShMem, ST: Stats, //CE: CustomEvent, { /// The embedded llmp event manager - llmp_mgr: LlmpEventManager, + llmp_mgr: LlmpEventManager, /// The sender to serialize the state for the next runner sender: LlmpSender, } -impl EventManager for LlmpRestartingEventManager +impl EventManager for LlmpRestartingEventManager where I: Input, + S: IfInteresting + Serialize, SH: ShMem, ST: Stats, //CE: CustomEvent, { @@ -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( - &mut self, - state: &mut State, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + 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( - &mut self, - state: &mut State, - executor: &mut E, - ) -> Result + fn process(&mut self, state: &mut S, executor: &mut E) -> Result where - C: Corpus, E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, OT: ObserversTuple, { self.llmp_mgr.process(state, executor) } - fn fire( - &mut self, - state: &mut State, - event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + fn fire(&mut self, state: &mut S, event: Event) -> 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 LlmpRestartingEventManager +impl LlmpRestartingEventManager where I: Input, + S: IfInteresting, SH: ShMem, ST: Stats, //CE: CustomEvent, { /// Create a new runner, the executed child doing the actual fuzzing. - pub fn new(llmp_mgr: LlmpEventManager, sender: LlmpSender) -> Self { + pub fn new(llmp_mgr: LlmpEventManager, sender: LlmpSender) -> 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( - //mgr: &mut LlmpEventManager, +pub fn setup_restarting_mgr( + //mgr: &mut LlmpEventManager, stats: ST, broker_port: u16, -) -> Result< - ( - Option>, - LlmpRestartingEventManager, - ), - Error, -> +) -> Result<(Option, LlmpRestartingEventManager), Error> where I: Input, - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, + S: DeserializeOwned + IfInteresting, 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::::new_on_port(stats, broker_port)?; + mgr = LlmpEventManager::::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::::existing_client_from_env( + let client_mgr = LlmpEventManager::::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, LlmpEventManager) = - deserialize_state_mgr(&msg)?; + let (state, mgr): (S, LlmpEventManager) = deserialize_state_mgr(&msg)?; (Some(state), LlmpRestartingEventManager::new(mgr, sender)) } diff --git a/libafl/src/events/logger.rs b/libafl/src/events/logger.rs index 5fe97054aa..ba3761ac32 100644 --- a/libafl/src/events/logger.rs +++ b/libafl/src/events/logger.rs @@ -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 +pub struct LoggerEventManager where I: Input, ST: Stats, //CE: CustomEvent, @@ -26,25 +23,17 @@ where stats: ST, /// The events that happened since the last handle_in_broker events: Vec>, + phantom: PhantomData, } -impl EventManager for LoggerEventManager +impl EventManager for LoggerEventManager where I: Input, ST: Stats, //CE: CustomEvent, { - fn process( - &mut self, - state: &mut State, - _executor: &mut E, - ) -> Result + fn process(&mut self, state: &mut S, _executor: &mut E) -> Result where - C: Corpus, E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, OT: ObserversTuple, { let count = self.events.len(); @@ -55,18 +44,7 @@ where Ok(count) } - fn fire( - &mut self, - _state: &mut State, - event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + fn fire(&mut self, _state: &mut S, event: Event) -> 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 LoggerEventManager +impl LoggerEventManager 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( - &mut self, - _state: &mut State, - event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + fn handle_in_client(&mut self, _state: &mut S, event: Event) -> Result<(), Error> { match event { _ => Err(Error::Unknown(format!( "Received illegal message that message should not have arrived: {:?}.", diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index b44c8923a5..d801f46aaa 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -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 +pub trait EventManager 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( - &mut self, - state: &mut State, - executor: &mut E, - ) -> Result + fn process(&mut self, state: &mut S, executor: &mut E) -> Result where - C: Corpus, E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, 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( - &mut self, - _state: &mut State, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + 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( - &mut self, - _state: &mut State, - event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple; + fn fire(&mut self, state: &mut S, event: Event) -> 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 { - phantom: PhantomData, +pub struct NopEventManager { + phantom: PhantomData<(I, S)>, } -impl EventManager for NopEventManager +impl EventManager for NopEventManager where I: Input, { - fn process( - &mut self, - _state: &mut State, - _executor: &mut E, - ) -> Result + fn process(&mut self, _state: &mut S, _executor: &mut E) -> Result where - C: Corpus, E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, OT: ObserversTuple, { Ok(0) } - fn fire( - &mut self, - _state: &mut State, - _event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + fn fire(&mut self, _state: &mut S, _event: Event) -> Result<(), Error> { Ok(()) } } diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 9f3b523719..feb4bc5a37 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -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( - &mut self, - _state: &mut State, - _event_mgr: &mut EM, - _input: &I, - ) -> Result<(), Error> + fn pre_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> where - R: Rand, - FT: FeedbacksTuple, - OC: Corpus, - OFT: FeedbacksTuple, - C: Corpus, - EM: EventManager, + EM: EventManager, { #[cfg(unix)] #[cfg(feature = "std")] unsafe { - set_oncrash_ptrs::( - _state, - _event_mgr, - self.observers(), - _input, - ); + set_oncrash_ptrs(state, event_mgr, self.observers(), input); } Ok(()) } #[inline] - fn post_exec( - &mut self, - _state: &State, - _event_mgr: &mut EM, - _input: &I, - ) -> Result<(), Error> + fn post_exec(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> where - R: Rand, - FT: FeedbacksTuple, - C: Corpus, - EM: EventManager, - OC: Corpus, - OFT: FeedbacksTuple, + EM: EventManager, { #[cfg(unix)] #[cfg(feature = "std")] unsafe { - reset_oncrash_ptrs::(); + 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( + pub fn new( name: &'static str, harness_fn: HarnessFunction, observers: OT, - _state: &mut State, + _state: &mut S, _event_mgr: &mut EM, ) -> Self where - R: Rand, - FT: FeedbacksTuple, - OC: Corpus, + EM: EventManager, + OC: Corpus, OFT: FeedbacksTuple, - C: Corpus, - EM: EventManager, + S: HasObjectives + HasSolutions, { #[cfg(feature = "std")] #[cfg(unix)] unsafe { - setup_crash_handlers::(); + setup_crash_handlers::(); } Self { @@ -181,25 +151,6 @@ where } } -/* -unsafe fn tidy_up_on_exit(mgr: &EM) -where -EM: EventManager, -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( - _sig: c_int, - info: siginfo_t, - _void: c_void, - ) where - EM: EventManager, - C: Corpus, + unsafe fn inmem_handle_crash(_sig: c_int, info: siginfo_t, _void: c_void) + where + EM: EventManager, OT: ObserversTuple, - OC: Corpus, + OC: Corpus, OFT: FeedbacksTuple, - FT: FeedbacksTuple, + S: HasObjectives + HasSolutions, 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) - .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( + unsafe fn inmem_handle_timeout( _sig: c_int, _info: siginfo_t, _void: c_void, ) where - EM: EventManager, - C: Corpus, - OC: Corpus, - OFT: FeedbacksTuple, + EM: EventManager, OT: ObserversTuple, - FT: FeedbacksTuple, + OC: Corpus, + OFT: FeedbacksTuple, + S: HasObjectives + HasSolutions, 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) - .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( - state: &mut State, + pub unsafe fn set_oncrash_ptrs( + state: &mut S, event_mgr: &mut EM, observers: &OT, input: &I, - ) where - EM: EventManager, - C: Corpus, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - 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() { + 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() + pub unsafe fn setup_crash_handlers() where - EM: EventManager, - C: Corpus, - OC: Corpus, - OFT: FeedbacksTuple, + EM: EventManager, OT: ObserversTuple, - FT: FeedbacksTuple, + OC: Corpus, + OFT: FeedbacksTuple, + S: HasObjectives + HasSolutions, 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:: as usize; + sa.sa_sigaction = inmem_handle_crash:: 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:: as usize; + sa.sa_sigaction = inmem_handle_timeout:: as usize; if sigaction(SIGUSR2, &mut sa as *mut sigaction, ptr::null_mut()) < 0 { panic!("Could not set up sigusr2 handler for timeouts"); } diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 77b6802a3a..dfe2740203 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -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( + fn pre_exec( &mut self, - _state: &mut State, + _state: &mut S, _event_mgr: &mut EM, _input: &I, ) -> Result<(), Error> where - R: Rand, - FT: FeedbacksTuple, - C: Corpus, - OC: Corpus, - OFT: FeedbacksTuple, - EM: EventManager, + EM: EventManager, { Ok(()) } #[inline] /// Called right after execution finished. - fn post_exec( - &mut self, - _state: &State, - _event_mgr: &mut EM, - _input: &I, - ) -> Result<(), Error> + fn post_exec(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> where - R: Rand, - FT: FeedbacksTuple, - C: Corpus, - OC: Corpus, - OFT: FeedbacksTuple, - EM: EventManager, + EM: EventManager, { Ok(()) } diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 724b0d4d41..76ac3abad5 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -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 diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs new file mode 100644 index 0000000000..da4b20e3f3 --- /dev/null +++ b/libafl/src/fuzzer.rs @@ -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 +where + ST: StagesTuple, + E: Executor, + EM: EventManager, + I: Input, +{ + fn stages(&self) -> &ST; + + fn stages_mut(&mut self) -> &mut ST; +} + +/// Holds a scheduler +pub trait HasCorpusScheduler +where + CS: CorpusScheduler, + I: Input, +{ + fn scheduler(&self) -> &CS; + + fn scheduler_mut(&mut self) -> &mut CS; +} + +/// The main fuzzer trait. +pub trait Fuzzer { + fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result; + + fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result; +} + +/// Your default fuzzer instance, for everyday use. +#[derive(Clone, Debug)] +pub struct StdFuzzer +where + CS: CorpusScheduler, + ST: StagesTuple, + E: Executor, + EM: EventManager, + I: Input, +{ + scheduler: CS, + stages: ST, + phantom: PhantomData<(E, EM, I, OT, S)>, +} + +impl HasStages for StdFuzzer +where + CS: CorpusScheduler, + ST: StagesTuple, + E: Executor, + EM: EventManager, + I: Input, +{ + fn stages(&self) -> &ST { + &self.stages + } + + fn stages_mut(&mut self) -> &mut ST { + &mut self.stages + } +} + +impl HasCorpusScheduler for StdFuzzer +where + CS: CorpusScheduler, + ST: StagesTuple, + E: Executor, + EM: EventManager, + I: Input, +{ + fn scheduler(&self) -> &CS { + &self.scheduler + } + + fn scheduler_mut(&mut self) -> &mut CS { + &mut self.scheduler + } +} + +impl Fuzzer for StdFuzzer +where + CS: CorpusScheduler, + S: HasExecutions, + ST: StagesTuple, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + I: Input, +{ + fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result { + 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 { + 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 StdFuzzer +where + CS: CorpusScheduler, + ST: StagesTuple, + E: Executor, + EM: EventManager, + I: Input, +{ + pub fn new(scheduler: CS, stages: ST) -> Self { + Self { + scheduler: scheduler, + stages: stages, + phantom: PhantomData, + } + } +} diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index b622a002f7..1cca7ee368 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -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)] diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index 09a62f698a..18b9b6a518 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -106,4 +106,3 @@ pub trait HasLen { /// The lenght fn len(&self) -> usize; } - diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index 2906185e91..c8f30e87d3 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -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 -where - ST: StagesTuple, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - 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, - manager: &mut EM, - ) -> Result { - 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, - 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 -where - ST: StagesTuple, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, -{ - stages: ST, - phantom: PhantomData<(EM, E, OC, OFT, OT, FT, C, I, R)>, -} - -impl Fuzzer - for StdFuzzer -where - ST: StagesTuple, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - I: Input, - R: Rand, -{ - fn stages(&self) -> &ST { - &self.stages - } - - fn stages_mut(&mut self) -> &mut ST { - &mut self.stages - } -} - -impl StdFuzzer -where - ST: StagesTuple, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, - 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 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()); } } +*/ diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index d8f0bd9ac2..2337152477 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -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 +pub trait Mutator where - C: Corpus, I: Input, - R: Rand, - S: HasCorpus + 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, diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index fc1368deb0..4b394e65a6 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -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 = - fn(&mut M, &mut R, &mut S, &mut I) -> Result; +pub type MutationFunction = fn(&M, &mut S, &mut I) -> Result; -pub trait ComposedByMutations +pub trait ComposedByMutations where - C: Corpus, I: Input, - R: Rand, - S: HasCorpus + HasMetadata, { /// Get a mutation by index - fn mutation_by_idx(&self, index: usize) -> MutationFunction; + fn mutation_by_idx(&self, index: usize) -> MutationFunction; /// Get the number of mutations fn mutations_count(&self) -> usize; /// Add a mutation - fn add_mutation(&mut self, mutation: MutationFunction); + fn add_mutation(&mut self, mutation: MutationFunction); } /// 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - // TODO: max_size instead of mutator? - mutator: &mut M, - rand: &mut R, - _: &mut S, + mutator: &M, + + state: &mut S, input: &mut I, ) -> Result where M: HasMaxSize, I: Input + HasBytesVec, + S: HasRand, 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( - mutator: &mut M, - rand: &mut R, - _: &mut S, + mutator: &M, + + state: &mut S, input: &mut I, ) -> Result where M: HasMaxSize, I: Input + HasBytesVec, + S: HasRand, 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( - mutator: &mut M, - rand: &mut R, - _: &mut S, + mutator: &M, + + state: &mut S, input: &mut I, ) -> Result where M: HasMaxSize, I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - _: &mut M, - rand: &mut R, - _: &mut S, + _: &M, + + state: &mut S, input: &mut I, ) -> Result where I: Input + HasBytesVec, + S: HasRand, 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( - mutator: &mut M, - rand: &mut R, + mutator: &M, + state: &mut S, input: &mut I, ) -> Result where M: HasMaxSize, - C: Corpus, + C: Corpus, I: Input + HasBytesVec, R: Rand, - S: HasCorpus, + S: HasRand + HasCorpus, { 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( - _: &mut M, - rand: &mut R, + _: &M, + state: &mut S, input: &mut I, ) -> Result where - C: Corpus, + C: Corpus, I: Input + HasBytesVec, R: Rand, - S: HasCorpus, + S: HasRand + HasCorpus, { 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( - _: &mut M, - rand: &mut R, + _: &M, + state: &mut S, input: &mut I, ) -> Result where - C: Corpus, + C: Corpus, I: Input + HasBytesVec, R: Rand, - S: HasCorpus, + S: HasRand + HasCorpus, { // 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>) -> Result: - Mutator + ComposedByMutations +pub use crate::mutators::mutations::*; +pub use crate::mutators::token_mutations::*; + +pub trait ScheduledMutator: Mutator + ComposedByMutations where - C: Corpus, I: Input, - R: Rand, - S: HasCorpus + 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 +pub struct StdScheduledMutator where - C: Corpus, I: Input, + S: HasRand, R: Rand, - S: HasCorpus + HasMetadata, { - mutations: Vec>, + mutations: Vec>, max_size: usize, + phantom: PhantomData, } -impl Debug for StdScheduledMutator +impl Debug for StdScheduledMutator where - C: Corpus, I: Input, + S: HasRand, R: Rand, - S: HasCorpus + HasMetadata, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -79,33 +65,25 @@ where } } -impl Mutator for StdScheduledMutator +impl Mutator for StdScheduledMutator where - C: Corpus, I: Input, + S: HasRand, R: Rand, - S: HasCorpus + 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 ComposedByMutations for StdScheduledMutator +impl ComposedByMutations for StdScheduledMutator where - C: Corpus, I: Input, + S: HasRand, R: Rand, - S: HasCorpus + HasMetadata, { #[inline] - fn mutation_by_idx(&self, index: usize) -> MutationFunction { + fn mutation_by_idx(&self, index: usize) -> MutationFunction { self.mutations[index] } @@ -115,27 +93,34 @@ where } #[inline] - fn add_mutation(&mut self, mutation: MutationFunction) { + fn add_mutation(&mut self, mutation: MutationFunction) { self.mutations.push(mutation) } } -impl ScheduledMutator for StdScheduledMutator +impl ScheduledMutator for StdScheduledMutator where - C: Corpus, I: Input, + S: HasRand, R: Rand, - S: HasCorpus + 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 HasMaxSize for StdScheduledMutator +impl HasMaxSize for StdScheduledMutator where - C: Corpus, I: Input, + S: HasRand, R: Rand, - S: HasCorpus + HasMetadata, { #[inline] fn max_size(&self) -> usize { @@ -148,64 +133,59 @@ where } } -impl StdScheduledMutator +impl StdScheduledMutator where - C: Corpus, I: Input, + S: HasRand, R: Rand, - S: HasCorpus + 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>) -> Self { + pub fn with_mutations(mutations: Vec>) -> 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 +#[derive(Clone, Debug)] +pub struct HavocBytesMutator where - SM: ScheduledMutator + HasMaxSize, - C: Corpus, + SM: ScheduledMutator + HasMaxSize, I: Input + HasBytesVec, + S: HasRand + HasCorpus + HasMetadata, + C: Corpus, R: Rand, - S: HasCorpus + HasMetadata, { scheduled: SM, phantom: PhantomData<(C, I, R, S)>, } -impl Mutator for HavocBytesMutator +impl Mutator for HavocBytesMutator where - SM: ScheduledMutator + HasMaxSize, - C: Corpus, + SM: ScheduledMutator + HasMaxSize, I: Input + HasBytesVec, + S: HasRand + HasCorpus + HasMetadata, + C: Corpus, R: Rand, - S: HasCorpus + 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 HasMaxSize for HavocBytesMutator +impl HasMaxSize for HavocBytesMutator where - SM: ScheduledMutator + HasMaxSize, - C: Corpus, + SM: ScheduledMutator + HasMaxSize, I: Input + HasBytesVec, + S: HasRand + HasCorpus + HasMetadata, + C: Corpus, R: Rand, - S: HasCorpus + HasMetadata, { #[inline] fn max_size(&self) -> usize { @@ -247,13 +227,13 @@ where } } -impl HavocBytesMutator +impl HavocBytesMutator where - SM: ScheduledMutator + HasMaxSize, - C: Corpus, + SM: ScheduledMutator + HasMaxSize, I: Input + HasBytesVec, + S: HasRand + HasCorpus + HasMetadata, + C: Corpus, R: Rand, - S: HasCorpus + HasMetadata, { /// Create a new HavocBytesMutator instance given a ScheduledMutator to wrap pub fn new(mut scheduled: SM) -> Self { @@ -266,16 +246,16 @@ where } } -impl Default for HavocBytesMutator, C, I, R, S> +impl Default for HavocBytesMutator> where - C: Corpus, I: Input + HasBytesVec, + S: HasRand + HasCorpus + HasMetadata, + C: Corpus, R: Rand, - S: HasCorpus + HasMetadata, { /// Create a new HavocBytesMutator instance wrapping StdScheduledMutator fn default() -> Self { - let mut scheduled = StdScheduledMutator::::new(); + let mut scheduled = StdScheduledMutator::::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 { } } } +*/ diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index a075fdaa4e..f0d6ff352a 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -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( - mutator: &mut M, - rand: &mut R, + mutator: &M, state: &mut S, input: &mut I, ) -> Result where M: HasMaxSize, I: Input + HasBytesVec, + S: HasMetadata + HasRand, R: Rand, - S: HasMetadata, { - let meta; - match state.metadata().get::() { - Some(t) => { - meta = t; - } - None => { + let tokens_len = { + let meta = state.metadata().get::(); + 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::().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( - _: &mut M, - rand: &mut R, + _: &M, state: &mut S, input: &mut I, ) -> Result where + M: HasMaxSize, I: Input + HasBytesVec, + S: HasMetadata + HasRand, R: Rand, - S: HasMetadata, { let size = input.bytes().len(); if size == 0 { return Ok(MutationResult::Skipped); } - let meta; - match state.metadata().get::() { - Some(t) => { - meta = t; - } - None => { + let tokens_len = { + let meta = state.metadata().get::(); + 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::().unwrap(); + let token = &meta.tokens[token_idx]; let mut len = token.len(); if off + len > size { len = size - off; diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 2ea67a3a03..abb2aadce4 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -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 +pub trait Stage where - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, + EM: EventManager, + E: Executor, I: Input, - R: Rand, { /// Run the stage fn perform( - &mut self, - rand: &mut R, + &self, + state: &mut S, executor: &mut E, - state: &mut State, manager: &mut EM, corpus_idx: usize, ) -> Result<(), Error>; } -pub trait StagesTuple +pub trait StagesTuple where - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, + EM: EventManager, + E: Executor, I: Input, - R: Rand, { fn perform_all( - &mut self, - rand: &mut R, + &self, + state: &mut S, executor: &mut E, - state: &mut State, manager: &mut EM, corpus_idx: usize, ) -> Result<(), Error>; - fn for_each(&self, f: fn(&dyn Stage)); - fn for_each_mut(&mut self, f: fn(&mut dyn Stage)); } -impl StagesTuple for () +impl StagesTuple for () where - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, + EM: EventManager, + E: Executor, I: Input, - R: Rand, { - fn perform_all( - &mut self, - _rand: &mut R, - _executor: &mut E, - _state: &mut State, - _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)) {} - fn for_each_mut(&mut self, _f: fn(&mut dyn Stage)) {} } -impl StagesTuple - for (Head, Tail) +impl StagesTuple for (Head, Tail) where - Head: Stage, - Tail: StagesTuple + TupleList, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, + Head: Stage, + Tail: StagesTuple + TupleList, + EM: EventManager, + E: Executor, I: Input, - R: Rand, { fn perform_all( - &mut self, - rand: &mut R, + &self, + state: &mut S, executor: &mut E, - state: &mut State, 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)) { - f(&self.0); - self.1.for_each(f) - } - - fn for_each_mut(&mut self, f: fn(&mut dyn Stage)) { - 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) } } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 9c6c7fbf3a..59a283f893 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -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: - Stage +pub trait MutationalStage: Stage where - M: Mutator>, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, + M: Mutator, I: Input, - R: Rand, + S: HasCorpus + Evaluator, + C: Corpus, + EM: EventManager, + E: Executor + HasObservers, + 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, 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 +#[derive(Clone, Debug)] +pub struct StdMutationalStage where - C: Corpus, - E: Executor + HasObservers, - EM: EventManager, - FT: FeedbacksTuple, + M: Mutator, I: Input, - M: Mutator>, - OC: Corpus, - OFT: FeedbacksTuple, + S: HasCorpus + Evaluator + HasRand, + C: Corpus, + EM: EventManager, + E: Executor + HasObservers, 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 MutationalStage - for StdMutationalStage +impl MutationalStage + for StdMutationalStage where - C: Corpus, - E: Executor + HasObservers, - EM: EventManager, - FT: FeedbacksTuple, + M: Mutator, I: Input, - M: Mutator>, - OC: Corpus, - OFT: FeedbacksTuple, + S: HasCorpus + Evaluator + HasRand, + C: Corpus, + EM: EventManager, + E: Executor + HasObservers, 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 Stage - for StdMutationalStage +impl Stage for StdMutationalStage where - M: Mutator>, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, + M: Mutator, I: Input, + S: HasCorpus + Evaluator + HasRand, + C: Corpus, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, R: Rand, { #[inline] fn perform( - &mut self, - rand: &mut R, + &self, + state: &mut S, executor: &mut E, - state: &mut State, 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 StdMutationalStage +impl StdMutationalStage where - M: Mutator>, - EM: EventManager, - E: Executor + HasObservers, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - C: Corpus, + M: Mutator, I: Input, + S: HasCorpus + Evaluator + HasRand, + C: Corpus, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, R: Rand, { /// Creates a new default mutational stage diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 983b556509..0bdc3dd1d1 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -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 +pub trait HasCorpus where - C: Corpus, + C: Corpus, 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 +where + C: Corpus, + 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 +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 +where + FT: FeedbacksTuple, + 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, 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 +where + FT: FeedbacksTuple, + 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 +where + I: Input, +{ + /// Evaluate if a set of observation channels has an interesting state + fn is_interesting( + &mut self, + input: &I, + observers: &OT, + exit_kind: ExitKind, + ) -> Result + 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, Error>; +} + +/// Evaluate an input modyfing the state of the fuzzer and returning a fitness +pub trait Evaluator: Sized +where + I: Input, +{ + /// Runs the input and triggers observers and feedback + fn evaluate_input( + &mut self, + input: I, + executor: &mut E, + event_mgr: &mut EM, + ) -> Result + where + E: Executor + HasObservers, + OT: ObserversTuple, + EM: EventManager; +} + /// The state a fuzz run. #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(bound = "FT: serde::de::DeserializeOwned")] -pub struct State +pub struct State where - C: Corpus, + C: Corpus, I: Input, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + SC: Corpus, OFT: FeedbacksTuple, { + /// 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, +} + +impl HasRand for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// 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 HasCorpus for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// 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 HasSolutions for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// 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 HasMetadata for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// 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 HasFeedbacks for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// 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 HasObjectives for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// 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 HasExecutions for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// 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 HasStartTime for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// 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 IfInteresting for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// Evaluate if a set of observation channels has an interesting state + fn is_interesting( + &mut self, + input: &I, + observers: &OT, + exit_kind: ExitKind, + ) -> Result + 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, 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 Evaluator for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// Process one input, adding to the respective corpuses if needed and firing the right events + #[inline] + fn evaluate_input( + &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 + where + E: Executor + HasObservers, + OT: ObserversTuple, + C: Corpus, + EM: EventManager, + { + 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 State +impl State where - C: Corpus, + C: Corpus, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + SC: Corpus, OFT: FeedbacksTuple, { pub fn load_from_directory( @@ -101,10 +488,10 @@ where in_dir: &Path, ) -> Result<(), Error> where - C: Corpus, + C: Corpus, E: Executor + HasObservers, OT: ObserversTuple, - EM: EventManager, + EM: EventManager, { 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, + C: Corpus, E: Executor + HasObservers, OT: ObserversTuple, - EM: EventManager, + EM: EventManager, { for in_dir in in_dirs { self.load_from_directory(executor, manager, in_dir)?; @@ -164,270 +551,51 @@ where } } -impl HasCorpus for State +impl State where - C: Corpus, + C: Corpus, I: Input, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + SC: Corpus, OFT: FeedbacksTuple, { - /// 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 HasMetadata for State -where - C: Corpus, - I: Input, - R: Rand, - FT: FeedbacksTuple, - OC: Corpus, - OFT: FeedbacksTuple, -{ - /// 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 State -where - C: Corpus, - I: Input, - R: Rand, - FT: FeedbacksTuple, - OC: Corpus, - OFT: FeedbacksTuple, -{ - /// 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( - &mut self, - input: &I, - observers: &OT, - exit_kind: ExitKind, - ) -> Result - 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( + pub fn execute_input( &mut self, input: &I, executor: &mut E, event_mgr: &mut EM, - ) -> Result<(u32, u32), Error> + ) -> Result<(u32, bool), Error> where E: Executor + HasObservers, OT: ObserversTuple, - C: Corpus, - EM: EventManager, + C: Corpus, + EM: EventManager, { 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, 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>, 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, Error> - where - C: Corpus, - { - 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, Error> - where - C: Corpus, - { - 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( - &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 - where - E: Executor + HasObservers, - OT: ObserversTuple, - C: Corpus, - EM: EventManager, - { - 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( &mut self, - rand: &mut R, executor: &mut E, generator: &mut G, manager: &mut EM, @@ -435,15 +603,15 @@ where ) -> Result<(), Error> where G: Generator, - C: Corpus, + C: Corpus, E: Executor + HasObservers, OT: ObserversTuple, - EM: EventManager, + EM: EventManager, { 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, } } diff --git a/libafl/src/utils.rs b/libafl/src/utils.rs index 3b206f22f3..e0dd6dd201 100644 --- a/libafl/src/utils.rs +++ b/libafl/src/utils.rs @@ -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);