]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
Merge from rustc
[rust.git] / src / tools / miri / src / borrow_tracker / stacked_borrows / mod.rs
1 //! Implements "Stacked Borrows".  See <https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md>
2 //! for further information.
3
4 pub mod diagnostics;
5 mod item;
6 mod stack;
7
8 use log::trace;
9 use std::cmp;
10 use std::fmt::Write;
11
12 use rustc_data_structures::fx::FxHashSet;
13 use rustc_middle::mir::{Mutability, RetagKind};
14 use rustc_middle::ty::{
15     self,
16     layout::{HasParamEnv, LayoutOf},
17 };
18 use rustc_target::abi::{Abi, Size};
19
20 use crate::borrow_tracker::{
21     stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder, TagHistory},
22     AccessKind, GlobalStateInner, ProtectorKind, RetagFields,
23 };
24 use crate::*;
25
26 use diagnostics::RetagCause;
27 pub use item::{Item, Permission};
28 pub use stack::Stack;
29
30 pub type AllocState = Stacks;
31
32 /// Extra per-allocation state.
33 #[derive(Clone, Debug)]
34 pub struct Stacks {
35     // Even reading memory can have effects on the stack, so we need a `RefCell` here.
36     stacks: RangeMap<Stack>,
37     /// Stores past operations on this allocation
38     history: AllocHistory,
39     /// The set of tags that have been exposed inside this allocation.
40     exposed_tags: FxHashSet<BorTag>,
41     /// Whether this memory has been modified since the last time the tag GC ran
42     modified_since_last_gc: bool,
43 }
44
45 /// Indicates which permissions to grant to the retagged pointer.
46 #[derive(Clone, Debug)]
47 enum NewPermission {
48     Uniform {
49         perm: Permission,
50         access: Option<AccessKind>,
51         protector: Option<ProtectorKind>,
52     },
53     FreezeSensitive {
54         freeze_perm: Permission,
55         freeze_access: Option<AccessKind>,
56         freeze_protector: Option<ProtectorKind>,
57         nonfreeze_perm: Permission,
58         nonfreeze_access: Option<AccessKind>,
59         // nonfreeze_protector must always be None
60     },
61 }
62
63 impl NewPermission {
64     /// A key function: determine the permissions to grant at a retag for the given kind of
65     /// reference/pointer.
66     fn from_ref_ty<'tcx>(
67         ty: ty::Ty<'tcx>,
68         kind: RetagKind,
69         cx: &crate::MiriInterpCx<'_, 'tcx>,
70     ) -> Self {
71         let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector);
72         match ty.kind() {
73             ty::Ref(_, pointee, Mutability::Mut) => {
74                 if kind == RetagKind::TwoPhase {
75                     // We mostly just give up on 2phase-borrows, and treat these exactly like raw pointers.
76                     assert!(protector.is_none()); // RetagKind can't be both FnEntry and TwoPhase.
77                     NewPermission::Uniform {
78                         perm: Permission::SharedReadWrite,
79                         access: None,
80                         protector: None,
81                     }
82                 } else if pointee.is_unpin(*cx.tcx, cx.param_env()) {
83                     // A regular full mutable reference.
84                     NewPermission::Uniform {
85                         perm: Permission::Unique,
86                         access: Some(AccessKind::Write),
87                         protector,
88                     }
89                 } else {
90                     NewPermission::Uniform {
91                         perm: Permission::SharedReadWrite,
92                         // FIXME: We emit `dereferenceable` for `!Unpin` mutable references, so we
93                         // should do fake accesses here. But then we run into
94                         // <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>, so for now
95                         // we don't do that.
96                         access: None,
97                         protector,
98                     }
99                 }
100             }
101             ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Mut, .. }) => {
102                 assert!(protector.is_none()); // RetagKind can't be both FnEntry and Raw.
103                 // Mutable raw pointer. No access, not protected.
104                 NewPermission::Uniform {
105                     perm: Permission::SharedReadWrite,
106                     access: None,
107                     protector: None,
108                 }
109             }
110             ty::Ref(_, _pointee, Mutability::Not) => {
111                 NewPermission::FreezeSensitive {
112                     freeze_perm: Permission::SharedReadOnly,
113                     freeze_access: Some(AccessKind::Read),
114                     freeze_protector: protector,
115                     nonfreeze_perm: Permission::SharedReadWrite,
116                     // Inside UnsafeCell, this does *not* count as an access, as there
117                     // might actually be mutable references further up the stack that
118                     // we have to keep alive.
119                     nonfreeze_access: None,
120                     // We do not protect inside UnsafeCell.
121                     // This fixes https://github.com/rust-lang/rust/issues/55005.
122                 }
123             }
124             ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Not, .. }) => {
125                 assert!(protector.is_none()); // RetagKind can't be both FnEntry and Raw.
126                 // `*const T`, when freshly created, are read-only in the frozen part.
127                 NewPermission::FreezeSensitive {
128                     freeze_perm: Permission::SharedReadOnly,
129                     freeze_access: Some(AccessKind::Read),
130                     freeze_protector: None,
131                     nonfreeze_perm: Permission::SharedReadWrite,
132                     nonfreeze_access: None,
133                 }
134             }
135             _ => unreachable!(),
136         }
137     }
138
139     fn protector(&self) -> Option<ProtectorKind> {
140         match self {
141             NewPermission::Uniform { protector, .. } => *protector,
142             NewPermission::FreezeSensitive { freeze_protector, .. } => *freeze_protector,
143         }
144     }
145 }
146
147 /// Error reporting
148 pub fn err_sb_ub<'tcx>(
149     msg: String,
150     help: Option<String>,
151     history: Option<TagHistory>,
152 ) -> InterpError<'tcx> {
153     err_machine_stop!(TerminationInfo::StackedBorrowsUb { msg, help, history })
154 }
155
156 // # Stacked Borrows Core Begin
157
158 /// We need to make at least the following things true:
159 ///
160 /// U1: After creating a `Uniq`, it is at the top.
161 /// U2: If the top is `Uniq`, accesses must be through that `Uniq` or remove it.
162 /// U3: If an access happens with a `Uniq`, it requires the `Uniq` to be in the stack.
163 ///
164 /// F1: After creating a `&`, the parts outside `UnsafeCell` have our `SharedReadOnly` on top.
165 /// F2: If a write access happens, it pops the `SharedReadOnly`.  This has three pieces:
166 ///     F2a: If a write happens granted by an item below our `SharedReadOnly`, the `SharedReadOnly`
167 ///          gets popped.
168 ///     F2b: No `SharedReadWrite` or `Unique` will ever be added on top of our `SharedReadOnly`.
169 /// F3: If an access happens with an `&` outside `UnsafeCell`,
170 ///     it requires the `SharedReadOnly` to still be in the stack.
171
172 /// Core relation on `Permission` to define which accesses are allowed
173 impl Permission {
174     /// This defines for a given permission, whether it permits the given kind of access.
175     fn grants(self, access: AccessKind) -> bool {
176         // Disabled grants nothing. Otherwise, all items grant read access, and except for SharedReadOnly they grant write access.
177         self != Permission::Disabled
178             && (access == AccessKind::Read || self != Permission::SharedReadOnly)
179     }
180 }
181
182 /// Determines whether an item was invalidated by a conflicting access, or by deallocation.
183 #[derive(Copy, Clone, Debug)]
184 enum ItemInvalidationCause {
185     Conflict,
186     Dealloc,
187 }
188
189 /// Core per-location operations: access, dealloc, reborrow.
190 impl<'tcx> Stack {
191     /// Find the first write-incompatible item above the given one --
192     /// i.e, find the height to which the stack will be truncated when writing to `granting`.
193     fn find_first_write_incompatible(&self, granting: usize) -> usize {
194         let perm = self.get(granting).unwrap().perm();
195         match perm {
196             Permission::SharedReadOnly => bug!("Cannot use SharedReadOnly for writing"),
197             Permission::Disabled => bug!("Cannot use Disabled for anything"),
198             Permission::Unique => {
199                 // On a write, everything above us is incompatible.
200                 granting + 1
201             }
202             Permission::SharedReadWrite => {
203                 // The SharedReadWrite *just* above us are compatible, to skip those.
204                 let mut idx = granting + 1;
205                 while let Some(item) = self.get(idx) {
206                     if item.perm() == Permission::SharedReadWrite {
207                         // Go on.
208                         idx += 1;
209                     } else {
210                         // Found first incompatible!
211                         break;
212                     }
213                 }
214                 idx
215             }
216         }
217     }
218
219     /// The given item was invalidated -- check its protectors for whether that will cause UB.
220     fn item_invalidated(
221         item: &Item,
222         global: &GlobalStateInner,
223         dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
224         cause: ItemInvalidationCause,
225     ) -> InterpResult<'tcx> {
226         if !global.tracked_pointer_tags.is_empty() {
227             dcx.check_tracked_tag_popped(item, global);
228         }
229
230         if !item.protected() {
231             return Ok(());
232         }
233
234         // We store tags twice, once in global.protected_tags and once in each call frame.
235         // We do this because consulting a single global set in this function is faster
236         // than attempting to search all call frames in the program for the `FrameExtra`
237         // (if any) which is protecting the popped tag.
238         //
239         // This duplication trades off making `end_call` slower to make this function faster. This
240         // trade-off is profitable in practice for a combination of two reasons.
241         // 1. A single protected tag can (and does in some programs) protect thousands of `Item`s.
242         //    Therefore, adding overhead in function call/return is profitable even if it only
243         //    saves a little work in this function.
244         // 2. Most frames protect only one or two tags. So this duplicative global turns a search
245         //    which ends up about linear in the number of protected tags in the program into a
246         //    constant time check (and a slow linear, because the tags in the frames aren't contiguous).
247         if let Some(&protector_kind) = global.protected_tags.get(&item.tag()) {
248             // The only way this is okay is if the protector is weak and we are deallocating with
249             // the right pointer.
250             let allowed = matches!(cause, ItemInvalidationCause::Dealloc)
251                 && matches!(protector_kind, ProtectorKind::WeakProtector);
252             if !allowed {
253                 return Err(dcx.protector_error(item, protector_kind).into());
254             }
255         }
256         Ok(())
257     }
258
259     /// Test if a memory `access` using pointer tagged `tag` is granted.
260     /// If yes, return the index of the item that granted it.
261     /// `range` refers the entire operation, and `offset` refers to the specific offset into the
262     /// allocation that we are currently checking.
263     fn access(
264         &mut self,
265         access: AccessKind,
266         tag: ProvenanceExtra,
267         global: &GlobalStateInner,
268         dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
269         exposed_tags: &FxHashSet<BorTag>,
270     ) -> InterpResult<'tcx> {
271         // Two main steps: Find granting item, remove incompatible items above.
272
273         // Step 1: Find granting item.
274         let granting_idx =
275             self.find_granting(access, tag, exposed_tags).map_err(|()| dcx.access_error(self))?;
276
277         // Step 2: Remove incompatible items above them.  Make sure we do not remove protected
278         // items.  Behavior differs for reads and writes.
279         // In case of wildcards/unknown matches, we remove everything that is *definitely* gone.
280         if access == AccessKind::Write {
281             // Remove everything above the write-compatible items, like a proper stack. This makes sure read-only and unique
282             // pointers become invalid on write accesses (ensures F2a, and ensures U2 for write accesses).
283             let first_incompatible_idx = if let Some(granting_idx) = granting_idx {
284                 // The granting_idx *might* be approximate, but any lower idx would remove more
285                 // things. Even if this is a Unique and the lower idx is an SRW (which removes
286                 // less), there is an SRW group boundary here so strictly more would get removed.
287                 self.find_first_write_incompatible(granting_idx)
288             } else {
289                 // We are writing to something in the unknown part.
290                 // There is a SRW group boundary between the unknown and the known, so everything is incompatible.
291                 0
292             };
293             self.pop_items_after(first_incompatible_idx, |item| {
294                 Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
295                 dcx.log_invalidation(item.tag());
296                 Ok(())
297             })?;
298         } else {
299             // On a read, *disable* all `Unique` above the granting item.  This ensures U2 for read accesses.
300             // The reason this is not following the stack discipline (by removing the first Unique and
301             // everything on top of it) is that in `let raw = &mut *x as *mut _; let _val = *x;`, the second statement
302             // would pop the `Unique` from the reborrow of the first statement, and subsequently also pop the
303             // `SharedReadWrite` for `raw`.
304             // This pattern occurs a lot in the standard library: create a raw pointer, then also create a shared
305             // reference and use that.
306             // We *disable* instead of removing `Unique` to avoid "connecting" two neighbouring blocks of SRWs.
307             let first_incompatible_idx = if let Some(granting_idx) = granting_idx {
308                 // The granting_idx *might* be approximate, but any lower idx would disable more things.
309                 granting_idx + 1
310             } else {
311                 // We are reading from something in the unknown part. That means *all* `Unique` we know about are dead now.
312                 0
313             };
314             self.disable_uniques_starting_at(first_incompatible_idx, |item| {
315                 Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
316                 dcx.log_invalidation(item.tag());
317                 Ok(())
318             })?;
319         }
320
321         // If this was an approximate action, we now collapse everything into an unknown.
322         if granting_idx.is_none() || matches!(tag, ProvenanceExtra::Wildcard) {
323             // Compute the upper bound of the items that remain.
324             // (This is why we did all the work above: to reduce the items we have to consider here.)
325             let mut max = BorTag::one();
326             for i in 0..self.len() {
327                 let item = self.get(i).unwrap();
328                 // Skip disabled items, they cannot be matched anyway.
329                 if !matches!(item.perm(), Permission::Disabled) {
330                     // We are looking for a strict upper bound, so add 1 to this tag.
331                     max = cmp::max(item.tag().succ().unwrap(), max);
332                 }
333             }
334             if let Some(unk) = self.unknown_bottom() {
335                 max = cmp::max(unk, max);
336             }
337             // Use `max` as new strict upper bound for everything.
338             trace!(
339                 "access: forgetting stack to upper bound {max} due to wildcard or unknown access",
340                 max = max.get(),
341             );
342             self.set_unknown_bottom(max);
343         }
344
345         // Done.
346         Ok(())
347     }
348
349     /// Deallocate a location: Like a write access, but also there must be no
350     /// active protectors at all because we will remove all items.
351     fn dealloc(
352         &mut self,
353         tag: ProvenanceExtra,
354         global: &GlobalStateInner,
355         dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
356         exposed_tags: &FxHashSet<BorTag>,
357     ) -> InterpResult<'tcx> {
358         // Step 1: Make a write access.
359         // As part of this we do regular protector checking, i.e. even weakly protected items cause UB when popped.
360         self.access(AccessKind::Write, tag, global, dcx, exposed_tags)?;
361
362         // Step 2: Pretend we remove the remaining items, checking if any are strongly protected.
363         for idx in (0..self.len()).rev() {
364             let item = self.get(idx).unwrap();
365             Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Dealloc)?;
366         }
367
368         Ok(())
369     }
370
371     /// Derive a new pointer from one with the given tag.
372     ///
373     /// `access` indicates which kind of memory access this retag itself should correspond to.
374     fn grant(
375         &mut self,
376         derived_from: ProvenanceExtra,
377         new: Item,
378         access: Option<AccessKind>,
379         global: &GlobalStateInner,
380         dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
381         exposed_tags: &FxHashSet<BorTag>,
382     ) -> InterpResult<'tcx> {
383         dcx.start_grant(new.perm());
384
385         // Compute where to put the new item.
386         // Either way, we ensure that we insert the new item in a way such that between
387         // `derived_from` and the new one, there are only items *compatible with* `derived_from`.
388         let new_idx = if let Some(access) = access {
389             // Simple case: We are just a regular memory access, and then push our thing on top,
390             // like a regular stack.
391             // This ensures F2b for `Unique`, by removing offending `SharedReadOnly`.
392             self.access(access, derived_from, global, dcx, exposed_tags)?;
393
394             // We insert "as far up as possible": We know only compatible items are remaining
395             // on top of `derived_from`, and we want the new item at the top so that we
396             // get the strongest possible guarantees.
397             // This ensures U1 and F1.
398             self.len()
399         } else {
400             // The tricky case: creating a new SRW permission without actually being an access.
401             assert!(new.perm() == Permission::SharedReadWrite);
402
403             // First we figure out which item grants our parent (`derived_from`) this kind of access.
404             // We use that to determine where to put the new item.
405             let granting_idx = self
406                 .find_granting(AccessKind::Write, derived_from, exposed_tags)
407                 .map_err(|()| dcx.grant_error(self))?;
408
409             let (Some(granting_idx), ProvenanceExtra::Concrete(_)) = (granting_idx, derived_from) else {
410                 // The parent is a wildcard pointer or matched the unknown bottom.
411                 // This is approximate. Nobody knows what happened, so forget everything.
412                 // The new thing is SRW anyway, so we cannot push it "on top of the unkown part"
413                 // (for all we know, it might join an SRW group inside the unknown).
414                 trace!("reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown");
415                 self.set_unknown_bottom(global.next_ptr_tag);
416                 return Ok(());
417             };
418
419             // SharedReadWrite can coexist with "existing loans", meaning they don't act like a write
420             // access.  Instead of popping the stack, we insert the item at the place the stack would
421             // be popped to (i.e., we insert it above all the write-compatible items).
422             // This ensures F2b by adding the new item below any potentially existing `SharedReadOnly`.
423             self.find_first_write_incompatible(granting_idx)
424         };
425
426         // Put the new item there.
427         trace!("reborrow: adding item {:?}", new);
428         self.insert(new_idx, new);
429         Ok(())
430     }
431 }
432 // # Stacked Borrows Core End
433
434 /// Integration with the BorTag garbage collector
435 impl Stacks {
436     pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<BorTag>) {
437         if self.modified_since_last_gc {
438             for stack in self.stacks.iter_mut_all() {
439                 if stack.len() > 64 {
440                     stack.retain(live_tags);
441                 }
442             }
443             self.modified_since_last_gc = false;
444         }
445     }
446 }
447
448 impl VisitTags for Stacks {
449     fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
450         for tag in self.exposed_tags.iter().copied() {
451             visit(tag);
452         }
453     }
454 }
455
456 /// Map per-stack operations to higher-level per-location-range operations.
457 impl<'tcx> Stacks {
458     /// Creates a new stack with an initial tag. For diagnostic purposes, we also need to know
459     /// the [`AllocId`] of the allocation this is associated with.
460     fn new(
461         size: Size,
462         perm: Permission,
463         tag: BorTag,
464         id: AllocId,
465         machine: &MiriMachine<'_, '_>,
466     ) -> Self {
467         let item = Item::new(tag, perm, false);
468         let stack = Stack::new(item);
469
470         Stacks {
471             stacks: RangeMap::new(size, stack),
472             history: AllocHistory::new(id, item, machine),
473             exposed_tags: FxHashSet::default(),
474             modified_since_last_gc: false,
475         }
476     }
477
478     /// Call `f` on every stack in the range.
479     fn for_each(
480         &mut self,
481         range: AllocRange,
482         mut dcx_builder: DiagnosticCxBuilder<'_, '_, 'tcx>,
483         mut f: impl FnMut(
484             &mut Stack,
485             &mut DiagnosticCx<'_, '_, '_, 'tcx>,
486             &mut FxHashSet<BorTag>,
487         ) -> InterpResult<'tcx>,
488     ) -> InterpResult<'tcx> {
489         self.modified_since_last_gc = true;
490         for (offset, stack) in self.stacks.iter_mut(range.start, range.size) {
491             let mut dcx = dcx_builder.build(&mut self.history, offset);
492             f(stack, &mut dcx, &mut self.exposed_tags)?;
493             dcx_builder = dcx.unbuild();
494         }
495         Ok(())
496     }
497 }
498
499 /// Glue code to connect with Miri Machine Hooks
500 impl Stacks {
501     pub fn new_allocation(
502         id: AllocId,
503         size: Size,
504         state: &mut GlobalStateInner,
505         kind: MemoryKind<MiriMemoryKind>,
506         machine: &MiriMachine<'_, '_>,
507     ) -> Self {
508         let (base_tag, perm) = match kind {
509             // New unique borrow. This tag is not accessible by the program,
510             // so it will only ever be used when using the local directly (i.e.,
511             // not through a pointer). That is, whenever we directly write to a local, this will pop
512             // everything else off the stack, invalidating all previous pointers,
513             // and in particular, *all* raw pointers.
514             MemoryKind::Stack => (state.base_ptr_tag(id, machine), Permission::Unique),
515             // Everything else is shared by default.
516             _ => (state.base_ptr_tag(id, machine), Permission::SharedReadWrite),
517         };
518         Stacks::new(size, perm, base_tag, id, machine)
519     }
520
521     #[inline(always)]
522     pub fn before_memory_read<'tcx, 'mir, 'ecx>(
523         &mut self,
524         alloc_id: AllocId,
525         tag: ProvenanceExtra,
526         range: AllocRange,
527         machine: &'ecx MiriMachine<'mir, 'tcx>,
528     ) -> InterpResult<'tcx>
529     where
530         'tcx: 'ecx,
531     {
532         trace!(
533             "read access with tag {:?}: {:?}, size {}",
534             tag,
535             Pointer::new(alloc_id, range.start),
536             range.size.bytes()
537         );
538         let dcx = DiagnosticCxBuilder::read(machine, tag, range);
539         let state = machine.borrow_tracker.as_ref().unwrap().borrow();
540         self.for_each(range, dcx, |stack, dcx, exposed_tags| {
541             stack.access(AccessKind::Read, tag, &state, dcx, exposed_tags)
542         })
543     }
544
545     #[inline(always)]
546     pub fn before_memory_write<'tcx>(
547         &mut self,
548         alloc_id: AllocId,
549         tag: ProvenanceExtra,
550         range: AllocRange,
551         machine: &mut MiriMachine<'_, 'tcx>,
552     ) -> InterpResult<'tcx> {
553         trace!(
554             "write access with tag {:?}: {:?}, size {}",
555             tag,
556             Pointer::new(alloc_id, range.start),
557             range.size.bytes()
558         );
559         let dcx = DiagnosticCxBuilder::write(machine, tag, range);
560         let state = machine.borrow_tracker.as_ref().unwrap().borrow();
561         self.for_each(range, dcx, |stack, dcx, exposed_tags| {
562             stack.access(AccessKind::Write, tag, &state, dcx, exposed_tags)
563         })
564     }
565
566     #[inline(always)]
567     pub fn before_memory_deallocation<'tcx>(
568         &mut self,
569         alloc_id: AllocId,
570         tag: ProvenanceExtra,
571         range: AllocRange,
572         machine: &mut MiriMachine<'_, 'tcx>,
573     ) -> InterpResult<'tcx> {
574         trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes());
575         let dcx = DiagnosticCxBuilder::dealloc(machine, tag);
576         let state = machine.borrow_tracker.as_ref().unwrap().borrow();
577         self.for_each(range, dcx, |stack, dcx, exposed_tags| {
578             stack.dealloc(tag, &state, dcx, exposed_tags)
579         })?;
580         Ok(())
581     }
582 }
583
584 /// Retagging/reborrowing.  There is some policy in here, such as which permissions
585 /// to grant for which references, and when to add protectors.
586 impl<'mir: 'ecx, 'tcx: 'mir, 'ecx> EvalContextPrivExt<'mir, 'tcx, 'ecx>
587     for crate::MiriInterpCx<'mir, 'tcx>
588 {
589 }
590 trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> {
591     /// Returns the `AllocId` the reborrow was done in, if some actual borrow stack manipulation
592     /// happened.
593     fn sb_reborrow(
594         &mut self,
595         place: &MPlaceTy<'tcx, Provenance>,
596         size: Size,
597         new_perm: NewPermission,
598         new_tag: BorTag,
599         retag_cause: RetagCause, // What caused this retag, for diagnostics only
600     ) -> InterpResult<'tcx, Option<AllocId>> {
601         let this = self.eval_context_mut();
602
603         // It is crucial that this gets called on all code paths, to ensure we track tag creation.
604         let log_creation = |this: &MiriInterpCx<'mir, 'tcx>,
605                             loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag
606          -> InterpResult<'tcx> {
607             let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
608             let ty = place.layout.ty;
609             if global.tracked_pointer_tags.contains(&new_tag) {
610                 let mut kind_str = String::new();
611                 match new_perm {
612                     NewPermission::Uniform { perm, .. } =>
613                         write!(kind_str, "{perm:?} permission").unwrap(),
614                     NewPermission::FreezeSensitive { freeze_perm, .. } if ty.is_freeze(*this.tcx, this.param_env()) =>
615                         write!(kind_str, "{freeze_perm:?} permission").unwrap(),
616                     NewPermission::FreezeSensitive { freeze_perm, nonfreeze_perm, .. }  =>
617                         write!(kind_str, "{freeze_perm:?}/{nonfreeze_perm:?} permission for frozen/non-frozen parts").unwrap(),
618                 }
619                 write!(kind_str, " (pointee type {ty})").unwrap();
620                 this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
621                     new_tag.inner(),
622                     Some(kind_str),
623                     loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, size), orig_tag)),
624                 ));
625             }
626             drop(global); // don't hold that reference any longer than we have to
627
628             let Some((alloc_id, base_offset, orig_tag)) = loc else {
629                 return Ok(())
630             };
631
632             let (_size, _align, alloc_kind) = this.get_alloc_info(alloc_id);
633             match alloc_kind {
634                 AllocKind::LiveData => {
635                     // This should have alloc_extra data, but `get_alloc_extra` can still fail
636                     // if converting this alloc_id from a global to a local one
637                     // uncovers a non-supported `extern static`.
638                     let extra = this.get_alloc_extra(alloc_id)?;
639                     let mut stacked_borrows = extra
640                         .borrow_tracker_sb()
641                         .borrow_mut();
642                     // Note that we create a *second* `DiagnosticCxBuilder` below for the actual retag.
643                     // FIXME: can this be done cleaner?
644                     let dcx = DiagnosticCxBuilder::retag(
645                         &this.machine,
646                         retag_cause,
647                         new_tag,
648                         orig_tag,
649                         alloc_range(base_offset, size),
650                     );
651                     let mut dcx = dcx.build(&mut stacked_borrows.history, base_offset);
652                     dcx.log_creation();
653                     if new_perm.protector().is_some() {
654                         dcx.log_protector();
655                     }
656                 },
657                 AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
658                     // No stacked borrows on these allocations.
659                 }
660             }
661             Ok(())
662         };
663
664         if size == Size::ZERO {
665             trace!(
666                 "reborrow of size 0: reference {:?} derived from {:?} (pointee {})",
667                 new_tag,
668                 place.ptr,
669                 place.layout.ty,
670             );
671             // Don't update any stacks for a zero-sized access; borrow stacks are per-byte and this
672             // touches no bytes so there is no stack to put this tag in.
673             // However, if the pointer for this operation points at a real allocation we still
674             // record where it was created so that we can issue a helpful diagnostic if there is an
675             // attempt to use it for a non-zero-sized access.
676             // Dangling slices are a common case here; it's valid to get their length but with raw
677             // pointer tagging for example all calls to get_unchecked on them are invalid.
678             if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr) {
679                 log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
680                 return Ok(Some(alloc_id));
681             }
682             // This pointer doesn't come with an AllocId. :shrug:
683             log_creation(this, None)?;
684             return Ok(None);
685         }
686
687         let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr)?;
688         log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
689
690         // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
691         let (alloc_size, _) = this.get_live_alloc_size_and_align(alloc_id)?;
692         if base_offset + size > alloc_size {
693             throw_ub!(PointerOutOfBounds {
694                 alloc_id,
695                 alloc_size,
696                 ptr_offset: this.machine_usize_to_isize(base_offset.bytes()),
697                 ptr_size: size,
698                 msg: CheckInAllocMsg::InboundsTest
699             });
700         }
701
702         trace!(
703             "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
704             new_tag,
705             orig_tag,
706             place.layout.ty,
707             Pointer::new(alloc_id, base_offset),
708             size.bytes()
709         );
710
711         if let Some(protect) = new_perm.protector() {
712             // See comment in `Stack::item_invalidated` for why we store the tag twice.
713             this.frame_mut().extra.borrow_tracker.as_mut().unwrap().protected_tags.push(new_tag);
714             this.machine
715                 .borrow_tracker
716                 .as_mut()
717                 .unwrap()
718                 .get_mut()
719                 .protected_tags
720                 .insert(new_tag, protect);
721         }
722
723         // Update the stacks, according to the new permission information we are given.
724         match new_perm {
725             NewPermission::Uniform { perm, access, protector } => {
726                 assert!(perm != Permission::SharedReadOnly);
727                 // Here we can avoid `borrow()` calls because we have mutable references.
728                 // Note that this asserts that the allocation is mutable -- but since we are creating a
729                 // mutable pointer, that seems reasonable.
730                 let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc_id)?;
731                 let stacked_borrows = alloc_extra.borrow_tracker_sb_mut().get_mut();
732                 let item = Item::new(new_tag, perm, protector.is_some());
733                 let range = alloc_range(base_offset, size);
734                 let global = machine.borrow_tracker.as_ref().unwrap().borrow();
735                 let dcx = DiagnosticCxBuilder::retag(
736                     machine,
737                     retag_cause,
738                     new_tag,
739                     orig_tag,
740                     alloc_range(base_offset, size),
741                 );
742                 stacked_borrows.for_each(range, dcx, |stack, dcx, exposed_tags| {
743                     stack.grant(orig_tag, item, access, &global, dcx, exposed_tags)
744                 })?;
745                 drop(global);
746                 if let Some(access) = access {
747                     assert_eq!(access, AccessKind::Write);
748                     // Make sure the data race model also knows about this.
749                     if let Some(data_race) = alloc_extra.data_race.as_mut() {
750                         data_race.write(alloc_id, range, machine)?;
751                     }
752                 }
753             }
754             NewPermission::FreezeSensitive {
755                 freeze_perm,
756                 freeze_access,
757                 freeze_protector,
758                 nonfreeze_perm,
759                 nonfreeze_access,
760             } => {
761                 // The permission is not uniform across the entire range!
762                 // We need a frozen-sensitive reborrow.
763                 // We have to use shared references to alloc/memory_extra here since
764                 // `visit_freeze_sensitive` needs to access the global state.
765                 let alloc_extra = this.get_alloc_extra(alloc_id)?;
766                 let mut stacked_borrows = alloc_extra.borrow_tracker_sb().borrow_mut();
767                 this.visit_freeze_sensitive(place, size, |mut range, frozen| {
768                     // Adjust range.
769                     range.start += base_offset;
770                     // We are only ever `SharedReadOnly` inside the frozen bits.
771                     let (perm, access, protector) = if frozen {
772                         (freeze_perm, freeze_access, freeze_protector)
773                     } else {
774                         (nonfreeze_perm, nonfreeze_access, None)
775                     };
776                     let item = Item::new(new_tag, perm, protector.is_some());
777                     let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
778                     let dcx = DiagnosticCxBuilder::retag(
779                         &this.machine,
780                         retag_cause,
781                         new_tag,
782                         orig_tag,
783                         alloc_range(base_offset, size),
784                     );
785                     stacked_borrows.for_each(range, dcx, |stack, dcx, exposed_tags| {
786                         stack.grant(orig_tag, item, access, &global, dcx, exposed_tags)
787                     })?;
788                     drop(global);
789                     if let Some(access) = access {
790                         assert_eq!(access, AccessKind::Read);
791                         // Make sure the data race model also knows about this.
792                         if let Some(data_race) = alloc_extra.data_race.as_ref() {
793                             data_race.read(alloc_id, range, &this.machine)?;
794                         }
795                     }
796                     Ok(())
797                 })?;
798             }
799         }
800
801         Ok(Some(alloc_id))
802     }
803
804     /// Retags an indidual pointer, returning the retagged version.
805     /// `kind` indicates what kind of reference is being created.
806     fn sb_retag_reference(
807         &mut self,
808         val: &ImmTy<'tcx, Provenance>,
809         new_perm: NewPermission,
810         cause: RetagCause, // What caused this retag, for diagnostics only
811     ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
812         let this = self.eval_context_mut();
813         // We want a place for where the ptr *points to*, so we get one.
814         let place = this.ref_to_mplace(val)?;
815         let size = this.size_and_align_of_mplace(&place)?.map(|(size, _)| size);
816         // FIXME: If we cannot determine the size (because the unsized tail is an `extern type`),
817         // bail out -- we cannot reasonably figure out which memory range to reborrow.
818         // See https://github.com/rust-lang/unsafe-code-guidelines/issues/276.
819         let size = match size {
820             Some(size) => size,
821             None => return Ok(val.clone()),
822         };
823
824         // Compute new borrow.
825         let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
826
827         // Reborrow.
828         let alloc_id = this.sb_reborrow(&place, size, new_perm, new_tag, cause)?;
829
830         // Adjust pointer.
831         let new_place = place.map_provenance(|p| {
832             p.map(|prov| {
833                 match alloc_id {
834                     Some(alloc_id) => {
835                         // If `reborrow` could figure out the AllocId of this ptr, hard-code it into the new one.
836                         // Even if we started out with a wildcard, this newly retagged pointer is tied to that allocation.
837                         Provenance::Concrete { alloc_id, tag: new_tag }
838                     }
839                     None => {
840                         // Looks like this has to stay a wildcard pointer.
841                         assert!(matches!(prov, Provenance::Wildcard));
842                         Provenance::Wildcard
843                     }
844                 }
845             })
846         });
847
848         // Return new pointer.
849         Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
850     }
851 }
852
853 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
854 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
855     fn sb_retag_ptr_value(
856         &mut self,
857         kind: RetagKind,
858         val: &ImmTy<'tcx, Provenance>,
859     ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
860         let this = self.eval_context_mut();
861         let new_perm = NewPermission::from_ref_ty(val.layout.ty, kind, this);
862         let retag_cause = match kind {
863             RetagKind::TwoPhase { .. } => RetagCause::TwoPhase,
864             RetagKind::FnEntry => unreachable!(),
865             RetagKind::Raw | RetagKind::Default => RetagCause::Normal,
866         };
867         this.sb_retag_reference(&val, new_perm, retag_cause)
868     }
869
870     fn sb_retag_place_contents(
871         &mut self,
872         kind: RetagKind,
873         place: &PlaceTy<'tcx, Provenance>,
874     ) -> InterpResult<'tcx> {
875         let this = self.eval_context_mut();
876         let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields;
877         let retag_cause = match kind {
878             RetagKind::Raw | RetagKind::TwoPhase { .. } => unreachable!(), // these can only happen in `retag_ptr_value`
879             RetagKind::FnEntry => RetagCause::FnEntry,
880             RetagKind::Default => RetagCause::Normal,
881         };
882         let mut visitor = RetagVisitor { ecx: this, kind, retag_cause, retag_fields };
883         return visitor.visit_value(place);
884
885         // The actual visitor.
886         struct RetagVisitor<'ecx, 'mir, 'tcx> {
887             ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>,
888             kind: RetagKind,
889             retag_cause: RetagCause,
890             retag_fields: RetagFields,
891         }
892         impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> {
893             #[inline(always)] // yes this helps in our benchmarks
894             fn retag_ptr_inplace(
895                 &mut self,
896                 place: &PlaceTy<'tcx, Provenance>,
897                 new_perm: NewPermission,
898                 retag_cause: RetagCause,
899             ) -> InterpResult<'tcx> {
900                 let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
901                 let val = self.ecx.sb_retag_reference(&val, new_perm, retag_cause)?;
902                 self.ecx.write_immediate(*val, place)?;
903                 Ok(())
904             }
905         }
906         impl<'ecx, 'mir, 'tcx> MutValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>>
907             for RetagVisitor<'ecx, 'mir, 'tcx>
908         {
909             type V = PlaceTy<'tcx, Provenance>;
910
911             #[inline(always)]
912             fn ecx(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> {
913                 self.ecx
914             }
915
916             fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
917                 // Boxes get a weak protectors, since they may be deallocated.
918                 let new_perm = NewPermission::Uniform {
919                     perm: Permission::Unique,
920                     access: Some(AccessKind::Write),
921                     protector: (self.kind == RetagKind::FnEntry)
922                         .then_some(ProtectorKind::WeakProtector),
923                 };
924                 self.retag_ptr_inplace(place, new_perm, self.retag_cause)
925             }
926
927             fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
928                 // If this place is smaller than a pointer, we know that it can't contain any
929                 // pointers we need to retag, so we can stop recursion early.
930                 // This optimization is crucial for ZSTs, because they can contain way more fields
931                 // than we can ever visit.
932                 if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
933                     return Ok(());
934                 }
935
936                 // Check the type of this value to see what to do with it (retag, or recurse).
937                 match place.layout.ty.kind() {
938                     ty::Ref(..) => {
939                         let new_perm =
940                             NewPermission::from_ref_ty(place.layout.ty, self.kind, self.ecx);
941                         self.retag_ptr_inplace(place, new_perm, self.retag_cause)?;
942                     }
943                     ty::RawPtr(..) => {
944                         // We do *not* want to recurse into raw pointers -- wide raw pointers have
945                         // fields, and for dyn Trait pointees those can have reference type!
946                     }
947                     ty::Adt(adt, _) if adt.is_box() => {
948                         // Recurse for boxes, they require some tricky handling and will end up in `visit_box` above.
949                         // (Yes this means we technically also recursively retag the allocator itself
950                         // even if field retagging is not enabled. *shrug*)
951                         self.walk_value(place)?;
952                     }
953                     _ => {
954                         // Not a reference/pointer/box. Only recurse if configured appropriately.
955                         let recurse = match self.retag_fields {
956                             RetagFields::No => false,
957                             RetagFields::Yes => true,
958                             RetagFields::OnlyScalar => {
959                                 // Matching `ArgAbi::new` at the time of writing, only fields of
960                                 // `Scalar` and `ScalarPair` ABI are considered.
961                                 matches!(place.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..))
962                             }
963                         };
964                         if recurse {
965                             self.walk_value(place)?;
966                         }
967                     }
968                 }
969
970                 Ok(())
971             }
972         }
973     }
974
975     /// After a stack frame got pushed, retag the return place so that we are sure
976     /// it does not alias with anything.
977     ///
978     /// This is a HACK because there is nothing in MIR that would make the retag
979     /// explicit. Also see <https://github.com/rust-lang/rust/issues/71117>.
980     fn sb_retag_return_place(&mut self) -> InterpResult<'tcx> {
981         let this = self.eval_context_mut();
982         let return_place = &this.frame().return_place;
983         if return_place.layout.is_zst() {
984             // There may not be any memory here, nothing to do.
985             return Ok(());
986         }
987         // We need this to be in-memory to use tagged pointers.
988         let return_place = this.force_allocation(&return_place.clone())?;
989
990         // We have to turn the place into a pointer to use the existing code.
991         // (The pointer type does not matter, so we use a raw pointer.)
992         let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?;
993         let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
994         // Reborrow it. With protection! That is part of the point.
995         let new_perm = NewPermission::Uniform {
996             perm: Permission::Unique,
997             access: Some(AccessKind::Write),
998             protector: Some(ProtectorKind::StrongProtector),
999         };
1000         let val = this.sb_retag_reference(&val, new_perm, RetagCause::FnReturn)?;
1001         // And use reborrowed pointer for return place.
1002         let return_place = this.ref_to_mplace(&val)?;
1003         this.frame_mut().return_place = return_place.into();
1004
1005         Ok(())
1006     }
1007
1008     /// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
1009     fn sb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
1010         let this = self.eval_context_mut();
1011
1012         // Function pointers and dead objects don't have an alloc_extra so we ignore them.
1013         // This is okay because accessing them is UB anyway, no need for any Stacked Borrows checks.
1014         // NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
1015         let (_size, _align, kind) = this.get_alloc_info(alloc_id);
1016         match kind {
1017             AllocKind::LiveData => {
1018                 // This should have alloc_extra data, but `get_alloc_extra` can still fail
1019                 // if converting this alloc_id from a global to a local one
1020                 // uncovers a non-supported `extern static`.
1021                 let alloc_extra = this.get_alloc_extra(alloc_id)?;
1022                 trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
1023                 alloc_extra.borrow_tracker_sb().borrow_mut().exposed_tags.insert(tag);
1024             }
1025             AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
1026                 // No stacked borrows on these allocations.
1027             }
1028         }
1029         Ok(())
1030     }
1031
1032     fn print_stacks(&mut self, alloc_id: AllocId) -> InterpResult<'tcx> {
1033         let this = self.eval_context_mut();
1034         let alloc_extra = this.get_alloc_extra(alloc_id)?;
1035         let stacks = alloc_extra.borrow_tracker_sb().borrow();
1036         for (range, stack) in stacks.stacks.iter_all() {
1037             print!("{range:?}: [");
1038             if let Some(bottom) = stack.unknown_bottom() {
1039                 print!(" unknown-bottom(..{bottom:?})");
1040             }
1041             for i in 0..stack.len() {
1042                 let item = stack.get(i).unwrap();
1043                 print!(" {:?}{:?}", item.perm(), item.tag());
1044             }
1045             println!(" ]");
1046         }
1047         Ok(())
1048     }
1049 }