From a0e30d01d3c5c2cd860d8e97b62f3fe0816de176 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 23 Jan 2024 22:38:06 +0100 Subject: [PATCH] QEMU: safe linking of extern "C" declarations (#1810) * safe linking initial commit * Fix static declaration. * Works also for static variables, even for thread-unsafe types. * Remove warnings. * fixed visibility for function. * remove `rustversion` from dependencies. --- libafl_qemu/Cargo.toml | 1 + libafl_qemu/build.rs | 7 ++++ libafl_qemu/src/emu.rs | 75 ++++++++++++++++++++++++++++++++++++++---- libafl_qemu/src/lib.rs | 1 + 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index 017fb91872..686ff24ac2 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -92,6 +92,7 @@ document-features = { version = "0.2", optional = true } [build-dependencies] pyo3-build-config = { version = "0.18", optional = true } +rustversion = "1.0" [lib] name = "libafl_qemu" diff --git a/libafl_qemu/build.rs b/libafl_qemu/build.rs index 9badc1d340..e9065387e1 100644 --- a/libafl_qemu/build.rs +++ b/libafl_qemu/build.rs @@ -8,6 +8,13 @@ mod host_specific { } } +#[rustversion::nightly] +fn main() { + println!("cargo:rustc-cfg=nightly"); + host_specific::build(); +} + +#[rustversion::not(nightly)] fn main() { host_specific::build(); } diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 251fe41ae4..dd4d5f176e 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -17,11 +17,73 @@ use std::{ffi::CString, ptr, slice::from_raw_parts, str::from_utf8_unchecked}; use libc::c_int; use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_traits::Num; +use paste::paste; use strum::IntoEnumIterator; use strum_macros::EnumIter; use crate::{GuestReg, Regs}; +/// Safe linking with of extern "C" functions. +/// This macro makes sure the declared symbol is defined *at link time*, avoiding declaring non-existant symbols +/// that could be silently ignored during linking if unused. +/// +/// This macro relies on a nightly feature, and can only be used in this mode +/// It is (nearly) a drop-in replacement for extern "C" { } blocks containing function and static declarations, and will have the same effect in practice. +macro_rules! extern_c_checked { + () => {}; + + ($visibility:vis fn $c_fn:ident($($param_ident:ident : $param_ty:ty),*) $( -> $ret_ty:ty )?; $($tail:tt)*) => { + paste! { + #[cfg_attr(nightly, used(linker))] + static [<__ $c_fn:upper __>]: unsafe extern "C" fn($($param_ty),*) $( -> $ret_ty )? = $c_fn; + } + + extern "C" { + $visibility fn $c_fn($($param_ident : $param_ty),*) $( -> $ret_ty )?; + } + + extern_c_checked!($($tail)*); + }; + + ($visibility:vis static $c_var:ident : $c_var_ty:ty; $($tail:tt)*) => { + paste! { + #[allow(non_camel_case_types)] + #[allow(unused)] + struct [<__ $c_var:upper _STRUCT__>] { member: &'static $c_var_ty } + + unsafe impl Sync for [<__ $c_var:upper _STRUCT__>] {} + + #[cfg_attr(nightly, used(linker))] + static [<__ $c_var:upper __>]: [<__ $c_var:upper _STRUCT__>] = unsafe { [<__ $c_var:upper _STRUCT__>] { member: &$c_var } }; + } + + extern "C" { + $visibility static $c_var: $c_var_ty; + } + + extern_c_checked!($($tail)*); + }; + + ($visibility:vis static mut $c_var:ident : $c_var_ty:ty; $($tail:tt)*) => { + paste! { + #[allow(non_camel_case_types)] + #[allow(unused)] + struct [<__ $c_var:upper _STRUCT__>] { member: &'static $c_var_ty } + + unsafe impl Sync for [<__ $c_var:upper _STRUCT__>] {} + + #[cfg_attr(nightly, used(linker))] + static mut [<__ $c_var:upper __>]: [<__ $c_var:upper _STRUCT__>] = unsafe { [<__ $c_var:upper _STRUCT__>] { member: &$c_var } }; + } + + extern "C" { + $visibility static mut $c_var: $c_var_ty; + } + + extern_c_checked!($($tail)*); + }; +} + pub type GuestAddr = libafl_qemu_sys::target_ulong; pub type GuestUsize = libafl_qemu_sys::target_ulong; pub type GuestIsize = libafl_qemu_sys::target_long; @@ -316,7 +378,7 @@ impl MapInfo { } #[cfg(emulation_mode = "usermode")] -extern "C" { +extern_c_checked! { fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32; fn libafl_qemu_run() -> i32; @@ -339,7 +401,7 @@ extern "C" { } #[cfg(emulation_mode = "systemmode")] -extern "C" { +extern_c_checked! { fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8); fn vm_start(); @@ -348,6 +410,8 @@ extern "C" { fn libafl_save_qemu_snapshot(name: *const u8, sync: bool); fn libafl_load_qemu_snapshot(name: *const u8, sync: bool); + + fn libafl_qemu_current_paging_id(cpu: CPUStatePtr) -> GuestPhysAddr; } #[cfg(emulation_mode = "systemmode")] @@ -358,7 +422,7 @@ extern "C" fn qemu_cleanup_atexit() { } // TODO rely completely on libafl_qemu_sys -extern "C" { +extern_c_checked! { //static libafl_page_size: GuestUsize; fn libafl_page_from_addr(addr: GuestAddr) -> GuestAddr; @@ -387,12 +451,9 @@ extern "C" { fn libafl_qemu_add_gdb_cmd( callback: extern "C" fn(*const (), *const u8, usize) -> i32, - data: *const (), + data: *const () ); fn libafl_qemu_gdb_reply(buf: *const u8, len: usize); - - #[cfg(emulation_mode = "systemmode")] - fn libafl_qemu_current_paging_id(cpu: CPUStatePtr) -> GuestPhysAddr; } #[cfg(emulation_mode = "usermode")] diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index 834f3742e3..493ab6c51b 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -1,3 +1,4 @@ +#![cfg_attr(nightly, feature(used_with_arg))] //! Welcome to `LibAFL` QEMU //! #![doc = include_str!("../../README.md")]