From fd83c10c1e52c7e12cbf1857038c235bf3a88b7b Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 26 Feb 2021 22:22:10 +0100 Subject: [PATCH 01/11] finish minimizer scheduler --- fuzzers/libfuzzer_libpng/src/fuzzer.rs | 13 +- libafl/src/corpus/{minset.rs => minimizer.rs} | 88 +++++++---- libafl/src/corpus/mod.rs | 8 +- libafl/src/corpus/testcase.rs | 41 +++-- libafl/src/feedbacks/map.rs | 142 ++---------------- libafl/src/mutators/token_mutations.rs | 8 +- libafl/src/state/mod.rs | 12 +- libafl/src/utils.rs | 5 + 8 files changed, 119 insertions(+), 198 deletions(-) rename libafl/src/corpus/{minset.rs => minimizer.rs} (64%) diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index 7a338a4282..0586583013 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -5,7 +5,10 @@ use std::{env, path::PathBuf}; use libafl::{ bolts::{shmem::UnixShMem, tuples::tuple_list}, - corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, + corpus::{ + Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, + RandCorpusScheduler, + }, events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, Executor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback}, @@ -101,7 +104,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> println!("We're a client, let's fuzz :)"); // Create a PNG dictionary if not existing - if state.metadata().get::().is_none() { + if state.metadatas().get::().is_none() { state.add_metadata(TokensMetadata::new(vec![ vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header "IHDR".as_bytes().to_vec(), @@ -115,8 +118,10 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let mutator = HavocBytesMutator::default(); let stage = StdMutationalStage::new(mutator); - // A fuzzer with just one stage and a random policy to get testcasess from the corpus - let fuzzer = StdFuzzer::new(RandCorpusScheduler::new(), tuple_list!(stage)); + // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(RandCorpusScheduler::new()); + //let scheduler = RandCorpusScheduler::new(); + let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); // Create the executor for an in-process function with just one observer for edge coverage let mut executor = InProcessExecutor::new( diff --git a/libafl/src/corpus/minset.rs b/libafl/src/corpus/minimizer.rs similarity index 64% rename from libafl/src/corpus/minset.rs rename to libafl/src/corpus/minimizer.rs index ed3eeb0cf8..9c49aa3293 100644 --- a/libafl/src/corpus/minset.rs +++ b/libafl/src/corpus/minimizer.rs @@ -1,15 +1,19 @@ use crate::{ bolts::serdeany::SerdeAny, corpus::{Corpus, CorpusScheduler, Testcase}, + feedbacks::MapIndexesMetadata, inputs::{HasLen, Input}, - state::{HasCorpus, HasMetadata}, + state::{HasCorpus, HasMetadata, HasRand}, + utils::{AsSlice, Rand}, Error, }; -use core::{iter::IntoIterator, marker::PhantomData}; +use core::marker::PhantomData; use hashbrown::{HashMap, HashSet}; use serde::{Deserialize, Serialize}; +pub const DEFAULT_SKIP_NOT_FAV_PROB: u64 = 95; + /// A testcase metadata saying if a testcase is favored #[derive(Serialize, Deserialize)] pub struct IsFavoredMetadata {} @@ -56,29 +60,29 @@ where } } -pub struct MinimizerCorpusScheduler +pub struct MinimizerCorpusScheduler where CS: CorpusScheduler, F: FavFactor, I: Input, - IT: IntoIterator + SerdeAny, - for<'a> &'a IT: IntoIterator, + M: AsSlice + SerdeAny, S: HasCorpus + HasMetadata, C: Corpus, { base: CS, - phantom: PhantomData<(C, F, I, IT, S)>, + skip_not_fav_prob: u64, + phantom: PhantomData<(C, F, I, M, R, S)>, } -impl CorpusScheduler for MinimizerCorpusScheduler +impl CorpusScheduler for MinimizerCorpusScheduler where CS: CorpusScheduler, F: FavFactor, I: Input, - IT: IntoIterator + SerdeAny, - for<'a> &'a IT: IntoIterator, - S: HasCorpus + HasMetadata, + M: AsSlice + SerdeAny, + S: HasCorpus + HasMetadata + HasRand, C: Corpus, + R: Rand, { /// Add an entry to the corpus and return its index fn on_add(&self, state: &mut S, idx: usize) -> Result<(), Error> { @@ -91,7 +95,7 @@ where self.base.on_replace(state, idx, testcase) } - /// Removes an entry from the corpus, returning it if it was present. + /// Removes an entry from the corpus, returning M if M was present. fn on_remove( &self, state: &mut S, @@ -101,27 +105,38 @@ where self.base.on_remove(state, idx, testcase) } - // TODO: IntoIter /// Gets the next entry fn next(&self, state: &mut S) -> Result { self.cull(state)?; - self.base.next(state) + let mut idx = self.base.next(state)?; + while { + let has = !state + .corpus() + .get(idx)? + .borrow() + .has_metadata::(); + has + } && state.rand_mut().below(100) < self.skip_not_fav_prob + { + idx = self.base.next(state)?; + } + Ok(idx) } } -impl MinimizerCorpusScheduler +impl MinimizerCorpusScheduler where CS: CorpusScheduler, F: FavFactor, I: Input, - IT: IntoIterator + SerdeAny, - for<'a> &'a IT: IntoIterator, - S: HasCorpus + HasMetadata, + M: AsSlice + SerdeAny, + S: HasCorpus + HasMetadata + HasRand, C: Corpus, + R: Rand, { pub fn update_score(&self, state: &mut S, idx: usize) -> Result<(), Error> { // Create a new top rated meta if not existing - if state.metadata().get::().is_none() { + if state.metadatas().get::().is_none() { state.add_metadata(TopRatedsMetadata::new()); } @@ -129,31 +144,32 @@ where { let mut entry = state.corpus().get(idx)?.borrow_mut(); let factor = F::compute(&mut *entry)?; - for elem in entry.metadatas().get::().ok_or_else(|| { + let meta = entry.metadatas().get::().ok_or_else(|| { Error::KeyNotFound(format!( "Metadata needed for MinimizerCorpusScheduler not found in testcase #{}", idx )) - })? { + })?; + for elem in meta.as_slice() { if let Some(old_idx) = state - .metadata() + .metadatas() .get::() .unwrap() .map - .get(&elem) + .get(elem) { if factor > F::compute(&mut *state.corpus().get(*old_idx)?.borrow_mut())? { continue; } } - new_favoreds.push((elem, idx)); + new_favoreds.push((*elem, idx)); } } for pair in new_favoreds { state - .metadata_mut() + .metadatas_mut() .get_mut::() .unwrap() .map @@ -164,19 +180,20 @@ where pub fn cull(&self, state: &mut S) -> Result<(), Error> { let mut acc = HashSet::new(); - let top_rated = state.metadata().get::().unwrap(); + let top_rated = state.metadatas().get::().unwrap(); for key in top_rated.map.keys() { if !acc.contains(key) { let idx = top_rated.map.get(key).unwrap(); let mut entry = state.corpus().get(*idx)?.borrow_mut(); - for elem in entry.metadatas().get::().ok_or_else(|| { + let meta = entry.metadatas().get::().ok_or_else(|| { Error::KeyNotFound(format!( "Metadata needed for MinimizerCorpusScheduler not found in testcase #{}", idx )) - })? { - acc.insert(elem); + })?; + for elem in meta.as_slice() { + acc.insert(*elem); } entry.add_metadata(IsFavoredMetadata {}); @@ -189,7 +206,22 @@ where pub fn new(base: CS) -> Self { Self { base: base, + skip_not_fav_prob: DEFAULT_SKIP_NOT_FAV_PROB, + phantom: PhantomData, + } + } + + pub fn with_skip_prob(base: CS, skip_not_fav_prob: u64) -> Self { + Self { + base: base, + skip_not_fav_prob: skip_not_fav_prob, phantom: PhantomData, } } } + +pub type LenTimeMinimizerCorpusScheduler = + MinimizerCorpusScheduler, I, M, R, S>; + +pub type IndexesLenTimeMinimizerCorpusScheduler = + MinimizerCorpusScheduler, I, MapIndexesMetadata, R, S>; diff --git a/libafl/src/corpus/mod.rs b/libafl/src/corpus/mod.rs index 0707f3d9be..6b2fc082d5 100644 --- a/libafl/src/corpus/mod.rs +++ b/libafl/src/corpus/mod.rs @@ -14,7 +14,12 @@ pub use ondisk::OnDiskCorpus; pub mod queue; pub use queue::QueueCorpusScheduler; -pub mod minset; +pub mod minimizer; +pub use minimizer::{ + FavFactor, IndexesLenTimeMinimizerCorpusScheduler, IsFavoredMetadata, + LenTimeMinimizerCorpusScheduler, LenTimeMulFavFactor, MinimizerCorpusScheduler, + TopRatedsMetadata, +}; use alloc::borrow::ToOwned; use core::{cell::RefCell, marker::PhantomData}; @@ -82,7 +87,6 @@ where Ok(()) } - // TODO: IntoIter /// Gets the next entry fn next(&self, state: &mut S) -> Result; } diff --git a/libafl/src/corpus/testcase.rs b/libafl/src/corpus/testcase.rs index 8b8cad970a..0da3c6505a 100644 --- a/libafl/src/corpus/testcase.rs +++ b/libafl/src/corpus/testcase.rs @@ -6,8 +6,9 @@ use core::{convert::Into, default::Default, option::Option, time::Duration}; use serde::{Deserialize, Serialize}; use crate::{ - bolts::serdeany::{SerdeAny, SerdeAnyMap}, + bolts::serdeany::SerdeAnyMap, inputs::{HasLen, Input}, + state::HasMetadata, Error, }; @@ -32,6 +33,23 @@ where cached_len: Option, } +impl HasMetadata for Testcase +where + I: Input, +{ + /// Get all the metadatas into an HashMap + #[inline] + fn metadatas(&self) -> &SerdeAnyMap { + &self.metadatas + } + + /// Get all the metadatas into an HashMap (mutable) + #[inline] + fn metadatas_mut(&mut self) -> &mut SerdeAnyMap { + &mut self.metadatas + } +} + /// Impl of a testcase impl Testcase where @@ -120,27 +138,6 @@ where self.fitness = fitness; } - /// Get all the metadatas into an HashMap - #[inline] - pub fn metadatas(&self) -> &SerdeAnyMap { - &self.metadatas - } - - /// Get all the metadatas into an HashMap (mutable) - #[inline] - pub fn metadatas_mut(&mut self) -> &mut SerdeAnyMap { - &mut self.metadatas - } - - /// Add a metadata - #[inline] - pub fn add_metadata(&mut self, meta: M) - where - M: SerdeAny, - { - self.metadatas.insert(meta); - } - /// Get the execution time of the testcase pub fn exec_time(&self) -> &Option { &self.exec_time diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index 9e1ead65af..18daa5cd5a 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -13,15 +13,14 @@ use crate::{ feedbacks::Feedback, inputs::Input, observers::{MapObserver, Observer, ObserversTuple}, + state::HasMetadata, + utils::AsSlice, Error, }; pub type MaxMapFeedback = MapFeedback, O>; pub type MinMapFeedback = MapFeedback, O>; -//pub type MaxMapTrackerFeedback = MapFeedback, O>; -//pub type MinMapTrackerFeedback = MapFeedback, O>; - /// A Reducer function is used to aggregate values for the novelty search pub trait Reducer: Serialize + serde::de::DeserializeOwned + 'static where @@ -74,122 +73,6 @@ where } } -/* -/// The most common AFL-like feedback type -#[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(bound = "T: serde::de::DeserializeOwned")] -pub struct MapFeedback -where - T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, - R: Reducer, - O: MapObserver, -{ - /// Contains information about untouched entries - history_map: Vec, - /// Name identifier of this instance - name: String, - /// Phantom Data of Reducer - phantom: PhantomData<(R, O)>, -} - -impl Feedback for MapFeedback -where - T: Integer - + Default - + Copy - + 'static - + serde::Serialize - + serde::de::DeserializeOwned - + core::fmt::Debug, - R: Reducer, - O: MapObserver, - I: Input, -{ - fn is_interesting( - &mut self, - _input: &I, - observers: &OT, - _exit_kind: ExitKind, - ) -> Result { - let mut interesting = 0; - // TODO optimize - let observer = observers.match_name_type::(&self.name).unwrap(); - let size = observer.usable_count(); - //println!("count: {:?}, map: {:?}, history: {:?}", size, observer.map(), &self.history_map); - for i in 0..size { - let history = self.history_map[i]; - let item = observer.map()[i]; - let reduced = R::reduce(history, item); - if history != reduced { - self.history_map[i] = reduced; - interesting += 1; - } - } - - //println!("..interesting: {:?}, new_history: {:?}\n", interesting, &self.history_map); - //std::thread::sleep(std::time::Duration::from_millis(100)); - - Ok(interesting) - } -} - -impl Named for MapFeedback -where - T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, - R: Reducer, - O: MapObserver, -{ - #[inline] - fn name(&self) -> &str { - self.name.as_str() - } -} - -impl MapFeedback -where - T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, - R: Reducer, - O: MapObserver + Observer, -{ - /// Create new MapFeedback - pub fn new(name: &'static str, map_size: usize) -> Self { - Self { - history_map: vec![T::default(); map_size], - phantom: PhantomData, - name: name.to_string(), - } - } - - /// Create new MapFeedback for the observer type. - /// Name should match that of the observer. - pub fn new_with_observer(name: &'static str, map_observer: &O) -> Self { - debug_assert_eq!(name, map_observer.name()); - Self { - history_map: vec![T::default(); map_observer.map().len()], - phantom: PhantomData, - name: name.to_string(), - } - } -} - -impl MapFeedback -where - T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, - R: Reducer, - O: MapObserver, -{ - /// Create new MapFeedback using a map observer, and a map. - /// The map can be shared. - pub fn with_history_map(name: &'static str, history_map: Vec) -> Self { - Self { - history_map: history_map, - name: name.to_string(), - phantom: PhantomData, - } - } -} -*/ - /// A testcase metadata holding a list of indexes of a map #[derive(Serialize, Deserialize)] pub struct MapIndexesMetadata { @@ -198,12 +81,10 @@ pub struct MapIndexesMetadata { crate::impl_serdeany!(MapIndexesMetadata); -impl IntoIterator for MapIndexesMetadata { - type Item = usize; - type IntoIter = alloc::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.list.into_iter() +impl AsSlice for MapIndexesMetadata { + /// Convert to a slice + fn as_slice(&self) -> &[usize] { + self.list.as_slice() } } @@ -221,15 +102,12 @@ pub struct MapNoveltiesMetadata { crate::impl_serdeany!(MapNoveltiesMetadata); -impl IntoIterator for MapNoveltiesMetadata { - type Item = usize; - type IntoIter = alloc::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.list.into_iter() +impl AsSlice for MapNoveltiesMetadata { + /// Convert to a slice + fn as_slice(&self) -> &[usize] { + self.list.as_slice() } } - impl MapNoveltiesMetadata { pub fn new(list: Vec) -> Self { Self { list } diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index bfd1f1f82a..6702afeb80 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -37,7 +37,7 @@ where { let max_size = state.max_size(); let tokens_len = { - let meta = state.metadata().get::(); + let meta = state.metadatas().get::(); if meta.is_none() { return Ok(MutationResult::Skipped); } @@ -51,7 +51,7 @@ where let size = input.bytes().len(); let off = state.rand_mut().below((size + 1) as u64) as usize; - let meta = state.metadata().get::().unwrap(); + let meta = state.metadatas().get::().unwrap(); let token = &meta.tokens[token_idx]; let mut len = token.len(); @@ -83,7 +83,7 @@ where } let tokens_len = { - let meta = state.metadata().get::(); + let meta = state.metadatas().get::(); if meta.is_none() { return Ok(MutationResult::Skipped); } @@ -96,7 +96,7 @@ where let off = state.rand_mut().below(size as u64) as usize; - let meta = state.metadata().get::().unwrap(); + let meta = state.metadatas().get::().unwrap(); let token = &meta.tokens[token_idx]; let mut len = token.len(); if off + len > size { diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index b00bddd23e..ef028d0401 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -73,9 +73,9 @@ where /// Trait for elements offering metadata pub trait HasMetadata { /// A map, storing all metadata - fn metadata(&self) -> &SerdeAnyMap; + fn metadatas(&self) -> &SerdeAnyMap; /// A map, storing all metadata (mut) - fn metadata_mut(&mut self) -> &mut SerdeAnyMap; + fn metadatas_mut(&mut self) -> &mut SerdeAnyMap; /// Add a metadata to the metadata map #[inline] @@ -83,7 +83,7 @@ pub trait HasMetadata { where M: SerdeAny, { - self.metadata_mut().insert(meta); + self.metadatas_mut().insert(meta); } /// Check for a metadata @@ -92,7 +92,7 @@ pub trait HasMetadata { where M: SerdeAny, { - self.metadata().get::().is_some() + self.metadatas().get::().is_some() } } @@ -307,13 +307,13 @@ where { /// Get all the metadata into an HashMap #[inline] - fn metadata(&self) -> &SerdeAnyMap { + fn metadatas(&self) -> &SerdeAnyMap { &self.metadata } /// Get all the metadata into an HashMap (mutable) #[inline] - fn metadata_mut(&mut self) -> &mut SerdeAnyMap { + fn metadatas_mut(&mut self) -> &mut SerdeAnyMap { &mut self.metadata } } diff --git a/libafl/src/utils.rs b/libafl/src/utils.rs index 7484c81c25..072c86e3c0 100644 --- a/libafl/src/utils.rs +++ b/libafl/src/utils.rs @@ -7,6 +7,11 @@ use xxhash_rust::xxh3::xxh3_64_with_seed; #[cfg(feature = "std")] use std::time::{SystemTime, UNIX_EPOCH}; +pub trait AsSlice { + /// Convert to a slice + fn as_slice(&self) -> &[T]; +} + pub type StdRand = RomuTrioRand; /// Ways to get random around here From 0f40ac05d40f137105b1dd087a46772f3f1c96ff Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Sat, 27 Feb 2021 15:12:19 +0100 Subject: [PATCH 02/11] readme --- README.md | 2 +- fuzzers/libfuzzer_libpng/README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2de72056ef..bbe2712ce0 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It is released as Free and Open Source Software under the GNU Lesser General Pub ## Example usages -We collect example fuzzers in `./fuzzers`. They can be build using `cargo build --example [fuzzer_name]` +We collect example fuzzers in `./fuzzers`. They can be build using `cargo build --example [fuzzer_name] --release` The best-tested fuzzer is `./fuzzers/libfuzzer_libpng`, a clone of libfuzzer using libafl for a libpng harness. See its readme [here](./fuzzers/libfuzzer_libpng/README.md). diff --git a/fuzzers/libfuzzer_libpng/README.md b/fuzzers/libfuzzer_libpng/README.md index dbd64b37ff..f56138c2b5 100644 --- a/fuzzers/libfuzzer_libpng/README.md +++ b/fuzzers/libfuzzer_libpng/README.md @@ -8,7 +8,7 @@ It has been tested on Linux. To build this example, run `cargo build --example libfuzzer_libpng --release`. This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. -Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the c++ harness)[./harness.cc] and the instrumented `libpng`. +Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. ## Run @@ -20,6 +20,6 @@ As this example uses in-process fuzzing, we added a Restarting Event Manager (`s This means each client will start itself again to listen for crashes and timeouts. By restarting the actual fuzzer, it can recover from these exit conditions. -In any real-world scenario, you should use `taskset` to pin each client to an empty cpu core, the lib does not pick an empty core automatically (yet). +In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). -For convenience, you may just run `./test.sh` in this folder to test it. \ No newline at end of file +For convenience, you may just run `./test.sh` in this folder to test it. From 3aa9439e80e25ef8a45e67258bbbab999ffd4ca1 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Sun, 28 Feb 2021 23:22:18 +0100 Subject: [PATCH 03/11] scheduler half finished --- fuzzers/libfuzzer_libpng/src/fuzzer.rs | 10 +++-- libafl/src/corpus/minimizer.rs | 3 ++ libafl/src/events/llmp.rs | 2 +- libafl/src/stages/mutational.rs | 2 +- libafl/src/state/mod.rs | 52 +++++++++++++++++--------- 5 files changed, 45 insertions(+), 24 deletions(-) diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index 0586583013..9e510e66d0 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -7,7 +7,7 @@ use libafl::{ bolts::{shmem::UnixShMem, tuples::tuple_list}, corpus::{ Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, - RandCorpusScheduler, + RandCorpusScheduler,QueueCorpusScheduler }, events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, Executor, ExitKind}, @@ -119,8 +119,8 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let stage = StdMutationalStage::new(mutator); // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus - let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(RandCorpusScheduler::new()); - //let scheduler = RandCorpusScheduler::new(); + //let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(RandCorpusScheduler::new()); + let scheduler = QueueCorpusScheduler::new(); let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); // Create the executor for an in-process function with just one observer for edge coverage @@ -139,6 +139,8 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> println!("Warning: LLVMFuzzerInitialize failed with -1") } } + + std::thread::sleep_ms(2000); // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { @@ -150,7 +152,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 state, &mut executor, &mut restarting_mgr)?; // Never reached diff --git a/libafl/src/corpus/minimizer.rs b/libafl/src/corpus/minimizer.rs index 9c49aa3293..98b3ab9495 100644 --- a/libafl/src/corpus/minimizer.rs +++ b/libafl/src/corpus/minimizer.rs @@ -179,6 +179,9 @@ where } pub fn cull(&self, state: &mut S) -> Result<(), Error> { + if state.metadatas().get::().is_none() { + return Ok(()); + } let mut acc = HashSet::new(); let top_rated = state.metadatas().get::().unwrap(); diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index a9c83155d7..2c2af29130 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -281,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, scheduler)?.is_none() { #[cfg(feature = "std")] println!("Added received Testcase"); } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 59a283f893..bf891f510f 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -55,7 +55,7 @@ where .clone(); self.mutator().mutate(state, &mut input_mut, i as i32)?; - let fitness = state.evaluate_input(input_mut, executor, manager)?; + let fitness = state.evaluate_input(input_mut, executor, manager, scheduler)?; self.mutator().post_exec(state, fitness, i as i32)?; } diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index ef028d0401..7467fe3cd7 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -10,7 +10,7 @@ use std::{ use crate::{ bolts::serdeany::{SerdeAny, SerdeAnyMap}, - corpus::{Corpus, Testcase}, + corpus::{Corpus, Testcase, CorpusScheduler}, events::{Event, EventManager, LogSeverity}, executors::{Executor, ExitKind, HasObservers}, feedbacks::FeedbacksTuple, @@ -175,7 +175,10 @@ 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>; + fn add_if_interesting(&mut self, input: &I, fitness: u32, scheduler: &CS) -> Result, Error> + where + CS: CorpusScheduler, + Self: Sized; } /// Evaluate an input modyfing the state of the fuzzer and returning a fitness @@ -184,16 +187,18 @@ where I: Input, { /// Runs the input and triggers observers and feedback - fn evaluate_input( + fn evaluate_input( &mut self, input: I, executor: &mut E, - event_mgr: &mut EM, + manager: &mut EM, + scheduler: &CS, ) -> Result where E: Executor + HasObservers, OT: ObserversTuple, - EM: EventManager; + EM: EventManager, + CS: CorpusScheduler; } /// The state a fuzz run. @@ -450,10 +455,15 @@ where /// 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> { + fn add_if_interesting(&mut self, input: &I, fitness: u32, scheduler: &CS) -> Result, Error> + where + CS: CorpusScheduler + { if fitness > 0 { let testcase = self.testcase_with_feedbacks_metadata(input.clone(), fitness)?; - Ok(Some(self.corpus.add(testcase)?)) // TODO scheduler hook + let idx = self.corpus.add(testcase)?; + scheduler.on_add(self, idx)?; + Ok(Some(idx)) } else { self.discard_feedbacks_metadata(input)?; Ok(None) @@ -472,18 +482,20 @@ where { /// Process one input, adding to the respective corpuses if needed and firing the right events #[inline] - fn evaluate_input( + 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, + scheduler: &CS, ) -> Result where E: Executor + HasObservers, OT: ObserversTuple, C: Corpus, EM: EventManager, + CS: CorpusScheduler { let (fitness, is_solution) = self.execute_input(&input, executor, manager)?; let observers = executor.observers(); @@ -493,7 +505,7 @@ where self.solutions_mut().add(Testcase::new(input.clone()))?; } - if !self.add_if_interesting(&input, fitness)?.is_none() { + if !self.add_if_interesting(&input, fitness, scheduler)?.is_none() { let observers_buf = manager.serialize_observers(observers)?; manager.fire( self, @@ -521,17 +533,18 @@ where SC: Corpus, OFT: FeedbacksTuple, { - pub fn load_from_directory( + pub fn load_from_directory( &mut self, executor: &mut E, manager: &mut EM, + scheduler: &CS, in_dir: &Path, ) -> Result<(), Error> where - C: Corpus, E: Executor + HasObservers, OT: ObserversTuple, EM: EventManager, + CS: CorpusScheduler { for entry in fs::read_dir(in_dir)? { let entry = entry?; @@ -549,34 +562,35 @@ where let bytes = fs::read(&path)?; let input = BytesInput::new(bytes); let (fitness, is_solution) = self.execute_input(&input, executor, manager)?; - if self.add_if_interesting(&input, fitness)?.is_none() { + if self.add_if_interesting(&input, fitness, scheduler)?.is_none() { println!("File {:?} was not interesting, skipped.", &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)?; + self.load_from_directory(executor, manager, scheduler, &path)?; } } Ok(()) } - pub fn load_initial_inputs( + pub fn load_initial_inputs( &mut self, executor: &mut E, manager: &mut EM, + scheduler: &CS, in_dirs: &[PathBuf], ) -> Result<(), Error> where - C: Corpus, E: Executor + HasObservers, OT: ObserversTuple, EM: EventManager, + CS: CorpusScheduler, { for in_dir in in_dirs { - self.load_from_directory(executor, manager, in_dir)?; + self.load_from_directory(executor, manager, scheduler, in_dir)?; } manager.fire( self, @@ -634,11 +648,12 @@ where Ok((fitness, is_solution)) } - pub fn generate_initial_inputs( + pub fn generate_initial_inputs( &mut self, executor: &mut E, generator: &mut G, manager: &mut EM, + scheduler: &CS, num: usize, ) -> Result<(), Error> where @@ -647,11 +662,12 @@ where E: Executor + HasObservers, OT: ObserversTuple, EM: EventManager, + CS: CorpusScheduler { let mut added = 0; for _ in 0..num { let input = generator.generate(self.rand_mut())?; - let fitness = self.evaluate_input(input, executor, manager)?; + let fitness = self.evaluate_input(input, executor, manager, scheduler)?; if fitness > 0 { added += 1; } From c245d5e5fb4ae01c066ab9387af4012a21da9126 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 1 Mar 2021 09:54:39 +0100 Subject: [PATCH 04/11] pass scheduler to stage --- libafl/src/events/llmp.rs | 19 ++++++++++++------- libafl/src/events/mod.rs | 7 +++++-- libafl/src/events/simple.rs | 6 ++++-- libafl/src/fuzzer.rs | 21 +++++++++++---------- libafl/src/stages/mod.rs | 21 ++++++++++++--------- libafl/src/stages/mutational.rs | 25 ++++++++++++++++--------- libafl/src/state/mod.rs | 4 ++-- 7 files changed, 62 insertions(+), 41 deletions(-) diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 2c2af29130..051167b732 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -22,6 +22,7 @@ use crate::{ executors::{Executor, HasObservers}, inputs::Input, observers::ObserversTuple, + corpus::CorpusScheduler, state::IfInteresting, stats::Stats, Error, @@ -252,14 +253,16 @@ where } // Handle arriving events in the client - fn handle_in_client( + fn handle_in_client( &mut self, state: &mut S, _sender_id: u32, event: Event, _executor: &mut E, + scheduler: &CS, ) -> Result<(), Error> where + CS: CorpusScheduler, E: Executor + HasObservers, OT: ObserversTuple, { @@ -315,10 +318,11 @@ where } } - fn process(&mut self, state: &mut S, executor: &mut E) -> Result + fn process(&mut self, state: &mut S, executor: &mut E, scheduler: &CS) -> Result where + CS: CorpusScheduler, E: Executor + HasObservers, - OT: ObserversTuple, + OT: ObserversTuple { // TODO: Get around local event copy by moving handle_in_client let mut events = vec![]; @@ -342,7 +346,7 @@ where }; let count = events.len(); events.drain(..).try_for_each(|(sender_id, event)| { - self.handle_in_client(state, sender_id, event, executor) + self.handle_in_client(state, sender_id, event, executor, scheduler) })?; Ok(count) } @@ -426,12 +430,13 @@ where .send_buf(_LLMP_TAG_RESTART, &state_corpus_serialized) } - fn process(&mut self, state: &mut S, executor: &mut E) -> Result + fn process(&mut self, state: &mut S, executor: &mut E, scheduler: &CS) -> Result where + CS: CorpusScheduler, E: Executor + HasObservers, - OT: ObserversTuple, + OT: ObserversTuple { - self.llmp_mgr.process(state, executor) + self.llmp_mgr.process(state, executor, scheduler) } fn fire(&mut self, state: &mut S, event: Event) -> Result<(), Error> { diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index 0ff73ead86..934a3163c7 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize}; use crate::{ executors::{Executor, HasObservers}, inputs::Input, + corpus::{ CorpusScheduler}, observers::ObserversTuple, Error, }; @@ -159,8 +160,9 @@ where /// Lookup for incoming events and process them. /// Return the number of processes events or an error - fn process(&mut self, state: &mut S, executor: &mut E) -> Result + fn process(&mut self, state: &mut S, executor: &mut E, scheduler: &CS) -> Result where + CS: CorpusScheduler, E: Executor + HasObservers, OT: ObserversTuple; @@ -203,8 +205,9 @@ impl EventManager for NopEventManager where I: Input, { - fn process(&mut self, _state: &mut S, _executor: &mut E) -> Result + fn process(&mut self, _state: &mut S, _executor: &mut E, _scheduler: &CS) -> Result where + CS: CorpusScheduler, E: Executor + HasObservers, OT: ObserversTuple, { diff --git a/libafl/src/events/simple.rs b/libafl/src/events/simple.rs index 5fb217ec37..b3f3fce830 100644 --- a/libafl/src/events/simple.rs +++ b/libafl/src/events/simple.rs @@ -7,6 +7,7 @@ use crate::{ executors::{Executor, HasObservers}, inputs::Input, observers::ObserversTuple, + corpus::CorpusScheduler, stats::Stats, Error, }; @@ -30,10 +31,11 @@ where I: Input, ST: Stats, //CE: CustomEvent, { - fn process(&mut self, state: &mut S, _executor: &mut E) -> Result + fn process(&mut self, state: &mut S, _executor: &mut E, _scheduler: &CS) -> Result where + CS: CorpusScheduler, E: Executor + HasObservers, - OT: ObserversTuple, + OT: ObserversTuple { let count = self.events.len(); while self.events.len() > 0 { diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index da4b20e3f3..127794458a 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -12,12 +12,13 @@ use crate::{ use core::marker::PhantomData; /// Holds a set of stages -pub trait HasStages +pub trait HasStages where - ST: StagesTuple, + ST: StagesTuple, E: Executor, EM: EventManager, I: Input, + Self: Sized, { fn stages(&self) -> &ST; @@ -47,7 +48,7 @@ pub trait Fuzzer { pub struct StdFuzzer where CS: CorpusScheduler, - ST: StagesTuple, + ST: StagesTuple, E: Executor, EM: EventManager, I: Input, @@ -57,10 +58,10 @@ where phantom: PhantomData<(E, EM, I, OT, S)>, } -impl HasStages for StdFuzzer +impl HasStages for StdFuzzer where CS: CorpusScheduler, - ST: StagesTuple, + ST: StagesTuple, E: Executor, EM: EventManager, I: Input, @@ -77,7 +78,7 @@ where impl HasCorpusScheduler for StdFuzzer where CS: CorpusScheduler, - ST: StagesTuple, + ST: StagesTuple, E: Executor, EM: EventManager, I: Input, @@ -95,7 +96,7 @@ impl Fuzzer for StdFuzzer, S: HasExecutions, - ST: StagesTuple, + ST: StagesTuple, EM: EventManager, E: Executor + HasObservers, OT: ObserversTuple, @@ -104,9 +105,9 @@ where 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)?; + self.stages().perform_all(state, executor, manager, self.scheduler(), idx)?; - manager.process(state, executor)?; + manager.process(state, executor, self.scheduler())?; Ok(idx) } @@ -133,7 +134,7 @@ where impl StdFuzzer where CS: CorpusScheduler, - ST: StagesTuple, + ST: StagesTuple, E: Executor, EM: EventManager, I: Input, diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index abb2aadce4..03ada9567d 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -7,7 +7,7 @@ use crate::{ /// 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, @@ -19,11 +19,12 @@ where state: &mut S, executor: &mut E, manager: &mut EM, + scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error>; } -pub trait StagesTuple +pub trait StagesTuple where EM: EventManager, E: Executor, @@ -34,25 +35,26 @@ where state: &mut S, executor: &mut E, manager: &mut EM, + scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error>; } -impl StagesTuple for () +impl StagesTuple for () where EM: EventManager, E: Executor, I: Input, { - fn perform_all(&self, _: &mut S, _: &mut E, _: &mut EM, _: usize) -> Result<(), Error> { + fn perform_all(&self, _: &mut S, _: &mut E, _: &mut EM, _: &CS, _: usize) -> Result<(), Error> { Ok(()) } } -impl StagesTuple for (Head, Tail) +impl StagesTuple for (Head, Tail) where - Head: Stage, - Tail: StagesTuple + TupleList, + Head: Stage, + Tail: StagesTuple + TupleList, EM: EventManager, E: Executor, I: Input, @@ -62,9 +64,10 @@ where state: &mut S, executor: &mut E, manager: &mut EM, + scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error> { - self.0.perform(state, executor, manager, corpus_idx)?; - self.1.perform_all(state, executor, manager, corpus_idx) + self.0.perform(state, executor, manager, scheduler, corpus_idx)?; + self.1.perform_all(state, executor, manager, scheduler, corpus_idx) } } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index bf891f510f..7a2731a058 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use crate::{ - corpus::Corpus, + corpus::{Corpus, CorpusScheduler}, events::EventManager, executors::{Executor, HasObservers}, inputs::Input, @@ -18,7 +18,7 @@ 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, I: Input, @@ -27,6 +27,7 @@ where EM: EventManager, E: Executor + HasObservers, OT: ObserversTuple, + CS: CorpusScheduler { /// The mutator registered for this stage fn mutator(&self) -> &M; @@ -43,6 +44,7 @@ where state: &mut S, executor: &mut E, manager: &mut EM, + scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error> { let num = self.iterations(state); @@ -67,7 +69,7 @@ pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128; /// The default mutational stage #[derive(Clone, Debug)] -pub struct StdMutationalStage +pub struct StdMutationalStage where M: Mutator, I: Input, @@ -76,14 +78,15 @@ where EM: EventManager, E: Executor + HasObservers, OT: ObserversTuple, + CS: CorpusScheduler, R: Rand, { mutator: M, - phantom: PhantomData<(C, E, EM, I, OT, R, S)>, + phantom: PhantomData<(C, CS, E, EM, I, OT, R, S)>, } -impl MutationalStage - for StdMutationalStage +impl MutationalStage + for StdMutationalStage where M: Mutator, I: Input, @@ -92,6 +95,7 @@ where EM: EventManager, E: Executor + HasObservers, OT: ObserversTuple, + CS: CorpusScheduler, R: Rand, { /// The mutator, added to this stage @@ -112,7 +116,7 @@ where } } -impl Stage for StdMutationalStage +impl Stage for StdMutationalStage where M: Mutator, I: Input, @@ -121,6 +125,7 @@ where EM: EventManager, E: Executor + HasObservers, OT: ObserversTuple, + CS: CorpusScheduler, R: Rand, { #[inline] @@ -129,13 +134,14 @@ where state: &mut S, executor: &mut E, manager: &mut EM, + scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error> { - self.perform_mutational(state, executor, manager, corpus_idx) + self.perform_mutational(state, executor, manager, scheduler, corpus_idx) } } -impl StdMutationalStage +impl StdMutationalStage where M: Mutator, I: Input, @@ -144,6 +150,7 @@ where EM: EventManager, E: Executor + HasObservers, OT: ObserversTuple, + CS: CorpusScheduler, R: Rand, { /// Creates a new default mutational stage diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 7467fe3cd7..e1d29ecb37 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -600,7 +600,7 @@ where phantom: PhantomData, }, )?; - manager.process(self, executor)?; + manager.process(self, executor, scheduler)?; Ok(()) } } @@ -680,7 +680,7 @@ where phantom: PhantomData, }, )?; - manager.process(self, executor)?; + manager.process(self, executor, scheduler)?; Ok(()) } From ff0b6089b41d16026c4f0abd48066e22ecc9aa7a Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 1 Mar 2021 09:55:00 +0100 Subject: [PATCH 05/11] have libpng example compiling --- fuzzers/libfuzzer_libpng/src/fuzzer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index 9e510e66d0..676eae67ba 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -12,7 +12,7 @@ use libafl::{ events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, Executor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback}, - fuzzer::{Fuzzer, StdFuzzer}, + fuzzer::{Fuzzer, StdFuzzer, HasCorpusScheduler}, inputs::Input, mutators::scheduled::HavocBytesMutator, mutators::token_mutations::TokensMetadata, @@ -145,7 +145,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs(&mut executor, &mut restarting_mgr, &corpus_dirs) + .load_initial_inputs(&mut executor, &mut restarting_mgr, fuzzer.scheduler(), &corpus_dirs) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs From 34cba3e59c782fd6cbb346e65c3b28b1bd6778cb Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 1 Mar 2021 10:00:53 +0100 Subject: [PATCH 06/11] fix bug in queue scheduler --- libafl/src/corpus/queue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl/src/corpus/queue.rs b/libafl/src/corpus/queue.rs index 2d7abdf3b2..5257a52d8e 100644 --- a/libafl/src/corpus/queue.rs +++ b/libafl/src/corpus/queue.rs @@ -32,7 +32,7 @@ where } else { let id = match state.corpus().current() { Some(cur) => { - if *cur + 1 > state.corpus().count() { + if *cur + 1 >= state.corpus().count() { 0 } else { *cur + 1 From 9ba508f77a38b8f073f0e5715326ae3138d2eae5 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 1 Mar 2021 10:04:28 +0100 Subject: [PATCH 07/11] IndexesLenTimeMinimizerCorpusScheduler in libpng example --- fuzzers/libfuzzer_libpng/src/fuzzer.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index 676eae67ba..66cd674c5d 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -6,8 +6,7 @@ use std::{env, path::PathBuf}; use libafl::{ bolts::{shmem::UnixShMem, tuples::tuple_list}, corpus::{ - Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, - RandCorpusScheduler,QueueCorpusScheduler + Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler }, events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, Executor, ExitKind}, @@ -92,7 +91,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), // Feedbacks to rate the interestingness of an input - tuple_list!(MaxMapFeedback::new_with_observer(&edges_observer)), + tuple_list!(MaxMapFeedback::new_with_observer_track(&edges_observer, true, false)), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(objective_dir), @@ -119,8 +118,8 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let stage = StdMutationalStage::new(mutator); // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus - //let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(RandCorpusScheduler::new()); - let scheduler = QueueCorpusScheduler::new(); + let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); + //let scheduler = QueueCorpusScheduler::new(); let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); // Create the executor for an in-process function with just one observer for edge coverage @@ -139,8 +138,6 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> println!("Warning: LLVMFuzzerInitialize failed with -1") } } - - std::thread::sleep_ms(2000); // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { From dc39a5591ac038ea3b2fad3469074b4aac9121cd Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 1 Mar 2021 10:04:59 +0100 Subject: [PATCH 08/11] format --- fuzzers/libfuzzer_libpng/src/fuzzer.rs | 20 +++++++++++---- libafl/src/events/llmp.rs | 25 ++++++++++++++----- libafl/src/events/mod.rs | 16 +++++++++--- libafl/src/events/simple.rs | 11 ++++++--- libafl/src/fuzzer.rs | 3 ++- libafl/src/stages/mod.rs | 18 ++++++++------ libafl/src/stages/mutational.rs | 5 ++-- libafl/src/state/mod.rs | 34 +++++++++++++++++++------- 8 files changed, 95 insertions(+), 37 deletions(-) diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index 66cd674c5d..c18aa49c7f 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -6,12 +6,13 @@ use std::{env, path::PathBuf}; use libafl::{ bolts::{shmem::UnixShMem, tuples::tuple_list}, corpus::{ - Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler + Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, + QueueCorpusScheduler, }, events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, Executor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback}, - fuzzer::{Fuzzer, StdFuzzer, HasCorpusScheduler}, + fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, inputs::Input, mutators::scheduled::HavocBytesMutator, mutators::token_mutations::TokensMetadata, @@ -91,7 +92,11 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), // Feedbacks to rate the interestingness of an input - tuple_list!(MaxMapFeedback::new_with_observer_track(&edges_observer, true, false)), + tuple_list!(MaxMapFeedback::new_with_observer_track( + &edges_observer, + true, + false + )), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(objective_dir), @@ -142,14 +147,19 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs(&mut executor, &mut restarting_mgr, fuzzer.scheduler(), &corpus_dirs) + .load_initial_inputs( + &mut executor, + &mut restarting_mgr, + fuzzer.scheduler(), + &corpus_dirs, + ) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs )); println!("We imported {} inputs from disk.", state.corpus().count()); } - + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; // Never reached diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 051167b732..102cab12d9 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -17,12 +17,12 @@ use crate::{ llmp::{self, LlmpClient, LlmpClientDescription, Tag}, shmem::ShMem, }, + corpus::CorpusScheduler, events::{BrokerEventResult, Event, EventManager}, executors::ExitKind, executors::{Executor, HasObservers}, inputs::Input, observers::ObserversTuple, - corpus::CorpusScheduler, state::IfInteresting, stats::Stats, Error, @@ -284,7 +284,10 @@ 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, scheduler)?.is_none() { + if !state + .add_if_interesting(&input, fitness, scheduler)? + .is_none() + { #[cfg(feature = "std")] println!("Added received Testcase"); } @@ -318,11 +321,16 @@ where } } - fn process(&mut self, state: &mut S, executor: &mut E, scheduler: &CS) -> Result + fn process( + &mut self, + state: &mut S, + executor: &mut E, + scheduler: &CS, + ) -> Result where CS: CorpusScheduler, E: Executor + HasObservers, - OT: ObserversTuple + OT: ObserversTuple, { // TODO: Get around local event copy by moving handle_in_client let mut events = vec![]; @@ -430,11 +438,16 @@ where .send_buf(_LLMP_TAG_RESTART, &state_corpus_serialized) } - fn process(&mut self, state: &mut S, executor: &mut E, scheduler: &CS) -> Result + fn process( + &mut self, + state: &mut S, + executor: &mut E, + scheduler: &CS, + ) -> Result where CS: CorpusScheduler, E: Executor + HasObservers, - OT: ObserversTuple + OT: ObserversTuple, { self.llmp_mgr.process(state, executor, scheduler) } diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index 934a3163c7..280c1ac8bf 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -10,9 +10,9 @@ use core::{fmt, marker::PhantomData, time::Duration}; use serde::{Deserialize, Serialize}; use crate::{ + corpus::CorpusScheduler, executors::{Executor, HasObservers}, inputs::Input, - corpus::{ CorpusScheduler}, observers::ObserversTuple, Error, }; @@ -160,7 +160,12 @@ where /// Lookup for incoming events and process them. /// Return the number of processes events or an error - fn process(&mut self, state: &mut S, executor: &mut E, scheduler: &CS) -> Result + fn process( + &mut self, + state: &mut S, + executor: &mut E, + scheduler: &CS, + ) -> Result where CS: CorpusScheduler, E: Executor + HasObservers, @@ -205,7 +210,12 @@ impl EventManager for NopEventManager where I: Input, { - fn process(&mut self, _state: &mut S, _executor: &mut E, _scheduler: &CS) -> Result + fn process( + &mut self, + _state: &mut S, + _executor: &mut E, + _scheduler: &CS, + ) -> Result where CS: CorpusScheduler, E: Executor + HasObservers, diff --git a/libafl/src/events/simple.rs b/libafl/src/events/simple.rs index b3f3fce830..ec91e2c1b1 100644 --- a/libafl/src/events/simple.rs +++ b/libafl/src/events/simple.rs @@ -3,11 +3,11 @@ use alloc::{string::ToString, vec::Vec}; use core::marker::PhantomData; use crate::{ + corpus::CorpusScheduler, events::{BrokerEventResult, Event, EventManager}, executors::{Executor, HasObservers}, inputs::Input, observers::ObserversTuple, - corpus::CorpusScheduler, stats::Stats, Error, }; @@ -31,11 +31,16 @@ where I: Input, ST: Stats, //CE: CustomEvent, { - fn process(&mut self, state: &mut S, _executor: &mut E, _scheduler: &CS) -> Result + fn process( + &mut self, + state: &mut S, + _executor: &mut E, + _scheduler: &CS, + ) -> Result where CS: CorpusScheduler, E: Executor + HasObservers, - OT: ObserversTuple + OT: ObserversTuple, { let count = self.events.len(); while self.events.len() > 0 { diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index 127794458a..d4507ed9b6 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -105,7 +105,8 @@ where 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, self.scheduler(), idx)?; + self.stages() + .perform_all(state, executor, manager, self.scheduler(), idx)?; manager.process(state, executor, self.scheduler())?; Ok(idx) diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 03ada9567d..e50a165d0b 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -7,7 +7,7 @@ use crate::{ /// 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, @@ -24,7 +24,7 @@ where ) -> Result<(), Error>; } -pub trait StagesTuple +pub trait StagesTuple where EM: EventManager, E: Executor, @@ -40,7 +40,7 @@ where ) -> Result<(), Error>; } -impl StagesTuple for () +impl StagesTuple for () where EM: EventManager, E: Executor, @@ -51,10 +51,10 @@ where } } -impl StagesTuple for (Head, Tail) +impl StagesTuple for (Head, Tail) where - Head: Stage, - Tail: StagesTuple + TupleList, + Head: Stage, + Tail: StagesTuple + TupleList, EM: EventManager, E: Executor, I: Input, @@ -67,7 +67,9 @@ where scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error> { - self.0.perform(state, executor, manager, scheduler, corpus_idx)?; - self.1.perform_all(state, executor, manager, scheduler, corpus_idx) + self.0 + .perform(state, executor, manager, scheduler, corpus_idx)?; + self.1 + .perform_all(state, executor, manager, scheduler, corpus_idx) } } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 7a2731a058..bf05d9c803 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -27,7 +27,7 @@ where EM: EventManager, E: Executor + HasObservers, OT: ObserversTuple, - CS: CorpusScheduler + CS: CorpusScheduler, { /// The mutator registered for this stage fn mutator(&self) -> &M; @@ -116,7 +116,8 @@ where } } -impl Stage for StdMutationalStage +impl Stage + for StdMutationalStage where M: Mutator, I: Input, diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index e1d29ecb37..b9ce39b076 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -10,7 +10,7 @@ use std::{ use crate::{ bolts::serdeany::{SerdeAny, SerdeAnyMap}, - corpus::{Corpus, Testcase, CorpusScheduler}, + corpus::{Corpus, CorpusScheduler, Testcase}, events::{Event, EventManager, LogSeverity}, executors::{Executor, ExitKind, HasObservers}, feedbacks::FeedbacksTuple, @@ -175,7 +175,12 @@ 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, scheduler: &CS) -> Result, Error> + fn add_if_interesting( + &mut self, + input: &I, + fitness: u32, + scheduler: &CS, + ) -> Result, Error> where CS: CorpusScheduler, Self: Sized; @@ -455,9 +460,14 @@ where /// 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, scheduler: &CS) -> Result, Error> + fn add_if_interesting( + &mut self, + input: &I, + fitness: u32, + scheduler: &CS, + ) -> Result, Error> where - CS: CorpusScheduler + CS: CorpusScheduler, { if fitness > 0 { let testcase = self.testcase_with_feedbacks_metadata(input.clone(), fitness)?; @@ -495,7 +505,7 @@ where OT: ObserversTuple, C: Corpus, EM: EventManager, - CS: CorpusScheduler + CS: CorpusScheduler, { let (fitness, is_solution) = self.execute_input(&input, executor, manager)?; let observers = executor.observers(); @@ -505,7 +515,10 @@ where self.solutions_mut().add(Testcase::new(input.clone()))?; } - if !self.add_if_interesting(&input, fitness, scheduler)?.is_none() { + if !self + .add_if_interesting(&input, fitness, scheduler)? + .is_none() + { let observers_buf = manager.serialize_observers(observers)?; manager.fire( self, @@ -544,7 +557,7 @@ where E: Executor + HasObservers, OT: ObserversTuple, EM: EventManager, - CS: CorpusScheduler + CS: CorpusScheduler, { for entry in fs::read_dir(in_dir)? { let entry = entry?; @@ -562,7 +575,10 @@ where let bytes = fs::read(&path)?; let input = BytesInput::new(bytes); let (fitness, is_solution) = self.execute_input(&input, executor, manager)?; - if self.add_if_interesting(&input, fitness, scheduler)?.is_none() { + if self + .add_if_interesting(&input, fitness, scheduler)? + .is_none() + { println!("File {:?} was not interesting, skipped.", &path); } if is_solution { @@ -662,7 +678,7 @@ where E: Executor + HasObservers, OT: ObserversTuple, EM: EventManager, - CS: CorpusScheduler + CS: CorpusScheduler, { let mut added = 0; for _ in 0..num { From 7c9fc88e66588c81458ed032b33749d686fbf9f9 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 1 Mar 2021 10:20:08 +0100 Subject: [PATCH 09/11] use hitcounts in libfuzzer rt --- fuzzers/libfuzzer_libpng/build.rs | 1 + fuzzers/libfuzzer_libpng/harness.cc | 7 +++---- fuzzers/libfuzzer_libpng/src/fuzzer.rs | 1 - fuzzers/libfuzzer_runtime/rt.c | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/fuzzers/libfuzzer_libpng/build.rs b/fuzzers/libfuzzer_libpng/build.rs index f19a4eed1d..d41462e8a8 100644 --- a/fuzzers/libfuzzer_libpng/build.rs +++ b/fuzzers/libfuzzer_libpng/build.rs @@ -95,6 +95,7 @@ fn main() { cc::Build::new() .include(&libpng_path) .flag("-fsanitize-coverage=trace-pc-guard") + // .define("HAS_DUMMY_CRASH", "1") .file("./harness.cc") .compile("libfuzzer-harness"); diff --git a/fuzzers/libfuzzer_libpng/harness.cc b/fuzzers/libfuzzer_libpng/harness.cc index 8d276ed86a..ea6680018f 100644 --- a/fuzzers/libfuzzer_libpng/harness.cc +++ b/fuzzers/libfuzzer_libpng/harness.cc @@ -20,8 +20,6 @@ #include -#define HAS_BUG 1 - #define PNG_INTERNAL #include "png.h" @@ -159,8 +157,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // This is going to be too slow. if (width && height > 100000000 / width) { PNG_CLEANUP - if (HAS_BUG) - asm("ud2"); +#ifdef HAS_DUMMY_CRASH + asm("ud2"); +#endif return 0; } diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index c18aa49c7f..8c37ef7cf5 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -124,7 +124,6 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); - //let scheduler = QueueCorpusScheduler::new(); let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); // Create the executor for an in-process function with just one observer for edge coverage diff --git a/fuzzers/libfuzzer_runtime/rt.c b/fuzzers/libfuzzer_runtime/rt.c index 0022249d8f..90f4fea89a 100644 --- a/fuzzers/libfuzzer_runtime/rt.c +++ b/fuzzers/libfuzzer_runtime/rt.c @@ -17,9 +17,9 @@ uint32_t __lafl_max_edges_size = 0; void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { uint32_t pos = *guard; - //uint16_t val = __lafl_edges_map[pos] + 1; - //__lafl_edges_map[pos] = ((uint8_t) val) + (uint8_t) (val >> 8); - __lafl_edges_map[pos] = 1; + uint16_t val = __lafl_edges_map[pos] + 1; + __lafl_edges_map[pos] = ((uint8_t) val) + (uint8_t) (val >> 8); + //__lafl_edges_map[pos] = 1; } From 405bfc5294545b90a796014bd57a30b38f8bf2ec Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 1 Mar 2021 10:31:47 +0100 Subject: [PATCH 10/11] fix #14 --- libafl/src/observers/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 01811cf81b..a63155c030 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -252,7 +252,7 @@ where } static COUNT_CLASS_LOOKUP: [u8; 256] = [ - 0, 1, 2, 0, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 0, 1, 2, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, From bd800b63f58111c18c58d47c77e530a11f0e730e Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 1 Mar 2021 10:34:59 +0100 Subject: [PATCH 11/11] hitcounts observer in libpng example --- fuzzers/libfuzzer_libpng/src/fuzzer.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index 8c37ef7cf5..328c8393fd 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -16,7 +16,7 @@ use libafl::{ inputs::Input, mutators::scheduled::HavocBytesMutator, mutators::token_mutations::TokensMetadata, - observers::StdMapObserver, + observers::{HitcountsMapObserver, StdMapObserver}, stages::mutational::StdMutationalStage, state::{HasCorpus, HasMetadata, State}, stats::SimpleStats, @@ -79,10 +79,11 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> .expect("Failed to setup the restarter".into()); // Create an observation channel using the coverage map - let edges_observer = - StdMapObserver::new_from_ptr("edges", unsafe { __lafl_edges_map }, unsafe { - __lafl_max_edges_size as usize - }); + let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr( + "edges", + unsafe { __lafl_edges_map }, + unsafe { __lafl_max_edges_size as usize }, + )); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| {