diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 250889eabda..8b34d0a9723 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -9,6 +9,15 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, }; +#[cfg(all(feature = "std", unix))] +use nix::{ + sys::wait::{waitpid, WaitStatus}, + unistd::{fork, ForkResult}, +}; + +#[cfg(all(feature = "std", unix))] +use crate::bolts::shmem::ShMemProvider; + #[cfg(unix)] use crate::bolts::os::unix_signals::setup_signal_handler; #[cfg(all(windows, feature = "std"))] @@ -696,6 +705,130 @@ mod windows_exception_handler { } } +#[cfg(all(feature = "std", unix))] +pub struct InProcessForkExecutor<'a, H, I, OT, S, SP> +where + H: FnMut(&I) -> ExitKind, + I: Input, + OT: ObserversTuple, + SP: ShMemProvider, +{ + harness_fn: &'a mut H, + shmem_provider: SP, + observers: OT, + phantom: PhantomData<(I, S)>, +} + +#[cfg(all(feature = "std", unix))] +impl<'a, EM, H, I, OT, S, Z, SP> Executor + for InProcessForkExecutor<'a, H, I, OT, S, SP> +where + H: FnMut(&I) -> ExitKind, + I: Input, + OT: ObserversTuple, + SP: ShMemProvider, +{ + #[allow(unreachable_code)] + #[inline] + fn run_target( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + input: &I, + ) -> Result { + unsafe { + self.shmem_provider.pre_fork()?; + match fork() { + Ok(ForkResult::Child) => { + // Child + self.shmem_provider.post_fork(true)?; + + (self.harness_fn)(input); + + std::process::exit(0); + + Ok(ExitKind::Ok) + } + Ok(ForkResult::Parent { child }) => { + // Parent + self.shmem_provider.post_fork(false)?; + + let res = waitpid(child, None)?; + match res { + WaitStatus::Signaled(_, _, _) => Ok(ExitKind::Crash), + _ => Ok(ExitKind::Ok), + } + } + Err(e) => Err(Error::from(e)), + } + } + } +} + +#[cfg(all(feature = "std", unix))] +impl<'a, H, I, OT, S, SP> InProcessForkExecutor<'a, H, I, OT, S, SP> +where + H: FnMut(&I) -> ExitKind, + I: Input, + OT: ObserversTuple, + SP: ShMemProvider, +{ + pub fn new( + harness_fn: &'a mut H, + observers: OT, + _fuzzer: &mut Z, + _state: &mut S, + _event_mgr: &mut EM, + shmem_provider: SP, + ) -> Result + where + EM: EventFirer + EventRestarter, + OC: Corpus, + OF: Feedback, + S: HasSolutions + HasClientPerfStats, + Z: HasObjective, + { + Ok(Self { + harness_fn, + shmem_provider, + observers, + phantom: PhantomData, + }) + } + + /// Retrieve the harness function. + #[inline] + pub fn harness(&self) -> &H { + self.harness_fn + } + + /// Retrieve the harness function for a mutable reference. + #[inline] + pub fn harness_mut(&mut self) -> &mut H { + self.harness_fn + } +} + +#[cfg(all(feature = "std", unix))] +impl<'a, H, I, OT, S, SP> HasObservers for InProcessForkExecutor<'a, H, I, OT, S, SP> +where + H: FnMut(&I) -> ExitKind, + I: Input, + OT: ObserversTuple, + SP: ShMemProvider, +{ + #[inline] + fn observers(&self) -> &OT { + &self.observers + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + &mut self.observers + } +} + #[cfg(test)] mod tests { use core::{marker::PhantomData, ptr}; @@ -706,6 +839,12 @@ mod tests { inputs::NopInput, }; + #[cfg(all(feature = "std", unix))] + use crate::{ + bolts::shmem::{ShMemProvider, StdShMemProvider}, + executors::InProcessForkExecutor, + }; + #[test] fn test_inmem_exec() { let mut harness = |_buf: &NopInput| ExitKind::Ok; @@ -722,4 +861,22 @@ mod tests { .run_target(&mut (), &mut (), &mut (), &input) .is_ok()); } + + #[test] + #[cfg(all(feature = "std", unix))] + fn test_inprocessfork_exec() { + let provider = StdShMemProvider::new().unwrap(); + + let mut harness = |_buf: &NopInput| ExitKind::Ok; + let mut in_process_fork_executor = InProcessForkExecutor::<_, NopInput, (), (), _> { + harness_fn: &mut harness, + shmem_provider: provider, + observers: tuple_list!(), + phantom: PhantomData, + }; + let input = NopInput {}; + assert!(in_process_fork_executor + .run_target(&mut (), &mut (), &mut (), &input) + .is_ok()); + } } diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index f3162a3f7d5..f90f6b63bcd 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -2,6 +2,9 @@ pub mod inprocess; pub use inprocess::InProcessExecutor; +#[cfg(all(feature = "std", unix))] +pub use inprocess::InProcessForkExecutor; + pub mod timeout; pub use timeout::TimeoutExecutor; diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index 685a1cb1d9c..6bed3664129 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -15,6 +15,8 @@ default = [] libfuzzer = [] sancov_pcguard_edges = [] sancov_pcguard_hitcounts = [] +sancov_pcguard_edges_ptr = [] +sancov_pcguard_hitcounts_ptr = [] sancov_value_profile = [] sancov_cmplog = [] sancov_pcguard = ["sancov_pcguard_hitcounts"] diff --git a/libafl_targets/src/coverage.rs b/libafl_targets/src/coverage.rs index 9f5121ecac3..66ac8575279 100644 --- a/libafl_targets/src/coverage.rs +++ b/libafl_targets/src/coverage.rs @@ -1,8 +1,19 @@ //! Coverage maps as static mut array use crate::EDGES_MAP_SIZE; +#[cfg(any( + feature = "sancov_pcguard_edges_ptr", + feature = "sancov_pcguard_hitcounts_ptr" +))] +use core::ptr; /// The map for edges. pub static mut EDGES_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; +#[cfg(any( + feature = "sancov_pcguard_edges_ptr", + feature = "sancov_pcguard_hitcounts_ptr" +))] +pub static mut EDGES_MAP_PTR: *mut u8 = ptr::null_mut(); + /// The max count of edges tracked. pub static mut MAX_EDGES_NUM: usize = 0; diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index 63b3ccccb2e..51bb795c271 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -2,9 +2,19 @@ include!(concat!(env!("OUT_DIR"), "/constants.rs")); -#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] +#[cfg(any( + feature = "sancov_pcguard_edges", + feature = "sancov_pcguard_hitcounts", + feature = "sancov_pcguard_edges_ptr", + feature = "sancov_pcguard_hitcounts_ptr" +))] pub mod sancov_pcguard; -#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] +#[cfg(any( + feature = "sancov_pcguard_edges", + feature = "sancov_pcguard_hitcounts", + feature = "sancov_pcguard_edges_ptr", + feature = "sancov_pcguard_hitcounts_ptr" +))] pub use sancov_pcguard::*; #[cfg(any(feature = "sancov_cmplog", feature = "sancov_value_profile"))] diff --git a/libafl_targets/src/sancov_pcguard.rs b/libafl_targets/src/sancov_pcguard.rs index fac770f4c5e..1a81f271765 100644 --- a/libafl_targets/src/sancov_pcguard.rs +++ b/libafl_targets/src/sancov_pcguard.rs @@ -1,7 +1,21 @@ //! [`LLVM` `PcGuard`](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards) runtime for `LibAFL`. +#[cfg(any( + feature = "sancov_pcguard_edges_ptr", + feature = "sancov_pcguard_hitcounts_ptr" +))] +use crate::coverage::EDGES_MAP_PTR; use crate::coverage::{EDGES_MAP, MAX_EDGES_NUM}; +#[cfg(all( + feature = "sancov_pcguard_edges_ptr", + feature = "sancov_pcguard_hitcounts_ptr" +))] +#[cfg(not(any(doc, feature = "clippy")))] +compile_error!( + "the libafl_targets `pcguard_edges_ptr` and `pcguard_hitcounts_ptr` features are mutually exclusive." +); + #[cfg(all(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] #[cfg(not(any(doc, feature = "clippy")))] compile_error!( @@ -13,7 +27,12 @@ compile_error!( /// # Safety /// Dereferences `guard`, reads the position from there, then dereferences the [`EDGES_MAP`] at that position. /// Should usually not be called directly. -#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] +#[cfg(any( + feature = "sancov_pcguard_edges", + feature = "sancov_pcguard_hitcounts", + feature = "sancov_pcguard_edges_ptr", + feature = "sancov_pcguard_hitcounts_ptr" +))] #[no_mangle] pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { let pos = *guard as usize; @@ -26,15 +45,38 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { let val = (*EDGES_MAP.get_unchecked(pos) as u8).wrapping_add(1); *EDGES_MAP.get_unchecked_mut(pos) = val; } + #[cfg(feature = "sancov_pcguard_edges_ptr")] + { + (EDGES_MAP_PTR as *mut u8).add(pos).write(1); + } + #[cfg(feature = "sancov_pcguard_hitcounts_ptr")] + { + let addr = (EDGES_MAP_PTR as *mut u8).add(pos); + let val = addr.read().wrapping_add(1); + addr.write(val); + } } /// Initialize the sancov `pc_guard` - usually called by `llvm`. /// /// # Safety /// Dereferences at `start` and writes to it. -#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] +#[cfg(any( + feature = "sancov_pcguard_edges", + feature = "sancov_pcguard_hitcounts", + feature = "sancov_pcguard_edges_ptr", + feature = "sancov_pcguard_hitcounts_ptr" +))] #[no_mangle] pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) { + #[cfg(any( + feature = "sancov_pcguard_edges_ptr", + feature = "sancov_pcguard_hitcounts_ptr" + ))] + if EDGES_MAP_PTR.is_null() { + EDGES_MAP_PTR = EDGES_MAP.as_mut_ptr(); + } + if start == stop || *start != 0 { return; }