diff --git a/fuzzers/qemu_systemmode/example/main.c b/fuzzers/qemu_systemmode/example/main.c index 5b1b1c7216..1d34d135de 100644 --- a/fuzzers/qemu_systemmode/example/main.c +++ b/fuzzers/qemu_systemmode/example/main.c @@ -5,9 +5,9 @@ int BREAKPOINT() { } 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 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; jData[i]) { @@ -35,4 +35,4 @@ int LLVMFuzzerTestOneInput(unsigned int* Data, unsigned int Size) { int main() { LLVMFuzzerTestOneInput(FUZZ_INPUT, 50); -} \ No newline at end of file +} diff --git a/fuzzers/qemu_systemmode/src/fuzzer.rs b/fuzzers/qemu_systemmode/src/fuzzer.rs index 9c0a8e3c8c..2a296212c1 100644 --- a/fuzzers/qemu_systemmode/src/fuzzer.rs +++ b/fuzzers/qemu_systemmode/src/fuzzer.rs @@ -99,6 +99,10 @@ pub fn fuzz() { // saved_regs.push(emu.cpu_from_index(0).read_reg(r).unwrap()); //} + let saved_cpu_states: Vec<_> = (0..emu.num_cpus()) + .map(|i| emu.cpu_from_index(i).save_state()) + .collect(); + // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |input: &BytesInput| { let target = input.target_bytes(); @@ -129,7 +133,11 @@ pub fn fuzz() { None => ExitKind::Crash, }; - emu.load_snapshot("start", true); + for (i, s) in saved_cpu_states.iter().enumerate() { + emu.cpu_from_index(i).restore_state(s); + } + + // emu.load_snapshot("start", true); ret } diff --git a/libafl_qemu/build_linux.rs b/libafl_qemu/build_linux.rs index a4bd05a528..be3f79d1ac 100644 --- a/libafl_qemu/build_linux.rs +++ b/libafl_qemu/build_linux.rs @@ -4,7 +4,7 @@ use which::which; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; -const QEMU_REVISION: &str = "658565bec0a68a84c733217fa9b7802562096559"; +const QEMU_REVISION: &str = "f26a5ca6137bb5d4d0dcfe5451fb16d4c0551c4e"; fn build_dep_check(tools: &[&str]) { for tool in tools { @@ -45,7 +45,6 @@ pub fn build() { println!("cargo:rerun-if-env-changed=EMULATION_MODE"); println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=CROSS_CC"); // Make sure we have at most one architecutre feature set // Else, we default to `x86_64` - having a default makes CI easier :) diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 35c768ea98..4c4c860186 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -1,12 +1,12 @@ //! Expose QEMU user `LibAFL` C api to Rust +#[cfg(emulation_mode = "usermode")] +use core::mem::MaybeUninit; use core::{ convert::Into, ffi::c_void, - ptr::{addr_of, addr_of_mut, null}, + ptr::{addr_of, addr_of_mut, copy_nonoverlapping, null}, }; -#[cfg(emulation_mode = "usermode")] -use core::{mem::MaybeUninit, ptr::copy_nonoverlapping}; use std::{ffi::CString, slice::from_raw_parts, str::from_utf8_unchecked}; #[cfg(emulation_mode = "usermode")] @@ -30,7 +30,8 @@ use pyo3::{prelude::*, PyIterProtocol}; pub const SKIP_EXEC_HOOK: u64 = u64::MAX; -type CPUStatePtr = *const c_void; +type CPUStatePtr = *mut c_void; +type CPUArchStatePtr = *mut c_void; #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter, PartialEq, Eq)] #[repr(i32)] @@ -254,6 +255,8 @@ extern "C" fn qemu_cleanup_atexit() { } extern "C" { + fn libafl_qemu_arch_state_size() -> usize; + // CPUState* libafl_qemu_get_cpu(int cpu_index); fn libafl_qemu_get_cpu(cpu_index: i32) -> CPUStatePtr; // int libafl_qemu_num_cpus(void); @@ -356,6 +359,9 @@ extern "C" { ); fn libafl_qemu_gdb_reply(buf: *const u8, len: usize); + fn libafl_qemu_cpu_arch_state(cpu: CPUStatePtr) -> CPUArchStatePtr; + // fn libafl_qemu_arch_state_cpu(env: CPUArchStatePtr) -> CPUStatePtr; + fn cpu_reset(cpu: CPUStatePtr); } @@ -437,6 +443,26 @@ extern "C" fn gdb_cmd(buf: *const u8, len: usize, data: *const ()) -> i32 { } } +#[derive(Clone, Debug)] +#[repr(C)] +pub struct SavedCPUState { + data: Vec, +} + +impl SavedCPUState { + #[must_use] + #[allow(clippy::uninit_vec)] + fn uninit() -> Self { + unsafe { + let len = libafl_qemu_arch_state_size(); + let mut data = Vec::with_capacity(len); + data.set_len(len); + + Self { data } + } + } +} + #[derive(Debug)] #[repr(C)] pub struct CPU { @@ -540,9 +566,32 @@ impl CPU { } } - pub fn cpu_reset(&self) { + pub fn reset(&self) { unsafe { cpu_reset(self.ptr) }; } + + #[must_use] + pub fn save_state(&self) -> SavedCPUState { + let mut saved = SavedCPUState::uninit(); + unsafe { + copy_nonoverlapping( + libafl_qemu_cpu_arch_state(self.ptr) as *mut u8, + saved.data.as_mut_ptr(), + saved.data.len(), + ); + } + saved + } + + pub fn restore_state(&self, saved: &SavedCPUState) { + unsafe { + copy_nonoverlapping( + saved.data.as_ptr(), + libafl_qemu_cpu_arch_state(self.ptr) as *mut u8, + saved.data.len(), + ); + } + } } static mut EMULATOR_IS_INITIALIZED: bool = false;