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) {
|
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 crash
|
//if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to 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]) {
|
||||||
@ -35,4 +35,4 @@ int LLVMFuzzerTestOneInput(unsigned int* Data, unsigned int Size) {
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
LLVMFuzzerTestOneInput(FUZZ_INPUT, 50);
|
LLVMFuzzerTestOneInput(FUZZ_INPUT, 50);
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,10 @@ pub fn fuzz() {
|
|||||||
// saved_regs.push(emu.cpu_from_index(0).read_reg(r).unwrap());
|
// 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
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |input: &BytesInput| {
|
let mut harness = |input: &BytesInput| {
|
||||||
let target = input.target_bytes();
|
let target = input.target_bytes();
|
||||||
@ -129,7 +133,11 @@ pub fn fuzz() {
|
|||||||
None => ExitKind::Crash,
|
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
|
ret
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use which::which;
|
|||||||
|
|
||||||
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||||
const QEMU_DIRNAME: &str = "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]) {
|
fn build_dep_check(tools: &[&str]) {
|
||||||
for tool in tools {
|
for tool in tools {
|
||||||
@ -45,7 +45,6 @@ pub fn build() {
|
|||||||
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
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
|
// Make sure we have at most one architecutre feature set
|
||||||
// Else, we default to `x86_64` - having a default makes CI easier :)
|
// Else, we default to `x86_64` - having a default makes CI easier :)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
//! Expose QEMU user `LibAFL` C api to Rust
|
//! Expose QEMU user `LibAFL` C api to Rust
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
use core::{
|
use core::{
|
||||||
convert::Into,
|
convert::Into,
|
||||||
ffi::c_void,
|
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};
|
use std::{ffi::CString, slice::from_raw_parts, str::from_utf8_unchecked};
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
@ -30,7 +30,8 @@ use pyo3::{prelude::*, PyIterProtocol};
|
|||||||
|
|
||||||
pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
|
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)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter, PartialEq, Eq)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -254,6 +255,8 @@ extern "C" fn qemu_cleanup_atexit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
fn libafl_qemu_arch_state_size() -> usize;
|
||||||
|
|
||||||
// CPUState* libafl_qemu_get_cpu(int cpu_index);
|
// CPUState* libafl_qemu_get_cpu(int cpu_index);
|
||||||
fn libafl_qemu_get_cpu(cpu_index: i32) -> CPUStatePtr;
|
fn libafl_qemu_get_cpu(cpu_index: i32) -> CPUStatePtr;
|
||||||
// int libafl_qemu_num_cpus(void);
|
// 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_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);
|
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)]
|
#[derive(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct CPU {
|
pub struct CPU {
|
||||||
@ -540,9 +566,32 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cpu_reset(&self) {
|
pub fn reset(&self) {
|
||||||
unsafe { cpu_reset(self.ptr) };
|
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;
|
static mut EMULATOR_IS_INITIALIZED: bool = false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user