Better RW errors for QEMU (#2260)

* better error for rw in qemu.

* fix python

* clippy

* Fix error in fuzzers

* Fix error in fuzzers

* fix systemmode error

* import
This commit is contained in:
Romain Malmain 2024-05-30 15:14:17 +02:00 committed by GitHub
parent e912216a37
commit 1102ea0fe7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 231 additions and 147 deletions

View File

@ -28,7 +28,7 @@ use libafl_bolts::{
use libafl_qemu::{
drcov::QemuDrCovHelper, elf::EasyElf, ArchExtras, CallingConvention, GuestAddr, GuestReg,
MmapPerms, Qemu, QemuExecutor, QemuExitReason, QemuHooks,
QemuInstrumentationAddressRangeFilter, QemuShutdownCause, Regs,
QemuInstrumentationAddressRangeFilter, QemuRWError, QemuShutdownCause, Regs,
};
use rangemap::RangeMap;
@ -155,7 +155,7 @@ pub fn fuzz() {
let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap();
let reset = |buf: &[u8], len: GuestReg| -> Result<(), String> {
let reset = |buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> {
unsafe {
qemu.write_mem(input_addr, buf);
qemu.write_reg(Regs::Pc, test_one_input_ptr)?;

View File

@ -158,7 +158,7 @@ impl<'a> Client<'a> {
let ret_addr: GuestAddr = qemu
.read_return_address()
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:}")))?;
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:?}")))?;
log::debug!("ret_addr = {ret_addr:#x}");
qemu.set_breakpoint(ret_addr);

View File

@ -24,15 +24,15 @@ impl<'a> Harness<'a> {
let pc: GuestReg = qemu
.read_reg(Regs::Pc)
.map_err(|e| Error::unknown(format!("Failed to read PC: {e:}")))?;
.map_err(|e| Error::unknown(format!("Failed to read PC: {e:?}")))?;
let stack_ptr: GuestAddr = qemu
.read_reg(Regs::Sp)
.map_err(|e| Error::unknown(format!("Failed to read stack pointer: {e:}")))?;
.map_err(|e| Error::unknown(format!("Failed to read stack pointer: {e:?}")))?;
let ret_addr: GuestAddr = qemu
.read_return_address()
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:}")))?;
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:?}")))?;
Ok(Harness {
qemu,
@ -62,23 +62,23 @@ impl<'a> Harness<'a> {
self.qemu
.write_reg(Regs::Pc, self.pc)
.map_err(|e| Error::unknown(format!("Failed to write PC: {e:}")))?;
.map_err(|e| Error::unknown(format!("Failed to write PC: {e:?}")))?;
self.qemu
.write_reg(Regs::Sp, self.stack_ptr)
.map_err(|e| Error::unknown(format!("Failed to write SP: {e:}")))?;
.map_err(|e| Error::unknown(format!("Failed to write SP: {e:?}")))?;
self.qemu
.write_return_address(self.ret_addr)
.map_err(|e| Error::unknown(format!("Failed to write return address: {e:}")))?;
.map_err(|e| Error::unknown(format!("Failed to write return address: {e:?}")))?;
self.qemu
.write_function_argument(CallingConvention::Cdecl, 0, self.input_addr)
.map_err(|e| Error::unknown(format!("Failed to write argument 0: {e:}")))?;
.map_err(|e| Error::unknown(format!("Failed to write argument 0: {e:?}")))?;
self.qemu
.write_function_argument(CallingConvention::Cdecl, 1, len)
.map_err(|e| Error::unknown(format!("Failed to write argument 1: {e:}")))?;
.map_err(|e| Error::unknown(format!("Failed to write argument 1: {e:?}")))?;
unsafe {
let _ = self.qemu.run();
};

View File

@ -32,7 +32,8 @@ use libafl_bolts::{
use libafl_qemu::{
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
elf::EasyElf,
Qemu, QemuExecutor, QemuExitError, QemuExitReason, QemuHooks, QemuShutdownCause, Regs,
Qemu, QemuExecutor, QemuExitError, QemuExitReason, QemuHooks, QemuRWError, QemuShutdownCause,
Regs,
};
use libafl_qemu_sys::GuestPhysAddr;
@ -134,7 +135,7 @@ pub fn fuzz() {
// If the execution stops at any point other then the designated breakpoint (e.g. a breakpoint on a panic method) we consider it a crash
let mut pcs = (0..qemu.num_cpus())
.map(|i| qemu.cpu_from_index(i))
.map(|cpu| -> Result<u32, String> { cpu.read_reg(Regs::Pc) });
.map(|cpu| -> Result<u32, QemuRWError> { cpu.read_reg(Regs::Pc) });
let ret = match pcs
.find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))
{

View File

@ -8,7 +8,7 @@ use pyo3::prelude::*;
pub use strum_macros::EnumIter;
pub use syscall_numbers::aarch64::*;
use crate::{sync_exit::ExitArgs, CallingConvention};
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
#[repr(i32)]
@ -91,27 +91,25 @@ pub fn capstone() -> capstone::arch::arm64::ArchCapstoneBuilder {
pub type GuestReg = u64;
impl crate::ArchExtras for crate::CPU {
fn read_return_address<T>(&self) -> Result<T, String>
fn read_return_address<T>(&self) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
self.read_reg(Regs::Lr)
}
fn write_return_address<T>(&self, val: T) -> Result<(), String>
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
self.write_reg(Regs::Lr, val)
}
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
let reg_id = match idx {
0 => Regs::X0,
@ -120,7 +118,12 @@ impl crate::ArchExtras for crate::CPU {
3 => Regs::X3,
4 => Regs::X4,
5 => Regs::X5,
r => return Err(format!("Unsupported argument: {r:}")),
r => {
return Err(QemuRWError::new_argument_error(
QemuRWErrorKind::Read,
i32::from(r),
))
}
};
self.read_reg(reg_id)
@ -131,19 +134,17 @@ impl crate::ArchExtras for crate::CPU {
conv: CallingConvention,
idx: i32,
val: T,
) -> Result<(), String>
) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
let val: GuestReg = val.into();
match idx {
0 => self.write_reg(Regs::X0, val),
1 => self.write_reg(Regs::X1, val),
_ => Err(format!("Unsupported argument: {idx:}")),
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
}
}
}

View File

@ -8,7 +8,7 @@ use pyo3::prelude::*;
pub use strum_macros::EnumIter;
pub use syscall_numbers::arm::*;
use crate::{sync_exit::ExitArgs, CallingConvention};
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
/// Registers for the ARM instruction set.
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
@ -88,27 +88,25 @@ pub fn capstone_thumb() -> capstone::arch::arm::ArchCapstoneBuilder {
pub type GuestReg = u32;
impl crate::ArchExtras for crate::CPU {
fn read_return_address<T>(&self) -> Result<T, String>
fn read_return_address<T>(&self) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
self.read_reg(Regs::Lr)
}
fn write_return_address<T>(&self, val: T) -> Result<(), String>
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
self.write_reg(Regs::Lr, val)
}
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
let reg_id = match idx {
0 => Regs::R0,
@ -116,7 +114,12 @@ impl crate::ArchExtras for crate::CPU {
2 => Regs::R2,
3 => Regs::R3,
// 4.. would be on the stack, let's not do this for now
r => return Err(format!("Unsupported argument: {r:}")),
r => {
return Err(QemuRWError::new_argument_error(
QemuRWErrorKind::Read,
i32::from(r),
))
}
};
self.read_reg(reg_id)
@ -127,19 +130,17 @@ impl crate::ArchExtras for crate::CPU {
conv: CallingConvention,
idx: i32,
val: T,
) -> Result<(), String>
) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
let val: GuestReg = val.into();
match idx {
0 => self.write_reg(Regs::R0, val),
1 => self.write_reg(Regs::R1, val),
_ => Err(format!("Unsupported argument: {idx:}")),
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
}
}
}

View File

@ -6,7 +6,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
use pyo3::prelude::*;
pub use strum_macros::EnumIter;
use crate::{sync_exit::ExitArgs, CallingConvention};
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
#[repr(i32)]
@ -92,27 +92,25 @@ impl Regs {
pub type GuestReg = u32;
impl crate::ArchExtras for crate::CPU {
fn read_return_address<T>(&self) -> Result<T, String>
fn read_return_address<T>(&self) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
self.read_reg(Regs::Lr)
}
fn write_return_address<T>(&self, val: T) -> Result<(), String>
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
self.write_reg(Regs::Lr, val)
}
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
// Note that 64 bit values may be passed in two registers (and may have padding), then this mapping is off.
let reg_id = match idx {
@ -122,7 +120,12 @@ impl crate::ArchExtras for crate::CPU {
3 => Regs::R3,
4 => Regs::R4,
5 => Regs::R5,
r => return Err(format!("Unsupported argument: {r:}")),
r => {
return Err(QemuRWError::new_argument_error(
QemuRWErrorKind::Read,
i32::from(r),
))
}
};
self.read_reg(reg_id)
@ -132,16 +135,14 @@ impl crate::ArchExtras for crate::CPU {
&self,
conv: CallingConvention,
idx: i32,
_val: T,
) -> Result<(), String>
val: T,
) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
// TODO
Err(format!("Unsupported argument: {idx:}"))
Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, idx))
}
}

