Skip to content
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

Implement enters target for guarding execution from double-mutability #2878

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion libafl/src/executors/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -425,6 +425,7 @@ where
OT: MatchName + ObserversTuple<I, S>,
S: HasExecutions,
T: CommandConfigurator<I, Pid> + Debug,
HT: InitableExecutorHooksTuple<S>,
{
/// Linux specific low level implementation, to directly handle `fork`, `exec` and use linux
/// `ptrace`
Expand Down
70 changes: 6 additions & 64 deletions libafl/src/executors/hooks/inprocess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<I, S> {
/// On crash C function pointer
#[cfg(feature = "std")]
pub crash_handler: *const c_void,
/// On timeout C function pointer
#[cfg(feature = "std")]
pub timeout_handler: *const c_void,
/// `TImer` struct
#[cfg(feature = "std")]
pub timer: TimerStruct,
Expand Down Expand Up @@ -185,53 +169,17 @@ impl<I, S> HasTimeout for InProcessHooks<I, S> {
}
}

impl<I, S> ExecutorHook<I, S> for InProcessHooks<I, S> {
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<I, S> InProcessHooks<I, S> {
/// Create new [`InProcessHooks`].
#[cfg(unix)]
#[allow(unused_variables)] // for `exec_tmout` without `std`
pub fn new<E, EM, OF, Z>(exec_tmout: Duration) -> Result<Self, Error>
where
E: Executor<EM, I, S, Z> + HasObservers + HasInProcessHooks<I, S>,
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
I: Input + Clone,
{
pub fn new(exec_tmout: Duration) -> Result<Self, Error> {
// # 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::<E, EM, I, OF, S, Z>();
// # 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.
Expand All @@ -243,12 +191,6 @@ impl<I, S> InProcessHooks<I, S> {
compiler_fence(Ordering::SeqCst);
Ok(Self {
#[cfg(feature = "std")]
crash_handler: unix_signal_handler::inproc_crash_handler::<E, EM, I, OF, S, Z>
as *const c_void,
#[cfg(feature = "std")]
timeout_handler: unix_signal_handler::inproc_timeout_handler::<E, EM, I, OF, S, Z>
as *const _,
#[cfg(feature = "std")]
timer: TimerStruct::new(exec_tmout),
phantom: PhantomData,
})
Expand Down Expand Up @@ -340,10 +282,6 @@ impl<I, S> InProcessHooks<I, S> {
#[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,
Expand Down Expand Up @@ -372,6 +310,8 @@ pub struct InProcessExecutorHandlerData {
#[cfg(feature = "std")]
pub(crate) timeout_handler: *const c_void,

pub(crate) timer: Option<TimerStruct>,

#[cfg(all(windows, feature = "std"))]
pub(crate) ptp_timer: Option<PTP_TIMER>,
#[cfg(all(windows, feature = "std"))]
Expand Down Expand Up @@ -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(),
Expand Down
3 changes: 0 additions & 3 deletions libafl/src/executors/hooks/inprocess_fork.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ pub struct InChildProcessHooks<I, S> {
}

impl<I, S> ExecutorHook<I, S> for InChildProcessHooks<I, S> {
/// 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 {
Expand Down
31 changes: 23 additions & 8 deletions libafl/src/executors/hooks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<I, S> {
pub trait InitableExecutorHook<S> {
/// 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<I, S> {
/// 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<I, S> {
pub trait InitableExecutorHooksTuple<S> {
/// 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<I, S> {
/// 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<I, S> ExecutorHooksTuple<I, S> for () {
impl<S> InitableExecutorHooksTuple<S> for () {
fn init_all(&mut self, _state: &mut S) {}
}

impl<I, S> ExecutorHooksTuple<I, S> for () {
fn pre_exec_all(&mut self, _state: &mut S, _input: &I) {}
fn post_exec_all(&mut self, _state: &mut S, _input: &I) {}
}

impl<Head, Tail, I, S> ExecutorHooksTuple<I, S> for (Head, Tail)
impl<Head, Tail, S> InitableExecutorHooksTuple<S> for (Head, Tail)
where
Head: ExecutorHook<I, S>,
Tail: ExecutorHooksTuple<I, S>,
Head: InitableExecutorHook<S>,
Tail: InitableExecutorHooksTuple<S>,
{
fn init_all(&mut self, state: &mut S) {
self.0.init(state);
self.1.init_all(state);
}
}

impl<Head, Tail, I, S> ExecutorHooksTuple<I, S> for (Head, Tail)
where
Head: ExecutorHook<I, S>,
Tail: ExecutorHooksTuple<I, S>,
{
fn pre_exec_all(&mut self, state: &mut S, input: &I) {
self.0.pre_exec(state, input);
self.1.pre_exec_all(state, input);
Expand Down
3 changes: 2 additions & 1 deletion libafl/src/executors/hooks/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,15 @@ 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;
}

/// 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)]
Expand Down
54 changes: 16 additions & 38 deletions libafl/src/executors/hooks/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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<E, EM, I, OF, S, Z>()
where
E: Executor<EM, I, S, Z> + HasObservers,
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
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::<E>();
let state = (*data).state_mut::<S>();
let input = (*data).take_current_input::<I>();
let fuzzer = (*data).fuzzer_mut::<Z>();
let event_mgr = (*data).event_mgr_mut::<EM>();

run_observers_and_save_state::<E, EM, I, OF, S, Z>(
executor,
state,
input,
fuzzer,
event_mgr,
ExitKind::Crash,
);
let func: HandlerFuncPtr = transmute((*data).crash_handler);

let mut empty_siginfo = MaybeUninit::<siginfo_t>::uninit().assume_init();
(func)(Signal::SigAbort, &mut empty_siginfo, None, data);

libc::_exit(128 + 6); // SIGABRT exit code
}
(*data).set_in_handler(in_handler);
}));
}
Expand All @@ -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<E, EM, I, OF, S, Z>(
pub unsafe fn inproc_timeout_handler<E, EM, I, S, Z>(
_signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, I, S, Z> + HasInProcessHooks<I, S> + HasObservers,
E: Executor<EM, I, S, Z> + HasObservers,
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
Z: HasObjective,
Z::Objective: Feedback<EM, I, E::Observers, S>,
I: Input + Clone,
{
// this stuff is for batch timeout
Expand All @@ -158,7 +136,7 @@ pub mod unix_signal_handler {

log::error!("Timeout in fuzz run.");

run_observers_and_save_state::<E, EM, I, OF, S, Z>(
run_observers_and_save_state::<E, EM, I, S, Z>(
executor,
state,
input,
Expand All @@ -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<E, EM, I, OF, S, Z>(
pub unsafe fn inproc_crash_handler<E, EM, I, S, Z>(
signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
Expand All @@ -186,9 +164,9 @@ pub mod unix_signal_handler {
E: Executor<EM, I, S, Z> + HasObservers,
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
Z: HasObjective,
Z::Objective: Feedback<EM, I, E::Observers, S>,
I: Input + Clone,
{
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
Expand Down Expand Up @@ -229,7 +207,7 @@ pub mod unix_signal_handler {
}
}

run_observers_and_save_state::<E, EM, I, OF, S, Z>(
run_observers_and_save_state::<E, EM, I, S, Z>(
executor,
state,
input,
Expand Down
Loading
Loading