Update QEMU syshook ret value (#3092)

* update syshook ret value
This commit is contained in:
Romain Malmain 2025-03-31 15:51:01 +02:00 committed by GitHub
parent c68b30ae2a
commit 184b69be8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 178 additions and 145 deletions

View File

@ -11,7 +11,7 @@ use crate::cargo_add_rpath;
pub const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; pub const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
pub const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; pub const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
pub const QEMU_REVISION: &str = "2a676d9cd8c474b5c0db1d77d2769e56e2ed8524"; pub const QEMU_REVISION: &str = "97bef506eed24ee8d0eda4a07c4419c55dae4acb";
pub struct BuildResult { pub struct BuildResult {
pub qemu_path: PathBuf, pub qemu_path: PathBuf,

View File

@ -1,5 +1,5 @@
/* 1.87.0-nightly */ /* 1.87.0-nightly */
/* qemu git hash: 2a676d9cd8c474b5c0db1d77d2769e56e2ed8524 */ /* qemu git hash: 97bef506eed24ee8d0eda4a07c4419c55dae4acb */
/* automatically generated by rust-bindgen 0.71.1 */ /* automatically generated by rust-bindgen 0.71.1 */
use libc::siginfo_t; use libc::siginfo_t;
@ -8687,37 +8687,131 @@ unsafe extern "C" {
unsafe extern "C" { unsafe extern "C" {
pub fn libafl_hook_new_thread_run(env: *mut CPUArchState, tid: u32) -> bool; pub fn libafl_hook_new_thread_run(env: *mut CPUArchState, tid: u32) -> bool;
} }
pub const libafl_syshook_ret_tag_LIBAFL_SYSHOOK_RUN: libafl_syshook_ret_tag =
libafl_syshook_ret_tag(0);
pub const libafl_syshook_ret_tag_LIBAFL_SYSHOOK_SKIP: libafl_syshook_ret_tag =
libafl_syshook_ret_tag(1);
impl ::std::ops::BitOr<libafl_syshook_ret_tag> for libafl_syshook_ret_tag {
type Output = Self;
#[inline]
fn bitor(self, other: Self) -> Self {
libafl_syshook_ret_tag(self.0 | other.0)
}
}
impl ::std::ops::BitOrAssign for libafl_syshook_ret_tag {
#[inline]
fn bitor_assign(&mut self, rhs: libafl_syshook_ret_tag) {
self.0 |= rhs.0;
}
}
impl ::std::ops::BitAnd<libafl_syshook_ret_tag> for libafl_syshook_ret_tag {
type Output = Self;
#[inline]
fn bitand(self, other: Self) -> Self {
libafl_syshook_ret_tag(self.0 & other.0)
}
}
impl ::std::ops::BitAndAssign for libafl_syshook_ret_tag {
#[inline]
fn bitand_assign(&mut self, rhs: libafl_syshook_ret_tag) {
self.0 &= rhs.0;
}
}
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct libafl_syshook_ret_tag(pub ::std::os::raw::c_uint);
#[repr(C)] #[repr(C)]
#[derive(Debug, Default, Copy, Clone)] #[derive(Copy, Clone)]
pub struct syshook_ret { pub struct libafl_syshook_ret {
pub retval: target_ulong, pub tag: libafl_syshook_ret_tag,
pub skip_syscall: bool, pub __bindgen_anon_1: libafl_syshook_ret__bindgen_ty_1,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union libafl_syshook_ret__bindgen_ty_1 {
pub syshook_skip_retval: target_ulong,
} }
#[allow(clippy::unnecessary_operation, clippy::identity_op)] #[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = { const _: () = {
["Size of syshook_ret"][::std::mem::size_of::<syshook_ret>() - 16usize]; ["Size of libafl_syshook_ret__bindgen_ty_1"]
["Alignment of syshook_ret"][::std::mem::align_of::<syshook_ret>() - 8usize]; [::std::mem::size_of::<libafl_syshook_ret__bindgen_ty_1>() - 8usize];
["Offset of field: syshook_ret::retval"][::std::mem::offset_of!(syshook_ret, retval) - 0usize]; ["Alignment of libafl_syshook_ret__bindgen_ty_1"]
["Offset of field: syshook_ret::skip_syscall"] [::std::mem::align_of::<libafl_syshook_ret__bindgen_ty_1>() - 8usize];
[::std::mem::offset_of!(syshook_ret, skip_syscall) - 8usize]; ["Offset of field: libafl_syshook_ret__bindgen_ty_1::syshook_skip_retval"]
[::std::mem::offset_of!(libafl_syshook_ret__bindgen_ty_1, syshook_skip_retval) - 0usize];
}; };
impl Default for libafl_syshook_ret__bindgen_ty_1 {
fn default() -> Self {
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
unsafe {
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
s.assume_init()
}
}
}
impl ::std::fmt::Debug for libafl_syshook_ret__bindgen_ty_1 {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "libafl_syshook_ret__bindgen_ty_1 {{ union }}")
}
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
["Size of libafl_syshook_ret"][::std::mem::size_of::<libafl_syshook_ret>() - 16usize];
["Alignment of libafl_syshook_ret"][::std::mem::align_of::<libafl_syshook_ret>() - 8usize];
["Offset of field: libafl_syshook_ret::tag"]
[::std::mem::offset_of!(libafl_syshook_ret, tag) - 0usize];
};
impl Default for libafl_syshook_ret {
fn default() -> Self {
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
unsafe {
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
s.assume_init()
}
}
}
impl ::std::fmt::Debug for libafl_syshook_ret {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(
f,
"libafl_syshook_ret {{ tag: {:?}, __bindgen_anon_1: {:?} }}",
self.tag, self.__bindgen_anon_1
)
}
}
pub type libafl_pre_syscall_cb = ::std::option::Option<
unsafe extern "C" fn(
data: u64,
sys_num: ::std::os::raw::c_int,
arg0: target_ulong,
arg1: target_ulong,
arg2: target_ulong,
arg3: target_ulong,
arg4: target_ulong,
arg5: target_ulong,
arg6: target_ulong,
arg7: target_ulong,
) -> libafl_syshook_ret,
>;
pub type libafl_post_syscall_cb = ::std::option::Option<
unsafe extern "C" fn(
data: u64,
ret: target_ulong,
sys_num: ::std::os::raw::c_int,
arg0: target_ulong,
arg1: target_ulong,
arg2: target_ulong,
arg3: target_ulong,
arg4: target_ulong,
arg5: target_ulong,
arg6: target_ulong,
arg7: target_ulong,
) -> target_ulong,
>;
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct libafl_pre_syscall_hook { pub struct libafl_pre_syscall_hook {
pub callback: ::std::option::Option< pub callback: libafl_pre_syscall_cb,
unsafe extern "C" fn(
data: u64,
sys_num: ::std::os::raw::c_int,
arg0: target_ulong,
arg1: target_ulong,
arg2: target_ulong,
arg3: target_ulong,
arg4: target_ulong,
arg5: target_ulong,
arg6: target_ulong,
arg7: target_ulong,
) -> syshook_ret,
>,
pub data: u64, pub data: u64,
pub num: usize, pub num: usize,
pub next: *mut libafl_pre_syscall_hook, pub next: *mut libafl_pre_syscall_hook,
@ -8748,21 +8842,7 @@ impl Default for libafl_pre_syscall_hook {
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct libafl_post_syscall_hook { pub struct libafl_post_syscall_hook {
pub callback: ::std::option::Option< pub callback: libafl_post_syscall_cb,
unsafe extern "C" fn(
data: u64,
ret: target_ulong,
sys_num: ::std::os::raw::c_int,
arg0: target_ulong,
arg1: target_ulong,
arg2: target_ulong,
arg3: target_ulong,
arg4: target_ulong,
arg5: target_ulong,
arg6: target_ulong,
arg7: target_ulong,
) -> target_ulong,
>,
pub data: u64, pub data: u64,
pub num: usize, pub num: usize,
pub next: *mut libafl_post_syscall_hook, pub next: *mut libafl_post_syscall_hook,
@ -8792,43 +8872,10 @@ impl Default for libafl_post_syscall_hook {
} }
} }
unsafe extern "C" { unsafe extern "C" {
pub fn libafl_add_pre_syscall_hook( pub fn libafl_add_pre_syscall_hook(callback: libafl_pre_syscall_cb, data: u64) -> usize;
callback: ::std::option::Option<
unsafe extern "C" fn(
data: u64,
sys_num: ::std::os::raw::c_int,
arg0: target_ulong,
arg1: target_ulong,
arg2: target_ulong,
arg3: target_ulong,
arg4: target_ulong,
arg5: target_ulong,
arg6: target_ulong,
arg7: target_ulong,
) -> syshook_ret,
>,
data: u64,
) -> usize;
} }
unsafe extern "C" { unsafe extern "C" {
pub fn libafl_add_post_syscall_hook( pub fn libafl_add_post_syscall_hook(callback: libafl_post_syscall_cb, data: u64) -> usize;
callback: ::std::option::Option<
unsafe extern "C" fn(
data: u64,
ret: target_ulong,
sys_num: ::std::os::raw::c_int,
arg0: target_ulong,
arg1: target_ulong,
arg2: target_ulong,
arg3: target_ulong,
arg4: target_ulong,
arg5: target_ulong,
arg6: target_ulong,
arg7: target_ulong,
) -> target_ulong,
>,
data: u64,
) -> usize;
} }
unsafe extern "C" { unsafe extern "C" {
pub fn libafl_qemu_remove_pre_syscall_hook(num: usize) -> ::std::os::raw::c_int; pub fn libafl_qemu_remove_pre_syscall_hook(num: usize) -> ::std::os::raw::c_int;

View File

@ -1,5 +1,5 @@
/* 1.87.0-nightly */ /* 1.87.0-nightly */
/* qemu git hash: 2a676d9cd8c474b5c0db1d77d2769e56e2ed8524 */ /* qemu git hash: 97bef506eed24ee8d0eda4a07c4419c55dae4acb */
/* automatically generated by rust-bindgen 0.71.1 */ /* automatically generated by rust-bindgen 0.71.1 */
#[repr(C)] #[repr(C)]

View File

@ -1,5 +1,5 @@
/* 1.87.0-nightly */ /* 1.87.0-nightly */
/* qemu git hash: 2a676d9cd8c474b5c0db1d77d2769e56e2ed8524 */ /* qemu git hash: 97bef506eed24ee8d0eda4a07c4419c55dae4acb */
/* automatically generated by rust-bindgen 0.71.1 */ /* automatically generated by rust-bindgen 0.71.1 */
#[repr(C)] #[repr(C)]

View File

@ -98,7 +98,7 @@ pub fn python_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
#[cfg(feature = "usermode")] #[cfg(feature = "usermode")]
m.add_class::<GuestMaps>()?; m.add_class::<GuestMaps>()?;
m.add_class::<SyscallHookResult>()?; m.add_class::<pybind::SyscallHookResult>()?;
m.add_class::<pybind::Qemu>()?; m.add_class::<pybind::Qemu>()?;
Ok(()) Ok(())

View File

@ -578,9 +578,9 @@ impl AsanGiovese {
} }
_ => (), _ => (),
} }
SyscallHookResult::new(Some(r)) SyscallHookResult::Skip(r)
} else { } else {
SyscallHookResult::new(None) SyscallHookResult::Run
} }
} }
@ -1350,9 +1350,9 @@ where
} }
_ => (), _ => (),
} }
SyscallHookResult::new(Some(0)) SyscallHookResult::Skip(0)
} else { } else {
SyscallHookResult::new(None) SyscallHookResult::Run
} }
} }

