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 unimap;
17mod wildcard;
18
19#[cfg(test)]
20mod exhaustive;
21
22use self::perms::Permission;
23pub use self::tree::Tree;
24
25pub type AllocState = Tree;
26
27impl<'tcx> Tree {
28 pub fn new_allocation(
30 id: AllocId,
31 size: Size,
32 state: &mut GlobalStateInner,
33 _kind: MemoryKind,
34 machine: &MiriMachine<'tcx>,
35 ) -> Self {
36 let tag = state.root_ptr_tag(id, machine); let span = machine.current_user_relevant_span();
38 Tree::new(tag, size, span)
39 }
40
41 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 interpret::Pointer::new(alloc_id, range.start),
56 range.size.bytes(),
57 );
58 let global = machine.borrow_tracker.as_ref().unwrap();
59 let span = machine.current_user_relevant_span();
60 self.perform_access(
61 prov,
62 Some((range, access_kind, diagnostics::AccessCause::Explicit(access_kind))),
63 global,
64 alloc_id,
65 span,
66 )
67 }
68
69 pub fn before_memory_deallocation(
71 &mut self,
72 alloc_id: AllocId,
73 prov: ProvenanceExtra,
74 size: Size,
75 machine: &MiriMachine<'tcx>,
76 ) -> InterpResult<'tcx> {
77 let global = machine.borrow_tracker.as_ref().unwrap();
78 let span = machine.current_user_relevant_span();
79 self.dealloc(prov, alloc_range(Size::ZERO, size), global, alloc_id, span)
80 }
81
82 pub fn release_protector(
89 &mut self,
90 machine: &MiriMachine<'tcx>,
91 global: &GlobalState,
92 tag: BorTag,
93 alloc_id: AllocId, ) -> InterpResult<'tcx> {
95 let span = machine.current_user_relevant_span();
96 self.perform_access(ProvenanceExtra::Concrete(tag), None, global, alloc_id, span)?;
98
99 self.update_exposure_for_protector_release(tag);
100
101 interp_ok(())
102 }
103}
104
105#[derive(Debug, Clone, Copy)]
107pub struct NewPermission {
108 freeze_perm: Permission,
110 freeze_access: bool,
112 nonfreeze_perm: Permission,
114 nonfreeze_access: bool,
117 outside_perm: Permission,
119 protector: Option<ProtectorKind>,
122}
123
124impl<'tcx> NewPermission {
125 fn new(
129 pointee: Ty<'tcx>,
130 ref_mutability: Option<Mutability>,
131 retag_kind: RetagKind,
132 cx: &crate::MiriInterpCx<'tcx>,
133 ) -> Option<Self> {
134 let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env());
135 let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
136 let is_protected = retag_kind == RetagKind::FnEntry;
137
138 if matches!(ref_mutability, Some(Mutability::Mut) | None if !ty_is_unpin) {
139 return None;
142 }
143
144 let freeze_perm = match ref_mutability {
145 Some(Mutability::Not) => Permission::new_frozen(),
147 _ => Permission::new_reserved_frz(),
149 };
150 let nonfreeze_perm = match ref_mutability {
151 Some(Mutability::Not) => Permission::new_cell(),
153 _ if is_protected => Permission::new_reserved_frz(),
155 _ => Permission::new_reserved_im(),
157 };
158
159 let initial_access = |perm: &Permission| !perm.is_cell();
161
162 Some(NewPermission {
163 freeze_perm,
164 freeze_access: initial_access(&freeze_perm),
165 nonfreeze_perm,
166 nonfreeze_access: initial_access(&nonfreeze_perm),
167 outside_perm: if ty_is_freeze { freeze_perm } else { nonfreeze_perm },
168 protector: is_protected.then_some(if ref_mutability.is_some() {
169 ProtectorKind::StrongProtector
171 } else {
172 ProtectorKind::WeakProtector
174 }),
175 })
176 }
177}
178
179impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
183trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
184 fn tb_reborrow(
186 &mut self,
187 place: &MPlaceTy<'tcx>, ptr_size: Size,
189 new_perm: NewPermission,
190 new_tag: BorTag,
191 ) -> InterpResult<'tcx, Option<Provenance>> {
192 let this = self.eval_context_mut();
193 this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable)?;
195
196 let log_creation = |this: &MiriInterpCx<'tcx>,
198 loc: Option<(AllocId, Size, ProvenanceExtra)>| -> InterpResult<'tcx> {
200 let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
201 let ty = place.layout.ty;
202 if global.tracked_pointer_tags.contains(&new_tag) {
203 let ty_is_freeze = ty.is_freeze(*this.tcx, this.typing_env());
204 let kind_str =
205 if ty_is_freeze {
206 format!("initial state {} (pointee type {ty})", new_perm.freeze_perm)
207 } else {
208 format!("initial state {}/{} outside/inside UnsafeCell (pointee type {ty})", new_perm.freeze_perm, new_perm.nonfreeze_perm)
209 };
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); let new_prov = place.ptr().provenance;
232 trace!(
233 "reborrow of size 0: reusing {:?} (pointee {})",
234 place.ptr(),
235 place.layout.ty,
236 );
237 log_creation(this, None)?;
238 return interp_ok(new_prov);
240 }
241 };
242
243 log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
244
245 let orig_tag = match parent_prov {
246 ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), ProvenanceExtra::Concrete(tag) => tag,
248 };
249
250 trace!(
251 "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
252 new_tag,
253 orig_tag,
254 place.layout.ty,
255 interpret::Pointer::new(alloc_id, base_offset),
256 ptr_size.bytes()
257 );
258
259 if let Some(protect) = new_perm.protector {
260 this.frame_mut()
264 .extra
265 .borrow_tracker
266 .as_mut()
267 .unwrap()
268 .protected_tags
269 .push((alloc_id, new_tag));
270 this.machine
271 .borrow_tracker
272 .as_mut()
273 .expect("We should have borrow tracking data")
274 .get_mut()
275 .protected_tags
276 .insert(new_tag, protect);
277 }
278
279 let alloc_kind = this.get_alloc_info(alloc_id).kind;
280 if !matches!(alloc_kind, AllocKind::LiveData) {
281 assert_eq!(ptr_size, Size::ZERO); return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
285 }
286
287 let protected = new_perm.protector.is_some();
288 let precise_interior_mut = this
289 .machine
290 .borrow_tracker
291 .as_mut()
292 .unwrap()
293 .get_mut()
294 .borrow_tracker_method
295 .get_tree_borrows_params()
296 .precise_interior_mut;
297
298 let loc_state = |frozen: bool| -> LocationState {
300 let (perm, access) = if frozen {
301 (new_perm.freeze_perm, new_perm.freeze_access)
302 } else {
303 (new_perm.nonfreeze_perm, new_perm.nonfreeze_access)
304 };
305 let sifa = perm.strongest_idempotent_foreign_access(protected);
306 if access {
307 LocationState::new_accessed(perm, sifa)
308 } else {
309 LocationState::new_non_accessed(perm, sifa)
310 }
311 };
312 let inside_perms = if !precise_interior_mut {
313 let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env());
315 let state = loc_state(ty_is_freeze);
316 DedupRangeMap::new(ptr_size, state)
317 } else {
318 let mut perms_map: DedupRangeMap<LocationState> = DedupRangeMap::new(
320 ptr_size,
321 LocationState::new_accessed(
322 Permission::new_disabled(),
323 IdempotentForeignAccess::None,
324 ),
325 );
326 this.visit_freeze_sensitive(place, ptr_size, |range, frozen| {
327 let state = loc_state(frozen);
328 for (_loc_range, loc) in perms_map.iter_mut(range.start, range.size) {
329 *loc = state;
330 }
331 interp_ok(())
332 })?;
333 perms_map
334 };
335
336 let alloc_extra = this.get_alloc_extra(alloc_id)?;
337 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
338
339 for (perm_range, perm) in inside_perms.iter_all() {
340 if perm.accessed() {
341 let range_in_alloc = AllocRange {
344 start: Size::from_bytes(perm_range.start) + base_offset,
345 size: Size::from_bytes(perm_range.end - perm_range.start),
346 };
347
348 tree_borrows.perform_access(
349 parent_prov,
350 Some((range_in_alloc, AccessKind::Read, diagnostics::AccessCause::Reborrow)),
351 this.machine.borrow_tracker.as_ref().unwrap(),
352 alloc_id,
353 this.machine.current_user_relevant_span(),
354 )?;
355
356 if range_in_alloc.size.bytes() > 0 {
358 if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() {
359 data_race.read_non_atomic(
360 alloc_id,
361 range_in_alloc,
362 NaReadType::Retag,
363 Some(place.layout.ty),
364 &this.machine,
365 )?
366 }
367 }
368 }
369 }
370
371 tree_borrows.new_child(
373 base_offset,
374 orig_tag,
375 new_tag,
376 inside_perms,
377 new_perm.outside_perm,
378 protected,
379 this.machine.current_user_relevant_span(),
380 )?;
381 drop(tree_borrows);
382
383 interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
384 }
385
386 fn tb_retag_place(
387 &mut self,
388 place: &MPlaceTy<'tcx>,
389 new_perm: NewPermission,
390 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
391 let this = self.eval_context_mut();
392
393 let reborrow_size =
399 this.size_and_align_of_val(place)?.map(|(size, _)| size).unwrap_or(place.layout.size);
400 trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size);
401
402 let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
409
410 let new_prov = this.tb_reborrow(place, reborrow_size, new_perm, new_tag)?;
412
413 interp_ok(place.clone().map_provenance(|_| new_prov.unwrap()))
417 }
418
419 fn tb_retag_reference(
421 &mut self,
422 val: &ImmTy<'tcx>,
423 new_perm: NewPermission,
424 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
425 let this = self.eval_context_mut();
426 let place = this.ref_to_mplace(val)?;
427 let new_place = this.tb_retag_place(&place, new_perm)?;
428 interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
429 }
430}
431
432impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
433pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
434 fn tb_retag_ptr_value(
437 &mut self,
438 kind: RetagKind,
439 val: &ImmTy<'tcx>,
440 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
441 let this = self.eval_context_mut();
442 let new_perm = match val.layout.ty.kind() {
443 &ty::Ref(_, pointee, mutability) =>
444 NewPermission::new(pointee, Some(mutability), kind, this),
445 _ => None,
446 };
447 if let Some(new_perm) = new_perm {
448 this.tb_retag_reference(val, new_perm)
449 } else {
450 interp_ok(val.clone())
451 }
452 }
453
454 fn tb_retag_place_contents(
456 &mut self,
457 kind: RetagKind,
458 place: &PlaceTy<'tcx>,
459 ) -> InterpResult<'tcx> {
460 let this = self.eval_context_mut();
461 let mut visitor = RetagVisitor { ecx: this, kind };
462 return visitor.visit_value(place);
463
464 struct RetagVisitor<'ecx, 'tcx> {
466 ecx: &'ecx mut MiriInterpCx<'tcx>,
467 kind: RetagKind,
468 }
469 impl<'ecx, 'tcx> RetagVisitor<'ecx, 'tcx> {
470 #[inline(always)] fn retag_ptr_inplace(
472 &mut self,
473 place: &PlaceTy<'tcx>,
474 new_perm: Option<NewPermission>,
475 ) -> InterpResult<'tcx> {
476 if let Some(new_perm) = new_perm {
477 let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
478 let val = self.ecx.tb_retag_reference(&val, new_perm)?;
479 self.ecx.write_immediate(*val, place)?;
480 }
481 interp_ok(())
482 }
483 }
484 impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> {
485 type V = PlaceTy<'tcx>;
486
487 #[inline(always)]
488 fn ecx(&self) -> &MiriInterpCx<'tcx> {
489 self.ecx
490 }
491
492 fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
496 if box_ty.is_box_global(*self.ecx.tcx) {
498 let pointee = place.layout.ty.builtin_deref(true).unwrap();
499 let new_perm =
500 NewPermission::new(pointee, None, self.kind, self.ecx);
501 self.retag_ptr_inplace(place, new_perm)?;
502 }
503 interp_ok(())
504 }
505
506 fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
507 if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
512 return interp_ok(());
513 }
514
515 match place.layout.ty.kind() {
517 &ty::Ref(_, pointee, mutability) => {
518 let new_perm =
519 NewPermission::new(pointee, Some(mutability), self.kind, self.ecx);
520 self.retag_ptr_inplace(place, new_perm)?;
521 }
522 ty::RawPtr(_, _) => {
523 }
528 ty::Adt(adt, _) if adt.is_box() => {
529 self.walk_value(place)?;
533 }
534 _ => {
535 self.walk_value(place)?;
537 }
538 }
539 interp_ok(())
540 }
541 }
542 }
543
544 fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
549 let this = self.eval_context_mut();
550
551 let new_perm = NewPermission {
553 freeze_perm: Permission::new_reserved_frz(),
558 freeze_access: true,
559 nonfreeze_perm: Permission::new_reserved_frz(),
560 nonfreeze_access: true,
561 outside_perm: Permission::new_reserved_frz(),
562 protector: Some(ProtectorKind::StrongProtector),
563 };
564 this.tb_retag_place(place, new_perm)
565 }
566
567 fn tb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
569 let this = self.eval_context_ref();
570
571 let kind = this.get_alloc_info(alloc_id).kind;
575 match kind {
576 AllocKind::LiveData => {
577 let alloc_extra = this.get_alloc_extra(alloc_id)?;
581 trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
582
583 let global = this.machine.borrow_tracker.as_ref().unwrap();
584 let protected_tags = &global.borrow().protected_tags;
585 let protected = protected_tags.contains_key(&tag);
586 alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag, protected);
587 }
588 AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => {
589 }
591 }
592 interp_ok(())
593 }
594
595 fn print_tree(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
597 let this = self.eval_context_mut();
598 let alloc_extra = this.get_alloc_extra(alloc_id)?;
599 let tree_borrows = alloc_extra.borrow_tracker_tb().borrow();
600 let borrow_tracker = &this.machine.borrow_tracker.as_ref().unwrap().borrow();
601 tree_borrows.print_tree(&borrow_tracker.protected_tags, show_unnamed)
602 }
603
604 fn tb_give_pointer_debug_name(
608 &mut self,
609 ptr: Pointer,
610 nth_parent: u8,
611 name: &str,
612 ) -> InterpResult<'tcx> {
613 let this = self.eval_context_mut();
614 let (tag, alloc_id) = match ptr.provenance {
615 Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id),
616 _ => {
617 eprintln!("Can't give the name {name} to Wildcard pointer");
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}