about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-11-21 07:57:33 +0000
committerbors <bors@rust-lang.org>2022-11-21 07:57:33 +0000
commit105dba79686f9e57ce48c07c736acd12f739ca58 (patch)
tree98ad5a159aa1885e1a05305454d572633598ed4f
parentc043a0e7d6f347f9b2c7f08c5b3a179470e0f0c5 (diff)
parent32e9d00585e29d291f9fa7866a00de6d230da8b2 (diff)
downloadrust-105dba79686f9e57ce48c07c736acd12f739ca58.tar.gz
rust-105dba79686f9e57ce48c07c736acd12f739ca58.zip
Auto merge of #2684 - RalfJung:stack-borrows-weak-protectors, r=saethlin
Stack borrows: weak protectors

This addresses the issue described in https://github.com/rust-lang/unsafe-code-guidelines/issues/376.
-rw-r--r--src/tools/miri/src/stacked_borrows/diagnostics.rs26
-rw-r--r--src/tools/miri/src/stacked_borrows/mod.rs123
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/aliasing_mut1.stderr4
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/aliasing_mut2.stderr4
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/aliasing_mut4.stderr4
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/box_noalias_violation.rs14
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/box_noalias_violation.stderr30
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs2
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr4
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector2.rs2
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector2.stderr4
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/illegal_write6.rs2
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/illegal_write6.stderr4
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector1.stderr4
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector2.stderr4
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector3.stderr4
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.rs2
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.stderr4
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.rs2
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.stderr4
20 files changed, 169 insertions, 78 deletions
diff --git a/src/tools/miri/src/stacked_borrows/diagnostics.rs b/src/tools/miri/src/stacked_borrows/diagnostics.rs
index d3843b03034..f307bf11edd 100644
--- a/src/tools/miri/src/stacked_borrows/diagnostics.rs
+++ b/src/tools/miri/src/stacked_borrows/diagnostics.rs
@@ -6,7 +6,7 @@ use rustc_span::{Span, SpanData};
 use rustc_target::abi::Size;
 
 use crate::helpers::CurrentSpan;
-use crate::stacked_borrows::{err_sb_ub, AccessKind, GlobalStateInner, Permission};
+use crate::stacked_borrows::{err_sb_ub, AccessKind, GlobalStateInner, Permission, ProtectorKind};
 use crate::*;
 
 use rustc_middle::mir::interpret::InterpError;
@@ -288,7 +288,11 @@ impl<'span, 'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'span, 'history, 'ecx, 'mir
             }
             Operation::Access(AccessOp { kind, range, .. }) =>
                 (*range, InvalidationCause::Access(*kind)),
-            _ => unreachable!("Tags can only be invalidated during a retag or access"),
+            Operation::Dealloc(_) => {
+                // This can be reached, but never be relevant later since the entire allocation is
+                // gone now.
+                return;
+            }
         };
         self.history.invalidations.push(Invalidation { tag, range, span, cause });
     }
@@ -369,7 +373,7 @@ impl<'span, 'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'span, 'history, 'ecx, 'mir
 
     /// Report a descriptive error when `new` could not be granted from `derived_from`.
     #[inline(never)] // This is only called on fatal code paths
-    pub fn grant_error(&self, perm: Permission, stack: &Stack) -> InterpError<'tcx> {
+    pub(super) fn grant_error(&self, perm: Permission, stack: &Stack) -> InterpError<'tcx> {
         let Operation::Retag(op) = &self.operation else {
             unreachable!("grant_error should only be called during a retag")
         };
@@ -389,7 +393,7 @@ impl<'span, 'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'span, 'history, 'ecx, 'mir
 
     /// Report a descriptive error when `access` is not permitted based on `tag`.
     #[inline(never)] // This is only called on fatal code paths
-    pub fn access_error(&self, stack: &Stack) -> InterpError<'tcx> {
+    pub(super) fn access_error(&self, stack: &Stack) -> InterpError<'tcx> {
         let Operation::Access(op) = &self.operation  else {
             unreachable!("access_error should only be called during an access")
         };
@@ -408,7 +412,11 @@ impl<'span, 'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'span, 'history, 'ecx, 'mir
     }
 
     #[inline(never)] // This is only called on fatal code paths
