miri/borrow_tracker/tree_borrows/
mod.rs1use rustc_abi::{BackendRepr, Size};
2use rustc_middle::mir::{Mutability, RetagKind};
3use rustc_middle::ty::layout::HasTypingEnv;
4use rustc_middle::ty::{self, Ty};
5
6use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind};
7use crate::concurrency::data_race::NaReadType;
8use crate::*;
9
10pub mod diagnostics;
11mod foreign_access_skipping;
12mod perms;
13mod tree;
14mod unimap;
15
16#[cfg(test)]
17mod exhaustive;
18
19use self::perms::Permission;
20pub use self::tree::Tree;
21
22pub type AllocState = Tree;
23
24impl<'tcx> Tree {
25 pub fn new_allocation(
27 id: AllocId,
28 size: Size,
29 state: &mut GlobalStateInner,
30 _kind: MemoryKind,
31 machine: &MiriMachine<'tcx>,
32 ) -> Self {
33 let tag = state.root_ptr_tag(id, machine); let span = machine.current_span();
35 Tree::new(tag, size, span)
36 }
37
38 pub fn before_memory_access(
41 &mut self,
42 access_kind: AccessKind,
43 alloc_id: AllocId,
44 prov: ProvenanceExtra,
45 range: AllocRange,
46 machine: &MiriMachine<'tcx>,
47 ) -> InterpResult<'tcx> {
48 trace!(
49 "{} with tag {:?}: {:?}, size {}",
50 access_kind,
51 prov,
52 interpret::Pointer::new(alloc_id, range.start),
53 range.size.bytes(),
54 );
55 let tag = match prov {
58 ProvenanceExtra::Concrete(tag) => tag,
59 ProvenanceExtra::Wildcard => return interp_ok(()),
60 };
61 let global = machine.borrow_tracker.as_ref().unwrap();
62 let span = machine.current_span();
63 self.perform_access(
64 tag,
65 Some((range, access_kind, 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 tag = match prov {
83 ProvenanceExtra::Concrete(tag) => tag,
84 ProvenanceExtra::Wildcard => return interp_ok(()),
85 };
86 let global = machine.borrow_tracker.as_ref().unwrap();
87 let span = machine.current_span();
88 self.dealloc(tag, alloc_range(Size::ZERO, size), global, alloc_id, span)
89 }
90
91 pub fn expose_tag(&mut self, _tag: BorTag) {
92 }
94
95 pub fn release_protector(
102 &mut self,
103 machine: &MiriMachine<'tcx>,
104 global: &GlobalState,
105 tag: BorTag,
106 alloc_id: AllocId, ) -> InterpResult<'tcx> {
108 let span = machine.current_span();
109 self.perform_access(tag, None, global, alloc_id, span)
111 }
112}
113
114#[derive(Debug, Clone, Copy)]
116struct NewPermission {
117 initial_state: Permission,
119 protector: Option<ProtectorKind>,
122 initial_read: bool,
126}
127
128impl<'tcx> NewPermission {
129 fn from_ref_ty(
131 pointee: Ty<'tcx>,
132 mutability: Mutability,
133 kind: RetagKind,
134 cx: &crate::MiriInterpCx<'tcx>,
135 ) -> Option<Self> {
136 let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
137 let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env());
138 let is_protected = kind == RetagKind::FnEntry;
139 let (initial_state, initial_read) = match mutability {
145 Mutability::Mut if ty_is_unpin =>
146 (Permission::new_reserved(ty_is_freeze, is_protected), true),
147 Mutability::Not if ty_is_freeze => (Permission::new_frozen(), true),
148 Mutability::Not if !ty_is_freeze => (Permission::new_cell(), false),
149 _ => return None,
153 };
154
155 let protector = is_protected.then_some(ProtectorKind::StrongProtector);
156 Some(Self { initial_state, protector, initial_read })
157 }
158
159 fn from_unique_ty(
163 ty: Ty<'tcx>,
164 kind: RetagKind,
165 cx: &crate::MiriInterpCx<'tcx>,
166 ) -> Option<Self> {
167 let pointee = ty.builtin_deref(true).unwrap();
168 pointee.is_unpin(*cx.tcx, cx.typing_env()).then_some(()).map(|()| {
169 let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
172 let protected = kind == RetagKind::FnEntry;
173 let initial_state = Permission::new_reserved(ty_is_freeze, protected);
174 Self {
175 initial_state,
176 protector: protected.then_some(ProtectorKind::WeakProtector),
177 initial_read: true,
178 }
179 })
180 }
181}
182
183impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
187trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
188 fn tb_reborrow(
190 &mut self,
191 place: &MPlaceTy<'tcx>, ptr_size: Size,
193 new_perm: NewPermission,
194 new_tag: BorTag,
195 ) -> InterpResult<'tcx, Option<Provenance>> {
196 let this = self.eval_context_mut();
197 assert!(new_perm.initial_state.is_initial());
199 this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable)?;
201
202 let log_creation = |this: &MiriInterpCx<'tcx>,
204 loc: Option<(AllocId, Size, ProvenanceExtra)>| -> InterpResult<'tcx> {
206 let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
207 let ty = place.layout.ty;
208 if global.tracked_pointer_tags.contains(&new_tag) {
209 let kind_str = format!("initial state {} (pointee type {ty})", new_perm.initial_state);
210 this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
211 new_tag.inner(),
212 Some(kind_str),
213 loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, ptr_size), orig_tag)),
214 ));
215 }
216 drop(global); interp_ok(())
218 };
219
220 trace!("Reborrow of size {:?}", ptr_size);
221 let (alloc_id, base_offset, parent_prov) = match this.ptr_try_get_alloc_id(place.ptr(), 0) {
222 Ok(data) => {
223 data
226 }
227 Err(_) => {
228 assert_eq!(ptr_size, Size::ZERO); trace!(
232 "reborrow of size 0: reference {:?} derived from {:?} (pointee {})",
233 new_tag,
234 place.ptr(),
235 place.layout.ty,
236 );
237 log_creation(this, None)?;
238 return interp_ok(place.ptr().provenance);
240 }
241 };
242 log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
243
244 let orig_tag = match parent_prov {
245 ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), ProvenanceExtra::Concrete(tag) => tag,
247 };
248
249 trace!(
250 "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
251 new_tag,
252 orig_tag,
253 place.layout.ty,
254 interpret::Pointer::new(alloc_id, base_offset),
255 ptr_size.bytes()
256 );
257
258 if let Some(protect) = new_perm.protector {
259 this.frame_mut()
263 .extra
264 .borrow_tracker
265 .as_mut()
266 .unwrap()
267 .protected_tags
268 .push((alloc_id, new_tag));
269 this.machine
270 .borrow_tracker
271 .as_mut()
272 .expect("We should have borrow tracking data")
273 .get_mut()
274 .protected_tags
275 .insert(new_tag, protect);
276 }
277
278 let alloc_kind = this.get_alloc_info(alloc_id).kind;
279 if !matches!(alloc_kind, AllocKind::LiveData) {
280 assert_eq!(ptr_size, Size::ZERO); return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
284 }
285
286 let span = this.machine.current_span();
287 let alloc_extra = this.get_alloc_extra(alloc_id)?;
288 let range = alloc_range(base_offset, ptr_size);
289 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
290
291 if new_perm.initial_read {
293 tree_borrows.perform_access(
294 orig_tag,
295 Some((range, AccessKind::Read, diagnostics::AccessCause::Reborrow)),
296 this.machine.borrow_tracker.as_ref().unwrap(),
297 alloc_id,
298 this.machine.current_span(),
299 )?;
300 }
301 tree_borrows.new_child(
303 orig_tag,
304 new_tag,
305 new_perm.initial_state,
306 range,
307 span,
308 new_perm.protector.is_some(),
309 )?;
310 drop(tree_borrows);
311
312 if range.size.bytes() > 0 && new_perm.initial_read {
314 if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() {
315 data_race.read(
316 alloc_id,
317 range,
318 NaReadType::Retag,
319 Some(place.layout.ty),
320 &this.machine,
321 )?;
322 }
323 }
324
325 interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
326 }
327
328 fn tb_retag_place(
329 &mut self,
330 place: &MPlaceTy<'tcx>,
331 new_perm: NewPermission,
332 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
333 let this = self.eval_context_mut();
334
335 let reborrow_size = this
341 .size_and_align_of_mplace(place)?
342 .map(|(size, _)| size)
343 .unwrap_or(place.layout.size);
344 trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size);
345
346 let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
353
354 let new_prov = this.tb_reborrow(place, reborrow_size, new_perm, new_tag)?;
356
357 interp_ok(place.clone().map_provenance(|_| new_prov.unwrap()))
361 }
362
363 fn tb_retag_reference(
365 &mut self,
366 val: &ImmTy<'tcx>,
367 new_perm: NewPermission,
368 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
369 let this = self.eval_context_mut();
370 let place = this.ref_to_mplace(val)?;
371 let new_place = this.tb_retag_place(&place, new_perm)?;
372 interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
373 }
374}
375
376impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
377pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
378 fn tb_retag_ptr_value(
381 &mut self,
382 kind: RetagKind,
383 val: &ImmTy<'tcx>,
384 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
385 let this = self.eval_context_mut();
386 let new_perm = match val.layout.ty.kind() {
387 &ty::Ref(_, pointee, mutability) =>
388 NewPermission::from_ref_ty(pointee, mutability, kind, this),
389 _ => None,
390 };
391 if let Some(new_perm) = new_perm {
392 this.tb_retag_reference(val, new_perm)
393 } else {
394 interp_ok(val.clone())
395 }
396 }
397
398 fn tb_retag_place_contents(
400 &mut self,
401 kind: RetagKind,
402 place: &PlaceTy<'tcx>,
403 ) -> InterpResult<'tcx> {
404 let this = self.eval_context_mut();
405 let options = this.machine.borrow_tracker.as_mut().unwrap().get_mut();
406 let retag_fields = options.retag_fields;
407 let mut visitor = RetagVisitor { ecx: this, kind, retag_fields };
408 return visitor.visit_value(place);
409
410 struct RetagVisitor<'ecx, 'tcx> {
412 ecx: &'ecx mut MiriInterpCx<'tcx>,
413 kind: RetagKind,
414 retag_fields: RetagFields,
415 }
416 impl<'ecx, 'tcx> RetagVisitor<'ecx, 'tcx> {
417 #[inline(always)] fn retag_ptr_inplace(
419 &mut self,
420 place: &PlaceTy<'tcx>,
421 new_perm: Option<NewPermission>,
422 ) -> InterpResult<'tcx> {
423 if let Some(new_perm) = new_perm {
424 let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
425 let val = self.ecx.tb_retag_reference(&val, new_perm)?;
426 self.ecx.write_immediate(*val, place)?;
427 }
428 interp_ok(())
429 }
430 }
431 impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> {
432 type V = PlaceTy<'tcx>;
433
434 #[inline(always)]
435 fn ecx(&self) -> &MiriInterpCx<'tcx> {
436 self.ecx
437 }
438
439 fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
443 if box_ty.is_box_global(*self.ecx.tcx) {
445 let new_perm =
446 NewPermission::from_unique_ty(place.layout.ty, self.kind, self.ecx);
447 self.retag_ptr_inplace(place, new_perm)?;
448 }
449 interp_ok(())
450 }
451
452 fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
453 if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
458 return interp_ok(());
459 }
460
461 match place.layout.ty.kind() {
463 &ty::Ref(_, pointee, mutability) => {
464 let new_perm =
465 NewPermission::from_ref_ty(pointee, mutability, self.kind, self.ecx);
466 self.retag_ptr_inplace(place, new_perm)?;
467 }
468 ty::RawPtr(_, _) => {
469 }
474 ty::Adt(adt, _) if adt.is_box() => {
475 self.walk_value(place)?;
479 }
480 _ => {
481 let recurse = match self.retag_fields {
483 RetagFields::No => false,
484 RetagFields::Yes => true,
485 RetagFields::OnlyScalar => {
486 matches!(
489 place.layout.backend_repr,
490 BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)
491 )
492 }
493 };
494 if recurse {
495 self.walk_value(place)?;
496 }
497 }
498 }
499 interp_ok(())
500 }
501 }
502 }
503
504 fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
509 let this = self.eval_context_mut();
510
511 let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env());
515 let new_perm = NewPermission {
517 initial_state: Permission::new_reserved(ty_is_freeze, true),
518 protector: Some(ProtectorKind::StrongProtector),
519 initial_read: true,
520 };
521 this.tb_retag_place(place, new_perm)
522 }
523
524 fn tb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
526 let this = self.eval_context_ref();
527
528 let kind = this.get_alloc_info(alloc_id).kind;
532 match kind {
533 AllocKind::LiveData => {
534 let alloc_extra = this.get_alloc_extra(alloc_id)?;
538 trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
539 alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag);
540 }
541 AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
542 }
544 }
545 interp_ok(())
546 }
547
548 fn print_tree(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
550 let this = self.eval_context_mut();
551 let alloc_extra = this.get_alloc_extra(alloc_id)?;
552 let tree_borrows = alloc_extra.borrow_tracker_tb().borrow();
553 let borrow_tracker = &this.machine.borrow_tracker.as_ref().unwrap().borrow();
554 tree_borrows.print_tree(&borrow_tracker.protected_tags, show_unnamed)
555 }
556
557 fn tb_give_pointer_debug_name(
561 &mut self,
562 ptr: Pointer,
563 nth_parent: u8,
564 name: &str,
565 ) -> InterpResult<'tcx> {
566 let this = self.eval_context_mut();
567 let (tag, alloc_id) = match ptr.provenance {
568 Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id),
569 _ => {
570 eprintln!("Can't give the name {name} to Wildcard pointer");
571 return interp_ok(());
572 }
573 };
574 let alloc_extra = this.get_alloc_extra(alloc_id)?;
575 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
576 tree_borrows.give_pointer_debug_name(tag, nth_parent, name)
577 }
578}