SIGINT handlers, and Release StateRestorer shmem (#894)

* drop not working

* why drop_in_place works but drop does not

* stop shmem leak

* don't kill -9 fuzzer

* don't put fuzzer background

* no &

* nostd

* fix

* fix

* windows, clippy

* fix

* fmt

* windows
This commit is contained in:
Dongjia "toka" Zhang 2022-12-05 02:56:19 +09:00 committed by GitHub
parent 3bad100cb7
commit cd8367d3e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 143 additions and 13 deletions

View File

@ -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(

View File

@ -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" ]

View File

@ -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" ]

View File

@ -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" ]

View File

@ -165,7 +165,7 @@ const LLMP_PAGE_HEADER_LEN: usize = size_of::<LlmpPage>();
/// 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<LlmpMsgHookResult, Error>,
{
#[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}");

View File

@ -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<SP> =
let mut staterestorer: StateRestorer<SP> =
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::<SP> 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()?;

View File

@ -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<SP>(
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<SP>).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<Signal> {
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)]

View File

@ -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<SP> =
let mut staterestorer: StateRestorer<SP> =
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::<SP> 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 {