TB: new tests

This commit is contained in:
Neven Villani 2023-03-16 14:49:00 +01:00
parent 8741303f6e
commit e243206ae3
30 changed files with 888 additions and 0 deletions

View File

@ -0,0 +1,19 @@
//@compile-flags: -Zmiri-tree-borrows
// Check that TB properly rejects alternating Reads and Writes, but tolerates
// alternating only Reads to Reserved mutable references.
pub fn main() {
let x = &mut 0u8;
let y = unsafe { &mut *(x as *mut u8) };
// Foreign Read, but this is a no-op from the point of view of y (still Reserved)
let _val = *x;
// Now we activate y, for this to succeed y needs to not have been Frozen
// by the previous operation
*y += 1; // Success
// This time y gets Frozen...
let _val = *x;
// ... and the next Write attempt fails.
*y += 1; // Failure //~ ERROR: /write access through .* is forbidden/
let _val = *x;
*y += 1; // Unreachable
}

View File

@ -0,0 +1,14 @@
error: Undefined Behavior: write access through <TAG> is forbidden because it is a child of <TAG> which is Frozen.
--> $DIR/alternate-read-write.rs:LL:CC
|
LL | *y += 1; // Failure
| ^^^^^^^ write access through <TAG> is forbidden because it is a child of <TAG> which is Frozen.
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= note: BACKTRACE:
= note: inside `main` at $DIR/alternate-read-write.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -0,0 +1,42 @@
//! Race-condition-like interaction between a read and a reborrow.
//! Even though no write or fake write occurs, reads have an effect on protected
//! Reserved. This is a protected-retag/read data race, but is not *detected* as
//! a data race violation because reborrows are not writes.
//!
//! This test is sensitive to the exact schedule so we disable preemption.
//@compile-flags: -Zmiri-tree-borrows -Zmiri-preemption-rate=0
use std::ptr::addr_of_mut;
use std::thread;
#[derive(Copy, Clone)]
struct SendPtr(*mut u8);
unsafe impl Send for SendPtr {}
// First thread is just a reborrow, but for an instant `x` is
// protected and thus vulnerable to foreign reads.
fn thread_1(x: &mut u8) -> SendPtr {
thread::yield_now(); // make the other thread go first
SendPtr(x as *mut u8)
}
// Second thread simply performs a read.
fn thread_2(x: &u8) {
let _val = *x;
}
fn main() {
let mut x = 0u8;
let x_1 = unsafe { &mut *addr_of_mut!(x) };
let xg = unsafe { &*addr_of_mut!(x) };
// The two threads are executed in parallel on aliasing pointers.
// UB occurs if the read of thread_2 occurs while the protector of thread_1
// is in place.
let hf = thread::spawn(move || thread_1(x_1));
let hg = thread::spawn(move || thread_2(xg));
let SendPtr(p) = hf.join().unwrap();
let () = hg.join().unwrap();
unsafe { *p = 1 }; //~ ERROR: /write access through .* is forbidden/
}

View File

@ -0,0 +1,14 @@
error: Undefined Behavior: write access through <TAG> is forbidden because it is a child of <TAG> which is Frozen.
--> $DIR/fragile-data-race.rs:LL:CC
|
LL | unsafe { *p = 1 };
| ^^^^^^ write access through <TAG> is forbidden because it is a child of <TAG> which is Frozen.
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= note: BACKTRACE:
= note: inside `main` at $DIR/fragile-data-race.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -0,0 +1,22 @@
//@compile-flags: -Zmiri-tree-borrows
// Check that in the case of locations outside the range of a pointer,
// protectors trigger if and only if the location has already been accessed
fn main() {
unsafe {
let data = &mut [0u8, 1, 2, 3];
let raw = data.as_mut_ptr();
stuff(&mut *raw, raw);
}
}
unsafe fn stuff(x: &mut u8, y: *mut u8) {
let xraw = x as *mut u8;
// No issue here: location 1 is not accessed
*y.add(1) = 42;
// Still no issue: location 2 is not invalidated
let _val = *xraw.add(2);
// However protector triggers if location is both accessed and invalidated
let _val = *xraw.add(3);
*y.add(3) = 42; //~ ERROR: /write access through .* is forbidden/
}

