Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,14 @@ fn main() {
miri_config.borrow_tracker =
Some(BorrowTrackerMethod::TreeBorrows(TreeBorrowsParams {
precise_interior_mut: true,
start_mut_ref_on_fn_entry_as_active: false,
}));
miri_config.provenance_mode = ProvenanceMode::Strict;
} else if arg == "-Zmiri-tree-borrows-strong" {
miri_config.borrow_tracker =
Some(BorrowTrackerMethod::TreeBorrows(TreeBorrowsParams {
precise_interior_mut: true,
start_mut_ref_on_fn_entry_as_active: true,
}));
miri_config.provenance_mode = ProvenanceMode::Strict;
} else if arg == "-Zmiri-tree-borrows-no-precise-interior-mut" {
Expand Down
1 change: 1 addition & 0 deletions src/borrow_tracker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ pub enum BorrowTrackerMethod {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TreeBorrowsParams {
pub precise_interior_mut: bool,
pub start_mut_ref_on_fn_entry_as_active: bool,
}

impl BorrowTrackerMethod {
Expand Down
56 changes: 54 additions & 2 deletions src/borrow_tracker/tree_borrows/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use rustc_abi::{BackendRepr, Size};
use rustc_middle::mir::{Mutability, RetagKind};
use rustc_middle::ty::layout::HasTypingEnv;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, InstanceKind, Ty};

use self::foreign_access_skipping::IdempotentForeignAccess;
use self::tree::LocationState;
use crate::borrow_tracker::tree_borrows::diagnostics::AccessCause;
use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind};
use crate::concurrency::data_race::NaReadType;
use crate::*;
Expand Down Expand Up @@ -130,6 +131,8 @@ pub struct NewPermission {
/// Whether this pointer is part of the arguments of a function call.
/// `protector` is `Some(_)` for all pointers marked `noalias`.
protector: Option<ProtectorKind>,
/// Whether an implicit write should be performed on retag.
do_a_write: bool,
}

impl<'tcx> NewPermission {
Expand All @@ -146,6 +149,33 @@ impl<'tcx> NewPermission {
let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
let is_protected = retag_kind == RetagKind::FnEntry;

let start_mut_ref_on_fn_entry_as_active = cx
.machine
.borrow_tracker
.as_ref()
.unwrap()
.borrow()
.borrow_tracker_method
.get_tree_borrows_params()
.start_mut_ref_on_fn_entry_as_active;

let current_function_involves_raw_pointers = 'b: {
let instance = cx
.machine
.threads
.active_thread_stack()
.last()
.expect("Current frame has no stack")
.instance();
let ty = instance.ty(*cx.tcx, cx.typing_env());
if !ty.is_fn() {
break 'b false;
}
let sig = ty.fn_sig(*cx.tcx);
matches!(instance.def, InstanceKind::Item(_))
&& sig.inputs_and_output().skip_binder().iter().any(|x| x.is_raw_ptr())
};

if matches!(ref_mutability, Some(Mutability::Mut) | None if !ty_is_unpin) {
// Mutable reference / Box to pinning type: retagging is a NOP.
// FIXME: with `UnsafePinned`, this should do proper per-byte tracking.
Expand Down Expand Up @@ -183,6 +213,10 @@ impl<'tcx> NewPermission {
// Weak protector for boxes
ProtectorKind::WeakProtector
}),
do_a_write: start_mut_ref_on_fn_entry_as_active
&& is_protected
&& !current_function_involves_raw_pointers
&& matches!(ref_mutability, Some(Mutability::Mut)),
Comment on lines +216 to +219
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding a new field, this should set both freeze_perm and nonfreeze_perm to Active.

That should then be used as a signal to control the AccessKind for the (already existing) initial access logic in tb_reborrow.

})
}
}
Expand Down Expand Up @@ -383,11 +417,28 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
base_offset,
orig_tag,
new_tag,
inside_perms,
inside_perms.clone(),
new_perm.outside_perm,
protected,
this.machine.current_span(),
)?;
if new_perm.do_a_write {
for (perm_range, _) in inside_perms.iter_all() {
// Some reborrows incur a write access to the new pointer, to make it active.
// Adjust range to be relative to allocation start (rather than to `place`).
let range_in_alloc = AllocRange {
start: Size::from_bytes(perm_range.start) + base_offset,
size: Size::from_bytes(perm_range.end - perm_range.start),
};
tree_borrows.perform_access(
new_tag,
Some((range_in_alloc, AccessKind::Write, AccessCause::Reborrow)),
this.machine.borrow_tracker.as_ref().unwrap(),
alloc_id,
this.machine.current_span(),
)?;
Comment on lines +433 to +439
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's already code a bit further up that does a write, that should be adjusted instead of adding a copy. We definitely don't want to do a read and a write.

}
}
drop(tree_borrows);

interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
Expand Down Expand Up @@ -587,6 +638,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
nonfreeze_access: true,
outside_perm: Permission::new_reserved_frz(),
protector: Some(ProtectorKind::StrongProtector),
do_a_write: false,
};
this.tb_retag_place(place, new_perm)
}
Expand Down
15 changes: 15 additions & 0 deletions tests/fail/both_borrows/spurious_write_may_be_added.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows-strong

fn may_insert_spurious_write(_x: &mut u32) {}

fn main() {
let target = &mut 42;
let target_alias = &*target;
let target_alias_ptr = target_alias as *const _;
may_insert_spurious_write(target);
// now `target_alias` is invalid
let _val = unsafe { *target_alias_ptr };
//~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/
//~[tree]| ERROR: /read access through .* is forbidden/
}
25 changes: 25 additions & 0 deletions tests/fail/both_borrows/spurious_write_may_be_added.stack.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
--> tests/fail/both_borrows/spurious_write_may_be_added.rs:LL:CC
|
LL | let _val = unsafe { *target_alias_ptr };
| ^^^^^^^^^^^^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x4]
--> tests/fail/both_borrows/spurious_write_may_be_added.rs:LL:CC
|
LL | let target_alias_ptr = target_alias as *const _;
| ^^^^^^^^^^^^
help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique function-entry retag inside this call
--> tests/fail/both_borrows/spurious_write_may_be_added.rs:LL:CC
|
LL | may_insert_spurious_write(target);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside `main` at tests/fail/both_borrows/spurious_write_may_be_added.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

27 changes: 27 additions & 0 deletions tests/fail/both_borrows/spurious_write_may_be_added.tree.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
--> tests/fail/both_borrows/spurious_write_may_be_added.rs:LL:CC
|
LL | let _val = unsafe { *target_alias_ptr };
| ^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> has state Disabled which forbids this child read access
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> tests/fail/both_borrows/spurious_write_may_be_added.rs:LL:CC
|
LL | let target_alias = &*target;
| ^^^^^^^^
help: the accessed tag <TAG> later transitioned to Disabled due to a reborrow (acting as a foreign read access) at offsets [0x0..0x4]
--> tests/fail/both_borrows/spurious_write_may_be_added.rs:LL:CC
|
LL | fn may_insert_spurious_write(_x: &mut u32) {}
| ^^
= help: this transition corresponds to a loss of read permissions
= note: BACKTRACE (of the first span):
= note: inside `main` at tests/fail/both_borrows/spurious_write_may_be_added.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

49 changes: 49 additions & 0 deletions tests/fail/both_borrows/spurious_write_may_be_added_data_race.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows-strong
use std::sync::{Arc, Barrier};

#[derive(Copy, Clone)]
struct SendPtr(*const u32);

unsafe impl Send for SendPtr {}

type IdxBarrier = (usize, Arc<Barrier>);

// Barriers to enforce the interleaving.
// This macro expects `synchronized!(thread, msg)` where `thread` is a `IdxBarrier`,
// and `msg` is the message to be displayed when the thread reaches this point in the execution.
macro_rules! synchronized {
($thread:expr, $msg:expr) => {{
let (thread_id, barrier) = &$thread;
eprintln!("Thread {} executing: {}", thread_id, $msg);
barrier.wait();
}};
}

fn may_insert_spurious_write(_x: &mut u32, b: IdxBarrier) {
synchronized!(b, "after enter");
synchronized!(b, "before exit");
}

