diff --git a/fuzzers/fuzzbench/src/lib.rs b/fuzzers/fuzzbench/src/lib.rs index 673758aeb9..c2051f1e64 100644 --- a/fuzzers/fuzzbench/src/lib.rs +++ b/fuzzers/fuzzbench/src/lib.rs @@ -390,8 +390,8 @@ fn fuzz( #[cfg(unix)] { let null_fd = file_null.as_raw_fd(); - dup2(null_fd, io::stdout().as_raw_fd())?; - dup2(null_fd, io::stderr().as_raw_fd())?; + // dup2(null_fd, io::stdout().as_raw_fd())?; + // dup2(null_fd, io::stderr().as_raw_fd())?; } // reopen file to make sure we're at the end log.replace( diff --git a/fuzzers/libfuzzer_libpng_accounting/Makefile.toml b/fuzzers/libfuzzer_libpng_accounting/Makefile.toml index 1ad37cc2b9..6f84428082 100644 --- a/fuzzers/libfuzzer_libpng_accounting/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_accounting/Makefile.toml @@ -82,7 +82,7 @@ windows_alias = "unsupported" [tasks.run_unix] script_runner = "@shell" script=''' -./${FUZZER_NAME} --cores 0 --input ./corpus & +./${FUZZER_NAME} --cores 0 --input ./corpus ''' dependencies = [ "fuzzer" ] diff --git a/fuzzers/libfuzzer_libpng_ctx/Makefile.toml b/fuzzers/libfuzzer_libpng_ctx/Makefile.toml index dc50cf4dd4..47aef9372a 100644 --- a/fuzzers/libfuzzer_libpng_ctx/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_ctx/Makefile.toml @@ -82,7 +82,7 @@ windows_alias = "unsupported" [tasks.run_unix] script_runner = "@shell" script=''' -./${FUZZER_NAME} --cores 0 --input ./corpus & +./${FUZZER_NAME} --cores 0 --input ./corpus ''' dependencies = [ "fuzzer" ] diff --git a/fuzzers/libfuzzer_libpng_launcher/Makefile.toml b/fuzzers/libfuzzer_libpng_launcher/Makefile.toml index 4aacab7394..f3188d9f44 100644 --- a/fuzzers/libfuzzer_libpng_launcher/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_launcher/Makefile.toml @@ -82,7 +82,7 @@ windows_alias = "unsupported" [tasks.run_unix] script_runner = "@shell" script=''' -./${FUZZER_NAME} --cores 0 --input ./corpus & +./${FUZZER_NAME} --cores 0 --input ./corpus ''' dependencies = [ "fuzzer" ] diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 5d5124f388..f2e587a06f 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -165,7 +165,7 @@ const LLMP_PAGE_HEADER_LEN: usize = size_of::(); /// The llmp broker registers a signal handler for cleanups on `SIGINT`. #[cfg(unix)] -static mut GLOBAL_SIGHANDLER_STATE: LlmpBrokerSignalHandler = LlmpBrokerSignalHandler { +static mut LLMP_SIGHANDLER_STATE: LlmpShutdownSignalHandler = LlmpShutdownSignalHandler { shutting_down: false, }; @@ -1770,12 +1770,12 @@ where /// A signal handler for the [`LlmpBroker`]. #[cfg(unix)] #[derive(Debug, Clone)] -pub struct LlmpBrokerSignalHandler { +pub struct LlmpShutdownSignalHandler { shutting_down: bool, } #[cfg(unix)] -impl Handler for LlmpBrokerSignalHandler { +impl Handler for LlmpShutdownSignalHandler { fn handle(&mut self, _signal: Signal, _info: siginfo_t, _context: &mut ucontext_t) { unsafe { ptr::write_volatile(&mut self.shutting_down, true); @@ -1957,7 +1957,7 @@ where #[cfg(unix)] #[allow(clippy::unused_self)] fn is_shutting_down(&self) -> bool { - unsafe { ptr::read_volatile(&GLOBAL_SIGHANDLER_STATE.shutting_down) } + unsafe { ptr::read_volatile(&LLMP_SIGHANDLER_STATE.shutting_down) } } /// Always returns true on platforms, where no shutdown signal handlers are supported @@ -1976,7 +1976,7 @@ where F: FnMut(ClientId, Tag, Flags, &[u8]) -> Result, { #[cfg(unix)] - if let Err(_e) = unsafe { setup_signal_handler(&mut GLOBAL_SIGHANDLER_STATE) } { + if let Err(_e) = unsafe { setup_signal_handler(&mut LLMP_SIGHANDLER_STATE) } { // We can live without a proper ctrl+c signal handler. Print and ignore. #[cfg(feature = "std")] println!("Failed to setup signal handlers: {_e}"); diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 2f8add2a1b..0e0d4c7477 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -31,6 +31,11 @@ use crate::bolts::{ }; #[cfg(feature = "std")] use crate::bolts::{llmp::LlmpConnection, shmem::StdShMemProvider, staterestore::StateRestorer}; +#[cfg(all(unix, feature = "std"))] +use crate::{ + bolts::os::unix_signals::setup_signal_handler, + events::{shutdown_handler, SHUTDOWN_SIGHANDLER_DATA}, +}; use crate::{ bolts::{ llmp::{self, Flags, LlmpClient, LlmpClientDescription, Tag}, @@ -885,17 +890,37 @@ where mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL); // First, create a channel from the current fuzzer to the next to store state between restarts. - let staterestorer: StateRestorer = + let mut staterestorer: StateRestorer = StateRestorer::new(self.shmem_provider.new_shmem(256 * 1024 * 1024)?); // Store the information to a map. staterestorer.write_to_env(_ENV_FUZZER_SENDER)?; + #[cfg(unix)] + unsafe { + let data = &mut SHUTDOWN_SIGHANDLER_DATA; + // Write the pointer to staterestorer so we can release its shmem later + core::ptr::write_volatile( + &mut data.staterestorer_ptr, + &mut staterestorer as *mut _ as *mut std::ffi::c_void, + ); + data.allocator_pid = std::process::id() as usize; + data.shutdown_handler = shutdown_handler:: as *const std::ffi::c_void; + } + + // We setup signal handlers to clean up shmem segments used by state restorer + #[cfg(unix)] + if let Err(_e) = unsafe { setup_signal_handler(&mut SHUTDOWN_SIGHANDLER_DATA) } { + // We can live without a proper ctrl+c signal handler. Print and ignore. + #[cfg(feature = "std")] + println!("Failed to setup signal handlers: {_e}"); + } + let mut ctr: u64 = 0; // Client->parent loop loop { println!("Spawning next client (id {ctr})"); - // On Unix, we fork + // On Unix, we fork (when fork feature is enabled) #[cfg(all(unix, feature = "fork"))] let child_status = { self.shmem_provider.pre_fork()?; diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index 6872f0e145..b8d34ed54c 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -8,6 +8,8 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; +#[cfg(all(unix, feature = "std"))] +use core::ffi::c_void; use core::{fmt, hash::Hasher, marker::PhantomData, time::Duration}; use ahash::AHasher; @@ -16,6 +18,10 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use uuid::Uuid; +#[cfg(all(unix, feature = "std"))] +use crate::bolts::os::unix_signals::{siginfo_t, ucontext_t, Handler, Signal}; +#[cfg(all(unix, feature = "std"))] +use crate::bolts::{shmem::ShMemProvider, staterestore::StateRestorer}; use crate::{ bolts::current_time, executors::ExitKind, @@ -27,6 +33,76 @@ use crate::{ Error, }; +/// Check if ctrl-c is sent with this struct +#[cfg(all(unix, feature = "std"))] +pub static mut SHUTDOWN_SIGHANDLER_DATA: ShutdownSignalData = ShutdownSignalData { + allocator_pid: 0, + staterestorer_ptr: core::ptr::null_mut(), + shutdown_handler: core::ptr::null(), +}; + +/// A signal handler for releasing staterestore shmem +/// This struct holds a pointer to StateRestore and clean up the shmem segment used by it. +#[cfg(all(unix, feature = "std"))] +#[derive(Debug, Clone)] +pub struct ShutdownSignalData { + allocator_pid: usize, + staterestorer_ptr: *mut c_void, + shutdown_handler: *const c_void, +} + +/// Type for shutdown handler +#[cfg(all(unix, feature = "std"))] +pub type ShutdownFuncPtr = + unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut ShutdownSignalData); + +/// Shutdown handler. `SigTerm`, `SigInterrupt`, `SigQuit` call this +/// We can't handle SIGKILL in the signal handler, this means that you shouldn't kill your fuzzer with `kill -9` because then the shmem segments are never freed +#[cfg(all(unix, feature = "std"))] +pub unsafe fn shutdown_handler( + signal: Signal, + _info: siginfo_t, + _context: &mut ucontext_t, + data: &mut ShutdownSignalData, +) where + SP: ShMemProvider, +{ + println!( + "Fuzzer shutdown by Signal: {} Pid: {}", + signal, + std::process::id() + ); + + let ptr = data.staterestorer_ptr; + if ptr.is_null() || data.allocator_pid != std::process::id() as usize { + // Do nothing + } else { + // The process allocated the staterestorer map must take care of it + let sr = (ptr as *mut StateRestorer).as_mut().unwrap(); + // println!("{:#?}", sr); + std::ptr::drop_in_place(sr); + } + println!("Bye!"); + std::process::exit(0); +} + +#[cfg(all(unix, feature = "std"))] +impl Handler for ShutdownSignalData { + fn handle(&mut self, signal: Signal, info: siginfo_t, context: &mut ucontext_t) { + unsafe { + let data = &mut SHUTDOWN_SIGHANDLER_DATA; + if !data.shutdown_handler.is_null() { + let func: ShutdownFuncPtr = std::mem::transmute(data.shutdown_handler); + (func)(signal, info, context, data); + } + } + } + + fn signals(&self) -> Vec { + vec![Signal::SigTerm, Signal::SigInterrupt, Signal::SigQuit] + } +} + /// A per-fuzzer unique `ID`, usually starting with `0` and increasing /// by `1` in multiprocessed `EventManager`s, such as [`self::llmp::LlmpEventManager`]. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] diff --git a/libafl/src/events/simple.rs b/libafl/src/events/simple.rs index 9594a8fbcb..6e9ce50338 100644 --- a/libafl/src/events/simple.rs +++ b/libafl/src/events/simple.rs @@ -5,6 +5,10 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; +#[cfg(all(unix, feature = "std"))] +use core::ffi::c_void; +#[cfg(all(unix, feature = "std"))] +use core::ptr::write_volatile; #[cfg(feature = "std")] use core::sync::atomic::{compiler_fence, Ordering}; use core::{fmt::Debug, marker::PhantomData}; @@ -17,6 +21,11 @@ use super::{CustomBufEventResult, CustomBufHandlerFn, HasCustomBufHandlers, Prog use crate::bolts::os::startable_self; #[cfg(all(feature = "std", feature = "fork", unix))] use crate::bolts::os::{fork, ForkResult}; +#[cfg(all(unix, feature = "std"))] +use crate::{ + bolts::os::unix_signals::setup_signal_handler, + events::{shutdown_handler, SHUTDOWN_SIGHANDLER_DATA}, +}; #[cfg(feature = "std")] use crate::{ bolts::{shmem::ShMemProvider, staterestore::StateRestorer}, @@ -436,11 +445,31 @@ where // We start ourself as child process to actually fuzz let mut staterestorer = if std::env::var(_ENV_FUZZER_SENDER).is_err() { // First, create a place to store state in, for restarts. - let staterestorer: StateRestorer = + let mut staterestorer: StateRestorer = StateRestorer::new(shmem_provider.new_shmem(256 * 1024 * 1024)?); //let staterestorer = { LlmpSender::new(shmem_provider.clone(), 0, false)? }; staterestorer.write_to_env(_ENV_FUZZER_SENDER)?; + #[cfg(unix)] + unsafe { + let data = &mut SHUTDOWN_SIGHANDLER_DATA; + // Write the pointer to staterestorer so we can release its shmem later + write_volatile( + &mut data.staterestorer_ptr, + &mut staterestorer as *mut _ as *mut c_void, + ); + data.allocator_pid = std::process::id() as usize; + data.shutdown_handler = shutdown_handler:: as *const c_void; + } + + // We setup signal handlers to clean up shmem segments used by state restorer + #[cfg(unix)] + if let Err(_e) = unsafe { setup_signal_handler(&mut SHUTDOWN_SIGHANDLER_DATA) } { + // We can live without a proper ctrl+c signal handler. Print and ignore. + #[cfg(feature = "std")] + println!("Failed to setup signal handlers: {_e}"); + } + let mut ctr: u64 = 0; // Client->parent loop loop {