From 9e9d95f93d6896f4d12c1f9e700fe044a5a46329 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 4 May 2021 13:54:46 +0200 Subject: [PATCH] Observers refactor (#84) * new observer structure with HasExecHooks * adapt libafl_frida to new observers * docstrings --- fuzzers/baby_fuzzer/src/main.rs | 1 - fuzzers/frida_libpng/src/fuzzer.rs | 78 +++++++------- fuzzers/libfuzzer_libmozjpeg/src/lib.rs | 1 - fuzzers/libfuzzer_libpng/src/lib.rs | 1 - fuzzers/libfuzzer_stb_image/src/main.rs | 1 - libafl/src/executors/inprocess.rs | 79 +++++++-------- libafl/src/executors/mod.rs | 129 ++++++++++++++---------- libafl/src/executors/timeout.rs | 112 ++++++++++---------- libafl/src/feedbacks/map.rs | 51 ++++------ libafl/src/feedbacks/mod.rs | 50 +++++---- libafl/src/lib.rs | 1 - libafl/src/observers/map.rs | 40 +++++--- libafl/src/observers/mod.rs | 68 ++----------- libafl/src/stages/mutational.rs | 22 ++-- libafl/src/state/mod.rs | 80 ++++++++------- libafl_frida/src/asan_rt.rs | 8 +- 16 files changed, 343 insertions(+), 379 deletions(-) diff --git a/fuzzers/baby_fuzzer/src/main.rs b/fuzzers/baby_fuzzer/src/main.rs index d53dfec70a..39302c5d4f 100644 --- a/fuzzers/baby_fuzzer/src/main.rs +++ b/fuzzers/baby_fuzzer/src/main.rs @@ -77,7 +77,6 @@ pub fn main() { // Create the executor for an in-process function with just one observer let mut executor = InProcessExecutor::new( - "in-process(signals)", &mut harness, tuple_list!(observer), &mut state, diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index d7b0c8cde4..89d317ea3d 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -2,14 +2,15 @@ //! The example harness is built for libpng. use libafl::{ - bolts::tuples::{tuple_list, Named}, + bolts::tuples::tuple_list, corpus::{ ondisk::OnDiskMetadataFormat, Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, }, - events::{setup_restarting_mgr_std, EventManager}, + events::setup_restarting_mgr_std, executors::{ - inprocess::InProcessExecutor, timeout::TimeoutExecutor, Executor, ExitKind, HasObservers, + inprocess::InProcessExecutor, timeout::TimeoutExecutor, Executor, ExitKind, HasExecHooks, + HasExecHooksTuple, HasObservers, HasObserversHooks, }, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -37,14 +38,14 @@ use libafl_frida::{ FridaOptions, }; -struct FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT> +struct FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { - base: TimeoutExecutor, I, OT>, + base: TimeoutExecutor, I>, /// Frida's dynamic rewriting engine stalker: Stalker<'a>, /// User provided callback for instrumentation @@ -53,19 +54,17 @@ where _phantom: PhantomData<&'b u8>, } -impl<'a, 'b, 'c, FH, H, I, OT> Executor for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT> +impl<'a, 'b, 'c, EM, FH, H, I, OT, S> Executor + for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { - /// Called right before exexution starts + /// Instruct the target about the input and run #[inline] - fn pre_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> - where - EM: EventManager, - { + fn run_target(&mut self, input: &I) -> Result { if self.helper.stalker_enabled() { if !self.followed { self.followed = true; @@ -77,15 +76,6 @@ where )) } } - - self.helper.pre_exec(input); - - self.base.pre_exec(state, event_mgr, input) - } - - /// Instruct the target about the input and run - #[inline] - fn run_target(&mut self, input: &I) -> Result { let res = self.base.run_target(input); if unsafe { ASAN_ERRORS.is_some() && !ASAN_ERRORS.as_ref().unwrap().is_empty() } { println!("Crashing target as it had ASAN errors"); @@ -93,29 +83,38 @@ where libc::raise(libc::SIGABRT); } } + if self.helper.stalker_enabled() { + self.stalker.deactivate(); + } res } +} + +impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasExecHooks + for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> +where + FH: FridaHelper<'b>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + /// Called right before exexution starts + #[inline] + fn pre_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> { + self.helper.pre_exec(input); + self.base.pre_exec(state, event_mgr, input) + } /// Called right after execution finished. #[inline] - fn post_exec( - &mut self, - state: &mut S, - event_mgr: &mut EM, - input: &I, - ) -> Result<(), Error> - where - EM: EventManager, - { - if self.helper.stalker_enabled() { - self.stalker.deactivate(); - } + fn post_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> { self.helper.post_exec(input); self.base.post_exec(state, event_mgr, input) } } -impl<'a, 'b, 'c, FH, H, I, OT> HasObservers for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT> +impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasObservers + for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, @@ -133,19 +132,17 @@ where } } -impl<'a, 'b, 'c, FH, H, I, OT> Named for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT> +impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasObserversHooks + for FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, - OT: ObserversTuple, + OT: ObserversTuple + HasExecHooksTuple, { - fn name(&self) -> &str { - self.base.name() - } } -impl<'a, 'b, 'c, FH, H, I, OT> FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT> +impl<'a, 'b, 'c, EM, FH, H, I, OT, S> FridaInProcessExecutor<'a, 'b, 'c, EM, FH, H, I, OT, S> where FH: FridaHelper<'b>, H: FnMut(&[u8]) -> ExitKind, @@ -154,7 +151,7 @@ where { pub fn new( gum: &'a Gum, - base: InProcessExecutor<'a, H, I, OT>, + base: InProcessExecutor<'a, EM, H, I, OT, S>, helper: &'c mut FH, timeout: Duration, ) -> Self { @@ -324,7 +321,6 @@ unsafe fn fuzz( let mut executor = FridaInProcessExecutor::new( &gum, InProcessExecutor::new( - "in-process(edges)", &mut frida_harness, tuple_list!(edges_observer, AsanErrorsObserver::new(&ASAN_ERRORS)), &mut state, diff --git a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs index c3e99c2dac..79ad03acb7 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs @@ -113,7 +113,6 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create the executor for an in-process function with observers for edge coverage, value-profile and allocations sizes let mut executor = InProcessExecutor::new( - "in-process(edges,cmp,alloc)", &mut harness, tuple_list!(edges_observer, cmps_observer, allocs_observer), &mut state, diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 4d1b78ecd5..4ddd2b5b70 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -120,7 +120,6 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create the executor for an in-process function with one observer for edge coverage and one for the execution time let mut executor = TimeoutExecutor::new( InProcessExecutor::new( - "in-process(edges,time)", &mut harness, tuple_list!(edges_observer, TimeObserver::new("time")), &mut state, diff --git a/fuzzers/libfuzzer_stb_image/src/main.rs b/fuzzers/libfuzzer_stb_image/src/main.rs index 1f018de01f..965a7ed42d 100644 --- a/fuzzers/libfuzzer_stb_image/src/main.rs +++ b/fuzzers/libfuzzer_stb_image/src/main.rs @@ -116,7 +116,6 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create the executor for an in-process function with just one observer for edge coverage let mut executor = InProcessExecutor::new( - "in-process(edges,time)", &mut harness, tuple_list!(edges_observer, TimeObserver::new("time")), &mut state, diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index ffaece9f05..68c1fca89a 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -14,10 +14,11 @@ use crate::bolts::os::unix_signals::setup_signal_handler; use crate::bolts::os::windows_exceptions::setup_exception_handler; use crate::{ - bolts::tuples::Named, corpus::Corpus, events::EventManager, - executors::{Executor, ExitKind, HasObservers}, + executors::{ + Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, + }, feedbacks::FeedbacksTuple, inputs::{HasTargetBytes, Input}, observers::ObserversTuple, @@ -26,34 +27,41 @@ use crate::{ }; /// The inmem executor simply calls a target function, then returns afterwards. -pub struct InProcessExecutor<'a, H, I, OT> +pub struct InProcessExecutor<'a, EM, H, I, OT, S> where H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { - /// The name of this executor instance, to address it from other components - name: &'static str, /// The harness function, being executed for each fuzzing loop execution harness_fn: &'a mut H, /// The observers, observing each run observers: OT, - phantom: PhantomData, + phantom: PhantomData<(EM, I, S)>, } -impl<'a, H, I, OT> Executor for InProcessExecutor<'a, H, I, OT> +impl<'a, EM, H, I, OT, S> Executor for InProcessExecutor<'a, EM, H, I, OT, S> where H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { #[inline] - fn pre_exec( - &mut self, - _state: &mut S, - _event_mgr: &mut EM, - _input: &I, - ) -> Result<(), Error> { + fn run_target(&mut self, input: &I) -> Result { + let bytes = input.target_bytes(); + let ret = (self.harness_fn)(bytes.as_slice()); + Ok(ret) + } +} + +impl<'a, EM, H, I, OT, S> HasExecHooks for InProcessExecutor<'a, EM, H, I, OT, S> +where + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + #[inline] + fn pre_exec(&mut self, _state: &mut S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> { #[cfg(unix)] unsafe { let data = &mut unix_signal_handler::GLOBAL_STATE; @@ -92,19 +100,7 @@ where } #[inline] - fn run_target(&mut self, input: &I) -> Result { - let bytes = input.target_bytes(); - let ret = (self.harness_fn)(bytes.as_slice()); - Ok(ret) - } - - #[inline] - fn post_exec( - &mut self, - _state: &mut S, - _event_mgr: &mut EM, - _input: &I, - ) -> Result<(), Error> { + fn post_exec(&mut self, _state: &mut S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> { #[cfg(unix)] unsafe { write_volatile( @@ -125,18 +121,7 @@ where } } -impl<'a, H, I, OT> Named for InProcessExecutor<'a, H, I, OT> -where - H: FnMut(&[u8]) -> ExitKind, - I: Input + HasTargetBytes, - OT: ObserversTuple, -{ - fn name(&self) -> &str { - self.name - } -} - -impl<'a, H, I, OT> HasObservers for InProcessExecutor<'a, H, I, OT> +impl<'a, EM, H, I, OT, S> HasObservers for InProcessExecutor<'a, EM, H, I, OT, S> where H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, @@ -153,7 +138,15 @@ where } } -impl<'a, H, I, OT> InProcessExecutor<'a, H, I, OT> +impl<'a, EM, H, I, OT, S> HasObserversHooks for InProcessExecutor<'a, EM, H, I, OT, S> +where + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple + HasExecHooksTuple, +{ +} + +impl<'a, EM, H, I, OT, S> InProcessExecutor<'a, EM, H, I, OT, S> where H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, @@ -162,12 +155,10 @@ where /// Create a new in mem executor. /// Caution: crash and restart in one of them will lead to odd behavior if multiple are used, /// depending on different corpus or state. - /// * `name` - the name of this executor (to address it along the way) /// * `harness_fn` - the harness, executiong the function /// * `observers` - the observers observing the target during execution /// This may return an error on unix, if signal handler setup fails - pub fn new( - name: &'static str, + pub fn new( harness_fn: &'a mut H, observers: OT, _state: &mut S, @@ -213,7 +204,6 @@ where Ok(Self { harness_fn, observers, - name, phantom: PhantomData, }) } @@ -696,10 +686,9 @@ mod tests { fn test_inmem_exec() { let mut harness = |_buf: &[u8]| ExitKind::Ok; - let mut in_process_executor = InProcessExecutor::<_, NopInput, ()> { + let mut in_process_executor = InProcessExecutor::<(), _, NopInput, (), ()> { harness_fn: &mut harness, observers: tuple_list!(), - name: "main", phantom: PhantomData, }; let mut input = NopInput {}; diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 08b26ed602..b79dc5b259 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -8,8 +8,7 @@ pub use timeout::TimeoutExecutor; use core::marker::PhantomData; use crate::{ - bolts::{serdeany::SerdeAny, tuples::Named}, - events::EventManager, + bolts::serdeany::SerdeAny, inputs::{HasTargetBytes, Input}, observers::ObserversTuple, Error, @@ -29,6 +28,57 @@ pub enum ExitKind { Custom(Box), } +/// Pre and post exec hooks +pub trait HasExecHooks { + /// Called right before exexution starts + #[inline] + fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { + Ok(()) + } + + /// Called right after execution finished. + #[inline] + fn post_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { + Ok(()) + } +} + +/// A haskell-style tuple of objects that have pre and post exec hooks +pub trait HasExecHooksTuple { + /// This is called right before the next execution. + fn pre_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error>; + + /// This is called right after the last execution + fn post_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error>; +} + +impl HasExecHooksTuple for () { + fn pre_exec_all(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { + Ok(()) + } + + fn post_exec_all(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { + Ok(()) + } +} + +impl HasExecHooksTuple for (Head, Tail) +where + Head: HasExecHooks, + Tail: HasExecHooksTuple, +{ + fn pre_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { + self.0.pre_exec(state, mgr, input)?; + self.1.pre_exec_all(state, mgr, input) + } + + fn post_exec_all(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { + self.0.post_exec(state, mgr, input)?; + self.1.post_exec_all(state, mgr, input) + } +} + +/// Holds a tuple of Observers pub trait HasObservers where OT: ObserversTuple, @@ -38,27 +88,41 @@ where /// Get the linked observers fn observers_mut(&mut self) -> &mut OT; +} - /// Reset the state of all the observes linked to this executor +/// Execute the exec hooks of the observers if they all implement HasExecHooks +pub trait HasObserversHooks: HasObservers +where + OT: ObserversTuple + HasExecHooksTuple, +{ #[inline] - fn pre_exec_observers(&mut self) -> Result<(), Error> { - self.observers_mut().pre_exec_all() + fn pre_exec_observers(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { + self.observers_mut().pre_exec_all(state, mgr, input) } /// Run the post exec hook for all the observes linked to this executor #[inline] - fn post_exec_observers(&mut self) -> Result<(), Error> { - self.observers_mut().post_exec_all() + fn post_exec_observers(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { + self.observers_mut().post_exec_all(state, mgr, input) } } +/// An executor takes the given inputs, and runs the harness/target. +pub trait Executor +where + I: Input, +{ + /// Instruct the target about the input and run + fn run_target(&mut self, input: &I) -> Result; +} + /// A simple executor that does nothing. /// If intput len is 0, `run_target` will return Err -struct NopExecutor { - phantom: PhantomData, +struct NopExecutor { + phantom: PhantomData<(EM, I, S)>, } -impl Executor for NopExecutor +impl Executor for NopExecutor where I: Input + HasTargetBytes, { @@ -71,48 +135,7 @@ where } } -impl Named for NopExecutor { - fn name(&self) -> &str { - &"NopExecutor" - } -} - -/// An executor takes the given inputs, and runs the harness/target. -pub trait Executor: Named -where - I: Input, -{ - /// Called right before exexution starts - #[inline] - fn pre_exec( - &mut self, - _state: &mut S, - _event_mgr: &mut EM, - _input: &I, - ) -> Result<(), Error> - where - EM: EventManager, - { - Ok(()) - } - - /// Called right after execution finished. - #[inline] - fn post_exec( - &mut self, - _state: &mut S, - _event_mgr: &mut EM, - _input: &I, - ) -> Result<(), Error> - where - EM: EventManager, - { - Ok(()) - } - - /// Instruct the target about the input and run - fn run_target(&mut self, input: &I) -> Result; -} +impl HasExecHooks for NopExecutor where I: Input + HasTargetBytes {} #[cfg(test)] mod test { @@ -125,7 +148,7 @@ mod test { fn nop_executor() { let empty_input = BytesInput::new(vec![]); let nonempty_input = BytesInput::new(vec![1u8]); - let mut executor = NopExecutor { + let mut executor = NopExecutor::<(), _, ()> { phantom: PhantomData, }; assert!(executor.run_target(&empty_input).is_err()); diff --git a/libafl/src/executors/timeout.rs b/libafl/src/executors/timeout.rs index 0b428aa855..cc90cfabe3 100644 --- a/libafl/src/executors/timeout.rs +++ b/libafl/src/executors/timeout.rs @@ -3,10 +3,10 @@ use core::{marker::PhantomData, time::Duration}; use crate::{ - bolts::tuples::Named, - events::EventManager, - executors::{Executor, ExitKind, HasObservers}, - inputs::{HasTargetBytes, Input}, + executors::{ + Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, + }, + inputs::Input, observers::ObserversTuple, Error, }; @@ -39,50 +39,20 @@ extern "C" { const ITIMER_REAL: c_int = 0; /// The timeout excutor is a wrapper that set a timeout before each run -pub struct TimeoutExecutor +pub struct TimeoutExecutor where - E: Executor + HasObservers, - I: Input + HasTargetBytes, - OT: ObserversTuple, + E: Executor, + I: Input, { executor: E, exec_tmout: Duration, - phantom: PhantomData<(I, OT)>, + phantom: PhantomData, } -impl Named for TimeoutExecutor +impl TimeoutExecutor where - E: Executor + HasObservers, - I: Input + HasTargetBytes, - OT: ObserversTuple, -{ - fn name(&self) -> &str { - self.executor.name() - } -} - -impl HasObservers for TimeoutExecutor -where - E: Executor + HasObservers, - I: Input + HasTargetBytes, - OT: ObserversTuple, -{ - #[inline] - fn observers(&self) -> &OT { - self.executor.observers() - } - - #[inline] - fn observers_mut(&mut self) -> &mut OT { - self.executor.observers_mut() - } -} - -impl TimeoutExecutor -where - E: Executor + HasObservers, - I: Input + HasTargetBytes, - OT: ObserversTuple, + E: Executor, + I: Input, { pub fn new(executor: E, exec_tmout: Duration) -> Self { Self { @@ -97,19 +67,48 @@ where } } -impl Executor for TimeoutExecutor +impl Executor for TimeoutExecutor +where + E: Executor, + I: Input, +{ + fn run_target(&mut self, input: &I) -> Result { + self.executor.run_target(input) + } +} + +impl HasObservers for TimeoutExecutor where E: Executor + HasObservers, - I: Input + HasTargetBytes, + I: Input, OT: ObserversTuple, { #[inline] - fn pre_exec, S>( - &mut self, - _state: &mut S, - _event_mgr: &mut EM, - _input: &I, - ) -> Result<(), Error> { + fn observers(&self) -> &OT { + self.executor.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.executor.observers_mut() + } +} + +impl HasObserversHooks for TimeoutExecutor +where + E: Executor + HasObservers, + I: Input, + OT: ObserversTuple + HasExecHooksTuple, +{ +} + +impl HasExecHooks for TimeoutExecutor +where + E: Executor + HasExecHooks, + I: Input, +{ + #[inline] + fn pre_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { #[cfg(unix)] unsafe { let milli_sec = self.exec_tmout.as_millis(); @@ -135,16 +134,11 @@ where // TODO let _ = self.exec_tmout.as_millis(); } - self.executor.pre_exec(_state, _event_mgr, _input) + self.executor.pre_exec(state, mgr, input) } #[inline] - fn post_exec, S>( - &mut self, - _state: &mut S, - _event_mgr: &mut EM, - _input: &I, - ) -> Result<(), Error> { + fn post_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { #[cfg(unix)] unsafe { let it_value = Timeval { @@ -168,10 +162,6 @@ where { // TODO } - self.executor.post_exec(_state, _event_mgr, _input) - } - - fn run_target(&mut self, input: &I) -> Result { - self.executor.run_target(input) + self.executor.post_exec(state, mgr, input) } } diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index 89374e154d..6ac2882f49 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -14,14 +14,14 @@ use crate::{ executors::ExitKind, feedbacks::Feedback, inputs::Input, - observers::{MapObserver, Observer, ObserversTuple}, + observers::{MapObserver, ObserversTuple}, state::HasMetadata, utils::AsSlice, Error, }; -pub type MaxMapFeedback = MapFeedback, O>; -pub type MinMapFeedback = MapFeedback, O>; +pub type MaxMapFeedback = MapFeedback; +pub type MinMapFeedback = MapFeedback; /// A Reducer function is used to aggregate values for the novelty search pub trait Reducer: Serialize + serde::de::DeserializeOwned + 'static @@ -32,14 +32,9 @@ where } #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct MaxReducer -where - T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, -{ - phantom: PhantomData, -} +pub struct MaxReducer {} -impl Reducer for MaxReducer +impl Reducer for MaxReducer where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, { @@ -54,14 +49,9 @@ where } #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct MinReducer -where - T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, -{ - phantom: PhantomData, -} +pub struct MinReducer {} -impl Reducer for MinReducer +impl Reducer for MinReducer where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, { @@ -119,7 +109,7 @@ impl MapNoveltiesMetadata { /// The most common AFL-like feedback type #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(bound = "T: serde::de::DeserializeOwned")] -pub struct MapFeedback +pub struct MapFeedback where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, R: Reducer, @@ -137,25 +127,22 @@ where phantom: PhantomData<(R, O)>, } -impl Feedback for MapFeedback +impl Feedback for MapFeedback where - T: Integer - + Default - + Copy - + 'static - + serde::Serialize - + serde::de::DeserializeOwned - + core::fmt::Debug, + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, R: Reducer, O: MapObserver, I: Input, { - fn is_interesting( + fn is_interesting( &mut self, _input: &I, observers: &OT, _exit_kind: &ExitKind, - ) -> Result { + ) -> Result + where + OT: ObserversTuple, + { let mut interesting = 0; // TODO optimize let observer = observers.match_name_type::(&self.name).unwrap(); @@ -243,7 +230,7 @@ where } } -impl Named for MapFeedback +impl Named for MapFeedback where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, R: Reducer, @@ -255,11 +242,11 @@ where } } -impl MapFeedback +impl MapFeedback where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, R: Reducer, - O: MapObserver + Observer, + O: MapObserver, { /// Create new `MapFeedback` pub fn new(name: &'static str, map_size: usize) -> Self { @@ -315,7 +302,7 @@ where } } -impl MapFeedback +impl MapFeedback where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, R: Reducer, diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 6cb8261383..e4d2f00814 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -25,12 +25,14 @@ where I: Input, { /// `is_interesting ` should return the "Interestingness" from 0 to 255 (percent times 2.55) - fn is_interesting( + fn is_interesting( &mut self, input: &I, observers: &OT, exit_kind: &ExitKind, - ) -> Result; + ) -> Result + where + OT: ObserversTuple; /// Append to the testcase the generated metadata in case of a new corpus item #[inline] @@ -50,12 +52,14 @@ where I: Input, { /// Get the total interestingness value from all feedbacks - fn is_interesting_all( + fn is_interesting_all( &mut self, input: &I, observers: &OT, exit_kind: &ExitKind, - ) -> Result; + ) -> Result + where + OT: ObserversTuple; /// Write metadata for this testcase fn append_metadata_all(&mut self, testcase: &mut Testcase) -> Result<(), Error>; @@ -69,12 +73,10 @@ where I: Input, { #[inline] - fn is_interesting_all( - &mut self, - _: &I, - _: &OT, - _: &ExitKind, - ) -> Result { + fn is_interesting_all(&mut self, _: &I, _: &OT, _: &ExitKind) -> Result + where + OT: ObserversTuple, + { Ok(0) } @@ -95,12 +97,15 @@ where Tail: FeedbacksTuple, I: Input, { - fn is_interesting_all( + fn is_interesting_all( &mut self, input: &I, observers: &OT, exit_kind: &ExitKind, - ) -> Result { + ) -> Result + where + OT: ObserversTuple, + { Ok(self.0.is_interesting(input, observers, exit_kind)? + self.1.is_interesting_all(input, observers, exit_kind)?) } @@ -124,12 +129,15 @@ impl Feedback for CrashFeedback where I: Input, { - fn is_interesting( + fn is_interesting( &mut self, _input: &I, _observers: &OT, exit_kind: &ExitKind, - ) -> Result { + ) -> Result + where + OT: ObserversTuple, + { if let ExitKind::Crash = exit_kind { Ok(1) } else { @@ -164,12 +172,15 @@ impl Feedback for TimeoutFeedback where I: Input, { - fn is_interesting( + fn is_interesting( &mut self, _input: &I, _observers: &OT, exit_kind: &ExitKind, - ) -> Result { + ) -> Result + where + OT: ObserversTuple, + { if let ExitKind::Timeout = exit_kind { Ok(1) } else { @@ -207,12 +218,15 @@ impl Feedback for TimeFeedback where I: Input, { - fn is_interesting( + fn is_interesting( &mut self, _input: &I, observers: &OT, _exit_kind: &ExitKind, - ) -> Result { + ) -> Result + where + OT: ObserversTuple, + { let observer = observers.match_first_type::().unwrap(); self.exec_time = *observer.last_runtime(); Ok(0) diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index 421506380d..bf7037bba0 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -194,7 +194,6 @@ mod tests { let mut harness = |_buf: &[u8]| ExitKind::Ok; let mut executor = InProcessExecutor::new( - "main", &mut harness, tuple_list!(), //Box::new(|_, _, _, _, _| ()), diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 2188105f55..169cac1206 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -11,6 +11,7 @@ use crate::{ ownedref::{OwnedArrayPtrMut, OwnedPtr}, tuples::Named, }, + executors::HasExecHooks, observers::Observer, Error, }; @@ -68,12 +69,18 @@ where name: String, } -impl Observer for StdMapObserver +impl Observer for StdMapObserver where + T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned +{ +} + +impl HasExecHooks for StdMapObserver where T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, + Self: MapObserver, { #[inline] - fn pre_exec(&mut self) -> Result<(), Error> { + fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { self.reset_map() } } @@ -170,12 +177,17 @@ where name: String, } -impl Observer for VariableMapObserver +impl Observer for VariableMapObserver where + T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned +{ +} + +impl HasExecHooks for VariableMapObserver where T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, { #[inline] - fn pre_exec(&mut self) -> Result<(), Error> { + fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { self.reset_map() } } @@ -264,7 +276,7 @@ where #[serde(bound = "M: serde::de::DeserializeOwned")] pub struct HitcountsMapObserver where - M: MapObserver, + M: serde::Serialize + serde::de::DeserializeOwned, { base: M, } @@ -284,27 +296,29 @@ static COUNT_CLASS_LOOKUP: [u8; 256] = [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, ]; -impl Observer for HitcountsMapObserver +impl Observer for HitcountsMapObserver where M: MapObserver {} + +impl HasExecHooks for HitcountsMapObserver where - M: MapObserver, + M: MapObserver + HasExecHooks, { #[inline] - fn pre_exec(&mut self) -> Result<(), Error> { - self.reset_map() + fn pre_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { + self.base.pre_exec(state, mgr, input) } #[inline] - fn post_exec(&mut self) -> Result<(), Error> { + fn post_exec(&mut self, state: &mut S, mgr: &mut EM, input: &I) -> Result<(), Error> { for x in self.map_mut().iter_mut() { *x = COUNT_CLASS_LOOKUP[*x as usize]; } - Ok(()) + self.base.post_exec(state, mgr, input) } } impl Named for HitcountsMapObserver where - M: MapObserver, + M: Named + serde::Serialize + serde::de::DeserializeOwned, { #[inline] fn name(&self) -> &str { @@ -349,7 +363,7 @@ where impl HitcountsMapObserver where - M: MapObserver, + M: serde::Serialize + serde::de::DeserializeOwned, { /// Creates a new MapObserver pub fn new(base: M) -> Self { diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 2ff66ca5fc..ef81ccd012 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -3,15 +3,13 @@ pub mod map; pub use map::*; -use alloc::{ - string::{String, ToString}, - vec::Vec, -}; +use alloc::string::{String, ToString}; use core::time::Duration; use serde::{Deserialize, Serialize}; use crate::{ bolts::tuples::{MatchFirstType, MatchNameAndType, MatchType, Named}, + executors::HasExecHooks, utils::current_time, Error, }; @@ -20,74 +18,26 @@ use crate::{ /// They can then be used by various sorts of feedback. pub trait Observer: Named + serde::Serialize + serde::de::DeserializeOwned + 'static { /// The testcase finished execution, calculate any changes. + /// Reserved for future use. #[inline] fn flush(&mut self) -> Result<(), Error> { Ok(()) } - - /// Resets the observer - fn pre_exec(&mut self) -> Result<(), Error>; - - /// This function is executed after each fuzz run - #[inline] - fn post_exec(&mut self) -> Result<(), Error> { - Ok(()) - } - - /// Serialize this observer's state only, to be restored later using deserialize_state - /// As opposed to completely serializing the observer, this is only needed when the fuzzer is to be restarted - /// If no state is needed to be kept, just return an empty vec. - /// Example: - /// >> The virgin_bits map in AFL needs to be in sync with the corpus - #[inline] - fn serialize_state(&mut self) -> Result, Error> { - Ok(vec![]) - } - - /// Restore the state from a given vec, priviously stored using `serialize_state` - #[inline] - fn deserialize_state(&mut self, serialized_state: &[u8]) -> Result<(), Error> { - let _ = serialized_state; - Ok(()) - } } -/// A hastkel-style tuple of observers +/// A haskell-style tuple of observers pub trait ObserversTuple: MatchNameAndType + MatchType + MatchFirstType + serde::Serialize + serde::de::DeserializeOwned { - /// Reset all executors in the tuple - /// This is called right before the next execution. - fn pre_exec_all(&mut self) -> Result<(), Error>; - - /// Do whatever you need to do after a run. - /// This is called right after the last execution - fn post_exec_all(&mut self) -> Result<(), Error>; } -impl ObserversTuple for () { - fn pre_exec_all(&mut self) -> Result<(), Error> { - Ok(()) - } - fn post_exec_all(&mut self) -> Result<(), Error> { - Ok(()) - } -} +impl ObserversTuple for () {} impl ObserversTuple for (Head, Tail) where Head: Observer, Tail: ObserversTuple, { - fn pre_exec_all(&mut self) -> Result<(), Error> { - self.0.pre_exec()?; - self.1.pre_exec_all() - } - - fn post_exec_all(&mut self) -> Result<(), Error> { - self.0.post_exec()?; - self.1.post_exec_all() - } } /// A simple observer, just overlooking the runtime of the target. @@ -113,14 +63,16 @@ impl TimeObserver { } } -impl Observer for TimeObserver { - fn pre_exec(&mut self) -> Result<(), Error> { +impl Observer for TimeObserver {} + +impl HasExecHooks for TimeObserver { + fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { self.last_runtime = None; self.start_time = current_time(); Ok(()) } - fn post_exec(&mut self) -> Result<(), Error> { + fn post_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { self.last_runtime = Some(current_time() - self.start_time); Ok(()) } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index a85a7d1dc3..1af825afe2 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use crate::{ corpus::{Corpus, CorpusScheduler}, events::EventManager, - executors::{Executor, HasObservers}, + executors::{Executor, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks}, inputs::Input, mutators::Mutator, observers::ObserversTuple, @@ -25,8 +25,8 @@ where S: HasCorpus + Evaluator, C: Corpus, EM: EventManager, - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + HasObservers + HasExecHooks + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, CS: CorpusScheduler, { /// The mutator registered for this stage @@ -76,8 +76,8 @@ where S: HasCorpus + Evaluator + HasRand, C: Corpus, EM: EventManager, - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + HasObservers + HasExecHooks + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, CS: CorpusScheduler, R: Rand, { @@ -94,8 +94,8 @@ where S: HasCorpus + Evaluator + HasRand, C: Corpus, EM: EventManager, - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + HasObservers + HasExecHooks + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, CS: CorpusScheduler, R: Rand, { @@ -125,8 +125,8 @@ where S: HasCorpus + Evaluator + HasRand, C: Corpus, EM: EventManager, - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + HasObservers + HasExecHooks + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, CS: CorpusScheduler, R: Rand, { @@ -150,8 +150,8 @@ where S: HasCorpus + Evaluator + HasRand, C: Corpus, EM: EventManager, - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + HasObservers + HasExecHooks + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, CS: CorpusScheduler, R: Rand, { diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index cd295ff9ea..54a7c1873f 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -12,7 +12,9 @@ use crate::{ bolts::serdeany::{SerdeAny, SerdeAnyMap}, corpus::{Corpus, CorpusScheduler, Testcase}, events::{Event, EventManager, LogSeverity}, - executors::{Executor, ExitKind, HasObservers}, + executors::{ + Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, + }, feedbacks::FeedbacksTuple, generators::Generator, inputs::Input, @@ -97,7 +99,7 @@ pub trait HasMetadata { } /// Trait for elements offering a feedbacks tuple -pub trait HasFeedbacks +pub trait HasFeedbacks: Sized where FT: FeedbacksTuple, I: Input, @@ -107,29 +109,10 @@ where /// 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 +pub trait HasObjectives: Sized where FT: FeedbacksTuple, I: Input, @@ -160,7 +143,7 @@ pub trait HasStartTime { } /// Add to the state if interesting -pub trait IfInteresting +pub trait IfInteresting: Sized where I: Input, { @@ -200,8 +183,11 @@ where scheduler: &CS, ) -> Result<(u32, Option), Error> where - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, EM: EventManager, CS: CorpusScheduler; } @@ -469,12 +455,13 @@ where CS: CorpusScheduler, { if fitness > 0 { - let testcase = self.testcase_with_feedbacks_metadata(input.clone(), fitness)?; + let mut testcase = Testcase::with_fitness(input.clone(), fitness); + self.feedbacks_mut().append_metadata_all(&mut testcase)?; let idx = self.corpus.add(testcase)?; scheduler.on_add(self, idx)?; Ok(Some(idx)) } else { - self.discard_feedbacks_metadata(input)?; + self.feedbacks_mut().discard_metadata_all(&input)?; Ok(None) } } @@ -500,8 +487,11 @@ where scheduler: &CS, ) -> Result<(u32, Option), Error> where - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, C: Corpus, EM: EventManager, CS: CorpusScheduler, @@ -555,8 +545,11 @@ where in_dir: &Path, ) -> Result<(), Error> where - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, EM: EventManager, CS: CorpusScheduler, { @@ -601,8 +594,11 @@ where in_dirs: &[PathBuf], ) -> Result<(), Error> where - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, EM: EventManager, CS: CorpusScheduler, { @@ -639,19 +635,22 @@ where event_mgr: &mut EM, ) -> Result<(u32, bool), Error> where - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, C: Corpus, EM: EventManager, { - executor.pre_exec_observers()?; + executor.pre_exec_observers(self, event_mgr, input)?; executor.pre_exec(self, event_mgr, input)?; let exit_kind = executor.run_target(input)?; executor.post_exec(self, event_mgr, input)?; *self.executions_mut() += 1; - executor.post_exec_observers()?; + executor.post_exec_observers(self, event_mgr, input)?; let observers = executor.observers(); let fitness = self @@ -676,8 +675,11 @@ where where G: Generator, C: Corpus, - E: Executor + HasObservers, - OT: ObserversTuple, + E: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, EM: EventManager, CS: CorpusScheduler, { diff --git a/libafl_frida/src/asan_rt.rs b/libafl_frida/src/asan_rt.rs index 3ffa69e876..ea60b06258 100644 --- a/libafl_frida/src/asan_rt.rs +++ b/libafl_frida/src/asan_rt.rs @@ -2,7 +2,7 @@ use hashbrown::HashMap; use libafl::{ bolts::{ownedref::OwnedPtr, tuples::Named}, corpus::Testcase, - executors::{CustomExitKind, ExitKind}, + executors::{CustomExitKind, ExitKind, HasExecHooks}, feedbacks::Feedback, inputs::{HasTargetBytes, Input}, observers::{Observer, ObserversTuple}, @@ -1625,8 +1625,10 @@ pub struct AsanErrorsObserver { errors: OwnedPtr>, } -impl Observer for AsanErrorsObserver { - fn pre_exec(&mut self) -> Result<(), Error> { +impl Observer for AsanErrorsObserver {} + +impl HasExecHooks for AsanErrorsObserver { + fn pre_exec(&mut self, _state: &mut S, _mgr: &mut EM, _input: &I) -> Result<(), Error> { unsafe { if ASAN_ERRORS.is_some() { ASAN_ERRORS.as_mut().unwrap().clear();