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