Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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)),
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(),
)?;
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