View File

@ -0,0 +1,19 @@
error: Undefined Behavior: write access through <TAG> is forbidden because it is a foreign tag for <TAG>, which would hence change from Reserved to Disabled, but <TAG> is protected
--> $DIR/outside-range.rs:LL:CC
|
LL | *y.add(3) = 42;
| ^^^^^^^^^^^^^^ write access through <TAG> is forbidden because it is a foreign tag for <TAG>, which would hence change from Reserved to Disabled, but <TAG> is protected
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= note: BACKTRACE:
= note: inside `stuff` at $DIR/outside-range.rs:LL:CC
note: inside `main`
--> $DIR/outside-range.rs:LL:CC
|
LL | stuff(&mut *raw, raw);
| ^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -0,0 +1,14 @@
//@compile-flags: -Zmiri-tree-borrows
// Read to local variable kills reborrows *and* raw pointers derived from them.
// This test would succeed under Stacked Borrows.
fn main() {
unsafe {
let mut root = 6u8;
let mref = &mut root;
let ptr = mref as *mut u8;
*ptr = 0; // Write
assert_eq!(root, 0); // Parent Read
*ptr = 0; //~ ERROR: /write access through .* is forbidden/
}
}

View File

@ -0,0 +1,14 @@
error: Undefined Behavior: write access through <TAG> is forbidden because it is a child of <TAG> which is Frozen.
--> $DIR/read-to-local.rs:LL:CC
|
LL | *ptr = 0;
| ^^^^^^^^ write access through <TAG> is forbidden because it is a child of <TAG> which is Frozen.
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= note: BACKTRACE:
= note: inside `main` at $DIR/read-to-local.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -0,0 +1,34 @@
//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
// Check how a Reserved with interior mutability
// responds to a Foreign Write under a Protector
#[path = "../../../utils/mod.rs"]
mod utils;
use utils::macros::*;
use std::cell::UnsafeCell;
fn main() {
unsafe {
let n = &mut UnsafeCell::new(0u8);
name!(n.get(), "base");
let x = &mut *(n as *mut UnsafeCell<_>);
name!(x.get(), "x");
let y = (&mut *n).get();
name!(y);
write_second(x, y);
unsafe fn write_second(x: &mut UnsafeCell<u8>, y: *mut u8) {
let alloc_id = alloc_id!(x.get());
name!(x.get(), "callee:x");
name!(x.get()=>1, "caller:x");
name!(y, "callee:y");
name!(y, "caller:y");
print_state!(alloc_id);
// Right before the faulty Write, x is
// - Reserved
// - with interior mut
// - Protected
*y = 1; //~ ERROR: /write access through .* is forbidden/
}
}
}

View File

@ -0,0 +1,28 @@
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Re*| └─┬──<TAG=base>
| Re*| ├─┬──<TAG=x>
| Re*| │ └─┬──<TAG=caller:x>
| Re*| │ └────<TAG=callee:x> Strongly protected
| Re*| └────<TAG=y,callee:y,caller:y>
──────────────────────────────────────────────────────────────────────
error: Undefined Behavior: write access through <TAG> (also named 'y,callee:y,caller:y') is forbidden because it is a foreign tag for <TAG> (also named 'callee:x'), which would hence change from Reserved to Disabled, but <TAG> (also named 'callee:x') is protected
--> $DIR/cell-protected-write.rs:LL:CC
|
LL | *y = 1;
| ^^^^^^ write access through <TAG> (also named 'y,callee:y,caller:y') is forbidden because it is a foreign tag for <TAG> (also named 'callee:x'), which would hence change from Reserved to Disabled, but <TAG> (also named 'callee:x') is protected
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= note: BACKTRACE:
= note: inside `main::write_second` at $DIR/cell-protected-write.rs:LL:CC
note: inside `main`
--> $DIR/cell-protected-write.rs:LL:CC
|
LL | write_second(x, y);
| ^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -0,0 +1,32 @@
//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
#[path = "../../../utils/mod.rs"]
mod utils;
use utils::macros::*;
// Check how a Reserved without interior mutability responds to a Foreign
// Write when under a protector
fn main() {
unsafe {
let n = &mut 0u8;
name!(n);
let x = &mut *(n as *mut _);
name!(x);
let y = (&mut *n) as *mut _;
name!(y);
write_second(x, y);
unsafe fn write_second(x: &mut u8, y: *mut u8) {
let alloc_id = alloc_id!(x);
name!(x, "callee:x");
name!(x=>1, "caller:x");
name!(y, "callee:y");
name!(y, "caller:y");
print_state!(alloc_id);
// Right before the faulty Write, x is
// - Reserved
// - Protected
// The Write turns it Disabled
*y = 0; //~ ERROR: /write access through .* is forbidden/
}
}
}

