]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/src/machine.rs
Auto merge of #99099 - Stargateur:phantomdata_debug, r=joshtriplett
[rust.git] / src / tools / miri / src / machine.rs
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
8 use rand::rngs::StdRng;
9 use rand::SeedableRng;
10
11 use rustc_ast::ast::Mutability;
12 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
13 #[allow(unused)]
14 use rustc_data_structures::static_assert_size;
15 use rustc_middle::{
16     mir,
17     ty::{
18         self,
19         layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout},
20         Instance, Ty, TyCtxt, TypeAndMut,
21     },
22 };
23 use rustc_span::def_id::{CrateNum, DefId};
24 use rustc_span::Symbol;
25 use rustc_target::abi::Size;
26 use rustc_target::spec::abi::Abi;
27
28 use crate::{
29     concurrency::{data_race, weak_memory},
30     shims::unix::FileHandler,
31     *,
32 };
33
34 // Some global facts about the emulated machine.
35 pub const PAGE_SIZE: u64 = 4 * 1024; // FIXME: adjust to target architecture
36 pub const STACK_ADDR: u64 = 32 * PAGE_SIZE; // not really about the "stack", but where we start assigning integer addresses to allocations
37 pub const STACK_SIZE: u64 = 16 * PAGE_SIZE; // whatever
38 pub const NUM_CPUS: u64 = 1;
39
40 /// Extra data stored with each stack frame
41 pub struct FrameData<'tcx> {
42     /// Extra data for Stacked Borrows.
43     pub stacked_borrows: Option<stacked_borrows::FrameExtra>,
44
45     /// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn`
46     /// called by `try`). When this frame is popped during unwinding a panic,
47     /// we stop unwinding, use the `CatchUnwindData` to handle catching.
48     pub catch_unwind: Option<CatchUnwindData<'tcx>>,
49
50     /// If `measureme` profiling is enabled, holds timing information
51     /// for the start of this frame. When we finish executing this frame,
52     /// we use this to register a completed event with `measureme`.
53     pub timing: Option<measureme::DetachedTiming>,
54 }
55
56 impl<'tcx> std::fmt::Debug for FrameData<'tcx> {
57     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58         // Omitting `timing`, it does not support `Debug`.
59         let FrameData { stacked_borrows, catch_unwind, timing: _ } = self;
60         f.debug_struct("FrameData")
61             .field("stacked_borrows", stacked_borrows)
62             .field("catch_unwind", catch_unwind)
63             .finish()
64     }
65 }
66
67 /// Extra memory kinds
68 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
69 pub enum MiriMemoryKind {
70     /// `__rust_alloc` memory.
71     Rust,
72     /// `malloc` memory.
73     C,
74     /// Windows `HeapAlloc` memory.
75     WinHeap,
76     /// Memory for args, errno, and other parts of the machine-managed environment.
77     /// This memory may leak.
78     Machine,
79     /// Memory allocated by the runtime (e.g. env vars). Separate from `Machine`
80     /// because we clean it up and leak-check it.
81     Runtime,
82     /// Globals copied from `tcx`.
83     /// This memory may leak.
84     Global,
85     /// Memory for extern statics.
86     /// This memory may leak.
87     ExternStatic,
88     /// Memory for thread-local statics.
89     /// This memory may leak.
90     Tls,
91 }
92
93 impl From<MiriMemoryKind> for MemoryKind<MiriMemoryKind> {
94     #[inline(always)]
95     fn from(kind: MiriMemoryKind) -> MemoryKind<MiriMemoryKind> {
96         MemoryKind::Machine(kind)
97     }
98 }
99
100 impl MayLeak for MiriMemoryKind {
101     #[inline(always)]
102     fn may_leak(self) -> bool {
103         use self::MiriMemoryKind::*;
104         match self {
105             Rust | C | WinHeap | Runtime => false,
106             Machine | Global | ExternStatic | Tls => true,
107         }
108     }
109 }
110
111 impl fmt::Display for MiriMemoryKind {
112     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113         use self::MiriMemoryKind::*;
114         match self {
115             Rust => write!(f, "Rust heap"),
116             C => write!(f, "C heap"),
117             WinHeap => write!(f, "Windows heap"),
118             Machine => write!(f, "machine-managed memory"),
119             Runtime => write!(f, "language runtime memory"),
120             Global => write!(f, "global (static or const)"),
121             ExternStatic => write!(f, "extern static"),
122             Tls => write!(f, "thread-local static"),
123         }
124     }
125 }
126
127 /// Pointer provenance.
128 #[derive(Debug, Clone, Copy)]
129 pub enum Provenance {
130     Concrete {
131         alloc_id: AllocId,
132         /// Stacked Borrows tag.
133         sb: SbTag,
134     },
135     Wildcard,
136 }
137
138 // This needs to be `Eq`+`Hash` because the `Machine` trait needs that because validity checking
139 // *might* be recursive and then it has to track which places have already been visited.
140 // However, comparing provenance is meaningless, since `Wildcard` might be any provenance -- and of
141 // course we don't actually do recursive checking.
142 // We could change `RefTracking` to strip provenance for its `seen` set but that type is generic so that is quite annoying.
143 // Instead owe add the required instances but make them panic.
144 impl PartialEq for Provenance {
145     fn eq(&self, _other: &Self) -> bool {
146         panic!("Provenance must not be compared")
147     }
148 }
149 impl Eq for Provenance {}
150 impl std::hash::Hash for Provenance {
151     fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {
152         panic!("Provenance must not be hashed")
153     }
154 }
155
156 /// The "extra" information a pointer has over a regular AllocId.
157 #[derive(Copy, Clone, PartialEq)]
158 pub enum ProvenanceExtra {
159     Concrete(SbTag),
160     Wildcard,
161 }
162
163 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
164 static_assert_size!(Pointer<Provenance>, 24);
165 // FIXME: this would with in 24bytes but layout optimizations are not smart enough
166 // #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
167 //static_assert_size!(Pointer<Option<Provenance>>, 24);
168 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
169 static_assert_size!(Scalar<Provenance>, 32);
170
171 impl interpret::Provenance for Provenance {
172     /// We use absolute addresses in the `offset` of a `Pointer<Provenance>`.
173     const OFFSET_IS_ADDR: bool = true;
174
175     /// We cannot err on partial overwrites, it happens too often in practice (due to unions).
176     const ERR_ON_PARTIAL_PTR_OVERWRITE: bool = false;
177
178     fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179         let (prov, addr) = ptr.into_parts(); // address is absolute
180         write!(f, "{:#x}", addr.bytes())?;
181
182         match prov {
183             Provenance::Concrete { alloc_id, sb } => {
184                 // Forward `alternate` flag to `alloc_id` printing.
185                 if f.alternate() {
186                     write!(f, "[{:#?}]", alloc_id)?;
187                 } else {
188                     write!(f, "[{:?}]", alloc_id)?;
189                 }
190                 // Print Stacked Borrows tag.
191                 write!(f, "{:?}", sb)?;
192             }
193             Provenance::Wildcard => {
194                 write!(f, "[wildcard]")?;
195             }
196         }
197
198         Ok(())
199     }
200
201     fn get_alloc_id(self) -> Option<AllocId> {
202         match self {
203             Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
204             Provenance::Wildcard => None,
205         }
206     }
207
208     fn join(left: Option<Self>, right: Option<Self>) -> Option<Self> {
209         match (left, right) {
210             // If both are the *same* concrete tag, that is the result.
211             (
212                 Some(Provenance::Concrete { alloc_id: left_alloc, sb: left_sb }),
213                 Some(Provenance::Concrete { alloc_id: right_alloc, sb: right_sb }),
214             ) if left_alloc == right_alloc && left_sb == right_sb => left,
215             // If one side is a wildcard, the best possible outcome is that it is equal to the other
216             // one, and we use that.
217             (Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o,
218             // Otherwise, fall back to `None`.
219             _ => None,
220         }
221     }
222 }
223
224 impl fmt::Debug for ProvenanceExtra {
225     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226         match self {
227             ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"),
228             ProvenanceExtra::Wildcard => write!(f, "<wildcard>"),
229         }
230     }
231 }
232
233 impl ProvenanceExtra {
234     pub fn and_then<T>(self, f: impl FnOnce(SbTag) -> Option<T>) -> Option<T> {
235         match self {
236             ProvenanceExtra::Concrete(pid) => f(pid),
237             ProvenanceExtra::Wildcard => None,
238         }
239     }
240 }
241
242 /// Extra per-allocation data
243 #[derive(Debug, Clone)]
244 pub struct AllocExtra {
245     /// Stacked Borrows state is only added if it is enabled.
246     pub stacked_borrows: Option<stacked_borrows::AllocExtra>,
247     /// Data race detection via the use of a vector-clock,
248     ///  this is only added if it is enabled.
249     pub data_race: Option<data_race::AllocExtra>,
250     /// Weak memory emulation via the use of store buffers,
251     ///  this is only added if it is enabled.
252     pub weak_memory: Option<weak_memory::AllocExtra>,
253 }
254
255 /// Precomputed layouts of primitive types
256 pub struct PrimitiveLayouts<'tcx> {
257     pub unit: TyAndLayout<'tcx>,
258     pub i8: TyAndLayout<'tcx>,
259     pub i16: TyAndLayout<'tcx>,
260     pub i32: TyAndLayout<'tcx>,
261     pub isize: TyAndLayout<'tcx>,
262     pub u8: TyAndLayout<'tcx>,
263     pub u16: TyAndLayout<'tcx>,
264     pub u32: TyAndLayout<'tcx>,
265     pub usize: TyAndLayout<'tcx>,
266     pub bool: TyAndLayout<'tcx>,
267     pub mut_raw_ptr: TyAndLayout<'tcx>,   // *mut ()
268     pub const_raw_ptr: TyAndLayout<'tcx>, // *const ()
269 }
270
271 impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
272     fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, LayoutError<'tcx>> {
273         let tcx = layout_cx.tcx;
274         let mut_raw_ptr = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut });
275         let const_raw_ptr = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not });
276         Ok(Self {
277             unit: layout_cx.layout_of(tcx.mk_unit())?,
278             i8: layout_cx.layout_of(tcx.types.i8)?,
279             i16: layout_cx.layout_of(tcx.types.i16)?,
280             i32: layout_cx.layout_of(tcx.types.i32)?,
281             isize: layout_cx.layout_of(tcx.types.isize)?,
282             u8: layout_cx.layout_of(tcx.types.u8)?,
283             u16: layout_cx.layout_of(tcx.types.u16)?,
284             u32: layout_cx.layout_of(tcx.types.u32)?,
285             usize: layout_cx.layout_of(tcx.types.usize)?,
286             bool: layout_cx.layout_of(tcx.types.bool)?,
287             mut_raw_ptr: layout_cx.layout_of(mut_raw_ptr)?,
288             const_raw_ptr: layout_cx.layout_of(const_raw_ptr)?,
289         })
290     }
291 }
292
293 /// The machine itself.
294 pub struct MiriMachine<'mir, 'tcx> {
295     // We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access.
296     pub tcx: TyCtxt<'tcx>,
297
298     /// Stacked Borrows global data.
299     pub stacked_borrows: Option<stacked_borrows::GlobalState>,
300
301     /// Data race detector global data.
302     pub data_race: Option<data_race::GlobalState>,
303
304     /// Ptr-int-cast module global data.
305     pub intptrcast: intptrcast::GlobalState,
306
307     /// Environment variables set by `setenv`.
308     /// Miri does not expose env vars from the host to the emulated program.
309     pub(crate) env_vars: EnvVars<'tcx>,
310
311     /// Program arguments (`Option` because we can only initialize them after creating the ecx).
312     /// These are *pointers* to argc/argv because macOS.
313     /// We also need the full command line as one string because of Windows.
314     pub(crate) argc: Option<MemPlace<Provenance>>,
315     pub(crate) argv: Option<MemPlace<Provenance>>,
316     pub(crate) cmd_line: Option<MemPlace<Provenance>>,
317
318     /// TLS state.
319     pub(crate) tls: TlsData<'tcx>,
320
321     /// What should Miri do when an op requires communicating with the host,
322     /// such as accessing host env vars, random number generation, and
323     /// file system access.
324     pub(crate) isolated_op: IsolatedOp,
325
326     /// Whether to enforce the validity invariant.
327     pub(crate) validate: bool,
328
329     /// Whether to enforce [ABI](Abi) of function calls.
330     pub(crate) enforce_abi: bool,
331
332     /// The table of file descriptors.
333     pub(crate) file_handler: shims::unix::FileHandler,
334     /// The table of directory descriptors.
335     pub(crate) dir_handler: shims::unix::DirHandler,
336
337     /// This machine's monotone clock.
338     pub(crate) clock: Clock,
339
340     /// The set of threads.
341     pub(crate) threads: ThreadManager<'mir, 'tcx>,
342
343     /// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
344     pub(crate) layouts: PrimitiveLayouts<'tcx>,
345
346     /// Allocations that are considered roots of static memory (that may leak).
347     pub(crate) static_roots: Vec<AllocId>,
348
349     /// The `measureme` profiler used to record timing information about
350     /// the emulated program.
351     profiler: Option<measureme::Profiler>,
352     /// Used with `profiler` to cache the `StringId`s for event names
353     /// uesd with `measureme`.
354     string_cache: FxHashMap<String, measureme::StringId>,
355
356     /// Cache of `Instance` exported under the given `Symbol` name.
357     /// `None` means no `Instance` exported under the given name is found.
358     pub(crate) exported_symbols_cache: FxHashMap<Symbol, Option<Instance<'tcx>>>,
359
360     /// Whether to raise a panic in the context of the evaluated process when unsupported
361     /// functionality is encountered. If `false`, an error is propagated in the Miri application context
362     /// instead (default behavior)
363     pub(crate) panic_on_unsupported: bool,
364
365     /// Equivalent setting as RUST_BACKTRACE on encountering an error.
366     pub(crate) backtrace_style: BacktraceStyle,
367
368     /// Crates which are considered local for the purposes of error reporting.
369     pub(crate) local_crates: Vec<CrateNum>,
370
371     /// Mapping extern static names to their base pointer.
372     extern_statics: FxHashMap<Symbol, Pointer<Provenance>>,
373
374     /// The random number generator used for resolving non-determinism.
375     /// Needs to be queried by ptr_to_int, hence needs interior mutability.
376     pub(crate) rng: RefCell<StdRng>,
377
378     /// The allocation IDs to report when they are being allocated
379     /// (helps for debugging memory leaks and use after free bugs).
380     tracked_alloc_ids: FxHashSet<AllocId>,
381
382     /// Controls whether alignment of memory accesses is being checked.
383     pub(crate) check_alignment: AlignmentCheck,
384
385     /// Failure rate of compare_exchange_weak, between 0.0 and 1.0
386     pub(crate) cmpxchg_weak_failure_rate: f64,
387
388     /// Corresponds to -Zmiri-mute-stdout-stderr and doesn't write the output but acts as if it succeeded.
389     pub(crate) mute_stdout_stderr: bool,
390
391     /// Whether weak memory emulation is enabled
392     pub(crate) weak_memory: bool,
393
394     /// The probability of the active thread being preempted at the end of each basic block.
395     pub(crate) preemption_rate: f64,
396
397     /// If `Some`, we will report the current stack every N basic blocks.
398     pub(crate) report_progress: Option<u32>,
399     // The total number of blocks that have been executed.
400     pub(crate) basic_block_count: u64,
401
402     /// Handle of the optional shared object file for external functions.
403     #[cfg(unix)]
404     pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>,
405
406     /// Run a garbage collector for SbTags every N basic blocks.
407     pub(crate) gc_interval: u32,
408     /// The number of blocks that passed since the last SbTag GC pass.
409     pub(crate) since_gc: u32,
410 }
411
412 impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
413     pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self {
414         let local_crates = helpers::get_local_crates(layout_cx.tcx);
415         let layouts =
416             PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
417         let profiler = config.measureme_out.as_ref().map(|out| {
418             measureme::Profiler::new(out).expect("Couldn't create `measureme` profiler")
419         });
420         let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
421         let stacked_borrows = config.stacked_borrows.then(|| {
422             RefCell::new(stacked_borrows::GlobalStateInner::new(
423                 config.tracked_pointer_tags.clone(),
424                 config.tracked_call_ids.clone(),
425                 config.retag_fields,
426             ))
427         });
428         let data_race = config.data_race_detector.then(|| data_race::GlobalState::new(config));
429         MiriMachine {
430             tcx: layout_cx.tcx,
431             stacked_borrows,
432             data_race,
433             intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config)),
434             // `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
435             env_vars: EnvVars::default(),
436             argc: None,
437             argv: None,
438             cmd_line: None,
439             tls: TlsData::default(),
440             isolated_op: config.isolated_op,
441             validate: config.validate,
442             enforce_abi: config.check_abi,
443             file_handler: FileHandler::new(config.mute_stdout_stderr),
444             dir_handler: Default::default(),
445             layouts,
446             threads: ThreadManager::default(),
447             static_roots: Vec::new(),
448             profiler,
449             string_cache: Default::default(),
450             exported_symbols_cache: FxHashMap::default(),
451             panic_on_unsupported: config.panic_on_unsupported,
452             backtrace_style: config.backtrace_style,
453             local_crates,
454             extern_statics: FxHashMap::default(),
455             rng: RefCell::new(rng),
456             tracked_alloc_ids: config.tracked_alloc_ids.clone(),
457             check_alignment: config.check_alignment,
458             cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
459             mute_stdout_stderr: config.mute_stdout_stderr,
460             weak_memory: config.weak_memory_emulation,
461             preemption_rate: config.preemption_rate,
462             report_progress: config.report_progress,
463             basic_block_count: 0,
464             clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
465             #[cfg(unix)]
466             external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| {
467                 let target_triple = layout_cx.tcx.sess.opts.target_triple.triple();
468                 // Check if host target == the session target.
469                 if env!("TARGET") != target_triple {
470                     panic!(
471                         "calling external C functions in linked .so file requires host and target to be the same: host={}, target={}",
472                         env!("TARGET"),
473                         target_triple,
474                     );
475                 }
476                 // Note: it is the user's responsibility to provide a correct SO file.
477                 // WATCH OUT: If an invalid/incorrect SO file is specified, this can cause
478                 // undefined behaviour in Miri itself!
479                 (
480                     unsafe {
481                         libloading::Library::new(lib_file_path)
482                             .expect("failed to read specified extern shared object file")
483                     },
484                     lib_file_path.clone(),
485                 )
486             }),
487             gc_interval: config.gc_interval,
488             since_gc: 0,
489         }
490     }
491
492     pub(crate) fn late_init(
493         this: &mut MiriInterpCx<'mir, 'tcx>,
494         config: &MiriConfig,
495     ) -> InterpResult<'tcx> {
496         EnvVars::init(this, config)?;
497         MiriMachine::init_extern_statics(this)?;
498         ThreadManager::init(this);
499         Ok(())
500     }
501
502     fn add_extern_static(
503         this: &mut MiriInterpCx<'mir, 'tcx>,
504         name: &str,
505         ptr: Pointer<Option<Provenance>>,
506     ) {
507         // This got just allocated, so there definitely is a pointer here.
508         let ptr = ptr.into_pointer_or_addr().unwrap();
509         this.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
510     }
511
512     fn alloc_extern_static(
513         this: &mut MiriInterpCx<'mir, 'tcx>,
514         name: &str,
515         val: ImmTy<'tcx, Provenance>,
516     ) -> InterpResult<'tcx> {
517         let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
518         this.write_immediate(*val, &place.into())?;
519         Self::add_extern_static(this, name, place.ptr);
520         Ok(())
521     }
522
523     /// Sets up the "extern statics" for this machine.
524     fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
525         match this.tcx.sess.target.os.as_ref() {
526             "linux" => {
527                 // "environ"
528                 Self::add_extern_static(
529                     this,
530                     "environ",
531                     this.machine.env_vars.environ.unwrap().ptr,
532                 );
533                 // A couple zero-initialized pointer-sized extern statics.
534                 // Most of them are for weak symbols, which we all set to null (indicating that the
535                 // symbol is not supported, and triggering fallback code which ends up calling a
536                 // syscall that we do support).
537                 for name in &["__cxa_thread_atexit_impl", "getrandom", "statx", "__clock_gettime64"]
538                 {
539                     let val = ImmTy::from_int(0, this.machine.layouts.usize);
540                     Self::alloc_extern_static(this, name, val)?;
541                 }
542             }
543             "freebsd" => {
544                 // "environ"
545                 Self::add_extern_static(
546                     this,
547                     "environ",
548                     this.machine.env_vars.environ.unwrap().ptr,
549                 );
550             }
551             "android" => {
552                 // "signal"
553                 let layout = this.machine.layouts.const_raw_ptr;
554                 let dlsym = Dlsym::from_str("signal".as_bytes(), &this.tcx.sess.target.os)?
555                     .expect("`signal` must be an actual dlsym on android");
556                 let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
557                 let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout);
558                 Self::alloc_extern_static(this, "signal", val)?;
559                 // A couple zero-initialized pointer-sized extern statics.
560                 // Most of them are for weak symbols, which we all set to null (indicating that the
561                 // symbol is not supported, and triggering fallback code.)
562                 for name in &["bsd_signal"] {
563                     let val = ImmTy::from_int(0, this.machine.layouts.usize);
564                     Self::alloc_extern_static(this, name, val)?;
565                 }
566             }
567             "windows" => {
568                 // "_tls_used"
569                 // This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
570                 let val = ImmTy::from_int(0, this.machine.layouts.u8);
571                 Self::alloc_extern_static(this, "_tls_used", val)?;
572             }
573             _ => {} // No "extern statics" supported on this target
574         }
575         Ok(())
576     }
577
578     pub(crate) fn communicate(&self) -> bool {
579         self.isolated_op == IsolatedOp::Allow
580     }
581
582     /// Check whether the stack frame that this `FrameInfo` refers to is part of a local crate.
583     pub(crate) fn is_local(&self, frame: &FrameInfo<'_>) -> bool {
584         let def_id = frame.instance.def_id();
585         def_id.is_local() || self.local_crates.contains(&def_id.krate)
586     }
587 }
588
589 /// A rustc InterpCx for Miri.
590 pub type MiriInterpCx<'mir, 'tcx> = InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>;
591
592 /// A little trait that's useful to be inherited by extension traits.
593 pub trait MiriInterpCxExt<'mir, 'tcx> {
594     fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'mir, 'tcx>;
595     fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'mir, 'tcx>;
596 }
597 impl<'mir, 'tcx> MiriInterpCxExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {
598     #[inline(always)]
599     fn eval_context_ref(&self) -> &MiriInterpCx<'mir, 'tcx> {
600         self
601     }
602     #[inline(always)]
603     fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> {
604         self
605     }
606 }
607
608 /// Machine hook implementations.
609 impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
610     type MemoryKind = MiriMemoryKind;
611     type ExtraFnVal = Dlsym;
612
613     type FrameExtra = FrameData<'tcx>;
614     type AllocExtra = AllocExtra;
615
616     type Provenance = Provenance;
617     type ProvenanceExtra = ProvenanceExtra;
618
619     type MemoryMap = MonoHashMap<
620         AllocId,
621         (MemoryKind<MiriMemoryKind>, Allocation<Provenance, Self::AllocExtra>),
622     >;
623
624     const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
625
626     const PANIC_ON_ALLOC_FAIL: bool = false;
627
628     #[inline(always)]
629     fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
630         ecx.machine.check_alignment != AlignmentCheck::None
631     }
632
633     #[inline(always)]
634     fn use_addr_for_alignment_check(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
635         ecx.machine.check_alignment == AlignmentCheck::Int
636     }
637
638     #[inline(always)]
639     fn enforce_validity(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
640         ecx.machine.validate
641     }
642
643     #[inline(always)]
644     fn enforce_abi(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
645         ecx.machine.enforce_abi
646     }
647
648     #[inline(always)]
649     fn checked_binop_checks_overflow(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
650         ecx.tcx.sess.overflow_checks()
651     }
652
653     #[inline(always)]
654     fn find_mir_or_eval_fn(
655         ecx: &mut MiriInterpCx<'mir, 'tcx>,
656         instance: ty::Instance<'tcx>,
657         abi: Abi,
658         args: &[OpTy<'tcx, Provenance>],
659         dest: &PlaceTy<'tcx, Provenance>,
660         ret: Option<mir::BasicBlock>,
661         unwind: StackPopUnwind,
662     ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
663         ecx.find_mir_or_eval_fn(instance, abi, args, dest, ret, unwind)
664     }
665
666     #[inline(always)]
667     fn call_extra_fn(
668         ecx: &mut MiriInterpCx<'mir, 'tcx>,
669         fn_val: Dlsym,
670         abi: Abi,
671         args: &[OpTy<'tcx, Provenance>],
672         dest: &PlaceTy<'tcx, Provenance>,
673         ret: Option<mir::BasicBlock>,
674         _unwind: StackPopUnwind,
675     ) -> InterpResult<'tcx> {
676         ecx.call_dlsym(fn_val, abi, args, dest, ret)
677     }
678
679     #[inline(always)]
680     fn call_intrinsic(
681         ecx: &mut MiriInterpCx<'mir, 'tcx>,
682         instance: ty::Instance<'tcx>,
683         args: &[OpTy<'tcx, Provenance>],
684         dest: &PlaceTy<'tcx, Provenance>,
685         ret: Option<mir::BasicBlock>,
686         unwind: StackPopUnwind,
687     ) -> InterpResult<'tcx> {
688         ecx.call_intrinsic(instance, args, dest, ret, unwind)
689     }
690
691     #[inline(always)]
692     fn assert_panic(
693         ecx: &mut MiriInterpCx<'mir, 'tcx>,
694         msg: &mir::AssertMessage<'tcx>,
695         unwind: Option<mir::BasicBlock>,
696     ) -> InterpResult<'tcx> {
697         ecx.assert_panic(msg, unwind)
698     }
699
700     #[inline(always)]
701     fn abort(_ecx: &mut MiriInterpCx<'mir, 'tcx>, msg: String) -> InterpResult<'tcx, !> {
702         throw_machine_stop!(TerminationInfo::Abort(msg))
703     }
704
705     #[inline(always)]
706     fn binary_ptr_op(
707         ecx: &MiriInterpCx<'mir, 'tcx>,
708         bin_op: mir::BinOp,
709         left: &ImmTy<'tcx, Provenance>,
710         right: &ImmTy<'tcx, Provenance>,
711     ) -> InterpResult<'tcx, (Scalar<Provenance>, bool, Ty<'tcx>)> {
712         ecx.binary_ptr_op(bin_op, left, right)
713     }
714
715     fn thread_local_static_base_pointer(
716         ecx: &mut MiriInterpCx<'mir, 'tcx>,
717         def_id: DefId,
718     ) -> InterpResult<'tcx, Pointer<Provenance>> {
719         ecx.get_or_create_thread_local_alloc(def_id)
720     }
721
722     fn extern_static_base_pointer(
723         ecx: &MiriInterpCx<'mir, 'tcx>,
724         def_id: DefId,
725     ) -> InterpResult<'tcx, Pointer<Provenance>> {
726         let link_name = ecx.item_link_name(def_id);
727         if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) {
728             // Various parts of the engine rely on `get_alloc_info` for size and alignment
729             // information. That uses the type information of this static.
730             // Make sure it matches the Miri allocation for this.
731             let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
732                 panic!("extern_statics cannot contain wildcards")
733             };
734             let (shim_size, shim_align, _kind) = ecx.get_alloc_info(alloc_id);
735             let extern_decl_layout =
736                 ecx.tcx.layout_of(ty::ParamEnv::empty().and(ecx.tcx.type_of(def_id))).unwrap();
737             if extern_decl_layout.size != shim_size || extern_decl_layout.align.abi != shim_align {
738                 throw_unsup_format!(
739                     "`extern` static `{name}` from crate `{krate}` has been declared \
740                     with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
741                     but Miri emulates it via an extern static shim \
742                     with a size of {shim_size} bytes and alignment of {shim_align} bytes",
743                     name = ecx.tcx.def_path_str(def_id),
744                     krate = ecx.tcx.crate_name(def_id.krate),
745                     decl_size = extern_decl_layout.size.bytes(),
746                     decl_align = extern_decl_layout.align.abi.bytes(),
747                     shim_size = shim_size.bytes(),
748                     shim_align = shim_align.bytes(),
749                 )
750             }
751             Ok(ptr)
752         } else {
753             throw_unsup_format!(
754                 "`extern` static `{name}` from crate `{krate}` is not supported by Miri",
755                 name = ecx.tcx.def_path_str(def_id),
756                 krate = ecx.tcx.crate_name(def_id.krate),
757             )
758         }
759     }
760
761     fn adjust_allocation<'b>(
762         ecx: &MiriInterpCx<'mir, 'tcx>,
763         id: AllocId,
764         alloc: Cow<'b, Allocation>,
765         kind: Option<MemoryKind<Self::MemoryKind>>,
766     ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>> {
767         let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
768         if ecx.machine.tracked_alloc_ids.contains(&id) {
769             ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(
770                 id,
771                 alloc.size(),
772                 alloc.align,
773                 kind,
774             ));
775         }
776
777         let alloc = alloc.into_owned();
778         let stacks = ecx.machine.stacked_borrows.as_ref().map(|stacked_borrows| {
779             Stacks::new_allocation(
780                 id,
781                 alloc.size(),
782                 stacked_borrows,
783                 kind,
784                 ecx.machine.current_span(),
785             )
786         });
787         let race_alloc = ecx.machine.data_race.as_ref().map(|data_race| {
788             data_race::AllocExtra::new_allocation(
789                 data_race,
790                 &ecx.machine.threads,
791                 alloc.size(),
792                 kind,
793             )
794         });
795         let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocExtra::new_allocation);
796         let alloc: Allocation<Provenance, Self::AllocExtra> = alloc.adjust_from_tcx(
797             &ecx.tcx,
798             AllocExtra {
799                 stacked_borrows: stacks.map(RefCell::new),
800                 data_race: race_alloc,
801                 weak_memory: buffer_alloc,
802             },
803             |ptr| ecx.global_base_pointer(ptr),
804         )?;
805         Ok(Cow::Owned(alloc))
806     }
807
808     fn adjust_alloc_base_pointer(
809         ecx: &MiriInterpCx<'mir, 'tcx>,
810         ptr: Pointer<AllocId>,
811     ) -> Pointer<Provenance> {
812         if cfg!(debug_assertions) {
813             // The machine promises to never call us on thread-local or extern statics.
814             let alloc_id = ptr.provenance;
815             match ecx.tcx.try_get_global_alloc(alloc_id) {
816                 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
817                     panic!("adjust_alloc_base_pointer called on thread-local static")
818                 }
819                 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => {
820                     panic!("adjust_alloc_base_pointer called on extern static")
821                 }
822                 _ => {}
823             }
824         }
825         let absolute_addr = intptrcast::GlobalStateInner::rel_ptr_to_addr(ecx, ptr);
826         let sb_tag = if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
827             stacked_borrows.borrow_mut().base_ptr_tag(ptr.provenance, &ecx.machine)
828         } else {
829             // Value does not matter, SB is disabled
830             SbTag::default()
831         };
832         Pointer::new(
833             Provenance::Concrete { alloc_id: ptr.provenance, sb: sb_tag },
834             Size::from_bytes(absolute_addr),
835         )
836     }
837
838     #[inline(always)]
839     fn ptr_from_addr_cast(
840         ecx: &MiriInterpCx<'mir, 'tcx>,
841         addr: u64,
842     ) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>> {
843         intptrcast::GlobalStateInner::ptr_from_addr_cast(ecx, addr)
844     }
845
846     fn expose_ptr(
847         ecx: &mut InterpCx<'mir, 'tcx, Self>,
848         ptr: Pointer<Self::Provenance>,
849     ) -> InterpResult<'tcx> {
850         match ptr.provenance {
851             Provenance::Concrete { alloc_id, sb } =>
852                 intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb),
853             Provenance::Wildcard => {
854                 // No need to do anything for wildcard pointers as
855                 // their provenances have already been previously exposed.
856                 Ok(())
857             }
858         }
859     }
860
861     /// Convert a pointer with provenance into an allocation-offset pair,
862     /// or a `None` with an absolute address if that conversion is not possible.
863     fn ptr_get_alloc(
864         ecx: &MiriInterpCx<'mir, 'tcx>,
865         ptr: Pointer<Self::Provenance>,
866     ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
867         let rel = intptrcast::GlobalStateInner::abs_ptr_to_rel(ecx, ptr);
868
869         rel.map(|(alloc_id, size)| {
870             let sb = match ptr.provenance {
871                 Provenance::Concrete { sb, .. } => ProvenanceExtra::Concrete(sb),
872                 Provenance::Wildcard => ProvenanceExtra::Wildcard,
873             };
874             (alloc_id, size, sb)
875         })
876     }
877
878     #[inline(always)]
879     fn before_memory_read(
880         _tcx: TyCtxt<'tcx>,
881         machine: &Self,
882         alloc_extra: &AllocExtra,
883         (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
884         range: AllocRange,
885     ) -> InterpResult<'tcx> {
886         if let Some(data_race) = &alloc_extra.data_race {
887             data_race.read(
888                 alloc_id,
889                 range,
890                 machine.data_race.as_ref().unwrap(),
891                 &machine.threads,
892             )?;
893         }
894         if let Some(stacked_borrows) = &alloc_extra.stacked_borrows {
895             stacked_borrows.borrow_mut().before_memory_read(
896                 alloc_id,
897                 prov_extra,
898                 range,
899                 machine.stacked_borrows.as_ref().unwrap(),
900                 machine.current_span(),
901                 &machine.threads,
902             )?;
903         }
904         if let Some(weak_memory) = &alloc_extra.weak_memory {
905             weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
906         }
907         Ok(())
908     }
909
910     #[inline(always)]
911     fn before_memory_write(
912         _tcx: TyCtxt<'tcx>,
913         machine: &mut Self,
914         alloc_extra: &mut AllocExtra,
915         (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
916         range: AllocRange,
917     ) -> InterpResult<'tcx> {
918         if let Some(data_race) = &mut alloc_extra.data_race {
919             data_race.write(
920                 alloc_id,
921                 range,
922                 machine.data_race.as_mut().unwrap(),
923                 &machine.threads,
924             )?;
925         }
926         if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
927             stacked_borrows.get_mut().before_memory_write(
928                 alloc_id,
929                 prov_extra,
930                 range,
931                 machine.stacked_borrows.as_ref().unwrap(),
932                 machine.current_span(),
933                 &machine.threads,
934             )?;
935         }
936         if let Some(weak_memory) = &alloc_extra.weak_memory {
937             weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
938         }
939         Ok(())
940     }
941
942     #[inline(always)]
943     fn before_memory_deallocation(
944         _tcx: TyCtxt<'tcx>,
945         machine: &mut Self,
946         alloc_extra: &mut AllocExtra,
947         (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
948         range: AllocRange,
949     ) -> InterpResult<'tcx> {
950         if machine.tracked_alloc_ids.contains(&alloc_id) {
951             machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
952         }
953         if let Some(data_race) = &mut alloc_extra.data_race {
954             data_race.deallocate(
955                 alloc_id,
956                 range,
957                 machine.data_race.as_mut().unwrap(),
958                 &machine.threads,
959             )?;
960         }
961         if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
962             stacked_borrows.get_mut().before_memory_deallocation(
963                 alloc_id,
964                 prove_extra,
965                 range,
966                 machine.stacked_borrows.as_ref().unwrap(),
967                 machine.current_span(),
968                 &machine.threads,
969             )
970         } else {
971             Ok(())
972         }
973     }
974
975     #[inline(always)]
976     fn retag(
977         ecx: &mut InterpCx<'mir, 'tcx, Self>,
978         kind: mir::RetagKind,
979         place: &PlaceTy<'tcx, Provenance>,
980     ) -> InterpResult<'tcx> {
981         if ecx.machine.stacked_borrows.is_some() { ecx.retag(kind, place) } else { Ok(()) }
982     }
983
984     #[inline(always)]
985     fn init_frame_extra(
986         ecx: &mut InterpCx<'mir, 'tcx, Self>,
987         frame: Frame<'mir, 'tcx, Provenance>,
988     ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>> {
989         // Start recording our event before doing anything else
990         let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
991             let fn_name = frame.instance.to_string();
992             let entry = ecx.machine.string_cache.entry(fn_name.clone());
993             let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
994
995             Some(profiler.start_recording_interval_event_detached(
996                 *name,
997                 measureme::EventId::from_label(*name),
998                 ecx.get_active_thread().to_u32(),
999             ))
1000         } else {
1001             None
1002         };
1003
1004         let stacked_borrows = ecx.machine.stacked_borrows.as_ref();
1005
1006         let extra = FrameData {
1007             stacked_borrows: stacked_borrows.map(|sb| sb.borrow_mut().new_frame(&ecx.machine)),
1008             catch_unwind: None,
1009             timing,
1010         };
1011         Ok(frame.with_extra(extra))
1012     }
1013
1014     fn stack<'a>(
1015         ecx: &'a InterpCx<'mir, 'tcx, Self>,
1016     ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] {
1017         ecx.active_thread_stack()
1018     }
1019
1020     fn stack_mut<'a>(
1021         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
1022     ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> {
1023         ecx.active_thread_stack_mut()
1024     }
1025
1026     fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
1027         ecx.machine.basic_block_count += 1u64; // a u64 that is only incremented by 1 will "never" overflow
1028         ecx.machine.since_gc += 1;
1029         // Possibly report our progress.
1030         if let Some(report_progress) = ecx.machine.report_progress {
1031             if ecx.machine.basic_block_count % u64::from(report_progress) == 0 {
1032                 ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport {
1033                     block_count: ecx.machine.basic_block_count,
1034                 });
1035             }
1036         }
1037
1038         // Search for SbTags to find all live pointers, then remove all other tags from borrow
1039         // stacks.
1040         // When debug assertions are enabled, run the GC as often as possible so that any cases
1041         // where it mistakenly removes an important tag become visible.
1042         if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
1043             ecx.machine.since_gc = 0;
1044             ecx.garbage_collect_tags()?;
1045         }
1046
1047         // These are our preemption points.
1048         ecx.maybe_preempt_active_thread();
1049
1050         // Make sure some time passes.
1051         ecx.machine.clock.tick();
1052
1053         Ok(())
1054     }
1055
1056     #[inline(always)]
1057     fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
1058         if ecx.machine.stacked_borrows.is_some() { ecx.retag_return_place() } else { Ok(()) }
1059     }
1060
1061     #[inline(always)]
1062     fn after_stack_pop(
1063         ecx: &mut InterpCx<'mir, 'tcx, Self>,
1064         mut frame: Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>,
1065         unwinding: bool,
1066     ) -> InterpResult<'tcx, StackPopJump> {
1067         let timing = frame.extra.timing.take();
1068         if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
1069             stacked_borrows.borrow_mut().end_call(&frame.extra);
1070         }
1071         let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
1072         if let Some(profiler) = ecx.machine.profiler.as_ref() {
1073             profiler.finish_recording_interval_event(timing.unwrap());
1074         }
1075         res
1076     }
1077 }