Better Unix Signal Handling Abstractions (#22)
* WIP: unix_signal_handling * WIP: unix_signal_handling, another try * only emit a single illegal instruction * unix_signal_handling: Now working * unix_signal_handling: squash warnings * unix_signal_handling: formatting * fix spelling * unix_signal_handling: add missing file * unix_signal_handling: port LlmpBroker * unix_signal_handling: fix missing import * moving towards no_std compatibility * unix_signal_handling: get rid of HashMap, Mutex and lazy-static * unix_signal_handling: formatting * readme * no_std fixes * fixed windows build Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
This commit is contained in:
parent
55def9b966
commit
b048ddf470
@ -128,7 +128,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
|||||||
tuple_list!(edges_observer),
|
tuple_list!(edges_observer),
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut restarting_mgr,
|
&mut restarting_mgr,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
// The actual target run starts here.
|
// The actual target run starts here.
|
||||||
// Call LLVMFUzzerInitialize() if present.
|
// Call LLVMFUzzerInitialize() if present.
|
||||||
|
@ -158,7 +158,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
|||||||
if (width && height > 100000000 / width) {
|
if (width && height > 100000000 / width) {
|
||||||
PNG_CLEANUP
|
PNG_CLEANUP
|
||||||
#ifdef HAS_DUMMY_CRASH
|
#ifdef HAS_DUMMY_CRASH
|
||||||
asm("ud2");
|
|
||||||
#ifdef __aarch64__
|
#ifdef __aarch64__
|
||||||
asm volatile (".word 0xf7f0a000\n");
|
asm volatile (".word 0xf7f0a000\n");
|
||||||
#else
|
#else
|
||||||
|
@ -150,7 +150,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
|||||||
tuple_list!(edges_observer),
|
tuple_list!(edges_observer),
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut restarting_mgr,
|
&mut restarting_mgr,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
// The actual target run starts here.
|
// The actual target run starts here.
|
||||||
// Call LLVMFUzzerInitialize() if present.
|
// Call LLVMFUzzerInitialize() if present.
|
||||||
|
@ -72,7 +72,7 @@ pub extern "C" fn fuzz_main_loop() {
|
|||||||
});
|
});
|
||||||
let edges_feedback = MaxMapFeedback::new_with_observer(&NAME_COV_MAP, &edges_observer);
|
let edges_feedback = MaxMapFeedback::new_with_observer(&NAME_COV_MAP, &edges_observer);
|
||||||
|
|
||||||
let executor = InProcessExecutor::new("QEMUFuzzer", harness, tuple_list!(edges_observer));
|
let executor = InProcessExecutor::new("QEMUFuzzer", harness, tuple_list!(edges_observer))?;
|
||||||
let mut state = State::new(tuple_list!(edges_feedback));
|
let mut state = State::new(tuple_list!(edges_feedback));
|
||||||
|
|
||||||
let mut engine = Engine::new(executor);
|
let mut engine = Engine::new(executor);
|
||||||
|
@ -54,6 +54,7 @@ ctor = "*"
|
|||||||
libafl_derive = { version = "*", optional = true, path = "../libafl_derive" }
|
libafl_derive = { version = "*", optional = true, path = "../libafl_derive" }
|
||||||
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } # an easy way to debug print SerdeAnyMap
|
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } # an easy way to debug print SerdeAnyMap
|
||||||
#TODO: for llmp brotli = { version = "3.3.0", default-features = false } # brotli compression
|
#TODO: for llmp brotli = { version = "3.3.0", default-features = false } # brotli compression
|
||||||
|
num_enum = "0.5.1"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2" # For (*nix) libc
|
libc = "0.2" # For (*nix) libc
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
#[cfg(windows)]
|
#[cfg(all(windows, feature = "std"))]
|
||||||
::windows::include_bindings!();
|
::windows::include_bindings!();
|
||||||
|
@ -66,8 +66,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::{
|
use std::{
|
||||||
env, fs,
|
env, fs,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
net::SocketAddr,
|
net::{SocketAddr, TcpListener, TcpStream},
|
||||||
net::{TcpListener, TcpStream},
|
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,10 +78,10 @@ use nix::{
|
|||||||
uio::IoVec,
|
uio::IoVec,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(all(feature = "std", unix))]
|
||||||
use std::{
|
use std::{
|
||||||
ffi::CStr,
|
ffi::CStr,
|
||||||
mem::zeroed,
|
|
||||||
os::unix::{
|
os::unix::{
|
||||||
self,
|
self,
|
||||||
net::{UnixListener, UnixStream},
|
net::{UnixListener, UnixStream},
|
||||||
@ -91,11 +90,10 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(all(feature = "std", unix))]
|
||||||
use libc::{
|
use libc::c_char;
|
||||||
c_char, c_int, c_void, malloc, sigaction, sigaltstack, siginfo_t, SA_NODEFER, SA_ONSTACK,
|
|
||||||
SA_SIGINFO, SIGINT, SIGQUIT, SIGTERM,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use crate::bolts::os::unix_signals::{c_void, setup_signal_handler, siginfo_t, Handler, Signal};
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::shmem::{ShMem, ShMemDescription},
|
bolts::shmem::{ShMem, ShMemDescription},
|
||||||
Error,
|
Error,
|
||||||
@ -131,6 +129,12 @@ const EOP_MSG_SIZE: usize =
|
|||||||
/// The header length of a llmp page in a shared map (until messages start)
|
/// The header length of a llmp page in a shared map (until messages start)
|
||||||
const LLMP_PAGE_HEADER_LEN: usize = size_of::<LlmpPage>();
|
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 {
|
||||||
|
shutting_down: false,
|
||||||
|
};
|
||||||
|
|
||||||
/// TAGs used thorughout llmp
|
/// TAGs used thorughout llmp
|
||||||
pub type Tag = u32;
|
pub type Tag = u32;
|
||||||
|
|
||||||
@ -873,7 +877,7 @@ where
|
|||||||
let last_message_offset = if self.last_msg_sent.is_null() {
|
let last_message_offset = if self.last_msg_sent.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(map.msg_to_offset(self.last_msg_sent)?)
|
Some(unsafe { map.msg_to_offset(self.last_msg_sent) }?)
|
||||||
};
|
};
|
||||||
Ok(LlmpDescription {
|
Ok(LlmpDescription {
|
||||||
shmem: map.shmem.description(),
|
shmem: map.shmem.description(),
|
||||||
@ -1084,7 +1088,7 @@ where
|
|||||||
let last_message_offset = if self.last_msg_recvd.is_null() {
|
let last_message_offset = if self.last_msg_recvd.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(map.msg_to_offset(self.last_msg_recvd)?)
|
Some(unsafe { map.msg_to_offset(self.last_msg_recvd) }?)
|
||||||
};
|
};
|
||||||
Ok(LlmpDescription {
|
Ok(LlmpDescription {
|
||||||
shmem: map.shmem.description(),
|
shmem: map.shmem.description(),
|
||||||
@ -1164,8 +1168,9 @@ where
|
|||||||
|
|
||||||
/// Gets the offset of a message on this here page.
|
/// Gets the offset of a message on this here page.
|
||||||
/// Will return IllegalArgument error if msg is not on page.
|
/// Will return IllegalArgument error if msg is not on page.
|
||||||
pub fn msg_to_offset(&self, msg: *const LlmpMsg) -> Result<u64, Error> {
|
/// # Safety
|
||||||
unsafe {
|
/// This dereferences msg, make sure to pass a proper pointer to it.
|
||||||
|
pub unsafe fn msg_to_offset(&self, msg: *const LlmpMsg) -> Result<u64, Error> {
|
||||||
let page = self.page();
|
let page = self.page();
|
||||||
if llmp_msg_in_page(page, msg) {
|
if llmp_msg_in_page(page, msg) {
|
||||||
// Cast both sides to u8 arrays, get the offset, then cast the return isize to u64
|
// Cast both sides to u8 arrays, get the offset, then cast the return isize to u64
|
||||||
@ -1177,7 +1182,6 @@ where
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve the stored msg from env_name + _OFFSET.
|
/// Retrieve the stored msg from env_name + _OFFSET.
|
||||||
/// It will restore the stored offset by env_name and return the message.
|
/// It will restore the stored offset by env_name and return the message.
|
||||||
@ -1198,7 +1202,7 @@ where
|
|||||||
} else {
|
} else {
|
||||||
env::set_var(
|
env::set_var(
|
||||||
&format!("{}_OFFSET", map_env_name),
|
&format!("{}_OFFSET", map_env_name),
|
||||||
format!("{}", self.msg_to_offset(msg)?),
|
format!("{}", unsafe { self.msg_to_offset(msg) }?),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1244,9 +1248,21 @@ where
|
|||||||
shutting_down: bool,
|
shutting_down: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// used to access the current broker in signal handler.
|
#[cfg(unix)]
|
||||||
#[cfg(all(feature = "std", unix))]
|
pub struct LlmpBrokerSignalHandler {
|
||||||
static mut CURRENT_BROKER_PTR: *const c_void = ptr::null();
|
shutting_down: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(unix))]
|
||||||
|
impl Handler for LlmpBrokerSignalHandler {
|
||||||
|
fn handle(&mut self, _signal: Signal, _info: siginfo_t, _void: c_void) {
|
||||||
|
unsafe { ptr::write_volatile(&mut self.shutting_down, true) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signals(&self) -> Vec<Signal> {
|
||||||
|
vec![Signal::SigTerm, Signal::SigInterrupt, Signal::SigQuit]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The broker forwards all messages to its own bus-like broadcast map.
|
/// The broker forwards all messages to its own bus-like broadcast map.
|
||||||
/// It may intercept messages passing through.
|
/// It may intercept messages passing through.
|
||||||
@ -1327,54 +1343,18 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called from an interrupt: Sets broker `shutting_down` flag to `true`.
|
/// Internal function, returns true when shuttdown is requested by a `SIGINT` signal
|
||||||
/// Currently only supported on `std` unix systems.
|
#[inline]
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(unix)]
|
||||||
fn shutdown(&mut self) {
|
fn is_shutting_down(&self) -> bool {
|
||||||
unsafe { ptr::write_volatile(&mut self.shutting_down, true) };
|
unsafe { !ptr::read_volatile(&GLOBAL_SIGHANDLER_STATE.shutting_down) }
|
||||||
compiler_fence(Ordering::SeqCst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "std", unix))]
|
/// Always returns true on platforms, where no shutdown signal handlers are supported
|
||||||
unsafe fn handle_signal(_sig: c_int, _: siginfo_t, _: c_void) {
|
#[inline]
|
||||||
if !CURRENT_BROKER_PTR.is_null() {
|
#[cfg(not(unix))]
|
||||||
let broker = (CURRENT_BROKER_PTR as *mut LlmpBroker<SH>)
|
fn is_shutting_down(&self) -> bool {
|
||||||
.as_mut()
|
true
|
||||||
.unwrap();
|
|
||||||
broker.shutdown();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For proper cleanup on sigint, we set up a sigint handler
|
|
||||||
#[cfg(all(feature = "std", unix))]
|
|
||||||
unsafe fn setup_sigint_handler(&mut self) {
|
|
||||||
CURRENT_BROKER_PTR = self as *const _ as *const c_void;
|
|
||||||
|
|
||||||
// First, set up our own stack to be used during segfault handling. (and specify `SA_ONSTACK` in `sigaction`)
|
|
||||||
let signal_stack_size = 2 << 22;
|
|
||||||
// TODO: We leak the signal stack. Removing the signal handlers, then freeing this mem on teardown would be more correct.
|
|
||||||
let signal_stack_ptr = malloc(signal_stack_size);
|
|
||||||
if signal_stack_ptr.is_null() {
|
|
||||||
panic!(
|
|
||||||
"Failed to allocate signal stack with {} bytes!",
|
|
||||||
signal_stack_size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
sigaltstack(signal_stack_ptr as _, ptr::null_mut() as _);
|
|
||||||
|
|
||||||
let mut sa: sigaction = zeroed();
|
|
||||||
libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
|
|
||||||
sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
|
|
||||||
sa.sa_sigaction = LlmpBroker::<SH>::handle_signal as usize;
|
|
||||||
for (sig, msg) in &[
|
|
||||||
(SIGTERM, "segterm"),
|
|
||||||
(SIGINT, "sigint"),
|
|
||||||
(SIGQUIT, "sigquit"),
|
|
||||||
] {
|
|
||||||
if sigaction(*sig, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
|
|
||||||
panic!("Could not set up {} handler", &msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loops infinitely, forwarding and handling all incoming messages from clients.
|
/// Loops infinitely, forwarding and handling all incoming messages from clients.
|
||||||
@ -1384,12 +1364,14 @@ where
|
|||||||
where
|
where
|
||||||
F: FnMut(u32, Tag, &[u8]) -> Result<LlmpMsgHookResult, Error>,
|
F: FnMut(u32, Tag, &[u8]) -> Result<LlmpMsgHookResult, Error>,
|
||||||
{
|
{
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(unix)]
|
||||||
unsafe {
|
if let Err(_e) = unsafe { setup_signal_handler(&mut GLOBAL_SIGHANDLER_STATE) } {
|
||||||
self.setup_sigint_handler()
|
// We can live without a proper ctrl+c signal handler. Print and ignore.
|
||||||
};
|
#[cfg(feature = "std")]
|
||||||
|
println!("Failed to setup signal handlers: {}", _e);
|
||||||
|
}
|
||||||
|
|
||||||
while unsafe { !ptr::read_volatile(&self.shutting_down) } {
|
while !self.is_shutting_down() {
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
self.once(on_new_msg)
|
self.once(on_new_msg)
|
||||||
.expect("An error occurred when brokering. Exiting.");
|
.expect("An error occurred when brokering. Exiting.");
|
||||||
@ -1873,7 +1855,7 @@ impl<SH> LlmpClient<SH>
|
|||||||
where
|
where
|
||||||
SH: ShMem + HasFd,
|
SH: ShMem + HasFd,
|
||||||
{
|
{
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(all(unix, feature = "std"))]
|
||||||
/// Create a LlmpClient, getting the ID from a given filename
|
/// Create a LlmpClient, getting the ID from a given filename
|
||||||
pub fn create_attach_to_unix(filename: &str) -> Result<Self, Error> {
|
pub fn create_attach_to_unix(filename: &str) -> Result<Self, Error> {
|
||||||
let stream = UnixStream::connect(filename)?;
|
let stream = UnixStream::connect(filename)?;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
pub mod bindings;
|
pub mod bindings;
|
||||||
pub mod llmp;
|
pub mod llmp;
|
||||||
|
pub mod os;
|
||||||
pub mod ownedref;
|
pub mod ownedref;
|
||||||
pub mod serdeany;
|
pub mod serdeany;
|
||||||
pub mod shmem;
|
pub mod shmem;
|
||||||
|
2
libafl/src/bolts/os/mod.rs
Normal file
2
libafl/src/bolts/os/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#[cfg(unix)]
|
||||||
|
pub mod unix_signals;
|
169
libafl/src/bolts/os/unix_signals.rs
Normal file
169
libafl/src/bolts/os/unix_signals.rs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::{
|
||||||
|
cell::UnsafeCell,
|
||||||
|
convert::TryFrom,
|
||||||
|
fmt::{self, Display, Formatter},
|
||||||
|
mem, ptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
use libc::{
|
||||||
|
c_int, malloc, sigaction, sigaltstack, sigemptyset, stack_t, SA_NODEFER, SA_ONSTACK,
|
||||||
|
SA_SIGINFO, SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGKILL, SIGPIPE,
|
||||||
|
SIGQUIT, SIGSEGV, SIGTERM, SIGUSR2,
|
||||||
|
};
|
||||||
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
pub use libc::{c_void, siginfo_t};
|
||||||
|
|
||||||
|
#[derive(IntoPrimitive, TryFromPrimitive, Hash, Clone, Copy)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum Signal {
|
||||||
|
SigAbort = SIGABRT,
|
||||||
|
SigBus = SIGBUS,
|
||||||
|
SigFloatingPointException = SIGFPE,
|
||||||
|
SigIllegalInstruction = SIGILL,
|
||||||
|
SigPipe = SIGPIPE,
|
||||||
|
SigSegmentationFault = SIGSEGV,
|
||||||
|
SigUser2 = SIGUSR2,
|
||||||
|
SigAlarm = SIGALRM,
|
||||||
|
SigHangUp = SIGHUP,
|
||||||
|
SigKill = SIGKILL,
|
||||||
|
SigQuit = SIGQUIT,
|
||||||
|
SigTerm = SIGTERM,
|
||||||
|
SigInterrupt = SIGINT,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static CRASH_SIGNALS: &[Signal] = &[
|
||||||
|
Signal::SigAbort,
|
||||||
|
Signal::SigBus,
|
||||||
|
Signal::SigFloatingPointException,
|
||||||
|
Signal::SigIllegalInstruction,
|
||||||
|
Signal::SigPipe,
|
||||||
|
Signal::SigSegmentationFault,
|
||||||
|
];
|
||||||
|
|
||||||
|
impl PartialEq for Signal {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
*self as i32 == *other as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Signal {}
|
||||||
|
|
||||||
|
unsafe impl Sync for Signal {}
|
||||||
|
|
||||||
|
impl Display for Signal {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
match self {
|
||||||
|
Signal::SigAbort => write!(f, "SIGABRT")?,
|
||||||
|
Signal::SigBus => write!(f, "SIGBUS")?,
|
||||||
|
Signal::SigFloatingPointException => write!(f, "SIGFPE")?,
|
||||||
|
Signal::SigIllegalInstruction => write!(f, "SIGILL")?,
|
||||||
|
Signal::SigPipe => write!(f, "SIGPIPE")?,
|
||||||
|
Signal::SigSegmentationFault => write!(f, "SIGSEGV")?,
|
||||||
|
Signal::SigUser2 => write!(f, "SIGUSR2")?,
|
||||||
|
Signal::SigAlarm => write!(f, "SIGALRM")?,
|
||||||
|
Signal::SigHangUp => write!(f, "SIGHUP")?,
|
||||||
|
Signal::SigKill => write!(f, "SIGKILL")?,
|
||||||
|
Signal::SigQuit => write!(f, "SIGQUIT")?,
|
||||||
|
Signal::SigTerm => write!(f, "SIGTERM")?,
|
||||||
|
Signal::SigInterrupt => write!(f, "SIGINT")?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Handler {
|
||||||
|
/// Handle a signal
|
||||||
|
fn handle(&mut self, signal: Signal, info: siginfo_t, _void: c_void);
|
||||||
|
/// Return a list of signals to handle
|
||||||
|
fn signals(&self) -> Vec<Signal>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HandlerHolder {
|
||||||
|
handler: UnsafeCell<*mut dyn Handler>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for HandlerHolder {}
|
||||||
|
|
||||||
|
/// Let's get 8 mb for now.
|
||||||
|
const SIGNAL_STACK_SIZE: usize = 2 << 22;
|
||||||
|
/// To be able to handle SIGSEGV when the stack is exhausted, we need our own little stack space.
|
||||||
|
static mut SIGNAL_STACK_PTR: *mut c_void = ptr::null_mut();
|
||||||
|
|
||||||
|
/// Keep track of which handler is registered for which signal
|
||||||
|
static mut SIGNAL_HANDLERS: [Option<HandlerHolder>; 32] = [
|
||||||
|
// We cannot use [None; 32] because it requires Copy. Ugly, but I don't think there's an
|
||||||
|
// alternative.
|
||||||
|
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||||
|
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Internal function that is being called whenever a signal we are registered for arrives.
|
||||||
|
/// # Safety
|
||||||
|
/// This should be somewhat safe to call for signals previously registered,
|
||||||
|
/// unless the signal handlers registered using [setup_signal_handler] are broken.
|
||||||
|
unsafe fn handle_signal(sig: c_int, info: siginfo_t, void: c_void) {
|
||||||
|
let signal = &Signal::try_from(sig).unwrap();
|
||||||
|
let handler = {
|
||||||
|
match &SIGNAL_HANDLERS[*signal as usize] {
|
||||||
|
Some(handler_holder) => &mut **handler_holder.handler.get(),
|
||||||
|
None => return,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
handler.handle(*signal, info, void);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Setup signal handlers in a somewhat rusty way.
|
||||||
|
/// This will allocate a signal stack and set the signal handlers accordingly.
|
||||||
|
/// It is, for example, used in the [crate::executors::InProcessExecutor] to restart the fuzzer in case of a crash,
|
||||||
|
/// or to handle `SIGINT` in the broker process.
|
||||||
|
/// # Safety
|
||||||
|
/// The signal handlers will be called on any signal. They should (tm) be async safe.
|
||||||
|
/// A lot can go south in signal handling. Be sure you know what you are doing.
|
||||||
|
pub unsafe fn setup_signal_handler<T: 'static + Handler>(handler: &mut T) -> Result<(), Error> {
|
||||||
|
// First, set up our own stack to be used during segfault handling. (and specify `SA_ONSTACK` in `sigaction`)
|
||||||
|
if SIGNAL_STACK_PTR.is_null() {
|
||||||
|
SIGNAL_STACK_PTR = malloc(SIGNAL_STACK_SIZE);
|
||||||
|
|
||||||
|
if SIGNAL_STACK_PTR.is_null() {
|
||||||
|
// Rust always panics on OOM, so we will, too.
|
||||||
|
panic!(
|
||||||
|
"Failed to allocate signal stack with {} bytes!",
|
||||||
|
SIGNAL_STACK_SIZE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut ss: stack_t = mem::zeroed();
|
||||||
|
ss.ss_size = SIGNAL_STACK_SIZE;
|
||||||
|
ss.ss_sp = SIGNAL_STACK_PTR;
|
||||||
|
sigaltstack(&mut ss as *mut stack_t, ptr::null_mut() as _);
|
||||||
|
|
||||||
|
let mut sa: sigaction = mem::zeroed();
|
||||||
|
sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
|
||||||
|
sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
|
||||||
|
sa.sa_sigaction = handle_signal as usize;
|
||||||
|
let signals = handler.signals();
|
||||||
|
for sig in signals {
|
||||||
|
SIGNAL_HANDLERS[sig as usize] = Some(HandlerHolder {
|
||||||
|
handler: UnsafeCell::new(handler as *mut dyn Handler),
|
||||||
|
});
|
||||||
|
|
||||||
|
if sigaction(sig as i32, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
{
|
||||||
|
let err_str = CString::new(format!("Failed to setup {} handler", sig)).unwrap();
|
||||||
|
libc::perror(err_str.as_ptr());
|
||||||
|
}
|
||||||
|
return Err(Error::Unknown(format!("Could not set up {} handler", sig)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -125,8 +125,7 @@ macro_rules! create_serde_registry_for_trait {
|
|||||||
panic!("Registry is already finalized!");
|
panic!("Registry is already finalized!");
|
||||||
}
|
}
|
||||||
|
|
||||||
let deserializers =
|
let deserializers = self.deserializers.get_or_insert_with(HashMap::default);
|
||||||
self.deserializers.get_or_insert_with(|| HashMap::default());
|
|
||||||
deserializers.insert(unpack_type_id(TypeId::of::<T>()), |de| {
|
deserializers.insert(unpack_type_id(TypeId::of::<T>()), |de| {
|
||||||
Ok(Box::new(erased_serde::deserialize::<T>(de)?))
|
Ok(Box::new(erased_serde::deserialize::<T>(de)?))
|
||||||
});
|
});
|
||||||
|
@ -235,7 +235,7 @@ pub mod unix_shmem {
|
|||||||
return 0 as *mut c_void;
|
return 0 as *mut c_void;
|
||||||
}
|
}
|
||||||
|
|
||||||
print!("shmat() = {:?}\n", ptr);
|
println!("shmat() = {:?}", ptr);
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,7 +429,7 @@ pub mod unix_shmem {
|
|||||||
);
|
);
|
||||||
(*shm).shm_id = shm_str
|
(*shm).shm_id = shm_str
|
||||||
.to_str()
|
.to_str()
|
||||||
.expect(&format!("illegal shm_str {:?}", shm_str))
|
.unwrap_or_else(|_| panic!("illegal shm_str {:?}", shm_str))
|
||||||
.parse::<i32>()
|
.parse::<i32>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
(*shm).map = shmat((*shm).shm_id, ptr::null(), 0 as c_int) as *mut c_uchar;
|
(*shm).map = shmat((*shm).shm_id, ptr::null(), 0 as c_int) as *mut c_uchar;
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
//! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective.
|
//! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective.
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use os_signals::set_oncrash_ptrs;
|
use core::ptr;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use crate::bolts::os::unix_signals::{c_void, setup_signal_handler};
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::tuples::Named,
|
bolts::tuples::Named,
|
||||||
corpus::Corpus,
|
corpus::Corpus,
|
||||||
@ -18,17 +19,6 @@ use crate::{
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[cfg(unix)]
|
|
||||||
use unix_signals as os_signals;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[cfg(unix)]
|
|
||||||
use self::os_signals::reset_oncrash_ptrs;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[cfg(unix)]
|
|
||||||
use self::os_signals::setup_crash_handlers;
|
|
||||||
|
|
||||||
/// The inmem executor harness
|
/// The inmem executor harness
|
||||||
type HarnessFunction<E> = fn(&E, &[u8]) -> ExitKind;
|
type HarnessFunction<E> = fn(&E, &[u8]) -> ExitKind;
|
||||||
|
|
||||||
@ -58,33 +48,23 @@ where
|
|||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_event_mgr: &mut EM,
|
_event_mgr: &mut EM,
|
||||||
_input: &I,
|
_input: &I,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error> {
|
||||||
where
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
{
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[cfg(feature = "std")]
|
|
||||||
unsafe {
|
unsafe {
|
||||||
set_oncrash_ptrs(_state, _event_mgr, self.observers(), _input);
|
unix_signal_handler::GLOBAL_STATE.current_input_ptr =
|
||||||
|
_input as *const _ as *const c_void;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn post_exec<EM, S>(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error>
|
|
||||||
where
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
{
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
reset_oncrash_ptrs();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
|
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
|
||||||
let bytes = input.target_bytes();
|
let bytes = input.target_bytes();
|
||||||
let ret = (self.harness_fn)(self, bytes.as_slice());
|
let ret = (self.harness_fn)(self, bytes.as_slice());
|
||||||
|
#[cfg(unix)]
|
||||||
|
unsafe {
|
||||||
|
unix_signal_handler::GLOBAL_STATE.current_input_ptr = ptr::null();
|
||||||
|
}
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,100 +102,267 @@ where
|
|||||||
{
|
{
|
||||||
/// Create a new in mem executor.
|
/// Create a new in mem executor.
|
||||||
/// Caution: crash and restart in one of them will lead to odd behavior if multiple are used,
|
/// Caution: crash and restart in one of them will lead to odd behavior if multiple are used,
|
||||||
/// depnding on different corpus or state.
|
/// depending on different corpus or state.
|
||||||
/// * `name` - the name of this executor (to address it along the way)
|
/// * `name` - the name of this executor (to address it along the way)
|
||||||
/// * `harness_fn` - the harness, executiong the function
|
/// * `harness_fn` - the harness, executiong the function
|
||||||
/// * `observers` - the observers observing the target during execution
|
/// * `observers` - the observers observing the target during execution
|
||||||
|
/// This may return an error on unix, if signal handler setup fails
|
||||||
pub fn new<EM, OC, OFT, S>(
|
pub fn new<EM, OC, OFT, S>(
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
harness_fn: HarnessFunction<Self>,
|
harness_fn: HarnessFunction<Self>,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_event_mgr: &mut EM,
|
_event_mgr: &mut EM,
|
||||||
) -> Self
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
EM: EventManager<I, S>,
|
EM: EventManager<I, S>,
|
||||||
OC: Corpus<I>,
|
OC: Corpus<I>,
|
||||||
OFT: FeedbacksTuple<I>,
|
OFT: FeedbacksTuple<I>,
|
||||||
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
||||||
{
|
{
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
unsafe {
|
unsafe {
|
||||||
setup_crash_handlers::<EM, I, OC, OFT, OT, S>();
|
let mut data = &mut unix_signal_handler::GLOBAL_STATE;
|
||||||
|
data.state_ptr = _state as *mut _ as *mut c_void;
|
||||||
|
data.event_mgr_ptr = _event_mgr as *mut _ as *mut c_void;
|
||||||
|
data.observers_ptr = &observers as *const _ as *const c_void;
|
||||||
|
data.crash_handler = unix_signal_handler::inproc_crash_handler::<EM, I, OC, OFT, OT, S>;
|
||||||
|
data.timeout_handler =
|
||||||
|
unix_signal_handler::inproc_timeout_handler::<EM, I, OC, OFT, OT, S>;
|
||||||
|
|
||||||
|
setup_signal_handler(data)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
harness_fn,
|
harness_fn,
|
||||||
observers,
|
observers,
|
||||||
name,
|
name,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub mod unix_signals {
|
mod unix_signal_handler {
|
||||||
|
use alloc::vec::Vec;
|
||||||
extern crate libc;
|
use core::ptr;
|
||||||
|
use libc::{c_void, siginfo_t};
|
||||||
// Unhandled signals: SIGALRM, SIGHUP, SIGINT, SIGKILL, SIGQUIT, SIGTERM
|
#[cfg(feature = "std")]
|
||||||
use libc::{
|
|
||||||
c_int, c_void, malloc, sigaction, sigaltstack, siginfo_t, SA_NODEFER, SA_ONSTACK,
|
|
||||||
SA_SIGINFO, SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGPIPE, SIGSEGV, SIGUSR2,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
io::{stdout, Write},
|
io::{stdout, Write},
|
||||||
mem, ptr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
bolts::os::unix_signals::{Handler, Signal},
|
||||||
corpus::{Corpus, Testcase},
|
corpus::{Corpus, Testcase},
|
||||||
events::{Event, EventManager},
|
events::{Event, EventManager},
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
feedbacks::FeedbacksTuple,
|
feedbacks::FeedbacksTuple,
|
||||||
inputs::Input,
|
inputs::{HasTargetBytes, Input},
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
state::{HasObjectives, HasSolutions},
|
state::{HasObjectives, HasSolutions},
|
||||||
};
|
};
|
||||||
/// Let's get 8 mb for now.
|
|
||||||
const SIGNAL_STACK_SIZE: usize = 2 << 22;
|
|
||||||
/// To be able to handle SIGSEGV when the stack is exhausted, we need our own little stack space.
|
|
||||||
static mut SIGNAL_STACK_PTR: *const c_void = ptr::null_mut();
|
|
||||||
|
|
||||||
/// Pointers to values only needed on crash. As the program will not continue after a crash,
|
/// Signal handling on unix systems needs some nasty unsafe.
|
||||||
/// we should (tm) be okay with raw pointers here,
|
pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
|
||||||
static mut STATE_PTR: *mut c_void = ptr::null_mut();
|
/// The state ptr for signal handling
|
||||||
static mut EVENT_MGR_PTR: *mut c_void = ptr::null_mut();
|
state_ptr: ptr::null_mut(),
|
||||||
static mut OBSERVERS_PTR: *const c_void = ptr::null();
|
/// The event manager ptr for signal handling
|
||||||
/// The (unsafe) pointer to the current inmem input, for the current run.
|
event_mgr_ptr: ptr::null_mut(),
|
||||||
/// This is needed for certain non-rust side effects, as well as unix signal handling.
|
/// The observers ptr for signal handling
|
||||||
static mut CURRENT_INPUT_PTR: *const c_void = ptr::null();
|
observers_ptr: ptr::null(),
|
||||||
|
/// The current input for signal handling
|
||||||
|
current_input_ptr: ptr::null(),
|
||||||
|
/// The crash handler fn
|
||||||
|
crash_handler: nop_handler,
|
||||||
|
/// The timeout handler fn
|
||||||
|
timeout_handler: nop_handler,
|
||||||
|
};
|
||||||
|
|
||||||
unsafe fn inmem_handle_crash<EM, I, OC, OFT, OT, S>(_sig: c_int, info: siginfo_t, _void: c_void)
|
pub struct InProcessExecutorHandlerData {
|
||||||
where
|
pub state_ptr: *mut c_void,
|
||||||
|
pub event_mgr_ptr: *mut c_void,
|
||||||
|
pub observers_ptr: *const c_void,
|
||||||
|
pub current_input_ptr: *const c_void,
|
||||||
|
pub crash_handler: unsafe fn(Signal, siginfo_t, c_void, data: &mut Self),
|
||||||
|
pub timeout_handler: unsafe fn(Signal, siginfo_t, c_void, data: &mut Self),
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for InProcessExecutorHandlerData {}
|
||||||
|
unsafe impl Sync for InProcessExecutorHandlerData {}
|
||||||
|
|
||||||
|
unsafe fn nop_handler(
|
||||||
|
_signal: Signal,
|
||||||
|
_info: siginfo_t,
|
||||||
|
_void: c_void,
|
||||||
|
_data: &mut InProcessExecutorHandlerData,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
impl Handler for InProcessExecutorHandlerData {
|
||||||
|
fn handle(&mut self, signal: Signal, info: siginfo_t, void: c_void) {
|
||||||
|
unsafe {
|
||||||
|
let data = &mut GLOBAL_STATE;
|
||||||
|
match signal {
|
||||||
|
Signal::SigUser2 => (data.timeout_handler)(signal, info, void, data),
|
||||||
|
_ => (data.crash_handler)(signal, info, void, data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signals(&self) -> Vec<Signal> {
|
||||||
|
vec![
|
||||||
|
Signal::SigUser2,
|
||||||
|
Signal::SigAbort,
|
||||||
|
Signal::SigBus,
|
||||||
|
Signal::SigPipe,
|
||||||
|
Signal::SigFloatingPointException,
|
||||||
|
Signal::SigIllegalInstruction,
|
||||||
|
Signal::SigSegmentationFault,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub unsafe fn inproc_timeout_handler<EM, I, OC, OFT, OT, S>(
|
||||||
|
_signal: Signal,
|
||||||
|
_info: siginfo_t,
|
||||||
|
_void: c_void,
|
||||||
|
data: &mut InProcessExecutorHandlerData,
|
||||||
|
) where
|
||||||
EM: EventManager<I, S>,
|
EM: EventManager<I, S>,
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
OC: Corpus<I>,
|
OC: Corpus<I>,
|
||||||
OFT: FeedbacksTuple<I>,
|
OFT: FeedbacksTuple<I>,
|
||||||
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
||||||
I: Input,
|
I: Input + HasTargetBytes,
|
||||||
{
|
{
|
||||||
if CURRENT_INPUT_PTR.is_null() {
|
let state = (data.state_ptr as *mut S).as_mut().unwrap();
|
||||||
|
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
|
||||||
|
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
|
||||||
|
|
||||||
|
if data.current_input_ptr.is_null() {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
dbg!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing. Exiting");
|
||||||
|
} else {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
println!("Timeout in fuzz run.");
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
let _ = stdout().flush();
|
||||||
|
|
||||||
|
let input = (data.current_input_ptr as *const I).as_ref().unwrap();
|
||||||
|
data.current_input_ptr = ptr::null();
|
||||||
|
|
||||||
|
let obj_fitness = state
|
||||||
|
.objectives_mut()
|
||||||
|
.is_interesting_all(&input, observers, ExitKind::Crash)
|
||||||
|
.expect("In timeout handler objectives failure.");
|
||||||
|
if obj_fitness > 0 {
|
||||||
|
state
|
||||||
|
.solutions_mut()
|
||||||
|
.add(Testcase::new(input.clone()))
|
||||||
|
.expect("In timeout handler solutions failure.");
|
||||||
|
event_mgr
|
||||||
|
.fire(
|
||||||
|
state,
|
||||||
|
Event::Objective {
|
||||||
|
objective_size: state.solutions().count(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("Could not send timeouting input");
|
||||||
|
}
|
||||||
|
|
||||||
|
event_mgr.on_restart(state).unwrap();
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
println!("Waiting for broker...");
|
||||||
|
event_mgr.await_restart_safe();
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
println!("Bye!");
|
||||||
|
|
||||||
|
event_mgr.await_restart_safe();
|
||||||
|
|
||||||
|
libc::_exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn inproc_crash_handler<EM, I, OC, OFT, OT, S>(
|
||||||
|
_signal: Signal,
|
||||||
|
_info: siginfo_t,
|
||||||
|
_void: c_void,
|
||||||
|
data: &mut InProcessExecutorHandlerData,
|
||||||
|
) where
|
||||||
|
EM: EventManager<I, S>,
|
||||||
|
OT: ObserversTuple,
|
||||||
|
OC: Corpus<I>,
|
||||||
|
OFT: FeedbacksTuple<I>,
|
||||||
|
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
||||||
|
I: Input + HasTargetBytes,
|
||||||
|
{
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
println!("Crashed with {}", _signal);
|
||||||
|
if !data.current_input_ptr.is_null() {
|
||||||
|
let state = (data.state_ptr as *mut S).as_mut().unwrap();
|
||||||
|
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
|
||||||
|
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
println!("Child crashed!");
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
let _ = stdout().flush();
|
||||||
|
|
||||||
|
let input = (data.current_input_ptr as *const I).as_ref().unwrap();
|
||||||
|
// Make sure we don't crash in the crash handler forever.
|
||||||
|
data.current_input_ptr = ptr::null();
|
||||||
|
|
||||||
|
let obj_fitness = state
|
||||||
|
.objectives_mut()
|
||||||
|
.is_interesting_all(&input, observers, ExitKind::Crash)
|
||||||
|
.expect("In crash handler objectives failure.");
|
||||||
|
if obj_fitness > 0 {
|
||||||
|
let new_input = input.clone();
|
||||||
|
state
|
||||||
|
.solutions_mut()
|
||||||
|
.add(Testcase::new(new_input))
|
||||||
|
.expect("In crash handler solutions failure.");
|
||||||
|
event_mgr
|
||||||
|
.fire(
|
||||||
|
state,
|
||||||
|
Event::Objective {
|
||||||
|
objective_size: state.solutions().count(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("Could not send crashing input");
|
||||||
|
}
|
||||||
|
|
||||||
|
event_mgr.on_restart(state).unwrap();
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
println!("Waiting for broker...");
|
||||||
|
event_mgr.await_restart_safe();
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
println!("Bye!");
|
||||||
|
|
||||||
|
libc::_exit(1);
|
||||||
|
} else {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
{
|
||||||
|
println!("Double crash\n");
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
let si_addr = { ((info._pad[0] as usize) | ((info._pad[1] as usize) << 32)) as usize };
|
let si_addr =
|
||||||
|
{ ((_info._pad[0] as usize) | ((_info._pad[1] as usize) << 32)) as usize };
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
let si_addr = { info.si_addr() as usize };
|
let si_addr = { _info.si_addr() as usize };
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.",
|
"We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.",
|
||||||
si_addr
|
si_addr
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// let's yolo-cat the maps for debugging, if possible.
|
// let's yolo-cat the maps for debugging, if possible.
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(all(target_os = "linux", feature = "std"))]
|
||||||
match fs::read_to_string("/proc/self/maps") {
|
match fs::read_to_string("/proc/self/maps") {
|
||||||
Ok(maps) => println!("maps:\n{}", maps),
|
Ok(maps) => println!("maps:\n{}", maps),
|
||||||
Err(e) => println!("Couldn't load mappings: {:?}", e),
|
Err(e) => println!("Couldn't load mappings: {:?}", e),
|
||||||
@ -230,177 +377,7 @@ pub mod unix_signals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO tell the parent to not restart
|
// TODO tell the parent to not restart
|
||||||
std::process::exit(1);
|
libc::_exit(1);
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
println!("Child crashed!");
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
let _ = stdout().flush();
|
|
||||||
|
|
||||||
let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap();
|
|
||||||
// Make sure we don't crash in the crash handler forever.
|
|
||||||
CURRENT_INPUT_PTR = ptr::null();
|
|
||||||
let state = (STATE_PTR as *mut S).as_mut().unwrap();
|
|
||||||
let mgr = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap();
|
|
||||||
|
|
||||||
let observers = (OBSERVERS_PTR as *const OT).as_ref().unwrap();
|
|
||||||
let obj_fitness = state
|
|
||||||
.objectives_mut()
|
|
||||||
.is_interesting_all(&input, observers, ExitKind::Crash)
|
|
||||||
.expect("In crash handler objectives failure.");
|
|
||||||
if obj_fitness > 0 {
|
|
||||||
state
|
|
||||||
.solutions_mut()
|
|
||||||
.add(Testcase::new(input.clone()))
|
|
||||||
.expect("In crash handler solutions failure.");
|
|
||||||
mgr.fire(
|
|
||||||
state,
|
|
||||||
Event::Objective {
|
|
||||||
objective_size: state.solutions().count(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.expect("Could not send crashing input");
|
|
||||||
}
|
|
||||||
|
|
||||||
mgr.on_restart(state).unwrap();
|
|
||||||
|
|
||||||
println!("Waiting for broker...");
|
|
||||||
mgr.await_restart_safe();
|
|
||||||
println!("Bye!");
|
|
||||||
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn inmem_handle_timeout<EM, I, OC, OFT, OT, S>(
|
|
||||||
_sig: c_int,
|
|
||||||
_info: siginfo_t,
|
|
||||||
_void: c_void,
|
|
||||||
) where
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
OT: ObserversTuple,
|
|
||||||
OC: Corpus<I>,
|
|
||||||
OFT: FeedbacksTuple<I>,
|
|
||||||
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
|
||||||
I: Input,
|
|
||||||
{
|
|
||||||
dbg!("TIMEOUT/SIGUSR2 received");
|
|
||||||
if CURRENT_INPUT_PTR.is_null() {
|
|
||||||
dbg!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing. Exiting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Timeout in fuzz run.");
|
|
||||||
let _ = stdout().flush();
|
|
||||||
|
|
||||||
let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap();
|
|
||||||
// Make sure we don't crash in the crash handler forever.
|
|
||||||
CURRENT_INPUT_PTR = ptr::null();
|
|
||||||
let state = (STATE_PTR as *mut S).as_mut().unwrap();
|
|
||||||
let mgr = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap();
|
|
||||||
|
|
||||||
let observers = (OBSERVERS_PTR as *const OT).as_ref().unwrap();
|
|
||||||
let obj_fitness = state
|
|
||||||
.objectives_mut()
|
|
||||||
.is_interesting_all(&input, observers, ExitKind::Crash)
|
|
||||||
.expect("In timeout handler objectives failure.");
|
|
||||||
if obj_fitness > 0 {
|
|
||||||
state
|
|
||||||
.solutions_mut()
|
|
||||||
.add(Testcase::new(input.clone()))
|
|
||||||
.expect("In timeout handler solutions failure.");
|
|
||||||
mgr.fire(
|
|
||||||
state,
|
|
||||||
Event::Objective {
|
|
||||||
objective_size: state.solutions().count(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.expect("Could not send timeouting input");
|
|
||||||
}
|
|
||||||
|
|
||||||
mgr.on_restart(state).unwrap();
|
|
||||||
|
|
||||||
println!("Waiting for broker...");
|
|
||||||
mgr.await_restart_safe();
|
|
||||||
println!("Bye!");
|
|
||||||
|
|
||||||
mgr.await_restart_safe();
|
|
||||||
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the oncrash pointers before executing a single testcase
|
|
||||||
/// # Safety
|
|
||||||
/// As long as no signals are called, this is fine.
|
|
||||||
/// Once a signal occurs, the pointer needs to be up to date.
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn set_oncrash_ptrs<EM, I, OT, S>(
|
|
||||||
state: &mut S,
|
|
||||||
event_mgr: &mut EM,
|
|
||||||
observers: &OT,
|
|
||||||
input: &I,
|
|
||||||
) {
|
|
||||||
CURRENT_INPUT_PTR = input as *const _ as *const c_void;
|
|
||||||
STATE_PTR = state as *mut _ as *mut c_void;
|
|
||||||
EVENT_MGR_PTR = event_mgr as *mut _ as *mut c_void;
|
|
||||||
OBSERVERS_PTR = observers as *const _ as *const c_void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets the oncrash pointers to null
|
|
||||||
#[inline]
|
|
||||||
pub fn reset_oncrash_ptrs() {
|
|
||||||
unsafe {
|
|
||||||
CURRENT_INPUT_PTR = ptr::null();
|
|
||||||
STATE_PTR = ptr::null_mut();
|
|
||||||
EVENT_MGR_PTR = ptr::null_mut();
|
|
||||||
OBSERVERS_PTR = ptr::null();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets up the crash handler
|
|
||||||
/// # Safety
|
|
||||||
/// Everything using signals is unsafe. Don't do it unless you really need to.
|
|
||||||
pub unsafe fn setup_crash_handlers<EM, I, OC, OFT, OT, S>()
|
|
||||||
where
|
|
||||||
EM: EventManager<I, S>,
|
|
||||||
OT: ObserversTuple,
|
|
||||||
OC: Corpus<I>,
|
|
||||||
OFT: FeedbacksTuple<I>,
|
|
||||||
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
|
||||||
I: Input,
|
|
||||||
{
|
|
||||||
// First, set up our own stack to be used during segfault handling. (and specify `SA_ONSTACK` in `sigaction`)
|
|
||||||
if SIGNAL_STACK_PTR.is_null() {
|
|
||||||
SIGNAL_STACK_PTR = malloc(SIGNAL_STACK_SIZE);
|
|
||||||
if SIGNAL_STACK_PTR.is_null() {
|
|
||||||
panic!(
|
|
||||||
"Failed to allocate signal stack with {} bytes!",
|
|
||||||
SIGNAL_STACK_SIZE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sigaltstack(SIGNAL_STACK_PTR as _, ptr::null_mut() as _);
|
|
||||||
|
|
||||||
let mut sa: sigaction = mem::zeroed();
|
|
||||||
libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
|
|
||||||
sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
|
|
||||||
sa.sa_sigaction = inmem_handle_crash::<EM, I, OC, OFT, OT, S> as usize;
|
|
||||||
for (sig, msg) in &[
|
|
||||||
(SIGSEGV, "segfault"),
|
|
||||||
(SIGBUS, "sigbus"),
|
|
||||||
(SIGABRT, "sigabrt"),
|
|
||||||
(SIGILL, "illegal instruction"),
|
|
||||||
(SIGFPE, "fp exception"),
|
|
||||||
(SIGPIPE, "pipe"),
|
|
||||||
] {
|
|
||||||
if sigaction(*sig, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
|
|
||||||
panic!("Could not set up {} handler", &msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sa.sa_sigaction = inmem_handle_timeout::<EM, I, OC, OFT, OT, S> as usize;
|
|
||||||
if sigaction(SIGUSR2, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
|
|
||||||
panic!("Could not set up sigusr2 handler for timeouts");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,8 @@ mod tests {
|
|||||||
//Box::new(|_, _, _, _, _| ()),
|
//Box::new(|_, _, _, _, _| ()),
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut event_manager,
|
&mut event_manager,
|
||||||
);
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut mutator = StdScheduledMutator::new();
|
let mut mutator = StdScheduledMutator::new();
|
||||||
mutator.add_mutation(mutation_bitflip);
|
mutator.add_mutation(mutation_bitflip);
|
||||||
|
@ -15,7 +15,7 @@ use std::{
|
|||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(any(unix, feature = "std"))]
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
pub trait AsSlice<T> {
|
pub trait AsSlice<T> {
|
||||||
@ -417,14 +417,18 @@ pub enum ForkResult {
|
|||||||
/// Unix has forks.
|
/// Unix has forks.
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// A Normal fork. Runs on in two processes. Should be memory safe in general.
|
/// A Normal fork. Runs on in two processes. Should be memory safe in general.
|
||||||
#[cfg(all(unix, feature = "std"))]
|
#[cfg(unix)]
|
||||||
pub unsafe fn fork() -> Result<ForkResult, Error> {
|
pub unsafe fn fork() -> Result<ForkResult, Error> {
|
||||||
match libc::fork() {
|
match libc::fork() {
|
||||||
pid if pid > 0 => Ok(ForkResult::Parent(ChildHandle { pid })),
|
pid if pid > 0 => Ok(ForkResult::Parent(ChildHandle { pid })),
|
||||||
pid if pid < 0 => {
|
pid if pid < 0 => {
|
||||||
// Getting errno from rust is hard, we'll just let the libc print to stderr for now.
|
// Getting errno from rust is hard, we'll just let the libc print to stderr for now.
|
||||||
// In any case, this should usually not happen.
|
// In any case, this should usually not happen.
|
||||||
libc::perror(CString::new("Fork failed").unwrap().as_ptr());
|
#[cfg(feature = "std")]
|
||||||
|
{
|
||||||
|
let err_str = CString::new("Fork failed").unwrap();
|
||||||
|
libc::perror(err_str.as_ptr());
|
||||||
|
}
|
||||||
Err(Error::Unknown(format!("Fork failed ({})", pid)))
|
Err(Error::Unknown(format!("Fork failed ({})", pid)))
|
||||||
}
|
}
|
||||||
_ => Ok(ForkResult::Child),
|
_ => Ok(ForkResult::Child),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user