miri/borrow_tracker/tree_borrows/mod.rs
1use rustc_abi::{BackendRepr, Size};
2use rustc_middle::mir::{Mutability, RetagKind};
3use rustc_middle::ty::layout::HasTypingEnv;
4use rustc_middle::ty::{self, Ty};
5
6use self::foreign_access_skipping::IdempotentForeignAccess;
7use self::tree::LocationState;
8use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind};
9use crate::concurrency::data_race::NaReadType;
10use crate::*;
11
12pub mod diagnostics;
13mod foreign_access_skipping;
14mod perms;
15mod tree;
16mod unimap;
17
18#[cfg(test)]
19mod exhaustive;
20
21use self::perms::Permission;
22pub use self::tree::Tree;
23
24pub type AllocState = Tree;
25
26impl<'tcx> Tree {
27 /// Create a new allocation, i.e. a new tree
28 pub fn new_allocation(
29 id: AllocId,
30 size: Size,
31 state: &mut GlobalStateInner,
32 _kind: MemoryKind,
33 machine: &MiriMachine<'tcx>,
34 ) -> Self {
35 let tag = state.root_ptr_tag(id, machine); // Fresh tag for the root
36 let span = machine.current_span();
37 Tree::new(tag, size, span)
38 }
39
40 /// Check that an access on the entire range is permitted, and update
41 /// the tree.
42 pub fn before_memory_access(
43 &mut self,
44 access_kind: AccessKind,
45 alloc_id: AllocId,
46 prov: ProvenanceExtra,
47 range: AllocRange,
48 machine: &MiriMachine<'tcx>,
49 ) -> InterpResult<'tcx> {
50 trace!(
51 "{} with tag {:?}: {:?}, size {}",
52 access_kind,
53 prov,
54 interpret::Pointer::new(alloc_id, range.start),
55 range.size.bytes(),
56 );
57 // TODO: for now we bail out on wildcard pointers. Eventually we should
58 // handle them as much as we can.
59 let tag = match prov {
60 ProvenanceExtra::Concrete(tag) => tag,
61 ProvenanceExtra::Wildcard => return interp_ok(()),
62 };
63 let global = machine.borrow_tracker.as_ref().unwrap();
64 let span = machine.current_span();
65 self.perform_access(
66 tag,
67 Some((range, access_kind, diagnostics::AccessCause::Explicit(access_kind))),
68 global,
69 alloc_id,
70 span,
71 )
72 }
73
74 /// Check that this pointer has permission to deallocate this range.
75 pub fn before_memory_deallocation(
76 &mut self,
77 alloc_id: AllocId,
78 prov: ProvenanceExtra,
79 size: Size,
80 machine: &MiriMachine<'tcx>,
81 ) -> InterpResult<'tcx> {
82 // TODO: for now we bail out on wildcard pointers. Eventually we should
83 // handle them as much as we can.
84 let tag = match prov {
85 ProvenanceExtra::Concrete(tag) => tag,
86 ProvenanceExtra::Wildcard => return interp_ok(()),
87 };
88 let global = machine.borrow_tracker.as_ref().unwrap();
89 let span = machine.current_span();
90 self.dealloc(tag, alloc_range(Size::ZERO, size), global, alloc_id, span)
91 }
92
93 pub fn expose_tag(&mut self, _tag: BorTag) {
94 // TODO
95 }
96
97 /// A tag just lost its protector.
98 ///
99 /// This emits a special kind of access that is only applied
100 /// to accessed locations, as a protection against other
101 /// tags not having been made aware of the existence of this
102 /// protector.
103 pub fn release_protector(
104 &mut self,
105 machine: &MiriMachine<'tcx>,
106 global: &GlobalState,
107 tag: BorTag,
108 alloc_id: AllocId, // diagnostics
109 ) -> InterpResult<'tcx> {
110 let span = machine.current_span();
111 // `None` makes it the magic on-protector-end operation
112 self.perform_access(tag, None, global, alloc_id, span)
113 }
114}
115
116/// Policy for a new borrow.
117#[derive(Debug, Clone, Copy)]
118pub struct NewPermission {
119 /// Permission for the frozen part of the range.
120 freeze_perm: Permission,
121 /// Whether a read access should be performed on the frozen part on a retag.
122 freeze_access: bool,
123 /// Permission for the non-frozen part of the range.
124 nonfreeze_perm: Permission,
125 /// Whether a read access should be performed on the non-frozen
126 /// part on a retag.
127 nonfreeze_access: bool,
128 /// Whether this pointer is part of the arguments of a function call.
129 /// `protector` is `Some(_)` for all pointers marked `noalias`.
130 protector: Option<ProtectorKind>,
131}
132
133impl<'tcx> NewPermission {
134 /// Determine NewPermission of the reference from the type of the pointee.
135 fn from_ref_ty(
136 pointee: Ty<'tcx>,
137 mutability: Mutability,
138 kind: RetagKind,
139 cx: &crate::MiriInterpCx<'tcx>,
140 ) -> Option<Self> {
141 let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env());
142 let is_protected = kind == RetagKind::FnEntry;
143 let protector = is_protected.then_some(ProtectorKind::StrongProtector);
144
145 Some(match mutability {
146 Mutability::Mut if ty_is_unpin =>
147 NewPermission {
148 freeze_perm: Permission::new_reserved(
149 /* ty_is_freeze */ true,
150 is_protected,
151 ),
152 freeze_access: true,
153 nonfreeze_perm: Permission::new_reserved(
154 /* ty_is_freeze */ false,
155 is_protected,
156 ),
157 // If we have a mutable reference, then the non-frozen part will
158 // have state `ReservedIM` or `Reserved`, which can have an initial read access
159 // performed on it because you cannot have multiple mutable borrows.
160 nonfreeze_access: true,
161 protector,
162 },
163 Mutability::Not =>
164 NewPermission {
165 freeze_perm: Permission::new_frozen(),
166 freeze_access: true,
167 nonfreeze_perm: Permission::new_cell(),
168 // If it is a shared reference, then the non-frozen
169 // part will have state `Cell`, which should not have an initial access,
170 // as this can cause data races when using thread-safe data types like
171 // `Mutex<T>`.
172 nonfreeze_access: false,
173 protector,
174 },
175 _ => return None,
176 })
177 }
178
179 /// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled).
180 /// These pointers allow deallocation so need a different kind of protector not handled
181 /// by `from_ref_ty`.
182 fn from_unique_ty(
183 ty: Ty<'tcx>,
184 kind: RetagKind,
185 cx: &crate::MiriInterpCx<'tcx>,
186 ) -> Option<Self> {
187 let pointee = ty.builtin_deref(true).unwrap();
188 pointee.is_unpin(*cx.tcx, cx.typing_env()).then_some(()).map(|()| {
189 // Regular `Unpin` box, give it `noalias` but only a weak protector
190 // because it is valid to deallocate it within the function.
191 let is_protected = kind == RetagKind::FnEntry;
192 let protector = is_protected.then_some(ProtectorKind::WeakProtector);
193 NewPermission {
194 freeze_perm: Permission::new_reserved(/* ty_is_freeze */ true, is_protected),
195 freeze_access: true,
196 nonfreeze_perm: Permission::new_reserved(
197 /* ty_is_freeze */ false,
198 is_protected,
199 ),
200 nonfreeze_access: true,
201 protector,
202 }
203 })
204 }
205}
206
207/// Retagging/reborrowing.
208/// Policy on which permission to grant to each pointer should be left to
209/// the implementation of NewPermission.
210impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
211trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
212 /// Returns the provenance that should be used henceforth.
213 fn tb_reborrow(
214 &mut self,
215 place: &MPlaceTy<'tcx>, // parent tag extracted from here
216 ptr_size: Size,
217 new_perm: NewPermission,
218 new_tag: BorTag,
219 ) -> InterpResult<'tcx, Option<Provenance>> {
220 let this = self.eval_context_mut();
221 // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
222 this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable)?;
223
224 // It is crucial that this gets called on all code paths, to ensure we track tag creation.
225 let log_creation = |this: &MiriInterpCx<'tcx>,
226 loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag
227 -> InterpResult<'tcx> {
228 let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
229 let ty = place.layout.ty;
230 if global.tracked_pointer_tags.contains(&new_tag) {
231 let ty_is_freeze = ty.is_freeze(*this.tcx, this.typing_env());
232 let kind_str =
233 if ty_is_freeze {
234 format!("initial state {} (pointee type {ty})", new_perm.freeze_perm)
235 } else {
236 format!("initial state {}/{} outside/inside UnsafeCell (pointee type {ty})", new_perm.freeze_perm, new_perm.nonfreeze_perm)
237 };
238 this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
239 new_tag.inner(),
240 Some(kind_str),
241 loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, ptr_size), orig_tag)),
242 ));
243 }
244 drop(global); // don't hold that reference any longer than we have to
245 interp_ok(())
246 };
247
248 trace!("Reborrow of size {:?}", ptr_size);
249 let (alloc_id, base_offset, parent_prov) = match this.ptr_try_get_alloc_id(place.ptr(), 0) {
250 Ok(data) => {
251 // Unlike SB, we *do* a proper retag for size 0 if can identify the allocation.
252 // After all, the pointer may be lazily initialized outside this initial range.
253 data
254 }
255 Err(_) => {
256 assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
257 // This pointer doesn't come with an AllocId, so there's no
258 // memory to do retagging in.
259 trace!(
260 "reborrow of size 0: reference {:?} derived from {:?} (pointee {})",
261 new_tag,
262 place.ptr(),
263 place.layout.ty,
264 );
265 log_creation(this, None)?;
266 // Keep original provenance.
267 return interp_ok(place.ptr().provenance);
268 }
269 };
270 log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
271
272 let orig_tag = match parent_prov {
273 ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), // TODO: handle wildcard pointers
274 ProvenanceExtra::Concrete(tag) => tag,
275 };
276
277 trace!(
278 "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
279 new_tag,
280 orig_tag,
281 place.layout.ty,
282 interpret::Pointer::new(alloc_id, base_offset),
283 ptr_size.bytes()
284 );
285
286 if let Some(protect) = new_perm.protector {
287 // We register the protection in two different places.
288 // This makes creating a protector slower, but checking whether a tag
289 // is protected faster.
290 this.frame_mut()
291 .extra
292 .borrow_tracker
293 .as_mut()
294 .unwrap()
295 .protected_tags
296 .push((alloc_id, new_tag));
297 this.machine
298 .borrow_tracker
299 .as_mut()
300 .expect("We should have borrow tracking data")
301 .get_mut()
302 .protected_tags
303 .insert(new_tag, protect);
304 }
305
306 let alloc_kind = this.get_alloc_info(alloc_id).kind;
307 if !matches!(alloc_kind, AllocKind::LiveData) {
308 assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
309 // There's not actually any bytes here where accesses could even be tracked.
310 // Just produce the new provenance, nothing else to do.
311 return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
312 }
313
314 let span = this.machine.current_span();
315 let alloc_extra = this.get_alloc_extra(alloc_id)?;
316 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
317
318 // Store initial permissions and their corresponding range.
319 let mut perms_map: RangeMap<LocationState> = RangeMap::new(
320 ptr_size,
321 LocationState::new_accessed(Permission::new_disabled(), IdempotentForeignAccess::None), // this will be overwritten
322 );
323 // Keep track of whether the node has any part that allows for interior mutability.
324 // FIXME: This misses `PhantomData<UnsafeCell<T>>` which could be considered a marker
325 // for requesting interior mutability.
326 let mut has_unsafe_cell = false;
327
328 // When adding a new node, the SIFA of its parents needs to be updated, potentially across
329 // the entire memory range. For the parts that are being accessed below, the access itself
330 // trivially takes care of that. However, we have to do some more work to also deal with
331 // the parts that are not being accessed. Specifically what we do is that we
332 // call `update_last_accessed_after_retag` on the SIFA of the permission set for the part of
333 // memory outside `perm_map` -- so that part is definitely taken care of. The remaining concern
334 // is the part of memory that is in the range of `perms_map`, but not accessed below.
335 // There we have two cases:
336 // * If we do have an `UnsafeCell` (`has_unsafe_cell` becomes true), then the non-accessed part
337 // uses `nonfreeze_perm`, so the `nonfreeze_perm` initialized parts are also fine. We enforce
338 // the `freeze_perm` parts to be accessed, and thus everything is taken care of.
339 // * If there is no `UnsafeCell`, then `freeze_perm` is used everywhere (both inside and outside the initial range),
340 // and we update everything to have the `freeze_perm`'s SIFA, so there are no issues. (And this assert below is not
341 // actually needed in this case).
342 assert!(new_perm.freeze_access);
343
344 let protected = new_perm.protector.is_some();
345 this.visit_freeze_sensitive(place, ptr_size, |range, frozen| {
346 has_unsafe_cell = has_unsafe_cell || !frozen;
347
348 // We are only ever `Frozen` inside the frozen bits.
349 let (perm, access) = if frozen {
350 (new_perm.freeze_perm, new_perm.freeze_access)
351 } else {
352 (new_perm.nonfreeze_perm, new_perm.nonfreeze_access)
353 };
354
355 // Store initial permissions.
356 for (_loc_range, loc) in perms_map.iter_mut(range.start, range.size) {
357 let sifa = perm.strongest_idempotent_foreign_access(protected);
358 // NOTE: Currently, `access` is false if and only if `perm` is Cell, so this `if`
359 // doesn't not change whether any code is UB or not. We could just always use
360 // `new_accessed` and everything would stay the same. But that seems conceptually
361 // odd, so we keep the initial "accessed" bit of the `LocationState` in sync with whether
362 // a read access is performed below.
363 if access {
364 *loc = LocationState::new_accessed(perm, sifa);
365 } else {
366 *loc = LocationState::new_non_accessed(perm, sifa);
367 }
368 }
369
370 // Some reborrows incur a read access to the parent.
371 if access {
372 // Adjust range to be relative to allocation start (rather than to `place`).
373 let mut range_in_alloc = range;
374 range_in_alloc.start += base_offset;
375
376 tree_borrows.perform_access(
377 orig_tag,
378 Some((range_in_alloc, AccessKind::Read, diagnostics::AccessCause::Reborrow)),
379 this.machine.borrow_tracker.as_ref().unwrap(),
380 alloc_id,
381 this.machine.current_span(),
382 )?;
383
384 // Also inform the data race model (but only if any bytes are actually affected).
385 if range.size.bytes() > 0 {
386 if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() {
387 data_race.read(
388 alloc_id,
389 range_in_alloc,
390 NaReadType::Retag,
391 Some(place.layout.ty),
392 &this.machine,
393 )?
394 }
395 }
396 }
397 interp_ok(())
398 })?;
399
400 // Record the parent-child pair in the tree.
401 tree_borrows.new_child(
402 base_offset,
403 orig_tag,
404 new_tag,
405 perms_map,
406 // Allow lazily writing to surrounding data if we found an `UnsafeCell`.
407 if has_unsafe_cell { new_perm.nonfreeze_perm } else { new_perm.freeze_perm },
408 protected,
409 span,
410 )?;
411 drop(tree_borrows);
412
413 interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
414 }
415
416 fn tb_retag_place(
417 &mut self,
418 place: &MPlaceTy<'tcx>,
419 new_perm: NewPermission,
420 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
421 let this = self.eval_context_mut();
422
423 // Determine the size of the reborrow.
424 // For most types this is the entire size of the place, however
425 // - when `extern type` is involved we use the size of the known prefix,
426 // - if the pointer is not reborrowed (raw pointer) then we override the size
427 // to do a zero-length reborrow.
428 let reborrow_size = this
429 .size_and_align_of_mplace(place)?
430 .map(|(size, _)| size)
431 .unwrap_or(place.layout.size);
432 trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size);
433
434 // This new tag is not guaranteed to actually be used.
435 //
436 // If you run out of tags, consider the following optimization: adjust `tb_reborrow`
437 // so that rather than taking as input a fresh tag and deciding whether it uses this
438 // one or the parent it instead just returns whether a new tag should be created.
439 // This will avoid creating tags than end up never being used.
440 let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
441
442 // Compute the actual reborrow.
443 let new_prov = this.tb_reborrow(place, reborrow_size, new_perm, new_tag)?;
444
445 // Adjust place.
446 // (If the closure gets called, that means the old provenance was `Some`, and hence the new
447 // one must also be `Some`.)
448 interp_ok(place.clone().map_provenance(|_| new_prov.unwrap()))
449 }
450
451 /// Retags an individual pointer, returning the retagged version.
452 fn tb_retag_reference(
453 &mut self,
454 val: &ImmTy<'tcx>,
455 new_perm: NewPermission,
456 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
457 let this = self.eval_context_mut();
458 let place = this.ref_to_mplace(val)?;
459 let new_place = this.tb_retag_place(&place, new_perm)?;
460 interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
461 }
462}
463
464impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
465pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
466 /// Retag a pointer. References are passed to `from_ref_ty` and
467 /// raw pointers are never reborrowed.
468 fn tb_retag_ptr_value(
469 &mut self,
470 kind: RetagKind,
471 val: &ImmTy<'tcx>,
472 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
473 let this = self.eval_context_mut();
474 let new_perm = match val.layout.ty.kind() {
475 &ty::Ref(_, pointee, mutability) =>
476 NewPermission::from_ref_ty(pointee, mutability, kind, this),
477 _ => None,
478 };
479 if let Some(new_perm) = new_perm {
480 this.tb_retag_reference(val, new_perm)
481 } else {
482 interp_ok(val.clone())
483 }
484 }
485
486 /// Retag all pointers that are stored in this place.
487 fn tb_retag_place_contents(
488 &mut self,
489 kind: RetagKind,
490 place: &PlaceTy<'tcx>,
491 ) -> InterpResult<'tcx> {
492 let this = self.eval_context_mut();
493 let options = this.machine.borrow_tracker.as_mut().unwrap().get_mut();
494 let retag_fields = options.retag_fields;
495 let mut visitor = RetagVisitor { ecx: this, kind, retag_fields };
496 return visitor.visit_value(place);
497
498 // The actual visitor.
499 struct RetagVisitor<'ecx, 'tcx> {
500 ecx: &'ecx mut MiriInterpCx<'tcx>,
501 kind: RetagKind,
502 retag_fields: RetagFields,
503 }
504 impl<'ecx, 'tcx> RetagVisitor<'ecx, 'tcx> {
505 #[inline(always)] // yes this helps in our benchmarks
506 fn retag_ptr_inplace(
507 &mut self,
508 place: &PlaceTy<'tcx>,
509 new_perm: Option<NewPermission>,
510 ) -> InterpResult<'tcx> {
511 if let Some(new_perm) = new_perm {
512 let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
513 let val = self.ecx.tb_retag_reference(&val, new_perm)?;
514 self.ecx.write_immediate(*val, place)?;
515 }
516 interp_ok(())
517 }
518 }
519 impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> {
520 type V = PlaceTy<'tcx>;
521
522 #[inline(always)]
523 fn ecx(&self) -> &MiriInterpCx<'tcx> {
524 self.ecx
525 }
526
527 /// Regardless of how `Unique` is handled, Boxes are always reborrowed.
528 /// When `Unique` is also reborrowed, then it behaves exactly like `Box`
529 /// except for the fact that `Box` has a non-zero-sized reborrow.
530 fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
531 // Only boxes for the global allocator get any special treatment.
532 if box_ty.is_box_global(*self.ecx.tcx) {
533 let new_perm =
534 NewPermission::from_unique_ty(place.layout.ty, self.kind, self.ecx);
535 self.retag_ptr_inplace(place, new_perm)?;
536 }
537 interp_ok(())
538 }
539
540 fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
541 // If this place is smaller than a pointer, we know that it can't contain any
542 // pointers we need to retag, so we can stop recursion early.
543 // This optimization is crucial for ZSTs, because they can contain way more fields
544 // than we can ever visit.
545 if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
546 return interp_ok(());
547 }
548
549 // Check the type of this value to see what to do with it (retag, or recurse).
550 match place.layout.ty.kind() {
551 &ty::Ref(_, pointee, mutability) => {
552 let new_perm =
553 NewPermission::from_ref_ty(pointee, mutability, self.kind, self.ecx);
554 self.retag_ptr_inplace(place, new_perm)?;
555 }
556 ty::RawPtr(_, _) => {
557 // We definitely do *not* want to recurse into raw pointers -- wide raw
558 // pointers have fields, and for dyn Trait pointees those can have reference
559 // type!
560 // We also do not want to reborrow them.
561 }
562 ty::Adt(adt, _) if adt.is_box() => {
563 // Recurse for boxes, they require some tricky handling and will end up in `visit_box` above.
564 // (Yes this means we technically also recursively retag the allocator itself
565 // even if field retagging is not enabled. *shrug*)
566 self.walk_value(place)?;
567 }
568 _ => {
569 // Not a reference/pointer/box. Only recurse if configured appropriately.
570 let recurse = match self.retag_fields {
571 RetagFields::No => false,
572 RetagFields::Yes => true,
573 RetagFields::OnlyScalar => {
574 // Matching `ArgAbi::new` at the time of writing, only fields of
575 // `Scalar` and `ScalarPair` ABI are considered.
576 matches!(
577 place.layout.backend_repr,
578 BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)
579 )
580 }
581 };
582 if recurse {
583 self.walk_value(place)?;
584 }
585 }
586 }
587 interp_ok(())
588 }
589 }
590 }
591
592 /// Protect a place so that it cannot be used any more for the duration of the current function
593 /// call.
594 ///
595 /// This is used to ensure soundness of in-place function argument/return passing.
596 fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
597 let this = self.eval_context_mut();
598
599 // Retag it. With protection! That is the entire point.
600 let new_perm = NewPermission {
601 // Note: If we are creating a protected Reserved, which can
602 // never be ReservedIM, the value of the `ty_is_freeze`
603 // argument doesn't matter
604 // (`ty_is_freeze || true` in `new_reserved` will always be `true`).
605 freeze_perm: Permission::new_reserved(
606 /* ty_is_freeze */ true, /* protected */ true,
607 ),
608 freeze_access: true,
609 nonfreeze_perm: Permission::new_reserved(
610 /* ty_is_freeze */ false, /* protected */ true,
611 ),
612 nonfreeze_access: true,
613 protector: Some(ProtectorKind::StrongProtector),
614 };
615 this.tb_retag_place(place, new_perm)
616 }
617
618 /// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
619 fn tb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
620 let this = self.eval_context_ref();
621
622 // Function pointers and dead objects don't have an alloc_extra so we ignore them.
623 // This is okay because accessing them is UB anyway, no need for any Tree Borrows checks.
624 // NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
625 let kind = this.get_alloc_info(alloc_id).kind;
626 match kind {
627 AllocKind::LiveData => {
628 // This should have alloc_extra data, but `get_alloc_extra` can still fail
629 // if converting this alloc_id from a global to a local one
630 // uncovers a non-supported `extern static`.
631 let alloc_extra = this.get_alloc_extra(alloc_id)?;
632 trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
633 alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag);
634 }
635 AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
636 // No tree borrows on these allocations.
637 }
638 }
639 interp_ok(())
640 }
641
642 /// Display the tree.
643 fn print_tree(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
644 let this = self.eval_context_mut();
645 let alloc_extra = this.get_alloc_extra(alloc_id)?;
646 let tree_borrows = alloc_extra.borrow_tracker_tb().borrow();
647 let borrow_tracker = &this.machine.borrow_tracker.as_ref().unwrap().borrow();
648 tree_borrows.print_tree(&borrow_tracker.protected_tags, show_unnamed)
649 }
650
651 /// Give a name to the pointer, usually the name it has in the source code (for debugging).
652 /// The name given is `name` and the pointer that receives it is the `nth_parent`
653 /// of `ptr` (with 0 representing `ptr` itself)
654 fn tb_give_pointer_debug_name(
655 &mut self,
656 ptr: Pointer,
657 nth_parent: u8,
658 name: &str,
659 ) -> InterpResult<'tcx> {
660 let this = self.eval_context_mut();
661 let (tag, alloc_id) = match ptr.provenance {
662 Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id),
663 _ => {
664 eprintln!("Can't give the name {name} to Wildcard pointer");
665 return interp_ok(());
666 }
667 };
668 let alloc_extra = this.get_alloc_extra(alloc_id)?;
669 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
670 tree_borrows.give_pointer_debug_name(tag, nth_parent, name)
671 }
672}