#![allow(clippy::std_instead_of_core)]
pub mod filter;
pub mod tracing;
#[doc(hidden)]
#[cfg(target_os = "linux")]
pub mod cpp_runtime {
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::pub_underscore_fields)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused_attributes)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
#[doc(hidden)]
pub use ctor;
use libafl::observers::concolic;
pub use libafl_bolts::shmem::StdShMem;
#[doc(hidden)]
pub use libc::atexit;
#[doc(hidden)]
pub use unchecked_unwrap;
#[doc(hidden)]
#[cfg(target_os = "linux")]
#[macro_export]
macro_rules! export_c_symbol {
(pub fn $name:ident($( $arg:ident : $type:ty ),*$(,)?) -> $ret:ty) => {
use $crate::cpp_runtime::*;
#[no_mangle]
pub unsafe extern "C" fn $name($( $arg : $type),*) -> $ret {
$crate::cpp_runtime::$name($( $arg ),*)
}
};
(pub fn $name:ident($( $arg:ident : $type:ty ),* $(,)?)) => {
$crate::export_c_symbol!(pub fn $name($( $arg : $type),*) -> ());
}
}
#[cfg(target_os = "linux")]
include!(concat!(env!("OUT_DIR"), "/cpp_exports_macro.rs"));
include!(concat!(env!("OUT_DIR"), "/rust_exports_macro.rs"));
macro_rules! rust_runtime_function_declaration {
(pub fn expression_unreachable(expressions: *mut RSymExpr, num_elements: usize), $c_name:ident;) => {
fn expression_unreachable(&mut self, exprs: &[RSymExpr]);
};
(pub fn $name:ident($( $arg:ident : $type:ty ),*$(,)?)$( -> $ret:ty)?, $c_name:ident;) => {
fn $name(&mut self, $( $arg : $type),*)$( -> Option<$ret>)?;
};
}
pub type RSymExpr = concolic::SymExprRef;
pub trait Runtime {
invoke_macro_with_rust_runtime_exports!(rust_runtime_function_declaration;);
}
#[doc(hidden)]
#[macro_export]
macro_rules! make_symexpr_optional {
(RSymExpr) => {Option<RSymExpr>};
($($type:tt)+) => {$($type)+};
}
#[doc(hidden)]
#[macro_export]
macro_rules! unwrap_option {
($param_name:ident : RSymExpr) => {
$param_name?
};
($param_name:ident : $($type:tt)+) => {
$param_name
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! export_rust_runtime_fn {
(pub fn expression_unreachable(expressions: *mut RSymExpr, num_elements: usize), $c_name:ident; $rt_cb:path) => {
#[expect(clippy::missing_safety_doc)]
#[no_mangle]
pub unsafe extern "C" fn _rsym_expression_unreachable(expressions: *mut RSymExpr, num_elements: usize) {
let slice = core::slice::from_raw_parts(expressions, num_elements);
$rt_cb(|rt| {
rt.expression_unreachable(slice);
})
}
};
(pub fn push_path_constraint(constraint: RSymExpr, taken: bool, site_id: usize), $c_name:ident; $rt_cb:path) => {
#[expect(clippy::missing_safety_doc)]
#[no_mangle]
pub unsafe extern "C" fn _rsym_push_path_constraint(constraint: Option<RSymExpr>, taken: bool, site_id: usize) {
if let Some(constraint) = constraint {
$rt_cb(|rt| {
rt.push_path_constraint(constraint, taken, site_id);
})
}
}
};
(pub fn build_integer_from_buffer(
buffer: *mut ::std::os::raw::c_void,
num_bits: ::std::os::raw::c_uint$(,)?) -> RSymExpr,$c_name:ident; $rt_cb:path) => {
#[expect(clippy::missing_safety_doc)]
#[no_mangle]
pub unsafe extern "C" fn _rsym_build_integer_from_buffer(buffer: *mut ::std::os::raw::c_void, num_bits: ::std::os::raw::c_uint) {
$rt_cb(|rt| {
rt.build_integer_from_buffer(buffer, num_bits);
})
}
};
(pub fn $name:ident($( $arg:ident : $(::)?$($type:ident)::+ ),*$(,)?)$( -> $($ret:ident)::+)?, $c_name:ident; $rt_cb:path) => {
#[expect(clippy::missing_safety_doc)]
#[no_mangle]
pub unsafe extern "C" fn $c_name( $($arg: $crate::make_symexpr_optional!($($type)::+),)* )$( -> $crate::make_symexpr_optional!($($ret)::+))? {
$rt_cb(|rt| {
$(let $arg = $crate::unwrap_option!($arg: $($type)::+);)*
rt.$name($($arg,)*)
})
}
};
}
macro_rules! impl_nop_runtime_fn {
(pub fn expression_unreachable(expressions: *mut RSymExpr, num_elements: usize), $c_name:ident;) => {
fn expression_unreachable(&mut self, _exprs: &[RSymExpr]) {core::default::Default::default()}
};
(pub fn $name:ident($( $arg:ident : $type:ty ),*$(,)?)$( -> $ret:ty)?, $c_name:ident;) => {
fn $name(&mut self, $( _ : $type),*)$( -> Option<$ret>)? {core::default::Default::default()}
};
}
#[derive(Debug, Copy, Clone)]
pub struct NopRuntime;
impl Runtime for NopRuntime {
invoke_macro_with_rust_runtime_exports!(impl_nop_runtime_fn;);
}
pub struct OptionalRuntime<RT> {
inner: Option<RT>,
}
impl<RT> OptionalRuntime<RT> {
pub fn new(rt: Option<RT>) -> Self {
Self { inner: rt }
}
pub fn into_inner(self) -> Option<RT> {
self.inner
}
}
macro_rules! rust_runtime_function_declaration {
(pub fn expression_unreachable(expressions: *mut RSymExpr, num_elements: usize), $c_name:ident;) => {
fn expression_unreachable(&mut self, exprs: &[RSymExpr]) {
if let Some(inner) = &mut self.inner {
inner.expression_unreachable(exprs);
}
}
};
(pub fn $name:ident($( $arg:ident : $type:ty ),*$(,)?) -> $ret:ty, $c_name:ident;) => {
fn $name(&mut self, $( $arg : $type),*) -> Option<$ret> {
if let Some(inner) = &mut self.inner {
inner.$name($($arg,)*)
} else {
None
}
}
};
(pub fn $name:ident($( $arg:ident : $type:ty ),*$(,)?), $c_name:ident;) => {
fn $name(&mut self, $( $arg : $type),*) {
if let Some(inner) = &mut self.inner {
inner.$name($($arg,)*);
}
}
};
}
impl<RT> Runtime for OptionalRuntime<RT>
where
RT: Runtime,
{
invoke_macro_with_rust_runtime_exports!(rust_runtime_function_declaration;);
}
#[macro_export]
macro_rules! export_runtime {
($constructor:expr => $rt:ty) => {
export_runtime!(@final $constructor => $rt);
};
($filter_constructor:expr => $filter:ty ; $($constructor:expr => $rt:ty);+) => {
export_runtime!(@final export_runtime!(@combine_constructor $filter_constructor; $($constructor);+) => export_runtime!(@combine_type $filter; $($rt);+));
};
(@combine_constructor $filter_constructor:expr ; $($constructor:expr);+) => {
$crate::filter::FilterRuntime::new($filter_constructor, export_runtime!(@combine_constructor $($constructor);+))
};
(@combine_constructor $constructor:expr) => {
$constructor
};
(@combine_type $filter:ty ; $($rt:ty);+) => {
$crate::filter::FilterRuntime<$filter, export_runtime!(@combine_type $($rt);+)>
};
(@combine_type $rt:ty) => {
$rt
};
(@final $constructor:expr => $rt:ty) => {
static mut GLOBAL_DATA: Option<$rt> = None;
#[cfg(not(test))]
$crate::ctor::declarative::ctor! {
#[ctor]
fn init() {
unsafe {
GLOBAL_DATA = Some($constructor);
$crate::atexit(fini);
}
}
}
extern "C" fn fini() {
unsafe {
if let Some(state) = GLOBAL_DATA.take() {
}
}
}
use $crate::RSymExpr;
fn with_state<R>(cb: impl FnOnce(&mut $rt) -> R) -> R {
use $crate::unchecked_unwrap::UncheckedUnwrap;
let s = unsafe { GLOBAL_DATA.as_mut().unchecked_unwrap() };
cb(s)
}
$crate::invoke_macro_with_rust_runtime_exports!($crate::export_rust_runtime_fn;with_state);
#[cfg(target_os="linux")]
$crate::export_cpp_runtime_functions!();
};
}