1 #![feature(strict_provenance)]
2 #![feature(pointer_byte_offsets)]
5 const PTR_SIZE: usize = mem::size_of::<&i32>();
9 partial_overwrite_then_restore();
10 bytewise_ptr_methods();
11 bytewise_custom_memcpy();
12 bytewise_custom_memcpy_chunked();
15 /// Some basic smoke tests for provenance.
18 let ptr = x as *const i32;
19 let addr: usize = unsafe { mem::transmute(ptr) }; // an integer without provenance
20 // But we can give provenance back via `with_addr`.
21 let ptr_back = ptr.with_addr(addr);
22 assert_eq!(unsafe { *ptr_back }, 42);
24 // It is preserved by MaybeUninit.
25 let addr_mu: mem::MaybeUninit<usize> = unsafe { mem::transmute(ptr) };
26 let ptr_back: *const i32 = unsafe { mem::transmute(addr_mu) };
27 assert_eq!(unsafe { *ptr_back }, 42);
30 /// Overwrite one byte of a pointer, then restore it.
31 fn partial_overwrite_then_restore() {
32 unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit<u8>; PTR_SIZE] {
36 // Returns a value with the same provenance as `x` but 0 for the integer value.
37 // `x` must be initialized.
38 unsafe fn zero_with_provenance(x: mem::MaybeUninit<u8>) -> mem::MaybeUninit<u8> {
39 let ptr = [x; PTR_SIZE];
40 let ptr: *const i32 = mem::transmute(ptr);
41 let mut ptr = ptr.with_addr(0);
42 ptr_bytes(&mut ptr)[0]
47 let mut ptr = ptr as *const i32;
48 // Get a bytewise view of the pointer.
49 let ptr_bytes = ptr_bytes(&mut ptr);
51 // The highest bytes must be 0 for this to work.
52 let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 };
53 assert_eq!(*ptr_bytes[hi].as_ptr().cast::<u8>(), 0);
54 // Overwrite provenance on the last byte.
55 ptr_bytes[hi] = mem::MaybeUninit::new(0);
56 // Restore it from the another byte.
57 ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]);
59 // Now ptr should be good again.
64 fn bytewise_ptr_methods() {
68 // Swap them, bytewise.
70 ptr::swap_nonoverlapping(
71 &mut ptr1 as *mut _ as *mut mem::MaybeUninit<u8>,
72 &mut ptr2 as *mut _ as *mut mem::MaybeUninit<u8>,
73 mem::size_of::<&i32>(),
77 // Make sure they still work.
81 // TODO: also test ptr::swap, ptr::copy, ptr::copy_nonoverlapping.
84 fn bytewise_custom_memcpy() {
85 unsafe fn memcpy<T>(to: *mut T, from: *const T) {
86 let to = to.cast::<mem::MaybeUninit<u8>>();
87 let from = from.cast::<mem::MaybeUninit<u8>>();
88 for i in 0..mem::size_of::<T>() {
89 let b = from.add(i).read();
98 unsafe { memcpy(&mut ptr2, &ptr1) };
100 // Make sure they still work.
101 assert_eq!(*ptr1, 1);
102 assert_eq!(*ptr2, 1);
105 fn bytewise_custom_memcpy_chunked() {
106 unsafe fn memcpy<T>(to: *mut T, from: *const T) {
107 assert!(mem::size_of::<T>() % mem::size_of::<usize>() == 0);
108 let count = mem::size_of::<T>() / mem::size_of::<usize>();
109 let to = to.cast::<mem::MaybeUninit<usize>>();
110 let from = from.cast::<mem::MaybeUninit<usize>>();
112 let b = from.add(i).read();
117 // Prepare an array where pointers are stored at... interesting... offsets.
118 let mut data = [0usize; 2 * PTR_SIZE];
119 let mut offsets = vec![];
120 for i in 0..mem::size_of::<usize>() {
121 // We have 2*PTR_SIZE room for each of these pointers.
122 let base = i * 2 * PTR_SIZE;
123 // This one is mis-aligned by `i`.
124 let offset = base + i;
125 offsets.push(offset);
127 unsafe { data.as_mut_ptr().byte_add(offset).cast::<&i32>().write_unaligned(&42) };
131 let mut data2 = [0usize; 2 * PTR_SIZE];
132 unsafe { memcpy(&mut data2, &data) };
134 // And check the result.
135 for &offset in &offsets {
136 let ptr = unsafe { data2.as_ptr().byte_add(offset).cast::<&i32>().read_unaligned() };
137 assert_eq!(*ptr, 42);