1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
//@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();
}
#[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.
pub 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;
}
}
pub 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);
}
|