Don't write pointers to the crash handlers at every execution (#2935)

* make it safe

* aa

* forgot to put it back

* stateful

* comment

* lol

* aa

* aa

* aa

* win

* lol

* lol

* a

* a

* i hate rust

---------

Co-authored-by: Your Name <you@example.com>
This commit is contained in:
Dongjia "toka" Zhang 2025-02-05 14:00:09 +01:00 committed by GitHub
parent 8398f8f99a
commit c09feeba4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 52 additions and 61 deletions

View File

@ -43,12 +43,6 @@ use crate::{inputs::Input, observers::ObserversTuple, state::HasCurrentTestcase}
/// The inmem executor's handlers. /// The inmem executor's handlers.
#[expect(missing_debug_implementations)] #[expect(missing_debug_implementations)]
pub struct InProcessHooks<I, S> { pub struct InProcessHooks<I, S> {
/// On crash C function pointer
#[cfg(feature = "std")]
pub crash_handler: *const c_void,
/// On timeout C function pointer
#[cfg(feature = "std")]
pub timeout_handler: *const c_void,
/// `TImer` struct /// `TImer` struct
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub timer: TimerStruct, pub timer: TimerStruct,
@ -196,20 +190,12 @@ impl<I, S> ExecutorHook<I, S> for InProcessHooks<I, S> {
fn init(&mut self, _state: &mut S) {} fn init(&mut self, _state: &mut S) {}
/// Call before running a target. /// Call before running a target.
fn pre_exec(&mut self, _state: &mut S, _input: &I) { fn pre_exec(&mut self, _state: &mut S, _input: &I) {
#[cfg(feature = "std")]
unsafe {
let data = &raw mut GLOBAL_STATE;
(*data).crash_handler = self.crash_handler;
(*data).timeout_handler = self.timeout_handler;
}
#[cfg(all(feature = "std", not(all(miri, target_vendor = "apple"))))] #[cfg(all(feature = "std", not(all(miri, target_vendor = "apple"))))]
self.timer_mut().set_timer(); self.timer_mut().set_timer();
} }
/// Call after running a target. /// Call after running a target.
fn post_exec(&mut self, _state: &mut S, _input: &I) { fn post_exec(&mut self, _state: &mut S, _input: &I) {
// timeout stuff
// # Safety // # Safety
// We're calling this only once per execution, in a single thread. // We're calling this only once per execution, in a single thread.
#[cfg(all(feature = "std", not(all(miri, target_vendor = "apple"))))] #[cfg(all(feature = "std", not(all(miri, target_vendor = "apple"))))]
@ -247,15 +233,19 @@ impl<I, S> InProcessHooks<I, S> {
unsafe { unsafe {
setup_signal_handler(data)?; setup_signal_handler(data)?;
} }
#[cfg(feature = "std")]
unsafe {
let data = &raw mut GLOBAL_STATE;
(*data).crash_handler =
unix_signal_handler::inproc_crash_handler::<E, EM, I, OF, S, Z> as *const c_void;
(*data).timeout_handler =
unix_signal_handler::inproc_timeout_handler::<E, EM, I, OF, S, Z> as *const _;
}
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
Ok(Self { Ok(Self {
#[cfg(feature = "std")] #[cfg(feature = "std")]
crash_handler: unix_signal_handler::inproc_crash_handler::<E, EM, I, OF, S, Z>
as *const c_void,
#[cfg(feature = "std")]
timeout_handler: unix_signal_handler::inproc_timeout_handler::<E, EM, I, OF, S, Z>
as *const _,
#[cfg(feature = "std")]
timer: TimerStruct::new(exec_tmout), timer: TimerStruct::new(exec_tmout),
phantom: PhantomData, phantom: PhantomData,
}) })
@ -288,7 +278,7 @@ impl<I, S> InProcessHooks<I, S> {
>(); >();
setup_exception_handler(data)?; setup_exception_handler(data)?;
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
let crash_handler = (*data).crash_handler =
crate::executors::hooks::windows::windows_exception_handler::inproc_crash_handler::< crate::executors::hooks::windows::windows_exception_handler::inproc_crash_handler::<
E, E,
EM, EM,
@ -306,10 +296,9 @@ impl<I, S> InProcessHooks<I, S> {
S, S,
Z, Z,
> as *const c_void; > as *const c_void;
(*data).timeout_handler = timeout_handler;
let timer = TimerStruct::new(exec_tmout, timeout_handler); let timer = TimerStruct::new(exec_tmout, timeout_handler);
ret = Ok(Self { ret = Ok(Self {
crash_handler,
timeout_handler,
timer, timer,
phantom: PhantomData, phantom: PhantomData,
}); });
@ -347,10 +336,6 @@ impl<I, S> InProcessHooks<I, S> {
#[cfg(not(windows))] #[cfg(not(windows))]
pub fn nop() -> Self { pub fn nop() -> Self {
Self { Self {
#[cfg(feature = "std")]
crash_handler: ptr::null(),
#[cfg(feature = "std")]
timeout_handler: ptr::null(),
#[cfg(feature = "std")] #[cfg(feature = "std")]
timer: TimerStruct::new(Duration::from_millis(5000)), timer: TimerStruct::new(Duration::from_millis(5000)),
phantom: PhantomData, phantom: PhantomData,
@ -374,10 +359,10 @@ pub struct InProcessExecutorHandlerData {
/// The timeout handler /// The timeout handler
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub(crate) crash_handler: *const c_void, pub crash_handler: *const c_void,
/// The timeout handler /// The timeout handler
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub(crate) timeout_handler: *const c_void, pub timeout_handler: *const c_void,
#[cfg(all(windows, feature = "std"))] #[cfg(all(windows, feature = "std"))]
pub(crate) ptp_timer: Option<PTP_TIMER>, pub(crate) ptp_timer: Option<PTP_TIMER>,
@ -501,7 +486,7 @@ impl InProcessExecutorHandlerData {
} }
/// Exception handling needs some nasty unsafe. /// Exception handling needs some nasty unsafe.
pub(crate) static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData { pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
// The state ptr for signal handling // The state ptr for signal handling
state_ptr: null_mut(), state_ptr: null_mut(),
// The event manager ptr for signal handling // The event manager ptr for signal handling

View File

@ -27,10 +27,6 @@ use crate::{
/// The inmem fork executor's hooks. /// The inmem fork executor's hooks.
#[derive(Debug)] #[derive(Debug)]
pub struct InChildProcessHooks<I, S> { pub struct InChildProcessHooks<I, S> {
/// On crash C function pointer
pub crash_handler: *const c_void,
/// On timeout C function pointer
pub timeout_handler: *const c_void,
phantom: PhantomData<(I, S)>, phantom: PhantomData<(I, S)>,
} }
@ -39,14 +35,7 @@ impl<I, S> ExecutorHook<I, S> for InChildProcessHooks<I, S> {
fn init(&mut self, _state: &mut S) {} fn init(&mut self, _state: &mut S) {}
/// Call before running a target. /// Call before running a target.
fn pre_exec(&mut self, _state: &mut S, _input: &I) { fn pre_exec(&mut self, _state: &mut S, _input: &I) {}
unsafe {
let data = &raw mut FORK_EXECUTOR_GLOBAL_DATA;
(*data).crash_handler = self.crash_handler;
(*data).timeout_handler = self.timeout_handler;
compiler_fence(Ordering::SeqCst);
}
}
fn post_exec(&mut self, _state: &mut S, _input: &I) {} fn post_exec(&mut self, _state: &mut S, _input: &I) {}
} }
@ -65,11 +54,11 @@ impl<I, S> InChildProcessHooks<I, S> {
#[cfg(not(miri))] #[cfg(not(miri))]
setup_signal_handler(data)?; setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
(*data).crash_handler =
child_signal_handlers::child_crash_handler::<E, I, S> as *const c_void;
(*data).timeout_handler =
child_signal_handlers::child_timeout_handler::<E, I, S> as *const c_void;
Ok(Self { Ok(Self {
crash_handler: child_signal_handlers::child_crash_handler::<E, I, S>
as *const c_void,
timeout_handler: child_signal_handlers::child_timeout_handler::<E, I, S>
as *const c_void,
phantom: PhantomData, phantom: PhantomData,
}) })
} }
@ -79,8 +68,6 @@ impl<I, S> InChildProcessHooks<I, S> {
#[must_use] #[must_use]
pub fn nop() -> Self { pub fn nop() -> Self {
Self { Self {
crash_handler: null(),
timeout_handler: null(),
phantom: PhantomData, phantom: PhantomData,
} }
} }

View File

@ -90,16 +90,19 @@ where
input: &I, input: &I,
) -> Result<ExitKind, Error> { ) -> Result<ExitKind, Error> {
*state.executions_mut() += 1; *state.executions_mut() += 1;
unsafe { unsafe {
let executor_ptr = ptr::from_ref(self) as *const c_void; let executor_ptr = ptr::from_ref(self) as *const c_void;
self.inner self.inner
.enter_target(fuzzer, state, mgr, input, executor_ptr); .enter_target(fuzzer, state, mgr, input, executor_ptr);
} }
self.inner.hooks.pre_exec_all(state, input); self.inner.hooks.pre_exec_all(state, input);
let ret = self.harness_fn.borrow_mut()(input); let ret = self.harness_fn.borrow_mut()(input);
self.inner.hooks.post_exec_all(state, input); self.inner.hooks.post_exec_all(state, input);
self.inner.leave_target(fuzzer, state, mgr, input); self.inner.leave_target(fuzzer, state, mgr, input);
Ok(ret) Ok(ret)
} }

View File

@ -86,6 +86,7 @@ where
input: &I, input: &I,
) -> Result<ExitKind, Error> { ) -> Result<ExitKind, Error> {
*state.executions_mut() += 1; *state.executions_mut() += 1;
unsafe { unsafe {
let executor_ptr = ptr::from_ref(self) as *const c_void; let executor_ptr = ptr::from_ref(self) as *const c_void;
self.inner self.inner
@ -96,6 +97,7 @@ where
let ret = self.harness_fn.borrow_mut()(&mut self.exposed_executor_state, state, input); let ret = self.harness_fn.borrow_mut()(&mut self.exposed_executor_state, state, input);
self.inner.hooks.post_exec_all(state, input); self.inner.hooks.post_exec_all(state, input);
self.inner.leave_target(fuzzer, state, mgr, input); self.inner.leave_target(fuzzer, state, mgr, input);
Ok(ret) Ok(ret)
} }

View File

@ -14,7 +14,7 @@ use libafl::state::HasCorpus;
use libafl::{ use libafl::{
events::{EventFirer, EventRestarter}, events::{EventFirer, EventRestarter},
executors::{ executors::{
hooks::inprocess::InProcessExecutorHandlerData, hooks::inprocess::{InProcessExecutorHandlerData, GLOBAL_STATE},
inprocess::{stateful::StatefulInProcessExecutor, HasInProcessHooks}, inprocess::{stateful::StatefulInProcessExecutor, HasInProcessHooks},
inprocess_fork::stateful::StatefulInProcessForkExecutor, inprocess_fork::stateful::StatefulInProcessForkExecutor,
Executor, ExitKind, HasObservers, Executor, ExitKind, HasObservers,
@ -243,25 +243,39 @@ where
OF: Feedback<EM, I, OT, S>, OF: Feedback<EM, I, OT, S>,
Z: HasObjective<Objective = OF> + HasScheduler<I, S> + ExecutionProcessor<EM, I, OT, S>, Z: HasObjective<Objective = OF> + HasScheduler<I, S> + ExecutionProcessor<EM, I, OT, S>,
{ {
let mut inner = StatefulInProcessExecutor::with_timeout( let inner = StatefulInProcessExecutor::with_timeout(
harness_fn, emulator, observers, fuzzer, state, event_mgr, timeout, harness_fn, emulator, observers, fuzzer, state, event_mgr, timeout,
)?; )?;
let data = &raw mut GLOBAL_STATE;
#[cfg(feature = "usermode")] #[cfg(feature = "usermode")]
{ unsafe {
inner.inprocess_hooks_mut().crash_handler = // rewrite the crash handler pointer
(*data).crash_handler =
inproc_qemu_crash_handler::<Self, EM, ET, I, OF, S, Z> as *const c_void; inproc_qemu_crash_handler::<Self, EM, ET, I, OF, S, Z> as *const c_void;
} }
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::< unsafe {
StatefulInProcessExecutor<'a, EM, Emulator<C, CM, ED, ET, I, S, SM>, H, I, OT, S, Z>, // rewrite the timeout handler pointer
EM, (*data).timeout_handler = inproc_qemu_timeout_handler::<
ET, StatefulInProcessExecutor<
I, 'a,
OF, EM,
S, Emulator<C, CM, ED, ET, I, S, SM>,
Z, H,
> as *const c_void; I,
OT,
S,
Z,
>,
EM,
ET,
I,
OF,
S,
Z,
> as *const c_void;
}
Ok(Self { Ok(Self {
inner, inner,