1use rustc_abi::{BackendRepr, Size};
2use rustc_middle::mir::{Mutability, RetagKind};
3use rustc_middle::ty::layout::HasTypingEnv;
4use rustc_middle::ty::{self, Ty};
5use rustc_span::def_id::DefId;
6
7use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind};
8use crate::concurrency::data_race::NaReadType;
9use crate::*;
10
11pub mod diagnostics;
12mod foreign_access_skipping;
13mod perms;
14mod tree;
15mod unimap;
16
17#[cfg(test)]
18mod exhaustive;
19
20use self::perms::Permission;
21pub use self::tree::Tree;
22
23pub type AllocState = Tree;
24
25impl<'tcx> Tree {
26 pub fn new_allocation(
28 id: AllocId,
29 size: Size,
30 state: &mut GlobalStateInner,
31 _kind: MemoryKind,
32 machine: &MiriMachine<'tcx>,
33 ) -> Self {
34 let tag = state.root_ptr_tag(id, machine); let span = machine.current_span();
36 Tree::new(tag, size, span)
37 }
38
39 pub fn before_memory_access(
42 &mut self,
43 access_kind: AccessKind,
44 alloc_id: AllocId,
45 prov: ProvenanceExtra,
46 range: AllocRange,
47 machine: &MiriMachine<'tcx>,
48 ) -> InterpResult<'tcx> {
49 trace!(
50 "{} with tag {:?}: {:?}, size {}",
51 access_kind,
52 prov,
53 interpret::Pointer::new(alloc_id, range.start),
54 range.size.bytes(),
55 );
56 let tag = match prov {
59 ProvenanceExtra::Concrete(tag) => tag,
60 ProvenanceExtra::Wildcard => return interp_ok(()),
61 };
62 let global = machine.borrow_tracker.as_ref().unwrap();
63 let span = machine.current_span();
64 self.perform_access(
65 tag,
66 Some((range, access_kind, 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 tag = match prov {
84 ProvenanceExtra::Concrete(tag) => tag,
85 ProvenanceExtra::Wildcard => return interp_ok(()),
86 };
87 let global = machine.borrow_tracker.as_ref().unwrap();
88 let span = machine.current_span();
89 self.dealloc(tag, alloc_range(Size::ZERO, size), global, alloc_id, span)
90 }
91
92 pub fn expose_tag(&mut self, _tag: BorTag) {
93 }
95
96 pub fn release_protector(
103 &mut self,
104 machine: &MiriMachine<'tcx>,
105 global: &GlobalState,
106 tag: BorTag,
107 alloc_id: AllocId, ) -> InterpResult<'tcx> {
109 let span = machine.current_span();
110 self.perform_access(tag, None, global, alloc_id, span)
112 }
113}
114
115#[derive(Debug, Clone, Copy)]
117struct NewPermission {
118 zero_size: bool,
121 initial_state: Permission,
123 protector: Option<ProtectorKind>,
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 = match mutability {
145 Mutability::Mut if ty_is_unpin => Permission::new_reserved(ty_is_freeze, is_protected),
146 Mutability::Not if ty_is_freeze => Permission::new_frozen(),
147 _ => return None,
152 };
153
154 let protector = is_protected.then_some(ProtectorKind::StrongProtector);
155 Some(Self { zero_size: false, initial_state, protector })
156 }
157
158 fn from_unique_ty(
162 ty: Ty<'tcx>,
163 kind: RetagKind,
164 cx: &crate::MiriInterpCx<'tcx>,
165 zero_size: bool,
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 = ty.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 zero_size,
176 initial_state,
177 protector: protected.then_some(ProtectorKind::WeakProtector),
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::InboundsTest)?;
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 tree_borrows.perform_access(
293 orig_tag,
294 Some((range, AccessKind::Read, diagnostics::AccessCause::Reborrow)),
295 this.machine.borrow_tracker.as_ref().unwrap(),
296 alloc_id,
297 this.machine.current_span(),
298 )?;
299 tree_borrows.new_child(
301 orig_tag,
302 new_tag,
303 new_perm.initial_state,
304 range,
305 span,
306 new_perm.protector.is_some(),
307 )?;
308 drop(tree_borrows);
309
310 if range.size.bytes() > 0 {
312 if let Some(data_race) = alloc_extra.data_race.as_ref() {
313 data_race.read(
314 alloc_id,
315 range,
316 NaReadType::Retag,
317 Some(place.layout.ty),
318 &this.machine,
319 )?;
320 }
321 }
322
323 interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
324 }
325
326 fn tb_retag_place(
327 &mut self,
328 place: &MPlaceTy<'tcx>,
329 new_perm: NewPermission,
330 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
331 let this = self.eval_context_mut();
332
333 let reborrow_size = match new_perm {
339 NewPermission { zero_size: false, .. } =>
340 this.size_and_align_of_mplace(place)?
341 .map(|(size, _)| size)
342 .unwrap_or(place.layout.size),
343 _ => Size::from_bytes(0),
344 };
345 trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size);
346
347 let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
354
355 let new_prov = this.tb_reborrow(place, reborrow_size, new_perm, new_tag)?;
357
358 interp_ok(place.clone().map_provenance(|_| new_prov.unwrap()))
362 }
363
364 fn tb_retag_reference(
366 &mut self,
367 val: &ImmTy<'tcx>,
368 new_perm: NewPermission,
369 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
370 let this = self.eval_context_mut();
371 let place = this.ref_to_mplace(val)?;
372 let new_place = this.tb_retag_place(&place, new_perm)?;
373 interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
374 }
375}
376
377impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
378pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
379 fn tb_retag_ptr_value(
382 &mut self,
383 kind: RetagKind,
384 val: &ImmTy<'tcx>,
385 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
386 let this = self.eval_context_mut();
387 let new_perm = match val.layout.ty.kind() {
388 &ty::Ref(_, pointee, mutability) =>
389 NewPermission::from_ref_ty(pointee, mutability, kind, this),
390 _ => None,
391 };
392 if let Some(new_perm) = new_perm {
393 this.tb_retag_reference(val, new_perm)
394 } else {
395 interp_ok(val.clone())
396 }
397 }
398
399 fn tb_retag_place_contents(
401 &mut self,
402 kind: RetagKind,
403 place: &PlaceTy<'tcx>,
404 ) -> InterpResult<'tcx> {
405 let this = self.eval_context_mut();
406 let options = this.machine.borrow_tracker.as_mut().unwrap().get_mut();
407 let retag_fields = options.retag_fields;
408 let unique_did =
409 options.unique_is_unique.then(|| this.tcx.lang_items().ptr_unique()).flatten();
410 let mut visitor = RetagVisitor { ecx: this, kind, retag_fields, unique_did };
411 return visitor.visit_value(place);
412
413 struct RetagVisitor<'ecx, 'tcx> {
415 ecx: &'ecx mut MiriInterpCx<'tcx>,
416 kind: RetagKind,
417 retag_fields: RetagFields,
418 unique_did: Option<DefId>,
419 }
420 impl<'ecx, 'tcx> RetagVisitor<'ecx, 'tcx> {
421 #[inline(always)] fn retag_ptr_inplace(
423 &mut self,
424 place: &PlaceTy<'tcx>,
425 new_perm: Option<NewPermission>,
426 ) -> InterpResult<'tcx> {
427 if let Some(new_perm) = new_perm {
428 let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
429 let val = self.ecx.tb_retag_reference(&val, new_perm)?;
430 self.ecx.write_immediate(*val, place)?;
431 }
432 interp_ok(())
433 }
434 }
435 impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> {
436 type V = PlaceTy<'tcx>;
437
438 #[inline(always)]
439 fn ecx(&self) -> &MiriInterpCx<'tcx> {
440 self.ecx
441 }
442
443 fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
447 if box_ty.is_box_global(*self.ecx.tcx) {
449 let new_perm = NewPermission::from_unique_ty(
450 place.layout.ty,
451 self.kind,
452 self.ecx,
453 false,
454 );
455 self.retag_ptr_inplace(place, new_perm)?;
456 }
457 interp_ok(())
458 }
459
460 fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
461 if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
466 return interp_ok(());
467 }
468
469 match place.layout.ty.kind() {
471 &ty::Ref(_, pointee, mutability) => {
472 let new_perm =
473 NewPermission::from_ref_ty(pointee, mutability, self.kind, self.ecx);
474 self.retag_ptr_inplace(place, new_perm)?;
475 }
476 ty::RawPtr(_, _) => {
477 }
482 ty::Adt(adt, _) if adt.is_box() => {
483 self.walk_value(place)?;
487 }
488 ty::Adt(adt, _) if self.unique_did == Some(adt.did()) => {
489 let place = inner_ptr_of_unique(self.ecx, place)?;
490 let new_perm = NewPermission::from_unique_ty(
491 place.layout.ty,
492 self.kind,
493 self.ecx,
494 true,
495 );
496 self.retag_ptr_inplace(&place, new_perm)?;
497 }
498 _ => {
499 let recurse = match self.retag_fields {
501 RetagFields::No => false,
502 RetagFields::Yes => true,
503 RetagFields::OnlyScalar => {
504 matches!(
507 place.layout.backend_repr,
508 BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)
509 )
510 }
511 };
512 if recurse {
513 self.walk_value(place)?;
514 }
515 }
516 }
517 interp_ok(())
518 }
519 }
520 }
521
522 fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
527 let this = self.eval_context_mut();
528
529 let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env());
533 let new_perm = NewPermission {
535 initial_state: Permission::new_reserved(ty_is_freeze, true),
536 zero_size: false,
537 protector: Some(ProtectorKind::StrongProtector),
538 };
539 this.tb_retag_place(place, new_perm)
540 }
541
542 fn tb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
544 let this = self.eval_context_ref();
545
546 let kind = this.get_alloc_info(alloc_id).kind;
550 match kind {
551 AllocKind::LiveData => {
552 let alloc_extra = this.get_alloc_extra(alloc_id)?;
556 trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
557 alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag);
558 }
559 AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
560 }
562 }
563 interp_ok(())
564 }
565
566 fn print_tree(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
568 let this = self.eval_context_mut();
569 let alloc_extra = this.get_alloc_extra(alloc_id)?;
570 let tree_borrows = alloc_extra.borrow_tracker_tb().borrow();
571 let borrow_tracker = &this.machine.borrow_tracker.as_ref().unwrap().borrow();
572 tree_borrows.print_tree(&borrow_tracker.protected_tags, show_unnamed)
573 }
574
575 fn tb_give_pointer_debug_name(
579 &mut self,
580 ptr: Pointer,
581 nth_parent: u8,
582 name: &str,
583 ) -> InterpResult<'tcx> {
584 let this = self.eval_context_mut();
585 let (tag, alloc_id) = match ptr.provenance {
586 Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id),
587 _ => {
588 eprintln!("Can't give the name {name} to Wildcard pointer");
589 return interp_ok(());
590 }
591 };
592 let alloc_extra = this.get_alloc_extra(alloc_id)?;
593 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
594 tree_borrows.give_pointer_debug_name(tag, nth_parent, name)
595 }
596}
597
598fn inner_ptr_of_unique<'tcx>(
602 ecx: &MiriInterpCx<'tcx>,
603 place: &PlaceTy<'tcx>,
604) -> InterpResult<'tcx, PlaceTy<'tcx>> {
605 assert_eq!(place.layout.fields.count(), 2, "Unique must have exactly 2 fields");
609 let (nonnull, phantom) = (ecx.project_field(place, 0)?, ecx.project_field(place, 1)?);
610 assert!(
611 phantom.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
612 "2nd field of `Unique` should be `PhantomData` but is `{:?}`",
613 phantom.layout.ty,
614 );
615 assert_eq!(nonnull.layout.fields.count(), 1, "NonNull must have exactly 1 field");
617 let ptr = ecx.project_field(&nonnull, 0)?;
618 interp_ok(ptr)
620}