View File

@ -0,0 +1,28 @@
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Res| └─┬──<TAG=n>
| Res| ├─┬──<TAG=x>
| Res| │ └─┬──<TAG=caller:x>
| Res| │ └────<TAG=callee:x> Strongly protected
| Res| └────<TAG=y,callee:y,caller:y>
──────────────────────────────────────────────────────────────────────
error: Undefined Behavior: write access through <TAG> (also named 'y,callee:y,caller:y') is forbidden because it is a foreign tag for <TAG> (also named 'callee:x'), which would hence change from Reserved to Disabled, but <TAG> (also named 'callee:x') is protected
--> $DIR/int-protected-write.rs:LL:CC
|
LL | *y = 0;
| ^^^^^^ write access through <TAG> (also named 'y,callee:y,caller:y') is forbidden because it is a foreign tag for <TAG> (also named 'callee:x'), which would hence change from Reserved to Disabled, but <TAG> (also named 'callee:x') is protected
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= note: BACKTRACE:
= note: inside `main::write_second` at $DIR/int-protected-write.rs:LL:CC
note: inside `main`
--> $DIR/int-protected-write.rs:LL:CC
|
LL | write_second(x, y);
| ^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -0,0 +1,28 @@
//! Make sure that a retag acts like a read for the data race model.
//! This is a retag/write race condition.
//!
//! This test is sensitive to the exact schedule so we disable preemption.
//@compile-flags: -Zmiri-tree-borrows -Zmiri-preemption-rate=0
#[derive(Copy, Clone)]
struct SendPtr(*mut u8);
unsafe impl Send for SendPtr {}
unsafe fn thread_1(SendPtr(p): SendPtr) {
let _r = &*p;
}
unsafe fn thread_2(SendPtr(p): SendPtr) {
*p = 5; //~ ERROR: Data race detected between (1) Read on thread `<unnamed>` and (2) Write on thread `<unnamed>`
}
fn main() {
let mut x = 0;
let p = std::ptr::addr_of_mut!(x);
let p = SendPtr(p);
let t1 = std::thread::spawn(move || unsafe { thread_1(p) });
let t2 = std::thread::spawn(move || unsafe { thread_2(p) });
let _ = t1.join();
let _ = t2.join();
}

View File

@ -0,0 +1,25 @@
error: Undefined Behavior: Data race detected between (1) Read on thread `<unnamed>` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
--> $DIR/retag-data-race.rs:LL:CC
|
LL | *p = 5;
| ^^^^^^ Data race detected between (1) Read on thread `<unnamed>` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
|
help: and (1) occurred earlier here
--> $DIR/retag-data-race.rs:LL:CC
|
LL | let _r = &*p;
| ^^^
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE (of the first span):
= note: inside `thread_2` at $DIR/retag-data-race.rs:LL:CC
note: inside closure
--> $DIR/retag-data-race.rs:LL:CC
|
LL | let t2 = std::thread::spawn(move || unsafe { thread_2(p) });
| ^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -0,0 +1,27 @@
//@compile-flags: -Zmiri-tree-borrows
// We invalidate a reference during a 2-phase borrow by doing a Foreign
// Write in between the initial reborrow and function entry. UB occurs
// on function entry when reborrow from a Disabled fails.
// This test would pass under Stacked Borrows, but Tree Borrows
// is more strict on 2-phase borrows.
struct Foo(u64);
impl Foo {
#[rustfmt::skip] // rustfmt is wrong about which line contains an error
fn add(&mut self, n: u64) -> u64 { //~ ERROR: /read access through .* is forbidden/
self.0 + n
}
}
pub fn main() {
let mut f = Foo(0);
let inner = &mut f.0 as *mut u64;
let _res = f.add(unsafe {
let n = f.0;
// This is the access at fault, but it's not immediately apparent because
// the reference that got invalidated is not under a Protector.
*inner = 42;
n
});
}

