From acee52ddfb76c324f8fc6418b3105d8af4cdb586 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 23 Nov 2020 16:47:40 +0100 Subject: [PATCH] tracker map feedback --- README.md | 9 ++ afl/src/engines/mod.rs | 2 +- afl/src/feedbacks/mod.rs | 180 +++++++++++++++++++++++++++++++---- afl/src/utils.rs | 52 +++++++++- fuzzers/libfuzzer/src/lib.rs | 7 +- 5 files changed, 223 insertions(+), 27 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000..59aaf03c0a --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +How to perf: + +``` +perf record -e task-clock ./PROGRAM + +perf report --stdio --dsos=PROGRAM + +rm perf.data +``` diff --git a/afl/src/engines/mod.rs b/afl/src/engines/mod.rs index bfb3f1ae09..60e121e545 100644 --- a/afl/src/engines/mod.rs +++ b/afl/src/engines/mod.rs @@ -126,7 +126,7 @@ where fitness += feedback.is_interesting(&input)?; } - if fitness >= 25 { + if fitness > 0 { let testcase: Rc> = Testcase::new(input).into(); for feedback in self.feedbacks_mut() { feedback.append_metadata(testcase.clone())?; diff --git a/afl/src/feedbacks/mod.rs b/afl/src/feedbacks/mod.rs index 07390bc141..fe9ae2742b 100644 --- a/afl/src/feedbacks/mod.rs +++ b/afl/src/feedbacks/mod.rs @@ -6,7 +6,7 @@ use core::cell::RefCell; use core::marker::PhantomData; use num::Integer; -use crate::corpus::Testcase; +use crate::corpus::{Testcase, TestcaseMetadata}; use crate::inputs::Input; use crate::observers::MapObserver; use crate::AflError; @@ -77,6 +77,16 @@ where } } +/// Returns a usable history map of the given size +pub fn create_history_map(map_size: usize) -> Rc>> +where + T: Default + Clone, +{ + { + Rc::new(RefCell::new(vec![T::default(); map_size])) + } +} + /// The most common AFL-like feedback type pub struct MapFeedback where @@ -102,26 +112,40 @@ where fn is_interesting(&mut self, _input: &I) -> Result { let mut interesting = 0; - // TODO: impl. correctly, optimize - for (history, map) in self - .history_map - .borrow_mut() - .iter_mut() - .zip(self.map_observer.borrow().map().iter()) - { - let reduced = R::reduce(*history, *map); - if *history != reduced { - *history = reduced; - interesting += 25; - if interesting >= 250 { - return Ok(255); - } + // TODO optimize + let size = self.map_observer.borrow().map().len(); + let mut history_map = self.history_map.borrow_mut(); + let observer = self.map_observer.borrow(); + for i in 0..size { + let history = history_map[i]; + let item = observer.map()[i]; + let reduced = R::reduce(history, item); + if history != reduced { + history_map[i] = reduced; + interesting += 1; } } + Ok(interesting) } } +impl MapFeedback +where + T: Integer + Copy + Default + 'static, + R: Reducer, + O: MapObserver, +{ + /// Create new MapFeedback using a map observer + pub fn new(map_observer: Rc>, map_size: usize) -> Self { + MapFeedback { + map_observer: map_observer, + history_map: create_history_map::(map_size), + phantom: PhantomData, + } + } +} + impl MapFeedback where T: Integer + Copy + 'static, @@ -130,7 +154,10 @@ where { /// Create new MapFeedback using a map observer, and a map. /// The map can be shared. - pub fn new(map_observer: Rc>, history_map: Rc>>) -> Self { + pub fn with_history_map( + map_observer: Rc>, + history_map: Rc>>, + ) -> Self { MapFeedback { map_observer: map_observer, history_map: history_map, @@ -139,15 +166,126 @@ where } } -/// Returns a usable history map of the given size -pub fn create_history_map(map_size: usize) -> Rc>> +pub struct MapNoveltiesMetadata { + novelties: Vec, +} +impl TestcaseMetadata for MapNoveltiesMetadata { + fn name(&self) -> &'static str { + "MapNoveltiesMetadata" + } +} +impl MapNoveltiesMetadata { + pub fn novelties(&self) -> &[usize] { + &self.novelties + } + + pub fn new(novelties: Vec) -> Self { + MapNoveltiesMetadata { + novelties: novelties, + } + } +} + +/// The most common AFL-like feedback type that adds metadata about newly discovered entries +pub struct MapTrackerFeedback where - T: Default + Clone, + T: Integer + Copy + 'static, + R: Reducer, + O: MapObserver, { - { - Rc::new(RefCell::new(vec![T::default(); map_size])) + /// Contains information about untouched entries + history_map: Rc>>, + /// The observer this feedback struct observes + map_observer: Rc>, + /// Phantom Data of Reducer + phantom: PhantomData, + /// Track novel entries indexes + novelties: Vec, +} + +impl Feedback for MapTrackerFeedback +where + T: Integer + Copy + 'static, + R: Reducer, + O: MapObserver, + I: Input, +{ + fn is_interesting(&mut self, _input: &I) -> Result { + let mut interesting = 0; + + // TODO optimize + let size = self.map_observer.borrow().map().len(); + let mut history_map = self.history_map.borrow_mut(); + let observer = self.map_observer.borrow(); + for i in 0..size { + let history = history_map[i]; + let item = observer.map()[i]; + let reduced = R::reduce(history, item); + if history != reduced { + history_map[i] = reduced; + interesting += 1; + self.novelties.push(i); + } + } + + Ok(interesting) + } + + fn append_metadata(&mut self, testcase: Rc>>) -> Result<(), AflError> { + let meta = Box::new(MapNoveltiesMetadata::new(core::mem::take( + &mut self.novelties, + ))); + testcase.borrow_mut().add_metadata(meta); + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + fn discard_metadata(&mut self) -> Result<(), AflError> { + self.novelties.clear(); + Ok(()) + } +} + +impl MapTrackerFeedback +where + T: Integer + Copy + Default + 'static, + R: Reducer, + O: MapObserver, +{ + /// Create new MapFeedback using a map observer + pub fn new(map_observer: Rc>, map_size: usize) -> Self { + MapTrackerFeedback { + map_observer: map_observer, + history_map: create_history_map::(map_size), + phantom: PhantomData, + novelties: vec![], + } + } +} + +impl MapTrackerFeedback +where + T: Integer + Copy + 'static, + R: Reducer, + O: MapObserver, +{ + /// Create new MapFeedback using a map observer, and a map. + /// The map can be shared. + pub fn with_history_map( + map_observer: Rc>, + history_map: Rc>>, + ) -> Self { + MapTrackerFeedback { + map_observer: map_observer, + history_map: history_map, + phantom: PhantomData, + novelties: vec![], + } } } pub type MaxMapFeedback = MapFeedback, O>; pub type MinMapFeedback = MapFeedback, O>; + +pub type MaxMapTrackerFeedback = MapFeedback, O>; +pub type MinMapTrackerFeedback = MapFeedback, O>; diff --git a/afl/src/utils.rs b/afl/src/utils.rs index 1ed8ab73e0..de469a80ea 100644 --- a/afl/src/utils.rs +++ b/afl/src/utils.rs @@ -175,7 +175,7 @@ pub struct XorShift64Rand { impl Rand for XorShift64Rand { fn set_seed(&mut self, seed: u64) { - self.rand_seed = seed; + self.rand_seed = seed ^ 0x1234567890abcdef; self.seeded = true; } @@ -219,6 +219,56 @@ impl XorShift64Rand { } } +/// XXH3 Based, hopefully speedy, rnd implementation +/// +#[derive(Copy, Clone, Debug, Default)] +pub struct Lehmer64Rand { + rand_seed: u128, + seeded: bool, +} + +impl Rand for Lehmer64Rand { + fn set_seed(&mut self, seed: u64) { + self.rand_seed = (seed as u128) ^ 0x1234567890abcdef; + self.seeded = true; + } + + fn next(&mut self) -> u64 { + self.rand_seed *= 0xda942042e4dd58b5; + return (self.rand_seed >> 64) as u64; + } +} + +impl Into>> for Lehmer64Rand { + fn into(self) -> Rc> { + Rc::new(RefCell::new(self)) + } +} + +impl Lehmer64Rand { + /// Creates a new Xoshiro rand with the given seed + pub fn new(seed: u64) -> Self { + let mut ret: Self = Default::default(); + ret.set_seed(seed); // TODO: Proper random seed? + ret + } + + pub fn to_rc_refcell(self) -> Rc> { + self.into() + } + + /// Creates a rand instance, pre-seeded with the current time in nanoseconds. + /// Needs stdlib timer + #[cfg(feature = "std")] + pub fn preseeded() -> Self { + let seed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64; + Self::new(seed) + } +} + #[cfg(feature = "std")] pub fn current_milliseconds() -> u64 { SystemTime::now() diff --git a/fuzzers/libfuzzer/src/lib.rs b/fuzzers/libfuzzer/src/lib.rs index 5abeeb1bbd..ddcb5be73f 100644 --- a/fuzzers/libfuzzer/src/lib.rs +++ b/fuzzers/libfuzzer/src/lib.rs @@ -8,7 +8,7 @@ use afl::engines::{generate_initial_inputs, Engine, State, StdEngine, StdState}; use afl::events::LoggerEventManager; use afl::executors::inmemory::InMemoryExecutor; use afl::executors::{Executor, ExitKind}; -use afl::feedbacks::{create_history_map, MaxMapFeedback}; +use afl::feedbacks::MaxMapTrackerFeedback; use afl::generators::RandPrintablesGenerator; use afl::mutators::scheduled::HavocBytesMutator; use afl::observers::StdMapObserver; @@ -39,15 +39,14 @@ pub extern "C" fn afl_libfuzzer_main() { let mut rand = StdRand::new(0); let corpus = InMemoryCorpus::new(); - let mut generator = RandPrintablesGenerator::new(1); + let mut generator = RandPrintablesGenerator::new(32); let mut events = LoggerEventManager::new(stderr()); let edges_observer = Rc::new(RefCell::new(StdMapObserver::new_from_ptr( unsafe { __lafl_edges_map }, unsafe { __lafl_max_edges_size as usize }, ))); - let edges_history_map = create_history_map::(MAP_SIZE); - let edges_feedback = MaxMapFeedback::new(edges_observer.clone(), edges_history_map); + let edges_feedback = MaxMapTrackerFeedback::new(edges_observer.clone(), MAP_SIZE); let executor = InMemoryExecutor::new(harness); let mut state = StdState::new(corpus, executor);