• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use log::trace;
2 
3 use rustc_target::abi::{Abi, Size};
4 
5 use crate::borrow_tracker::{AccessKind, GlobalStateInner, ProtectorKind, RetagFields};
6 use rustc_middle::{
7     mir::{Mutability, RetagKind},
8     ty::{
9         self,
10         layout::{HasParamEnv, LayoutOf},
11         Ty,
12     },
13 };
14 use rustc_span::def_id::DefId;
15 
16 use crate::*;
17 
18 pub mod diagnostics;
19 mod perms;
20 mod tree;
21 mod unimap;
22 use perms::Permission;
23 pub use tree::Tree;
24 
25 pub type AllocState = Tree;
26 
27 impl<'tcx> Tree {
28     /// Create a new allocation, i.e. a new tree
new_allocation( id: AllocId, size: Size, state: &mut GlobalStateInner, _kind: MemoryKind<machine::MiriMemoryKind>, machine: &MiriMachine<'_, 'tcx>, ) -> Self29     pub fn new_allocation(
30         id: AllocId,
31         size: Size,
32         state: &mut GlobalStateInner,
33         _kind: MemoryKind<machine::MiriMemoryKind>,
34         machine: &MiriMachine<'_, 'tcx>,
35     ) -> Self {
36         let tag = state.base_ptr_tag(id, machine); // Fresh tag for the root
37         let span = machine.current_span();
38         Tree::new(tag, size, span)
39     }
40 
41     /// Check that an access on the entire range is permitted, and update
42     /// the tree.
before_memory_access( &mut self, access_kind: AccessKind, alloc_id: AllocId, prov: ProvenanceExtra, range: AllocRange, machine: &MiriMachine<'_, 'tcx>, ) -> InterpResult<'tcx>43     pub fn before_memory_access(
44         &mut self,
45         access_kind: AccessKind,
46         alloc_id: AllocId,
47         prov: ProvenanceExtra,
48         range: AllocRange,
49         machine: &MiriMachine<'_, 'tcx>,
50     ) -> InterpResult<'tcx> {
51         trace!(
52             "{} with tag {:?}: {:?}, size {}",
53             access_kind,
54             prov,
55             Pointer::new(alloc_id, range.start),
56             range.size.bytes(),
57         );
58         // TODO: for now we bail out on wildcard pointers. Eventually we should
59         // handle them as much as we can.
60         let tag = match prov {
61             ProvenanceExtra::Concrete(tag) => tag,
62             ProvenanceExtra::Wildcard => return Ok(()),
63         };
64         let global = machine.borrow_tracker.as_ref().unwrap();
65         let span = machine.current_span();
66         self.perform_access(
67             access_kind,
68             tag,
69             range,
70             global,
71             span,
72             diagnostics::AccessCause::Explicit(access_kind),
73         )
74     }
75 
76     /// Check that this pointer has permission to deallocate this range.
before_memory_deallocation( &mut self, _alloc_id: AllocId, prov: ProvenanceExtra, range: AllocRange, machine: &MiriMachine<'_, 'tcx>, ) -> InterpResult<'tcx>77     pub fn before_memory_deallocation(
78         &mut self,
79         _alloc_id: AllocId,
80         prov: ProvenanceExtra,
81         range: AllocRange,
82         machine: &MiriMachine<'_, 'tcx>,
83     ) -> InterpResult<'tcx> {
84         // TODO: for now we bail out on wildcard pointers. Eventually we should
85         // handle them as much as we can.
86         let tag = match prov {
87             ProvenanceExtra::Concrete(tag) => tag,
88             ProvenanceExtra::Wildcard => return Ok(()),
89         };
90         let global = machine.borrow_tracker.as_ref().unwrap();
91         let span = machine.current_span();
92         self.dealloc(tag, range, global, span)
93     }
94 
expose_tag(&mut self, _tag: BorTag)95     pub fn expose_tag(&mut self, _tag: BorTag) {
96         // TODO
97     }
98 }
99 
100 /// Policy for a new borrow.
101 #[derive(Debug, Clone, Copy)]
102 struct NewPermission {
103     /// Optionally ignore the actual size to do a zero-size reborrow.
104     /// If this is set then `dereferenceable` is not enforced.
105     zero_size: bool,
106     /// Which permission should the pointer start with.
107     initial_state: Permission,
108     /// Whether this pointer is part of the arguments of a function call.
109     /// `protector` is `Some(_)` for all pointers marked `noalias`.
110     protector: Option<ProtectorKind>,
111 }
112 
113 impl<'tcx> NewPermission {
114     /// Determine NewPermission of the reference from the type of the pointee.
from_ref_ty( pointee: Ty<'tcx>, mutability: Mutability, kind: RetagKind, cx: &crate::MiriInterpCx<'_, 'tcx>, ) -> Option<Self>115     fn from_ref_ty(
116         pointee: Ty<'tcx>,
117         mutability: Mutability,
118         kind: RetagKind,
119         cx: &crate::MiriInterpCx<'_, 'tcx>,
120     ) -> Option<Self> {
121         let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.param_env());
122         let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.param_env());
123         let initial_state = match mutability {
124             Mutability::Mut if ty_is_unpin => Permission::new_unique_2phase(ty_is_freeze),
125             Mutability::Not if ty_is_freeze => Permission::new_frozen(),
126             // Raw pointers never enter this function so they are not handled.
127             // However raw pointers are not the only pointers that take the parent
128             // tag, this also happens for `!Unpin` `&mut`s and interior mutable
129             // `&`s, which are excluded above.
130             _ => return None,
131         };
132 
133         let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector);
134         Some(Self { zero_size: false, initial_state, protector })
135     }
136 
137     /// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled).
138     /// These pointers allow deallocation so need a different kind of protector not handled
139     /// by `from_ref_ty`.
from_unique_ty( ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'_, 'tcx>, zero_size: bool, ) -> Option<Self>140     fn from_unique_ty(
141         ty: Ty<'tcx>,
142         kind: RetagKind,
143         cx: &crate::MiriInterpCx<'_, 'tcx>,
144         zero_size: bool,
145     ) -> Option<Self> {
146         let pointee = ty.builtin_deref(true).unwrap().ty;
147         pointee.is_unpin(*cx.tcx, cx.param_env()).then_some(()).map(|()| {
148             // Regular `Unpin` box, give it `noalias` but only a weak protector
149             // because it is valid to deallocate it within the function.
150             let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.param_env());
151             Self {
152                 zero_size,
153                 initial_state: Permission::new_unique_2phase(ty_is_freeze),
154                 protector: (kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector),
155             }
156         })
157     }
158 }
159 
160 /// Retagging/reborrowing.
161 /// Policy on which permission to grant to each pointer should be left to
162 /// the implementation of NewPermission.
163 impl<'mir: 'ecx, 'tcx: 'mir, 'ecx> EvalContextPrivExt<'mir, 'tcx, 'ecx>
164     for crate::MiriInterpCx<'mir, 'tcx>
165 {
166 }
167 trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> {
168     /// Returns the `AllocId` the reborrow was done in, if there is some actual
169     /// memory associated with this pointer. Returns `None` if there is no actual
170     /// memory allocated. Also checks that the reborrow of size `ptr_size` is
171     /// within bounds of the allocation.
172     ///
173     /// Also returns the tag that the pointer should get, which is essentially
174     /// `if new_perm.is_some() { new_tag } else { parent_tag }` along with
175     /// some logging (always) and fake reads (if `new_perm` is
176     /// `Some(NewPermission { perform_read_access: true }`).
tb_reborrow( &mut self, place: &MPlaceTy<'tcx, Provenance>, ptr_size: Size, new_perm: Option<NewPermission>, new_tag: BorTag, ) -> InterpResult<'tcx, Option<(AllocId, BorTag)>>177     fn tb_reborrow(
178         &mut self,
179         place: &MPlaceTy<'tcx, Provenance>, // parent tag extracted from here
180         ptr_size: Size,
181         new_perm: Option<NewPermission>,
182         new_tag: BorTag,
183     ) -> InterpResult<'tcx, Option<(AllocId, BorTag)>> {
184         let this = self.eval_context_mut();
185 
186         // It is crucial that this gets called on all code paths, to ensure we track tag creation.
187         let log_creation = |this: &MiriInterpCx<'mir, 'tcx>,
188                             loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag
189          -> InterpResult<'tcx> {
190             let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
191             let ty = place.layout.ty;
192             if global.tracked_pointer_tags.contains(&new_tag) {
193                 let kind_str = format!("{new_perm:?} (pointee type {ty})");
194                 this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
195                     new_tag.inner(),
196                     Some(kind_str),
197                     loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, ptr_size), orig_tag)),
198                 ));
199             }
200             drop(global); // don't hold that reference any longer than we have to
201             Ok(())
202         };
203 
204         trace!("Reborrow of size {:?}", ptr_size);
205         let (alloc_id, base_offset, parent_prov) = if ptr_size > Size::ZERO {
206             this.ptr_get_alloc_id(place.ptr)?
207         } else {
208             match this.ptr_try_get_alloc_id(place.ptr) {
209                 Ok(data) => data,
210                 Err(_) => {
211                     // This pointer doesn't come with an AllocId, so there's no
212                     // memory to do retagging in.
213                     trace!(
214                         "reborrow of size 0: reference {:?} derived from {:?} (pointee {})",
215                         new_tag,
216                         place.ptr,
217                         place.layout.ty,
218                     );
219                     log_creation(this, None)?;
220                     return Ok(None);
221                 }
222             }
223         };
224         let orig_tag = match parent_prov {
225             ProvenanceExtra::Wildcard => return Ok(None), // TODO: handle wildcard pointers
226             ProvenanceExtra::Concrete(tag) => tag,
227         };
228 
229         // Protection against trying to get a reference to a vtable:
230         // vtables do not have an alloc_extra so the call to
231         // `get_alloc_extra` that follows fails.
232         let (alloc_size, _align, alloc_kind) = this.get_alloc_info(alloc_id);
233         if ptr_size == Size::ZERO && !matches!(alloc_kind, AllocKind::LiveData) {
234             return Ok(Some((alloc_id, orig_tag)));
235         }
236 
237         log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
238 
239         // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
240         if base_offset + ptr_size > alloc_size {
241             throw_ub!(PointerOutOfBounds {
242                 alloc_id,
243                 alloc_size,
244                 ptr_offset: this.target_usize_to_isize(base_offset.bytes()),
245                 ptr_size,
246                 msg: CheckInAllocMsg::InboundsTest
247             });
248         }
249 
250         trace!(
251             "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
252             new_tag,
253             orig_tag,
254             place.layout.ty,
255             Pointer::new(alloc_id, base_offset),
256             ptr_size.bytes()
257         );
258 
259         let Some(new_perm) = new_perm else { return Ok(Some((alloc_id, orig_tag))); };
260 
261         if let Some(protect) = new_perm.protector {
262             // We register the protection in two different places.
263             // This makes creating a protector slower, but checking whether a tag
264             // is protected faster.
265             this.frame_mut().extra.borrow_tracker.as_mut().unwrap().protected_tags.push(new_tag);
266             this.machine
267                 .borrow_tracker
268                 .as_mut()
269                 .expect("We should have borrow tracking data")
270                 .get_mut()
271                 .protected_tags
272                 .insert(new_tag, protect);
273         }
274 
275         let span = this.machine.current_span();
276         let alloc_extra = this.get_alloc_extra(alloc_id)?;
277         let range = alloc_range(base_offset, ptr_size);
278         let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
279 
280         // All reborrows incur a (possibly zero-sized) read access to the parent
281         {
282             let global = &this.machine.borrow_tracker.as_ref().unwrap();
283             let span = this.machine.current_span();
284             tree_borrows.perform_access(
285                 AccessKind::Read,
286                 orig_tag,
287                 range,
288                 global,
289                 span,
290                 diagnostics::AccessCause::Reborrow,
291             )?;
292             if let Some(data_race) = alloc_extra.data_race.as_ref() {
293                 data_race.read(alloc_id, range, &this.machine)?;
294             }
295         }
296 
297         // Record the parent-child pair in the tree.
298         tree_borrows.new_child(orig_tag, new_tag, new_perm.initial_state, range, span)?;
299         Ok(Some((alloc_id, new_tag)))
300     }
301 
302     /// Retags an individual pointer, returning the retagged version.
tb_retag_reference( &mut self, val: &ImmTy<'tcx, Provenance>, new_perm: Option<NewPermission>, ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>>303     fn tb_retag_reference(
304         &mut self,
305         val: &ImmTy<'tcx, Provenance>,
306         new_perm: Option<NewPermission>,
307     ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
308         let this = self.eval_context_mut();
309         // We want a place for where the ptr *points to*, so we get one.
310         let place = this.ref_to_mplace(val)?;
311 
312         // Determine the size of the reborrow.
313         // For most types this is the entire size of the place, however
314         // - when `extern type` is involved we use the size of the known prefix,
315         // - if the pointer is not reborrowed (raw pointer) or if `zero_size` is set
316         // then we override the size to do a zero-length reborrow.
317         let reborrow_size = match new_perm {
318             Some(NewPermission { zero_size: false, .. }) =>
319                 this.size_and_align_of_mplace(&place)?
320                     .map(|(size, _)| size)
321                     .unwrap_or(place.layout.size),
322             _ => Size::from_bytes(0),
323         };
324         trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size);
325 
326         // This new tag is not guaranteed to actually be used.
327         //
328         // If you run out of tags, consider the following optimization: adjust `tb_reborrow`
329         // so that rather than taking as input a fresh tag and deciding whether it uses this
330         // one or the parent it instead just returns whether a new tag should be created.
331         // This will avoid creating tags than end up never being used.
332         let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
333 
334         // Compute the actual reborrow.
335         let reborrowed = this.tb_reborrow(&place, reborrow_size, new_perm, new_tag)?;
336 
337         // Adjust pointer.
338         let new_place = place.map_provenance(|p| {
339             p.map(|prov| {
340                 match reborrowed {
341                     Some((alloc_id, actual_tag)) => {
342                         // If `reborrow` could figure out the AllocId of this ptr, hard-code it into the new one.
343                         // Even if we started out with a wildcard, this newly retagged pointer is tied to that allocation.
344                         Provenance::Concrete { alloc_id, tag: actual_tag }
345                     }
346                     None => {
347                         // Looks like this has to stay a wildcard pointer.
348                         assert!(matches!(prov, Provenance::Wildcard));
349                         Provenance::Wildcard
350                     }
351                 }
352             })
353         });
354 
355         // Return new pointer.
356         Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
357     }
358 }
359 
360 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
361 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
362     /// Retag a pointer. References are passed to `from_ref_ty` and
363     /// raw pointers are never reborrowed.
tb_retag_ptr_value( &mut self, kind: RetagKind, val: &ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>>364     fn tb_retag_ptr_value(
365         &mut self,
366         kind: RetagKind,
367         val: &ImmTy<'tcx, Provenance>,
368     ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
369         let this = self.eval_context_mut();
370         let new_perm = match val.layout.ty.kind() {
371             &ty::Ref(_, pointee, mutability) =>
372                 NewPermission::from_ref_ty(pointee, mutability, kind, this),
373             _ => None,
374         };
375         this.tb_retag_reference(val, new_perm)
376     }
377 
378     /// Retag all pointers that are stored in this place.
tb_retag_place_contents( &mut self, kind: RetagKind, place: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx>379     fn tb_retag_place_contents(
380         &mut self,
381         kind: RetagKind,
382         place: &PlaceTy<'tcx, Provenance>,
383     ) -> InterpResult<'tcx> {
384         let this = self.eval_context_mut();
385         let options = this.machine.borrow_tracker.as_mut().unwrap().get_mut();
386         let retag_fields = options.retag_fields;
387         let unique_did =
388             options.unique_is_unique.then(|| this.tcx.lang_items().ptr_unique()).flatten();
389         let mut visitor = RetagVisitor { ecx: this, kind, retag_fields, unique_did };
390         return visitor.visit_value(place);
391 
392         // The actual visitor.
393         struct RetagVisitor<'ecx, 'mir, 'tcx> {
394             ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>,
395             kind: RetagKind,
396             retag_fields: RetagFields,
397             unique_did: Option<DefId>,
398         }
399         impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> {
400             #[inline(always)] // yes this helps in our benchmarks
401             fn retag_ptr_inplace(
402                 &mut self,
403                 place: &PlaceTy<'tcx, Provenance>,
404                 new_perm: Option<NewPermission>,
405             ) -> InterpResult<'tcx> {
406                 let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
407                 let val = self.ecx.tb_retag_reference(&val, new_perm)?;
408                 self.ecx.write_immediate(*val, place)?;
409                 Ok(())
410             }
411         }
412         impl<'ecx, 'mir, 'tcx> MutValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>>
413             for RetagVisitor<'ecx, 'mir, 'tcx>
414         {
415             type V = PlaceTy<'tcx, Provenance>;
416 
417             #[inline(always)]
418             fn ecx(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> {
419                 self.ecx
420             }
421 
422             /// Regardless of how `Unique` is handled, Boxes are always reborrowed.
423             /// When `Unique` is also reborrowed, then it behaves exactly like `Box`
424             /// except for the fact that `Box` has a non-zero-sized reborrow.
425             fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
426                 let new_perm = NewPermission::from_unique_ty(
427                     place.layout.ty,
428                     self.kind,
429                     self.ecx,
430                     /* zero_size */ false,
431                 );
432                 self.retag_ptr_inplace(place, new_perm)
433             }
434 
435             fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
436                 // If this place is smaller than a pointer, we know that it can't contain any
437                 // pointers we need to retag, so we can stop recursion early.
438                 // This optimization is crucial for ZSTs, because they can contain way more fields
439                 // than we can ever visit.
440                 if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
441                     return Ok(());
442                 }
443 
444                 // Check the type of this value to see what to do with it (retag, or recurse).
445                 match place.layout.ty.kind() {
446                     &ty::Ref(_, pointee, mutability) => {
447                         let new_perm =
448                             NewPermission::from_ref_ty(pointee, mutability, self.kind, self.ecx);
449                         self.retag_ptr_inplace(place, new_perm)?;
450                     }
451                     ty::RawPtr(_) => {
452                         // We definitely do *not* want to recurse into raw pointers -- wide raw
453                         // pointers have fields, and for dyn Trait pointees those can have reference
454                         // type!
455                         // We also do not want to reborrow them.
456                     }
457                     ty::Adt(adt, _) if adt.is_box() => {
458                         // Recurse for boxes, they require some tricky handling and will end up in `visit_box` above.
459                         // (Yes this means we technically also recursively retag the allocator itself
460                         // even if field retagging is not enabled. *shrug*)
461                         self.walk_value(place)?;
462                     }
463                     ty::Adt(adt, _) if self.unique_did == Some(adt.did()) => {
464                         let place = inner_ptr_of_unique(self.ecx, place)?;
465                         let new_perm = NewPermission::from_unique_ty(
466                             place.layout.ty,
467                             self.kind,
468                             self.ecx,
469                             /* zero_size */ true,
470                         );
471                         self.retag_ptr_inplace(&place, new_perm)?;
472                     }
473                     _ => {
474                         // Not a reference/pointer/box. Only recurse if configured appropriately.
475                         let recurse = match self.retag_fields {
476                             RetagFields::No => false,
477                             RetagFields::Yes => true,
478                             RetagFields::OnlyScalar => {
479                                 // Matching `ArgAbi::new` at the time of writing, only fields of
480                                 // `Scalar` and `ScalarPair` ABI are considered.
481                                 matches!(place.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..))
482                             }
483                         };
484                         if recurse {
485                             self.walk_value(place)?;
486                         }
487                     }
488                 }
489                 Ok(())
490             }
491         }
492     }
493 
494     /// After a stack frame got pushed, retag the return place so that we are sure
495     /// it does not alias with anything.
496     ///
497     /// This is a HACK because there is nothing in MIR that would make the retag
498     /// explicit. Also see <https://github.com/rust-lang/rust/issues/71117>.
tb_retag_return_place(&mut self) -> InterpResult<'tcx>499     fn tb_retag_return_place(&mut self) -> InterpResult<'tcx> {
500         let this = self.eval_context_mut();
501         //this.debug_hint_location();
502         let return_place = &this.frame().return_place;
503         if return_place.layout.is_zst() {
504             // There may not be any memory here, nothing to do.
505             return Ok(());
506         }
507         // We need this to be in-memory to use tagged pointers.
508         let return_place = this.force_allocation(&return_place.clone())?;
509 
510         // We have to turn the place into a pointer to use the existing code.
511         // (The pointer type does not matter, so we use a raw pointer.)
512         let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx,return_place.layout.ty))?;
513         let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
514         // Reborrow it. With protection! That is part of the point.
515         // FIXME: do we truly want a 2phase borrow here?
516         let new_perm = Some(NewPermission {
517             initial_state: Permission::new_unique_2phase(/*freeze*/ false),
518             zero_size: false,
519             protector: Some(ProtectorKind::StrongProtector),
520         });
521         let val = this.tb_retag_reference(&val, new_perm)?;
522         // And use reborrowed pointer for return place.
523         let return_place = this.ref_to_mplace(&val)?;
524         this.frame_mut().return_place = return_place.into();
525 
526         Ok(())
527     }
528 
529     /// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
tb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx>530     fn tb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
531         let this = self.eval_context_mut();
532 
533         // Function pointers and dead objects don't have an alloc_extra so we ignore them.
534         // This is okay because accessing them is UB anyway, no need for any Tree Borrows checks.
535         // NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
536         let (_size, _align, kind) = this.get_alloc_info(alloc_id);
537         match kind {
538             AllocKind::LiveData => {
539                 // This should have alloc_extra data, but `get_alloc_extra` can still fail
540                 // if converting this alloc_id from a global to a local one
541                 // uncovers a non-supported `extern static`.
542                 let alloc_extra = this.get_alloc_extra(alloc_id)?;
543                 trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
544                 alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag);
545             }
546             AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
547                 // No tree borrows on these allocations.
548             }
549         }
550         Ok(())
551     }
552 
553     /// Display the tree.
print_tree(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx>554     fn print_tree(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
555         let this = self.eval_context_mut();
556         let alloc_extra = this.get_alloc_extra(alloc_id)?;
557         let tree_borrows = alloc_extra.borrow_tracker_tb().borrow();
558         let borrow_tracker = &this.machine.borrow_tracker.as_ref().unwrap().borrow();
559         tree_borrows.print_tree(&borrow_tracker.protected_tags, show_unnamed)
560     }
561 
562     /// Give a name to the pointer, usually the name it has in the source code (for debugging).
563     /// The name given is `name` and the pointer that receives it is the `nth_parent`
564     /// of `ptr` (with 0 representing `ptr` itself)
tb_give_pointer_debug_name( &mut self, ptr: Pointer<Option<Provenance>>, nth_parent: u8, name: &str, ) -> InterpResult<'tcx>565     fn tb_give_pointer_debug_name(
566         &mut self,
567         ptr: Pointer<Option<Provenance>>,
568         nth_parent: u8,
569         name: &str,
570     ) -> InterpResult<'tcx> {
571         let this = self.eval_context_mut();
572         let (tag, alloc_id) = match ptr.provenance {
573             Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id),
574             _ => {
575                 eprintln!("Can't give the name {name} to Wildcard pointer");
576                 return Ok(());
577             }
578         };
579         let alloc_extra = this.get_alloc_extra(alloc_id)?;
580         let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
581         tree_borrows.give_pointer_debug_name(tag, nth_parent, name)
582     }
583 }
584 
585 /// Takes a place for a `Unique` and turns it into a place with the inner raw pointer.
586 /// I.e. input is what you get from the visitor upon encountering an `adt` that is `Unique`,
587 /// and output can be used by `retag_ptr_inplace`.
inner_ptr_of_unique<'tcx>( ecx: &mut MiriInterpCx<'_, 'tcx>, place: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>>588 fn inner_ptr_of_unique<'tcx>(
589     ecx: &mut MiriInterpCx<'_, 'tcx>,
590     place: &PlaceTy<'tcx, Provenance>,
591 ) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>> {
592     // Follows the same layout as `interpret/visitor.rs:walk_value` for `Box` in
593     // `rustc_const_eval`, just with one fewer layer.
594     // Here we have a `Unique(NonNull(*mut), PhantomData)`
595     assert_eq!(place.layout.fields.count(), 2, "Unique must have exactly 2 fields");
596     let (nonnull, phantom) = (ecx.place_field(place, 0)?, ecx.place_field(place, 1)?);
597     assert!(
598         phantom.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
599         "2nd field of `Unique` should be `PhantomData` but is `{:?}`",
600         phantom.layout.ty,
601     );
602     // Now down to `NonNull(*mut)`
603     assert_eq!(nonnull.layout.fields.count(), 1, "NonNull must have exactly 1 field");
604     let ptr = ecx.place_field(&nonnull, 0)?;
605     // Finally a plain `*mut`
606     Ok(ptr)
607 }
608