#[cfg(unix)]
pub mod unix_signal_handler {
use alloc::{boxed::Box, string::String, vec::Vec};
use core::mem::transmute;
use std::{io::Write, panic};
use libafl_bolts::os::{
SIGNAL_RECURSION_EXIT,
unix_signals::{Signal, SignalHandler, ucontext_t},
};
use libc::siginfo_t;
use crate::{
events::{EventFirer, EventRestarter},
executors::{
Executor, ExitKind, HasObservers, common_signals,
hooks::inprocess::{GLOBAL_STATE, HasTimeout, InProcessExecutorHandlerData},
inprocess::{HasInProcessHooks, run_observers_and_save_state},
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasCurrentTestcase, HasExecutions, HasSolutions},
};
pub(crate) type HandlerFuncPtr = unsafe fn(
Signal,
&mut siginfo_t,
Option<&mut ucontext_t>,
data: *mut InProcessExecutorHandlerData,
);
#[cfg(unix)]
impl SignalHandler for InProcessExecutorHandlerData {
unsafe fn handle(
&mut self,
signal: Signal,
info: &mut siginfo_t,
context: Option<&mut ucontext_t>,
) {
unsafe {
let data = &raw mut GLOBAL_STATE;
let (max_depth_reached, signal_depth) = (*data).signal_handler_enter();
if max_depth_reached {
log::error!(
"The in process signal handler has been triggered {signal_depth} times recursively, which is not expected. Exiting with error code {SIGNAL_RECURSION_EXIT}..."
);
libc::exit(SIGNAL_RECURSION_EXIT);
}
match signal {
Signal::SigUser2 | Signal::SigAlarm => {
if !(*data).timeout_handler.is_null() {
let func: HandlerFuncPtr = transmute((*data).timeout_handler);
(func)(signal, info, context, data);
}
}
_ => {
if !(*data).crash_handler.is_null() {
let func: HandlerFuncPtr = transmute((*data).crash_handler);
func(signal, info, context, data);
}
}
}
(*data).signal_handler_exit();
}
}
fn signals(&self) -> Vec<Signal> {
common_signals()
}
}
pub fn setup_panic_hook<E, EM, I, OF, S, Z>()
where
E: Executor<EM, I, S, Z> + HasObservers,
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
I: Input + Clone,
{
let old_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| unsafe {
old_hook(panic_info);
let data = &raw mut GLOBAL_STATE;
let (max_depth_reached, signal_depth) = (*data).signal_handler_enter();
if max_depth_reached {
log::error!(
"The in process signal handler has been triggered {signal_depth} times recursively, which is not expected. Exiting with error code {SIGNAL_RECURSION_EXIT}..."
);
libc::exit(SIGNAL_RECURSION_EXIT);
}
if (*data).is_valid() {
let executor = (*data).executor_mut::<E>();
let state = (*data).state_mut::<S>();
let input = (*data).take_current_input::<I>();
let fuzzer = (*data).fuzzer_mut::<Z>();
let event_mgr = (*data).event_mgr_mut::<EM>();
run_observers_and_save_state::<E, EM, I, OF, S, Z>(
executor,
state,
input,
fuzzer,
event_mgr,
ExitKind::Crash,
);
libc::_exit(128 + 6); }
(*data).signal_handler_exit();
}));
}
#[cfg(unix)]
#[allow(clippy::needless_pass_by_value)] pub unsafe fn inproc_timeout_handler<E, EM, I, OF, S, Z>(
_signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: HasInProcessHooks<I, S> + HasObservers,
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
I: Input + Clone,
{
unsafe {
if !data.executor_ptr.is_null()
&& data
.executor_mut::<E>()
.inprocess_hooks_mut()
.handle_timeout(data)
{
return;
}
if !data.is_valid() {
log::warn!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing.");
return;
}
let executor = data.executor_mut::<E>();
let state = data.state_mut::<S>();
let event_mgr = data.event_mgr_mut::<EM>();
let fuzzer = data.fuzzer_mut::<Z>();
let input = data.take_current_input::<I>();
log::error!("Timeout in fuzz run.");
run_observers_and_save_state::<E, EM, I, OF, S, Z>(
executor,
state,
input,
fuzzer,
event_mgr,
ExitKind::Timeout,
);
log::info!("Exiting");
libc::_exit(55);
}
}
#[allow(clippy::needless_pass_by_value)] pub unsafe fn inproc_crash_handler<E, EM, I, OF, S, Z>(
signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, I, S, Z> + HasObservers,
E::Observers: ObserversTuple<I, S>,
EM: EventFirer<I, S> + EventRestarter<S>,
OF: Feedback<EM, I, E::Observers, S>,
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
Z: HasObjective<Objective = OF>,
I: Input + Clone,
{
unsafe {
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
let _context = _context.map(|p| {
&mut *(((core::ptr::from_mut(p) as *mut libc::c_void as usize) + 128)
as *mut libc::c_void as *mut ucontext_t)
});
log::error!("Crashed with {signal}");
if data.is_valid() {
let executor = data.executor_mut::<E>();
let state = data.state_mut::<S>();
let event_mgr = data.event_mgr_mut::<EM>();
let fuzzer = data.fuzzer_mut::<Z>();
let input = data.take_current_input::<I>();
log::error!("Child crashed!");
{
let mut bsod = Vec::new();
{
let mut writer = std::io::BufWriter::new(&mut bsod);
let _ = writeln!(writer, "input: {:?}", input.generate_name(None));
let bsod = libafl_bolts::minibsod::generate_minibsod(
&mut writer,
signal,
_info,
_context.as_deref(),
);
if bsod.is_err() {
log::error!("generate_minibsod failed");
}
let _ = writer.flush();
}
if let Ok(r) = core::str::from_utf8(&bsod) {
log::error!("{r}");
}
}
run_observers_and_save_state::<E, EM, I, OF, S, Z>(
executor,
state,
input,
fuzzer,
event_mgr,
ExitKind::Crash,
);
} else {
{
log::error!("Double crash\n");
#[cfg(target_os = "android")]
let si_addr = (_info._pad[0] as i64) | ((_info._pad[1] as i64) << 32);
#[cfg(not(target_os = "android"))]
let si_addr = { _info.si_addr() as usize };
log::error!(
"We crashed at addr 0x{si_addr:x}, but are not in the target... Bug in the fuzzer? Exiting."
);
{
let mut bsod = Vec::new();
{
let mut writer = std::io::BufWriter::new(&mut bsod);
let bsod = libafl_bolts::minibsod::generate_minibsod(
&mut writer,
signal,
_info,
_context.as_deref(),
);
if bsod.is_err() {
log::error!("generate_minibsod failed");
}
let _ = writer.flush();
}
if let Ok(r) = core::str::from_utf8(&bsod) {
log::error!("{r}");
}
}
}
{
log::error!("Type QUIT to restart the child");
let mut line = String::new();
while line.trim() != "QUIT" {
let _ = std::io::stdin().read_line(&mut line);
}
}
}
libc::_exit(128 + (signal as i32));
}
}
}