Call the original QEMU user crash handler in libafl_qemu (#1575)

* Call the original QEMU user crash handler in libafl_qemu

* Return if real crash or not

* merge

* Fix singal handlers in libafl and libafl_qemu

* doc and clippy

* clippy

* clippy

* clippy

* slirp

* fix

* fix system
This commit is contained in:
Andrea Fioraldi 2023-10-05 15:24:21 +02:00 committed by GitHub
parent d4e9107fc2
commit cffbf069d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 112 additions and 32 deletions

View File

@ -68,7 +68,7 @@ pub struct ShutdownSignalData {
/// Type for shutdown handler /// Type for shutdown handler
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
pub type ShutdownFuncPtr = pub type ShutdownFuncPtr =
unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut ShutdownSignalData); unsafe fn(Signal, &mut siginfo_t, &mut ucontext_t, data: &mut ShutdownSignalData);
/// Shutdown handler. `SigTerm`, `SigInterrupt`, `SigQuit` call this /// Shutdown handler. `SigTerm`, `SigInterrupt`, `SigQuit` call this
/// We can't handle SIGKILL in the signal handler, this means that you shouldn't kill your fuzzer with `kill -9` because then the shmem segments are never freed /// We can't handle SIGKILL in the signal handler, this means that you shouldn't kill your fuzzer with `kill -9` because then the shmem segments are never freed
@ -79,7 +79,7 @@ pub type ShutdownFuncPtr =
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
pub unsafe fn shutdown_handler<SP>( pub unsafe fn shutdown_handler<SP>(
signal: Signal, signal: Signal,
_info: siginfo_t, _info: &mut siginfo_t,
_context: &mut ucontext_t, _context: &mut ucontext_t,
data: &ShutdownSignalData, data: &ShutdownSignalData,
) where ) where
@ -106,7 +106,7 @@ pub unsafe fn shutdown_handler<SP>(
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
impl Handler for ShutdownSignalData { impl Handler for ShutdownSignalData {
fn handle(&mut self, signal: Signal, info: siginfo_t, context: &mut ucontext_t) { fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: &mut ucontext_t) {
unsafe { unsafe {
let data = &mut SHUTDOWN_SIGHANDLER_DATA; let data = &mut SHUTDOWN_SIGHANDLER_DATA;
if !data.shutdown_handler.is_null() { if !data.shutdown_handler.is_null() {

View File

@ -418,13 +418,13 @@ impl InProcessHandlers {
/// The global state of the in-process harness. /// The global state of the in-process harness.
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct InProcessExecutorHandlerData { pub struct InProcessExecutorHandlerData {
state_ptr: *mut c_void, state_ptr: *mut c_void,
event_mgr_ptr: *mut c_void, event_mgr_ptr: *mut c_void,
fuzzer_ptr: *mut c_void, fuzzer_ptr: *mut c_void,
executor_ptr: *const c_void, executor_ptr: *const c_void,
pub current_input_ptr: *const c_void, pub(crate) current_input_ptr: *const c_void,
pub in_handler: bool, pub(crate) in_handler: bool,
/// The timeout handler /// The timeout handler
#[cfg(any(unix, feature = "std"))] #[cfg(any(unix, feature = "std"))]
@ -478,7 +478,7 @@ impl InProcessExecutorHandlerData {
} }
#[cfg(any(unix, feature = "std"))] #[cfg(any(unix, feature = "std"))]
pub fn is_valid(&self) -> bool { pub(crate) fn is_valid(&self) -> bool {
!self.current_input_ptr.is_null() !self.current_input_ptr.is_null()
} }
@ -631,8 +631,9 @@ pub fn run_observers_and_save_state<E, EM, OF, Z>(
log::info!("Bye!"); log::info!("Bye!");
} }
/// The inprocess executor singal handling code for unix
#[cfg(unix)] #[cfg(unix)]
mod unix_signal_handler { pub mod unix_signal_handler {
use alloc::vec::Vec; use alloc::vec::Vec;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use alloc::{boxed::Box, string::String}; use alloc::{boxed::Box, string::String};
@ -658,12 +659,12 @@ mod unix_signal_handler {
}; };
pub(crate) type HandlerFuncPtr = pub(crate) type HandlerFuncPtr =
unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut InProcessExecutorHandlerData); unsafe fn(Signal, &mut siginfo_t, &mut ucontext_t, data: &mut InProcessExecutorHandlerData);
/// A handler that does nothing. /// A handler that does nothing.
/*pub fn nop_handler( /*pub fn nop_handler(
_signal: Signal, _signal: Signal,
_info: siginfo_t, _info: &mut siginfo_t,
_context: &mut ucontext_t, _context: &mut ucontext_t,
_data: &mut InProcessExecutorHandlerData, _data: &mut InProcessExecutorHandlerData,
) { ) {
@ -671,7 +672,7 @@ mod unix_signal_handler {
#[cfg(unix)] #[cfg(unix)]
impl Handler for InProcessExecutorHandlerData { impl Handler for InProcessExecutorHandlerData {
fn handle(&mut self, signal: Signal, info: siginfo_t, context: &mut ucontext_t) { fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: &mut ucontext_t) {
unsafe { unsafe {
let data = &mut GLOBAL_STATE; let data = &mut GLOBAL_STATE;
let in_handler = data.set_in_handler(true); let in_handler = data.set_in_handler(true);
@ -748,10 +749,15 @@ mod unix_signal_handler {
})); }));
} }
/// Timeout-Handler for in-process fuzzing.
/// It will store the current State to shmem, then exit.
///
/// # Safety
/// Well, signal handling is not safe
#[cfg(unix)] #[cfg(unix)]
pub(crate) unsafe fn inproc_timeout_handler<E, EM, OF, Z>( pub unsafe fn inproc_timeout_handler<E, EM, OF, Z>(
_signal: Signal, _signal: Signal,
_info: siginfo_t, _info: &mut siginfo_t,
_context: &mut ucontext_t, _context: &mut ucontext_t,
data: &mut InProcessExecutorHandlerData, data: &mut InProcessExecutorHandlerData,
) where ) where
@ -795,10 +801,13 @@ mod unix_signal_handler {
/// Crash-Handler for in-process fuzzing. /// Crash-Handler for in-process fuzzing.
/// Will be used for signal handling. /// Will be used for signal handling.
/// It will store the current State to shmem, then exit. /// It will store the current State to shmem, then exit.
///
/// # Safety
/// Well, signal handling is not safe
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub(crate) unsafe fn inproc_crash_handler<E, EM, OF, Z>( pub unsafe fn inproc_crash_handler<E, EM, OF, Z>(
signal: Signal, signal: Signal,
_info: siginfo_t, _info: &mut siginfo_t,
_context: &mut ucontext_t, _context: &mut ucontext_t,
data: &mut InProcessExecutorHandlerData, data: &mut InProcessExecutorHandlerData,
) where ) where
@ -991,7 +1000,7 @@ pub mod windows_asan_handler {
} }
#[cfg(all(windows, feature = "std"))] #[cfg(all(windows, feature = "std"))]
mod windows_exception_handler { pub mod windows_exception_handler {
#[cfg(feature = "std")] #[cfg(feature = "std")]
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::{string::String, vec::Vec}; use alloc::{string::String, vec::Vec};
@ -1058,6 +1067,9 @@ mod windows_exception_handler {
} }
/// invokes the `post_exec` hook on all observer in case of panic /// invokes the `post_exec` hook on all observer in case of panic
///
/// # Safety
/// Well, exception handling is not safe
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn setup_panic_hook<E, EM, OF, Z>() pub fn setup_panic_hook<E, EM, OF, Z>()
where where
@ -1117,6 +1129,9 @@ mod windows_exception_handler {
} }
/// Timeout handler for windows /// Timeout handler for windows
///
/// # Safety
/// Well, exception handling is not safe
pub unsafe extern "system" fn inproc_timeout_handler<E, EM, OF, Z>( pub unsafe extern "system" fn inproc_timeout_handler<E, EM, OF, Z>(
_p0: *mut u8, _p0: *mut u8,
global_state: *mut c_void, global_state: *mut c_void,
@ -1180,8 +1195,12 @@ mod windows_exception_handler {
// log::info!("TIMER INVOKED!"); // log::info!("TIMER INVOKED!");
} }
/// Crash handler for windows
///
/// # Safety
/// Well, exception handling is not safe
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub(crate) unsafe fn inproc_crash_handler<E, EM, OF, Z>( pub unsafe fn inproc_crash_handler<E, EM, OF, Z>(
exception_pointers: *mut EXCEPTION_POINTERS, exception_pointers: *mut EXCEPTION_POINTERS,
data: &mut InProcessExecutorHandlerData, data: &mut InProcessExecutorHandlerData,
) where ) where
@ -1303,7 +1322,7 @@ mod windows_exception_handler {
/// The signature of the crash handler function /// The signature of the crash handler function
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
pub(crate) type ForkHandlerFuncPtr = pub(crate) type ForkHandlerFuncPtr =
unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut InProcessForkExecutorGlobalData); unsafe fn(Signal, &mut siginfo_t, &mut ucontext_t, data: &mut InProcessForkExecutorGlobalData);
/// The inmem fork executor's handlers. /// The inmem fork executor's handlers.
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
@ -1443,7 +1462,7 @@ pub(crate) static mut FORK_EXECUTOR_GLOBAL_DATA: InProcessForkExecutorGlobalData
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
impl Handler for InProcessForkExecutorGlobalData { impl Handler for InProcessForkExecutorGlobalData {
fn handle(&mut self, signal: Signal, info: siginfo_t, context: &mut ucontext_t) { fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: &mut ucontext_t) {
match signal { match signal {
Signal::SigUser2 | Signal::SigAlarm => unsafe { Signal::SigUser2 | Signal::SigAlarm => unsafe {
if !FORK_EXECUTOR_GLOBAL_DATA.timeout_handler.is_null() { if !FORK_EXECUTOR_GLOBAL_DATA.timeout_handler.is_null() {
@ -2052,7 +2071,7 @@ pub mod child_signal_handlers {
#[cfg(unix)] #[cfg(unix)]
pub(crate) unsafe fn child_crash_handler<E>( pub(crate) unsafe fn child_crash_handler<E>(
_signal: Signal, _signal: Signal,
_info: siginfo_t, _info: &mut siginfo_t,
_context: &mut ucontext_t, _context: &mut ucontext_t,
data: &mut InProcessForkExecutorGlobalData, data: &mut InProcessForkExecutorGlobalData,
) where ) where
@ -2074,7 +2093,7 @@ pub mod child_signal_handlers {
#[cfg(unix)] #[cfg(unix)]
pub(crate) unsafe fn child_timeout_handler<E>( pub(crate) unsafe fn child_timeout_handler<E>(
_signal: Signal, _signal: Signal,
_info: siginfo_t, _info: &mut siginfo_t,
_context: &mut ucontext_t, _context: &mut ucontext_t,
data: &mut InProcessForkExecutorGlobalData, data: &mut InProcessForkExecutorGlobalData,
) where ) where

View File

@ -1939,7 +1939,7 @@ pub struct LlmpShutdownSignalHandler {
#[cfg(unix)] #[cfg(unix)]
impl Handler for LlmpShutdownSignalHandler { impl Handler for LlmpShutdownSignalHandler {
fn handle(&mut self, _signal: Signal, _info: siginfo_t, _context: &mut ucontext_t) { fn handle(&mut self, _signal: Signal, _info: &mut siginfo_t, _context: &mut ucontext_t) {
unsafe { unsafe {
ptr::write_volatile(&mut self.shutting_down, true); ptr::write_volatile(&mut self.shutting_down, true);
} }

View File

@ -836,7 +836,7 @@ fn write_minibsod<W: Write>(writer: &mut BufWriter<W>) -> Result<(), std::io::Er
pub fn generate_minibsod<W: Write>( pub fn generate_minibsod<W: Write>(
writer: &mut BufWriter<W>, writer: &mut BufWriter<W>,
signal: Signal, signal: Signal,
_siginfo: siginfo_t, _siginfo: &siginfo_t,
ucontext: &ucontext_t, ucontext: &ucontext_t,
) -> Result<(), std::io::Error> { ) -> Result<(), std::io::Error> {
writeln!(writer, "{:━^100}", " CRASH ")?; writeln!(writer, "{:━^100}", " CRASH ")?;

View File

@ -378,7 +378,7 @@ impl Display for Signal {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub trait Handler { pub trait Handler {
/// Handle a signal /// Handle a signal
fn handle(&mut self, signal: Signal, info: siginfo_t, _context: &mut ucontext_t); fn handle(&mut self, signal: Signal, info: &mut siginfo_t, _context: &mut ucontext_t);
/// Return a list of signals to handle /// Return a list of signals to handle
fn signals(&self) -> Vec<Signal>; fn signals(&self) -> Vec<Signal>;
} }
@ -414,7 +414,7 @@ static mut SIGNAL_HANDLERS: [Option<HandlerHolder>; 32] = [
/// This should be somewhat safe to call for signals previously registered, /// This should be somewhat safe to call for signals previously registered,
/// unless the signal handlers registered using [`setup_signal_handler()`] are broken. /// unless the signal handlers registered using [`setup_signal_handler()`] are broken.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
unsafe fn handle_signal(sig: c_int, info: siginfo_t, void: *mut c_void) { unsafe fn handle_signal(sig: c_int, info: *mut siginfo_t, void: *mut c_void) {
let signal = &Signal::try_from(sig).unwrap(); let signal = &Signal::try_from(sig).unwrap();
let handler = { let handler = {
match &SIGNAL_HANDLERS[*signal as usize] { match &SIGNAL_HANDLERS[*signal as usize] {
@ -424,7 +424,7 @@ unsafe fn handle_signal(sig: c_int, info: siginfo_t, void: *mut c_void) {
}; };
handler.handle( handler.handle(
*signal, *signal,
info, &mut ptr::read_unaligned(info),
&mut ptr::read_unaligned(void as *mut ucontext_t), &mut ptr::read_unaligned(void as *mut ucontext_t),
); );
} }

View File

@ -8,7 +8,7 @@ use which::which;
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
const QEMU_REVISION: &str = "ff5bc3d934044a5a5466759525f0371ccf86152e"; const QEMU_REVISION: &str = "ead06288fd597e72cbf50db1c89386f952592860";
fn build_dep_check(tools: &[&str]) { fn build_dep_check(tools: &[&str]) {
for tool in tools { for tool in tools {
@ -216,7 +216,6 @@ pub fn build(
.arg("--disable-gtk") .arg("--disable-gtk")
.arg("--disable-guest-agent") .arg("--disable-guest-agent")
.arg("--disable-guest-agent-msi") .arg("--disable-guest-agent-msi")
.arg("--disable-hax")
.arg("--disable-hvf") .arg("--disable-hvf")
.arg("--disable-iconv") .arg("--disable-iconv")
.arg("--disable-jack") .arg("--disable-jack")

View File

@ -7,12 +7,14 @@ use core::{
mem::MaybeUninit, mem::MaybeUninit,
ptr::{addr_of, copy_nonoverlapping, null}, ptr::{addr_of, copy_nonoverlapping, null},
}; };
use std::{cell::OnceCell, slice::from_raw_parts, str::from_utf8_unchecked}; #[cfg(emulation_mode = "usermode")]
use std::cell::OnceCell;
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
use std::{ use std::{
ffi::{CStr, CString}, ffi::{CStr, CString},
ptr::null_mut, ptr::null_mut,
}; };
use std::{slice::from_raw_parts, str::from_utf8_unchecked};
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
use libc::c_int; use libc::c_int;

View File

@ -1,6 +1,10 @@
//! A `QEMU`-based executor for binary-only instrumentation in `LibAFL` //! A `QEMU`-based executor for binary-only instrumentation in `LibAFL`
#[cfg(emulation_mode = "usermode")]
use core::ffi::c_void;
use core::fmt::{self, Debug, Formatter}; use core::fmt::{self, Debug, Formatter};
#[cfg(emulation_mode = "usermode")]
use libafl::executors::inprocess::InProcessExecutorHandlerData;
#[cfg(feature = "fork")] #[cfg(feature = "fork")]
use libafl::{ use libafl::{
events::EventManager, events::EventManager,
@ -9,7 +13,7 @@ use libafl::{
}; };
use libafl::{ use libafl::{
events::{EventFirer, EventRestarter}, events::{EventFirer, EventRestarter},
executors::{Executor, ExitKind, HasObservers, InProcessExecutor}, executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers},
feedbacks::Feedback, feedbacks::Feedback,
fuzzer::HasObjective, fuzzer::HasObjective,
inputs::UsesInput, inputs::UsesInput,
@ -17,6 +21,8 @@ use libafl::{
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasSolutions, State, UsesState}, state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasSolutions, State, UsesState},
Error, Error,
}; };
#[cfg(emulation_mode = "usermode")]
use libafl_bolts::os::unix_signals::{siginfo_t, ucontext_t, Signal};
#[cfg(feature = "fork")] #[cfg(feature = "fork")]
use libafl_bolts::shmem::ShMemProvider; use libafl_bolts::shmem::ShMemProvider;
@ -29,9 +35,9 @@ where
OT: ObserversTuple<S>, OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>, QT: QemuHelperTuple<S>,
{ {
first_exec: bool,
hooks: &'a mut QemuHooks<'a, QT, S>,
inner: InProcessExecutor<'a, H, OT, S>, inner: InProcessExecutor<'a, H, OT, S>,
hooks: &'a mut QemuHooks<'a, QT, S>,
first_exec: bool,
} }
impl<'a, H, OT, QT, S> Debug for QemuExecutor<'a, H, OT, QT, S> impl<'a, H, OT, QT, S> Debug for QemuExecutor<'a, H, OT, QT, S>
@ -49,6 +55,46 @@ where
} }
} }
#[cfg(emulation_mode = "usermode")]
extern "C" {
// Original QEMU user signal handler
fn libafl_qemu_handle_crash(signal: i32, info: *mut siginfo_t, puc: *mut c_void) -> i32;
}
#[cfg(emulation_mode = "usermode")]
static mut USE_LIBAFL_CRASH_HANDLER: bool = false;
#[cfg(emulation_mode = "usermode")]
#[no_mangle]
pub unsafe extern "C" fn libafl_executor_reinstall_handlers() {
USE_LIBAFL_CRASH_HANDLER = true;
}
#[cfg(emulation_mode = "usermode")]
pub unsafe fn inproc_qemu_crash_handler<E, EM, OF, Z>(
signal: Signal,
info: &mut siginfo_t,
context: &mut ucontext_t,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasClientPerfMonitor + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
let real_crash = if USE_LIBAFL_CRASH_HANDLER {
true
} else {
libafl_qemu_handle_crash(signal as i32, info, context as *mut _ as *mut c_void) != 0
};
if real_crash {
libafl::executors::inprocess::unix_signal_handler::inproc_crash_handler::<E, EM, OF, Z>(
signal, info, context, data,
);
}
}
impl<'a, H, OT, QT, S> QemuExecutor<'a, H, OT, QT, S> impl<'a, H, OT, QT, S> QemuExecutor<'a, H, OT, QT, S>
where where
H: FnMut(&S::Input) -> ExitKind, H: FnMut(&S::Input) -> ExitKind,
@ -70,6 +116,20 @@ where
S: State + HasExecutions + HasCorpus + HasSolutions + HasClientPerfMonitor, S: State + HasExecutions + HasCorpus + HasSolutions + HasClientPerfMonitor,
Z: HasObjective<Objective = OF, State = S>, Z: HasObjective<Objective = OF, State = S>,
{ {
#[cfg(emulation_mode = "usermode")]
{
let mut inner =
InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?;
inner.handlers_mut().crash_handler =
inproc_qemu_crash_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z>
as *const c_void;
Ok(Self {
first_exec: true,
hooks,
inner,
})
}
#[cfg(not(emulation_mode = "usermode"))]
Ok(Self { Ok(Self {
first_exec: true, first_exec: true,
hooks, hooks,