Make executor state available to the harness V2 (#1900)
* inital commit. * clippy * tests * clippy * adapt example * systemmode. * renaming * fmt * fix lints. * more lint fix. * even more lint fixes. * always more lint fixes. * lint fix. * allow unused qualifications for crate when it could be confusing. * Still lint fixes. * Lint fixes on generated code. * Some lint fixes. * renamed modules as well.
This commit is contained in:
parent
44b0b0ad82
commit
55a300d508
@ -1,4 +1,4 @@
|
||||
use core::ptr::addr_of_mut;
|
||||
use core::{fmt::Debug, ptr::addr_of_mut};
|
||||
use std::{marker::PhantomData, process};
|
||||
|
||||
#[cfg(feature = "simplemgr")]
|
||||
@ -70,7 +70,7 @@ pub struct Instance<'a, M: Monitor> {
|
||||
impl<'a, M: Monitor> Instance<'a, M> {
|
||||
pub fn run<QT>(&mut self, helpers: QT, state: Option<ClientState>) -> Result<(), Error>
|
||||
where
|
||||
QT: QemuHelperTuple<ClientState>,
|
||||
QT: QemuHelperTuple<ClientState> + Debug,
|
||||
{
|
||||
let mut hooks = QemuHooks::new(self.emu.clone(), helpers);
|
||||
|
||||
|
@ -41,7 +41,10 @@ use crate::{
|
||||
Error,
|
||||
};
|
||||
|
||||
/// The process executor simply calls a target function, as mutable reference to a closure
|
||||
/// A version of `InProcessExecutor` with a state accessible from the harness.
|
||||
pub mod stateful;
|
||||
|
||||
/// The process executor simply calls a target function, as mutable reference to a closure.
|
||||
pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor<H, &'a mut H, (), OT, S>;
|
||||
|
||||
/// The inprocess executor that allows hooks
|
||||
@ -56,6 +59,20 @@ pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor<
|
||||
S,
|
||||
>;
|
||||
|
||||
/// The internal state of `GenericInProcessExecutor`.
|
||||
pub struct GenericInProcessExecutorInner<HT, OT, S>
|
||||
where
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
/// The observers, observing each run
|
||||
observers: OT,
|
||||
// Crash and timeout hah
|
||||
hooks: (InProcessHooks, HT),
|
||||
phantom: PhantomData<S>,
|
||||
}
|
||||
|
||||
/// The inmem executor simply calls a target function, then returns afterwards.
|
||||
#[allow(dead_code)]
|
||||
pub struct GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
@ -66,13 +83,22 @@ where
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
/// The harness function, being executed for each fuzzing loop execution
|
||||
harness_fn: HB,
|
||||
/// The observers, observing each run
|
||||
observers: OT,
|
||||
// Crash and timeout hah
|
||||
hooks: (InProcessHooks, HT),
|
||||
phantom: PhantomData<(S, *const H)>,
|
||||
inner: GenericInProcessExecutorInner<HT, OT, S>,
|
||||
phantom: PhantomData<(*const H, HB)>,
|
||||
}
|
||||
|
||||
impl<HT, OT, S> Debug for GenericInProcessExecutorInner<HT, OT, S>
|
||||
where
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
S: State,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("GenericInProcessExecutorState")
|
||||
.field("observers", &self.observers)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S> Debug for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
@ -85,15 +111,24 @@ where
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("GenericInProcessExecutor")
|
||||
.field("inner", &self.inner)
|
||||
.field("harness_fn", &"<fn>")
|
||||
.field("observers", &self.observers)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<HT, OT, S> UsesState for GenericInProcessExecutorInner<HT, OT, S>
|
||||
where
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S> UsesState for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
where
|
||||
H: ?Sized + FnMut(&S::Input) -> ExitKind,
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
@ -102,9 +137,18 @@ where
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<HT, OT, S> UsesObservers for GenericInProcessExecutorInner<HT, OT, S>
|
||||
where
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
type Observers = OT;
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S> UsesObservers for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
where
|
||||
H: ?Sized + FnMut(&S::Input) -> ExitKind,
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
@ -131,21 +175,19 @@ where
|
||||
input: &Self::Input,
|
||||
) -> Result<ExitKind, Error> {
|
||||
*state.executions_mut() += 1;
|
||||
self.enter_target(fuzzer, state, mgr, input);
|
||||
self.hooks.pre_exec_all(fuzzer, state, mgr, input);
|
||||
self.inner.enter_target(fuzzer, state, mgr, input);
|
||||
self.inner.hooks.pre_exec_all(fuzzer, state, mgr, input);
|
||||
|
||||
let ret = (self.harness_fn.borrow_mut())(input);
|
||||
|
||||
self.hooks.post_exec_all(fuzzer, state, mgr, input);
|
||||
self.leave_target(fuzzer, state, mgr, input);
|
||||
self.inner.hooks.post_exec_all(fuzzer, state, mgr, input);
|
||||
self.inner.leave_target(fuzzer, state, mgr, input);
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S> HasObservers for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
impl<HT, OT, S> HasObservers for GenericInProcessExecutorInner<HT, OT, S>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
@ -160,13 +202,31 @@ where
|
||||
&mut self.observers
|
||||
}
|
||||
}
|
||||
impl<H, HB, HT, OT, S> GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
|
||||
impl<H, HB, HT, OT, S> HasObservers for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> &OT {
|
||||
self.inner.observers()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn observers_mut(&mut self) -> &mut OT {
|
||||
self.inner.observers_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<HT, OT, S> GenericInProcessExecutorInner<HT, OT, S>
|
||||
where
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
/// This function marks the boundary between the fuzzer and the target
|
||||
#[inline]
|
||||
@ -223,30 +283,27 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, OT, S> InProcessExecutor<'a, H, OT, S>
|
||||
impl<OT, S> GenericInProcessExecutorInner<(), OT, S>
|
||||
where
|
||||
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S>,
|
||||
S: HasExecutions + HasSolutions + HasCorpus + State,
|
||||
{
|
||||
/// Create a new in mem executor with the default timeout (5 sec)
|
||||
pub fn new<EM, OF, Z>(
|
||||
harness_fn: &'a mut H,
|
||||
pub fn new<E, EM, OF, Z>(
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S>,
|
||||
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
Self::with_timeout_generic(
|
||||
Self::with_timeout_generic::<E, EM, OF, Z>(
|
||||
tuple_list!(),
|
||||
harness_fn,
|
||||
observers,
|
||||
fuzzer,
|
||||
state,
|
||||
@ -258,8 +315,7 @@ where
|
||||
/// Create a new in mem executor with the default timeout and use batch mode (5 sec)
|
||||
/// Do not use batched mode timeouts with cmplog cores. It is not supported
|
||||
#[cfg(all(feature = "std", target_os = "linux"))]
|
||||
pub fn batched_timeouts<EM, OF, Z>(
|
||||
harness_fn: &'a mut H,
|
||||
pub fn batched_timeouts<E, EM, OF, Z>(
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
@ -267,15 +323,14 @@ where
|
||||
exec_tmout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S>,
|
||||
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let mut me = Self::with_timeout_generic(
|
||||
let mut me = Self::with_timeout_generic::<E, EM, OF, Z>(
|
||||
tuple_list!(),
|
||||
harness_fn,
|
||||
observers,
|
||||
fuzzer,
|
||||
state,
|
||||
@ -293,8 +348,7 @@ where
|
||||
/// * `harness_fn` - the harness, executing the function
|
||||
/// * `observers` - the observers observing the target during execution
|
||||
/// This may return an error on unix, if signal handler setup fails
|
||||
pub fn with_timeout<EM, OF, Z>(
|
||||
harness_fn: &'a mut H,
|
||||
pub fn with_timeout<E, EM, OF, Z>(
|
||||
observers: OT,
|
||||
_fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
@ -302,13 +356,13 @@ where
|
||||
timeout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S>,
|
||||
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let default = InProcessHooks::new::<Self, EM, OF, Z>(timeout)?;
|
||||
let default = InProcessHooks::new::<E, EM, OF, Z>(timeout)?;
|
||||
let mut hooks = tuple_list!(default).merge(tuple_list!());
|
||||
hooks.init_all::<Self, S>(state);
|
||||
|
||||
@ -336,7 +390,6 @@ where
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
observers,
|
||||
hooks,
|
||||
phantom: PhantomData,
|
||||
@ -344,13 +397,231 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S> GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
impl<'a, H, OT, S> InProcessExecutor<'a, H, OT, S>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S>,
|
||||
S: HasExecutions + HasSolutions + HasCorpus + State,
|
||||
{
|
||||
/// Create a new in mem executor with the default timeout (5 sec)
|
||||
pub fn new<EM, OF, Z>(
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S> + HasObservers,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
Self::with_timeout_generic(
|
||||
tuple_list!(),
|
||||
harness_fn,
|
||||
observers,
|
||||
fuzzer,
|
||||
state,
|
||||
event_mgr,
|
||||
Duration::from_millis(5000),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new in mem executor with the default timeout and use batch mode(5 sec)
|
||||
#[cfg(all(feature = "std", target_os = "linux"))]
|
||||
pub fn batched_timeouts<EM, OF, Z>(
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
exec_tmout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let inner = GenericInProcessExecutorInner::batched_timeouts::<Self, EM, OF, Z>(
|
||||
observers, fuzzer, state, event_mgr, exec_tmout,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// * `user_hooks` - the hooks run before and after the harness's execution
|
||||
/// * `harness_fn` - the harness, executing the function
|
||||
/// * `observers` - the observers observing the target during execution
|
||||
/// This may return an error on unix, if signal handler setup fails
|
||||
pub fn with_timeout<EM, OF, Z>(
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S> + HasObservers,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let inner = GenericInProcessExecutorInner::with_timeout::<Self, EM, OF, Z>(
|
||||
observers, fuzzer, state, event_mgr, timeout,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<HT, OT, S> GenericInProcessExecutorInner<HT, OT, S>
|
||||
where
|
||||
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: HasExecutions + HasSolutions + HasCorpus + State,
|
||||
{
|
||||
/// Create a new in mem executor with the default timeout (5 sec)
|
||||
pub fn generic<E, EM, OF, Z>(
|
||||
user_hooks: HT,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
Self::with_timeout_generic::<E, EM, OF, Z>(
|
||||
user_hooks,
|
||||
observers,
|
||||
fuzzer,
|
||||
state,
|
||||
event_mgr,
|
||||
Duration::from_millis(5000),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new in mem executor with the default timeout and use batch mode(5 sec)
|
||||
#[cfg(all(feature = "std", target_os = "linux"))]
|
||||
pub fn batched_timeout_generic<E, EM, OF, Z>(
|
||||
user_hooks: HT,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
exec_tmout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let mut me = Self::with_timeout_generic::<E, EM, OF, Z>(
|
||||
user_hooks, observers, fuzzer, state, event_mgr, exec_tmout,
|
||||
)?;
|
||||
me.hooks_mut().0.timer_mut().batch_mode = true;
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// * `user_hooks` - the hooks run before and after the harness's execution
|
||||
/// * `harness_fn` - the harness, executing the function
|
||||
/// * `observers` - the observers observing the target during execution
|
||||
/// This may return an error on unix, if signal handler setup fails
|
||||
pub fn with_timeout_generic<E, EM, OF, Z>(
|
||||
user_hooks: HT,
|
||||
observers: OT,
|
||||
_fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let default = InProcessHooks::new::<E, EM, OF, Z>(timeout)?;
|
||||
let mut hooks = tuple_list!(default).merge(user_hooks);
|
||||
hooks.init_all::<Self, S>(state);
|
||||
|
||||
#[cfg(windows)]
|
||||
// Some initialization necessary for windows.
|
||||
unsafe {
|
||||
/*
|
||||
See https://github.com/AFLplusplus/LibAFL/pull/403
|
||||
This one reserves certain amount of memory for the stack.
|
||||
If stack overflow happens during fuzzing on windows, the program is transferred to our exception handler for windows.
|
||||
However, if we run out of the stack memory again in this exception handler, we'll crash with STATUS_ACCESS_VIOLATION.
|
||||
We need this API call because with the llmp_compression
|
||||
feature enabled, the exception handler uses a lot of stack memory (in the compression lib code) on release build.
|
||||
As far as I have observed, the compression uses around 0x10000 bytes, but for safety let's just reserve 0x20000 bytes for our exception handlers.
|
||||
This number 0x20000 could vary depending on the compilers optimization for future compression library changes.
|
||||
*/
|
||||
let mut stack_reserved = 0x20000;
|
||||
SetThreadStackGuarantee(&mut stack_reserved)?;
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", windows))]
|
||||
{
|
||||
// set timeout for the handler
|
||||
*hooks.0.millis_sec_mut() = timeout.as_millis() as i64;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
observers,
|
||||
hooks,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// The inprocess handlers
|
||||
#[inline]
|
||||
pub fn hooks(&self) -> &(InProcessHooks, HT) {
|
||||
&self.hooks
|
||||
}
|
||||
|
||||
/// The inprocess handlers (mutable)
|
||||
#[inline]
|
||||
pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) {
|
||||
&mut self.hooks
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S> GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State + HasExecutions + HasSolutions + HasCorpus,
|
||||
{
|
||||
/// Create a new in mem executor with the default timeout (5 sec)
|
||||
pub fn generic<EM, OF, Z>(
|
||||
@ -362,7 +633,7 @@ where
|
||||
event_mgr: &mut EM,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S>,
|
||||
Self: Executor<EM, Z, State = S> + HasObservers,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
@ -391,17 +662,21 @@ where
|
||||
exec_tmout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S>,
|
||||
Self: Executor<EM, Z, State = S> + HasObservers,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let mut me = Self::with_timeout_generic(
|
||||
user_hooks, harness_fn, observers, fuzzer, state, event_mgr, exec_tmout,
|
||||
let inner = GenericInProcessExecutorInner::batched_timeout_generic::<Self, EM, OF, Z>(
|
||||
user_hooks, observers, fuzzer, state, event_mgr, exec_tmout,
|
||||
)?;
|
||||
me.hooks_mut().0.timer_mut().batch_mode = true;
|
||||
Ok(me)
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new in mem executor.
|
||||
@ -415,49 +690,25 @@ where
|
||||
user_hooks: HT,
|
||||
harness_fn: HB,
|
||||
observers: OT,
|
||||
_fuzzer: &mut Z,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S>,
|
||||
Self: Executor<EM, Z, State = S> + HasObservers,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let default = InProcessHooks::new::<Self, EM, OF, Z>(timeout)?;
|
||||
let mut hooks = tuple_list!(default).merge(user_hooks);
|
||||
hooks.init_all::<Self, S>(state);
|
||||
|
||||
#[cfg(windows)]
|
||||
// Some initialization necessary for windows.
|
||||
unsafe {
|
||||
/*
|
||||
See https://github.com/AFLplusplus/LibAFL/pull/403
|
||||
This one reserves certain amount of memory for the stack.
|
||||
If stack overflow happens during fuzzing on windows, the program is transferred to our exception handler for windows.
|
||||
However, if we run out of the stack memory again in this exception handler, we'll crash with STATUS_ACCESS_VIOLATION.
|
||||
We need this API call because with the llmp_compression
|
||||
feature enabled, the exception handler uses a lot of stack memory (in the compression lib code) on release build.
|
||||
As far as I have observed, the compression uses around 0x10000 bytes, but for safety let's just reserve 0x20000 bytes for our exception handlers.
|
||||
This number 0x20000 could vary depending on the compilers optimization for future compression library changes.
|
||||
*/
|
||||
let mut stack_reserved = 0x20000;
|
||||
SetThreadStackGuarantee(&mut stack_reserved)?;
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", windows))]
|
||||
{
|
||||
// set timeout for the handler
|
||||
*hooks.0.millis_sec_mut() = timeout.as_millis() as i64;
|
||||
}
|
||||
let inner = GenericInProcessExecutorInner::with_timeout_generic::<Self, EM, OF, Z>(
|
||||
user_hooks, observers, fuzzer, state, event_mgr, timeout,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
observers,
|
||||
hooks,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
@ -477,13 +728,13 @@ where
|
||||
/// The inprocess handlers
|
||||
#[inline]
|
||||
pub fn hooks(&self) -> &(InProcessHooks, HT) {
|
||||
&self.hooks
|
||||
self.inner.hooks()
|
||||
}
|
||||
|
||||
/// The inprocess handlers (mutable)
|
||||
#[inline]
|
||||
pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) {
|
||||
&mut self.hooks
|
||||
self.inner.hooks_mut()
|
||||
}
|
||||
}
|
||||
|
||||
@ -496,10 +747,8 @@ pub trait HasInProcessHooks {
|
||||
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks;
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S> HasInProcessHooks for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
impl<HT, OT, S> HasInProcessHooks for GenericInProcessExecutorInner<HT, OT, S>
|
||||
where
|
||||
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State + HasExecutions + HasSolutions + HasCorpus,
|
||||
@ -517,6 +766,27 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S> HasInProcessHooks for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||
where
|
||||
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State + HasExecutions + HasSolutions + HasCorpus,
|
||||
{
|
||||
/// the timeout handler
|
||||
#[inline]
|
||||
fn inprocess_hooks(&self) -> &InProcessHooks {
|
||||
self.inner.inprocess_hooks()
|
||||
}
|
||||
|
||||
/// the timeout handler
|
||||
#[inline]
|
||||
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks {
|
||||
self.inner.inprocess_hooks_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
/// Save state if it is an objective
|
416
libafl/src/executors/inprocess/stateful.rs
Normal file
416
libafl/src/executors/inprocess/stateful.rs
Normal file
@ -0,0 +1,416 @@
|
||||
use alloc::boxed::Box;
|
||||
use core::{
|
||||
borrow::BorrowMut,
|
||||
fmt::{self, Debug, Formatter},
|
||||
marker::PhantomData,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use libafl_bolts::tuples::tuple_list;
|
||||
|
||||
use crate::{
|
||||
events::{EventFirer, EventRestarter},
|
||||
executors::{
|
||||
hooks::{inprocess::InProcessHooks, ExecutorHooksTuple},
|
||||
inprocess::{GenericInProcessExecutorInner, HasInProcessHooks},
|
||||
Executor, ExitKind, HasObservers,
|
||||
},
|
||||
feedbacks::Feedback,
|
||||
fuzzer::HasObjective,
|
||||
inputs::UsesInput,
|
||||
observers::{ObserversTuple, UsesObservers},
|
||||
state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState},
|
||||
Error,
|
||||
};
|
||||
|
||||
/// The process executor simply calls a target function, as mutable reference to a closure
|
||||
/// The internal state of the executor is made available to the harness.
|
||||
pub type StatefulInProcessExecutor<'a, H, OT, S, ES> =
|
||||
StatefulGenericInProcessExecutor<H, &'a mut H, (), OT, S, ES>;
|
||||
|
||||
/// The process executor simply calls a target function, as boxed `FnMut` trait object
|
||||
/// The internal state of the executor is made available to the harness.
|
||||
pub type OwnedInProcessExecutor<OT, S, ES> = StatefulGenericInProcessExecutor<
|
||||
dyn FnMut(&<S as UsesInput>::Input, &mut ES) -> ExitKind,
|
||||
Box<dyn FnMut(&<S as UsesInput>::Input, &mut ES) -> ExitKind>,
|
||||
(),
|
||||
OT,
|
||||
S,
|
||||
ES,
|
||||
>;
|
||||
|
||||
/// The inmem executor simply calls a target function, then returns afterwards.
|
||||
/// The harness can access the internal state of the executor.
|
||||
#[allow(dead_code)]
|
||||
pub struct StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
/// The harness function, being executed for each fuzzing loop execution
|
||||
harness_fn: HB,
|
||||
/// The state used as argument of the harness
|
||||
pub exposed_executor_state: ES,
|
||||
/// Inner state of the executor
|
||||
pub inner: GenericInProcessExecutorInner<HT, OT, S>,
|
||||
phantom: PhantomData<(ES, *const H)>,
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S, ES> Debug for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
S: State,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("StatefulGenericInProcessExecutor")
|
||||
.field("harness_fn", &"<fn>")
|
||||
.field("inner", &self.inner)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S, ES> UsesState for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S, ES> UsesObservers for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
type Observers = OT;
|
||||
}
|
||||
|
||||
impl<EM, H, HB, HT, OT, S, Z, ES> Executor<EM, Z>
|
||||
for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||
where
|
||||
EM: UsesState<State = S>,
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State + HasExecutions,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
fn run_target(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut Self::State,
|
||||
mgr: &mut EM,
|
||||
input: &Self::Input,
|
||||
) -> Result<ExitKind, Error> {
|
||||
*state.executions_mut() += 1;
|
||||
self.inner.enter_target(fuzzer, state, mgr, input);
|
||||
self.inner.hooks.pre_exec_all(fuzzer, state, mgr, input);
|
||||
|
||||
let ret = (self.harness_fn.borrow_mut())(input, &mut self.exposed_executor_state);
|
||||
|
||||
self.inner.hooks.post_exec_all(fuzzer, state, mgr, input);
|
||||
self.inner.leave_target(fuzzer, state, mgr, input);
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S, ES> HasObservers for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> &OT {
|
||||
self.inner.observers()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn observers_mut(&mut self) -> &mut OT {
|
||||
self.inner.observers_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, OT, S, ES> StatefulInProcessExecutor<'a, H, OT, S, ES>
|
||||
where
|
||||
H: FnMut(&<S as UsesInput>::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S>,
|
||||
S: HasExecutions + HasSolutions + HasCorpus + State,
|
||||
{
|
||||
/// Create a new in mem executor with the default timeout (5 sec)
|
||||
pub fn new<EM, OF, Z>(
|
||||
harness_fn: &'a mut H,
|
||||
exposed_executor_state: ES,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S>,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
Self::with_timeout_generic(
|
||||
tuple_list!(),
|
||||
harness_fn,
|
||||
exposed_executor_state,
|
||||
observers,
|
||||
fuzzer,
|
||||
state,
|
||||
event_mgr,
|
||||
Duration::from_millis(5000),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new in mem executor with the default timeout and use batch mode(5 sec)
|
||||
#[cfg(all(feature = "std", target_os = "linux"))]
|
||||
pub fn batched_timeouts<EM, OF, Z>(
|
||||
harness_fn: &'a mut H,
|
||||
exposed_executor_state: ES,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
exec_tmout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S>,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let inner = GenericInProcessExecutorInner::batched_timeouts::<Self, EM, OF, Z>(
|
||||
observers, fuzzer, state, event_mgr, exec_tmout,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
exposed_executor_state,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// * `user_hooks` - the hooks run before and after the harness's execution
|
||||
/// * `harness_fn` - the harness, executing the function
|
||||
/// * `observers` - the observers observing the target during execution
|
||||
/// This may return an error on unix, if signal handler setup fails
|
||||
pub fn with_timeout<EM, OF, Z>(
|
||||
harness_fn: &'a mut H,
|
||||
exposed_executor_state: ES,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
Self: Executor<EM, Z, State = S>,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let inner = GenericInProcessExecutorInner::with_timeout::<Self, EM, OF, Z>(
|
||||
observers, fuzzer, state, event_mgr, timeout,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
exposed_executor_state,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S, ES> StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
{
|
||||
/// The executor state given to the harness
|
||||
pub fn exposed_executor_state(&self) -> &ES {
|
||||
&self.exposed_executor_state
|
||||
}
|
||||
|
||||
/// The mutable executor state given to the harness
|
||||
pub fn exposed_executor_state_mut(&mut self) -> &mut ES {
|
||||
&mut self.exposed_executor_state
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S, ES> StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State + HasExecutions + HasSolutions + HasCorpus,
|
||||
{
|
||||
/// Create a new in mem executor with the default timeout (5 sec)
|
||||
pub fn generic<EM, OF, Z>(
|
||||
user_hooks: HT,
|
||||
harness_fn: HB,
|
||||
exposed_executor_state: ES,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
Self::with_timeout_generic(
|
||||
user_hooks,
|
||||
harness_fn,
|
||||
exposed_executor_state,
|
||||
observers,
|
||||
fuzzer,
|
||||
state,
|
||||
event_mgr,
|
||||
Duration::from_millis(5000),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new in mem executor with the default timeout and use batch mode(5 sec)
|
||||
#[cfg(all(feature = "std", target_os = "linux"))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn batched_timeout_generic<EM, OF, Z>(
|
||||
user_hooks: HT,
|
||||
harness_fn: HB,
|
||||
exposed_executor_state: ES,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
exec_tmout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let inner = GenericInProcessExecutorInner::batched_timeout_generic::<Self, EM, OF, Z>(
|
||||
user_hooks, observers, fuzzer, state, event_mgr, exec_tmout,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
exposed_executor_state,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// * `user_hooks` - the hooks run before and after the harness's execution
|
||||
/// * `harness_fn` - the harness, executing the function
|
||||
/// * `observers` - the observers observing the target during execution
|
||||
/// This may return an error on unix, if signal handler setup fails
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn with_timeout_generic<EM, OF, Z>(
|
||||
user_hooks: HT,
|
||||
harness_fn: HB,
|
||||
exposed_executor_state: ES,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: State,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let inner = GenericInProcessExecutorInner::with_timeout_generic::<Self, EM, OF, Z>(
|
||||
user_hooks, observers, fuzzer, state, event_mgr, timeout,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
exposed_executor_state,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve the harness function.
|
||||
#[inline]
|
||||
pub fn harness(&self) -> &H {
|
||||
self.harness_fn.borrow()
|
||||
}
|
||||
|
||||
/// Retrieve the harness function for a mutable reference.
|
||||
#[inline]
|
||||
pub fn harness_mut(&mut self) -> &mut H {
|
||||
self.harness_fn.borrow_mut()
|
||||
}
|
||||
|
||||
/// The inprocess handlers
|
||||
#[inline]
|
||||
pub fn hooks(&self) -> &(InProcessHooks, HT) {
|
||||
self.inner.hooks()
|
||||
}
|
||||
|
||||
/// The inprocess handlers (mutable)
|
||||
#[inline]
|
||||
pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) {
|
||||
self.inner.hooks_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, HB, HT, OT, S, ES> HasInProcessHooks
|
||||
for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||
where
|
||||
H: FnMut(&<S as UsesInput>::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HB: BorrowMut<H>,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State + HasExecutions + HasSolutions + HasCorpus,
|
||||
{
|
||||
/// the timeout handler
|
||||
#[inline]
|
||||
fn inprocess_hooks(&self) -> &InProcessHooks {
|
||||
self.inner.inprocess_hooks()
|
||||
}
|
||||
|
||||
/// the timeout handler
|
||||
#[inline]
|
||||
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks {
|
||||
self.inner.inprocess_hooks_mut()
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ use libafl_bolts::{
|
||||
use libc::siginfo_t;
|
||||
use nix::{
|
||||
sys::wait::{waitpid, WaitStatus},
|
||||
unistd::{fork, ForkResult},
|
||||
unistd::{fork, ForkResult, Pid},
|
||||
};
|
||||
|
||||
use super::hooks::ExecutorHooksTuple;
|
||||
@ -35,6 +35,7 @@ use crate::{
|
||||
state::{HasExecutions, HasSolutions, State, UsesState},
|
||||
Error,
|
||||
};
|
||||
|
||||
/// The signature of the crash handler function
|
||||
pub(crate) type ForkHandlerFuncPtr = unsafe fn(
|
||||
Signal,
|
||||
@ -46,20 +47,27 @@ pub(crate) type ForkHandlerFuncPtr = unsafe fn(
|
||||
#[cfg(all(unix, not(target_os = "linux")))]
|
||||
use crate::executors::hooks::timer::{setitimer, Itimerval, Timeval, ITIMER_REAL};
|
||||
|
||||
/// The `InProcessForkExecutor` with no user hooks
|
||||
pub type InProcessForkExecutor<'a, H, OT, S, SP> =
|
||||
GenericInProcessForkExecutor<'a, H, (), OT, S, SP>;
|
||||
/// A version of `InProcessForkExecutor` with a state accessible from the harness.
|
||||
pub mod stateful;
|
||||
|
||||
impl<'a, H, OT, S, SP> InProcessForkExecutor<'a, H, OT, S, SP>
|
||||
/// The `InProcessForkExecutor` with no user hooks
|
||||
pub type InProcessForkExecutor<'a, H, OT, S, SP, EM, Z> =
|
||||
GenericInProcessForkExecutor<'a, H, (), OT, S, SP, EM, Z>;
|
||||
|
||||
impl<'a, H, OT, S, SP, EM, Z, OF> InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
S: State,
|
||||
OT: ObserversTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OF: Feedback<S>,
|
||||
S: HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
/// The constructor for `InProcessForkExecutor`
|
||||
pub fn new<EM, OF, Z>(
|
||||
pub fn new(
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
@ -67,13 +75,7 @@ where
|
||||
event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
shmem_provider: SP,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OF: Feedback<S>,
|
||||
S: HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
) -> Result<Self, Error> {
|
||||
Self::with_hooks(
|
||||
tuple_list!(),
|
||||
harness_fn,
|
||||
@ -87,37 +89,54 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// [`GenericInProcessForkExecutor`] is an executor that forks the current process before each execution.
|
||||
pub struct GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
|
||||
/// Inner state of GenericInProcessExecutor-like structures.
|
||||
pub struct GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S>,
|
||||
S: UsesInput,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
hooks: (InChildProcessHooks, HT),
|
||||
harness_fn: &'a mut H,
|
||||
shmem_provider: SP,
|
||||
observers: OT,
|
||||
#[cfg(target_os = "linux")]
|
||||
itimerspec: libc::itimerspec,
|
||||
#[cfg(all(unix, not(target_os = "linux")))]
|
||||
itimerval: Itimerval,
|
||||
phantom: PhantomData<S>,
|
||||
phantom: PhantomData<(S, EM, Z)>,
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP> Debug for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
|
||||
/// [`GenericInProcessForkExecutor`] is an executor that forks the current process before each execution.
|
||||
pub struct GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
OT: ObserversTuple<S>,
|
||||
S: UsesInput,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
harness_fn: &'a mut H,
|
||||
inner: GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>,
|
||||
}
|
||||
|
||||
impl<HT, OT, S, SP, EM, Z> Debug for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
S: UsesInput,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple + Debug,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
#[cfg(target_os = "linux")]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("GenericInProcessForkExecutor")
|
||||
f.debug_struct("GenericInProcessForkExecutorInner")
|
||||
.field("hooks", &self.hooks)
|
||||
.field("observers", &self.observers)
|
||||
.field("shmem_provider", &self.shmem_provider)
|
||||
.field("itimerspec", &self.itimerspec)
|
||||
@ -128,7 +147,7 @@ where
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
return f
|
||||
.debug_struct("GenericInProcessForkExecutor")
|
||||
.debug_struct("GenericInProcessForkExecutorInner")
|
||||
.field("observers", &self.observers)
|
||||
.field("shmem_provider", &self.shmem_provider)
|
||||
.field("itimerval", &self.itimerval)
|
||||
@ -136,26 +155,163 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP> UsesState for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
|
||||
impl<'a, H, HT, OT, S, SP, EM, Z> Debug
|
||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
S: UsesInput,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple + Debug,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
#[cfg(target_os = "linux")]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("GenericInProcessForkExecutor")
|
||||
.field("GenericInProcessForkExecutorInner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
return f
|
||||
.debug_struct("GenericInProcessForkExecutor")
|
||||
.field("GenericInProcessForkExecutorInner", &self.inner)
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
|
||||
impl<HT, OT, S, SP, EM, Z> UsesState for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: ?Sized + FnMut(&S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<'a, EM, H, HT, OT, S, SP, Z> Executor<EM, Z>
|
||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
|
||||
impl<'a, H, HT, OT, S, SP, EM, Z> UsesState
|
||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
EM: UsesState<State = S>,
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<EM, HT, OT, S, SP, Z> GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
S: State + UsesInput,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
unsafe fn pre_run_target_child(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut <GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z> as UsesState>::State,
|
||||
mgr: &mut EM,
|
||||
input: &<GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z> as UsesInput>::Input,
|
||||
) -> Result<(), Error> {
|
||||
self.shmem_provider.post_fork(true)?;
|
||||
|
||||
self.enter_target(fuzzer, state, mgr, input);
|
||||
self.hooks.pre_exec_all(fuzzer, state, mgr, input);
|
||||
|
||||
self.observers
|
||||
.pre_exec_child_all(state, input)
|
||||
.expect("Failed to run post_exec on observers");
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let mut timerid: libc::timer_t = null_mut();
|
||||
// creates a new per-process interval timer
|
||||
// we can't do this from the parent, timerid is unique to each process.
|
||||
libc::timer_create(libc::CLOCK_MONOTONIC, null_mut(), addr_of_mut!(timerid));
|
||||
|
||||
// log::info!("Set timer! {:#?} {timerid:#?}", self.itimerspec);
|
||||
let _: i32 = libc::timer_settime(timerid, 0, addr_of_mut!(self.itimerspec), null_mut());
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
setitimer(ITIMER_REAL, &mut self.itimerval, null_mut());
|
||||
}
|
||||
// log::trace!("{v:#?} {}", nix::errno::errno());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn post_run_target_child(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut <GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z> as UsesState>::State,
|
||||
mgr: &mut EM,
|
||||
input: &<GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z> as UsesInput>::Input,
|
||||
) {
|
||||
self.observers
|
||||
.post_exec_child_all(state, input, &ExitKind::Ok)
|
||||
.expect("Failed to run post_exec on observers");
|
||||
|
||||
self.hooks.post_exec_all(fuzzer, state, mgr, input);
|
||||
self.leave_target(fuzzer, state, mgr, input);
|
||||
|
||||
libc::_exit(0);
|
||||
}
|
||||
|
||||
fn parent(&mut self, child: Pid) -> Result<ExitKind, Error> {
|
||||
// log::trace!("from parent {} child is {}", std::process::id(), child);
|
||||
self.shmem_provider.post_fork(false)?;
|
||||
|
||||
let res = waitpid(child, None)?;
|
||||
log::trace!("{res:#?}");
|
||||
match res {
|
||||
WaitStatus::Signaled(_, signal, _) => match signal {
|
||||
nix::sys::signal::Signal::SIGALRM | nix::sys::signal::Signal::SIGUSR2 => {
|
||||
Ok(ExitKind::Timeout)
|
||||
}
|
||||
_ => Ok(ExitKind::Crash),
|
||||
},
|
||||
WaitStatus::Exited(_, code) => {
|
||||
if code > 128 && code < 160 {
|
||||
// Signal exit codes
|
||||
let signal = code - 128;
|
||||
if signal == Signal::SigAlarm as libc::c_int
|
||||
|| signal == Signal::SigUser2 as libc::c_int
|
||||
{
|
||||
Ok(ExitKind::Timeout)
|
||||
} else {
|
||||
Ok(ExitKind::Crash)
|
||||
}
|
||||
} else {
|
||||
Ok(ExitKind::Ok)
|
||||
}
|
||||
}
|
||||
_ => Ok(ExitKind::Ok),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, EM, H, HT, OT, S, SP, Z> Executor<EM, Z>
|
||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
S: State + HasExecutions,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
#[allow(unreachable_code)]
|
||||
@ -170,86 +326,18 @@ where
|
||||
*state.executions_mut() += 1;
|
||||
|
||||
unsafe {
|
||||
self.shmem_provider.pre_fork()?;
|
||||
self.inner.shmem_provider.pre_fork()?;
|
||||
match fork() {
|
||||
Ok(ForkResult::Child) => {
|
||||
// Child
|
||||
self.shmem_provider.post_fork(true)?;
|
||||
|
||||
self.enter_target(fuzzer, state, mgr, input);
|
||||
self.hooks.pre_exec_all(fuzzer, state, mgr, input);
|
||||
|
||||
self.observers
|
||||
.pre_exec_child_all(state, input)
|
||||
.expect("Failed to run post_exec on observers");
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let mut timerid: libc::timer_t = null_mut();
|
||||
// creates a new per-process interval timer
|
||||
// we can't do this from the parent, timerid is unique to each process.
|
||||
libc::timer_create(
|
||||
libc::CLOCK_MONOTONIC,
|
||||
null_mut(),
|
||||
addr_of_mut!(timerid),
|
||||
);
|
||||
|
||||
// log::info!("Set timer! {:#?} {timerid:#?}", self.itimerspec);
|
||||
let _: i32 = libc::timer_settime(
|
||||
timerid,
|
||||
0,
|
||||
addr_of_mut!(self.itimerspec),
|
||||
null_mut(),
|
||||
);
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
setitimer(ITIMER_REAL, &mut self.itimerval, null_mut());
|
||||
}
|
||||
// log::trace!("{v:#?} {}", nix::errno::errno());
|
||||
self.inner.pre_run_target_child(fuzzer, state, mgr, input)?;
|
||||
(self.harness_fn)(input);
|
||||
|
||||
self.observers
|
||||
.post_exec_child_all(state, input, &ExitKind::Ok)
|
||||
.expect("Failed to run post_exec on observers");
|
||||
|
||||
self.hooks.post_exec_all(fuzzer, state, mgr, input);
|
||||
self.leave_target(fuzzer, state, mgr, input);
|
||||
|
||||
libc::_exit(0);
|
||||
|
||||
self.inner.post_run_target_child(fuzzer, state, mgr, input);
|
||||
Ok(ExitKind::Ok)
|
||||
}
|
||||
Ok(ForkResult::Parent { child }) => {
|
||||
// Parent
|
||||
// log::trace!("from parent {} child is {}", std::process::id(), child);
|
||||
self.shmem_provider.post_fork(false)?;
|
||||
|
||||
let res = waitpid(child, None)?;
|
||||
log::trace!("{res:#?}");
|
||||
match res {
|
||||
WaitStatus::Signaled(_, signal, _) => match signal {
|
||||
nix::sys::signal::Signal::SIGALRM
|
||||
| nix::sys::signal::Signal::SIGUSR2 => Ok(ExitKind::Timeout),
|
||||
_ => Ok(ExitKind::Crash),
|
||||
},
|
||||
WaitStatus::Exited(_, code) => {
|
||||
if code > 128 && code < 160 {
|
||||
// Signal exit codes
|
||||
let signal = code - 128;
|
||||
if signal == Signal::SigAlarm as libc::c_int
|
||||
|| signal == Signal::SigUser2 as libc::c_int
|
||||
{
|
||||
Ok(ExitKind::Timeout)
|
||||
} else {
|
||||
Ok(ExitKind::Crash)
|
||||
}
|
||||
} else {
|
||||
Ok(ExitKind::Ok)
|
||||
}
|
||||
}
|
||||
_ => Ok(ExitKind::Ok),
|
||||
}
|
||||
self.inner.parent(child)
|
||||
}
|
||||
Err(e) => Err(Error::from(e)),
|
||||
}
|
||||
@ -257,17 +345,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
|
||||
impl<HT, OT, S, SP, EM, Z> GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
HT: ExecutorHooksTuple,
|
||||
S: State,
|
||||
OT: ObserversTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
#[inline]
|
||||
/// This function marks the boundary between the fuzzer and the target.
|
||||
pub fn enter_target<EM, Z>(
|
||||
pub fn enter_target(
|
||||
&mut self,
|
||||
_fuzzer: &mut Z,
|
||||
state: &mut <Self as UsesState>::State,
|
||||
@ -294,7 +383,7 @@ where
|
||||
|
||||
#[inline]
|
||||
/// This function marks the boundary between the fuzzer and the target.
|
||||
pub fn leave_target<EM, Z>(
|
||||
pub fn leave_target(
|
||||
&mut self,
|
||||
_fuzzer: &mut Z,
|
||||
_state: &mut <Self as UsesState>::State,
|
||||
@ -307,22 +396,15 @@ where
|
||||
/// Creates a new [`GenericInProcessForkExecutor`] with custom hooks
|
||||
#[cfg(target_os = "linux")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn with_hooks<EM, OF, Z>(
|
||||
pub fn with_hooks(
|
||||
userhooks: HT,
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
_fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
shmem_provider: SP,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OF: Feedback<S>,
|
||||
S: HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
) -> Result<Self, Error> {
|
||||
let default_hooks = InChildProcessHooks::new::<Self>()?;
|
||||
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
|
||||
hooks.init_all::<Self, S>(state);
|
||||
@ -342,7 +424,6 @@ where
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
shmem_provider,
|
||||
observers,
|
||||
hooks,
|
||||
@ -354,22 +435,15 @@ where
|
||||
/// Creates a new [`GenericInProcessForkExecutor`], non linux
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn with_hooks<EM, OF, Z>(
|
||||
pub fn with_hooks(
|
||||
userhooks: HT,
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
_fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
shmem_provider: SP,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OF: Feedback<S>,
|
||||
S: HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
) -> Result<Self, Error> {
|
||||
let default_hooks = InChildProcessHooks::new::<Self>()?;
|
||||
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
|
||||
hooks.init_all::<Self, S>(state);
|
||||
@ -389,7 +463,6 @@ where
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
shmem_provider,
|
||||
observers,
|
||||
hooks,
|
||||
@ -397,6 +470,45 @@ where
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP, EM, Z, OF> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OF: Feedback<S>,
|
||||
S: State + HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
/// Creates a new [`GenericInProcessForkExecutor`] with custom hooks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn with_hooks(
|
||||
userhooks: HT,
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
shmem_provider: SP,
|
||||
) -> Result<Self, Error>
|
||||
where {
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
inner: GenericInProcessForkExecutorInner::with_hooks(
|
||||
userhooks,
|
||||
observers,
|
||||
fuzzer,
|
||||
state,
|
||||
event_mgr,
|
||||
timeout,
|
||||
shmem_provider,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve the harness function.
|
||||
#[inline]
|
||||
@ -411,24 +523,40 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP> UsesObservers for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
|
||||
impl<HT, OT, S, SP, EM, Z> UsesObservers for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: ?Sized + FnMut(&S::Input) -> ExitKind,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
type Observers = OT;
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP> HasObservers for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
|
||||
impl<'a, H, HT, OT, S, SP, EM, Z> UsesObservers
|
||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
type Observers = OT;
|
||||
}
|
||||
|
||||
impl<HT, OT, S, SP, EM, Z> HasObservers for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
HT: ExecutorHooksTuple,
|
||||
S: State,
|
||||
OT: ObserversTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> &OT {
|
||||
@ -440,6 +568,29 @@ where
|
||||
&mut self.observers
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP, EM, Z> HasObservers
|
||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||
HT: ExecutorHooksTuple,
|
||||
S: State,
|
||||
OT: ObserversTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> &OT {
|
||||
self.inner.observers()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn observers_mut(&mut self) -> &mut OT {
|
||||
self.inner.observers_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// signal hooks and `panic_hooks` for the child process
|
||||
|
||||
pub mod child_signal_handlers {
|
||||
@ -539,7 +690,10 @@ pub mod child_signal_handlers {
|
||||
mod tests {
|
||||
use libafl_bolts::tuples::tuple_list;
|
||||
|
||||
use crate::{executors::ExitKind, inputs::NopInput};
|
||||
use crate::{
|
||||
executors::{inprocess_fork::GenericInProcessForkExecutorInner, Executor, ExitKind},
|
||||
inputs::NopInput,
|
||||
};
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
@ -557,7 +711,7 @@ mod tests {
|
||||
events::SimpleEventManager,
|
||||
executors::{
|
||||
hooks::inprocess_fork::InChildProcessHooks,
|
||||
inprocess_fork::GenericInProcessForkExecutor, Executor,
|
||||
inprocess_fork::GenericInProcessForkExecutor,
|
||||
},
|
||||
fuzzer::test::NopFuzzer,
|
||||
state::NopState,
|
||||
@ -590,22 +744,26 @@ mod tests {
|
||||
let mut harness = |_buf: &NopInput| ExitKind::Ok;
|
||||
let default = InChildProcessHooks::nop();
|
||||
#[cfg(target_os = "linux")]
|
||||
let mut in_process_fork_executor = GenericInProcessForkExecutor::<_, (), (), _, _> {
|
||||
hooks: tuple_list!(default),
|
||||
let mut in_process_fork_executor = GenericInProcessForkExecutor {
|
||||
harness_fn: &mut harness,
|
||||
shmem_provider: provider,
|
||||
observers: tuple_list!(),
|
||||
itimerspec,
|
||||
phantom: PhantomData,
|
||||
inner: GenericInProcessForkExecutorInner {
|
||||
hooks: tuple_list!(default),
|
||||
shmem_provider: provider,
|
||||
observers: tuple_list!(),
|
||||
itimerspec,
|
||||
phantom: PhantomData,
|
||||
},
|
||||
};
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let mut in_process_fork_executor = GenericInProcessForkExecutor::<_, (), (), _, _> {
|
||||
let mut in_process_fork_executor = GenericInProcessForkExecutor {
|
||||
harness_fn: &mut harness,
|
||||
shmem_provider: provider,
|
||||
observers: tuple_list!(),
|
||||
hooks: tuple_list!(default),
|
||||
itimerval: itimerspec,
|
||||
phantom: PhantomData,
|
||||
inner: GenericInProcessForkExecutorInner {
|
||||
hooks: tuple_list!(default),
|
||||
shmem_provider: provider,
|
||||
observers: tuple_list!(),
|
||||
itimerval: itimerspec,
|
||||
phantom: PhantomData,
|
||||
},
|
||||
};
|
||||
let input = NopInput {};
|
||||
let mut fuzzer = NopFuzzer::new();
|
256
libafl/src/executors/inprocess_fork/stateful.rs
Normal file
256
libafl/src/executors/inprocess_fork/stateful.rs
Normal file
@ -0,0 +1,256 @@
|
||||
//! The `StatefulGenericInProcessForkExecutor` to do forking before executing the harness in-processly. Harness can access internal state.
|
||||
use core::{
|
||||
fmt::{self, Debug, Formatter},
|
||||
marker::PhantomData,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use libafl_bolts::{shmem::ShMemProvider, tuples::tuple_list};
|
||||
use nix::unistd::{fork, ForkResult};
|
||||
|
||||
use super::super::hooks::ExecutorHooksTuple;
|
||||
use crate::{
|
||||
events::{EventFirer, EventRestarter},
|
||||
executors::{
|
||||
inprocess_fork::GenericInProcessForkExecutorInner, Executor, ExitKind, HasObservers,
|
||||
},
|
||||
feedbacks::Feedback,
|
||||
fuzzer::HasObjective,
|
||||
inputs::UsesInput,
|
||||
observers::{ObserversTuple, UsesObservers},
|
||||
state::{HasExecutions, HasSolutions, State, UsesState},
|
||||
Error,
|
||||
};
|
||||
|
||||
/// The `StatefulInProcessForkExecutor` with no user hooks
|
||||
pub type StatefulInProcessForkExecutor<'a, H, OT, S, SP, ES, EM, Z> =
|
||||
StatefulGenericInProcessForkExecutor<'a, H, (), OT, S, SP, ES, EM, Z>;
|
||||
|
||||
impl<'a, H, OT, S, SP, ES, EM, Z, OF> StatefulInProcessForkExecutor<'a, H, OT, S, SP, ES, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OF: Feedback<S>,
|
||||
S: State + HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
/// The constructor for `InProcessForkExecutor`
|
||||
pub fn new(
|
||||
harness_fn: &'a mut H,
|
||||
exposed_executor_state: ES,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
shmem_provider: SP,
|
||||
) -> Result<Self, Error> {
|
||||
Self::with_hooks(
|
||||
tuple_list!(),
|
||||
harness_fn,
|
||||
exposed_executor_state,
|
||||
observers,
|
||||
fuzzer,
|
||||
state,
|
||||
event_mgr,
|
||||
timeout,
|
||||
shmem_provider,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`StatefulGenericInProcessForkExecutor`] is an executor that forks the current process before each execution. Harness can access some internal state.
|
||||
pub struct StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S>,
|
||||
S: UsesInput,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
harness_fn: &'a mut H,
|
||||
exposed_executor_state: ES,
|
||||
inner: GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>,
|
||||
phantom: PhantomData<ES>,
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP, ES, EM, Z> Debug
|
||||
for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
S: UsesInput,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple + Debug,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
#[cfg(target_os = "linux")]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("GenericInProcessForkExecutor")
|
||||
.field("GenericInProcessForkExecutionInner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
return f
|
||||
.debug_struct("GenericInProcessForkExecutor")
|
||||
.field("GenericInProcessForkExecutionInner", &self.inner)
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP, ES, EM, Z> UsesState
|
||||
for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<'a, EM, H, HT, OT, S, SP, Z, ES, OF> Executor<EM, Z>
|
||||
for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
S: State + HasExecutions,
|
||||
SP: ShMemProvider,
|
||||
HT: ExecutorHooksTuple,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
OF: Feedback<S>,
|
||||
{
|
||||
#[allow(unreachable_code)]
|
||||
#[inline]
|
||||
fn run_target(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut Self::State,
|
||||
mgr: &mut EM,
|
||||
input: &Self::Input,
|
||||
) -> Result<ExitKind, Error> {
|
||||
*state.executions_mut() += 1;
|
||||
|
||||
unsafe {
|
||||
self.inner.shmem_provider.pre_fork()?;
|
||||
match fork() {
|
||||
Ok(ForkResult::Child) => {
|
||||
// Child
|
||||
self.inner.pre_run_target_child(fuzzer, state, mgr, input)?;
|
||||
(self.harness_fn)(input, &mut self.exposed_executor_state);
|
||||
self.inner.post_run_target_child(fuzzer, state, mgr, input);
|
||||
Ok(ExitKind::Ok)
|
||||
}
|
||||
Ok(ForkResult::Parent { child }) => {
|
||||
// Parent
|
||||
self.inner.parent(child)
|
||||
}
|
||||
Err(e) => Err(Error::from(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP, ES, EM, Z, OF>
|
||||
StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
Z: UsesState<State = S>,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OF: Feedback<S>,
|
||||
S: State + HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
/// Creates a new [`StatefulGenericInProcessForkExecutor`] with custom hooks
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn with_hooks(
|
||||
userhooks: HT,
|
||||
harness_fn: &'a mut H,
|
||||
exposed_executor_state: ES,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
shmem_provider: SP,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
exposed_executor_state,
|
||||
inner: GenericInProcessForkExecutorInner::with_hooks(
|
||||
userhooks,
|
||||
observers,
|
||||
fuzzer,
|
||||
state,
|
||||
event_mgr,
|
||||
timeout,
|
||||
shmem_provider,
|
||||
)?,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve the harness function.
|
||||
#[inline]
|
||||
pub fn harness(&self) -> &H {
|
||||
self.harness_fn
|
||||
}
|
||||
|
||||
/// Retrieve the harness function for a mutable reference.
|
||||
#[inline]
|
||||
pub fn harness_mut(&mut self) -> &mut H {
|
||||
self.harness_fn
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP, ES, EM, Z> UsesObservers
|
||||
for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HT: ExecutorHooksTuple,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
type Observers = OT;
|
||||
}
|
||||
|
||||
impl<'a, H, HT, OT, S, SP, ES, EM, Z> HasObservers
|
||||
for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
||||
HT: ExecutorHooksTuple,
|
||||
S: State,
|
||||
OT: ObserversTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> &OT {
|
||||
self.inner.observers()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn observers_mut(&mut self) -> &mut OT {
|
||||
self.inner.observers_mut()
|
||||
}
|
||||
}
|
@ -7,8 +7,6 @@ use core::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
use libafl::inputs::UsesInput;
|
||||
#[cfg(feature = "fork")]
|
||||
use libafl::{
|
||||
events::EventManager,
|
||||
@ -34,28 +32,39 @@ use libafl_bolts::shmem::ShMemProvider;
|
||||
|
||||
use crate::{emu::Emulator, helper::QemuHelperTuple, hooks::QemuHooks};
|
||||
|
||||
/// A version of `QemuExecutor` with a state accessible from the harness.
|
||||
pub mod stateful;
|
||||
|
||||
pub struct QemuExecutorState<'a, QT, S>
|
||||
where
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State + HasExecutions,
|
||||
{
|
||||
hooks: &'a mut QemuHooks<QT, S>,
|
||||
first_exec: bool,
|
||||
}
|
||||
|
||||
pub struct QemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: State,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
inner: InProcessExecutor<'a, H, OT, S>,
|
||||
hooks: &'a mut QemuHooks<QT, S>,
|
||||
first_exec: bool,
|
||||
state: QemuExecutorState<'a, QT, S>,
|
||||
}
|
||||
|
||||
impl<'a, H, OT, QT, S> Debug for QemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: State,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
QT: QemuHelperTuple<S> + Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("QemuExecutor")
|
||||
.field("hooks", &self.hooks)
|
||||
.field("hooks", &self.state.hooks)
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
@ -68,17 +77,19 @@ extern "C" {
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
pub unsafe fn inproc_qemu_crash_handler<E, EM, OF, Z>(
|
||||
pub unsafe fn inproc_qemu_crash_handler<'a, E, EM, OF, Z, QT, S>(
|
||||
signal: Signal,
|
||||
info: &mut siginfo_t,
|
||||
mut context: Option<&mut ucontext_t>,
|
||||
_data: &mut InProcessExecutorHandlerData,
|
||||
info: &'a mut siginfo_t,
|
||||
mut context: Option<&'a mut ucontext_t>,
|
||||
_data: &'a mut InProcessExecutorHandlerData,
|
||||
) where
|
||||
E: Executor<EM, Z> + HasObservers,
|
||||
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
|
||||
OF: Feedback<E::State>,
|
||||
E::State: HasExecutions + HasSolutions + HasCorpus,
|
||||
Z: HasObjective<Objective = OF, State = E::State>,
|
||||
QT: QemuHelperTuple<S> + Debug + 'a,
|
||||
S: State + HasExecutions + 'a,
|
||||
{
|
||||
let puc = match &mut context {
|
||||
Some(v) => ptr::from_mut::<ucontext_t>(*v) as *mut c_void,
|
||||
@ -88,7 +99,7 @@ pub unsafe fn inproc_qemu_crash_handler<E, EM, OF, Z>(
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
static mut BREAK_ON_TMOUT: bool = false;
|
||||
pub(crate) static mut BREAK_ON_TMOUT: bool = false;
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
extern "C" {
|
||||
@ -96,11 +107,11 @@ extern "C" {
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
pub unsafe fn inproc_qemu_timeout_handler<E, EM, OF, Z>(
|
||||
pub unsafe fn inproc_qemu_timeout_handler<'a, E, EM, OF, Z>(
|
||||
signal: Signal,
|
||||
info: &mut siginfo_t,
|
||||
context: Option<&mut ucontext_t>,
|
||||
data: &mut InProcessExecutorHandlerData,
|
||||
info: &'a mut siginfo_t,
|
||||
context: Option<&'a mut ucontext_t>,
|
||||
data: &'a mut InProcessExecutorHandlerData,
|
||||
) where
|
||||
E: Executor<EM, Z> + HasObservers + HasInProcessHooks,
|
||||
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
|
||||
@ -117,12 +128,61 @@ pub unsafe fn inproc_qemu_timeout_handler<E, EM, OF, Z>(
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, QT, S> QemuExecutorState<'a, QT, S>
|
||||
where
|
||||
S: State + HasExecutions,
|
||||
QT: QemuHelperTuple<S> + Debug,
|
||||
{
|
||||
pub fn new<E, EM, OF, OT, Z>(hooks: &'a mut QemuHooks<QT, S>) -> Result<Self, Error>
|
||||
where
|
||||
E: Executor<EM, Z, State = S> + HasInProcessHooks + HasObservers,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OF: Feedback<S>,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State + HasExecutions + HasCorpus + HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
{
|
||||
let handler = |hooks: &mut QemuHooks<QT, S>, host_sig| {
|
||||
eprintln!("Crashed with signal {host_sig}");
|
||||
unsafe {
|
||||
libafl::executors::inprocess::generic_inproc_crash_handler::<E, EM, OF, Z>();
|
||||
}
|
||||
if let Some(cpu) = hooks.emulator().current_cpu() {
|
||||
eprint!("Context:\n{}", cpu.display_context());
|
||||
}
|
||||
};
|
||||
|
||||
hooks.crash_closure(Box::new(handler));
|
||||
}
|
||||
Ok(QemuExecutorState {
|
||||
first_exec: true,
|
||||
hooks,
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn hooks(&self) -> &QemuHooks<QT, S> {
|
||||
self.hooks
|
||||
}
|
||||
|
||||
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
|
||||
self.hooks
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn emulator(&self) -> &Emulator {
|
||||
self.hooks.emulator()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, OT, QT, S> QemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: State,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
QT: QemuHelperTuple<S> + Debug,
|
||||
{
|
||||
pub fn new<EM, OF, Z>(
|
||||
hooks: &'a mut QemuHooks<QT, S>,
|
||||
@ -142,40 +202,25 @@ where
|
||||
let mut inner = InProcessExecutor::with_timeout(
|
||||
harness_fn, observers, fuzzer, state, event_mgr, timeout,
|
||||
)?;
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
{
|
||||
inner.inprocess_hooks_mut().crash_handler =
|
||||
inproc_qemu_crash_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z>
|
||||
inproc_qemu_crash_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z, QT, S>
|
||||
as *const c_void;
|
||||
|
||||
let handler = |hooks: &mut QemuHooks<QT, S>, host_sig| {
|
||||
eprintln!("Crashed with signal {host_sig}");
|
||||
unsafe {
|
||||
libafl::executors::inprocess::generic_inproc_crash_handler::<
|
||||
InProcessExecutor<'a, H, OT, S>,
|
||||
EM,
|
||||
OF,
|
||||
Z,
|
||||
>();
|
||||
}
|
||||
if let Some(cpu) = hooks.emulator().current_cpu() {
|
||||
eprint!("Context:\n{}", cpu.display_context());
|
||||
}
|
||||
};
|
||||
|
||||
hooks.crash_closure(Box::new(handler));
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
{
|
||||
inner.inprocess_hooks_mut().timeout_handler =
|
||||
inproc_qemu_timeout_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z>
|
||||
as *const c_void;
|
||||
}
|
||||
Ok(Self {
|
||||
first_exec: true,
|
||||
hooks,
|
||||
inner,
|
||||
})
|
||||
|
||||
let state =
|
||||
QemuExecutorState::new::<InProcessExecutor<'a, H, OT, S>, EM, OF, OT, Z>(hooks)?;
|
||||
|
||||
Ok(Self { inner, state })
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &InProcessExecutor<'a, H, OT, S> {
|
||||
@ -194,26 +239,65 @@ where
|
||||
}
|
||||
|
||||
pub fn hooks(&self) -> &QemuHooks<QT, S> {
|
||||
self.hooks
|
||||
self.state.hooks()
|
||||
}
|
||||
|
||||
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
|
||||
self.hooks
|
||||
self.state.hooks_mut()
|
||||
}
|
||||
|
||||
pub fn emulator(&self) -> &Emulator {
|
||||
self.hooks.emulator()
|
||||
self.state.emulator()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, EM, H, OT, QT, S, Z> Executor<EM, Z> for QemuExecutor<'a, H, OT, QT, S>
|
||||
impl<'a, QT, S> QemuExecutorState<'a, QT, S>
|
||||
where
|
||||
EM: UsesState<State = S>,
|
||||
S: State + HasExecutions + HasCorpus + HasSolutions,
|
||||
QT: QemuHelperTuple<S> + Debug,
|
||||
{
|
||||
fn pre_exec<E, EM, OF, Z>(&mut self, input: &E::Input, emu: &Emulator)
|
||||
where
|
||||
E: Executor<EM, Z, State = S>,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OF: Feedback<S>,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
if self.first_exec {
|
||||
self.hooks.helpers().first_exec_all(self.hooks);
|
||||
self.first_exec = false;
|
||||
}
|
||||
self.hooks.helpers_mut().pre_exec_all(emu, input);
|
||||
}
|
||||
|
||||
fn post_exec<E, EM, OT, OF, Z>(
|
||||
&mut self,
|
||||
input: &E::Input,
|
||||
emu: &Emulator,
|
||||
observers: &mut OT,
|
||||
exit_kind: &mut ExitKind,
|
||||
) where
|
||||
E: Executor<EM, Z, State = S> + HasObservers,
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OT: ObserversTuple<S>,
|
||||
OF: Feedback<S>,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
self.hooks
|
||||
.helpers_mut()
|
||||
.post_exec_all(emu, input, observers, exit_kind);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, EM, H, OT, OF, QT, S, Z> Executor<EM, Z> for QemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: State + HasExecutions,
|
||||
S: State + HasExecutions + HasCorpus + HasSolutions,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
Z: UsesState<State = S>,
|
||||
OF: Feedback<S>,
|
||||
QT: QemuHelperTuple<S> + Debug,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
fn run_target(
|
||||
&mut self,
|
||||
@ -223,15 +307,11 @@ where
|
||||
input: &Self::Input,
|
||||
) -> Result<ExitKind, Error> {
|
||||
let emu = Emulator::get().unwrap();
|
||||
if self.first_exec {
|
||||
self.hooks.helpers().first_exec_all(self.hooks);
|
||||
self.first_exec = false;
|
||||
}
|
||||
self.hooks.helpers_mut().pre_exec_all(&emu, input);
|
||||
self.state.pre_exec::<Self, EM, OF, Z>(input, &emu);
|
||||
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
||||
self.hooks.helpers_mut().post_exec_all(
|
||||
&emu,
|
||||
self.state.post_exec::<Self, EM, OT, OF, Z>(
|
||||
input,
|
||||
&emu,
|
||||
self.inner.observers_mut(),
|
||||
&mut exit_kind,
|
||||
);
|
||||
@ -244,7 +324,7 @@ where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State,
|
||||
S: State + HasExecutions,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
@ -254,7 +334,7 @@ where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State,
|
||||
S: State + HasExecutions,
|
||||
{
|
||||
type Observers = OT;
|
||||
}
|
||||
@ -262,7 +342,7 @@ where
|
||||
impl<'a, H, OT, QT, S> HasObservers for QemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: State,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
@ -278,46 +358,53 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
pub struct QemuForkExecutor<'a, H, OT, QT, S, SP>
|
||||
pub struct QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: UsesInput,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
first_exec: bool,
|
||||
hooks: &'a mut QemuHooks<QT, S>,
|
||||
inner: InProcessForkExecutor<'a, H, OT, S, SP>,
|
||||
inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>,
|
||||
state: QemuExecutorState<'a, QT, S>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, H, OT, QT, S, SP> Debug for QemuForkExecutor<'a, H, OT, QT, S, SP>
|
||||
impl<'a, H, OT, QT, S, SP, EM, Z> Debug for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: UsesInput,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
QT: QemuHelperTuple<S> + Debug,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("QemuForkExecutor")
|
||||
.field("hooks", &self.hooks)
|
||||
.field("hooks", &self.state.hooks)
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, H, OT, QT, S, SP> QemuForkExecutor<'a, H, OT, QT, S, SP>
|
||||
impl<'a, H, OT, QT, S, SP, EM, Z, OF> QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: State,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
pub fn new<EM, OF, Z>(
|
||||
pub fn new(
|
||||
hooks: &'a mut QemuHooks<QT, S>,
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
@ -326,18 +413,10 @@ where
|
||||
event_mgr: &mut EM,
|
||||
shmem_provider: SP,
|
||||
timeout: core::time::Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
EM: EventFirer<State = S> + EventRestarter,
|
||||
OF: Feedback<S>,
|
||||
S: HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
) -> Result<Self, Error> {
|
||||
assert!(!QT::HOOKS_DO_SIDE_EFFECTS, "When using QemuForkExecutor, the hooks must not do any side effect as they will happen in the child process and then discarded");
|
||||
|
||||
Ok(Self {
|
||||
first_exec: true,
|
||||
hooks,
|
||||
inner: InProcessForkExecutor::new(
|
||||
harness_fn,
|
||||
observers,
|
||||
@ -347,40 +426,46 @@ where
|
||||
timeout,
|
||||
shmem_provider,
|
||||
)?,
|
||||
state: QemuExecutorState {
|
||||
first_exec: true,
|
||||
hooks,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &InProcessForkExecutor<'a, H, OT, S, SP> {
|
||||
pub fn inner(&self) -> &InProcessForkExecutor<'a, H, OT, S, SP, EM, Z> {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn inner_mut(&mut self) -> &mut InProcessForkExecutor<'a, H, OT, S, SP> {
|
||||
pub fn inner_mut(&mut self) -> &mut InProcessForkExecutor<'a, H, OT, S, SP, EM, Z> {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
pub fn hooks(&self) -> &QemuHooks<QT, S> {
|
||||
self.hooks
|
||||
self.state.hooks
|
||||
}
|
||||
|
||||
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
|
||||
self.hooks
|
||||
self.state.hooks
|
||||
}
|
||||
|
||||
pub fn emulator(&self) -> &Emulator {
|
||||
self.hooks.emulator()
|
||||
self.state.hooks.emulator()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, EM, H, OT, QT, S, Z, SP> Executor<EM, Z> for QemuForkExecutor<'a, H, OT, QT, S, SP>
|
||||
impl<'a, EM, H, OT, QT, S, Z, SP, OF> Executor<EM, Z>
|
||||
for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
|
||||
where
|
||||
EM: EventManager<InProcessForkExecutor<'a, H, OT, S, SP>, Z, State = S>,
|
||||
EM: EventManager<InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>, Z, State = S>,
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: State + HasMetadata + HasExecutions + HasLastReportTime,
|
||||
OT: ObserversTuple<S>,
|
||||
S: State + HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + HasSolutions,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
QT: QemuHelperTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
Z: UsesState<State = S>,
|
||||
OF: Feedback<S>,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
fn run_target(
|
||||
&mut self,
|
||||
@ -390,13 +475,13 @@ where
|
||||
input: &Self::Input,
|
||||
) -> Result<ExitKind, Error> {
|
||||
let emu = Emulator::get().unwrap();
|
||||
if self.first_exec {
|
||||
self.hooks.helpers().first_exec_all(self.hooks);
|
||||
self.first_exec = false;
|
||||
if self.state.first_exec {
|
||||
self.state.hooks.helpers().first_exec_all(self.state.hooks);
|
||||
self.state.first_exec = false;
|
||||
}
|
||||
self.hooks.helpers_mut().pre_exec_all(&emu, input);
|
||||
self.state.hooks.helpers_mut().pre_exec_all(&emu, input);
|
||||
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
||||
self.hooks.helpers_mut().post_exec_all(
|
||||
self.state.hooks.helpers_mut().post_exec_all(
|
||||
&emu,
|
||||
input,
|
||||
self.inner.observers_mut(),
|
||||
@ -407,37 +492,43 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, H, OT, QT, S, SP> UsesObservers for QemuForkExecutor<'a, H, OT, QT, S, SP>
|
||||
impl<'a, H, OT, QT, S, SP, EM, Z> UsesObservers for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State,
|
||||
S: State + HasExecutions,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
type Observers = OT;
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, H, OT, QT, S, SP> UsesState for QemuForkExecutor<'a, H, OT, QT, S, SP>
|
||||
impl<'a, H, OT, QT, S, SP, EM, Z> UsesState for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State,
|
||||
S: State + HasExecutions,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
impl<'a, H, OT, QT, S, SP> HasObservers for QemuForkExecutor<'a, H, OT, QT, S, SP>
|
||||
impl<'a, H, OT, QT, S, SP, EM, Z> HasObservers for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
S: State,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
SP: ShMemProvider,
|
||||
EM: UsesState<State = S>,
|
||||
Z: UsesState<State = S>,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> &OT {
|
212
libafl_qemu/src/executor/stateful.rs
Normal file
212
libafl_qemu/src/executor/stateful.rs
Normal file
@ -0,0 +1,212 @@
|
||||
//! A `QEMU`-based executor for binary-only instrumentation in `LibAFL`
|
||||
use core::{
|
||||
ffi::c_void,
|
||||
fmt::{self, Debug, Formatter},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use libafl::{
|
||||
events::{EventFirer, EventRestarter},
|
||||
executors::{
|
||||
inprocess::{stateful::StatefulInProcessExecutor, HasInProcessHooks},
|
||||
Executor, ExitKind, HasObservers,
|
||||
},
|
||||
feedbacks::Feedback,
|
||||
fuzzer::HasObjective,
|
||||
observers::{ObserversTuple, UsesObservers},
|
||||
state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState},
|
||||
Error,
|
||||
};
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
use crate::executor::inproc_qemu_crash_handler;
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
use crate::executor::{inproc_qemu_timeout_handler, BREAK_ON_TMOUT};
|
||||
use crate::{
|
||||
emu::Emulator, executor::QemuExecutorState, helper::QemuHelperTuple, hooks::QemuHooks,
|
||||
};
|
||||
|
||||
pub struct StatefulQemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
inner: StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>,
|
||||
}
|
||||
|
||||
impl<'a, H, OT, QT, S> Debug for StatefulQemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S> + Debug,
|
||||
QT: QemuHelperTuple<S> + Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("QemuExecutor")
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, OT, QT, S> StatefulQemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S> + Debug,
|
||||
{
|
||||
pub fn new<EM, OF, Z>(
|
||||
hooks: &'a mut QemuHooks<QT, S>,
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
timeout: Duration,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
OF: Feedback<S>,
|
||||
S: State + HasExecutions + HasCorpus + HasSolutions,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
let qemu_state = QemuExecutorState::new::<
|
||||
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>,
|
||||
EM,
|
||||
OF,
|
||||
OT,
|
||||
Z,
|
||||
>(hooks)?;
|
||||
|
||||
let mut inner = StatefulInProcessExecutor::with_timeout(
|
||||
harness_fn, qemu_state, observers, fuzzer, state, event_mgr, timeout,
|
||||
)?;
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
{
|
||||
inner.inprocess_hooks_mut().crash_handler = inproc_qemu_crash_handler::<
|
||||
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>,
|
||||
EM,
|
||||
OF,
|
||||
Z,
|
||||
QT,
|
||||
S,
|
||||
> as *const c_void;
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
{
|
||||
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::<
|
||||
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>,
|
||||
EM,
|
||||
OF,
|
||||
Z,
|
||||
> as *const c_void;
|
||||
}
|
||||
|
||||
Ok(Self { inner })
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>> {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
pub fn break_on_timeout(&mut self) {
|
||||
unsafe {
|
||||
BREAK_ON_TMOUT = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inner_mut(
|
||||
&mut self,
|
||||
) -> &mut StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>> {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
pub fn hooks(&self) -> &QemuHooks<QT, S> {
|
||||
self.inner.exposed_executor_state().hooks()
|
||||
}
|
||||
|
||||
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
|
||||
self.inner.exposed_executor_state_mut().hooks_mut()
|
||||
}
|
||||
|
||||
pub fn emulator(&self) -> &Emulator {
|
||||
self.inner.exposed_executor_state().emulator()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, EM, H, OT, OF, QT, S, Z> Executor<EM, Z> for StatefulQemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
|
||||
S: State + HasExecutions + HasCorpus + HasSolutions,
|
||||
OT: ObserversTuple<S>,
|
||||
OF: Feedback<S>,
|
||||
QT: QemuHelperTuple<S> + Debug,
|
||||
Z: HasObjective<Objective = OF, State = S>,
|
||||
{
|
||||
fn run_target(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut Self::State,
|
||||
mgr: &mut EM,
|
||||
input: &Self::Input,
|
||||
) -> Result<ExitKind, Error> {
|
||||
let emu = Emulator::get().unwrap();
|
||||
self.inner
|
||||
.exposed_executor_state_mut()
|
||||
.pre_exec::<Self, EM, OF, Z>(input, &emu);
|
||||
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
||||
self.inner
|
||||
.exposed_executor_state
|
||||
.post_exec::<Self, EM, OT, OF, Z>(
|
||||
input,
|
||||
&emu,
|
||||
self.inner.inner.observers_mut(),
|
||||
&mut exit_kind,
|
||||
);
|
||||
Ok(exit_kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, OT, QT, S> UsesState for StatefulQemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State + HasExecutions,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<'a, H, OT, QT, S> UsesObservers for StatefulQemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State + HasExecutions,
|
||||
{
|
||||
type Observers = OT;
|
||||
}
|
||||
|
||||
impl<'a, H, OT, QT, S> HasObservers for StatefulQemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
|
||||
S: State + HasExecutions,
|
||||
OT: ObserversTuple<S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> &OT {
|
||||
self.inner.observers()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn observers_mut(&mut self) -> &mut OT {
|
||||
self.inner.observers_mut()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user