diff --git a/fuzzers/qemu_systemmode/Cargo.toml b/fuzzers/qemu_systemmode/Cargo.toml index 4d1ecd9d02..d0b27cb497 100644 --- a/fuzzers/qemu_systemmode/Cargo.toml +++ b/fuzzers/qemu_systemmode/Cargo.toml @@ -18,3 +18,4 @@ codegen-units = 1 libafl = { path = "../../libafl/" } libafl_bolts = { path = "../../libafl_bolts/" } libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } +env_logger = "*" diff --git a/fuzzers/qemu_systemmode/example/main.c b/fuzzers/qemu_systemmode/example/main.c index 858251ba2b..eddf5e2c99 100644 --- a/fuzzers/qemu_systemmode/example/main.c +++ b/fuzzers/qemu_systemmode/example/main.c @@ -1,12 +1,11 @@ -int BREAKPOINT() { +int __attribute__ ((noinline)) BREAKPOINT() { for (;;) {} } 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++) { - // if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to - // crash + // if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to crash for (int j = i + 1; j < Size; j++) { if (Data[j] == 0) { continue; } if (Data[j] > Data[i]) { diff --git a/fuzzers/qemu_systemmode/src/fuzzer.rs b/fuzzers/qemu_systemmode/src/fuzzer.rs index 3a9622b935..c0e79e0703 100644 --- a/fuzzers/qemu_systemmode/src/fuzzer.rs +++ b/fuzzers/qemu_systemmode/src/fuzzer.rs @@ -37,6 +37,8 @@ use libafl_qemu::{ pub static mut MAX_INPUT_SIZE: usize = 50; pub fn fuzz() { + env_logger::init(); + if let Ok(s) = env::var("FUZZ_SIZE") { str::parse::(&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())); // Create a QEMU in-process executor - let executor = QemuExecutor::new( + let mut executor = QemuExecutor::new( &mut hooks, &mut harness, tuple_list!(edges_observer, time_observer), @@ -203,6 +205,9 @@ pub fn fuzz() { ) .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 let mut executor = TimeoutExecutor::new(executor, timeout); diff --git a/libafl_bolts/src/minibsod.rs b/libafl_bolts/src/minibsod.rs index 48eb80ab9a..1a39888316 100644 --- a/libafl_bolts/src/minibsod.rs +++ b/libafl_bolts/src/minibsod.rs @@ -845,7 +845,7 @@ pub fn generate_minibsod( writeln!(writer, "{:━^100}", " REGISTERS ")?; dump_registers(writer, uctx)?; } else { - writeln!(writer, "Received signal {}", signal)?; + writeln!(writer, "Received signal {signal}")?; } writeln!(writer, "{:━^100}", " BACKTRACE ")?; writeln!(writer, "{:?}", backtrace::Backtrace::new())?; diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 67a6d2b51a..fb949f7ae9 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -1,10 +1,9 @@ //! 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::{ + ffi::c_void, + fmt::{self, Debug, Formatter}, +}; -#[cfg(emulation_mode = "usermode")] -use libafl::executors::inprocess::InProcessExecutorHandlerData; #[cfg(feature = "fork")] use libafl::{ events::EventManager, @@ -13,7 +12,10 @@ use libafl::{ }; use libafl::{ events::{EventFirer, EventRestarter}, - executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers}, + executors::{ + inprocess::{InProcessExecutor, InProcessExecutorHandlerData}, + Executor, ExitKind, HasObservers, + }, feedbacks::Feedback, fuzzer::HasObjective, inputs::UsesInput, @@ -21,7 +23,6 @@ use libafl::{ state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasSolutions, State, UsesState}, Error, }; -#[cfg(emulation_mode = "usermode")] use libafl_bolts::os::unix_signals::{siginfo_t, ucontext_t, Signal}; #[cfg(feature = "fork")] 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( signal: Signal, info: &mut siginfo_t, - context: &mut ucontext_t, + mut context: Option<&mut ucontext_t>, data: &mut InProcessExecutorHandlerData, ) where E: Executor + HasObservers, @@ -86,7 +87,11 @@ pub unsafe fn inproc_qemu_crash_handler( 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 + 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 { libafl::executors::inprocess::unix_signal_handler::inproc_crash_handler::( @@ -95,6 +100,36 @@ pub unsafe fn inproc_qemu_crash_handler( } } +#[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( + signal: Signal, + info: &mut siginfo_t, + context: Option<&mut ucontext_t>, + data: &mut InProcessExecutorHandlerData, +) where + E: Executor + HasObservers, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: HasSolutions + HasClientPerfMonitor + HasCorpus, + Z: HasObjective, +{ + if BREAK_ON_TMOUT { + qemu_system_debug_request(); + } else { + libafl::executors::inprocess::unix_signal_handler::inproc_timeout_handler::( + signal, info, context, data, + ); + } +} + impl<'a, H, OT, QT, S> QemuExecutor<'a, H, OT, QT, S> where H: FnMut(&S::Input) -> ExitKind, @@ -116,24 +151,23 @@ where S: State + HasExecutions + HasCorpus + HasSolutions + HasClientPerfMonitor, Z: HasObjective, { + let mut inner = InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?; #[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::, EM, OF, Z> as *const c_void; - Ok(Self { - first_exec: true, - hooks, - inner, - }) } - #[cfg(not(emulation_mode = "usermode"))] + #[cfg(emulation_mode = "systemmode")] + { + inner.handlers_mut().timeout_handler = + inproc_qemu_timeout_handler::, EM, OF, Z> + as *const c_void; + } Ok(Self { first_exec: true, hooks, - inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?, + inner, }) } @@ -141,6 +175,13 @@ where &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> { &mut self.inner }