miri/borrow_tracker/tree_borrows/
mod.rs1use rustc_abi::Size;
2use rustc_hir::find_attr;
3use rustc_middle::mir::Mutability;
4use rustc_middle::ty::layout::HasTypingEnv;
5use rustc_middle::ty::{self, Ty};
6
7use self::foreign_access_skipping::IdempotentForeignAccess;
8use self::tree::LocationState;
9use crate::borrow_tracker::{AccessKind, GlobalState, GlobalStateInner, ProtectorKind};
10use crate::concurrency::data_race::{NaReadType, NaWriteType};
11use crate::*;
12
13pub mod diagnostics;
14mod foreign_access_skipping;
15mod perms;
16mod tree;
17mod tree_visitor;
18mod unimap;
19mod wildcard;
20
21#[cfg(test)]
22mod exhaustive;
23
24use self::perms::Permission;
25pub use self::tree::Tree;
26
27pub type AllocState = Tree;
28
29impl<'tcx> Tree {
30 pub fn new_allocation(
32 id: AllocId,
33 size: Size,
34 state: &mut GlobalStateInner,
35 _kind: MemoryKind,
36 machine: &MiriMachine<'tcx>,
37 ) -> Self {
38 let tag = state.root_ptr_tag(id, machine); let span = machine.current_user_relevant_span();
40 Tree::new(tag, size, span)
41 }
42
43 pub fn before_memory_access(
46 &mut self,
47 access_kind: AccessKind,
48 alloc_id: AllocId,
49 prov: ProvenanceExtra,
50 range: AllocRange,
51 machine: &MiriMachine<'tcx>,
52 ) -> InterpResult<'tcx> {
53 trace!(
54 "{} with tag {:?}: {:?}, size {}",
55 access_kind,
56 prov,
57 interpret::Pointer::new(alloc_id, range.start),
58 range.size.bytes(),
59 );
60 let global = machine.borrow_tracker.as_ref().unwrap();
61 let span = machine.current_user_relevant_span();
62 self.perform_access(
63 prov,
64 range,
65 access_kind,
66 diagnostics::AccessCause::Explicit(access_kind),
67 global,
68 alloc_id,
69 span,
70 )
71 }
72
73 pub fn before_memory_deallocation(
75 &mut self,
76 alloc_id: AllocId,
77 prov: ProvenanceExtra,
78 size: Size,
79 machine: &MiriMachine<'tcx>,
80 ) -> InterpResult<'tcx> {
81 let global = machine.borrow_tracker.as_ref().unwrap();
82 let span = machine.current_user_relevant_span();
83 self.dealloc(prov, alloc_range(Size::ZERO, size), global, alloc_id, span)
84 }
85
86 pub fn release_protector(
93 &mut self,
94 machine: &MiriMachine<'tcx>,
95 global: &GlobalState,
96 tag: BorTag,
97 alloc_id: AllocId, ) -> InterpResult<'tcx> {
99 let span = machine.current_user_relevant_span();
100 self.perform_protector_end_access(tag, global, alloc_id, span)?;
101
102 self.update_exposure_for_protector_release(tag);
103
104 interp_ok(())
105 }
106}
107
108#[derive(Debug, Clone, Copy)]
110pub struct NewPermission {
111 freeze_perm: Permission,
113 nonfreeze_perm: Permission,
115 outside_perm: Permission,
117 protector: Option<ProtectorKind>,
120}
121
122impl<'tcx> NewPermission {
123 fn new(
127 pointee: Ty<'tcx>,
128 ref_mutability: Option<Mutability>,
129 mode: RetagMode,
130 cx: &MiriInterpCx<'tcx>,
131 ) -> Option<Self> {
132 if mode == RetagMode::None {
133 return None;
134 }
135
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 = mode == RetagMode::FnEntry;
140
141 let implicit_writes_enabled = is_protected && {
146 let implicit_writes = cx.get_tree_borrows_params().implicit_writes;
147 let def_id = cx.frame().instance().def_id();
148 implicit_writes && !find_attr!(cx.tcx, def_id, RustcNoWritable)
149 };
150
151 if matches!(ref_mutability, Some(Mutability::Mut) | None if !ty_is_unpin) {
152 return None;
155 }
156
157 enum Part {
158 InsideFrozen,
159 InsideUnsafeCell,
160 Outside,
161 }
162 use Part::*;
163
164 let perm = |part: Part| {
165 let frozen = match part {
168 InsideFrozen => true,
169 InsideUnsafeCell => false,
170 Outside => ty_is_freeze,
171 };
172 match ref_mutability {
173 Some(Mutability::Not) =>
175 if frozen {
176 Permission::new_frozen()
177 } else {
178 Permission::new_cell()
179 },
180 Some(Mutability::Mut) => {
182 if implicit_writes_enabled && !matches!(part, Outside) {
183 Permission::new_unique()
185 } else if is_protected || frozen {
186 Permission::new_reserved_frz()
189 } else {
190 Permission::new_reserved_im()
191 }
192 }
193 None => {
195 if implicit_writes_enabled && !matches!(part, Outside) {
196 Permission::new_unique()
198 } else if is_protected || frozen {
199 Permission::new_reserved_frz()
202 } else {
203 Permission::new_reserved_im()
204 }
205 }
206 }
207 };
208
209 Some(NewPermission {
210 freeze_perm: perm(InsideFrozen),
211 nonfreeze_perm: perm(InsideUnsafeCell),
212 outside_perm: perm(Outside),
213 protector: is_protected.then_some(if ref_mutability.is_some() {
214 ProtectorKind::StrongProtector
216 } else {
217 ProtectorKind::WeakProtector
219 }),
220 })
221 }
222}
223
224impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
228trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
229 #[track_caller]
230 #[inline]
231 fn get_tree_borrows_params(&self) -> TreeBorrowsParams {
232 let this = self.eval_context_ref();
233 let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap().borrow();
234 borrow_tracker.borrow_tracker_method.get_tree_borrows_params()
235 }
236
237 fn tb_reborrow(
239 &mut self,
240 place: &MPlaceTy<'tcx>, ptr_size: Size,
242 new_perm: NewPermission,
243 new_tag: BorTag,
244 ) -> InterpResult<'tcx, Option<Provenance>> {
245 let this = self.eval_context_mut();
246 this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable)?;
248
249 let log_creation = |this: &MiriInterpCx<'tcx>,
251 loc: Option<(AllocId, Size, ProvenanceExtra)>| -> InterpResult<'tcx> {
253 let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
254 let ty = place.layout.ty;
255 if global.tracked_pointer_tags.contains(&new_tag) {
256 let ty_is_freeze = ty.is_freeze(*this.tcx, this.typing_env());
257 let kind_str =
258 if ty_is_freeze {
259 format!("initial state {} (pointee type {ty})", new_perm.freeze_perm)
260 } else {
261 format!("initial state {}/{} outside/inside UnsafeCell (pointee type {ty})", new_perm.freeze_perm, new_perm.nonfreeze_perm)
262 };
263 this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
264 new_tag.inner(),
265 Some(kind_str),
266 loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, ptr_size), orig_tag)),
267 ));
268 }
269 drop(global); interp_ok(())
271 };
272
273 trace!("Reborrow of size {:?}", ptr_size);
274 let Ok((alloc_id, base_offset, parent_prov)) = this.ptr_try_get_alloc_id(place.ptr(), 0)
277 else {
278 assert_eq!(ptr_size, Size::ZERO); let new_prov = place.ptr().provenance;
282 trace!("reborrow of size 0: reusing {:?} (pointee {})", place.ptr(), place.layout.ty,);
283 log_creation(this, None)?;
284 return interp_ok(new_prov);
286 };
287 let new_prov = Provenance::Concrete { alloc_id, tag: new_tag };
288
289 log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
290
291 trace!(
292 "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
293 new_tag,
294 parent_prov,
295 place.layout.ty,
296 interpret::Pointer::new(alloc_id, base_offset),
297 ptr_size.bytes()
298 );
299
300 if let Some(protect) = new_perm.protector {
301 this.frame_mut()
305 .extra
306 .borrow_tracker
307 .as_mut()
308 .unwrap()
309 .protected_tags
310 .push((alloc_id, new_tag));
311 this.machine
312 .borrow_tracker
313 .as_mut()
314 .expect("We should have borrow tracking data")
315 .get_mut()
316 .protected_tags
317 .insert(new_tag, protect);
318 }
319
320 let alloc_kind = this.get_alloc_info(alloc_id).kind;
321 if !matches!(alloc_kind, AllocKind::LiveData) {
322 assert_eq!(ptr_size, Size::ZERO); return interp_ok(Some(new_prov));
326 }
327
328 let protected = new_perm.protector.is_some();
329 let precise_interior_mut = this.get_tree_borrows_params().precise_interior_mut;
330
331 let loc_state = |frozen: bool| -> LocationState {
333 let perm = if frozen { new_perm.freeze_perm } else { new_perm.nonfreeze_perm };
334 let sifa = perm.strongest_idempotent_foreign_access(protected);
335
336 if perm.associated_access().is_some() {
337 LocationState::new_accessed(perm, sifa)
338 } else {
339 LocationState::new_non_accessed(perm, sifa)
340 }
341 };
342 let inside_perms = if !precise_interior_mut {
343 let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env());
345 DedupRangeMap::new(ptr_size, loc_state(ty_is_freeze))
346 } else {
347 let mut perms_map = DedupRangeMap::new(
349 ptr_size,
350 LocationState::new_accessed(
351 Permission::new_disabled(),
352 IdempotentForeignAccess::None,
353 ),
354 );
355 this.visit_freeze_sensitive(place, ptr_size, |range, frozen| {
356 let state = loc_state(frozen);
357 for (_loc_range, loc) in perms_map.iter_mut(range.start, range.size) {
358 *loc = state;
359 }
360 interp_ok(())
361 })?;
362 perms_map
363 };
364
365 let alloc_extra = this.get_alloc_extra(alloc_id)?;
366 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
367
368 for (perm_range, loc_state) in inside_perms.iter_all() {
369 if let Some(access) = loc_state.permission().associated_access() {
370 if access == AccessKind::Write
375 && this.get_alloc_mutability(alloc_id).unwrap().is_not()
376 {
377 throw_ub!(WriteToReadOnly(alloc_id))
378 }
379
380 let range_in_alloc = AllocRange {
382 start: Size::from_bytes(perm_range.start) + base_offset,
383 size: Size::from_bytes(perm_range.end - perm_range.start),
384 };
385
386 tree_borrows.perform_access(
387 parent_prov,
388 range_in_alloc,
389 access,
390 diagnostics::AccessCause::Reborrow(access),
391 this.machine.borrow_tracker.as_ref().unwrap(),
392 alloc_id,
393 this.machine.current_user_relevant_span(),
394 )?;
395
396 if range_in_alloc.size.bytes() > 0 {
398 if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() {
399 match access {
400 AccessKind::Read =>
401 data_race.read_non_atomic(
402 alloc_id,
403 range_in_alloc,
404 NaReadType::Retag,
405 Some(place.layout.ty),
406 &this.machine,
407 )?,
408 AccessKind::Write =>
409 data_race.write_non_atomic(
410 alloc_id,
411 range_in_alloc,
412 NaWriteType::Retag,
413 Some(place.layout.ty),
414 &this.machine,
415 )?,
416 };
417 }
418 }
419 }
420 }
421
422 tree_borrows.new_child(
424 base_offset,
425 parent_prov,
426 new_tag,
427 inside_perms,
428 new_perm.outside_perm,
429 protected,
430 this.machine.current_user_relevant_span(),
431 )?;
432
433 interp_ok(Some(new_prov))
434 }
435
436 fn tb_retag_place(
437 &mut self,
438 place: &MPlaceTy<'tcx>,
439 new_perm: NewPermission,
440 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
441 let this = self.eval_context_mut();
442
443 let reborrow_size =
449 this.size_and_align_of_val(place)?.map(|(size, _)| size).unwrap_or(place.layout.size);
450 trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size);
451
452 let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
459
460 let new_prov = this.tb_reborrow(place, reborrow_size, new_perm, new_tag)?;
462
463 interp_ok(place.clone().map_provenance(|_| new_prov.unwrap()))
467 }
468}
469
470impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
471pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
472 fn tb_retag_ptr_value(
475 &mut self,
476 val: &ImmTy<'tcx>,
477 ty: Ty<'tcx>,
478 mode: RetagMode,
479 ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
480 let this = self.eval_context_mut();
481 let new_perm = match *ty.kind() {
482 ty::Ref(_, pointee, mutability) =>
483 NewPermission::new(pointee, Some(mutability), mode, this),
484 _ if ty.is_box() => {
485 let box_custom_allocator_unique =
486 this.get_tree_borrows_params().box_custom_allocator_unique;
487 if box_custom_allocator_unique || ty.is_box_global(*this.tcx) {
488 NewPermission::new(ty.builtin_deref(true).unwrap(), None, mode, this)
490 } else {
491 None
493 }
494 }
495
496 ty::RawPtr(..) => {
497 assert!(mode == RetagMode::Raw);
498 None
500 }
501 _ => panic!("tb_retag_ptr_value: invalid type {ty}"),
502 };
503 if let Some(new_perm) = new_perm {
504 let place = this.imm_ptr_to_mplace(val)?;
505 let new_place = this.tb_retag_place(&place, new_perm)?;
506 interp_ok(Some(ImmTy::from_immediate(new_place.to_ref(this), val.layout)))
507 } else {
508 interp_ok(None)
509 }
510 }
511
512 fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
517 let this = self.eval_context_mut();
518
519 let new_perm = NewPermission {
521 freeze_perm: Permission::new_reserved_frz(),
526 nonfreeze_perm: Permission::new_reserved_frz(),
527 outside_perm: Permission::new_reserved_frz(),
528 protector: Some(ProtectorKind::StrongProtector),
529 };
530 this.tb_retag_place(place, new_perm)
531 }
532
533 fn tb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
535 let this = self.eval_context_ref();
536
537 let kind = this.get_alloc_info(alloc_id).kind;
541 match kind {
542 AllocKind::LiveData => {
543 let alloc_extra = this.get_alloc_extra(alloc_id)?;
547 trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
548
549 let global = this.machine.borrow_tracker.as_ref().unwrap();
550 let protected_tags = &global.borrow().protected_tags;
551 let protected = protected_tags.contains_key(&tag);
552 alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag, protected);
553 }
554 AllocKind::Function
555 | AllocKind::VTable
556 | AllocKind::TypeId
557 | AllocKind::Dead
558 | AllocKind::VaList => {
559 }
561 }
562 interp_ok(())
563 }
564
565 fn print_tree(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
567 let this = self.eval_context_mut();
568 let alloc_extra = this.get_alloc_extra(alloc_id)?;
569 let tree_borrows = alloc_extra.borrow_tracker_tb().borrow();
570 let borrow_tracker = &this.machine.borrow_tracker.as_ref().unwrap().borrow();
571 tree_borrows.print_tree(&borrow_tracker.protected_tags, show_unnamed)
572 }
573
574 fn tb_give_pointer_debug_name(
578 &mut self,
579 ptr: Pointer,
580 nth_parent: u8,
581 name: &str,
582 ) -> InterpResult<'tcx> {
583 let this = self.eval_context_mut();
584 let (tag, alloc_id) = match ptr.provenance {
585 Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id),
586 Some(Provenance::Wildcard) => {
587 eprintln!("Can't give the name {name} to wildcard pointer");
588 return interp_ok(());
589 }
590 None => {
591 eprintln!("Can't give the name {name} to pointer without provenance");
592 return interp_ok(());
593 }
594 };
595 let alloc_extra = this.get_alloc_extra(alloc_id)?;
596 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
597 tree_borrows.give_pointer_debug_name(tag, nth_parent, name)
598 }
599}