diff --git a/libafl_qemu/libafl_qemu_build/src/build.rs b/libafl_qemu/libafl_qemu_build/src/build.rs index acd4bd83cc..0f85b6dc9f 100644 --- a/libafl_qemu/libafl_qemu_build/src/build.rs +++ b/libafl_qemu/libafl_qemu_build/src/build.rs @@ -11,7 +11,7 @@ use crate::cargo_add_rpath; pub const QEMU_URL: &str = "https://github.com/AFLplusplus/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 qemu_path: PathBuf, diff --git a/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs b/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs index 98c2a0489b..fdc5a87735 100644 --- a/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs +++ b/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs @@ -1,5 +1,5 @@ /* 1.87.0-nightly */ -/* qemu git hash: 2a676d9cd8c474b5c0db1d77d2769e56e2ed8524 */ +/* qemu git hash: 97bef506eed24ee8d0eda4a07c4419c55dae4acb */ /* automatically generated by rust-bindgen 0.71.1 */ use libc::siginfo_t; @@ -8687,37 +8687,131 @@ unsafe extern "C" { unsafe extern "C" { 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 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 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)] -#[derive(Debug, Default, Copy, Clone)] -pub struct syshook_ret { - pub retval: target_ulong, - pub skip_syscall: bool, +#[derive(Copy, Clone)] +pub struct libafl_syshook_ret { + pub tag: libafl_syshook_ret_tag, + 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)] const _: () = { - ["Size of syshook_ret"][::std::mem::size_of::() - 16usize]; - ["Alignment of syshook_ret"][::std::mem::align_of::() - 8usize]; - ["Offset of field: syshook_ret::retval"][::std::mem::offset_of!(syshook_ret, retval) - 0usize]; - ["Offset of field: syshook_ret::skip_syscall"] - [::std::mem::offset_of!(syshook_ret, skip_syscall) - 8usize]; + ["Size of libafl_syshook_ret__bindgen_ty_1"] + [::std::mem::size_of::() - 8usize]; + ["Alignment of libafl_syshook_ret__bindgen_ty_1"] + [::std::mem::align_of::() - 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::::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::() - 16usize]; + ["Alignment of libafl_syshook_ret"][::std::mem::align_of::() - 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::::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)] #[derive(Debug, Copy, Clone)] pub struct libafl_pre_syscall_hook { - pub 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, - >, + pub callback: libafl_pre_syscall_cb, pub data: u64, pub num: usize, pub next: *mut libafl_pre_syscall_hook, @@ -8748,21 +8842,7 @@ impl Default for libafl_pre_syscall_hook { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct libafl_post_syscall_hook { - pub 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, - >, + pub callback: libafl_post_syscall_cb, pub data: u64, pub num: usize, pub next: *mut libafl_post_syscall_hook, @@ -8792,43 +8872,10 @@ impl Default for libafl_post_syscall_hook { } } unsafe extern "C" { - pub fn libafl_add_pre_syscall_hook( - 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; + pub fn libafl_add_pre_syscall_hook(callback: libafl_pre_syscall_cb, data: u64) -> usize; } unsafe extern "C" { - pub fn libafl_add_post_syscall_hook( - 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; + pub fn libafl_add_post_syscall_hook(callback: libafl_post_syscall_cb, data: u64) -> usize; } unsafe extern "C" { pub fn libafl_qemu_remove_pre_syscall_hook(num: usize) -> ::std::os::raw::c_int; diff --git a/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs b/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs index 3b1381bf1a..80f3f558eb 100644 --- a/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs +++ b/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs @@ -1,5 +1,5 @@ /* 1.87.0-nightly */ -/* qemu git hash: 2a676d9cd8c474b5c0db1d77d2769e56e2ed8524 */ +/* qemu git hash: 97bef506eed24ee8d0eda4a07c4419c55dae4acb */ /* automatically generated by rust-bindgen 0.71.1 */ #[repr(C)] diff --git a/libafl_qemu/runtime/nyx_stub_bindings.rs b/libafl_qemu/runtime/nyx_stub_bindings.rs index f0c853aaed..8d38d38894 100644 --- a/libafl_qemu/runtime/nyx_stub_bindings.rs +++ b/libafl_qemu/runtime/nyx_stub_bindings.rs @@ -1,5 +1,5 @@ /* 1.87.0-nightly */ -/* qemu git hash: 2a676d9cd8c474b5c0db1d77d2769e56e2ed8524 */ +/* qemu git hash: 97bef506eed24ee8d0eda4a07c4419c55dae4acb */ /* automatically generated by rust-bindgen 0.71.1 */ #[repr(C)] diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index 00972bf367..1a17b91c54 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -98,7 +98,7 @@ pub fn python_module(m: &Bound<'_, PyModule>) -> PyResult<()> { #[cfg(feature = "usermode")] m.add_class::()?; - m.add_class::()?; + m.add_class::()?; m.add_class::()?; Ok(()) diff --git a/libafl_qemu/src/modules/usermode/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs index 9e2bbbd0a8..76c6795049 100644 --- a/libafl_qemu/src/modules/usermode/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -578,9 +578,9 @@ impl AsanGiovese { } _ => (), } - SyscallHookResult::new(Some(r)) + SyscallHookResult::Skip(r) } else { - SyscallHookResult::new(None) + SyscallHookResult::Run } } @@ -1350,9 +1350,9 @@ where } _ => (), } - SyscallHookResult::new(Some(0)) + SyscallHookResult::Skip(0) } else { - SyscallHookResult::new(None) + SyscallHookResult::Run } } diff --git a/libafl_qemu/src/modules/usermode/injections.rs b/libafl_qemu/src/modules/usermode/injections.rs index 0f43e6f929..993a5b2c30 100644 --- a/libafl_qemu/src/modules/usermode/injections.rs +++ b/libafl_qemu/src/modules/usermode/injections.rs @@ -400,13 +400,13 @@ where let first_parameter = unsafe { if (*c_array.offset(1)).is_null() { - return SyscallHookResult::new(None); + return SyscallHookResult::Run; } CStr::from_ptr(*c_array.offset(1)).to_string_lossy() }; let second_parameter = unsafe { if (*c_array.offset(2)).is_null() { - return SyscallHookResult::new(None); + return SyscallHookResult::Run; } CStr::from_ptr(*c_array.offset(2)).to_string_lossy() }; @@ -419,9 +419,9 @@ where //println!("PARAMETERS First {} Second {}", first_parameter, second_ } - SyscallHookResult::new(Some(0)) + SyscallHookResult::Skip(0) } else { - SyscallHookResult::new(None) + SyscallHookResult::Run } } diff --git a/libafl_qemu/src/modules/usermode/redirect_stdin.rs b/libafl_qemu/src/modules/usermode/redirect_stdin.rs index 1c3154952f..ea46c8d9b0 100644 --- a/libafl_qemu/src/modules/usermode/redirect_stdin.rs +++ b/libafl_qemu/src/modules/usermode/redirect_stdin.rs @@ -114,7 +114,7 @@ where { let h = emulator_modules.get_mut::().unwrap(); if h.input_addr.is_null() { - return SyscallHookResult::new(None); + return SyscallHookResult::Run; } if syscall == SYS_read as i32 && x0 == 0 { /* @@ -143,7 +143,7 @@ where }; // println!("copied {}", size); h.read += size as usize; - return SyscallHookResult::new(Some(size)); + return SyscallHookResult::Skip(size); } - SyscallHookResult::new(None) + SyscallHookResult::Run } diff --git a/libafl_qemu/src/modules/usermode/snapshot.rs b/libafl_qemu/src/modules/usermode/snapshot.rs index 177abda037..d8d925abe1 100644 --- a/libafl_qemu/src/modules/usermode/snapshot.rs +++ b/libafl_qemu/src/modules/usermode/snapshot.rs @@ -888,11 +888,11 @@ where if i64::from(sys_num) == SYS_munmap { let h = emulator_modules.get_mut::().unwrap(); 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)] diff --git a/libafl_qemu/src/qemu/hooks.rs b/libafl_qemu/src/qemu/hooks.rs index 405a65a06d..31dcbb6f7a 100644 --- a/libafl_qemu/src/qemu/hooks.rs +++ b/libafl_qemu/src/qemu/hooks.rs @@ -8,8 +8,6 @@ use core::{ffi::c_void, fmt::Debug, mem::transmute, ptr}; use libafl::executors::hooks::inprocess::inprocess_get_state; use libafl_qemu_sys::{CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, GuestUsize}; -#[cfg(feature = "python")] -use pyo3::{FromPyObject, pyclass, pymethods}; use crate::{ HookData, HookId, @@ -85,12 +83,20 @@ pub enum Hook { 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)] -#[cfg_attr(feature = "python", pyclass)] -#[cfg_attr(feature = "python", derive(FromPyObject))] -pub struct SyscallHookResult { - pub retval: GuestAddr, - pub skip_syscall: bool, +pub enum SyscallHookResult { + /// Runs the syscall after the hook is executed. The return value will be the one of the + /// syscall itself. + /// If you need to change the return value of the syscall, please use a post-syscall hook. + Run, + /// Skip the syscall, and make the syscall return the value provided in the field in the target. + Skip(GuestAddr), } impl Hook { @@ -1281,7 +1287,7 @@ impl QemuHooks { 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); 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) -> Self { - Self::new_internal(value) - } -} - -impl SyscallHookResult { - #[cfg(not(feature = "python"))] - #[must_use] - pub fn new(value: Option) -> Self { - Self::new_internal(value) - } - - #[must_use] - fn new_internal(value: Option) -> Self { - value.map_or( - Self { - retval: 0, - skip_syscall: false, - }, - |v| Self { - retval: v, - skip_syscall: true, - }, - ) - } -} diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index 11990aeacf..67c0dc6a09 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -1238,6 +1238,8 @@ impl QemuMemoryChunk { pub mod pybind { use pyo3::{exceptions::PyValueError, prelude::*}; + #[cfg(feature = "usermode")] + pub use super::usermode::pybind::*; use super::{GuestAddr, GuestUsize}; static mut PY_GENERIC_HOOKS: Vec<(GuestAddr, PyObject)> = vec![]; diff --git a/libafl_qemu/src/qemu/usermode.rs b/libafl_qemu/src/qemu/usermode.rs index a41c563927..f727bdf84c 100644 --- a/libafl_qemu/src/qemu/usermode.rs +++ b/libafl_qemu/src/qemu/usermode.rs @@ -467,17 +467,24 @@ impl Qemu { pub mod pybind { use libafl_qemu_sys::{GuestAddr, MmapPerms}; use pyo3::{ - Bound, PyObject, PyResult, Python, - conversion::FromPyObject, + Bound, FromPyObject, PyObject, PyResult, Python, exceptions::PyValueError, - pymethods, + pyclass, pymethods, types::{PyAnyMethods, PyInt}, }; - use crate::{pybind::Qemu, qemu::hooks::SyscallHookResult}; + use crate::{pybind::Qemu, qemu::hooks}; static mut PY_SYSCALL_HOOK: Option = None; + #[pyclass] + #[derive(FromPyObject)] + pub struct SyscallHookResult { + /// if None: run. + /// else: skip with given value. + skip: Option, + } + extern "C" fn py_syscall_hook_wrapper( _data: u64, sys_num: i32, @@ -489,25 +496,31 @@ pub mod pybind { a5: u64, a6: u64, a7: u64, - ) -> SyscallHookResult { + ) -> hooks::SyscallHookResult { unsafe { (&raw const PY_SYSCALL_HOOK).read() }.map_or_else( - || SyscallHookResult::new(None), + || hooks::SyscallHookResult::Run, |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.bind(py); if any.is_none() { - SyscallHookResult::new(None) + hooks::SyscallHookResult::Run } else { let a: Result<&Bound<'_, PyInt>, _> = any.downcast_exact(); if let Ok(i) = a { - SyscallHookResult::new(Some( + hooks::SyscallHookResult::Skip( i.extract().expect("Invalid syscall hook return value"), - )) + ) } else { - SyscallHookResult::extract_bound(ret.bind(py)) - .expect("The syscall hook must return a SyscallHookResult") + let syscall = SyscallHookResult::extract_bound(ret.bind(py)) + .expect("The syscall hook must return a SyscallHookResult"); + + if let Some(ret) = syscall.skip { + hooks::SyscallHookResult::Skip(ret) + } else { + hooks::SyscallHookResult::Run + } } } })