View File

@ -8,7 +8,7 @@ use pyo3::prelude::*;
pub use strum_macros::EnumIter;
pub use syscall_numbers::x86::*;
use crate::{sync_exit::ExitArgs, CallingConvention, GuestAddr};
use crate::{sync_exit::ExitArgs, CallingConvention, GuestAddr, QemuRWError, QemuRWErrorKind};
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
#[repr(i32)]
@ -67,7 +67,7 @@ pub fn capstone() -> capstone::arch::x86::ArchCapstoneBuilder {
pub type GuestReg = u32;
impl crate::ArchExtras for crate::CPU {
fn read_return_address<T>(&self) -> Result<T, String>
fn read_return_address<T>(&self) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
@ -77,7 +77,7 @@ impl crate::ArchExtras for crate::CPU {
Ok(GuestReg::from_le_bytes(ret_addr).into())
}
fn write_return_address<T>(&self, val: T) -> Result<(), String>
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
@ -88,13 +88,11 @@ impl crate::ArchExtras for crate::CPU {
Ok(())
}
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
match idx {
0..=1 => {
@ -112,7 +110,12 @@ impl crate::ArchExtras for crate::CPU {
}
Ok(GuestReg::from_le_bytes(val).into())
}
_ => Err(format!("Unsupported argument: {idx:}")),
r => {
return Err(QemuRWError::new_argument_error(
QemuRWErrorKind::Read,
i32::from(r),
))
}
}
}
@ -121,13 +124,11 @@ impl crate::ArchExtras for crate::CPU {
conv: CallingConvention,
idx: i32,
val: T,
) -> Result<(), String>
) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
match idx {
0..=1 => {
@ -146,7 +147,7 @@ impl crate::ArchExtras for crate::CPU {
}
Ok(())
}
_ => Err(format!("Unsupported argument: {idx:}")),
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
}
}
}