-    pub fn protector_error(&self, item: &Item) -> InterpError<'tcx> {
+    pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpError<'tcx> {
+        let protected = match kind {
+            ProtectorKind::WeakProtector => "weakly protected",
+            ProtectorKind::StrongProtector => "strongly protected",
+        };
         let call_id = self
             .threads
             .all_stacks()
@@ -422,10 +430,7 @@ impl<'span, 'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'span, 'history, 'ecx, 'mir
         match self.operation {
             Operation::Dealloc(_) =>
                 err_sb_ub(
-                    format!(
-                        "deallocating while item {:?} is protected by call {:?}",
-                        item, call_id
-                    ),
+                    format!("deallocating while item {item:?} is {protected} by call {call_id:?}",),
                     None,
                     None,
                 ),
@@ -433,8 +438,7 @@ impl<'span, 'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'span, 'history, 'ecx, 'mir
             | Operation::Access(AccessOp { tag, .. }) =>
                 err_sb_ub(
                     format!(
-                        "not granting access to tag {:?} because that would remove {:?} which is protected because it is an argument of call {:?}",
-                        tag, item, call_id
+                        "not granting access to tag {tag:?} because that would remove {item:?} which is {protected} because it is an argument of call {call_id:?}",
                     ),
                     None,
                     tag.and_then(|tag| self.get_logs_relevant_to(tag, Some(item.tag()))),
diff --git a/src/tools/miri/src/stacked_borrows/mod.rs b/src/tools/miri/src/stacked_borrows/mod.rs
index f2e2df5ad08..bc52428910a 100644
--- a/src/tools/miri/src/stacked_borrows/mod.rs
+++ b/src/tools/miri/src/stacked_borrows/mod.rs
@@ -65,7 +65,7 @@ pub struct FrameExtra {
     /// incremental updates of the global list of protected tags stored in the
     /// `stacked_borrows::GlobalState` upon function return, and if we attempt to pop a protected
     /// tag, to identify which call is responsible for protecting the tag.
-    /// See `Stack::item_popped` for more explanation.
+    /// See `Stack::item_invalidated` for more explanation.
     ///
     /// This will contain one tag per reference passed to the function, so
     /// a size of 2 is enough for the vast majority of functions.
@@ -91,6 +91,26 @@ pub struct Stacks {
     modified_since_last_gc: bool,
 }
 
+/// The flavor of the protector.
+#[derive(Copy, Clone, Debug)]
+enum ProtectorKind {
+    /// Protected against aliasing violations from other pointers.
+    ///
+    /// Items protected like this cause UB when they are invalidated, *but* the pointer itself may
+    /// still be used to issue a deallocation.
+    ///
+    /// This is required for LLVM IR pointers that are `noalias` but *not* `dereferenceable`.
+    WeakProtector,
+
+    /// Protected against any kind of invalidation.
+    ///
+    /// Items protected like this cause UB when they are invalidated or the memory is deallocated.
+    /// This is strictly stronger protection than `WeakProtector`.
+    ///
+    /// This is required for LLVM IR pointers that are `dereferenceable` (and also allows `noalias`).
+    StrongProtector,
+}
+
 /// Extra global state, available to the memory access hooks.
 #[derive(Debug)]
 pub struct GlobalStateInner {
@@ -102,12 +122,12 @@ pub struct GlobalStateInner {
     base_ptr_tags: FxHashMap<AllocId, SbTag>,
     /// Next unused call ID (for protectors).
     next_call_id: CallId,
-    /// All currently protected tags.
+    /// All currently protected tags, and the status of their protection.
     /// An item is protected if its tag is in this set, *and* it has the "protected" bit set.
     /// We add tags to this when they are created with a protector in `reborrow`, and
     /// we remove tags from this when the call which is protecting them returns, in
-    /// `GlobalStateInner::end_call`. See `Stack::item_popped` for more details.
-    protected_tags: FxHashSet<SbTag>,
+    /// `GlobalStateInner::end_call`. See `Stack::item_invalidated` for more details.
+    protected_tags: FxHashMap<SbTag, ProtectorKind>,
     /// The pointer ids to trace
     tracked_pointer_tags: FxHashSet<SbTag>,
     /// The call ids to trace
@@ -189,7 +209,7 @@ impl GlobalStateInner {
             next_ptr_tag: SbTag(NonZeroU64::new(1).unwrap()),
             base_ptr_tags: FxHashMap::default(),
             next_call_id: NonZeroU64::new(1).unwrap(),
-            protected_tags: FxHashSet::default(),
+            protected_tags: FxHashMap::default(),
             tracked_pointer_tags,
             tracked_call_ids,
             retag_fields,
@@ -272,6 +292,13 @@ impl Permission {
     }
 }
 
+/// Determines whether an item was invalidated by a conflicting access, or by deallocation.
+#[derive(Copy, Clone, Debug)]
+enum ItemInvalidationCause {
+    Conflict,
+    Dealloc,
+}
+
 /// Core per-location operations: access, dealloc, reborrow.
 impl<'tcx> Stack {
     /// Find the first write-incompatible item above the given one --
@@ -310,10 +337,11 @@ impl<'tcx> Stack {
     /// Within `provoking_access, the `AllocRange` refers the entire operation, and
     /// the `Size` refers to the specific location in the `AllocRange` that we are
     /// currently checking.
-    fn item_popped(
+    fn item_invalidated(
         item: &Item,
         global: &GlobalStateInner,
         dcx: &mut DiagnosticCx<'_, '_, '_, '_, 'tcx>,
+        cause: ItemInvalidationCause,
     ) -> InterpResult<'tcx> {
         if !global.tracked_pointer_tags.is_empty() {
             dcx.check_tracked_tag_popped(item, global);
@@ -336,8 +364,14 @@ impl<'tcx> Stack {
         // 2. Most frames protect only one or two tags. So this duplicative global turns a search
         //    which ends up about linear in the number of protected tags in the program into a
         //    constant time check (and a slow linear, because the tags in the frames aren't contiguous).
-        if global.protected_tags.contains(&item.tag()) {
-            return Err(dcx.protector_error(item).into());
+        if let Some(&protector_kind) = global.protected_tags.get(&item.tag()) {
+            // The only way this is okay is if the protector is weak and we are deallocating with
+            // the right pointer.
+            let allowed = matches!(cause, ItemInvalidationCause::Dealloc)
+                && matches!(protector_kind, ProtectorKind::WeakProtector);
+            if !allowed {
+                return Err(dcx.protector_error(item, protector_kind).into());
+            }
         }
         Ok(())
     }
@@ -350,7 +384,7 @@ impl<'tcx> Stack {
         &mut self,
         access: AccessKind,
         tag: ProvenanceExtra,
-        global: &mut GlobalStateInner,
+        global: &GlobalStateInner,
         dcx: &mut DiagnosticCx<'_, '_, '_, '_, 'tcx>,
         exposed_tags: &FxHashSet<SbTag>,
     ) -> InterpResult<'tcx> {
@@ -377,7 +411,7 @@ impl<'tcx> Stack {
                 0
             };
             self.pop_items_after(first_incompatible_idx, |item| {
-                Stack::item_popped(&item, global, dcx)?;
+                Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
                 dcx.log_invalidation(item.tag());
                 Ok(())
             })?;
@@ -398,7 +432,7 @@ impl<'tcx> Stack {
                 0
             };
             self.disable_uniques_starting_at(first_incompatible_idx, |item| {
-                Stack::item_popped(&item, global, dcx)?;
+                Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
                 dcx.log_invalidation(item.tag());
                 Ok(())
             })?;
@@ -440,14 +474,15 @@ impl<'tcx> Stack {
         dcx: &mut DiagnosticCx<'_, '_, '_, '_, 'tcx>,
         exposed_tags: &FxHashSet<SbTag>,
     ) -> InterpResult<'tcx> {
-        // Step 1: Make sure there is a granting item.
-        self.find_granting(AccessKind::Write, tag, exposed_tags)
+        // Step 1: Make a write access.
+        // As part of this we do regular protector checking, i.e. even weakly protected items cause UB when popped.
+        self.access(AccessKind::Write, tag, global, dcx, exposed_tags)
             .map_err(|_| dcx.dealloc_error())?;
 
-        // Step 2: Consider all items removed. This checks for protectors.
+        // Step 2: Pretend we remove the remaining items, checking if any are strongly protected.
         for idx in (0..self.len()).rev() {
             let item = self.get(idx).unwrap();
-            Stack::item_popped(&item, global, dcx)?;
+            Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Dealloc)?;
         }
 
         Ok(())
@@ -463,7 +498,7 @@ impl<'tcx> Stack {
         &mut self,
         derived_from: ProvenanceExtra,
         new: Item,
-        global: &mut GlobalStateInner,
+        global: &GlobalStateInner,
         dcx: &mut DiagnosticCx<'_, '_, '_, '_, 'tcx>,
         exposed_tags: &FxHashSet<SbTag>,
     ) -> InterpResult<'tcx> {
@@ -633,9 +668,9 @@ impl Stacks {
             range.size.bytes()
         );
         let dcx = DiagnosticCxBuilder::read(&mut current_span, threads, tag, range);
-        let mut state = state.borrow_mut();
+        let state = state.borrow();
         self.for_each(range, dcx, |stack, dcx, exposed_tags| {
-            stack.access(AccessKind::Read, tag, &mut state, dcx, exposed_tags)
+            stack.access(AccessKind::Read, tag, &state, dcx, exposed_tags)
         })
     }
 
