QEMU generic memory iterator (#2148)
* QEMU generic memory iterator + Refactoring * Generic Memory Iterator (systemmode only for now): It is now possible to iterator over memory ranges, independently of the address kind * Refactoring or Emulator / Qemu structures: they are now handled separately in different files * Refactoring of Exit Handlers: Result / Error structs have been clarified * Simple handler for signals * add new `check-cfg` calls for libafl qemu
This commit is contained in:
parent
a16fb88f3e
commit
bed500471a
@ -51,8 +51,8 @@ use libafl_qemu::{
|
||||
elf::EasyElf,
|
||||
filter_qemu_args,
|
||||
hooks::QemuHooks,
|
||||
GuestReg, MmapPerms, Qemu, QemuExitReason, QemuExitReasonError, QemuForkExecutor,
|
||||
QemuShutdownCause, Regs,
|
||||
GuestReg, MmapPerms, Qemu, QemuExitError, QemuExitReason, QemuForkExecutor, QemuShutdownCause,
|
||||
Regs,
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use nix::unistd::dup;
|
||||
@ -328,7 +328,7 @@ fn fuzz(
|
||||
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
||||
process::exit(0)
|
||||
}
|
||||
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
||||
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
|
||||
_ => panic!("Unexpected QEMU exit."),
|
||||
}
|
||||
}
|
||||
|
@ -60,8 +60,8 @@ use libafl_qemu::{
|
||||
MmapPerms,
|
||||
Qemu,
|
||||
QemuExecutor,
|
||||
QemuExitError,
|
||||
QemuExitReason,
|
||||
QemuExitReasonError,
|
||||
QemuShutdownCause,
|
||||
Regs,
|
||||
};
|
||||
@ -350,7 +350,7 @@ fn fuzz(
|
||||
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
||||
process::exit(0)
|
||||
}
|
||||
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
||||
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
|
||||
_ => panic!("Unexpected QEMU exit."),
|
||||
}
|
||||
}
|
||||
|
@ -29,8 +29,8 @@ use libafl_bolts::{
|
||||
use libafl_qemu::{
|
||||
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
|
||||
elf::EasyElf,
|
||||
ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitReason,
|
||||
QemuExitReasonError, QemuForkExecutor, QemuHooks, QemuShutdownCause, Regs,
|
||||
ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitError,
|
||||
QemuExitReason, QemuForkExecutor, QemuHooks, QemuShutdownCause, Regs,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -211,7 +211,7 @@ pub fn fuzz() -> Result<(), Error> {
|
||||
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
||||
process::exit(0)
|
||||
}
|
||||
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
||||
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
|
||||
_ => panic!("Unexpected QEMU exit."),
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use std::{env, path::PathBuf, process};
|
||||
|
||||
use libafl::{
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||
events::{launcher::Launcher, EventConfig, CTRL_C_EXIT},
|
||||
events::{launcher::Launcher, EventConfig},
|
||||
executors::ExitKind,
|
||||
feedback_or, feedback_or_fast,
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
@ -29,13 +29,13 @@ use libafl_bolts::{
|
||||
};
|
||||
use libafl_qemu::{
|
||||
breakpoint::Breakpoint,
|
||||
command::{Command, EmulatorMemoryChunk, EndCommand, StartCommand},
|
||||
command::{Command, EndCommand, StartCommand},
|
||||
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
||||
elf::EasyElf,
|
||||
emu::Emulator,
|
||||
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
||||
EmuExitReasonError, FastSnapshotManager, GuestPhysAddr, GuestReg, HandlerError, HandlerResult,
|
||||
QemuHooks, StdEmuExitHandler,
|
||||
EmulatorMemoryChunk, FastSnapshotManager, GuestPhysAddr, GuestReg, QemuHooks,
|
||||
StdEmulatorExitHandler,
|
||||
};
|
||||
|
||||
// use libafl_qemu::QemuSnapshotBuilder; // for normal qemu snapshot
|
||||
@ -93,7 +93,7 @@ pub fn fuzz() {
|
||||
let emu_snapshot_manager = FastSnapshotManager::new(false);
|
||||
|
||||
// Choose Exit Handler
|
||||
let emu_exit_handler = StdEmuExitHandler::new(emu_snapshot_manager);
|
||||
let emu_exit_handler = StdEmulatorExitHandler::new(emu_snapshot_manager);
|
||||
|
||||
// Create emulator
|
||||
let emu = Emulator::new(&args, &env, emu_exit_handler).unwrap();
|
||||
@ -126,29 +126,10 @@ pub fn fuzz() {
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness =
|
||||
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe {
|
||||
match emu.run(input, qemu_executor_state) {
|
||||
Ok(handler_result) => match handler_result {
|
||||
HandlerResult::UnhandledExit(unhandled_exit) => {
|
||||
panic!("Unhandled exit: {}", unhandled_exit)
|
||||
}
|
||||
HandlerResult::EndOfRun(exit_kind) => return exit_kind,
|
||||
HandlerResult::Interrupted => {
|
||||
std::process::exit(CTRL_C_EXIT);
|
||||
}
|
||||
},
|
||||
Err(handler_error) => match handler_error {
|
||||
HandlerError::QemuExitReasonError(emu_exit_reason_error) => {
|
||||
match emu_exit_reason_error {
|
||||
EmuExitReasonError::UnknownKind => panic!("unknown kind"),
|
||||
EmuExitReasonError::UnexpectedExit => return ExitKind::Crash,
|
||||
_ => {
|
||||
panic!("Emu Exit unhandled error: {:?}", emu_exit_reason_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => panic!("Unhandled error: {:?}", handler_error),
|
||||
},
|
||||
}
|
||||
emu.run(input, qemu_executor_state)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Create an observation channel using the coverage map
|
||||
|
@ -5,7 +5,7 @@ use std::{env, path::PathBuf, process};
|
||||
|
||||
use libafl::{
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||
events::{launcher::Launcher, EventConfig, CTRL_C_EXIT},
|
||||
events::{launcher::Launcher, EventConfig},
|
||||
executors::ExitKind,
|
||||
feedback_or, feedback_or_fast,
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
@ -22,7 +22,7 @@ use libafl::{
|
||||
use libafl_bolts::{
|
||||
core_affinity::Cores,
|
||||
current_nanos,
|
||||
os::unix_signals::Signal,
|
||||
os::unix_signals::{Signal, CTRL_C_EXIT},
|
||||
ownedref::OwnedMutSlice,
|
||||
rands::StdRand,
|
||||
shmem::{ShMemProvider, StdShMemProvider},
|
||||
@ -32,8 +32,7 @@ use libafl_bolts::{
|
||||
use libafl_qemu::{
|
||||
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
||||
elf::EasyElf,
|
||||
emu::Qemu,
|
||||
QemuExecutor, QemuExitReason, QemuExitReasonError, QemuHooks, QemuShutdownCause, Regs,
|
||||
Qemu, QemuExecutor, QemuExitError, QemuExitReason, QemuHooks, QemuShutdownCause, Regs,
|
||||
};
|
||||
use libafl_qemu_sys::GuestPhysAddr;
|
||||
|
||||
@ -128,7 +127,7 @@ pub fn fuzz() {
|
||||
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(
|
||||
Signal::SigInterrupt,
|
||||
))) => process::exit(CTRL_C_EXIT),
|
||||
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
||||
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
|
||||
_ => panic!("Unexpected QEMU exit."),
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,7 @@ use std::{env, path::PathBuf, process};
|
||||
|
||||
use libafl::{
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||
events::{launcher::Launcher, EventConfig, CTRL_C_EXIT},
|
||||
executors::ExitKind,
|
||||
events::{launcher::Launcher, EventConfig},
|
||||
feedback_or, feedback_or_fast,
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
@ -31,8 +30,7 @@ use libafl_qemu::{
|
||||
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
||||
emu::Emulator,
|
||||
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
||||
EmuExitReasonError, FastSnapshotManager, HandlerError, HandlerResult, QemuHooks,
|
||||
StdEmuExitHandler,
|
||||
FastSnapshotManager, QemuHooks, StdEmulatorExitHandler,
|
||||
};
|
||||
|
||||
// use libafl_qemu::QemuSnapshotBuilder; for normal qemu snapshot
|
||||
@ -56,8 +54,8 @@ pub fn fuzz() {
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
// let emu_snapshot_manager = QemuSnapshotBuilder::new(true);
|
||||
let emu_snapshot_manager = FastSnapshotManager::new(false); // Create a snapshot manager (normal or fast for now).
|
||||
let emu_exit_handler: StdEmuExitHandler<FastSnapshotManager> =
|
||||
StdEmuExitHandler::new(emu_snapshot_manager); // Create an exit handler: it is the entity taking the decision of what should be done when QEMU returns.
|
||||
let emu_exit_handler: StdEmulatorExitHandler<FastSnapshotManager> =
|
||||
StdEmulatorExitHandler::new(emu_snapshot_manager); // Create an exit handler: it is the entity taking the decision of what should be done when QEMU returns.
|
||||
let emu = Emulator::new(&args, &env, emu_exit_handler).unwrap(); // Create the emulator
|
||||
|
||||
let devices = emu.list_devices();
|
||||
@ -66,30 +64,10 @@ pub fn fuzz() {
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness =
|
||||
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe {
|
||||
match emu.run(input, qemu_executor_state) {
|
||||
Ok(handler_result) => match handler_result {
|
||||
HandlerResult::UnhandledExit(unhandled_exit) => {
|
||||
panic!("Unhandled exit: {}", unhandled_exit)
|
||||
}
|
||||
HandlerResult::EndOfRun(exit_kind) => exit_kind,
|
||||
HandlerResult::Interrupted => {
|
||||
println!("Interrupted.");
|
||||
std::process::exit(CTRL_C_EXIT);
|
||||
}
|
||||
},
|
||||
Err(handler_error) => match handler_error {
|
||||
HandlerError::QemuExitReasonError(emu_exit_reason_error) => {
|
||||
match emu_exit_reason_error {
|
||||
EmuExitReasonError::UnknownKind => panic!("unknown kind"),
|
||||
EmuExitReasonError::UnexpectedExit => ExitKind::Crash,
|
||||
_ => {
|
||||
panic!("Emu Exit unhandled error: {:?}", emu_exit_reason_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => panic!("Unhandled error: {:?}", handler_error),
|
||||
},
|
||||
}
|
||||
emu.run(input, qemu_executor_state)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Create an observation channel using the coverage map
|
||||
|
@ -36,7 +36,9 @@ use libafl_bolts::{
|
||||
tuples::{Handle, Handler},
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use libafl_bolts::{llmp::LlmpConnection, shmem::StdShMemProvider, staterestore::StateRestorer};
|
||||
use libafl_bolts::{
|
||||
llmp::LlmpConnection, os::CTRL_C_EXIT, shmem::StdShMemProvider, staterestore::StateRestorer,
|
||||
};
|
||||
use libafl_bolts::{
|
||||
llmp::{self, LlmpClient, LlmpClientDescription, Tag},
|
||||
shmem::ShMemProvider,
|
||||
@ -1581,7 +1583,7 @@ where
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
if child_status == crate::events::CTRL_C_EXIT || staterestorer.wants_to_exit() {
|
||||
if child_status == CTRL_C_EXIT || staterestorer.wants_to_exit() {
|
||||
// if ctrl-c is pressed, we end up in this branch
|
||||
if let Err(err) = mgr.detach_from_broker(self.broker_port) {
|
||||
log::error!("Failed to detach from broker: {err}");
|
||||
|
@ -31,7 +31,7 @@ use ahash::RandomState;
|
||||
#[cfg(feature = "std")]
|
||||
pub use launcher::*;
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
use libafl_bolts::os::unix_signals::{siginfo_t, ucontext_t, Handler, Signal};
|
||||
use libafl_bolts::os::unix_signals::{siginfo_t, ucontext_t, Handler, Signal, CTRL_C_EXIT};
|
||||
#[cfg(feature = "adaptive_serialization")]
|
||||
use libafl_bolts::tuples::{Handle, MatchNameRef};
|
||||
use libafl_bolts::{current_time, ClientId};
|
||||
@ -55,13 +55,6 @@ use crate::{
|
||||
state::HasScalabilityMonitor,
|
||||
};
|
||||
|
||||
/// The special exit code when the target exited throught ctrl-c
|
||||
#[cfg(unix)]
|
||||
pub const CTRL_C_EXIT: i32 = 100;
|
||||
/// The special exit code when the target exited throught ctrl-c
|
||||
#[cfg(windows)]
|
||||
pub const CTRL_C_EXIT: i32 = -1073741510;
|
||||
|
||||
/// Check if ctrl-c is sent with this struct
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
pub static mut EVENTMGR_SIGHANDLER_STATE: ShutdownSignalData = ShutdownSignalData {};
|
||||
|
@ -18,7 +18,7 @@ use libafl_bolts::os::unix_signals::setup_signal_handler;
|
||||
use libafl_bolts::os::{fork, ForkResult};
|
||||
use libafl_bolts::ClientId;
|
||||
#[cfg(feature = "std")]
|
||||
use libafl_bolts::{shmem::ShMemProvider, staterestore::StateRestorer};
|
||||
use libafl_bolts::{os::CTRL_C_EXIT, shmem::ShMemProvider, staterestore::StateRestorer};
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
@ -520,7 +520,7 @@ where
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
if child_status == crate::events::CTRL_C_EXIT || staterestorer.wants_to_exit() {
|
||||
if child_status == CTRL_C_EXIT || staterestorer.wants_to_exit() {
|
||||
return Err(Error::shutting_down());
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,8 @@ pub mod unix_shmem_server;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub mod unix_signals;
|
||||
#[cfg(unix)]
|
||||
pub use unix_signals::CTRL_C_EXIT;
|
||||
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
pub mod pipes;
|
||||
@ -28,9 +30,10 @@ use std::{fs::File, os::fd::AsRawFd, sync::OnceLock};
|
||||
#[cfg(all(windows, feature = "std"))]
|
||||
#[allow(missing_docs, overflowing_literals)]
|
||||
pub mod windows_exceptions;
|
||||
|
||||
#[cfg(unix)]
|
||||
use libc::pid_t;
|
||||
#[cfg(all(windows, feature = "std"))]
|
||||
pub use windows_exceptions::CTRL_C_EXIT;
|
||||
|
||||
/// A file that we keep open, pointing to /dev/null
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
|
@ -20,6 +20,9 @@ pub use libc::c_ulong;
|
||||
#[cfg(feature = "std")]
|
||||
use nix::errno::{errno, Errno};
|
||||
|
||||
/// The special exit code when the target exited through ctrl-c
|
||||
pub const CTRL_C_EXIT: i32 = 100;
|
||||
|
||||
/// ARMv7-specific representation of a saved context
|
||||
#[cfg(target_arch = "arm")]
|
||||
#[derive(Debug)]
|
||||
@ -302,6 +305,19 @@ pub enum Signal {
|
||||
SigTrap = SIGTRAP,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Signal {
|
||||
/// Handle an incoming signal
|
||||
pub fn handle(&self) {
|
||||
match self {
|
||||
Signal::SigInterrupt | Signal::SigQuit | Signal::SigTerm => {
|
||||
std::process::exit(CTRL_C_EXIT)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Signal {
|
||||
type Error = Error;
|
||||
|
||||
|
@ -25,6 +25,9 @@ pub use windows::Win32::{
|
||||
|
||||
use crate::Error;
|
||||
|
||||
/// The special exit code when the target exited through ctrl-c
|
||||
pub const CTRL_C_EXIT: i32 = -1073741510;
|
||||
|
||||
// For VEH
|
||||
const EXCEPTION_CONTINUE_EXECUTION: c_long = -1;
|
||||
|
||||
|
@ -89,7 +89,8 @@ paste = "1"
|
||||
enum-map = "2.7"
|
||||
serde_yaml = { version = "0.8", optional = true } # For parsing the injections yaml file
|
||||
toml = { version = "0.4.2", optional = true } # For parsing the injections toml file
|
||||
pyo3 = { version = "0.18", optional = true }
|
||||
pyo3 = { version = "0.18", optional = true , features = ["multiple-pymethods"]}
|
||||
bytes-utils = "0.1"
|
||||
# Document all features of this crate (for `cargo doc`)
|
||||
document-features = { version = "0.2", optional = true }
|
||||
|
||||
|
@ -11,10 +11,12 @@ mod host_specific {
|
||||
#[rustversion::nightly]
|
||||
fn main() {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
println!("cargo::rustc-check-cfg=cfg(nightly)");
|
||||
host_specific::build();
|
||||
}
|
||||
|
||||
#[rustversion::not(nightly)]
|
||||
fn main() {
|
||||
println!("cargo::rustc-check-cfg=cfg(nightly)");
|
||||
host_specific::build();
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ pub fn build() {
|
||||
let runtime_bindings_file = out_dir.join("libafl_qemu_bindings.rs");
|
||||
let stub_runtime_bindings_file = src_dir.join("runtime/libafl_qemu_stub_bindings.rs");
|
||||
|
||||
println!("cargo::rustc-check-cfg=cfg(emulation_mode, values(\"usermode\", \"systemmode\"))");
|
||||
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
||||
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
||||
|
||||
@ -65,6 +66,7 @@ pub fn build() {
|
||||
};
|
||||
println!("cargo:rerun-if-env-changed=CPU_TARGET");
|
||||
println!("cargo:rustc-cfg=cpu_target=\"{cpu_target}\"");
|
||||
println!("cargo::rustc-check-cfg=cfg(cpu_target, values(\"x86_64\", \"arm\", \"aarch64\", \"i386\", \"mips\", \"ppc\", \"hexagon\"))");
|
||||
|
||||
let cross_cc = if (emulation_mode == "usermode") && (qemu_asan || qemu_asan_guest) {
|
||||
// TODO try to autodetect a cross compiler with the arch name (e.g. aarch64-linux-gnu-gcc)
|
||||
|
@ -84,6 +84,7 @@ const WRAPPER_HEADER: &str = r#"
|
||||
#include "libafl/exit.h"
|
||||
#include "libafl/hook.h"
|
||||
#include "libafl/jit.h"
|
||||
#include "libafl/utils.h"
|
||||
|
||||
"#;
|
||||
|
||||
|
@ -11,7 +11,7 @@ use crate::cargo_add_rpath;
|
||||
|
||||
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||
const QEMU_REVISION: &str = "538e6b02c36838e0de469b39dd03fef05678444f";
|
||||
const QEMU_REVISION: &str = "9f3e2399ee9b106dfbb8c3afcdfdf30e235fc88f";
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub struct BuildResult {
|
||||
|
@ -8,6 +8,15 @@ mod host_specific {
|
||||
}
|
||||
}
|
||||
|
||||
#[rustversion::nightly]
|
||||
fn main() {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
println!("cargo::rustc-check-cfg=cfg(nightly)");
|
||||
host_specific::build();
|
||||
}
|
||||
|
||||
#[rustversion::not(nightly)]
|
||||
fn main() {
|
||||
println!("cargo::rustc-check-cfg=cfg(nightly)");
|
||||
host_specific::build();
|
||||
}
|
||||
|
@ -46,17 +46,18 @@ pub fn build() {
|
||||
"usermode".to_string()
|
||||
})
|
||||
};
|
||||
println!("cargo::rustc-check-cfg=cfg(emulation_mode, values(\"usermode\", \"systemmode\"))");
|
||||
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
||||
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
||||
|
||||
// Make sure we have at most one architecutre feature set
|
||||
// Else, we default to `x86_64` - having a default makes CI easier :)
|
||||
assert_unique_feature!("arm", "aarch64", "i386", "i86_64", "mips", "ppc", "hexagon");
|
||||
assert_unique_feature!("arm", "aarch64", "i386", "x86_64", "mips", "ppc", "hexagon");
|
||||
|
||||
// Make sure that we don't have BE set for any architecture other than arm and mips
|
||||
// Sure aarch64 may support BE, but its not in common usage and we don't
|
||||
// need it yet and so haven't tested it
|
||||
assert_unique_feature!("be", "aarch64", "i386", "i86_64", "hexagon");
|
||||
assert_unique_feature!("be", "aarch64", "i386", "x86_64", "hexagon");
|
||||
|
||||
let cpu_target = if cfg!(feature = "x86_64") {
|
||||
"x86_64".to_string()
|
||||
@ -82,6 +83,7 @@ pub fn build() {
|
||||
};
|
||||
println!("cargo:rerun-if-env-changed=CPU_TARGET");
|
||||
println!("cargo:rustc-cfg=cpu_target=\"{cpu_target}\"");
|
||||
println!("cargo::rustc-check-cfg=cfg(cpu_target, values(\"x86_64\", \"arm\", \"aarch64\", \"i386\", \"mips\", \"ppc\", \"hexagon\"))");
|
||||
|
||||
let jobs = env::var("NUM_JOBS")
|
||||
.ok()
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![cfg_attr(nightly, feature(used_with_arg))]
|
||||
/*!
|
||||
`libafl_qemu_sys` is the crate exporting C symbols from QEMU.
|
||||
Have a look at `libafl_qemu` for higher-level abstractions.
|
||||
|
@ -2340,7 +2340,6 @@ pub type DeviceReset = ::std::option::Option<unsafe extern "C" fn(dev: *mut Devi
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct DeviceClass {
|
||||
pub parent_class: ObjectClass,
|
||||
#[doc = " @categories: device categories device belongs to"]
|
||||
pub categories: [::std::os::raw::c_ulong; 1usize],
|
||||
#[doc = " @fw_name: name used to identify device to firmware interfaces"]
|
||||
pub fw_name: *const ::std::os::raw::c_char,
|
||||
@ -12055,8 +12054,7 @@ extern "C" {
|
||||
}
|
||||
pub const libafl_exit_reason_kind_INTERNAL: libafl_exit_reason_kind = libafl_exit_reason_kind(0);
|
||||
pub const libafl_exit_reason_kind_BREAKPOINT: libafl_exit_reason_kind = libafl_exit_reason_kind(1);
|
||||
pub const libafl_exit_reason_kind_SYNC_BACKDOOR: libafl_exit_reason_kind =
|
||||
libafl_exit_reason_kind(2);
|
||||
pub const libafl_exit_reason_kind_SYNC_EXIT: libafl_exit_reason_kind = libafl_exit_reason_kind(2);
|
||||
impl ::std::ops::BitOr<libafl_exit_reason_kind> for libafl_exit_reason_kind {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
@ -12119,21 +12117,18 @@ fn bindgen_test_layout_libafl_exit_reason_breakpoint() {
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct libafl_exit_reason_sync_backdoor {}
|
||||
pub struct libafl_exit_reason_sync_exit {}
|
||||
#[test]
|
||||
fn bindgen_test_layout_libafl_exit_reason_sync_backdoor() {
|
||||
fn bindgen_test_layout_libafl_exit_reason_sync_exit() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<libafl_exit_reason_sync_backdoor>(),
|
||||
::std::mem::size_of::<libafl_exit_reason_sync_exit>(),
|
||||
0usize,
|
||||
concat!("Size of: ", stringify!(libafl_exit_reason_sync_backdoor))
|
||||
concat!("Size of: ", stringify!(libafl_exit_reason_sync_exit))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<libafl_exit_reason_sync_backdoor>(),
|
||||
::std::mem::align_of::<libafl_exit_reason_sync_exit>(),
|
||||
1usize,
|
||||
concat!(
|
||||
"Alignment of ",
|
||||
stringify!(libafl_exit_reason_sync_backdoor)
|
||||
)
|
||||
concat!("Alignment of ", stringify!(libafl_exit_reason_sync_exit))
|
||||
);
|
||||
}
|
||||
#[repr(C)]
|
||||
@ -12200,7 +12195,7 @@ pub struct libafl_exit_reason {
|
||||
pub union libafl_exit_reason__bindgen_ty_1 {
|
||||
pub internal: libafl_exit_reason_internal,
|
||||
pub breakpoint: libafl_exit_reason_breakpoint,
|
||||
pub backdoor: libafl_exit_reason_sync_backdoor,
|
||||
pub sync_exit: libafl_exit_reason_sync_exit,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_libafl_exit_reason__bindgen_ty_1() {
|
||||
@ -12241,13 +12236,13 @@ fn bindgen_test_layout_libafl_exit_reason__bindgen_ty_1() {
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).backdoor) as usize - ptr as usize },
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).sync_exit) as usize - ptr as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(libafl_exit_reason__bindgen_ty_1),
|
||||
"::",
|
||||
stringify!(backdoor)
|
||||
stringify!(sync_exit)
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -13890,6 +13885,9 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn libafl_jit_trace_block_single(data: u64, id: u64) -> usize;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn libafl_qemu_host_page_size() -> usize;
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct kvm_dirty_gfn {
|
||||
|
@ -26,6 +26,7 @@ pub const __USE_ATFILE: u32 = 1;
|
||||
pub const __USE_FORTIFY_LEVEL: u32 = 0;
|
||||
pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0;
|
||||
pub const __GLIBC_USE_DEPRECATED_SCANF: u32 = 0;
|
||||
pub const __GLIBC_USE_C2X_STRTOL: u32 = 0;
|
||||
pub const _STDC_PREDEF_H: u32 = 1;
|
||||
pub const __STDC_IEC_559__: u32 = 1;
|
||||
pub const __STDC_IEC_60559_BFP__: u32 = 201404;
|
||||
@ -34,7 +35,7 @@ pub const __STDC_IEC_60559_COMPLEX__: u32 = 201404;
|
||||
pub const __STDC_ISO_10646__: u32 = 201706;
|
||||
pub const __GNU_LIBRARY__: u32 = 6;
|
||||
pub const __GLIBC__: u32 = 2;
|
||||
pub const __GLIBC_MINOR__: u32 = 35;
|
||||
pub const __GLIBC_MINOR__: u32 = 39;
|
||||
pub const _SYS_CDEFS_H: u32 = 1;
|
||||
pub const __glibc_c99_flexarr_available: u32 = 1;
|
||||
pub const __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI: u32 = 0;
|
||||
@ -58,6 +59,7 @@ pub const _BITS_TIME64_H: u32 = 1;
|
||||
pub const _BITS_WCHAR_H: u32 = 1;
|
||||
pub const _BITS_STDINT_INTN_H: u32 = 1;
|
||||
pub const _BITS_STDINT_UINTN_H: u32 = 1;
|
||||
pub const _BITS_STDINT_LEAST_H: u32 = 1;
|
||||
pub const INT8_MIN: i32 = -128;
|
||||
pub const INT16_MIN: i32 = -32768;
|
||||
pub const INT32_MIN: i32 = -2147483648;
|
||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
||||
pub use strum_macros::EnumIter;
|
||||
pub use syscall_numbers::aarch64::*;
|
||||
|
||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
||||
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||
|
||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||
#[repr(i32)]
|
||||
@ -49,19 +49,19 @@ pub enum Regs {
|
||||
Pstate = 33,
|
||||
}
|
||||
|
||||
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||
static EXIT_ARCH_REGS: OnceLock<EnumMap<ExitArgs, Regs>> = OnceLock::new();
|
||||
|
||||
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||
EXIT_ARCH_REGS.get_or_init(|| {
|
||||
enum_map! {
|
||||
BackdoorArgs::Ret => Regs::X0,
|
||||
BackdoorArgs::Cmd => Regs::X0,
|
||||
BackdoorArgs::Arg1 => Regs::X1,
|
||||
BackdoorArgs::Arg2 => Regs::X2,
|
||||
BackdoorArgs::Arg3 => Regs::X3,
|
||||
BackdoorArgs::Arg4 => Regs::X4,
|
||||
BackdoorArgs::Arg5 => Regs::X5,
|
||||
BackdoorArgs::Arg6 => Regs::X6,
|
||||
ExitArgs::Ret => Regs::X0,
|
||||
ExitArgs::Cmd => Regs::X0,
|
||||
ExitArgs::Arg1 => Regs::X1,
|
||||
ExitArgs::Arg2 => Regs::X2,
|
||||
ExitArgs::Arg3 => Regs::X3,
|
||||
ExitArgs::Arg4 => Regs::X4,
|
||||
ExitArgs::Arg5 => Regs::X5,
|
||||
ExitArgs::Arg6 => Regs::X6,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
||||
pub use strum_macros::EnumIter;
|
||||
pub use syscall_numbers::arm::*;
|
||||
|
||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
||||
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||
|
||||
/// Registers for the ARM instruction set.
|
||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||
@ -33,19 +33,19 @@ pub enum Regs {
|
||||
R25 = 25,
|
||||
}
|
||||
|
||||
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||
static EXIT_ARCH_REGS: OnceLock<EnumMap<ExitArgs, Regs>> = OnceLock::new();
|
||||
|
||||
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||
EXIT_ARCH_REGS.get_or_init(|| {
|
||||
enum_map! {
|
||||
BackdoorArgs::Ret => Regs::R0,
|
||||
BackdoorArgs::Cmd => Regs::R0,
|
||||
BackdoorArgs::Arg1 => Regs::R1,
|
||||
BackdoorArgs::Arg2 => Regs::R2,
|
||||
BackdoorArgs::Arg3 => Regs::R3,
|
||||
BackdoorArgs::Arg4 => Regs::R4,
|
||||
BackdoorArgs::Arg5 => Regs::R5,
|
||||
BackdoorArgs::Arg6 => Regs::R6,
|
||||
ExitArgs::Ret => Regs::R0,
|
||||
ExitArgs::Cmd => Regs::R0,
|
||||
ExitArgs::Arg1 => Regs::R1,
|
||||
ExitArgs::Arg2 => Regs::R2,
|
||||
ExitArgs::Arg3 => Regs::R3,
|
||||
ExitArgs::Arg4 => Regs::R4,
|
||||
ExitArgs::Arg5 => Regs::R5,
|
||||
ExitArgs::Arg6 => Regs::R6,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use pyo3::prelude::*;
|
||||
pub use strum_macros::EnumIter;
|
||||
|
||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
||||
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||
|
||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||
#[repr(i32)]
|
||||
@ -64,19 +64,19 @@ pub enum Regs {
|
||||
Pktcnthi = 51,
|
||||
}
|
||||
|
||||
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||
static EXIT_ARCH_REGS: OnceLock<EnumMap<ExitArgs, Regs>> = OnceLock::new();
|
||||
|
||||
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||
EXIT_ARCH_REGS.get_or_init(|| {
|
||||
enum_map! {
|
||||
BackdoorArgs::Ret => Regs::R0,
|
||||
BackdoorArgs::Cmd => Regs::R0,
|
||||
BackdoorArgs::Arg1 => Regs::R1,
|
||||
BackdoorArgs::Arg2 => Regs::R2,
|
||||
BackdoorArgs::Arg3 => Regs::R3,
|
||||
BackdoorArgs::Arg4 => Regs::R4,
|
||||
BackdoorArgs::Arg5 => Regs::R5,
|
||||
BackdoorArgs::Arg6 => Regs::R6,
|
||||
ExitArgs::Ret => Regs::R0,
|
||||
ExitArgs::Cmd => Regs::R0,
|
||||
ExitArgs::Arg1 => Regs::R1,
|
||||
ExitArgs::Arg2 => Regs::R2,
|
||||
ExitArgs::Arg3 => Regs::R3,
|
||||
ExitArgs::Arg4 => Regs::R4,
|
||||
ExitArgs::Arg5 => Regs::R5,
|
||||
ExitArgs::Arg6 => Regs::R6,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
||||
pub use strum_macros::EnumIter;
|
||||
pub use syscall_numbers::x86::*;
|
||||
|
||||
use crate::{sync_exit::BackdoorArgs, CallingConvention, GuestAddr};
|
||||
use crate::{sync_exit::ExitArgs, CallingConvention, GuestAddr};
|
||||
|
||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||
#[repr(i32)]
|
||||
@ -25,19 +25,19 @@ pub enum Regs {
|
||||
Eflags = 9,
|
||||
}
|
||||
|
||||
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||
static EXIT_ARCH_REGS: OnceLock<EnumMap<ExitArgs, Regs>> = OnceLock::new();
|
||||
|
||||
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||
EXIT_ARCH_REGS.get_or_init(|| {
|
||||
enum_map! {
|
||||
BackdoorArgs::Ret => Regs::Eax,
|
||||
BackdoorArgs::Cmd => Regs::Eax,
|
||||
BackdoorArgs::Arg1 => Regs::Edi,
|
||||
BackdoorArgs::Arg2 => Regs::Esi,
|
||||
BackdoorArgs::Arg3 => Regs::Edx,
|
||||
BackdoorArgs::Arg4 => Regs::Ebx,
|
||||
BackdoorArgs::Arg5 => Regs::Ecx,
|
||||
BackdoorArgs::Arg6 => Regs::Ebp,
|
||||
ExitArgs::Ret => Regs::Eax,
|
||||
ExitArgs::Cmd => Regs::Eax,
|
||||
ExitArgs::Arg1 => Regs::Edi,
|
||||
ExitArgs::Arg2 => Regs::Esi,
|
||||
ExitArgs::Arg3 => Regs::Edx,
|
||||
ExitArgs::Arg4 => Regs::Ebx,
|
||||
ExitArgs::Arg5 => Regs::Ecx,
|
||||
ExitArgs::Arg6 => Regs::Ebp,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use pyo3::prelude::*;
|
||||
pub use strum_macros::EnumIter;
|
||||
pub use syscall_numbers::mips::*;
|
||||
|
||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
||||
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||
|
||||
/// Registers for the MIPS instruction set.
|
||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||
@ -49,19 +49,19 @@ pub enum Regs {
|
||||
Pc = 37,
|
||||
}
|
||||
|
||||
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||
static EXIT_ARCH_REGS: OnceLock<EnumMap<ExitArgs, Regs>> = OnceLock::new();
|
||||
|
||||
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||
EXIT_ARCH_REGS.get_or_init(|| {
|
||||
enum_map! {
|
||||
BackdoorArgs::Ret => Regs::V0,
|
||||
BackdoorArgs::Cmd => Regs::V0,
|
||||
BackdoorArgs::Arg1 => Regs::A0,
|
||||
BackdoorArgs::Arg2 => Regs::A1,
|
||||
BackdoorArgs::Arg3 => Regs::A2,
|
||||
BackdoorArgs::Arg4 => Regs::A3,
|
||||
BackdoorArgs::Arg5 => Regs::T0,
|
||||
BackdoorArgs::Arg6 => Regs::T1,
|
||||
ExitArgs::Ret => Regs::V0,
|
||||
ExitArgs::Cmd => Regs::V0,
|
||||
ExitArgs::Arg1 => Regs::A0,
|
||||
ExitArgs::Arg2 => Regs::A1,
|
||||
ExitArgs::Arg3 => Regs::A2,
|
||||
ExitArgs::Arg4 => Regs::A3,
|
||||
ExitArgs::Arg5 => Regs::T0,
|
||||
ExitArgs::Arg6 => Regs::T1,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use pyo3::prelude::*;
|
||||
pub use strum_macros::EnumIter;
|
||||
pub use syscall_numbers::powerpc::*;
|
||||
|
||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
||||
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||
|
||||
/// Registers for the MIPS instruction set.
|
||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||
@ -88,19 +88,19 @@ pub enum Regs {
|
||||
Fpscr = 70,
|
||||
}
|
||||
|
||||
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||
static EXIT_ARCH_REGS: OnceLock<EnumMap<ExitArgs, Regs>> = OnceLock::new();
|
||||
|
||||
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||
EXIT_ARCH_REGS.get_or_init(|| {
|
||||
enum_map! {
|
||||
BackdoorArgs::Ret => Regs::R3,
|
||||
BackdoorArgs::Cmd => Regs::R0,
|
||||
BackdoorArgs::Arg1 => Regs::R3,
|
||||
BackdoorArgs::Arg2 => Regs::R4,
|
||||
BackdoorArgs::Arg3 => Regs::R5,
|
||||
BackdoorArgs::Arg4 => Regs::R6,
|
||||
BackdoorArgs::Arg5 => Regs::R7,
|
||||
BackdoorArgs::Arg6 => Regs::R8,
|
||||
ExitArgs::Ret => Regs::R3,
|
||||
ExitArgs::Cmd => Regs::R0,
|
||||
ExitArgs::Arg1 => Regs::R3,
|
||||
ExitArgs::Arg2 => Regs::R4,
|
||||
ExitArgs::Arg3 => Regs::R5,
|
||||
ExitArgs::Arg4 => Regs::R6,
|
||||
ExitArgs::Arg5 => Regs::R7,
|
||||
ExitArgs::Arg6 => Regs::R8,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
||||
pub use strum_macros::EnumIter;
|
||||
pub use syscall_numbers::x86_64::*;
|
||||
|
||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
||||
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||
|
||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||
#[repr(i32)]
|
||||
@ -33,19 +33,19 @@ pub enum Regs {
|
||||
Rflags = 17,
|
||||
}
|
||||
|
||||
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||
static EXIT_ARCH_REGS: OnceLock<EnumMap<ExitArgs, Regs>> = OnceLock::new();
|
||||
|
||||
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||
EXIT_ARCH_REGS.get_or_init(|| {
|
||||
enum_map! {
|
||||
BackdoorArgs::Ret => Regs::Rax,
|
||||
BackdoorArgs::Cmd => Regs::Rax,
|
||||
BackdoorArgs::Arg1 => Regs::Rdi,
|
||||
BackdoorArgs::Arg2 => Regs::Rsi,
|
||||
BackdoorArgs::Arg3 => Regs::Rdx,
|
||||
BackdoorArgs::Arg4 => Regs::R10,
|
||||
BackdoorArgs::Arg5 => Regs::R8,
|
||||
BackdoorArgs::Arg6 => Regs::R9,
|
||||
ExitArgs::Ret => Regs::Rax,
|
||||
ExitArgs::Cmd => Regs::Rax,
|
||||
ExitArgs::Arg1 => Regs::Rdi,
|
||||
ExitArgs::Arg2 => Regs::Rsi,
|
||||
ExitArgs::Arg3 => Regs::Rdx,
|
||||
ExitArgs::Arg4 => Regs::R10,
|
||||
ExitArgs::Arg5 => Regs::R8,
|
||||
ExitArgs::Arg6 => Regs::R9,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,24 +1,28 @@
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::{
|
||||
fmt::{Debug, Display, Formatter},
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
use enum_map::Enum;
|
||||
use enum_map::{enum_map, Enum, EnumMap};
|
||||
use libafl::{
|
||||
executors::ExitKind,
|
||||
inputs::HasTargetBytes,
|
||||
state::{HasExecutions, State},
|
||||
};
|
||||
use libafl_bolts::AsSlice;
|
||||
use libafl_qemu_sys::{GuestPhysAddr, GuestVirtAddr};
|
||||
use num_enum::TryFromPrimitive;
|
||||
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr};
|
||||
use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
use crate::QemuInstrumentationPagingFilter;
|
||||
use crate::{
|
||||
executor::QemuExecutorState, sync_exit::SyncBackdoorError, EmuExitHandler, Emulator,
|
||||
GuestAddrKind, GuestReg, HandlerError, HasInstrumentationFilter, InnerHandlerResult,
|
||||
InputLocation, IsFilter, IsSnapshotManager, Qemu, QemuHelperTuple,
|
||||
QemuInstrumentationAddressRangeFilter, Regs, StdEmuExitHandler, StdInstrumentationFilter, CPU,
|
||||
executor::QemuExecutorState, get_exit_arch_regs, sync_exit::ExitArgs, Emulator,
|
||||
EmulatorExitHandler, EmulatorMemoryChunk, ExitHandlerError, ExitHandlerResult, GuestReg,
|
||||
HasInstrumentationFilter, InputLocation, IsFilter, IsSnapshotManager, Qemu, QemuHelperTuple,
|
||||
QemuInstrumentationAddressRangeFilter, Regs, StdEmulatorExitHandler, StdInstrumentationFilter,
|
||||
CPU,
|
||||
};
|
||||
|
||||
pub const VERSION: u64 = bindings::LIBAFL_QEMU_HDR_VERSION_NUMBER as u64;
|
||||
@ -39,7 +43,7 @@ mod bindings {
|
||||
|
||||
#[derive(Debug, Clone, TryFromPrimitive)]
|
||||
#[repr(u64)]
|
||||
pub enum NativeBackdoorCommand {
|
||||
pub enum NativeCommand {
|
||||
StartVirt = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_VIRT.0 as u64, // Shortcut for Save + InputVirt
|
||||
StartPhys = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_PHYS.0 as u64, // Shortcut for Save + InputPhys
|
||||
InputVirt = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_INPUT_VIRT.0 as u64, // The address is a virtual address using the paging currently running in the VM.
|
||||
@ -64,7 +68,7 @@ pub trait IsCommand<QT, S, E>
|
||||
where
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State + HasExecutions,
|
||||
E: EmuExitHandler<QT, S>,
|
||||
E: EmulatorExitHandler<QT, S>,
|
||||
{
|
||||
/// Used to know whether the command can be run during a backdoor, or if it is necessary to go out of
|
||||
/// the QEMU VM to run the command.
|
||||
@ -81,7 +85,7 @@ where
|
||||
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||
input: &S::Input,
|
||||
ret_reg: Option<Regs>,
|
||||
) -> Result<InnerHandlerResult, HandlerError>;
|
||||
) -> Result<Option<ExitHandlerResult>, ExitHandlerError>;
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
@ -102,8 +106,123 @@ pub enum Command {
|
||||
AddressRangeFilterCommand(AddressRangeFilterCommand),
|
||||
}
|
||||
|
||||
pub static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CommandError {
|
||||
UnknownCommand(GuestReg),
|
||||
RegError(String),
|
||||
VersionDifference(u64),
|
||||
}
|
||||
|
||||
impl From<TryFromPrimitiveError<NativeCommand>> for CommandError {
|
||||
fn from(error: TryFromPrimitiveError<NativeCommand>) -> Self {
|
||||
CommandError::UnknownCommand(error.number.try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for CommandError {
|
||||
fn from(error_string: String) -> Self {
|
||||
CommandError::RegError(error_string)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Qemu> for Command {
|
||||
type Error = CommandError;
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn try_from(qemu: Qemu) -> Result<Self, Self::Error> {
|
||||
let arch_regs_map: &'static EnumMap<ExitArgs, Regs> = get_exit_arch_regs();
|
||||
let cmd_id: GuestReg = qemu.read_reg::<Regs, GuestReg>(arch_regs_map[ExitArgs::Cmd])?;
|
||||
|
||||
Ok(match u64::from(cmd_id).try_into()? {
|
||||
NativeCommand::Save => Command::SaveCommand(SaveCommand),
|
||||
NativeCommand::Load => Command::LoadCommand(LoadCommand),
|
||||
NativeCommand::InputVirt => {
|
||||
let virt_addr: GuestVirtAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Command::InputCommand(InputCommand::new(
|
||||
EmulatorMemoryChunk::virt(
|
||||
virt_addr,
|
||||
max_input_size,
|
||||
qemu.current_cpu().unwrap(),
|
||||
),
|
||||
qemu.current_cpu().unwrap(),
|
||||
))
|
||||
}
|
||||
NativeCommand::InputPhys => {
|
||||
let phys_addr: GuestPhysAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Command::InputCommand(InputCommand::new(
|
||||
EmulatorMemoryChunk::phys(
|
||||
phys_addr,
|
||||
max_input_size,
|
||||
Some(qemu.current_cpu().unwrap()),
|
||||
),
|
||||
qemu.current_cpu().unwrap(),
|
||||
))
|
||||
}
|
||||
NativeCommand::End => {
|
||||
let native_exit_kind: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let native_exit_kind: Result<NativeExitKind, _> =
|
||||
u64::from(native_exit_kind).try_into();
|
||||
|
||||
let exit_kind = native_exit_kind.ok().and_then(|k| {
|
||||
EMU_EXIT_KIND_MAP.get_or_init(|| {
|
||||
enum_map! {
|
||||
NativeExitKind::Unknown => None,
|
||||
NativeExitKind::Ok => Some(ExitKind::Ok),
|
||||
NativeExitKind::Crash => Some(ExitKind::Crash)
|
||||
}
|
||||
})[k]
|
||||
});
|
||||
|
||||
Command::EndCommand(EndCommand::new(exit_kind))
|
||||
}
|
||||
NativeCommand::StartPhys => {
|
||||
let input_phys_addr: GuestPhysAddr =
|
||||
qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Command::StartCommand(StartCommand::new(EmulatorMemoryChunk::phys(
|
||||
input_phys_addr,
|
||||
max_input_size,
|
||||
Some(qemu.current_cpu().unwrap()),
|
||||
)))
|
||||
}
|
||||
NativeCommand::StartVirt => {
|
||||
let input_virt_addr: GuestVirtAddr =
|
||||
qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Command::StartCommand(StartCommand::new(EmulatorMemoryChunk::virt(
|
||||
input_virt_addr,
|
||||
max_input_size,
|
||||
qemu.current_cpu().unwrap(),
|
||||
)))
|
||||
}
|
||||
NativeCommand::Version => {
|
||||
let client_version = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
|
||||
Command::VersionCommand(VersionCommand::new(client_version))
|
||||
}
|
||||
NativeCommand::VaddrFilterAllowRange => {
|
||||
let vaddr_start: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
|
||||
let vaddr_end: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
|
||||
|
||||
Command::AddressRangeFilterCommand(FilterCommand::new(
|
||||
#[allow(clippy::single_range_in_vec_init)]
|
||||
QemuInstrumentationAddressRangeFilter::AllowList(vec![vaddr_start..vaddr_end]),
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with enum_dispatch implementation
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for Command
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmulatorExitHandler<SM>> for Command
|
||||
where
|
||||
SM: IsSnapshotManager,
|
||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||
@ -113,90 +232,94 @@ where
|
||||
fn usable_at_runtime(&self) -> bool {
|
||||
match self {
|
||||
Command::SaveCommand(cmd) => {
|
||||
<SaveCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||
}
|
||||
Command::LoadCommand(cmd) => {
|
||||
<LoadCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||
}
|
||||
Command::InputCommand(cmd) => {
|
||||
<InputCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||
}
|
||||
Command::StartCommand(cmd) => {
|
||||
<StartCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||
}
|
||||
Command::EndCommand(cmd) => {
|
||||
<EndCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||
}
|
||||
Command::VersionCommand(cmd) => {
|
||||
<VersionCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||
}
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
Command::PagingFilterCommand(cmd) => {
|
||||
<PagingFilterCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(
|
||||
<SaveCommand as IsCommand<QT, S, StdEmulatorExitHandler<SM>>>::usable_at_runtime(
|
||||
cmd,
|
||||
)
|
||||
}
|
||||
Command::LoadCommand(cmd) => {
|
||||
<LoadCommand as IsCommand<QT, S, StdEmulatorExitHandler<SM>>>::usable_at_runtime(
|
||||
cmd,
|
||||
)
|
||||
}
|
||||
Command::InputCommand(cmd) => {
|
||||
<InputCommand as IsCommand<QT, S, StdEmulatorExitHandler<SM>>>::usable_at_runtime(
|
||||
cmd,
|
||||
)
|
||||
}
|
||||
Command::StartCommand(cmd) => {
|
||||
<StartCommand as IsCommand<QT, S, StdEmulatorExitHandler<SM>>>::usable_at_runtime(
|
||||
cmd,
|
||||
)
|
||||
}
|
||||
Command::EndCommand(cmd) => {
|
||||
<EndCommand as IsCommand<QT, S, StdEmulatorExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||
}
|
||||
Command::VersionCommand(cmd) => {
|
||||
<VersionCommand as IsCommand<QT, S, StdEmulatorExitHandler<SM>>>::usable_at_runtime(
|
||||
cmd,
|
||||
)
|
||||
}
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
Command::PagingFilterCommand(cmd) => <PagingFilterCommand as IsCommand<
|
||||
QT,
|
||||
S,
|
||||
StdEmulatorExitHandler<SM>,
|
||||
>>::usable_at_runtime(cmd),
|
||||
Command::AddressRangeFilterCommand(cmd) => <AddressRangeFilterCommand as IsCommand<
|
||||
QT,
|
||||
S,
|
||||
StdEmuExitHandler<SM>,
|
||||
StdEmulatorExitHandler<SM>,
|
||||
>>::usable_at_runtime(cmd),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||
input: &S::Input,
|
||||
ret_reg: Option<Regs>,
|
||||
) -> Result<InnerHandlerResult, HandlerError> {
|
||||
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||
match self {
|
||||
Command::SaveCommand(cmd) => {
|
||||
<SaveCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
||||
cmd,
|
||||
emu,
|
||||
qemu_executor_state,
|
||||
input,
|
||||
ret_reg,
|
||||
)
|
||||
}
|
||||
Command::LoadCommand(cmd) => {
|
||||
<LoadCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
||||
cmd,
|
||||
emu,
|
||||
qemu_executor_state,
|
||||
input,
|
||||
ret_reg,
|
||||
)
|
||||
}
|
||||
Command::SaveCommand(cmd) => <SaveCommand as IsCommand<
|
||||
QT,
|
||||
S,
|
||||
StdEmulatorExitHandler<SM>,
|
||||
>>::run(
|
||||
cmd, emu, qemu_executor_state, input, ret_reg
|
||||
),
|
||||
Command::LoadCommand(cmd) => <LoadCommand as IsCommand<
|
||||
QT,
|
||||
S,
|
||||
StdEmulatorExitHandler<SM>,
|
||||
>>::run(
|
||||
cmd, emu, qemu_executor_state, input, ret_reg
|
||||
),
|
||||
Command::InputCommand(cmd) => <InputCommand as IsCommand<
|
||||
QT,
|
||||
S,
|
||||
StdEmuExitHandler<SM>,
|
||||
StdEmulatorExitHandler<SM>,
|
||||
>>::run(
|
||||
cmd, emu, qemu_executor_state, input, ret_reg
|
||||
),
|
||||
Command::StartCommand(cmd) => <StartCommand as IsCommand<
|
||||
QT,
|
||||
S,
|
||||
StdEmuExitHandler<SM>,
|
||||
StdEmulatorExitHandler<SM>,
|
||||
>>::run(
|
||||
cmd, emu, qemu_executor_state, input, ret_reg
|
||||
),
|
||||
Command::EndCommand(cmd) => <EndCommand as IsCommand<
|
||||
QT,
|
||||
S,
|
||||
StdEmulatorExitHandler<SM>,
|
||||
>>::run(
|
||||
cmd, emu, qemu_executor_state, input, ret_reg
|
||||
),
|
||||
Command::EndCommand(cmd) => {
|
||||
<EndCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
||||
cmd,
|
||||
emu,
|
||||
qemu_executor_state,
|
||||
input,
|
||||
ret_reg,
|
||||
)
|
||||
}
|
||||
Command::VersionCommand(cmd) => <VersionCommand as IsCommand<
|
||||
QT,
|
||||
S,
|
||||
StdEmuExitHandler<SM>,
|
||||
StdEmulatorExitHandler<SM>,
|
||||
>>::run(
|
||||
cmd, emu, qemu_executor_state, input, ret_reg
|
||||
),
|
||||
@ -204,12 +327,12 @@ where
|
||||
Command::PagingFilterCommand(cmd) => <PagingFilterCommand as IsCommand<
|
||||
QT,
|
||||
S,
|
||||
StdEmuExitHandler<SM>,
|
||||
StdEmulatorExitHandler<SM>,
|
||||
>>::run(
|
||||
cmd, emu, qemu_executor_state, input, ret_reg
|
||||
),
|
||||
Command::AddressRangeFilterCommand(cmd) => {
|
||||
<AddressRangeFilterCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
||||
<AddressRangeFilterCommand as IsCommand<QT, S, StdEmulatorExitHandler<SM>>>::run(
|
||||
cmd,
|
||||
emu,
|
||||
qemu_executor_state,
|
||||
@ -221,17 +344,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EmulatorMemoryChunk {
|
||||
addr: GuestAddrKind,
|
||||
size: GuestReg,
|
||||
cpu: Option<CPU>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SaveCommand;
|
||||
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for SaveCommand
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmulatorExitHandler<SM>> for SaveCommand
|
||||
where
|
||||
SM: IsSnapshotManager,
|
||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||
@ -244,7 +360,7 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||
#[cfg(emulation_mode = "systemmode")] qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||
#[cfg(not(emulation_mode = "systemmode"))] _qemu_executor_state: &mut QemuExecutorState<
|
||||
QT,
|
||||
@ -252,14 +368,14 @@ where
|
||||
>,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<InnerHandlerResult, HandlerError> {
|
||||
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||
let qemu = emu.qemu();
|
||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||
|
||||
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
|
||||
emu_exit_handler
|
||||
.set_snapshot_id(snapshot_id)
|
||||
.map_err(|_| HandlerError::MultipleSnapshotDefinition)?;
|
||||
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?;
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
{
|
||||
@ -278,14 +394,14 @@ where
|
||||
*paging_filter = QemuInstrumentationPagingFilter::AllowList(allowed_paging_ids);
|
||||
}
|
||||
|
||||
Ok(InnerHandlerResult::Continue)
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoadCommand;
|
||||
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for LoadCommand
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmulatorExitHandler<SM>> for LoadCommand
|
||||
where
|
||||
SM: IsSnapshotManager,
|
||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||
@ -298,23 +414,23 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<InnerHandlerResult, HandlerError> {
|
||||
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||
let qemu = emu.qemu();
|
||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||
|
||||
let snapshot_id = emu_exit_handler
|
||||
.snapshot_id()
|
||||
.ok_or(HandlerError::SnapshotNotFound)?;
|
||||
.ok_or(ExitHandlerError::SnapshotNotFound)?;
|
||||
|
||||
emu_exit_handler
|
||||
.snapshot_manager_borrow_mut()
|
||||
.restore(&snapshot_id, qemu)?;
|
||||
|
||||
Ok(InnerHandlerResult::Continue)
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,7 +440,7 @@ pub struct InputCommand {
|
||||
cpu: CPU,
|
||||
}
|
||||
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for InputCommand
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmulatorExitHandler<SM>> for InputCommand
|
||||
where
|
||||
SM: IsSnapshotManager,
|
||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||
@ -337,11 +453,11 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||
input: &S::Input,
|
||||
ret_reg: Option<Regs>,
|
||||
) -> Result<InnerHandlerResult, HandlerError> {
|
||||
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||
let qemu = emu.qemu();
|
||||
|
||||
let ret_value = self.location.write(qemu, input.target_bytes().as_slice());
|
||||
@ -350,7 +466,7 @@ where
|
||||
self.cpu.write_reg(reg, ret_value).unwrap();
|
||||
}
|
||||
|
||||
Ok(InnerHandlerResult::Continue)
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -359,7 +475,7 @@ pub struct StartCommand {
|
||||
input_location: EmulatorMemoryChunk,
|
||||
}
|
||||
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for StartCommand
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmulatorExitHandler<SM>> for StartCommand
|
||||
where
|
||||
SM: IsSnapshotManager,
|
||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||
@ -372,18 +488,18 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||
input: &S::Input,
|
||||
ret_reg: Option<Regs>,
|
||||
) -> Result<InnerHandlerResult, HandlerError> {
|
||||
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||
let qemu = emu.qemu();
|
||||
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
|
||||
|
||||
emu_exit_handler
|
||||
.set_snapshot_id(snapshot_id)
|
||||
.map_err(|_| HandlerError::MultipleSnapshotDefinition)?;
|
||||
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?;
|
||||
|
||||
emu_exit_handler
|
||||
.set_input_location(InputLocation::new(
|
||||
@ -401,14 +517,14 @@ where
|
||||
qemu.write_reg(reg, ret_value).unwrap();
|
||||
}
|
||||
|
||||
Ok(InnerHandlerResult::Continue)
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EndCommand(Option<ExitKind>);
|
||||
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for EndCommand
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmulatorExitHandler<SM>> for EndCommand
|
||||
where
|
||||
SM: IsSnapshotManager,
|
||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||
@ -421,29 +537,29 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<InnerHandlerResult, HandlerError> {
|
||||
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||
|
||||
let snapshot_id = emu_exit_handler
|
||||
.snapshot_id()
|
||||
.ok_or(HandlerError::SnapshotNotFound)?;
|
||||
.ok_or(ExitHandlerError::SnapshotNotFound)?;
|
||||
|
||||
emu_exit_handler
|
||||
.snapshot_manager_borrow_mut()
|
||||
.restore(&snapshot_id, emu.qemu())?;
|
||||
|
||||
Ok(InnerHandlerResult::EndOfRun(self.0.unwrap()))
|
||||
Ok(Some(ExitHandlerResult::EndOfRun(self.0.unwrap())))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VersionCommand(u64);
|
||||
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for VersionCommand
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmulatorExitHandler<SM>> for VersionCommand
|
||||
where
|
||||
SM: IsSnapshotManager,
|
||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||
@ -456,18 +572,18 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||
_emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<InnerHandlerResult, HandlerError> {
|
||||
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||
let guest_version = self.0;
|
||||
|
||||
if VERSION == guest_version {
|
||||
Ok(InnerHandlerResult::Continue)
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(HandlerError::SyncBackdoorError(
|
||||
SyncBackdoorError::VersionDifference(guest_version),
|
||||
Err(ExitHandlerError::CommandError(
|
||||
CommandError::VersionDifference(guest_version),
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -482,7 +598,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for PagingFilterCommand
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmulatorExitHandler<SM>> for PagingFilterCommand
|
||||
where
|
||||
SM: IsSnapshotManager,
|
||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||
@ -495,11 +611,11 @@ where
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||
_emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<InnerHandlerResult, HandlerError> {
|
||||
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
|
||||
|
||||
let paging_filter =
|
||||
@ -509,11 +625,11 @@ where
|
||||
|
||||
*paging_filter = self.filter.clone();
|
||||
|
||||
Ok(InnerHandlerResult::Continue)
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for AddressRangeFilterCommand
|
||||
impl<SM, QT, S> IsCommand<QT, S, StdEmulatorExitHandler<SM>> for AddressRangeFilterCommand
|
||||
where
|
||||
SM: IsSnapshotManager,
|
||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||
@ -527,11 +643,11 @@ where
|
||||
#[allow(clippy::type_complexity)] // TODO: refactor with correct type.
|
||||
fn run(
|
||||
&self,
|
||||
_emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||
_emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||
_input: &S::Input,
|
||||
_ret_reg: Option<Regs>,
|
||||
) -> Result<InnerHandlerResult, HandlerError> {
|
||||
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
|
||||
|
||||
let addr_range_filter =
|
||||
@ -541,7 +657,7 @@ where
|
||||
|
||||
*addr_range_filter = self.filter.clone();
|
||||
|
||||
Ok(InnerHandlerResult::Continue)
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -568,13 +684,13 @@ impl Display for Command {
|
||||
Command::SaveCommand(_) => write!(f, "Save VM"),
|
||||
Command::LoadCommand(_) => write!(f, "Reload VM"),
|
||||
Command::InputCommand(input_command) => {
|
||||
write!(f, "Set fuzzing input @{}", input_command.location.addr)
|
||||
write!(f, "Set fuzzing input @{}", input_command.location.addr())
|
||||
}
|
||||
Command::StartCommand(start_command) => {
|
||||
write!(
|
||||
f,
|
||||
"Start fuzzing with input @{}",
|
||||
start_command.input_location.addr
|
||||
start_command.input_location.addr()
|
||||
)
|
||||
}
|
||||
Command::EndCommand(end_command) => write!(f, "Exit of kind {:?}", end_command.0),
|
||||
@ -613,66 +729,13 @@ impl InputCommand {
|
||||
}
|
||||
}
|
||||
|
||||
impl EmulatorMemoryChunk {
|
||||
#[must_use]
|
||||
pub fn phys(addr: GuestPhysAddr, size: GuestReg, cpu: Option<CPU>) -> Self {
|
||||
Self {
|
||||
addr: GuestAddrKind::Physical(addr),
|
||||
size,
|
||||
cpu,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn virt(addr: GuestVirtAddr, size: GuestReg, cpu: CPU) -> Self {
|
||||
Self {
|
||||
addr: GuestAddrKind::Virtual(addr),
|
||||
size,
|
||||
cpu: Some(cpu),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bytes effectively written.
|
||||
#[must_use]
|
||||
pub fn write(&self, qemu: &Qemu, input: &[u8]) -> GuestReg {
|
||||
let max_len: usize = self.size.try_into().unwrap();
|
||||
|
||||
let input_sliced = if input.len() > max_len {
|
||||
&input[0..max_len]
|
||||
} else {
|
||||
input
|
||||
};
|
||||
|
||||
match self.addr {
|
||||
GuestAddrKind::Physical(hwaddr) => unsafe {
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
{
|
||||
// For now the default behaviour is to fall back to virtual addresses
|
||||
qemu.write_mem(hwaddr.try_into().unwrap(), input_sliced);
|
||||
}
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
{
|
||||
qemu.write_phys_mem(hwaddr, input_sliced);
|
||||
}
|
||||
},
|
||||
GuestAddrKind::Virtual(vaddr) => unsafe {
|
||||
self.cpu
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.write_mem(vaddr.try_into().unwrap(), input_sliced);
|
||||
},
|
||||
};
|
||||
|
||||
input_sliced.len().try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for InputCommand {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} (0x{:x} max nb bytes)",
|
||||
self.location.addr, self.location.size
|
||||
self.location.addr(),
|
||||
self.location.size()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,22 +1,15 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::{c_void, CStr, CString},
|
||||
fmt::Debug,
|
||||
mem::MaybeUninit,
|
||||
ptr::null_mut,
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
};
|
||||
|
||||
use libafl::state::{HasExecutions, State};
|
||||
use libafl_qemu_sys::{
|
||||
libafl_load_qemu_snapshot, libafl_qemu_current_paging_id, libafl_save_qemu_snapshot,
|
||||
qemu_cleanup, qemu_main_loop, vm_start, GuestAddr, GuestPhysAddr, GuestVirtAddr,
|
||||
};
|
||||
use libafl_qemu_sys::GuestPhysAddr;
|
||||
|
||||
use crate::{
|
||||
emu::{libafl_page_from_addr, IsSnapshotManager},
|
||||
EmuExitHandler, Emulator, MemAccessInfo, Qemu, QemuExitReason, QemuExitReasonError,
|
||||
QemuHelperTuple, SnapshotId, SnapshotManagerError, CPU,
|
||||
emu::IsSnapshotManager, DeviceSnapshotFilter, Emulator, EmulatorExitHandler, Qemu,
|
||||
QemuHelperTuple, SnapshotId, SnapshotManagerError,
|
||||
};
|
||||
|
||||
impl SnapshotId {
|
||||
@ -25,9 +18,7 @@ impl SnapshotId {
|
||||
|
||||
let unique_id = UNIQUE_ID.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
SnapshotId {
|
||||
id: unique_id.clone(),
|
||||
}
|
||||
SnapshotId { id: unique_id }
|
||||
}
|
||||
|
||||
fn inner(&self) -> u64 {
|
||||
@ -84,7 +75,7 @@ impl FastSnapshotManager {
|
||||
}
|
||||
|
||||
pub unsafe fn get(&self, id: &SnapshotId) -> FastSnapshotPtr {
|
||||
self.snapshots.get(id).unwrap().clone()
|
||||
*self.snapshots.get(id).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,18 +127,18 @@ impl IsSnapshotManager for FastSnapshotManager {
|
||||
snapshot_id: &SnapshotId,
|
||||
qemu: &Qemu,
|
||||
) -> Result<(), SnapshotManagerError> {
|
||||
let fast_snapshot_ptr = self
|
||||
let fast_snapshot_ptr = *self
|
||||
.snapshots
|
||||
.get(snapshot_id)
|
||||
.ok_or(SnapshotManagerError::SnapshotIdNotFound(
|
||||
snapshot_id.clone(),
|
||||
))?
|
||||
.clone();
|
||||
.ok_or(SnapshotManagerError::SnapshotIdNotFound(*snapshot_id))?;
|
||||
|
||||
qemu.restore_fast_snapshot(fast_snapshot_ptr);
|
||||
unsafe {
|
||||
qemu.restore_fast_snapshot(fast_snapshot_ptr);
|
||||
}
|
||||
|
||||
if self.check_memory_consistency {
|
||||
let nb_inconsistencies = qemu.check_fast_snapshot_memory_consistency(fast_snapshot_ptr);
|
||||
let nb_inconsistencies =
|
||||
unsafe { qemu.check_fast_snapshot_memory_consistency(fast_snapshot_ptr) };
|
||||
|
||||
if nb_inconsistencies > 0 {
|
||||
return Err(SnapshotManagerError::MemoryInconsistencies(
|
||||
@ -160,251 +151,11 @@ impl IsSnapshotManager for FastSnapshotManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DeviceSnapshotFilter {
|
||||
All,
|
||||
AllowList(Vec<String>),
|
||||
DenyList(Vec<String>),
|
||||
}
|
||||
|
||||
impl DeviceSnapshotFilter {
|
||||
fn enum_id(&self) -> libafl_qemu_sys::DeviceSnapshotKind {
|
||||
match self {
|
||||
DeviceSnapshotFilter::All => libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALL,
|
||||
DeviceSnapshotFilter::AllowList(_) => {
|
||||
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALLOWLIST
|
||||
}
|
||||
DeviceSnapshotFilter::DenyList(_) => {
|
||||
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_DENYLIST
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn devices(&self, v: &mut Vec<*mut i8>) -> *mut *mut i8 {
|
||||
v.clear();
|
||||
match self {
|
||||
DeviceSnapshotFilter::All => null_mut(),
|
||||
DeviceSnapshotFilter::AllowList(l) | DeviceSnapshotFilter::DenyList(l) => {
|
||||
for name in l {
|
||||
v.push(name.as_bytes().as_ptr() as *mut i8);
|
||||
}
|
||||
v.push(core::ptr::null_mut());
|
||||
v.as_mut_ptr()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) extern "C" fn qemu_cleanup_atexit() {
|
||||
unsafe {
|
||||
qemu_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
impl CPU {
|
||||
#[must_use]
|
||||
pub fn get_phys_addr(&self, vaddr: GuestAddr) -> Option<GuestPhysAddr> {
|
||||
unsafe {
|
||||
let page = libafl_page_from_addr(vaddr);
|
||||
let mut attrs = MaybeUninit::<libafl_qemu_sys::MemTxAttrs>::uninit();
|
||||
let paddr = libafl_qemu_sys::cpu_get_phys_page_attrs_debug(
|
||||
self.ptr,
|
||||
page as GuestVirtAddr,
|
||||
attrs.as_mut_ptr(),
|
||||
);
|
||||
if paddr == (-1i64 as GuestPhysAddr) {
|
||||
None
|
||||
} else {
|
||||
Some(paddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_phys_addr_tlb(
|
||||
&self,
|
||||
vaddr: GuestAddr,
|
||||
info: MemAccessInfo,
|
||||
is_store: bool,
|
||||
) -> Option<GuestPhysAddr> {
|
||||
unsafe {
|
||||
let pminfo = libafl_qemu_sys::make_plugin_meminfo(
|
||||
info.oi,
|
||||
if is_store {
|
||||
libafl_qemu_sys::qemu_plugin_mem_rw_QEMU_PLUGIN_MEM_W
|
||||
} else {
|
||||
libafl_qemu_sys::qemu_plugin_mem_rw_QEMU_PLUGIN_MEM_R
|
||||
},
|
||||
);
|
||||
let phwaddr = libafl_qemu_sys::qemu_plugin_get_hwaddr(pminfo, vaddr as GuestVirtAddr);
|
||||
if phwaddr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(libafl_qemu_sys::qemu_plugin_hwaddr_phys_addr(phwaddr) as GuestPhysAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn current_paging_id(&self) -> Option<GuestPhysAddr> {
|
||||
let paging_id = unsafe { libafl_qemu_current_paging_id(self.ptr) };
|
||||
|
||||
if paging_id == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(paging_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a value to a guest address.
|
||||
///
|
||||
/// # Safety
|
||||
/// This will write to a translated guest address (using `g2h`).
|
||||
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||
/// This may only be safely used for valid guest addresses!
|
||||
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
||||
// TODO use gdbstub's target_cpu_memory_rw_debug
|
||||
libafl_qemu_sys::cpu_memory_rw_debug(
|
||||
self.ptr,
|
||||
addr as GuestVirtAddr,
|
||||
buf.as_ptr() as *mut _,
|
||||
buf.len(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Read a value from a guest address.
|
||||
///
|
||||
/// # Safety
|
||||
/// This will read from a translated guest address (using `g2h`).
|
||||
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||
/// This may only be safely used for valid guest addresses!
|
||||
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
||||
// TODO use gdbstub's target_cpu_memory_rw_debug
|
||||
libafl_qemu_sys::cpu_memory_rw_debug(
|
||||
self.ptr,
|
||||
addr as GuestVirtAddr,
|
||||
buf.as_mut_ptr() as *mut _,
|
||||
buf.len(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn page_size(&self) -> usize {
|
||||
unsafe { libafl_qemu_sys::qemu_target_page_size() }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
impl Qemu {
|
||||
/// Write a value to a phsical guest address, including ROM areas.
|
||||
pub unsafe fn write_phys_mem(&self, paddr: GuestPhysAddr, buf: &[u8]) {
|
||||
libafl_qemu_sys::cpu_physical_memory_rw(
|
||||
paddr,
|
||||
buf.as_ptr() as *mut _,
|
||||
buf.len() as u64,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Read a value from a physical guest address.
|
||||
pub unsafe fn read_phys_mem(&self, paddr: GuestPhysAddr, buf: &mut [u8]) {
|
||||
libafl_qemu_sys::cpu_physical_memory_rw(
|
||||
paddr,
|
||||
buf.as_mut_ptr() as *mut _,
|
||||
buf.len() as u64,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
/// This function will run the emulator until the next breakpoint / sync exit, or until finish.
|
||||
/// It is a low-level function and simply kicks QEMU.
|
||||
/// # Safety
|
||||
///
|
||||
/// Should, in general, be safe to call.
|
||||
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
||||
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitReasonError> {
|
||||
vm_start();
|
||||
qemu_main_loop();
|
||||
|
||||
self.post_run()
|
||||
}
|
||||
|
||||
pub fn save_snapshot(&self, name: &str, sync: bool) {
|
||||
let s = CString::new(name).expect("Invalid snapshot name");
|
||||
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||
}
|
||||
|
||||
pub fn load_snapshot(&self, name: &str, sync: bool) {
|
||||
let s = CString::new(name).expect("Invalid snapshot name");
|
||||
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn create_fast_snapshot(&self, track: bool) -> FastSnapshotPtr {
|
||||
unsafe {
|
||||
libafl_qemu_sys::syx_snapshot_new(
|
||||
track,
|
||||
true,
|
||||
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALL,
|
||||
null_mut(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn create_fast_snapshot_filter(
|
||||
&self,
|
||||
track: bool,
|
||||
device_filter: &DeviceSnapshotFilter,
|
||||
) -> FastSnapshotPtr {
|
||||
let mut v = vec![];
|
||||
unsafe {
|
||||
libafl_qemu_sys::syx_snapshot_new(
|
||||
track,
|
||||
true,
|
||||
device_filter.enum_id(),
|
||||
device_filter.devices(&mut v),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn restore_fast_snapshot(&self, snapshot: FastSnapshotPtr) {
|
||||
unsafe { libafl_qemu_sys::syx_snapshot_root_restore(snapshot) }
|
||||
}
|
||||
|
||||
pub fn check_fast_snapshot_memory_consistency(&self, snapshot: FastSnapshotPtr) -> u64 {
|
||||
unsafe { libafl_qemu_sys::syx_snapshot_check_memory_consistency(snapshot) }
|
||||
}
|
||||
|
||||
pub fn list_devices(&self) -> Vec<String> {
|
||||
let mut r = vec![];
|
||||
unsafe {
|
||||
let devices = libafl_qemu_sys::device_list_all();
|
||||
if devices.is_null() {
|
||||
return r;
|
||||
}
|
||||
|
||||
let mut ptr = devices;
|
||||
while !(*ptr).is_null() {
|
||||
let c_str: &CStr = CStr::from_ptr(*ptr);
|
||||
let name = c_str.to_str().unwrap().to_string();
|
||||
r.push(name);
|
||||
|
||||
ptr = ptr.add(1);
|
||||
}
|
||||
|
||||
libc::free(devices as *mut c_void);
|
||||
r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<QT, S, E> Emulator<QT, S, E>
|
||||
where
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State + HasExecutions,
|
||||
E: EmuExitHandler<QT, S>,
|
||||
E: EmulatorExitHandler<QT, S>,
|
||||
{
|
||||
/// Write a value to a phsical guest address, including ROM areas.
|
||||
pub unsafe fn write_phys_mem(&self, paddr: GuestPhysAddr, buf: &[u8]) {
|
||||
@ -438,11 +189,11 @@ where
|
||||
self.qemu.create_fast_snapshot_filter(track, device_filter)
|
||||
}
|
||||
|
||||
pub fn restore_fast_snapshot(&self, snapshot: FastSnapshotPtr) {
|
||||
pub unsafe fn restore_fast_snapshot(&self, snapshot: FastSnapshotPtr) {
|
||||
self.qemu.restore_fast_snapshot(snapshot)
|
||||
}
|
||||
|
||||
pub fn check_fast_snapshot_memory_consistency(&self, snapshot: FastSnapshotPtr) -> u64 {
|
||||
pub unsafe fn check_fast_snapshot_memory_consistency(&self, snapshot: FastSnapshotPtr) -> u64 {
|
||||
self.qemu.check_fast_snapshot_memory_consistency(snapshot)
|
||||
}
|
||||
|
||||
|
@ -1,365 +1,16 @@
|
||||
use core::{mem::MaybeUninit, ptr::copy_nonoverlapping};
|
||||
use std::{cell::OnceCell, slice::from_raw_parts, str::from_utf8_unchecked};
|
||||
|
||||
use libafl_qemu_sys::{
|
||||
exec_path, free_self_maps, guest_base, libafl_dump_core_hook, libafl_force_dfl, libafl_get_brk,
|
||||
libafl_load_addr, libafl_maps_first, libafl_maps_next, libafl_qemu_run, libafl_set_brk,
|
||||
mmap_next_start, pageflags_get_root, read_self_maps, strlen, GuestAddr, GuestUsize,
|
||||
IntervalTreeNode, IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess,
|
||||
};
|
||||
use libc::c_int;
|
||||
#[cfg(feature = "python")]
|
||||
use pyo3::prelude::*;
|
||||
use libafl_qemu_sys::{GuestAddr, MmapPerms, VerifyAccess};
|
||||
|
||||
use crate::{
|
||||
emu::{HasExecutions, State},
|
||||
sync_exit::SyncBackdoorError,
|
||||
EmuExitHandler, Emulator, HookData, NewThreadHookId, PostSyscallHookId, PreSyscallHookId, Qemu,
|
||||
QemuExitReason, QemuExitReasonError, QemuHelperTuple, SyscallHookResult, CPU,
|
||||
Emulator, EmulatorExitHandler, GuestMaps, HookData, NewThreadHookId, PostSyscallHookId,
|
||||
PreSyscallHookId, QemuHelperTuple, SyscallHookResult,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HandlerError {
|
||||
EmuExitReasonError(QemuExitReasonError),
|
||||
SyncBackdoorError(SyncBackdoorError),
|
||||
MultipleInputDefinition,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
||||
pub struct GuestMaps {
|
||||
self_maps_root: *mut IntervalTreeRoot,
|
||||
pageflags_node: *mut IntervalTreeNode,
|
||||
}
|
||||
|
||||
// Consider a private new only for Emulator
|
||||
impl GuestMaps {
|
||||
#[must_use]
|
||||
pub(crate) fn new() -> Self {
|
||||
unsafe {
|
||||
let pageflags_root = pageflags_get_root();
|
||||
let self_maps_root = read_self_maps();
|
||||
let pageflags_first = libafl_maps_first(pageflags_root);
|
||||
Self {
|
||||
self_maps_root,
|
||||
pageflags_node: pageflags_first,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for GuestMaps {
|
||||
type Item = MapInfo;
|
||||
|
||||
#[allow(clippy::uninit_assumed_init)]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
let mut ret = MaybeUninit::uninit();
|
||||
|
||||
self.pageflags_node =
|
||||
libafl_maps_next(self.pageflags_node, self.self_maps_root, ret.as_mut_ptr());
|
||||
|
||||
let ret = ret.assume_init();
|
||||
|
||||
if ret.is_valid {
|
||||
Some(ret.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[pymethods]
|
||||
impl GuestMaps {
|
||||
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
|
||||
slf
|
||||
}
|
||||
fn __next__(mut slf: PyRefMut<Self>) -> Option<PyObject> {
|
||||
Python::with_gil(|py| slf.next().map(|x| x.into_py(py)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GuestMaps {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
free_self_maps(self.self_maps_root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPU {
|
||||
/// Write a value to a guest address.
|
||||
///
|
||||
/// # Safety
|
||||
/// This will write to a translated guest address (using `g2h`).
|
||||
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||
/// This may only be safely used for valid guest addresses!
|
||||
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
||||
let host_addr = Qemu::get().unwrap().g2h(addr);
|
||||
copy_nonoverlapping(buf.as_ptr(), host_addr, buf.len());
|
||||
}
|
||||
|
||||
/// Read a value from a guest address.
|
||||
///
|
||||
/// # Safety
|
||||
/// This will read from a translated guest address (using `g2h`).
|
||||
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||
/// This may only be safely used for valid guest addresses!
|
||||
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
||||
let host_addr = Qemu::get().unwrap().g2h(addr);
|
||||
copy_nonoverlapping(host_addr, buf.as_mut_ptr(), buf.len());
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn page_size(&self) -> usize {
|
||||
thread_local! {
|
||||
static PAGE_SIZE: OnceCell<usize> = const { OnceCell::new() };
|
||||
}
|
||||
|
||||
PAGE_SIZE.with(|s| {
|
||||
*s.get_or_init(|| {
|
||||
unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) }
|
||||
.try_into()
|
||||
.expect("Invalid page size")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
impl Qemu {
|
||||
#[must_use]
|
||||
pub fn mappings(&self) -> GuestMaps {
|
||||
GuestMaps::new()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn g2h<T>(&self, addr: GuestAddr) -> *mut T {
|
||||
unsafe { (addr as usize + guest_base) as *mut T }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn h2g<T>(&self, addr: *const T) -> GuestAddr {
|
||||
unsafe { (addr as usize - guest_base) as GuestAddr }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn access_ok(&self, kind: VerifyAccess, addr: GuestAddr, size: usize) -> bool {
|
||||
self.current_cpu()
|
||||
.unwrap_or_else(|| self.cpu_from_index(0))
|
||||
.access_ok(kind, addr, size)
|
||||
}
|
||||
|
||||
pub fn force_dfl(&self) {
|
||||
unsafe {
|
||||
libafl_force_dfl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// This function will run the emulator until the next breakpoint, or until finish.
|
||||
/// # Safety
|
||||
///
|
||||
/// Should, in general, be safe to call.
|
||||
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
||||
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitReasonError> {
|
||||
libafl_qemu_run();
|
||||
|
||||
self.post_run()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn binary_path<'a>(&self) -> &'a str {
|
||||
unsafe { from_utf8_unchecked(from_raw_parts(exec_path, strlen(exec_path))) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn load_addr(&self) -> GuestAddr {
|
||||
unsafe { libafl_load_addr() as GuestAddr }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_brk(&self) -> GuestAddr {
|
||||
unsafe { libafl_get_brk() as GuestAddr }
|
||||
}
|
||||
|
||||
pub fn set_brk(&self, brk: GuestAddr) {
|
||||
unsafe { libafl_set_brk(brk.into()) };
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_mmap_start(&self) -> GuestAddr {
|
||||
unsafe { mmap_next_start }
|
||||
}
|
||||
|
||||
pub fn set_mmap_start(&self, start: GuestAddr) {
|
||||
unsafe { mmap_next_start = start };
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
fn mmap(
|
||||
self,
|
||||
addr: GuestAddr,
|
||||
size: usize,
|
||||
perms: MmapPerms,
|
||||
flags: c_int,
|
||||
) -> Result<GuestAddr, ()> {
|
||||
let res = unsafe {
|
||||
libafl_qemu_sys::target_mmap(addr, size as GuestUsize, perms.into(), flags, -1, 0)
|
||||
};
|
||||
if res <= 0 {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(res as GuestAddr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_private(
|
||||
&self,
|
||||
addr: GuestAddr,
|
||||
size: usize,
|
||||
perms: MmapPerms,
|
||||
) -> Result<GuestAddr, String> {
|
||||
self.mmap(addr, size, perms, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS)
|
||||
.map_err(|()| format!("Failed to map {addr}"))
|
||||
.map(|addr| addr as GuestAddr)
|
||||
}
|
||||
|
||||
pub fn map_fixed(
|
||||
&self,
|
||||
addr: GuestAddr,
|
||||
size: usize,
|
||||
perms: MmapPerms,
|
||||
) -> Result<GuestAddr, String> {
|
||||
self.mmap(
|
||||
addr,
|
||||
size,
|
||||
perms,
|
||||
libc::MAP_FIXED | libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
|
||||
)
|
||||
.map_err(|()| format!("Failed to map {addr}"))
|
||||
.map(|addr| addr as GuestAddr)
|
||||
}
|
||||
|
||||
pub fn mprotect(&self, addr: GuestAddr, size: usize, perms: MmapPerms) -> Result<(), String> {
|
||||
let res = unsafe {
|
||||
libafl_qemu_sys::target_mprotect(addr.into(), size as GuestUsize, perms.into())
|
||||
};
|
||||
if res == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Failed to mprotect {addr}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> {
|
||||
if unsafe { libafl_qemu_sys::target_munmap(addr.into(), size as GuestUsize) } == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Failed to unmap {addr}"))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn add_pre_syscall_hook<T: Into<HookData>>(
|
||||
&self,
|
||||
data: T,
|
||||
callback: extern "C" fn(
|
||||
T,
|
||||
i32,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
) -> SyscallHookResult,
|
||||
) -> PreSyscallHookId {
|
||||
unsafe {
|
||||
let data: u64 = data.into().0;
|
||||
let callback: extern "C" fn(
|
||||
u64,
|
||||
i32,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
) -> libafl_qemu_sys::syshook_ret = core::mem::transmute(callback);
|
||||
let num = libafl_qemu_sys::libafl_add_pre_syscall_hook(Some(callback), data);
|
||||
PreSyscallHookId(num)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn add_post_syscall_hook<T: Into<HookData>>(
|
||||
&self,
|
||||
data: T,
|
||||
callback: extern "C" fn(
|
||||
T,
|
||||
GuestAddr,
|
||||
i32,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
) -> GuestAddr,
|
||||
) -> PostSyscallHookId {
|
||||
unsafe {
|
||||
let data: u64 = data.into().0;
|
||||
let callback: extern "C" fn(
|
||||
u64,
|
||||
GuestAddr,
|
||||
i32,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
) -> GuestAddr = core::mem::transmute(callback);
|
||||
let num = libafl_qemu_sys::libafl_add_post_syscall_hook(Some(callback), data);
|
||||
PostSyscallHookId(num)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_new_thread_hook<T: Into<HookData>>(
|
||||
&self,
|
||||
data: T,
|
||||
callback: extern "C" fn(T, tid: u32) -> bool,
|
||||
) -> NewThreadHookId {
|
||||
unsafe {
|
||||
let data: u64 = data.into().0;
|
||||
let callback: extern "C" fn(u64, u32) -> bool = core::mem::transmute(callback);
|
||||
let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data);
|
||||
NewThreadHookId(num)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn set_crash_hook(&self, callback: extern "C" fn(i32)) {
|
||||
unsafe {
|
||||
libafl_dump_core_hook = callback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<QT, S, E> Emulator<QT, S, E>
|
||||
where
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State + HasExecutions,
|
||||
E: EmuExitHandler<QT, S>,
|
||||
E: EmulatorExitHandler<QT, S>,
|
||||
{
|
||||
/// This function gets the memory mappings from the emulator.
|
||||
#[must_use]
|
||||
|
@ -8,7 +8,9 @@ use core::{
|
||||
};
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
use libafl::{events::EventManager, executors::InProcessForkExecutor, state::HasLastReportTime};
|
||||
use libafl::{
|
||||
events::EventManager, executors::InProcessForkExecutor, state::HasLastReportTime, HasMetadata,
|
||||
};
|
||||
use libafl::{
|
||||
events::{EventFirer, EventRestarter},
|
||||
executors::{
|
||||
@ -20,7 +22,7 @@ use libafl::{
|
||||
fuzzer::HasObjective,
|
||||
observers::{ObserversTuple, UsesObservers},
|
||||
state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState},
|
||||
Error, HasMetadata,
|
||||
Error,
|
||||
};
|
||||
#[cfg(feature = "fork")]
|
||||
use libafl_bolts::shmem::ShMemProvider;
|
||||
|
@ -17,12 +17,12 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use rangemap::RangeMap;
|
||||
|
||||
use crate::{
|
||||
emu::{EmuError, MemAccessInfo, SyscallHookResult},
|
||||
helpers::{
|
||||
calls::FullBacktraceCollector, HasInstrumentationFilter, IsFilter, QemuHelper,
|
||||
QemuHelperTuple, QemuInstrumentationAddressRangeFilter,
|
||||
},
|
||||
hooks::{Hook, QemuHooks},
|
||||
qemu::{MemAccessInfo, QemuInitError, SyscallHookResult},
|
||||
snapshot::QemuSnapshotHelper,
|
||||
sys::TCGTemp,
|
||||
GuestAddr, Qemu, Regs,
|
||||
@ -668,7 +668,7 @@ static mut ASAN_INITED: bool = false;
|
||||
pub fn init_qemu_with_asan(
|
||||
args: &mut Vec<String>,
|
||||
env: &mut [(String, String)],
|
||||
) -> Result<(Qemu, Pin<Box<AsanGiovese>>), EmuError> {
|
||||
) -> Result<(Qemu, Pin<Box<AsanGiovese>>), QemuInitError> {
|
||||
let current = env::current_exe().unwrap();
|
||||
let asan_lib = fs::canonicalize(current)
|
||||
.unwrap()
|
||||
|
@ -12,12 +12,12 @@ use libafl::{inputs::UsesInput, HasMetadata};
|
||||
#[cfg(not(feature = "clippy"))]
|
||||
use crate::sys::libafl_tcg_gen_asan;
|
||||
use crate::{
|
||||
emu::{EmuError, MemAccessInfo, Qemu},
|
||||
helpers::{
|
||||
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
||||
QemuInstrumentationAddressRangeFilter,
|
||||
},
|
||||
hooks::{Hook, QemuHooks},
|
||||
qemu::{MemAccessInfo, Qemu, QemuInitError},
|
||||
sys::TCGTemp,
|
||||
GuestAddr, MapInfo,
|
||||
};
|
||||
@ -27,7 +27,7 @@ static mut ASAN_GUEST_INITED: bool = false;
|
||||
pub fn init_qemu_with_asan_guest(
|
||||
args: &mut Vec<String>,
|
||||
env: &mut [(String, String)],
|
||||
) -> Result<(Qemu, String), EmuError> {
|
||||
) -> Result<(Qemu, String), QemuInitError> {
|
||||
let current = env::current_exe().unwrap();
|
||||
let asan_lib = fs::canonicalize(current)
|
||||
.unwrap()
|
||||
|
@ -12,12 +12,12 @@ use thread_local::ThreadLocal;
|
||||
|
||||
use crate::{
|
||||
capstone,
|
||||
emu::ArchExtras,
|
||||
helpers::{
|
||||
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
||||
QemuInstrumentationAddressRangeFilter,
|
||||
},
|
||||
hooks::{Hook, QemuHooks},
|
||||
qemu::ArchExtras,
|
||||
Qemu,
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,7 @@ pub use libafl_targets::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
use crate::{capstone, emu::ArchExtras, CallingConvention, Qemu};
|
||||
use crate::{capstone, qemu::ArchExtras, CallingConvention, Qemu};
|
||||
use crate::{
|
||||
helpers::{
|
||||
hash_me, HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
||||
|
@ -539,8 +539,7 @@ where
|
||||
let paging_id = hooks
|
||||
.qemu()
|
||||
.current_cpu()
|
||||
.map(|cpu| cpu.current_paging_id())
|
||||
.flatten();
|
||||
.and_then(|cpu| cpu.current_paging_id());
|
||||
|
||||
if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) {
|
||||
return None;
|
||||
@ -609,8 +608,7 @@ where
|
||||
let paging_id = hooks
|
||||
.qemu()
|
||||
.current_cpu()
|
||||
.map(|cpu| cpu.current_paging_id())
|
||||
.flatten();
|
||||
.and_then(|cpu| cpu.current_paging_id());
|
||||
|
||||
if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) {
|
||||
return None;
|
||||
@ -676,8 +674,7 @@ where
|
||||
let paging_id = hooks
|
||||
.qemu()
|
||||
.current_cpu()
|
||||
.map(|cpu| cpu.current_paging_id())
|
||||
.flatten();
|
||||
.and_then(|cpu| cpu.current_paging_id());
|
||||
|
||||
if !h.must_instrument(pc, paging_id) {
|
||||
return None;
|
||||
|
@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[cfg(not(cpu_target = "hexagon"))]
|
||||
use crate::SYS_execve;
|
||||
use crate::{
|
||||
elf::EasyElf, emu::ArchExtras, CallingConvention, Hook, Qemu, QemuHelper, QemuHelperTuple,
|
||||
elf::EasyElf, qemu::ArchExtras, CallingConvention, Hook, Qemu, QemuHelper, QemuHelperTuple,
|
||||
QemuHooks, SyscallHookResult,
|
||||
};
|
||||
#[cfg(cpu_target = "hexagon")]
|
||||
|
@ -1,5 +1,5 @@
|
||||
use core::{fmt::Debug, ops::Range};
|
||||
use std::{collections::HashSet, hash::BuildHasher};
|
||||
use std::{cell::UnsafeCell, collections::HashSet, hash::BuildHasher};
|
||||
|
||||
use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
|
||||
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
|
||||
@ -137,6 +137,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> HasInstrumentationFilter<(), S> for ()
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
fn filter(&self) -> &() {
|
||||
self
|
||||
}
|
||||
|
||||
fn filter_mut(&mut self) -> &mut () {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Head, F, S> HasInstrumentationFilter<F, S> for (Head, ())
|
||||
where
|
||||
Head: QemuHelper<S> + HasInstrumentationFilter<F, S>,
|
||||
@ -272,6 +285,31 @@ pub trait StdInstrumentationFilter<S: UsesInput>:
|
||||
{
|
||||
}
|
||||
|
||||
static mut EMPTY_ADDRESS_FILTER: UnsafeCell<QemuInstrumentationAddressRangeFilter> =
|
||||
UnsafeCell::new(QemuFilterList::None);
|
||||
static mut EMPTY_PAGING_FILTER: UnsafeCell<QemuInstrumentationPagingFilter> =
|
||||
UnsafeCell::new(QemuFilterList::None);
|
||||
|
||||
impl<S> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S> for () {
|
||||
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
||||
&QemuFilterList::None
|
||||
}
|
||||
|
||||
fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter {
|
||||
unsafe { EMPTY_ADDRESS_FILTER.get_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> HasInstrumentationFilter<QemuInstrumentationPagingFilter, S> for () {
|
||||
fn filter(&self) -> &QemuInstrumentationPagingFilter {
|
||||
&QemuFilterList::None
|
||||
}
|
||||
|
||||
fn filter_mut(&mut self) -> &mut QemuInstrumentationPagingFilter {
|
||||
unsafe { EMPTY_PAGING_FILTER.get_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
impl<Head, S> StdInstrumentationFilter<S> for (Head, ())
|
||||
where
|
||||
@ -290,12 +328,26 @@ where
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
impl<S> StdInstrumentationFilter<S> for () where S: UsesInput {}
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
impl<S> StdInstrumentationFilter<S> for () where S: UsesInput {}
|
||||
|
||||
pub trait IsFilter: Debug {
|
||||
type FilterParameter;
|
||||
|
||||
fn allowed(&self, filter_parameter: Self::FilterParameter) -> bool;
|
||||
}
|
||||
|
||||
impl IsFilter for () {
|
||||
type FilterParameter = ();
|
||||
|
||||
fn allowed(&self, _filter_parameter: Self::FilterParameter) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IsAddressFilter: IsFilter<FilterParameter = GuestAddr> {}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
|
@ -25,9 +25,9 @@ use crate::SYS_mmap2;
|
||||
use crate::SYS_newfstatat;
|
||||
use crate::{
|
||||
asan::QemuAsanHelper,
|
||||
emu::SyscallHookResult,
|
||||
helpers::{QemuHelper, QemuHelperTuple},
|
||||
hooks::{Hook, QemuHooks},
|
||||
qemu::SyscallHookResult,
|
||||
Qemu, SYS_fstat, SYS_fstatfs, SYS_futex, SYS_getrandom, SYS_mprotect, SYS_mremap, SYS_munmap,
|
||||
SYS_pread64, SYS_read, SYS_readlinkat, SYS_statfs,
|
||||
};
|
||||
|
@ -19,10 +19,10 @@ use libafl::{
|
||||
};
|
||||
use libafl_qemu_sys::{CPUArchStatePtr, FatPtr, GuestAddr, GuestUsize};
|
||||
|
||||
pub use crate::emu::SyscallHookResult;
|
||||
pub use crate::qemu::SyscallHookResult;
|
||||
use crate::{
|
||||
emu::{MemAccessInfo, Qemu, SKIP_EXEC_HOOK},
|
||||
helpers::QemuHelperTuple,
|
||||
qemu::{MemAccessInfo, Qemu, SKIP_EXEC_HOOK},
|
||||
sys::TCGTemp,
|
||||
BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, HookId, InstructionHookId, ReadHookId,
|
||||
WriteHookId,
|
||||
|
@ -1,4 +1,3 @@
|
||||
#![cfg_attr(nightly, feature(used_with_arg))]
|
||||
//! Welcome to `LibAFL` QEMU
|
||||
//!
|
||||
//! __Warning__: The documentation is built by default for `x86_64` in `usermode`. To access the documentation of other architectures or `systemmode`, the documentation must be rebuilt with the right features.
|
||||
@ -49,6 +48,9 @@ pub use executor::QemuExecutor;
|
||||
#[cfg(feature = "fork")]
|
||||
pub use executor::QemuForkExecutor;
|
||||
|
||||
pub mod qemu;
|
||||
pub use qemu::*;
|
||||
|
||||
pub mod emu;
|
||||
pub use emu::*;
|
||||
|
||||
@ -89,16 +91,19 @@ pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
|
||||
m.add_submodule(regsm)?;
|
||||
|
||||
let mmapm = PyModule::new(py, "mmap")?;
|
||||
for r in emu::MmapPerms::iter() {
|
||||
for r in sys::MmapPerms::iter() {
|
||||
let v: i32 = r.into();
|
||||
mmapm.add(&format!("{r:?}"), v)?;
|
||||
}
|
||||
m.add_submodule(mmapm)?;
|
||||
|
||||
m.add_class::<emu::MapInfo>()?;
|
||||
m.add_class::<emu::GuestMaps>()?;
|
||||
m.add_class::<emu::SyscallHookResult>()?;
|
||||
m.add_class::<emu::pybind::Qemu>()?;
|
||||
m.add_class::<sys::MapInfo>()?;
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
m.add_class::<qemu::GuestMaps>()?;
|
||||
|
||||
m.add_class::<qemu::SyscallHookResult>()?;
|
||||
m.add_class::<qemu::pybind::Qemu>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
1216
libafl_qemu/src/qemu/mod.rs
Normal file
1216
libafl_qemu/src/qemu/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
440
libafl_qemu/src/qemu/systemmode.rs
Normal file
440
libafl_qemu/src/qemu/systemmode.rs
Normal file
@ -0,0 +1,440 @@
|
||||
use std::{
|
||||
ffi::{c_void, CStr, CString},
|
||||
marker::PhantomData,
|
||||
mem::MaybeUninit,
|
||||
ptr::null_mut,
|
||||
slice,
|
||||
};
|
||||
|
||||
use bytes_utils::SegmentedBuf;
|
||||
use libafl_qemu_sys::{
|
||||
libafl_load_qemu_snapshot, libafl_page_from_addr, libafl_qemu_current_paging_id,
|
||||
libafl_save_qemu_snapshot, qemu_cleanup, qemu_main_loop, vm_start, GuestAddr, GuestPhysAddr,
|
||||
GuestUsize, GuestVirtAddr,
|
||||
};
|
||||
use num_traits::Zero;
|
||||
|
||||
use crate::{
|
||||
EmulatorMemoryChunk, FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuExitError,
|
||||
QemuExitReason, CPU,
|
||||
};
|
||||
|
||||
pub(super) extern "C" fn qemu_cleanup_atexit() {
|
||||
unsafe {
|
||||
qemu_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DeviceSnapshotFilter {
|
||||
All,
|
||||
AllowList(Vec<String>),
|
||||
DenyList(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct PhysMemoryChunk {
|
||||
addr: GuestPhysAddr,
|
||||
size: usize,
|
||||
qemu: Qemu,
|
||||
cpu: CPU,
|
||||
}
|
||||
|
||||
pub struct PhysMemoryIter {
|
||||
addr: GuestAddrKind, // This address is correct when the iterator enters next, except if the remaining len is 0
|
||||
remaining_len: usize,
|
||||
qemu: Qemu,
|
||||
cpu: CPU,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct HostMemoryIter<'a> {
|
||||
addr: GuestPhysAddr, // This address is correct when the iterator enters next, except if the remaining len is 0
|
||||
remaining_len: usize,
|
||||
qemu: Qemu,
|
||||
cpu: CPU,
|
||||
phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl DeviceSnapshotFilter {
|
||||
fn enum_id(&self) -> libafl_qemu_sys::DeviceSnapshotKind {
|
||||
match self {
|
||||
DeviceSnapshotFilter::All => libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALL,
|
||||
DeviceSnapshotFilter::AllowList(_) => {
|
||||
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALLOWLIST
|
||||
}
|
||||
DeviceSnapshotFilter::DenyList(_) => {
|
||||
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_DENYLIST
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn devices(&self, v: &mut Vec<*mut i8>) -> *mut *mut i8 {
|
||||
v.clear();
|
||||
match self {
|
||||
DeviceSnapshotFilter::All => null_mut(),
|
||||
DeviceSnapshotFilter::AllowList(l) | DeviceSnapshotFilter::DenyList(l) => {
|
||||
for name in l {
|
||||
v.push(name.as_bytes().as_ptr() as *mut i8);
|
||||
}
|
||||
v.push(core::ptr::null_mut());
|
||||
v.as_mut_ptr()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPU {
|
||||
#[must_use]
|
||||
pub fn get_phys_addr(&self, vaddr: GuestVirtAddr) -> Option<GuestPhysAddr> {
|
||||
unsafe {
|
||||
let page = libafl_page_from_addr(vaddr as GuestUsize) as GuestVirtAddr;
|
||||
let mut attrs = MaybeUninit::<libafl_qemu_sys::MemTxAttrs>::uninit();
|
||||
let paddr = libafl_qemu_sys::cpu_get_phys_page_attrs_debug(
|
||||
self.ptr,
|
||||
page as GuestVirtAddr,
|
||||
attrs.as_mut_ptr(),
|
||||
);
|
||||
if paddr == (-1i64 as GuestPhysAddr) {
|
||||
None
|
||||
} else {
|
||||
Some(paddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_phys_addr_tlb(
|
||||
&self,
|
||||
vaddr: GuestAddr,
|
||||
info: MemAccessInfo,
|
||||
is_store: bool,
|
||||
) -> Option<GuestPhysAddr> {
|
||||
unsafe {
|
||||
let pminfo = libafl_qemu_sys::make_plugin_meminfo(
|
||||
info.oi,
|
||||
if is_store {
|
||||
libafl_qemu_sys::qemu_plugin_mem_rw_QEMU_PLUGIN_MEM_W
|
||||
} else {
|
||||
libafl_qemu_sys::qemu_plugin_mem_rw_QEMU_PLUGIN_MEM_R
|
||||
},
|
||||
);
|
||||
let phwaddr = libafl_qemu_sys::qemu_plugin_get_hwaddr(pminfo, vaddr as GuestVirtAddr);
|
||||
if phwaddr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(libafl_qemu_sys::qemu_plugin_hwaddr_phys_addr(phwaddr) as GuestPhysAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn current_paging_id(&self) -> Option<GuestPhysAddr> {
|
||||
let paging_id = unsafe { libafl_qemu_current_paging_id(self.ptr) };
|
||||
|
||||
if paging_id == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(paging_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a value to a guest address.
|
||||
///
|
||||
/// # Safety
|
||||
/// This will write to a translated guest address (using `g2h`).
|
||||
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||
/// This may only be safely used for valid guest addresses!
|
||||
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
||||
// TODO use gdbstub's target_cpu_memory_rw_debug
|
||||
libafl_qemu_sys::cpu_memory_rw_debug(
|
||||
self.ptr,
|
||||
addr as GuestVirtAddr,
|
||||
buf.as_ptr() as *mut _,
|
||||
buf.len(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Read a value from a guest address.
|
||||
///
|
||||
/// # Safety
|
||||
/// This will read from a translated guest address (using `g2h`).
|
||||
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||
/// This may only be safely used for valid guest addresses!
|
||||
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
||||
// TODO use gdbstub's target_cpu_memory_rw_debug
|
||||
libafl_qemu_sys::cpu_memory_rw_debug(
|
||||
self.ptr,
|
||||
addr as GuestVirtAddr,
|
||||
buf.as_mut_ptr() as *mut _,
|
||||
buf.len(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
impl Qemu {
|
||||
pub fn guest_page_size(&self) -> usize {
|
||||
4096
|
||||
}
|
||||
|
||||
/// Write a value to a physical guest address, including ROM areas.
|
||||
pub unsafe fn write_phys_mem(&self, paddr: GuestPhysAddr, buf: &[u8]) {
|
||||
libafl_qemu_sys::cpu_physical_memory_rw(
|
||||
paddr,
|
||||
buf.as_ptr() as *mut _,
|
||||
buf.len() as u64,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Read a value from a physical guest address.
|
||||
pub unsafe fn read_phys_mem(&self, paddr: GuestPhysAddr, buf: &mut [u8]) {
|
||||
libafl_qemu_sys::cpu_physical_memory_rw(
|
||||
paddr,
|
||||
buf.as_mut_ptr() as *mut _,
|
||||
buf.len() as u64,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
/// This function will run the emulator until the next breakpoint / sync exit, or until finish.
|
||||
/// It is a low-level function and simply kicks QEMU.
|
||||
/// # Safety
|
||||
///
|
||||
/// Should, in general, be safe to call.
|
||||
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
||||
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitError> {
|
||||
vm_start();
|
||||
qemu_main_loop();
|
||||
|
||||
self.post_run()
|
||||
}
|
||||
|
||||
pub fn save_snapshot(&self, name: &str, sync: bool) {
|
||||
let s = CString::new(name).expect("Invalid snapshot name");
|
||||
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||
}
|
||||
|
||||
pub fn load_snapshot(&self, name: &str, sync: bool) {
|
||||
let s = CString::new(name).expect("Invalid snapshot name");
|
||||
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn create_fast_snapshot(&self, track: bool) -> FastSnapshotPtr {
|
||||
unsafe {
|
||||
libafl_qemu_sys::syx_snapshot_new(
|
||||
track,
|
||||
true,
|
||||
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALL,
|
||||
null_mut(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn create_fast_snapshot_filter(
|
||||
&self,
|
||||
track: bool,
|
||||
device_filter: &DeviceSnapshotFilter,
|
||||
) -> FastSnapshotPtr {
|
||||
let mut v = vec![];
|
||||
unsafe {
|
||||
libafl_qemu_sys::syx_snapshot_new(
|
||||
track,
|
||||
true,
|
||||
device_filter.enum_id(),
|
||||
device_filter.devices(&mut v),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn restore_fast_snapshot(&self, snapshot: FastSnapshotPtr) {
|
||||
libafl_qemu_sys::syx_snapshot_root_restore(snapshot)
|
||||
}
|
||||
|
||||
pub unsafe fn check_fast_snapshot_memory_consistency(&self, snapshot: FastSnapshotPtr) -> u64 {
|
||||
libafl_qemu_sys::syx_snapshot_check_memory_consistency(snapshot)
|
||||
}
|
||||
|
||||
pub fn list_devices(&self) -> Vec<String> {
|
||||
let mut r = vec![];
|
||||
unsafe {
|
||||
let devices = libafl_qemu_sys::device_list_all();
|
||||
if devices.is_null() {
|
||||
return r;
|
||||
}
|
||||
|
||||
let mut ptr = devices;
|
||||
while !(*ptr).is_null() {
|
||||
let c_str: &CStr = CStr::from_ptr(*ptr);
|
||||
let name = c_str.to_str().unwrap().to_string();
|
||||
r.push(name);
|
||||
|
||||
ptr = ptr.add(1);
|
||||
}
|
||||
|
||||
libc::free(devices as *mut c_void);
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn target_page_size(&self) -> usize {
|
||||
unsafe { libafl_qemu_sys::qemu_target_page_size() }
|
||||
}
|
||||
}
|
||||
|
||||
impl EmulatorMemoryChunk {
|
||||
pub fn phys_iter(&self, qemu: Qemu) -> PhysMemoryIter {
|
||||
PhysMemoryIter {
|
||||
addr: self.addr,
|
||||
remaining_len: self.size as usize,
|
||||
qemu,
|
||||
cpu: if let Some(cpu) = self.cpu {
|
||||
cpu
|
||||
} else {
|
||||
qemu.current_cpu().unwrap()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host_iter(&self, qemu: Qemu) -> Box<dyn Iterator<Item = &[u8]>> {
|
||||
Box::new(
|
||||
self.phys_iter(qemu)
|
||||
.map(move |phys_mem_chunk| HostMemoryIter {
|
||||
addr: phys_mem_chunk.addr,
|
||||
remaining_len: phys_mem_chunk.size,
|
||||
qemu,
|
||||
cpu: phys_mem_chunk.cpu,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
.flatten()
|
||||
.into_iter(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_host_segmented_buf(&self, qemu: Qemu) -> SegmentedBuf<&[u8]> {
|
||||
self.host_iter(qemu).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysMemoryChunk {
|
||||
pub fn new(addr: GuestPhysAddr, size: usize, qemu: Qemu, cpu: CPU) -> Self {
|
||||
Self {
|
||||
addr,
|
||||
size,
|
||||
qemu,
|
||||
cpu,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for HostMemoryIter<'a> {
|
||||
type Item = &'a [u8];
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.remaining_len.is_zero() {
|
||||
None
|
||||
} else {
|
||||
// Host memory allocation is always host-page aligned, so we can freely go from host page to host page.
|
||||
let start_host_addr: *const u8 =
|
||||
unsafe { libafl_qemu_sys::libafl_paddr2host(self.cpu.ptr, self.addr, false) };
|
||||
let host_page_size = Qemu::get().unwrap().host_page_size();
|
||||
let mut size_taken: usize = std::cmp::min(
|
||||
(start_host_addr as usize).next_multiple_of(host_page_size),
|
||||
self.remaining_len,
|
||||
);
|
||||
|
||||
self.remaining_len -= size_taken;
|
||||
self.addr += size_taken as GuestPhysAddr;
|
||||
|
||||
// Now self.addr is host-page aligned
|
||||
while self.remaining_len > 0 {
|
||||
let next_page_host_addr: *const u8 =
|
||||
unsafe { libafl_qemu_sys::libafl_paddr2host(self.cpu.ptr, self.addr, false) };
|
||||
|
||||
// Non-contiguous, we stop here for the slice
|
||||
if next_page_host_addr != start_host_addr {
|
||||
unsafe { return Some(slice::from_raw_parts(start_host_addr, size_taken)) }
|
||||
}
|
||||
|
||||
// The host memory is contiguous, we can widen the slice up to the next host page
|
||||
size_taken += std::cmp::min(self.remaining_len, host_page_size);
|
||||
|
||||
self.remaining_len -= size_taken;
|
||||
self.addr += size_taken as GuestPhysAddr;
|
||||
}
|
||||
|
||||
// We finished to explore the memory, return the last slice.
|
||||
assert_eq!(self.remaining_len, 0);
|
||||
|
||||
unsafe { return Some(slice::from_raw_parts(start_host_addr, size_taken)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PhysMemoryIter {
|
||||
type Item = PhysMemoryChunk;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.remaining_len.is_zero() {
|
||||
None
|
||||
} else {
|
||||
// Physical memory allocation is always physical-page aligned, so we can freely go from host page to host page.
|
||||
let vaddr = match &mut self.addr {
|
||||
GuestAddrKind::Virtual(vaddr) => vaddr,
|
||||
GuestAddrKind::Physical(paddr) => {
|
||||
let sz = self.remaining_len;
|
||||
self.remaining_len = 0;
|
||||
return Some(PhysMemoryChunk::new(*paddr, sz, self.qemu, self.cpu));
|
||||
}
|
||||
};
|
||||
let start_phys_addr: GuestPhysAddr = self.cpu.get_phys_addr(*vaddr)?;
|
||||
let phys_page_size = self.qemu.guest_page_size();
|
||||
|
||||
// TODO: Turn this into a generic function
|
||||
let mut size_taken: usize = std::cmp::min(
|
||||
(start_phys_addr as usize).next_multiple_of(phys_page_size),
|
||||
self.remaining_len,
|
||||
);
|
||||
|
||||
self.remaining_len -= size_taken;
|
||||
*vaddr += size_taken as GuestPhysAddr;
|
||||
|
||||
// Now self.addr is host-page aligned
|
||||
while self.remaining_len > 0 {
|
||||
let next_page_phys_addr: GuestPhysAddr = self.cpu.get_phys_addr(*vaddr)?;
|
||||
|
||||
// Non-contiguous, we stop here for the slice
|
||||
if next_page_phys_addr != start_phys_addr {
|
||||
return Some(PhysMemoryChunk::new(
|
||||
start_phys_addr,
|
||||
size_taken,
|
||||
self.qemu,
|
||||
self.cpu,
|
||||
));
|
||||
}
|
||||
|
||||
// The host memory is contiguous, we can widen the slice up to the next host page
|
||||
size_taken += std::cmp::min(self.remaining_len, phys_page_size);
|
||||
|
||||
self.remaining_len -= size_taken;
|
||||
*vaddr += size_taken as GuestPhysAddr;
|
||||
}
|
||||
|
||||
// We finished to explore the memory, return the last slice.
|
||||
assert_eq!(self.remaining_len, 0);
|
||||
|
||||
Some(PhysMemoryChunk::new(
|
||||
start_phys_addr,
|
||||
size_taken,
|
||||
self.qemu,
|
||||
self.cpu,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
444
libafl_qemu/src/qemu/usermode.rs
Normal file
444
libafl_qemu/src/qemu/usermode.rs
Normal file
@ -0,0 +1,444 @@
|
||||
use std::{
|
||||
intrinsics::copy_nonoverlapping, mem::MaybeUninit, slice::from_raw_parts,
|
||||
str::from_utf8_unchecked,
|
||||
};
|
||||
|
||||
use libafl_qemu_sys::{
|
||||
exec_path, free_self_maps, guest_base, libafl_dump_core_hook, libafl_force_dfl, libafl_get_brk,
|
||||
libafl_load_addr, libafl_maps_first, libafl_maps_next, libafl_qemu_run, libafl_set_brk,
|
||||
mmap_next_start, pageflags_get_root, read_self_maps, strlen, GuestAddr, GuestUsize,
|
||||
IntervalTreeNode, IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess,
|
||||
};
|
||||
use libc::c_int;
|
||||
#[cfg(feature = "python")]
|
||||
use pyo3::{pyclass, pymethods, IntoPy, PyObject, PyRef, PyRefMut, Python};
|
||||
|
||||
use crate::{
|
||||
HookData, NewThreadHookId, PostSyscallHookId, PreSyscallHookId, Qemu, QemuExitError,
|
||||
QemuExitReason, SyscallHookResult, CPU,
|
||||
};
|
||||
|
||||
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
||||
pub struct GuestMaps {
|
||||
self_maps_root: *mut IntervalTreeRoot,
|
||||
pageflags_node: *mut IntervalTreeNode,
|
||||
}
|
||||
|
||||
// Consider a private new only for Emulator
|
||||
impl GuestMaps {
|
||||
#[must_use]
|
||||
pub(crate) fn new() -> Self {
|
||||
unsafe {
|
||||
let pageflags_root = pageflags_get_root();
|
||||
let self_maps_root = read_self_maps();
|
||||
let pageflags_first = libafl_maps_first(pageflags_root);
|
||||
Self {
|
||||
self_maps_root,
|
||||
pageflags_node: pageflags_first,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for GuestMaps {
|
||||
type Item = MapInfo;
|
||||
|
||||
#[allow(clippy::uninit_assumed_init)]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
let mut ret = MaybeUninit::uninit();
|
||||
|
||||
self.pageflags_node =
|
||||
libafl_maps_next(self.pageflags_node, self.self_maps_root, ret.as_mut_ptr());
|
||||
|
||||
let ret = ret.assume_init();
|
||||
|
||||
if ret.is_valid {
|
||||
Some(ret.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[pymethods]
|
||||
impl GuestMaps {
|
||||
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
|
||||
slf
|
||||
}
|
||||
fn __next__(mut slf: PyRefMut<Self>) -> Option<PyObject> {
|
||||
Python::with_gil(|py| slf.next().map(|x| x.into_py(py)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GuestMaps {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
free_self_maps(self.self_maps_root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPU {
|
||||
/// Write a value to a guest address.
|
||||
///
|
||||
/// # Safety
|
||||
/// This will write to a translated guest address (using `g2h`).
|
||||
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||
/// This may only be safely used for valid guest addresses!
|
||||
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
||||
let host_addr = Qemu::get().unwrap().g2h(addr);
|
||||
copy_nonoverlapping(buf.as_ptr(), host_addr, buf.len());
|
||||
}
|
||||
|
||||
/// Read a value from a guest address.
|
||||
///
|
||||
/// # Safety
|
||||
/// This will read from a translated guest address (using `g2h`).
|
||||
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||
/// This may only be safely used for valid guest addresses!
|
||||
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
||||
let host_addr = Qemu::get().unwrap().g2h(addr);
|
||||
copy_nonoverlapping(host_addr, buf.as_mut_ptr(), buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
impl Qemu {
|
||||
#[must_use]
|
||||
pub fn mappings(&self) -> GuestMaps {
|
||||
GuestMaps::new()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn g2h<T>(&self, addr: GuestAddr) -> *mut T {
|
||||
unsafe { (addr as usize + guest_base) as *mut T }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn h2g<T>(&self, addr: *const T) -> GuestAddr {
|
||||
unsafe { (addr as usize - guest_base) as GuestAddr }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn access_ok(&self, kind: VerifyAccess, addr: GuestAddr, size: usize) -> bool {
|
||||
self.current_cpu()
|
||||
.unwrap_or_else(|| self.cpu_from_index(0))
|
||||
.access_ok(kind, addr, size)
|
||||
}
|
||||
|
||||
pub fn force_dfl(&self) {
|
||||
unsafe {
|
||||
libafl_force_dfl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// This function will run the emulator until the next breakpoint, or until finish.
|
||||
/// # Safety
|
||||
///
|
||||
/// Should, in general, be safe to call.
|
||||
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
||||
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitError> {
|
||||
libafl_qemu_run();
|
||||
|
||||
self.post_run()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn binary_path<'a>(&self) -> &'a str {
|
||||
unsafe { from_utf8_unchecked(from_raw_parts(exec_path, strlen(exec_path))) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn load_addr(&self) -> GuestAddr {
|
||||
unsafe { libafl_load_addr() as GuestAddr }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_brk(&self) -> GuestAddr {
|
||||
unsafe { libafl_get_brk() as GuestAddr }
|
||||
}
|
||||
|
||||
pub fn set_brk(&self, brk: GuestAddr) {
|
||||
unsafe { libafl_set_brk(brk.into()) };
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_mmap_start(&self) -> GuestAddr {
|
||||
unsafe { mmap_next_start }
|
||||
}
|
||||
|
||||
pub fn set_mmap_start(&self, start: GuestAddr) {
|
||||
unsafe { mmap_next_start = start };
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
fn mmap(
|
||||
self,
|
||||
addr: GuestAddr,
|
||||
size: usize,
|
||||
perms: MmapPerms,
|
||||
flags: c_int,
|
||||
) -> Result<GuestAddr, ()> {
|
||||
let res = unsafe {
|
||||
libafl_qemu_sys::target_mmap(addr, size as GuestUsize, perms.into(), flags, -1, 0)
|
||||
};
|
||||
if res <= 0 {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(res as GuestAddr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_private(
|
||||
&self,
|
||||
addr: GuestAddr,
|
||||
size: usize,
|
||||
perms: MmapPerms,
|
||||
) -> Result<GuestAddr, String> {
|
||||
self.mmap(addr, size, perms, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS)
|
||||
.map_err(|()| format!("Failed to map {addr}"))
|
||||
.map(|addr| addr as GuestAddr)
|
||||
}
|
||||
|
||||
pub fn map_fixed(
|
||||
&self,
|
||||
addr: GuestAddr,
|
||||
size: usize,
|
||||
perms: MmapPerms,
|
||||
) -> Result<GuestAddr, String> {
|
||||
self.mmap(
|
||||
addr,
|
||||
size,
|
||||
perms,
|
||||
libc::MAP_FIXED | libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
|
||||
)
|
||||
.map_err(|()| format!("Failed to map {addr}"))
|
||||
.map(|addr| addr as GuestAddr)
|
||||
}
|
||||
|
||||
pub fn mprotect(&self, addr: GuestAddr, size: usize, perms: MmapPerms) -> Result<(), String> {
|
||||
let res = unsafe {
|
||||
libafl_qemu_sys::target_mprotect(addr.into(), size as GuestUsize, perms.into())
|
||||
};
|
||||
if res == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Failed to mprotect {addr}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> {
|
||||
if unsafe { libafl_qemu_sys::target_munmap(addr.into(), size as GuestUsize) } == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Failed to unmap {addr}"))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn add_pre_syscall_hook<T: Into<HookData>>(
|
||||
&self,
|
||||
data: T,
|
||||
callback: extern "C" fn(
|
||||
T,
|
||||
i32,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
) -> SyscallHookResult,
|
||||
) -> PreSyscallHookId {
|
||||
unsafe {
|
||||
let data: u64 = data.into().0;
|
||||
let callback: extern "C" fn(
|
||||
u64,
|
||||
i32,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
) -> libafl_qemu_sys::syshook_ret = core::mem::transmute(callback);
|
||||
let num = libafl_qemu_sys::libafl_add_pre_syscall_hook(Some(callback), data);
|
||||
PreSyscallHookId(num)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn add_post_syscall_hook<T: Into<HookData>>(
|
||||
&self,
|
||||
data: T,
|
||||
callback: extern "C" fn(
|
||||
T,
|
||||
GuestAddr,
|
||||
i32,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
) -> GuestAddr,
|
||||
) -> PostSyscallHookId {
|
||||
unsafe {
|
||||
let data: u64 = data.into().0;
|
||||
let callback: extern "C" fn(
|
||||
u64,
|
||||
GuestAddr,
|
||||
i32,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
GuestAddr,
|
||||
) -> GuestAddr = core::mem::transmute(callback);
|
||||
let num = libafl_qemu_sys::libafl_add_post_syscall_hook(Some(callback), data);
|
||||
PostSyscallHookId(num)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_new_thread_hook<T: Into<HookData>>(
|
||||
&self,
|
||||
data: T,
|
||||
callback: extern "C" fn(T, tid: u32) -> bool,
|
||||
) -> NewThreadHookId {
|
||||
unsafe {
|
||||
let data: u64 = data.into().0;
|
||||
let callback: extern "C" fn(u64, u32) -> bool = core::mem::transmute(callback);
|
||||
let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data);
|
||||
NewThreadHookId(num)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn set_crash_hook(&self, callback: extern "C" fn(i32)) {
|
||||
unsafe {
|
||||
libafl_dump_core_hook = callback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
pub mod pybind {
|
||||
use libafl_qemu_sys::{GuestAddr, MmapPerms};
|
||||
use pyo3::{
|
||||
exceptions::PyValueError, pymethods, types::PyInt, FromPyObject, PyObject, PyResult, Python,
|
||||
};
|
||||
|
||||
use crate::{pybind::Qemu, SyscallHookResult};
|
||||
|
||||
static mut PY_SYSCALL_HOOK: Option<PyObject> = None;
|
||||
|
||||
extern "C" fn py_syscall_hook_wrapper(
|
||||
_data: u64,
|
||||
sys_num: i32,
|
||||
a0: u64,
|
||||
a1: u64,
|
||||
a2: u64,
|
||||
a3: u64,
|
||||
a4: u64,
|
||||
a5: u64,
|
||||
a6: u64,
|
||||
a7: u64,
|
||||
) -> SyscallHookResult {
|
||||
unsafe { PY_SYSCALL_HOOK.as_ref() }.map_or_else(
|
||||
|| SyscallHookResult::new(None),
|
||||
|obj| {
|
||||
let args = (sys_num, a0, a1, a2, a3, a4, a5, a6, a7);
|
||||
Python::with_gil(|py| {
|
||||
let ret = obj.call1(py, args).expect("Error in the syscall hook");
|
||||
let any = ret.as_ref(py);
|
||||
if any.is_none() {
|
||||
SyscallHookResult::new(None)
|
||||
} else {
|
||||
let a: Result<&PyInt, _> = any.downcast();
|
||||
if let Ok(i) = a {
|
||||
SyscallHookResult::new(Some(
|
||||
i.extract().expect("Invalid syscall hook return value"),
|
||||
))
|
||||
} else {
|
||||
SyscallHookResult::extract(any)
|
||||
.expect("The syscall hook must return a SyscallHookResult")
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl Qemu {
|
||||
fn g2h(&self, addr: GuestAddr) -> u64 {
|
||||
self.qemu.g2h::<*const u8>(addr) as u64
|
||||
}
|
||||
|
||||
fn h2g(&self, addr: u64) -> GuestAddr {
|
||||
self.qemu.h2g(addr as *const u8)
|
||||
}
|
||||
|
||||
fn binary_path(&self) -> String {
|
||||
self.qemu.binary_path().to_owned()
|
||||
}
|
||||
|
||||
fn load_addr(&self) -> GuestAddr {
|
||||
self.qemu.load_addr()
|
||||
}
|
||||
|
||||
fn map_private(&self, addr: GuestAddr, size: usize, perms: i32) -> PyResult<GuestAddr> {
|
||||
if let Ok(p) = MmapPerms::try_from(perms) {
|
||||
self.qemu
|
||||
.map_private(addr, size, p)
|
||||
.map_err(PyValueError::new_err)
|
||||
} else {
|
||||
Err(PyValueError::new_err("Invalid perms"))
|
||||
}
|
||||
}
|
||||
|
||||
fn map_fixed(&self, addr: GuestAddr, size: usize, perms: i32) -> PyResult<GuestAddr> {
|
||||
if let Ok(p) = MmapPerms::try_from(perms) {
|
||||
self.qemu
|
||||
.map_fixed(addr, size, p)
|
||||
.map_err(PyValueError::new_err)
|
||||
} else {
|
||||
Err(PyValueError::new_err("Invalid perms"))
|
||||
}
|
||||
}
|
||||
|
||||
fn mprotect(&self, addr: GuestAddr, size: usize, perms: i32) -> PyResult<()> {
|
||||
if let Ok(p) = MmapPerms::try_from(perms) {
|
||||
self.qemu
|
||||
.mprotect(addr, size, p)
|
||||
.map_err(PyValueError::new_err)
|
||||
} else {
|
||||
Err(PyValueError::new_err("Invalid perms"))
|
||||
}
|
||||
}
|
||||
|
||||
fn unmap(&self, addr: GuestAddr, size: usize) -> PyResult<()> {
|
||||
self.qemu.unmap(addr, size).map_err(PyValueError::new_err)
|
||||
}
|
||||
|
||||
fn set_syscall_hook(&self, hook: PyObject) {
|
||||
unsafe {
|
||||
PY_SYSCALL_HOOK = Some(hook);
|
||||
}
|
||||
self.qemu
|
||||
.add_pre_syscall_hook(0u64, py_syscall_hook_wrapper);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +1,11 @@
|
||||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
sync::OnceLock,
|
||||
};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use enum_map::{enum_map, Enum, EnumMap};
|
||||
use libafl::{
|
||||
executors::ExitKind,
|
||||
state::{HasExecutions, State},
|
||||
};
|
||||
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr};
|
||||
use num_enum::TryFromPrimitiveError;
|
||||
use enum_map::Enum;
|
||||
|
||||
use crate::{
|
||||
command::{
|
||||
Command, EmulatorMemoryChunk, EndCommand, FilterCommand, InputCommand, LoadCommand,
|
||||
NativeBackdoorCommand, NativeExitKind, SaveCommand, StartCommand, VersionCommand,
|
||||
},
|
||||
get_backdoor_arch_regs, EmuExitHandler, Emulator, GuestReg, QemuHelperTuple,
|
||||
QemuInstrumentationAddressRangeFilter, Regs, CPU,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SyncBackdoorError {
|
||||
UnknownCommand(GuestReg),
|
||||
RegError(String),
|
||||
VersionDifference(u64),
|
||||
}
|
||||
|
||||
impl From<String> for SyncBackdoorError {
|
||||
fn from(error_string: String) -> Self {
|
||||
SyncBackdoorError::RegError(error_string)
|
||||
}
|
||||
}
|
||||
use crate::{command::Command, get_exit_arch_regs, GuestReg, Regs, CPU};
|
||||
|
||||
#[derive(Debug, Clone, Enum)]
|
||||
pub enum BackdoorArgs {
|
||||
pub enum ExitArgs {
|
||||
Ret,
|
||||
Cmd,
|
||||
Arg1,
|
||||
@ -45,177 +16,35 @@ pub enum BackdoorArgs {
|
||||
Arg6,
|
||||
}
|
||||
|
||||
static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
|
||||
|
||||
impl From<TryFromPrimitiveError<NativeBackdoorCommand>> for SyncBackdoorError {
|
||||
fn from(error: TryFromPrimitiveError<NativeBackdoorCommand>) -> Self {
|
||||
SyncBackdoorError::UnknownCommand(error.number.try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SyncBackdoor {
|
||||
pub struct SyncExit {
|
||||
command: Command,
|
||||
arch_regs_map: &'static EnumMap<BackdoorArgs, Regs>,
|
||||
}
|
||||
|
||||
impl SyncBackdoor {
|
||||
impl SyncExit {
|
||||
#[must_use]
|
||||
pub fn new(command: Command) -> Self {
|
||||
Self { command }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn command(&self) -> &Command {
|
||||
&self.command
|
||||
}
|
||||
|
||||
pub fn ret(&self, cpu: &CPU, value: GuestReg) -> Result<(), SyncBackdoorError> {
|
||||
Ok(cpu.write_reg(self.arch_regs_map[BackdoorArgs::Ret], value)?)
|
||||
pub fn ret(&self, cpu: &CPU, value: GuestReg) {
|
||||
cpu.write_reg(get_exit_arch_regs()[ExitArgs::Ret], value)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn ret_reg(&self) -> Regs {
|
||||
self.arch_regs_map[BackdoorArgs::Ret]
|
||||
get_exit_arch_regs()[ExitArgs::Ret]
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SyncBackdoor {
|
||||
impl Display for SyncExit {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.command)
|
||||
}
|
||||
}
|
||||
|
||||
impl<QT, S, E> TryFrom<&Emulator<QT, S, E>> for SyncBackdoor
|
||||
where
|
||||
E: EmuExitHandler<QT, S>,
|
||||
QT: QemuHelperTuple<S>,
|
||||
S: State + HasExecutions,
|
||||
{
|
||||
type Error = SyncBackdoorError;
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn try_from(emu: &Emulator<QT, S, E>) -> Result<Self, Self::Error> {
|
||||
let arch_regs_map: &'static EnumMap<BackdoorArgs, Regs> = get_backdoor_arch_regs();
|
||||
let cmd_id: GuestReg = emu
|
||||
.qemu()
|
||||
.read_reg::<Regs, GuestReg>(arch_regs_map[BackdoorArgs::Cmd])?;
|
||||
|
||||
Ok(match u64::from(cmd_id).try_into()? {
|
||||
NativeBackdoorCommand::Save => SyncBackdoor {
|
||||
command: Command::SaveCommand(SaveCommand),
|
||||
arch_regs_map,
|
||||
},
|
||||
NativeBackdoorCommand::Load => SyncBackdoor {
|
||||
command: Command::LoadCommand(LoadCommand),
|
||||
arch_regs_map,
|
||||
},
|
||||
NativeBackdoorCommand::InputVirt => {
|
||||
let virt_addr: GuestVirtAddr =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||
let max_input_size: GuestReg =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?;
|
||||
|
||||
SyncBackdoor {
|
||||
command: Command::InputCommand(InputCommand::new(
|
||||
EmulatorMemoryChunk::virt(
|
||||
virt_addr,
|
||||
max_input_size,
|
||||
emu.qemu().current_cpu().unwrap().clone(),
|
||||
),
|
||||
emu.qemu().current_cpu().unwrap(),
|
||||
)),
|
||||
arch_regs_map,
|
||||
}
|
||||
}
|
||||
NativeBackdoorCommand::InputPhys => {
|
||||
let phys_addr: GuestPhysAddr =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||
let max_input_size: GuestReg =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?;
|
||||
|
||||
SyncBackdoor {
|
||||
command: Command::InputCommand(InputCommand::new(
|
||||
EmulatorMemoryChunk::phys(
|
||||
phys_addr,
|
||||
max_input_size,
|
||||
Some(emu.qemu().current_cpu().unwrap().clone()),
|
||||
),
|
||||
emu.qemu().current_cpu().unwrap(),
|
||||
)),
|
||||
arch_regs_map,
|
||||
}
|
||||
}
|
||||
NativeBackdoorCommand::End => {
|
||||
let native_exit_kind: GuestReg =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||
let native_exit_kind: Result<NativeExitKind, _> =
|
||||
u64::from(native_exit_kind).try_into();
|
||||
|
||||
let exit_kind = native_exit_kind.ok().and_then(|k| {
|
||||
EMU_EXIT_KIND_MAP.get_or_init(|| {
|
||||
enum_map! {
|
||||
NativeExitKind::Unknown => None,
|
||||
NativeExitKind::Ok => Some(ExitKind::Ok),
|
||||
NativeExitKind::Crash => Some(ExitKind::Crash)
|
||||
}
|
||||
})[k]
|
||||
});
|
||||
|
||||
SyncBackdoor {
|
||||
command: Command::EndCommand(EndCommand::new(exit_kind)),
|
||||
arch_regs_map,
|
||||
}
|
||||
}
|
||||
NativeBackdoorCommand::StartPhys => {
|
||||
let input_phys_addr: GuestPhysAddr =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||
let max_input_size: GuestReg =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?;
|
||||
|
||||
SyncBackdoor {
|
||||
command: Command::StartCommand(StartCommand::new(EmulatorMemoryChunk::phys(
|
||||
input_phys_addr,
|
||||
max_input_size,
|
||||
Some(emu.qemu().current_cpu().unwrap().clone()),
|
||||
))),
|
||||
arch_regs_map,
|
||||
}
|
||||
}
|
||||
NativeBackdoorCommand::StartVirt => {
|
||||
let input_virt_addr: GuestVirtAddr =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||
let max_input_size: GuestReg =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?;
|
||||
|
||||
SyncBackdoor {
|
||||
command: Command::StartCommand(StartCommand::new(EmulatorMemoryChunk::virt(
|
||||
input_virt_addr,
|
||||
max_input_size,
|
||||
emu.qemu().current_cpu().unwrap().clone(),
|
||||
))),
|
||||
arch_regs_map,
|
||||
}
|
||||
}
|
||||
NativeBackdoorCommand::Version => {
|
||||
let client_version = emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||
|
||||
SyncBackdoor {
|
||||
command: Command::VersionCommand(VersionCommand::new(client_version)),
|
||||
arch_regs_map,
|
||||
}
|
||||
}
|
||||
NativeBackdoorCommand::VaddrFilterAllowRange => {
|
||||
let vaddr_start: GuestAddr =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||
let vaddr_end: GuestAddr =
|
||||
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?;
|
||||
|
||||
SyncBackdoor {
|
||||
command: Command::AddressRangeFilterCommand(FilterCommand::new(
|
||||
#[allow(clippy::single_range_in_vec_init)]
|
||||
QemuInstrumentationAddressRangeFilter::AllowList(vec![
|
||||
vaddr_start..vaddr_end,
|
||||
]),
|
||||
)),
|
||||
arch_regs_map,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ use libafl_bolts::{
|
||||
tuples::{tuple_list, Handler, Merge},
|
||||
AsSlice,
|
||||
};
|
||||
pub use libafl_qemu::emu::Qemu;
|
||||
pub use libafl_qemu::qemu::Qemu;
|
||||
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
|
||||
use libafl_qemu::QemuCmpLogHelper;
|
||||
use libafl_qemu::{edges, QemuEdgeCoverageHelper, QemuExecutor, QemuHooks};
|
||||
@ -454,7 +454,7 @@ pub mod pybind {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use libafl_bolts::core_affinity::Cores;
|
||||
use libafl_qemu::emu::pybind::Qemu;
|
||||
use libafl_qemu::qemu::pybind::Qemu;
|
||||
use pyo3::{prelude::*, types::PyBytes};
|
||||
|
||||
use crate::qemu;
|
||||
|
Loading…
x
Reference in New Issue
Block a user