View File

@ -7,7 +7,7 @@ use pyo3::prelude::*;
pub use strum_macros::EnumIter;
pub use syscall_numbers::mips::*;
use crate::{sync_exit::ExitArgs, CallingConvention};
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
/// Registers for the MIPS instruction set.
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
@ -88,27 +88,25 @@ pub fn capstone() -> capstone::arch::mips::ArchCapstoneBuilder {
pub type GuestReg = u32;
impl crate::ArchExtras for crate::CPU {
fn read_return_address<T>(&self) -> Result<T, String>
fn read_return_address<T>(&self) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
self.read_reg(Regs::Ra)
}
fn write_return_address<T>(&self, val: T) -> Result<(), String>
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
self.write_reg(Regs::Ra, val)
}
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
let reg_id = match idx {
0 => Regs::A0,
@ -116,7 +114,12 @@ impl crate::ArchExtras for crate::CPU {
2 => Regs::A2,
3 => Regs::A3,
// 4.. would be on the stack, let's not do this for now
r => return Err(format!("Unsupported argument: {r:}")),
r => {
return Err(QemuRWError::new_argument_error(
QemuRWErrorKind::Read,
i32::from(r),
))
}
};
self.read_reg(reg_id)
@ -127,19 +130,17 @@ impl crate::ArchExtras for crate::CPU {
conv: CallingConvention,
idx: i32,
val: T,
) -> Result<(), String>
) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
let val: GuestReg = val.into();
match idx {
0 => self.write_reg(Regs::A0, val),
1 => self.write_reg(Regs::A1, val),
_ => Err(format!("Unsupported argument: {idx:}")),
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
}
}
}

