diff --git a/fuzzers/qemu_launcher/src/instance.rs b/fuzzers/qemu_launcher/src/instance.rs index 81ee9b086b..a879308ecc 100644 --- a/fuzzers/qemu_launcher/src/instance.rs +++ b/fuzzers/qemu_launcher/src/instance.rs @@ -1,4 +1,4 @@ -use core::ptr::addr_of_mut; +use core::{fmt::Debug, ptr::addr_of_mut}; use std::{marker::PhantomData, process}; #[cfg(feature = "simplemgr")] @@ -70,7 +70,7 @@ pub struct Instance<'a, M: Monitor> { impl<'a, M: Monitor> Instance<'a, M> { pub fn run(&mut self, helpers: QT, state: Option) -> Result<(), Error> where - QT: QemuHelperTuple, + QT: QemuHelperTuple + Debug, { let mut hooks = QemuHooks::new(self.emu.clone(), helpers); diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess/mod.rs similarity index 71% rename from libafl/src/executors/inprocess.rs rename to libafl/src/executors/inprocess/mod.rs index 2fc826c270..f7b79d9000 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -41,7 +41,10 @@ use crate::{ Error, }; -/// The process executor simply calls a target function, as mutable reference to a closure +/// A version of `InProcessExecutor` with a state accessible from the harness. +pub mod stateful; + +/// The process executor simply calls a target function, as mutable reference to a closure. pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor; /// The inprocess executor that allows hooks @@ -56,6 +59,20 @@ pub type OwnedInProcessExecutor = GenericInProcessExecutor< S, >; +/// The internal state of `GenericInProcessExecutor`. +pub struct GenericInProcessExecutorInner +where + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + /// The observers, observing each run + observers: OT, + // Crash and timeout hah + hooks: (InProcessHooks, HT), + phantom: PhantomData, +} + /// The inmem executor simply calls a target function, then returns afterwards. #[allow(dead_code)] pub struct GenericInProcessExecutor @@ -66,13 +83,22 @@ where OT: ObserversTuple, S: State, { - /// The harness function, being executed for each fuzzing loop execution harness_fn: HB, - /// The observers, observing each run - observers: OT, - // Crash and timeout hah - hooks: (InProcessHooks, HT), - phantom: PhantomData<(S, *const H)>, + inner: GenericInProcessExecutorInner, + phantom: PhantomData<(*const H, HB)>, +} + +impl Debug for GenericInProcessExecutorInner +where + HT: ExecutorHooksTuple, + OT: ObserversTuple + Debug, + S: State, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("GenericInProcessExecutorState") + .field("observers", &self.observers) + .finish_non_exhaustive() + } } impl Debug for GenericInProcessExecutor @@ -85,15 +111,24 @@ where { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("GenericInProcessExecutor") + .field("inner", &self.inner) .field("harness_fn", &"") - .field("observers", &self.observers) .finish_non_exhaustive() } } +impl UsesState for GenericInProcessExecutorInner +where + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + type State = S; +} + impl UsesState for GenericInProcessExecutor where - H: ?Sized + FnMut(&S::Input) -> ExitKind, + H: FnMut(&S::Input) -> ExitKind + ?Sized, HB: BorrowMut, HT: ExecutorHooksTuple, OT: ObserversTuple, @@ -102,9 +137,18 @@ where type State = S; } +impl UsesObservers for GenericInProcessExecutorInner +where + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + type Observers = OT; +} + impl UsesObservers for GenericInProcessExecutor where - H: ?Sized + FnMut(&S::Input) -> ExitKind, + H: FnMut(&S::Input) -> ExitKind + ?Sized, HB: BorrowMut, HT: ExecutorHooksTuple, OT: ObserversTuple, @@ -131,21 +175,19 @@ where input: &Self::Input, ) -> Result { *state.executions_mut() += 1; - self.enter_target(fuzzer, state, mgr, input); - self.hooks.pre_exec_all(fuzzer, state, mgr, input); + self.inner.enter_target(fuzzer, state, mgr, input); + self.inner.hooks.pre_exec_all(fuzzer, state, mgr, input); let ret = (self.harness_fn.borrow_mut())(input); - self.hooks.post_exec_all(fuzzer, state, mgr, input); - self.leave_target(fuzzer, state, mgr, input); + self.inner.hooks.post_exec_all(fuzzer, state, mgr, input); + self.inner.leave_target(fuzzer, state, mgr, input); Ok(ret) } } -impl HasObservers for GenericInProcessExecutor +impl HasObservers for GenericInProcessExecutorInner where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - HB: BorrowMut, HT: ExecutorHooksTuple, OT: ObserversTuple, S: State, @@ -160,13 +202,31 @@ where &mut self.observers } } -impl GenericInProcessExecutor + +impl HasObservers for GenericInProcessExecutor where H: FnMut(&S::Input) -> ExitKind + ?Sized, HB: BorrowMut, HT: ExecutorHooksTuple, OT: ObserversTuple, S: State, +{ + #[inline] + fn observers(&self) -> &OT { + self.inner.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.inner.observers_mut() + } +} + +impl GenericInProcessExecutorInner +where + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, { /// This function marks the boundary between the fuzzer and the target #[inline] @@ -223,30 +283,27 @@ where } } -impl<'a, H, OT, S> InProcessExecutor<'a, H, OT, S> +impl GenericInProcessExecutorInner<(), OT, S> where - H: FnMut(&::Input) -> ExitKind + ?Sized, OT: ObserversTuple, S: HasExecutions + HasSolutions + HasCorpus + State, { /// Create a new in mem executor with the default timeout (5 sec) - pub fn new( - harness_fn: &'a mut H, + pub fn new( observers: OT, fuzzer: &mut Z, state: &mut S, event_mgr: &mut EM, ) -> Result where - Self: Executor, + E: Executor + HasObservers + HasInProcessHooks, EM: EventFirer + EventRestarter, OF: Feedback, S: State, Z: HasObjective, { - Self::with_timeout_generic( + Self::with_timeout_generic::( tuple_list!(), - harness_fn, observers, fuzzer, state, @@ -258,8 +315,7 @@ where /// Create a new in mem executor with the default timeout and use batch mode (5 sec) /// Do not use batched mode timeouts with cmplog cores. It is not supported #[cfg(all(feature = "std", target_os = "linux"))] - pub fn batched_timeouts( - harness_fn: &'a mut H, + pub fn batched_timeouts( observers: OT, fuzzer: &mut Z, state: &mut S, @@ -267,15 +323,14 @@ where exec_tmout: Duration, ) -> Result where - Self: Executor, + E: Executor + HasObservers + HasInProcessHooks, EM: EventFirer + EventRestarter, OF: Feedback, S: State, Z: HasObjective, { - let mut me = Self::with_timeout_generic( + let mut me = Self::with_timeout_generic::( tuple_list!(), - harness_fn, observers, fuzzer, state, @@ -293,8 +348,7 @@ where /// * `harness_fn` - the harness, executing the function /// * `observers` - the observers observing the target during execution /// This may return an error on unix, if signal handler setup fails - pub fn with_timeout( - harness_fn: &'a mut H, + pub fn with_timeout( observers: OT, _fuzzer: &mut Z, state: &mut S, @@ -302,13 +356,13 @@ where timeout: Duration, ) -> Result where - Self: Executor, + E: Executor + HasObservers + HasInProcessHooks, EM: EventFirer + EventRestarter, OF: Feedback, S: State, Z: HasObjective, { - let default = InProcessHooks::new::(timeout)?; + let default = InProcessHooks::new::(timeout)?; let mut hooks = tuple_list!(default).merge(tuple_list!()); hooks.init_all::(state); @@ -336,7 +390,6 @@ where } Ok(Self { - harness_fn, observers, hooks, phantom: PhantomData, @@ -344,13 +397,231 @@ where } } -impl GenericInProcessExecutor +impl<'a, H, OT, S> InProcessExecutor<'a, H, OT, S> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + OT: ObserversTuple, + S: HasExecutions + HasSolutions + HasCorpus + State, +{ + /// Create a new in mem executor with the default timeout (5 sec) + pub fn new( + harness_fn: &'a mut H, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + ) -> Result + where + Self: Executor + HasObservers, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + Self::with_timeout_generic( + tuple_list!(), + harness_fn, + observers, + fuzzer, + state, + event_mgr, + Duration::from_millis(5000), + ) + } + + /// Create a new in mem executor with the default timeout and use batch mode(5 sec) + #[cfg(all(feature = "std", target_os = "linux"))] + pub fn batched_timeouts( + harness_fn: &'a mut H, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + exec_tmout: Duration, + ) -> Result + where + Self: Executor + HasObservers + HasInProcessHooks, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let inner = GenericInProcessExecutorInner::batched_timeouts::( + observers, fuzzer, state, event_mgr, exec_tmout, + )?; + + Ok(Self { + harness_fn, + inner, + phantom: PhantomData, + }) + } + + /// Create a new in mem executor. + /// Caution: crash and restart in one of them will lead to odd behavior if multiple are used, + /// depending on different corpus or state. + /// * `user_hooks` - the hooks run before and after the harness's execution + /// * `harness_fn` - the harness, executing the function + /// * `observers` - the observers observing the target during execution + /// This may return an error on unix, if signal handler setup fails + pub fn with_timeout( + harness_fn: &'a mut H, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + timeout: Duration, + ) -> Result + where + Self: Executor + HasObservers, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let inner = GenericInProcessExecutorInner::with_timeout::( + observers, fuzzer, state, event_mgr, timeout, + )?; + + Ok(Self { + harness_fn, + inner, + phantom: PhantomData, + }) + } +} + +impl GenericInProcessExecutorInner where - H: FnMut(&::Input) -> ExitKind + ?Sized, - HB: BorrowMut, HT: ExecutorHooksTuple, OT: ObserversTuple, S: HasExecutions + HasSolutions + HasCorpus + State, +{ + /// Create a new in mem executor with the default timeout (5 sec) + pub fn generic( + user_hooks: HT, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + ) -> Result + where + E: Executor + HasObservers + HasInProcessHooks, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + Self::with_timeout_generic::( + user_hooks, + observers, + fuzzer, + state, + event_mgr, + Duration::from_millis(5000), + ) + } + + /// Create a new in mem executor with the default timeout and use batch mode(5 sec) + #[cfg(all(feature = "std", target_os = "linux"))] + pub fn batched_timeout_generic( + user_hooks: HT, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + exec_tmout: Duration, + ) -> Result + where + E: Executor + HasObservers + HasInProcessHooks, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let mut me = Self::with_timeout_generic::( + user_hooks, observers, fuzzer, state, event_mgr, exec_tmout, + )?; + me.hooks_mut().0.timer_mut().batch_mode = true; + Ok(me) + } + + /// Create a new in mem executor. + /// Caution: crash and restart in one of them will lead to odd behavior if multiple are used, + /// depending on different corpus or state. + /// * `user_hooks` - the hooks run before and after the harness's execution + /// * `harness_fn` - the harness, executing the function + /// * `observers` - the observers observing the target during execution + /// This may return an error on unix, if signal handler setup fails + pub fn with_timeout_generic( + user_hooks: HT, + observers: OT, + _fuzzer: &mut Z, + state: &mut S, + _event_mgr: &mut EM, + timeout: Duration, + ) -> Result + where + E: Executor + HasObservers + HasInProcessHooks, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let default = InProcessHooks::new::(timeout)?; + let mut hooks = tuple_list!(default).merge(user_hooks); + hooks.init_all::(state); + + #[cfg(windows)] + // Some initialization necessary for windows. + unsafe { + /* + See https://github.com/AFLplusplus/LibAFL/pull/403 + This one reserves certain amount of memory for the stack. + If stack overflow happens during fuzzing on windows, the program is transferred to our exception handler for windows. + However, if we run out of the stack memory again in this exception handler, we'll crash with STATUS_ACCESS_VIOLATION. + We need this API call because with the llmp_compression + feature enabled, the exception handler uses a lot of stack memory (in the compression lib code) on release build. + As far as I have observed, the compression uses around 0x10000 bytes, but for safety let's just reserve 0x20000 bytes for our exception handlers. + This number 0x20000 could vary depending on the compilers optimization for future compression library changes. + */ + let mut stack_reserved = 0x20000; + SetThreadStackGuarantee(&mut stack_reserved)?; + } + + #[cfg(all(feature = "std", windows))] + { + // set timeout for the handler + *hooks.0.millis_sec_mut() = timeout.as_millis() as i64; + } + + Ok(Self { + observers, + hooks, + phantom: PhantomData, + }) + } + + /// The inprocess handlers + #[inline] + pub fn hooks(&self) -> &(InProcessHooks, HT) { + &self.hooks + } + + /// The inprocess handlers (mutable) + #[inline] + pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) { + &mut self.hooks + } +} + +impl GenericInProcessExecutor +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State + HasExecutions + HasSolutions + HasCorpus, { /// Create a new in mem executor with the default timeout (5 sec) pub fn generic( @@ -362,7 +633,7 @@ where event_mgr: &mut EM, ) -> Result where - Self: Executor, + Self: Executor + HasObservers, EM: EventFirer + EventRestarter, OF: Feedback, S: State, @@ -391,17 +662,21 @@ where exec_tmout: Duration, ) -> Result where - Self: Executor, + Self: Executor + HasObservers, EM: EventFirer + EventRestarter, OF: Feedback, S: State, Z: HasObjective, { - let mut me = Self::with_timeout_generic( - user_hooks, harness_fn, observers, fuzzer, state, event_mgr, exec_tmout, + let inner = GenericInProcessExecutorInner::batched_timeout_generic::( + user_hooks, observers, fuzzer, state, event_mgr, exec_tmout, )?; - me.hooks_mut().0.timer_mut().batch_mode = true; - Ok(me) + + Ok(Self { + harness_fn, + inner, + phantom: PhantomData, + }) } /// Create a new in mem executor. @@ -415,49 +690,25 @@ where user_hooks: HT, harness_fn: HB, observers: OT, - _fuzzer: &mut Z, + fuzzer: &mut Z, state: &mut S, - _event_mgr: &mut EM, + event_mgr: &mut EM, timeout: Duration, ) -> Result where - Self: Executor, + Self: Executor + HasObservers, EM: EventFirer + EventRestarter, OF: Feedback, S: State, Z: HasObjective, { - let default = InProcessHooks::new::(timeout)?; - let mut hooks = tuple_list!(default).merge(user_hooks); - hooks.init_all::(state); - - #[cfg(windows)] - // Some initialization necessary for windows. - unsafe { - /* - See https://github.com/AFLplusplus/LibAFL/pull/403 - This one reserves certain amount of memory for the stack. - If stack overflow happens during fuzzing on windows, the program is transferred to our exception handler for windows. - However, if we run out of the stack memory again in this exception handler, we'll crash with STATUS_ACCESS_VIOLATION. - We need this API call because with the llmp_compression - feature enabled, the exception handler uses a lot of stack memory (in the compression lib code) on release build. - As far as I have observed, the compression uses around 0x10000 bytes, but for safety let's just reserve 0x20000 bytes for our exception handlers. - This number 0x20000 could vary depending on the compilers optimization for future compression library changes. - */ - let mut stack_reserved = 0x20000; - SetThreadStackGuarantee(&mut stack_reserved)?; - } - - #[cfg(all(feature = "std", windows))] - { - // set timeout for the handler - *hooks.0.millis_sec_mut() = timeout.as_millis() as i64; - } + let inner = GenericInProcessExecutorInner::with_timeout_generic::( + user_hooks, observers, fuzzer, state, event_mgr, timeout, + )?; Ok(Self { harness_fn, - observers, - hooks, + inner, phantom: PhantomData, }) } @@ -477,13 +728,13 @@ where /// The inprocess handlers #[inline] pub fn hooks(&self) -> &(InProcessHooks, HT) { - &self.hooks + self.inner.hooks() } /// The inprocess handlers (mutable) #[inline] pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) { - &mut self.hooks + self.inner.hooks_mut() } } @@ -496,10 +747,8 @@ pub trait HasInProcessHooks { fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks; } -impl HasInProcessHooks for GenericInProcessExecutor +impl HasInProcessHooks for GenericInProcessExecutorInner where - H: FnMut(&::Input) -> ExitKind + ?Sized, - HB: BorrowMut, HT: ExecutorHooksTuple, OT: ObserversTuple, S: State + HasExecutions + HasSolutions + HasCorpus, @@ -517,6 +766,27 @@ where } } +impl HasInProcessHooks for GenericInProcessExecutor +where + H: FnMut(&::Input) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State + HasExecutions + HasSolutions + HasCorpus, +{ + /// 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] #[allow(clippy::too_many_arguments)] /// Save state if it is an objective diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs new file mode 100644 index 0000000000..f44a398663 --- /dev/null +++ b/libafl/src/executors/inprocess/stateful.rs @@ -0,0 +1,416 @@ +use alloc::boxed::Box; +use core::{ + borrow::BorrowMut, + fmt::{self, Debug, Formatter}, + marker::PhantomData, + time::Duration, +}; + +use libafl_bolts::tuples::tuple_list; + +use crate::{ + events::{EventFirer, EventRestarter}, + executors::{ + hooks::{inprocess::InProcessHooks, ExecutorHooksTuple}, + inprocess::{GenericInProcessExecutorInner, HasInProcessHooks}, + Executor, ExitKind, HasObservers, + }, + feedbacks::Feedback, + fuzzer::HasObjective, + inputs::UsesInput, + observers::{ObserversTuple, UsesObservers}, + state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState}, + Error, +}; + +/// The process executor simply calls a target function, as mutable reference to a closure +/// The internal state of the executor is made available to the harness. +pub type StatefulInProcessExecutor<'a, H, OT, S, ES> = + StatefulGenericInProcessExecutor; + +/// The process executor simply calls a target function, as boxed `FnMut` trait object +/// The internal state of the executor is made available to the harness. +pub type OwnedInProcessExecutor = StatefulGenericInProcessExecutor< + dyn FnMut(&::Input, &mut ES) -> ExitKind, + Box::Input, &mut ES) -> ExitKind>, + (), + OT, + S, + ES, +>; + +/// The inmem executor simply calls a target function, then returns afterwards. +/// The harness can access the internal state of the executor. +#[allow(dead_code)] +pub struct StatefulGenericInProcessExecutor +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + /// The harness function, being executed for each fuzzing loop execution + harness_fn: HB, + /// The state used as argument of the harness + pub exposed_executor_state: ES, + /// Inner state of the executor + pub inner: GenericInProcessExecutorInner, + phantom: PhantomData<(ES, *const H)>, +} + +impl Debug for StatefulGenericInProcessExecutor +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple + Debug, + S: State, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("StatefulGenericInProcessExecutor") + .field("harness_fn", &"") + .field("inner", &self.inner) + .finish_non_exhaustive() + } +} + +impl UsesState for StatefulGenericInProcessExecutor +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + type State = S; +} + +impl UsesObservers for StatefulGenericInProcessExecutor +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + type Observers = OT; +} + +impl Executor + for StatefulGenericInProcessExecutor +where + EM: UsesState, + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State + HasExecutions, + Z: UsesState, +{ + fn run_target( + &mut self, + fuzzer: &mut Z, + state: &mut Self::State, + mgr: &mut EM, + input: &Self::Input, + ) -> Result { + *state.executions_mut() += 1; + self.inner.enter_target(fuzzer, state, mgr, input); + self.inner.hooks.pre_exec_all(fuzzer, state, mgr, input); + + let ret = (self.harness_fn.borrow_mut())(input, &mut self.exposed_executor_state); + + self.inner.hooks.post_exec_all(fuzzer, state, mgr, input); + self.inner.leave_target(fuzzer, state, mgr, input); + Ok(ret) + } +} + +impl HasObservers for StatefulGenericInProcessExecutor +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + #[inline] + fn observers(&self) -> &OT { + self.inner.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.inner.observers_mut() + } +} + +impl<'a, H, OT, S, ES> StatefulInProcessExecutor<'a, H, OT, S, ES> +where + H: FnMut(&::Input, &mut ES) -> ExitKind + ?Sized, + OT: ObserversTuple, + S: HasExecutions + HasSolutions + HasCorpus + State, +{ + /// Create a new in mem executor with the default timeout (5 sec) + pub fn new( + harness_fn: &'a mut H, + exposed_executor_state: ES, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + ) -> Result + where + Self: Executor, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + Self::with_timeout_generic( + tuple_list!(), + harness_fn, + exposed_executor_state, + observers, + fuzzer, + state, + event_mgr, + Duration::from_millis(5000), + ) + } + + /// Create a new in mem executor with the default timeout and use batch mode(5 sec) + #[cfg(all(feature = "std", target_os = "linux"))] + pub fn batched_timeouts( + harness_fn: &'a mut H, + exposed_executor_state: ES, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + exec_tmout: Duration, + ) -> Result + where + Self: Executor, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let inner = GenericInProcessExecutorInner::batched_timeouts::( + observers, fuzzer, state, event_mgr, exec_tmout, + )?; + + Ok(Self { + harness_fn, + exposed_executor_state, + inner, + phantom: PhantomData, + }) + } + + /// Create a new in mem executor. + /// Caution: crash and restart in one of them will lead to odd behavior if multiple are used, + /// depending on different corpus or state. + /// * `user_hooks` - the hooks run before and after the harness's execution + /// * `harness_fn` - the harness, executing the function + /// * `observers` - the observers observing the target during execution + /// This may return an error on unix, if signal handler setup fails + pub fn with_timeout( + harness_fn: &'a mut H, + exposed_executor_state: ES, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + timeout: Duration, + ) -> Result + where + Self: Executor, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let inner = GenericInProcessExecutorInner::with_timeout::( + observers, fuzzer, state, event_mgr, timeout, + )?; + + Ok(Self { + harness_fn, + exposed_executor_state, + inner, + phantom: PhantomData, + }) + } +} + +impl StatefulGenericInProcessExecutor +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + /// The executor state given to the harness + pub fn exposed_executor_state(&self) -> &ES { + &self.exposed_executor_state + } + + /// The mutable executor state given to the harness + pub fn exposed_executor_state_mut(&mut self) -> &mut ES { + &mut self.exposed_executor_state + } +} + +impl StatefulGenericInProcessExecutor +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State + HasExecutions + HasSolutions + HasCorpus, +{ + /// Create a new in mem executor with the default timeout (5 sec) + pub fn generic( + user_hooks: HT, + harness_fn: HB, + exposed_executor_state: ES, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + ) -> Result + where + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + Self::with_timeout_generic( + user_hooks, + harness_fn, + exposed_executor_state, + observers, + fuzzer, + state, + event_mgr, + Duration::from_millis(5000), + ) + } + + /// Create a new in mem executor with the default timeout and use batch mode(5 sec) + #[cfg(all(feature = "std", target_os = "linux"))] + #[allow(clippy::too_many_arguments)] + pub fn batched_timeout_generic( + user_hooks: HT, + harness_fn: HB, + exposed_executor_state: ES, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + exec_tmout: Duration, + ) -> Result + where + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let inner = GenericInProcessExecutorInner::batched_timeout_generic::( + user_hooks, observers, fuzzer, state, event_mgr, exec_tmout, + )?; + + Ok(Self { + harness_fn, + exposed_executor_state, + inner, + phantom: PhantomData, + }) + } + + /// Create a new in mem executor. + /// Caution: crash and restart in one of them will lead to odd behavior if multiple are used, + /// depending on different corpus or state. + /// * `user_hooks` - the hooks run before and after the harness's execution + /// * `harness_fn` - the harness, executing the function + /// * `observers` - the observers observing the target during execution + /// This may return an error on unix, if signal handler setup fails + #[allow(clippy::too_many_arguments)] + pub fn with_timeout_generic( + user_hooks: HT, + harness_fn: HB, + exposed_executor_state: ES, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + timeout: Duration, + ) -> Result + where + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let inner = GenericInProcessExecutorInner::with_timeout_generic::( + user_hooks, observers, fuzzer, state, event_mgr, timeout, + )?; + + Ok(Self { + harness_fn, + exposed_executor_state, + inner, + 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 hooks(&self) -> &(InProcessHooks, HT) { + self.inner.hooks() + } + + /// The inprocess handlers (mutable) + #[inline] + pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) { + self.inner.hooks_mut() + } +} + +impl HasInProcessHooks + for StatefulGenericInProcessExecutor +where + H: FnMut(&::Input, &mut ES) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State + HasExecutions + HasSolutions + HasCorpus, +{ + /// 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() + } +} diff --git a/libafl/src/executors/inprocess_fork.rs b/libafl/src/executors/inprocess_fork/mod.rs similarity index 59% rename from libafl/src/executors/inprocess_fork.rs rename to libafl/src/executors/inprocess_fork/mod.rs index a5aef5bcfc..fd0da8867a 100644 --- a/libafl/src/executors/inprocess_fork.rs +++ b/libafl/src/executors/inprocess_fork/mod.rs @@ -16,7 +16,7 @@ use libafl_bolts::{ use libc::siginfo_t; use nix::{ sys::wait::{waitpid, WaitStatus}, - unistd::{fork, ForkResult}, + unistd::{fork, ForkResult, Pid}, }; use super::hooks::ExecutorHooksTuple; @@ -35,6 +35,7 @@ use crate::{ state::{HasExecutions, HasSolutions, State, UsesState}, Error, }; + /// The signature of the crash handler function pub(crate) type ForkHandlerFuncPtr = unsafe fn( Signal, @@ -46,20 +47,27 @@ pub(crate) type ForkHandlerFuncPtr = unsafe fn( #[cfg(all(unix, not(target_os = "linux")))] use crate::executors::hooks::timer::{setitimer, Itimerval, Timeval, ITIMER_REAL}; -/// The `InProcessForkExecutor` with no user hooks -pub type InProcessForkExecutor<'a, H, OT, S, SP> = - GenericInProcessForkExecutor<'a, H, (), OT, S, SP>; +/// A version of `InProcessForkExecutor` with a state accessible from the harness. +pub mod stateful; -impl<'a, H, OT, S, SP> InProcessForkExecutor<'a, H, OT, S, SP> +/// The `InProcessForkExecutor` with no user hooks +pub type InProcessForkExecutor<'a, H, OT, S, SP, EM, Z> = + GenericInProcessForkExecutor<'a, H, (), OT, S, SP, EM, Z>; + +impl<'a, H, OT, S, SP, EM, Z, OF> InProcessForkExecutor<'a, H, OT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind + ?Sized, S: State, OT: ObserversTuple, SP: ShMemProvider, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: HasSolutions, + Z: HasObjective, { #[allow(clippy::too_many_arguments)] /// The constructor for `InProcessForkExecutor` - pub fn new( + pub fn new( harness_fn: &'a mut H, observers: OT, fuzzer: &mut Z, @@ -67,13 +75,7 @@ where event_mgr: &mut EM, timeout: Duration, shmem_provider: SP, - ) -> Result - where - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasSolutions, - Z: HasObjective, - { + ) -> Result { Self::with_hooks( tuple_list!(), harness_fn, @@ -87,37 +89,54 @@ where } } -/// [`GenericInProcessForkExecutor`] is an executor that forks the current process before each execution. -pub struct GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +/// Inner state of GenericInProcessExecutor-like structures. +pub struct GenericInProcessForkExecutorInner where - H: FnMut(&S::Input) -> ExitKind + ?Sized, OT: ObserversTuple, S: UsesInput, SP: ShMemProvider, HT: ExecutorHooksTuple, + EM: UsesState, + Z: UsesState, { hooks: (InChildProcessHooks, HT), - harness_fn: &'a mut H, shmem_provider: SP, observers: OT, #[cfg(target_os = "linux")] itimerspec: libc::itimerspec, #[cfg(all(unix, not(target_os = "linux")))] itimerval: Itimerval, - phantom: PhantomData, + phantom: PhantomData<(S, EM, Z)>, } -impl<'a, H, HT, OT, S, SP> Debug for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +/// [`GenericInProcessForkExecutor`] is an executor that forks the current process before each execution. +pub struct GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind + ?Sized, - OT: ObserversTuple + Debug, + OT: ObserversTuple, S: UsesInput, SP: ShMemProvider, HT: ExecutorHooksTuple, + EM: UsesState, + Z: UsesState, +{ + harness_fn: &'a mut H, + inner: GenericInProcessForkExecutorInner, +} + +impl Debug for GenericInProcessForkExecutorInner +where + OT: ObserversTuple + Debug, + S: UsesInput, + SP: ShMemProvider, + HT: ExecutorHooksTuple + Debug, + EM: UsesState, + Z: UsesState, { #[cfg(target_os = "linux")] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("GenericInProcessForkExecutor") + f.debug_struct("GenericInProcessForkExecutorInner") + .field("hooks", &self.hooks) .field("observers", &self.observers) .field("shmem_provider", &self.shmem_provider) .field("itimerspec", &self.itimerspec) @@ -128,7 +147,7 @@ where fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { #[cfg(not(target_os = "linux"))] return f - .debug_struct("GenericInProcessForkExecutor") + .debug_struct("GenericInProcessForkExecutorInner") .field("observers", &self.observers) .field("shmem_provider", &self.shmem_provider) .field("itimerval", &self.itimerval) @@ -136,26 +155,163 @@ where } } -impl<'a, H, HT, OT, S, SP> UsesState for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +impl<'a, H, HT, OT, S, SP, EM, Z> Debug + for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + OT: ObserversTuple + Debug, + S: UsesInput, + SP: ShMemProvider, + HT: ExecutorHooksTuple + Debug, + EM: UsesState, + Z: UsesState, +{ + #[cfg(target_os = "linux")] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("GenericInProcessForkExecutor") + .field("GenericInProcessForkExecutorInner", &self.inner) + .finish() + } + + #[cfg(not(target_os = "linux"))] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + #[cfg(not(target_os = "linux"))] + return f + .debug_struct("GenericInProcessForkExecutor") + .field("GenericInProcessForkExecutorInner", &self.inner) + .finish(); + } +} + +impl UsesState for GenericInProcessForkExecutorInner where - H: ?Sized + FnMut(&S::Input) -> ExitKind, OT: ObserversTuple, S: State, SP: ShMemProvider, HT: ExecutorHooksTuple, + EM: UsesState, + Z: UsesState, { type State = S; } -impl<'a, EM, H, HT, OT, S, SP, Z> Executor - for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +impl<'a, H, HT, OT, S, SP, EM, Z> UsesState + for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> where - EM: UsesState, H: FnMut(&S::Input) -> ExitKind + ?Sized, OT: ObserversTuple, + S: State, + SP: ShMemProvider, + HT: ExecutorHooksTuple, + EM: UsesState, + Z: UsesState, +{ + type State = S; +} + +impl GenericInProcessForkExecutorInner +where + OT: ObserversTuple + Debug, + S: State + UsesInput, + SP: ShMemProvider, + HT: ExecutorHooksTuple, + EM: EventFirer + EventRestarter, + Z: UsesState, +{ + unsafe fn pre_run_target_child( + &mut self, + fuzzer: &mut Z, + state: &mut as UsesState>::State, + mgr: &mut EM, + input: & as UsesInput>::Input, + ) -> Result<(), Error> { + self.shmem_provider.post_fork(true)?; + + self.enter_target(fuzzer, state, mgr, input); + self.hooks.pre_exec_all(fuzzer, state, mgr, input); + + self.observers + .pre_exec_child_all(state, input) + .expect("Failed to run post_exec on observers"); + + #[cfg(target_os = "linux")] + { + let mut timerid: libc::timer_t = null_mut(); + // creates a new per-process interval timer + // we can't do this from the parent, timerid is unique to each process. + libc::timer_create(libc::CLOCK_MONOTONIC, null_mut(), addr_of_mut!(timerid)); + + // log::info!("Set timer! {:#?} {timerid:#?}", self.itimerspec); + let _: i32 = libc::timer_settime(timerid, 0, addr_of_mut!(self.itimerspec), null_mut()); + } + #[cfg(not(target_os = "linux"))] + { + setitimer(ITIMER_REAL, &mut self.itimerval, null_mut()); + } + // log::trace!("{v:#?} {}", nix::errno::errno()); + + Ok(()) + } + + unsafe fn post_run_target_child( + &mut self, + fuzzer: &mut Z, + state: &mut as UsesState>::State, + mgr: &mut EM, + input: & as UsesInput>::Input, + ) { + self.observers + .post_exec_child_all(state, input, &ExitKind::Ok) + .expect("Failed to run post_exec on observers"); + + self.hooks.post_exec_all(fuzzer, state, mgr, input); + self.leave_target(fuzzer, state, mgr, input); + + libc::_exit(0); + } + + fn parent(&mut self, child: Pid) -> Result { + // log::trace!("from parent {} child is {}", std::process::id(), child); + self.shmem_provider.post_fork(false)?; + + let res = waitpid(child, None)?; + log::trace!("{res:#?}"); + match res { + WaitStatus::Signaled(_, signal, _) => match signal { + nix::sys::signal::Signal::SIGALRM | nix::sys::signal::Signal::SIGUSR2 => { + Ok(ExitKind::Timeout) + } + _ => Ok(ExitKind::Crash), + }, + WaitStatus::Exited(_, code) => { + if code > 128 && code < 160 { + // Signal exit codes + let signal = code - 128; + if signal == Signal::SigAlarm as libc::c_int + || signal == Signal::SigUser2 as libc::c_int + { + Ok(ExitKind::Timeout) + } else { + Ok(ExitKind::Crash) + } + } else { + Ok(ExitKind::Ok) + } + } + _ => Ok(ExitKind::Ok), + } + } +} + +impl<'a, EM, H, HT, OT, S, SP, Z> Executor + for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + OT: ObserversTuple + Debug, S: State + HasExecutions, SP: ShMemProvider, HT: ExecutorHooksTuple, + EM: EventFirer + EventRestarter, Z: UsesState, { #[allow(unreachable_code)] @@ -170,86 +326,18 @@ where *state.executions_mut() += 1; unsafe { - self.shmem_provider.pre_fork()?; + self.inner.shmem_provider.pre_fork()?; match fork() { Ok(ForkResult::Child) => { // Child - self.shmem_provider.post_fork(true)?; - - self.enter_target(fuzzer, state, mgr, input); - self.hooks.pre_exec_all(fuzzer, state, mgr, input); - - self.observers - .pre_exec_child_all(state, input) - .expect("Failed to run post_exec on observers"); - - #[cfg(target_os = "linux")] - { - let mut timerid: libc::timer_t = null_mut(); - // creates a new per-process interval timer - // we can't do this from the parent, timerid is unique to each process. - libc::timer_create( - libc::CLOCK_MONOTONIC, - null_mut(), - addr_of_mut!(timerid), - ); - - // log::info!("Set timer! {:#?} {timerid:#?}", self.itimerspec); - let _: i32 = libc::timer_settime( - timerid, - 0, - addr_of_mut!(self.itimerspec), - null_mut(), - ); - } - #[cfg(not(target_os = "linux"))] - { - setitimer(ITIMER_REAL, &mut self.itimerval, null_mut()); - } - // log::trace!("{v:#?} {}", nix::errno::errno()); + self.inner.pre_run_target_child(fuzzer, state, mgr, input)?; (self.harness_fn)(input); - - self.observers - .post_exec_child_all(state, input, &ExitKind::Ok) - .expect("Failed to run post_exec on observers"); - - self.hooks.post_exec_all(fuzzer, state, mgr, input); - self.leave_target(fuzzer, state, mgr, input); - - libc::_exit(0); - + self.inner.post_run_target_child(fuzzer, state, mgr, input); 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), - } + self.inner.parent(child) } Err(e) => Err(Error::from(e)), } @@ -257,17 +345,18 @@ where } } -impl<'a, H, HT, OT, S, SP> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +impl GenericInProcessForkExecutorInner where - H: FnMut(&S::Input) -> ExitKind + ?Sized, HT: ExecutorHooksTuple, S: State, OT: ObserversTuple, SP: ShMemProvider, + EM: EventFirer + EventRestarter, + Z: UsesState, { #[inline] /// This function marks the boundary between the fuzzer and the target. - pub fn enter_target( + pub fn enter_target( &mut self, _fuzzer: &mut Z, state: &mut ::State, @@ -294,7 +383,7 @@ where #[inline] /// This function marks the boundary between the fuzzer and the target. - pub fn leave_target( + pub fn leave_target( &mut self, _fuzzer: &mut Z, _state: &mut ::State, @@ -307,22 +396,15 @@ where /// Creates a new [`GenericInProcessForkExecutor`] with custom hooks #[cfg(target_os = "linux")] #[allow(clippy::too_many_arguments)] - pub fn with_hooks( + pub fn with_hooks( userhooks: HT, - harness_fn: &'a mut H, observers: OT, _fuzzer: &mut Z, state: &mut S, _event_mgr: &mut EM, timeout: Duration, shmem_provider: SP, - ) -> Result - where - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasSolutions, - Z: HasObjective, - { + ) -> Result { let default_hooks = InChildProcessHooks::new::()?; let mut hooks = tuple_list!(default_hooks).merge(userhooks); hooks.init_all::(state); @@ -342,7 +424,6 @@ where }; Ok(Self { - harness_fn, shmem_provider, observers, hooks, @@ -354,22 +435,15 @@ where /// Creates a new [`GenericInProcessForkExecutor`], non linux #[cfg(not(target_os = "linux"))] #[allow(clippy::too_many_arguments)] - pub fn with_hooks( + pub fn with_hooks( userhooks: HT, - harness_fn: &'a mut H, observers: OT, _fuzzer: &mut Z, state: &mut S, _event_mgr: &mut EM, timeout: Duration, shmem_provider: SP, - ) -> Result - where - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasSolutions, - Z: HasObjective, - { + ) -> Result { let default_hooks = InChildProcessHooks::new::()?; let mut hooks = tuple_list!(default_hooks).merge(userhooks); hooks.init_all::(state); @@ -389,7 +463,6 @@ where }; Ok(Self { - harness_fn, shmem_provider, observers, hooks, @@ -397,6 +470,45 @@ where phantom: PhantomData, }) } +} + +impl<'a, H, HT, OT, S, SP, EM, Z, OF> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + SP: ShMemProvider, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State + HasSolutions, + Z: HasObjective, +{ + /// Creates a new [`GenericInProcessForkExecutor`] with custom hooks + #[allow(clippy::too_many_arguments)] + pub fn with_hooks( + userhooks: HT, + harness_fn: &'a mut H, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + timeout: Duration, + shmem_provider: SP, + ) -> Result +where { + Ok(Self { + harness_fn, + inner: GenericInProcessForkExecutorInner::with_hooks( + userhooks, + observers, + fuzzer, + state, + event_mgr, + timeout, + shmem_provider, + )?, + }) + } /// Retrieve the harness function. #[inline] @@ -411,24 +523,40 @@ where } } -impl<'a, H, HT, OT, S, SP> UsesObservers for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +impl UsesObservers for GenericInProcessForkExecutorInner where - H: ?Sized + FnMut(&S::Input) -> ExitKind, HT: ExecutorHooksTuple, OT: ObserversTuple, S: State, SP: ShMemProvider, + EM: UsesState, + Z: UsesState, { type Observers = OT; } -impl<'a, H, HT, OT, S, SP> HasObservers for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +impl<'a, H, HT, OT, S, SP, EM, Z> UsesObservers + for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind + ?Sized, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, + SP: ShMemProvider, + EM: UsesState, + Z: UsesState, +{ + type Observers = OT; +} + +impl HasObservers for GenericInProcessForkExecutorInner +where HT: ExecutorHooksTuple, S: State, OT: ObserversTuple, SP: ShMemProvider, + EM: UsesState, + Z: UsesState, { #[inline] fn observers(&self) -> &OT { @@ -440,6 +568,29 @@ where &mut self.observers } } + +impl<'a, H, HT, OT, S, SP, EM, Z> HasObservers + for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP, EM, Z> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + HT: ExecutorHooksTuple, + S: State, + OT: ObserversTuple, + SP: ShMemProvider, + EM: UsesState, + Z: UsesState, +{ + #[inline] + fn observers(&self) -> &OT { + self.inner.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.inner.observers_mut() + } +} + /// signal hooks and `panic_hooks` for the child process pub mod child_signal_handlers { @@ -539,7 +690,10 @@ pub mod child_signal_handlers { mod tests { use libafl_bolts::tuples::tuple_list; - use crate::{executors::ExitKind, inputs::NopInput}; + use crate::{ + executors::{inprocess_fork::GenericInProcessForkExecutorInner, Executor, ExitKind}, + inputs::NopInput, + }; #[test] #[cfg_attr(miri, ignore)] @@ -557,7 +711,7 @@ mod tests { events::SimpleEventManager, executors::{ hooks::inprocess_fork::InChildProcessHooks, - inprocess_fork::GenericInProcessForkExecutor, Executor, + inprocess_fork::GenericInProcessForkExecutor, }, fuzzer::test::NopFuzzer, state::NopState, @@ -590,22 +744,26 @@ mod tests { let mut harness = |_buf: &NopInput| ExitKind::Ok; let default = InChildProcessHooks::nop(); #[cfg(target_os = "linux")] - let mut in_process_fork_executor = GenericInProcessForkExecutor::<_, (), (), _, _> { - hooks: tuple_list!(default), + let mut in_process_fork_executor = GenericInProcessForkExecutor { harness_fn: &mut harness, - shmem_provider: provider, - observers: tuple_list!(), - itimerspec, - phantom: PhantomData, + inner: GenericInProcessForkExecutorInner { + hooks: tuple_list!(default), + shmem_provider: provider, + observers: tuple_list!(), + itimerspec, + phantom: PhantomData, + }, }; #[cfg(not(target_os = "linux"))] - let mut in_process_fork_executor = GenericInProcessForkExecutor::<_, (), (), _, _> { + let mut in_process_fork_executor = GenericInProcessForkExecutor { harness_fn: &mut harness, - shmem_provider: provider, - observers: tuple_list!(), - hooks: tuple_list!(default), - itimerval: itimerspec, - phantom: PhantomData, + inner: GenericInProcessForkExecutorInner { + hooks: tuple_list!(default), + shmem_provider: provider, + observers: tuple_list!(), + itimerval: itimerspec, + phantom: PhantomData, + }, }; let input = NopInput {}; let mut fuzzer = NopFuzzer::new(); diff --git a/libafl/src/executors/inprocess_fork/stateful.rs b/libafl/src/executors/inprocess_fork/stateful.rs new file mode 100644 index 0000000000..b38182ca40 --- /dev/null +++ b/libafl/src/executors/inprocess_fork/stateful.rs @@ -0,0 +1,256 @@ +//! The `StatefulGenericInProcessForkExecutor` to do forking before executing the harness in-processly. Harness can access internal state. +use core::{ + fmt::{self, Debug, Formatter}, + marker::PhantomData, + time::Duration, +}; + +use libafl_bolts::{shmem::ShMemProvider, tuples::tuple_list}; +use nix::unistd::{fork, ForkResult}; + +use super::super::hooks::ExecutorHooksTuple; +use crate::{ + events::{EventFirer, EventRestarter}, + executors::{ + inprocess_fork::GenericInProcessForkExecutorInner, Executor, ExitKind, HasObservers, + }, + feedbacks::Feedback, + fuzzer::HasObjective, + inputs::UsesInput, + observers::{ObserversTuple, UsesObservers}, + state::{HasExecutions, HasSolutions, State, UsesState}, + Error, +}; + +/// The `StatefulInProcessForkExecutor` with no user hooks +pub type StatefulInProcessForkExecutor<'a, H, OT, S, SP, ES, EM, Z> = + StatefulGenericInProcessForkExecutor<'a, H, (), OT, S, SP, ES, EM, Z>; + +impl<'a, H, OT, S, SP, ES, EM, Z, OF> StatefulInProcessForkExecutor<'a, H, OT, S, SP, ES, EM, Z> +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + OT: ObserversTuple, + SP: ShMemProvider, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State + HasSolutions, + Z: HasObjective, +{ + #[allow(clippy::too_many_arguments)] + /// The constructor for `InProcessForkExecutor` + pub fn new( + harness_fn: &'a mut H, + exposed_executor_state: ES, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + timeout: Duration, + shmem_provider: SP, + ) -> Result { + Self::with_hooks( + tuple_list!(), + harness_fn, + exposed_executor_state, + observers, + fuzzer, + state, + event_mgr, + timeout, + shmem_provider, + ) + } +} + +/// [`StatefulGenericInProcessForkExecutor`] is an executor that forks the current process before each execution. Harness can access some internal state. +pub struct StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + OT: ObserversTuple, + S: UsesInput, + SP: ShMemProvider, + HT: ExecutorHooksTuple, + EM: UsesState, + Z: UsesState, +{ + harness_fn: &'a mut H, + exposed_executor_state: ES, + inner: GenericInProcessForkExecutorInner, + phantom: PhantomData, +} + +impl<'a, H, HT, OT, S, SP, ES, EM, Z> Debug + for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + OT: ObserversTuple + Debug, + S: UsesInput, + SP: ShMemProvider, + HT: ExecutorHooksTuple + Debug, + EM: UsesState, + Z: UsesState, +{ + #[cfg(target_os = "linux")] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("GenericInProcessForkExecutor") + .field("GenericInProcessForkExecutionInner", &self.inner) + .finish() + } + + #[cfg(not(target_os = "linux"))] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + #[cfg(not(target_os = "linux"))] + return f + .debug_struct("GenericInProcessForkExecutor") + .field("GenericInProcessForkExecutionInner", &self.inner) + .finish(); + } +} + +impl<'a, H, HT, OT, S, SP, ES, EM, Z> UsesState + for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + OT: ObserversTuple, + S: State, + SP: ShMemProvider, + HT: ExecutorHooksTuple, + EM: UsesState, + Z: UsesState, +{ + type State = S; +} + +impl<'a, EM, H, HT, OT, S, SP, Z, ES, OF> Executor + for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + OT: ObserversTuple + Debug, + S: State + HasExecutions, + SP: ShMemProvider, + HT: ExecutorHooksTuple, + EM: EventFirer + EventRestarter, + Z: HasObjective, + OF: Feedback, +{ + #[allow(unreachable_code)] + #[inline] + fn run_target( + &mut self, + fuzzer: &mut Z, + state: &mut Self::State, + mgr: &mut EM, + input: &Self::Input, + ) -> Result { + *state.executions_mut() += 1; + + unsafe { + self.inner.shmem_provider.pre_fork()?; + match fork() { + Ok(ForkResult::Child) => { + // Child + self.inner.pre_run_target_child(fuzzer, state, mgr, input)?; + (self.harness_fn)(input, &mut self.exposed_executor_state); + self.inner.post_run_target_child(fuzzer, state, mgr, input); + Ok(ExitKind::Ok) + } + Ok(ForkResult::Parent { child }) => { + // Parent + self.inner.parent(child) + } + Err(e) => Err(Error::from(e)), + } + } + } +} + +impl<'a, H, HT, OT, S, SP, ES, EM, Z, OF> + StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + SP: ShMemProvider, + Z: UsesState, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State + HasSolutions, + Z: HasObjective, +{ + /// Creates a new [`StatefulGenericInProcessForkExecutor`] with custom hooks + #[allow(clippy::too_many_arguments)] + pub fn with_hooks( + userhooks: HT, + harness_fn: &'a mut H, + exposed_executor_state: ES, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + timeout: Duration, + shmem_provider: SP, + ) -> Result { + Ok(Self { + harness_fn, + exposed_executor_state, + inner: GenericInProcessForkExecutorInner::with_hooks( + userhooks, + observers, + fuzzer, + state, + event_mgr, + timeout, + shmem_provider, + )?, + 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 + } +} + +impl<'a, H, HT, OT, S, SP, ES, EM, Z> UsesObservers + for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, + SP: ShMemProvider, + EM: UsesState, + Z: UsesState, +{ + type Observers = OT; +} + +impl<'a, H, HT, OT, S, SP, ES, EM, Z> HasObservers + for StatefulGenericInProcessForkExecutor<'a, H, HT, OT, S, SP, ES, EM, Z> +where + H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized, + HT: ExecutorHooksTuple, + S: State, + OT: ObserversTuple, + SP: ShMemProvider, + EM: UsesState, + Z: UsesState, +{ + #[inline] + fn observers(&self) -> &OT { + self.inner.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.inner.observers_mut() + } +} diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor/mod.rs similarity index 64% rename from libafl_qemu/src/executor.rs rename to libafl_qemu/src/executor/mod.rs index 6b91540cf1..11078ab8c8 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor/mod.rs @@ -7,8 +7,6 @@ use core::{ time::Duration, }; -#[cfg(feature = "fork")] -use libafl::inputs::UsesInput; #[cfg(feature = "fork")] use libafl::{ events::EventManager, @@ -34,28 +32,39 @@ use libafl_bolts::shmem::ShMemProvider; use crate::{emu::Emulator, helper::QemuHelperTuple, hooks::QemuHooks}; +/// A version of `QemuExecutor` with a state accessible from the harness. +pub mod stateful; + +pub struct QemuExecutorState<'a, QT, S> +where + QT: QemuHelperTuple, + S: State + HasExecutions, +{ + hooks: &'a mut QemuHooks, + first_exec: bool, +} + pub struct QemuExecutor<'a, H, OT, QT, S> where H: FnMut(&S::Input) -> ExitKind, - S: State, + S: State + HasExecutions, OT: ObserversTuple, QT: QemuHelperTuple, { inner: InProcessExecutor<'a, H, OT, S>, - hooks: &'a mut QemuHooks, - first_exec: bool, + state: QemuExecutorState<'a, QT, S>, } impl<'a, H, OT, QT, S> Debug for QemuExecutor<'a, H, OT, QT, S> where H: FnMut(&S::Input) -> ExitKind, - S: State, + S: State + HasExecutions, OT: ObserversTuple + Debug, QT: QemuHelperTuple + Debug, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QemuExecutor") - .field("hooks", &self.hooks) + .field("hooks", &self.state.hooks) .field("inner", &self.inner) .finish() } @@ -68,17 +77,19 @@ extern "C" { } #[cfg(emulation_mode = "usermode")] -pub unsafe fn inproc_qemu_crash_handler( +pub unsafe fn inproc_qemu_crash_handler<'a, E, EM, OF, Z, QT, S>( signal: Signal, - info: &mut siginfo_t, - mut context: Option<&mut ucontext_t>, - _data: &mut InProcessExecutorHandlerData, + info: &'a mut siginfo_t, + mut context: Option<&'a mut ucontext_t>, + _data: &'a mut InProcessExecutorHandlerData, ) where E: Executor + HasObservers, EM: EventFirer + EventRestarter, OF: Feedback, E::State: HasExecutions + HasSolutions + HasCorpus, Z: HasObjective, + QT: QemuHelperTuple + Debug + 'a, + S: State + HasExecutions + 'a, { let puc = match &mut context { Some(v) => ptr::from_mut::(*v) as *mut c_void, @@ -88,7 +99,7 @@ pub unsafe fn inproc_qemu_crash_handler( } #[cfg(emulation_mode = "systemmode")] -static mut BREAK_ON_TMOUT: bool = false; +pub(crate) static mut BREAK_ON_TMOUT: bool = false; #[cfg(emulation_mode = "systemmode")] extern "C" { @@ -96,11 +107,11 @@ extern "C" { } #[cfg(emulation_mode = "systemmode")] -pub unsafe fn inproc_qemu_timeout_handler( +pub unsafe fn inproc_qemu_timeout_handler<'a, E, EM, OF, Z>( signal: Signal, - info: &mut siginfo_t, - context: Option<&mut ucontext_t>, - data: &mut InProcessExecutorHandlerData, + info: &'a mut siginfo_t, + context: Option<&'a mut ucontext_t>, + data: &'a mut InProcessExecutorHandlerData, ) where E: Executor + HasObservers + HasInProcessHooks, EM: EventFirer + EventRestarter, @@ -117,12 +128,61 @@ pub unsafe fn inproc_qemu_timeout_handler( } } +impl<'a, QT, S> QemuExecutorState<'a, QT, S> +where + S: State + HasExecutions, + QT: QemuHelperTuple + Debug, +{ + pub fn new(hooks: &'a mut QemuHooks) -> Result + where + E: Executor + HasInProcessHooks + HasObservers, + EM: EventFirer + EventRestarter, + OF: Feedback, + OT: ObserversTuple, + S: State + HasExecutions + HasCorpus + HasSolutions, + Z: HasObjective, + { + #[cfg(emulation_mode = "usermode")] + { + let handler = |hooks: &mut QemuHooks, host_sig| { + eprintln!("Crashed with signal {host_sig}"); + unsafe { + libafl::executors::inprocess::generic_inproc_crash_handler::(); + } + if let Some(cpu) = hooks.emulator().current_cpu() { + eprint!("Context:\n{}", cpu.display_context()); + } + }; + + hooks.crash_closure(Box::new(handler)); + } + Ok(QemuExecutorState { + first_exec: true, + hooks, + }) + } + + #[must_use] + pub fn hooks(&self) -> &QemuHooks { + self.hooks + } + + pub fn hooks_mut(&mut self) -> &mut QemuHooks { + self.hooks + } + + #[must_use] + pub fn emulator(&self) -> &Emulator { + self.hooks.emulator() + } +} + impl<'a, H, OT, QT, S> QemuExecutor<'a, H, OT, QT, S> where H: FnMut(&S::Input) -> ExitKind, - S: State, + S: State + HasExecutions, OT: ObserversTuple, - QT: QemuHelperTuple, + QT: QemuHelperTuple + Debug, { pub fn new( hooks: &'a mut QemuHooks, @@ -142,40 +202,25 @@ where let mut inner = InProcessExecutor::with_timeout( harness_fn, observers, fuzzer, state, event_mgr, timeout, )?; + #[cfg(emulation_mode = "usermode")] { inner.inprocess_hooks_mut().crash_handler = - inproc_qemu_crash_handler::, EM, OF, Z> + inproc_qemu_crash_handler::, EM, OF, Z, QT, S> as *const c_void; - - let handler = |hooks: &mut QemuHooks, host_sig| { - eprintln!("Crashed with signal {host_sig}"); - unsafe { - libafl::executors::inprocess::generic_inproc_crash_handler::< - InProcessExecutor<'a, H, OT, S>, - EM, - OF, - Z, - >(); - } - if let Some(cpu) = hooks.emulator().current_cpu() { - eprint!("Context:\n{}", cpu.display_context()); - } - }; - - hooks.crash_closure(Box::new(handler)); } + #[cfg(emulation_mode = "systemmode")] { inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::, EM, OF, Z> as *const c_void; } - Ok(Self { - first_exec: true, - hooks, - inner, - }) + + let state = + QemuExecutorState::new::, EM, OF, OT, Z>(hooks)?; + + Ok(Self { inner, state }) } pub fn inner(&self) -> &InProcessExecutor<'a, H, OT, S> { @@ -194,26 +239,65 @@ where } pub fn hooks(&self) -> &QemuHooks { - self.hooks + self.state.hooks() } pub fn hooks_mut(&mut self) -> &mut QemuHooks { - self.hooks + self.state.hooks_mut() } pub fn emulator(&self) -> &Emulator { - self.hooks.emulator() + self.state.emulator() } } -impl<'a, EM, H, OT, QT, S, Z> Executor for QemuExecutor<'a, H, OT, QT, S> +impl<'a, QT, S> QemuExecutorState<'a, QT, S> where - EM: UsesState, + S: State + HasExecutions + HasCorpus + HasSolutions, + QT: QemuHelperTuple + Debug, +{ + fn pre_exec(&mut self, input: &E::Input, emu: &Emulator) + where + E: Executor, + EM: EventFirer + EventRestarter, + OF: Feedback, + Z: HasObjective, + { + if self.first_exec { + self.hooks.helpers().first_exec_all(self.hooks); + self.first_exec = false; + } + self.hooks.helpers_mut().pre_exec_all(emu, input); + } + + fn post_exec( + &mut self, + input: &E::Input, + emu: &Emulator, + observers: &mut OT, + exit_kind: &mut ExitKind, + ) where + E: Executor + HasObservers, + EM: EventFirer + EventRestarter, + OT: ObserversTuple, + OF: Feedback, + Z: HasObjective, + { + self.hooks + .helpers_mut() + .post_exec_all(emu, input, observers, exit_kind); + } +} + +impl<'a, EM, H, OT, OF, QT, S, Z> Executor for QemuExecutor<'a, H, OT, QT, S> +where + EM: EventFirer + EventRestarter, H: FnMut(&S::Input) -> ExitKind, - S: State + HasExecutions, + S: State + HasExecutions + HasCorpus + HasSolutions, OT: ObserversTuple, - QT: QemuHelperTuple, - Z: UsesState, + OF: Feedback, + QT: QemuHelperTuple + Debug, + Z: HasObjective, { fn run_target( &mut self, @@ -223,15 +307,11 @@ where input: &Self::Input, ) -> Result { let emu = Emulator::get().unwrap(); - if self.first_exec { - self.hooks.helpers().first_exec_all(self.hooks); - self.first_exec = false; - } - self.hooks.helpers_mut().pre_exec_all(&emu, input); + self.state.pre_exec::(input, &emu); let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?; - self.hooks.helpers_mut().post_exec_all( - &emu, + self.state.post_exec::( input, + &emu, self.inner.observers_mut(), &mut exit_kind, ); @@ -244,7 +324,7 @@ where H: FnMut(&S::Input) -> ExitKind, OT: ObserversTuple, QT: QemuHelperTuple, - S: State, + S: State + HasExecutions, { type State = S; } @@ -254,7 +334,7 @@ where H: FnMut(&S::Input) -> ExitKind, OT: ObserversTuple, QT: QemuHelperTuple, - S: State, + S: State + HasExecutions, { type Observers = OT; } @@ -262,7 +342,7 @@ where impl<'a, H, OT, QT, S> HasObservers for QemuExecutor<'a, H, OT, QT, S> where H: FnMut(&S::Input) -> ExitKind, - S: State, + S: State + HasExecutions, OT: ObserversTuple, QT: QemuHelperTuple, { @@ -278,46 +358,53 @@ where } #[cfg(feature = "fork")] -pub struct QemuForkExecutor<'a, H, OT, QT, S, SP> +pub struct QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind, - S: UsesInput, + S: State + HasExecutions, OT: ObserversTuple, QT: QemuHelperTuple, SP: ShMemProvider, + EM: UsesState, + Z: UsesState, { - first_exec: bool, - hooks: &'a mut QemuHooks, - inner: InProcessForkExecutor<'a, H, OT, S, SP>, + inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>, + state: QemuExecutorState<'a, QT, S>, } #[cfg(feature = "fork")] -impl<'a, H, OT, QT, S, SP> Debug for QemuForkExecutor<'a, H, OT, QT, S, SP> +impl<'a, H, OT, QT, S, SP, EM, Z> Debug for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind, - S: UsesInput, + S: State + HasExecutions, OT: ObserversTuple + Debug, QT: QemuHelperTuple + Debug, SP: ShMemProvider, + EM: UsesState, + Z: UsesState, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QemuForkExecutor") - .field("hooks", &self.hooks) + .field("hooks", &self.state.hooks) .field("inner", &self.inner) .finish() } } #[cfg(feature = "fork")] -impl<'a, H, OT, QT, S, SP> QemuForkExecutor<'a, H, OT, QT, S, SP> +impl<'a, H, OT, QT, S, SP, EM, Z, OF> QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind, - S: State, + S: State + HasExecutions, OT: ObserversTuple, QT: QemuHelperTuple, SP: ShMemProvider, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: HasSolutions, + Z: HasObjective, { - pub fn new( + pub fn new( hooks: &'a mut QemuHooks, harness_fn: &'a mut H, observers: OT, @@ -326,18 +413,10 @@ where event_mgr: &mut EM, shmem_provider: SP, timeout: core::time::Duration, - ) -> Result - where - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasSolutions, - Z: HasObjective, - { + ) -> Result { assert!(!QT::HOOKS_DO_SIDE_EFFECTS, "When using QemuForkExecutor, the hooks must not do any side effect as they will happen in the child process and then discarded"); Ok(Self { - first_exec: true, - hooks, inner: InProcessForkExecutor::new( harness_fn, observers, @@ -347,40 +426,46 @@ where timeout, shmem_provider, )?, + state: QemuExecutorState { + first_exec: true, + hooks, + }, }) } - pub fn inner(&self) -> &InProcessForkExecutor<'a, H, OT, S, SP> { + pub fn inner(&self) -> &InProcessForkExecutor<'a, H, OT, S, SP, EM, Z> { &self.inner } - pub fn inner_mut(&mut self) -> &mut InProcessForkExecutor<'a, H, OT, S, SP> { + pub fn inner_mut(&mut self) -> &mut InProcessForkExecutor<'a, H, OT, S, SP, EM, Z> { &mut self.inner } pub fn hooks(&self) -> &QemuHooks { - self.hooks + self.state.hooks } pub fn hooks_mut(&mut self) -> &mut QemuHooks { - self.hooks + self.state.hooks } pub fn emulator(&self) -> &Emulator { - self.hooks.emulator() + self.state.hooks.emulator() } } #[cfg(feature = "fork")] -impl<'a, EM, H, OT, QT, S, Z, SP> Executor for QemuForkExecutor<'a, H, OT, QT, S, SP> +impl<'a, EM, H, OT, QT, S, Z, SP, OF> Executor + for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> where - EM: EventManager, Z, State = S>, + EM: EventManager, Z, State = S>, H: FnMut(&S::Input) -> ExitKind, - S: State + HasMetadata + HasExecutions + HasLastReportTime, - OT: ObserversTuple, + S: State + HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + HasSolutions, + OT: ObserversTuple + Debug, QT: QemuHelperTuple, SP: ShMemProvider, - Z: UsesState, + OF: Feedback, + Z: HasObjective, { fn run_target( &mut self, @@ -390,13 +475,13 @@ where input: &Self::Input, ) -> Result { let emu = Emulator::get().unwrap(); - if self.first_exec { - self.hooks.helpers().first_exec_all(self.hooks); - self.first_exec = false; + if self.state.first_exec { + self.state.hooks.helpers().first_exec_all(self.state.hooks); + self.state.first_exec = false; } - self.hooks.helpers_mut().pre_exec_all(&emu, input); + self.state.hooks.helpers_mut().pre_exec_all(&emu, input); let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?; - self.hooks.helpers_mut().post_exec_all( + self.state.hooks.helpers_mut().post_exec_all( &emu, input, self.inner.observers_mut(), @@ -407,37 +492,43 @@ where } #[cfg(feature = "fork")] -impl<'a, H, OT, QT, S, SP> UsesObservers for QemuForkExecutor<'a, H, OT, QT, S, SP> +impl<'a, H, OT, QT, S, SP, EM, Z> UsesObservers for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind, OT: ObserversTuple, QT: QemuHelperTuple, - S: State, + S: State + HasExecutions, SP: ShMemProvider, + EM: UsesState, + Z: UsesState, { type Observers = OT; } #[cfg(feature = "fork")] -impl<'a, H, OT, QT, S, SP> UsesState for QemuForkExecutor<'a, H, OT, QT, S, SP> +impl<'a, H, OT, QT, S, SP, EM, Z> UsesState for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind, OT: ObserversTuple, QT: QemuHelperTuple, - S: State, + S: State + HasExecutions, SP: ShMemProvider, + EM: UsesState, + Z: UsesState, { type State = S; } #[cfg(feature = "fork")] -impl<'a, H, OT, QT, S, SP> HasObservers for QemuForkExecutor<'a, H, OT, QT, S, SP> +impl<'a, H, OT, QT, S, SP, EM, Z> HasObservers for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z> where H: FnMut(&S::Input) -> ExitKind, - S: State, + S: State + HasExecutions, OT: ObserversTuple, QT: QemuHelperTuple, SP: ShMemProvider, + EM: UsesState, + Z: UsesState, { #[inline] fn observers(&self) -> &OT { diff --git a/libafl_qemu/src/executor/stateful.rs b/libafl_qemu/src/executor/stateful.rs new file mode 100644 index 0000000000..512c9a8847 --- /dev/null +++ b/libafl_qemu/src/executor/stateful.rs @@ -0,0 +1,212 @@ +//! A `QEMU`-based executor for binary-only instrumentation in `LibAFL` +use core::{ + ffi::c_void, + fmt::{self, Debug, Formatter}, + time::Duration, +}; + +use libafl::{ + events::{EventFirer, EventRestarter}, + executors::{ + inprocess::{stateful::StatefulInProcessExecutor, HasInProcessHooks}, + Executor, ExitKind, HasObservers, + }, + feedbacks::Feedback, + fuzzer::HasObjective, + observers::{ObserversTuple, UsesObservers}, + state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState}, + Error, +}; + +#[cfg(emulation_mode = "usermode")] +use crate::executor::inproc_qemu_crash_handler; +#[cfg(emulation_mode = "systemmode")] +use crate::executor::{inproc_qemu_timeout_handler, BREAK_ON_TMOUT}; +use crate::{ + emu::Emulator, executor::QemuExecutorState, helper::QemuHelperTuple, hooks::QemuHooks, +}; + +pub struct StatefulQemuExecutor<'a, H, OT, QT, S> +where + H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, + S: State + HasExecutions, + OT: ObserversTuple, + QT: QemuHelperTuple, +{ + inner: StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>, +} + +impl<'a, H, OT, QT, S> Debug for StatefulQemuExecutor<'a, H, OT, QT, S> +where + H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, + S: State + HasExecutions, + OT: ObserversTuple + Debug, + QT: QemuHelperTuple + Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("QemuExecutor") + .field("inner", &self.inner) + .finish() + } +} + +impl<'a, H, OT, QT, S> StatefulQemuExecutor<'a, H, OT, QT, S> +where + H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, + S: State + HasExecutions, + OT: ObserversTuple, + QT: QemuHelperTuple + Debug, +{ + pub fn new( + hooks: &'a mut QemuHooks, + harness_fn: &'a mut H, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + timeout: Duration, + ) -> Result + where + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State + HasExecutions + HasCorpus + HasSolutions, + Z: HasObjective, + { + let qemu_state = QemuExecutorState::new::< + StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>, + EM, + OF, + OT, + Z, + >(hooks)?; + + let mut inner = StatefulInProcessExecutor::with_timeout( + harness_fn, qemu_state, observers, fuzzer, state, event_mgr, timeout, + )?; + + #[cfg(emulation_mode = "usermode")] + { + inner.inprocess_hooks_mut().crash_handler = inproc_qemu_crash_handler::< + StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>, + EM, + OF, + Z, + QT, + S, + > as *const c_void; + } + + #[cfg(emulation_mode = "systemmode")] + { + inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::< + StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>, + EM, + OF, + Z, + > as *const c_void; + } + + Ok(Self { inner }) + } + + pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>> { + &self.inner + } + + #[cfg(emulation_mode = "systemmode")] + pub fn break_on_timeout(&mut self) { + unsafe { + BREAK_ON_TMOUT = true; + } + } + + pub fn inner_mut( + &mut self, + ) -> &mut StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>> { + &mut self.inner + } + + pub fn hooks(&self) -> &QemuHooks { + self.inner.exposed_executor_state().hooks() + } + + pub fn hooks_mut(&mut self) -> &mut QemuHooks { + self.inner.exposed_executor_state_mut().hooks_mut() + } + + pub fn emulator(&self) -> &Emulator { + self.inner.exposed_executor_state().emulator() + } +} + +impl<'a, EM, H, OT, OF, QT, S, Z> Executor for StatefulQemuExecutor<'a, H, OT, QT, S> +where + EM: EventFirer + EventRestarter, + H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, + S: State + HasExecutions + HasCorpus + HasSolutions, + OT: ObserversTuple, + OF: Feedback, + QT: QemuHelperTuple + Debug, + Z: HasObjective, +{ + fn run_target( + &mut self, + fuzzer: &mut Z, + state: &mut Self::State, + mgr: &mut EM, + input: &Self::Input, + ) -> Result { + let emu = Emulator::get().unwrap(); + self.inner + .exposed_executor_state_mut() + .pre_exec::(input, &emu); + let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?; + self.inner + .exposed_executor_state + .post_exec::( + input, + &emu, + self.inner.inner.observers_mut(), + &mut exit_kind, + ); + Ok(exit_kind) + } +} + +impl<'a, H, OT, QT, S> UsesState for StatefulQemuExecutor<'a, H, OT, QT, S> +where + H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, + OT: ObserversTuple, + QT: QemuHelperTuple, + S: State + HasExecutions, +{ + type State = S; +} + +impl<'a, H, OT, QT, S> UsesObservers for StatefulQemuExecutor<'a, H, OT, QT, S> +where + H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, + OT: ObserversTuple, + QT: QemuHelperTuple, + S: State + HasExecutions, +{ + type Observers = OT; +} + +impl<'a, H, OT, QT, S> HasObservers for StatefulQemuExecutor<'a, H, OT, QT, S> +where + H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind, + S: State + HasExecutions, + OT: ObserversTuple, + QT: QemuHelperTuple, +{ + #[inline] + fn observers(&self) -> &OT { + self.inner.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.inner.observers_mut() + } +}