@@ -656,9 +691,9 @@ impl Stacks {
             range.size.bytes()
         );
         let dcx = DiagnosticCxBuilder::write(&mut current_span, threads, tag, range);
-        let mut state = state.borrow_mut();
+        let state = state.borrow();
         self.for_each(range, dcx, |stack, dcx, exposed_tags| {
-            stack.access(AccessKind::Write, tag, &mut state, dcx, exposed_tags)
+            stack.access(AccessKind::Write, tag, &state, dcx, exposed_tags)
         })
     }
 
@@ -698,7 +733,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
         kind: RefKind,
         retag_cause: RetagCause, // What caused this retag, for diagnostics only
         new_tag: SbTag,
-        protect: bool,
+        protect: Option<ProtectorKind>,
     ) -> InterpResult<'tcx, Option<AllocId>> {
         let this = self.eval_context_mut();
 
@@ -761,7 +796,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
                     );
                     let mut dcx = dcx.build(&mut stacked_borrows.history, base_offset);
                     dcx.log_creation();
-                    if protect {
+                    if protect.is_some() {
                         dcx.log_protector();
                     }
                 }
@@ -821,10 +856,16 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
             size.bytes()
         );
 
-        if protect {
-            // See comment in `Stack::item_popped` for why we store the tag twice.
+        if let Some(protect) = protect {
+            // See comment in `Stack::item_invalidated` for why we store the tag twice.
             this.frame_mut().extra.stacked_borrows.as_mut().unwrap().protected_tags.push(new_tag);
-            this.machine.stacked_borrows.as_mut().unwrap().get_mut().protected_tags.insert(new_tag);
+            this.machine
+                .stacked_borrows
+                .as_mut()
+                .unwrap()
+                .get_mut()
+                .protected_tags
+                .insert(new_tag, protect);
         }
 
         // Update the stacks.
@@ -866,14 +907,14 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
                         Permission::SharedReadWrite
                     };
                     let protected = if frozen {
-                        protect
+                        protect.is_some()
                     } else {
                         // We do not protect inside UnsafeCell.
                         // This fixes https://github.com/rust-lang/rust/issues/55005.
                         false
                     };
                     let item = Item::new(new_tag, perm, protected);
