blob: 57def78b0ab17d9154189d6057733fd549d4c0aa [file] [log] [blame]
//@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);
}