Skip to content

inprocessfork executor #237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Aug 7, 2021
157 changes: 157 additions & 0 deletions libafl/src/executors/inprocess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))]
Expand Down Expand Up @@ -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<I, S>,
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<EM, I, S, Z>
for InProcessForkExecutor<'a, H, I, OT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
SP: ShMemProvider,
{
#[allow(unreachable_code)]
#[inline]
fn run_target(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
input: &I,
) -> Result<ExitKind, Error> {
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<I, S>,
SP: ShMemProvider,
{
pub fn new<EM, OC, OF, Z>(
harness_fn: &'a mut H,
observers: OT,
_fuzzer: &mut Z,
_state: &mut S,
_event_mgr: &mut EM,
shmem_provider: SP,
) -> Result<Self, Error>
where
EM: EventFirer<I, S> + EventRestarter<S>,
OC: Corpus<I>,
OF: Feedback<I, S>,
S: HasSolutions<OC, I> + HasClientPerfStats,
Z: HasObjective<I, OF, S>,
{
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<I, OT, S> for InProcessForkExecutor<'a, H, I, OT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
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};
Expand All @@ -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;
Expand All @@ -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());
}
}
3 changes: 3 additions & 0 deletions libafl/src/executors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions libafl_targets/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
11 changes: 11 additions & 0 deletions libafl_targets/src/coverage.rs
Original file line number Diff line number Diff line change
@@ -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;
14 changes: 12 additions & 2 deletions libafl_targets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))]
Expand Down
46 changes: 44 additions & 2 deletions libafl_targets/src/sancov_pcguard.rs
Original file line number Diff line number Diff line change
@@ -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!(
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down