-                    let mut global = this.machine.stacked_borrows.as_ref().unwrap().borrow_mut();
+                    let global = this.machine.stacked_borrows.as_ref().unwrap().borrow();
                     let dcx = DiagnosticCxBuilder::retag(
                         &mut current_span, // FIXME avoid this `clone`
                         &this.machine.threads,
@@ -883,7 +924,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
                         alloc_range(base_offset, size),
                     );
                     stacked_borrows.for_each(range, dcx, |stack, dcx, exposed_tags| {
-                        stack.grant(orig_tag, item, &mut global, dcx, exposed_tags)
+                        stack.grant(orig_tag, item, &global, dcx, exposed_tags)
                     })
                 })?;
                 return Ok(Some(alloc_id));
@@ -899,9 +940,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
             .as_mut()
             .expect("we should have Stacked Borrows data")
             .borrow_mut();
-        let item = Item::new(new_tag, perm, protect);
+        let item = Item::new(new_tag, perm, protect.is_some());
         let range = alloc_range(base_offset, size);
-        let mut global = machine.stacked_borrows.as_ref().unwrap().borrow_mut();
+        let global = machine.stacked_borrows.as_ref().unwrap().borrow();
         // FIXME: can't share this with the current_span inside log_creation
         let current_span = &mut machine.current_span();
         let dcx = DiagnosticCxBuilder::retag(
@@ -913,7 +954,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
             alloc_range(base_offset, size),
         );
         stacked_borrows.for_each(range, dcx, |stack, dcx, exposed_tags| {
-            stack.grant(orig_tag, item, &mut global, dcx, exposed_tags)
+            stack.grant(orig_tag, item, &global, dcx, exposed_tags)
         })?;
 
         Ok(Some(alloc_id))
