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.
This commit is contained in:
Romain Malmain 2024-01-23 22:38:06 +01:00 committed by GitHub
parent 2ac154d473
commit a0e30d01d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 77 additions and 7 deletions

View File

@ -92,6 +92,7 @@ document-features = { version = "0.2", optional = true }
[build-dependencies] [build-dependencies]
pyo3-build-config = { version = "0.18", optional = true } pyo3-build-config = { version = "0.18", optional = true }
rustversion = "1.0"
[lib] [lib]
name = "libafl_qemu" name = "libafl_qemu"

View File

@ -8,6 +8,13 @@ mod host_specific {
} }
} }
#[rustversion::nightly]
fn main() {
println!("cargo:rustc-cfg=nightly");
host_specific::build();
}
#[rustversion::not(nightly)]
fn main() { fn main() {
host_specific::build(); host_specific::build();
} }

View File

@ -17,11 +17,73 @@ use std::{ffi::CString, ptr, slice::from_raw_parts, str::from_utf8_unchecked};
use libc::c_int; use libc::c_int;
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use num_traits::Num; use num_traits::Num;
use paste::paste;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use strum_macros::EnumIter; use strum_macros::EnumIter;
use crate::{GuestReg, Regs}; 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 GuestAddr = libafl_qemu_sys::target_ulong;
pub type GuestUsize = libafl_qemu_sys::target_ulong; pub type GuestUsize = libafl_qemu_sys::target_ulong;
pub type GuestIsize = libafl_qemu_sys::target_long; pub type GuestIsize = libafl_qemu_sys::target_long;
@ -316,7 +378,7 @@ impl MapInfo {
} }
#[cfg(emulation_mode = "usermode")] #[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 qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
fn libafl_qemu_run() -> i32; fn libafl_qemu_run() -> i32;
@ -339,7 +401,7 @@ extern "C" {
} }
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
extern "C" { extern_c_checked! {
fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8); fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8);
fn vm_start(); fn vm_start();
@ -348,6 +410,8 @@ extern "C" {
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool); fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
fn libafl_load_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")] #[cfg(emulation_mode = "systemmode")]
@ -358,7 +422,7 @@ extern "C" fn qemu_cleanup_atexit() {
} }
// TODO rely completely on libafl_qemu_sys // TODO rely completely on libafl_qemu_sys
extern "C" { extern_c_checked! {
//static libafl_page_size: GuestUsize; //static libafl_page_size: GuestUsize;
fn libafl_page_from_addr(addr: GuestAddr) -> GuestAddr; fn libafl_page_from_addr(addr: GuestAddr) -> GuestAddr;
@ -387,12 +451,9 @@ extern "C" {
fn libafl_qemu_add_gdb_cmd( fn libafl_qemu_add_gdb_cmd(
callback: extern "C" fn(*const (), *const u8, usize) -> i32, callback: extern "C" fn(*const (), *const u8, usize) -> i32,
data: *const (), data: *const ()
); );
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize); 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")] #[cfg(emulation_mode = "usermode")]

View File

@ -1,3 +1,4 @@
#![cfg_attr(nightly, feature(used_with_arg))]
//! Welcome to `LibAFL` QEMU //! Welcome to `LibAFL` QEMU
//! //!
#![doc = include_str!("../../README.md")] #![doc = include_str!("../../README.md")]