1 //! Global machine state as well as implementation of the interpreter engine 2 //! `Machine` trait. 3 4 use std::borrow::Cow; 5 use std::cell::RefCell; 6 use std::fmt; 7 use std::path::Path; 8 use std::process; 9 10 use rand::rngs::StdRng; 11 use rand::SeedableRng; 12 13 use rustc_ast::ast::Mutability; 14 use rustc_const_eval::const_eval::CheckAlignment; 15 use rustc_data_structures::fx::{FxHashMap, FxHashSet}; 16 #[allow(unused)] 17 use rustc_data_structures::static_assert_size; 18 use rustc_middle::{ 19 mir, 20 ty::{ 21 self, 22 layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout}, 23 Instance, Ty, TyCtxt, TypeAndMut, 24 }, 25 }; 26 use rustc_span::def_id::{CrateNum, DefId}; 27 use rustc_span::Symbol; 28 use rustc_target::abi::{Align, Size}; 29 use rustc_target::spec::abi::Abi; 30 31 use crate::{ 32 concurrency::{data_race, weak_memory}, 33 shims::unix::FileHandler, 34 *, 35 }; 36 37 /// First real-time signal. 38 /// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35 39 /// as typical values. 40 pub const SIGRTMIN: i32 = 34; 41 42 /// Last real-time signal. 43 /// `signal(7)` says it must be between 32 and 64 and specifies 44 /// `SIGRTMAX` - `SIGRTMIN` >= 8 (which is the value of `_POSIX_RTSIG_MAX`) 45 pub const SIGRTMAX: i32 = 42; 46 47 /// Extra data stored with each stack frame 48 pub struct FrameExtra<'tcx> { 49 /// Extra data for the Borrow Tracker. 50 pub borrow_tracker: Option<borrow_tracker::FrameState>, 51 52 /// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn` 53 /// called by `try`). When this frame is popped during unwinding a panic, 54 /// we stop unwinding, use the `CatchUnwindData` to handle catching. 55 pub catch_unwind: Option<CatchUnwindData<'tcx>>, 56 57 /// If `measureme` profiling is enabled, holds timing information 58 /// for the start of this frame. When we finish executing this frame, 59 /// we use this to register a completed event with `measureme`. 60 pub timing: Option<measureme::DetachedTiming>, 61 62 /// Indicates whether a `Frame` is part of a workspace-local crate and is also not 63 /// `#[track_caller]`. We compute this once on creation and store the result, as an 64 /// optimization. 65 /// This is used by `MiriMachine::current_span` and `MiriMachine::caller_span` 66 pub is_user_relevant: bool, 67 } 68 69 impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 71 // Omitting `timing`, it does not support `Debug`. 72 let FrameExtra { borrow_tracker, catch_unwind, timing: _, is_user_relevant: _ } = self; 73 f.debug_struct("FrameData") 74 .field("borrow_tracker", borrow_tracker) 75 .field("catch_unwind", catch_unwind) 76 .finish() 77 } 78 } 79 80 impl VisitTags for FrameExtra<'_> { visit_tags(&self, visit: &mut dyn FnMut(BorTag))81 fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { 82 let FrameExtra { catch_unwind, borrow_tracker, timing: _, is_user_relevant: _ } = self; 83 84 catch_unwind.visit_tags(visit); 85 borrow_tracker.visit_tags(visit); 86 } 87 } 88 89 /// Extra memory kinds 90 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 91 pub enum MiriMemoryKind { 92 /// `__rust_alloc` memory. 93 Rust, 94 /// `miri_alloc` memory. 95 Miri, 96 /// `malloc` memory. 97 C, 98 /// Windows `HeapAlloc` memory. 99 WinHeap, 100 /// Memory for args, errno, and other parts of the machine-managed environment. 101 /// This memory may leak. 102 Machine, 103 /// Memory allocated by the runtime (e.g. env vars). Separate from `Machine` 104 /// because we clean it up and leak-check it. 105 Runtime, 106 /// Globals copied from `tcx`. 107 /// This memory may leak. 108 Global, 109 /// Memory for extern statics. 110 /// This memory may leak. 111 ExternStatic, 112 /// Memory for thread-local statics. 113 /// This memory may leak. 114 Tls, 115 /// Memory mapped directly by the program 116 Mmap, 117 } 118 119 impl From<MiriMemoryKind> for MemoryKind<MiriMemoryKind> { 120 #[inline(always)] from(kind: MiriMemoryKind) -> MemoryKind<MiriMemoryKind>121 fn from(kind: MiriMemoryKind) -> MemoryKind<MiriMemoryKind> { 122 MemoryKind::Machine(kind) 123 } 124 } 125 126 impl MayLeak for MiriMemoryKind { 127 #[inline(always)] may_leak(self) -> bool128 fn may_leak(self) -> bool { 129 use self::MiriMemoryKind::*; 130 match self { 131 Rust | Miri | C | WinHeap | Runtime => false, 132 Machine | Global | ExternStatic | Tls | Mmap => true, 133 } 134 } 135 } 136 137 impl fmt::Display for MiriMemoryKind { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 139 use self::MiriMemoryKind::*; 140 match self { 141 Rust => write!(f, "Rust heap"), 142 Miri => write!(f, "Miri bare-metal heap"), 143 C => write!(f, "C heap"), 144 WinHeap => write!(f, "Windows heap"), 145 Machine => write!(f, "machine-managed memory"), 146 Runtime => write!(f, "language runtime memory"), 147 Global => write!(f, "global (static or const)"), 148 ExternStatic => write!(f, "extern static"), 149 Tls => write!(f, "thread-local static"), 150 Mmap => write!(f, "mmap"), 151 } 152 } 153 } 154 155 /// Pointer provenance. 156 #[derive(Clone, Copy)] 157 pub enum Provenance { 158 Concrete { 159 alloc_id: AllocId, 160 /// Borrow Tracker tag. 161 tag: BorTag, 162 }, 163 Wildcard, 164 } 165 166 // This needs to be `Eq`+`Hash` because the `Machine` trait needs that because validity checking 167 // *might* be recursive and then it has to track which places have already been visited. 168 // However, comparing provenance is meaningless, since `Wildcard` might be any provenance -- and of 169 // course we don't actually do recursive checking. 170 // We could change `RefTracking` to strip provenance for its `seen` set but that type is generic so that is quite annoying. 171 // Instead owe add the required instances but make them panic. 172 impl PartialEq for Provenance { eq(&self, _other: &Self) -> bool173 fn eq(&self, _other: &Self) -> bool { 174 panic!("Provenance must not be compared") 175 } 176 } 177 impl Eq for Provenance {} 178 impl std::hash::Hash for Provenance { hash<H: std::hash::Hasher>(&self, _state: &mut H)179 fn hash<H: std::hash::Hasher>(&self, _state: &mut H) { 180 panic!("Provenance must not be hashed") 181 } 182 } 183 184 /// The "extra" information a pointer has over a regular AllocId. 185 #[derive(Copy, Clone, PartialEq)] 186 pub enum ProvenanceExtra { 187 Concrete(BorTag), 188 Wildcard, 189 } 190 191 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] 192 static_assert_size!(Pointer<Provenance>, 24); 193 // FIXME: this would with in 24bytes but layout optimizations are not smart enough 194 // #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] 195 //static_assert_size!(Pointer<Option<Provenance>>, 24); 196 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] 197 static_assert_size!(Scalar<Provenance>, 32); 198 199 impl fmt::Debug for Provenance { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 201 match self { 202 Provenance::Concrete { alloc_id, tag } => { 203 // Forward `alternate` flag to `alloc_id` printing. 204 if f.alternate() { 205 write!(f, "[{alloc_id:#?}]")?; 206 } else { 207 write!(f, "[{alloc_id:?}]")?; 208 } 209 // Print Borrow Tracker tag. 210 write!(f, "{tag:?}")?; 211 } 212 Provenance::Wildcard => { 213 write!(f, "[wildcard]")?; 214 } 215 } 216 Ok(()) 217 } 218 } 219 220 impl interpret::Provenance for Provenance { 221 /// We use absolute addresses in the `offset` of a `Pointer<Provenance>`. 222 const OFFSET_IS_ADDR: bool = true; 223 get_alloc_id(self) -> Option<AllocId>224 fn get_alloc_id(self) -> Option<AllocId> { 225 match self { 226 Provenance::Concrete { alloc_id, .. } => Some(alloc_id), 227 Provenance::Wildcard => None, 228 } 229 } 230 join(left: Option<Self>, right: Option<Self>) -> Option<Self>231 fn join(left: Option<Self>, right: Option<Self>) -> Option<Self> { 232 match (left, right) { 233 // If both are the *same* concrete tag, that is the result. 234 ( 235 Some(Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }), 236 Some(Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }), 237 ) if left_alloc == right_alloc && left_tag == right_tag => left, 238 // If one side is a wildcard, the best possible outcome is that it is equal to the other 239 // one, and we use that. 240 (Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o, 241 // Otherwise, fall back to `None`. 242 _ => None, 243 } 244 } 245 } 246 247 impl fmt::Debug for ProvenanceExtra { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 249 match self { 250 ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"), 251 ProvenanceExtra::Wildcard => write!(f, "<wildcard>"), 252 } 253 } 254 } 255 256 impl ProvenanceExtra { and_then<T>(self, f: impl FnOnce(BorTag) -> Option<T>) -> Option<T>257 pub fn and_then<T>(self, f: impl FnOnce(BorTag) -> Option<T>) -> Option<T> { 258 match self { 259 ProvenanceExtra::Concrete(pid) => f(pid), 260 ProvenanceExtra::Wildcard => None, 261 } 262 } 263 } 264 265 /// Extra per-allocation data 266 #[derive(Debug, Clone)] 267 pub struct AllocExtra<'tcx> { 268 /// Global state of the borrow tracker, if enabled. 269 pub borrow_tracker: Option<borrow_tracker::AllocState>, 270 /// Data race detection via the use of a vector-clock. 271 /// This is only added if it is enabled. 272 pub data_race: Option<data_race::AllocState>, 273 /// Weak memory emulation via the use of store buffers. 274 /// This is only added if it is enabled. 275 pub weak_memory: Option<weak_memory::AllocState>, 276 /// A backtrace to where this allocation was allocated. 277 /// As this is recorded for leak reports, it only exists 278 /// if this allocation is leakable. The backtrace is not 279 /// pruned yet; that should be done before printing it. 280 pub backtrace: Option<Vec<FrameInfo<'tcx>>>, 281 } 282 283 impl VisitTags for AllocExtra<'_> { visit_tags(&self, visit: &mut dyn FnMut(BorTag))284 fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { 285 let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self; 286 287 borrow_tracker.visit_tags(visit); 288 data_race.visit_tags(visit); 289 weak_memory.visit_tags(visit); 290 } 291 } 292 293 /// Precomputed layouts of primitive types 294 pub struct PrimitiveLayouts<'tcx> { 295 pub unit: TyAndLayout<'tcx>, 296 pub i8: TyAndLayout<'tcx>, 297 pub i16: TyAndLayout<'tcx>, 298 pub i32: TyAndLayout<'tcx>, 299 pub i64: TyAndLayout<'tcx>, 300 pub i128: TyAndLayout<'tcx>, 301 pub isize: TyAndLayout<'tcx>, 302 pub u8: TyAndLayout<'tcx>, 303 pub u16: TyAndLayout<'tcx>, 304 pub u32: TyAndLayout<'tcx>, 305 pub u64: TyAndLayout<'tcx>, 306 pub u128: TyAndLayout<'tcx>, 307 pub usize: TyAndLayout<'tcx>, 308 pub bool: TyAndLayout<'tcx>, 309 pub mut_raw_ptr: TyAndLayout<'tcx>, // *mut () 310 pub const_raw_ptr: TyAndLayout<'tcx>, // *const () 311 } 312 313 impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> { new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, &'tcx LayoutError<'tcx>>314 fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, &'tcx LayoutError<'tcx>> { 315 let tcx = layout_cx.tcx; 316 let mut_raw_ptr = Ty::new_ptr(tcx,TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut }); 317 let const_raw_ptr = Ty::new_ptr(tcx,TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not }); 318 Ok(Self { 319 unit: layout_cx.layout_of(Ty::new_unit(tcx,))?, 320 i8: layout_cx.layout_of(tcx.types.i8)?, 321 i16: layout_cx.layout_of(tcx.types.i16)?, 322 i32: layout_cx.layout_of(tcx.types.i32)?, 323 i64: layout_cx.layout_of(tcx.types.i64)?, 324 i128: layout_cx.layout_of(tcx.types.i128)?, 325 isize: layout_cx.layout_of(tcx.types.isize)?, 326 u8: layout_cx.layout_of(tcx.types.u8)?, 327 u16: layout_cx.layout_of(tcx.types.u16)?, 328 u32: layout_cx.layout_of(tcx.types.u32)?, 329 u64: layout_cx.layout_of(tcx.types.u64)?, 330 u128: layout_cx.layout_of(tcx.types.u128)?, 331 usize: layout_cx.layout_of(tcx.types.usize)?, 332 bool: layout_cx.layout_of(tcx.types.bool)?, 333 mut_raw_ptr: layout_cx.layout_of(mut_raw_ptr)?, 334 const_raw_ptr: layout_cx.layout_of(const_raw_ptr)?, 335 }) 336 } 337 uint(&self, size: Size) -> Option<TyAndLayout<'tcx>>338 pub fn uint(&self, size: Size) -> Option<TyAndLayout<'tcx>> { 339 match size.bits() { 340 8 => Some(self.u8), 341 16 => Some(self.u16), 342 32 => Some(self.u32), 343 64 => Some(self.u64), 344 128 => Some(self.u128), 345 _ => None, 346 } 347 } 348 int(&self, size: Size) -> Option<TyAndLayout<'tcx>>349 pub fn int(&self, size: Size) -> Option<TyAndLayout<'tcx>> { 350 match size.bits() { 351 8 => Some(self.i8), 352 16 => Some(self.i16), 353 32 => Some(self.i32), 354 64 => Some(self.i64), 355 128 => Some(self.i128), 356 _ => None, 357 } 358 } 359 } 360 361 /// The machine itself. 362 /// 363 /// If you add anything here that stores machine values, remember to update 364 /// `visit_all_machine_values`! 365 pub struct MiriMachine<'mir, 'tcx> { 366 // We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access. 367 pub tcx: TyCtxt<'tcx>, 368 369 /// Global data for borrow tracking. 370 pub borrow_tracker: Option<borrow_tracker::GlobalState>, 371 372 /// Data race detector global data. 373 pub data_race: Option<data_race::GlobalState>, 374 375 /// Ptr-int-cast module global data. 376 pub intptrcast: intptrcast::GlobalState, 377 378 /// Environment variables set by `setenv`. 379 /// Miri does not expose env vars from the host to the emulated program. 380 pub(crate) env_vars: EnvVars<'tcx>, 381 382 /// Return place of the main function. 383 pub(crate) main_fn_ret_place: Option<MemPlace<Provenance>>, 384 385 /// Program arguments (`Option` because we can only initialize them after creating the ecx). 386 /// These are *pointers* to argc/argv because macOS. 387 /// We also need the full command line as one string because of Windows. 388 pub(crate) argc: Option<MemPlace<Provenance>>, 389 pub(crate) argv: Option<MemPlace<Provenance>>, 390 pub(crate) cmd_line: Option<MemPlace<Provenance>>, 391 392 /// TLS state. 393 pub(crate) tls: TlsData<'tcx>, 394 395 /// What should Miri do when an op requires communicating with the host, 396 /// such as accessing host env vars, random number generation, and 397 /// file system access. 398 pub(crate) isolated_op: IsolatedOp, 399 400 /// Whether to enforce the validity invariant. 401 pub(crate) validate: bool, 402 403 /// Whether to enforce [ABI](Abi) of function calls. 404 pub(crate) enforce_abi: bool, 405 406 /// The table of file descriptors. 407 pub(crate) file_handler: shims::unix::FileHandler, 408 /// The table of directory descriptors. 409 pub(crate) dir_handler: shims::unix::DirHandler, 410 411 /// This machine's monotone clock. 412 pub(crate) clock: Clock, 413 414 /// The set of threads. 415 pub(crate) threads: ThreadManager<'mir, 'tcx>, 416 417 /// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri. 418 pub(crate) layouts: PrimitiveLayouts<'tcx>, 419 420 /// Allocations that are considered roots of static memory (that may leak). 421 pub(crate) static_roots: Vec<AllocId>, 422 423 /// The `measureme` profiler used to record timing information about 424 /// the emulated program. 425 profiler: Option<measureme::Profiler>, 426 /// Used with `profiler` to cache the `StringId`s for event names 427 /// uesd with `measureme`. 428 string_cache: FxHashMap<String, measureme::StringId>, 429 430 /// Cache of `Instance` exported under the given `Symbol` name. 431 /// `None` means no `Instance` exported under the given name is found. 432 pub(crate) exported_symbols_cache: FxHashMap<Symbol, Option<Instance<'tcx>>>, 433 434 /// Whether to raise a panic in the context of the evaluated process when unsupported 435 /// functionality is encountered. If `false`, an error is propagated in the Miri application context 436 /// instead (default behavior) 437 pub(crate) panic_on_unsupported: bool, 438 439 /// Equivalent setting as RUST_BACKTRACE on encountering an error. 440 pub(crate) backtrace_style: BacktraceStyle, 441 442 /// Crates which are considered local for the purposes of error reporting. 443 pub(crate) local_crates: Vec<CrateNum>, 444 445 /// Mapping extern static names to their base pointer. 446 extern_statics: FxHashMap<Symbol, Pointer<Provenance>>, 447 448 /// The random number generator used for resolving non-determinism. 449 /// Needs to be queried by ptr_to_int, hence needs interior mutability. 450 pub(crate) rng: RefCell<StdRng>, 451 452 /// The allocation IDs to report when they are being allocated 453 /// (helps for debugging memory leaks and use after free bugs). 454 tracked_alloc_ids: FxHashSet<AllocId>, 455 456 /// Controls whether alignment of memory accesses is being checked. 457 pub(crate) check_alignment: AlignmentCheck, 458 459 /// Failure rate of compare_exchange_weak, between 0.0 and 1.0 460 pub(crate) cmpxchg_weak_failure_rate: f64, 461 462 /// Corresponds to -Zmiri-mute-stdout-stderr and doesn't write the output but acts as if it succeeded. 463 pub(crate) mute_stdout_stderr: bool, 464 465 /// Whether weak memory emulation is enabled 466 pub(crate) weak_memory: bool, 467 468 /// The probability of the active thread being preempted at the end of each basic block. 469 pub(crate) preemption_rate: f64, 470 471 /// If `Some`, we will report the current stack every N basic blocks. 472 pub(crate) report_progress: Option<u32>, 473 // The total number of blocks that have been executed. 474 pub(crate) basic_block_count: u64, 475 476 /// Handle of the optional shared object file for external functions. 477 #[cfg(target_os = "linux")] 478 pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>, 479 #[cfg(not(target_os = "linux"))] 480 pub external_so_lib: Option<!>, 481 482 /// Run a garbage collector for BorTags every N basic blocks. 483 pub(crate) gc_interval: u32, 484 /// The number of blocks that passed since the last BorTag GC pass. 485 pub(crate) since_gc: u32, 486 487 /// The number of CPUs to be reported by miri. 488 pub(crate) num_cpus: u32, 489 490 /// Determines Miri's page size and associated values 491 pub(crate) page_size: u64, 492 pub(crate) stack_addr: u64, 493 pub(crate) stack_size: u64, 494 495 /// Whether to collect a backtrace when each allocation is created, just in case it leaks. 496 pub(crate) collect_leak_backtraces: bool, 497 } 498 499 impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self500 pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self { 501 let tcx = layout_cx.tcx; 502 let local_crates = helpers::get_local_crates(tcx); 503 let layouts = 504 PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types"); 505 let profiler = config.measureme_out.as_ref().map(|out| { 506 let crate_name = layout_cx 507 .tcx 508 .sess 509 .opts 510 .crate_name 511 .clone() 512 .unwrap_or_else(|| "unknown-crate".to_string()); 513 let pid = process::id(); 514 // We adopt the same naming scheme for the profiler output that rustc uses. In rustc, 515 // the PID is padded so that the nondeterministic value of the PID does not spread 516 // nondeterminisim to the allocator. In Miri we are not aiming for such performance 517 // control, we just pad for consistency with rustc. 518 let filename = format!("{crate_name}-{pid:07}"); 519 let path = Path::new(out).join(filename); 520 measureme::Profiler::new(path).expect("Couldn't create `measureme` profiler") 521 }); 522 let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0)); 523 let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config)); 524 let data_race = config.data_race_detector.then(|| data_race::GlobalState::new(config)); 525 // Determine page size, stack address, and stack size. 526 // These values are mostly meaningless, but the stack address is also where we start 527 // allocating physical integer addresses for all allocations. 528 let page_size = if let Some(page_size) = config.page_size { 529 page_size 530 } else { 531 let target = &tcx.sess.target; 532 match target.arch.as_ref() { 533 "wasm32" | "wasm64" => 64 * 1024, // https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances 534 "aarch64" => 535 if target.options.vendor.as_ref() == "apple" { 536 // No "definitive" source, but see: 537 // https://www.wwdcnotes.com/notes/wwdc20/10214/ 538 // https://github.com/ziglang/zig/issues/11308 etc. 539 16 * 1024 540 } else { 541 4 * 1024 542 }, 543 _ => 4 * 1024, 544 } 545 }; 546 // On 16bit targets, 32 pages is more than the entire address space! 547 let stack_addr = if tcx.pointer_size().bits() < 32 { page_size } else { page_size * 32 }; 548 let stack_size = 549 if tcx.pointer_size().bits() < 32 { page_size * 4 } else { page_size * 16 }; 550 MiriMachine { 551 tcx, 552 borrow_tracker, 553 data_race, 554 intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config, stack_addr)), 555 // `env_vars` depends on a full interpreter so we cannot properly initialize it yet. 556 env_vars: EnvVars::default(), 557 main_fn_ret_place: None, 558 argc: None, 559 argv: None, 560 cmd_line: None, 561 tls: TlsData::default(), 562 isolated_op: config.isolated_op, 563 validate: config.validate, 564 enforce_abi: config.check_abi, 565 file_handler: FileHandler::new(config.mute_stdout_stderr), 566 dir_handler: Default::default(), 567 layouts, 568 threads: ThreadManager::default(), 569 static_roots: Vec::new(), 570 profiler, 571 string_cache: Default::default(), 572 exported_symbols_cache: FxHashMap::default(), 573 panic_on_unsupported: config.panic_on_unsupported, 574 backtrace_style: config.backtrace_style, 575 local_crates, 576 extern_statics: FxHashMap::default(), 577 rng: RefCell::new(rng), 578 tracked_alloc_ids: config.tracked_alloc_ids.clone(), 579 check_alignment: config.check_alignment, 580 cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate, 581 mute_stdout_stderr: config.mute_stdout_stderr, 582 weak_memory: config.weak_memory_emulation, 583 preemption_rate: config.preemption_rate, 584 report_progress: config.report_progress, 585 basic_block_count: 0, 586 clock: Clock::new(config.isolated_op == IsolatedOp::Allow), 587 #[cfg(target_os = "linux")] 588 external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| { 589 let target_triple = layout_cx.tcx.sess.opts.target_triple.triple(); 590 // Check if host target == the session target. 591 if env!("TARGET") != target_triple { 592 panic!( 593 "calling external C functions in linked .so file requires host and target to be the same: host={}, target={}", 594 env!("TARGET"), 595 target_triple, 596 ); 597 } 598 // Note: it is the user's responsibility to provide a correct SO file. 599 // WATCH OUT: If an invalid/incorrect SO file is specified, this can cause 600 // undefined behaviour in Miri itself! 601 ( 602 unsafe { 603 libloading::Library::new(lib_file_path) 604 .expect("failed to read specified extern shared object file") 605 }, 606 lib_file_path.clone(), 607 ) 608 }), 609 #[cfg(not(target_os = "linux"))] 610 external_so_lib: config.external_so_file.as_ref().map(|_| { 611 panic!("loading external .so files is only supported on Linux") 612 }), 613 gc_interval: config.gc_interval, 614 since_gc: 0, 615 num_cpus: config.num_cpus, 616 page_size, 617 stack_addr, 618 stack_size, 619 collect_leak_backtraces: config.collect_leak_backtraces, 620 } 621 } 622 late_init( this: &mut MiriInterpCx<'mir, 'tcx>, config: &MiriConfig, on_main_stack_empty: StackEmptyCallback<'mir, 'tcx>, ) -> InterpResult<'tcx>623 pub(crate) fn late_init( 624 this: &mut MiriInterpCx<'mir, 'tcx>, 625 config: &MiriConfig, 626 on_main_stack_empty: StackEmptyCallback<'mir, 'tcx>, 627 ) -> InterpResult<'tcx> { 628 EnvVars::init(this, config)?; 629 MiriMachine::init_extern_statics(this)?; 630 ThreadManager::init(this, on_main_stack_empty); 631 Ok(()) 632 } 633 add_extern_static( this: &mut MiriInterpCx<'mir, 'tcx>, name: &str, ptr: Pointer<Option<Provenance>>, )634 fn add_extern_static( 635 this: &mut MiriInterpCx<'mir, 'tcx>, 636 name: &str, 637 ptr: Pointer<Option<Provenance>>, 638 ) { 639 // This got just allocated, so there definitely is a pointer here. 640 let ptr = ptr.into_pointer_or_addr().unwrap(); 641 this.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap(); 642 } 643 alloc_extern_static( this: &mut MiriInterpCx<'mir, 'tcx>, name: &str, val: ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx>644 fn alloc_extern_static( 645 this: &mut MiriInterpCx<'mir, 'tcx>, 646 name: &str, 647 val: ImmTy<'tcx, Provenance>, 648 ) -> InterpResult<'tcx> { 649 let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?; 650 this.write_immediate(*val, &place.into())?; 651 Self::add_extern_static(this, name, place.ptr); 652 Ok(()) 653 } 654 655 /// Sets up the "extern statics" for this machine. init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>656 fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { 657 // "__rust_no_alloc_shim_is_unstable" 658 let val = ImmTy::from_int(0, this.machine.layouts.u8); 659 Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?; 660 661 match this.tcx.sess.target.os.as_ref() { 662 "linux" => { 663 // "environ" 664 Self::add_extern_static( 665 this, 666 "environ", 667 this.machine.env_vars.environ.unwrap().ptr, 668 ); 669 // A couple zero-initialized pointer-sized extern statics. 670 // Most of them are for weak symbols, which we all set to null (indicating that the 671 // symbol is not supported, and triggering fallback code which ends up calling a 672 // syscall that we do support). 673 for name in &["__cxa_thread_atexit_impl", "getrandom", "statx", "__clock_gettime64"] 674 { 675 let val = ImmTy::from_int(0, this.machine.layouts.usize); 676 Self::alloc_extern_static(this, name, val)?; 677 } 678 } 679 "freebsd" => { 680 // "environ" 681 Self::add_extern_static( 682 this, 683 "environ", 684 this.machine.env_vars.environ.unwrap().ptr, 685 ); 686 } 687 "android" => { 688 // "signal" 689 let layout = this.machine.layouts.const_raw_ptr; 690 let dlsym = Dlsym::from_str("signal".as_bytes(), &this.tcx.sess.target.os)? 691 .expect("`signal` must be an actual dlsym on android"); 692 let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym)); 693 let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout); 694 Self::alloc_extern_static(this, "signal", val)?; 695 // A couple zero-initialized pointer-sized extern statics. 696 // Most of them are for weak symbols, which we all set to null (indicating that the 697 // symbol is not supported, and triggering fallback code.) 698 for name in &["bsd_signal"] { 699 let val = ImmTy::from_int(0, this.machine.layouts.usize); 700 Self::alloc_extern_static(this, name, val)?; 701 } 702 } 703 "windows" => { 704 // "_tls_used" 705 // This is some obscure hack that is part of the Windows TLS story. It's a `u8`. 706 let val = ImmTy::from_int(0, this.machine.layouts.u8); 707 Self::alloc_extern_static(this, "_tls_used", val)?; 708 } 709 _ => {} // No "extern statics" supported on this target 710 } 711 Ok(()) 712 } 713 communicate(&self) -> bool714 pub(crate) fn communicate(&self) -> bool { 715 self.isolated_op == IsolatedOp::Allow 716 } 717 718 /// Check whether the stack frame that this `FrameInfo` refers to is part of a local crate. is_local(&self, frame: &FrameInfo<'_>) -> bool719 pub(crate) fn is_local(&self, frame: &FrameInfo<'_>) -> bool { 720 let def_id = frame.instance.def_id(); 721 def_id.is_local() || self.local_crates.contains(&def_id.krate) 722 } 723 724 /// Called when the interpreter is going to shut down abnormally, such as due to a Ctrl-C. handle_abnormal_termination(&mut self)725 pub(crate) fn handle_abnormal_termination(&mut self) { 726 // All strings in the profile data are stored in a single string table which is not 727 // written to disk until the profiler is dropped. If the interpreter exits without dropping 728 // the profiler, it is not possible to interpret the profile data and all measureme tools 729 // will panic when given the file. 730 drop(self.profiler.take()); 731 } 732 round_up_to_multiple_of_page_size(&self, length: u64) -> Option<u64>733 pub(crate) fn round_up_to_multiple_of_page_size(&self, length: u64) -> Option<u64> { 734 #[allow(clippy::arithmetic_side_effects)] // page size is nonzero 735 (length.checked_add(self.page_size - 1)? / self.page_size).checked_mul(self.page_size) 736 } 737 page_align(&self) -> Align738 pub(crate) fn page_align(&self) -> Align { 739 Align::from_bytes(self.page_size).unwrap() 740 } 741 } 742 743 impl VisitTags for MiriMachine<'_, '_> { visit_tags(&self, visit: &mut dyn FnMut(BorTag))744 fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { 745 #[rustfmt::skip] 746 let MiriMachine { 747 threads, 748 tls, 749 env_vars, 750 main_fn_ret_place, 751 argc, 752 argv, 753 cmd_line, 754 extern_statics, 755 dir_handler, 756 borrow_tracker, 757 data_race, 758 intptrcast, 759 file_handler, 760 tcx: _, 761 isolated_op: _, 762 validate: _, 763 enforce_abi: _, 764 clock: _, 765 layouts: _, 766 static_roots: _, 767 profiler: _, 768 string_cache: _, 769 exported_symbols_cache: _, 770 panic_on_unsupported: _, 771 backtrace_style: _, 772 local_crates: _, 773 rng: _, 774 tracked_alloc_ids: _, 775 check_alignment: _, 776 cmpxchg_weak_failure_rate: _, 777 mute_stdout_stderr: _, 778 weak_memory: _, 779 preemption_rate: _, 780 report_progress: _, 781 basic_block_count: _, 782 external_so_lib: _, 783 gc_interval: _, 784 since_gc: _, 785 num_cpus: _, 786 page_size: _, 787 stack_addr: _, 788 stack_size: _, 789 collect_leak_backtraces: _, 790 } = self; 791 792 threads.visit_tags(visit); 793 tls.visit_tags(visit); 794 env_vars.visit_tags(visit); 795 dir_handler.visit_tags(visit); 796 file_handler.visit_tags(visit); 797 data_race.visit_tags(visit); 798 borrow_tracker.visit_tags(visit); 799 intptrcast.visit_tags(visit); 800 main_fn_ret_place.visit_tags(visit); 801 argc.visit_tags(visit); 802 argv.visit_tags(visit); 803 cmd_line.visit_tags(visit); 804 for ptr in extern_statics.values() { 805 ptr.visit_tags(visit); 806 } 807 } 808 } 809 810 /// A rustc InterpCx for Miri. 811 pub type MiriInterpCx<'mir, 'tcx> = InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>; 812 813 /// A little trait that's useful to be inherited by extension traits. 814 pub trait MiriInterpCxExt<'mir, 'tcx> { eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'mir, 'tcx>815 fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'mir, 'tcx>; eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'mir, 'tcx>816 fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'mir, 'tcx>; 817 } 818 impl<'mir, 'tcx> MiriInterpCxExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> { 819 #[inline(always)] eval_context_ref(&self) -> &MiriInterpCx<'mir, 'tcx>820 fn eval_context_ref(&self) -> &MiriInterpCx<'mir, 'tcx> { 821 self 822 } 823 #[inline(always)] eval_context_mut(&mut self) -> &mut MiriInterpCx<'mir, 'tcx>824 fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> { 825 self 826 } 827 } 828 829 /// Machine hook implementations. 830 impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { 831 type MemoryKind = MiriMemoryKind; 832 type ExtraFnVal = Dlsym; 833 834 type FrameExtra = FrameExtra<'tcx>; 835 type AllocExtra = AllocExtra<'tcx>; 836 837 type Provenance = Provenance; 838 type ProvenanceExtra = ProvenanceExtra; 839 type Bytes = Box<[u8]>; 840 841 type MemoryMap = MonoHashMap< 842 AllocId, 843 (MemoryKind<MiriMemoryKind>, Allocation<Provenance, Self::AllocExtra, Self::Bytes>), 844 >; 845 846 const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global); 847 848 const PANIC_ON_ALLOC_FAIL: bool = false; 849 850 #[inline(always)] enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> CheckAlignment851 fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> CheckAlignment { 852 if ecx.machine.check_alignment == AlignmentCheck::None { 853 CheckAlignment::No 854 } else { 855 CheckAlignment::Error 856 } 857 } 858 859 #[inline(always)] use_addr_for_alignment_check(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool860 fn use_addr_for_alignment_check(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { 861 ecx.machine.check_alignment == AlignmentCheck::Int 862 } 863 alignment_check_failed( _ecx: &InterpCx<'mir, 'tcx, Self>, has: Align, required: Align, _check: CheckAlignment, ) -> InterpResult<'tcx, ()>864 fn alignment_check_failed( 865 _ecx: &InterpCx<'mir, 'tcx, Self>, 866 has: Align, 867 required: Align, 868 _check: CheckAlignment, 869 ) -> InterpResult<'tcx, ()> { 870 throw_ub!(AlignmentCheckFailed { has, required }) 871 } 872 873 #[inline(always)] enforce_validity(ecx: &MiriInterpCx<'mir, 'tcx>, _layout: TyAndLayout<'tcx>) -> bool874 fn enforce_validity(ecx: &MiriInterpCx<'mir, 'tcx>, _layout: TyAndLayout<'tcx>) -> bool { 875 ecx.machine.validate 876 } 877 878 #[inline(always)] enforce_abi(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool879 fn enforce_abi(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { 880 ecx.machine.enforce_abi 881 } 882 883 #[inline(always)] ignore_optional_overflow_checks(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool884 fn ignore_optional_overflow_checks(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { 885 !ecx.tcx.sess.overflow_checks() 886 } 887 888 #[inline(always)] find_mir_or_eval_fn( ecx: &mut MiriInterpCx<'mir, 'tcx>, instance: ty::Instance<'tcx>, abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &PlaceTy<'tcx, Provenance>, ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>889 fn find_mir_or_eval_fn( 890 ecx: &mut MiriInterpCx<'mir, 'tcx>, 891 instance: ty::Instance<'tcx>, 892 abi: Abi, 893 args: &[OpTy<'tcx, Provenance>], 894 dest: &PlaceTy<'tcx, Provenance>, 895 ret: Option<mir::BasicBlock>, 896 unwind: mir::UnwindAction, 897 ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { 898 ecx.find_mir_or_eval_fn(instance, abi, args, dest, ret, unwind) 899 } 900 901 #[inline(always)] call_extra_fn( ecx: &mut MiriInterpCx<'mir, 'tcx>, fn_val: Dlsym, abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &PlaceTy<'tcx, Provenance>, ret: Option<mir::BasicBlock>, _unwind: mir::UnwindAction, ) -> InterpResult<'tcx>902 fn call_extra_fn( 903 ecx: &mut MiriInterpCx<'mir, 'tcx>, 904 fn_val: Dlsym, 905 abi: Abi, 906 args: &[OpTy<'tcx, Provenance>], 907 dest: &PlaceTy<'tcx, Provenance>, 908 ret: Option<mir::BasicBlock>, 909 _unwind: mir::UnwindAction, 910 ) -> InterpResult<'tcx> { 911 ecx.call_dlsym(fn_val, abi, args, dest, ret) 912 } 913 914 #[inline(always)] call_intrinsic( ecx: &mut MiriInterpCx<'mir, 'tcx>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, Provenance>], dest: &PlaceTy<'tcx, Provenance>, ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction, ) -> InterpResult<'tcx>915 fn call_intrinsic( 916 ecx: &mut MiriInterpCx<'mir, 'tcx>, 917 instance: ty::Instance<'tcx>, 918 args: &[OpTy<'tcx, Provenance>], 919 dest: &PlaceTy<'tcx, Provenance>, 920 ret: Option<mir::BasicBlock>, 921 unwind: mir::UnwindAction, 922 ) -> InterpResult<'tcx> { 923 ecx.call_intrinsic(instance, args, dest, ret, unwind) 924 } 925 926 #[inline(always)] assert_panic( ecx: &mut MiriInterpCx<'mir, 'tcx>, msg: &mir::AssertMessage<'tcx>, unwind: mir::UnwindAction, ) -> InterpResult<'tcx>927 fn assert_panic( 928 ecx: &mut MiriInterpCx<'mir, 'tcx>, 929 msg: &mir::AssertMessage<'tcx>, 930 unwind: mir::UnwindAction, 931 ) -> InterpResult<'tcx> { 932 ecx.assert_panic(msg, unwind) 933 } 934 935 #[inline(always)] abort(_ecx: &mut MiriInterpCx<'mir, 'tcx>, msg: String) -> InterpResult<'tcx, !>936 fn abort(_ecx: &mut MiriInterpCx<'mir, 'tcx>, msg: String) -> InterpResult<'tcx, !> { 937 throw_machine_stop!(TerminationInfo::Abort(msg)) 938 } 939 940 #[inline(always)] binary_ptr_op( ecx: &MiriInterpCx<'mir, 'tcx>, bin_op: mir::BinOp, left: &ImmTy<'tcx, Provenance>, right: &ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx, (Scalar<Provenance>, bool, Ty<'tcx>)>941 fn binary_ptr_op( 942 ecx: &MiriInterpCx<'mir, 'tcx>, 943 bin_op: mir::BinOp, 944 left: &ImmTy<'tcx, Provenance>, 945 right: &ImmTy<'tcx, Provenance>, 946 ) -> InterpResult<'tcx, (Scalar<Provenance>, bool, Ty<'tcx>)> { 947 ecx.binary_ptr_op(bin_op, left, right) 948 } 949 thread_local_static_base_pointer( ecx: &mut MiriInterpCx<'mir, 'tcx>, def_id: DefId, ) -> InterpResult<'tcx, Pointer<Provenance>>950 fn thread_local_static_base_pointer( 951 ecx: &mut MiriInterpCx<'mir, 'tcx>, 952 def_id: DefId, 953 ) -> InterpResult<'tcx, Pointer<Provenance>> { 954 ecx.get_or_create_thread_local_alloc(def_id) 955 } 956 extern_static_base_pointer( ecx: &MiriInterpCx<'mir, 'tcx>, def_id: DefId, ) -> InterpResult<'tcx, Pointer<Provenance>>957 fn extern_static_base_pointer( 958 ecx: &MiriInterpCx<'mir, 'tcx>, 959 def_id: DefId, 960 ) -> InterpResult<'tcx, Pointer<Provenance>> { 961 let link_name = ecx.item_link_name(def_id); 962 if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) { 963 // Various parts of the engine rely on `get_alloc_info` for size and alignment 964 // information. That uses the type information of this static. 965 // Make sure it matches the Miri allocation for this. 966 let Provenance::Concrete { alloc_id, .. } = ptr.provenance else { 967 panic!("extern_statics cannot contain wildcards") 968 }; 969 let (shim_size, shim_align, _kind) = ecx.get_alloc_info(alloc_id); 970 let def_ty = ecx.tcx.type_of(def_id).subst_identity(); 971 let extern_decl_layout = ecx.tcx.layout_of(ty::ParamEnv::empty().and(def_ty)).unwrap(); 972 if extern_decl_layout.size != shim_size || extern_decl_layout.align.abi != shim_align { 973 throw_unsup_format!( 974 "`extern` static `{name}` from crate `{krate}` has been declared \ 975 with a size of {decl_size} bytes and alignment of {decl_align} bytes, \ 976 but Miri emulates it via an extern static shim \ 977 with a size of {shim_size} bytes and alignment of {shim_align} bytes", 978 name = ecx.tcx.def_path_str(def_id), 979 krate = ecx.tcx.crate_name(def_id.krate), 980 decl_size = extern_decl_layout.size.bytes(), 981 decl_align = extern_decl_layout.align.abi.bytes(), 982 shim_size = shim_size.bytes(), 983 shim_align = shim_align.bytes(), 984 ) 985 } 986 Ok(ptr) 987 } else { 988 throw_unsup_format!( 989 "`extern` static `{name}` from crate `{krate}` is not supported by Miri", 990 name = ecx.tcx.def_path_str(def_id), 991 krate = ecx.tcx.crate_name(def_id.krate), 992 ) 993 } 994 } 995 adjust_allocation<'b>( ecx: &MiriInterpCx<'mir, 'tcx>, id: AllocId, alloc: Cow<'b, Allocation>, kind: Option<MemoryKind<Self::MemoryKind>>, ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>>996 fn adjust_allocation<'b>( 997 ecx: &MiriInterpCx<'mir, 'tcx>, 998 id: AllocId, 999 alloc: Cow<'b, Allocation>, 1000 kind: Option<MemoryKind<Self::MemoryKind>>, 1001 ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>> { 1002 let kind = kind.expect("we set our STATIC_KIND so this cannot be None"); 1003 if ecx.machine.tracked_alloc_ids.contains(&id) { 1004 ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc( 1005 id, 1006 alloc.size(), 1007 alloc.align, 1008 kind, 1009 )); 1010 } 1011 1012 let alloc = alloc.into_owned(); 1013 let borrow_tracker = ecx 1014 .machine 1015 .borrow_tracker 1016 .as_ref() 1017 .map(|bt| bt.borrow_mut().new_allocation(id, alloc.size(), kind, &ecx.machine)); 1018 1019 let race_alloc = ecx.machine.data_race.as_ref().map(|data_race| { 1020 data_race::AllocState::new_allocation( 1021 data_race, 1022 &ecx.machine.threads, 1023 alloc.size(), 1024 kind, 1025 ecx.machine.current_span(), 1026 ) 1027 }); 1028 let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocState::new_allocation); 1029 1030 // If an allocation is leaked, we want to report a backtrace to indicate where it was 1031 // allocated. We don't need to record a backtrace for allocations which are allowed to 1032 // leak. 1033 let backtrace = if kind.may_leak() || !ecx.machine.collect_leak_backtraces { 1034 None 1035 } else { 1036 Some(ecx.generate_stacktrace()) 1037 }; 1038 1039 let alloc: Allocation<Provenance, Self::AllocExtra> = alloc.adjust_from_tcx( 1040 &ecx.tcx, 1041 AllocExtra { 1042 borrow_tracker, 1043 data_race: race_alloc, 1044 weak_memory: buffer_alloc, 1045 backtrace, 1046 }, 1047 |ptr| ecx.global_base_pointer(ptr), 1048 )?; 1049 Ok(Cow::Owned(alloc)) 1050 } 1051 adjust_alloc_base_pointer( ecx: &MiriInterpCx<'mir, 'tcx>, ptr: Pointer<AllocId>, ) -> InterpResult<'tcx, Pointer<Provenance>>1052 fn adjust_alloc_base_pointer( 1053 ecx: &MiriInterpCx<'mir, 'tcx>, 1054 ptr: Pointer<AllocId>, 1055 ) -> InterpResult<'tcx, Pointer<Provenance>> { 1056 if cfg!(debug_assertions) { 1057 // The machine promises to never call us on thread-local or extern statics. 1058 let alloc_id = ptr.provenance; 1059 match ecx.tcx.try_get_global_alloc(alloc_id) { 1060 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => { 1061 panic!("adjust_alloc_base_pointer called on thread-local static") 1062 } 1063 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => { 1064 panic!("adjust_alloc_base_pointer called on extern static") 1065 } 1066 _ => {} 1067 } 1068 } 1069 let absolute_addr = intptrcast::GlobalStateInner::rel_ptr_to_addr(ecx, ptr)?; 1070 let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker { 1071 borrow_tracker.borrow_mut().base_ptr_tag(ptr.provenance, &ecx.machine) 1072 } else { 1073 // Value does not matter, SB is disabled 1074 BorTag::default() 1075 }; 1076 Ok(Pointer::new( 1077 Provenance::Concrete { alloc_id: ptr.provenance, tag }, 1078 Size::from_bytes(absolute_addr), 1079 )) 1080 } 1081 1082 #[inline(always)] ptr_from_addr_cast( ecx: &MiriInterpCx<'mir, 'tcx>, addr: u64, ) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>>1083 fn ptr_from_addr_cast( 1084 ecx: &MiriInterpCx<'mir, 'tcx>, 1085 addr: u64, 1086 ) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>> { 1087 intptrcast::GlobalStateInner::ptr_from_addr_cast(ecx, addr) 1088 } 1089 expose_ptr( ecx: &mut InterpCx<'mir, 'tcx, Self>, ptr: Pointer<Self::Provenance>, ) -> InterpResult<'tcx>1090 fn expose_ptr( 1091 ecx: &mut InterpCx<'mir, 'tcx, Self>, 1092 ptr: Pointer<Self::Provenance>, 1093 ) -> InterpResult<'tcx> { 1094 match ptr.provenance { 1095 Provenance::Concrete { alloc_id, tag } => 1096 intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, tag), 1097 Provenance::Wildcard => { 1098 // No need to do anything for wildcard pointers as 1099 // their provenances have already been previously exposed. 1100 Ok(()) 1101 } 1102 } 1103 } 1104 1105 /// Convert a pointer with provenance into an allocation-offset pair, 1106 /// or a `None` with an absolute address if that conversion is not possible. ptr_get_alloc( ecx: &MiriInterpCx<'mir, 'tcx>, ptr: Pointer<Self::Provenance>, ) -> Option<(AllocId, Size, Self::ProvenanceExtra)>1107 fn ptr_get_alloc( 1108 ecx: &MiriInterpCx<'mir, 'tcx>, 1109 ptr: Pointer<Self::Provenance>, 1110 ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> { 1111 let rel = intptrcast::GlobalStateInner::abs_ptr_to_rel(ecx, ptr); 1112 1113 rel.map(|(alloc_id, size)| { 1114 let tag = match ptr.provenance { 1115 Provenance::Concrete { tag, .. } => ProvenanceExtra::Concrete(tag), 1116 Provenance::Wildcard => ProvenanceExtra::Wildcard, 1117 }; 1118 (alloc_id, size, tag) 1119 }) 1120 } 1121 1122 #[inline(always)] before_memory_read( _tcx: TyCtxt<'tcx>, machine: &Self, alloc_extra: &AllocExtra<'tcx>, (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra), range: AllocRange, ) -> InterpResult<'tcx>1123 fn before_memory_read( 1124 _tcx: TyCtxt<'tcx>, 1125 machine: &Self, 1126 alloc_extra: &AllocExtra<'tcx>, 1127 (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra), 1128 range: AllocRange, 1129 ) -> InterpResult<'tcx> { 1130 if let Some(data_race) = &alloc_extra.data_race { 1131 data_race.read(alloc_id, range, machine)?; 1132 } 1133 if let Some(borrow_tracker) = &alloc_extra.borrow_tracker { 1134 borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?; 1135 } 1136 if let Some(weak_memory) = &alloc_extra.weak_memory { 1137 weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap()); 1138 } 1139 Ok(()) 1140 } 1141 1142 #[inline(always)] before_memory_write( _tcx: TyCtxt<'tcx>, machine: &mut Self, alloc_extra: &mut AllocExtra<'tcx>, (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra), range: AllocRange, ) -> InterpResult<'tcx>1143 fn before_memory_write( 1144 _tcx: TyCtxt<'tcx>, 1145 machine: &mut Self, 1146 alloc_extra: &mut AllocExtra<'tcx>, 1147 (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra), 1148 range: AllocRange, 1149 ) -> InterpResult<'tcx> { 1150 if let Some(data_race) = &mut alloc_extra.data_race { 1151 data_race.write(alloc_id, range, machine)?; 1152 } 1153 if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { 1154 borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?; 1155 } 1156 if let Some(weak_memory) = &alloc_extra.weak_memory { 1157 weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap()); 1158 } 1159 Ok(()) 1160 } 1161 1162 #[inline(always)] before_memory_deallocation( _tcx: TyCtxt<'tcx>, machine: &mut Self, alloc_extra: &mut AllocExtra<'tcx>, (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra), range: AllocRange, ) -> InterpResult<'tcx>1163 fn before_memory_deallocation( 1164 _tcx: TyCtxt<'tcx>, 1165 machine: &mut Self, 1166 alloc_extra: &mut AllocExtra<'tcx>, 1167 (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra), 1168 range: AllocRange, 1169 ) -> InterpResult<'tcx> { 1170 if machine.tracked_alloc_ids.contains(&alloc_id) { 1171 machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id)); 1172 } 1173 if let Some(data_race) = &mut alloc_extra.data_race { 1174 data_race.deallocate(alloc_id, range, machine)?; 1175 } 1176 if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { 1177 borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, range, machine)?; 1178 } 1179 Ok(()) 1180 } 1181 1182 #[inline(always)] retag_ptr_value( ecx: &mut InterpCx<'mir, 'tcx, Self>, kind: mir::RetagKind, val: &ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>>1183 fn retag_ptr_value( 1184 ecx: &mut InterpCx<'mir, 'tcx, Self>, 1185 kind: mir::RetagKind, 1186 val: &ImmTy<'tcx, Provenance>, 1187 ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { 1188 if ecx.machine.borrow_tracker.is_some() { 1189 ecx.retag_ptr_value(kind, val) 1190 } else { 1191 Ok(val.clone()) 1192 } 1193 } 1194 1195 #[inline(always)] retag_place_contents( ecx: &mut InterpCx<'mir, 'tcx, Self>, kind: mir::RetagKind, place: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx>1196 fn retag_place_contents( 1197 ecx: &mut InterpCx<'mir, 'tcx, Self>, 1198 kind: mir::RetagKind, 1199 place: &PlaceTy<'tcx, Provenance>, 1200 ) -> InterpResult<'tcx> { 1201 if ecx.machine.borrow_tracker.is_some() { 1202 ecx.retag_place_contents(kind, place)?; 1203 } 1204 Ok(()) 1205 } 1206 1207 #[inline(always)] init_frame_extra( ecx: &mut InterpCx<'mir, 'tcx, Self>, frame: Frame<'mir, 'tcx, Provenance>, ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>>1208 fn init_frame_extra( 1209 ecx: &mut InterpCx<'mir, 'tcx, Self>, 1210 frame: Frame<'mir, 'tcx, Provenance>, 1211 ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> { 1212 // Start recording our event before doing anything else 1213 let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() { 1214 let fn_name = frame.instance.to_string(); 1215 let entry = ecx.machine.string_cache.entry(fn_name.clone()); 1216 let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name)); 1217 1218 Some(profiler.start_recording_interval_event_detached( 1219 *name, 1220 measureme::EventId::from_label(*name), 1221 ecx.get_active_thread().to_u32(), 1222 )) 1223 } else { 1224 None 1225 }; 1226 1227 let borrow_tracker = ecx.machine.borrow_tracker.as_ref(); 1228 1229 let extra = FrameExtra { 1230 borrow_tracker: borrow_tracker.map(|bt| bt.borrow_mut().new_frame(&ecx.machine)), 1231 catch_unwind: None, 1232 timing, 1233 is_user_relevant: ecx.machine.is_user_relevant(&frame), 1234 }; 1235 1236 Ok(frame.with_extra(extra)) 1237 } 1238 stack<'a>( ecx: &'a InterpCx<'mir, 'tcx, Self>, ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>]1239 fn stack<'a>( 1240 ecx: &'a InterpCx<'mir, 'tcx, Self>, 1241 ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { 1242 ecx.active_thread_stack() 1243 } 1244 stack_mut<'a>( ecx: &'a mut InterpCx<'mir, 'tcx, Self>, ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>1245 fn stack_mut<'a>( 1246 ecx: &'a mut InterpCx<'mir, 'tcx, Self>, 1247 ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> { 1248 ecx.active_thread_stack_mut() 1249 } 1250 before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>1251 fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { 1252 ecx.machine.basic_block_count += 1u64; // a u64 that is only incremented by 1 will "never" overflow 1253 ecx.machine.since_gc += 1; 1254 // Possibly report our progress. 1255 if let Some(report_progress) = ecx.machine.report_progress { 1256 if ecx.machine.basic_block_count % u64::from(report_progress) == 0 { 1257 ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport { 1258 block_count: ecx.machine.basic_block_count, 1259 }); 1260 } 1261 } 1262 1263 // Search for BorTags to find all live pointers, then remove all other tags from borrow 1264 // stacks. 1265 // When debug assertions are enabled, run the GC as often as possible so that any cases 1266 // where it mistakenly removes an important tag become visible. 1267 if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval { 1268 ecx.machine.since_gc = 0; 1269 ecx.garbage_collect_tags()?; 1270 } 1271 1272 // These are our preemption points. 1273 ecx.maybe_preempt_active_thread(); 1274 1275 // Make sure some time passes. 1276 ecx.machine.clock.tick(); 1277 1278 Ok(()) 1279 } 1280 1281 #[inline(always)] after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>1282 fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { 1283 if ecx.frame().extra.is_user_relevant { 1284 // We just pushed a local frame, so we know that the topmost local frame is the topmost 1285 // frame. If we push a non-local frame, there's no need to do anything. 1286 let stack_len = ecx.active_thread_stack().len(); 1287 ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1); 1288 } 1289 if ecx.machine.borrow_tracker.is_some() { 1290 ecx.retag_return_place()?; 1291 } 1292 Ok(()) 1293 } 1294 1295 #[inline(always)] after_stack_pop( ecx: &mut InterpCx<'mir, 'tcx, Self>, mut frame: Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>, unwinding: bool, ) -> InterpResult<'tcx, StackPopJump>1296 fn after_stack_pop( 1297 ecx: &mut InterpCx<'mir, 'tcx, Self>, 1298 mut frame: Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>, 1299 unwinding: bool, 1300 ) -> InterpResult<'tcx, StackPopJump> { 1301 if frame.extra.is_user_relevant { 1302 // All that we store is whether or not the frame we just removed is local, so now we 1303 // have no idea where the next topmost local frame is. So we recompute it. 1304 // (If this ever becomes a bottleneck, we could have `push` store the previous 1305 // user-relevant frame and restore that here.) 1306 ecx.active_thread_mut().recompute_top_user_relevant_frame(); 1307 } 1308 let timing = frame.extra.timing.take(); 1309 if let Some(borrow_tracker) = &ecx.machine.borrow_tracker { 1310 borrow_tracker.borrow_mut().end_call(&frame.extra); 1311 } 1312 let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding); 1313 if let Some(profiler) = ecx.machine.profiler.as_ref() { 1314 profiler.finish_recording_interval_event(timing.unwrap()); 1315 } 1316 res 1317 } 1318 } 1319