@@ -926,7 +967,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
         val: &ImmTy<'tcx, Provenance>,
         kind: RefKind,
         retag_cause: RetagCause, // What caused this retag, for diagnostics only
-        protect: bool,
+        protect: Option<ProtectorKind>,
     ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
         let this = self.eval_context_mut();
         // We want a place for where the ptr *points to*, so we get one.
@@ -996,7 +1037,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 place: &PlaceTy<'tcx, Provenance>,
                 ref_kind: RefKind,
                 retag_cause: RetagCause,
-                protector: bool,
+                protector: Option<ProtectorKind>,
             ) -> InterpResult<'tcx> {
                 let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
                 let val = self.ecx.retag_reference(&val, ref_kind, retag_cause, protector)?;
@@ -1015,13 +1056,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             }
 
             fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
-                // Boxes do not get a protector: protectors reflect that references outlive the call
-                // they were passed in to; that's just not the case for boxes.
+                // Boxes get a weak protectors, since they may be deallocated.
                 self.retag_place(
                     place,
                     RefKind::Unique { two_phase: false },
                     self.retag_cause,
-                    /*protector*/ false,
+                    /*protector*/
+                    (self.kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector),
                 )
             }
 
@@ -1046,7 +1087,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                             place,
                             ref_kind,
                             self.retag_cause,
-                            /*protector*/ self.kind == RetagKind::FnEntry,
+                            /*protector*/
+                            (self.kind == RetagKind::FnEntry)
+                                .then_some(ProtectorKind::StrongProtector),
                         )?;
                     }
                     ty::RawPtr(tym) => {
@@ -1059,7 +1102,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                                 place,
                                 RefKind::Raw { mutable: tym.mutbl == Mutability::Mut },
                                 self.retag_cause,
-                                /*protector*/ false,
+                                /*protector*/ None,
                             )?;
                         }
                     }
@@ -1110,12 +1153,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         // (The pointer type does not matter, so we use a raw pointer.)
         let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?;
         let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
-        // Reborrow it.
+        // Reborrow it. With protection! That is part of the point.
         let val = this.retag_reference(
             &val,
             RefKind::Unique { two_phase: false },
             RetagCause::FnReturn,
-            /*protector*/ true,
+            /*protector*/ Some(ProtectorKind::StrongProtector),
         )?;
         // And use reborrowed pointer for return place.
         let return_place = this.ref_to_mplace(&val)?;
diff --git a/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut1.stderr b/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut1.stderr
index 5d4679b13ad..268d253ad5b 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut1.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut1.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
   --> $DIR/aliasing_mut1.rs:LL:CC
    |
 LL | pub fn safe(_x: &mut i32, _y: &mut i32) {}
-   |                           ^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
+   |                           ^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
    |
    = 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
diff --git a/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut2.stderr b/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut2.stderr
index c8408c150e7..77a542f45a2 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut2.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut2.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
   --> $DIR/aliasing_mut2.rs:LL:CC
    |
 LL | pub fn safe(_x: &i32, _y: &mut i32) {}