fn main() {
let target = &mut 42;
let target_alias = &*target;
let target_alias_ptr = SendPtr(target_alias as *const _);

let barrier = Arc::new(Barrier::new(2));
let bx = (1, Arc::clone(&barrier));
let by = (2, Arc::clone(&barrier));

let join_handle = std::thread::spawn(move || {
synchronized!(bx, "before read");
let ptr = target_alias_ptr;
// now `target_alias` is invalid
let _val = unsafe { *ptr.0 };
//~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/
//~[tree]| ERROR: /read access through .* is forbidden/
synchronized!(bx, "after read");
});

may_insert_spurious_write(target, by);
let _ = join_handle.join();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Thread 2 executing: after enter
Thread 1 executing: before read
error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
--> tests/fail/both_borrows/spurious_write_may_be_added_data_race.rs:LL:CC
|
LL | let _val = unsafe { *ptr.0 };
| ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x4]
--> tests/fail/both_borrows/spurious_write_may_be_added_data_race.rs:LL:CC
|
LL | let target_alias_ptr = SendPtr(target_alias as *const _);
| ^^^^^^^^^^^^
help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique function-entry retag inside this call
--> tests/fail/both_borrows/spurious_write_may_be_added_data_race.rs:LL:CC
|
LL | may_insert_spurious_write(target, by);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: BACKTRACE (of the first span) on thread `unnamed-ID`:
= note: inside closure at tests/fail/both_borrows/spurious_write_may_be_added_data_race.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Thread 2 executing: after enter
Thread 1 executing: before read
error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
--> tests/fail/both_borrows/spurious_write_may_be_added_data_race.rs:LL:CC
|
LL | let _val = unsafe { *ptr.0 };
| ^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> has state Disabled which forbids this child read access
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> tests/fail/both_borrows/spurious_write_may_be_added_data_race.rs:LL:CC
|
LL | let target_alias = &*target;
| ^^^^^^^^
help: the accessed tag <TAG> later transitioned to Disabled due to a reborrow (acting as a foreign read access) at offsets [0x0..0x4]
--> tests/fail/both_borrows/spurious_write_may_be_added_data_race.rs:LL:CC
|
LL | fn may_insert_spurious_write(_x: &mut u32, b: IdxBarrier) {
| ^^
= help: this transition corresponds to a loss of read permissions
= note: BACKTRACE (of the first span) on thread `unnamed-ID`:
= note: inside closure at tests/fail/both_borrows/spurious_write_may_be_added_data_race.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

3 changes: 2 additions & 1 deletion tests/pass/both_borrows/2phase.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//@revisions: stack tree
//@revisions: stack tree tree_strong
//@[tree]compile-flags: -Zmiri-tree-borrows
//@[tree_strong]compile-flags: -Zmiri-tree-borrows-strong

trait S: Sized {
fn tpb(&mut self, _s: Self) {}
Expand Down
3 changes: 2 additions & 1 deletion tests/pass/both_borrows/basic_aliasing_model.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//@revisions: stack tree
//@revisions: stack tree tree_strong
//@[tree]compile-flags: -Zmiri-tree-borrows
//@[tree_strong]compile-flags: -Zmiri-tree-borrows-strong
#![feature(allocator_api)]
use std::alloc::{Layout, alloc, dealloc};
use std::cell::Cell;
Expand Down
3 changes: 2 additions & 1 deletion tests/pass/both_borrows/interior_mutability.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//@revisions: stack tree
//@revisions: stack tree tree_strong
//@[tree]compile-flags: -Zmiri-tree-borrows
//@[tree_strong]compile-flags: -Zmiri-tree-borrows-strong
#![allow(dangerous_implicit_autorefs)]

use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
Expand Down
3 changes: 2 additions & 1 deletion tests/pass/both_borrows/smallvec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
//! What makes it interesting as a test is that it relies on Stacked Borrow's "quirk"
//! in a fundamental, hard-to-fix-without-full-trees way.
//@revisions: stack tree
//@revisions: stack tree tree_strong
//@[tree]compile-flags: -Zmiri-tree-borrows
//@[tree_strong]compile-flags: -Zmiri-tree-borrows-strong

use std::marker::PhantomData;
use std::mem::{ManuallyDrop, MaybeUninit};
Expand Down
3 changes: 2 additions & 1 deletion tests/pass/both_borrows/unsafe_pinned.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//@revisions: stack tree
//@revisions: stack tree tree_strong
//@[tree]compile-flags: -Zmiri-tree-borrows
//@[tree_strong]compile-flags: -Zmiri-tree-borrows-strong
#![feature(unsafe_pinned)]

use std::pin::UnsafePinned;
Expand Down