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};
|
use std::{marker::PhantomData, process};
|
||||||
|
|
||||||
#[cfg(feature = "simplemgr")]
|
#[cfg(feature = "simplemgr")]
|
||||||
@ -70,7 +70,7 @@ pub struct Instance<'a, M: Monitor> {
|
|||||||
impl<'a, M: Monitor> Instance<'a, M> {
|
impl<'a, M: Monitor> Instance<'a, M> {
|
||||||
pub fn run<QT>(&mut self, helpers: QT, state: Option<ClientState>) -> Result<(), Error>
|
pub fn run<QT>(&mut self, helpers: QT, state: Option<ClientState>) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
QT: QemuHelperTuple<ClientState>,
|
QT: QemuHelperTuple<ClientState> + Debug,
|
||||||
{
|
{
|
||||||
let mut hooks = QemuHooks::new(self.emu.clone(), helpers);
|
let mut hooks = QemuHooks::new(self.emu.clone(), helpers);
|
||||||
|
|
||||||
|
@ -41,7 +41,10 @@ use crate::{
|
|||||||
Error,
|
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>;
|
pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor<H, &'a mut H, (), OT, S>;
|
||||||
|
|
||||||
/// The inprocess executor that allows hooks
|
/// The inprocess executor that allows hooks
|
||||||
@ -56,6 +59,20 @@ pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor<
|
|||||||
S,
|
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.
|
/// The inmem executor simply calls a target function, then returns afterwards.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct GenericInProcessExecutor<H, HB, HT, OT, S>
|
pub struct GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||||
@ -66,13 +83,22 @@ where
|
|||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: State,
|
S: State,
|
||||||
{
|
{
|
||||||
/// The harness function, being executed for each fuzzing loop execution
|
|
||||||
harness_fn: HB,
|
harness_fn: HB,
|
||||||
/// The observers, observing each run
|
inner: GenericInProcessExecutorInner<HT, OT, S>,
|
||||||
observers: OT,
|
phantom: PhantomData<(*const H, HB)>,
|
||||||
// Crash and timeout hah
|
}
|
||||||
hooks: (InProcessHooks, HT),
|
|
||||||
phantom: PhantomData<(S, *const H)>,
|
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>
|
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 {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("GenericInProcessExecutor")
|
f.debug_struct("GenericInProcessExecutor")
|
||||||
|
.field("inner", &self.inner)
|
||||||
.field("harness_fn", &"<fn>")
|
.field("harness_fn", &"<fn>")
|
||||||
.field("observers", &self.observers)
|
|
||||||
.finish_non_exhaustive()
|
.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>
|
impl<H, HB, HT, OT, S> UsesState for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||||
where
|
where
|
||||||
H: ?Sized + FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
@ -102,9 +137,18 @@ where
|
|||||||
type State = S;
|
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>
|
impl<H, HB, HT, OT, S> UsesObservers for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||||
where
|
where
|
||||||
H: ?Sized + FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
@ -131,21 +175,19 @@ where
|
|||||||
input: &Self::Input,
|
input: &Self::Input,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> Result<ExitKind, Error> {
|
||||||
*state.executions_mut() += 1;
|
*state.executions_mut() += 1;
|
||||||
self.enter_target(fuzzer, state, mgr, input);
|
self.inner.enter_target(fuzzer, state, mgr, input);
|
||||||
self.hooks.pre_exec_all(fuzzer, state, mgr, input);
|
self.inner.hooks.pre_exec_all(fuzzer, state, mgr, input);
|
||||||
|
|
||||||
let ret = (self.harness_fn.borrow_mut())(input);
|
let ret = (self.harness_fn.borrow_mut())(input);
|
||||||
|
|
||||||
self.hooks.post_exec_all(fuzzer, state, mgr, input);
|
self.inner.hooks.post_exec_all(fuzzer, state, mgr, input);
|
||||||
self.leave_target(fuzzer, state, mgr, input);
|
self.inner.leave_target(fuzzer, state, mgr, input);
|
||||||
Ok(ret)
|
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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
|
||||||
HB: BorrowMut<H>,
|
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: State,
|
S: State,
|
||||||
@ -160,13 +202,31 @@ where
|
|||||||
&mut self.observers
|
&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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: State,
|
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
|
/// This function marks the boundary between the fuzzer and the target
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -223,30 +283,27 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, H, OT, S> InProcessExecutor<'a, H, OT, S>
|
impl<OT, S> GenericInProcessExecutorInner<(), OT, S>
|
||||||
where
|
where
|
||||||
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: HasExecutions + HasSolutions + HasCorpus + State,
|
S: HasExecutions + HasSolutions + HasCorpus + State,
|
||||||
{
|
{
|
||||||
/// Create a new in mem executor with the default timeout (5 sec)
|
/// Create a new in mem executor with the default timeout (5 sec)
|
||||||
pub fn new<EM, OF, Z>(
|
pub fn new<E, EM, OF, Z>(
|
||||||
harness_fn: &'a mut H,
|
|
||||||
observers: OT,
|
observers: OT,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
event_mgr: &mut EM,
|
event_mgr: &mut EM,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
Self: Executor<EM, Z, State = S>,
|
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
||||||
EM: EventFirer<State = S> + EventRestarter,
|
EM: EventFirer<State = S> + EventRestarter,
|
||||||
OF: Feedback<S>,
|
OF: Feedback<S>,
|
||||||
S: State,
|
S: State,
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
Z: HasObjective<Objective = OF, State = S>,
|
||||||
{
|
{
|
||||||
Self::with_timeout_generic(
|
Self::with_timeout_generic::<E, EM, OF, Z>(
|
||||||
tuple_list!(),
|
tuple_list!(),
|
||||||
harness_fn,
|
|
||||||
observers,
|
observers,
|
||||||
fuzzer,
|
fuzzer,
|
||||||
state,
|
state,
|
||||||
@ -258,8 +315,7 @@ where
|
|||||||
/// Create a new in mem executor with the default timeout and use batch mode (5 sec)
|
/// 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
|
/// Do not use batched mode timeouts with cmplog cores. It is not supported
|
||||||
#[cfg(all(feature = "std", target_os = "linux"))]
|
#[cfg(all(feature = "std", target_os = "linux"))]
|
||||||
pub fn batched_timeouts<EM, OF, Z>(
|
pub fn batched_timeouts<E, EM, OF, Z>(
|
||||||
harness_fn: &'a mut H,
|
|
||||||
observers: OT,
|
observers: OT,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
@ -267,15 +323,14 @@ where
|
|||||||
exec_tmout: Duration,
|
exec_tmout: Duration,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
Self: Executor<EM, Z, State = S>,
|
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
||||||
EM: EventFirer<State = S> + EventRestarter,
|
EM: EventFirer<State = S> + EventRestarter,
|
||||||
OF: Feedback<S>,
|
OF: Feedback<S>,
|
||||||
S: State,
|
S: State,
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
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!(),
|
tuple_list!(),
|
||||||
harness_fn,
|
|
||||||
observers,
|
observers,
|
||||||
fuzzer,
|
fuzzer,
|
||||||
state,
|
state,
|
||||||
@ -293,8 +348,7 @@ where
|
|||||||
/// * `harness_fn` - the harness, executing the function
|
/// * `harness_fn` - the harness, executing the function
|
||||||
/// * `observers` - the observers observing the target during execution
|
/// * `observers` - the observers observing the target during execution
|
||||||
/// This may return an error on unix, if signal handler setup fails
|
/// This may return an error on unix, if signal handler setup fails
|
||||||
pub fn with_timeout<EM, OF, Z>(
|
pub fn with_timeout<E, EM, OF, Z>(
|
||||||
harness_fn: &'a mut H,
|
|
||||||
observers: OT,
|
observers: OT,
|
||||||
_fuzzer: &mut Z,
|
_fuzzer: &mut Z,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
@ -302,13 +356,13 @@ where
|
|||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
Self: Executor<EM, Z, State = S>,
|
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
||||||
EM: EventFirer<State = S> + EventRestarter,
|
EM: EventFirer<State = S> + EventRestarter,
|
||||||
OF: Feedback<S>,
|
OF: Feedback<S>,
|
||||||
S: State,
|
S: State,
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
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!());
|
let mut hooks = tuple_list!(default).merge(tuple_list!());
|
||||||
hooks.init_all::<Self, S>(state);
|
hooks.init_all::<Self, S>(state);
|
||||||
|
|
||||||
@ -336,7 +390,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
harness_fn,
|
|
||||||
observers,
|
observers,
|
||||||
hooks,
|
hooks,
|
||||||
phantom: PhantomData,
|
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
|
where
|
||||||
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
|
||||||
HB: BorrowMut<H>,
|
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: HasExecutions + HasSolutions + HasCorpus + State,
|
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)
|
/// Create a new in mem executor with the default timeout (5 sec)
|
||||||
pub fn generic<EM, OF, Z>(
|
pub fn generic<EM, OF, Z>(
|
||||||
@ -362,7 +633,7 @@ where
|
|||||||
event_mgr: &mut EM,
|
event_mgr: &mut EM,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
Self: Executor<EM, Z, State = S>,
|
Self: Executor<EM, Z, State = S> + HasObservers,
|
||||||
EM: EventFirer<State = S> + EventRestarter,
|
EM: EventFirer<State = S> + EventRestarter,
|
||||||
OF: Feedback<S>,
|
OF: Feedback<S>,
|
||||||
S: State,
|
S: State,
|
||||||
@ -391,17 +662,21 @@ where
|
|||||||
exec_tmout: Duration,
|
exec_tmout: Duration,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
Self: Executor<EM, Z, State = S>,
|
Self: Executor<EM, Z, State = S> + HasObservers,
|
||||||
EM: EventFirer<State = S> + EventRestarter,
|
EM: EventFirer<State = S> + EventRestarter,
|
||||||
OF: Feedback<S>,
|
OF: Feedback<S>,
|
||||||
S: State,
|
S: State,
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
Z: HasObjective<Objective = OF, State = S>,
|
||||||
{
|
{
|
||||||
let mut me = Self::with_timeout_generic(
|
let inner = GenericInProcessExecutorInner::batched_timeout_generic::<Self, EM, OF, Z>(
|
||||||
user_hooks, harness_fn, observers, fuzzer, state, event_mgr, exec_tmout,
|
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.
|
/// Create a new in mem executor.
|
||||||
@ -415,49 +690,25 @@ where
|
|||||||
user_hooks: HT,
|
user_hooks: HT,
|
||||||
harness_fn: HB,
|
harness_fn: HB,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
_fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
_event_mgr: &mut EM,
|
event_mgr: &mut EM,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
Self: Executor<EM, Z, State = S>,
|
Self: Executor<EM, Z, State = S> + HasObservers,
|
||||||
EM: EventFirer<State = S> + EventRestarter,
|
EM: EventFirer<State = S> + EventRestarter,
|
||||||
OF: Feedback<S>,
|
OF: Feedback<S>,
|
||||||
S: State,
|
S: State,
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
Z: HasObjective<Objective = OF, State = S>,
|
||||||
{
|
{
|
||||||
let default = InProcessHooks::new::<Self, EM, OF, Z>(timeout)?;
|
let inner = GenericInProcessExecutorInner::with_timeout_generic::<Self, EM, OF, Z>(
|
||||||
let mut hooks = tuple_list!(default).merge(user_hooks);
|
user_hooks, observers, fuzzer, state, event_mgr, timeout,
|
||||||
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 {
|
Ok(Self {
|
||||||
harness_fn,
|
harness_fn,
|
||||||
observers,
|
inner,
|
||||||
hooks,
|
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -477,13 +728,13 @@ where
|
|||||||
/// The inprocess handlers
|
/// The inprocess handlers
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hooks(&self) -> &(InProcessHooks, HT) {
|
pub fn hooks(&self) -> &(InProcessHooks, HT) {
|
||||||
&self.hooks
|
self.inner.hooks()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The inprocess handlers (mutable)
|
/// The inprocess handlers (mutable)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) {
|
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;
|
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
|
where
|
||||||
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
|
||||||
HB: BorrowMut<H>,
|
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: State + HasExecutions + HasSolutions + HasCorpus,
|
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]
|
#[inline]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
/// Save state if it is an objective
|
/// 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 libc::siginfo_t;
|
||||||
use nix::{
|
use nix::{
|
||||||
sys::wait::{waitpid, WaitStatus},
|
sys::wait::{waitpid, WaitStatus},
|
||||||
unistd::{fork, ForkResult},
|
unistd::{fork, ForkResult, Pid},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::hooks::ExecutorHooksTuple;
|
use super::hooks::ExecutorHooksTuple;
|
||||||
@ -35,6 +35,7 @@ use crate::{
|
|||||||
state::{HasExecutions, HasSolutions, State, UsesState},
|
state::{HasExecutions, HasSolutions, State, UsesState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The signature of the crash handler function
|
/// The signature of the crash handler function
|
||||||
pub(crate) type ForkHandlerFuncPtr = unsafe fn(
|
pub(crate) type ForkHandlerFuncPtr = unsafe fn(
|
||||||
Signal,
|
Signal,
|
||||||
@ -46,20 +47,27 @@ pub(crate) type ForkHandlerFuncPtr = unsafe fn(
|
|||||||
#[cfg(all(unix, not(target_os = "linux")))]
|
#[cfg(all(unix, not(target_os = "linux")))]
|
||||||
use crate::executors::hooks::timer::{setitimer, Itimerval, Timeval, ITIMER_REAL};
|
use crate::executors::hooks::timer::{setitimer, Itimerval, Timeval, ITIMER_REAL};
|
||||||
|
|
||||||
/// The `InProcessForkExecutor` with no user hooks
|
/// A version of `InProcessForkExecutor` with a state accessible from the harness.
|
||||||
pub type InProcessForkExecutor<'a, H, OT, S, SP> =
|
pub mod stateful;
|
||||||
GenericInProcessForkExecutor<'a, H, (), OT, S, SP>;
|
|
||||||
|
|
||||||
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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
S: State,
|
S: State,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
SP: ShMemProvider,
|
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)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
/// The constructor for `InProcessForkExecutor`
|
/// The constructor for `InProcessForkExecutor`
|
||||||
pub fn new<EM, OF, Z>(
|
pub fn new(
|
||||||
harness_fn: &'a mut H,
|
harness_fn: &'a mut H,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -67,13 +75,7 @@ where
|
|||||||
event_mgr: &mut EM,
|
event_mgr: &mut EM,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
shmem_provider: SP,
|
shmem_provider: SP,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error> {
|
||||||
where
|
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
|
||||||
OF: Feedback<S>,
|
|
||||||
S: HasSolutions,
|
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
|
||||||
{
|
|
||||||
Self::with_hooks(
|
Self::with_hooks(
|
||||||
tuple_list!(),
|
tuple_list!(),
|
||||||
harness_fn,
|
harness_fn,
|
||||||
@ -87,37 +89,54 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`GenericInProcessForkExecutor`] is an executor that forks the current process before each execution.
|
/// Inner state of GenericInProcessExecutor-like structures.
|
||||||
pub struct GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
|
pub struct GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
hooks: (InChildProcessHooks, HT),
|
hooks: (InChildProcessHooks, HT),
|
||||||
harness_fn: &'a mut H,
|
|
||||||
shmem_provider: SP,
|
shmem_provider: SP,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
itimerspec: libc::itimerspec,
|
itimerspec: libc::itimerspec,
|
||||||
#[cfg(all(unix, not(target_os = "linux")))]
|
#[cfg(all(unix, not(target_os = "linux")))]
|
||||||
itimerval: Itimerval,
|
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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
OT: ObserversTuple<S> + Debug,
|
OT: ObserversTuple<S>,
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
HT: ExecutorHooksTuple,
|
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")]
|
#[cfg(target_os = "linux")]
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
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("observers", &self.observers)
|
||||||
.field("shmem_provider", &self.shmem_provider)
|
.field("shmem_provider", &self.shmem_provider)
|
||||||
.field("itimerspec", &self.itimerspec)
|
.field("itimerspec", &self.itimerspec)
|
||||||
@ -128,7 +147,7 @@ where
|
|||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
return f
|
return f
|
||||||
.debug_struct("GenericInProcessForkExecutor")
|
.debug_struct("GenericInProcessForkExecutorInner")
|
||||||
.field("observers", &self.observers)
|
.field("observers", &self.observers)
|
||||||
.field("shmem_provider", &self.shmem_provider)
|
.field("shmem_provider", &self.shmem_provider)
|
||||||
.field("itimerval", &self.itimerval)
|
.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
|
where
|
||||||
H: ?Sized + FnMut(&S::Input) -> ExitKind,
|
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: State,
|
S: State,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
type State = S;
|
type State = S;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, EM, H, HT, OT, S, SP, Z> Executor<EM, Z>
|
impl<'a, H, HT, OT, S, SP, EM, Z> UsesState
|
||||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
|
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||||
where
|
where
|
||||||
EM: UsesState<State = S>,
|
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
OT: ObserversTuple<S>,
|
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,
|
S: State + HasExecutions,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
|
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||||
Z: UsesState<State = S>,
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
@ -170,86 +326,18 @@ where
|
|||||||
*state.executions_mut() += 1;
|
*state.executions_mut() += 1;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
self.shmem_provider.pre_fork()?;
|
self.inner.shmem_provider.pre_fork()?;
|
||||||
match fork() {
|
match fork() {
|
||||||
Ok(ForkResult::Child) => {
|
Ok(ForkResult::Child) => {
|
||||||
// Child
|
// Child
|
||||||
self.shmem_provider.post_fork(true)?;
|
self.inner.pre_run_target_child(fuzzer, state, mgr, input)?;
|
||||||
|
|
||||||
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.harness_fn)(input);
|
(self.harness_fn)(input);
|
||||||
|
self.inner.post_run_target_child(fuzzer, state, mgr, 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);
|
|
||||||
|
|
||||||
Ok(ExitKind::Ok)
|
Ok(ExitKind::Ok)
|
||||||
}
|
}
|
||||||
Ok(ForkResult::Parent { child }) => {
|
Ok(ForkResult::Parent { child }) => {
|
||||||
// Parent
|
// Parent
|
||||||
// log::trace!("from parent {} child is {}", std::process::id(), child);
|
self.inner.parent(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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => Err(Error::from(e)),
|
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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
S: State,
|
S: State,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
|
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
/// This function marks the boundary between the fuzzer and the target.
|
/// This function marks the boundary between the fuzzer and the target.
|
||||||
pub fn enter_target<EM, Z>(
|
pub fn enter_target(
|
||||||
&mut self,
|
&mut self,
|
||||||
_fuzzer: &mut Z,
|
_fuzzer: &mut Z,
|
||||||
state: &mut <Self as UsesState>::State,
|
state: &mut <Self as UsesState>::State,
|
||||||
@ -294,7 +383,7 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// This function marks the boundary between the fuzzer and the target.
|
/// This function marks the boundary between the fuzzer and the target.
|
||||||
pub fn leave_target<EM, Z>(
|
pub fn leave_target(
|
||||||
&mut self,
|
&mut self,
|
||||||
_fuzzer: &mut Z,
|
_fuzzer: &mut Z,
|
||||||
_state: &mut <Self as UsesState>::State,
|
_state: &mut <Self as UsesState>::State,
|
||||||
@ -307,22 +396,15 @@ where
|
|||||||
/// Creates a new [`GenericInProcessForkExecutor`] with custom hooks
|
/// Creates a new [`GenericInProcessForkExecutor`] with custom hooks
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn with_hooks<EM, OF, Z>(
|
pub fn with_hooks(
|
||||||
userhooks: HT,
|
userhooks: HT,
|
||||||
harness_fn: &'a mut H,
|
|
||||||
observers: OT,
|
observers: OT,
|
||||||
_fuzzer: &mut Z,
|
_fuzzer: &mut Z,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
_event_mgr: &mut EM,
|
_event_mgr: &mut EM,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
shmem_provider: SP,
|
shmem_provider: SP,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error> {
|
||||||
where
|
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
|
||||||
OF: Feedback<S>,
|
|
||||||
S: HasSolutions,
|
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
|
||||||
{
|
|
||||||
let default_hooks = InChildProcessHooks::new::<Self>()?;
|
let default_hooks = InChildProcessHooks::new::<Self>()?;
|
||||||
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
|
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
|
||||||
hooks.init_all::<Self, S>(state);
|
hooks.init_all::<Self, S>(state);
|
||||||
@ -342,7 +424,6 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
harness_fn,
|
|
||||||
shmem_provider,
|
shmem_provider,
|
||||||
observers,
|
observers,
|
||||||
hooks,
|
hooks,
|
||||||
@ -354,22 +435,15 @@ where
|
|||||||
/// Creates a new [`GenericInProcessForkExecutor`], non linux
|
/// Creates a new [`GenericInProcessForkExecutor`], non linux
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn with_hooks<EM, OF, Z>(
|
pub fn with_hooks(
|
||||||
userhooks: HT,
|
userhooks: HT,
|
||||||
harness_fn: &'a mut H,
|
|
||||||
observers: OT,
|
observers: OT,
|
||||||
_fuzzer: &mut Z,
|
_fuzzer: &mut Z,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
_event_mgr: &mut EM,
|
_event_mgr: &mut EM,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
shmem_provider: SP,
|
shmem_provider: SP,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error> {
|
||||||
where
|
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
|
||||||
OF: Feedback<S>,
|
|
||||||
S: HasSolutions,
|
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
|
||||||
{
|
|
||||||
let default_hooks = InChildProcessHooks::new::<Self>()?;
|
let default_hooks = InChildProcessHooks::new::<Self>()?;
|
||||||
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
|
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
|
||||||
hooks.init_all::<Self, S>(state);
|
hooks.init_all::<Self, S>(state);
|
||||||
@ -389,7 +463,6 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
harness_fn,
|
|
||||||
shmem_provider,
|
shmem_provider,
|
||||||
observers,
|
observers,
|
||||||
hooks,
|
hooks,
|
||||||
@ -397,6 +470,45 @@ where
|
|||||||
phantom: PhantomData,
|
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.
|
/// Retrieve the harness function.
|
||||||
#[inline]
|
#[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
|
where
|
||||||
H: ?Sized + FnMut(&S::Input) -> ExitKind,
|
|
||||||
HT: ExecutorHooksTuple,
|
HT: ExecutorHooksTuple,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: State,
|
S: State,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
type Observers = OT;
|
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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
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,
|
HT: ExecutorHooksTuple,
|
||||||
S: State,
|
S: State,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn observers(&self) -> &OT {
|
fn observers(&self) -> &OT {
|
||||||
@ -440,6 +568,29 @@ where
|
|||||||
&mut self.observers
|
&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
|
/// signal hooks and `panic_hooks` for the child process
|
||||||
|
|
||||||
pub mod child_signal_handlers {
|
pub mod child_signal_handlers {
|
||||||
@ -539,7 +690,10 @@ pub mod child_signal_handlers {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use libafl_bolts::tuples::tuple_list;
|
use libafl_bolts::tuples::tuple_list;
|
||||||
|
|
||||||
use crate::{executors::ExitKind, inputs::NopInput};
|
use crate::{
|
||||||
|
executors::{inprocess_fork::GenericInProcessForkExecutorInner, Executor, ExitKind},
|
||||||
|
inputs::NopInput,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore)]
|
#[cfg_attr(miri, ignore)]
|
||||||
@ -557,7 +711,7 @@ mod tests {
|
|||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
executors::{
|
executors::{
|
||||||
hooks::inprocess_fork::InChildProcessHooks,
|
hooks::inprocess_fork::InChildProcessHooks,
|
||||||
inprocess_fork::GenericInProcessForkExecutor, Executor,
|
inprocess_fork::GenericInProcessForkExecutor,
|
||||||
},
|
},
|
||||||
fuzzer::test::NopFuzzer,
|
fuzzer::test::NopFuzzer,
|
||||||
state::NopState,
|
state::NopState,
|
||||||
@ -590,22 +744,26 @@ mod tests {
|
|||||||
let mut harness = |_buf: &NopInput| ExitKind::Ok;
|
let mut harness = |_buf: &NopInput| ExitKind::Ok;
|
||||||
let default = InChildProcessHooks::nop();
|
let default = InChildProcessHooks::nop();
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
let mut in_process_fork_executor = GenericInProcessForkExecutor::<_, (), (), _, _> {
|
let mut in_process_fork_executor = GenericInProcessForkExecutor {
|
||||||
hooks: tuple_list!(default),
|
|
||||||
harness_fn: &mut harness,
|
harness_fn: &mut harness,
|
||||||
shmem_provider: provider,
|
inner: GenericInProcessForkExecutorInner {
|
||||||
observers: tuple_list!(),
|
hooks: tuple_list!(default),
|
||||||
itimerspec,
|
shmem_provider: provider,
|
||||||
phantom: PhantomData,
|
observers: tuple_list!(),
|
||||||
|
itimerspec,
|
||||||
|
phantom: PhantomData,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
let mut in_process_fork_executor = GenericInProcessForkExecutor::<_, (), (), _, _> {
|
let mut in_process_fork_executor = GenericInProcessForkExecutor {
|
||||||
harness_fn: &mut harness,
|
harness_fn: &mut harness,
|
||||||
shmem_provider: provider,
|
inner: GenericInProcessForkExecutorInner {
|
||||||
observers: tuple_list!(),
|
hooks: tuple_list!(default),
|
||||||
hooks: tuple_list!(default),
|
shmem_provider: provider,
|
||||||
itimerval: itimerspec,
|
observers: tuple_list!(),
|
||||||
phantom: PhantomData,
|
itimerval: itimerspec,
|
||||||
|
phantom: PhantomData,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
let input = NopInput {};
|
let input = NopInput {};
|
||||||
let mut fuzzer = NopFuzzer::new();
|
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,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
|
||||||
use libafl::inputs::UsesInput;
|
|
||||||
#[cfg(feature = "fork")]
|
#[cfg(feature = "fork")]
|
||||||
use libafl::{
|
use libafl::{
|
||||||
events::EventManager,
|
events::EventManager,
|
||||||
@ -34,28 +32,39 @@ use libafl_bolts::shmem::ShMemProvider;
|
|||||||
|
|
||||||
use crate::{emu::Emulator, helper::QemuHelperTuple, hooks::QemuHooks};
|
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>
|
pub struct QemuExecutor<'a, H, OT, QT, S>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
S: State,
|
S: State + HasExecutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
inner: InProcessExecutor<'a, H, OT, S>,
|
inner: InProcessExecutor<'a, H, OT, S>,
|
||||||
hooks: &'a mut QemuHooks<QT, S>,
|
state: QemuExecutorState<'a, QT, S>,
|
||||||
first_exec: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, H, OT, QT, S> Debug for QemuExecutor<'a, H, OT, QT, S>
|
impl<'a, H, OT, QT, S> Debug for QemuExecutor<'a, H, OT, QT, S>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
S: State,
|
S: State + HasExecutions,
|
||||||
OT: ObserversTuple<S> + Debug,
|
OT: ObserversTuple<S> + Debug,
|
||||||
QT: QemuHelperTuple<S> + Debug,
|
QT: QemuHelperTuple<S> + Debug,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("QemuExecutor")
|
f.debug_struct("QemuExecutor")
|
||||||
.field("hooks", &self.hooks)
|
.field("hooks", &self.state.hooks)
|
||||||
.field("inner", &self.inner)
|
.field("inner", &self.inner)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
@ -68,17 +77,19 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[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,
|
signal: Signal,
|
||||||
info: &mut siginfo_t,
|
info: &'a mut siginfo_t,
|
||||||
mut context: Option<&mut ucontext_t>,
|
mut context: Option<&'a mut ucontext_t>,
|
||||||
_data: &mut InProcessExecutorHandlerData,
|
_data: &'a mut InProcessExecutorHandlerData,
|
||||||
) where
|
) where
|
||||||
E: Executor<EM, Z> + HasObservers,
|
E: Executor<EM, Z> + HasObservers,
|
||||||
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
|
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
|
||||||
OF: Feedback<E::State>,
|
OF: Feedback<E::State>,
|
||||||
E::State: HasExecutions + HasSolutions + HasCorpus,
|
E::State: HasExecutions + HasSolutions + HasCorpus,
|
||||||
Z: HasObjective<Objective = OF, State = E::State>,
|
Z: HasObjective<Objective = OF, State = E::State>,
|
||||||
|
QT: QemuHelperTuple<S> + Debug + 'a,
|
||||||
|
S: State + HasExecutions + 'a,
|
||||||
{
|
{
|
||||||
let puc = match &mut context {
|
let puc = match &mut context {
|
||||||
Some(v) => ptr::from_mut::<ucontext_t>(*v) as *mut c_void,
|
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")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
static mut BREAK_ON_TMOUT: bool = false;
|
pub(crate) static mut BREAK_ON_TMOUT: bool = false;
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -96,11 +107,11 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[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,
|
signal: Signal,
|
||||||
info: &mut siginfo_t,
|
info: &'a mut siginfo_t,
|
||||||
context: Option<&mut ucontext_t>,
|
context: Option<&'a mut ucontext_t>,
|
||||||
data: &mut InProcessExecutorHandlerData,
|
data: &'a mut InProcessExecutorHandlerData,
|
||||||
) where
|
) where
|
||||||
E: Executor<EM, Z> + HasObservers + HasInProcessHooks,
|
E: Executor<EM, Z> + HasObservers + HasInProcessHooks,
|
||||||
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
|
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>
|
impl<'a, H, OT, QT, S> QemuExecutor<'a, H, OT, QT, S>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
S: State,
|
S: State + HasExecutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S> + Debug,
|
||||||
{
|
{
|
||||||
pub fn new<EM, OF, Z>(
|
pub fn new<EM, OF, Z>(
|
||||||
hooks: &'a mut QemuHooks<QT, S>,
|
hooks: &'a mut QemuHooks<QT, S>,
|
||||||
@ -142,40 +202,25 @@ where
|
|||||||
let mut inner = InProcessExecutor::with_timeout(
|
let mut inner = InProcessExecutor::with_timeout(
|
||||||
harness_fn, observers, fuzzer, state, event_mgr, timeout,
|
harness_fn, observers, fuzzer, state, event_mgr, timeout,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
{
|
{
|
||||||
inner.inprocess_hooks_mut().crash_handler =
|
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;
|
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")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
{
|
{
|
||||||
inner.inprocess_hooks_mut().timeout_handler =
|
inner.inprocess_hooks_mut().timeout_handler =
|
||||||
inproc_qemu_timeout_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z>
|
inproc_qemu_timeout_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z>
|
||||||
as *const c_void;
|
as *const c_void;
|
||||||
}
|
}
|
||||||
Ok(Self {
|
|
||||||
first_exec: true,
|
let state =
|
||||||
hooks,
|
QemuExecutorState::new::<InProcessExecutor<'a, H, OT, S>, EM, OF, OT, Z>(hooks)?;
|
||||||
inner,
|
|
||||||
})
|
Ok(Self { inner, state })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner(&self) -> &InProcessExecutor<'a, H, OT, S> {
|
pub fn inner(&self) -> &InProcessExecutor<'a, H, OT, S> {
|
||||||
@ -194,26 +239,65 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn hooks(&self) -> &QemuHooks<QT, S> {
|
pub fn hooks(&self) -> &QemuHooks<QT, S> {
|
||||||
self.hooks
|
self.state.hooks()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
|
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
|
||||||
self.hooks
|
self.state.hooks_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emulator(&self) -> &Emulator {
|
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
|
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,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
S: State + HasExecutions,
|
S: State + HasExecutions + HasCorpus + HasSolutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
OF: Feedback<S>,
|
||||||
Z: UsesState<State = S>,
|
QT: QemuHelperTuple<S> + Debug,
|
||||||
|
Z: HasObjective<Objective = OF, State = S>,
|
||||||
{
|
{
|
||||||
fn run_target(
|
fn run_target(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -223,15 +307,11 @@ where
|
|||||||
input: &Self::Input,
|
input: &Self::Input,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> Result<ExitKind, Error> {
|
||||||
let emu = Emulator::get().unwrap();
|
let emu = Emulator::get().unwrap();
|
||||||
if self.first_exec {
|
self.state.pre_exec::<Self, EM, OF, Z>(input, &emu);
|
||||||
self.hooks.helpers().first_exec_all(self.hooks);
|
|
||||||
self.first_exec = false;
|
|
||||||
}
|
|
||||||
self.hooks.helpers_mut().pre_exec_all(&emu, input);
|
|
||||||
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
||||||
self.hooks.helpers_mut().post_exec_all(
|
self.state.post_exec::<Self, EM, OT, OF, Z>(
|
||||||
&emu,
|
|
||||||
input,
|
input,
|
||||||
|
&emu,
|
||||||
self.inner.observers_mut(),
|
self.inner.observers_mut(),
|
||||||
&mut exit_kind,
|
&mut exit_kind,
|
||||||
);
|
);
|
||||||
@ -244,7 +324,7 @@ where
|
|||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
S: State,
|
S: State + HasExecutions,
|
||||||
{
|
{
|
||||||
type State = S;
|
type State = S;
|
||||||
}
|
}
|
||||||
@ -254,7 +334,7 @@ where
|
|||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
S: State,
|
S: State + HasExecutions,
|
||||||
{
|
{
|
||||||
type Observers = OT;
|
type Observers = OT;
|
||||||
}
|
}
|
||||||
@ -262,7 +342,7 @@ where
|
|||||||
impl<'a, H, OT, QT, S> HasObservers for QemuExecutor<'a, H, OT, QT, S>
|
impl<'a, H, OT, QT, S> HasObservers for QemuExecutor<'a, H, OT, QT, S>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
S: State,
|
S: State + HasExecutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
@ -278,46 +358,53 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[cfg(feature = "fork")]
|
||||||
pub struct QemuForkExecutor<'a, H, OT, QT, S, SP>
|
pub struct QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
S: UsesInput,
|
S: State + HasExecutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
first_exec: bool,
|
inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>,
|
||||||
hooks: &'a mut QemuHooks<QT, S>,
|
state: QemuExecutorState<'a, QT, S>,
|
||||||
inner: InProcessForkExecutor<'a, H, OT, S, SP>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
S: UsesInput,
|
S: State + HasExecutions,
|
||||||
OT: ObserversTuple<S> + Debug,
|
OT: ObserversTuple<S> + Debug,
|
||||||
QT: QemuHelperTuple<S> + Debug,
|
QT: QemuHelperTuple<S> + Debug,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("QemuForkExecutor")
|
f.debug_struct("QemuForkExecutor")
|
||||||
.field("hooks", &self.hooks)
|
.field("hooks", &self.state.hooks)
|
||||||
.field("inner", &self.inner)
|
.field("inner", &self.inner)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
S: State,
|
S: State + HasExecutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
SP: ShMemProvider,
|
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>,
|
hooks: &'a mut QemuHooks<QT, S>,
|
||||||
harness_fn: &'a mut H,
|
harness_fn: &'a mut H,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
@ -326,18 +413,10 @@ where
|
|||||||
event_mgr: &mut EM,
|
event_mgr: &mut EM,
|
||||||
shmem_provider: SP,
|
shmem_provider: SP,
|
||||||
timeout: core::time::Duration,
|
timeout: core::time::Duration,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error> {
|
||||||
where
|
|
||||||
EM: EventFirer<State = S> + EventRestarter,
|
|
||||||
OF: Feedback<S>,
|
|
||||||
S: HasSolutions,
|
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
|
||||||
{
|
|
||||||
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");
|
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 {
|
Ok(Self {
|
||||||
first_exec: true,
|
|
||||||
hooks,
|
|
||||||
inner: InProcessForkExecutor::new(
|
inner: InProcessForkExecutor::new(
|
||||||
harness_fn,
|
harness_fn,
|
||||||
observers,
|
observers,
|
||||||
@ -347,40 +426,46 @@ where
|
|||||||
timeout,
|
timeout,
|
||||||
shmem_provider,
|
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
|
&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
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hooks(&self) -> &QemuHooks<QT, S> {
|
pub fn hooks(&self) -> &QemuHooks<QT, S> {
|
||||||
self.hooks
|
self.state.hooks
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
|
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
|
||||||
self.hooks
|
self.state.hooks
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emulator(&self) -> &Emulator {
|
pub fn emulator(&self) -> &Emulator {
|
||||||
self.hooks.emulator()
|
self.state.hooks.emulator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[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
|
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,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
S: State + HasMetadata + HasExecutions + HasLastReportTime,
|
S: State + HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + HasSolutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S> + Debug,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
Z: UsesState<State = S>,
|
OF: Feedback<S>,
|
||||||
|
Z: HasObjective<Objective = OF, State = S>,
|
||||||
{
|
{
|
||||||
fn run_target(
|
fn run_target(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -390,13 +475,13 @@ where
|
|||||||
input: &Self::Input,
|
input: &Self::Input,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> Result<ExitKind, Error> {
|
||||||
let emu = Emulator::get().unwrap();
|
let emu = Emulator::get().unwrap();
|
||||||
if self.first_exec {
|
if self.state.first_exec {
|
||||||
self.hooks.helpers().first_exec_all(self.hooks);
|
self.state.hooks.helpers().first_exec_all(self.state.hooks);
|
||||||
self.first_exec = false;
|
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)?;
|
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,
|
&emu,
|
||||||
input,
|
input,
|
||||||
self.inner.observers_mut(),
|
self.inner.observers_mut(),
|
||||||
@ -407,37 +492,43 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
S: State,
|
S: State + HasExecutions,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
type Observers = OT;
|
type Observers = OT;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
S: State,
|
S: State + HasExecutions,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
type State = S;
|
type State = S;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[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
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&S::Input) -> ExitKind,
|
||||||
S: State,
|
S: State + HasExecutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn observers(&self) -> &OT {
|
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