View File

@ -0,0 +1,26 @@
error: Undefined Behavior: read access through <TAG> is forbidden because it is a child of <TAG> which is Disabled.
--> $DIR/write-during-2phase.rs:LL:CC
|
LL | fn add(&mut self, n: u64) -> u64 {
| ^^^^^^^^^ read access through <TAG> is forbidden because it is a child of <TAG> which is Disabled.
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= note: BACKTRACE:
= note: inside `Foo::add` at $DIR/write-during-2phase.rs:LL:CC
note: inside `main`
--> $DIR/write-during-2phase.rs:LL:CC
|
LL | let _res = f.add(unsafe {
| ________________^
LL | | let n = f.0;
LL | | // This is the access at fault, but it's not immediately apparent because
LL | | // the reference that got invalidated is not under a Protector.
LL | | *inner = 42;
LL | | n
LL | | });
| |______^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View File

@ -0,0 +1,27 @@
//@compile-flags: -Zmiri-tree-borrows
// Counterpart to tests/fail/tree-borrows/write-during-2phase.rs,
// this is the opposite situation: the Write is not problematic because
// the Protector has not yet been added and the Reserved has interior
// mutability.
use core::cell::Cell;
trait Thing: Sized {
fn do_the_thing(&mut self, _s: i32) {}
}
impl<T> Thing for Cell<T> {}
fn main() {
let mut x = Cell::new(1);
let l = &x;
x.do_the_thing({
// Several Foreign accesses (both Reads and Writes) to the location
// being reborrowed. Reserved + unprotected + interior mut
// makes the pointer immune to everything as long as all accesses
// are child accesses to its parent pointer x.
x.set(3);
l.set(4);
x.get() + l.get()
});
}

View File

@ -0,0 +1,27 @@
//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
#[path = "../../utils/mod.rs"]
mod utils;
use utils::macros::*;
use std::cell::UnsafeCell;
// UnsafeCells use the parent tag, so it is possible to use them with
// few restrictions when only among themselves.
fn main() {
unsafe {
let data = &mut UnsafeCell::new(0u8);
name!(data.get(), "data");
let x = &*data;
name!(x.get(), "x");
let y = &*data;
name!(y.get(), "y");
let alloc_id = alloc_id!(data.get());
print_state!(alloc_id);
// y and x tolerate alternating Writes
*y.get() = 1;
*x.get() = 2;
*y.get() = 3;
*x.get() = 4;
print_state!(alloc_id);
}
}

View File

@ -0,0 +1,10 @@
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Re*| └────<TAG=data,x,y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └────<TAG=data,x,y>
──────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,29 @@
//@compile-flags: -Zmiri-tree-borrows
// copy_nonoverlapping works regardless of the order in which we construct
// the arguments.
pub fn main() {
test_to_from();
test_from_to();
}
fn test_to_from() {
unsafe {
let data = &mut [0u64, 1];
let to = data.as_mut_ptr().add(1);
let from = data.as_ptr();
std::ptr::copy_nonoverlapping(from, to, 1);
}
}
// Stacked Borrows would not have liked this one because the `as_mut_ptr` reborrow
// invalidates the earlier pointer obtained from `as_ptr`, but Tree Borrows is fine
// with it.
fn test_from_to() {
unsafe {
let data = &mut [0u64, 1];
let from = data.as_ptr();
let to = data.as_mut_ptr().add(1);
std::ptr::copy_nonoverlapping(from, to, 1);
}
}

View File

@ -0,0 +1,32 @@
//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
// Check that a protector goes back to normal behavior when the function
// returns.
#[path = "../../utils/mod.rs"]
mod utils;
use utils::macros::*;
fn main() {
unsafe {
let data = &mut 0u8;
name!(data);
let alloc_id = alloc_id!(data);
let x = &mut *data;
name!(x);
print_state!(alloc_id);
do_nothing(x); // creates then removes a Protector for a child of x
let y = &mut *data;
name!(y);
print_state!(alloc_id);
// Invalidates the previous reborrow, but its Protector has been removed.
*y = 1;
print_state!(alloc_id);
}
}
unsafe fn do_nothing(x: &mut u8) {
name!(x, "callee:x");
name!(x=>1, "caller:x");
let alloc_id = alloc_id!(x);
print_state!(alloc_id);
}

View File

@ -0,0 +1,32 @@
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Res| └─┬──<TAG=data>
| Res| └────<TAG=x>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Res| └─┬──<TAG=data>
| Res| └─┬──<TAG=x>
| Res| └─┬──<TAG=caller:x>
| Res| └────<TAG=callee:x> Strongly protected
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Res| └─┬──<TAG=data>
| Res| ├─┬──<TAG=x>
| Res| │ └─┬──<TAG=caller:x>
| Res| │ └────<TAG=callee:x>
| Res| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=data>
| Dis| ├─┬──<TAG=x>
| Dis| │ └─┬──<TAG=caller:x>
| Dis| │ └────<TAG=callee:x>
| Act| └────<TAG=y>
──────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,73 @@
//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
#[path = "../../utils/mod.rs"]
mod utils;
use utils::macros::*;
// Check the formatting of the trees.
fn main() {
unsafe {
alignment_check();
structure_check();
}
}
// Alignment check: we split the array at indexes with different amounts of
// decimal digits to verify proper padding.
unsafe fn alignment_check() {
let data: &mut [u8] = &mut [0; 1024];
name!(data.as_ptr()=>2, "data");
let alloc_id = alloc_id!(data.as_ptr());
let x = &mut data[1];
name!(x as *mut _, "data[1]");
*x = 1;
let x = &mut data[10];
name!(x as *mut _, "data[10]");
*x = 1;
let x = &mut data[100];
name!(x as *mut _, "data[100]");
*x = 1;
let _val = data[100]; // So that the above is Frz
let x = &mut data[1000];
name!(x as *mut _, "data[1000]");
*x = 1;
print_state!(alloc_id);
}
// Tree structure check: somewhat complex organization of reborrows.
unsafe fn structure_check() {
let x = &0u8;
name!(x);
let xa = &*x;
name!(xa);
let xb = &*x;
name!(xb);
let xc = &*x;
name!(xc);
let xaa = &*xa;
name!(xaa);
let xab = &*xa;
name!(xab);
let xba = &*xb;
name!(xba);
let xbaa = &*xba;
name!(xbaa);
let xbaaa = &*xbaa;
name!(xbaaa);
let xbaaaa = &*xbaaa;
name!(xbaaaa);
let xca = &*xc;
name!(xca);
let xcb = &*xc;
name!(xcb);
let xcaa = &*xca;
name!(xcaa);
let xcab = &*xca;
name!(xcab);
let xcba = &*xcb;
name!(xcba);
let xcbb = &*xcb;
name!(xcbb);
let alloc_id = alloc_id!(x);
print_state!(alloc_id);
}

View File

@ -0,0 +1,29 @@
─────────────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1.. 2.. 10.. 11..100..101..1000..1001..1024
| Res| Act| Res| Act| Res| Act| Res| Act| Res| └─┬──<TAG=data>
|----| Act|----|?Dis|----|?Dis| ----| ?Dis| ----| ├────<TAG=data[1]>
|----|----|----| Act|----|?Dis| ----| ?Dis| ----| ├────<TAG=data[10]>
|----|----|----|----|----| Frz| ----| ?Dis| ----| ├────<TAG=data[100]>
|----|----|----|----|----|----| ----| Act| ----| └────<TAG=data[1000]>
─────────────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Frz| └─┬──<TAG=x>
| Frz| ├─┬──<TAG=xa>
| Frz| │ ├────<TAG=xaa>
| Frz| │ └────<TAG=xab>
| Frz| ├─┬──<TAG=xb>
| Frz| │ └─┬──<TAG=xba>
| Frz| │ └─┬──<TAG=xbaa>
| Frz| │ └─┬──<TAG=xbaaa>
| Frz| │ └────<TAG=xbaaaa>
| Frz| └─┬──<TAG=xc>
| Frz| ├─┬──<TAG=xca>
| Frz| │ ├────<TAG=xcaa>
| Frz| │ └────<TAG=xcab>
| Frz| └─┬──<TAG=xcb>
| Frz| ├────<TAG=xcba>
| Frz| └────<TAG=xcbb>
──────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,14 @@
//@compile-flags: -Zmiri-tree-borrows
// 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 main() {
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;
}
}

