1use std::cell::RefCell;
2use std::num::NonZero;
3use std::{fmt, mem};
4
5use rustc_abi::Size;
6use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7use rustc_middle::ty::Ty;
8use smallvec::SmallVec;
9
10use crate::*;
11pub mod stacked_borrows;
12pub mod tree_borrows;
13
14#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
16pub enum AccessKind {
17 Read,
18 Write,
19}
20
21impl fmt::Display for AccessKind {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 match self {
24 AccessKind::Read => write!(f, "read access"),
25 AccessKind::Write => write!(f, "write access"),
26 }
27 }
28}
29
30#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
32pub struct BorTag(NonZero<u64>);
33
34impl BorTag {
35 pub fn new(i: u64) -> Option<Self> {
36 NonZero::new(i).map(BorTag)
37 }
38
39 pub fn get(&self) -> u64 {
40 self.0.get()
41 }
42
43 pub fn inner(&self) -> NonZero<u64> {
44 self.0
45 }
46
47 pub fn succ(self) -> Option<Self> {
48 self.0.checked_add(1).map(Self)
49 }
50
51 pub fn one() -> Self {
53 Self::new(1).unwrap()
54 }
55}
56
57impl std::default::Default for BorTag {
58 fn default() -> Self {
60 Self::one()
61 }
62}
63
64impl fmt::Debug for BorTag {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(f, "<{}>", self.0)
67 }
68}
69
70#[derive(Debug)]
72pub struct FrameState {
73 protected_tags: SmallVec<[(AllocId, BorTag); 2]>,
85}
86
87impl VisitProvenance for FrameState {
88 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
89 for (id, tag) in &self.protected_tags {
96 visit(Some(*id), Some(*tag));
97 }
98 }
99}
100
101#[derive(Debug)]
103pub struct GlobalStateInner {
104 borrow_tracker_method: BorrowTrackerMethod,
106 retag_mode: RetagMode,
108 next_ptr_tag: BorTag,
110 root_ptr_tags: FxHashMap<AllocId, BorTag>,
114 protected_tags: FxHashMap<BorTag, ProtectorKind>,
119 tracked_pointer_tags: FxHashSet<BorTag>,
121}
122
123impl VisitProvenance for GlobalStateInner {
124 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
125 }
129}
130
131pub type GlobalState = RefCell<GlobalStateInner>;
133
134#[derive(Copy, Clone, Debug, PartialEq, Eq)]
136pub enum ProtectorKind {
137 WeakProtector,
144
145 StrongProtector,
152}
153
154impl GlobalStateInner {
156 pub fn new(
157 borrow_tracker_method: BorrowTrackerMethod,
158 tracked_pointer_tags: FxHashSet<BorTag>,
159 ) -> Self {
160 GlobalStateInner {
161 borrow_tracker_method,
162 retag_mode: RetagMode::Default,
163 next_ptr_tag: BorTag::one(),
164 root_ptr_tags: FxHashMap::default(),
165 protected_tags: FxHashMap::default(),
166 tracked_pointer_tags,
167 }
168 }
169
170 fn new_ptr(&mut self) -> BorTag {
172 let id = self.next_ptr_tag;
173 self.next_ptr_tag = id.succ().unwrap();
174 id
175 }
176
177 pub fn new_frame(&mut self) -> FrameState {
178 FrameState { protected_tags: SmallVec::new() }
179 }
180
181 fn end_call(&mut self, frame: &machine::FrameExtra<'_>) {
182 for (_, tag) in &frame
183 .borrow_tracker
184 .as_ref()
185 .expect("we should have borrow tracking data")
186 .protected_tags
187 {
188 self.protected_tags.remove(tag);
189 }
190 }
191
192 pub fn root_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_>) -> BorTag {
193 self.root_ptr_tags.get(&id).copied().unwrap_or_else(|| {
194 let tag = self.new_ptr();
195 if self.tracked_pointer_tags.contains(&tag) {
196 machine.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
197 tag.inner(),
198 None,
199 None,
200 ));
201 }
202 trace!("New allocation {:?} has rpot tag {:?}", id, tag);
203 self.root_ptr_tags.try_insert(id, tag).unwrap();
204 tag
205 })
206 }
207
208 pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_>) {
209 self.root_ptr_tags.retain(|id, _| allocs.is_live(*id));
210 }
211
212 pub fn borrow_tracker_method(&self) -> BorrowTrackerMethod {
213 self.borrow_tracker_method
214 }
215}
216
217#[derive(Debug, Copy, Clone, PartialEq, Eq)]
219pub enum BorrowTrackerMethod {
220 StackedBorrows,
222 TreeBorrows(TreeBorrowsParams),
224}
225
226#[derive(Debug, Copy, Clone, PartialEq, Eq)]
228pub struct TreeBorrowsParams {
229 pub precise_interior_mut: bool,
231 pub implicit_writes: bool,
233 pub box_custom_allocator_unique: bool,
235}
236
237impl BorrowTrackerMethod {
238 pub fn instantiate_global_state(self, config: &MiriConfig) -> GlobalState {
239 RefCell::new(GlobalStateInner::new(self, config.tracked_pointer_tags.clone()))
240 }
241
242 #[track_caller]
243 pub fn get_tree_borrows_params(self) -> TreeBorrowsParams {
244 match self {
245 BorrowTrackerMethod::TreeBorrows(params) => params,
246 _ => panic!("can only be called when `BorrowTrackerMethod` is `TreeBorrows`"),
247 }
248 }
249}
250
251impl GlobalStateInner {
252 pub fn new_allocation(
253 &mut self,
254 id: AllocId,
255 alloc_size: Size,
256 kind: MemoryKind,
257 machine: &MiriMachine<'_>,
258 ) -> AllocState {
259 let _trace = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind);
260 match self.borrow_tracker_method {
261 BorrowTrackerMethod::StackedBorrows =>
262 AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation(
263 id, alloc_size, self, kind, machine,
264 )))),
265 BorrowTrackerMethod::TreeBorrows { .. } =>
266 AllocState::TreeBorrows(Box::new(RefCell::new(Tree::new_allocation(
267 id, alloc_size, self, kind, machine,
268 )))),
269 }
270 }
271}
272
273impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
274pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
275 fn retag_ptr_value(
276 &mut self,
277 val: &ImmTy<'tcx>,
278 ty: Ty<'tcx>,
279 ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
280 let _trace = enter_trace_span!(borrow_tracker::retag_ptr_value, ?ty);
281 let this = self.eval_context_mut();
282 let state = this.machine.borrow_tracker.as_mut().unwrap().get_mut();
283 let method = state.borrow_tracker_method;
284 let retag_mode = state.retag_mode;
285 info!("retag_ptr_value: type={ty}, mode={retag_mode:?}, val={:?}", **val);
286 match method {
287 BorrowTrackerMethod::StackedBorrows => this.sb_retag_ptr_value(val, ty, retag_mode),
288 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_retag_ptr_value(val, ty, retag_mode),
289 }
290 }
291
292 fn with_retag_mode<T>(
293 &mut self,
294 mode: RetagMode,
295 f: impl FnOnce(&mut Self) -> InterpResult<'tcx, T>,
296 ) -> InterpResult<'tcx, T> {
297 let state = self.eval_context_mut().machine.borrow_tracker.as_mut().unwrap().get_mut();
299 let old_mode = mem::replace(&mut state.retag_mode, mode);
300
301 let ret = f(self);
302
303 let state = self.eval_context_mut().machine.borrow_tracker.as_mut().unwrap().get_mut();
305 state.retag_mode = old_mode;
306
307 ret
308 }
309
310 fn protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
311 let _trace = enter_trace_span!(borrow_tracker::protect_place, ?place);
312 let this = self.eval_context_mut();
313 let method = this.machine.borrow_tracker.as_mut().unwrap().get_mut().borrow_tracker_method;
314 match method {
315 BorrowTrackerMethod::StackedBorrows => this.sb_protect_place(place),
316 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_protect_place(place),
317 }
318 }
319
320 fn expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
321 let _trace =
322 enter_trace_span!(borrow_tracker::expose_tag, alloc_id = alloc_id.0, tag = tag.0);
323 let this = self.eval_context_ref();
324 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
325 match method {
326 BorrowTrackerMethod::StackedBorrows => this.sb_expose_tag(alloc_id, tag),
327 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_expose_tag(alloc_id, tag),
328 }
329 }
330
331 fn give_pointer_debug_name(
332 &mut self,
333 ptr: Pointer,
334 nth_parent: u8,
335 name: &str,
336 ) -> InterpResult<'tcx> {
337 let this = self.eval_context_mut();
338 let method = this.machine.borrow_tracker.as_mut().unwrap().get_mut().borrow_tracker_method;
339 match method {
340 BorrowTrackerMethod::StackedBorrows => {
341 this.tcx.tcx.dcx().warn("Stacked Borrows does not support named pointers; `miri_pointer_name` is a no-op");
342 interp_ok(())
343 }
344 BorrowTrackerMethod::TreeBorrows { .. } =>
345 this.tb_give_pointer_debug_name(ptr, nth_parent, name),
346 }
347 }
348
349 fn print_borrow_state(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
350 let this = self.eval_context_mut();
351 let Some(borrow_tracker) = &mut this.machine.borrow_tracker else {
352 eprintln!("attempted to print borrow state, but no borrow state is being tracked");
353 return interp_ok(());
354 };
355 let method = borrow_tracker.get_mut().borrow_tracker_method;
356 match method {
357 BorrowTrackerMethod::StackedBorrows => this.print_stacks(alloc_id),
358 BorrowTrackerMethod::TreeBorrows { .. } => this.print_tree(alloc_id, show_unnamed),
359 }
360 }
361
362 fn on_stack_pop(
363 &self,
364 frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
365 ) -> InterpResult<'tcx> {
366 let _trace = enter_trace_span!(borrow_tracker::on_stack_pop);
367 let this = self.eval_context_ref();
368 let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap();
369 for (alloc_id, tag) in &frame
373 .extra
374 .borrow_tracker
375 .as_ref()
376 .expect("we should have borrow tracking data")
377 .protected_tags
378 {
379 let kind = this.get_alloc_info(*alloc_id).kind;
386 if matches!(kind, AllocKind::LiveData) {
387 let alloc_extra = this.get_alloc_extra(*alloc_id)?; let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap();
389 alloc_borrow_tracker.release_protector(
390 &this.machine,
391 borrow_tracker,
392 *tag,
393 *alloc_id,
394 )?;
395 }
396 }
397 borrow_tracker.borrow_mut().end_call(&frame.extra);
398
399 interp_ok(())
400 }
401}
402
403#[derive(Debug, Clone)]
405pub enum AllocState {
406 StackedBorrows(Box<RefCell<stacked_borrows::AllocState>>),
408 TreeBorrows(Box<RefCell<tree_borrows::AllocState>>),
410}
411
412impl machine::AllocExtra<'_> {
413 #[track_caller]
414 pub fn borrow_tracker_sb(&self) -> &RefCell<stacked_borrows::AllocState> {
415 match self.borrow_tracker {
416 Some(AllocState::StackedBorrows(ref sb)) => sb,
417 _ => panic!("expected Stacked Borrows borrow tracking, got something else"),
418 }
419 }
420
421 #[track_caller]
422 pub fn borrow_tracker_sb_mut(&mut self) -> &mut RefCell<stacked_borrows::AllocState> {
423 match self.borrow_tracker {
424 Some(AllocState::StackedBorrows(ref mut sb)) => sb,
425 _ => panic!("expected Stacked Borrows borrow tracking, got something else"),
426 }
427 }
428
429 #[track_caller]
430 pub fn borrow_tracker_tb(&self) -> &RefCell<tree_borrows::AllocState> {
431 match self.borrow_tracker {
432 Some(AllocState::TreeBorrows(ref tb)) => tb,
433 _ => panic!("expected Tree Borrows borrow tracking, got something else"),
434 }
435 }
436}
437
438impl AllocState {
439 pub fn before_memory_read<'tcx>(
440 &self,
441 alloc_id: AllocId,
442 prov_extra: ProvenanceExtra,
443 range: AllocRange,
444 machine: &MiriMachine<'tcx>,
445 ) -> InterpResult<'tcx> {
446 let _trace = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0);
447 match self {
448 AllocState::StackedBorrows(sb) =>
449 sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine),
450 AllocState::TreeBorrows(tb) =>
451 tb.borrow_mut().before_memory_access(
452 AccessKind::Read,
453 alloc_id,
454 prov_extra,
455 range,
456 machine,
457 ),
458 }
459 }
460
461 pub fn before_memory_write<'tcx>(
462 &mut self,
463 alloc_id: AllocId,
464 prov_extra: ProvenanceExtra,
465 range: AllocRange,
466 machine: &MiriMachine<'tcx>,
467 ) -> InterpResult<'tcx> {
468 let _trace = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0);
469 match self {
470 AllocState::StackedBorrows(sb) =>
471 sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine),
472 AllocState::TreeBorrows(tb) =>
473 tb.get_mut().before_memory_access(
474 AccessKind::Write,
475 alloc_id,
476 prov_extra,
477 range,
478 machine,
479 ),
480 }
481 }
482
483 pub fn before_memory_deallocation<'tcx>(
484 &mut self,
485 alloc_id: AllocId,
486 prov_extra: ProvenanceExtra,
487 size: Size,
488 machine: &MiriMachine<'tcx>,
489 ) -> InterpResult<'tcx> {
490 let _trace =
491 enter_trace_span!(borrow_tracker::before_memory_deallocation, alloc_id = alloc_id.0);
492 match self {
493 AllocState::StackedBorrows(sb) =>
494 sb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
495 AllocState::TreeBorrows(tb) =>
496 tb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
497 }
498 }
499
500 pub fn remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>) {
501 let _trace = enter_trace_span!(borrow_tracker::remove_unreachable_tags);
502 match self {
503 AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags),
504 AllocState::TreeBorrows(tb) => tb.borrow_mut().remove_unreachable_tags(tags),
505 }
506 }
507
508 pub fn release_protector<'tcx>(
510 &self,
511 machine: &MiriMachine<'tcx>,
512 global: &GlobalState,
513 tag: BorTag,
514 alloc_id: AllocId, ) -> InterpResult<'tcx> {
516 let _trace = enter_trace_span!(
517 borrow_tracker::release_protector,
518 alloc_id = alloc_id.0,
519 tag = tag.0
520 );
521 match self {
522 AllocState::StackedBorrows(_sb) => interp_ok(()),
523 AllocState::TreeBorrows(tb) =>
524 tb.borrow_mut().release_protector(machine, global, tag, alloc_id),
525 }
526 }
527}
528
529impl VisitProvenance for AllocState {
530 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
531 let _trace = enter_trace_span!(borrow_tracker::visit_provenance);
532 match self {
533 AllocState::StackedBorrows(sb) => sb.visit_provenance(visit),
534 AllocState::TreeBorrows(tb) => tb.visit_provenance(visit),
535 }
536 }
537}