From 8437c4adb7da06eff0502841e160a11662eabce7 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 19 May 2021 23:57:00 +0200 Subject: [PATCH] cmp observer and feedback --- libafl/src/executors/combined.rs | 7 +- libafl/src/feedbacks/cmp.rs | 80 ++++++++++++++------- libafl/src/feedbacks/mod.rs | 3 + libafl/src/observers/cmp.rs | 119 +++++++++++++++++++++++++++---- libafl_targets/Cargo.toml | 2 +- 5 files changed, 165 insertions(+), 46 deletions(-) diff --git a/libafl/src/executors/combined.rs b/libafl/src/executors/combined.rs index 535a63377b..9d208e8bac 100644 --- a/libafl/src/executors/combined.rs +++ b/libafl/src/executors/combined.rs @@ -21,7 +21,6 @@ where { primary: A, secondary: B, - exec_tmout: Duration, phantom: PhantomData, } @@ -80,7 +79,7 @@ where } } -impl HasObserversHooks for TimeoutExecutor +impl HasObserversHooks for CombinedExecutor where A: Executor + HasObservers, B: Executor, @@ -89,9 +88,9 @@ where { } -impl HasExecHooks for TimeoutExecutor +impl HasExecHooks for CombinedExecutor where - A: Executor + HasObservers, + A: Executor + HasExecHooks, B: Executor, I: Input, { diff --git a/libafl/src/feedbacks/cmp.rs b/libafl/src/feedbacks/cmp.rs index 1617d204bd..dd51c058c7 100644 --- a/libafl/src/feedbacks/cmp.rs +++ b/libafl/src/feedbacks/cmp.rs @@ -1,18 +1,14 @@ -use alloc::{ - string::{String, ToString}, -}; +use alloc::string::{String, ToString}; use core::marker::PhantomData; -use num::Integer; use serde::{Deserialize, Serialize}; use crate::{ - bolts::tuples::Named, + bolts::{tuples::Named, AsSlice}, executors::ExitKind, - feedbacks::{Feedback, FeedbackState, FeedbackStatesTuple}, + feedbacks::Feedback, inputs::Input, - observers::{CmpObserver, ObserversTuple}, - state::{HasFeedbackStates, HasMetadata}, - utils::AsSlice, + observers::{CmpMap, CmpObserver, CmpValues, ObserversTuple}, + state::HasMetadata, Error, }; @@ -20,15 +16,15 @@ use crate::{ #[derive(Serialize, Deserialize)] pub struct CmpValuesMetadata { /// A `list` of values. - pub list: Vec<(u64, u64)>, + pub list: Vec, } - + crate::impl_serdeany!(CmpValuesMetadata); -impl AsSlice<(u64, u64)> for CmpValuesMetadata { +impl AsSlice for CmpValuesMetadata { /// Convert to a slice #[must_use] - fn as_slice(&self) -> &[(u64, u64)] { + fn as_slice(&self) -> &[CmpValues] { self.list.as_slice() } } @@ -36,24 +32,31 @@ impl AsSlice<(u64, u64)> for CmpValuesMetadata { impl CmpValuesMetadata { /// Creates a new [`struct@CmpValuesMetadata`] #[must_use] - pub fn new(list: Vec<(u64, u64)>) -> Self { - Self { list } + pub fn new() -> Self { + Self { list: vec![] } } } #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct CmpFeedback { +pub struct CmpFeedback +where + CO: CmpObserver, + CM: CmpMap, +{ name: String, - phantom: PhantomData + phantom: PhantomData<(CM, CO)>, } -impl Feedback for CmpFeedback +impl Feedback for CmpFeedback where I: Input, + CO: CmpObserver, + CM: CmpMap, + S: HasMetadata, { fn is_interesting( &mut self, - _state: &mut S, + state: &mut S, _input: &I, observers: &OT, _exit_kind: &ExitKind, @@ -61,36 +64,59 @@ where 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(); - // TODO + 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 { +impl Named for CmpFeedback +where + CO: CmpObserver, + CM: CmpMap, +{ #[inline] fn name(&self) -> &str { self.name.as_str() } } -impl CmpFeedback { +impl CmpFeedback +where + CO: CmpObserver, + CM: CmpMap, +{ /// Creates a new [`CmpFeedback`] #[must_use] - pub fn new(name: &'static str) -> Self { + pub fn with_name(name: &'static str) -> Self { Self { name: name.to_string(), - phantom: PhantomData + phantom: PhantomData, } } /// Creates a new [`CmpFeedback`] #[must_use] - pub fn new_with_observer(observer: &CmpObserver) -> Self { + pub fn new(observer: &CO) -> Self { Self { name: observer.name().to_string(), - phantom: PhantomData + phantom: PhantomData, } } } diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index b7e001b8f0..ce3c2390c0 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -4,6 +4,9 @@ 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/observers/cmp.rs b/libafl/src/observers/cmp.rs index b375ff93a8..3fbd4b7681 100644 --- a/libafl/src/observers/cmp.rs +++ b/libafl/src/observers/cmp.rs @@ -5,34 +5,125 @@ use alloc::{ vec::Vec, }; -use core::slice::from_raw_parts_mut; -use num_enum::{IntoPrimitive, TryFromPrimitive}; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ - bolts::{ - ownedref::{OwnedRefMut, OwnedSliceMut}, - tuples::Named, - }, + bolts::{ownedref::OwnedRefMut, tuples::Named}, executors::HasExecHooks, observers::Observer, Error, }; -/// A [`CmpObserver`] observes the traced comparisons during the current execution -pub trait CmpObserver: Observer { - /// Get the number of usable cmps (all by default) - fn usable_count(&self) -> usize; +#[derive(Serialize, Deserialize)] +pub enum CmpValues { + U8((u8, u8)), + U16((u16, u16)), + U32((u32, u32)), + U64((u64, u64)), + Bytes((Vec, Vec)), +} +/// A [`CmpMap`] traces comparisons during the current execution +pub trait CmpMap: Serialize + DeserializeOwned { /// Get the number of cmps fn len(&self) -> usize; - fn executions_for(idx: usize) -> usize; + fn executions_for(&self, idx: usize) -> usize; - fn bytes_for(idx: usize) -> usize; + fn usable_executions_for(&self, idx: usize) -> usize; - fn values_of(idx: usize, execution: usize) -> (T, T); + fn values_of(&self, idx: usize, execution: usize) -> CmpValues; /// Reset the state fn reset(&mut self) -> Result<(), Error>; } + +/// A [`CmpObserver`] observes the traced comparisons during the current execution using a [`CmpMap`] +pub trait CmpObserver: Observer +where + CM: CmpMap, +{ + /// Get the number of usable cmps (all by default) + fn usable_count(&self) -> usize; + + fn map(&self) -> &CM; + + fn map_mut(&mut self) -> &mut CM; +} + +/// A standard [`CmpObserver`] observer +#[derive(Serialize, Deserialize, Debug)] +#[serde(bound = "CM: serde::de::DeserializeOwned")] +pub struct StdCmpObserver<'a, CM> +where + CM: CmpMap, +{ + map: OwnedRefMut<'a, CM>, + size: Option>, + name: String, +} + +impl<'a, CM> CmpObserver for StdCmpObserver<'a, CM> +where + CM: CmpMap, +{ + /// Get the number of usable cmps (all by default) + fn usable_count(&self) -> usize { + match &self.size { + None => self.map().len(), + Some(o) => *o.as_ref(), + } + } + + fn map(&self) -> &CM { + self.map.as_ref() + } + + fn map_mut(&mut self) -> &mut CM { + self.map.as_mut() + } +} + +impl<'a, CM> Observer for StdCmpObserver<'a, CM> where CM: CmpMap {} + +impl<'a, CM, EM, I, S, Z> HasExecHooks for StdCmpObserver<'a, CM> +where + CM: CmpMap, +{ + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { + self.map.as_mut().reset()?; + Ok(()) + } +} + +impl<'a, CM> Named for StdCmpObserver<'a, CM> +where + CM: CmpMap, +{ + fn name(&self) -> &str { + &self.name + } +} + +impl<'a, CM> StdCmpObserver<'a, CM> +where + CM: CmpMap, +{ + /// Creates a new [`CmpObserver`] with the given name. + #[must_use] + pub fn new(name: &'static str, map: &'a mut CM) -> Self { + Self { + name: name.to_string(), + size: None, + map: OwnedRefMut::Ref(map), + } + } + + // TODO with_size +} diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index 071977e0f2..c73ceb2deb 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -25,4 +25,4 @@ cc = { version = "1.0", features = ["parallel"] } [dependencies] rangemap = "0.1.10" -libafl = { path = "../libafl", version = "0.2.1", features = [] } +libafl = { path = "../libafl", version = "0.3", features = [] }