Improve readability of InProcessExecutor-related code (#1912)
* 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. * Separated inner from InProcessExecutor. * fix * unused import * unused import * fix import * fix import
This commit is contained in:
parent
55a300d508
commit
3b3e2f6efa
414
libafl/src/executors/inprocess/inner.rs
Normal file
414
libafl/src/executors/inprocess/inner.rs
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The internal state of `GenericInProcessExecutor`.
|
||||||
|
pub struct GenericInProcessExecutorInner<HT, OT, S>
|
||||||
|
where
|
||||||
|
HT: ExecutorHooksTuple,
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
S: State,
|
||||||
|
{
|
||||||
|
/// The observers, observing each run
|
||||||
|
pub(super) observers: OT,
|
||||||
|
// Crash and timeout hah
|
||||||
|
pub(super) hooks: (InProcessHooks, HT),
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<HT, OT, S> UsesState for GenericInProcessExecutorInner<HT, OT, S>
|
||||||
|
where
|
||||||
|
HT: ExecutorHooksTuple,
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
S: State,
|
||||||
|
{
|
||||||
|
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<HT, OT, S> HasObservers for GenericInProcessExecutorInner<HT, OT, S>
|
||||||
|
where
|
||||||
|
HT: ExecutorHooksTuple,
|
||||||
|
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,
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
S: State,
|
||||||
|
{
|
||||||
|
/// This function marks the boundary between the fuzzer and the target
|
||||||
|
#[inline]
|
||||||
|
pub fn enter_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),
|
||||||
|
ptr::from_ref(input) as *const c_void,
|
||||||
|
);
|
||||||
|
write_volatile(
|
||||||
|
addr_of_mut!((*data).executor_ptr),
|
||||||
|
ptr::from_ref(self) as *const c_void,
|
||||||
|
);
|
||||||
|
// Direct raw pointers access /aliasing is pretty undefined behavior.
|
||||||
|
// Since the state and event may have moved in memory, refresh them right before the signal may happen
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function marks the boundary between the fuzzer and the target
|
||||||
|
#[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<OT, S> GenericInProcessExecutorInner<(), OT, S>
|
||||||
|
where
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
S: HasExecutions + HasSolutions + HasCorpus + State,
|
||||||
|
{
|
||||||
|
/// Create a new in mem executor with the default timeout (5 sec)
|
||||||
|
pub fn new<E, EM, OF, Z>(
|
||||||
|
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>(
|
||||||
|
tuple_list!(),
|
||||||
|
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)
|
||||||
|
/// Do not use batched mode timeouts with cmplog cores. It is not supported
|
||||||
|
#[cfg(all(feature = "std", target_os = "linux"))]
|
||||||
|
pub fn batched_timeouts<E, EM, OF, Z>(
|
||||||
|
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>(
|
||||||
|
tuple_list!(),
|
||||||
|
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<E, EM, OF, Z>(
|
||||||
|
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(tuple_list!());
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HT, OT, S> GenericInProcessExecutorInner<HT, OT, S>
|
||||||
|
where
|
||||||
|
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<HT, OT, S> HasInProcessHooks for GenericInProcessExecutorInner<HT, OT, S>
|
||||||
|
where
|
||||||
|
HT: ExecutorHooksTuple,
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
S: State + HasExecutions + HasSolutions + HasCorpus,
|
||||||
|
{
|
||||||
|
/// the timeout handler
|
||||||
|
#[inline]
|
||||||
|
fn inprocess_hooks(&self) -> &InProcessHooks {
|
||||||
|
&self.hooks.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the timeout handler
|
||||||
|
#[inline]
|
||||||
|
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks {
|
||||||
|
&mut self.hooks.0
|
||||||
|
}
|
||||||
|
}
|
@ -5,32 +5,25 @@
|
|||||||
#![allow(clippy::needless_pass_by_value)]
|
#![allow(clippy::needless_pass_by_value)]
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
#[cfg(any(unix, feature = "std"))]
|
||||||
|
use core::ptr::addr_of_mut;
|
||||||
use core::{
|
use core::{
|
||||||
borrow::BorrowMut,
|
borrow::BorrowMut,
|
||||||
ffi::c_void,
|
|
||||||
fmt::{self, Debug, Formatter},
|
fmt::{self, Debug, Formatter},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ptr::{self, addr_of_mut, null, write_volatile},
|
|
||||||
sync::atomic::{compiler_fence, Ordering},
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use libafl_bolts::tuples::{tuple_list, Merge};
|
use libafl_bolts::tuples::tuple_list;
|
||||||
#[cfg(windows)]
|
|
||||||
use windows::Win32::System::Threading::SetThreadStackGuarantee;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "std", target_os = "linux"))]
|
#[cfg(any(unix, feature = "std"))]
|
||||||
use crate::executors::hooks::inprocess::HasTimeout;
|
use crate::executors::hooks::inprocess::GLOBAL_STATE;
|
||||||
#[cfg(all(windows, feature = "std"))]
|
|
||||||
use crate::executors::hooks::inprocess::HasTimeout;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, Testcase},
|
corpus::{Corpus, Testcase},
|
||||||
events::{Event, EventFirer, EventRestarter},
|
events::{Event, EventFirer, EventRestarter},
|
||||||
executors::{
|
executors::{
|
||||||
hooks::{
|
hooks::{inprocess::InProcessHooks, ExecutorHooksTuple},
|
||||||
inprocess::{InProcessHooks, GLOBAL_STATE},
|
inprocess::inner::GenericInProcessExecutorInner,
|
||||||
ExecutorHooksTuple,
|
|
||||||
},
|
|
||||||
Executor, ExitKind, HasObservers,
|
Executor, ExitKind, HasObservers,
|
||||||
},
|
},
|
||||||
feedbacks::Feedback,
|
feedbacks::Feedback,
|
||||||
@ -41,6 +34,8 @@ use crate::{
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The inner structure of `InProcessExecutor`.
|
||||||
|
pub mod inner;
|
||||||
/// A version of `InProcessExecutor` with a state accessible from the harness.
|
/// A version of `InProcessExecutor` with a state accessible from the harness.
|
||||||
pub mod stateful;
|
pub mod stateful;
|
||||||
|
|
||||||
@ -59,20 +54,6 @@ 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>
|
||||||
@ -88,19 +69,6 @@ where
|
|||||||
phantom: PhantomData<(*const H, HB)>,
|
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>
|
impl<H, HB, HT, OT, S> Debug for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
@ -117,15 +85,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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: FnMut(&S::Input) -> ExitKind + ?Sized,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
@ -137,15 +96,6 @@ 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: FnMut(&S::Input) -> ExitKind + ?Sized,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
@ -186,23 +136,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<HT, OT, S> HasObservers for GenericInProcessExecutorInner<HT, OT, S>
|
|
||||||
where
|
|
||||||
HT: ExecutorHooksTuple,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
S: State,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn observers(&self) -> &OT {
|
|
||||||
&self.observers
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn observers_mut(&mut self) -> &mut OT {
|
|
||||||
&mut self.observers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H, HB, HT, OT, S> HasObservers for 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,
|
||||||
@ -222,181 +155,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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]
|
|
||||||
pub fn enter_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),
|
|
||||||
ptr::from_ref(input) as *const c_void,
|
|
||||||
);
|
|
||||||
write_volatile(
|
|
||||||
addr_of_mut!((*data).executor_ptr),
|
|
||||||
ptr::from_ref(self) as *const c_void,
|
|
||||||
);
|
|
||||||
// Direct raw pointers access /aliasing is pretty undefined behavior.
|
|
||||||
// Since the state and event may have moved in memory, refresh them right before the signal may happen
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function marks the boundary between the fuzzer and the target
|
|
||||||
#[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<OT, S> GenericInProcessExecutorInner<(), OT, S>
|
|
||||||
where
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
S: HasExecutions + HasSolutions + HasCorpus + State,
|
|
||||||
{
|
|
||||||
/// Create a new in mem executor with the default timeout (5 sec)
|
|
||||||
pub fn new<E, EM, OF, Z>(
|
|
||||||
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>(
|
|
||||||
tuple_list!(),
|
|
||||||
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)
|
|
||||||
/// Do not use batched mode timeouts with cmplog cores. It is not supported
|
|
||||||
#[cfg(all(feature = "std", target_os = "linux"))]
|
|
||||||
pub fn batched_timeouts<E, EM, OF, Z>(
|
|
||||||
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>(
|
|
||||||
tuple_list!(),
|
|
||||||
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<E, EM, OF, Z>(
|
|
||||||
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(tuple_list!());
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, H, OT, S> InProcessExecutor<'a, H, OT, S>
|
impl<'a, H, OT, S> InProcessExecutor<'a, H, OT, S>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
@ -440,7 +198,7 @@ where
|
|||||||
exec_tmout: Duration,
|
exec_tmout: Duration,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
Self: Executor<EM, Z, State = S> + HasObservers + HasInProcessHooks,
|
Self: Executor<EM, Z, State = S>,
|
||||||
EM: EventFirer<State = S> + EventRestarter,
|
EM: EventFirer<State = S> + EventRestarter,
|
||||||
OF: Feedback<S>,
|
OF: Feedback<S>,
|
||||||
S: State,
|
S: State,
|
||||||
@ -491,130 +249,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<HT, OT, S> GenericInProcessExecutorInner<HT, OT, S>
|
|
||||||
where
|
|
||||||
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>
|
impl<H, HB, HT, OT, S> GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
@ -747,25 +381,6 @@ pub trait HasInProcessHooks {
|
|||||||
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks;
|
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<HT, OT, S> HasInProcessHooks for GenericInProcessExecutorInner<HT, OT, S>
|
|
||||||
where
|
|
||||||
HT: ExecutorHooksTuple,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
S: State + HasExecutions + HasSolutions + HasCorpus,
|
|
||||||
{
|
|
||||||
/// the timeout handler
|
|
||||||
#[inline]
|
|
||||||
fn inprocess_hooks(&self) -> &InProcessHooks {
|
|
||||||
&self.hooks.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the timeout handler
|
|
||||||
#[inline]
|
|
||||||
fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks {
|
|
||||||
&mut self.hooks.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H, HB, HT, OT, S> HasInProcessHooks for GenericInProcessExecutor<H, HB, HT, OT, S>
|
impl<H, HB, HT, OT, S> HasInProcessHooks for GenericInProcessExecutor<H, HB, HT, OT, S>
|
||||||
where
|
where
|
||||||
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
||||||
|
351
libafl/src/executors/inprocess_fork/inner.rs
Normal file
351
libafl/src/executors/inprocess_fork/inner.rs
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
use core::{
|
||||||
|
ffi::c_void,
|
||||||
|
fmt::{self, Debug, Formatter},
|
||||||
|
marker::PhantomData,
|
||||||
|
ptr::{self, addr_of_mut, null_mut, write_volatile},
|
||||||
|
sync::atomic::{compiler_fence, Ordering},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use libafl_bolts::{
|
||||||
|
os::unix_signals::Signal,
|
||||||
|
shmem::ShMemProvider,
|
||||||
|
tuples::{tuple_list, Merge},
|
||||||
|
};
|
||||||
|
use nix::{
|
||||||
|
sys::wait::{waitpid, WaitStatus},
|
||||||
|
unistd::Pid,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(all(unix, not(target_os = "linux")))]
|
||||||
|
use crate::executors::hooks::timer::{setitimer, Itimerval, Timeval, ITIMER_REAL};
|
||||||
|
use crate::{
|
||||||
|
events::{EventFirer, EventRestarter},
|
||||||
|
executors::{
|
||||||
|
hooks::{
|
||||||
|
inprocess_fork::{InChildProcessHooks, FORK_EXECUTOR_GLOBAL_DATA},
|
||||||
|
ExecutorHooksTuple,
|
||||||
|
},
|
||||||
|
ExitKind, HasObservers,
|
||||||
|
},
|
||||||
|
inputs::UsesInput,
|
||||||
|
observers::{ObserversTuple, UsesObservers},
|
||||||
|
state::{State, UsesState},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Inner state of GenericInProcessExecutor-like structures.
|
||||||
|
pub struct GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||||
|
where
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
S: UsesInput,
|
||||||
|
SP: ShMemProvider,
|
||||||
|
HT: ExecutorHooksTuple,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
|
{
|
||||||
|
pub(super) hooks: (InChildProcessHooks, HT),
|
||||||
|
pub(super) shmem_provider: SP,
|
||||||
|
pub(super) observers: OT,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub(super) itimerspec: libc::itimerspec,
|
||||||
|
#[cfg(all(unix, not(target_os = "linux")))]
|
||||||
|
pub(super) itimerval: Itimerval,
|
||||||
|
pub(super) phantom: PhantomData<(S, 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("GenericInProcessForkExecutorInner")
|
||||||
|
.field("hooks", &self.hooks)
|
||||||
|
.field("observers", &self.observers)
|
||||||
|
.field("shmem_provider", &self.shmem_provider)
|
||||||
|
.field("itimerspec", &self.itimerspec)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
return f
|
||||||
|
.debug_struct("GenericInProcessForkExecutorInner")
|
||||||
|
.field("observers", &self.observers)
|
||||||
|
.field("shmem_provider", &self.shmem_provider)
|
||||||
|
.field("itimerval", &self.itimerval)
|
||||||
|
.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HT, OT, S, SP, EM, Z> UsesState for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||||
|
where
|
||||||
|
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>,
|
||||||
|
{
|
||||||
|
pub(super) 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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) 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<HT, OT, S, SP, EM, Z> GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||||
|
where
|
||||||
|
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(
|
||||||
|
&mut self,
|
||||||
|
_fuzzer: &mut Z,
|
||||||
|
state: &mut <Self as UsesState>::State,
|
||||||
|
_event_mgr: &mut EM,
|
||||||
|
input: &<Self as UsesInput>::Input,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
let data = addr_of_mut!(FORK_EXECUTOR_GLOBAL_DATA);
|
||||||
|
write_volatile(
|
||||||
|
addr_of_mut!((*data).executor_ptr),
|
||||||
|
ptr::from_ref(self) as *const c_void,
|
||||||
|
);
|
||||||
|
write_volatile(
|
||||||
|
addr_of_mut!((*data).current_input_ptr),
|
||||||
|
ptr::from_ref(input) as *const c_void,
|
||||||
|
);
|
||||||
|
write_volatile(
|
||||||
|
addr_of_mut!((*data).state_ptr),
|
||||||
|
ptr::from_mut(state) as *mut c_void,
|
||||||
|
);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// This function marks the boundary between the fuzzer and the target.
|
||||||
|
pub fn leave_target(
|
||||||
|
&mut self,
|
||||||
|
_fuzzer: &mut Z,
|
||||||
|
_state: &mut <Self as UsesState>::State,
|
||||||
|
_event_mgr: &mut EM,
|
||||||
|
_input: &<Self as UsesInput>::Input,
|
||||||
|
) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`GenericInProcessForkExecutorInner`] with custom hooks
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn with_hooks(
|
||||||
|
userhooks: HT,
|
||||||
|
observers: OT,
|
||||||
|
_fuzzer: &mut Z,
|
||||||
|
state: &mut S,
|
||||||
|
_event_mgr: &mut EM,
|
||||||
|
timeout: Duration,
|
||||||
|
shmem_provider: SP,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let default_hooks = InChildProcessHooks::new::<Self>()?;
|
||||||
|
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
|
||||||
|
hooks.init_all::<Self, S>(state);
|
||||||
|
|
||||||
|
let milli_sec = timeout.as_millis();
|
||||||
|
let it_value = libc::timespec {
|
||||||
|
tv_sec: (milli_sec / 1000) as _,
|
||||||
|
tv_nsec: ((milli_sec % 1000) * 1000 * 1000) as _,
|
||||||
|
};
|
||||||
|
let it_interval = libc::timespec {
|
||||||
|
tv_sec: 0,
|
||||||
|
tv_nsec: 0,
|
||||||
|
};
|
||||||
|
let itimerspec = libc::itimerspec {
|
||||||
|
it_interval,
|
||||||
|
it_value,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
shmem_provider,
|
||||||
|
observers,
|
||||||
|
hooks,
|
||||||
|
itimerspec,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`GenericInProcessForkExecutor`], non linux
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn with_hooks(
|
||||||
|
userhooks: HT,
|
||||||
|
observers: OT,
|
||||||
|
_fuzzer: &mut Z,
|
||||||
|
state: &mut S,
|
||||||
|
_event_mgr: &mut EM,
|
||||||
|
timeout: Duration,
|
||||||
|
shmem_provider: SP,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let default_hooks = InChildProcessHooks::new::<Self>()?;
|
||||||
|
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
|
||||||
|
hooks.init_all::<Self, S>(state);
|
||||||
|
|
||||||
|
let milli_sec = timeout.as_millis();
|
||||||
|
let it_value = Timeval {
|
||||||
|
tv_sec: (milli_sec / 1000) as i64,
|
||||||
|
tv_usec: (milli_sec % 1000) as i64,
|
||||||
|
};
|
||||||
|
let it_interval = Timeval {
|
||||||
|
tv_sec: 0,
|
||||||
|
tv_usec: 0,
|
||||||
|
};
|
||||||
|
let itimerval = Itimerval {
|
||||||
|
it_interval,
|
||||||
|
it_value,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
shmem_provider,
|
||||||
|
observers,
|
||||||
|
hooks,
|
||||||
|
itimerval,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HT, OT, S, SP, EM, Z> UsesObservers for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
||||||
|
where
|
||||||
|
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 {
|
||||||
|
&self.observers
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn observers_mut(&mut self) -> &mut OT {
|
||||||
|
&mut self.observers
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,23 @@
|
|||||||
//! The `GenericInProcessForkExecutor` to do forking before executing the harness in-processly
|
//! The `GenericInProcessForkExecutor` to do forking before executing the harness in-processly
|
||||||
use core::{
|
use core::{
|
||||||
ffi::c_void,
|
|
||||||
fmt::{self, Debug, Formatter},
|
fmt::{self, Debug, Formatter},
|
||||||
marker::PhantomData,
|
|
||||||
ptr::{self, addr_of_mut, null_mut, write_volatile},
|
|
||||||
sync::atomic::{compiler_fence, Ordering},
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
os::unix_signals::{ucontext_t, Signal},
|
os::unix_signals::{ucontext_t, Signal},
|
||||||
shmem::ShMemProvider,
|
shmem::ShMemProvider,
|
||||||
tuples::{tuple_list, Merge},
|
tuples::tuple_list,
|
||||||
};
|
};
|
||||||
use libc::siginfo_t;
|
use libc::siginfo_t;
|
||||||
use nix::{
|
use nix::unistd::{fork, ForkResult};
|
||||||
sys::wait::{waitpid, WaitStatus},
|
|
||||||
unistd::{fork, ForkResult, Pid},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::hooks::ExecutorHooksTuple;
|
use super::hooks::ExecutorHooksTuple;
|
||||||
use crate::{
|
use crate::{
|
||||||
events::{EventFirer, EventRestarter},
|
events::{EventFirer, EventRestarter},
|
||||||
executors::{
|
executors::{
|
||||||
hooks::inprocess_fork::{
|
hooks::inprocess_fork::InProcessForkExecutorGlobalData,
|
||||||
InChildProcessHooks, InProcessForkExecutorGlobalData, FORK_EXECUTOR_GLOBAL_DATA,
|
inprocess_fork::inner::GenericInProcessForkExecutorInner, Executor, ExitKind, HasObservers,
|
||||||
},
|
|
||||||
Executor, ExitKind, HasObservers,
|
|
||||||
},
|
},
|
||||||
feedbacks::Feedback,
|
feedbacks::Feedback,
|
||||||
fuzzer::HasObjective,
|
fuzzer::HasObjective,
|
||||||
@ -44,9 +35,8 @@ pub(crate) type ForkHandlerFuncPtr = unsafe fn(
|
|||||||
data: *mut InProcessForkExecutorGlobalData,
|
data: *mut InProcessForkExecutorGlobalData,
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(all(unix, not(target_os = "linux")))]
|
/// The inner structure of `InProcessForkExecutor`.
|
||||||
use crate::executors::hooks::timer::{setitimer, Itimerval, Timeval, ITIMER_REAL};
|
pub mod inner;
|
||||||
|
|
||||||
/// A version of `InProcessForkExecutor` with a state accessible from the harness.
|
/// A version of `InProcessForkExecutor` with a state accessible from the harness.
|
||||||
pub mod stateful;
|
pub mod stateful;
|
||||||
|
|
||||||
@ -89,26 +79,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inner state of GenericInProcessExecutor-like structures.
|
|
||||||
pub struct GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
|
||||||
where
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
S: UsesInput,
|
|
||||||
SP: ShMemProvider,
|
|
||||||
HT: ExecutorHooksTuple,
|
|
||||||
EM: UsesState<State = S>,
|
|
||||||
Z: UsesState<State = S>,
|
|
||||||
{
|
|
||||||
hooks: (InChildProcessHooks, HT),
|
|
||||||
shmem_provider: SP,
|
|
||||||
observers: OT,
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
itimerspec: libc::itimerspec,
|
|
||||||
#[cfg(all(unix, not(target_os = "linux")))]
|
|
||||||
itimerval: Itimerval,
|
|
||||||
phantom: PhantomData<(S, EM, Z)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [`GenericInProcessForkExecutor`] is an executor that forks the current process before each execution.
|
/// [`GenericInProcessForkExecutor`] is an executor that forks the current process before each execution.
|
||||||
pub struct GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
pub struct GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||||
where
|
where
|
||||||
@ -124,37 +94,6 @@ where
|
|||||||
inner: GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>,
|
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("GenericInProcessForkExecutorInner")
|
|
||||||
.field("hooks", &self.hooks)
|
|
||||||
.field("observers", &self.observers)
|
|
||||||
.field("shmem_provider", &self.shmem_provider)
|
|
||||||
.field("itimerspec", &self.itimerspec)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
return f
|
|
||||||
.debug_struct("GenericInProcessForkExecutorInner")
|
|
||||||
.field("observers", &self.observers)
|
|
||||||
.field("shmem_provider", &self.shmem_provider)
|
|
||||||
.field("itimerval", &self.itimerval)
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, H, HT, OT, S, SP, EM, Z> Debug
|
impl<'a, H, HT, OT, S, SP, EM, Z> Debug
|
||||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||||
where
|
where
|
||||||
@ -183,18 +122,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<HT, OT, S, SP, EM, Z> UsesState for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
|
||||||
where
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
S: State,
|
|
||||||
SP: ShMemProvider,
|
|
||||||
HT: ExecutorHooksTuple,
|
|
||||||
EM: UsesState<State = S>,
|
|
||||||
Z: UsesState<State = S>,
|
|
||||||
{
|
|
||||||
type State = S;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, H, HT, OT, S, SP, EM, Z> UsesState
|
impl<'a, H, HT, OT, S, SP, EM, Z> UsesState
|
||||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||||
where
|
where
|
||||||
@ -209,100 +136,6 @@ where
|
|||||||
type 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>
|
impl<'a, EM, H, HT, OT, S, SP, Z> Executor<EM, Z>
|
||||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||||
where
|
where
|
||||||
@ -345,133 +178,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<HT, OT, S, SP, EM, Z> GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
|
||||||
where
|
|
||||||
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(
|
|
||||||
&mut self,
|
|
||||||
_fuzzer: &mut Z,
|
|
||||||
state: &mut <Self as UsesState>::State,
|
|
||||||
_event_mgr: &mut EM,
|
|
||||||
input: &<Self as UsesInput>::Input,
|
|
||||||
) {
|
|
||||||
unsafe {
|
|
||||||
let data = addr_of_mut!(FORK_EXECUTOR_GLOBAL_DATA);
|
|
||||||
write_volatile(
|
|
||||||
addr_of_mut!((*data).executor_ptr),
|
|
||||||
ptr::from_ref(self) as *const c_void,
|
|
||||||
);
|
|
||||||
write_volatile(
|
|
||||||
addr_of_mut!((*data).current_input_ptr),
|
|
||||||
ptr::from_ref(input) as *const c_void,
|
|
||||||
);
|
|
||||||
write_volatile(
|
|
||||||
addr_of_mut!((*data).state_ptr),
|
|
||||||
ptr::from_mut(state) as *mut c_void,
|
|
||||||
);
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// This function marks the boundary between the fuzzer and the target.
|
|
||||||
pub fn leave_target(
|
|
||||||
&mut self,
|
|
||||||
_fuzzer: &mut Z,
|
|
||||||
_state: &mut <Self as UsesState>::State,
|
|
||||||
_event_mgr: &mut EM,
|
|
||||||
_input: &<Self as UsesInput>::Input,
|
|
||||||
) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new [`GenericInProcessForkExecutor`] with custom hooks
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn with_hooks(
|
|
||||||
userhooks: HT,
|
|
||||||
observers: OT,
|
|
||||||
_fuzzer: &mut Z,
|
|
||||||
state: &mut S,
|
|
||||||
_event_mgr: &mut EM,
|
|
||||||
timeout: Duration,
|
|
||||||
shmem_provider: SP,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let default_hooks = InChildProcessHooks::new::<Self>()?;
|
|
||||||
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
|
|
||||||
hooks.init_all::<Self, S>(state);
|
|
||||||
|
|
||||||
let milli_sec = timeout.as_millis();
|
|
||||||
let it_value = libc::timespec {
|
|
||||||
tv_sec: (milli_sec / 1000) as _,
|
|
||||||
tv_nsec: ((milli_sec % 1000) * 1000 * 1000) as _,
|
|
||||||
};
|
|
||||||
let it_interval = libc::timespec {
|
|
||||||
tv_sec: 0,
|
|
||||||
tv_nsec: 0,
|
|
||||||
};
|
|
||||||
let itimerspec = libc::itimerspec {
|
|
||||||
it_interval,
|
|
||||||
it_value,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
shmem_provider,
|
|
||||||
observers,
|
|
||||||
hooks,
|
|
||||||
itimerspec,
|
|
||||||
phantom: PhantomData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new [`GenericInProcessForkExecutor`], non linux
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn with_hooks(
|
|
||||||
userhooks: HT,
|
|
||||||
observers: OT,
|
|
||||||
_fuzzer: &mut Z,
|
|
||||||
state: &mut S,
|
|
||||||
_event_mgr: &mut EM,
|
|
||||||
timeout: Duration,
|
|
||||||
shmem_provider: SP,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let default_hooks = InChildProcessHooks::new::<Self>()?;
|
|
||||||
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
|
|
||||||
hooks.init_all::<Self, S>(state);
|
|
||||||
|
|
||||||
let milli_sec = timeout.as_millis();
|
|
||||||
let it_value = Timeval {
|
|
||||||
tv_sec: (milli_sec / 1000) as i64,
|
|
||||||
tv_usec: (milli_sec % 1000) as i64,
|
|
||||||
};
|
|
||||||
let it_interval = Timeval {
|
|
||||||
tv_sec: 0,
|
|
||||||
tv_usec: 0,
|
|
||||||
};
|
|
||||||
let itimerval = Itimerval {
|
|
||||||
it_interval,
|
|
||||||
it_value,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
shmem_provider,
|
|
||||||
observers,
|
|
||||||
hooks,
|
|
||||||
itimerval,
|
|
||||||
phantom: PhantomData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, H, HT, OT, S, SP, EM, Z, OF> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
impl<'a, H, HT, OT, S, SP, EM, Z, OF> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
H: FnMut(&S::Input) -> ExitKind + ?Sized,
|
||||||
@ -523,18 +229,6 @@ where {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<HT, OT, S, SP, EM, Z> UsesObservers for GenericInProcessForkExecutorInner<HT, OT, S, SP, EM, Z>
|
|
||||||
where
|
|
||||||
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, EM, Z> UsesObservers
|
impl<'a, H, HT, OT, S, SP, EM, Z> UsesObservers
|
||||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||||
where
|
where
|
||||||
@ -549,26 +243,6 @@ where
|
|||||||
type Observers = OT;
|
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 {
|
|
||||||
&self.observers
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn observers_mut(&mut self) -> &mut OT {
|
|
||||||
&mut self.observers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, H, HT, OT, S, SP, EM, Z> HasObservers
|
impl<'a, H, HT, OT, S, SP, EM, Z> HasObservers
|
||||||
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z>
|
||||||
where
|
where
|
||||||
|
Loading…
x
Reference in New Issue
Block a user