View File

@ -0,0 +1,24 @@
//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
#[path = "../../utils/mod.rs"]
mod utils;
use utils::macros::*;
// To check that a reborrow is counted as a Read access, we use a reborrow
// with no additional Read to Freeze an Active pointer.
fn main() {
unsafe {
let parent = &mut 0u8;
name!(parent);
let alloc_id = alloc_id!(parent);
let x = &mut *parent;
name!(x);
*x = 0; // x is now Active
print_state!(alloc_id);
let y = &mut *parent;
name!(y);
// Check in the debug output that x has been Frozen by the reborrow
print_state!(alloc_id);
}
}

View File

@ -0,0 +1,13 @@
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=parent>
| Act| └────<TAG=x>
──────────────────────────────────────────────────────────────────────
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=parent>
| Frz| ├────<TAG=x>
| Res| └────<TAG=y>
──────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,127 @@
//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
#[path = "../../utils/mod.rs"]
mod utils;
use utils::macros::*;
use utils::miri_extern::miri_write_to_stderr;
use std::cell::UnsafeCell;
// We exhaustively check that Reserved behaves as we want under all of the
// following conditions:
// - with or without interior mutability
// - with or without a protector
// - for a foreign read or write
// Of these cases, those in this file are the ones that must not cause
// immediate UB, and those that do are in tests/fail/tree-borrows/reserved/
// and are the combinations [_ + protected + write]
fn main() {
unsafe {
cell_protected_read();
cell_unprotected_read();
cell_unprotected_write();
int_protected_read();
int_unprotected_read();
int_unprotected_write();
}
}
unsafe fn print(msg: &str) {
miri_write_to_stderr(msg.as_bytes());
miri_write_to_stderr("\n".as_bytes());
}
unsafe fn read_second<T>(x: &mut T, y: *mut u8) {
name!(x as *mut T as *mut u8=>1, "caller:x");
name!(x as *mut T as *mut u8, "callee:x");
name!(y, "caller:y");
name!(y, "callee:y");
let _val = *y;
}
// Foreign Read on a interior mutable Protected Reserved turns it Frozen.
unsafe fn cell_protected_read() {
print("[interior mut + protected] Foreign Read: Re* -> Frz");
let base = &mut UnsafeCell::new(0u8);
name!(base.get(), "base");
let alloc_id = alloc_id!(base.get());
let x = &mut *(base as *mut UnsafeCell<u8>);
name!(x.get(), "x");
let y = (&mut *base).get();
name!(y);
read_second(x, y); // Foreign Read for callee:x
print_state!(alloc_id);
}
// Foreign Read on an interior mutable pointer is a noop.
unsafe fn cell_unprotected_read() {
print("[interior mut] Foreign Read: Re* -> Re*");
let base = &mut UnsafeCell::new(0u64);
name!(base.get(), "base");
let alloc_id = alloc_id!(base.get());
let x = &mut *(base as *mut UnsafeCell<_>);
name!(x.get(), "x");
let y = (&mut *base).get();
name!(y);
let _val = *y; // Foreign Read for x
print_state!(alloc_id);
}
// Foreign Write on an interior mutable pointer is a noop.
// Also y must become Active.
unsafe fn cell_unprotected_write() {
print("[interior mut] Foreign Write: Re* -> Re*");
let base = &mut UnsafeCell::new(0u64);
name!(base.get(), "base");
let alloc_id = alloc_id!(base.get());
let x = &mut *(base as *mut UnsafeCell<u64>);
name!(x.get(), "x");
let y = (&mut *base).get();
name!(y);
*y = 1; // Foreign Write for x
print_state!(alloc_id);
}
// Foreign Read on a Protected Reserved turns it Frozen.
unsafe fn int_protected_read() {
print("[protected] Foreign Read: Res -> Frz");
let base = &mut 0u8;
let alloc_id = alloc_id!(base);
name!(base);
let x = &mut *(base as *mut u8);
name!(x);
let y = (&mut *base) as *mut u8;
name!(y);
read_second(x, y); // Foreign Read for callee:x
print_state!(alloc_id);
}
// Foreign Read on a Reserved is a noop.
// Also y must become Active.
unsafe fn int_unprotected_read() {
print("[] Foreign Read: Res -> Res");
let base = &mut 0u8;
name!(base);
let alloc_id = alloc_id!(base);
let x = &mut *(base as *mut u8);
name!(x);
let y = (&mut *base) as *mut u8;
name!(y);
let _val = *y; // Foreign Read for x
print_state!(alloc_id);
}
// Foreign Write on a Reserved turns it Disabled.
unsafe fn int_unprotected_write() {
print("[] Foreign Write: Res -> Dis");
let base = &mut 0u8;
name!(base);
let alloc_id = alloc_id!(base);
let x = &mut *(base as *mut u8);
name!(x);
let y = (&mut *base) as *mut u8;
name!(y);
*y = 1; // Foreign Write for x
print_state!(alloc_id);
}

