1use std::cell::RefCell;
2use std::fmt;
3use std::num::NonZero;
4
5use rustc_abi::Size;
6use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7use rustc_middle::mir::RetagKind;
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 next_ptr_tag: BorTag,
108 root_ptr_tags: FxHashMap<AllocId, BorTag>,
112 protected_tags: FxHashMap<BorTag, ProtectorKind>,
117 tracked_pointer_tags: FxHashSet<BorTag>,
119}
120
121impl VisitProvenance for GlobalStateInner {
122 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
123 }
127}
128
129pub type GlobalState = RefCell<GlobalStateInner>;
131
132#[derive(Copy, Clone, Debug, PartialEq, Eq)]
134pub enum ProtectorKind {
135 WeakProtector,
142
143 StrongProtector,
150}
151
152impl GlobalStateInner {
154 pub fn new(
155 borrow_tracker_method: BorrowTrackerMethod,
156 tracked_pointer_tags: FxHashSet<BorTag>,
157 ) -> Self {
158 GlobalStateInner {
159 borrow_tracker_method,
160 next_ptr_tag: BorTag::one(),
161 root_ptr_tags: FxHashMap::default(),
162 protected_tags: FxHashMap::default(),
163 tracked_pointer_tags,
164 }
165 }
166
167 fn new_ptr(&mut self) -> BorTag {
169 let id = self.next_ptr_tag;
170 self.next_ptr_tag = id.succ().unwrap();
171 id
172 }
173
174 pub fn new_frame(&mut self) -> FrameState {
175 FrameState { protected_tags: SmallVec::new() }
176 }
177
178 fn end_call(&mut self, frame: &machine::FrameExtra<'_>) {
179 for (_, tag) in &frame
180 .borrow_tracker
181 .as_ref()
182 .expect("we should have borrow tracking data")
183 .protected_tags
184 {
185 self.protected_tags.remove(tag);
186 }
187 }
188
189 pub fn root_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_>) -> BorTag {
190 self.root_ptr_tags.get(&id).copied().unwrap_or_else(|| {
191 let tag = self.new_ptr();
192 if self.tracked_pointer_tags.contains(&tag) {
193 machine.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
194 tag.inner(),
195 None,
196 None,
197 ));
198 }
199 trace!("New allocation {:?} has rpot tag {:?}", id, tag);
200 self.root_ptr_tags.try_insert(id, tag).unwrap();
201 tag
202 })
203 }
204
205 pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_>) {
206 self.root_ptr_tags.retain(|id, _| allocs.is_live(*id));
207 }
208
209 pub fn borrow_tracker_method(&self) -> BorrowTrackerMethod {
210 self.borrow_tracker_method
211 }
212}
213
214#[derive(Debug, Copy, Clone, PartialEq, Eq)]
216pub enum BorrowTrackerMethod {
217 StackedBorrows,
219 TreeBorrows(TreeBorrowsParams),
221}
222
223#[derive(Debug, Copy, Clone, PartialEq, Eq)]
225pub struct TreeBorrowsParams {
226 pub precise_interior_mut: bool,
227 pub implicit_writes: bool,
229}
230
231impl BorrowTrackerMethod {
232 pub fn instantiate_global_state(self, config: &MiriConfig) -> GlobalState {
233 RefCell::new(GlobalStateInner::new(self, config.tracked_pointer_tags.clone()))
234 }
235
236 pub fn get_tree_borrows_params(self) -> TreeBorrowsParams {
237 match self {
238 BorrowTrackerMethod::TreeBorrows(params) => params,
239 _ => panic!("can only be called when `BorrowTrackerMethod` is `TreeBorrows`"),
240 }
241 }
242}
243
244impl GlobalStateInner {
245 pub fn new_allocation(
246 &mut self,
247 id: AllocId,
248 alloc_size: Size,
249 kind: MemoryKind,
250 machine: &MiriMachine<'_>,
251 ) -> AllocState {
252 let _trace = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind);
253 match self.borrow_tracker_method {
254 BorrowTrackerMethod::StackedBorrows =>
255 AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation(
256 id, alloc_size, self, kind, machine,
257 )))),
258 BorrowTrackerMethod::TreeBorrows { .. } =>
259 AllocState::TreeBorrows(Box::new(RefCell::new(Tree::new_allocation(
260 id, alloc_size, self, kind, machine,
261 )))),
262 }
263 }
264}
265
266impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
267pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
268 fn retag_ptr_value(
269 &mut self,
270 kind: RetagKind,
271 val: &ImmTy<'tcx>,
272 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
273 let _trace = enter_trace_span!(borrow_tracker::retag_ptr_value, ?kind, ?val.layout);
274 let this = self.eval_context_mut();
275 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
276 match method {
277 BorrowTrackerMethod::StackedBorrows => this.sb_retag_ptr_value(kind, val),
278 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_retag_ptr_value(kind, val),
279 }
280 }
281
282 fn retag_place_contents(
283 &mut self,
284 kind: RetagKind,
285 place: &PlaceTy<'tcx>,
286 ) -> InterpResult<'tcx> {
287 let _trace = enter_trace_span!(borrow_tracker::retag_place_contents, ?kind, ?place);
288 let this = self.eval_context_mut();
289 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
290 match method {
291 BorrowTrackerMethod::StackedBorrows => this.sb_retag_place_contents(kind, place),
292 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_retag_place_contents(kind, place),
293 }
294 }
295
296 fn protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
297 let _trace = enter_trace_span!(borrow_tracker::protect_place, ?place);
298 let this = self.eval_context_mut();
299 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
300 match method {
301 BorrowTrackerMethod::StackedBorrows => this.sb_protect_place(place),
302 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_protect_place(place),
303 }
304 }
305
306 fn expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
307 let _trace =
308 enter_trace_span!(borrow_tracker::expose_tag, alloc_id = alloc_id.0, tag = tag.0);
309 let this = self.eval_context_ref();
310 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
311 match method {
312 BorrowTrackerMethod::StackedBorrows => this.sb_expose_tag(alloc_id, tag),
313 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_expose_tag(alloc_id, tag),
314 }
315 }
316
317 fn give_pointer_debug_name(
318 &mut self,
319 ptr: Pointer,
320 nth_parent: u8,
321 name: &str,
322 ) -> InterpResult<'tcx> {
323 let this = self.eval_context_mut();
324 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
325 match method {
326 BorrowTrackerMethod::StackedBorrows => {
327 this.tcx.tcx.dcx().warn("Stacked Borrows does not support named pointers; `miri_pointer_name` is a no-op");
328 interp_ok(())
329 }
330 BorrowTrackerMethod::TreeBorrows { .. } =>
331 this.tb_give_pointer_debug_name(ptr, nth_parent, name),
332 }
333 }
334
335 fn print_borrow_state(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
336 let this = self.eval_context_mut();
337 let Some(borrow_tracker) = &this.machine.borrow_tracker else {
338 eprintln!("attempted to print borrow state, but no borrow state is being tracked");
339 return interp_ok(());
340 };
341 let method = borrow_tracker.borrow().borrow_tracker_method;
342 match method {
343 BorrowTrackerMethod::StackedBorrows => this.print_stacks(alloc_id),
344 BorrowTrackerMethod::TreeBorrows { .. } => this.print_tree(alloc_id, show_unnamed),
345 }
346 }
347
348 fn on_stack_pop(
349 &self,
350 frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
351 ) -> InterpResult<'tcx> {
352 let _trace = enter_trace_span!(borrow_tracker::on_stack_pop);
353 let this = self.eval_context_ref();
354 let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap();
355 for (alloc_id, tag) in &frame
359 .extra
360 .borrow_tracker
361 .as_ref()
362 .expect("we should have borrow tracking data")
363 .protected_tags
364 {
365 let kind = this.get_alloc_info(*alloc_id).kind;
372 if matches!(kind, AllocKind::LiveData) {
373 let alloc_extra = this.get_alloc_extra(*alloc_id)?; let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap();
375 alloc_borrow_tracker.release_protector(
376 &this.machine,
377 borrow_tracker,
378 *tag,
379 *alloc_id,
380 )?;
381 }
382 }
383 borrow_tracker.borrow_mut().end_call(&frame.extra);
384
385 interp_ok(())
386 }
387}
388
389#[derive(Debug, Clone)]
391pub enum AllocState {
392 StackedBorrows(Box<RefCell<stacked_borrows::AllocState>>),
394 TreeBorrows(Box<RefCell<tree_borrows::AllocState>>),
396}
397
398impl machine::AllocExtra<'_> {
399 #[track_caller]
400 pub fn borrow_tracker_sb(&self) -> &RefCell<stacked_borrows::AllocState> {
401 match self.borrow_tracker {
402 Some(AllocState::StackedBorrows(ref sb)) => sb,
403 _ => panic!("expected Stacked Borrows borrow tracking, got something else"),
404 }
405 }
406
407 #[track_caller]
408 pub fn borrow_tracker_sb_mut(&mut self) -> &mut RefCell<stacked_borrows::AllocState> {
409 match self.borrow_tracker {
410 Some(AllocState::StackedBorrows(ref mut sb)) => sb,
411 _ => panic!("expected Stacked Borrows borrow tracking, got something else"),
412 }
413 }
414
415 #[track_caller]
416 pub fn borrow_tracker_tb(&self) -> &RefCell<tree_borrows::AllocState> {
417 match self.borrow_tracker {
418 Some(AllocState::TreeBorrows(ref tb)) => tb,
419 _ => panic!("expected Tree Borrows borrow tracking, got something else"),
420 }
421 }
422}
423
424impl AllocState {
425 pub fn before_memory_read<'tcx>(
426 &self,
427 alloc_id: AllocId,
428 prov_extra: ProvenanceExtra,
429 range: AllocRange,
430 machine: &MiriMachine<'tcx>,
431 ) -> InterpResult<'tcx> {
432 let _trace = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0);
433 match self {
434 AllocState::StackedBorrows(sb) =>
435 sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine),
436 AllocState::TreeBorrows(tb) =>
437 tb.borrow_mut().before_memory_access(
438 AccessKind::Read,
439 alloc_id,
440 prov_extra,
441 range,
442 machine,
443 ),
444 }
445 }
446
447 pub fn before_memory_write<'tcx>(
448 &mut self,
449 alloc_id: AllocId,
450 prov_extra: ProvenanceExtra,
451 range: AllocRange,
452 machine: &MiriMachine<'tcx>,
453 ) -> InterpResult<'tcx> {
454 let _trace = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0);
455 match self {
456 AllocState::StackedBorrows(sb) =>
457 sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine),
458 AllocState::TreeBorrows(tb) =>
459 tb.get_mut().before_memory_access(
460 AccessKind::Write,
461 alloc_id,
462 prov_extra,
463 range,
464 machine,
465 ),
466 }
467 }
468
469 pub fn before_memory_deallocation<'tcx>(
470 &mut self,
471 alloc_id: AllocId,
472 prov_extra: ProvenanceExtra,
473 size: Size,
474 machine: &MiriMachine<'tcx>,
475 ) -> InterpResult<'tcx> {
476 let _trace =
477 enter_trace_span!(borrow_tracker::before_memory_deallocation, alloc_id = alloc_id.0);
478 match self {
479 AllocState::StackedBorrows(sb) =>
480 sb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
481 AllocState::TreeBorrows(tb) =>
482 tb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
483 }
484 }
485
486 pub fn remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>) {
487 let _trace = enter_trace_span!(borrow_tracker::remove_unreachable_tags);
488 match self {
489 AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags),
490 AllocState::TreeBorrows(tb) => tb.borrow_mut().remove_unreachable_tags(tags),
491 }
492 }
493
494 pub fn release_protector<'tcx>(
496 &self,
497 machine: &MiriMachine<'tcx>,
498 global: &GlobalState,
499 tag: BorTag,
500 alloc_id: AllocId, ) -> InterpResult<'tcx> {
502 let _trace = enter_trace_span!(
503 borrow_tracker::release_protector,
504 alloc_id = alloc_id.0,
505 tag = tag.0
506 );
507 match self {
508 AllocState::StackedBorrows(_sb) => interp_ok(()),
509 AllocState::TreeBorrows(tb) =>
510 tb.borrow_mut().release_protector(machine, global, tag, alloc_id),
511 }
512 }
513}
514
515impl VisitProvenance for AllocState {
516 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
517 let _trace = enter_trace_span!(borrow_tracker::visit_provenance);
518 match self {
519 AllocState::StackedBorrows(sb) => sb.visit_provenance(visit),
520 AllocState::TreeBorrows(tb) => tb.visit_provenance(visit),
521 }
522 }
523}