use core::{
ffi::c_void,
fmt::{self, Debug, Formatter},
marker::PhantomData,
ptr::{self, addr_of_mut, null, write_volatile},
sync::atomic::{compiler_fence, Ordering},
time::Duration,
};
use libafl_bolts::tuples::{tuple_list, Merge};
#[cfg(windows)]
use windows::Win32::System::Threading::SetThreadStackGuarantee;
#[cfg(all(feature = "std", target_os = "linux"))]
use crate::executors::hooks::inprocess::HasTimeout;
#[cfg(all(windows, feature = "std"))]
use crate::executors::hooks::inprocess::HasTimeout;
use crate::{
events::{EventFirer, EventRestarter},
executors::{
hooks::{
inprocess::{InProcessHooks, GLOBAL_STATE},
ExecutorHooksTuple,
},
inprocess::HasInProcessHooks,
Executor, HasObservers,
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::UsesInput,
observers::{ObserversTuple, UsesObservers},
state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState},
Error,
};
pub struct GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State,
{
pub(super) observers: OT,
pub(super) hooks: (InProcessHooks<S>, HT),
phantom: PhantomData<S>,
}
impl<HT, OT, S> Debug for GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple<S>,
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<HT, OT, S> UsesState for GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State,
{
type State = S;
}
impl<HT, OT, S> UsesObservers for GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State,
{
type Observers = OT;
}
impl<HT, OT, S> HasObservers for GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State,
{
#[inline]
fn observers(&self) -> &OT {
&self.observers
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
&mut self.observers
}
}
impl<HT, OT, S> GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State,
{
#[inline]
pub unsafe fn enter_target<EM, Z>(
&mut self,
fuzzer: &mut Z,
state: &mut <Self as UsesState>::State,
mgr: &mut EM,
input: &<Self as UsesInput>::Input,
executor_ptr: *const c_void,
) {
unsafe {
let data = addr_of_mut!(GLOBAL_STATE);
write_volatile(
addr_of_mut!((*data).current_input_ptr),
ptr::from_ref(input) as *const c_void,
);
write_volatile(addr_of_mut!((*data).executor_ptr), executor_ptr);
write_volatile(
addr_of_mut!((*data).state_ptr),
ptr::from_mut(state) as *mut c_void,
);
write_volatile(
addr_of_mut!((*data).event_mgr_ptr),
ptr::from_mut(mgr) as *mut c_void,
);
write_volatile(
addr_of_mut!((*data).fuzzer_ptr),
ptr::from_mut(fuzzer) as *mut c_void,
);
compiler_fence(Ordering::SeqCst);
}
}
#[inline]
pub fn leave_target<EM, Z>(
&mut self,
_fuzzer: &mut Z,
_state: &mut <Self as UsesState>::State,
_mgr: &mut EM,
_input: &<Self as UsesInput>::Input,
) {
unsafe {
let data = addr_of_mut!(GLOBAL_STATE);
write_volatile(addr_of_mut!((*data).current_input_ptr), null());
compiler_fence(Ordering::SeqCst);
}
}
}
impl<HT, OT, S> GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: HasExecutions + HasSolutions + HasCorpus + State,
{
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<S>,
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),
)
}
#[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<S>,
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)
}
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<S>,
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>(state);
#[cfg(windows)]
unsafe {
let mut stack_reserved = 0x20000;
SetThreadStackGuarantee(&mut stack_reserved)?;
}
#[cfg(all(feature = "std", windows))]
{
*hooks.0.millis_sec_mut() = timeout.as_millis() as i64;
}
Ok(Self {
observers,
hooks,
phantom: PhantomData,
})
}
#[inline]
pub fn hooks(&self) -> &(InProcessHooks<S>, HT) {
&self.hooks
}
#[inline]
pub fn hooks_mut(&mut self) -> &mut (InProcessHooks<S>, HT) {
&mut self.hooks
}
}
impl<HT, OT, S> HasInProcessHooks<S> for GenericInProcessExecutorInner<HT, OT, S>
where
HT: ExecutorHooksTuple<S>,
OT: ObserversTuple<S>,
S: State + HasExecutions + HasSolutions + HasCorpus,
{
#[inline]
fn inprocess_hooks(&self) -> &InProcessHooks<S> {
&self.hooks.0
}
#[inline]
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks<S> {
&mut self.hooks.0
}
}