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