-   |                       ^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
+   |                       ^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
    |
    = 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
diff --git a/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut4.stderr b/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut4.stderr
index c53fe70f6dd..e592b154a73 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut4.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/aliasing_mut4.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
   --> $DIR/aliasing_mut4.rs:LL:CC
    |
 LL | pub fn safe(_x: &i32, _y: &mut Cell<i32>) {}
-   |                       ^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
+   |                       ^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
    |
    = 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
diff --git a/src/tools/miri/tests/fail/stacked_borrows/box_noalias_violation.rs b/src/tools/miri/tests/fail/stacked_borrows/box_noalias_violation.rs
new file mode 100644
index 00000000000..2067149b6c8
--- /dev/null
+++ b/src/tools/miri/tests/fail/stacked_borrows/box_noalias_violation.rs
@@ -0,0 +1,14 @@
+unsafe fn test(mut x: Box<i32>, y: *const i32) -> i32 {
+    // We will call this in a way that x and y alias.
+    *x = 5;
+    std::mem::forget(x);
+    *y //~ERROR: weakly protected
+}
+
+fn main() {
+    unsafe {
+        let mut v = 42;
+        let ptr = &mut v as *mut i32;
+        test(Box::from_raw(ptr), ptr);
+    }
+}
diff --git a/src/tools/miri/tests/fail/stacked_borrows/box_noalias_violation.stderr b/src/tools/miri/tests/fail/stacked_borrows/box_noalias_violation.stderr
new file mode 100644
index 00000000000..3c84cbcfd51
--- /dev/null
+++ b/src/tools/miri/tests/fail/stacked_borrows/box_noalias_violation.stderr
@@ -0,0 +1,30 @@
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is weakly protected because it is an argument of call ID
+  --> $DIR/box_noalias_violation.rs:LL:CC
+   |
+LL |     *y
+   |     ^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is weakly protected because it is an argument of call ID
+   |
+   = 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 SharedReadWrite retag at offsets [0x0..0x4]
+  --> $DIR/box_noalias_violation.rs:LL:CC
+   |
+LL |         let ptr = &mut v as *mut i32;
+   |                   ^^^^^^
+help: <TAG> is this argument
+  --> $DIR/box_noalias_violation.rs:LL:CC
+   |
+LL | unsafe fn test(mut x: Box<i32>, y: *const i32) -> i32 {
+   |                ^^^^^
+   = note: BACKTRACE:
+   = note: inside `test` at $DIR/box_noalias_violation.rs:LL:CC
+note: inside `main` at $DIR/box_noalias_violation.rs:LL:CC
+  --> $DIR/box_noalias_violation.rs:LL:CC
+   |
+LL |         test(Box::from_raw(ptr), ptr);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs b/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs
index 9b710424c55..4036dce5beb 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs
+++ b/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs
@@ -1,4 +1,4 @@
-//@error-pattern: /deallocating while item \[Unique for .*\] is protected/
+//@error-pattern: /deallocating while item \[Unique for .*\] is strongly protected/
 
 fn inner(x: &mut i32, f: fn(&mut i32)) {
     // `f` may mutate, but it may not deallocate!
diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr b/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr
index a5db4a00c69..bb3eaec1e85 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: deallocating while item [Unique for <TAG>] is protected by call ID
+error: Undefined Behavior: deallocating while item [Unique for <TAG>] is strongly protected by call ID
   --> RUSTLIB/alloc/src/alloc.rs:LL:CC
    |
 LL |     unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [Unique for <TAG>] is protected by call ID
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [Unique for <TAG>] is strongly protected by call ID
    |
    = 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
diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector2.rs b/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector2.rs
index 36e133e3836..fd67dccd14d 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector2.rs
+++ b/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector2.rs
@@ -1,4 +1,4 @@
-//@error-pattern: /deallocating while item \[SharedReadWrite for .*\] is protected/
+//@error-pattern: /deallocating while item \[SharedReadWrite for .*\] is strongly protected/
 use std::marker::PhantomPinned;
 
 pub struct NotUnpin(i32, PhantomPinned);
diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector2.stderr b/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector2.stderr
index 99c6ee6eb07..25bab1aa564 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector2.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector2.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: deallocating while item [SharedReadWrite for <TAG>] is protected by call ID
+error: Undefined Behavior: deallocating while item [SharedReadWrite for <TAG>] is strongly protected by call ID
   --> RUSTLIB/alloc/src/alloc.rs:LL:CC
    |
 LL |     unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [SharedReadWrite for <TAG>] is protected by call ID
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [SharedReadWrite for <TAG>] is strongly protected by call ID
    |
    = 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
diff --git a/src/tools/miri/tests/fail/stacked_borrows/illegal_write6.rs b/src/tools/miri/tests/fail/stacked_borrows/illegal_write6.rs
index 448f1493367..7983b30ea18 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/illegal_write6.rs
+++ b/src/tools/miri/tests/fail/stacked_borrows/illegal_write6.rs
@@ -7,6 +7,6 @@ fn main() {
 fn foo(a: &mut u32, y: *mut u32) -> u32 {
     *a = 1;
     let _b = &*a;
-    unsafe { *y = 2 }; //~ ERROR: /not granting access .* because that would remove .* which is protected/
+    unsafe { *y = 2 }; //~ ERROR: /not granting access .* because that would remove .* which is strongly protected/
     return *a;
 }
diff --git a/src/tools/miri/tests/fail/stacked_borrows/illegal_write6.stderr b/src/tools/miri/tests/fail/stacked_borrows/illegal_write6.stderr
index 331faa89f86..1a627b8a883 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/illegal_write6.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/illegal_write6.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
   --> $DIR/illegal_write6.rs:LL:CC
    |
 LL |     unsafe { *y = 2 };
-   |              ^^^^^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
+   |              ^^^^^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
    |
    = 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
diff --git a/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector1.stderr b/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector1.stderr
index f87bd2319ab..1ef36b7ac10 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector1.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector1.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
   --> $DIR/invalidate_against_protector1.rs:LL:CC
    |
 LL |     let _val = unsafe { *x };
-   |                         ^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
+   |                         ^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
    |
    = 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
diff --git a/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector2.stderr b/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector2.stderr
index 07c51a39b82..941b936e5d7 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector2.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector2.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
   --> $DIR/invalidate_against_protector2.rs:LL:CC
    |
 LL |     unsafe { *x = 0 };
-   |              ^^^^^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
+   |              ^^^^^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
    |
    = 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
diff --git a/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector3.stderr b/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector3.stderr
index afda15ea160..176a859ee8a 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector3.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/invalidate_against_protector3.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
   --> $DIR/invalidate_against_protector3.rs:LL:CC
    |
 LL |     unsafe { *x = 0 };
-   |              ^^^^^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is protected because it is an argument of call ID
+   |              ^^^^^^ not granting access to tag <TAG> because that would remove [SharedReadOnly for <TAG>] which is strongly protected because it is an argument of call ID
    |
    = 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
diff --git a/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.rs b/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.rs
index cc774500a3c..c19bcb99cc1 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.rs
+++ b/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.rs
@@ -1,4 +1,4 @@
-//@error-pattern: which is protected
+//@error-pattern: which is strongly protected
 struct Newtype<'a>(&'a mut i32, i32);
 
 fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) {
diff --git a/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.stderr b/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.stderr
index 60a8b2a6260..70186dd3a53 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
   --> RUSTLIB/alloc/src/boxed.rs:LL:CC
    |
 LL |         Box(unsafe { Unique::new_unchecked(raw) }, alloc)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
    |
    = 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
diff --git a/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.rs b/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.rs
index 1aa6e240e30..2bbe7122ec7 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.rs
+++ b/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.rs
@@ -1,4 +1,4 @@
-//@error-pattern: which is protected
+//@error-pattern: which is strongly protected
 struct Newtype<'a>(&'a mut i32);
 
 fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) {
diff --git a/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.stderr b/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.stderr
index 06a9b86c6f4..69fa27c9c09 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
   --> RUSTLIB/alloc/src/boxed.rs:LL:CC
    |
 LL |         Box(unsafe { Unique::new_unchecked(raw) }, alloc)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is protected because it is an argument of call ID
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
    |
    = 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