View File

@ -400,13 +400,13 @@ where
let first_parameter = unsafe { let first_parameter = unsafe {
if (*c_array.offset(1)).is_null() { if (*c_array.offset(1)).is_null() {
return SyscallHookResult::new(None); return SyscallHookResult::Run;
} }
CStr::from_ptr(*c_array.offset(1)).to_string_lossy() CStr::from_ptr(*c_array.offset(1)).to_string_lossy()
}; };
let second_parameter = unsafe { let second_parameter = unsafe {
if (*c_array.offset(2)).is_null() { if (*c_array.offset(2)).is_null() {
return SyscallHookResult::new(None); return SyscallHookResult::Run;
} }
CStr::from_ptr(*c_array.offset(2)).to_string_lossy() CStr::from_ptr(*c_array.offset(2)).to_string_lossy()
}; };
@ -419,9 +419,9 @@ where
//println!("PARAMETERS First {} Second {}", first_parameter, second_ //println!("PARAMETERS First {} Second {}", first_parameter, second_
} }
SyscallHookResult::new(Some(0)) SyscallHookResult::Skip(0)
} else { } else {
SyscallHookResult::new(None) SyscallHookResult::Run
} }
} }

View File

@ -114,7 +114,7 @@ where
{ {
let h = emulator_modules.get_mut::<RedirectStdinModule>().unwrap(); let h = emulator_modules.get_mut::<RedirectStdinModule>().unwrap();
if h.input_addr.is_null() { if h.input_addr.is_null() {
return SyscallHookResult::new(None); return SyscallHookResult::Run;
} }
if syscall == SYS_read as i32 && x0 == 0 { if syscall == SYS_read as i32 && x0 == 0 {
/* /*
@ -143,7 +143,7 @@ where
}; };
// println!("copied {}", size); // println!("copied {}", size);
h.read += size as usize; h.read += size as usize;
return SyscallHookResult::new(Some(size)); return SyscallHookResult::Skip(size);
} }
SyscallHookResult::new(None) SyscallHookResult::Run
} }

View File

@ -888,11 +888,11 @@ where
if i64::from(sys_num) == SYS_munmap { if i64::from(sys_num) == SYS_munmap {
let h = emulator_modules.get_mut::<SnapshotModule>().unwrap(); let h = emulator_modules.get_mut::<SnapshotModule>().unwrap();
if !h.is_unmap_allowed(a0 as GuestAddr, a1 as usize) { if !h.is_unmap_allowed(a0 as GuestAddr, a1 as usize) {
return SyscallHookResult::new(Some(0)); return SyscallHookResult::Skip(0);
} }
} }
SyscallHookResult::new(None) SyscallHookResult::Run
} }
#[expect(non_upper_case_globals, clippy::too_many_arguments)] #[expect(non_upper_case_globals, clippy::too_many_arguments)]

View File

@ -8,8 +8,6 @@ use core::{ffi::c_void, fmt::Debug, mem::transmute, ptr};
use libafl::executors::hooks::inprocess::inprocess_get_state; use libafl::executors::hooks::inprocess::inprocess_get_state;
use libafl_qemu_sys::{CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, GuestUsize}; use libafl_qemu_sys::{CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, GuestUsize};
#[cfg(feature = "python")]
use pyo3::{FromPyObject, pyclass, pymethods};
use crate::{ use crate::{
HookData, HookId, HookData, HookId,
@ -85,12 +83,20 @@ pub enum Hook<F, C, R: Clone> {
Empty, Empty,
} }
/// Syshook result representation
///
/// # Safety
///
/// This enum is shadowed by another enum in QEMU (`libafl_syshook_ret`). Any change made to this
/// enum should be propagated to the C enum as well.
#[repr(C)] #[repr(C)]
#[cfg_attr(feature = "python", pyclass)] pub enum SyscallHookResult {
#[cfg_attr(feature = "python", derive(FromPyObject))] /// Runs the syscall after the hook is executed. The return value will be the one of the
pub struct SyscallHookResult { /// syscall itself.
pub retval: GuestAddr, /// If you need to change the return value of the syscall, please use a post-syscall hook.
pub skip_syscall: bool, Run,
/// Skip the syscall, and make the syscall return the value provided in the field in the target.
Skip(GuestAddr),
} }
impl<F, C, R: Clone> Hook<F, C, R> { impl<F, C, R: Clone> Hook<F, C, R> {
@ -1281,7 +1287,7 @@ impl QemuHooks {
GuestAddr, GuestAddr,
GuestAddr, GuestAddr,
GuestAddr, GuestAddr,
) -> libafl_qemu_sys::syshook_ret = transmute(callback); ) -> libafl_qemu_sys::libafl_syshook_ret = transmute(callback);
let num = libafl_qemu_sys::libafl_add_pre_syscall_hook(Some(callback), data); let num = libafl_qemu_sys::libafl_add_pre_syscall_hook(Some(callback), data);
PreSyscallHookId(num) PreSyscallHookId(num)
} }
@ -1324,38 +1330,3 @@ impl QemuHooks {
} }
} }
} }
#[cfg(feature = "python")]
#[pymethods]
impl SyscallHookResult {
#[new]
#[pyo3(signature = (
value=None
))]
#[must_use]
pub fn new(value: Option<GuestAddr>) -> Self {
Self::new_internal(value)
}
}
impl SyscallHookResult {
#[cfg(not(feature = "python"))]
#[must_use]
pub fn new(value: Option<GuestAddr>) -> Self {
Self::new_internal(value)
}
#[must_use]
fn new_internal(value: Option<GuestAddr>) -> Self {
value.map_or(
Self {
retval: 0,
skip_syscall: false,
},
|v| Self {
retval: v,
skip_syscall: true,
},
)
}
}

View File

@ -1238,6 +1238,8 @@ impl QemuMemoryChunk {
pub mod pybind { pub mod pybind {
use pyo3::{exceptions::PyValueError, prelude::*}; use pyo3::{exceptions::PyValueError, prelude::*};
#[cfg(feature = "usermode")]
pub use super::usermode::pybind::*;
use super::{GuestAddr, GuestUsize}; use super::{GuestAddr, GuestUsize};
static mut PY_GENERIC_HOOKS: Vec<(GuestAddr, PyObject)> = vec![]; static mut PY_GENERIC_HOOKS: Vec<(GuestAddr, PyObject)> = vec![];

View File

@ -467,17 +467,24 @@ impl Qemu {
pub mod pybind { pub mod pybind {
use libafl_qemu_sys::{GuestAddr, MmapPerms}; use libafl_qemu_sys::{GuestAddr, MmapPerms};
use pyo3::{ use pyo3::{
Bound, PyObject, PyResult, Python, Bound, FromPyObject, PyObject, PyResult, Python,
conversion::FromPyObject,
exceptions::PyValueError, exceptions::PyValueError,
pymethods, pyclass, pymethods,
types::{PyAnyMethods, PyInt}, types::{PyAnyMethods, PyInt},
}; };
use crate::{pybind::Qemu, qemu::hooks::SyscallHookResult}; use crate::{pybind::Qemu, qemu::hooks};
static mut PY_SYSCALL_HOOK: Option<PyObject> = None; static mut PY_SYSCALL_HOOK: Option<PyObject> = None;
#[pyclass]
#[derive(FromPyObject)]
pub struct SyscallHookResult {
/// if None: run.
/// else: skip with given value.
skip: Option<GuestAddr>,
}
extern "C" fn py_syscall_hook_wrapper( extern "C" fn py_syscall_hook_wrapper(
_data: u64, _data: u64,
sys_num: i32, sys_num: i32,
@ -489,25 +496,31 @@ pub mod pybind {
a5: u64, a5: u64,
a6: u64, a6: u64,
a7: u64, a7: u64,
) -> SyscallHookResult { ) -> hooks::SyscallHookResult {
unsafe { (&raw const PY_SYSCALL_HOOK).read() }.map_or_else( unsafe { (&raw const PY_SYSCALL_HOOK).read() }.map_or_else(
|| SyscallHookResult::new(None), || hooks::SyscallHookResult::Run,
|obj| { |obj| {
let args = (sys_num, a0, a1, a2, a3, a4, a5, a6, a7); let args = (sys_num, a0, a1, a2, a3, a4, a5, a6, a7);
Python::with_gil(|py| { Python::with_gil(|py| {
let ret = obj.call1(py, args).expect("Error in the syscall hook"); let ret = obj.call1(py, args).expect("Error in the syscall hook");
let any = ret.bind(py); let any = ret.bind(py);
if any.is_none() { if any.is_none() {
SyscallHookResult::new(None) hooks::SyscallHookResult::Run
} else { } else {
let a: Result<&Bound<'_, PyInt>, _> = any.downcast_exact(); let a: Result<&Bound<'_, PyInt>, _> = any.downcast_exact();
if let Ok(i) = a { if let Ok(i) = a {
SyscallHookResult::new(Some( hooks::SyscallHookResult::Skip(
i.extract().expect("Invalid syscall hook return value"), i.extract().expect("Invalid syscall hook return value"),
)) )
} else { } else {
SyscallHookResult::extract_bound(ret.bind(py)) let syscall = SyscallHookResult::extract_bound(ret.bind(py))
.expect("The syscall hook must return a SyscallHookResult") .expect("The syscall hook must return a SyscallHookResult");
if let Some(ret) = syscall.skip {
hooks::SyscallHookResult::Skip(ret)
} else {
hooks::SyscallHookResult::Run
}
} }
} }
}) })