Skip to content

Commit

Permalink
Adding support for shutdown upon Ctrl+C on Windows for LLMP (AFLplusp…
Browse files Browse the repository at this point in the history
…lus#1704)

* Adding support for shutdown upon Ctrl+C on Windows for LLMP

* PR comments and clippy suggestions addressed

* Enable CI for PR branches and manually triggered CI

* Removed an empty line that broke compilation on some platforms

* Trying to fix nostd compilation

* Trying to fix nostd compilation for nightly toolchain

* Removing use that is unused on some platforms

* Trying to fix build on the nightly toolchain

* Trying to fix build on the nightly toolchain, take 2

* Unifying LlmpShutdownSignalHandler

* Fmt fix

* Making the handler pub(crate)

* Nightly toolchain fmt fixes

---------

Co-authored-by: Dongjia "toka" Zhang <[email protected]>
  • Loading branch information
mkravchik and tokatoka authored Dec 5, 2023
1 parent 686d29a commit b336411
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 19 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ name: build and test

on:
push:
branches: [ main ]
branches: [ main, 'pr/**' ]
pull_request:
branches: [ main ]

workflow_dispatch:
env:
CARGO_TERM_COLOR: always

Expand Down
2 changes: 1 addition & 1 deletion libafl_bolts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ libc = "0.2" # For (*nix) libc
uds = { version = "0.4", optional = true, default-features = false }

[target.'cfg(windows)'.dependencies]
windows = { version = "0.51.1", features = ["Win32_Foundation", "Win32_System_Threading", "Win32_System_Diagnostics_Debug", "Win32_System_Kernel", "Win32_System_Memory", "Win32_Security", "Win32_System_SystemInformation"] }
windows = { version = "0.51.1", features = ["Win32_Foundation", "Win32_System_Threading", "Win32_System_Diagnostics_Debug", "Win32_System_Kernel", "Win32_System_Memory", "Win32_Security", "Win32_System_SystemInformation", "Win32_System_Console"] }

[target.'cfg(windows)'.build-dependencies]
windows = "0.51.1"
Expand Down
60 changes: 46 additions & 14 deletions libafl_bolts/src/llmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ use crate::current_time;
use crate::os::unix_signals::setup_signal_handler;
#[cfg(unix)]
use crate::os::unix_signals::{siginfo_t, ucontext_t, Handler, Signal};
#[cfg(all(windows, feature = "std"))]
use crate::os::windows_exceptions::{setup_ctrl_handler, CtrlHandler};
use crate::{
shmem::{ShMem, ShMemDescription, ShMemId, ShMemProvider},
ClientId, Error,
Expand Down Expand Up @@ -175,7 +177,7 @@ const EOP_MSG_SIZE: usize =
const LLMP_PAGE_HEADER_LEN: usize = size_of::<LlmpPage>();

/// The llmp broker registers a signal handler for cleanups on `SIGINT`.
#[cfg(unix)]
#[cfg(any(unix, all(windows, feature = "std")))]
static mut LLMP_SIGHANDLER_STATE: LlmpShutdownSignalHandler = LlmpShutdownSignalHandler {
shutting_down: false,
};
Expand Down Expand Up @@ -1962,7 +1964,9 @@ where
}

/// A signal handler for the [`LlmpBroker`].
#[cfg(unix)]
/// On unix, it handles signals
/// On Windows - control signals (e.g., CTRL+C)
#[cfg(any(unix, all(windows, feature = "std")))]
#[derive(Debug, Clone)]
pub struct LlmpShutdownSignalHandler {
shutting_down: bool,
Expand All @@ -1986,6 +1990,18 @@ impl Handler for LlmpShutdownSignalHandler {
}
}

#[cfg(all(windows, feature = "std"))]
impl CtrlHandler for LlmpShutdownSignalHandler {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn handle(&mut self, ctrl_type: u32) -> bool {
log::info!("LLMP: Received shutdown signal, ctrl_type {:?}", ctrl_type);
unsafe {
ptr::write_volatile(&mut self.shutting_down, true);
}
true
}
}

/// The broker forwards all messages to its own bus-like broadcast map.
/// It may intercept messages passing through.
impl<SP> LlmpBroker<SP>
Expand Down Expand Up @@ -2242,15 +2258,37 @@ where

/// Internal function, returns true when shuttdown is requested by a `SIGINT` signal
#[inline]
#[cfg(unix)]
#[cfg(any(unix, all(windows, feature = "std")))]
#[allow(clippy::unused_self)]
fn is_shutting_down(&self) -> bool {
unsafe { ptr::read_volatile(ptr::addr_of!(LLMP_SIGHANDLER_STATE.shutting_down)) }
}

#[cfg(any(all(unix, not(miri)), all(windows, feature = "std")))]
fn setup_handlers() {
#[cfg(all(unix, not(miri)))]
if let Err(e) = unsafe { setup_signal_handler(&mut LLMP_SIGHANDLER_STATE) } {
// We can live without a proper ctrl+c signal handler - Ignore.
log::info!("Failed to setup signal handlers: {e}");
} else {
log::info!("Successfully setup signal handlers");
}

#[cfg(all(windows, feature = "std"))]
if let Err(e) = unsafe { setup_ctrl_handler(&mut LLMP_SIGHANDLER_STATE) } {
// We can live without a proper ctrl+c signal handler - Ignore.
log::info!("Failed to setup control handlers: {e}");
} else {
log::info!(
"{}: Broker successfully setup control handlers",
std::process::id().to_string()
);
}
}

/// Always returns true on platforms, where no shutdown signal handlers are supported
#[inline]
#[cfg(not(unix))]
#[cfg(not(any(unix, all(windows, feature = "std"))))]
#[allow(clippy::unused_self)]
fn is_shutting_down(&self) -> bool {
false
Expand Down Expand Up @@ -2280,11 +2318,8 @@ where
{
use super::current_milliseconds;

#[cfg(all(unix, not(miri)))]
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.
log::info!("Failed to setup signal handlers: {_e}");
}
#[cfg(any(all(unix, not(miri)), all(windows, feature = "std")))]
Self::setup_handlers();

let timeout = timeout.as_millis() as u64;
let mut end_time = current_milliseconds() + timeout;
Expand Down Expand Up @@ -2344,11 +2379,8 @@ where
where
F: FnMut(ClientId, Tag, Flags, &[u8]) -> Result<LlmpMsgHookResult, Error>,
{
#[cfg(all(unix, not(miri)))]
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.
log::info!("Failed to setup signal handlers: {_e}");
}
#[cfg(any(all(unix, not(miri)), all(windows, feature = "std")))]
Self::setup_handlers();

while !self.is_shutting_down() {
self.once(on_new_msg)
Expand Down
72 changes: 70 additions & 2 deletions libafl_bolts/src/os/windows_exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ use core::{
};
use std::os::raw::{c_long, c_void};

use log::info;
use num_enum::TryFromPrimitive;
pub use windows::Win32::{
Foundation::NTSTATUS,
Foundation::{BOOL, NTSTATUS},
System::{
Console::{SetConsoleCtrlHandler, CTRL_BREAK_EVENT, CTRL_C_EVENT, PHANDLER_ROUTINE},
Diagnostics::Debug::{
AddVectoredExceptionHandler, UnhandledExceptionFilter, EXCEPTION_POINTERS,
},
Expand Down Expand Up @@ -321,11 +323,21 @@ unsafe fn internal_handle_exception(
.unwrap();
match &EXCEPTION_HANDLERS[index] {
Some(handler_holder) => {
info!(
"{:?}: Handling exception {}",
std::process::id(),
exception_code
);
let handler = &mut **handler_holder.handler.get();
handler.handle(exception_code, exception_pointers);
EXCEPTION_CONTINUE_EXECUTION
}
None => {
info!(
"{:?}: No handler for exception {}",
std::process::id(),
exception_code
);
// Go to Default one
let handler_holder = &EXCEPTION_HANDLERS[EXCEPTION_HANDLERS_SIZE - 1]
.as_ref()
Expand All @@ -351,7 +363,7 @@ pub unsafe extern "system" fn handle_exception(
.unwrap()
.ExceptionCode;
let exception_code = ExceptionCode::try_from(code.0).unwrap();
// log::info!("Received exception; code: {}", exception_code);
log::info!("Received exception; code: {}", exception_code);
internal_handle_exception(exception_code, exception_pointers)
}

Expand Down Expand Up @@ -406,3 +418,59 @@ pub unsafe fn setup_exception_handler<T: 'static + Handler>(handler: &mut T) ->
);
Ok(())
}

#[cfg(feature = "alloc")]
pub(crate) trait CtrlHandler {
/// Handle an exception
fn handle(&mut self, ctrl_type: u32) -> bool;
}

struct CtrlHandlerHolder {
handler: UnsafeCell<*mut dyn CtrlHandler>,
}

/// Keep track of which handler is registered for which exception
static mut CTRL_HANDLER: Option<CtrlHandlerHolder> = None;

/// Set `ConsoleCtrlHandler` to catch Ctrl-C
/// # Safety
/// Same safety considerations as in `setup_exception_handler`
pub(crate) unsafe fn setup_ctrl_handler<T: 'static + CtrlHandler>(
handler: &mut T,
) -> Result<(), Error> {
write_volatile(
&mut CTRL_HANDLER,
Some(CtrlHandlerHolder {
handler: UnsafeCell::new(handler as *mut dyn CtrlHandler),
}),
);
compiler_fence(Ordering::SeqCst);

// Log the result of SetConsoleCtrlHandler
let result = SetConsoleCtrlHandler(Some(ctrl_handler), true);
match result {
Ok(()) => {
info!("SetConsoleCtrlHandler succeeded");
Ok(())
}
Err(err) => {
info!("SetConsoleCtrlHandler failed");
Err(Error::from(err))
}
}
}

unsafe extern "system" fn ctrl_handler(ctrl_type: u32) -> BOOL {
match &CTRL_HANDLER {
Some(handler_holder) => {
info!("{:?}: Handling ctrl {}", std::process::id(), ctrl_type);
let handler = &mut *handler_holder.handler.get();
if let Some(ctrl_handler) = handler.as_mut() {
(*ctrl_handler).handle(ctrl_type).into()
} else {
false.into()
}
}
None => false.into(),
}
}

0 comments on commit b336411

Please sign in to comment.