View File

@ -7,7 +7,7 @@ use pyo3::prelude::*;
pub use strum_macros::EnumIter;
pub use syscall_numbers::powerpc::*;
use crate::{sync_exit::ExitArgs, CallingConvention};
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
/// Registers for the MIPS instruction set.
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
@ -128,27 +128,25 @@ pub fn capstone() -> capstone::arch::ppc::ArchCapstoneBuilder {
pub type GuestReg = u32;
impl crate::ArchExtras for crate::CPU {
fn read_return_address<T>(&self) -> Result<T, String>
fn read_return_address<T>(&self) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
self.read_reg(Regs::Lr)
}
fn write_return_address<T>(&self, val: T) -> Result<(), String>
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
self.write_reg(Regs::Lr, val)
}
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
let reg_id = match idx {
0 => Regs::R3,
@ -157,7 +155,12 @@ impl crate::ArchExtras for crate::CPU {
3 => Regs::R6,
4 => Regs::R7,
5 => Regs::R8,
r => return Err(format!("Unsupported argument: {r:}")),
r => {
return Err(QemuRWError::new_argument_error(
QemuRWErrorKind::Read,
i32::from(r),
))
}
};
self.read_reg(reg_id)
@ -168,19 +171,17 @@ impl crate::ArchExtras for crate::CPU {
conv: CallingConvention,
idx: i32,
val: T,
) -> Result<(), String>
) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
let val: GuestReg = val.into();
match idx {
0 => self.write_reg(Regs::R3, val),
1 => self.write_reg(Regs::R4, val),
_ => Err(format!("Unsupported argument: {idx:}")),
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
}
}
}

View File

@ -8,7 +8,7 @@ use pyo3::prelude::*;
pub use strum_macros::EnumIter;
pub use syscall_numbers::x86_64::*;
use crate::{sync_exit::ExitArgs, CallingConvention};
use crate::{sync_exit::ExitArgs, CallingConvention, QemuRWError, QemuRWErrorKind};
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
#[repr(i32)]
@ -76,7 +76,7 @@ pub fn capstone() -> capstone::arch::x86::ArchCapstoneBuilder {
pub type GuestReg = u64;
impl crate::ArchExtras for crate::CPU {
fn read_return_address<T>(&self) -> Result<T, String>
fn read_return_address<T>(&self) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
@ -86,7 +86,7 @@ impl crate::ArchExtras for crate::CPU {
Ok(GuestReg::from_le_bytes(ret_addr).into())
}
fn write_return_address<T>(&self, val: T) -> Result<(), String>
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
@ -97,13 +97,11 @@ impl crate::ArchExtras for crate::CPU {
Ok(())
}
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Read, CallingConvention::Cdecl, conv)?;
let reg_id = match idx {
0 => Regs::Rdi,
@ -112,7 +110,12 @@ impl crate::ArchExtras for crate::CPU {
3 => Regs::Rcx,
4 => Regs::R8,
5 => Regs::R9,
r => return Err(format!("Unsupported argument: {r:}")),
r => {
return Err(QemuRWError::new_argument_error(
QemuRWErrorKind::Read,
i32::from(r),
))
}
};
self.read_reg(reg_id)
@ -123,19 +126,17 @@ impl crate::ArchExtras for crate::CPU {
conv: CallingConvention,
idx: i32,
val: T,
) -> Result<(), String>
) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
if conv != CallingConvention::Cdecl {
return Err(format!("Unsupported calling convention: {conv:#?}"));
}
QemuRWError::check_conv(QemuRWErrorKind::Write, CallingConvention::Cdecl, conv)?;
let val: GuestReg = val.into();
match idx {
0 => self.write_reg(Regs::Rdi, val),
1 => self.write_reg(Regs::Rsi, val),
_ => Err(format!("Unsupported argument: {idx:}")),
r => Err(QemuRWError::new_argument_error(QemuRWErrorKind::Write, r)),
}
}
}

View File

