From 16d4c36f12cc754a4e26586330a359f6cee19c4e Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 20 May 2021 11:49:28 +0200 Subject: [PATCH] working random cmplog mutations --- fuzzers/libfuzzer_stb_image/src/main.rs | 18 ++-- libafl/src/bolts/tuples.rs | 15 +-- libafl/src/feedbacks/cmp.rs | 122 ------------------------ libafl/src/feedbacks/mod.rs | 3 - libafl/src/mutators/token_mutations.rs | 5 +- libafl/src/observers/cmp.rs | 110 ++++++++++++++++++++- libafl_targets/src/cmplog.rs | 16 +++- 7 files changed, 136 insertions(+), 153 deletions(-) delete mode 100644 libafl/src/feedbacks/cmp.rs diff --git a/fuzzers/libfuzzer_stb_image/src/main.rs b/fuzzers/libfuzzer_stb_image/src/main.rs index 14256237a9..b7a144ebe7 100644 --- a/fuzzers/libfuzzer_stb_image/src/main.rs +++ b/fuzzers/libfuzzer_stb_image/src/main.rs @@ -12,15 +12,15 @@ use libafl::{ events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, - feedbacks::{CmpFeedback, CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback}, + feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - mutators::token_mutations::{I2SRandReplace, Tokens}, + mutators::token_mutations::I2SRandReplace, observers::{StdMapObserver, TimeObserver}, stages::{StdMutationalStage, TracingStage}, state::{HasCorpus, StdState}, - stats::MultiStats, + stats::SimpleStats, Error, }; @@ -73,7 +73,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re let time_observer = TimeObserver::new("time"); let cmplog = unsafe { &mut CMPLOG_MAP }; - let cmplog_observer = CmpLogObserver::new("cmplog", cmplog); + let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true); let cmplog = unsafe { &mut CMPLOG_MAP }; let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true); @@ -155,9 +155,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re } // Secondary harness due to mut ownership - let mut harness = |input: &BytesInput| { - let target = input.target_bytes(); - let buf = target.as_slice(); + let mut harness = |buf: &[u8]| { libfuzzer_test_one_input(buf); ExitKind::Ok }; @@ -172,14 +170,16 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re )?); // Setup a randomic Input2State stage - let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new()))); + + //let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new()))); // Setup a basic mutator let mutator = StdScheduledMutator::new(havoc_mutations()); let mutational = StdMutationalStage::new(mutator); // The order of the stages matter! - let mut stages = tuple_list!(tracing, i2s, mutational); + + let mut stages = tuple_list!(tracing, mutational); fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?; diff --git a/libafl/src/bolts/tuples.rs b/libafl/src/bolts/tuples.rs index 8f9ab3d289..e9ae0f0c83 100644 --- a/libafl/src/bolts/tuples.rs +++ b/libafl/src/bolts/tuples.rs @@ -361,12 +361,9 @@ where } /// Merge two `TupeList` -pub trait Merge: TupleList -where - T: TupleList, -{ +pub trait Merge { /// The Resulting [`TupleList`], of an [`Merge::merge()`] call - type MergeResult: TupleList; + type MergeResult; /// Merge and return the merged tuple #[must_use] @@ -374,10 +371,7 @@ where } /// Implement merge for an empty tuple list. -impl Merge for () -where - T: TupleList, -{ +impl Merge for () { type MergeResult = T; fn merge(self, value: T) -> Self::MergeResult { @@ -388,10 +382,7 @@ where /// Implement merge for non-empty tuple list. impl Merge for (Head, Tail) where - T: TupleList, - Self: TupleList, Tail: Merge, - (Head, Tail::MergeResult): TupleList, { type MergeResult = (Head, Tail::MergeResult); diff --git a/libafl/src/feedbacks/cmp.rs b/libafl/src/feedbacks/cmp.rs deleted file mode 100644 index dd51c058c7..0000000000 --- a/libafl/src/feedbacks/cmp.rs +++ /dev/null @@ -1,122 +0,0 @@ -use alloc::string::{String, ToString}; -use core::marker::PhantomData; -use serde::{Deserialize, Serialize}; - -use crate::{ - bolts::{tuples::Named, AsSlice}, - executors::ExitKind, - feedbacks::Feedback, - inputs::Input, - observers::{CmpMap, CmpObserver, CmpValues, ObserversTuple}, - state::HasMetadata, - Error, -}; - -/// A state metadata holding a list of values logged from comparisons -#[derive(Serialize, Deserialize)] -pub struct CmpValuesMetadata { - /// A `list` of values. - pub list: Vec, -} - -crate::impl_serdeany!(CmpValuesMetadata); - -impl AsSlice for CmpValuesMetadata { - /// Convert to a slice - #[must_use] - fn as_slice(&self) -> &[CmpValues] { - self.list.as_slice() - } -} - -impl CmpValuesMetadata { - /// Creates a new [`struct@CmpValuesMetadata`] - #[must_use] - pub fn new() -> Self { - Self { list: vec![] } - } -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct CmpFeedback -where - CO: CmpObserver, - CM: CmpMap, -{ - name: String, - phantom: PhantomData<(CM, CO)>, -} - -impl Feedback for CmpFeedback -where - I: Input, - CO: CmpObserver, - CM: CmpMap, - S: HasMetadata, -{ - fn is_interesting( - &mut self, - state: &mut S, - _input: &I, - observers: &OT, - _exit_kind: &ExitKind, - ) -> Result - where - OT: ObserversTuple, - { - if state.metadata().get::().is_none() { - state.add_metadata(CmpValuesMetadata::new()); - } - let meta = state.metadata_mut().get_mut::().unwrap(); - meta.list.clear(); - // TODO Replace with match_name_type when stable - let observer = observers.match_name::(self.name()).unwrap(); - let count = observer.usable_count(); - for i in 0..count { - let execs = observer.map().usable_executions_for(i); - if execs > 0 { - // Recongize loops and discard - // TODO - for j in 0..execs { - meta.list.push(observer.map().values_of(i, j)); - } - } - } - Ok(false) - } -} - -impl Named for CmpFeedback -where - CO: CmpObserver, - CM: CmpMap, -{ - #[inline] - fn name(&self) -> &str { - self.name.as_str() - } -} - -impl CmpFeedback -where - CO: CmpObserver, - CM: CmpMap, -{ - /// Creates a new [`CmpFeedback`] - #[must_use] - pub fn with_name(name: &'static str) -> Self { - Self { - name: name.to_string(), - phantom: PhantomData, - } - } - - /// Creates a new [`CmpFeedback`] - #[must_use] - pub fn new(observer: &CO) -> Self { - Self { - name: observer.name().to_string(), - phantom: PhantomData, - } - } -} diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index ce3c2390c0..b7e001b8f0 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -4,9 +4,6 @@ pub mod map; pub use map::*; -pub mod cmp; -pub use cmp::*; - use alloc::string::{String, ToString}; use serde::{Deserialize, Serialize}; diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index f2f1733b7f..c50e671c11 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -15,10 +15,9 @@ use std::{ use crate::{ bolts::rands::Rand, - feedbacks::cmp::CmpValuesMetadata, inputs::{HasBytesVec, Input}, mutators::{buffer_self_copy, mutations::buffer_copy, MutationResult, Mutator, Named}, - observers::cmp::CmpValues, + observers::cmp::{CmpValues, CmpValuesMetadata}, state::{HasMaxSize, HasMetadata, HasRand}, Error, }; @@ -449,6 +448,8 @@ where } } + //println!("{:?}", result); + Ok(result) } } diff --git a/libafl/src/observers/cmp.rs b/libafl/src/observers/cmp.rs index 61e84944a6..4f0cd4c18d 100644 --- a/libafl/src/observers/cmp.rs +++ b/libafl/src/observers/cmp.rs @@ -8,13 +8,14 @@ use alloc::{ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ - bolts::{ownedref::OwnedRefMut, tuples::Named}, + bolts::{ownedref::OwnedRefMut, tuples::Named, AsSlice}, executors::HasExecHooks, observers::Observer, + state::HasMetadata, Error, }; -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub enum CmpValues { U8((u8, u8)), U16((u16, u16)), @@ -23,6 +24,53 @@ pub enum CmpValues { Bytes((Vec, Vec)), } +impl CmpValues { + pub fn is_numeric(&self) -> bool { + match self { + CmpValues::U8(_) => true, + CmpValues::U16(_) => true, + CmpValues::U32(_) => true, + CmpValues::U64(_) => true, + _ => false, + } + } + + pub fn to_u64_tuple(&self) -> Option<(u64, u64)> { + match self { + CmpValues::U8(t) => Some((t.0 as u64, t.1 as u64)), + CmpValues::U16(t) => Some((t.0 as u64, t.1 as u64)), + CmpValues::U32(t) => Some((t.0 as u64, t.1 as u64)), + CmpValues::U64(t) => Some((t.0 as u64, t.1 as u64)), + _ => None, + } + } +} + +/// A state metadata holding a list of values logged from comparisons +#[derive(Serialize, Deserialize)] +pub struct CmpValuesMetadata { + /// A `list` of values. + pub list: Vec, +} + +crate::impl_serdeany!(CmpValuesMetadata); + +impl AsSlice for CmpValuesMetadata { + /// Convert to a slice + #[must_use] + fn as_slice(&self) -> &[CmpValues] { + self.list.as_slice() + } +} + +impl CmpValuesMetadata { + /// Creates a new [`struct@CmpValuesMetadata`] + #[must_use] + pub fn new() -> Self { + Self { list: vec![] } + } +} + /// A [`CmpMap`] traces comparisons during the current execution pub trait CmpMap: Serialize + DeserializeOwned { /// Get the number of cmps @@ -49,6 +97,64 @@ where fn map(&self) -> &CM; fn map_mut(&mut self) -> &mut CM; + + fn add_cmpvalues_meta(&mut self, state: &mut S) + where + S: HasMetadata, + { + if state.metadata().get::().is_none() { + state.add_metadata(CmpValuesMetadata::new()); + } + let meta = state.metadata_mut().get_mut::().unwrap(); + meta.list.clear(); + let count = self.usable_count(); + for i in 0..count { + let execs = self.map().usable_executions_for(i); + if execs > 0 { + // Recongize loops and discard if needed + if execs > 4 { + let mut increasing_v0 = 0; + let mut increasing_v1 = 0; + let mut decreasing_v0 = 0; + let mut decreasing_v1 = 0; + + let mut last: Option = None; + for j in 0..execs { + let val = self.map().values_of(i, j); + if let Some(l) = last.and_then(|x| x.to_u64_tuple()) { + if let Some(v) = val.to_u64_tuple() { + if l.0.wrapping_add(1) == v.0 { + increasing_v0 += 1; + } + if l.1.wrapping_add(1) == v.1 { + increasing_v1 += 1; + } + if l.0.wrapping_sub(1) == v.0 { + decreasing_v0 += 1; + } + if l.1.wrapping_sub(1) == v.1 { + decreasing_v1 += 1; + } + } + } + last = Some(val); + } + // We check for execs-2 because the logged execs may wrap and have something like + // 8 9 10 3 4 5 6 7 + if increasing_v0 >= execs - 2 + || increasing_v1 >= execs - 2 + || decreasing_v0 >= execs - 2 + || decreasing_v1 >= execs - 2 + { + continue; + } + } + for j in 0..execs { + meta.list.push(self.map().values_of(i, j)); + } + } + } + } } /// A standard [`CmpObserver`] observer diff --git a/libafl_targets/src/cmplog.rs b/libafl_targets/src/cmplog.rs index 182ea2539c..e217bfb7ce 100644 --- a/libafl_targets/src/cmplog.rs +++ b/libafl_targets/src/cmplog.rs @@ -5,6 +5,7 @@ use libafl::{ bolts::{ownedref::OwnedRefMut, tuples::Named}, executors::HasExecHooks, observers::{CmpMap, CmpObserver, CmpValues, Observer}, + state::HasMetadata, Error, }; @@ -148,6 +149,7 @@ pub use libafl_cmplog_enabled as CMPLOG_ENABLED; pub struct CmpLogObserver<'a> { map: OwnedRefMut<'a, CmpLogMap>, size: Option>, + add_meta: bool, name: String, } @@ -171,7 +173,10 @@ impl<'a> CmpObserver for CmpLogObserver<'a> { impl<'a> Observer for CmpLogObserver<'a> {} -impl<'a, EM, I, S, Z> HasExecHooks for CmpLogObserver<'a> { +impl<'a, EM, I, S, Z> HasExecHooks for CmpLogObserver<'a> +where + S: HasMetadata, +{ fn pre_exec( &mut self, _fuzzer: &mut Z, @@ -189,13 +194,17 @@ impl<'a, EM, I, S, Z> HasExecHooks for CmpLogObserver<'a> { fn post_exec( &mut self, _fuzzer: &mut Z, - _state: &mut S, + state: &mut S, _mgr: &mut EM, _input: &I, ) -> Result<(), Error> { unsafe { CMPLOG_ENABLED = 0; } + + if self.add_meta { + self.add_cmpvalues_meta(state); + } Ok(()) } } @@ -209,11 +218,12 @@ impl<'a> Named for CmpLogObserver<'a> { impl<'a> CmpLogObserver<'a> { /// Creates a new [`CmpLogObserver`] with the given name. #[must_use] - pub fn new(name: &'static str, map: &'a mut CmpLogMap) -> Self { + pub fn new(name: &'static str, map: &'a mut CmpLogMap, add_meta: bool) -> Self { Self { name: name.to_string(), size: None, + add_meta, map: OwnedRefMut::Ref(map), } }