diff --git a/src/lib.rs b/src/lib.rs index 6206c52..7935c19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ //! So far, all our bugs are implemented using a single soundness hole in the Rust compiler. //! //! The explanation is detailed in the [`lifetime_expansion`] module. - #![deny(unsafe_code)] // The actual exploit @@ -13,6 +12,7 @@ pub mod references; pub mod segfault; pub mod transmute; pub mod use_after_free; +pub mod safe_ffi; pub use lifetime_expansion::*; diff --git a/src/safe_ffi.rs b/src/safe_ffi.rs new file mode 100644 index 0000000..81097f6 --- /dev/null +++ b/src/safe_ffi.rs @@ -0,0 +1,53 @@ +#[macro_export] +macro_rules! ffi { + (@ ffi {$($vis:tt)*} {$($id:tt)*} fn $i:ident ($($t:ident : $ty:ty),*$(,)?) $(-> $ret:ty)? $(;$($other:tt)*)? ) => { + $($vis:tt)* use safe_wrapper::safe_wrapper as $i; + mod safe_wrapper { + safe!{pub(super) $($id)* fn $i ($($t:$ty),*) $(->$ret)? := safe_wrapper} + $($id)* { + fn $i ($($t:$ty),*) $(-> $ret)?; + } + } + $( + ffi!{$($other)*} + )? + }; + (@ safe {$($vis:tt)*} {$($id:tt)*} fn $i:ident ($($t:ident : $ty:ty),*$(,)?) $(-> $ret:ty)? := $ni:ident) => { + $($vis)? $($id)* fn $ni ($($t:$ty),*) $(-> $ret)? { + crate::transmute::transmute::$ret)?, $($id)* fn ($($ty),*) $(->$ret)?>($i)($($t),*) + } + }; + (@ $command:ident {$($t1:tt)*} {$($t2:tt)*} pub ( $($vis:tt)* ) $($t3:tt)* ) => {ffi!{@ $command {$($t1)*pub($($vis)*)} {$($t2)*} $($t3)*}}; + (@ $command:ident {$($t1:tt)*} {$($t2:tt)*} pub $($t3:tt)* ) => {ffi!{@ $command {$($t1)*pub} {$($t2)*} $($t3)*}}; + (@ $command:ident {$($t1:tt)*} {$($t2:tt)*} $i:tt $($t3:tt)* ) => {ffi!{@ $command {$($t1)*} {$($t2)*$i} $($t3)*}}; + (@ $command:ident $($t:tt)*) => {compile_error!{concat!("no rules matches @ ",stringify!($command))}}; // should be error + ()=>{}; + ($($t:tt)*) => {ffi!{@ffi {} {} $($t)*}} +} +#[macro_export] +macro_rules! safe { + ()=>{}; + ($($t:tt)*) => {ffi!{@safe {} {} $($t)*}}; +} +#[cfg(test)] +mod test { + // create safe printf function + ffi!{ + extern "C" fn printf(fmt: *const i8)->i32 + } + // create an unsafe function which may call later in totally safe block. + mod r#unsafe { + #[allow(unsafe_code)] + pub unsafe fn r#unsafe()->i32{1} + } + // use it and send it to safe macro + use r#unsafe::r#unsafe; + safe!{ + fn r#unsafe()->i32 := very_safe + } + #[test] + fn test_unsafe_and_ffi() { + assert_eq!(very_safe(),1); // it is r#unsafe which is called + assert_eq!(printf(c"Fine.\n".as_ptr()),6); // printf in libc is called. + } +}