Skip to content

Commit

Permalink
libafl_qemu: Structured error types for reading/writing registers
Browse files Browse the repository at this point in the history
  • Loading branch information
langston-barrett committed May 17, 2024
1 parent d05adf7 commit 0156a1c
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 32 deletions.
10 changes: 5 additions & 5 deletions libafl_qemu/src/arch/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl crate::ArchExtras for crate::CPU {
where
T: From<GuestReg>,
{
let stack_ptr: GuestReg = self.read_reg(Regs::Rsp)?;
let stack_ptr: GuestReg = self.read_reg(Regs::Rsp).map_err(|e| e.to_string())?;
let mut ret_addr = [0; size_of::<GuestReg>()];
unsafe { self.read_mem(stack_ptr, &mut ret_addr) };
Ok(GuestReg::from_le_bytes(ret_addr).into())
Expand All @@ -90,7 +90,7 @@ impl crate::ArchExtras for crate::CPU {
where
T: Into<GuestReg>,
{
let stack_ptr: GuestReg = self.read_reg(Regs::Rsp)?;
let stack_ptr: GuestReg = self.read_reg(Regs::Rsp).map_err(|e| e.to_string())?;
let val: GuestReg = val.into();
let ret_addr = val.to_le_bytes();
unsafe { self.write_mem(stack_ptr, &ret_addr) };
Expand All @@ -115,7 +115,7 @@ impl crate::ArchExtras for crate::CPU {
r => return Err(format!("Unsupported argument: {r:}")),
};

self.read_reg(reg_id)
self.read_reg(reg_id).map_err(|e| e.to_string())
}

fn write_function_argument<T>(
Expand All @@ -133,8 +133,8 @@ impl crate::ArchExtras for crate::CPU {

let val: GuestReg = val.into();
match idx {
0 => self.write_reg(Regs::Rdi, val),
1 => self.write_reg(Regs::Rsi, val),
0 => self.write_reg(Regs::Rdi, val).map_err(|e| e.to_string()),
1 => self.write_reg(Regs::Rsi, val).map_err(|e| e.to_string()),
_ => Err(format!("Unsupported argument: {idx:}")),
}
}
Expand Down
16 changes: 11 additions & 5 deletions libafl_qemu/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use crate::{
executor::QemuExecutorState, get_exit_arch_regs, sync_exit::ExitArgs, Emulator,
EmulatorExitHandler, EmulatorMemoryChunk, ExitHandlerError, ExitHandlerResult, GuestReg,
HasInstrumentationFilter, InputLocation, IsFilter, IsSnapshotManager, Qemu, QemuHelperTuple,
QemuInstrumentationAddressRangeFilter, Regs, StdEmulatorExitHandler, StdInstrumentationFilter,
CPU,
QemuInstrumentationAddressRangeFilter, ReadRegError, Regs, StdEmulatorExitHandler,
StdInstrumentationFilter, WriteRegError, CPU,
};

pub const VERSION: u64 = bindings::LIBAFL_QEMU_HDR_VERSION_NUMBER as u64;
Expand Down Expand Up @@ -121,9 +121,15 @@ impl From<TryFromPrimitiveError<NativeCommand>> for CommandError {
}
}

impl From<String> for CommandError {
fn from(error_string: String) -> Self {
CommandError::RegError(error_string)
impl<R: Debug> From<ReadRegError<R>> for CommandError {
fn from(e: ReadRegError<R>) -> Self {
CommandError::RegError(e.to_string())
}
}

impl<R: Debug, T: Debug> From<WriteRegError<R, T>> for CommandError {
fn from(e: WriteRegError<R, T>) -> Self {
CommandError::RegError(e.to_string())
}
}

Expand Down
11 changes: 6 additions & 5 deletions libafl_qemu/src/emu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ use crate::{
sys::TCGTemp,
BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, EmulatorMemoryChunk, GuestReg, HookData,
HookId, InstructionHookId, MemAccessInfo, Qemu, QemuExitError, QemuExitReason, QemuHelperTuple,
QemuInitError, QemuShutdownCause, ReadHookId, Regs, StdInstrumentationFilter, WriteHookId, CPU,
QemuInitError, QemuShutdownCause, ReadHookId, ReadRegError, Regs, StdInstrumentationFilter,
WriteHookId, WriteRegError, CPU,
};

#[cfg(emulation_mode = "usermode")]
Expand Down Expand Up @@ -506,21 +507,21 @@ where
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn write_reg<R, T>(&self, reg: R, val: T) -> Result<(), String>
pub fn write_reg<R, T>(&self, reg: R, val: T) -> Result<(), WriteRegError<R, T>>
where
T: Num + PartialOrd + Copy + Into<GuestReg>,
R: Into<i32>,
R: Copy + Into<i32>,
{
self.qemu.write_reg(reg, val)
}

#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn read_reg<R, T>(&self, reg: R) -> Result<T, String>
pub fn read_reg<R, T>(&self, reg: R) -> Result<T, ReadRegError<R>>
where
T: Num + PartialOrd + Copy + From<GuestReg>,
R: Into<i32>,
R: Copy + Into<i32>,
{
self.qemu.read_reg(reg)
}
Expand Down
68 changes: 51 additions & 17 deletions libafl_qemu/src/qemu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,35 @@ pub trait ArchExtras {
T: Into<GuestReg>;
}

#[derive(Copy, Clone, Debug)]
pub struct ReadRegError<R>(pub R);

impl<R: fmt::Debug> Display for ReadRegError<R> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Failed to read register {:?}", self.0)
}
}

impl<R: fmt::Debug> std::error::Error for ReadRegError<R> {}

#[derive(Copy, Clone, Debug)]
pub struct WriteRegError<R, T> {
pub reg: R,
pub val: T,
}

impl<R: fmt::Debug, T: fmt::Debug> Display for WriteRegError<R, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"Failed to write value {:?} to register {:?}",
self.val, self.reg
)
}
}

