Save and restore CPU state in libafl_qemu (#907)
* libafl_qemu: fix systemmode with slirp dependency libslirp will be dropped from future QEMU releases (see https://wiki.qemu.org/ChangeLog/7.0). This change adds the "slirp" feature, which links with the host-systems libslirp. * libafl_qemu: enable systemmode snapshots, vm_start Re-enable snapshot functions. Start the VM before qemu_main_loop. * libafl_qemu: allow synchronous snapshotting Add a flag to take snapshots synchronosly. This should be used to take or load snapshots while the emulator is not running. * libafl_qemu: fallback cpu for read-/write_mem In systemmode, current_cpu may not be set. In such cases use the first cpus memory access methods. * fuzzers: add example for libafl_qemu in systemmode * libafl_qemu: update libafl-qemu-bridge revision * libafl_qemu: add memory access by physcial address * fix liabfl_qemu example Use GuestAddr and physical memory access * ci: install libslirp-dev for libafl_qemu * fuzzers/qemu_systemmode: clean up example * libafl_qemu: remove obsolete functions emu::libafl_cpu_thread_fn emu::libafl_start_vcpu emu::start * fuzzers/qemu_systemmode: simplify example * improve build_linux.rs * Update qemu_systemmode fuzzer * upd * clippy * Save and restore CPU state in libafl_qemu * clippy * Clone * upd * upd Co-authored-by: Alwin Berger <alwin.berger@tu-dortmund.de>
This commit is contained in:
parent
7b0039606b
commit
3f627aaf0b
@ -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<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]) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 :)
|
||||
|
@ -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<u8>,
|
||||
}
|
||||
|
||||
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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user