| //@revisions: tree tree_implicit_writes | |
| //@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows-implicit-writes | |
| //@compile-flags: -Zmiri-tree-borrows | |
| #![feature(allocator_api)] | |
| use std::{mem, ptr}; | |
| // Test various tree-borrows-specific things | |
| // (i.e., these do not work the same under SB). | |
| fn main() { | |
| aliasing_read_only_mutable_refs(); | |
| string_as_mut_ptr(); | |
| two_mut_protected_same_alloc(); | |
| direct_mut_to_const_raw(); | |
| local_addr_of_mut(); | |
| returned_mut_is_usable(); | |
| array_pointer_access(); | |
| } | |
| #[allow(unused_assignments)] | |
| fn local_addr_of_mut() { | |
| let mut local = 0; | |
| let ptr = ptr::addr_of_mut!(local); | |
| // In SB, `local` and `*ptr` would have different tags, but in TB they have the same tag. | |
| local = 1; | |
| unsafe { *ptr = 2 }; | |
| local = 3; | |
| unsafe { *ptr = 4 }; | |
| } | |
| // Tree Borrows has no issue with several mutable references existing | |
| // at the same time, as long as they are used only immutably. | |
| // I.e. multiple Reserved can coexist. | |
| fn aliasing_read_only_mutable_refs() { | |
| unsafe { | |
| let base = &mut 42u64; | |
| let r1 = &mut *(base as *mut u64); | |
| let r2 = &mut *(base as *mut u64); | |
| let _l = *r1; | |
| let _l = *r2; | |
| } | |
| } | |
| fn string_as_mut_ptr() { | |
| // This errors in Stacked Borrows since as_mut_ptr restricts the provenance, | |
| // but with Tree Borrows it should work. | |
| unsafe { | |
| let mut s = String::from("hello"); | |
| s.reserve(1); // make the `str` that `s` derefs to not cover the entire `s`. | |
| // Prevent automatically dropping the String's data | |
| let mut s = mem::ManuallyDrop::new(s); | |
| let ptr = s.as_mut_ptr(); | |
| let len = s.len(); | |
| let capacity = s.capacity(); | |
| let s = String::from_raw_parts(ptr, len, capacity); | |
| assert_eq!(String::from("hello"), s); | |
| } | |
| } | |
| // This function checks that there is no issue with having two mutable references | |
| // from the same allocation both under a protector. | |
| // This is safe code, it must absolutely not be UB. | |
| // This test failing is a symptom of forgetting to check that only initialized | |
| // locations can cause protector UB. | |
| fn two_mut_protected_same_alloc() { | |
| fn write_second(_x: &mut u8, y: &mut u8) { | |
| // write through `y` will make some locations of `x` (protected) | |
| // become Disabled. Those locations are outside of the range on which | |
| // `x` is initialized, and the protector must not trigger. | |
| *y = 1; | |
| } | |
| let mut data = (0u8, 1u8); | |
| write_second(&mut data.0, &mut data.1); | |
| } | |
| // This checks that a reborrowed mutable reference returned from a function | |
| // is actually writeable. | |
| // The fact that this is not obvious is due to the addition of | |
| // implicit reads on function exit that might freeze the return value. | |
| fn returned_mut_is_usable() { | |
| fn reborrow(x: &mut u8) -> &mut u8 { | |
| let y = &mut *x; | |
| // Activate the reference so that it is vulnerable to foreign reads. | |
| *y = *y; | |
| y | |
| // An implicit read through `x` is inserted here. | |
| } | |
| let mut data = 0; | |
| let x = &mut data; | |
| let y = reborrow(x); | |
| *y = 1; | |
| } | |
| // Make sure that coercing &mut T to *const T produces a writeable pointer. | |
| fn direct_mut_to_const_raw() { | |
| let x = &mut 0; | |
| let y: *const i32 = x; | |
| unsafe { | |
| *(y as *mut i32) = 1; | |
| } | |
| assert_eq!(*x, 1); | |
| } | |
| // Tests that accessing the same array using disjoint pointers does not cause UB under Tree Borrows. | |
| // The pointer will access the memory range using the outside permission, thus testing if it has been set correctly. | |
| // In particular, in implicit_writes mode we used to set the "outside" permissions to | |
| // `Unique`, which caused an ICE. | |
| // Put differently, this checks the absence of subobject provenance for arrays. | |
| fn array_pointer_access() { | |
| let mut x = [1u8; 11]; | |
| let y = (&raw mut x).cast(); | |
| let res = foo(&mut x[0], y); | |
| assert_eq!(res, 20); | |
| } | |
| fn foo(x: &mut u8, y: *mut u8) -> u8 { | |
| unsafe { | |
| let x: *mut u8 = x; | |
| let res1 = access_nexts(x.add(1), y.add(1)); | |
| let res2 = access_nexts(y.add(1), x.add(1)); | |
| res1 + res2 | |
| } | |
| } | |
| fn access_nexts(x: *mut u8, y: *mut u8) -> u8 { | |
| let mut sum = 0; | |
| for i in 0..5 { | |
| sum += unsafe { *x.add(2 * i) }; | |
| sum += unsafe { *y.add(2 * i + 1) }; | |
| } | |
| sum | |
| } |