diff --git a/crates/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs b/crates/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs index d31dae9b21..dc76182fa5 100644 --- a/crates/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs +++ b/crates/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs @@ -8237,6 +8237,9 @@ unsafe extern "C" { unsafe extern "C" { pub fn libafl_qemu_run() -> ::std::os::raw::c_int; } +unsafe extern "C" { + pub fn libafl_qemu_run_single_cpu(cpu_index: *mut CPUState) -> ::std::os::raw::c_int; +} unsafe extern "C" { pub fn libafl_set_qemu_env(env: *mut CPUArchState); } diff --git a/crates/libafl_qemu/src/qemu/usermode.rs b/crates/libafl_qemu/src/qemu/usermode.rs index 3e882f44f4..261fd5cdaf 100644 --- a/crates/libafl_qemu/src/qemu/usermode.rs +++ b/crates/libafl_qemu/src/qemu/usermode.rs @@ -5,6 +5,9 @@ use std::{ str::from_utf8_unchecked_mut, }; +use crate::{QemuExitError, QemuExitReason, QemuShutdownCause}; +use crate::qemu::transmute; + use libafl_bolts::{Error, os::unix_signals::Signal}; #[cfg(not(feature = "systemmode"))] use libafl_qemu_sys::libafl_qemu_run; @@ -14,6 +17,7 @@ use libafl_qemu_sys::{ libafl_get_initial_brk, libafl_load_addr, libafl_maps_first, libafl_maps_next, libafl_set_brk, mmap_next_start, pageflags_get_root, read_self_maps, }; +use libafl_qemu_sys::{libafl_get_exit_reason, libafl_qemu_run_single_cpu}; use libc::{c_int, c_uchar, siginfo_t, strlen}; #[cfg(feature = "python")] use pyo3::{IntoPyObject, Py, PyRef, PyRefMut, Python, pyclass, pymethods}; @@ -491,6 +495,57 @@ impl Qemu { }, } } + + /// Run a single, caller-selected CPU until the next exit. + /// + /// # Safety + /// + /// The provided [`CPU`] must belong to this QEMU instance and remain valid for the duration of + /// the run. + #[allow(dead_code)] + pub unsafe fn run_single_cpu(&self, cpu: CPU) -> Result { + unsafe { + libafl_qemu_run_single_cpu(cpu.raw_ptr()); + } + + let exit_reason = unsafe { libafl_get_exit_reason() }; + if exit_reason.is_null() { + Err(QemuExitError::UnexpectedExit) + } else { + let exit_reason: &mut libafl_qemu_sys::libafl_exit_reason = + unsafe { transmute(&mut *exit_reason) }; + Ok(match exit_reason.kind { + libafl_qemu_sys::libafl_exit_reason_kind_INTERNAL => unsafe { + let qemu_shutdown_cause: QemuShutdownCause = match exit_reason.data.internal.cause { + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_NONE => QemuShutdownCause::None, + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_HOST_ERROR => QemuShutdownCause::HostError, + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_HOST_QMP_QUIT => QemuShutdownCause::HostQmpQuit, + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET => QemuShutdownCause::HostQmpSystemReset, + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_HOST_SIGNAL => { + QemuShutdownCause::HostSignal( + Signal::try_from(exit_reason.data.internal.signal).unwrap(), + ) + } + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_HOST_UI => QemuShutdownCause::HostUi, + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_GUEST_SHUTDOWN => QemuShutdownCause::GuestShutdown, + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_GUEST_RESET => QemuShutdownCause::GuestReset, + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_GUEST_PANIC => QemuShutdownCause::GuestPanic, + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_SUBSYSTEM_RESET => QemuShutdownCause::SubsystemReset, + libafl_qemu_sys::ShutdownCause_SHUTDOWN_CAUSE_SNAPSHOT_LOAD => QemuShutdownCause::SnapshotLoad, + _ => panic!("shutdown cause not handled."), + }; + QemuExitReason::End(qemu_shutdown_cause) + }, + libafl_qemu_sys::libafl_exit_reason_kind_BREAKPOINT => unsafe { + let bp_addr = exit_reason.data.breakpoint.addr; + QemuExitReason::Breakpoint(bp_addr) + }, + libafl_qemu_sys::libafl_exit_reason_kind_CUSTOM_INSN => QemuExitReason::SyncExit, + libafl_qemu_sys::libafl_exit_reason_kind_CRASH => QemuExitReason::Crash, + _ => return Err(QemuExitError::UnknownKind), + }) + } + } } #[cfg(feature = "python")]