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,
|
elf::EasyElf,
|
||||||
filter_qemu_args,
|
filter_qemu_args,
|
||||||
hooks::QemuHooks,
|
hooks::QemuHooks,
|
||||||
GuestReg, MmapPerms, Qemu, QemuExitReason, QemuExitReasonError, QemuForkExecutor,
|
GuestReg, MmapPerms, Qemu, QemuExitError, QemuExitReason, QemuForkExecutor, QemuShutdownCause,
|
||||||
QemuShutdownCause, Regs,
|
Regs,
|
||||||
};
|
};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use nix::unistd::dup;
|
use nix::unistd::dup;
|
||||||
@ -328,7 +328,7 @@ fn fuzz(
|
|||||||
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
||||||
process::exit(0)
|
process::exit(0)
|
||||||
}
|
}
|
||||||
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
|
||||||
_ => panic!("Unexpected QEMU exit."),
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,8 +60,8 @@ use libafl_qemu::{
|
|||||||
MmapPerms,
|
MmapPerms,
|
||||||
Qemu,
|
Qemu,
|
||||||
QemuExecutor,
|
QemuExecutor,
|
||||||
|
QemuExitError,
|
||||||
QemuExitReason,
|
QemuExitReason,
|
||||||
QemuExitReasonError,
|
|
||||||
QemuShutdownCause,
|
QemuShutdownCause,
|
||||||
Regs,
|
Regs,
|
||||||
};
|
};
|
||||||
@ -350,7 +350,7 @@ fn fuzz(
|
|||||||
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
||||||
process::exit(0)
|
process::exit(0)
|
||||||
}
|
}
|
||||||
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
|
||||||
_ => panic!("Unexpected QEMU exit."),
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,8 @@ use libafl_bolts::{
|
|||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
|
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitReason,
|
ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitError,
|
||||||
QemuExitReasonError, QemuForkExecutor, QemuHooks, QemuShutdownCause, Regs,
|
QemuExitReason, QemuForkExecutor, QemuHooks, QemuShutdownCause, Regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -211,7 +211,7 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
||||||
process::exit(0)
|
process::exit(0)
|
||||||
}
|
}
|
||||||
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
|
||||||
_ => panic!("Unexpected QEMU exit."),
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use std::{env, path::PathBuf, process};
|
|||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
events::{launcher::Launcher, EventConfig, CTRL_C_EXIT},
|
events::{launcher::Launcher, EventConfig},
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
feedback_or, feedback_or_fast,
|
feedback_or, feedback_or_fast,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
@ -29,13 +29,13 @@ use libafl_bolts::{
|
|||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
breakpoint::Breakpoint,
|
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},
|
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
emu::Emulator,
|
emu::Emulator,
|
||||||
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
||||||
EmuExitReasonError, FastSnapshotManager, GuestPhysAddr, GuestReg, HandlerError, HandlerResult,
|
EmulatorMemoryChunk, FastSnapshotManager, GuestPhysAddr, GuestReg, QemuHooks,
|
||||||
QemuHooks, StdEmuExitHandler,
|
StdEmulatorExitHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
// use libafl_qemu::QemuSnapshotBuilder; // for normal qemu snapshot
|
// use libafl_qemu::QemuSnapshotBuilder; // for normal qemu snapshot
|
||||||
@ -93,7 +93,7 @@ pub fn fuzz() {
|
|||||||
let emu_snapshot_manager = FastSnapshotManager::new(false);
|
let emu_snapshot_manager = FastSnapshotManager::new(false);
|
||||||
|
|
||||||
// Choose Exit Handler
|
// Choose Exit Handler
|
||||||
let emu_exit_handler = StdEmuExitHandler::new(emu_snapshot_manager);
|
let emu_exit_handler = StdEmulatorExitHandler::new(emu_snapshot_manager);
|
||||||
|
|
||||||
// Create emulator
|
// Create emulator
|
||||||
let emu = Emulator::new(&args, &env, emu_exit_handler).unwrap();
|
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
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness =
|
let mut harness =
|
||||||
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe {
|
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe {
|
||||||
match emu.run(input, qemu_executor_state) {
|
emu.run(input, qemu_executor_state)
|
||||||
Ok(handler_result) => match handler_result {
|
.unwrap()
|
||||||
HandlerResult::UnhandledExit(unhandled_exit) => {
|
.try_into()
|
||||||
panic!("Unhandled exit: {}", unhandled_exit)
|
.unwrap()
|
||||||
}
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
|
@ -5,7 +5,7 @@ use std::{env, path::PathBuf, process};
|
|||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
events::{launcher::Launcher, EventConfig, CTRL_C_EXIT},
|
events::{launcher::Launcher, EventConfig},
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
feedback_or, feedback_or_fast,
|
feedback_or, feedback_or_fast,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
@ -22,7 +22,7 @@ use libafl::{
|
|||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
core_affinity::Cores,
|
core_affinity::Cores,
|
||||||
current_nanos,
|
current_nanos,
|
||||||
os::unix_signals::Signal,
|
os::unix_signals::{Signal, CTRL_C_EXIT},
|
||||||
ownedref::OwnedMutSlice,
|
ownedref::OwnedMutSlice,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
@ -32,8 +32,7 @@ use libafl_bolts::{
|
|||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
emu::Qemu,
|
Qemu, QemuExecutor, QemuExitError, QemuExitReason, QemuHooks, QemuShutdownCause, Regs,
|
||||||
QemuExecutor, QemuExitReason, QemuExitReasonError, QemuHooks, QemuShutdownCause, Regs,
|
|
||||||
};
|
};
|
||||||
use libafl_qemu_sys::GuestPhysAddr;
|
use libafl_qemu_sys::GuestPhysAddr;
|
||||||
|
|
||||||
@ -128,7 +127,7 @@ pub fn fuzz() {
|
|||||||
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(
|
||||||
Signal::SigInterrupt,
|
Signal::SigInterrupt,
|
||||||
))) => process::exit(CTRL_C_EXIT),
|
))) => process::exit(CTRL_C_EXIT),
|
||||||
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
|
||||||
_ => panic!("Unexpected QEMU exit."),
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,8 +5,7 @@ use std::{env, path::PathBuf, process};
|
|||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
events::{launcher::Launcher, EventConfig, CTRL_C_EXIT},
|
events::{launcher::Launcher, EventConfig},
|
||||||
executors::ExitKind,
|
|
||||||
feedback_or, feedback_or_fast,
|
feedback_or, feedback_or_fast,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
@ -31,8 +30,7 @@ use libafl_qemu::{
|
|||||||
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
||||||
emu::Emulator,
|
emu::Emulator,
|
||||||
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
||||||
EmuExitReasonError, FastSnapshotManager, HandlerError, HandlerResult, QemuHooks,
|
FastSnapshotManager, QemuHooks, StdEmulatorExitHandler,
|
||||||
StdEmuExitHandler,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// use libafl_qemu::QemuSnapshotBuilder; for normal qemu snapshot
|
// use libafl_qemu::QemuSnapshotBuilder; for normal qemu snapshot
|
||||||
@ -56,8 +54,8 @@ pub fn fuzz() {
|
|||||||
let env: Vec<(String, String)> = env::vars().collect();
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
// let emu_snapshot_manager = QemuSnapshotBuilder::new(true);
|
// 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_snapshot_manager = FastSnapshotManager::new(false); // Create a snapshot manager (normal or fast for now).
|
||||||
let emu_exit_handler: StdEmuExitHandler<FastSnapshotManager> =
|
let emu_exit_handler: StdEmulatorExitHandler<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.
|
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 emu = Emulator::new(&args, &env, emu_exit_handler).unwrap(); // Create the emulator
|
||||||
|
|
||||||
let devices = emu.list_devices();
|
let devices = emu.list_devices();
|
||||||
@ -66,30 +64,10 @@ pub fn fuzz() {
|
|||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness =
|
let mut harness =
|
||||||
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe {
|
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe {
|
||||||
match emu.run(input, qemu_executor_state) {
|
emu.run(input, qemu_executor_state)
|
||||||
Ok(handler_result) => match handler_result {
|
.unwrap()
|
||||||
HandlerResult::UnhandledExit(unhandled_exit) => {
|
.try_into()
|
||||||
panic!("Unhandled exit: {}", unhandled_exit)
|
.unwrap()
|
||||||
}
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
|
@ -36,7 +36,9 @@ use libafl_bolts::{
|
|||||||
tuples::{Handle, Handler},
|
tuples::{Handle, Handler},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
#[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::{
|
use libafl_bolts::{
|
||||||
llmp::{self, LlmpClient, LlmpClientDescription, Tag},
|
llmp::{self, LlmpClient, LlmpClientDescription, Tag},
|
||||||
shmem::ShMemProvider,
|
shmem::ShMemProvider,
|
||||||
@ -1581,7 +1583,7 @@ where
|
|||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
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 ctrl-c is pressed, we end up in this branch
|
||||||
if let Err(err) = mgr.detach_from_broker(self.broker_port) {
|
if let Err(err) = mgr.detach_from_broker(self.broker_port) {
|
||||||
log::error!("Failed to detach from broker: {err}");
|
log::error!("Failed to detach from broker: {err}");
|
||||||
|
@ -31,7 +31,7 @@ use ahash::RandomState;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use launcher::*;
|
pub use launcher::*;
|
||||||
#[cfg(all(unix, feature = "std"))]
|
#[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")]
|
#[cfg(feature = "adaptive_serialization")]
|
||||||
use libafl_bolts::tuples::{Handle, MatchNameRef};
|
use libafl_bolts::tuples::{Handle, MatchNameRef};
|
||||||
use libafl_bolts::{current_time, ClientId};
|
use libafl_bolts::{current_time, ClientId};
|
||||||
@ -55,13 +55,6 @@ use crate::{
|
|||||||
state::HasScalabilityMonitor,
|
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
|
/// Check if ctrl-c is sent with this struct
|
||||||
#[cfg(all(unix, feature = "std"))]
|
#[cfg(all(unix, feature = "std"))]
|
||||||
pub static mut EVENTMGR_SIGHANDLER_STATE: ShutdownSignalData = ShutdownSignalData {};
|
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::os::{fork, ForkResult};
|
||||||
use libafl_bolts::ClientId;
|
use libafl_bolts::ClientId;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use libafl_bolts::{shmem::ShMemProvider, staterestore::StateRestorer};
|
use libafl_bolts::{os::CTRL_C_EXIT, shmem::ShMemProvider, staterestore::StateRestorer};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
||||||
@ -520,7 +520,7 @@ where
|
|||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
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());
|
return Err(Error::shutting_down());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ pub mod unix_shmem_server;
|
|||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub mod unix_signals;
|
pub mod unix_signals;
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub use unix_signals::CTRL_C_EXIT;
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "std"))]
|
#[cfg(all(unix, feature = "std"))]
|
||||||
pub mod pipes;
|
pub mod pipes;
|
||||||
@ -28,9 +30,10 @@ use std::{fs::File, os::fd::AsRawFd, sync::OnceLock};
|
|||||||
#[cfg(all(windows, feature = "std"))]
|
#[cfg(all(windows, feature = "std"))]
|
||||||
#[allow(missing_docs, overflowing_literals)]
|
#[allow(missing_docs, overflowing_literals)]
|
||||||
pub mod windows_exceptions;
|
pub mod windows_exceptions;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use libc::pid_t;
|
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
|
/// A file that we keep open, pointing to /dev/null
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(all(feature = "std", unix))]
|
||||||
|
@ -20,6 +20,9 @@ pub use libc::c_ulong;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use nix::errno::{errno, Errno};
|
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
|
/// ARMv7-specific representation of a saved context
|
||||||
#[cfg(target_arch = "arm")]
|
#[cfg(target_arch = "arm")]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -302,6 +305,19 @@ pub enum Signal {
|
|||||||
SigTrap = SIGTRAP,
|
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 {
|
impl TryFrom<&str> for Signal {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
@ -25,6 +25,9 @@ pub use windows::Win32::{
|
|||||||
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
|
/// The special exit code when the target exited through ctrl-c
|
||||||
|
pub const CTRL_C_EXIT: i32 = -1073741510;
|
||||||
|
|
||||||
// For VEH
|
// For VEH
|
||||||
const EXCEPTION_CONTINUE_EXECUTION: c_long = -1;
|
const EXCEPTION_CONTINUE_EXECUTION: c_long = -1;
|
||||||
|
|
||||||
|
@ -89,7 +89,8 @@ paste = "1"
|
|||||||
enum-map = "2.7"
|
enum-map = "2.7"
|
||||||
serde_yaml = { version = "0.8", optional = true } # For parsing the injections yaml file
|
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
|
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 all features of this crate (for `cargo doc`)
|
||||||
document-features = { version = "0.2", optional = true }
|
document-features = { version = "0.2", optional = true }
|
||||||
|
|
||||||
|
@ -11,10 +11,12 @@ mod host_specific {
|
|||||||
#[rustversion::nightly]
|
#[rustversion::nightly]
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo:rustc-cfg=nightly");
|
println!("cargo:rustc-cfg=nightly");
|
||||||
|
println!("cargo::rustc-check-cfg=cfg(nightly)");
|
||||||
host_specific::build();
|
host_specific::build();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustversion::not(nightly)]
|
#[rustversion::not(nightly)]
|
||||||
fn main() {
|
fn main() {
|
||||||
|
println!("cargo::rustc-check-cfg=cfg(nightly)");
|
||||||
host_specific::build();
|
host_specific::build();
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ pub fn build() {
|
|||||||
let runtime_bindings_file = out_dir.join("libafl_qemu_bindings.rs");
|
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");
|
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:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
||||||
println!("cargo:rerun-if-env-changed=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:rerun-if-env-changed=CPU_TARGET");
|
||||||
println!("cargo:rustc-cfg=cpu_target=\"{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) {
|
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)
|
// 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/exit.h"
|
||||||
#include "libafl/hook.h"
|
#include "libafl/hook.h"
|
||||||
#include "libafl/jit.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_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||||
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||||
const QEMU_REVISION: &str = "538e6b02c36838e0de469b39dd03fef05678444f";
|
const QEMU_REVISION: &str = "9f3e2399ee9b106dfbb8c3afcdfdf30e235fc88f";
|
||||||
|
|
||||||
#[allow(clippy::module_name_repetitions)]
|
#[allow(clippy::module_name_repetitions)]
|
||||||
pub struct BuildResult {
|
pub struct BuildResult {
|
||||||
|
@ -8,6 +8,15 @@ mod host_specific {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustversion::nightly]
|
||||||
fn main() {
|
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();
|
host_specific::build();
|
||||||
}
|
}
|
||||||
|
@ -46,17 +46,18 @@ pub fn build() {
|
|||||||
"usermode".to_string()
|
"usermode".to_string()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
println!("cargo::rustc-check-cfg=cfg(emulation_mode, values(\"usermode\", \"systemmode\"))");
|
||||||
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
||||||
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
||||||
|
|
||||||
// Make sure we have at most one architecutre feature set
|
// Make sure we have at most one architecutre feature set
|
||||||
// Else, we default to `x86_64` - having a default makes CI easier :)
|
// Else, we default to `x86_64` - having a default makes CI easier :)
|
||||||
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
|
// 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
|
// Sure aarch64 may support BE, but its not in common usage and we don't
|
||||||
// need it yet and so haven't tested it
|
// 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") {
|
let cpu_target = if cfg!(feature = "x86_64") {
|
||||||
"x86_64".to_string()
|
"x86_64".to_string()
|
||||||
@ -82,6 +83,7 @@ pub fn build() {
|
|||||||
};
|
};
|
||||||
println!("cargo:rerun-if-env-changed=CPU_TARGET");
|
println!("cargo:rerun-if-env-changed=CPU_TARGET");
|
||||||
println!("cargo:rustc-cfg=cpu_target=\"{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")
|
let jobs = env::var("NUM_JOBS")
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#![cfg_attr(nightly, feature(used_with_arg))]
|
||||||
/*!
|
/*!
|
||||||
`libafl_qemu_sys` is the crate exporting C symbols from QEMU.
|
`libafl_qemu_sys` is the crate exporting C symbols from QEMU.
|
||||||
Have a look at `libafl_qemu` for higher-level abstractions.
|
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)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct DeviceClass {
|
pub struct DeviceClass {
|
||||||
pub parent_class: ObjectClass,
|
pub parent_class: ObjectClass,
|
||||||
#[doc = " @categories: device categories device belongs to"]
|
|
||||||
pub categories: [::std::os::raw::c_ulong; 1usize],
|
pub categories: [::std::os::raw::c_ulong; 1usize],
|
||||||
#[doc = " @fw_name: name used to identify device to firmware interfaces"]
|
#[doc = " @fw_name: name used to identify device to firmware interfaces"]
|
||||||
pub fw_name: *const ::std::os::raw::c_char,
|
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_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_BREAKPOINT: libafl_exit_reason_kind = libafl_exit_reason_kind(1);
|
||||||
pub const libafl_exit_reason_kind_SYNC_BACKDOOR: libafl_exit_reason_kind =
|
pub const libafl_exit_reason_kind_SYNC_EXIT: libafl_exit_reason_kind = libafl_exit_reason_kind(2);
|
||||||
libafl_exit_reason_kind(2);
|
|
||||||
impl ::std::ops::BitOr<libafl_exit_reason_kind> for libafl_exit_reason_kind {
|
impl ::std::ops::BitOr<libafl_exit_reason_kind> for libafl_exit_reason_kind {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -12119,21 +12117,18 @@ fn bindgen_test_layout_libafl_exit_reason_breakpoint() {
|
|||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
pub struct libafl_exit_reason_sync_backdoor {}
|
pub struct libafl_exit_reason_sync_exit {}
|
||||||
#[test]
|
#[test]
|
||||||
fn bindgen_test_layout_libafl_exit_reason_sync_backdoor() {
|
fn bindgen_test_layout_libafl_exit_reason_sync_exit() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
::std::mem::size_of::<libafl_exit_reason_sync_backdoor>(),
|
::std::mem::size_of::<libafl_exit_reason_sync_exit>(),
|
||||||
0usize,
|
0usize,
|
||||||
concat!("Size of: ", stringify!(libafl_exit_reason_sync_backdoor))
|
concat!("Size of: ", stringify!(libafl_exit_reason_sync_exit))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
::std::mem::align_of::<libafl_exit_reason_sync_backdoor>(),
|
::std::mem::align_of::<libafl_exit_reason_sync_exit>(),
|
||||||
1usize,
|
1usize,
|
||||||
concat!(
|
concat!("Alignment of ", stringify!(libafl_exit_reason_sync_exit))
|
||||||
"Alignment of ",
|
|
||||||
stringify!(libafl_exit_reason_sync_backdoor)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -12200,7 +12195,7 @@ pub struct libafl_exit_reason {
|
|||||||
pub union libafl_exit_reason__bindgen_ty_1 {
|
pub union libafl_exit_reason__bindgen_ty_1 {
|
||||||
pub internal: libafl_exit_reason_internal,
|
pub internal: libafl_exit_reason_internal,
|
||||||
pub breakpoint: libafl_exit_reason_breakpoint,
|
pub breakpoint: libafl_exit_reason_breakpoint,
|
||||||
pub backdoor: libafl_exit_reason_sync_backdoor,
|
pub sync_exit: libafl_exit_reason_sync_exit,
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn bindgen_test_layout_libafl_exit_reason__bindgen_ty_1() {
|
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!(
|
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,
|
0usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Offset of field: ",
|
"Offset of field: ",
|
||||||
stringify!(libafl_exit_reason__bindgen_ty_1),
|
stringify!(libafl_exit_reason__bindgen_ty_1),
|
||||||
"::",
|
"::",
|
||||||
stringify!(backdoor)
|
stringify!(sync_exit)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -13890,6 +13885,9 @@ extern "C" {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn libafl_jit_trace_block_single(data: u64, id: u64) -> usize;
|
pub fn libafl_jit_trace_block_single(data: u64, id: u64) -> usize;
|
||||||
}
|
}
|
||||||
|
extern "C" {
|
||||||
|
pub fn libafl_qemu_host_page_size() -> usize;
|
||||||
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
pub struct kvm_dirty_gfn {
|
pub struct kvm_dirty_gfn {
|
||||||
|
@ -26,6 +26,7 @@ pub const __USE_ATFILE: u32 = 1;
|
|||||||
pub const __USE_FORTIFY_LEVEL: u32 = 0;
|
pub const __USE_FORTIFY_LEVEL: u32 = 0;
|
||||||
pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0;
|
pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0;
|
||||||
pub const __GLIBC_USE_DEPRECATED_SCANF: 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_PREDEF_H: u32 = 1;
|
||||||
pub const __STDC_IEC_559__: u32 = 1;
|
pub const __STDC_IEC_559__: u32 = 1;
|
||||||
pub const __STDC_IEC_60559_BFP__: u32 = 201404;
|
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 __STDC_ISO_10646__: u32 = 201706;
|
||||||
pub const __GNU_LIBRARY__: u32 = 6;
|
pub const __GNU_LIBRARY__: u32 = 6;
|
||||||
pub const __GLIBC__: u32 = 2;
|
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 _SYS_CDEFS_H: u32 = 1;
|
||||||
pub const __glibc_c99_flexarr_available: u32 = 1;
|
pub const __glibc_c99_flexarr_available: u32 = 1;
|
||||||
pub const __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI: u32 = 0;
|
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_WCHAR_H: u32 = 1;
|
||||||
pub const _BITS_STDINT_INTN_H: u32 = 1;
|
pub const _BITS_STDINT_INTN_H: u32 = 1;
|
||||||
pub const _BITS_STDINT_UINTN_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 INT8_MIN: i32 = -128;
|
||||||
pub const INT16_MIN: i32 = -32768;
|
pub const INT16_MIN: i32 = -32768;
|
||||||
pub const INT32_MIN: i32 = -2147483648;
|
pub const INT32_MIN: i32 = -2147483648;
|
||||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::aarch64::*;
|
pub use syscall_numbers::aarch64::*;
|
||||||
|
|
||||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -49,19 +49,19 @@ pub enum Regs {
|
|||||||
Pstate = 33,
|
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> {
|
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
EXIT_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
BackdoorArgs::Ret => Regs::X0,
|
ExitArgs::Ret => Regs::X0,
|
||||||
BackdoorArgs::Cmd => Regs::X0,
|
ExitArgs::Cmd => Regs::X0,
|
||||||
BackdoorArgs::Arg1 => Regs::X1,
|
ExitArgs::Arg1 => Regs::X1,
|
||||||
BackdoorArgs::Arg2 => Regs::X2,
|
ExitArgs::Arg2 => Regs::X2,
|
||||||
BackdoorArgs::Arg3 => Regs::X3,
|
ExitArgs::Arg3 => Regs::X3,
|
||||||
BackdoorArgs::Arg4 => Regs::X4,
|
ExitArgs::Arg4 => Regs::X4,
|
||||||
BackdoorArgs::Arg5 => Regs::X5,
|
ExitArgs::Arg5 => Regs::X5,
|
||||||
BackdoorArgs::Arg6 => Regs::X6,
|
ExitArgs::Arg6 => Regs::X6,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::arm::*;
|
pub use syscall_numbers::arm::*;
|
||||||
|
|
||||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||||
|
|
||||||
/// Registers for the ARM instruction set.
|
/// Registers for the ARM instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
@ -33,19 +33,19 @@ pub enum Regs {
|
|||||||
R25 = 25,
|
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> {
|
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
EXIT_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
BackdoorArgs::Ret => Regs::R0,
|
ExitArgs::Ret => Regs::R0,
|
||||||
BackdoorArgs::Cmd => Regs::R0,
|
ExitArgs::Cmd => Regs::R0,
|
||||||
BackdoorArgs::Arg1 => Regs::R1,
|
ExitArgs::Arg1 => Regs::R1,
|
||||||
BackdoorArgs::Arg2 => Regs::R2,
|
ExitArgs::Arg2 => Regs::R2,
|
||||||
BackdoorArgs::Arg3 => Regs::R3,
|
ExitArgs::Arg3 => Regs::R3,
|
||||||
BackdoorArgs::Arg4 => Regs::R4,
|
ExitArgs::Arg4 => Regs::R4,
|
||||||
BackdoorArgs::Arg5 => Regs::R5,
|
ExitArgs::Arg5 => Regs::R5,
|
||||||
BackdoorArgs::Arg6 => Regs::R6,
|
ExitArgs::Arg6 => Regs::R6,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
|
|||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
|
|
||||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -64,19 +64,19 @@ pub enum Regs {
|
|||||||
Pktcnthi = 51,
|
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> {
|
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
EXIT_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
BackdoorArgs::Ret => Regs::R0,
|
ExitArgs::Ret => Regs::R0,
|
||||||
BackdoorArgs::Cmd => Regs::R0,
|
ExitArgs::Cmd => Regs::R0,
|
||||||
BackdoorArgs::Arg1 => Regs::R1,
|
ExitArgs::Arg1 => Regs::R1,
|
||||||
BackdoorArgs::Arg2 => Regs::R2,
|
ExitArgs::Arg2 => Regs::R2,
|
||||||
BackdoorArgs::Arg3 => Regs::R3,
|
ExitArgs::Arg3 => Regs::R3,
|
||||||
BackdoorArgs::Arg4 => Regs::R4,
|
ExitArgs::Arg4 => Regs::R4,
|
||||||
BackdoorArgs::Arg5 => Regs::R5,
|
ExitArgs::Arg5 => Regs::R5,
|
||||||
BackdoorArgs::Arg6 => Regs::R6,
|
ExitArgs::Arg6 => Regs::R6,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::x86::*;
|
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)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -25,19 +25,19 @@ pub enum Regs {
|
|||||||
Eflags = 9,
|
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> {
|
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
EXIT_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
BackdoorArgs::Ret => Regs::Eax,
|
ExitArgs::Ret => Regs::Eax,
|
||||||
BackdoorArgs::Cmd => Regs::Eax,
|
ExitArgs::Cmd => Regs::Eax,
|
||||||
BackdoorArgs::Arg1 => Regs::Edi,
|
ExitArgs::Arg1 => Regs::Edi,
|
||||||
BackdoorArgs::Arg2 => Regs::Esi,
|
ExitArgs::Arg2 => Regs::Esi,
|
||||||
BackdoorArgs::Arg3 => Regs::Edx,
|
ExitArgs::Arg3 => Regs::Edx,
|
||||||
BackdoorArgs::Arg4 => Regs::Ebx,
|
ExitArgs::Arg4 => Regs::Ebx,
|
||||||
BackdoorArgs::Arg5 => Regs::Ecx,
|
ExitArgs::Arg5 => Regs::Ecx,
|
||||||
BackdoorArgs::Arg6 => Regs::Ebp,
|
ExitArgs::Arg6 => Regs::Ebp,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::mips::*;
|
pub use syscall_numbers::mips::*;
|
||||||
|
|
||||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||||
|
|
||||||
/// Registers for the MIPS instruction set.
|
/// Registers for the MIPS instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
@ -49,19 +49,19 @@ pub enum Regs {
|
|||||||
Pc = 37,
|
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> {
|
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
EXIT_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
BackdoorArgs::Ret => Regs::V0,
|
ExitArgs::Ret => Regs::V0,
|
||||||
BackdoorArgs::Cmd => Regs::V0,
|
ExitArgs::Cmd => Regs::V0,
|
||||||
BackdoorArgs::Arg1 => Regs::A0,
|
ExitArgs::Arg1 => Regs::A0,
|
||||||
BackdoorArgs::Arg2 => Regs::A1,
|
ExitArgs::Arg2 => Regs::A1,
|
||||||
BackdoorArgs::Arg3 => Regs::A2,
|
ExitArgs::Arg3 => Regs::A2,
|
||||||
BackdoorArgs::Arg4 => Regs::A3,
|
ExitArgs::Arg4 => Regs::A3,
|
||||||
BackdoorArgs::Arg5 => Regs::T0,
|
ExitArgs::Arg5 => Regs::T0,
|
||||||
BackdoorArgs::Arg6 => Regs::T1,
|
ExitArgs::Arg6 => Regs::T1,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::powerpc::*;
|
pub use syscall_numbers::powerpc::*;
|
||||||
|
|
||||||
use crate::{sync_exit::BackdoorArgs, CallingConvention};
|
use crate::{sync_exit::ExitArgs, CallingConvention};
|
||||||
|
|
||||||
/// Registers for the MIPS instruction set.
|
/// Registers for the MIPS instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
@ -88,19 +88,19 @@ pub enum Regs {
|
|||||||
Fpscr = 70,
|
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> {
|
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
EXIT_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
BackdoorArgs::Ret => Regs::R3,
|
ExitArgs::Ret => Regs::R3,
|
||||||
BackdoorArgs::Cmd => Regs::R0,
|
ExitArgs::Cmd => Regs::R0,
|
||||||
BackdoorArgs::Arg1 => Regs::R3,
|
ExitArgs::Arg1 => Regs::R3,
|
||||||
BackdoorArgs::Arg2 => Regs::R4,
|
ExitArgs::Arg2 => Regs::R4,
|
||||||
BackdoorArgs::Arg3 => Regs::R5,
|
ExitArgs::Arg3 => Regs::R5,
|
||||||
BackdoorArgs::Arg4 => Regs::R6,
|
ExitArgs::Arg4 => Regs::R6,
|
||||||
BackdoorArgs::Arg5 => Regs::R7,
|
ExitArgs::Arg5 => Regs::R7,
|
||||||
BackdoorArgs::Arg6 => Regs::R8,
|
ExitArgs::Arg6 => Regs::R8,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::x86_64::*;
|
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)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -33,19 +33,19 @@ pub enum Regs {
|
|||||||
Rflags = 17,
|
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> {
|
pub fn get_exit_arch_regs() -> &'static EnumMap<ExitArgs, Regs> {
|
||||||
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
EXIT_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
BackdoorArgs::Ret => Regs::Rax,
|
ExitArgs::Ret => Regs::Rax,
|
||||||
BackdoorArgs::Cmd => Regs::Rax,
|
ExitArgs::Cmd => Regs::Rax,
|
||||||
BackdoorArgs::Arg1 => Regs::Rdi,
|
ExitArgs::Arg1 => Regs::Rdi,
|
||||||
BackdoorArgs::Arg2 => Regs::Rsi,
|
ExitArgs::Arg2 => Regs::Rsi,
|
||||||
BackdoorArgs::Arg3 => Regs::Rdx,
|
ExitArgs::Arg3 => Regs::Rdx,
|
||||||
BackdoorArgs::Arg4 => Regs::R10,
|
ExitArgs::Arg4 => Regs::R10,
|
||||||
BackdoorArgs::Arg5 => Regs::R8,
|
ExitArgs::Arg5 => Regs::R8,
|
||||||
BackdoorArgs::Arg6 => Regs::R9,
|
ExitArgs::Arg6 => Regs::R9,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,28 @@
|
|||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
use std::collections::HashSet;
|
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::{
|
use libafl::{
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
inputs::HasTargetBytes,
|
inputs::HasTargetBytes,
|
||||||
state::{HasExecutions, State},
|
state::{HasExecutions, State},
|
||||||
};
|
};
|
||||||
use libafl_bolts::AsSlice;
|
use libafl_bolts::AsSlice;
|
||||||
use libafl_qemu_sys::{GuestPhysAddr, GuestVirtAddr};
|
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr};
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
use crate::QemuInstrumentationPagingFilter;
|
use crate::QemuInstrumentationPagingFilter;
|
||||||
use crate::{
|
use crate::{
|
||||||
executor::QemuExecutorState, sync_exit::SyncBackdoorError, EmuExitHandler, Emulator,
|
executor::QemuExecutorState, get_exit_arch_regs, sync_exit::ExitArgs, Emulator,
|
||||||
GuestAddrKind, GuestReg, HandlerError, HasInstrumentationFilter, InnerHandlerResult,
|
EmulatorExitHandler, EmulatorMemoryChunk, ExitHandlerError, ExitHandlerResult, GuestReg,
|
||||||
InputLocation, IsFilter, IsSnapshotManager, Qemu, QemuHelperTuple,
|
HasInstrumentationFilter, InputLocation, IsFilter, IsSnapshotManager, Qemu, QemuHelperTuple,
|
||||||
QemuInstrumentationAddressRangeFilter, Regs, StdEmuExitHandler, StdInstrumentationFilter, CPU,
|
QemuInstrumentationAddressRangeFilter, Regs, StdEmulatorExitHandler, StdInstrumentationFilter,
|
||||||
|
CPU,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const VERSION: u64 = bindings::LIBAFL_QEMU_HDR_VERSION_NUMBER as u64;
|
pub const VERSION: u64 = bindings::LIBAFL_QEMU_HDR_VERSION_NUMBER as u64;
|
||||||
@ -39,7 +43,7 @@ mod bindings {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, TryFromPrimitive)]
|
#[derive(Debug, Clone, TryFromPrimitive)]
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
pub enum NativeBackdoorCommand {
|
pub enum NativeCommand {
|
||||||
StartVirt = bindings::LibaflQemuCommand_LIBAFL_QEMU_COMMAND_START_VIRT.0 as u64, // Shortcut for Save + InputVirt
|
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
|
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.
|
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
|
where
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
S: State + HasExecutions,
|
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
|
/// 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.
|
/// the QEMU VM to run the command.
|
||||||
@ -81,7 +85,7 @@ where
|
|||||||
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
ret_reg: Option<Regs>,
|
ret_reg: Option<Regs>,
|
||||||
) -> Result<InnerHandlerResult, HandlerError>;
|
) -> Result<Option<ExitHandlerResult>, ExitHandlerError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
@ -102,8 +106,123 @@ pub enum Command {
|
|||||||
AddressRangeFilterCommand(AddressRangeFilterCommand),
|
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
|
// 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
|
where
|
||||||
SM: IsSnapshotManager,
|
SM: IsSnapshotManager,
|
||||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
@ -113,90 +232,94 @@ where
|
|||||||
fn usable_at_runtime(&self) -> bool {
|
fn usable_at_runtime(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Command::SaveCommand(cmd) => {
|
Command::SaveCommand(cmd) => {
|
||||||
<SaveCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
<SaveCommand as IsCommand<QT, S, StdEmulatorExitHandler<SM>>>::usable_at_runtime(
|
||||||
}
|
|
||||||
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(
|
|
||||||
cmd,
|
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<
|
Command::AddressRangeFilterCommand(cmd) => <AddressRangeFilterCommand as IsCommand<
|
||||||
QT,
|
QT,
|
||||||
S,
|
S,
|
||||||
StdEmuExitHandler<SM>,
|
StdEmulatorExitHandler<SM>,
|
||||||
>>::usable_at_runtime(cmd),
|
>>::usable_at_runtime(cmd),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||||
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
ret_reg: Option<Regs>,
|
ret_reg: Option<Regs>,
|
||||||
) -> Result<InnerHandlerResult, HandlerError> {
|
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||||
match self {
|
match self {
|
||||||
Command::SaveCommand(cmd) => {
|
Command::SaveCommand(cmd) => <SaveCommand as IsCommand<
|
||||||
<SaveCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
QT,
|
||||||
cmd,
|
S,
|
||||||
emu,
|
StdEmulatorExitHandler<SM>,
|
||||||
qemu_executor_state,
|
>>::run(
|
||||||
input,
|
cmd, emu, qemu_executor_state, input, ret_reg
|
||||||
ret_reg,
|
),
|
||||||
)
|
Command::LoadCommand(cmd) => <LoadCommand as IsCommand<
|
||||||
}
|
QT,
|
||||||
Command::LoadCommand(cmd) => {
|
S,
|
||||||
<LoadCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
StdEmulatorExitHandler<SM>,
|
||||||
cmd,
|
>>::run(
|
||||||
emu,
|
cmd, emu, qemu_executor_state, input, ret_reg
|
||||||
qemu_executor_state,
|
),
|
||||||
input,
|
|
||||||
ret_reg,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Command::InputCommand(cmd) => <InputCommand as IsCommand<
|
Command::InputCommand(cmd) => <InputCommand as IsCommand<
|
||||||
QT,
|
QT,
|
||||||
S,
|
S,
|
||||||
StdEmuExitHandler<SM>,
|
StdEmulatorExitHandler<SM>,
|
||||||
>>::run(
|
>>::run(
|
||||||
cmd, emu, qemu_executor_state, input, ret_reg
|
cmd, emu, qemu_executor_state, input, ret_reg
|
||||||
),
|
),
|
||||||
Command::StartCommand(cmd) => <StartCommand as IsCommand<
|
Command::StartCommand(cmd) => <StartCommand as IsCommand<
|
||||||
QT,
|
QT,
|
||||||
S,
|
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(
|
>>::run(
|
||||||
cmd, emu, qemu_executor_state, input, ret_reg
|
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<
|
Command::VersionCommand(cmd) => <VersionCommand as IsCommand<
|
||||||
QT,
|
QT,
|
||||||
S,
|
S,
|
||||||
StdEmuExitHandler<SM>,
|
StdEmulatorExitHandler<SM>,
|
||||||
>>::run(
|
>>::run(
|
||||||
cmd, emu, qemu_executor_state, input, ret_reg
|
cmd, emu, qemu_executor_state, input, ret_reg
|
||||||
),
|
),
|
||||||
@ -204,12 +327,12 @@ where
|
|||||||
Command::PagingFilterCommand(cmd) => <PagingFilterCommand as IsCommand<
|
Command::PagingFilterCommand(cmd) => <PagingFilterCommand as IsCommand<
|
||||||
QT,
|
QT,
|
||||||
S,
|
S,
|
||||||
StdEmuExitHandler<SM>,
|
StdEmulatorExitHandler<SM>,
|
||||||
>>::run(
|
>>::run(
|
||||||
cmd, emu, qemu_executor_state, input, ret_reg
|
cmd, emu, qemu_executor_state, input, ret_reg
|
||||||
),
|
),
|
||||||
Command::AddressRangeFilterCommand(cmd) => {
|
Command::AddressRangeFilterCommand(cmd) => {
|
||||||
<AddressRangeFilterCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
<AddressRangeFilterCommand as IsCommand<QT, S, StdEmulatorExitHandler<SM>>>::run(
|
||||||
cmd,
|
cmd,
|
||||||
emu,
|
emu,
|
||||||
qemu_executor_state,
|
qemu_executor_state,
|
||||||
@ -221,17 +344,10 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct EmulatorMemoryChunk {
|
|
||||||
addr: GuestAddrKind,
|
|
||||||
size: GuestReg,
|
|
||||||
cpu: Option<CPU>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SaveCommand;
|
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
|
where
|
||||||
SM: IsSnapshotManager,
|
SM: IsSnapshotManager,
|
||||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
@ -244,7 +360,7 @@ where
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&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(emulation_mode = "systemmode")] qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
#[cfg(not(emulation_mode = "systemmode"))] _qemu_executor_state: &mut QemuExecutorState<
|
#[cfg(not(emulation_mode = "systemmode"))] _qemu_executor_state: &mut QemuExecutorState<
|
||||||
QT,
|
QT,
|
||||||
@ -252,14 +368,14 @@ where
|
|||||||
>,
|
>,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_ret_reg: Option<Regs>,
|
_ret_reg: Option<Regs>,
|
||||||
) -> Result<InnerHandlerResult, HandlerError> {
|
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||||
let qemu = emu.qemu();
|
let qemu = emu.qemu();
|
||||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||||
|
|
||||||
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
|
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
|
||||||
emu_exit_handler
|
emu_exit_handler
|
||||||
.set_snapshot_id(snapshot_id)
|
.set_snapshot_id(snapshot_id)
|
||||||
.map_err(|_| HandlerError::MultipleSnapshotDefinition)?;
|
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?;
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
{
|
{
|
||||||
@ -278,14 +394,14 @@ where
|
|||||||
*paging_filter = QemuInstrumentationPagingFilter::AllowList(allowed_paging_ids);
|
*paging_filter = QemuInstrumentationPagingFilter::AllowList(allowed_paging_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(InnerHandlerResult::Continue)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LoadCommand;
|
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
|
where
|
||||||
SM: IsSnapshotManager,
|
SM: IsSnapshotManager,
|
||||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
@ -298,23 +414,23 @@ where
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||||
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_ret_reg: Option<Regs>,
|
_ret_reg: Option<Regs>,
|
||||||
) -> Result<InnerHandlerResult, HandlerError> {
|
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||||
let qemu = emu.qemu();
|
let qemu = emu.qemu();
|
||||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||||
|
|
||||||
let snapshot_id = emu_exit_handler
|
let snapshot_id = emu_exit_handler
|
||||||
.snapshot_id()
|
.snapshot_id()
|
||||||
.ok_or(HandlerError::SnapshotNotFound)?;
|
.ok_or(ExitHandlerError::SnapshotNotFound)?;
|
||||||
|
|
||||||
emu_exit_handler
|
emu_exit_handler
|
||||||
.snapshot_manager_borrow_mut()
|
.snapshot_manager_borrow_mut()
|
||||||
.restore(&snapshot_id, qemu)?;
|
.restore(&snapshot_id, qemu)?;
|
||||||
|
|
||||||
Ok(InnerHandlerResult::Continue)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,7 +440,7 @@ pub struct InputCommand {
|
|||||||
cpu: CPU,
|
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
|
where
|
||||||
SM: IsSnapshotManager,
|
SM: IsSnapshotManager,
|
||||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
@ -337,11 +453,11 @@ where
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||||
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
ret_reg: Option<Regs>,
|
ret_reg: Option<Regs>,
|
||||||
) -> Result<InnerHandlerResult, HandlerError> {
|
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||||
let qemu = emu.qemu();
|
let qemu = emu.qemu();
|
||||||
|
|
||||||
let ret_value = self.location.write(qemu, input.target_bytes().as_slice());
|
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();
|
self.cpu.write_reg(reg, ret_value).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(InnerHandlerResult::Continue)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +475,7 @@ pub struct StartCommand {
|
|||||||
input_location: EmulatorMemoryChunk,
|
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
|
where
|
||||||
SM: IsSnapshotManager,
|
SM: IsSnapshotManager,
|
||||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
@ -372,18 +488,18 @@ where
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||||
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
ret_reg: Option<Regs>,
|
ret_reg: Option<Regs>,
|
||||||
) -> Result<InnerHandlerResult, HandlerError> {
|
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||||
let qemu = emu.qemu();
|
let qemu = emu.qemu();
|
||||||
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
|
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
|
||||||
|
|
||||||
emu_exit_handler
|
emu_exit_handler
|
||||||
.set_snapshot_id(snapshot_id)
|
.set_snapshot_id(snapshot_id)
|
||||||
.map_err(|_| HandlerError::MultipleSnapshotDefinition)?;
|
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?;
|
||||||
|
|
||||||
emu_exit_handler
|
emu_exit_handler
|
||||||
.set_input_location(InputLocation::new(
|
.set_input_location(InputLocation::new(
|
||||||
@ -401,14 +517,14 @@ where
|
|||||||
qemu.write_reg(reg, ret_value).unwrap();
|
qemu.write_reg(reg, ret_value).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(InnerHandlerResult::Continue)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EndCommand(Option<ExitKind>);
|
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
|
where
|
||||||
SM: IsSnapshotManager,
|
SM: IsSnapshotManager,
|
||||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
@ -421,29 +537,29 @@ where
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||||
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_ret_reg: Option<Regs>,
|
_ret_reg: Option<Regs>,
|
||||||
) -> Result<InnerHandlerResult, HandlerError> {
|
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||||
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||||
|
|
||||||
let snapshot_id = emu_exit_handler
|
let snapshot_id = emu_exit_handler
|
||||||
.snapshot_id()
|
.snapshot_id()
|
||||||
.ok_or(HandlerError::SnapshotNotFound)?;
|
.ok_or(ExitHandlerError::SnapshotNotFound)?;
|
||||||
|
|
||||||
emu_exit_handler
|
emu_exit_handler
|
||||||
.snapshot_manager_borrow_mut()
|
.snapshot_manager_borrow_mut()
|
||||||
.restore(&snapshot_id, emu.qemu())?;
|
.restore(&snapshot_id, emu.qemu())?;
|
||||||
|
|
||||||
Ok(InnerHandlerResult::EndOfRun(self.0.unwrap()))
|
Ok(Some(ExitHandlerResult::EndOfRun(self.0.unwrap())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct VersionCommand(u64);
|
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
|
where
|
||||||
SM: IsSnapshotManager,
|
SM: IsSnapshotManager,
|
||||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
@ -456,18 +572,18 @@ where
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
_emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||||
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_ret_reg: Option<Regs>,
|
_ret_reg: Option<Regs>,
|
||||||
) -> Result<InnerHandlerResult, HandlerError> {
|
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||||
let guest_version = self.0;
|
let guest_version = self.0;
|
||||||
|
|
||||||
if VERSION == guest_version {
|
if VERSION == guest_version {
|
||||||
Ok(InnerHandlerResult::Continue)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Err(HandlerError::SyncBackdoorError(
|
Err(ExitHandlerError::CommandError(
|
||||||
SyncBackdoorError::VersionDifference(guest_version),
|
CommandError::VersionDifference(guest_version),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,7 +598,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[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
|
where
|
||||||
SM: IsSnapshotManager,
|
SM: IsSnapshotManager,
|
||||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
@ -495,11 +611,11 @@ where
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
_emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||||
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_ret_reg: Option<Regs>,
|
_ret_reg: Option<Regs>,
|
||||||
) -> Result<InnerHandlerResult, HandlerError> {
|
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||||
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
|
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
|
||||||
|
|
||||||
let paging_filter =
|
let paging_filter =
|
||||||
@ -509,11 +625,11 @@ where
|
|||||||
|
|
||||||
*paging_filter = self.filter.clone();
|
*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
|
where
|
||||||
SM: IsSnapshotManager,
|
SM: IsSnapshotManager,
|
||||||
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
@ -527,11 +643,11 @@ where
|
|||||||
#[allow(clippy::type_complexity)] // TODO: refactor with correct type.
|
#[allow(clippy::type_complexity)] // TODO: refactor with correct type.
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
_emu: &Emulator<QT, S, StdEmulatorExitHandler<SM>>,
|
||||||
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_ret_reg: Option<Regs>,
|
_ret_reg: Option<Regs>,
|
||||||
) -> Result<InnerHandlerResult, HandlerError> {
|
) -> Result<Option<ExitHandlerResult>, ExitHandlerError> {
|
||||||
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
|
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
|
||||||
|
|
||||||
let addr_range_filter =
|
let addr_range_filter =
|
||||||
@ -541,7 +657,7 @@ where
|
|||||||
|
|
||||||
*addr_range_filter = self.filter.clone();
|
*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::SaveCommand(_) => write!(f, "Save VM"),
|
||||||
Command::LoadCommand(_) => write!(f, "Reload VM"),
|
Command::LoadCommand(_) => write!(f, "Reload VM"),
|
||||||
Command::InputCommand(input_command) => {
|
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) => {
|
Command::StartCommand(start_command) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Start fuzzing with input @{}",
|
"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),
|
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 {
|
impl Display for InputCommand {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{} (0x{:x} max nb bytes)",
|
"{} (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::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
ffi::{c_void, CStr, CString},
|
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
mem::MaybeUninit,
|
|
||||||
ptr::null_mut,
|
|
||||||
sync::atomic::{AtomicU64, Ordering},
|
sync::atomic::{AtomicU64, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use libafl::state::{HasExecutions, State};
|
use libafl::state::{HasExecutions, State};
|
||||||
use libafl_qemu_sys::{
|
use libafl_qemu_sys::GuestPhysAddr;
|
||||||
libafl_load_qemu_snapshot, libafl_qemu_current_paging_id, libafl_save_qemu_snapshot,
|
|
||||||
qemu_cleanup, qemu_main_loop, vm_start, GuestAddr, GuestPhysAddr, GuestVirtAddr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::{libafl_page_from_addr, IsSnapshotManager},
|
emu::IsSnapshotManager, DeviceSnapshotFilter, Emulator, EmulatorExitHandler, Qemu,
|
||||||
EmuExitHandler, Emulator, MemAccessInfo, Qemu, QemuExitReason, QemuExitReasonError,
|
QemuHelperTuple, SnapshotId, SnapshotManagerError,
|
||||||
QemuHelperTuple, SnapshotId, SnapshotManagerError, CPU,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl SnapshotId {
|
impl SnapshotId {
|
||||||
@ -25,9 +18,7 @@ impl SnapshotId {
|
|||||||
|
|
||||||
let unique_id = UNIQUE_ID.fetch_add(1, Ordering::SeqCst);
|
let unique_id = UNIQUE_ID.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
||||||
SnapshotId {
|
SnapshotId { id: unique_id }
|
||||||
id: unique_id.clone(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inner(&self) -> u64 {
|
fn inner(&self) -> u64 {
|
||||||
@ -84,7 +75,7 @@ impl FastSnapshotManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn get(&self, id: &SnapshotId) -> FastSnapshotPtr {
|
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,
|
snapshot_id: &SnapshotId,
|
||||||
qemu: &Qemu,
|
qemu: &Qemu,
|
||||||
) -> Result<(), SnapshotManagerError> {
|
) -> Result<(), SnapshotManagerError> {
|
||||||
let fast_snapshot_ptr = self
|
let fast_snapshot_ptr = *self
|
||||||
.snapshots
|
.snapshots
|
||||||
.get(snapshot_id)
|
.get(snapshot_id)
|
||||||
.ok_or(SnapshotManagerError::SnapshotIdNotFound(
|
.ok_or(SnapshotManagerError::SnapshotIdNotFound(*snapshot_id))?;
|
||||||
snapshot_id.clone(),
|
|
||||||
))?
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
qemu.restore_fast_snapshot(fast_snapshot_ptr);
|
unsafe {
|
||||||
|
qemu.restore_fast_snapshot(fast_snapshot_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
if self.check_memory_consistency {
|
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 {
|
if nb_inconsistencies > 0 {
|
||||||
return Err(SnapshotManagerError::MemoryInconsistencies(
|
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>
|
impl<QT, S, E> Emulator<QT, S, E>
|
||||||
where
|
where
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
S: State + HasExecutions,
|
S: State + HasExecutions,
|
||||||
E: EmuExitHandler<QT, S>,
|
E: EmulatorExitHandler<QT, S>,
|
||||||
{
|
{
|
||||||
/// Write a value to a phsical guest address, including ROM areas.
|
/// Write a value to a phsical guest address, including ROM areas.
|
||||||
pub unsafe fn write_phys_mem(&self, paddr: GuestPhysAddr, buf: &[u8]) {
|
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)
|
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)
|
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)
|
self.qemu.check_fast_snapshot_memory_consistency(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,365 +1,16 @@
|
|||||||
use core::{mem::MaybeUninit, ptr::copy_nonoverlapping};
|
use libafl_qemu_sys::{GuestAddr, MmapPerms, VerifyAccess};
|
||||||
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 crate::{
|
use crate::{
|
||||||
emu::{HasExecutions, State},
|
emu::{HasExecutions, State},
|
||||||
sync_exit::SyncBackdoorError,
|
Emulator, EmulatorExitHandler, GuestMaps, HookData, NewThreadHookId, PostSyscallHookId,
|
||||||
EmuExitHandler, Emulator, HookData, NewThreadHookId, PostSyscallHookId, PreSyscallHookId, Qemu,
|
PreSyscallHookId, QemuHelperTuple, SyscallHookResult,
|
||||||
QemuExitReason, QemuExitReasonError, QemuHelperTuple, SyscallHookResult, CPU,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[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>
|
impl<QT, S, E> Emulator<QT, S, E>
|
||||||
where
|
where
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
S: State + HasExecutions,
|
S: State + HasExecutions,
|
||||||
E: EmuExitHandler<QT, S>,
|
E: EmulatorExitHandler<QT, S>,
|
||||||
{
|
{
|
||||||
/// This function gets the memory mappings from the emulator.
|
/// This function gets the memory mappings from the emulator.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -8,7 +8,9 @@ use core::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[cfg(feature = "fork")]
|
||||||
use libafl::{events::EventManager, executors::InProcessForkExecutor, state::HasLastReportTime};
|
use libafl::{
|
||||||
|
events::EventManager, executors::InProcessForkExecutor, state::HasLastReportTime, HasMetadata,
|
||||||
|
};
|
||||||
use libafl::{
|
use libafl::{
|
||||||
events::{EventFirer, EventRestarter},
|
events::{EventFirer, EventRestarter},
|
||||||
executors::{
|
executors::{
|
||||||
@ -20,7 +22,7 @@ use libafl::{
|
|||||||
fuzzer::HasObjective,
|
fuzzer::HasObjective,
|
||||||
observers::{ObserversTuple, UsesObservers},
|
observers::{ObserversTuple, UsesObservers},
|
||||||
state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState},
|
state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState},
|
||||||
Error, HasMetadata,
|
Error,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "fork")]
|
#[cfg(feature = "fork")]
|
||||||
use libafl_bolts::shmem::ShMemProvider;
|
use libafl_bolts::shmem::ShMemProvider;
|
||||||
|
@ -17,12 +17,12 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
|
|||||||
use rangemap::RangeMap;
|
use rangemap::RangeMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::{EmuError, MemAccessInfo, SyscallHookResult},
|
|
||||||
helpers::{
|
helpers::{
|
||||||
calls::FullBacktraceCollector, HasInstrumentationFilter, IsFilter, QemuHelper,
|
calls::FullBacktraceCollector, HasInstrumentationFilter, IsFilter, QemuHelper,
|
||||||
QemuHelperTuple, QemuInstrumentationAddressRangeFilter,
|
QemuHelperTuple, QemuInstrumentationAddressRangeFilter,
|
||||||
},
|
},
|
||||||
hooks::{Hook, QemuHooks},
|
hooks::{Hook, QemuHooks},
|
||||||
|
qemu::{MemAccessInfo, QemuInitError, SyscallHookResult},
|
||||||
snapshot::QemuSnapshotHelper,
|
snapshot::QemuSnapshotHelper,
|
||||||
sys::TCGTemp,
|
sys::TCGTemp,
|
||||||
GuestAddr, Qemu, Regs,
|
GuestAddr, Qemu, Regs,
|
||||||
@ -668,7 +668,7 @@ static mut ASAN_INITED: bool = false;
|
|||||||
pub fn init_qemu_with_asan(
|
pub fn init_qemu_with_asan(
|
||||||
args: &mut Vec<String>,
|
args: &mut Vec<String>,
|
||||||
env: &mut [(String, String)],
|
env: &mut [(String, String)],
|
||||||
) -> Result<(Qemu, Pin<Box<AsanGiovese>>), EmuError> {
|
) -> Result<(Qemu, Pin<Box<AsanGiovese>>), QemuInitError> {
|
||||||
let current = env::current_exe().unwrap();
|
let current = env::current_exe().unwrap();
|
||||||
let asan_lib = fs::canonicalize(current)
|
let asan_lib = fs::canonicalize(current)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -12,12 +12,12 @@ use libafl::{inputs::UsesInput, HasMetadata};
|
|||||||
#[cfg(not(feature = "clippy"))]
|
#[cfg(not(feature = "clippy"))]
|
||||||
use crate::sys::libafl_tcg_gen_asan;
|
use crate::sys::libafl_tcg_gen_asan;
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::{EmuError, MemAccessInfo, Qemu},
|
|
||||||
helpers::{
|
helpers::{
|
||||||
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
||||||
QemuInstrumentationAddressRangeFilter,
|
QemuInstrumentationAddressRangeFilter,
|
||||||
},
|
},
|
||||||
hooks::{Hook, QemuHooks},
|
hooks::{Hook, QemuHooks},
|
||||||
|
qemu::{MemAccessInfo, Qemu, QemuInitError},
|
||||||
sys::TCGTemp,
|
sys::TCGTemp,
|
||||||
GuestAddr, MapInfo,
|
GuestAddr, MapInfo,
|
||||||
};
|
};
|
||||||
@ -27,7 +27,7 @@ static mut ASAN_GUEST_INITED: bool = false;
|
|||||||
pub fn init_qemu_with_asan_guest(
|
pub fn init_qemu_with_asan_guest(
|
||||||
args: &mut Vec<String>,
|
args: &mut Vec<String>,
|
||||||
env: &mut [(String, String)],
|
env: &mut [(String, String)],
|
||||||
) -> Result<(Qemu, String), EmuError> {
|
) -> Result<(Qemu, String), QemuInitError> {
|
||||||
let current = env::current_exe().unwrap();
|
let current = env::current_exe().unwrap();
|
||||||
let asan_lib = fs::canonicalize(current)
|
let asan_lib = fs::canonicalize(current)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -12,12 +12,12 @@ use thread_local::ThreadLocal;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
capstone,
|
capstone,
|
||||||
emu::ArchExtras,
|
|
||||||
helpers::{
|
helpers::{
|
||||||
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
||||||
QemuInstrumentationAddressRangeFilter,
|
QemuInstrumentationAddressRangeFilter,
|
||||||
},
|
},
|
||||||
hooks::{Hook, QemuHooks},
|
hooks::{Hook, QemuHooks},
|
||||||
|
qemu::ArchExtras,
|
||||||
Qemu,
|
Qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ pub use libafl_targets::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
use crate::{capstone, emu::ArchExtras, CallingConvention, Qemu};
|
use crate::{capstone, qemu::ArchExtras, CallingConvention, Qemu};
|
||||||
use crate::{
|
use crate::{
|
||||||
helpers::{
|
helpers::{
|
||||||
hash_me, HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
hash_me, HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
||||||
|
@ -539,8 +539,7 @@ where
|
|||||||
let paging_id = hooks
|
let paging_id = hooks
|
||||||
.qemu()
|
.qemu()
|
||||||
.current_cpu()
|
.current_cpu()
|
||||||
.map(|cpu| cpu.current_paging_id())
|
.and_then(|cpu| cpu.current_paging_id());
|
||||||
.flatten();
|
|
||||||
|
|
||||||
if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) {
|
if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) {
|
||||||
return None;
|
return None;
|
||||||
@ -609,8 +608,7 @@ where
|
|||||||
let paging_id = hooks
|
let paging_id = hooks
|
||||||
.qemu()
|
.qemu()
|
||||||
.current_cpu()
|
.current_cpu()
|
||||||
.map(|cpu| cpu.current_paging_id())
|
.and_then(|cpu| cpu.current_paging_id());
|
||||||
.flatten();
|
|
||||||
|
|
||||||
if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) {
|
if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) {
|
||||||
return None;
|
return None;
|
||||||
@ -676,8 +674,7 @@ where
|
|||||||
let paging_id = hooks
|
let paging_id = hooks
|
||||||
.qemu()
|
.qemu()
|
||||||
.current_cpu()
|
.current_cpu()
|
||||||
.map(|cpu| cpu.current_paging_id())
|
.and_then(|cpu| cpu.current_paging_id());
|
||||||
.flatten();
|
|
||||||
|
|
||||||
if !h.must_instrument(pc, paging_id) {
|
if !h.must_instrument(pc, paging_id) {
|
||||||
return None;
|
return None;
|
||||||
|
@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
use crate::SYS_execve;
|
use crate::SYS_execve;
|
||||||
use crate::{
|
use crate::{
|
||||||
elf::EasyElf, emu::ArchExtras, CallingConvention, Hook, Qemu, QemuHelper, QemuHelperTuple,
|
elf::EasyElf, qemu::ArchExtras, CallingConvention, Hook, Qemu, QemuHelper, QemuHelperTuple,
|
||||||
QemuHooks, SyscallHookResult,
|
QemuHooks, SyscallHookResult,
|
||||||
};
|
};
|
||||||
#[cfg(cpu_target = "hexagon")]
|
#[cfg(cpu_target = "hexagon")]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use core::{fmt::Debug, ops::Range};
|
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::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
|
||||||
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
|
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, ())
|
impl<Head, F, S> HasInstrumentationFilter<F, S> for (Head, ())
|
||||||
where
|
where
|
||||||
Head: QemuHelper<S> + HasInstrumentationFilter<F, S>,
|
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")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
impl<Head, S> StdInstrumentationFilter<S> for (Head, ())
|
impl<Head, S> StdInstrumentationFilter<S> for (Head, ())
|
||||||
where
|
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 {
|
pub trait IsFilter: Debug {
|
||||||
type FilterParameter;
|
type FilterParameter;
|
||||||
|
|
||||||
fn allowed(&self, filter_parameter: Self::FilterParameter) -> bool;
|
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> {}
|
pub trait IsAddressFilter: IsFilter<FilterParameter = GuestAddr> {}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
@ -25,9 +25,9 @@ use crate::SYS_mmap2;
|
|||||||
use crate::SYS_newfstatat;
|
use crate::SYS_newfstatat;
|
||||||
use crate::{
|
use crate::{
|
||||||
asan::QemuAsanHelper,
|
asan::QemuAsanHelper,
|
||||||
emu::SyscallHookResult,
|
|
||||||
helpers::{QemuHelper, QemuHelperTuple},
|
helpers::{QemuHelper, QemuHelperTuple},
|
||||||
hooks::{Hook, QemuHooks},
|
hooks::{Hook, QemuHooks},
|
||||||
|
qemu::SyscallHookResult,
|
||||||
Qemu, SYS_fstat, SYS_fstatfs, SYS_futex, SYS_getrandom, SYS_mprotect, SYS_mremap, SYS_munmap,
|
Qemu, SYS_fstat, SYS_fstatfs, SYS_futex, SYS_getrandom, SYS_mprotect, SYS_mremap, SYS_munmap,
|
||||||
SYS_pread64, SYS_read, SYS_readlinkat, SYS_statfs,
|
SYS_pread64, SYS_read, SYS_readlinkat, SYS_statfs,
|
||||||
};
|
};
|
||||||
|
@ -19,10 +19,10 @@ use libafl::{
|
|||||||
};
|
};
|
||||||
use libafl_qemu_sys::{CPUArchStatePtr, FatPtr, GuestAddr, GuestUsize};
|
use libafl_qemu_sys::{CPUArchStatePtr, FatPtr, GuestAddr, GuestUsize};
|
||||||
|
|
||||||
pub use crate::emu::SyscallHookResult;
|
pub use crate::qemu::SyscallHookResult;
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::{MemAccessInfo, Qemu, SKIP_EXEC_HOOK},
|
|
||||||
helpers::QemuHelperTuple,
|
helpers::QemuHelperTuple,
|
||||||
|
qemu::{MemAccessInfo, Qemu, SKIP_EXEC_HOOK},
|
||||||
sys::TCGTemp,
|
sys::TCGTemp,
|
||||||
BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, HookId, InstructionHookId, ReadHookId,
|
BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, HookId, InstructionHookId, ReadHookId,
|
||||||
WriteHookId,
|
WriteHookId,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
#![cfg_attr(nightly, feature(used_with_arg))]
|
|
||||||
//! Welcome to `LibAFL` QEMU
|
//! 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.
|
//! __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")]
|
#[cfg(feature = "fork")]
|
||||||
pub use executor::QemuForkExecutor;
|
pub use executor::QemuForkExecutor;
|
||||||
|
|
||||||
|
pub mod qemu;
|
||||||
|
pub use qemu::*;
|
||||||
|
|
||||||
pub mod emu;
|
pub mod emu;
|
||||||
pub use emu::*;
|
pub use emu::*;
|
||||||
|
|
||||||
@ -89,16 +91,19 @@ pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
|
|||||||
m.add_submodule(regsm)?;
|
m.add_submodule(regsm)?;
|
||||||
|
|
||||||
let mmapm = PyModule::new(py, "mmap")?;
|
let mmapm = PyModule::new(py, "mmap")?;
|
||||||
for r in emu::MmapPerms::iter() {
|
for r in sys::MmapPerms::iter() {
|
||||||
let v: i32 = r.into();
|
let v: i32 = r.into();
|
||||||
mmapm.add(&format!("{r:?}"), v)?;
|
mmapm.add(&format!("{r:?}"), v)?;
|
||||||
}
|
}
|
||||||
m.add_submodule(mmapm)?;
|
m.add_submodule(mmapm)?;
|
||||||
|
|
||||||
m.add_class::<emu::MapInfo>()?;
|
m.add_class::<sys::MapInfo>()?;
|
||||||
m.add_class::<emu::GuestMaps>()?;
|
|
||||||
m.add_class::<emu::SyscallHookResult>()?;
|
#[cfg(emulation_mode = "usermode")]
|
||||||
m.add_class::<emu::pybind::Qemu>()?;
|
m.add_class::<qemu::GuestMaps>()?;
|
||||||
|
|
||||||
|
m.add_class::<qemu::SyscallHookResult>()?;
|
||||||
|
m.add_class::<qemu::pybind::Qemu>()?;
|
||||||
|
|
||||||
Ok(())
|
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::{
|
use std::fmt::{Display, Formatter};
|
||||||
fmt::{Display, Formatter},
|
|
||||||
sync::OnceLock,
|
|
||||||
};
|
|
||||||
|
|
||||||
use enum_map::{enum_map, Enum, EnumMap};
|
use enum_map::Enum;
|
||||||
use libafl::{
|
|
||||||
executors::ExitKind,
|
|
||||||
state::{HasExecutions, State},
|
|
||||||
};
|
|
||||||
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr};
|
|
||||||
use num_enum::TryFromPrimitiveError;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{command::Command, get_exit_arch_regs, GuestReg, Regs, CPU};
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Enum)]
|
#[derive(Debug, Clone, Enum)]
|
||||||
pub enum BackdoorArgs {
|
pub enum ExitArgs {
|
||||||
Ret,
|
Ret,
|
||||||
Cmd,
|
Cmd,
|
||||||
Arg1,
|
Arg1,
|
||||||
@ -45,177 +16,35 @@ pub enum BackdoorArgs {
|
|||||||
Arg6,
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SyncBackdoor {
|
pub struct SyncExit {
|
||||||
command: Command,
|
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]
|
#[must_use]
|
||||||
pub fn command(&self) -> &Command {
|
pub fn command(&self) -> &Command {
|
||||||
&self.command
|
&self.command
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ret(&self, cpu: &CPU, value: GuestReg) -> Result<(), SyncBackdoorError> {
|
pub fn ret(&self, cpu: &CPU, value: GuestReg) {
|
||||||
Ok(cpu.write_reg(self.arch_regs_map[BackdoorArgs::Ret], value)?)
|
cpu.write_reg(get_exit_arch_regs()[ExitArgs::Ret], value)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn ret_reg(&self) -> Regs {
|
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 {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.command)
|
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},
|
tuples::{tuple_list, Handler, Merge},
|
||||||
AsSlice,
|
AsSlice,
|
||||||
};
|
};
|
||||||
pub use libafl_qemu::emu::Qemu;
|
pub use libafl_qemu::qemu::Qemu;
|
||||||
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
|
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
|
||||||
use libafl_qemu::QemuCmpLogHelper;
|
use libafl_qemu::QemuCmpLogHelper;
|
||||||
use libafl_qemu::{edges, QemuEdgeCoverageHelper, QemuExecutor, QemuHooks};
|
use libafl_qemu::{edges, QemuEdgeCoverageHelper, QemuExecutor, QemuHooks};
|
||||||
@ -454,7 +454,7 @@ pub mod pybind {
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use libafl_bolts::core_affinity::Cores;
|
use libafl_bolts::core_affinity::Cores;
|
||||||
use libafl_qemu::emu::pybind::Qemu;
|
use libafl_qemu::qemu::pybind::Qemu;
|
||||||
use pyo3::{prelude::*, types::PyBytes};
|
use pyo3::{prelude::*, types::PyBytes};
|
||||||
|
|
||||||
use crate::qemu;
|
use crate::qemu;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user