Skip to content

Commit

Permalink
Fix Ctrl-C Handler (AFLplusplus#2124)
Browse files Browse the repository at this point in the history
* fix

* fix

* win

* win clp

* a

* FMT

* aaaaaaaaaaa

* aa

---------

Co-authored-by: Your Name <[email protected]>
  • Loading branch information
tokatoka and Your Name authored Apr 30, 2024
1 parent 61ac4ea commit b231803
Show file tree
Hide file tree
Showing 7 changed files with 371 additions and 272 deletions.
64 changes: 30 additions & 34 deletions libafl/src/events/llmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use typed_builder::TypedBuilder;
use super::{hooks::EventManagerHooksTuple, CustomBufEventResult, CustomBufHandlerFn};
#[cfg(any(feature = "std", feature = "adaptive_serialization"))]
use crate::events::AdaptiveSerializer;
#[cfg(all(unix, feature = "std"))]
#[cfg(all(unix, feature = "std", not(miri)))]
use crate::events::EVENTMGR_SIGHANDLER_STATE;
#[cfg(feature = "adaptive_serialization")]
use crate::observers::TimeObserver;
Expand Down Expand Up @@ -1426,19 +1426,6 @@ where
S: State + HasExecutions,
MT: Monitor + Clone,
{
/// Internal function, returns true when shuttdown is requested by a `SIGINT` signal
#[inline]
#[allow(clippy::unused_self)]
fn is_shutting_down() -> bool {
#[cfg(unix)]
unsafe {
core::ptr::read_volatile(core::ptr::addr_of!(EVENTMGR_SIGHANDLER_STATE.shutting_down))
}

#[cfg(windows)]
false
}

/// Launch the broker and the clients and fuzz
pub fn launch(&mut self) -> Result<(Option<S>, LlmpRestartingEventManager<EMH, S, SP>), Error> {
// We start ourself as child process to actually fuzz
Expand Down Expand Up @@ -1549,15 +1536,6 @@ where
// Store the information to a map.
staterestorer.write_to_env(_ENV_FUZZER_SENDER)?;

// We setup signal handlers to clean up shmem segments used by state restorer
#[cfg(all(unix, not(miri)))]
if let Err(_e) =
unsafe { setup_signal_handler(addr_of_mut!(EVENTMGR_SIGHANDLER_STATE)) }
{
// We can live without a proper ctrl+c signal handler. Print and ignore.
log::error!("Failed to setup signal handlers: {_e}");
}

let mut ctr: u64 = 0;
// Client->parent loop
loop {
Expand All @@ -1570,31 +1548,56 @@ where
match unsafe { fork() }? {
ForkResult::Parent(handle) => {
unsafe {
EVENTMGR_SIGHANDLER_STATE.set_exit_from_main();
libc::signal(libc::SIGINT, libc::SIG_IGN);
}
self.shmem_provider.post_fork(false)?;
handle.status()
}
ForkResult::Child => {
// We setup signal handlers to clean up shmem segments used by state restorer
#[cfg(all(unix, not(miri)))]
if let Err(_e) = unsafe {
setup_signal_handler(addr_of_mut!(EVENTMGR_SIGHANDLER_STATE))
} {
// We can live without a proper ctrl+c signal handler. Print and ignore.
log::error!("Failed to setup signal handlers: {_e}");
}
// println!("child {}", std::process::id());
self.shmem_provider.post_fork(true)?;
break (staterestorer, self.shmem_provider.clone(), core_id);
}
}
};

#[cfg(all(unix, not(feature = "fork")))]
// If this guy wants to fork, then ignore sigit
#[cfg(any(windows, not(feature = "fork")))]
unsafe {
EVENTMGR_SIGHANDLER_STATE.set_exit_from_main();
#[cfg(windows)]
libafl_bolts::os::windows_exceptions::signal(
libafl_bolts::os::windows_exceptions::SIGINT,
libafl_bolts::os::windows_exceptions::sig_ign(),
);

#[cfg(unix)]
libc::signal(libc::SIGINT, libc::SIG_IGN);
}

// On Windows (or in any case without fork), we spawn ourself again
#[cfg(any(windows, not(feature = "fork")))]
let child_status = startable_self()?.status()?;
#[cfg(all(unix, not(feature = "fork")))]
#[cfg(any(windows, not(feature = "fork")))]
let child_status = child_status.code().unwrap_or_default();

compiler_fence(Ordering::SeqCst);

if child_status == crate::events::CTRL_C_EXIT || staterestorer.wants_to_exit() {
// if ctrl-c is pressed, we end up in this branch
if let Err(err) = mgr.detach_from_broker(self.broker_port) {
log::error!("Failed to detach from broker: {err}");
}
return Err(Error::shutting_down());
}

#[allow(clippy::manual_assert)]
if !staterestorer.has_content() && !self.serialize_state.oom_safe() {
if let Err(err) = mgr.detach_from_broker(self.broker_port) {
Expand All @@ -1608,13 +1611,6 @@ where
panic!("Fuzzer-respawner: Storing state in crashed fuzzer instance did not work, no point to spawn the next client! This can happen if the child calls `exit()`, in that case make sure it uses `abort()`, if it got killed unrecoverable (OOM), or if there is a bug in the fuzzer itself. (Child exited with: {child_status})");
}

if staterestorer.wants_to_exit() || Self::is_shutting_down() {
// if ctrl-c is pressed, we end up in this branch
if let Err(err) = mgr.detach_from_broker(self.broker_port) {
log::error!("Failed to detach from broker: {err}");
}
return Err(Error::shutting_down());
}
ctr = ctr.wrapping_add(1);
}
} else {
Expand Down
60 changes: 20 additions & 40 deletions libafl/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,31 +55,23 @@ use crate::{
state::HasScalabilityMonitor,
};

/// The special exit code when the target exited throught ctrl-c
#[cfg(unix)]
pub const CTRL_C_EXIT: i32 = 100;
/// The special exit code when the target exited throught ctrl-c
#[cfg(windows)]
pub const CTRL_C_EXIT: i32 = -1073741510;

/// Check if ctrl-c is sent with this struct
#[cfg(all(unix, feature = "std"))]
pub static mut EVENTMGR_SIGHANDLER_STATE: ShutdownSignalData = ShutdownSignalData {
shutting_down: false,
exit_from_main: false,
};
pub static mut EVENTMGR_SIGHANDLER_STATE: ShutdownSignalData = ShutdownSignalData {};

/// A signal handler for releasing `StateRestore` `ShMem`
/// This struct holds a pointer to `StateRestore` and clean up the `ShMem` segment used by it.
/// A signal handler for catching ctrl-c.
/// The purpose of this signal handler is solely for calling `exit()` with a specific exit code 100
/// In this way, the restarting manager can tell that we really want to exit
#[cfg(all(unix, feature = "std"))]
#[derive(Debug, Clone)]
pub struct ShutdownSignalData {
shutting_down: bool,
exit_from_main: bool,
}

#[cfg(all(unix, feature = "std"))]
impl ShutdownSignalData {
/// Set the flag to true, indicating that this process has allocated shmem
pub fn set_exit_from_main(&mut self) {
unsafe {
core::ptr::write_volatile(core::ptr::addr_of_mut!(self.exit_from_main), true);
}
}
}
pub struct 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
Expand All @@ -91,27 +83,15 @@ impl Handler for ShutdownSignalData {
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
) {
/*
println!(
"in handler! {} {}",
self.exit_from_main,
std::process::id()
);
*/
// if this process has not allocated any shmem. then simply exit()
if !self.exit_from_main {
unsafe {
#[cfg(unix)]
libc::_exit(0);

#[cfg(windows)]
windows::Win32::System::Threading::ExitProcess(1);
}
}

// else wait till the next is_shutting_down() is called. then the process will exit throught main().
// println!("in handler! {}", std::process::id());
unsafe {
core::ptr::write_volatile(core::ptr::addr_of_mut!(self.shutting_down), true);
// println!("Exiting from the handler....");

#[cfg(unix)]
libc::_exit(100);

#[cfg(windows)]
windows::Win32::System::Threading::ExitProcess(100);
}
}

Expand Down
55 changes: 22 additions & 33 deletions libafl/src/events/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use libafl_bolts::{shmem::ShMemProvider, staterestore::StateRestorer};
use serde::{de::DeserializeOwned, Serialize};

use super::{CustomBufEventResult, CustomBufHandlerFn, HasCustomBufHandlers, ProgressReporter};
#[cfg(all(unix, feature = "std"))]
#[cfg(all(unix, feature = "std", not(miri)))]
use crate::events::EVENTMGR_SIGHANDLER_STATE;
use crate::{
events::{
Expand Down Expand Up @@ -453,19 +453,6 @@ where
}
}

/// Internal function, returns true when shuttdown is requested by a `SIGINT` signal
#[inline]
#[allow(clippy::unused_self)]
fn is_shutting_down() -> bool {
#[cfg(unix)]
unsafe {
core::ptr::read_volatile(core::ptr::addr_of!(EVENTMGR_SIGHANDLER_STATE.shutting_down))
}

#[cfg(windows)]
false
}

/// Launch the simple restarting manager.
/// This [`EventManager`] is simple and single threaded,
/// but can still used shared maps to recover from crashes and timeouts.
Expand All @@ -488,15 +475,6 @@ where
//let staterestorer = { LlmpSender::new(shmem_provider.clone(), 0, false)? };
staterestorer.write_to_env(_ENV_FUZZER_SENDER)?;

// We setup signal handlers to clean up shmem segments used by state restorer
#[cfg(all(unix, not(miri)))]
if let Err(_e) =
unsafe { setup_signal_handler(addr_of_mut!(EVENTMGR_SIGHANDLER_STATE)) }
{
// We can live without a proper ctrl+c signal handler. Print and ignore.
log::error!("Failed to setup signal handlers: {_e}");
}

let mut ctr: u64 = 0;
// Client->parent loop
loop {
Expand All @@ -509,37 +487,48 @@ where
match unsafe { fork() }? {
ForkResult::Parent(handle) => {
unsafe {
// The parent will later exit through is_shutting down below
// if the process exits gracefully, it cleans up the shmem.
EVENTMGR_SIGHANDLER_STATE.set_exit_from_main();
libc::signal(libc::SIGINT, libc::SIG_IGN);
}
shmem_provider.post_fork(false)?;
handle.status()
}
ForkResult::Child => {
// We setup signal handlers to clean up shmem segments used by state restorer
#[cfg(all(unix, not(miri)))]
if let Err(_e) = unsafe {
setup_signal_handler(addr_of_mut!(EVENTMGR_SIGHANDLER_STATE))
} {
// We can live without a proper ctrl+c signal handler. Print and ignore.
log::error!("Failed to setup signal handlers: {_e}");
}
shmem_provider.post_fork(true)?;
break staterestorer;
}
}
};

// Same, as fork version, mark this main thread as the shmem allocator
// then it will not call exit or exitprocess in the sigint handler
// so that it exits after cleaning up the shmem segments
#[cfg(all(unix, not(feature = "fork")))]
// If this guy wants to fork, then ignore sigit
#[cfg(any(windows, not(feature = "fork")))]
unsafe {
EVENTMGR_SIGHANDLER_STATE.set_exit_from_main();
#[cfg(windows)]
libafl_bolts::os::windows_exceptions::signal(
libafl_bolts::os::windows_exceptions::SIGINT,
libafl_bolts::os::windows_exceptions::sig_ign(),
);

#[cfg(unix)]
libc::signal(libc::SIGINT, libc::SIG_IGN);
}

// On Windows (or in any case without forks), we spawn ourself again
#[cfg(any(windows, not(feature = "fork")))]
let child_status = startable_self()?.status()?;
#[cfg(all(unix, not(feature = "fork")))]
#[cfg(any(windows, not(feature = "fork")))]
let child_status = child_status.code().unwrap_or_default();

compiler_fence(Ordering::SeqCst);

if staterestorer.wants_to_exit() || Self::is_shutting_down() {
if child_status == crate::events::CTRL_C_EXIT || staterestorer.wants_to_exit() {
return Err(Error::shutting_down());
}

Expand Down
Loading

0 comments on commit b231803

Please sign in to comment.