#![allow(clippy::needless_pass_by_value)]
use alloc::boxed::Box;
#[cfg(any(unix, feature = "std"))]
use core::ptr::addr_of_mut;
use core::{
borrow::BorrowMut,
ffi::c_void,
fmt::{self, Debug, Formatter},
marker::PhantomData,
ptr,
time::Duration,
};
use libafl_bolts::tuples::tuple_list;
#[cfg(any(unix, feature = "std"))]
use crate::executors::hooks::inprocess::GLOBAL_STATE;
use crate::{
corpus::{Corpus, Testcase},
events::{Event, EventFirer, EventRestarter},
executors::{
hooks::{inprocess::InProcessHooks, ExecutorHooksTuple},
inprocess::inner::GenericInProcessExecutorInner,
Executor, ExitKind, HasObservers,
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::UsesInput,
observers::{ObserversTuple, UsesObservers},
state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState},
Error, HasMetadata,
};
pub mod inner;
pub mod stateful;
pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor<H, &'a mut H, (), OT, S>;
pub type HookableInProcessExecutor<'a, H, HT, OT, S> =
GenericInProcessExecutor<H, &'a mut H, HT, OT, S>;
pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor<
dyn FnMut(&<S as UsesInput>::Input) -> ExitKind,
Box<dyn FnMut(&<S as UsesInput>::Input) -> ExitKind>,
(),
OT,
S,
>;
#[allow(dead_code)]
pub struct GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State,
{
harness_fn: HB,
inner: GenericInProcessExecutorInner<HT, OT, S>,
phantom: PhantomData<(*const H, HB)>,
}
impl<H, HB, HT, OT, S> Debug for GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S> + Debug,
S: State,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("GenericInProcessExecutor")
.field("inner", &self.inner)
.field("harness_fn", &"<fn>")
.finish_non_exhaustive()
}
}
impl<H, HB, HT, OT, S> UsesState for GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State,
{
type State = S;
}
impl<H, HB, HT, OT, S> UsesObservers for GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State,
{
type Observers = OT;
}
impl<EM, H, HB, HT, OT, S, Z> Executor<EM, Z> for GenericInProcessExecutor<H, HB, HT, OT, S>
where
EM: UsesState<State = S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<S>,
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;
unsafe {
let executor_ptr = ptr::from_ref(self) as *const c_void;
self.inner
.enter_target(fuzzer, state, mgr, input, executor_ptr);
}
self.inner.hooks.pre_exec_all(state, input);
let ret = (self.harness_fn.borrow_mut())(input);
self.inner.hooks.post_exec_all(state, 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>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<S>,
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> InProcessExecutor<'a, H, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: HasExecutions + HasSolutions + HasCorpus + State,
{
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),
)
}
#[cfg(all(feature = "std", target_os = "linux"))]
pub fn batched_timeout<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>,
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>(
tuple_list!(),
observers,
fuzzer,
state,
event_mgr,
exec_tmout,
)?;
Ok(Self {
harness_fn,
inner,
phantom: PhantomData,
})
}
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_generic::<Self, EM, OF, Z>(
tuple_list!(),
observers,
fuzzer,
state,
event_mgr,
timeout,
)?;
Ok(Self {
harness_fn,
inner,
phantom: PhantomData,
})
}
}
impl<H, HB, HT, OT, S> GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State + HasExecutions + HasSolutions + HasCorpus,
{
pub fn generic<EM, OF, Z>(
user_hooks: HT,
harness_fn: HB,
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(
user_hooks,
harness_fn,
observers,
fuzzer,
state,
event_mgr,
Duration::from_millis(5000),
)
}
#[cfg(all(feature = "std", target_os = "linux"))]
pub fn batched_timeout_generic<EM, OF, Z>(
user_hooks: HT,
harness_fn: HB,
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,
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,
inner,
phantom: PhantomData,
})
}
pub fn with_timeout_generic<EM, OF, Z>(
user_hooks: HT,
harness_fn: HB,
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_generic::<Self, EM, OF, Z>(
user_hooks, observers, fuzzer, state, event_mgr, timeout,
)?;
Ok(Self {
harness_fn,
inner,
phantom: PhantomData,
})
}
#[inline]
pub fn harness(&self) -> &H {
self.harness_fn.borrow()
}
#[inline]
pub fn harness_mut(&mut self) -> &mut H {
self.harness_fn.borrow_mut()
}
#[inline]
pub fn hooks(&self) -> &(InProcessHooks<S>, HT) {
self.inner.hooks()
}
#[inline]
pub fn hooks_mut(&mut self) -> &mut (InProcessHooks<S>, HT) {
self.inner.hooks_mut()
}
}
pub trait HasInProcessHooks<S>
where
S: UsesInput,
{
fn inprocess_hooks(&self) -> &InProcessHooks<S>;
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks<S>;
}
impl<H, HB, HT, OT, S> HasInProcessHooks<S> for GenericInProcessExecutor<H, HB, HT, OT, S>
where
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State + HasExecutions + HasSolutions + HasCorpus,
{
#[inline]
fn inprocess_hooks(&self) -> &InProcessHooks<S> {
self.inner.inprocess_hooks()
}
#[inline]
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks<S> {
self.inner.inprocess_hooks_mut()
}
}
#[inline]
#[allow(clippy::too_many_arguments)]
pub fn run_observers_and_save_state<E, EM, OF, Z>(
executor: &mut E,
state: &mut E::State,
input: &<E::State as UsesInput>::Input,
fuzzer: &mut Z,
event_mgr: &mut EM,
exitkind: ExitKind,
) where
E: 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>,
{
let observers = executor.observers_mut();
observers
.post_exec_all(state, input, &exitkind)
.expect("Observers post_exec_all failed");
let interesting = fuzzer
.objective_mut()
.is_interesting(state, event_mgr, input, observers, &exitkind)
.expect("In run_observers_and_save_state objective failure.");
if interesting {
let executions = *state.executions();
let mut new_testcase = Testcase::with_executions(input.clone(), executions);
new_testcase.add_metadata(exitkind);
new_testcase.set_parent_id_optional(*state.corpus().current());
fuzzer
.objective_mut()
.append_metadata(state, event_mgr, observers, &mut new_testcase)
.expect("Failed adding metadata");
state
.solutions_mut()
.add(new_testcase)
.expect("In run_observers_and_save_state solutions failure.");
event_mgr
.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
executions,
time: libafl_bolts::current_time(),
},
)
.expect("Could not save state in run_observers_and_save_state");
}
event_mgr.on_restart(state).unwrap();
log::info!("Bye!");
}
#[cfg(any(unix, feature = "std"))]
pub unsafe fn generic_inproc_crash_handler<E, EM, OF, Z>()
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>,
{
let data = addr_of_mut!(GLOBAL_STATE);
let in_handler = (*data).set_in_handler(true);
if (*data).is_valid() {
let executor = (*data).executor_mut::<E>();
let state = (*data).state_mut::<E::State>();
let event_mgr = (*data).event_mgr_mut::<EM>();
let fuzzer = (*data).fuzzer_mut::<Z>();
let input = (*data).take_current_input::<<E::State as UsesInput>::Input>();
run_observers_and_save_state::<E, EM, OF, Z>(
executor,
state,
input,
fuzzer,
event_mgr,
ExitKind::Crash,
);
}
(*data).set_in_handler(in_handler);
}
#[cfg(test)]
mod tests {
use libafl_bolts::tuples::tuple_list;
use crate::{
corpus::InMemoryCorpus,
events::NopEventManager,
executors::{Executor, ExitKind, InProcessExecutor},
feedbacks::CrashFeedback,
inputs::{NopInput, UsesInput},
schedulers::RandScheduler,
state::StdState,
StdFuzzer,
};
impl UsesInput for () {
type Input = NopInput;
}
#[test]
#[allow(clippy::let_unit_value)]
fn test_inmem_exec() {
let mut harness = |_buf: &NopInput| ExitKind::Ok;
let rand = libafl_bolts::rands::XkcdRand::new();
let corpus = InMemoryCorpus::<NopInput>::new();
let solutions = InMemoryCorpus::new();
let mut objective = CrashFeedback::new();
let mut feedback = tuple_list!();
let sche = RandScheduler::new();
let mut mgr = NopEventManager::new();
let mut state =
StdState::new(rand, corpus, solutions, &mut feedback, &mut objective).unwrap();
let mut fuzzer = StdFuzzer::<_, _, _, ()>::new(sche, feedback, objective);
let mut in_process_executor = InProcessExecutor::new(
&mut harness,
tuple_list!(),
&mut fuzzer,
&mut state,
&mut mgr,
)
.unwrap();
let input = NopInput {};
in_process_executor
.run_target(&mut fuzzer, &mut state, &mut mgr, &input)
.unwrap();
}
}