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:
parent
e912216a37
commit
1102ea0fe7
@ -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)?;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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)))
|
||||
{
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user