miri/borrow_tracker/tree_borrows/
mod.rs1use rustc_abi::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::{AccessKind, GlobalState, GlobalStateInner, ProtectorKind};
9use crate::concurrency::data_race::NaReadType;
10use crate::*;
11
12pub mod diagnostics;
13mod foreign_access_skipping;
14mod perms;
15mod tree;
16mod tree_visitor;
17mod unimap;
18mod wildcard;
19
20#[cfg(test)]
21mod exhaustive;
22
23use self::perms::Permission;
24pub use self::tree::Tree;
25
26pub type AllocState = Tree;
27
28impl<'tcx> Tree {
29 pub fn new_allocation(
31 id: AllocId,
32 size: Size,
33 state: &mut GlobalStateInner,
34 _kind: MemoryKind,
35 machine: &MiriMachine<'tcx>,
36 ) -> Self {
37 let tag = state.root_ptr_tag(id, machine); let span = machine.current_user_relevant_span();
39 Tree::new(tag, size, span)
40 }
41
42 pub fn before_memory_access(
45 &mut self,
46 access_kind: AccessKind,
47 alloc_id: AllocId,
48 prov: ProvenanceExtra,
49 range: AllocRange,
50 machine: &MiriMachine<'tcx>,
51 ) -> InterpResult<'tcx> {
52 trace!(
53 "{} with tag {:?}: {:?}, size {}",
54 access_kind,
55 prov,
56 interpret::Pointer::new(alloc_id, range.start),
57 range.size.bytes(),
58 );
59 let global = machine.borrow_tracker.as_ref().unwrap();
60 let span = machine.current_user_relevant_span();
61 self.perform_access(
62 prov,
63 range,
64 access_kind,
65 diagnostics::AccessCause::Explicit(access_kind),
66 global,
67 alloc_id,
68 span,
69 )
70 }
71
72 pub fn before_memory_deallocation(
74 &mut self,
75 alloc_id: AllocId,
76 prov: ProvenanceExtra,
77 size: Size,
78 machine: &MiriMachine<'tcx>,
79 ) -> InterpResult<'tcx> {
80 let global = machine.borrow_tracker.as_ref().unwrap();
81 let span = machine.current_user_relevant_span();
82 self.dealloc(prov, alloc_range(Size::ZERO, size), global, alloc_id, span)
83 }
84
85 pub fn release_protector(
92 &mut self,
93 machine: &MiriMachine<'tcx>,
94 global: &GlobalState,
95 tag: BorTag,
96 alloc_id: AllocId, ) -> InterpResult<'tcx> {
98 let span = machine.current_user_relevant_span();
99 self.perform_protector_end_access(tag, global, alloc_id, span)?;
100
101 self.update_exposure_for_protector_release(tag);
102
103 interp_ok(())
104 }
105}
106
107#[derive(Debug, Clone, Copy)]
109pub struct NewPermission {
110 freeze_perm: Permission,
112 freeze_access: bool,
114 nonfreeze_perm: Permission,
116 nonfreeze_access: bool,
119 outside_perm: Permission,
121 protector: Option<ProtectorKind>,
124}
125
126impl<'tcx> NewPermission {
127 fn new(
131 pointee: Ty<'tcx>,
132 ref_mutability: Option<Mutability>,
133 retag_kind: RetagKind,
134 cx: &crate::MiriInterpCx<'tcx>,
135 ) -> Option<Self> {
136 let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env())
137 && pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env());
138 let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
139 let is_protected = retag_kind == RetagKind::FnEntry;
140
141 if matches!(ref_mutability, Some(Mutability::Mut) | None if !ty_is_unpin) {
142 return None;
145 }
146
147 let freeze_perm = match ref_mutability {
148 Some(Mutability::Not) => Permission::new_frozen(),
150 _ => Permission::new_reserved_frz(),
152 };
153 let nonfreeze_perm = match ref_mutability {
154 Some(Mutability::Not) => Permission::new_cell(),
156 _ if is_protected => Permission::new_reserved_frz(),
158 _ => Permission::new_reserved_im(),
160 };
161
162 let initial_access = |perm: &Permission| !perm.is_cell();
164
165 Some(NewPermission {
166 freeze_perm,
167 freeze_access: initial_access(&freeze_perm),
168 nonfreeze_perm,
169 nonfreeze_access: initial_access(&nonfreeze_perm),
170 outside_perm: if ty_is_freeze { freeze_perm } else { nonfreeze_perm },
171 protector: is_protected.then_some(if ref_mutability.is_some() {
172 ProtectorKind::StrongProtector
174 } else {
175 ProtectorKind::WeakProtector
177 }),
178 })
179 }
180}
181
182impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
186trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
187 fn tb_reborrow(
189 &mut self,
190 place: &MPlaceTy<'tcx>, ptr_size: Size,
192 new_perm: NewPermission,
193 new_tag: BorTag,
194 ) -> InterpResult<'tcx, Option<Provenance>> {
195 let this = self.eval_context_mut();
196 this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable)?;
198
199 let log_creation = |this: &MiriInterpCx<'tcx>,
201 loc: Option<(AllocId, Size, ProvenanceExtra)>| -> InterpResult<'tcx> {
203 let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
204 let ty = place.layout.ty;
205 if global.tracked_pointer_tags.contains(&new_tag) {
206 let ty_is_freeze = ty.is_freeze(*this.tcx, this.typing_env());
207 let kind_str =
208 if ty_is_freeze {
209 format!("initial state {} (pointee type {ty})", new_perm.freeze_perm)
210 } else {
211 format!("initial state {}/{} outside/inside UnsafeCell (pointee type {ty})", new_perm.freeze_perm, new_perm.nonfreeze_perm)
212 };
213 this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
214 new_tag.inner(),
215 Some(kind_str),
216 loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, ptr_size), orig_tag)),
217 ));
218 }
219 drop(global); interp_ok(())
221 };
222
223 trace!("Reborrow of size {:?}", ptr_size);
224 let Ok((alloc_id, base_offset, parent_prov)) = this.ptr_try_get_alloc_id(place.ptr(), 0)
227 else {
228 assert_eq!(ptr_size, Size::ZERO); let new_prov = place.ptr().provenance;
232 trace!("reborrow of size 0: reusing {:?} (pointee {})", place.ptr(), place.layout.ty,);
233 log_creation(this, None)?;
234 return interp_ok(new_prov);
236 };
237 let new_prov = Provenance::Concrete { alloc_id, tag: new_tag };
238
239 log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
240
241 trace!(
242 "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
243 new_tag,
244 parent_prov,
245 place.layout.ty,
246 interpret::Pointer::new(alloc_id, base_offset),
247 ptr_size.bytes()
248 );
249
250 if let Some(protect) = new_perm.protector {
251 this.frame_mut()
255 .extra
256 .borrow_tracker
257 .as_mut()
258 .unwrap()
259 .protected_tags
260 .push((alloc_id, new_tag));
261 this.machine
262 .borrow_tracker
263 .as_mut()
264 .expect("We should have borrow tracking data")
265 .get_mut()
266 .protected_tags
267 .insert(new_tag, protect);
268 }
269
270 let alloc_kind = this.get_alloc_info(alloc_id).kind;
271 if !matches!(alloc_kind, AllocKind::LiveData) {
272 assert_eq!(ptr_size, Size::ZERO); return interp_ok(Some(new_prov));
276 }
277
278 let protected = new_perm.protector.is_some();
279 let precise_interior_mut = this
280 .machine
281 .borrow_tracker
282 .as_mut()
283 .unwrap()
284 .get_mut()
285 .borrow_tracker_method
286 .get_tree_borrows_params()
287 .precise_interior_mut;
288
289 let loc_state = |frozen: bool| -> LocationState {
291 let (perm, access) = if frozen {
292 (new_perm.freeze_perm, new_perm.freeze_access)
293 } else {
294 (new_perm.nonfreeze_perm, new_perm.nonfreeze_access)
295 };
296 let sifa = perm.strongest_idempotent_foreign_access(protected);
297 if access {
298 LocationState::new_accessed(perm, sifa)
299 } else {
300 LocationState::new_non_accessed(perm, sifa)
301 }
302 };
303 let inside_perms = if !precise_interior_mut {
304 let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env());
306 let state = loc_state(ty_is_freeze);
307 DedupRangeMap::new(ptr_size, state)
308 } else {
309 let mut perms_map: DedupRangeMap<LocationState> = DedupRangeMap::new(
311 ptr_size,
312 LocationState::new_accessed(
313 Permission::new_disabled(),
314 IdempotentForeignAccess::None,
315 ),
316 );
317 this.visit_freeze_sensitive(place, ptr_size, |range, frozen| {
318 let state = loc_state(frozen);
319 for (_loc_range, loc) in perms_map.iter_mut(range.start, range.size) {
320 *loc = state;
321 }
322 interp_ok(())
323 })?;
324 perms_map
325 };
326
327 let alloc_extra = this.get_alloc_extra(alloc_id)?;
328 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
329
330 for (perm_range, perm) in inside_perms.iter_all() {
331 if perm.accessed() {
332 let range_in_alloc = AllocRange {
335 start: Size::from_bytes(perm_range.start) + base_offset,
336 size: Size::from_bytes(perm_range.end - perm_range.start),
337 };
338
339 tree_borrows.perform_access(
340 parent_prov,
341 range_in_alloc,
342 AccessKind::Read,
343 diagnostics::AccessCause::Reborrow,
344 this.machine.borrow_tracker.as_ref().unwrap(),
345 alloc_id,
346 this.machine.current_user_relevant_span(),
347 )?;
348
349 if range_in_alloc.size.bytes() > 0 {
351 if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() {
352 data_race.read_non_atomic(
353 alloc_id,
354 range_in_alloc,
355 NaReadType::Retag,
356 Some(place.layout.ty),
357 &this.machine,
358 )?
359 }
360 }
361 }
362 }
363 tree_borrows.new_child(
365 base_offset,
366 parent_prov,
367 new_tag,
368 inside_perms,
369 new_perm.outside_perm,
370 protected,
371 this.machine.current_user_relevant_span(),
372 )?;
373 drop(tree_borrows);
374
375 interp_ok(Some(new_prov))
376 }
377
378 fn tb_retag_place(
379 &mut self,
380 place: &MPlaceTy<'tcx>,
381 new_perm: NewPermission,
382 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
383 let this = self.eval_context_mut();
384
385 let reborrow_size =
391 this.size_and_align_of_val(place)?.map(|(size, _)| size).unwrap_or(place.layout.size);
392 trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size);
393
394 let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
401
402 let new_prov = this.tb_reborrow(place, reborrow_size, new_perm, new_tag)?;
404
405 interp_ok(place.clone().map_provenance(|_| new_prov.unwrap()))
409 }
410
411 fn tb_retag_reference(
413 &mut self,
414 val: &ImmTy<'tcx>,
415 new_perm: NewPermission,
416 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
417 let this = self.eval_context_mut();
418 let place = this.ref_to_mplace(val)?;
419 let new_place = this.tb_retag_place(&place, new_perm)?;
420 interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
421 }
422}
423
424impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
425pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
426 fn tb_retag_ptr_value(
429 &mut self,
430 kind: RetagKind,
431 val: &ImmTy<'tcx>,
432 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
433 let this = self.eval_context_mut();
434 let new_perm = match val.layout.ty.kind() {
435 &ty::Ref(_, pointee, mutability) =>
436 NewPermission::new(pointee, Some(mutability), kind, this),
437 _ => None,
438 };
439 if let Some(new_perm) = new_perm {
440 this.tb_retag_reference(val, new_perm)
441 } else {
442 interp_ok(val.clone())
443 }
444 }
445
446 fn tb_retag_place_contents(
448 &mut self,
449 kind: RetagKind,
450 place: &PlaceTy<'tcx>,
451 ) -> InterpResult<'tcx> {
452 let this = self.eval_context_mut();
453 let mut visitor = RetagVisitor { ecx: this, kind };
454 return visitor.visit_value(place);
455
456 struct RetagVisitor<'ecx, 'tcx> {
458 ecx: &'ecx mut MiriInterpCx<'tcx>,
459 kind: RetagKind,
460 }
461 impl<'ecx, 'tcx> RetagVisitor<'ecx, 'tcx> {
462 #[inline(always)] fn retag_ptr_inplace(
464 &mut self,
465 place: &PlaceTy<'tcx>,
466 new_perm: Option<NewPermission>,
467 ) -> InterpResult<'tcx> {
468 if let Some(new_perm) = new_perm {
469 let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
470 let val = self.ecx.tb_retag_reference(&val, new_perm)?;
471 self.ecx.write_immediate(*val, place)?;
472 }
473 interp_ok(())
474 }
475 }
476 impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> {
477 type V = PlaceTy<'tcx>;
478
479 #[inline(always)]
480 fn ecx(&self) -> &MiriInterpCx<'tcx> {
481 self.ecx
482 }
483
484 fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
488 if box_ty.is_box_global(*self.ecx.tcx) {
490 let pointee = place.layout.ty.builtin_deref(true).unwrap();
491 let new_perm =
492 NewPermission::new(pointee, None, self.kind, self.ecx);
493 self.retag_ptr_inplace(place, new_perm)?;
494 }
495 interp_ok(())
496 }
497
498 fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
499 if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
504 return interp_ok(());
505 }
506
507 match place.layout.ty.kind() {
509 &ty::Ref(_, pointee, mutability) => {
510 let new_perm =
511 NewPermission::new(pointee, Some(mutability), self.kind, self.ecx);
512 self.retag_ptr_inplace(place, new_perm)?;
513 }
514 ty::RawPtr(_, _) => {
515 }
520 ty::Adt(adt, _) if adt.is_box() => {
521 self.walk_value(place)?;
525 }
526 _ => {
527 self.walk_value(place)?;
529 }
530 }
531 interp_ok(())
532 }
533 }
534 }
535
536 fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
541 let this = self.eval_context_mut();
542
543 let new_perm = NewPermission {
545 freeze_perm: Permission::new_reserved_frz(),
550 freeze_access: true,
551 nonfreeze_perm: Permission::new_reserved_frz(),
552 nonfreeze_access: true,
553 outside_perm: Permission::new_reserved_frz(),
554 protector: Some(ProtectorKind::StrongProtector),
555 };
556 this.tb_retag_place(place, new_perm)
557 }
558
559 fn tb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
561 let this = self.eval_context_ref();
562
563 let kind = this.get_alloc_info(alloc_id).kind;
567 match kind {
568 AllocKind::LiveData => {
569 let alloc_extra = this.get_alloc_extra(alloc_id)?;
573 trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
574
575 let global = this.machine.borrow_tracker.as_ref().unwrap();
576 let protected_tags = &global.borrow().protected_tags;
577 let protected = protected_tags.contains_key(&tag);
578 alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag, protected);
579 }
580 AllocKind::Function
581 | AllocKind::VTable
582 | AllocKind::TypeId
583 | AllocKind::Dead
584 | AllocKind::VaList => {
585 }
587 }
588 interp_ok(())
589 }
590
591 fn print_tree(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
593 let this = self.eval_context_mut();
594 let alloc_extra = this.get_alloc_extra(alloc_id)?;
595 let tree_borrows = alloc_extra.borrow_tracker_tb().borrow();
596 let borrow_tracker = &this.machine.borrow_tracker.as_ref().unwrap().borrow();
597 tree_borrows.print_tree(&borrow_tracker.protected_tags, show_unnamed)
598 }
599
600 fn tb_give_pointer_debug_name(
604 &mut self,
605 ptr: Pointer,
606 nth_parent: u8,
607 name: &str,
608 ) -> InterpResult<'tcx> {
609 let this = self.eval_context_mut();
610 let (tag, alloc_id) = match ptr.provenance {
611 Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id),
612 Some(Provenance::Wildcard) => {
613 eprintln!("Can't give the name {name} to wildcard pointer");
614 return interp_ok(());
615 }
616 None => {
617 eprintln!("Can't give the name {name} to pointer without provenance");
618 return interp_ok(());
619 }
620 };
621 let alloc_extra = this.get_alloc_extra(alloc_id)?;
622 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
623 tree_borrows.give_pointer_debug_name(tag, nth_parent, name)
624 }
625}