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:
Romain Malmain 2024-03-05 19:28:46 +01:00 committed by GitHub
parent 44b0b0ad82
commit 55a300d508
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 1725 additions and 322 deletions

View File

@ -1,4 +1,4 @@
use core::ptr::addr_of_mut;
use core::{fmt::Debug, ptr::addr_of_mut};
use std::{marker::PhantomData, process};
#[cfg(feature = "simplemgr")]
@ -70,7 +70,7 @@ pub struct Instance<'a, M: Monitor> {
impl<'a, M: Monitor> Instance<'a, M> {
pub fn run<QT>(&mut self, helpers: QT, state: Option<ClientState>) -> Result<(), Error>
where
QT: QemuHelperTuple<ClientState>,
QT: QemuHelperTuple<ClientState> + Debug,
{
let mut hooks = QemuHooks::new(self.emu.clone(), helpers);

View File

@ -41,7 +41,10 @@ use crate::{
Error,
};
/// The process executor simply calls a target function, as mutable reference to a closure
/// A version of `InProcessExecutor` with a state accessible from the harness.
pub mod stateful;
/// The process executor simply calls a target function, as mutable reference to a closure.
pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor<H, &'a mut H, (), OT, S>;
/// The inprocess executor that allows hooks
@ -56,6 +59,20 @@ pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor<
S,
>;
/// The internal state of `GenericInProcessExecutor`.
pub struct GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State,
{
/// The observers, observing each run
observers: OT,
// Crash and timeout hah
hooks: (InProcessHooks, HT),
phantom: PhantomData<S>,
}
/// The inmem executor simply calls a target function, then returns afterwards.
#[allow(dead_code)]
pub struct GenericInProcessExecutor<H, HB, HT, OT, S>
@ -66,13 +83,22 @@ where
OT: ObserversTuple<S>,
S: State,
{
/// The harness function, being executed for each fuzzing loop execution
harness_fn: HB,
/// The observers, observing each run
observers: OT,
// Crash and timeout hah
hooks: (InProcessHooks, HT),
phantom: PhantomData<(S, *const H)>,
inner: GenericInProcessExecutorInner<HT, OT, S>,
phantom: PhantomData<(*const H, HB)>,
}
impl<HT, OT, S> Debug for GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple,
OT: ObserversTuple<S> + Debug,
S: State,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("GenericInProcessExecutorState")
.field("observers", &self.observers)
.finish_non_exhaustive()
}
}
impl<H, HB, HT, OT, S> Debug for GenericInProcessExecutor<H, HB, HT, OT, S>
@ -85,15 +111,24 @@ where
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("GenericInProcessExecutor")
.field("inner", &self.inner)
.field("harness_fn", &"<fn>")
.field("observers", &self.observers)
.finish_non_exhaustive()
}
}
impl<HT, OT, S> UsesState for GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State,
{
type State = S;
}
impl<H, HB, HT, OT, S> UsesState for GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
@ -102,9 +137,18 @@ where
type State = S;
}
impl<HT, OT, S> UsesObservers for GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State,
{
type Observers = OT;
}
impl<H, HB, HT, OT, S> UsesObservers for GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
@ -131,21 +175,19 @@ where
input: &Self::Input,
) -> Result<ExitKind, Error> {
*state.executions_mut() += 1;
self.enter_target(fuzzer, state, mgr, input);
self.hooks.pre_exec_all(fuzzer, state, mgr, input);
self.inner.enter_target(fuzzer, state, mgr, input);
self.inner.hooks.pre_exec_all(fuzzer, state, mgr, input);
let ret = (self.harness_fn.borrow_mut())(input);
self.hooks.post_exec_all(fuzzer, state, mgr, input);
self.leave_target(fuzzer, state, mgr, input);
self.inner.hooks.post_exec_all(fuzzer, state, mgr, input);
self.inner.leave_target(fuzzer, state, mgr, input);
Ok(ret)
}
}
impl<H, HB, HT, OT, S> HasObservers for GenericInProcessExecutor<H, HB, HT, OT, S>
impl<HT, OT, S> HasObservers for GenericInProcessExecutorInner<HT, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State,
@ -160,13 +202,31 @@ where
&mut self.observers
}
}
impl<H, HB, HT, OT, S> GenericInProcessExecutor<H, HB, HT, OT, S>
impl<H, HB, HT, OT, S> HasObservers for GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State,
{
#[inline]
fn observers(&self) -> &OT {
self.inner.observers()
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
self.inner.observers_mut()
}
}
impl<HT, OT, S> GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State,
{
/// This function marks the boundary between the fuzzer and the target
#[inline]
@ -223,30 +283,27 @@ where
}
}
impl<'a, H, OT, S> InProcessExecutor<'a, H, OT, S>
impl<OT, S> GenericInProcessExecutorInner<(), OT, S>
where
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: HasExecutions + HasSolutions + HasCorpus + State,
{
/// Create a new in mem executor with the default timeout (5 sec)
pub fn new<EM, OF, Z>(
harness_fn: &'a mut H,
pub fn new<E, EM, OF, Z>(
observers: OT,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
) -> Result<Self, Error>
where
Self: Executor<EM, Z, State = S>,
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
Self::with_timeout_generic(
Self::with_timeout_generic::<E, EM, OF, Z>(
tuple_list!(),
harness_fn,
observers,
fuzzer,
state,
@ -258,8 +315,7 @@ where
/// Create a new in mem executor with the default timeout and use batch mode (5 sec)
/// Do not use batched mode timeouts with cmplog cores. It is not supported
#[cfg(all(feature = "std", target_os = "linux"))]
pub fn batched_timeouts<EM, OF, Z>(
harness_fn: &'a mut H,
pub fn batched_timeouts<E, EM, OF, Z>(
observers: OT,
fuzzer: &mut Z,
state: &mut S,
@ -267,15 +323,14 @@ where
exec_tmout: Duration,
) -> Result<Self, Error>
where
Self: Executor<EM, Z, State = S>,
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
let mut me = Self::with_timeout_generic(
let mut me = Self::with_timeout_generic::<E, EM, OF, Z>(
tuple_list!(),
harness_fn,
observers,
fuzzer,
state,
@ -293,8 +348,7 @@ where
/// * `harness_fn` - the harness, executing the function
/// * `observers` - the observers observing the target during execution
/// This may return an error on unix, if signal handler setup fails
pub fn with_timeout<EM, OF, Z>(
harness_fn: &'a mut H,
pub fn with_timeout<E, EM, OF, Z>(
observers: OT,
_fuzzer: &mut Z,
state: &mut S,
@ -302,13 +356,13 @@ where
timeout: Duration,
) -> Result<Self, Error>
where
Self: Executor<EM, Z, State = S>,
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
let default = InProcessHooks::new::<Self, EM, OF, Z>(timeout)?;
let default = InProcessHooks::new::<E, EM, OF, Z>(timeout)?;
let mut hooks = tuple_list!(default).merge(tuple_list!());
hooks.init_all::<Self, S>(state);
@ -336,7 +390,6 @@ where
}
Ok(Self {
harness_fn,
observers,
hooks,
phantom: PhantomData,
@ -344,13 +397,231 @@ where
}
}
impl<H, HB, HT, OT, S> GenericInProcessExecutor<H, HB, HT, OT, S>
impl<'a, H, OT, S> InProcessExecutor<'a, H, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: HasExecutions + HasSolutions + HasCorpus + State,
{
/// Create a new in mem executor with the default timeout (5 sec)
pub fn new<EM, OF, Z>(
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
) -> Result<Self, Error>
where
Self: Executor<EM, Z, State = S> + HasObservers,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
Self::with_timeout_generic(
tuple_list!(),
harness_fn,
observers,
fuzzer,
state,
event_mgr,
Duration::from_millis(5000),
)
}
/// Create a new in mem executor with the default timeout and use batch mode(5 sec)
#[cfg(all(feature = "std", target_os = "linux"))]
pub fn batched_timeouts<EM, OF, Z>(
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
exec_tmout: Duration,
) -> Result<Self, Error>
where
Self: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
let inner = GenericInProcessExecutorInner::batched_timeouts::<Self, EM, OF, Z>(
observers, fuzzer, state, event_mgr, exec_tmout,
)?;
Ok(Self {
harness_fn,
inner,
phantom: PhantomData,
})
}
/// Create a new in mem executor.
/// Caution: crash and restart in one of them will lead to odd behavior if multiple are used,
/// depending on different corpus or state.
/// * `user_hooks` - the hooks run before and after the harness's execution
/// * `harness_fn` - the harness, executing the function
/// * `observers` - the observers observing the target during execution
/// This may return an error on unix, if signal handler setup fails
pub fn with_timeout<EM, OF, Z>(
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
timeout: Duration,
) -> Result<Self, Error>
where
Self: Executor<EM, Z, State = S> + HasObservers,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
let inner = GenericInProcessExecutorInner::with_timeout::<Self, EM, OF, Z>(
observers, fuzzer, state, event_mgr, timeout,
)?;
Ok(Self {
harness_fn,
inner,
phantom: PhantomData,
})
}
}
impl<HT, OT, S> GenericInProcessExecutorInner<HT, OT, S>
where
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: HasExecutions + HasSolutions + HasCorpus + State,
{
/// Create a new in mem executor with the default timeout (5 sec)
pub fn generic<E, EM, OF, Z>(
user_hooks: HT,
observers: OT,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
) -> Result<Self, Error>
where
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
Self::with_timeout_generic::<E, EM, OF, Z>(
user_hooks,
observers,
fuzzer,
state,
event_mgr,
Duration::from_millis(5000),
)
}
/// Create a new in mem executor with the default timeout and use batch mode(5 sec)
#[cfg(all(feature = "std", target_os = "linux"))]
pub fn batched_timeout_generic<E, EM, OF, Z>(
user_hooks: HT,
observers: OT,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
exec_tmout: Duration,
) -> Result<Self, Error>
where
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
let mut me = Self::with_timeout_generic::<E, EM, OF, Z>(
user_hooks, observers, fuzzer, state, event_mgr, exec_tmout,
)?;
me.hooks_mut().0.timer_mut().batch_mode = true;
Ok(me)
}
/// Create a new in mem executor.
/// Caution: crash and restart in one of them will lead to odd behavior if multiple are used,
/// depending on different corpus or state.
/// * `user_hooks` - the hooks run before and after the harness's execution
/// * `harness_fn` - the harness, executing the function
/// * `observers` - the observers observing the target during execution
/// This may return an error on unix, if signal handler setup fails
pub fn with_timeout_generic<E, EM, OF, Z>(
user_hooks: HT,
observers: OT,
_fuzzer: &mut Z,
state: &mut S,
_event_mgr: &mut EM,
timeout: Duration,
) -> Result<Self, Error>
where
E: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
let default = InProcessHooks::new::<E, EM, OF, Z>(timeout)?;
let mut hooks = tuple_list!(default).merge(user_hooks);
hooks.init_all::<Self, S>(state);
#[cfg(windows)]
// Some initialization necessary for windows.
unsafe {
/*
See https://github.com/AFLplusplus/LibAFL/pull/403
This one reserves certain amount of memory for the stack.
If stack overflow happens during fuzzing on windows, the program is transferred to our exception handler for windows.
However, if we run out of the stack memory again in this exception handler, we'll crash with STATUS_ACCESS_VIOLATION.
We need this API call because with the llmp_compression
feature enabled, the exception handler uses a lot of stack memory (in the compression lib code) on release build.
As far as I have observed, the compression uses around 0x10000 bytes, but for safety let's just reserve 0x20000 bytes for our exception handlers.
This number 0x20000 could vary depending on the compilers optimization for future compression library changes.
*/
let mut stack_reserved = 0x20000;
SetThreadStackGuarantee(&mut stack_reserved)?;
}
#[cfg(all(feature = "std", windows))]
{
// set timeout for the handler
*hooks.0.millis_sec_mut() = timeout.as_millis() as i64;
}
Ok(Self {
observers,
hooks,
phantom: PhantomData,
})
}
/// The inprocess handlers
#[inline]
pub fn hooks(&self) -> &(InProcessHooks, HT) {
&self.hooks
}
/// The inprocess handlers (mutable)
#[inline]
pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) {
&mut self.hooks
}
}
impl<H, HB, HT, OT, S> GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State + HasExecutions + HasSolutions + HasCorpus,
{
/// Create a new in mem executor with the default timeout (5 sec)
pub fn generic<EM, OF, Z>(
@ -362,7 +633,7 @@ where
event_mgr: &mut EM,
) -> Result<Self, Error>
where
Self: Executor<EM, Z, State = S>,
Self: Executor<EM, Z, State = S> + HasObservers,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
@ -391,17 +662,21 @@ where
exec_tmout: Duration,
) -> Result<Self, Error>
where
Self: Executor<EM, Z, State = S>,
Self: Executor<EM, Z, State = S> + HasObservers,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
let mut me = Self::with_timeout_generic(
user_hooks, harness_fn, observers, fuzzer, state, event_mgr, exec_tmout,
let inner = GenericInProcessExecutorInner::batched_timeout_generic::<Self, EM, OF, Z>(
user_hooks, observers, fuzzer, state, event_mgr, exec_tmout,
)?;
me.hooks_mut().0.timer_mut().batch_mode = true;
Ok(me)
Ok(Self {
harness_fn,
inner,
phantom: PhantomData,
})
}
/// Create a new in mem executor.
@ -415,49 +690,25 @@ where
user_hooks: HT,
harness_fn: HB,
observers: OT,
_fuzzer: &mut Z,
fuzzer: &mut Z,
state: &mut S,
_event_mgr: &mut EM,
event_mgr: &mut EM,
timeout: Duration,
) -> Result<Self, Error>
where
Self: Executor<EM, Z, State = S>,
Self: Executor<EM, Z, State = S> + HasObservers,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: State,
Z: HasObjective<Objective = OF, State = S>,
{
let default = InProcessHooks::new::<Self, EM, OF, Z>(timeout)?;
let mut hooks = tuple_list!(default).merge(user_hooks);
hooks.init_all::<Self, S>(state);
#[cfg(windows)]
// Some initialization necessary for windows.
unsafe {
/*
See https://github.com/AFLplusplus/LibAFL/pull/403
This one reserves certain amount of memory for the stack.
If stack overflow happens during fuzzing on windows, the program is transferred to our exception handler for windows.
However, if we run out of the stack memory again in this exception handler, we'll crash with STATUS_ACCESS_VIOLATION.
We need this API call because with the llmp_compression
feature enabled, the exception handler uses a lot of stack memory (in the compression lib code) on release build.
As far as I have observed, the compression uses around 0x10000 bytes, but for safety let's just reserve 0x20000 bytes for our exception handlers.
This number 0x20000 could vary depending on the compilers optimization for future compression library changes.
*/
let mut stack_reserved = 0x20000;
SetThreadStackGuarantee(&mut stack_reserved)?;
}
#[cfg(all(feature = "std", windows))]
{
// set timeout for the handler
*hooks.0.millis_sec_mut() = timeout.as_millis() as i64;
}
let inner = GenericInProcessExecutorInner::with_timeout_generic::<Self, EM, OF, Z>(
user_hooks, observers, fuzzer, state, event_mgr, timeout,
)?;
Ok(Self {
harness_fn,
observers,
hooks,
inner,
phantom: PhantomData,
})
}
@ -477,13 +728,13 @@ where
/// The inprocess handlers
#[inline]
pub fn hooks(&self) -> &(InProcessHooks, HT) {
&self.hooks
self.inner.hooks()
}
/// The inprocess handlers (mutable)
#[inline]
pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) {
&mut self.hooks
self.inner.hooks_mut()
}
}
@ -496,10 +747,8 @@ pub trait HasInProcessHooks {
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks;
}
impl<H, HB, HT, OT, S> HasInProcessHooks for GenericInProcessExecutor<H, HB, HT, OT, S>
impl<HT, OT, S> HasInProcessHooks for GenericInProcessExecutorInner<HT, OT, S>
where
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State + HasExecutions + HasSolutions + HasCorpus,
@ -517,6 +766,27 @@ where
}
}
impl<H, HB, HT, OT, S> HasInProcessHooks for GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State + HasExecutions + HasSolutions + HasCorpus,
{
/// the timeout handler
#[inline]
fn inprocess_hooks(&self) -> &InProcessHooks {
self.inner.inprocess_hooks()
}
/// the timeout handler
#[inline]
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks {
self.inner.inprocess_hooks_mut()
}
}
#[inline]
#[allow(clippy::too_many_arguments)]
/// Save state if it is an objective

