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),
|
||||
&mut state,
|
||||
&mut restarting_mgr,
|
||||
);
|
||||
)?;
|
||||
|
||||
// The actual target run starts here.
|
||||
// Call LLVMFUzzerInitialize() if present.
|
||||
|
@ -158,7 +158,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
if (width && height > 100000000 / width) {
|
||||
PNG_CLEANUP
|
||||
#ifdef HAS_DUMMY_CRASH
|
||||
asm("ud2");
|
||||
#ifdef __aarch64__
|
||||
asm volatile (".word 0xf7f0a000\n");
|
||||
#else
|
||||
|
@ -150,7 +150,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
tuple_list!(edges_observer),
|
||||
&mut state,
|
||||
&mut restarting_mgr,
|
||||
);
|
||||
)?;
|
||||
|
||||
// The actual target run starts here.
|
||||
// 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 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 engine = Engine::new(executor);
|
||||
|
@ -54,6 +54,7 @@ ctor = "*"
|
||||
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
|
||||
#TODO: for llmp brotli = { version = "3.3.0", default-features = false } # brotli compression
|
||||
num_enum = "0.5.1"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2" # For (*nix) libc
|
||||
|
@ -1,2 +1,2 @@
|
||||
#[cfg(windows)]
|
||||
#[cfg(all(windows, feature = "std"))]
|
||||
::windows::include_bindings!();
|
||||
|
@ -66,8 +66,7 @@ use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
env, fs,
|
||||
io::{Read, Write},
|
||||
net::SocketAddr,
|
||||
net::{TcpListener, TcpStream},
|
||||
net::{SocketAddr, TcpListener, TcpStream},
|
||||
thread,
|
||||
};
|
||||
|
||||
@ -79,10 +78,10 @@ use nix::{
|
||||
uio::IoVec,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
mem::zeroed,
|
||||
os::unix::{
|
||||
self,
|
||||
net::{UnixListener, UnixStream},
|
||||
@ -91,11 +90,10 @@ use std::{
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
use libc::{
|
||||
c_char, c_int, c_void, malloc, sigaction, sigaltstack, siginfo_t, SA_NODEFER, SA_ONSTACK,
|
||||
SA_SIGINFO, SIGINT, SIGQUIT, SIGTERM,
|
||||
};
|
||||
use libc::c_char;
|
||||
|
||||
#[cfg(unix)]
|
||||
use crate::bolts::os::unix_signals::{c_void, setup_signal_handler, siginfo_t, Handler, Signal};
|
||||
use crate::{
|
||||
bolts::shmem::{ShMem, ShMemDescription},
|
||||
Error,
|
||||
@ -131,6 +129,12 @@ const EOP_MSG_SIZE: usize =
|
||||
/// The header length of a llmp page in a shared map (until messages start)
|
||||
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
|
||||
pub type Tag = u32;
|
||||
|
||||
@ -873,7 +877,7 @@ where
|
||||
let last_message_offset = if self.last_msg_sent.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(map.msg_to_offset(self.last_msg_sent)?)
|
||||
Some(unsafe { map.msg_to_offset(self.last_msg_sent) }?)
|
||||
};
|
||||
Ok(LlmpDescription {
|
||||
shmem: map.shmem.description(),
|
||||
@ -1084,7 +1088,7 @@ where
|
||||
let last_message_offset = if self.last_msg_recvd.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(map.msg_to_offset(self.last_msg_recvd)?)
|
||||
Some(unsafe { map.msg_to_offset(self.last_msg_recvd) }?)
|
||||
};
|
||||
Ok(LlmpDescription {
|
||||
shmem: map.shmem.description(),
|
||||
@ -1164,8 +1168,9 @@ where
|
||||
|
||||
/// Gets the offset of a message on this here page.
|
||||
/// Will return IllegalArgument error if msg is not on page.
|
||||
pub fn msg_to_offset(&self, msg: *const LlmpMsg) -> Result<u64, Error> {
|
||||
unsafe {
|
||||
/// # Safety
|
||||
/// 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();
|
||||
if llmp_msg_in_page(page, msg) {
|
||||
// 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.
|
||||
/// It will restore the stored offset by env_name and return the message.
|
||||
@ -1198,7 +1202,7 @@ where
|
||||
} else {
|
||||
env::set_var(
|
||||
&format!("{}_OFFSET", map_env_name),
|
||||
format!("{}", self.msg_to_offset(msg)?),
|
||||
format!("{}", unsafe { self.msg_to_offset(msg) }?),
|
||||
)
|
||||
};
|
||||
Ok(())
|
||||
@ -1244,9 +1248,21 @@ where
|
||||
shutting_down: bool,
|
||||
}
|
||||
|
||||
/// used to access the current broker in signal handler.
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
static mut CURRENT_BROKER_PTR: *const c_void = ptr::null();
|
||||
#[cfg(unix)]
|
||||
pub struct LlmpBrokerSignalHandler {
|
||||
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.
|
||||
/// It may intercept messages passing through.
|
||||
@ -1327,54 +1343,18 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called from an interrupt: Sets broker `shutting_down` flag to `true`.
|
||||
/// Currently only supported on `std` unix systems.
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
fn shutdown(&mut self) {
|
||||
unsafe { ptr::write_volatile(&mut self.shutting_down, true) };
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
/// Internal function, returns true when shuttdown is requested by a `SIGINT` signal
|
||||
#[inline]
|
||||
#[cfg(unix)]
|
||||
fn is_shutting_down(&self) -> bool {
|
||||
unsafe { !ptr::read_volatile(&GLOBAL_SIGHANDLER_STATE.shutting_down) }
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
unsafe fn handle_signal(_sig: c_int, _: siginfo_t, _: c_void) {
|
||||
if !CURRENT_BROKER_PTR.is_null() {
|
||||
let broker = (CURRENT_BROKER_PTR as *mut LlmpBroker<SH>)
|
||||
.as_mut()
|
||||
.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);
|
||||
}
|
||||
}
|
||||
/// Always returns true on platforms, where no shutdown signal handlers are supported
|
||||
#[inline]
|
||||
#[cfg(not(unix))]
|
||||
fn is_shutting_down(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Loops infinitely, forwarding and handling all incoming messages from clients.
|
||||
@ -1384,12 +1364,14 @@ where
|
||||
where
|
||||
F: FnMut(u32, Tag, &[u8]) -> Result<LlmpMsgHookResult, Error>,
|
||||
{
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
unsafe {
|
||||
self.setup_sigint_handler()
|
||||
};
|
||||
#[cfg(unix)]
|
||||
if let Err(_e) = unsafe { setup_signal_handler(&mut GLOBAL_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);
|
||||
}
|
||||
|
||||
while unsafe { !ptr::read_volatile(&self.shutting_down) } {
|
||||
while !self.is_shutting_down() {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
self.once(on_new_msg)
|
||||
.expect("An error occurred when brokering. Exiting.");
|
||||
@ -1873,7 +1855,7 @@ impl<SH> LlmpClient<SH>
|
||||
where
|
||||
SH: ShMem + HasFd,
|
||||
{
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
/// Create a LlmpClient, getting the ID from a given filename
|
||||
pub fn create_attach_to_unix(filename: &str) -> Result<Self, Error> {
|
||||
let stream = UnixStream::connect(filename)?;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
pub mod bindings;
|
||||
pub mod llmp;
|
||||
pub mod os;
|
||||
pub mod ownedref;
|
||||
pub mod serdeany;
|
||||
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!");
|
||||
}
|
||||
|
||||
let deserializers =
|
||||
self.deserializers.get_or_insert_with(|| HashMap::default());
|
||||
let deserializers = self.deserializers.get_or_insert_with(HashMap::default);
|
||||
deserializers.insert(unpack_type_id(TypeId::of::<T>()), |de| {
|
||||
Ok(Box::new(erased_serde::deserialize::<T>(de)?))
|
||||
});
|
||||
|
@ -235,7 +235,7 @@ pub mod unix_shmem {
|
||||
return 0 as *mut c_void;
|
||||
}
|
||||
|
||||
print!("shmat() = {:?}\n", ptr);
|
||||
println!("shmat() = {:?}", ptr);
|
||||
ptr
|
||||
}
|
||||
|
||||
@ -429,7 +429,7 @@ pub mod unix_shmem {
|
||||
);
|
||||
(*shm).shm_id = shm_str
|
||||
.to_str()
|
||||
.expect(&format!("illegal shm_str {:?}", shm_str))
|
||||
.unwrap_or_else(|_| panic!("illegal shm_str {:?}", shm_str))
|
||||
.parse::<i32>()
|
||||
.unwrap();
|
||||
(*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.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
#[cfg(feature = "std")]
|
||||
#[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::{
|
||||
bolts::tuples::Named,
|
||||
corpus::Corpus,
|
||||
@ -18,17 +19,6 @@ use crate::{
|
||||
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
|
||||
type HarnessFunction<E> = fn(&E, &[u8]) -> ExitKind;
|
||||
|
||||
@ -58,33 +48,23 @@ where
|
||||
_state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
_input: &I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
EM: EventManager<I, S>,
|
||||
{
|
||||
) -> Result<(), Error> {
|
||||
#[cfg(unix)]
|
||||
#[cfg(feature = "std")]
|
||||
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(())
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
|
||||
let bytes = input.target_bytes();
|
||||
let ret = (self.harness_fn)(self, bytes.as_slice());
|
||||
#[cfg(unix)]
|
||||
unsafe {
|
||||
unix_signal_handler::GLOBAL_STATE.current_input_ptr = ptr::null();
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
@ -122,100 +102,267 @@ where
|
||||
{
|
||||
/// Create a new in mem executor.
|
||||
/// 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)
|
||||
/// * `harness_fn` - the harness, executiong the function
|
||||
/// * `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>(
|
||||
name: &'static str,
|
||||
harness_fn: HarnessFunction<Self>,
|
||||
observers: OT,
|
||||
_state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
) -> Self
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
EM: EventManager<I, S>,
|
||||
OC: Corpus<I>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
||||
{
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(unix)]
|
||||
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,
|
||||
observers,
|
||||
name,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(unix)]
|
||||
pub mod unix_signals {
|
||||
|
||||
extern crate libc;
|
||||
|
||||
// Unhandled signals: SIGALRM, SIGHUP, SIGINT, SIGKILL, SIGQUIT, SIGTERM
|
||||
use libc::{
|
||||
c_int, c_void, malloc, sigaction, sigaltstack, siginfo_t, SA_NODEFER, SA_ONSTACK,
|
||||
SA_SIGINFO, SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGPIPE, SIGSEGV, SIGUSR2,
|
||||
};
|
||||
|
||||
mod unix_signal_handler {
|
||||
use alloc::vec::Vec;
|
||||
use core::ptr;
|
||||
use libc::{c_void, siginfo_t};
|
||||
#[cfg(feature = "std")]
|
||||
use std::{
|
||||
fs,
|
||||
io::{stdout, Write},
|
||||
mem, ptr,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bolts::os::unix_signals::{Handler, Signal},
|
||||
corpus::{Corpus, Testcase},
|
||||
events::{Event, EventManager},
|
||||
executors::ExitKind,
|
||||
feedbacks::FeedbacksTuple,
|
||||
inputs::Input,
|
||||
inputs::{HasTargetBytes, Input},
|
||||
observers::ObserversTuple,
|
||||
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,
|
||||
/// we should (tm) be okay with raw pointers here,
|
||||
static mut STATE_PTR: *mut c_void = ptr::null_mut();
|
||||
static mut EVENT_MGR_PTR: *mut c_void = ptr::null_mut();
|
||||
static mut OBSERVERS_PTR: *const c_void = ptr::null();
|
||||
/// The (unsafe) pointer to the current inmem input, for the current run.
|
||||
/// This is needed for certain non-rust side effects, as well as unix signal handling.
|
||||
static mut CURRENT_INPUT_PTR: *const c_void = ptr::null();
|
||||
/// Signal handling on unix systems needs some nasty unsafe.
|
||||
pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
|
||||
/// The state ptr for signal handling
|
||||
state_ptr: ptr::null_mut(),
|
||||
/// The event manager ptr for signal handling
|
||||
event_mgr_ptr: ptr::null_mut(),
|
||||
/// The observers ptr for signal handling
|
||||
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)
|
||||
where
|
||||
pub struct InProcessExecutorHandlerData {
|
||||
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>,
|
||||
OT: ObserversTuple,
|
||||
OC: Corpus<I>,
|
||||
OFT: FeedbacksTuple<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")]
|
||||
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"))]
|
||||
let si_addr = { info.si_addr() as usize };
|
||||
let si_addr = { _info.si_addr() as usize };
|
||||
|
||||
println!(
|
||||
"We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.",
|
||||
si_addr
|
||||
);
|
||||
}
|
||||
// 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") {
|
||||
Ok(maps) => println!("maps:\n{}", maps),
|
||||
Err(e) => println!("Couldn't load mappings: {:?}", e),
|
||||
@ -230,177 +377,7 @@ pub mod unix_signals {
|
||||
}
|
||||
|
||||
// TODO tell the parent to not restart
|
||||
std::process::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");
|
||||
libc::_exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +182,8 @@ mod tests {
|
||||
//Box::new(|_, _, _, _, _| ()),
|
||||
&mut state,
|
||||
&mut event_manager,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut mutator = StdScheduledMutator::new();
|
||||
mutator.add_mutation(mutation_bitflip);
|
||||
|
@ -15,7 +15,7 @@ use std::{
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(any(unix, feature = "std"))]
|
||||
use crate::Error;
|
||||
|
||||
pub trait AsSlice<T> {
|
||||
@ -417,14 +417,18 @@ pub enum ForkResult {
|
||||
/// Unix has forks.
|
||||
/// # Safety
|
||||
/// 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> {
|
||||
match libc::fork() {
|
||||
pid if pid > 0 => Ok(ForkResult::Parent(ChildHandle { pid })),
|
||||
pid if pid < 0 => {
|
||||
// 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.
|
||||
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)))
|
||||
}
|
||||
_ => Ok(ForkResult::Child),
|
||||
|
Loading…
x
Reference in New Issue
Block a user