| //@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::<i32>::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::<i32>(), 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<i32> = 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<i32> = 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<i32> = 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); |
| } |