impl<R: fmt::Debug, T: fmt::Debug> std::error::Error for WriteRegError<R, T> {}

#[allow(clippy::unused_self)]
impl CPU {
#[must_use]
Expand Down Expand Up @@ -335,37 +364,38 @@ impl CPU {
unsafe { libafl_qemu_num_regs(self.ptr) }
}

pub fn write_reg<R, T>(&self, reg: R, val: T) -> Result<(), String>
pub fn write_reg<R, T>(&self, reg: R, val: T) -> Result<(), WriteRegError<R, T>>
where
R: Into<i32>,
T: Into<GuestReg>,
R: Copy + Into<i32>,
T: Copy + Into<GuestReg>,
{
let reg = reg.into();
let val0 = val;

#[cfg(feature = "be")]
let val = GuestReg::to_be(val.into());

#[cfg(not(feature = "be"))]
let val = GuestReg::to_le(val.into());

let success = unsafe { libafl_qemu_write_reg(self.ptr, reg, addr_of!(val) as *const u8) };
let success =
unsafe { libafl_qemu_write_reg(self.ptr, reg.into(), addr_of!(val) as *const u8) };
if success == 0 {
Err(format!("Failed to write to register {reg}"))
Err(WriteRegError { reg, val: val0 })
} else {
Ok(())
}
}

pub fn read_reg<R, T>(&self, reg: R) -> Result<T, String>
pub fn read_reg<R, T>(&self, reg: R) -> Result<T, ReadRegError<R>>
where
R: Into<i32>,
R: Copy + Into<i32>,
T: From<GuestReg>,
{
unsafe {
let reg = reg.into();
let mut val = MaybeUninit::uninit();
let success = libafl_qemu_read_reg(self.ptr, reg, val.as_mut_ptr() as *mut u8);
let success = libafl_qemu_read_reg(self.ptr, reg.into(), val.as_mut_ptr() as *mut u8);
if success == 0 {
Err(format!("Failed to read register {reg}"))
Err(ReadRegError(reg))
} else {
#[cfg(feature = "be")]
return Ok(GuestReg::from_be(val.assume_init()).into());
Expand Down Expand Up @@ -678,18 +708,18 @@ impl Qemu {
self.current_cpu().unwrap().num_regs()
}

pub fn write_reg<R, T>(&self, reg: R, val: T) -> Result<(), String>
pub fn write_reg<R, T>(&self, reg: R, val: T) -> Result<(), WriteRegError<R, T>>
where
T: Num + PartialOrd + Copy + Into<GuestReg>,
R: Into<i32>,
R: Copy + Into<i32>,
{
self.current_cpu().unwrap().write_reg(reg, val)
}

pub fn read_reg<R, T>(&self, reg: R) -> Result<T, String>
pub fn read_reg<R, T>(&self, reg: R) -> Result<T, ReadRegError<R>>
where
T: Num + PartialOrd + Copy + From<GuestReg>,
R: Into<i32>,
R: Copy + Into<i32>,
{
self.current_cpu().unwrap().read_reg(reg)
}
Expand Down Expand Up @@ -1177,11 +1207,15 @@ pub mod pybind {
}

fn write_reg(&self, reg: i32, val: GuestUsize) -> PyResult<()> {
self.qemu.write_reg(reg, val).map_err(PyValueError::new_err)
self.qemu
.write_reg(reg, val)
.map_err(|e| PyValueError::new_err(e.to_string()))
}

fn read_reg(&self, reg: i32) -> PyResult<GuestUsize> {
self.qemu.read_reg(reg).map_err(PyValueError::new_err)
self.qemu
.read_reg(reg)
.map_err(|e| PyValueError::new_err(e.to_string()))
}

fn set_breakpoint(&self, addr: GuestAddr) {
Expand Down

0 comments on commit 0156a1c

Please sign in to comment.