Observers refactor (#84)

* new observer structure with HasExecHooks

* adapt libafl_frida to new observers

* docstrings
This commit is contained in:
Andrea Fioraldi 2021-05-04 13:54:46 +02:00
parent 116a51270c
commit 9e9d95f93d
16 changed files with 343 additions and 379 deletions

View File

@ -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,

View File

@ -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<InProcessExecutor<'a, H, I, OT>, I, OT>,
base: TimeoutExecutor<InProcessExecutor<'a, EM, H, I, OT, S>, 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<I> for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT>
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> Executor<I>
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<EM, S>(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error>
where
EM: EventManager<I, S>,
{
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
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<ExitKind, Error> {
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<EM, I, S>
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<EM, S>(
&mut self,
state: &mut S,
event_mgr: &mut EM,
input: &I,
) -> Result<(), Error>
where
EM: EventManager<I, S>,
{
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<OT> for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT>
impl<'a, 'b, 'c, EM, FH, H, I, OT, S> HasObservers<OT>
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<EM, I, OT, S>
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<EM, I, S>,
{
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,

View File

@ -113,7 +113,6 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, 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,

View File

@ -120,7 +120,6 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, 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,

View File

@ -116,7 +116,6 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, 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,

View File

@ -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<I>,
phantom: PhantomData<(EM, I, S)>,
}
impl<'a, H, I, OT> Executor<I> for InProcessExecutor<'a, H, I, OT>
impl<'a, EM, H, I, OT, S> Executor<I> for InProcessExecutor<'a, EM, H, I, OT, S>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
#[inline]
fn pre_exec<EM, S>(
&mut self,
_state: &mut S,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
let bytes = input.target_bytes();
let ret = (self.harness_fn)(bytes.as_slice());
Ok(ret)
}
}
impl<'a, EM, H, I, OT, S> HasExecHooks<EM, I, S> 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<ExitKind, Error> {
let bytes = input.target_bytes();
let ret = (self.harness_fn)(bytes.as_slice());
Ok(ret)
}
#[inline]
fn post_exec<EM, S>(
&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<OT> for InProcessExecutor<'a, H, I, OT>
impl<'a, EM, H, I, OT, S> HasObservers<OT> 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<EM, I, OT, S> for InProcessExecutor<'a, EM, H, I, OT, S>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
{
}
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<EM, OC, OFT, S>(
name: &'static str,
pub fn new<OC, OFT>(
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 {};

View File

@ -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<dyn CustomExitKind>),
}
/// Pre and post exec hooks
pub trait HasExecHooks<EM, I, S> {
/// 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<EM, I, S> {
/// 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<EM, I, S> HasExecHooksTuple<EM, I, S> 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<EM, I, S, Head, Tail> HasExecHooksTuple<EM, I, S> for (Head, Tail)
where
Head: HasExecHooks<EM, I, S>,
Tail: HasExecHooksTuple<EM, I, S>,
{
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<OT>
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<EM, I, OT, S>: HasObservers<OT>
where
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
{
#[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<I>
where
I: Input,
{
/// Instruct the target about the input and run
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error>;
}
/// A simple executor that does nothing.
/// If intput len is 0, `run_target` will return Err
struct NopExecutor<I> {
phantom: PhantomData<I>,
struct NopExecutor<EM, I, S> {
phantom: PhantomData<(EM, I, S)>,
}
impl<I> Executor<I> for NopExecutor<I>
impl<EM, I, S> Executor<I> for NopExecutor<EM, I, S>
where
I: Input + HasTargetBytes,
{
@ -71,48 +135,7 @@ where
}
}
impl<I> Named for NopExecutor<I> {
fn name(&self) -> &str {
&"NopExecutor"
}
}
/// An executor takes the given inputs, and runs the harness/target.
pub trait Executor<I>: Named
where
I: Input,
{
/// Called right before exexution starts
#[inline]
fn pre_exec<EM, S>(
&mut self,
_state: &mut S,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error>
where
EM: EventManager<I, S>,
{
Ok(())
}
/// Called right after execution finished.
#[inline]
fn post_exec<EM, S>(
&mut self,
_state: &mut S,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error>
where
EM: EventManager<I, S>,
{
Ok(())
}
/// Instruct the target about the input and run
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error>;
}
impl<EM, I, S> HasExecHooks<EM, I, S> for NopExecutor<EM, I, S> 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());

View File

@ -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<E, I, OT>
pub struct TimeoutExecutor<E, I>
where
E: Executor<I> + HasObservers<OT>,
I: Input + HasTargetBytes,
OT: ObserversTuple,
E: Executor<I>,
I: Input,
{
executor: E,
exec_tmout: Duration,
phantom: PhantomData<(I, OT)>,
phantom: PhantomData<I>,
}
impl<E, I, OT> Named for TimeoutExecutor<E, I, OT>
impl<E, I> TimeoutExecutor<E, I>
where
E: Executor<I> + HasObservers<OT>,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
fn name(&self) -> &str {
self.executor.name()
}
}
impl<E, I, OT> HasObservers<OT> for TimeoutExecutor<E, I, OT>
where
E: Executor<I> + HasObservers<OT>,
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<E, I, OT> TimeoutExecutor<E, I, OT>
where
E: Executor<I> + HasObservers<OT>,
I: Input + HasTargetBytes,
OT: ObserversTuple,
E: Executor<I>,
I: Input,
{
pub fn new(executor: E, exec_tmout: Duration) -> Self {
Self {
@ -97,19 +67,48 @@ where
}
}
impl<E, I, OT> Executor<I> for TimeoutExecutor<E, I, OT>
impl<E, I> Executor<I> for TimeoutExecutor<E, I>
where
E: Executor<I>,
I: Input,
{
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
self.executor.run_target(input)
}
}
impl<E, I, OT> HasObservers<OT> for TimeoutExecutor<E, I>
where
E: Executor<I> + HasObservers<OT>,
I: Input + HasTargetBytes,
I: Input,
OT: ObserversTuple,
{
#[inline]
fn pre_exec<EM: EventManager<I, S>, 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<E, EM, I, OT, S> HasObserversHooks<EM, I, OT, S> for TimeoutExecutor<E, I>
where
E: Executor<I> + HasObservers<OT>,
I: Input,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
{
}
impl<E, EM, I, S> HasExecHooks<EM, I, S> for TimeoutExecutor<E, I>
where
E: Executor<I> + HasExecHooks<EM, I, S>,
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<EM: EventManager<I, S>, 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<ExitKind, Error> {
self.executor.run_target(input)
self.executor.post_exec(state, mgr, input)
}
}

View File

@ -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<T, O> = MapFeedback<T, MaxReducer<T>, O>;
pub type MinMapFeedback<T, O> = MapFeedback<T, MinReducer<T>, O>;
pub type MaxMapFeedback<O, T> = MapFeedback<O, MaxReducer, T>;
pub type MinMapFeedback<O, T> = MapFeedback<O, MinReducer, T>;
/// A Reducer function is used to aggregate values for the novelty search
pub trait Reducer<T>: Serialize + serde::de::DeserializeOwned + 'static
@ -32,14 +32,9 @@ where
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct MaxReducer<T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{
phantom: PhantomData<T>,
}
pub struct MaxReducer {}
impl<T> Reducer<T> for MaxReducer<T>
impl<T> Reducer<T> 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<T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{
phantom: PhantomData<T>,
}
pub struct MinReducer {}
impl<T> Reducer<T> for MinReducer<T>
impl<T> Reducer<T> 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<T, R, O>
pub struct MapFeedback<O, R, T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
R: Reducer<T>,
@ -137,25 +127,22 @@ where
phantom: PhantomData<(R, O)>,
}
impl<T, R, O, I> Feedback<I> for MapFeedback<T, R, O>
impl<O, R, T, I> Feedback<I> for MapFeedback<O, R, T>
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<T>,
O: MapObserver<T>,
I: Input,
{
fn is_interesting<OT: ObserversTuple>(
fn is_interesting<OT>(
&mut self,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<u32, Error> {
) -> Result<u32, Error>
where
OT: ObserversTuple,
{
let mut interesting = 0;
// TODO optimize
let observer = observers.match_name_type::<O>(&self.name).unwrap();
@ -243,7 +230,7 @@ where
}
}
impl<T, R, O> Named for MapFeedback<T, R, O>
impl<O, R, T> Named for MapFeedback<O, R, T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
R: Reducer<T>,
@ -255,11 +242,11 @@ where
}
}
impl<T, R, O> MapFeedback<T, R, O>
impl<O, R, T> MapFeedback<O, R, T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
R: Reducer<T>,
O: MapObserver<T> + Observer,
O: MapObserver<T>,
{
/// Create new `MapFeedback`
pub fn new(name: &'static str, map_size: usize) -> Self {
@ -315,7 +302,7 @@ where
}
}
impl<T, R, O> MapFeedback<T, R, O>
impl<O, R, T> MapFeedback<O, R, T>
where
T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
R: Reducer<T>,

View File

@ -25,12 +25,14 @@ where
I: Input,
{
/// `is_interesting ` should return the "Interestingness" from 0 to 255 (percent times 2.55)
fn is_interesting<OT: ObserversTuple>(
fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error>;
) -> Result<u32, Error>
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<OT: ObserversTuple>(
fn is_interesting_all<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error>;
) -> Result<u32, Error>
where
OT: ObserversTuple;
/// Write metadata for this testcase
fn append_metadata_all(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error>;
@ -69,12 +73,10 @@ where
I: Input,
{
#[inline]
fn is_interesting_all<OT: ObserversTuple>(
&mut self,
_: &I,
_: &OT,
_: &ExitKind,
) -> Result<u32, Error> {
fn is_interesting_all<OT>(&mut self, _: &I, _: &OT, _: &ExitKind) -> Result<u32, Error>
where
OT: ObserversTuple,
{
Ok(0)
}
@ -95,12 +97,15 @@ where
Tail: FeedbacksTuple<I>,
I: Input,
{
fn is_interesting_all<OT: ObserversTuple>(
fn is_interesting_all<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error> {
) -> Result<u32, Error>
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<I> Feedback<I> for CrashFeedback
where
I: Input,
{
fn is_interesting<OT: ObserversTuple>(
fn is_interesting<OT>(
&mut self,
_input: &I,
_observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error> {
) -> Result<u32, Error>
where
OT: ObserversTuple,
{
if let ExitKind::Crash = exit_kind {
Ok(1)
} else {
@ -164,12 +172,15 @@ impl<I> Feedback<I> for TimeoutFeedback
where
I: Input,
{
fn is_interesting<OT: ObserversTuple>(
fn is_interesting<OT>(
&mut self,
_input: &I,
_observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error> {
) -> Result<u32, Error>
where
OT: ObserversTuple,
{
if let ExitKind::Timeout = exit_kind {
Ok(1)
} else {
@ -207,12 +218,15 @@ impl<I> Feedback<I> for TimeFeedback
where
I: Input,
{
fn is_interesting<OT: ObserversTuple>(
fn is_interesting<OT>(
&mut self,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<u32, Error> {
) -> Result<u32, Error>
where
OT: ObserversTuple,
{
let observer = observers.match_first_type::<TimeObserver>().unwrap();
self.exec_time = *observer.last_runtime();
Ok(0)

View File

@ -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(|_, _, _, _, _| ()),

View File

@ -11,6 +11,7 @@ use crate::{
ownedref::{OwnedArrayPtrMut, OwnedPtr},
tuples::Named,
},
executors::HasExecHooks,
observers::Observer,
Error,
};
@ -68,12 +69,18 @@ where
name: String,
}
impl<T> Observer for StdMapObserver<T>
impl<T> Observer for StdMapObserver<T> where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned
{
}
impl<EM, I, S, T> HasExecHooks<EM, I, S> for StdMapObserver<T>
where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
Self: MapObserver<T>,
{
#[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<T> Observer for VariableMapObserver<T>
impl<T> Observer for VariableMapObserver<T> where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned
{
}
impl<EM, I, S, T> HasExecHooks<EM, I, S> for VariableMapObserver<T>
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<M>
where
M: MapObserver<u8>,
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<M> Observer for HitcountsMapObserver<M>
impl<M> Observer for HitcountsMapObserver<M> where M: MapObserver<u8> {}
impl<EM, I, S, M> HasExecHooks<EM, I, S> for HitcountsMapObserver<M>
where
M: MapObserver<u8>,
M: MapObserver<u8> + HasExecHooks<EM, I, S>,
{
#[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<M> Named for HitcountsMapObserver<M>
where
M: MapObserver<u8>,
M: Named + serde::Serialize + serde::de::DeserializeOwned,
{
#[inline]
fn name(&self) -> &str {
@ -349,7 +363,7 @@ where
impl<M> HitcountsMapObserver<M>
where
M: MapObserver<u8>,
M: serde::Serialize + serde::de::DeserializeOwned,
{
/// Creates a new MapObserver
pub fn new(base: M) -> Self {

View File

@ -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<Vec<u8>, 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<Head, Tail> 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<EM, I, S> HasExecHooks<EM, I, S> 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(())
}

View File

@ -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<C, I> + Evaluator<I>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
CS: CorpusScheduler<I, S>,
{
/// The mutator registered for this stage
@ -76,8 +76,8 @@ where
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
CS: CorpusScheduler<I, S>,
R: Rand,
{
@ -94,8 +94,8 @@ where
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
CS: CorpusScheduler<I, S>,
R: Rand,
{
@ -125,8 +125,8 @@ where
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
CS: CorpusScheduler<I, S>,
R: Rand,
{
@ -150,8 +150,8 @@ where
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S>,
CS: CorpusScheduler<I, S>,
R: Rand,
{

View File

@ -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<FT, I>
pub trait HasFeedbacks<FT, I>: Sized
where
FT: FeedbacksTuple<I>,
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<Testcase<I>, 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<FT, I>
pub trait HasObjectives<FT, I>: Sized
where
FT: FeedbacksTuple<I>,
I: Input,
@ -160,7 +143,7 @@ pub trait HasStartTime {
}
/// Add to the state if interesting
pub trait IfInteresting<I>
pub trait IfInteresting<I>: Sized
where
I: Input,
{
@ -200,8 +183,11 @@ where
scheduler: &CS,
) -> Result<(u32, Option<usize>), Error>
where
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
EM: EventManager<I, Self>,
CS: CorpusScheduler<I, Self>;
}
@ -469,12 +455,13 @@ where
CS: CorpusScheduler<I, Self>,
{
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<usize>), Error>
where
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
C: Corpus<I>,
EM: EventManager<I, Self>,
CS: CorpusScheduler<I, Self>,
@ -555,8 +545,11 @@ where
in_dir: &Path,
) -> Result<(), Error>
where
E: Executor<BytesInput> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<BytesInput>
+ HasObservers<OT>
+ HasExecHooks<EM, BytesInput, Self>
+ HasObserversHooks<EM, BytesInput, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, BytesInput, Self>,
EM: EventManager<BytesInput, Self>,
CS: CorpusScheduler<BytesInput, Self>,
{
@ -601,8 +594,11 @@ where
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
E: Executor<BytesInput> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<BytesInput>
+ HasObservers<OT>
+ HasExecHooks<EM, BytesInput, Self>
+ HasObserversHooks<EM, BytesInput, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, BytesInput, Self>,
EM: EventManager<BytesInput, Self>,
CS: CorpusScheduler<BytesInput, Self>,
{
@ -639,19 +635,22 @@ where
event_mgr: &mut EM,
) -> Result<(u32, bool), Error>
where
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
C: Corpus<I>,
EM: EventManager<I, Self>,
{
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<I, R>,
C: Corpus<I>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
EM: EventManager<I, Self>,
CS: CorpusScheduler<I, Self>,
{

View File

@ -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<Option<AsanErrors>>,
}
impl Observer for AsanErrorsObserver {
fn pre_exec(&mut self) -> Result<(), Error> {
impl Observer for AsanErrorsObserver {}
impl<EM, I, S> HasExecHooks<EM, I, S> 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();