//@revisions: trace notrace //@[trace] only-target: x86_64-unknown-linux-gnu i686-unknown-linux-gnu //@[trace] compile-flags: -Zmiri-native-lib-enable-tracing //@compile-flags: -Zmiri-permissive-provenance #![feature(box_as_ptr)] use std::mem::MaybeUninit; use std::ptr; fn main() { test_increment_int(); test_init_int(); test_init_array(); test_init_static_inner(); test_exposed(); test_swap_ptr(); test_swap_ptr_tuple(); test_overwrite_dangling(); test_pass_dangling(); test_swap_ptr_triple_dangling(); test_return_ptr(); test_pass_ptr_as_int(); test_pass_ptr_via_previously_shared_mem(); } /// Test function that modifies an int. fn test_increment_int() { extern "C" { fn increment_int(ptr: *mut i32); } let mut x = 11; unsafe { increment_int(&mut x) }; assert_eq!(x, 12); } /// Test function that initializes an int. fn test_init_int() { extern "C" { fn init_int(ptr: *mut i32, val: i32); } let mut x = MaybeUninit::::uninit(); let val = 21; let x = unsafe { init_int(x.as_mut_ptr(), val); x.assume_init() }; assert_eq!(x, val); } /// Test function that initializes an array. fn test_init_array() { extern "C" { fn init_array(ptr: *mut i32, len: usize, val: i32); } const LEN: usize = 3; let mut array = MaybeUninit::<[i32; LEN]>::uninit(); let val = 31; let array = unsafe { init_array(array.as_mut_ptr().cast::(), LEN, val); array.assume_init() }; assert_eq!(array, [val; LEN]); } /// Test function that initializes an int pointed to by an immutable static. fn test_init_static_inner() { #[repr(C)] struct SyncPtr { ptr: *mut i32, } unsafe impl Sync for SyncPtr {} extern "C" { fn init_static_inner(s_ptr: *const SyncPtr, val: i32); } static mut INNER: MaybeUninit = MaybeUninit::uninit(); #[allow(static_mut_refs)] static STATIC: SyncPtr = SyncPtr { ptr: unsafe { INNER.as_mut_ptr() } }; let val = 41; let inner = unsafe { init_static_inner(&STATIC, val); INNER.assume_init() }; assert_eq!(inner, val); } // Test function that marks an allocation as exposed. fn test_exposed() { extern "C" { fn ignore_ptr(ptr: *const i32); } let x = 51; let ptr = &raw const x; let p = ptr.addr(); unsafe { ignore_ptr(ptr) }; assert_eq!(unsafe { *(p as *const i32) }, x); } /// Test function that swaps two pointers and exposes the alloc of an int. fn test_swap_ptr() { extern "C" { fn swap_ptr(pptr0: *mut *const i32, pptr1: *mut *const i32); } let x = 61; let (mut ptr0, mut ptr1) = (&raw const x, ptr::null()); unsafe { swap_ptr(&mut ptr0, &mut ptr1) }; assert_eq!(unsafe { *ptr1 }, x); } /// Test function that swaps two pointers in a struct and exposes the alloc of an int. fn test_swap_ptr_tuple() { #[repr(C)] struct Tuple { ptr0: *const i32, ptr1: *const i32, } extern "C" { fn swap_ptr_tuple(t_ptr: *mut Tuple); } let x = 71; let mut tuple = Tuple { ptr0: &raw const x, ptr1: ptr::null() }; unsafe { swap_ptr_tuple(&mut tuple) } assert_eq!(unsafe { *tuple.ptr1 }, x); } /// Test function that interacts with a dangling pointer. fn test_overwrite_dangling() { extern "C" { fn overwrite_ptr(pptr: *mut *const i32); } let b = Box::new(81); let mut ptr = Box::as_ptr(&b); drop(b); unsafe { overwrite_ptr(&mut ptr) }; assert_eq!(ptr, ptr::null()); } /// Test function that passes a dangling pointer. fn test_pass_dangling() { extern "C" { fn ignore_ptr(ptr: *const i32); } let b = Box::new(91); let ptr = Box::as_ptr(&b); drop(b); unsafe { ignore_ptr(ptr) }; } /// Test function that interacts with a struct storing a dangling pointer. fn test_swap_ptr_triple_dangling() { #[repr(C)] struct Triple { ptr0: *const i32, ptr1: *const i32, ptr2: *const i32, } extern "C" { fn swap_ptr_triple_dangling(t_ptr: *const Triple); } let x = 101; let b = Box::new(111); let ptr = Box::as_ptr(&b); drop(b); let z = 121; let triple = Triple { ptr0: &raw const x, ptr1: ptr, ptr2: &raw const z }; unsafe { swap_ptr_triple_dangling(&triple) } assert_eq!(unsafe { *triple.ptr2 }, x); } /// Test function that directly returns its pointer argument. fn test_return_ptr() { extern "C" { fn return_ptr(ptr: *const i32) -> *const i32; } let x = 131; let ptr = &raw const x; let ptr = unsafe { return_ptr(ptr) }; assert_eq!(unsafe { *ptr }, x); } /// Test casting a pointer to an integer and passing that to C. fn test_pass_ptr_as_int() { extern "C" { fn pass_ptr_as_int(ptr: usize, set_to_val: i32); } let mut m: MaybeUninit = MaybeUninit::uninit(); unsafe { pass_ptr_as_int(m.as_mut_ptr() as usize, 42) }; assert_eq!(unsafe { m.assume_init() }, 42); } fn test_pass_ptr_via_previously_shared_mem() { extern "C" { fn set_shared_mem(ptr: *mut *mut i32); fn init_ptr_stored_in_shared_mem(val: i32); } let mut m: *mut i32 = ptr::null_mut(); let ptr_to_m = &raw mut m; unsafe { set_shared_mem(&raw mut m) }; let mut m2: MaybeUninit = MaybeUninit::uninit(); // Store a pointer to m2 somewhere that C code can access it. unsafe { ptr_to_m.write(m2.as_mut_ptr()) }; // Have C code write there. unsafe { init_ptr_stored_in_shared_mem(42) }; // Ensure this memory is now considered initialized. assert_eq!(unsafe { m2.assume_init() }, 42); }