View File

@ -0,0 +1,52 @@
[interior mut + protected] Foreign Read: Re* -> Frz
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Re*| └─┬──<TAG=base>
| Re*| ├─┬──<TAG=x>
| Re*| │ └─┬──<TAG=caller:x>
| Frz| │ └────<TAG=callee:x>
| Re*| └────<TAG=y,caller:y,callee:y>
──────────────────────────────────────────────────────────────────────
[interior mut] Foreign Read: Re* -> Re*
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 8
| Re*| └─┬──<TAG=base>
| Re*| ├────<TAG=x>
| Re*| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
[interior mut] Foreign Write: Re* -> Re*
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 8
| Act| └─┬──<TAG=base>
| Re*| ├────<TAG=x>
| Act| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
[protected] Foreign Read: Res -> Frz
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Res| └─┬──<TAG=base>
| Res| ├─┬──<TAG=x>
| Res| │ └─┬──<TAG=caller:x>
| Frz| │ └────<TAG=callee:x>
| Res| └────<TAG=y,caller:y,callee:y>
──────────────────────────────────────────────────────────────────────
[] Foreign Read: Res -> Res
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Res| └─┬──<TAG=base>
| Res| ├────<TAG=x>
| Res| └────<TAG=y>
──────────────────────────────────────────────────────────────────────
[] Foreign Write: Res -> Dis
──────────────────────────────────────────────────────────────────────
Warning: this tree is indicative only. Some tags may have been hidden.
0.. 1
| Act| └─┬──<TAG=base>
| Dis| ├────<TAG=x>
| Act| └────<TAG=y>
──────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,13 @@
//@compile-flags: -Zmiri-tree-borrows
use core::cell::UnsafeCell;
use core::mem;
fn main() {
unsafe {
let x = &0i32;
// As long as we only read, transmuting this to UnsafeCell should be fine.
let cell_x: &UnsafeCell<i32> = mem::transmute(&x);
let _val = *cell_x.get();
}
}