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