From c18ca2d14fe7205d9447e29ebde146614aa26535 Mon Sep 17 00:00:00 2001 From: Addison Crump Date: Tue, 21 Jan 2025 17:26:51 +0100 Subject: [PATCH 1/2] implement enters target for guarding execution from double-mutability --- libafl/src/executors/hooks/timer.rs | 2 +- libafl/src/executors/inprocess/inner.rs | 75 ++++++++------- libafl/src/executors/inprocess/mod.rs | 40 ++++---- libafl/src/executors/inprocess/stateful.rs | 73 ++++++++------- libafl/src/executors/inprocess_fork/inner.rs | 91 ++++++++++--------- libafl/src/executors/inprocess_fork/mod.rs | 26 ++++-- .../src/executors/inprocess_fork/stateful.rs | 35 ++++--- libafl/src/executors/mod.rs | 24 +++++ 8 files changed, 220 insertions(+), 146 deletions(-) diff --git a/libafl/src/executors/hooks/timer.rs b/libafl/src/executors/hooks/timer.rs index 3fb7dd72e6..1cdb5b8f79 100644 --- a/libafl/src/executors/hooks/timer.rs +++ b/libafl/src/executors/hooks/timer.rs @@ -62,7 +62,7 @@ pub(crate) struct Itimerval { extern "C" { pub(crate) fn setitimer( which: libc::c_int, - new_value: *mut Itimerval, + new_value: *const Itimerval, old_value: *mut Itimerval, ) -> libc::c_int; } diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index f94317e0cd..cd1ddc10ce 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -5,6 +5,7 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, time::Duration, }; +use std::marker::PhantomData; use libafl_bolts::tuples::{tuple_list, Merge, RefIndexable}; #[cfg(windows)] @@ -22,7 +23,7 @@ use crate::{ ExecutorHooksTuple, }, inprocess::HasInProcessHooks, - Executor, HasObservers, + EntersTarget, Executor, HasObservers, }, feedbacks::Feedback, fuzzer::HasObjective, @@ -65,36 +66,62 @@ impl HasObservers for GenericInProcessExecutorInner } } -impl GenericInProcessExecutorInner -where - OT: ObserversTuple, +/// A simple guard, to be used with [`GenericInProcessExecutorInner`]. +#[derive(Debug)] +pub struct InProcessGuard<'a> { + phantom: PhantomData<&'a ()>, +} + +impl Drop for InProcessGuard<'_> { + fn drop(&mut self) { + unsafe { + let data = &raw mut GLOBAL_STATE; + + write_volatile(&raw mut (*data).current_input_ptr, null()); + compiler_fence(Ordering::SeqCst); + } + } +} + +impl EntersTarget + for GenericInProcessExecutorInner { + type Guard<'a> + = InProcessGuard<'a> + where + E: 'a, + EM: 'a, + I: 'a, + S: 'a, + Z: 'a; + /// This function marks the boundary between the fuzzer and the target /// /// # Safety /// This function sets a bunch of raw pointers in global variables, reused in other parts of /// the code. - // TODO: Remove EM and Z from function bound and add it to struct instead to avoid possible type confusion #[inline] - pub unsafe fn enter_target( - &mut self, - fuzzer: &mut Z, - state: &mut S, - mgr: &mut EM, - input: &I, - executor_ptr: *const c_void, - ) { + fn enter_target<'a>( + executor: &'a mut E, + fuzzer: &'a mut Z, + state: &'a mut S, + mgr: &'a mut EM, + input: &'a I, + ) -> Self::Guard<'a> { unsafe { let data = &raw mut GLOBAL_STATE; write_volatile( &raw mut (*data).current_input_ptr, ptr::from_ref(input) as *const c_void, ); - write_volatile(&raw mut (*data).executor_ptr, executor_ptr); + write_volatile( + &raw mut (*data).executor_ptr, + ptr::from_mut(executor) as *mut 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( - &raw mut ((*data).state_ptr), + &raw mut (*data).state_ptr, ptr::from_mut(state) as *mut c_void, ); write_volatile( @@ -107,22 +134,8 @@ where ); compiler_fence(Ordering::SeqCst); } - } - - /// This function marks the boundary between the fuzzer and the target - #[inline] - pub fn leave_target( - &mut self, - _fuzzer: &mut Z, - _state: &mut S, - _mgr: &mut EM, - _input: &I, - ) { - unsafe { - let data = &raw mut GLOBAL_STATE; - - write_volatile(&raw mut (*data).current_input_ptr, null()); - compiler_fence(Ordering::SeqCst); + InProcessGuard { + phantom: PhantomData, } } } diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index 07e3fa1233..6ad427e0cc 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -5,10 +5,8 @@ use alloc::boxed::Box; use core::{ borrow::BorrowMut, - ffi::c_void, fmt::{self, Debug, Formatter}, marker::PhantomData, - ptr, time::Duration, }; @@ -24,7 +22,7 @@ use crate::{ executors::{ hooks::{inprocess::InProcessHooks, ExecutorHooksTuple}, inprocess::inner::GenericInProcessExecutorInner, - Executor, ExitKind, HasObservers, + EntersTarget, Executor, ExitKind, HasObservers, }, feedbacks::Feedback, fuzzer::HasObjective, @@ -57,7 +55,7 @@ pub type OwnedInProcessExecutor = GenericInProcessExecutor< /// The inmem executor simply calls a target function, then returns afterwards. pub struct GenericInProcessExecutor { - harness_fn: HB, + harness_fn: Option, inner: GenericInProcessExecutorInner, phantom: PhantomData<(*const H, HB)>, } @@ -91,17 +89,21 @@ where input: &I, ) -> Result { *state.executions_mut() += 1; - unsafe { - let executor_ptr = ptr::from_ref(self) as *const c_void; - self.inner - .enter_target(fuzzer, state, mgr, input, executor_ptr); - } self.inner.hooks.pre_exec_all(state, input); + let Some(mut harness_fn) = self.harness_fn.take() else { + return Err(Error::illegal_state("We attempted to call the target without a harness function. This indicates that we somehow called the harness again from within the panic handler.")); + }; + + let guard = GenericInProcessExecutorInner::::enter_target( + self, fuzzer, state, mgr, input, + ); + + let ret = harness_fn.borrow_mut()(input); - let ret = self.harness_fn.borrow_mut()(input); + drop(guard); + self.harness_fn = Some(harness_fn); self.inner.hooks.post_exec_all(state, input); - self.inner.leave_target(fuzzer, state, mgr, input); Ok(ret) } } @@ -176,7 +178,7 @@ where )?; Ok(Self { - harness_fn, + harness_fn: Some(harness_fn), inner, phantom: PhantomData, }) @@ -213,7 +215,7 @@ where )?; Ok(Self { - harness_fn, + harness_fn: Some(harness_fn), inner, phantom: PhantomData, }) @@ -276,7 +278,7 @@ where )?; Ok(Self { - harness_fn, + harness_fn: Some(harness_fn), inner, phantom: PhantomData, }) @@ -309,7 +311,7 @@ where )?; Ok(Self { - harness_fn, + harness_fn: Some(harness_fn), inner, phantom: PhantomData, }) @@ -317,14 +319,14 @@ where /// Retrieve the harness function. #[inline] - pub fn harness(&self) -> &H { - self.harness_fn.borrow() + pub fn harness(&self) -> Option<&H> { + self.harness_fn.as_ref().map(|h| h.borrow()) } /// Retrieve the harness function for a mutable reference. #[inline] - pub fn harness_mut(&mut self) -> &mut H { - self.harness_fn.borrow_mut() + pub fn harness_mut(&mut self) -> Option<&mut H> { + self.harness_fn.as_mut().map(|h| h.borrow_mut()) } /// The inprocess handlers diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs index 943afffcae..93ec9c012b 100644 --- a/libafl/src/executors/inprocess/stateful.rs +++ b/libafl/src/executors/inprocess/stateful.rs @@ -1,10 +1,8 @@ use alloc::boxed::Box; use core::{ borrow::BorrowMut, - ffi::c_void, fmt::{self, Debug, Formatter}, marker::PhantomData, - ptr, time::Duration, }; @@ -15,7 +13,7 @@ use crate::{ executors::{ hooks::{inprocess::InProcessHooks, ExecutorHooksTuple}, inprocess::{GenericInProcessExecutorInner, HasInProcessHooks}, - Executor, ExitKind, HasObservers, + EntersTarget, Executor, ExitKind, HasObservers, }, feedbacks::Feedback, fuzzer::HasObjective, @@ -46,12 +44,12 @@ pub type OwnedInProcessExecutor = StatefulGenericInProcessExecutor /// The harness can access the internal state of the executor. pub struct StatefulGenericInProcessExecutor { /// The harness function, being executed for each fuzzing loop execution - harness_fn: HB, + harness_fn: Option, /// The state used as argument of the harness - pub exposed_executor_state: ES, + exposed_executor_state: Option, /// Inner state of the executor - pub inner: GenericInProcessExecutorInner, - phantom: PhantomData<(ES, *const H)>, + inner: GenericInProcessExecutorInner, + phantom: PhantomData<*const H>, } impl Debug for StatefulGenericInProcessExecutor @@ -69,7 +67,7 @@ where impl Executor for StatefulGenericInProcessExecutor where - H: FnMut(&mut ES, &mut S, &I) -> ExitKind + Sized, + H: FnMut(&mut ES, &I) -> ExitKind + Sized, HB: BorrowMut, HT: ExecutorHooksTuple, OT: ObserversTuple, @@ -83,17 +81,24 @@ where input: &I, ) -> Result { *state.executions_mut() += 1; - unsafe { - let executor_ptr = ptr::from_ref(self) as *const c_void; - self.inner - .enter_target(fuzzer, state, mgr, input, executor_ptr); - } + self.inner.hooks.pre_exec_all(state, input); + let Some(mut harness_fn) = self.harness_fn.take() else { + return Err(Error::illegal_state("We attempted to call the target without a harness function. This indicates that we somehow called the harness again from within the panic handler.")); + }; + let Some(mut exposed_executor_state) = self.exposed_executor_state.take() else { + return Err(Error::illegal_state("We attempted to call the target without a harness function. This indicates that we somehow called the harness again from within the panic handler.")); + }; + let guard = GenericInProcessExecutorInner::::enter_target( + self, fuzzer, state, mgr, input, + ); - let ret = self.harness_fn.borrow_mut()(&mut self.exposed_executor_state, state, input); + let ret = harness_fn.borrow_mut()(&mut exposed_executor_state, input); + drop(guard); + self.harness_fn = Some(harness_fn); + self.exposed_executor_state = Some(exposed_executor_state); self.inner.hooks.post_exec_all(state, input); - self.inner.leave_target(fuzzer, state, mgr, input); Ok(ret) } } @@ -101,7 +106,7 @@ where impl HasObservers for StatefulGenericInProcessExecutor where - H: FnMut(&mut ES, &mut S, &I) -> ExitKind + Sized, + H: FnMut(&mut ES, &I) -> ExitKind + Sized, HB: BorrowMut, HT: ExecutorHooksTuple, OT: ObserversTuple, @@ -120,7 +125,7 @@ where impl<'a, H, I, OT, S, ES> StatefulInProcessExecutor<'a, ES, H, I, OT, S> where - H: FnMut(&mut ES, &mut S, &I) -> ExitKind + Sized, + H: FnMut(&mut ES, &I) -> ExitKind + Sized, OT: ObserversTuple, S: HasExecutions + HasSolutions + HasCurrentTestcase, I: Clone + Input, @@ -177,8 +182,8 @@ where )?; Ok(Self { - harness_fn, - exposed_executor_state, + harness_fn: Some(harness_fn), + exposed_executor_state: Some(exposed_executor_state), inner, phantom: PhantomData, }) @@ -216,8 +221,8 @@ where )?; Ok(Self { - harness_fn, - exposed_executor_state, + harness_fn: Some(harness_fn), + exposed_executor_state: Some(exposed_executor_state), inner, phantom: PhantomData, }) @@ -226,19 +231,19 @@ where impl StatefulGenericInProcessExecutor { /// The executor state given to the harness - pub fn exposed_executor_state(&self) -> &ES { - &self.exposed_executor_state + pub fn exposed_executor_state(&self) -> Option<&ES> { + self.exposed_executor_state.as_ref() } /// The mutable executor state given to the harness - pub fn exposed_executor_state_mut(&mut self) -> &mut ES { - &mut self.exposed_executor_state + pub fn exposed_executor_state_mut(&mut self) -> Option<&mut ES> { + self.exposed_executor_state.as_mut() } } impl StatefulGenericInProcessExecutor where - H: FnMut(&mut ES, &mut S, &I) -> ExitKind + Sized, + H: FnMut(&mut ES, &I) -> ExitKind + Sized, HB: BorrowMut, HT: ExecutorHooksTuple, I: Input + Clone, @@ -295,8 +300,8 @@ where )?; Ok(Self { - harness_fn, - exposed_executor_state, + harness_fn: Some(harness_fn), + exposed_executor_state: Some(exposed_executor_state), inner, phantom: PhantomData, }) @@ -331,8 +336,8 @@ where )?; Ok(Self { - harness_fn, - exposed_executor_state, + harness_fn: Some(harness_fn), + exposed_executor_state: Some(exposed_executor_state), inner, phantom: PhantomData, }) @@ -340,14 +345,14 @@ where /// Retrieve the harness function. #[inline] - pub fn harness(&self) -> &H { - self.harness_fn.borrow() + pub fn harness(&self) -> Option<&H> { + self.harness_fn.as_ref().map(|h| h.borrow()) } /// Retrieve the harness function for a mutable reference. #[inline] - pub fn harness_mut(&mut self) -> &mut H { - self.harness_fn.borrow_mut() + pub fn harness_mut(&mut self) -> Option<&mut H> { + self.harness_fn.as_mut().map(|h| h.borrow_mut()) } /// The inprocess handlers diff --git a/libafl/src/executors/inprocess_fork/inner.rs b/libafl/src/executors/inprocess_fork/inner.rs index 7a9bb569fa..dde8f68b1a 100644 --- a/libafl/src/executors/inprocess_fork/inner.rs +++ b/libafl/src/executors/inprocess_fork/inner.rs @@ -25,7 +25,7 @@ use crate::{ inprocess_fork::{InChildProcessHooks, FORK_EXECUTOR_GLOBAL_DATA}, ExecutorHooksTuple, }, - ExitKind, HasObservers, + EntersTarget, ExitKind, HasObservers, }, observers::ObserversTuple, Error, @@ -110,21 +110,23 @@ where OT: ObserversTuple, SP: ShMemProvider, { - pub(super) unsafe fn pre_run_target_child( - &mut self, - fuzzer: &mut Z, - state: &mut S, - mgr: &mut EM, - input: &I, - ) -> Result<(), Error> { + pub(super) unsafe fn pre_run_target_child<'a>( + &'a mut self, + fuzzer: &'a mut Z, + state: &'a mut S, + mgr: &'a mut EM, + input: &'a I, + ) -> Result, Error> { self.shmem_provider.post_fork(true)?; - self.enter_target(fuzzer, state, mgr, input); self.hooks.pre_exec_all(state, input); + let itimerspec = self.itimerspec; + self.observers .pre_exec_child_all(state, input) .expect("Failed to run post_exec on observers"); + let guard = Self::enter_target(self, fuzzer, state, mgr, input); #[cfg(target_os = "linux")] { @@ -134,30 +136,23 @@ where libc::timer_create(libc::CLOCK_MONOTONIC, null_mut(), &raw mut timerid); // log::info!("Set timer! {:#?} {timerid:#?}", self.itimerspec); - let _: i32 = libc::timer_settime(timerid, 0, &raw mut self.itimerspec, null_mut()); + let _: i32 = libc::timer_settime(timerid, 0, &itimerspec, null_mut()); } #[cfg(not(target_os = "linux"))] { - setitimer(ITIMER_REAL, &mut self.itimerval, null_mut()); + setitimer(ITIMER_REAL, &itimerval, null_mut()); } // log::trace!("{v:#?} {}", nix::errno::errno()); - Ok(()) + Ok(guard) } - pub(super) unsafe fn post_run_target_child( - &mut self, - fuzzer: &mut Z, - state: &mut S, - mgr: &mut EM, - input: &I, - ) { + pub(super) unsafe fn post_run_target_child(&mut self, state: &mut S, input: &I) { self.observers .post_exec_child_all(state, input, &ExitKind::Ok) .expect("Failed to run post_exec on observers"); self.hooks.post_exec_all(state, input); - self.leave_target(fuzzer, state, mgr, input); libc::_exit(0); } @@ -195,44 +190,58 @@ where } } -impl GenericInProcessForkExecutorInner -where - HT: ExecutorHooksTuple, - OT: ObserversTuple, +/// A simple guard, to be used with [`GenericInProcessForkExecutorInner`]. +#[derive(Debug)] +pub struct InProcessForkGuard<'a> { + phantom: PhantomData<&'a ()>, +} + +impl EntersTarget + for GenericInProcessForkExecutorInner { - #[inline] - /// This function marks the boundary between the fuzzer and the target. - pub fn enter_target(&mut self, _fuzzer: &mut Z, state: &mut S, _event_mgr: &mut EM, input: &I) { + type Guard<'a> + = InProcessForkGuard<'a> + where + E: 'a, + EM: 'a, + I: 'a, + S: 'a, + Z: 'a; + + fn enter_target<'a>( + executor: &'a mut E, + _fuzzer: &'a mut Z, + state: &'a mut S, + _event_mgr: &'a mut EM, + input: &'a I, + ) -> Self::Guard<'a> { unsafe { let data = &raw mut FORK_EXECUTOR_GLOBAL_DATA; write_volatile( &raw mut (*data).executor_ptr, - ptr::from_ref(self) as *const c_void, + ptr::from_ref(executor) as *const c_void, ); write_volatile( &raw mut (*data).current_input_ptr, ptr::from_ref(input) as *const c_void, ); write_volatile( - &raw mut ((*data).state_ptr), + &raw mut (*data).state_ptr, ptr::from_mut(state) as *mut c_void, ); compiler_fence(Ordering::SeqCst); } + InProcessForkGuard { + phantom: Default::default(), + } } +} - #[inline] - /// This function marks the boundary between the fuzzer and the target. - pub fn leave_target( - &mut self, - _fuzzer: &mut Z, - _state: &mut S, - _event_mgr: &mut EM, - _input: &I, - ) { - // do nothing - } - +impl GenericInProcessForkExecutorInner +where + HT: ExecutorHooksTuple, + OT: ObserversTuple, +{ /// Creates a new [`GenericInProcessForkExecutorInner`] with custom hooks #[cfg(target_os = "linux")] pub fn with_hooks( diff --git a/libafl/src/executors/inprocess_fork/mod.rs b/libafl/src/executors/inprocess_fork/mod.rs index 9144a0d14a..bb8a5e85de 100644 --- a/libafl/src/executors/inprocess_fork/mod.rs +++ b/libafl/src/executors/inprocess_fork/mod.rs @@ -74,7 +74,7 @@ where /// On Linux, when fuzzing a Rust target, set `panic = "abort"` in your `Cargo.toml` (see [Cargo documentation](https://doc.rust-lang.org/cargo/reference/profiles.html#panic)). /// Else panics can not be caught by `LibAFL`. pub struct GenericInProcessForkExecutor<'a, EM, H, HT, I, OT, S, SP, Z> { - harness_fn: &'a mut H, + harness_fn: Option<&'a mut H>, inner: GenericInProcessForkExecutorInner, } @@ -126,9 +126,17 @@ where match fork() { Ok(ForkResult::Child) => { // Child - self.inner.pre_run_target_child(fuzzer, state, mgr, input)?; - (self.harness_fn)(input); - self.inner.post_run_target_child(fuzzer, state, mgr, input); + let Some(harness_fn) = self.harness_fn.take() else { + return Err(Error::illegal_state("We attempted to call the target without a harness function. This indicates that we somehow called the harness again from within the panic handler.")); + }; + + let guard = self.inner.pre_run_target_child(fuzzer, state, mgr, input)?; + harness_fn(input); + drop(guard); + + self.inner.post_run_target_child(state, input); + + self.harness_fn = Some(harness_fn); Ok(ExitKind::Ok) } Ok(ForkResult::Parent { child }) => { @@ -160,7 +168,7 @@ where ) -> Result where { Ok(Self { - harness_fn, + harness_fn: Some(harness_fn), inner: GenericInProcessForkExecutorInner::with_hooks( userhooks, observers, @@ -175,14 +183,14 @@ where { /// Retrieve the harness function. #[inline] - pub fn harness(&self) -> &H { - self.harness_fn + pub fn harness(&self) -> Option<&H> { + self.harness_fn.as_deref() } /// Retrieve the harness function for a mutable reference. #[inline] - pub fn harness_mut(&mut self) -> &mut H { - self.harness_fn + pub fn harness_mut(&mut self) -> Option<&mut H> { + self.harness_fn.as_deref_mut() } } diff --git a/libafl/src/executors/inprocess_fork/stateful.rs b/libafl/src/executors/inprocess_fork/stateful.rs index f6a206bf20..61103199ce 100644 --- a/libafl/src/executors/inprocess_fork/stateful.rs +++ b/libafl/src/executors/inprocess_fork/stateful.rs @@ -61,9 +61,9 @@ where /// [`StatefulGenericInProcessForkExecutor`] is an executor that forks the current process before each execution. Harness can access some internal state. pub struct StatefulGenericInProcessForkExecutor<'a, EM, ES, H, HT, I, OT, S, SP, Z> { /// The harness function, being executed for each fuzzing loop execution - harness_fn: &'a mut H, + harness_fn: Option<&'a mut H>, /// The state used as argument of the harness - pub exposed_executor_state: ES, + pub exposed_executor_state: Option, /// Inner state of the executor pub inner: GenericInProcessForkExecutorInner, } @@ -116,9 +116,22 @@ where match fork() { Ok(ForkResult::Child) => { // Child - self.inner.pre_run_target_child(fuzzer, state, mgr, input)?; - (self.harness_fn)(&mut self.exposed_executor_state, input); - self.inner.post_run_target_child(fuzzer, state, mgr, input); + let Some(harness_fn) = self.harness_fn.take() else { + return Err(Error::illegal_state("We attempted to call the target without a harness function. This indicates that we somehow called the harness again from within the panic handler.")); + }; + let Some(mut exposed_executor_state) = self.exposed_executor_state.take() + else { + return Err(Error::illegal_state("We attempted to call the target without a harness function. This indicates that we somehow called the harness again from within the panic handler.")); + }; + + let guard = self.inner.pre_run_target_child(fuzzer, state, mgr, input)?; + harness_fn(&mut exposed_executor_state, input); + drop(guard); + + self.inner.post_run_target_child(state, input); + + self.harness_fn = Some(harness_fn); + self.exposed_executor_state = Some(exposed_executor_state); Ok(ExitKind::Ok) } Ok(ForkResult::Parent { child }) => { @@ -151,8 +164,8 @@ where shmem_provider: SP, ) -> Result { Ok(Self { - harness_fn, - exposed_executor_state, + harness_fn: Some(harness_fn), + exposed_executor_state: Some(exposed_executor_state), inner: GenericInProcessForkExecutorInner::with_hooks( userhooks, observers, @@ -167,14 +180,14 @@ where /// Retrieve the harness function. #[inline] - pub fn harness(&self) -> &H { - self.harness_fn + pub fn harness(&self) -> Option<&H> { + self.harness_fn.as_deref() } /// Retrieve the harness function for a mutable reference. #[inline] - pub fn harness_mut(&mut self) -> &mut H { - self.harness_fn + pub fn harness_mut(&mut self) -> Option<&mut H> { + self.harness_fn.as_deref_mut() } } diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 993857df9e..9c9cf984f1 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -67,6 +67,30 @@ pub enum ExitKind { // Custom(Box), } +/// Trait which enters and exits the target. When entering the target, the corresponding fuzzer, +/// state, event manager, and input are held in a guard until it is dropped, at which point these +/// references may once again be used. +pub trait EntersTarget { + /// Holds the references to the fuzzer components until it is dropped. + type Guard<'a> + where + E: 'a, + EM: 'a, + I: 'a, + S: 'a, + Z: 'a; + + /// Enter the target. Until the guard is dropped, these references may not otherwise be used. + #[must_use] + fn enter_target<'a>( + executor: &'a mut E, + fuzzer: &'a mut Z, + state: &'a mut S, + event_mgr: &'a mut EM, + input: &'a I, + ) -> Self::Guard<'a>; +} + /// How one of the diffing executions finished. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[cfg_attr( From 846f9b55d90fc7d02d5a8a1cd62e52435039e966 Mon Sep 17 00:00:00 2001 From: Addison Crump Date: Tue, 21 Jan 2025 21:23:27 +0100 Subject: [PATCH 2/2] break everything, but demo using guards for entry/exit on holding items in global data --- libafl/src/executors/command.rs | 3 +- libafl/src/executors/hooks/inprocess.rs | 70 +--------- libafl/src/executors/hooks/inprocess_fork.rs | 3 - libafl/src/executors/hooks/mod.rs | 31 ++-- libafl/src/executors/hooks/timer.rs | 1 + libafl/src/executors/hooks/unix.rs | 54 +++---- libafl/src/executors/inprocess/inner.rs | 140 ++++++++++--------- libafl/src/executors/inprocess/mod.rs | 90 +++++------- libafl/src/executors/inprocess/stateful.rs | 27 +--- 9 files changed, 160 insertions(+), 259 deletions(-) diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 4e60cafebe..3694fe2587 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -49,7 +49,7 @@ use super::HasTimeout; #[cfg(target_os = "linux")] use crate::executors::hooks::ExecutorHooksTuple; use crate::{ - executors::{Executor, ExitKind, HasObservers}, + executors::{hooks::InitableExecutorHooksTuple, Executor, ExitKind, HasObservers}, inputs::HasTargetBytes, observers::{ObserversTuple, StdErrObserver, StdOutObserver}, state::HasExecutions, @@ -425,6 +425,7 @@ where OT: MatchName + ObserversTuple, S: HasExecutions, T: CommandConfigurator + Debug, + HT: InitableExecutorHooksTuple, { /// Linux specific low level implementation, to directly handle `fork`, `exec` and use linux /// `ptrace` diff --git a/libafl/src/executors/hooks/inprocess.rs b/libafl/src/executors/hooks/inprocess.rs index 6357f16592..0d0cf8485e 100644 --- a/libafl/src/executors/hooks/inprocess.rs +++ b/libafl/src/executors/hooks/inprocess.rs @@ -21,27 +21,11 @@ use windows::Win32::System::Threading::{CRITICAL_SECTION, PTP_TIMER}; #[cfg(feature = "std")] use crate::executors::hooks::timer::TimerStruct; -#[cfg(all(unix, feature = "std"))] -use crate::executors::hooks::unix::unix_signal_handler; -use crate::{ - events::{EventFirer, EventRestarter}, - executors::{hooks::ExecutorHook, inprocess::HasInProcessHooks, Executor, HasObservers}, - feedbacks::Feedback, - state::{HasExecutions, HasSolutions}, - Error, HasObjective, -}; -#[cfg(any(unix, windows))] -use crate::{inputs::Input, observers::ObserversTuple, state::HasCurrentTestcase}; +use crate::Error; /// The inmem executor's handlers. #[expect(missing_debug_implementations)] pub struct InProcessHooks { - /// 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 #[cfg(feature = "std")] pub timer: TimerStruct, @@ -185,53 +169,17 @@ impl HasTimeout for InProcessHooks { } } -impl ExecutorHook for InProcessHooks { - fn init(&mut self, _state: &mut S) {} - /// Call before running a target. - 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"))))] - self.timer_mut().set_timer(); - } - - /// Call after running a target. - fn post_exec(&mut self, _state: &mut S, _input: &I) { - // timeout stuff - // # Safety - // We're calling this only once per execution, in a single thread. - #[cfg(all(feature = "std", not(all(miri, target_vendor = "apple"))))] - self.timer_mut().unset_timer(); - } -} - impl InProcessHooks { /// Create new [`InProcessHooks`]. #[cfg(unix)] #[allow(unused_variables)] // for `exec_tmout` without `std` - pub fn new(exec_tmout: Duration) -> Result - where - E: Executor + HasObservers + HasInProcessHooks, - E::Observers: ObserversTuple, - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasExecutions + HasSolutions + HasCurrentTestcase, - Z: HasObjective, - I: Input + Clone, - { + pub fn new(exec_tmout: Duration) -> Result { // # Safety // We get a pointer to `GLOBAL_STATE` that will be initialized at this point in time. // This unsafe is needed in stable but not in nightly. Remove in the future(?) #[expect(unused_unsafe)] #[cfg(all(not(miri), unix, feature = "std"))] let data = unsafe { &raw mut GLOBAL_STATE }; - #[cfg(feature = "std")] - unix_signal_handler::setup_panic_hook::(); // # Safety // Setting up the signal handlers with a pointer to the `GLOBAL_STATE` which should not be NULL at this point. // We are the sole users of `GLOBAL_STATE` right now, and only dereference it in case of Segfault/Panic. @@ -243,12 +191,6 @@ impl InProcessHooks { compiler_fence(Ordering::SeqCst); Ok(Self { #[cfg(feature = "std")] - crash_handler: unix_signal_handler::inproc_crash_handler:: - as *const c_void, - #[cfg(feature = "std")] - timeout_handler: unix_signal_handler::inproc_timeout_handler:: - as *const _, - #[cfg(feature = "std")] timer: TimerStruct::new(exec_tmout), phantom: PhantomData, }) @@ -340,10 +282,6 @@ impl InProcessHooks { #[cfg(not(windows))] pub fn nop() -> Self { Self { - #[cfg(feature = "std")] - crash_handler: ptr::null(), - #[cfg(feature = "std")] - timeout_handler: ptr::null(), #[cfg(feature = "std")] timer: TimerStruct::new(Duration::from_millis(5000)), phantom: PhantomData, @@ -372,6 +310,8 @@ pub struct InProcessExecutorHandlerData { #[cfg(feature = "std")] pub(crate) timeout_handler: *const c_void, + pub(crate) timer: Option, + #[cfg(all(windows, feature = "std"))] pub(crate) ptp_timer: Option, #[cfg(all(windows, feature = "std"))] @@ -449,6 +389,8 @@ pub(crate) static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExec in_handler: false, + timer: None, + // The crash handler fn #[cfg(feature = "std")] crash_handler: ptr::null(), diff --git a/libafl/src/executors/hooks/inprocess_fork.rs b/libafl/src/executors/hooks/inprocess_fork.rs index 753a5f6ce6..11b23189c1 100644 --- a/libafl/src/executors/hooks/inprocess_fork.rs +++ b/libafl/src/executors/hooks/inprocess_fork.rs @@ -35,9 +35,6 @@ pub struct InChildProcessHooks { } impl ExecutorHook for InChildProcessHooks { - /// Init this hook - fn init(&mut self, _state: &mut S) {} - /// Call before running a target. fn pre_exec(&mut self, _state: &mut S, _input: &I) { unsafe { diff --git a/libafl/src/executors/hooks/mod.rs b/libafl/src/executors/hooks/mod.rs index 187704bca6..bfc9426c1b 100644 --- a/libafl/src/executors/hooks/mod.rs +++ b/libafl/src/executors/hooks/mod.rs @@ -24,42 +24,57 @@ pub mod timer; #[cfg(all(feature = "intel_pt", target_os = "linux"))] pub mod intel_pt; -/// The hook that runs before and after the executor runs the target -pub trait ExecutorHook { +pub trait InitableExecutorHook { /// Init this hook fn init(&mut self, state: &mut S); +} + +/// The hook that runs before and after the executor runs the target +pub trait ExecutorHook { /// The hook that runs before runs the target fn pre_exec(&mut self, state: &mut S, input: &I); /// The hook that runs before runs the target fn post_exec(&mut self, state: &mut S, input: &I); } -/// The hook that runs before and after the executor runs the target -pub trait ExecutorHooksTuple { +pub trait InitableExecutorHooksTuple { /// Init these hooks fn init_all(&mut self, state: &mut S); +} + +/// The hook that runs before and after the executor runs the target +pub trait ExecutorHooksTuple { /// The hooks that runs before runs the target fn pre_exec_all(&mut self, state: &mut S, input: &I); /// The hooks that runs after runs the target fn post_exec_all(&mut self, state: &mut S, input: &I); } -impl ExecutorHooksTuple for () { +impl InitableExecutorHooksTuple for () { fn init_all(&mut self, _state: &mut S) {} +} + +impl ExecutorHooksTuple for () { fn pre_exec_all(&mut self, _state: &mut S, _input: &I) {} fn post_exec_all(&mut self, _state: &mut S, _input: &I) {} } -impl ExecutorHooksTuple for (Head, Tail) +impl InitableExecutorHooksTuple for (Head, Tail) where - Head: ExecutorHook, - Tail: ExecutorHooksTuple, + Head: InitableExecutorHook, + Tail: InitableExecutorHooksTuple, { fn init_all(&mut self, state: &mut S) { self.0.init(state); self.1.init_all(state); } +} +impl ExecutorHooksTuple for (Head, Tail) +where + Head: ExecutorHook, + Tail: ExecutorHooksTuple, +{ fn pre_exec_all(&mut self, state: &mut S, input: &I) { self.0.pre_exec(state, input); self.1.pre_exec_all(state, input); diff --git a/libafl/src/executors/hooks/timer.rs b/libafl/src/executors/hooks/timer.rs index 1cdb5b8f79..070d65157d 100644 --- a/libafl/src/executors/hooks/timer.rs +++ b/libafl/src/executors/hooks/timer.rs @@ -70,6 +70,7 @@ extern "C" { /// The strcut about all the internals of the timer. /// This struct absorb all platform specific differences about timer. #[expect(missing_debug_implementations)] +#[derive(Debug)] pub struct TimerStruct { // timeout time (windows) #[cfg(windows)] diff --git a/libafl/src/executors/hooks/unix.rs b/libafl/src/executors/hooks/unix.rs index b78a585a9c..9863366a03 100644 --- a/libafl/src/executors/hooks/unix.rs +++ b/libafl/src/executors/hooks/unix.rs @@ -3,7 +3,7 @@ pub mod unix_signal_handler { use alloc::{boxed::Box, string::String, vec::Vec}; use core::mem::transmute; - use std::{io::Write, panic}; + use std::{io::Write, mem::MaybeUninit, panic}; use libafl_bolts::os::unix_signals::{ucontext_t, Signal, SignalHandler}; use libc::siginfo_t; @@ -13,7 +13,7 @@ pub mod unix_signal_handler { executors::{ common_signals, hooks::inprocess::{HasTimeout, InProcessExecutorHandlerData, GLOBAL_STATE}, - inprocess::{run_observers_and_save_state, HasInProcessHooks}, + inprocess::run_observers_and_save_state, Executor, ExitKind, HasObservers, }, feedbacks::Feedback, @@ -76,40 +76,18 @@ pub mod unix_signal_handler { } /// invokes the `post_exec` hook on all observer in case of panic - pub fn setup_panic_hook() - where - E: Executor + HasObservers, - E::Observers: ObserversTuple, - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasExecutions + HasSolutions + HasCurrentTestcase, - Z: HasObjective, - I: Input + Clone, - { + pub fn setup_panic_hook() { let old_hook = panic::take_hook(); panic::set_hook(Box::new(move |panic_info| unsafe { old_hook(panic_info); let data = &raw mut GLOBAL_STATE; let in_handler = (*data).set_in_handler(true); - if (*data).is_valid() { - // We are fuzzing! - let executor = (*data).executor_mut::(); - let state = (*data).state_mut::(); - let input = (*data).take_current_input::(); - let fuzzer = (*data).fuzzer_mut::(); - let event_mgr = (*data).event_mgr_mut::(); - run_observers_and_save_state::( - executor, - state, - input, - fuzzer, - event_mgr, - ExitKind::Crash, - ); + let func: HandlerFuncPtr = transmute((*data).crash_handler); + + let mut empty_siginfo = MaybeUninit::::uninit().assume_init(); + (func)(Signal::SigAbort, &mut empty_siginfo, None, data); - libc::_exit(128 + 6); // SIGABRT exit code - } (*data).set_in_handler(in_handler); })); } @@ -121,18 +99,18 @@ pub mod unix_signal_handler { /// Well, signal handling is not safe #[cfg(unix)] #[allow(clippy::needless_pass_by_value)] // nightly no longer requires this - pub unsafe fn inproc_timeout_handler( + pub unsafe fn inproc_timeout_handler( _signal: Signal, _info: &mut siginfo_t, _context: Option<&mut ucontext_t>, data: &mut InProcessExecutorHandlerData, ) where - E: Executor + HasInProcessHooks + HasObservers, + E: Executor + HasObservers, E::Observers: ObserversTuple, EM: EventFirer + EventRestarter, - OF: Feedback, S: HasExecutions + HasSolutions + HasCurrentTestcase, - Z: HasObjective, + Z: HasObjective, + Z::Objective: Feedback, I: Input + Clone, { // this stuff is for batch timeout @@ -158,7 +136,7 @@ pub mod unix_signal_handler { log::error!("Timeout in fuzz run."); - run_observers_and_save_state::( + run_observers_and_save_state::( executor, state, input, @@ -177,7 +155,7 @@ pub mod unix_signal_handler { /// # Safety /// Well, signal handling is not safe #[allow(clippy::needless_pass_by_value)] // nightly no longer requires this - pub unsafe fn inproc_crash_handler( + pub unsafe fn inproc_crash_handler( signal: Signal, _info: &mut siginfo_t, _context: Option<&mut ucontext_t>, @@ -186,9 +164,9 @@ pub mod unix_signal_handler { E: Executor + HasObservers, E::Observers: ObserversTuple, EM: EventFirer + EventRestarter, - OF: Feedback, S: HasExecutions + HasSolutions + HasCurrentTestcase, - Z: HasObjective, + Z: HasObjective, + Z::Objective: Feedback, I: Input + Clone, { #[cfg(all(target_os = "android", target_arch = "aarch64"))] @@ -229,7 +207,7 @@ pub mod unix_signal_handler { } } - run_observers_and_save_state::( + run_observers_and_save_state::( executor, state, input, diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index cd1ddc10ce..1a51b91eb0 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -1,47 +1,43 @@ use core::{ ffi::c_void, fmt::{self, Debug, Formatter}, + marker::PhantomData, ptr::{self, null, write_volatile}, sync::atomic::{compiler_fence, Ordering}, time::Duration, }; -use std::marker::PhantomData; -use libafl_bolts::tuples::{tuple_list, Merge, RefIndexable}; +use libafl_bolts::tuples::RefIndexable; #[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::GLOBAL_STATE, timer::TimerStruct, unix::unix_signal_handler, + InitableExecutorHooksTuple, }, - inprocess::HasInProcessHooks, EntersTarget, Executor, HasObservers, }, feedbacks::Feedback, - fuzzer::HasObjective, inputs::Input, observers::ObserversTuple, state::{HasCurrentTestcase, HasExecutions, HasSolutions}, - Error, + Error, HasObjective, }; /// The internal state of `GenericInProcessExecutor`. -pub struct GenericInProcessExecutorInner { +pub struct GenericInProcessExecutorInner { /// The observers, observing each run pub(super) observers: OT, // Crash and timeout hah - pub(super) hooks: (InProcessHooks, HT), + pub(super) hooks: HT, } -impl Debug for GenericInProcessExecutorInner +impl Debug for GenericInProcessExecutorInner where OT: Debug, { @@ -52,7 +48,7 @@ where } } -impl HasObservers for GenericInProcessExecutorInner { +impl HasObservers for GenericInProcessExecutorInner { type Observers = OT; #[inline] @@ -77,14 +73,24 @@ impl Drop for InProcessGuard<'_> { unsafe { let data = &raw mut GLOBAL_STATE; + write_volatile(&raw mut (*data).timeout_handler, null()); + write_volatile(&raw mut (*data).crash_handler, null()); write_volatile(&raw mut (*data).current_input_ptr, null()); + compiler_fence(Ordering::SeqCst); } } } -impl EntersTarget - for GenericInProcessExecutorInner +impl EntersTarget for GenericInProcessExecutorInner +where + E: Executor + HasObservers, + E::Observers: ObserversTuple, + EM: EventFirer + EventRestarter, + S: HasExecutions + HasSolutions + HasCurrentTestcase, + Z: HasObjective, + Z::Objective: Feedback, + I: Input + Clone, { type Guard<'a> = InProcessGuard<'a> @@ -132,6 +138,21 @@ impl EntersTarget &raw mut (*data).fuzzer_ptr, ptr::from_mut(fuzzer) as *mut c_void, ); + write_volatile( + &raw mut (*data).crash_handler, + unix_signal_handler::inproc_crash_handler:: as *mut c_void, + ); + write_volatile( + &raw mut (*data).timeout_handler, + unix_signal_handler::inproc_timeout_handler:: as *mut c_void, + ); + + // TODO should this be not(any(...))? Why would we only not run with miri on apple? + #[cfg(all(feature = "std", not(all(miri, target_vendor = "apple"))))] + if let Some(timer) = (*data).timer.as_mut() { + timer.set_timer(); + } + compiler_fence(Ordering::SeqCst); } InProcessGuard { @@ -140,14 +161,9 @@ impl EntersTarget } } -impl GenericInProcessExecutorInner -where - HT: ExecutorHooksTuple, - OT: ObserversTuple, - S: HasExecutions + HasSolutions, -{ +impl GenericInProcessExecutorInner { /// Create a new in mem executor with the default timeout (5 sec) - pub fn generic( + pub fn generic( user_hooks: HT, observers: OT, fuzzer: &mut Z, @@ -155,15 +171,9 @@ where event_mgr: &mut EM, ) -> Result where - E: Executor + HasObservers + HasInProcessHooks, - E::Observers: ObserversTuple, - EM: EventFirer + EventRestarter, - I: Input + Clone, - OF: Feedback, - S: HasCurrentTestcase + HasSolutions, - Z: HasObjective, + HT: InitableExecutorHooksTuple, { - Self::with_timeout_generic::( + Self::with_timeout_generic::( user_hooks, observers, fuzzer, @@ -175,7 +185,7 @@ where /// 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( + pub fn batched_timeout_generic( user_hooks: HT, observers: OT, fuzzer: &mut Z, @@ -184,18 +194,17 @@ where exec_tmout: Duration, ) -> Result where - E: Executor + HasObservers + HasInProcessHooks, - E::Observers: ObserversTuple, - EM: EventFirer + EventRestarter, - I: Input + Clone, - OF: Feedback, - S: HasCurrentTestcase + HasSolutions, - Z: HasObjective, + HT: InitableExecutorHooksTuple, { - let mut me = Self::with_timeout_generic::( + let me = Self::with_timeout_generic::( user_hooks, observers, fuzzer, state, event_mgr, exec_tmout, )?; - me.hooks_mut().0.timer_mut().batch_mode = true; + unsafe { + let data = &raw mut GLOBAL_STATE; + (*data).timer.as_mut().unwrap().batch_mode = true; + } + compiler_fence(Ordering::SeqCst); + Ok(me) } @@ -207,8 +216,8 @@ where /// * `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( - user_hooks: HT, + pub fn with_timeout_generic( + mut hooks: HT, observers: OT, _fuzzer: &mut Z, state: &mut S, @@ -216,16 +225,27 @@ where timeout: Duration, ) -> Result where - E: Executor + HasObservers + HasInProcessHooks, - E::Observers: ObserversTuple, - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasCurrentTestcase + HasSolutions, - Z: HasObjective, - I: Input + Clone, + HT: InitableExecutorHooksTuple, { - let default = InProcessHooks::new::(timeout)?; - let mut hooks = tuple_list!(default).merge(user_hooks); + // # Safety + // We get a pointer to `GLOBAL_STATE` that will be initialized at this point in time. + // This unsafe is needed in stable but not in nightly. Remove in the future(?) + #[expect(unused_unsafe)] + #[cfg(all(not(miri), unix, feature = "std"))] + let data = unsafe { &raw mut GLOBAL_STATE }; + // # Safety + // Setting up the signal handlers with a pointer to the `GLOBAL_STATE` which should not be NULL at this point. + // We are the sole users of `GLOBAL_STATE` right now, and only dereference it in case of Segfault/Panic. + // In that case we get the mutable borrow. Otherwise we don't use it. + #[cfg(all(not(miri), unix, feature = "std"))] + unsafe { + libafl_bolts::os::unix_signals::setup_signal_handler(data)?; + (*data).timer = Some(TimerStruct::new(timeout)); + } + compiler_fence(Ordering::SeqCst); + + #[cfg(feature = "std")] + unix_signal_handler::setup_panic_hook(); hooks.init_all(state); #[cfg(windows)] @@ -256,27 +276,13 @@ where /// The inprocess handlers #[inline] - pub fn hooks(&self) -> &(InProcessHooks, HT) { + pub fn hooks(&self) -> &HT { &self.hooks } /// The inprocess handlers (mutable) #[inline] - pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) { + pub fn hooks_mut(&mut self) -> &mut HT { &mut self.hooks } } - -impl HasInProcessHooks for GenericInProcessExecutorInner { - /// 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 - } -} diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index 6ad427e0cc..3017f68ed5 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -17,10 +17,10 @@ use crate::executors::hooks::inprocess::GLOBAL_STATE; #[cfg(any(unix, feature = "std"))] use crate::ExecutionProcessor; use crate::{ - corpus::{Corpus, Testcase}, + corpus::{Corpus, HasCurrentCorpusId, Testcase}, events::{Event, EventFirer, EventRestarter}, executors::{ - hooks::{inprocess::InProcessHooks, ExecutorHooksTuple}, + hooks::{ExecutorHooksTuple, InitableExecutorHooksTuple}, inprocess::inner::GenericInProcessExecutorInner, EntersTarget, Executor, ExitKind, HasObservers, }, @@ -56,8 +56,9 @@ pub type OwnedInProcessExecutor = GenericInProcessExecutor< /// The inmem executor simply calls a target function, then returns afterwards. pub struct GenericInProcessExecutor { harness_fn: Option, - inner: GenericInProcessExecutorInner, - phantom: PhantomData<(*const H, HB)>, + inner: GenericInProcessExecutorInner, + // TODO technically, we would like to be able to get rid of I, S here eventually + phantom: PhantomData<(*const H, I, S)>, } impl Debug for GenericInProcessExecutor @@ -75,11 +76,15 @@ where impl Executor for GenericInProcessExecutor where - S: HasExecutions, + I: Input, + S: HasExecutions + HasSolutions + HasCorpus + HasCurrentCorpusId, OT: ObserversTuple, HT: ExecutorHooksTuple, HB: BorrowMut, H: FnMut(&I) -> ExitKind + Sized, + Z: HasObjective, + Z::Objective: Feedback::Observers, S>, + EM: EventFirer + EventRestarter, { fn run_target( &mut self, @@ -94,9 +99,8 @@ where return Err(Error::illegal_state("We attempted to call the target without a harness function. This indicates that we somehow called the harness again from within the panic handler.")); }; - let guard = GenericInProcessExecutorInner::::enter_target( - self, fuzzer, state, mgr, input, - ); + let guard = + GenericInProcessExecutorInner::::enter_target(self, fuzzer, state, mgr, input); let ret = harness_fn.borrow_mut()(input); @@ -142,7 +146,7 @@ where OF: Feedback, Z: HasObjective, { - Self::with_timeout_generic::( + Self::with_timeout_generic::( tuple_list!(), harness_fn, observers, @@ -168,7 +172,7 @@ where OF: Feedback, Z: HasObjective, { - let inner = GenericInProcessExecutorInner::batched_timeout_generic::( + let inner = GenericInProcessExecutorInner::batched_timeout_generic::( tuple_list!(), observers, fuzzer, @@ -205,7 +209,7 @@ where OF: Feedback, Z: HasObjective, { - let inner = GenericInProcessExecutorInner::with_timeout_generic::( + let inner = GenericInProcessExecutorInner::with_timeout_generic::( tuple_list!(), observers, fuzzer, @@ -226,7 +230,7 @@ impl GenericInProcessExecutor where H: FnMut(&I) -> ExitKind + Sized, HB: BorrowMut, - HT: ExecutorHooksTuple, + HT: ExecutorHooksTuple + InitableExecutorHooksTuple, OT: ObserversTuple, S: HasCurrentTestcase + HasExecutions + HasSolutions, I: Input, @@ -245,7 +249,7 @@ where OF: Feedback, Z: HasObjective, { - Self::with_timeout_generic::( + Self::with_timeout_generic::( user_hooks, harness_fn, observers, @@ -258,7 +262,7 @@ where /// 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( + pub fn batched_timeout_generic( user_hooks: HT, harness_fn: HB, observers: OT, @@ -269,11 +273,10 @@ where ) -> Result where EM: EventFirer + EventRestarter, - - OF: Feedback, - Z: HasObjective, + Z: HasObjective, + Z::Objective: Feedback, { - let inner = GenericInProcessExecutorInner::batched_timeout_generic::( + let inner = GenericInProcessExecutorInner::batched_timeout_generic::( user_hooks, observers, fuzzer, state, event_mgr, exec_tmout, )?; @@ -292,7 +295,7 @@ where /// * `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( + pub fn with_timeout_generic( user_hooks: HT, harness_fn: HB, observers: OT, @@ -303,10 +306,10 @@ where ) -> Result where EM: EventFirer + EventRestarter, - OF: Feedback, - Z: HasObjective, + Z: HasObjective, + Z::Objective: Feedback, { - let inner = GenericInProcessExecutorInner::with_timeout_generic::( + let inner = GenericInProcessExecutorInner::with_timeout_generic::( user_hooks, observers, fuzzer, state, event_mgr, timeout, )?; @@ -331,45 +334,20 @@ where /// The inprocess handlers #[inline] - pub fn hooks(&self) -> &(InProcessHooks, HT) { + pub fn hooks(&self) -> &HT { self.inner.hooks() } /// The inprocess handlers (mutable) #[inline] - pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) { + pub fn hooks_mut(&mut self) -> &mut HT { self.inner.hooks_mut() } } -/// The struct has [`InProcessHooks`]. -pub trait HasInProcessHooks { - /// Get the in-process handlers. - fn inprocess_hooks(&self) -> &InProcessHooks; - - /// Get the mut in-process handlers. - fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks; -} - -impl HasInProcessHooks - for GenericInProcessExecutor -{ - /// the timeout handler - #[inline] - fn inprocess_hooks(&self) -> &InProcessHooks { - self.inner.inprocess_hooks() - } - - /// the timeout handler - #[inline] - fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks { - self.inner.inprocess_hooks_mut() - } -} - #[inline] /// Save state if it is an objective -pub fn run_observers_and_save_state( +pub fn run_observers_and_save_state( executor: &mut E, state: &mut S, input: &I, @@ -380,9 +358,9 @@ pub fn run_observers_and_save_state( E: Executor + HasObservers, E::Observers: ObserversTuple, EM: EventFirer + EventRestarter, - OF: Feedback, S: HasExecutions + HasSolutions + HasCorpus + HasCurrentTestcase, - Z: HasObjective, + Z: HasObjective, + Z::Objective: Feedback, I: Input + Clone, { let mut observers = executor.observers_mut(); @@ -439,15 +417,15 @@ pub fn run_observers_and_save_state( /// # Safety /// This will directly access `GLOBAL_STATE` and related data pointers #[cfg(any(unix, feature = "std"))] -pub unsafe fn generic_inproc_crash_handler() +pub unsafe fn generic_inproc_crash_handler() where E: Executor + HasObservers, E::Observers: ObserversTuple, EM: EventFirer + EventRestarter, - OF: Feedback, S: HasExecutions + HasSolutions + HasCurrentTestcase, I: Input + Clone, - Z: HasObjective + ExecutionProcessor, + Z: HasObjective + ExecutionProcessor, + Z::Objective: Feedback, { let data = &raw mut GLOBAL_STATE; let in_handler = (*data).set_in_handler(true); @@ -459,7 +437,7 @@ where let fuzzer = (*data).fuzzer_mut::(); let input = (*data).take_current_input::(); - run_observers_and_save_state::( + run_observers_and_save_state::( executor, state, input, diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs index 93ec9c012b..c400a2dc5e 100644 --- a/libafl/src/executors/inprocess/stateful.rs +++ b/libafl/src/executors/inprocess/stateful.rs @@ -12,7 +12,7 @@ use crate::{ events::{EventFirer, EventRestarter}, executors::{ hooks::{inprocess::InProcessHooks, ExecutorHooksTuple}, - inprocess::{GenericInProcessExecutorInner, HasInProcessHooks}, + inprocess::GenericInProcessExecutorInner, EntersTarget, Executor, ExitKind, HasObservers, }, feedbacks::Feedback, @@ -48,8 +48,8 @@ pub struct StatefulGenericInProcessExecutor { /// The state used as argument of the harness exposed_executor_state: Option, /// Inner state of the executor - inner: GenericInProcessExecutorInner, - phantom: PhantomData<*const H>, + inner: GenericInProcessExecutorInner, + phantom: PhantomData<(*const H, I, S)>, } impl Debug for StatefulGenericInProcessExecutor @@ -89,9 +89,8 @@ where let Some(mut exposed_executor_state) = self.exposed_executor_state.take() else { return Err(Error::illegal_state("We attempted to call the target without a harness function. This indicates that we somehow called the harness again from within the panic handler.")); }; - let guard = GenericInProcessExecutorInner::::enter_target( - self, fuzzer, state, mgr, input, - ); + let guard = + GenericInProcessExecutorInner::::enter_target(self, fuzzer, state, mgr, input); let ret = harness_fn.borrow_mut()(&mut exposed_executor_state, input); drop(guard); @@ -367,19 +366,3 @@ where self.inner.hooks_mut() } } - -impl HasInProcessHooks - for StatefulGenericInProcessExecutor -{ - /// the timeout handler - #[inline] - fn inprocess_hooks(&self) -> &InProcessHooks { - self.inner.inprocess_hooks() - } - - /// the timeout handler - #[inline] - fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks { - self.inner.inprocess_hooks_mut() - } -}