1 use std::cell::RefCell; 2 use std::fmt; 3 use std::num::NonZeroU64; 4 5 use log::trace; 6 use smallvec::SmallVec; 7 8 use rustc_data_structures::fx::{FxHashMap, FxHashSet}; 9 use rustc_middle::mir::RetagKind; 10 use rustc_target::abi::Size; 11 12 use crate::*; 13 pub mod stacked_borrows; 14 pub mod tree_borrows; 15 16 pub type CallId = NonZeroU64; 17 18 /// Tracking pointer provenance 19 #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] 20 pub struct BorTag(NonZeroU64); 21 22 impl BorTag { new(i: u64) -> Option<Self>23 pub fn new(i: u64) -> Option<Self> { 24 NonZeroU64::new(i).map(BorTag) 25 } 26 get(&self) -> u6427 pub fn get(&self) -> u64 { 28 self.0.get() 29 } 30 inner(&self) -> NonZeroU6431 pub fn inner(&self) -> NonZeroU64 { 32 self.0 33 } 34 succ(self) -> Option<Self>35 pub fn succ(self) -> Option<Self> { 36 self.0.checked_add(1).map(Self) 37 } 38 39 /// The minimum representable tag one() -> Self40 pub fn one() -> Self { 41 Self::new(1).unwrap() 42 } 43 } 44 45 impl std::default::Default for BorTag { 46 /// The default to be used when borrow tracking is disabled default() -> Self47 fn default() -> Self { 48 Self::one() 49 } 50 } 51 52 impl fmt::Debug for BorTag { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 54 write!(f, "<{}>", self.0) 55 } 56 } 57 58 /// Per-call-stack-frame data for borrow tracking 59 #[derive(Debug)] 60 pub struct FrameState { 61 /// The ID of the call this frame corresponds to. 62 pub call_id: CallId, 63 64 /// If this frame is protecting any tags, they are listed here. We use this list to do 65 /// incremental updates of the global list of protected tags stored in the 66 /// `stacked_borrows::GlobalState` upon function return, and if we attempt to pop a protected 67 /// tag, to identify which call is responsible for protecting the tag. 68 /// See `Stack::item_popped` for more explanation. 69 /// 70 /// This will contain one tag per reference passed to the function, so 71 /// a size of 2 is enough for the vast majority of functions. 72 pub protected_tags: SmallVec<[BorTag; 2]>, 73 } 74 75 impl VisitTags for FrameState { visit_tags(&self, _visit: &mut dyn FnMut(BorTag))76 fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { 77 // `protected_tags` are fine to GC. 78 } 79 } 80 81 /// Extra global state, available to the memory access hooks. 82 #[derive(Debug)] 83 pub struct GlobalStateInner { 84 /// Borrow tracker method currently in use. 85 pub borrow_tracker_method: BorrowTrackerMethod, 86 /// Next unused pointer ID (tag). 87 pub next_ptr_tag: BorTag, 88 /// Table storing the "base" tag for each allocation. 89 /// The base tag is the one used for the initial pointer. 90 /// We need this in a separate table to handle cyclic statics. 91 pub base_ptr_tags: FxHashMap<AllocId, BorTag>, 92 /// Next unused call ID (for protectors). 93 pub next_call_id: CallId, 94 /// All currently protected tags. 95 /// An item is protected if its tag is in this set, *and* it has the "protected" bit set. 96 /// We add tags to this when they are created with a protector in `reborrow`, and 97 /// we remove tags from this when the call which is protecting them returns, in 98 /// `GlobalStateInner::end_call`. See `Stack::item_popped` for more details. 99 pub protected_tags: FxHashMap<BorTag, ProtectorKind>, 100 /// The pointer ids to trace 101 pub tracked_pointer_tags: FxHashSet<BorTag>, 102 /// The call ids to trace 103 pub tracked_call_ids: FxHashSet<CallId>, 104 /// Whether to recurse into datatypes when searching for pointers to retag. 105 pub retag_fields: RetagFields, 106 /// Whether `core::ptr::Unique` gets special (`Box`-like) handling. 107 pub unique_is_unique: bool, 108 } 109 110 impl VisitTags for GlobalStateInner { visit_tags(&self, _visit: &mut dyn FnMut(BorTag))111 fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { 112 // The only candidate is base_ptr_tags, and that does not need visiting since we don't ever 113 // GC the bottommost tag. 114 } 115 } 116 117 /// We need interior mutable access to the global state. 118 pub type GlobalState = RefCell<GlobalStateInner>; 119 120 /// Indicates which kind of access is being performed. 121 #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] 122 pub enum AccessKind { 123 Read, 124 Write, 125 } 126 127 impl fmt::Display for AccessKind { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 129 match self { 130 AccessKind::Read => write!(f, "read access"), 131 AccessKind::Write => write!(f, "write access"), 132 } 133 } 134 } 135 136 /// Policy on whether to recurse into fields to retag 137 #[derive(Copy, Clone, Debug)] 138 pub enum RetagFields { 139 /// Don't retag any fields. 140 No, 141 /// Retag all fields. 142 Yes, 143 /// Only retag fields of types with Scalar and ScalarPair layout, 144 /// to match the LLVM `noalias` we generate. 145 OnlyScalar, 146 } 147 148 /// The flavor of the protector. 149 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 150 pub enum ProtectorKind { 151 /// Protected against aliasing violations from other pointers. 152 /// 153 /// Items protected like this cause UB when they are invalidated, *but* the pointer itself may 154 /// still be used to issue a deallocation. 155 /// 156 /// This is required for LLVM IR pointers that are `noalias` but *not* `dereferenceable`. 157 WeakProtector, 158 159 /// Protected against any kind of invalidation. 160 /// 161 /// Items protected like this cause UB when they are invalidated or the memory is deallocated. 162 /// This is strictly stronger protection than `WeakProtector`. 163 /// 164 /// This is required for LLVM IR pointers that are `dereferenceable` (and also allows `noalias`). 165 StrongProtector, 166 } 167 168 /// Utilities for initialization and ID generation 169 impl GlobalStateInner { new( borrow_tracker_method: BorrowTrackerMethod, tracked_pointer_tags: FxHashSet<BorTag>, tracked_call_ids: FxHashSet<CallId>, retag_fields: RetagFields, unique_is_unique: bool, ) -> Self170 pub fn new( 171 borrow_tracker_method: BorrowTrackerMethod, 172 tracked_pointer_tags: FxHashSet<BorTag>, 173 tracked_call_ids: FxHashSet<CallId>, 174 retag_fields: RetagFields, 175 unique_is_unique: bool, 176 ) -> Self { 177 GlobalStateInner { 178 borrow_tracker_method, 179 next_ptr_tag: BorTag::one(), 180 base_ptr_tags: FxHashMap::default(), 181 next_call_id: NonZeroU64::new(1).unwrap(), 182 protected_tags: FxHashMap::default(), 183 tracked_pointer_tags, 184 tracked_call_ids, 185 retag_fields, 186 unique_is_unique, 187 } 188 } 189 190 /// Generates a new pointer tag. Remember to also check track_pointer_tags and log its creation! new_ptr(&mut self) -> BorTag191 pub fn new_ptr(&mut self) -> BorTag { 192 let id = self.next_ptr_tag; 193 self.next_ptr_tag = id.succ().unwrap(); 194 id 195 } 196 new_frame(&mut self, machine: &MiriMachine<'_, '_>) -> FrameState197 pub fn new_frame(&mut self, machine: &MiriMachine<'_, '_>) -> FrameState { 198 let call_id = self.next_call_id; 199 trace!("new_frame: Assigning call ID {}", call_id); 200 if self.tracked_call_ids.contains(&call_id) { 201 machine.emit_diagnostic(NonHaltingDiagnostic::CreatedCallId(call_id)); 202 } 203 self.next_call_id = NonZeroU64::new(call_id.get() + 1).unwrap(); 204 FrameState { call_id, protected_tags: SmallVec::new() } 205 } 206 end_call(&mut self, frame: &machine::FrameExtra<'_>)207 pub fn end_call(&mut self, frame: &machine::FrameExtra<'_>) { 208 for tag in &frame 209 .borrow_tracker 210 .as_ref() 211 .expect("we should have borrow tracking data") 212 .protected_tags 213 { 214 self.protected_tags.remove(tag); 215 } 216 } 217 base_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_, '_>) -> BorTag218 pub fn base_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_, '_>) -> BorTag { 219 self.base_ptr_tags.get(&id).copied().unwrap_or_else(|| { 220 let tag = self.new_ptr(); 221 if self.tracked_pointer_tags.contains(&tag) { 222 machine.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag( 223 tag.inner(), 224 None, 225 None, 226 )); 227 } 228 trace!("New allocation {:?} has base tag {:?}", id, tag); 229 self.base_ptr_tags.try_insert(id, tag).unwrap(); 230 tag 231 }) 232 } 233 } 234 235 /// Which borrow tracking method to use 236 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 237 pub enum BorrowTrackerMethod { 238 /// Stacked Borrows, as implemented in borrow_tracker/stacked_borrows 239 StackedBorrows, 240 /// Tree borrows, as implemented in borrow_tracker/tree_borrows 241 TreeBorrows, 242 } 243 244 impl BorrowTrackerMethod { instantiate_global_state(self, config: &MiriConfig) -> GlobalState245 pub fn instantiate_global_state(self, config: &MiriConfig) -> GlobalState { 246 RefCell::new(GlobalStateInner::new( 247 self, 248 config.tracked_pointer_tags.clone(), 249 config.tracked_call_ids.clone(), 250 config.retag_fields, 251 config.unique_is_unique, 252 )) 253 } 254 } 255 256 impl GlobalStateInner { new_allocation( &mut self, id: AllocId, alloc_size: Size, kind: MemoryKind<machine::MiriMemoryKind>, machine: &MiriMachine<'_, '_>, ) -> AllocState257 pub fn new_allocation( 258 &mut self, 259 id: AllocId, 260 alloc_size: Size, 261 kind: MemoryKind<machine::MiriMemoryKind>, 262 machine: &MiriMachine<'_, '_>, 263 ) -> AllocState { 264 match self.borrow_tracker_method { 265 BorrowTrackerMethod::StackedBorrows => 266 AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation( 267 id, alloc_size, self, kind, machine, 268 )))), 269 BorrowTrackerMethod::TreeBorrows => 270 AllocState::TreeBorrows(Box::new(RefCell::new(Tree::new_allocation( 271 id, alloc_size, self, kind, machine, 272 )))), 273 } 274 } 275 } 276 277 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} 278 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { retag_ptr_value( &mut self, kind: RetagKind, val: &ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>>279 fn retag_ptr_value( 280 &mut self, 281 kind: RetagKind, 282 val: &ImmTy<'tcx, Provenance>, 283 ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { 284 let this = self.eval_context_mut(); 285 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; 286 match method { 287 BorrowTrackerMethod::StackedBorrows => this.sb_retag_ptr_value(kind, val), 288 BorrowTrackerMethod::TreeBorrows => this.tb_retag_ptr_value(kind, val), 289 } 290 } 291 retag_place_contents( &mut self, kind: RetagKind, place: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx>292 fn retag_place_contents( 293 &mut self, 294 kind: RetagKind, 295 place: &PlaceTy<'tcx, Provenance>, 296 ) -> InterpResult<'tcx> { 297 let this = self.eval_context_mut(); 298 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; 299 match method { 300 BorrowTrackerMethod::StackedBorrows => this.sb_retag_place_contents(kind, place), 301 BorrowTrackerMethod::TreeBorrows => this.tb_retag_place_contents(kind, place), 302 } 303 } 304 retag_return_place(&mut self) -> InterpResult<'tcx>305 fn retag_return_place(&mut self) -> InterpResult<'tcx> { 306 let this = self.eval_context_mut(); 307 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; 308 match method { 309 BorrowTrackerMethod::StackedBorrows => this.sb_retag_return_place(), 310 BorrowTrackerMethod::TreeBorrows => this.tb_retag_return_place(), 311 } 312 } 313 expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx>314 fn expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> { 315 let this = self.eval_context_mut(); 316 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; 317 match method { 318 BorrowTrackerMethod::StackedBorrows => this.sb_expose_tag(alloc_id, tag), 319 BorrowTrackerMethod::TreeBorrows => this.tb_expose_tag(alloc_id, tag), 320 } 321 } 322 give_pointer_debug_name( &mut self, ptr: Pointer<Option<Provenance>>, nth_parent: u8, name: &str, ) -> InterpResult<'tcx>323 fn give_pointer_debug_name( 324 &mut self, 325 ptr: Pointer<Option<Provenance>>, 326 nth_parent: u8, 327 name: &str, 328 ) -> InterpResult<'tcx> { 329 let this = self.eval_context_mut(); 330 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; 331 match method { 332 BorrowTrackerMethod::StackedBorrows => { 333 this.tcx.tcx.sess.warn("Stacked Borrows does not support named pointers; `miri_pointer_name` is a no-op"); 334 Ok(()) 335 } 336 BorrowTrackerMethod::TreeBorrows => 337 this.tb_give_pointer_debug_name(ptr, nth_parent, name), 338 } 339 } 340 print_borrow_state(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx>341 fn print_borrow_state(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> { 342 let this = self.eval_context_mut(); 343 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; 344 match method { 345 BorrowTrackerMethod::StackedBorrows => this.print_stacks(alloc_id), 346 BorrowTrackerMethod::TreeBorrows => this.print_tree(alloc_id, show_unnamed), 347 } 348 } 349 } 350 351 /// Extra per-allocation data for borrow tracking 352 #[derive(Debug, Clone)] 353 pub enum AllocState { 354 /// Data corresponding to Stacked Borrows 355 StackedBorrows(Box<RefCell<stacked_borrows::AllocState>>), 356 /// Data corresponding to Tree Borrows 357 TreeBorrows(Box<RefCell<tree_borrows::AllocState>>), 358 } 359 360 impl machine::AllocExtra<'_> { 361 #[track_caller] borrow_tracker_sb(&self) -> &RefCell<stacked_borrows::AllocState>362 pub fn borrow_tracker_sb(&self) -> &RefCell<stacked_borrows::AllocState> { 363 match self.borrow_tracker { 364 Some(AllocState::StackedBorrows(ref sb)) => sb, 365 _ => panic!("expected Stacked Borrows borrow tracking, got something else"), 366 } 367 } 368 369 #[track_caller] borrow_tracker_sb_mut(&mut self) -> &mut RefCell<stacked_borrows::AllocState>370 pub fn borrow_tracker_sb_mut(&mut self) -> &mut RefCell<stacked_borrows::AllocState> { 371 match self.borrow_tracker { 372 Some(AllocState::StackedBorrows(ref mut sb)) => sb, 373 _ => panic!("expected Stacked Borrows borrow tracking, got something else"), 374 } 375 } 376 377 #[track_caller] borrow_tracker_tb(&self) -> &RefCell<tree_borrows::AllocState>378 pub fn borrow_tracker_tb(&self) -> &RefCell<tree_borrows::AllocState> { 379 match self.borrow_tracker { 380 Some(AllocState::TreeBorrows(ref tb)) => tb, 381 _ => panic!("expected Tree Borrows borrow tracking, got something else"), 382 } 383 } 384 } 385 386 impl AllocState { before_memory_read<'tcx>( &self, alloc_id: AllocId, prov_extra: ProvenanceExtra, range: AllocRange, machine: &MiriMachine<'_, 'tcx>, ) -> InterpResult<'tcx>387 pub fn before_memory_read<'tcx>( 388 &self, 389 alloc_id: AllocId, 390 prov_extra: ProvenanceExtra, 391 range: AllocRange, 392 machine: &MiriMachine<'_, 'tcx>, 393 ) -> InterpResult<'tcx> { 394 match self { 395 AllocState::StackedBorrows(sb) => 396 sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine), 397 AllocState::TreeBorrows(tb) => 398 tb.borrow_mut().before_memory_access( 399 AccessKind::Read, 400 alloc_id, 401 prov_extra, 402 range, 403 machine, 404 ), 405 } 406 } 407 before_memory_write<'tcx>( &mut self, alloc_id: AllocId, prov_extra: ProvenanceExtra, range: AllocRange, machine: &mut MiriMachine<'_, 'tcx>, ) -> InterpResult<'tcx>408 pub fn before_memory_write<'tcx>( 409 &mut self, 410 alloc_id: AllocId, 411 prov_extra: ProvenanceExtra, 412 range: AllocRange, 413 machine: &mut MiriMachine<'_, 'tcx>, 414 ) -> InterpResult<'tcx> { 415 match self { 416 AllocState::StackedBorrows(sb) => 417 sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine), 418 AllocState::TreeBorrows(tb) => 419 tb.get_mut().before_memory_access( 420 AccessKind::Write, 421 alloc_id, 422 prov_extra, 423 range, 424 machine, 425 ), 426 } 427 } 428 before_memory_deallocation<'tcx>( &mut self, alloc_id: AllocId, prov_extra: ProvenanceExtra, range: AllocRange, machine: &mut MiriMachine<'_, 'tcx>, ) -> InterpResult<'tcx>429 pub fn before_memory_deallocation<'tcx>( 430 &mut self, 431 alloc_id: AllocId, 432 prov_extra: ProvenanceExtra, 433 range: AllocRange, 434 machine: &mut MiriMachine<'_, 'tcx>, 435 ) -> InterpResult<'tcx> { 436 match self { 437 AllocState::StackedBorrows(sb) => 438 sb.get_mut().before_memory_deallocation(alloc_id, prov_extra, range, machine), 439 AllocState::TreeBorrows(tb) => 440 tb.get_mut().before_memory_deallocation(alloc_id, prov_extra, range, machine), 441 } 442 } 443 remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>)444 pub fn remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>) { 445 match self { 446 AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags), 447 AllocState::TreeBorrows(tb) => tb.borrow_mut().remove_unreachable_tags(tags), 448 } 449 } 450 } 451 452 impl VisitTags for AllocState { visit_tags(&self, visit: &mut dyn FnMut(BorTag))453 fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { 454 match self { 455 AllocState::StackedBorrows(sb) => sb.visit_tags(visit), 456 AllocState::TreeBorrows(tb) => tb.visit_tags(visit), 457 } 458 } 459 } 460