//@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(strict_provenance)] use std::{mem, ptr}; const PTR_SIZE: usize = mem::size_of::<&i32>(); fn main() { basic(); partial_overwrite_then_restore(); bytewise_ptr_methods(); bytewise_custom_memcpy(); bytewise_custom_memcpy_chunked(); int_load_strip_provenance(); } /// Some basic smoke tests for provenance. fn basic() { let x = &42; let ptr = x as *const i32; let addr: usize = unsafe { mem::transmute(ptr) }; // an integer without provenance // But we can give provenance back via `with_addr`. let ptr_back = ptr.with_addr(addr); assert_eq!(unsafe { *ptr_back }, 42); // It is preserved by MaybeUninit. let addr_mu: mem::MaybeUninit = unsafe { mem::transmute(ptr) }; let ptr_back: *const i32 = unsafe { mem::transmute(addr_mu) }; assert_eq!(unsafe { *ptr_back }, 42); } /// Overwrite one byte of a pointer, then restore it. fn partial_overwrite_then_restore() { unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit; PTR_SIZE] { mem::transmute(ptr) } // Returns a value with the same provenance as `x` but 0 for the integer value. // `x` must be initialized. unsafe fn zero_with_provenance(x: mem::MaybeUninit) -> mem::MaybeUninit { let ptr = [x; PTR_SIZE]; let ptr: *const i32 = mem::transmute(ptr); let mut ptr = ptr.with_addr(0); ptr_bytes(&mut ptr)[0] } unsafe { let ptr = &42; let mut ptr = ptr as *const i32; // Get a bytewise view of the pointer. let ptr_bytes = ptr_bytes(&mut ptr); // The highest bytes must be 0 for this to work. let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 }; assert_eq!(*ptr_bytes[hi].as_ptr().cast::(), 0); // Overwrite provenance on the last byte. ptr_bytes[hi] = mem::MaybeUninit::new(0); // Restore it from the another byte. ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]); // Now ptr should be good again. assert_eq!(*ptr, 42); } } fn bytewise_ptr_methods() { let mut ptr1 = &1; let mut ptr2 = &2; // Swap them, bytewise. unsafe { ptr::swap_nonoverlapping( &mut ptr1 as *mut _ as *mut mem::MaybeUninit, &mut ptr2 as *mut _ as *mut mem::MaybeUninit, mem::size_of::<&i32>(), ); } // Make sure they still work. assert_eq!(*ptr1, 2); assert_eq!(*ptr2, 1); // TODO: also test ptr::swap, ptr::copy, ptr::copy_nonoverlapping. } fn bytewise_custom_memcpy() { unsafe fn memcpy(to: *mut T, from: *const T) { let to = to.cast::>(); let from = from.cast::>(); for i in 0..mem::size_of::() { let b = from.add(i).read(); to.add(i).write(b); } } let ptr1 = &1; let mut ptr2 = &2; // Copy, bytewise. unsafe { memcpy(&mut ptr2, &ptr1) }; // Make sure they still work. assert_eq!(*ptr1, 1); assert_eq!(*ptr2, 1); } fn bytewise_custom_memcpy_chunked() { unsafe fn memcpy(to: *mut T, from: *const T) { assert!(mem::size_of::() % mem::size_of::() == 0); let count = mem::size_of::() / mem::size_of::(); let to = to.cast::>(); let from = from.cast::>(); for i in 0..count { let b = from.add(i).read(); to.add(i).write(b); } } // Prepare an array where pointers are stored at... interesting... offsets. let mut data = [0usize; 2 * PTR_SIZE]; let mut offsets = vec![]; for i in 0..mem::size_of::() { // We have 2*PTR_SIZE room for each of these pointers. let base = i * 2 * PTR_SIZE; // This one is mis-aligned by `i`. let offset = base + i; offsets.push(offset); // Store it there. unsafe { data.as_mut_ptr().byte_add(offset).cast::<&i32>().write_unaligned(&42) }; } // Now memcpy that. let mut data2 = [0usize; 2 * PTR_SIZE]; unsafe { memcpy(&mut data2, &data) }; // And check the result. for &offset in &offsets { let ptr = unsafe { data2.as_ptr().byte_add(offset).cast::<&i32>().read_unaligned() }; assert_eq!(*ptr, 42); } } fn int_load_strip_provenance() { let ptrs = [&42]; let ints: [usize; 1] = unsafe { mem::transmute(ptrs) }; assert_eq!(ptrs[0] as *const _ as usize, ints[0]); }