View 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()
}
}

View File

@ -16,7 +16,7 @@ use libafl_bolts::{
use libc::siginfo_t;
use nix::{
sys::wait::{waitpid, WaitStatus},
unistd::{fork, ForkResult},
unistd::{fork, ForkResult, Pid},
};
use super::hooks::ExecutorHooksTuple;
@ -35,6 +35,7 @@ use crate::{
state::{HasExecutions, HasSolutions, State, UsesState},
Error,
};
/// The signature of the crash handler function
pub(crate) type ForkHandlerFuncPtr = unsafe fn(
Signal,
@ -46,20 +47,27 @@ pub(crate) type ForkHandlerFuncPtr = unsafe fn(
#[cfg(all(unix, not(target_os = "linux")))]
use crate::executors::hooks::timer::{setitimer, Itimerval, Timeval, ITIMER_REAL};
/// The `InProcessForkExecutor` with no user hooks
pub type InProcessForkExecutor<'a, H, OT, S, SP> =
GenericInProcessForkExecutor<'a, H, (), OT, S, SP>;
/// A version of `InProcessForkExecutor` with a state accessible from the harness.
pub mod stateful;
impl<'a, H, OT, S, SP> InProcessForkExecutor<'a, H, OT, S, SP>
/// The `InProcessForkExecutor` with no user hooks
pub type InProcessForkExecutor<'a, H, OT, S, SP, EM, Z> =
GenericInProcessForkExecutor<'a, H, (), OT, S, SP, EM, Z>;
impl<'a, H, OT, S, SP, EM, Z, OF> InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
S: State,
OT: ObserversTuple<S>,
SP: ShMemProvider,
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
#[allow(clippy::too_many_arguments)]
/// The constructor for `InProcessForkExecutor`
pub fn new<EM, OF, Z>(
pub fn new(
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
@ -67,13 +75,7 @@ where
event_mgr: &mut EM,
timeout: Duration,
shmem_provider: SP,
) -> Result<Self, Error>
where
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
) -> Result<Self, Error> {
Self::with_hooks(
tuple_list!(),
harness_fn,
@ -87,37 +89,54 @@ where
}
}
/// [`GenericInProcessForkExecutor`] is an executor that forks the current process before each execution.
pub struct GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
/// Inner state of GenericInProcessExecutor-like structures.
pub struct GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
HT: ExecutorHooksTuple,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
hooks: (InChildProcessHooks, HT),
harness_fn: &'a mut H,
shmem_provider: SP,
observers: OT,
#[cfg(target_os = "linux")]
itimerspec: libc::itimerspec,
#[cfg(all(unix, not(target_os = "linux")))]
itimerval: Itimerval,
phantom: PhantomData<S>,
phantom: PhantomData<(S, EM, Z)>,
}
impl<'a, H, HT, OT, S, SP> Debug for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
/// [`GenericInProcessForkExecutor`] is an executor that forks the current process before each execution.
pub struct GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S> + Debug,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
HT: ExecutorHooksTuple,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
harness_fn: &'a mut H,
inner: GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>,
}
impl<HT, OT, S, SP, EM, Z> Debug for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
where
OT: ObserversTuple<S> + Debug,
S: UsesInput,
SP: ShMemProvider,
HT: ExecutorHooksTuple + Debug,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
#[cfg(target_os = "linux")]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("GenericInProcessForkExecutor")
f.debug_struct("GenericInProcessForkExecutorInner")
.field("hooks", &self.hooks)
.field("observers", &self.observers)
.field("shmem_provider", &self.shmem_provider)
.field("itimerspec", &self.itimerspec)
@ -128,7 +147,7 @@ where
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
#[cfg(not(target_os = "linux"))]
return f
.debug_struct("GenericInProcessForkExecutor")
.debug_struct("GenericInProcessForkExecutorInner")
.field("observers", &self.observers)
.field("shmem_provider", &self.shmem_provider)
.field("itimerval", &self.itimerval)
@ -136,26 +155,163 @@ where
}
}
impl<'a, H, HT, OT, S, SP> UsesState for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
impl<'a, H, HT, OT, S, SP, EM, Z> Debug
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S> + Debug,
S: UsesInput,
SP: ShMemProvider,
HT: ExecutorHooksTuple + Debug,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
#[cfg(target_os = "linux")]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("GenericInProcessForkExecutor")
.field("GenericInProcessForkExecutorInner", &self.inner)
.finish()
}
#[cfg(not(target_os = "linux"))]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
#[cfg(not(target_os = "linux"))]
return f
.debug_struct("GenericInProcessForkExecutor")
.field("GenericInProcessForkExecutorInner", &self.inner)
.finish();
}
}
impl<HT, OT, S, SP, EM, Z> UsesState for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: State,
SP: ShMemProvider,
HT: ExecutorHooksTuple,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
type State = S;
}
impl<'a, EM, H, HT, OT, S, SP, Z> Executor<EM, Z>
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
impl<'a, H, HT, OT, S, SP, EM, Z> UsesState
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
where
EM: UsesState<State = S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: State,
SP: ShMemProvider,
HT: ExecutorHooksTuple,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
type State = S;
}
impl<EM, HT, OT, S, SP, Z> GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
where
OT: ObserversTuple<S> + Debug,
S: State + UsesInput,
SP: ShMemProvider,
HT: ExecutorHooksTuple,
EM: EventFirer<State = S> + EventRestarter<State = S>,
Z: UsesState<State = S>,
{
unsafe fn pre_run_target_child(
&mut self,
fuzzer: &mut Z,
state: &mut <GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z> as UsesState>::State,
mgr: &mut EM,
input: &<GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z> as UsesInput>::Input,
) -> Result<(), Error> {
self.shmem_provider.post_fork(true)?;
self.enter_target(fuzzer, state, mgr, input);
self.hooks.pre_exec_all(fuzzer, state, mgr, input);
self.observers
.pre_exec_child_all(state, input)
.expect("Failed to run post_exec on observers");
#[cfg(target_os = "linux")]
{
let mut timerid: libc::timer_t = null_mut();
// creates a new per-process interval timer
// we can't do this from the parent, timerid is unique to each process.
libc::timer_create(libc::CLOCK_MONOTONIC, null_mut(), addr_of_mut!(timerid));
// log::info!("Set timer! {:#?} {timerid:#?}", self.itimerspec);
let _: i32 = libc::timer_settime(timerid, 0, addr_of_mut!(self.itimerspec), null_mut());
}
#[cfg(not(target_os = "linux"))]
{
setitimer(ITIMER_REAL, &mut self.itimerval, null_mut());
}
// log::trace!("{v:#?} {}", nix::errno::errno());
Ok(())
}
unsafe fn post_run_target_child(
&mut self,
fuzzer: &mut Z,
state: &mut <GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z> as UsesState>::State,
mgr: &mut EM,
input: &<GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z> as UsesInput>::Input,
) {
self.observers
.post_exec_child_all(state, input, &ExitKind::Ok)
.expect("Failed to run post_exec on observers");
self.hooks.post_exec_all(fuzzer, state, mgr, input);
self.leave_target(fuzzer, state, mgr, input);
libc::_exit(0);
}
fn parent(&mut self, child: Pid) -> Result<ExitKind, Error> {
// log::trace!("from parent {} child is {}", std::process::id(), child);
self.shmem_provider.post_fork(false)?;
let res = waitpid(child, None)?;
log::trace!("{res:#?}");
match res {
WaitStatus::Signaled(_, signal, _) => match signal {
nix::sys::signal::Signal::SIGALRM | nix::sys::signal::Signal::SIGUSR2 => {
Ok(ExitKind::Timeout)
}
_ => Ok(ExitKind::Crash),
},
WaitStatus::Exited(_, code) => {
if code > 128 && code < 160 {
// Signal exit codes
let signal = code - 128;
if signal == Signal::SigAlarm as libc::c_int
|| signal == Signal::SigUser2 as libc::c_int
{
Ok(ExitKind::Timeout)
} else {
Ok(ExitKind::Crash)
}
} else {
Ok(ExitKind::Ok)
}
}
_ => Ok(ExitKind::Ok),
}
}
}
impl<'a, EM, H, HT, OT, S, SP, Z> Executor<EM, Z>
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S> + Debug,
S: State + HasExecutions,
SP: ShMemProvider,
HT: ExecutorHooksTuple,
EM: EventFirer<State = S> + EventRestarter<State = S>,
Z: UsesState<State = S>,
{
#[allow(unreachable_code)]
@ -170,86 +326,18 @@ where
*state.executions_mut() += 1;
unsafe {
self.shmem_provider.pre_fork()?;
self.inner.shmem_provider.pre_fork()?;
match fork() {
Ok(ForkResult::Child) => {
// Child
self.shmem_provider.post_fork(true)?;
self.enter_target(fuzzer, state, mgr, input);
self.hooks.pre_exec_all(fuzzer, state, mgr, input);
self.observers
.pre_exec_child_all(state, input)
.expect("Failed to run post_exec on observers");
#[cfg(target_os = "linux")]
{
let mut timerid: libc::timer_t = null_mut();
// creates a new per-process interval timer
// we can't do this from the parent, timerid is unique to each process.
libc::timer_create(
libc::CLOCK_MONOTONIC,
null_mut(),
addr_of_mut!(timerid),
);
// log::info!("Set timer! {:#?} {timerid:#?}", self.itimerspec);
let _: i32 = libc::timer_settime(
timerid,
0,
addr_of_mut!(self.itimerspec),
null_mut(),
);
}
#[cfg(not(target_os = "linux"))]
{
setitimer(ITIMER_REAL, &mut self.itimerval, null_mut());
}
// log::trace!("{v:#?} {}", nix::errno::errno());
self.inner.pre_run_target_child(fuzzer, state, mgr, input)?;
(self.harness_fn)(input);
self.observers
.post_exec_child_all(state, input, &ExitKind::Ok)
.expect("Failed to run post_exec on observers");
self.hooks.post_exec_all(fuzzer, state, mgr, input);
self.leave_target(fuzzer, state, mgr, input);
libc::_exit(0);
self.inner.post_run_target_child(fuzzer, state, mgr, input);
Ok(ExitKind::Ok)
}
Ok(ForkResult::Parent { child }) => {
// Parent
// log::trace!("from parent {} child is {}", std::process::id(), child);
self.shmem_provider.post_fork(false)?;
let res = waitpid(child, None)?;
log::trace!("{res:#?}");
match res {
WaitStatus::Signaled(_, signal, _) => match signal {
nix::sys::signal::Signal::SIGALRM
| nix::sys::signal::Signal::SIGUSR2 => Ok(ExitKind::Timeout),
_ => Ok(ExitKind::Crash),
},
WaitStatus::Exited(_, code) => {
if code > 128 && code < 160 {
// Signal exit codes
let signal = code - 128;
if signal == Signal::SigAlarm as libc::c_int
|| signal == Signal::SigUser2 as libc::c_int
{
Ok(ExitKind::Timeout)
} else {
Ok(ExitKind::Crash)
}
} else {
Ok(ExitKind::Ok)
}
}
_ => Ok(ExitKind::Ok),
}
self.inner.parent(child)
}
Err(e) => Err(Error::from(e)),
}
@ -257,17 +345,18 @@ where
}
}
impl<'a, H, HT, OT, S, SP> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
impl<HT, OT, S, SP, EM, Z> GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HT: ExecutorHooksTuple,
S: State,
OT: ObserversTuple<S>,
SP: ShMemProvider,
EM: EventFirer<State = S> + EventRestarter<State = S>,
Z: UsesState<State = S>,
{
#[inline]
/// This function marks the boundary between the fuzzer and the target.
pub fn enter_target<EM, Z>(
pub fn enter_target(
&mut self,
_fuzzer: &mut Z,
state: &mut <Self as UsesState>::State,
@ -294,7 +383,7 @@ where
#[inline]
/// This function marks the boundary between the fuzzer and the target.
pub fn leave_target<EM, Z>(
pub fn leave_target(
&mut self,
_fuzzer: &mut Z,
_state: &mut <Self as UsesState>::State,
@ -307,22 +396,15 @@ where
/// Creates a new [`GenericInProcessForkExecutor`] with custom hooks
#[cfg(target_os = "linux")]
#[allow(clippy::too_many_arguments)]
pub fn with_hooks<EM, OF, Z>(
pub fn with_hooks(
userhooks: HT,
harness_fn: &'a mut H,
observers: OT,
_fuzzer: &mut Z,
state: &mut S,
_event_mgr: &mut EM,
timeout: Duration,
shmem_provider: SP,
) -> Result<Self, Error>
where
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
) -> Result<Self, Error> {
let default_hooks = InChildProcessHooks::new::<Self>()?;
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
hooks.init_all::<Self, S>(state);
@ -342,7 +424,6 @@ where
};
Ok(Self {
harness_fn,
shmem_provider,
observers,
hooks,
@ -354,22 +435,15 @@ where
/// Creates a new [`GenericInProcessForkExecutor`], non linux
#[cfg(not(target_os = "linux"))]
#[allow(clippy::too_many_arguments)]
pub fn with_hooks<EM, OF, Z>(
pub fn with_hooks(
userhooks: HT,
harness_fn: &'a mut H,
observers: OT,
_fuzzer: &mut Z,
state: &mut S,
_event_mgr: &mut EM,
timeout: Duration,
shmem_provider: SP,
) -> Result<Self, Error>
where
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
) -> Result<Self, Error> {
let default_hooks = InChildProcessHooks::new::<Self>()?;
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
hooks.init_all::<Self, S>(state);
@ -389,7 +463,6 @@ where
};
Ok(Self {
harness_fn,
shmem_provider,
observers,
hooks,
@ -397,6 +470,45 @@ where
phantom: PhantomData,
})
}
}
impl<'a, H, HT, OT, S, SP, EM, Z, OF> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
SP: ShMemProvider,
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: State + HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
/// Creates a new [`GenericInProcessForkExecutor`] with custom hooks
#[allow(clippy::too_many_arguments)]
pub fn with_hooks(
userhooks: HT,
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
timeout: Duration,
shmem_provider: SP,
) -> Result<Self, Error>
where {
Ok(Self {
harness_fn,
inner: GenericInProcessForkExecutorInner::with_hooks(
userhooks,
observers,
fuzzer,
state,
event_mgr,
timeout,
shmem_provider,
)?,
})
}
/// Retrieve the harness function.
#[inline]
@ -411,24 +523,40 @@ where
}
}
impl<'a, H, HT, OT, S, SP> UsesObservers for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
impl<HT, OT, S, SP, EM, Z> UsesObservers for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
type Observers = OT;
}
impl<'a, H, HT, OT, S, SP> HasObservers for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
impl<'a, H, HT, OT, S, SP, EM, Z> UsesObservers
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HT: ExecutorHooksTuple,
OT: ObserversTuple<S>,
S: State,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
type Observers = OT;
}
impl<HT, OT, S, SP, EM, Z> HasObservers for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
where
HT: ExecutorHooksTuple,
S: State,
OT: ObserversTuple<S>,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
#[inline]
fn observers(&self) -> &OT {
@ -440,6 +568,29 @@ where
&mut self.observers
}
}
impl<'a, H, HT, OT, S, SP, EM, Z> HasObservers
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HT: ExecutorHooksTuple,
S: State,
OT: ObserversTuple<S>,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
#[inline]
fn observers(&self) -> &OT {
self.inner.observers()
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
self.inner.observers_mut()
}
}
/// signal hooks and `panic_hooks` for the child process
pub mod child_signal_handlers {
@ -539,7 +690,10 @@ pub mod child_signal_handlers {
mod tests {
use libafl_bolts::tuples::tuple_list;
use crate::{executors::ExitKind, inputs::NopInput};
use crate::{
executors::{inprocess_fork::GenericInProcessForkExecutorInner, Executor, ExitKind},
inputs::NopInput,
};
#[test]
#[cfg_attr(miri, ignore)]
@ -557,7 +711,7 @@ mod tests {
events::SimpleEventManager,
executors::{
hooks::inprocess_fork::InChildProcessHooks,
inprocess_fork::GenericInProcessForkExecutor, Executor,
inprocess_fork::GenericInProcessForkExecutor,
},
fuzzer::test::NopFuzzer,
state::NopState,
@ -590,22 +744,26 @@ mod tests {
let mut harness = |_buf: &NopInput| ExitKind::Ok;
let default = InChildProcessHooks::nop();
#[cfg(target_os = "linux")]
let mut in_process_fork_executor = GenericInProcessForkExecutor::<_, (), (), _, _> {
hooks: tuple_list!(default),
let mut in_process_fork_executor = GenericInProcessForkExecutor {
harness_fn: &mut harness,
shmem_provider: provider,
observers: tuple_list!(),
itimerspec,
phantom: PhantomData,
inner: GenericInProcessForkExecutorInner {
hooks: tuple_list!(default),
shmem_provider: provider,
observers: tuple_list!(),
itimerspec,
phantom: PhantomData,
},
};
#[cfg(not(target_os = "linux"))]
let mut in_process_fork_executor = GenericInProcessForkExecutor::<_, (), (), _, _> {
let mut in_process_fork_executor = GenericInProcessForkExecutor {
harness_fn: &mut harness,
shmem_provider: provider,
observers: tuple_list!(),
hooks: tuple_list!(default),
itimerval: itimerspec,
phantom: PhantomData,
inner: GenericInProcessForkExecutorInner {
hooks: tuple_list!(default),
shmem_provider: provider,
observers: tuple_list!(),
itimerval: itimerspec,
phantom: PhantomData,
},
};
let input = NopInput {};
let mut fuzzer = NopFuzzer::new();

View 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()
}
}

View File

@ -7,8 +7,6 @@ use core::{
time::Duration,
};
#[cfg(feature = "fork")]
use libafl::inputs::UsesInput;
#[cfg(feature = "fork")]
use libafl::{
events::EventManager,
@ -34,28 +32,39 @@ use libafl_bolts::shmem::ShMemProvider;
use crate::{emu::Emulator, helper::QemuHelperTuple, hooks::QemuHooks};
/// A version of `QemuExecutor` with a state accessible from the harness.
pub mod stateful;
pub struct QemuExecutorState<'a, QT, S>
where
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
{
hooks: &'a mut QemuHooks<QT, S>,
first_exec: bool,
}
pub struct QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&S::Input) -> ExitKind,
S: State,
S: State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
{
inner: InProcessExecutor<'a, H, OT, S>,
hooks: &'a mut QemuHooks<QT, S>,
first_exec: bool,
state: QemuExecutorState<'a, QT, S>,
}
impl<'a, H, OT, QT, S> Debug for QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&S::Input) -> ExitKind,
S: State,
S: State + HasExecutions,
OT: ObserversTuple<S> + Debug,
QT: QemuHelperTuple<S> + Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuExecutor")
.field("hooks", &self.hooks)
.field("hooks", &self.state.hooks)
.field("inner", &self.inner)
.finish()
}
@ -68,17 +77,19 @@ extern "C" {
}
#[cfg(emulation_mode = "usermode")]
pub unsafe fn inproc_qemu_crash_handler<E, EM, OF, Z>(
pub unsafe fn inproc_qemu_crash_handler<'a, E, EM, OF, Z, QT, S>(
signal: Signal,
info: &mut siginfo_t,
mut context: Option<&mut ucontext_t>,
_data: &mut InProcessExecutorHandlerData,
info: &'a mut siginfo_t,
mut context: Option<&'a mut ucontext_t>,
_data: &'a mut InProcessExecutorHandlerData,
) where
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
QT: QemuHelperTuple<S> + Debug + 'a,
S: State + HasExecutions + 'a,
{
let puc = match &mut context {
Some(v) => ptr::from_mut::<ucontext_t>(*v) as *mut c_void,
@ -88,7 +99,7 @@ pub unsafe fn inproc_qemu_crash_handler<E, EM, OF, Z>(
}
#[cfg(emulation_mode = "systemmode")]
static mut BREAK_ON_TMOUT: bool = false;
pub(crate) static mut BREAK_ON_TMOUT: bool = false;
#[cfg(emulation_mode = "systemmode")]
extern "C" {
@ -96,11 +107,11 @@ extern "C" {
}
#[cfg(emulation_mode = "systemmode")]
pub unsafe fn inproc_qemu_timeout_handler<E, EM, OF, Z>(
pub unsafe fn inproc_qemu_timeout_handler<'a, E, EM, OF, Z>(
signal: Signal,
info: &mut siginfo_t,
context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
info: &'a mut siginfo_t,
context: Option<&'a mut ucontext_t>,
data: &'a mut InProcessExecutorHandlerData,
) where
E: Executor<EM, Z> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
@ -117,12 +128,61 @@ pub unsafe fn inproc_qemu_timeout_handler<E, EM, OF, Z>(
}
}
impl<'a, QT, S> QemuExecutorState<'a, QT, S>
where
S: State + HasExecutions,
QT: QemuHelperTuple<S> + Debug,
{
pub fn new<E, EM, OF, OT, Z>(hooks: &'a mut QemuHooks<QT, S>) -> Result<Self, Error>
where
E: Executor<EM, Z, State = S> + HasInProcessHooks + HasObservers,
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
OT: ObserversTuple<S>,
S: State + HasExecutions + HasCorpus + HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
#[cfg(emulation_mode = "usermode")]
{
let handler = |hooks: &mut QemuHooks<QT, S>, host_sig| {
eprintln!("Crashed with signal {host_sig}");
unsafe {
libafl::executors::inprocess::generic_inproc_crash_handler::<E, EM, OF, Z>();
}
if let Some(cpu) = hooks.emulator().current_cpu() {
eprint!("Context:\n{}", cpu.display_context());
}
};
hooks.crash_closure(Box::new(handler));
}
Ok(QemuExecutorState {
first_exec: true,
hooks,
})
}
#[must_use]
pub fn hooks(&self) -> &QemuHooks<QT, S> {
self.hooks
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
self.hooks
}
#[must_use]
pub fn emulator(&self) -> &Emulator {
self.hooks.emulator()
}
}
impl<'a, H, OT, QT, S> QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&S::Input) -> ExitKind,
S: State,
S: State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
QT: QemuHelperTuple<S> + Debug,
{
pub fn new<EM, OF, Z>(
hooks: &'a mut QemuHooks<QT, S>,
@ -142,40 +202,25 @@ where
let mut inner = InProcessExecutor::with_timeout(
harness_fn, observers, fuzzer, state, event_mgr, timeout,
)?;
#[cfg(emulation_mode = "usermode")]
{
inner.inprocess_hooks_mut().crash_handler =
inproc_qemu_crash_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z>
inproc_qemu_crash_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z, QT, S>
as *const c_void;
let handler = |hooks: &mut QemuHooks<QT, S>, host_sig| {
eprintln!("Crashed with signal {host_sig}");
unsafe {
libafl::executors::inprocess::generic_inproc_crash_handler::<
InProcessExecutor<'a, H, OT, S>,
EM,
OF,
Z,
>();
}
if let Some(cpu) = hooks.emulator().current_cpu() {
eprint!("Context:\n{}", cpu.display_context());
}
};
hooks.crash_closure(Box::new(handler));
}
#[cfg(emulation_mode = "systemmode")]
{
inner.inprocess_hooks_mut().timeout_handler =
inproc_qemu_timeout_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z>
as *const c_void;
}
Ok(Self {
first_exec: true,
hooks,
inner,
})
let state =
QemuExecutorState::new::<InProcessExecutor<'a, H, OT, S>, EM, OF, OT, Z>(hooks)?;
Ok(Self { inner, state })
}
pub fn inner(&self) -> &InProcessExecutor<'a, H, OT, S> {
@ -194,26 +239,65 @@ where
}
pub fn hooks(&self) -> &QemuHooks<QT, S> {
self.hooks
self.state.hooks()
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
self.hooks
self.state.hooks_mut()
}
pub fn emulator(&self) -> &Emulator {
self.hooks.emulator()
self.state.emulator()
}
}
impl<'a, EM, H, OT, QT, S, Z> Executor<EM, Z> for QemuExecutor<'a, H, OT, QT, S>
impl<'a, QT, S> QemuExecutorState<'a, QT, S>
where
EM: UsesState<State = S>,
S: State + HasExecutions + HasCorpus + HasSolutions,
QT: QemuHelperTuple<S> + Debug,
{
fn pre_exec<E, EM, OF, Z>(&mut self, input: &E::Input, emu: &Emulator)
where
E: Executor<EM, Z, State = S>,
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
Z: HasObjective<Objective = OF, State = S>,
{
if self.first_exec {
self.hooks.helpers().first_exec_all(self.hooks);
self.first_exec = false;
}
self.hooks.helpers_mut().pre_exec_all(emu, input);
}
fn post_exec<E, EM, OT, OF, Z>(
&mut self,
input: &E::Input,
emu: &Emulator,
observers: &mut OT,
exit_kind: &mut ExitKind,
) where
E: Executor<EM, Z, State = S> + HasObservers,
EM: EventFirer<State = S> + EventRestarter<State = S>,
OT: ObserversTuple<S>,
OF: Feedback<S>,
Z: HasObjective<Objective = OF, State = S>,
{
self.hooks
.helpers_mut()
.post_exec_all(emu, input, observers, exit_kind);
}
}
impl<'a, EM, H, OT, OF, QT, S, Z> Executor<EM, Z> for QemuExecutor<'a, H, OT, QT, S>
where
EM: EventFirer<State = S> + EventRestarter<State = S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasExecutions,
S: State + HasExecutions + HasCorpus + HasSolutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
Z: UsesState<State = S>,
OF: Feedback<S>,
QT: QemuHelperTuple<S> + Debug,
Z: HasObjective<Objective = OF, State = S>,
{
fn run_target(
&mut self,
@ -223,15 +307,11 @@ where
input: &Self::Input,
) -> Result<ExitKind, Error> {
let emu = Emulator::get().unwrap();
if self.first_exec {
self.hooks.helpers().first_exec_all(self.hooks);
self.first_exec = false;
}
self.hooks.helpers_mut().pre_exec_all(&emu, input);
self.state.pre_exec::<Self, EM, OF, Z>(input, &emu);
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
self.hooks.helpers_mut().post_exec_all(
&emu,
self.state.post_exec::<Self, EM, OT, OF, Z>(
input,
&emu,
self.inner.observers_mut(),
&mut exit_kind,
);
@ -244,7 +324,7 @@ where
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: State,
S: State + HasExecutions,
{
type State = S;
}
@ -254,7 +334,7 @@ where
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: State,
S: State + HasExecutions,
{
type Observers = OT;
}
@ -262,7 +342,7 @@ where
impl<'a, H, OT, QT, S> HasObservers for QemuExecutor<'a, H, OT, QT, S>
where
H: FnMut(&S::Input) -> ExitKind,
S: State,
S: State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
{
@ -278,46 +358,53 @@ where
}
#[cfg(feature = "fork")]
pub struct QemuForkExecutor<'a, H, OT, QT, S, SP>
pub struct QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
S: State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
first_exec: bool,
hooks: &'a mut QemuHooks<QT, S>,
inner: InProcessForkExecutor<'a, H, OT, S, SP>,
inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>,
state: QemuExecutorState<'a, QT, S>,
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP> Debug for QemuForkExecutor<'a, H, OT, QT, S, SP>
impl<'a, H, OT, QT, S, SP, EM, Z> Debug for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind,
S: UsesInput,
S: State + HasExecutions,
OT: ObserversTuple<S> + Debug,
QT: QemuHelperTuple<S> + Debug,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuForkExecutor")
.field("hooks", &self.hooks)
.field("hooks", &self.state.hooks)
.field("inner", &self.inner)
.finish()
}
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP> QemuForkExecutor<'a, H, OT, QT, S, SP>
impl<'a, H, OT, QT, S, SP, EM, Z, OF> QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind,
S: State,
S: State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
SP: ShMemProvider,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
pub fn new<EM, OF, Z>(
pub fn new(
hooks: &'a mut QemuHooks<QT, S>,
harness_fn: &'a mut H,
observers: OT,
@ -326,18 +413,10 @@ where
event_mgr: &mut EM,
shmem_provider: SP,
timeout: core::time::Duration,
) -> Result<Self, Error>
where
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
) -> Result<Self, Error> {
assert!(!QT::HOOKS_DO_SIDE_EFFECTS, "When using QemuForkExecutor, the hooks must not do any side effect as they will happen in the child process and then discarded");
Ok(Self {
first_exec: true,
hooks,
inner: InProcessForkExecutor::new(
harness_fn,
observers,
@ -347,40 +426,46 @@ where
timeout,
shmem_provider,
)?,
state: QemuExecutorState {
first_exec: true,
hooks,
},
})
}
pub fn inner(&self) -> &InProcessForkExecutor<'a, H, OT, S, SP> {
pub fn inner(&self) -> &InProcessForkExecutor<'a, H, OT, S, SP, EM, Z> {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut InProcessForkExecutor<'a, H, OT, S, SP> {
pub fn inner_mut(&mut self) -> &mut InProcessForkExecutor<'a, H, OT, S, SP, EM, Z> {
&mut self.inner
}
pub fn hooks(&self) -> &QemuHooks<QT, S> {
self.hooks
self.state.hooks
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
self.hooks
self.state.hooks
}
pub fn emulator(&self) -> &Emulator {
self.hooks.emulator()
self.state.hooks.emulator()
}
}
#[cfg(feature = "fork")]
impl<'a, EM, H, OT, QT, S, Z, SP> Executor<EM, Z> for QemuForkExecutor<'a, H, OT, QT, S, SP>
impl<'a, EM, H, OT, QT, S, Z, SP, OF> Executor<EM, Z>
for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
where
EM: EventManager<InProcessForkExecutor<'a, H, OT, S, SP>, Z, State = S>,
EM: EventManager<InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>, Z, State = S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasMetadata + HasExecutions + HasLastReportTime,
OT: ObserversTuple<S>,
S: State + HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + HasSolutions,
OT: ObserversTuple<S> + Debug,
QT: QemuHelperTuple<S>,
SP: ShMemProvider,
Z: UsesState<State = S>,
OF: Feedback<S>,
Z: HasObjective<Objective = OF, State = S>,
{
fn run_target(
&mut self,
@ -390,13 +475,13 @@ where
input: &Self::Input,
) -> Result<ExitKind, Error> {
let emu = Emulator::get().unwrap();
if self.first_exec {
self.hooks.helpers().first_exec_all(self.hooks);
self.first_exec = false;
if self.state.first_exec {
self.state.hooks.helpers().first_exec_all(self.state.hooks);
self.state.first_exec = false;
}
self.hooks.helpers_mut().pre_exec_all(&emu, input);
self.state.hooks.helpers_mut().pre_exec_all(&emu, input);
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
self.hooks.helpers_mut().post_exec_all(
self.state.hooks.helpers_mut().post_exec_all(
&emu,
input,
self.inner.observers_mut(),
@ -407,37 +492,43 @@ where
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP> UsesObservers for QemuForkExecutor<'a, H, OT, QT, S, SP>
impl<'a, H, OT, QT, S, SP, EM, Z> UsesObservers for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: State,
S: State + HasExecutions,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
type Observers = OT;
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP> UsesState for QemuForkExecutor<'a, H, OT, QT, S, SP>
impl<'a, H, OT, QT, S, SP, EM, Z> UsesState for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: State,
S: State + HasExecutions,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
type State = S;
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP> HasObservers for QemuForkExecutor<'a, H, OT, QT, S, SP>
impl<'a, H, OT, QT, S, SP, EM, Z> HasObservers for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
where
H: FnMut(&S::Input) -> ExitKind,
S: State,
S: State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
#[inline]
fn observers(&self) -> &OT {

View 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()
}
}