@ -28,8 +28,8 @@ use crate::{
sync_exit::ExitArgs,
Emulator, EmulatorExitHandler, EmulatorMemoryChunk, ExitHandlerError, ExitHandlerResult,
GuestReg, HasInstrumentationFilter, InputLocation, IsFilter, IsSnapshotManager, Qemu,
QemuHelperTuple, QemuInstrumentationAddressRangeFilter, Regs, StdEmulatorExitHandler,
StdInstrumentationFilter, CPU,
QemuHelperTuple, QemuInstrumentationAddressRangeFilter, QemuRWError, Regs,
StdEmulatorExitHandler, StdInstrumentationFilter, CPU,
};
pub mod parser;
@ -217,13 +217,13 @@ pub type AddressRangeFilterCommand = FilterCommand<QemuInstrumentationAddressRan
#[derive(Debug, Clone)]
pub enum CommandError {
UnknownCommand(GuestReg),
RegError(String),
RWError(QemuRWError),
VersionDifference(u64),
}
impl From<String> for CommandError {
fn from(error_string: String) -> Self {
CommandError::RegError(error_string)
impl From<QemuRWError> for CommandError {
fn from(error: QemuRWError) -> Self {
CommandError::RWError(error)
}
}

View File

@ -35,7 +35,7 @@ use crate::{
sys::TCGTemp,
BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, EmulatorMemoryChunk, GuestReg, HookData,
HookId, InstructionHookId, MemAccessInfo, Qemu, QemuExitError, QemuExitReason, QemuHelperTuple,
QemuInitError, QemuShutdownCause, QemuSnapshotCheckResult, ReadHookId, Regs,
QemuInitError, QemuRWError, QemuShutdownCause, QemuSnapshotCheckResult, ReadHookId, Regs,
StdInstrumentationFilter, WriteHookId, CPU,
};
@ -570,10 +570,10 @@ 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<(), QemuRWError>
where
T: Num + PartialOrd + Copy + Into<GuestReg>,
R: Into<i32>,
R: Into<i32> + Clone,
{
self.qemu.write_reg(reg, val)
}
@ -581,10 +581,10 @@ where
#[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, QemuRWError>
where
T: Num + PartialOrd + Copy + From<GuestReg>,
R: Into<i32>,
R: Into<i32> + Clone,
{
self.qemu.read_reg(reg)
}

View File

@ -111,6 +111,61 @@ pub struct QemuSnapshotCheckResult {
nb_page_inconsistencies: u64,
}
#[derive(Debug, Clone)]
pub enum QemuRWErrorKind {
Read,
Write,
}
#[derive(Debug, Clone)]
pub enum QemuRWErrorCause {
WrongCallingConvention(CallingConvention, CallingConvention), // expected, given
WrongArgument(i32),
CurrentCpuNotFound,
Reg(i32),
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct QemuRWError {
kind: QemuRWErrorKind,
cause: QemuRWErrorCause,
cpu: Option<CPUStatePtr>, // Only makes sense when cause != CurrentCpuNotFound
}
impl QemuRWError {
#[must_use]
pub fn new(kind: QemuRWErrorKind, cause: QemuRWErrorCause, cpu: Option<CPUStatePtr>) -> Self {
Self { kind, cause, cpu }
}
#[must_use]
pub fn current_cpu_not_found(kind: QemuRWErrorKind) -> Self {
Self::new(kind, QemuRWErrorCause::CurrentCpuNotFound, None)
}
#[must_use]
pub fn new_argument_error(kind: QemuRWErrorKind, reg_id: i32) -> Self {
Self::new(kind, QemuRWErrorCause::WrongArgument(reg_id), None)
}
pub fn check_conv(
kind: QemuRWErrorKind,
expected_conv: CallingConvention,
given_conv: CallingConvention,
) -> Result<(), Self> {
if expected_conv != given_conv {
return Err(Self::new(
kind,
QemuRWErrorCause::WrongCallingConvention(expected_conv, given_conv),
None,
));
}
Ok(())
}
}
/// Represents a QEMU snapshot check result for which no error was detected
impl Default for QemuSnapshotCheckResult {
fn default() -> Self {
@ -183,7 +238,7 @@ pub struct CPU {
ptr: CPUStatePtr,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub enum CallingConvention {
Cdecl,
}
@ -283,13 +338,13 @@ impl From<libafl_qemu_sys::MemOpIdx> for MemAccessInfo {
}
pub trait ArchExtras {
fn read_return_address<T>(&self) -> Result<T, String>
fn read_return_address<T>(&self) -> Result<T, QemuRWError>
where
T: From<GuestReg>;
fn write_return_address<T>(&self, val: T) -> Result<(), String>
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
where
T: Into<GuestReg>;
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, QemuRWError>
where
T: From<GuestReg>;
fn write_function_argument<T>(
@ -297,7 +352,7 @@ pub trait ArchExtras {
conv: CallingConvention,
idx: i32,
val: T,
) -> Result<(), String>
) -> Result<(), QemuRWError>
where
T: Into<GuestReg>;
}
@ -349,37 +404,46 @@ 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<(), QemuRWError>
where
R: Into<i32>,
R: Into<i32> + Clone,
T: Into<GuestReg>,
{
let reg = reg.into();
let reg_id = reg.clone().into();
#[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_id, addr_of!(val) as *const u8) };
if success == 0 {
Err(format!("Failed to write to register {reg}"))
Err(QemuRWError {
kind: QemuRWErrorKind::Write,
cause: QemuRWErrorCause::Reg(reg.into()),
cpu: Some(self.ptr),
})
} 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, QemuRWError>
where
R: Into<i32>,
R: Into<i32> + Clone,
T: From<GuestReg>,
{
unsafe {
let reg = reg.into();
let reg_id = reg.clone().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_id, val.as_mut_ptr() as *mut u8);
if success == 0 {
Err(format!("Failed to read register {reg}"))
Err(QemuRWError {
kind: QemuRWErrorKind::Write,
cause: QemuRWErrorCause::Reg(reg.into()),
cpu: Some(self.ptr),
})
} else {
#[cfg(feature = "be")]
return Ok(GuestReg::from_be(val.assume_init()).into());
@ -692,20 +756,24 @@ 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<(), QemuRWError>
where
T: Num + PartialOrd + Copy + Into<GuestReg>,
R: Into<i32>,
R: Into<i32> + Clone,
{
self.current_cpu().unwrap().write_reg(reg, val)
self.current_cpu()
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Write))?
.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, QemuRWError>
where
T: Num + PartialOrd + Copy + From<GuestReg>,
R: Into<i32>,
R: Into<i32> + Clone,
{
self.current_cpu().unwrap().read_reg(reg)
self.current_cpu()
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Read))?
.read_reg(reg)
}
pub fn set_breakpoint(&self, addr: GuestAddr) {
@ -947,30 +1015,34 @@ impl Qemu {
}
impl ArchExtras for Qemu {
fn read_return_address<T>(&self) -> Result<T, String>
fn read_return_address<T>(&self) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
self.current_cpu()
.ok_or("Failed to get current CPU")?
.ok_or(QemuRWError {
kind: QemuRWErrorKind::Read,
cause: QemuRWErrorCause::CurrentCpuNotFound,
cpu: None,
})?
.read_return_address::<T>()
}
fn write_return_address<T>(&self, val: T) -> Result<(), String>
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
self.current_cpu()
.ok_or("Failed to get current CPU")?
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Write))?
.write_return_address::<T>(val)
}
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, String>
fn read_function_argument<T>(&self, conv: CallingConvention, idx: u8) -> Result<T, QemuRWError>
where
T: From<GuestReg>,
{
self.current_cpu()
.ok_or("Failed to get current CPU")?
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Read))?
.read_function_argument::<T>(conv, idx)
}
@ -979,12 +1051,12 @@ impl ArchExtras for Qemu {
conv: CallingConvention,
idx: i32,
val: T,
) -> Result<(), String>
) -> Result<(), QemuRWError>
where
T: Into<GuestReg>,
{
self.current_cpu()
.ok_or("Failed to get current CPU")?
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Write))?
.write_function_argument::<T>(conv, idx, val)
}
}
@ -1191,11 +1263,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(|_| PyValueError::new_err("write register error"))
}
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(|_| PyValueError::new_err("read register error"))
}
fn set_breakpoint(&self, addr: GuestAddr) {