Break on timeout in QEMU system mode (#1619)

* Break on timeout in QEMU system mode

* fix

* fix

* fix
This commit is contained in:
Andrea Fioraldi 2023-10-11 14:01:18 +02:00 committed by GitHub
parent 31f4669794
commit 47cd4dfea6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 24 deletions

View File

@ -18,3 +18,4 @@ codegen-units = 1
libafl = { path = "../../libafl/" } libafl = { path = "../../libafl/" }
libafl_bolts = { path = "../../libafl_bolts/" } libafl_bolts = { path = "../../libafl_bolts/" }
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
env_logger = "*"

View File

@ -1,12 +1,11 @@
int BREAKPOINT() { int __attribute__ ((noinline)) BREAKPOINT() {
for (;;) {} for (;;) {}
} }
int LLVMFuzzerTestOneInput(unsigned int *Data, unsigned int Size) { int LLVMFuzzerTestOneInput(unsigned int *Data, unsigned int Size) {
// if (Data[3] == 0) {while(1){}} // cause a timeout if (Data[3] == 0) {while(1){}} // cause a timeout
for (int i = 0; i < Size; i++) { for (int i = 0; i < Size; i++) {
// if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to // if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to crash
// crash
for (int j = i + 1; j < Size; j++) { for (int j = i + 1; j < Size; j++) {
if (Data[j] == 0) { continue; } if (Data[j] == 0) { continue; }
if (Data[j] > Data[i]) { if (Data[j] > Data[i]) {

View File

@ -37,6 +37,8 @@ use libafl_qemu::{
pub static mut MAX_INPUT_SIZE: usize = 50; pub static mut MAX_INPUT_SIZE: usize = 50;
pub fn fuzz() { pub fn fuzz() {
env_logger::init();
if let Ok(s) = env::var("FUZZ_SIZE") { if let Ok(s) = env::var("FUZZ_SIZE") {
str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number"); str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number");
}; };
@ -193,7 +195,7 @@ pub fn fuzz() {
let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default())); let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default()));
// Create a QEMU in-process executor // Create a QEMU in-process executor
let executor = QemuExecutor::new( let mut executor = QemuExecutor::new(
&mut hooks, &mut hooks,
&mut harness, &mut harness,
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),
@ -203,6 +205,9 @@ pub fn fuzz() {
) )
.expect("Failed to create QemuExecutor"); .expect("Failed to create QemuExecutor");
// Instead of calling the timeout handler and restart the process, trigger a breakpoint ASAP
executor.break_on_timeout();
// Wrap the executor to keep track of the timeout // Wrap the executor to keep track of the timeout
let mut executor = TimeoutExecutor::new(executor, timeout); let mut executor = TimeoutExecutor::new(executor, timeout);

View File

@ -845,7 +845,7 @@ pub fn generate_minibsod<W: Write>(
writeln!(writer, "{:━^100}", " REGISTERS ")?; writeln!(writer, "{:━^100}", " REGISTERS ")?;
dump_registers(writer, uctx)?; dump_registers(writer, uctx)?;
} else { } else {
writeln!(writer, "Received signal {}", signal)?; writeln!(writer, "Received signal {signal}")?;
} }
writeln!(writer, "{:━^100}", " BACKTRACE ")?; writeln!(writer, "{:━^100}", " BACKTRACE ")?;
writeln!(writer, "{:?}", backtrace::Backtrace::new())?; writeln!(writer, "{:?}", backtrace::Backtrace::new())?;

View File

@ -1,10 +1,9 @@
//! 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::{
use core::ffi::c_void; ffi::c_void,
use core::fmt::{self, Debug, Formatter}; 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,
@ -13,7 +12,10 @@ use libafl::{
}; };
use libafl::{ use libafl::{
events::{EventFirer, EventRestarter}, events::{EventFirer, EventRestarter},
executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers}, executors::{
inprocess::{InProcessExecutor, InProcessExecutorHandlerData},
Executor, ExitKind, HasObservers,
},
feedbacks::Feedback, feedbacks::Feedback,
fuzzer::HasObjective, fuzzer::HasObjective,
inputs::UsesInput, inputs::UsesInput,
@ -21,7 +23,6 @@ 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}; 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;
@ -74,7 +75,7 @@ pub unsafe extern "C" fn libafl_executor_reinstall_handlers() {
pub unsafe fn inproc_qemu_crash_handler<E, EM, OF, Z>( pub unsafe fn inproc_qemu_crash_handler<E, EM, OF, Z>(
signal: Signal, signal: Signal,
info: &mut siginfo_t, info: &mut siginfo_t,
context: &mut ucontext_t, mut context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData, data: &mut InProcessExecutorHandlerData,
) where ) where
E: Executor<EM, Z> + HasObservers, E: Executor<EM, Z> + HasObservers,
@ -86,7 +87,11 @@ pub unsafe fn inproc_qemu_crash_handler<E, EM, OF, Z>(
let real_crash = if USE_LIBAFL_CRASH_HANDLER { let real_crash = if USE_LIBAFL_CRASH_HANDLER {
true true
} else { } else {
libafl_qemu_handle_crash(signal as i32, info, context as *mut _ as *mut c_void) != 0 let puc = match &mut context {
Some(v) => (*v) as *mut ucontext_t as *mut c_void,
None => core::ptr::null_mut(),
};
libafl_qemu_handle_crash(signal as i32, info, puc) != 0
}; };
if real_crash { if real_crash {
libafl::executors::inprocess::unix_signal_handler::inproc_crash_handler::<E, EM, OF, Z>( libafl::executors::inprocess::unix_signal_handler::inproc_crash_handler::<E, EM, OF, Z>(
@ -95,6 +100,36 @@ pub unsafe fn inproc_qemu_crash_handler<E, EM, OF, Z>(
} }
} }
#[cfg(emulation_mode = "systemmode")]
static mut BREAK_ON_TMOUT: bool = false;
#[cfg(emulation_mode = "systemmode")]
extern "C" {
fn qemu_system_debug_request();
}
#[cfg(emulation_mode = "systemmode")]
pub unsafe fn inproc_qemu_timeout_handler<E, EM, OF, Z>(
signal: Signal,
info: &mut siginfo_t,
context: Option<&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>,
{
if BREAK_ON_TMOUT {
qemu_system_debug_request();
} else {
libafl::executors::inprocess::unix_signal_handler::inproc_timeout_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,
@ -116,31 +151,37 @@ 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>,
{ {
let mut inner = InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?;
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
{ {
let mut inner =
InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?;
inner.handlers_mut().crash_handler = inner.handlers_mut().crash_handler =
inproc_qemu_crash_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z> inproc_qemu_crash_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z>
as *const c_void; as *const c_void;
}
#[cfg(emulation_mode = "systemmode")]
{
inner.handlers_mut().timeout_handler =
inproc_qemu_timeout_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z>
as *const c_void;
}
Ok(Self { Ok(Self {
first_exec: true, first_exec: true,
hooks, hooks,
inner, inner,
}) })
} }
#[cfg(not(emulation_mode = "usermode"))]
Ok(Self {
first_exec: true,
hooks,
inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?,
})
}
pub fn inner(&self) -> &InProcessExecutor<'a, H, OT, S> { pub fn inner(&self) -> &InProcessExecutor<'a, H, OT, S> {
&self.inner &self.inner
} }
#[cfg(emulation_mode = "systemmode")]
pub fn break_on_timeout(&mut self) {
unsafe {
BREAK_ON_TMOUT = true;
}
}
pub fn inner_mut(&mut self) -> &mut InProcessExecutor<'a, H, OT, S> { pub fn inner_mut(&mut self) -> &mut InProcessExecutor<'a, H, OT, S> {
&mut self.inner &mut self.inner
} }