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,
230 pub implicit_writes: bool,
232}
233
234impl BorrowTrackerMethod {
235 pub fn instantiate_global_state(self, config: &MiriConfig) -> GlobalState {
236 RefCell::new(GlobalStateInner::new(self, config.tracked_pointer_tags.clone()))
237 }
238
239 pub fn get_tree_borrows_params(self) -> TreeBorrowsParams {
240 match self {
241 BorrowTrackerMethod::TreeBorrows(params) => params,
242 _ => panic!("can only be called when `BorrowTrackerMethod` is `TreeBorrows`"),
243 }
244 }
245}
246
247impl GlobalStateInner {
248 pub fn new_allocation(
249 &mut self,
250 id: AllocId,
251 alloc_size: Size,
252 kind: MemoryKind,
253 machine: &MiriMachine<'_>,
254 ) -> AllocState {
255 let _trace = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind);
256 match self.borrow_tracker_method {
257 BorrowTrackerMethod::StackedBorrows =>
258 AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation(
259 id, alloc_size, self, kind, machine,
260 )))),
261 BorrowTrackerMethod::TreeBorrows { .. } =>
262 AllocState::TreeBorrows(Box::new(RefCell::new(Tree::new_allocation(
263 id, alloc_size, self, kind, machine,
264 )))),
265 }
266 }
267}
268
269impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
270pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
271 fn retag_ptr_value(
272 &mut self,
273 val: &ImmTy<'tcx>,
274 ty: Ty<'tcx>,
275 ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
276 let _trace = enter_trace_span!(borrow_tracker::retag_ptr_value, ?ty);
277 let this = self.eval_context_mut();
278 let state = this.machine.borrow_tracker.as_mut().unwrap().get_mut();
279 let method = state.borrow_tracker_method;
280 let retag_mode = state.retag_mode;
281 info!("retag_ptr_value: type={ty}, mode={retag_mode:?}, val={:?}", **val);
282 match method {
283 BorrowTrackerMethod::StackedBorrows => this.sb_retag_ptr_value(val, ty, retag_mode),
284 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_retag_ptr_value(val, ty, retag_mode),
285 }
286 }
287
288 fn with_retag_mode<T>(
289 &mut self,
290 mode: RetagMode,
291 f: impl FnOnce(&mut Self) -> InterpResult<'tcx, T>,
292 ) -> InterpResult<'tcx, T> {
293 let state = self.eval_context_mut().machine.borrow_tracker.as_mut().unwrap().get_mut();
295 let old_mode = mem::replace(&mut state.retag_mode, mode);
296
297 let ret = f(self);
298
299 let state = self.eval_context_mut().machine.borrow_tracker.as_mut().unwrap().get_mut();
301 state.retag_mode = old_mode;
302
303 ret
304 }
305
306 fn protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
307 let _trace = enter_trace_span!(borrow_tracker::protect_place, ?place);
308 let this = self.eval_context_mut();
309 let method = this.machine.borrow_tracker.as_mut().unwrap().get_mut().borrow_tracker_method;
310 match method {
311 BorrowTrackerMethod::StackedBorrows => this.sb_protect_place(place),
312 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_protect_place(place),
313 }
314 }
315
316 fn expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
317 let _trace =
318 enter_trace_span!(borrow_tracker::expose_tag, alloc_id = alloc_id.0, tag = tag.0);
319 let this = self.eval_context_ref();
320 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
321 match method {
322 BorrowTrackerMethod::StackedBorrows => this.sb_expose_tag(alloc_id, tag),
323 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_expose_tag(alloc_id, tag),
324 }
325 }
326
327 fn give_pointer_debug_name(
328 &mut self,
329 ptr: Pointer,
330 nth_parent: u8,
331 name: &str,
332 ) -> InterpResult<'tcx> {
333 let this = self.eval_context_mut();
334 let method = this.machine.borrow_tracker.as_mut().unwrap().get_mut().borrow_tracker_method;
335 match method {
336 BorrowTrackerMethod::StackedBorrows => {
337 this.tcx.tcx.dcx().warn("Stacked Borrows does not support named pointers; `miri_pointer_name` is a no-op");
338 interp_ok(())
339 }
340 BorrowTrackerMethod::TreeBorrows { .. } =>
341 this.tb_give_pointer_debug_name(ptr, nth_parent, name),
342 }
343 }
344
345 fn print_borrow_state(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
346 let this = self.eval_context_mut();
347 let Some(borrow_tracker) = &mut this.machine.borrow_tracker else {
348 eprintln!("attempted to print borrow state, but no borrow state is being tracked");
349 return interp_ok(());
350 };
351 let method = borrow_tracker.get_mut().borrow_tracker_method;
352 match method {
353 BorrowTrackerMethod::StackedBorrows => this.print_stacks(alloc_id),
354 BorrowTrackerMethod::TreeBorrows { .. } => this.print_tree(alloc_id, show_unnamed),
355 }
356 }
357
358 fn on_stack_pop(
359 &self,
360 frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
361 ) -> InterpResult<'tcx> {
362 let _trace = enter_trace_span!(borrow_tracker::on_stack_pop);
363 let this = self.eval_context_ref();
364 let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap();
365 for (alloc_id, tag) in &frame
369 .extra
370 .borrow_tracker
371 .as_ref()
372 .expect("we should have borrow tracking data")
373 .protected_tags
374 {
375 let kind = this.get_alloc_info(*alloc_id).kind;
382 if matches!(kind, AllocKind::LiveData) {
383 let alloc_extra = this.get_alloc_extra(*alloc_id)?; let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap();
385 alloc_borrow_tracker.release_protector(
386 &this.machine,
387 borrow_tracker,
388 *tag,
389 *alloc_id,
390 )?;
391 }
392 }
393 borrow_tracker.borrow_mut().end_call(&frame.extra);
394
395 interp_ok(())
396 }
397}
398
399#[derive(Debug, Clone)]
401pub enum AllocState {
402 StackedBorrows(Box<RefCell<stacked_borrows::AllocState>>),
404 TreeBorrows(Box<RefCell<tree_borrows::AllocState>>),
406}
407
408impl machine::AllocExtra<'_> {
409 #[track_caller]
410 pub fn borrow_tracker_sb(&self) -> &RefCell<stacked_borrows::AllocState> {
411 match self.borrow_tracker {
412 Some(AllocState::StackedBorrows(ref sb)) => sb,
413 _ => panic!("expected Stacked Borrows borrow tracking, got something else"),
414 }
415 }
416
417 #[track_caller]
418 pub fn borrow_tracker_sb_mut(&mut self) -> &mut RefCell<stacked_borrows::AllocState> {
419 match self.borrow_tracker {
420 Some(AllocState::StackedBorrows(ref mut sb)) => sb,
421 _ => panic!("expected Stacked Borrows borrow tracking, got something else"),
422 }
423 }
424
425 #[track_caller]
426 pub fn borrow_tracker_tb(&self) -> &RefCell<tree_borrows::AllocState> {
427 match self.borrow_tracker {
428 Some(AllocState::TreeBorrows(ref tb)) => tb,
429 _ => panic!("expected Tree Borrows borrow tracking, got something else"),
430 }
431 }
432}
433
434impl AllocState {
435 pub fn before_memory_read<'tcx>(
436 &self,
437 alloc_id: AllocId,
438 prov_extra: ProvenanceExtra,
439 range: AllocRange,
440 machine: &MiriMachine<'tcx>,
441 ) -> InterpResult<'tcx> {
442 let _trace = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0);
443 match self {
444 AllocState::StackedBorrows(sb) =>
445 sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine),
446 AllocState::TreeBorrows(tb) =>
447 tb.borrow_mut().before_memory_access(
448 AccessKind::Read,
449 alloc_id,
450 prov_extra,
451 range,
452 machine,
453 ),
454 }
455 }
456
457 pub fn before_memory_write<'tcx>(
458 &mut self,
459 alloc_id: AllocId,
460 prov_extra: ProvenanceExtra,
461 range: AllocRange,
462 machine: &MiriMachine<'tcx>,
463 ) -> InterpResult<'tcx> {
464 let _trace = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0);
465 match self {
466 AllocState::StackedBorrows(sb) =>
467 sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine),
468 AllocState::TreeBorrows(tb) =>
469 tb.get_mut().before_memory_access(
470 AccessKind::Write,
471 alloc_id,
472 prov_extra,
473 range,
474 machine,
475 ),
476 }
477 }
478
479 pub fn before_memory_deallocation<'tcx>(
480 &mut self,
481 alloc_id: AllocId,
482 prov_extra: ProvenanceExtra,
483 size: Size,
484 machine: &MiriMachine<'tcx>,
485 ) -> InterpResult<'tcx> {
486 let _trace =
487 enter_trace_span!(borrow_tracker::before_memory_deallocation, alloc_id = alloc_id.0);
488 match self {
489 AllocState::StackedBorrows(sb) =>
490 sb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
491 AllocState::TreeBorrows(tb) =>
492 tb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
493 }
494 }
495
496 pub fn remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>) {
497 let _trace = enter_trace_span!(borrow_tracker::remove_unreachable_tags);
498 match self {
499 AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags),
500 AllocState::TreeBorrows(tb) => tb.borrow_mut().remove_unreachable_tags(tags),
501 }
502 }
503
504 pub fn release_protector<'tcx>(
506 &self,
507 machine: &MiriMachine<'tcx>,
508 global: &GlobalState,
509 tag: BorTag,
510 alloc_id: AllocId, ) -> InterpResult<'tcx> {
512 let _trace = enter_trace_span!(
513 borrow_tracker::release_protector,
514 alloc_id = alloc_id.0,
515 tag = tag.0
516 );
517 match self {
518 AllocState::StackedBorrows(_sb) => interp_ok(()),
519 AllocState::TreeBorrows(tb) =>
520 tb.borrow_mut().release_protector(machine, global, tag, alloc_id),
521 }
522 }
523}
524
525impl VisitProvenance for AllocState {
526 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
527 let _trace = enter_trace_span!(borrow_tracker::visit_provenance);
528 match self {
529 AllocState::StackedBorrows(sb) => sb.visit_provenance(visit),
530 AllocState::TreeBorrows(tb) => tb.visit_provenance(visit),
531 }
532 }
533}