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 Some((range, access_kind, diagnostics::AccessCause::Explicit(access_kind))),
64 global,
65 alloc_id,
66 span,
67 )
68 }
69
70 pub fn before_memory_deallocation(
72 &mut self,
73 alloc_id: AllocId,
74 prov: ProvenanceExtra,
75 size: Size,
76 machine: &MiriMachine<'tcx>,
77 ) -> InterpResult<'tcx> {
78 let global = machine.borrow_tracker.as_ref().unwrap();
79 let span = machine.current_user_relevant_span();
80 self.dealloc(prov, alloc_range(Size::ZERO, size), global, alloc_id, span)
81 }
82
83 pub fn release_protector(
90 &mut self,
91 machine: &MiriMachine<'tcx>,
92 global: &GlobalState,
93 tag: BorTag,
94 alloc_id: AllocId, ) -> InterpResult<'tcx> {
96 let span = machine.current_user_relevant_span();
97 self.perform_access(ProvenanceExtra::Concrete(tag), None, global, alloc_id, span)?;
99
100 self.update_exposure_for_protector_release(tag);
101
102 interp_ok(())
103 }
104}
105
106#[derive(Debug, Clone, Copy)]
108pub struct NewPermission {
109 freeze_perm: Permission,
111 freeze_access: bool,
113 nonfreeze_perm: Permission,
115 nonfreeze_access: bool,
118 outside_perm: Permission,
120 protector: Option<ProtectorKind>,
123}
124
125impl<'tcx> NewPermission {
126 fn new(
130 pointee: Ty<'tcx>,
131 ref_mutability: Option<Mutability>,
132 retag_kind: RetagKind,
133 cx: &crate::MiriInterpCx<'tcx>,
134 ) -> Option<Self> {
135 let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env());
136 let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
137 let is_protected = retag_kind == RetagKind::FnEntry;
138
139 if matches!(ref_mutability, Some(Mutability::Mut) | None if !ty_is_unpin) {
140 return None;
143 }
144
145 let freeze_perm = match ref_mutability {
146 Some(Mutability::Not) => Permission::new_frozen(),
148 _ => Permission::new_reserved_frz(),
150 };
151 let nonfreeze_perm = match ref_mutability {
152 Some(Mutability::Not) => Permission::new_cell(),
154 _ if is_protected => Permission::new_reserved_frz(),
156 _ => Permission::new_reserved_im(),
158 };
159
160 let initial_access = |perm: &Permission| !perm.is_cell();
162
163 Some(NewPermission {
164 freeze_perm,
165 freeze_access: initial_access(&freeze_perm),
166 nonfreeze_perm,
167 nonfreeze_access: initial_access(&nonfreeze_perm),
168 outside_perm: if ty_is_freeze { freeze_perm } else { nonfreeze_perm },
169 protector: is_protected.then_some(if ref_mutability.is_some() {
170 ProtectorKind::StrongProtector
172 } else {
173 ProtectorKind::WeakProtector
175 }),
176 })
177 }
178}
179
180impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
184trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
185 fn tb_reborrow(
187 &mut self,
188 place: &MPlaceTy<'tcx>, ptr_size: Size,
190 new_perm: NewPermission,
191 new_tag: BorTag,
192 ) -> InterpResult<'tcx, Option<Provenance>> {
193 let this = self.eval_context_mut();
194 this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable)?;
196
197 let log_creation = |this: &MiriInterpCx<'tcx>,
199 loc: Option<(AllocId, Size, ProvenanceExtra)>| -> InterpResult<'tcx> {
201 let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
202 let ty = place.layout.ty;
203 if global.tracked_pointer_tags.contains(&new_tag) {
204 let ty_is_freeze = ty.is_freeze(*this.tcx, this.typing_env());
205 let kind_str =
206 if ty_is_freeze {
207 format!("initial state {} (pointee type {ty})", new_perm.freeze_perm)
208 } else {
209 format!("initial state {}/{} outside/inside UnsafeCell (pointee type {ty})", new_perm.freeze_perm, new_perm.nonfreeze_perm)
210 };
211 this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
212 new_tag.inner(),
213 Some(kind_str),
214 loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, ptr_size), orig_tag)),
215 ));
216 }
217 drop(global); interp_ok(())
219 };
220
221 trace!("Reborrow of size {:?}", ptr_size);
222 let (alloc_id, base_offset, parent_prov) = match this.ptr_try_get_alloc_id(place.ptr(), 0) {
223 Ok(data) => {
224 data
227 }
228 Err(_) => {
229 assert_eq!(ptr_size, Size::ZERO); let new_prov = place.ptr().provenance;
233 trace!(
234 "reborrow of size 0: reusing {:?} (pointee {})",
235 place.ptr(),
236 place.layout.ty,
237 );
238 log_creation(this, None)?;
239 return interp_ok(new_prov);
241 }
242 };
243 let new_prov = Provenance::Concrete { alloc_id, tag: new_tag };
244
245 log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
246
247 trace!(
248 "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
249 new_tag,
250 parent_prov,
251 place.layout.ty,
252 interpret::Pointer::new(alloc_id, base_offset),
253 ptr_size.bytes()
254 );
255
256 if let Some(protect) = new_perm.protector {
257 this.frame_mut()
261 .extra
262 .borrow_tracker
263 .as_mut()
264 .unwrap()
265 .protected_tags
266 .push((alloc_id, new_tag));
267 this.machine
268 .borrow_tracker
269 .as_mut()
270 .expect("We should have borrow tracking data")
271 .get_mut()
272 .protected_tags
273 .insert(new_tag, protect);
274 }
275
276 let alloc_kind = this.get_alloc_info(alloc_id).kind;
277 if !matches!(alloc_kind, AllocKind::LiveData) {
278 assert_eq!(ptr_size, Size::ZERO); return interp_ok(Some(new_prov));
282 }
283
284 let protected = new_perm.protector.is_some();
285 let precise_interior_mut = this
286 .machine
287 .borrow_tracker
288 .as_mut()
289 .unwrap()
290 .get_mut()
291 .borrow_tracker_method
292 .get_tree_borrows_params()
293 .precise_interior_mut;
294
295 let loc_state = |frozen: bool| -> LocationState {
297 let (perm, access) = if frozen {
298 (new_perm.freeze_perm, new_perm.freeze_access)
299 } else {
300 (new_perm.nonfreeze_perm, new_perm.nonfreeze_access)
301 };
302 let sifa = perm.strongest_idempotent_foreign_access(protected);
303 if access {
304 LocationState::new_accessed(perm, sifa)
305 } else {
306 LocationState::new_non_accessed(perm, sifa)
307 }
308 };
309 let inside_perms = if !precise_interior_mut {
310 let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env());
312 let state = loc_state(ty_is_freeze);
313 DedupRangeMap::new(ptr_size, state)
314 } else {
315 let mut perms_map: DedupRangeMap<LocationState> = DedupRangeMap::new(
317 ptr_size,
318 LocationState::new_accessed(
319 Permission::new_disabled(),
320 IdempotentForeignAccess::None,
321 ),
322 );
323 this.visit_freeze_sensitive(place, ptr_size, |range, frozen| {
324 let state = loc_state(frozen);
325 for (_loc_range, loc) in perms_map.iter_mut(range.start, range.size) {
326 *loc = state;
327 }
328 interp_ok(())
329 })?;
330 perms_map
331 };
332
333 let alloc_extra = this.get_alloc_extra(alloc_id)?;
334 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
335
336 for (perm_range, perm) in inside_perms.iter_all() {
337 if perm.accessed() {
338 let range_in_alloc = AllocRange {
341 start: Size::from_bytes(perm_range.start) + base_offset,
342 size: Size::from_bytes(perm_range.end - perm_range.start),
343 };
344
345 tree_borrows.perform_access(
346 parent_prov,
347 Some((range_in_alloc, AccessKind::Read, diagnostics::AccessCause::Reborrow)),
348 this.machine.borrow_tracker.as_ref().unwrap(),
349 alloc_id,
350 this.machine.current_user_relevant_span(),
351 )?;
352
353 if range_in_alloc.size.bytes() > 0 {
355 if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() {
356 data_race.read_non_atomic(
357 alloc_id,
358 range_in_alloc,
359 NaReadType::Retag,
360 Some(place.layout.ty),
361 &this.machine,
362 )?
363 }
364 }
365 }
366 }
367 tree_borrows.new_child(
369 base_offset,
370 parent_prov,
371 new_tag,
372 inside_perms,
373 new_perm.outside_perm,
374 protected,
375 this.machine.current_user_relevant_span(),
376 )?;
377 drop(tree_borrows);
378
379 interp_ok(Some(new_prov))
380 }
381
382 fn tb_retag_place(
383 &mut self,
384 place: &MPlaceTy<'tcx>,
385 new_perm: NewPermission,
386 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
387 let this = self.eval_context_mut();
388
389 let reborrow_size =
395 this.size_and_align_of_val(place)?.map(|(size, _)| size).unwrap_or(place.layout.size);
396 trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size);
397
398 let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
405
406 let new_prov = this.tb_reborrow(place, reborrow_size, new_perm, new_tag)?;
408
409 interp_ok(place.clone().map_provenance(|_| new_prov.unwrap()))
413 }
414
415 fn tb_retag_reference(
417 &mut self,
418 val: &ImmTy<'tcx>,
419 new_perm: NewPermission,
420 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
421 let this = self.eval_context_mut();
422 let place = this.ref_to_mplace(val)?;
423 let new_place = this.tb_retag_place(&place, new_perm)?;
424 interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
425 }
426}
427
428impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
429pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
430 fn tb_retag_ptr_value(
433 &mut self,
434 kind: RetagKind,
435 val: &ImmTy<'tcx>,
436 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
437 let this = self.eval_context_mut();
438 let new_perm = match val.layout.ty.kind() {
439 &ty::Ref(_, pointee, mutability) =>
440 NewPermission::new(pointee, Some(mutability), kind, this),
441 _ => None,
442 };
443 if let Some(new_perm) = new_perm {
444 this.tb_retag_reference(val, new_perm)
445 } else {
446 interp_ok(val.clone())
447 }
448 }
449
450 fn tb_retag_place_contents(
452 &mut self,
453 kind: RetagKind,
454 place: &PlaceTy<'tcx>,
455 ) -> InterpResult<'tcx> {
456 let this = self.eval_context_mut();
457 let mut visitor = RetagVisitor { ecx: this, kind };
458 return visitor.visit_value(place);
459
460 struct RetagVisitor<'ecx, 'tcx> {
462 ecx: &'ecx mut MiriInterpCx<'tcx>,
463 kind: RetagKind,
464 }
465 impl<'ecx, 'tcx> RetagVisitor<'ecx, 'tcx> {
466 #[inline(always)] fn retag_ptr_inplace(
468 &mut self,
469 place: &PlaceTy<'tcx>,
470 new_perm: Option<NewPermission>,
471 ) -> InterpResult<'tcx> {
472 if let Some(new_perm) = new_perm {
473 let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
474 let val = self.ecx.tb_retag_reference(&val, new_perm)?;
475 self.ecx.write_immediate(*val, place)?;
476 }
477 interp_ok(())
478 }
479 }
480 impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> {
481 type V = PlaceTy<'tcx>;
482
483 #[inline(always)]
484 fn ecx(&self) -> &MiriInterpCx<'tcx> {
485 self.ecx
486 }
487
488 fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
492 if box_ty.is_box_global(*self.ecx.tcx) {
494 let pointee = place.layout.ty.builtin_deref(true).unwrap();
495 let new_perm =
496 NewPermission::new(pointee, None, self.kind, self.ecx);
497 self.retag_ptr_inplace(place, new_perm)?;
498 }
499 interp_ok(())
500 }
501
502 fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
503 if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
508 return interp_ok(());
509 }
510
511 match place.layout.ty.kind() {
513 &ty::Ref(_, pointee, mutability) => {
514 let new_perm =
515 NewPermission::new(pointee, Some(mutability), self.kind, self.ecx);
516 self.retag_ptr_inplace(place, new_perm)?;
517 }
518 ty::RawPtr(_, _) => {
519 }
524 ty::Adt(adt, _) if adt.is_box() => {
525 self.walk_value(place)?;
529 }
530 _ => {
531 self.walk_value(place)?;
533 }
534 }
535 interp_ok(())
536 }
537 }
538 }
539
540 fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
545 let this = self.eval_context_mut();
546
547 let new_perm = NewPermission {
549 freeze_perm: Permission::new_reserved_frz(),
554 freeze_access: true,
555 nonfreeze_perm: Permission::new_reserved_frz(),
556 nonfreeze_access: true,
557 outside_perm: Permission::new_reserved_frz(),
558 protector: Some(ProtectorKind::StrongProtector),
559 };
560 this.tb_retag_place(place, new_perm)
561 }
562
563 fn tb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
565 let this = self.eval_context_ref();
566
567 let kind = this.get_alloc_info(alloc_id).kind;
571 match kind {
572 AllocKind::LiveData => {
573 let alloc_extra = this.get_alloc_extra(alloc_id)?;
577 trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
578
579 let global = this.machine.borrow_tracker.as_ref().unwrap();
580 let protected_tags = &global.borrow().protected_tags;
581 let protected = protected_tags.contains_key(&tag);
582 alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag, protected);
583 }
584 AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => {
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 _ => {
613 eprintln!("Can't give the name {name} to Wildcard pointer");
614 return interp_ok(());
615 }
616 };
617 let alloc_extra = this.get_alloc_extra(alloc_id)?;
618 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
619 tree_borrows.give_pointer_debug_name(tag, nth_parent, name)
620 }
621}