Andrea Fioraldi 406e77faa9
QEMU Asan backtrace and report (#1628)
* wip

* ExtractFirstRefMutType

* Asan report with backtrace

* Print asan reports and fix backtraces in libafl qemu

* print context

* enlarge redzone

* nopstate

* fix

* reproducer

* clippy

* clippy

* Fix android

* Crash hook
2023-10-25 15:58:32 +02:00

2282 lines
74 KiB
Rust

//! The [`InProcessExecutor`] is a libfuzzer-like executor, that will simply call a function.
//! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective.
//!
//! Needs the `fork` feature flag.
#![allow(clippy::needless_pass_by_value)]
use alloc::boxed::Box;
#[cfg(all(unix, feature = "std"))]
use alloc::vec::Vec;
#[cfg(all(feature = "std", unix, target_os = "linux"))]
use core::ptr::addr_of_mut;
#[cfg(all(unix, feature = "std"))]
use core::time::Duration;
use core::{
borrow::BorrowMut,
ffi::c_void,
fmt::{self, Debug, Formatter},
marker::PhantomData,
ptr::{self, null_mut},
};
#[cfg(any(unix, all(windows, feature = "std")))]
use core::{
ptr::write_volatile,
sync::atomic::{compiler_fence, Ordering},
};
#[cfg(all(feature = "std", unix))]
use std::intrinsics::transmute;
#[cfg(all(unix, not(miri)))]
use libafl_bolts::os::unix_signals::setup_signal_handler;
#[cfg(all(feature = "std", unix))]
use libafl_bolts::os::unix_signals::{ucontext_t, Handler, Signal};
#[cfg(all(windows, feature = "std"))]
use libafl_bolts::os::windows_exceptions::setup_exception_handler;
#[cfg(all(feature = "std", unix))]
use libafl_bolts::shmem::ShMemProvider;
#[cfg(all(feature = "std", unix))]
use libc::siginfo_t;
#[cfg(all(feature = "std", unix))]
use nix::{
sys::wait::{waitpid, WaitStatus},
unistd::{fork, ForkResult},
};
#[cfg(windows)]
use windows::Win32::System::Threading::SetThreadStackGuarantee;
#[cfg(all(windows, feature = "std"))]
use windows::Win32::System::Threading::PTP_TIMER;
use crate::{
events::{EventFirer, EventRestarter},
executors::{Executor, ExitKind, HasObservers},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::UsesInput,
observers::{ObserversTuple, UsesObservers},
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasSolutions, UsesState},
Error,
};
/// 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 process executor simply calls a target function, as boxed `FnMut` trait object
pub type OwnedInProcessExecutor<OT, S> = GenericInProcessExecutor<
dyn FnMut(&<S as UsesInput>::Input) -> ExitKind,
Box<dyn FnMut(&<S as UsesInput>::Input) -> ExitKind>,
OT,
S,
>;
/// The inmem executor simply calls a target function, then returns afterwards.
#[allow(dead_code)]
pub struct GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
OT: ObserversTuple<S>,
S: UsesInput,
{
/// The harness function, being executed for each fuzzing loop execution
harness_fn: HB,
/// The observers, observing each run
observers: OT,
// Crash and timeout hah
handlers: InProcessHandlers,
phantom: PhantomData<(S, *const H)>,
}
impl<H, HB, OT, S> Debug for GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
OT: ObserversTuple<S>,
S: UsesInput,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("GenericInProcessExecutor")
.field("harness_fn", &"<fn>")
.field("observers", &self.observers)
.finish_non_exhaustive()
}
}
impl<H, HB, OT, S> UsesState for GenericInProcessExecutor<H, HB, OT, S>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
HB: BorrowMut<H>,
OT: ObserversTuple<S>,
S: UsesInput,
{
type State = S;
}
impl<H, HB, OT, S> UsesObservers for GenericInProcessExecutor<H, HB, OT, S>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
HB: BorrowMut<H>,
OT: ObserversTuple<S>,
S: UsesInput,
{
type Observers = OT;
}
impl<EM, H, HB, OT, S, Z> Executor<EM, Z> for GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
EM: UsesState<State = S>,
OT: ObserversTuple<S>,
S: UsesInput + 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.handlers
.pre_run_target(self, fuzzer, state, mgr, input);
let ret = (self.harness_fn.borrow_mut())(input);
self.handlers.post_run_target();
Ok(ret)
}
}
impl<H, HB, OT, S> HasObservers for GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
OT: ObserversTuple<S>,
S: UsesInput,
{
#[inline]
fn observers(&self) -> &OT {
&self.observers
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
&mut self.observers
}
}
impl<H, HB, OT, S> GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
OT: ObserversTuple<S>,
S: HasSolutions + HasClientPerfMonitor + HasCorpus,
{
/// 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.
/// * `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 new<EM, OF, Z>(
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>,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
Z: HasObjective<Objective = OF, State = S>,
{
let handlers = InProcessHandlers::new::<Self, EM, OF, Z>()?;
#[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)?;
}
Ok(Self {
harness_fn,
observers,
handlers,
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 handlers(&self) -> &InProcessHandlers {
&self.handlers
}
/// The inprocess handlers (mutable)
#[inline]
pub fn handlers_mut(&mut self) -> &mut InProcessHandlers {
&mut self.handlers
}
}
/// The struct has [`InProcessHandlers`].
#[cfg(windows)]
pub trait HasInProcessHandlers {
/// Get the in-process handlers.
fn inprocess_handlers(&self) -> &InProcessHandlers;
}
#[cfg(windows)]
impl<H, HB, OT, S> HasInProcessHandlers for GenericInProcessExecutor<H, HB, OT, S>
where
H: FnMut(&<S as UsesInput>::Input) -> ExitKind + ?Sized,
HB: BorrowMut<H>,
OT: ObserversTuple<S>,
S: HasSolutions + HasClientPerfMonitor + HasCorpus,
{
/// the timeout handler
#[inline]
fn inprocess_handlers(&self) -> &InProcessHandlers {
&self.handlers
}
}
/// The inmem executor's handlers.
#[derive(Debug)]
pub struct InProcessHandlers {
/// On crash C function pointer
#[cfg(any(unix, feature = "std"))]
pub crash_handler: *const c_void,
/// On timeout C function pointer
#[cfg(any(unix, feature = "std"))]
pub timeout_handler: *const c_void,
}
impl InProcessHandlers {
/// Call before running a target.
#[allow(clippy::unused_self)]
pub fn pre_run_target<E, EM, I, S, Z>(
&self,
_executor: &E,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) {
#[cfg(unix)]
unsafe {
let data = &mut GLOBAL_STATE;
write_volatile(
&mut data.current_input_ptr,
_input as *const _ as *const c_void,
);
write_volatile(
&mut data.executor_ptr,
_executor as *const _ as *const c_void,
);
data.crash_handler = self.crash_handler;
data.timeout_handler = self.timeout_handler;
// 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(&mut data.state_ptr, _state as *mut _ as *mut c_void);
write_volatile(&mut data.event_mgr_ptr, _mgr as *mut _ as *mut c_void);
write_volatile(&mut data.fuzzer_ptr, _fuzzer as *mut _ as *mut c_void);
compiler_fence(Ordering::SeqCst);
}
#[cfg(all(windows, feature = "std"))]
unsafe {
let data = &mut GLOBAL_STATE;
write_volatile(
&mut data.current_input_ptr,
_input as *const _ as *const c_void,
);
write_volatile(
&mut data.executor_ptr,
_executor as *const _ as *const c_void,
);
data.crash_handler = self.crash_handler;
data.timeout_handler = self.timeout_handler;
// 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(&mut data.state_ptr, _state as *mut _ as *mut c_void);
write_volatile(&mut data.event_mgr_ptr, _mgr as *mut _ as *mut c_void);
write_volatile(&mut data.fuzzer_ptr, _fuzzer as *mut _ as *mut c_void);
compiler_fence(Ordering::SeqCst);
}
}
/// Call after running a target.
#[allow(clippy::unused_self)]
pub fn post_run_target(&self) {
#[cfg(unix)]
unsafe {
write_volatile(&mut GLOBAL_STATE.current_input_ptr, ptr::null());
compiler_fence(Ordering::SeqCst);
}
#[cfg(all(windows, feature = "std"))]
unsafe {
write_volatile(&mut GLOBAL_STATE.current_input_ptr, ptr::null());
compiler_fence(Ordering::SeqCst);
}
}
/// Create new [`InProcessHandlers`].
#[cfg(not(all(windows, feature = "std")))]
pub fn new<E, EM, OF, Z>() -> Result<Self, Error>
where
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
#[cfg(unix)]
#[cfg_attr(miri, allow(unused_variables))]
unsafe {
let data = &mut GLOBAL_STATE;
#[cfg(feature = "std")]
unix_signal_handler::setup_panic_hook::<E, EM, OF, Z>();
#[cfg(not(miri))]
setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst);
Ok(Self {
crash_handler: unix_signal_handler::inproc_crash_handler::<E, EM, OF, Z>
as *const c_void,
timeout_handler: unix_signal_handler::inproc_timeout_handler::<E, EM, OF, Z>
as *const _,
})
}
#[cfg(not(any(unix, feature = "std")))]
Ok(Self {})
}
/// Create new [`InProcessHandlers`].
#[cfg(all(windows, feature = "std"))]
pub fn new<E, EM, OF, Z>() -> Result<Self, Error>
where
E: Executor<EM, Z> + HasObservers + HasInProcessHandlers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
unsafe {
let data = &mut GLOBAL_STATE;
#[cfg(feature = "std")]
windows_exception_handler::setup_panic_hook::<E, EM, OF, Z>();
setup_exception_handler(data)?;
compiler_fence(Ordering::SeqCst);
Ok(Self {
crash_handler: windows_exception_handler::inproc_crash_handler::<E, EM, OF, Z>
as *const _,
timeout_handler: windows_exception_handler::inproc_timeout_handler::<E, EM, OF, Z>
as *const c_void,
})
}
}
/// Replace the handlers with `nop` handlers, deactivating the handlers
#[must_use]
pub fn nop() -> Self {
let ret;
#[cfg(any(unix, feature = "std"))]
{
ret = Self {
crash_handler: ptr::null(),
timeout_handler: ptr::null(),
};
}
#[cfg(not(any(unix, feature = "std")))]
{
ret = Self {};
}
ret
}
}
/// The global state of the in-process harness.
#[derive(Debug)]
pub struct InProcessExecutorHandlerData {
state_ptr: *mut c_void,
event_mgr_ptr: *mut c_void,
fuzzer_ptr: *mut c_void,
executor_ptr: *const c_void,
pub(crate) current_input_ptr: *const c_void,
pub(crate) in_handler: bool,
/// The timeout handler
#[cfg(any(unix, feature = "std"))]
crash_handler: *const c_void,
/// The timeout handler
#[cfg(any(unix, feature = "std"))]
timeout_handler: *const c_void,
#[cfg(all(windows, feature = "std"))]
pub(crate) ptp_timer: Option<PTP_TIMER>,
#[cfg(all(windows, feature = "std"))]
pub(crate) in_target: u64,
#[cfg(all(windows, feature = "std"))]
pub(crate) critical: *mut c_void,
#[cfg(all(windows, feature = "std"))]
pub(crate) timeout_input_ptr: *mut c_void,
#[cfg(any(unix, feature = "std"))]
pub(crate) timeout_executor_ptr: *mut c_void,
}
unsafe impl Send for InProcessExecutorHandlerData {}
unsafe impl Sync for InProcessExecutorHandlerData {}
impl InProcessExecutorHandlerData {
#[cfg(any(unix, feature = "std"))]
fn executor_mut<'a, E>(&self) -> &'a mut E {
unsafe { (self.executor_ptr as *mut E).as_mut().unwrap() }
}
#[cfg(any(unix, feature = "std"))]
fn state_mut<'a, S>(&self) -> &'a mut S {
unsafe { (self.state_ptr as *mut S).as_mut().unwrap() }
}
#[cfg(any(unix, feature = "std"))]
fn event_mgr_mut<'a, EM>(&self) -> &'a mut EM {
unsafe { (self.event_mgr_ptr as *mut EM).as_mut().unwrap() }
}
#[cfg(any(unix, feature = "std"))]
fn fuzzer_mut<'a, Z>(&self) -> &'a mut Z {
unsafe { (self.fuzzer_ptr as *mut Z).as_mut().unwrap() }
}
#[cfg(any(unix, feature = "std"))]
fn take_current_input<'a, I>(&mut self) -> &'a I {
let r = unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() };
self.current_input_ptr = ptr::null();
r
}
#[cfg(any(unix, feature = "std"))]
pub(crate) fn is_valid(&self) -> bool {
!self.current_input_ptr.is_null()
}
#[cfg(any(unix, feature = "std"))]
fn timeout_executor_mut<'a, E>(&self) -> &'a mut crate::executors::timeout::TimeoutExecutor<E> {
unsafe {
(self.timeout_executor_ptr as *mut crate::executors::timeout::TimeoutExecutor<E>)
.as_mut()
.unwrap()
}
}
#[cfg(any(unix, feature = "std"))]
fn set_in_handler(&mut self, v: bool) -> bool {
let old = self.in_handler;
self.in_handler = v;
old
}
}
/// Exception handling needs some nasty unsafe.
pub(crate) static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
// The state ptr for signal handling
state_ptr: null_mut(),
// The event manager ptr for signal handling
event_mgr_ptr: null_mut(),
// The fuzzer ptr for signal handling
fuzzer_ptr: null_mut(),
// The executor ptr for signal handling
executor_ptr: ptr::null(),
// The current input for signal handling
current_input_ptr: ptr::null(),
in_handler: false,
// The crash handler fn
#[cfg(any(unix, feature = "std"))]
crash_handler: ptr::null(),
// The timeout handler fn
#[cfg(any(unix, feature = "std"))]
timeout_handler: ptr::null(),
#[cfg(all(windows, feature = "std"))]
ptp_timer: None,
#[cfg(all(windows, feature = "std"))]
in_target: 0,
#[cfg(all(windows, feature = "std"))]
critical: null_mut(),
#[cfg(all(windows, feature = "std"))]
timeout_input_ptr: null_mut(),
#[cfg(any(unix, feature = "std"))]
timeout_executor_ptr: null_mut(),
};
/// Get the inprocess [`crate::state::State`]
#[must_use]
pub fn inprocess_get_state<'a, S>() -> Option<&'a mut S> {
unsafe { (GLOBAL_STATE.state_ptr as *mut S).as_mut() }
}
/// Get the [`crate::events::EventManager`]
#[must_use]
pub fn inprocess_get_event_manager<'a, EM>() -> Option<&'a mut EM> {
unsafe { (GLOBAL_STATE.event_mgr_ptr as *mut EM).as_mut() }
}
/// Gets the inprocess [`crate::fuzzer::Fuzzer`]
#[must_use]
pub fn inprocess_get_fuzzer<'a, F>() -> Option<&'a mut F> {
unsafe { (GLOBAL_STATE.fuzzer_ptr as *mut F).as_mut() }
}
/// Gets the inprocess [`Executor`]
#[must_use]
pub fn inprocess_get_executor<'a, E>() -> Option<&'a mut E> {
unsafe { (GLOBAL_STATE.executor_ptr as *mut E).as_mut() }
}
/// Gets the inprocess input
#[must_use]
pub fn inprocess_get_input<'a, I>() -> Option<&'a I> {
unsafe { (GLOBAL_STATE.current_input_ptr as *const I).as_ref() }
}
/// Know if we ar eexecuting in a crash/timeout handler
#[must_use]
pub fn inprocess_in_handler() -> bool {
unsafe { GLOBAL_STATE.in_handler }
}
use crate::{
corpus::{Corpus, Testcase},
events::Event,
state::HasMetadata,
};
#[inline]
#[allow(clippy::too_many_arguments)]
/// Save state if it is an objective
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: HasSolutions + HasClientPerfMonitor + 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 mut new_testcase = Testcase::new(input.clone());
new_testcase.add_metadata(exitkind);
new_testcase.set_parent_id_optional(*state.corpus().current());
fuzzer
.objective_mut()
.append_metadata(state, 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(),
},
)
.expect("Could not save state in run_observers_and_save_state");
}
// Serialize the state and wait safely for the broker to read pending messages
event_mgr.on_restart(state).unwrap();
log::info!("Bye!");
}
/// The inprocess executor singal handling code for unix
#[cfg(unix)]
pub mod unix_signal_handler {
use alloc::vec::Vec;
#[cfg(feature = "std")]
use alloc::{boxed::Box, string::String};
use core::mem::transmute;
#[cfg(feature = "std")]
use std::{io::Write, panic};
use libafl_bolts::os::unix_signals::{ucontext_t, Handler, Signal};
use libc::siginfo_t;
#[cfg(feature = "std")]
use crate::inputs::Input;
use crate::{
events::{EventFirer, EventRestarter},
executors::{
inprocess::{run_observers_and_save_state, InProcessExecutorHandlerData, GLOBAL_STATE},
Executor, ExitKind, HasObservers,
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::UsesInput,
state::{HasClientPerfMonitor, HasCorpus, HasSolutions},
};
pub(crate) type HandlerFuncPtr = unsafe fn(
Signal,
&mut siginfo_t,
Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
);
/// A handler that does nothing.
/*pub fn nop_handler(
_signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
_data: &mut InProcessExecutorHandlerData,
) {
}*/
#[cfg(unix)]
impl Handler for InProcessExecutorHandlerData {
fn handle(
&mut self,
signal: Signal,
info: &mut siginfo_t,
context: Option<&mut ucontext_t>,
) {
unsafe {
let data = &mut GLOBAL_STATE;
let in_handler = data.set_in_handler(true);
match signal {
Signal::SigUser2 | Signal::SigAlarm => {
if !data.timeout_handler.is_null() {
let func: HandlerFuncPtr = transmute(data.timeout_handler);
(func)(signal, info, context, data);
}
}
_ => {
if !data.crash_handler.is_null() {
let func: HandlerFuncPtr = transmute(data.crash_handler);
(func)(signal, info, context, data);
}
}
}
data.set_in_handler(in_handler);
}
}
fn signals(&self) -> Vec<Signal> {
vec![
Signal::SigAlarm,
Signal::SigUser2,
Signal::SigAbort,
Signal::SigBus,
Signal::SigPipe,
Signal::SigFloatingPointException,
Signal::SigIllegalInstruction,
Signal::SigSegmentationFault,
Signal::SigTrap,
]
}
}
/// invokes the `post_exec` hook on all observer in case of panic
#[cfg(feature = "std")]
pub fn setup_panic_hook<E, EM, OF, Z>()
where
E: HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
let old_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
old_hook(panic_info);
let data = unsafe { &mut GLOBAL_STATE };
let in_handler = data.set_in_handler(true);
if data.is_valid() {
// We are fuzzing!
let executor = data.executor_mut::<E>();
let state = data.state_mut::<E::State>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
run_observers_and_save_state::<E, EM, OF, Z>(
executor,
state,
input,
fuzzer,
event_mgr,
ExitKind::Crash,
);
unsafe {
libc::_exit(128 + 6);
} // SIGABRT exit code
}
data.set_in_handler(in_handler);
}));
}
/// Timeout-Handler for in-process fuzzing.
/// It will store the current State to shmem, then exit.
///
/// # Safety
/// Well, signal handling is not safe
#[cfg(unix)]
pub unsafe fn inproc_timeout_handler<E, EM, OF, Z>(
_signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
if !data.timeout_executor_ptr.is_null()
&& data.timeout_executor_mut::<E>().handle_timeout(data)
{
return;
}
if !data.is_valid() {
log::warn!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing.");
return;
}
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>();
log::error!("Timeout in fuzz run.");
run_observers_and_save_state::<E, EM, OF, Z>(
executor,
state,
input,
fuzzer,
event_mgr,
ExitKind::Timeout,
);
libc::_exit(55);
}
/// Crash-Handler for in-process fuzzing.
/// Will be used for signal handling.
/// It will store the current State to shmem, then exit.
///
/// # Safety
/// Well, signal handling is not safe
#[allow(clippy::too_many_lines)]
pub unsafe fn inproc_crash_handler<E, EM, OF, Z>(
signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
let _context = _context.map(|p| {
&mut *(((p as *mut _ as *mut libc::c_void as usize) + 128) as *mut libc::c_void
as *mut ucontext_t)
});
log::error!("Crashed with {signal}");
if data.is_valid() {
let executor = data.executor_mut::<E>();
// disarms timeout in case of TimeoutExecutor
executor.post_run_reset();
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>();
log::error!("Child crashed!");
#[cfg(all(feature = "std", unix))]
{
let mut bsod = Vec::new();
{
let mut writer = std::io::BufWriter::new(&mut bsod);
writeln!(writer, "input: {:?}", input.generate_name(0)).unwrap();
libafl_bolts::minibsod::generate_minibsod(
&mut writer,
signal,
_info,
_context.as_deref(),
)
.unwrap();
writer.flush().unwrap();
}
log::error!("{}", std::str::from_utf8(&bsod).unwrap());
}
run_observers_and_save_state::<E, EM, OF, Z>(
executor,
state,
input,
fuzzer,
event_mgr,
ExitKind::Crash,
);
} else {
{
log::error!("Double crash\n");
#[cfg(target_os = "android")]
let si_addr = (_info._pad[0] as i64) | ((_info._pad[1] as i64) << 32);
#[cfg(not(target_os = "android"))]
let si_addr = { _info.si_addr() as usize };
log::error!(
"We crashed at addr 0x{si_addr:x}, but are not in the target... Bug in the fuzzer? Exiting."
);
#[cfg(all(feature = "std", unix))]
{
let mut bsod = Vec::new();
{
let mut writer = std::io::BufWriter::new(&mut bsod);
libafl_bolts::minibsod::generate_minibsod(
&mut writer,
signal,
_info,
_context.as_deref(),
)
.unwrap();
writer.flush().unwrap();
}
log::error!("{}", std::str::from_utf8(&bsod).unwrap());
}
}
#[cfg(feature = "std")]
{
log::error!("Type QUIT to restart the child");
let mut line = String::new();
while line.trim() != "QUIT" {
std::io::stdin().read_line(&mut line).unwrap();
}
}
// TODO tell the parent to not restart
}
libc::_exit(128 + (signal as i32));
}
}
/// Same as `inproc_crash_handler`, but this is called when address sanitizer exits, not from the exception handler
#[cfg(all(windows, feature = "std"))]
pub mod windows_asan_handler {
use alloc::string::String;
use core::sync::atomic::{compiler_fence, Ordering};
use windows::Win32::System::Threading::{
EnterCriticalSection, LeaveCriticalSection, CRITICAL_SECTION,
};
use crate::{
events::{EventFirer, EventRestarter},
executors::{
inprocess::{run_observers_and_save_state, GLOBAL_STATE},
Executor, ExitKind, HasObservers,
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::UsesInput,
state::{HasClientPerfMonitor, HasCorpus, HasSolutions},
};
/// # Safety
/// ASAN deatch handler
pub unsafe extern "C" fn asan_death_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: HasSolutions + HasClientPerfMonitor + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
let data = &mut GLOBAL_STATE;
data.set_in_handler(true);
// Have we set a timer_before?
if data.ptp_timer.is_some() {
/*
We want to prevent the timeout handler being run while the main thread is executing the crash handler
Timeout handler runs if it has access to the critical section or data.in_target == 0
Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid.
*/
compiler_fence(Ordering::SeqCst);
EnterCriticalSection(data.critical as *mut CRITICAL_SECTION);
compiler_fence(Ordering::SeqCst);
data.in_target = 0;
compiler_fence(Ordering::SeqCst);
LeaveCriticalSection(data.critical as *mut CRITICAL_SECTION);
compiler_fence(Ordering::SeqCst);
}
log::error!("ASAN detected crash!");
if data.current_input_ptr.is_null() {
{
log::error!("Double crash\n");
log::error!(
"ASAN detected crash but we're not in the target... Bug in the fuzzer? Exiting.",
);
}
#[cfg(feature = "std")]
{
log::error!("Type QUIT to restart the child");
let mut line = String::new();
while line.trim() != "QUIT" {
std::io::stdin().read_line(&mut line).unwrap();
}
}
// TODO tell the parent to not restart
} else {
let executor = data.executor_mut::<E>();
// reset timer
if data.ptp_timer.is_some() {
executor.post_run_reset();
data.ptp_timer = None;
}
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
log::error!("Child crashed!");
// Make sure we don't crash in the crash handler forever.
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,
);
}
// Don't need to exit, Asan will exit for us
// ExitProcess(1);
}
}
#[cfg(all(windows, feature = "std"))]
pub mod windows_exception_handler {
#[cfg(feature = "std")]
use alloc::boxed::Box;
use alloc::{string::String, vec::Vec};
use core::{
ffi::c_void,
mem::transmute,
ptr,
sync::atomic::{compiler_fence, Ordering},
};
#[cfg(feature = "std")]
use std::panic;
use libafl_bolts::os::windows_exceptions::{
ExceptionCode, Handler, CRASH_EXCEPTIONS, EXCEPTION_HANDLERS_SIZE, EXCEPTION_POINTERS,
};
use windows::Win32::System::Threading::{
EnterCriticalSection, ExitProcess, LeaveCriticalSection, CRITICAL_SECTION,
};
use crate::{
events::{EventFirer, EventRestarter},
executors::{
inprocess::{
run_observers_and_save_state, HasInProcessHandlers, InProcessExecutorHandlerData,
GLOBAL_STATE,
},
Executor, ExitKind, HasObservers,
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::UsesInput,
state::{HasClientPerfMonitor, HasCorpus, HasSolutions},
};
pub(crate) type HandlerFuncPtr =
unsafe fn(*mut EXCEPTION_POINTERS, &mut InProcessExecutorHandlerData);
/*pub unsafe fn nop_handler(
_code: ExceptionCode,
_exception_pointers: *mut EXCEPTION_POINTERS,
_data: &mut InProcessExecutorHandlerData,
) {
}*/
impl Handler for InProcessExecutorHandlerData {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn handle(&mut self, _code: ExceptionCode, exception_pointers: *mut EXCEPTION_POINTERS) {
unsafe {
let data = &mut GLOBAL_STATE;
let in_handler = data.set_in_handler(true);
if !data.crash_handler.is_null() {
let func: HandlerFuncPtr = transmute(data.crash_handler);
(func)(exception_pointers, data);
}
data.set_in_handler(in_handler);
}
}
fn exceptions(&self) -> Vec<ExceptionCode> {
let crash_list = CRASH_EXCEPTIONS.to_vec();
assert!(crash_list.len() < EXCEPTION_HANDLERS_SIZE - 1);
crash_list
}
}
/// invokes the `post_exec` hook on all observer in case of panic
///
/// # Safety
/// Well, exception handling is not safe
#[cfg(feature = "std")]
pub fn setup_panic_hook<E, EM, OF, Z>()
where
E: HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
let old_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
let data = unsafe { &mut GLOBAL_STATE };
let in_handler = data.set_in_handler(true);
// Have we set a timer_before?
unsafe {
if data.ptp_timer.is_some() {
/*
We want to prevent the timeout handler being run while the main thread is executing the crash handler
Timeout handler runs if it has access to the critical section or data.in_target == 0
Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid.
*/
compiler_fence(Ordering::SeqCst);
EnterCriticalSection(data.critical as *mut CRITICAL_SECTION);
compiler_fence(Ordering::SeqCst);
data.in_target = 0;
compiler_fence(Ordering::SeqCst);
LeaveCriticalSection(data.critical as *mut CRITICAL_SECTION);
compiler_fence(Ordering::SeqCst);
}
}
if data.is_valid() {
// We are fuzzing!
let executor = data.executor_mut::<E>();
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
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,
);
unsafe {
ExitProcess(1);
}
}
old_hook(panic_info);
data.set_in_handler(in_handler);
}));
}
/// Timeout handler for windows
///
/// # Safety
/// Well, exception handling is not safe
pub unsafe extern "system" fn inproc_timeout_handler<E, EM, OF, Z>(
_p0: *mut u8,
global_state: *mut c_void,
_p1: *mut u8,
) where
E: HasObservers + HasInProcessHandlers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
let data: &mut InProcessExecutorHandlerData =
&mut *(global_state as *mut InProcessExecutorHandlerData);
compiler_fence(Ordering::SeqCst);
EnterCriticalSection((data.critical as *mut CRITICAL_SECTION).as_mut().unwrap());
compiler_fence(Ordering::SeqCst);
if !data.timeout_executor_ptr.is_null()
&& data.timeout_executor_mut::<E>().handle_timeout(data)
{
compiler_fence(Ordering::SeqCst);
LeaveCriticalSection((data.critical as *mut CRITICAL_SECTION).as_mut().unwrap());
compiler_fence(Ordering::SeqCst);
return;
}
if data.in_target == 1 {
let executor = data.executor_mut::<E>();
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
if data.timeout_input_ptr.is_null() {
log::error!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing. Exiting");
} else {
log::error!("Timeout in fuzz run.");
let input = (data.timeout_input_ptr as *const <E::State as UsesInput>::Input)
.as_ref()
.unwrap();
data.timeout_input_ptr = ptr::null_mut();
run_observers_and_save_state::<E, EM, OF, Z>(
executor,
state,
input,
fuzzer,
event_mgr,
ExitKind::Timeout,
);
compiler_fence(Ordering::SeqCst);
ExitProcess(1);
}
}
compiler_fence(Ordering::SeqCst);
LeaveCriticalSection((data.critical as *mut CRITICAL_SECTION).as_mut().unwrap());
compiler_fence(Ordering::SeqCst);
// log::info!("TIMER INVOKED!");
}
/// Crash handler for windows
///
/// # Safety
/// Well, exception handling is not safe
#[allow(clippy::too_many_lines)]
pub unsafe fn inproc_crash_handler<E, EM, OF, Z>(
exception_pointers: *mut EXCEPTION_POINTERS,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
// Have we set a timer_before?
if data.ptp_timer.is_some() {
/*
We want to prevent the timeout handler being run while the main thread is executing the crash handler
Timeout handler runs if it has access to the critical section or data.in_target == 0
Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid.
*/
compiler_fence(Ordering::SeqCst);
EnterCriticalSection(data.critical as *mut CRITICAL_SECTION);
compiler_fence(Ordering::SeqCst);
data.in_target = 0;
compiler_fence(Ordering::SeqCst);
LeaveCriticalSection(data.critical as *mut CRITICAL_SECTION);
compiler_fence(Ordering::SeqCst);
}
// Is this really crash?
let mut is_crash = true;
#[cfg(feature = "std")]
if let Some(exception_pointers) = exception_pointers.as_mut() {
let code = ExceptionCode::try_from(
exception_pointers
.ExceptionRecord
.as_mut()
.unwrap()
.ExceptionCode
.0,
)
.unwrap();
let exception_list = data.exceptions();
if exception_list.contains(&code) {
log::error!("Crashed with {code}");
} else {
// log::trace!("Exception code received, but {code} is not in CRASH_EXCEPTIONS");
is_crash = false;
}
} else {
log::error!("Crashed without exception (probably due to SIGABRT)");
};
if data.current_input_ptr.is_null() {
{
log::error!("Double crash\n");
let crash_addr = exception_pointers
.as_mut()
.unwrap()
.ExceptionRecord
.as_mut()
.unwrap()
.ExceptionAddress as usize;
log::error!(
"We crashed at addr 0x{crash_addr:x}, but are not in the target... Bug in the fuzzer? Exiting."
);
}
#[cfg(feature = "std")]
{
log::error!("Type QUIT to restart the child");
let mut line = String::new();
while line.trim() != "QUIT" {
std::io::stdin().read_line(&mut line).unwrap();
}
}
// TODO tell the parent to not restart
} else {
let executor = data.executor_mut::<E>();
// reset timer
if data.ptp_timer.is_some() {
executor.post_run_reset();
data.ptp_timer = None;
}
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
if is_crash {
log::error!("Child crashed!");
} else {
// log::info!("Exception received!");
}
// Make sure we don't crash in the crash handler forever.
if is_crash {
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,
);
} else {
// This is not worth saving
}
}
if is_crash {
log::info!("Exiting!");
ExitProcess(1);
}
// log::info!("Not Exiting!");
}
}
/// The signature of the crash handler function
#[cfg(all(feature = "std", unix))]
pub(crate) type ForkHandlerFuncPtr = unsafe fn(
Signal,
&mut siginfo_t,
Option<&mut ucontext_t>,
data: &mut InProcessForkExecutorGlobalData,
);
/// The inmem fork executor's handlers.
#[cfg(all(feature = "std", unix))]
#[derive(Debug)]
pub struct InChildProcessHandlers {
/// On crash C function pointer
pub crash_handler: *const c_void,
/// On timeout C function pointer
pub timeout_handler: *const c_void,
}
#[cfg(all(feature = "std", unix))]
impl InChildProcessHandlers {
/// Call before running a target.
pub fn pre_run_target<E, I, S>(&self, executor: &E, state: &mut S, input: &I) {
unsafe {
let data = &mut FORK_EXECUTOR_GLOBAL_DATA;
write_volatile(
&mut data.executor_ptr,
executor as *const _ as *const c_void,
);
write_volatile(
&mut data.current_input_ptr,
input as *const _ as *const c_void,
);
write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void);
data.crash_handler = self.crash_handler;
data.timeout_handler = self.timeout_handler;
compiler_fence(Ordering::SeqCst);
}
}
/// Create new [`InChildProcessHandlers`].
pub fn new<E>() -> Result<Self, Error>
where
E: HasObservers,
{
#[cfg_attr(miri, allow(unused_variables))]
unsafe {
let data = &mut FORK_EXECUTOR_GLOBAL_DATA;
// child_signal_handlers::setup_child_panic_hook::<E, I, OT, S>();
#[cfg(not(miri))]
setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst);
Ok(Self {
crash_handler: child_signal_handlers::child_crash_handler::<E> as *const c_void,
timeout_handler: ptr::null(),
})
}
}
/// Create new [`InChildProcessHandlers`].
pub fn with_timeout<E>() -> Result<Self, Error>
where
E: HasObservers,
{
#[cfg_attr(miri, allow(unused_variables))]
unsafe {
let data = &mut FORK_EXECUTOR_GLOBAL_DATA;
// child_signal_handlers::setup_child_panic_hook::<E, I, OT, S>();
#[cfg(not(miri))]
setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst);
Ok(Self {
crash_handler: child_signal_handlers::child_crash_handler::<E> as *const c_void,
timeout_handler: child_signal_handlers::child_timeout_handler::<E> as *const c_void,
})
}
}
/// Replace the handlers with `nop` handlers, deactivating the handlers
#[must_use]
pub fn nop() -> Self {
Self {
crash_handler: ptr::null(),
timeout_handler: ptr::null(),
}
}
}
/// The global state of the in-process-fork harness.
#[cfg(all(feature = "std", unix))]
#[derive(Debug)]
pub(crate) struct InProcessForkExecutorGlobalData {
/// Stores a pointer to the fork executor struct
pub executor_ptr: *const c_void,
/// Stores a pointer to the state
pub state_ptr: *const c_void,
/// Stores a pointer to the current input
pub current_input_ptr: *const c_void,
/// Stores a pointer to the crash_handler function
pub crash_handler: *const c_void,
/// Stores a pointer to the timeout_handler function
pub timeout_handler: *const c_void,
}
#[cfg(all(feature = "std", unix))]
unsafe impl Sync for InProcessForkExecutorGlobalData {}
#[cfg(all(feature = "std", unix))]
unsafe impl Send for InProcessForkExecutorGlobalData {}
#[cfg(all(feature = "std", unix))]
impl InProcessForkExecutorGlobalData {
fn executor_mut<'a, E>(&self) -> &'a mut E {
unsafe { (self.executor_ptr as *mut E).as_mut().unwrap() }
}
fn state_mut<'a, S>(&self) -> &'a mut S {
unsafe { (self.state_ptr as *mut S).as_mut().unwrap() }
}
/*fn current_input<'a, I>(&self) -> &'a I {
unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() }
}*/
fn take_current_input<'a, I>(&mut self) -> &'a I {
let r = unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() };
self.current_input_ptr = ptr::null();
r
}
fn is_valid(&self) -> bool {
!self.current_input_ptr.is_null()
}
}
/// a static variable storing the global state
#[cfg(all(feature = "std", unix))]
pub(crate) static mut FORK_EXECUTOR_GLOBAL_DATA: InProcessForkExecutorGlobalData =
InProcessForkExecutorGlobalData {
executor_ptr: ptr::null(),
state_ptr: ptr::null(),
current_input_ptr: ptr::null(),
crash_handler: ptr::null(),
timeout_handler: ptr::null(),
};
#[cfg(all(feature = "std", unix))]
impl Handler for InProcessForkExecutorGlobalData {
fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: Option<&mut ucontext_t>) {
match signal {
Signal::SigUser2 | Signal::SigAlarm => unsafe {
if !FORK_EXECUTOR_GLOBAL_DATA.timeout_handler.is_null() {
let func: ForkHandlerFuncPtr =
transmute(FORK_EXECUTOR_GLOBAL_DATA.timeout_handler);
(func)(signal, info, context, &mut FORK_EXECUTOR_GLOBAL_DATA);
}
},
_ => unsafe {
if !FORK_EXECUTOR_GLOBAL_DATA.crash_handler.is_null() {
let func: ForkHandlerFuncPtr =
transmute(FORK_EXECUTOR_GLOBAL_DATA.crash_handler);
(func)(signal, info, context, &mut FORK_EXECUTOR_GLOBAL_DATA);
}
},
}
}
fn signals(&self) -> Vec<Signal> {
vec![
Signal::SigAlarm,
Signal::SigUser2,
Signal::SigAbort,
Signal::SigBus,
Signal::SigPipe,
Signal::SigFloatingPointException,
Signal::SigIllegalInstruction,
Signal::SigSegmentationFault,
Signal::SigTrap,
]
}
}
#[repr(C)]
#[cfg(all(feature = "std", unix, not(target_os = "linux")))]
struct Timeval {
pub tv_sec: i64,
pub tv_usec: i64,
}
#[cfg(all(feature = "std", unix, not(target_os = "linux")))]
impl Debug for Timeval {
#[allow(clippy::cast_sign_loss)]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"Timeval {{ tv_sec: {:?}, tv_usec: {:?} (tv: {:?}) }}",
self.tv_sec,
self.tv_usec,
Duration::new(self.tv_sec as _, (self.tv_usec * 1000) as _)
)
}
}
#[repr(C)]
#[cfg(all(feature = "std", unix, not(target_os = "linux")))]
#[derive(Debug)]
struct Itimerval {
pub it_interval: Timeval,
pub it_value: Timeval,
}
#[cfg(all(feature = "std", unix, not(target_os = "linux")))]
extern "C" {
fn setitimer(
which: libc::c_int,
new_value: *mut Itimerval,
old_value: *mut Itimerval,
) -> libc::c_int;
}
#[cfg(all(feature = "std", unix, not(target_os = "linux")))]
const ITIMER_REAL: libc::c_int = 0;
/// [`InProcessForkExecutor`] is an executor that forks the current process before each execution.
#[cfg(all(feature = "std", unix))]
pub struct InProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
harness_fn: &'a mut H,
shmem_provider: SP,
observers: OT,
handlers: InChildProcessHandlers,
phantom: PhantomData<S>,
}
/// Timeout executor for [`InProcessForkExecutor`]
#[cfg(all(feature = "std", unix))]
pub struct TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
harness_fn: &'a mut H,
shmem_provider: SP,
observers: OT,
handlers: InChildProcessHandlers,
#[cfg(target_os = "linux")]
itimerspec: libc::itimerspec,
#[cfg(all(unix, not(target_os = "linux")))]
itimerval: Itimerval,
phantom: PhantomData<S>,
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> Debug for InProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("InProcessForkExecutor")
.field("observers", &self.observers)
.field("shmem_provider", &self.shmem_provider)
.finish()
}
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> Debug for TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
#[cfg(target_os = "linux")]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("TimeoutInProcessForkExecutor")
.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("TimeoutInProcessForkExecutor")
.field("observers", &self.observers)
.field("shmem_provider", &self.shmem_provider)
.field("itimerval", &self.itimerval)
.finish();
}
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> UsesState for InProcessForkExecutor<'a, H, OT, S, SP>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type State = S;
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> UsesState for TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type State = S;
}
#[cfg(all(feature = "std", unix))]
impl<'a, EM, H, OT, S, SP, Z> Executor<EM, Z> for InProcessForkExecutor<'a, H, OT, S, SP>
where
EM: UsesState<State = S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput + HasExecutions,
SP: ShMemProvider,
Z: UsesState<State = 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.shmem_provider.pre_fork()?;
match fork() {
Ok(ForkResult::Child) => {
// Child
self.shmem_provider.post_fork(true)?;
self.handlers.pre_run_target(self, state, input);
self.observers
.pre_exec_child_all(state, input)
.expect("Failed to run post_exec on observers");
(self.harness_fn)(input);
self.observers
.post_exec_child_all(state, input, &ExitKind::Ok)
.expect("Failed to run post_exec on observers");
libc::_exit(0);
Ok(ExitKind::Ok)
}
Ok(ForkResult::Parent { child }) => {
// Parent
// log::info!("from parent {} child is {}", std::process::id(), child);
self.shmem_provider.post_fork(false)?;
let res = waitpid(child, None)?;
match res {
WaitStatus::Signaled(_, _, _) => Ok(ExitKind::Crash),
WaitStatus::Exited(_, code) => {
if code > 128 && code < 160 {
// Signal exit codes
Ok(ExitKind::Crash)
} else {
Ok(ExitKind::Ok)
}
}
_ => Ok(ExitKind::Ok),
}
}
Err(e) => Err(Error::from(e)),
}
}
}
}
#[cfg(all(feature = "std", unix))]
impl<'a, EM, H, OT, S, SP, Z> Executor<EM, Z> for TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
EM: UsesState<State = S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput + HasExecutions,
SP: ShMemProvider,
Z: UsesState<State = 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.shmem_provider.pre_fork()?;
match fork() {
Ok(ForkResult::Child) => {
// Child
self.shmem_provider.post_fork(true)?;
self.handlers.pre_run_target(self, state, input);
self.observers
.pre_exec_child_all(state, input)
.expect("Failed to run post_exec on observers");
#[cfg(target_os = "linux")]
{
let mut timerid: libc::timer_t = null_mut();
// creates a new per-process interval timer
// we can't do this from the parent, timerid is unique to each process.
libc::timer_create(
libc::CLOCK_MONOTONIC,
null_mut(),
addr_of_mut!(timerid),
);
// log::info!("Set timer! {:#?} {timerid:#?}", self.itimerspec);
let _: i32 = libc::timer_settime(
timerid,
0,
addr_of_mut!(self.itimerspec),
null_mut(),
);
}
#[cfg(not(target_os = "linux"))]
{
setitimer(ITIMER_REAL, &mut self.itimerval, null_mut());
}
// log::trace!("{v:#?} {}", nix::errno::errno());
(self.harness_fn)(input);
self.observers
.post_exec_child_all(state, input, &ExitKind::Ok)
.expect("Failed to run post_exec on observers");
libc::_exit(0);
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),
}
}
Err(e) => Err(Error::from(e)),
}
}
}
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> InProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
/// Creates a new [`InProcessForkExecutor`]
pub fn new<EM, OF, Z>(
harness_fn: &'a mut H,
observers: OT,
_fuzzer: &mut Z,
_state: &mut S,
_event_mgr: &mut EM,
shmem_provider: SP,
) -> Result<Self, Error>
where
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: HasSolutions + HasClientPerfMonitor,
Z: HasObjective<Objective = OF, State = S>,
{
let handlers = InChildProcessHandlers::new::<Self>()?;
Ok(Self {
harness_fn,
shmem_provider,
observers,
handlers,
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
}
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
S: UsesInput,
OT: ObserversTuple<S>,
SP: ShMemProvider,
{
/// Creates a new [`TimeoutInProcessForkExecutor`]
#[cfg(target_os = "linux")]
pub fn new<EM, OF, Z>(
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 + HasClientPerfMonitor,
Z: HasObjective<Objective = OF, State = S>,
{
let handlers = InChildProcessHandlers::with_timeout::<Self>()?;
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 {
harness_fn,
shmem_provider,
observers,
handlers,
itimerspec,
phantom: PhantomData,
})
}
/// Creates a new [`TimeoutInProcessForkExecutor`], non linux
#[cfg(not(target_os = "linux"))]
pub fn new<EM, OF, Z>(
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 + HasClientPerfMonitor,
Z: HasObjective<Objective = OF, State = S>,
{
let handlers = InChildProcessHandlers::with_timeout::<Self>()?;
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 {
harness_fn,
shmem_provider,
observers,
handlers,
itimerval,
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
}
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> UsesObservers for InProcessForkExecutor<'a, H, OT, S, SP>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type Observers = OT;
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> UsesObservers for TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: ?Sized + FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
S: UsesInput,
SP: ShMemProvider,
{
type Observers = OT;
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> HasObservers for InProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
S: UsesInput,
OT: ObserversTuple<S>,
SP: ShMemProvider,
{
#[inline]
fn observers(&self) -> &OT {
&self.observers
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
&mut self.observers
}
}
#[cfg(all(feature = "std", unix))]
impl<'a, H, OT, S, SP> HasObservers for TimeoutInProcessForkExecutor<'a, H, OT, S, SP>
where
H: FnMut(&S::Input) -> ExitKind + ?Sized,
S: UsesInput,
OT: ObserversTuple<S>,
SP: ShMemProvider,
{
#[inline]
fn observers(&self) -> &OT {
&self.observers
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
&mut self.observers
}
}
/// signal handlers and `panic_hooks` for the child process
#[cfg(all(feature = "std", unix))]
pub mod child_signal_handlers {
use alloc::boxed::Box;
use std::panic;
use libafl_bolts::os::unix_signals::{ucontext_t, Signal};
use libc::siginfo_t;
use super::{InProcessForkExecutorGlobalData, FORK_EXECUTOR_GLOBAL_DATA};
use crate::{
executors::{ExitKind, HasObservers},
inputs::UsesInput,
observers::ObserversTuple,
};
/// invokes the `post_exec_child` hook on all observer in case the child process panics
pub fn setup_child_panic_hook<E>()
where
E: HasObservers,
{
let old_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
old_hook(panic_info);
let data = unsafe { &mut FORK_EXECUTOR_GLOBAL_DATA };
if data.is_valid() {
let executor = data.executor_mut::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<E::State>();
// Invalidate data to not execute again the observer hooks in the crash handler
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
observers
.post_exec_child_all(state, input, &ExitKind::Crash)
.expect("Failed to run post_exec on observers");
// std::process::abort();
unsafe { libc::_exit(128 + 6) }; // ABORT exit code
}
}));
}
/// invokes the `post_exec` hook on all observer in case the child process crashes
///
/// # Safety
/// The function should only be called from a child crash handler.
/// It will dereference the `data` pointer and assume it's valid.
#[cfg(unix)]
pub(crate) unsafe fn child_crash_handler<E>(
_signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessForkExecutorGlobalData,
) where
E: HasObservers,
{
if data.is_valid() {
let executor = data.executor_mut::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<E::State>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
observers
.post_exec_child_all(state, input, &ExitKind::Crash)
.expect("Failed to run post_exec on observers");
}
libc::_exit(128 + (_signal as i32));
}
#[cfg(unix)]
pub(crate) unsafe fn child_timeout_handler<E>(
_signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessForkExecutorGlobalData,
) where
E: HasObservers,
{
if data.is_valid() {
let executor = data.executor_mut::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<E::State>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
observers
.post_exec_child_all(state, input, &ExitKind::Timeout)
.expect("Failed to run post_exec on observers");
}
libc::_exit(128 + (_signal as i32));
}
}
#[cfg(test)]
mod tests {
use core::marker::PhantomData;
use libafl_bolts::tuples::tuple_list;
#[cfg(all(feature = "std", feature = "fork", unix))]
use serial_test::serial;
use crate::{
events::NopEventManager,
executors::{inprocess::InProcessHandlers, Executor, ExitKind, InProcessExecutor},
inputs::{NopInput, UsesInput},
state::NopState,
NopFuzzer,
};
impl UsesInput for () {
type Input = NopInput;
}
#[test]
fn test_inmem_exec() {
let mut harness = |_buf: &NopInput| ExitKind::Ok;
let mut in_process_executor = InProcessExecutor::<_, _, _> {
harness_fn: &mut harness,
observers: tuple_list!(),
handlers: InProcessHandlers::nop(),
phantom: PhantomData,
};
let input = NopInput {};
in_process_executor
.run_target(
&mut NopFuzzer::new(),
&mut NopState::new(),
&mut NopEventManager::new(),
&input,
)
.unwrap();
}
#[test]
#[serial]
#[cfg_attr(miri, ignore)]
#[cfg(all(feature = "std", feature = "fork", unix))]
fn test_inprocessfork_exec() {
use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider};
use crate::{
events::SimpleEventManager,
executors::{inprocess::InChildProcessHandlers, InProcessForkExecutor},
state::NopState,
NopFuzzer,
};
let provider = StdShMemProvider::new().unwrap();
let mut harness = |_buf: &NopInput| ExitKind::Ok;
let mut in_process_fork_executor = InProcessForkExecutor::<_, (), _, _> {
harness_fn: &mut harness,
shmem_provider: provider,
observers: tuple_list!(),
handlers: InChildProcessHandlers::nop(),
phantom: PhantomData,
};
let input = NopInput {};
let mut fuzzer = NopFuzzer::new();
let mut state = NopState::new();
let mut mgr = SimpleEventManager::printing();
in_process_fork_executor
.run_target(&mut fuzzer, &mut state, &mut mgr, &input)
.unwrap();
}
}
#[cfg(feature = "python")]
#[allow(missing_docs)]
/// `InProcess` Python bindings
pub mod pybind {
use alloc::boxed::Box;
use pyo3::{prelude::*, types::PyBytes};
use crate::{
events::pybind::PythonEventManager,
executors::{inprocess::OwnedInProcessExecutor, pybind::PythonExecutor, ExitKind},
fuzzer::pybind::PythonStdFuzzerWrapper,
inputs::{BytesInput, HasBytesVec},
observers::pybind::PythonObserversTuple,
state::pybind::{PythonStdState, PythonStdStateWrapper},
};
#[pyclass(unsendable, name = "InProcessExecutor")]
#[derive(Debug)]
/// Python class for OwnedInProcessExecutor (i.e. InProcessExecutor with owned harness)
pub struct PythonOwnedInProcessExecutor {
/// Rust wrapped OwnedInProcessExecutor object
pub inner: OwnedInProcessExecutor<PythonObserversTuple, PythonStdState>,
}
#[pymethods]
impl PythonOwnedInProcessExecutor {
#[new]
fn new(
harness: PyObject,
py_observers: PythonObserversTuple,
py_fuzzer: &mut PythonStdFuzzerWrapper,
py_state: &mut PythonStdStateWrapper,
py_event_manager: &mut PythonEventManager,
) -> Self {
Self {
inner: OwnedInProcessExecutor::new(
Box::new(move |input: &BytesInput| {
Python::with_gil(|py| -> PyResult<()> {
let args = (PyBytes::new(py, input.bytes()),);
harness.call1(py, args)?;
Ok(())
})
.unwrap();
ExitKind::Ok
}),
py_observers,
py_fuzzer.unwrap_mut(),
py_state.unwrap_mut(),
py_event_manager,
)
.expect("Failed to create the Executor"),
}
}
#[must_use]
pub fn as_executor(slf: Py<Self>) -> PythonExecutor {
PythonExecutor::new_inprocess(slf)
}
}
/// Register the classes to the python module
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<PythonOwnedInProcessExecutor>()?;
Ok(())
}
}