Skip to main content

miri/
machine.rs

1//! Global machine state as well as implementation of the interpreter engine
2//! `Machine` trait.
3
4use std::borrow::Cow;
5use std::cell::{Cell, RefCell};
6use std::collections::BTreeMap;
7use std::path::Path;
8use std::rc::Rc;
9use std::{fmt, process};
10
11use rand::rngs::StdRng;
12use rand::{RngExt, SeedableRng};
13use rustc_abi::{Align, ExternAbi, Size};
14use rustc_apfloat::{Float, FloatConvert};
15use rustc_ast::Mutability;
16use rustc_ast::expand::allocator::{self, SpecialAllocatorMethod};
17use rustc_data_structures::either::Either;
18use rustc_data_structures::fx::{FxHashMap, FxHashSet};
19#[allow(unused)]
20use rustc_data_structures::static_assert_size;
21use rustc_hir::attrs::{InlineAttr, Linkage};
22use rustc_hir::def::DefKind;
23use rustc_log::tracing;
24use rustc_middle::middle::codegen_fn_attrs::TargetFeatureKind;
25use rustc_middle::mir;
26use rustc_middle::query::TyCtxtAt;
27use rustc_middle::ty::layout::{
28    HasTyCtxt, HasTypingEnv, LayoutCx, LayoutError, LayoutOf, TyAndLayout,
29};
30use rustc_middle::ty::{self, AtomicOrdering, Instance, Ty, TyCtxt};
31use rustc_session::config::InliningThreshold;
32use rustc_span::def_id::{CrateNum, DefId};
33use rustc_span::{Span, SpanData, Symbol};
34use rustc_symbol_mangling::mangle_internal_symbol;
35use rustc_target::callconv::FnAbi;
36use rustc_target::spec::{Arch, Os};
37
38use crate::alloc_addresses::EvalContextExt;
39use crate::concurrency::data_race::{self, NaReadType, NaWriteType};
40use crate::concurrency::sync::SyncObj;
41use crate::concurrency::{
42    AllocDataRaceHandler, GenmcCtx, GenmcEvalContextExt as _, GlobalDataRaceHandler, weak_memory,
43};
44use crate::helpers::is_no_core;
45use crate::*;
46
47/// First real-time signal.
48/// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35
49/// as typical values.
50pub const SIGRTMIN: i32 = 34;
51
52/// Last real-time signal.
53/// `signal(7)` says it must be between 32 and 64 and specifies
54/// `SIGRTMAX` - `SIGRTMIN` >= 8 (which is the value of `_POSIX_RTSIG_MAX`)
55pub const SIGRTMAX: i32 = 42;
56
57/// Each anonymous global (constant, vtable, function pointer, ...) has multiple addresses, but only
58/// this many. Since const allocations are never deallocated, choosing a new [`AllocId`] and thus
59/// base address for each evaluation would produce unbounded memory usage.
60const ADDRS_PER_ANON_GLOBAL: usize = 32;
61
62#[derive(Copy, Clone, Debug, PartialEq)]
63pub enum AlignmentCheck {
64    /// Do not check alignment.
65    None,
66    /// Check alignment "symbolically", i.e., using only the requested alignment for an allocation and not its real base address.
67    Symbolic,
68    /// Check alignment on the actual physical integer address.
69    Int,
70}
71
72#[derive(Copy, Clone, Debug, PartialEq)]
73pub enum RejectOpWith {
74    /// Isolated op is rejected with an abort of the machine.
75    Abort,
76
77    /// If not Abort, miri returns an error for an isolated op.
78    /// Following options determine if user should be warned about such error.
79    /// Do not print warning about rejected isolated op.
80    NoWarning,
81
82    /// Print a warning about rejected isolated op, with backtrace.
83    Warning,
84
85    /// Print a warning about rejected isolated op, without backtrace.
86    WarningWithoutBacktrace,
87}
88
89#[derive(Copy, Clone, Debug, PartialEq)]
90pub enum IsolatedOp {
91    /// Reject an op requiring communication with the host. By
92    /// default, miri rejects the op with an abort. If not, it returns
93    /// an error code, and prints a warning about it. Warning levels
94    /// are controlled by `RejectOpWith` enum.
95    Reject(RejectOpWith),
96
97    /// Execute op requiring communication with the host, i.e. disable isolation.
98    Allow,
99}
100
101#[derive(Debug, Copy, Clone, PartialEq, Eq)]
102pub enum BacktraceStyle {
103    /// Prints a terser backtrace which ideally only contains relevant information.
104    Short,
105    /// Prints a backtrace with all possible information.
106    Full,
107    /// Prints only the frame that the error occurs in.
108    Off,
109}
110
111#[derive(Debug, Copy, Clone, PartialEq, Eq)]
112pub enum ValidationMode {
113    /// Do not perform any kind of validation.
114    No,
115    /// Validate the interior of the value, but not things behind references.
116    Shallow,
117    /// Fully recursively validate references.
118    Deep,
119}
120
121#[derive(Debug, Copy, Clone, PartialEq, Eq)]
122pub enum FloatRoundingErrorMode {
123    /// Apply a random error (the default).
124    Random,
125    /// Don't apply any error.
126    None,
127    /// Always apply the maximum error (with a random sign).
128    Max,
129}
130
131/// Extra data stored with each stack frame
132pub struct FrameExtra<'tcx> {
133    /// Extra data for the Borrow Tracker.
134    pub borrow_tracker: Option<borrow_tracker::FrameState>,
135
136    /// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn`
137    /// called by `try`). When this frame is popped during unwinding a panic,
138    /// we stop unwinding, use the `CatchUnwindData` to handle catching.
139    pub catch_unwind: Option<CatchUnwindData<'tcx>>,
140
141    /// If `measureme` profiling is enabled, holds timing information
142    /// for the start of this frame. When we finish executing this frame,
143    /// we use this to register a completed event with `measureme`.
144    pub timing: Option<measureme::DetachedTiming>,
145
146    /// Indicates how user-relevant this frame is. `#[track_caller]` frames are never relevant.
147    /// Frames from user-relevant crates are maximally relevant; frames from other crates are less
148    /// relevant.
149    pub user_relevance: u8,
150
151    /// Data race detector per-frame data.
152    pub data_race: Option<data_race::FrameState>,
153}
154
155impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        // Omitting `timing`, it does not support `Debug`.
158        let FrameExtra { borrow_tracker, catch_unwind, timing: _, user_relevance, data_race } =
159            self;
160        f.debug_struct("FrameData")
161            .field("borrow_tracker", borrow_tracker)
162            .field("catch_unwind", catch_unwind)
163            .field("user_relevance", user_relevance)
164            .field("data_race", data_race)
165            .finish()
166    }
167}
168
169impl VisitProvenance for FrameExtra<'_> {
170    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
171        let FrameExtra { catch_unwind, borrow_tracker, timing: _, user_relevance: _, data_race: _ } =
172            self;
173
174        catch_unwind.visit_provenance(visit);
175        borrow_tracker.visit_provenance(visit);
176    }
177}
178
179/// Extra memory kinds
180#[derive(Debug, Copy, Clone, PartialEq, Eq)]
181pub enum MiriMemoryKind {
182    /// `__rust_alloc` memory.
183    Rust,
184    /// `miri_alloc` memory.
185    Miri,
186    /// `malloc` memory.
187    C,
188    /// Windows `HeapAlloc` memory.
189    WinHeap,
190    /// Windows "local" memory (to be freed with `LocalFree`)
191    WinLocal,
192    /// Memory for args, errno, env vars, and other parts of the machine-managed environment.
193    /// This memory may leak.
194    Machine,
195    /// Memory allocated by the runtime, e.g. for readdir. Separate from `Machine` because we clean
196    /// it up (or expect the user to invoke operations that clean it up) and leak-check it.
197    Runtime,
198    /// Globals copied from `tcx`.
199    /// This memory may leak.
200    Global,
201    /// Memory for extern statics.
202    /// This memory may leak.
203    ExternStatic,
204    /// Memory for thread-local statics.
205    /// This memory may leak.
206    Tls,
207    /// Memory mapped directly by the program.
208    Mmap,
209    /// Memory allocated for `getaddrinfo` result.
210    SocketAddress,
211}
212
213impl From<MiriMemoryKind> for MemoryKind {
214    #[inline(always)]
215    fn from(kind: MiriMemoryKind) -> MemoryKind {
216        MemoryKind::Machine(kind)
217    }
218}
219
220impl MayLeak for MiriMemoryKind {
221    #[inline(always)]
222    fn may_leak(self) -> bool {
223        use self::MiriMemoryKind::*;
224        match self {
225            Rust | Miri | C | WinHeap | WinLocal | Runtime => false,
226            Machine | Global | ExternStatic | Tls | Mmap | SocketAddress => true,
227        }
228    }
229}
230
231impl MiriMemoryKind {
232    /// Whether we have a useful allocation span for an allocation of this kind.
233    fn should_save_allocation_span(self) -> bool {
234        use self::MiriMemoryKind::*;
235        match self {
236            // Heap allocations are fine since the `Allocation` is created immediately.
237            Rust | Miri | C | WinHeap | WinLocal | Mmap => true,
238            // Everything else is unclear, let's not show potentially confusing spans.
239            Machine | Global | ExternStatic | Tls | Runtime | SocketAddress => false,
240        }
241    }
242}
243
244impl fmt::Display for MiriMemoryKind {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        use self::MiriMemoryKind::*;
247        match self {
248            Rust => write!(f, "Rust heap"),
249            Miri => write!(f, "Miri bare-metal heap"),
250            C => write!(f, "C heap"),
251            WinHeap => write!(f, "Windows heap"),
252            WinLocal => write!(f, "Windows local memory"),
253            Machine => write!(f, "machine-managed memory"),
254            Runtime => write!(f, "language runtime memory"),
255            Global => write!(f, "global (static or const)"),
256            ExternStatic => write!(f, "extern static"),
257            Tls => write!(f, "thread-local static"),
258            Mmap => write!(f, "mmap"),
259            SocketAddress => write!(f, "socket address"),
260        }
261    }
262}
263
264pub type MemoryKind = interpret::MemoryKind<MiriMemoryKind>;
265
266/// Pointer provenance.
267// This needs to be `Eq`+`Hash` because the `Machine` trait needs that because validity checking
268// *might* be recursive and then it has to track which places have already been visited.
269// These implementations are a bit questionable, and it means we may check the same place multiple
270// times with different provenance, but that is in general not wrong.
271#[derive(Clone, Copy, PartialEq, Eq, Hash)]
272pub enum Provenance {
273    /// For pointers with concrete provenance. we exactly know which allocation they are attached to
274    /// and what their borrow tag is.
275    Concrete {
276        alloc_id: AllocId,
277        /// Borrow Tracker tag.
278        tag: BorTag,
279    },
280    /// Pointers with wildcard provenance are created on int-to-ptr casts. According to the
281    /// specification, we should at that point angelically "guess" a provenance that will make all
282    /// future uses of this pointer work, if at all possible. Of course such a semantics cannot be
283    /// actually implemented in Miri. So instead, we approximate this, erroring on the side of
284    /// accepting too much code rather than rejecting correct code: a pointer with wildcard
285    /// provenance "acts like" any previously exposed pointer. Each time it is used, we check
286    /// whether *some* exposed pointer could have done what we want to do, and if the answer is yes
287    /// then we allow the access. This allows too much code in two ways:
288    /// - The same wildcard pointer can "take the role" of multiple different exposed pointers on
289    ///   subsequent memory accesses.
290    /// - In the aliasing model, we don't just have to know the borrow tag of the pointer used for
291    ///   the access, we also have to update the aliasing state -- and that update can be very
292    ///   different depending on which borrow tag we pick! Stacked Borrows has support for this by
293    ///   switching to a stack that is only approximately known, i.e. we over-approximate the effect
294    ///   of using *any* exposed pointer for this access, and only keep information about the borrow
295    ///   stack that would be true with all possible choices.
296    Wildcard,
297}
298
299/// The "extra" information a pointer has over a regular AllocId.
300#[derive(Copy, Clone, PartialEq)]
301pub enum ProvenanceExtra {
302    Concrete(BorTag),
303    Wildcard,
304}
305
306#[cfg(target_pointer_width = "64")]
307static_assert_size!(StrictPointer, 24);
308// Pointer does not fit as the layout algorithm isn't smart enough (but also, we tried using
309// pattern types to get a larger niche that makes this fit and it didn't improve performance).
310// #[cfg(target_pointer_width = "64")]
311//static_assert_size!(Pointer, 24);
312#[cfg(target_pointer_width = "64")]
313static_assert_size!(Scalar, 32);
314
315impl fmt::Debug for Provenance {
316    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317        match self {
318            Provenance::Concrete { alloc_id, tag } => {
319                // Forward `alternate` flag to `alloc_id` printing.
320                if f.alternate() {
321                    write!(f, "[{alloc_id:#?}]")?;
322                } else {
323                    write!(f, "[{alloc_id:?}]")?;
324                }
325                // Print Borrow Tracker tag.
326                write!(f, "{tag:?}")?;
327            }
328            Provenance::Wildcard => {
329                write!(f, "[wildcard]")?;
330            }
331        }
332        Ok(())
333    }
334}
335
336impl interpret::Provenance for Provenance {
337    /// We use absolute addresses in the `offset` of a `StrictPointer`.
338    const OFFSET_IS_ADDR: bool = true;
339
340    /// Miri implements wildcard provenance.
341    const WILDCARD: Option<Self> = Some(Provenance::Wildcard);
342
343    fn get_alloc_id(self) -> Option<AllocId> {
344        match self {
345            Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
346            Provenance::Wildcard => None,
347        }
348    }
349
350    fn fmt(ptr: &interpret::Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351        let (prov, addr) = ptr.into_raw_parts(); // offset is absolute address
352        write!(f, "{:#x}", addr.bytes())?;
353        if f.alternate() {
354            write!(f, "{prov:#?}")?;
355        } else {
356            write!(f, "{prov:?}")?;
357        }
358        Ok(())
359    }
360}
361
362impl fmt::Debug for ProvenanceExtra {
363    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364        match self {
365            ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"),
366            ProvenanceExtra::Wildcard => write!(f, "<wildcard>"),
367        }
368    }
369}
370
371impl ProvenanceExtra {
372    pub fn and_then<T>(self, f: impl FnOnce(BorTag) -> Option<T>) -> Option<T> {
373        match self {
374            ProvenanceExtra::Concrete(pid) => f(pid),
375            ProvenanceExtra::Wildcard => None,
376        }
377    }
378}
379
380/// Extra per-allocation data
381#[derive(Debug)]
382pub struct AllocExtra<'tcx> {
383    /// Global state of the borrow tracker, if enabled.
384    pub borrow_tracker: Option<borrow_tracker::AllocState>,
385    /// Extra state for data race detection.
386    ///
387    /// Invariant: The enum variant must match the enum variant in the `data_race` field on `MiriMachine`
388    pub data_race: AllocDataRaceHandler,
389    /// A backtrace to where this allocation was allocated.
390    /// As this is recorded for leak reports, it only exists
391    /// if this allocation is leakable. The backtrace is not
392    /// pruned yet; that should be done before printing it.
393    pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
394    /// Synchronization objects like to attach extra data to particular addresses. We store that
395    /// inside the relevant allocation, to ensure that everything is removed when the allocation is
396    /// freed.
397    /// This maps offsets to synchronization-primitive-specific data.
398    pub sync_objs: BTreeMap<Size, Box<dyn SyncObj>>,
399}
400
401// We need a `Clone` impl because the machine passes `Allocation` through `Cow`...
402// but that should never end up actually cloning our `AllocExtra`.
403impl<'tcx> Clone for AllocExtra<'tcx> {
404    fn clone(&self) -> Self {
405        panic!("our allocations should never be cloned");
406    }
407}
408
409impl VisitProvenance for AllocExtra<'_> {
410    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
411        let AllocExtra { borrow_tracker, data_race, backtrace: _, sync_objs: _ } = self;
412
413        borrow_tracker.visit_provenance(visit);
414        data_race.visit_provenance(visit);
415    }
416}
417
418/// Precomputed layouts of primitive types
419pub struct PrimitiveLayouts<'tcx> {
420    pub unit: TyAndLayout<'tcx>,
421    pub i8: TyAndLayout<'tcx>,
422    pub i16: TyAndLayout<'tcx>,
423    pub i32: TyAndLayout<'tcx>,
424    pub i64: TyAndLayout<'tcx>,
425    pub i128: TyAndLayout<'tcx>,
426    pub isize: TyAndLayout<'tcx>,
427    pub u8: TyAndLayout<'tcx>,
428    pub u16: TyAndLayout<'tcx>,
429    pub u32: TyAndLayout<'tcx>,
430    pub u64: TyAndLayout<'tcx>,
431    pub u128: TyAndLayout<'tcx>,
432    pub usize: TyAndLayout<'tcx>,
433    pub bool: TyAndLayout<'tcx>,
434    pub unit_ptr_mut: TyAndLayout<'tcx>,   // *mut ()
435    pub unit_ptr_const: TyAndLayout<'tcx>, // *const ()
436    pub void_ptr_mut: TyAndLayout<'tcx>,   // *mut c_void
437    pub void_ptr_const: TyAndLayout<'tcx>, // *const c_void
438    pub fn_ptr: TyAndLayout<'tcx>,         // extern "C" fn()
439}
440
441impl<'tcx> PrimitiveLayouts<'tcx> {
442    fn new(layout_cx: LayoutCx<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
443        let tcx = layout_cx.tcx();
444
445        let unit_ptr_mut = Ty::new_mut_ptr(tcx, tcx.types.unit);
446        let unit_ptr_const = Ty::new_imm_ptr(tcx, tcx.types.unit);
447        // We fall back to `()` if the lang item is missing, so `no_core` works better with Miri.
448        let c_void = match tcx.lang_items().c_void() {
449            Some(c_void) => ty::Instance::mono(tcx, c_void).ty(tcx, layout_cx.typing_env),
450            None => tcx.types.unit,
451        };
452        let void_ptr_mut = Ty::new_mut_ptr(tcx, c_void);
453        let void_ptr_const = Ty::new_imm_ptr(tcx, c_void);
454
455        let sig_kind = ty::FnSigKind::default()
456            .set_abi(ExternAbi::C { unwind: false })
457            .set_safety(rustc_hir::Safety::Safe);
458        let fn_ptr =
459            Ty::new_fn_ptr(tcx, ty::Binder::dummy(tcx.mk_fn_sig([], tcx.types.unit, sig_kind)));
460
461        Ok(Self {
462            unit: layout_cx.layout_of(tcx.types.unit)?,
463            i8: layout_cx.layout_of(tcx.types.i8)?,
464            i16: layout_cx.layout_of(tcx.types.i16)?,
465            i32: layout_cx.layout_of(tcx.types.i32)?,
466            i64: layout_cx.layout_of(tcx.types.i64)?,
467            i128: layout_cx.layout_of(tcx.types.i128)?,
468            isize: layout_cx.layout_of(tcx.types.isize)?,
469            u8: layout_cx.layout_of(tcx.types.u8)?,
470            u16: layout_cx.layout_of(tcx.types.u16)?,
471            u32: layout_cx.layout_of(tcx.types.u32)?,
472            u64: layout_cx.layout_of(tcx.types.u64)?,
473            u128: layout_cx.layout_of(tcx.types.u128)?,
474            usize: layout_cx.layout_of(tcx.types.usize)?,
475            bool: layout_cx.layout_of(tcx.types.bool)?,
476            unit_ptr_mut: layout_cx.layout_of(unit_ptr_mut)?,
477            unit_ptr_const: layout_cx.layout_of(unit_ptr_const)?,
478            void_ptr_mut: layout_cx.layout_of(void_ptr_mut)?,
479            void_ptr_const: layout_cx.layout_of(void_ptr_const)?,
480            fn_ptr: layout_cx.layout_of(fn_ptr)?,
481        })
482    }
483
484    pub fn uint(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
485        match size.bits() {
486            8 => Some(self.u8),
487            16 => Some(self.u16),
488            32 => Some(self.u32),
489            64 => Some(self.u64),
490            128 => Some(self.u128),
491            _ => None,
492        }
493    }
494
495    pub fn int(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
496        match size.bits() {
497            8 => Some(self.i8),
498            16 => Some(self.i16),
499            32 => Some(self.i32),
500            64 => Some(self.i64),
501            128 => Some(self.i128),
502            _ => None,
503        }
504    }
505}
506
507/// The machine itself.
508///
509/// If you add anything here that stores machine values, remember to update
510/// `visit_all_machine_values`!
511pub struct MiriMachine<'tcx> {
512    // We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access.
513    pub tcx: TyCtxt<'tcx>,
514
515    /// Global data for borrow tracking.
516    pub borrow_tracker: Option<borrow_tracker::GlobalState>,
517
518    /// Depending on settings, this will be `None`,
519    /// global data for a data race detector,
520    /// or the context required for running in GenMC mode.
521    ///
522    /// Invariant: The enum variant must match the enum variant of `AllocDataRaceHandler` in the `data_race` field of all `AllocExtra`.
523    pub data_race: GlobalDataRaceHandler,
524
525    /// Ptr-int-cast module global data.
526    pub alloc_addresses: alloc_addresses::GlobalState,
527
528    /// Environment variables.
529    pub(crate) env_vars: EnvVars<'tcx>,
530
531    /// Return place of the main function.
532    pub(crate) main_fn_ret_place: Option<MPlaceTy<'tcx>>,
533
534    /// Program arguments (`Option` because we can only initialize them after creating the ecx).
535    /// These are *pointers* to argc/argv because macOS.
536    /// We also need the full command line as one string because of Windows.
537    pub(crate) argc: Option<Pointer>,
538    pub(crate) argv: Option<Pointer>,
539    pub(crate) cmd_line: Option<Pointer>,
540
541    /// TLS state.
542    pub(crate) tls: TlsData<'tcx>,
543
544    /// What should Miri do when an op requires communicating with the host,
545    /// such as accessing host env vars, random number generation, and
546    /// file system access.
547    pub(crate) isolated_op: IsolatedOp,
548
549    /// Whether to enforce the validity invariant.
550    pub(crate) validation: ValidationMode,
551
552    /// The table of file descriptors.
553    pub(crate) fds: shims::FdTable,
554    /// The table of directory descriptors.
555    pub(crate) dirs: shims::DirTable,
556
557    /// Managing file descriptors whose readiness needs to be updated.
558    pub(crate) delayed_readiness_updates: Rc<shims::DelayedReadinessUpdates>,
559
560    /// This machine's monotone clock.
561    pub(crate) monotonic_clock: MonotonicClock,
562
563    /// The set of threads.
564    pub(crate) threads: ThreadManager<'tcx>,
565
566    /// Handles blocking I/O and polling for completion.
567    pub(crate) blocking_io: BlockingIoManager,
568
569    /// Stores which thread is eligible to run on which CPUs.
570    /// This has no effect at all, it is just tracked to produce the correct result
571    /// in `sched_getaffinity`
572    /// This will be `None` when running `#![no_core]` crates.
573    pub(crate) thread_cpu_affinity: Option<FxHashMap<ThreadId, shims::CpuAffinityMask>>,
574
575    /// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
576    pub(crate) layouts: PrimitiveLayouts<'tcx>,
577
578    /// Allocations that are considered roots of static memory (that may leak).
579    pub(crate) static_roots: Vec<AllocId>,
580
581    /// The `measureme` profiler used to record timing information about
582    /// the emulated program.
583    profiler: Option<measureme::Profiler>,
584    /// Used with `profiler` to cache the `StringId`s for event names
585    /// used with `measureme`.
586    string_cache: FxHashMap<String, measureme::StringId>,
587
588    /// Cache of `Instance` exported under the given `Symbol` name.
589    /// `None` means no `Instance` exported under the given name is found.
590    pub(crate) exported_symbols_cache: RefCell<FxHashMap<Symbol, Option<Instance<'tcx>>>>,
591
592    /// Equivalent setting as RUST_BACKTRACE on encountering an error.
593    pub(crate) backtrace_style: BacktraceStyle,
594
595    /// Crates which are considered user-relevant for the purposes of error reporting.
596    pub(crate) user_relevant_crates: Vec<CrateNum>,
597
598    /// Mapping extern static names to their pointer.
599    pub(crate) extern_statics: FxHashMap<Symbol, StrictPointer>,
600    /// Statics with `import_linkage` have an extra indirection
601    /// (<https://github.com/rust-lang/rust/issues/156468>) so we keep them in a separate table.
602    pub(crate) extern_statics_imports: FxHashMap<Symbol, StrictPointer>,
603    /// A pointer to the allocation we provide for non-existent weak symbols.
604    pub(crate) extern_static_weak_import_default: Option<StrictPointer>,
605
606    /// The random number generator used for resolving non-determinism.
607    /// Needs to be queried by ptr_to_int, hence needs interior mutability.
608    pub(crate) rng: RefCell<StdRng>,
609
610    /// The allocator used for the machine's `AllocBytes` in native-libs mode.
611    pub(crate) allocator: Option<Rc<RefCell<crate::alloc::isolated_alloc::IsolatedAlloc>>>,
612
613    /// The allocation IDs to report when they are being allocated
614    /// (helps for debugging memory leaks and use after free bugs).
615    pub(crate) tracked_alloc_ids: FxHashSet<AllocId>,
616    /// For the tracked alloc ids, also report read/write accesses.
617    track_alloc_accesses: bool,
618
619    /// Controls whether alignment of memory accesses is being checked.
620    pub(crate) check_alignment: AlignmentCheck,
621
622    /// Failure rate of compare_exchange_weak, between 0.0 and 1.0
623    pub(crate) cmpxchg_weak_failure_rate: f64,
624
625    /// The probability of the active thread being preempted at the end of each basic block.
626    pub(crate) preemption_rate: f64,
627
628    /// If `Some`, we will report the current stack every N basic blocks.
629    pub(crate) report_progress: Option<u32>,
630    // The total number of blocks that have been executed.
631    pub(crate) basic_block_count: u64,
632
633    /// Handle of the optional shared object file for native functions.
634    #[cfg(all(feature = "native-lib", unix))]
635    pub native_lib: Vec<(libloading::Library, std::path::PathBuf)>,
636    #[cfg(not(all(feature = "native-lib", unix)))]
637    pub native_lib: Vec<!>,
638    /// A memory location for exchanging the current `ecx` pointer with native code.
639    #[cfg(all(feature = "native-lib", unix))]
640    pub native_lib_ecx_interchange: &'static Cell<usize>,
641
642    /// Run a garbage collector for BorTags every N basic blocks.
643    pub(crate) gc_interval: u32,
644    /// The number of blocks that passed since the last BorTag GC pass.
645    pub(crate) since_gc: u32,
646
647    /// The number of CPUs to be reported by miri.
648    pub(crate) num_cpus: u32,
649
650    /// Determines Miri's page size and associated values
651    pub(crate) page_size: u64,
652    pub(crate) stack_addr: u64,
653    pub(crate) stack_size: u64,
654
655    /// Whether to collect a backtrace when each allocation is created, just in case it leaks.
656    pub(crate) collect_leak_backtraces: bool,
657
658    /// The spans we will use to report where an allocation was created and deallocated in
659    /// diagnostics.
660    pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>)>>,
661
662    /// For each allocation, an offset inside that allocation that was deemed aligned even for
663    /// symbolic alignment checks. This cannot be stored in `AllocExtra` since it needs to be
664    /// tracked for vtables and function allocations as well as regular allocations.
665    ///
666    /// Invariant: the promised alignment will never be less than the native alignment of the
667    /// allocation.
668    pub(crate) symbolic_alignment: RefCell<FxHashMap<AllocId, (Size, Align)>>,
669
670    /// A cache of "data range" computations for unions (i.e., the offsets of non-padding bytes).
671    union_data_ranges: FxHashMap<Ty<'tcx>, RangeSet>,
672
673    /// Caches the sanity-checks for various pthread primitives.
674    pub(crate) pthread_mutex_sanity: Cell<bool>,
675    pub(crate) pthread_rwlock_sanity: Cell<bool>,
676    pub(crate) pthread_condvar_sanity: Cell<bool>,
677
678    /// (Foreign) symbols that are synthesized as part of the allocator shim: the key indicates the
679    /// name of the symbol being synthesized; the value indicates whether this should invoke some
680    /// other symbol or whether this has special allocator semantics.
681    pub(crate) allocator_shim_symbols: FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>>,
682    /// Cache for `mangle_internal_symbol`.
683    pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>,
684
685    /// Whether floating-point operations can behave non-deterministically.
686    pub float_nondet: bool,
687    /// Whether floating-point operations can have a non-deterministic rounding error.
688    pub float_rounding_error: FloatRoundingErrorMode,
689
690    /// Whether Miri artificially introduces short reads/writes on file descriptors.
691    pub short_fd_operations: bool,
692}
693
694impl<'tcx> MiriMachine<'tcx> {
695    /// Create a new MiriMachine.
696    ///
697    /// Invariant: `genmc_ctx.is_some() == config.genmc_config.is_some()`
698    pub(crate) fn new(
699        config: &MiriConfig,
700        layout_cx: LayoutCx<'tcx>,
701        genmc_ctx: Option<Rc<GenmcCtx>>,
702    ) -> Self {
703        let tcx = layout_cx.tcx();
704        let user_relevant_crates = Self::get_user_relevant_crates(tcx, config);
705        let layouts =
706            PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
707        let profiler = config.measureme_out.as_ref().map(|out| {
708            let crate_name =
709                tcx.sess.opts.crate_name.clone().unwrap_or_else(|| "unknown-crate".to_string());
710            let pid = process::id();
711            // We adopt the same naming scheme for the profiler output that rustc uses. In rustc,
712            // the PID is padded so that the nondeterministic value of the PID does not spread
713            // nondeterminism to the allocator. In Miri we are not aiming for such performance
714            // control, we just pad for consistency with rustc.
715            let filename = format!("{crate_name}-{pid:07}");
716            let path = Path::new(out).join(filename);
717            measureme::Profiler::new(path).expect("Couldn't create `measureme` profiler")
718        });
719        let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
720        let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config));
721        let data_race = if config.genmc_config.is_some() {
722            // `genmc_ctx` persists across executions, so we don't create a new one here.
723            GlobalDataRaceHandler::Genmc(genmc_ctx.unwrap())
724        } else if config.data_race_detector {
725            GlobalDataRaceHandler::Vclocks(Box::new(data_race::GlobalState::new(config)))
726        } else {
727            GlobalDataRaceHandler::None
728        };
729        // Determine page size, stack address, and stack size.
730        // These values are mostly meaningless, but the stack address is also where we start
731        // allocating physical integer addresses for all allocations.
732        let page_size = if let Some(page_size) = config.page_size {
733            page_size
734        } else {
735            let target = &tcx.sess.target;
736            match target.arch {
737                Arch::Wasm32 | Arch::Wasm64 => 64 * 1024, // https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances
738                Arch::AArch64 if target.is_like_darwin => {
739                    // No "definitive" source, but see:
740                    // https://www.wwdcnotes.com/notes/wwdc20/10214/
741                    // https://github.com/ziglang/zig/issues/11308 etc.
742                    16 * 1024
743                }
744                _ => 4 * 1024,
745            }
746        };
747        // On 16bit targets, 32 pages is more than the entire address space!
748        let stack_addr = if tcx.pointer_size().bits() < 32 { page_size } else { page_size * 32 };
749        let stack_size =
750            if tcx.pointer_size().bits() < 32 { page_size * 4 } else { page_size * 16 };
751        assert!(
752            usize::try_from(config.num_cpus).unwrap() <= shims::cpu_affinity::MAX_CPUS,
753            "miri only supports up to {} CPUs, but {} were configured",
754            shims::cpu_affinity::MAX_CPUS,
755            config.num_cpus
756        );
757        let threads = ThreadManager::new(config);
758        let thread_cpu_affinity =
759            if matches!(&tcx.sess.target.os, Os::Linux | Os::FreeBsd | Os::Android)
760                && !is_no_core(tcx)
761            {
762                let mut affinity = FxHashMap::default();
763                affinity.insert(
764                    threads.active_thread(),
765                    shims::CpuAffinityMask::new(&layout_cx, config.num_cpus),
766                );
767                Some(affinity)
768            } else {
769                None
770            };
771        let blocking_io = BlockingIoManager::new(config.isolated_op == IsolatedOp::Allow)
772            .expect("Couldn't create poll instance");
773        let alloc_addresses =
774            RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr, tcx));
775
776        MiriMachine {
777            tcx,
778            borrow_tracker,
779            data_race,
780            alloc_addresses,
781            // `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
782            env_vars: EnvVars::default(),
783            main_fn_ret_place: None,
784            argc: None,
785            argv: None,
786            cmd_line: None,
787            tls: TlsData::default(),
788            isolated_op: config.isolated_op,
789            validation: config.validation,
790            fds: shims::FdTable::init(config.mute_stdout_stderr),
791            delayed_readiness_updates: Rc::new(shims::DelayedReadinessUpdates::default()),
792            dirs: Default::default(),
793            layouts,
794            threads,
795            thread_cpu_affinity,
796            blocking_io,
797            static_roots: Vec::new(),
798            profiler,
799            string_cache: Default::default(),
800            exported_symbols_cache: RefCell::new(FxHashMap::default()),
801            backtrace_style: config.backtrace_style,
802            user_relevant_crates,
803            extern_statics: FxHashMap::default(),
804            extern_statics_imports: FxHashMap::default(),
805            extern_static_weak_import_default: None,
806            rng: RefCell::new(rng),
807            allocator: (!config.native_lib.is_empty())
808                .then(|| Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new()))),
809            tracked_alloc_ids: config.tracked_alloc_ids.clone(),
810            track_alloc_accesses: config.track_alloc_accesses,
811            check_alignment: config.check_alignment,
812            cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
813            preemption_rate: config.preemption_rate,
814            report_progress: config.report_progress,
815            basic_block_count: 0,
816            monotonic_clock: MonotonicClock::new(config.isolated_op == IsolatedOp::Allow),
817            #[cfg(all(feature = "native-lib", unix))]
818            native_lib: config.native_lib.iter().map(|lib_file_path| {
819                let host_triple = rustc_session::config::host_tuple();
820                let target_triple = tcx.sess.opts.target_triple.tuple();
821                // Check if host target == the session target.
822                if host_triple != target_triple {
823                    panic!(
824                        "calling native C functions in linked .so file requires host and target to be the same: \
825                        host={host_triple}, target={target_triple}",
826                    );
827                }
828                // Note: it is the user's responsibility to provide a correct SO file.
829                // WATCH OUT: If an invalid/incorrect SO file is specified, this can cause
830                // undefined behaviour in Miri itself!
831                (
832                    unsafe {
833                        libloading::Library::new(lib_file_path)
834                            .expect("failed to read specified extern shared object file")
835                    },
836                    lib_file_path.clone(),
837                )
838            }).collect(),
839            #[cfg(all(feature = "native-lib", unix))]
840            native_lib_ecx_interchange: Box::leak(Box::new(Cell::new(0))),
841            #[cfg(not(all(feature = "native-lib", unix)))]
842            native_lib: config.native_lib.iter().map(|_| {
843                panic!("calling functions from native libraries via FFI is not supported in this build of Miri")
844            }).collect(),
845            gc_interval: config.gc_interval,
846            since_gc: 0,
847            num_cpus: config.num_cpus,
848            page_size,
849            stack_addr,
850            stack_size,
851            collect_leak_backtraces: config.collect_leak_backtraces,
852            allocation_spans: RefCell::new(FxHashMap::default()),
853            symbolic_alignment: RefCell::new(FxHashMap::default()),
854            union_data_ranges: FxHashMap::default(),
855            pthread_mutex_sanity: Cell::new(false),
856            pthread_rwlock_sanity: Cell::new(false),
857            pthread_condvar_sanity: Cell::new(false),
858            allocator_shim_symbols: Self::allocator_shim_symbols(tcx),
859            mangle_internal_symbol_cache: Default::default(),
860            float_nondet: config.float_nondet,
861            float_rounding_error: config.float_rounding_error,
862            short_fd_operations: config.short_fd_operations,
863        }
864    }
865
866    fn allocator_shim_symbols(
867        tcx: TyCtxt<'tcx>,
868    ) -> FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>> {
869        use rustc_codegen_ssa::base::allocator_shim_contents;
870
871        // codegen uses `allocator_kind_for_codegen` here, but that's only needed to deal with
872        // dylibs which we do not support.
873        let Some(kind) = tcx.allocator_kind(()) else {
874            return Default::default();
875        };
876        let methods = allocator_shim_contents(tcx, kind);
877        let mut symbols = FxHashMap::default();
878        for method in methods {
879            let from_name = Symbol::intern(&mangle_internal_symbol(
880                tcx,
881                &allocator::global_fn_name(method.name),
882            ));
883            let to = match method.special {
884                Some(special) => Either::Right(special),
885                None =>
886                    Either::Left(Symbol::intern(&mangle_internal_symbol(
887                        tcx,
888                        &allocator::default_fn_name(method.name),
889                    ))),
890            };
891            symbols.try_insert(from_name, to).unwrap();
892        }
893        symbols
894    }
895
896    /// Retrieve the list of user-relevant crates based on MIRI_LOCAL_CRATES as set by cargo-miri,
897    /// and extra crates set in the config.
898    fn get_user_relevant_crates(tcx: TyCtxt<'_>, config: &MiriConfig) -> Vec<CrateNum> {
899        // Convert the local crate names from the passed-in config into CrateNums so that they can
900        // be looked up quickly during execution
901        let local_crate_names = std::env::var("MIRI_LOCAL_CRATES")
902            .map(|crates| crates.split(',').map(|krate| krate.to_string()).collect::<Vec<_>>())
903            .unwrap_or_default();
904        let mut local_crates = Vec::new();
905        for &crate_num in tcx.crates(()) {
906            let name = tcx.crate_name(crate_num);
907            let name = name.as_str();
908            if local_crate_names
909                .iter()
910                .chain(&config.user_relevant_crates)
911                .any(|local_name| local_name == name)
912            {
913                local_crates.push(crate_num);
914            }
915        }
916        local_crates
917    }
918
919    pub(crate) fn late_init(
920        ecx: &mut MiriInterpCx<'tcx>,
921        config: &MiriConfig,
922        on_main_stack_empty: StackEmptyCallback<'tcx>,
923    ) -> InterpResult<'tcx> {
924        EnvVars::init(ecx, config)?;
925        MiriMachine::init_extern_statics(ecx)?;
926        ThreadManager::init(ecx, on_main_stack_empty);
927        interp_ok(())
928    }
929
930    pub(crate) fn communicate(&self) -> bool {
931        self.isolated_op == IsolatedOp::Allow
932    }
933
934    /// Check whether the stack frame that this `FrameInfo` refers to is part of a local crate.
935    pub(crate) fn is_local(&self, instance: ty::Instance<'tcx>) -> bool {
936        let def_id = instance.def_id();
937        def_id.is_local() || self.user_relevant_crates.contains(&def_id.krate)
938    }
939
940    /// Called when the interpreter is going to shut down abnormally, such as due to a Ctrl-C.
941    pub(crate) fn handle_abnormal_termination(&mut self) {
942        // All strings in the profile data are stored in a single string table which is not
943        // written to disk until the profiler is dropped. If the interpreter exits without dropping
944        // the profiler, it is not possible to interpret the profile data and all measureme tools
945        // will panic when given the file.
946        drop(self.profiler.take());
947    }
948
949    pub(crate) fn page_align(&self) -> Align {
950        Align::from_bytes(self.page_size).unwrap()
951    }
952
953    pub(crate) fn allocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
954        self.allocation_spans
955            .borrow()
956            .get(&alloc_id)
957            .map(|(allocated, _deallocated)| allocated.data())
958    }
959
960    pub(crate) fn deallocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
961        self.allocation_spans
962            .borrow()
963            .get(&alloc_id)
964            .and_then(|(_allocated, deallocated)| *deallocated)
965            .map(Span::data)
966    }
967
968    fn init_allocation(
969        ecx: &MiriInterpCx<'tcx>,
970        id: AllocId,
971        kind: MemoryKind,
972        size: Size,
973        align: Align,
974    ) -> InterpResult<'tcx, AllocExtra<'tcx>> {
975        if ecx.machine.tracked_alloc_ids.contains(&id) {
976            ecx.emit_diagnostic(NonHaltingDiagnostic::TrackingAlloc(id, size, align));
977        }
978
979        let borrow_tracker = ecx
980            .machine
981            .borrow_tracker
982            .as_ref()
983            .map(|bt| bt.borrow_mut().new_allocation(id, size, kind, &ecx.machine));
984
985        let data_race = match &ecx.machine.data_race {
986            GlobalDataRaceHandler::None => AllocDataRaceHandler::None,
987            GlobalDataRaceHandler::Vclocks(data_race) =>
988                AllocDataRaceHandler::Vclocks(
989                    data_race::AllocState::new_allocation(
990                        data_race,
991                        &ecx.machine.threads,
992                        size,
993                        kind,
994                        ecx.machine.current_user_relevant_span(),
995                    ),
996                    data_race.weak_memory.then(weak_memory::AllocState::new_allocation),
997                ),
998            GlobalDataRaceHandler::Genmc(_genmc_ctx) => {
999                // GenMC learns about new allocations directly from the alloc_addresses module,
1000                // since it has to be able to control the address at which they are placed.
1001                AllocDataRaceHandler::Genmc
1002            }
1003        };
1004
1005        // If an allocation is leaked, we want to report a backtrace to indicate where it was
1006        // allocated. We don't need to record a backtrace for allocations which are allowed to
1007        // leak.
1008        let backtrace = if kind.may_leak() || !ecx.machine.collect_leak_backtraces {
1009            None
1010        } else {
1011            Some(ecx.generate_stacktrace())
1012        };
1013
1014        if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) {
1015            ecx.machine
1016                .allocation_spans
1017                .borrow_mut()
1018                .insert(id, (ecx.machine.current_user_relevant_span(), None));
1019        }
1020
1021        interp_ok(AllocExtra {
1022            borrow_tracker,
1023            data_race,
1024            backtrace,
1025            sync_objs: BTreeMap::default(),
1026        })
1027    }
1028}
1029
1030impl VisitProvenance for MiriMachine<'_> {
1031    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
1032        #[rustfmt::skip]
1033        let MiriMachine {
1034            threads,
1035            thread_cpu_affinity: _,
1036            tls,
1037            env_vars,
1038            main_fn_ret_place,
1039            argc,
1040            argv,
1041            cmd_line,
1042            extern_statics,
1043            extern_statics_imports,
1044            extern_static_weak_import_default,
1045            dirs,
1046            borrow_tracker,
1047            data_race,
1048            alloc_addresses,
1049            fds,
1050            blocking_io:_,
1051            delayed_readiness_updates: _,
1052            tcx: _,
1053            isolated_op: _,
1054            validation: _,
1055            monotonic_clock: _,
1056            layouts: _,
1057            static_roots: _,
1058            profiler: _,
1059            string_cache: _,
1060            exported_symbols_cache: _,
1061            backtrace_style: _,
1062            user_relevant_crates: _,
1063            rng: _,
1064            allocator: _,
1065            tracked_alloc_ids: _,
1066            track_alloc_accesses: _,
1067            check_alignment: _,
1068            cmpxchg_weak_failure_rate: _,
1069            preemption_rate: _,
1070            report_progress: _,
1071            basic_block_count: _,
1072            native_lib: _,
1073            #[cfg(all(feature = "native-lib", unix))]
1074            native_lib_ecx_interchange: _,
1075            gc_interval: _,
1076            since_gc: _,
1077            num_cpus: _,
1078            page_size: _,
1079            stack_addr: _,
1080            stack_size: _,
1081            collect_leak_backtraces: _,
1082            allocation_spans: _,
1083            symbolic_alignment: _,
1084            union_data_ranges: _,
1085            pthread_mutex_sanity: _,
1086            pthread_rwlock_sanity: _,
1087            pthread_condvar_sanity: _,
1088            allocator_shim_symbols: _,
1089            mangle_internal_symbol_cache: _,
1090            float_nondet: _,
1091            float_rounding_error: _,
1092            short_fd_operations: _,
1093        } = self;
1094
1095        threads.visit_provenance(visit);
1096        tls.visit_provenance(visit);
1097        env_vars.visit_provenance(visit);
1098        dirs.visit_provenance(visit);
1099        fds.visit_provenance(visit);
1100        data_race.visit_provenance(visit);
1101        borrow_tracker.visit_provenance(visit);
1102        alloc_addresses.visit_provenance(visit);
1103        main_fn_ret_place.visit_provenance(visit);
1104        argc.visit_provenance(visit);
1105        argv.visit_provenance(visit);
1106        cmd_line.visit_provenance(visit);
1107        extern_static_weak_import_default.visit_provenance(visit);
1108        extern_statics.visit_provenance(visit);
1109        extern_statics_imports.visit_provenance(visit);
1110    }
1111}
1112
1113/// A rustc InterpCx for Miri.
1114pub type MiriInterpCx<'tcx> = InterpCx<'tcx, MiriMachine<'tcx>>;
1115
1116/// A little trait that's useful to be inherited by extension traits.
1117pub trait MiriInterpCxExt<'tcx> {
1118    fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'tcx>;
1119    fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'tcx>;
1120}
1121impl<'tcx> MiriInterpCxExt<'tcx> for MiriInterpCx<'tcx> {
1122    #[inline(always)]
1123    fn eval_context_ref(&self) -> &MiriInterpCx<'tcx> {
1124        self
1125    }
1126    #[inline(always)]
1127    fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'tcx> {
1128        self
1129    }
1130}
1131
1132/// Machine hook implementations.
1133impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
1134    type MemoryKind = MiriMemoryKind;
1135    type ExtraFnVal = DynSym;
1136
1137    type FrameExtra = FrameExtra<'tcx>;
1138    type AllocExtra = AllocExtra<'tcx>;
1139
1140    type Provenance = Provenance;
1141    type ProvenanceExtra = ProvenanceExtra;
1142    type Bytes = MiriAllocBytes;
1143
1144    type MemoryMap =
1145        MonoHashMap<AllocId, (MemoryKind, Allocation<Provenance, Self::AllocExtra, Self::Bytes>)>;
1146
1147    const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
1148
1149    const PANIC_ON_ALLOC_FAIL: bool = false;
1150
1151    #[inline(always)]
1152    fn enforce_alignment(ecx: &MiriInterpCx<'tcx>) -> bool {
1153        ecx.machine.check_alignment != AlignmentCheck::None
1154    }
1155
1156    #[inline(always)]
1157    fn alignment_check(
1158        ecx: &MiriInterpCx<'tcx>,
1159        alloc_id: AllocId,
1160        alloc_align: Align,
1161        alloc_kind: AllocKind,
1162        offset: Size,
1163        align: Align,
1164    ) -> Option<Misalignment> {
1165        if ecx.machine.check_alignment != AlignmentCheck::Symbolic {
1166            // Just use the built-in check.
1167            return None;
1168        }
1169        if alloc_kind != AllocKind::LiveData {
1170            // Can't have any extra info here.
1171            return None;
1172        }
1173        // Let's see which alignment we have been promised for this allocation.
1174        let (promised_offset, promised_align) = ecx
1175            .machine
1176            .symbolic_alignment
1177            .borrow()
1178            .get(&alloc_id)
1179            .copied()
1180            .unwrap_or((Size::ZERO, alloc_align));
1181        if promised_align < align {
1182            // Definitely not enough.
1183            Some(Misalignment { has: promised_align, required: align })
1184        } else {
1185            // What's the offset between us and the promised alignment?
1186            let distance = offset.bytes().wrapping_sub(promised_offset.bytes());
1187            // That must also be aligned.
1188            if distance.is_multiple_of(align.bytes()) {
1189                // All looking good!
1190                None
1191            } else {
1192                // The biggest power of two through which `distance` is divisible.
1193                let distance_pow2 = 1 << distance.trailing_zeros();
1194                Some(Misalignment {
1195                    has: Align::from_bytes(distance_pow2).unwrap(),
1196                    required: align,
1197                })
1198            }
1199        }
1200    }
1201
1202    #[inline(always)]
1203    fn enforce_validity(ecx: &MiriInterpCx<'tcx>, _layout: TyAndLayout<'tcx>) -> bool {
1204        ecx.machine.validation != ValidationMode::No
1205    }
1206    #[inline(always)]
1207    fn enforce_validity_recursively(
1208        ecx: &InterpCx<'tcx, Self>,
1209        _layout: TyAndLayout<'tcx>,
1210    ) -> bool {
1211        ecx.machine.validation == ValidationMode::Deep
1212    }
1213
1214    #[inline(always)]
1215    fn ignore_optional_overflow_checks(ecx: &MiriInterpCx<'tcx>) -> bool {
1216        !ecx.tcx.sess.overflow_checks()
1217    }
1218
1219    fn check_fn_target_features(
1220        ecx: &MiriInterpCx<'tcx>,
1221        instance: ty::Instance<'tcx>,
1222    ) -> InterpResult<'tcx> {
1223        let attrs = ecx.tcx.codegen_instance_attrs(instance.def);
1224        if attrs
1225            .target_features
1226            .iter()
1227            .any(|feature| !ecx.tcx.sess.internal_target_features.contains(&feature.name))
1228        {
1229            let unavailable = attrs
1230                .target_features
1231                .iter()
1232                .filter(|&feature| {
1233                    feature.kind != TargetFeatureKind::Implied
1234                        && !ecx.tcx.sess.internal_target_features.contains(&feature.name)
1235                })
1236                .fold(String::new(), |mut s, feature| {
1237                    if !s.is_empty() {
1238                        s.push_str(", ");
1239                    }
1240                    s.push_str(feature.name.as_str());
1241                    s
1242                });
1243            let msg = format!(
1244                "calling a function that requires unavailable target features: {unavailable}"
1245            );
1246            // On WASM, this is not UB, but instead gets rejected during validation of the module
1247            // (see #84988).
1248            if ecx.tcx.sess.target.is_like_wasm {
1249                throw_machine_stop!(TerminationInfo::Abort(msg));
1250            } else {
1251                throw_ub_format!("{msg}");
1252            }
1253        }
1254        interp_ok(())
1255    }
1256
1257    #[inline(always)]
1258    fn find_mir_or_eval_fn(
1259        ecx: &mut MiriInterpCx<'tcx>,
1260        instance: ty::Instance<'tcx>,
1261        abi: &FnAbi<'tcx, Ty<'tcx>>,
1262        args: &[FnArg<'tcx>],
1263        dest: &PlaceTy<'tcx>,
1264        ret: Option<mir::BasicBlock>,
1265        unwind: mir::UnwindAction,
1266    ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
1267        // For foreign items, try to see if we can emulate them.
1268        if ecx.tcx.is_foreign_item(instance.def_id()) {
1269            let _trace = enter_trace_span!("emulate_foreign_item");
1270            // An external function call that does not have a MIR body. We either find MIR elsewhere
1271            // or emulate its effect.
1272            // This will be Ok(None) if we're emulating the intrinsic entirely within Miri (no need
1273            // to run extra MIR), and Ok(Some(body)) if we found MIR to run for the
1274            // foreign function
1275            // Any needed call to `goto_block` will be performed by `emulate_foreign_item`.
1276            let args = MiriInterpCx::copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit?
1277            let link_name = Symbol::intern(ecx.tcx.symbol_name(instance).name);
1278            return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind);
1279        }
1280
1281        if ecx.machine.data_race.as_genmc_ref().is_some()
1282            && ecx.genmc_intercept_function(instance, args, dest)?
1283        {
1284            ecx.return_to_block(ret)?;
1285            return interp_ok(None);
1286        }
1287
1288        // Otherwise, load the MIR.
1289        let _trace = enter_trace_span!("load_mir");
1290        interp_ok(Some((ecx.load_mir(instance.def, None)?, instance)))
1291    }
1292
1293    #[inline(always)]
1294    fn call_extra_fn(
1295        ecx: &mut MiriInterpCx<'tcx>,
1296        fn_val: DynSym,
1297        abi: &FnAbi<'tcx, Ty<'tcx>>,
1298        args: &[FnArg<'tcx>],
1299        dest: &PlaceTy<'tcx>,
1300        ret: Option<mir::BasicBlock>,
1301        unwind: mir::UnwindAction,
1302    ) -> InterpResult<'tcx> {
1303        let args = MiriInterpCx::copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit?
1304        ecx.emulate_dyn_sym(fn_val, abi, &args, dest, ret, unwind)
1305    }
1306
1307    #[inline(always)]
1308    fn call_intrinsic(
1309        ecx: &mut MiriInterpCx<'tcx>,
1310        instance: ty::Instance<'tcx>,
1311        args: &[OpTy<'tcx>],
1312        dest: &PlaceTy<'tcx>,
1313        ret: Option<mir::BasicBlock>,
1314        unwind: mir::UnwindAction,
1315    ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
1316        ecx.call_intrinsic(instance, args, dest, ret, unwind)
1317    }
1318
1319    #[inline(always)]
1320    fn call_llvm_intrinsic(
1321        ecx: &mut MiriInterpCx<'tcx>,
1322        instance: ty::Instance<'tcx>,
1323        args: &[OpTy<'tcx>],
1324        dest: &PlaceTy<'tcx>,
1325        ret: Option<mir::BasicBlock>,
1326    ) -> InterpResult<'tcx, ()> {
1327        ecx.call_llvm_intrinsic(instance, args, dest, ret)
1328    }
1329
1330    #[inline(always)]
1331    fn assert_panic(
1332        ecx: &mut MiriInterpCx<'tcx>,
1333        msg: &mir::AssertMessage<'tcx>,
1334        unwind: mir::UnwindAction,
1335    ) -> InterpResult<'tcx> {
1336        ecx.assert_panic(msg, unwind)
1337    }
1338
1339    fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> {
1340        ecx.start_panic_nounwind(msg)
1341    }
1342
1343    fn unwind_terminate(
1344        ecx: &mut InterpCx<'tcx, Self>,
1345        reason: mir::UnwindTerminateReason,
1346    ) -> InterpResult<'tcx> {
1347        // Call the lang item.
1348        let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap();
1349        let panic = ty::Instance::mono(ecx.tcx.tcx, panic);
1350        ecx.call_function(
1351            panic,
1352            ExternAbi::Rust,
1353            &[],
1354            None,
1355            ReturnContinuation::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
1356        )?;
1357        interp_ok(())
1358    }
1359
1360    #[inline(always)]
1361    fn binary_ptr_op(
1362        ecx: &MiriInterpCx<'tcx>,
1363        bin_op: mir::BinOp,
1364        left: &ImmTy<'tcx>,
1365        right: &ImmTy<'tcx>,
1366    ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1367        ecx.binary_ptr_op(bin_op, left, right)
1368    }
1369
1370    fn atomic_load(
1371        ecx: &MiriInterpCx<'tcx>,
1372        place: &MPlaceTy<'tcx>,
1373        ordering: AtomicOrdering,
1374    ) -> InterpResult<'tcx, Scalar> {
1375        ecx.read_scalar_atomic(place, AtomicReadOrd::from(ordering))
1376    }
1377
1378    fn atomic_store(
1379        ecx: &mut MiriInterpCx<'tcx>,
1380        place: &MPlaceTy<'tcx>,
1381        val: &ImmTy<'tcx>,
1382        ordering: AtomicOrdering,
1383    ) -> InterpResult<'tcx> {
1384        ecx.write_scalar_atomic(val.to_scalar(), place, AtomicWriteOrd::from(ordering))
1385    }
1386
1387    fn atomic_rmw(
1388        ecx: &mut MiriInterpCx<'tcx>,
1389        place: &MPlaceTy<'tcx>,
1390        op: AtomicRmwOp,
1391        operand: &ImmTy<'tcx>,
1392        ordering: AtomicOrdering,
1393    ) -> InterpResult<'tcx, Scalar> {
1394        ecx.atomic_rmw(place, operand, op, AtomicRwOrd::from(ordering))
1395    }
1396
1397    fn atomic_compare_exchange(
1398        ecx: &mut MiriInterpCx<'tcx>,
1399        place: &MPlaceTy<'tcx>,
1400        expected_old: &ImmTy<'tcx>,
1401        new: &ImmTy<'tcx>,
1402        can_fail_spuriously: bool,
1403        success_ordering: AtomicOrdering,
1404        failure_ordering: AtomicOrdering,
1405    ) -> InterpResult<'tcx, (Scalar, bool)> {
1406        ecx.atomic_compare_exchange(
1407            place,
1408            expected_old,
1409            new.to_scalar(),
1410            AtomicRwOrd::from(success_ordering),
1411            AtomicReadOrd::from(failure_ordering),
1412            can_fail_spuriously,
1413        )
1414    }
1415
1416    fn atomic_fence(
1417        ecx: &MiriInterpCx<'tcx>,
1418        ordering: AtomicOrdering,
1419        singlethread: bool,
1420    ) -> InterpResult<'tcx> {
1421        if singlethread {
1422            // We don't support signal handlers or interrupts so this is a NOP.
1423            return interp_ok(());
1424        }
1425        ecx.atomic_fence(AtomicFenceOrd::from(ordering))
1426    }
1427
1428    #[inline(always)]
1429    fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(
1430        ecx: &InterpCx<'tcx, Self>,
1431        inputs: &[F1],
1432    ) -> F2 {
1433        ecx.generate_nan(inputs)
1434    }
1435
1436    #[inline(always)]
1437    fn apply_float_nondet(
1438        ecx: &mut InterpCx<'tcx, Self>,
1439        val: ImmTy<'tcx>,
1440    ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1441        crate::math::apply_random_float_error_to_imm(ecx, val, 4)
1442    }
1443
1444    #[inline(always)]
1445    fn equal_float_min_max<F: Float>(ecx: &MiriInterpCx<'tcx>, a: F, b: F) -> F {
1446        ecx.equal_float_min_max(a, b)
1447    }
1448
1449    #[inline(always)]
1450    fn float_fuse_mul_add(ecx: &InterpCx<'tcx, Self>) -> bool {
1451        ecx.machine.float_nondet && ecx.machine.rng.borrow_mut().random()
1452    }
1453
1454    #[inline(always)]
1455    fn runtime_checks(
1456        ecx: &InterpCx<'tcx, Self>,
1457        r: mir::RuntimeChecks,
1458    ) -> InterpResult<'tcx, bool> {
1459        interp_ok(r.value(ecx.tcx.sess))
1460    }
1461
1462    #[inline(always)]
1463    fn thread_local_static_pointer(
1464        ecx: &mut MiriInterpCx<'tcx>,
1465        def_id: DefId,
1466    ) -> InterpResult<'tcx, StrictPointer> {
1467        ecx.get_or_create_thread_local_alloc(def_id)
1468    }
1469
1470    fn extern_static_pointer(
1471        ecx: &MiriInterpCx<'tcx>,
1472        def_id: DefId,
1473    ) -> InterpResult<'tcx, StrictPointer> {
1474        let link_name = Symbol::intern(ecx.tcx.symbol_name(Instance::mono(*ecx.tcx, def_id)).name);
1475        let def_ty = ecx.tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
1476        let extern_decl_layout =
1477            ecx.tcx.layout_of(ecx.typing_env().as_query_input(def_ty)).unwrap();
1478
1479        // Look up the `ptr` in the right map, depending on whether this is an "import"
1480        // static or a real one.
1481        let ptr = match ecx.tcx.codegen_fn_attrs(def_id).import_linkage {
1482            None => ecx.machine.extern_statics.get(&link_name),
1483            Some(_) => ecx.machine.extern_statics_imports.get(&link_name),
1484        };
1485        if let Some(&ptr) = ptr {
1486            ecx.check_shim_symbol_clash(link_name)?;
1487            // Various parts of the engine rely on `get_alloc_info` for size and alignment
1488            // information. That uses the type information of this static.
1489            // Make sure it matches the Miri allocation for this.
1490            let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
1491                panic!("extern_statics cannot contain wildcards")
1492            };
1493            let info = ecx.get_alloc_info(alloc_id);
1494            if extern_decl_layout.size > info.size || extern_decl_layout.align.abi > info.align {
1495                throw_ub_format!(
1496                    "extern static `{link_name}` has been declared as `{krate}::{name}` \
1497                    with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
1498                    but Miri emulates it via an extern static shim \
1499                    with a size of {shim_size} bytes and alignment of {shim_align} bytes",
1500                    name = ecx.tcx.def_path_str(def_id),
1501                    krate = ecx.tcx.crate_name(def_id.krate),
1502                    decl_size = extern_decl_layout.size.bytes(),
1503                    decl_align = extern_decl_layout.align.bytes(),
1504                    shim_size = info.size.bytes(),
1505                    shim_align = info.align.bytes(),
1506                )
1507            }
1508            interp_ok(ptr)
1509        } else if ecx.tcx.codegen_fn_attrs(def_id).import_linkage == Some(Linkage::ExternalWeak) {
1510            // Symbols with weak linkage default to null if they are not defined. However we can't
1511            // create new allocations here. On the plus side we know rustc rejects non-ptr-sized
1512            // weak statics so we can just use a single global "null" allocation for all of them.
1513            // The memory we are assigning this address to is anyway somewhat "fake", it's an
1514            // indirection introduced by how Rust represents external symbols with linkage (see
1515            // <https://github.com/rust-lang/rust/issues/156468>). So we can just specify that such
1516            // memory does not have unique addresses, despite being technically a `static`.
1517            assert_eq!(
1518                extern_decl_layout.size,
1519                ecx.tcx.data_layout.pointer_size(),
1520                "non-pointer-sized weak static"
1521            );
1522            interp_ok(
1523                ecx.machine
1524                    .extern_static_weak_import_default
1525                    .expect("`missing_weak_symbol` should have been initialized"),
1526            )
1527        } else {
1528            // Look for a Rust static with this symbol name in the crate graph.
1529            let Some(instance) = ecx.lookup_exported_static(link_name)? else {
1530                throw_unsup_format!("extern static `{link_name}` is not supported by Miri");
1531            };
1532            // Evaluate the static to get its allocation.
1533            let place = ecx.eval_global(instance)?;
1534            let static_ptr = place.ptr().into_pointer_or_addr().unwrap();
1535            // Validate the allocation matches the declared size and alignment.
1536            let alloc_id = static_ptr.provenance.get_alloc_id().unwrap();
1537            let info = ecx.get_alloc_info(alloc_id);
1538            if extern_decl_layout.size > info.size || extern_decl_layout.align.abi > info.align {
1539                throw_ub_format!(
1540                    "extern static `{link_name}` has been declared as `{krate}::{name}` \
1541                    with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
1542                    but the exported static with that name has a size of {shim_size} bytes and \
1543                    alignment of {shim_align} bytes",
1544                    name = ecx.tcx.def_path_str(def_id),
1545                    krate = ecx.tcx.crate_name(def_id.krate),
1546                    decl_size = extern_decl_layout.size.bytes(),
1547                    decl_align = extern_decl_layout.align.bytes(),
1548                    shim_size = info.size.bytes(),
1549                    shim_align = info.align.bytes(),
1550                )
1551            }
1552            // Check that the mutability of the declared static matches that of the backing.
1553            // If the backing static can be modified (because it is a `static mut`, or because
1554            // it is a `static` whose type has interior mutability) while the declaration here
1555            // is a non-mut `static` with a `Freeze` type, then the compiler's assumption that
1556            // the value never changes may be violated, so this may cause UB.
1557            // This is somehow defensive, as the allocation might be mutable but no mutation
1558            // ever happens, but this is probably the most precise thing we can do.
1559            // Specially, the second case is very defensive and we may be able to lift it.
1560            let DefKind::Static { mutability, .. } = ecx.tcx.def_kind(def_id) else {
1561                unreachable!("`{def_id:?}` is not a static");
1562            };
1563            let decl_is_mut =
1564                !(mutability == Mutability::Not && ecx.type_is_freeze(extern_decl_layout.ty));
1565            let backing_is_mut = ecx.get_alloc_mutability(alloc_id)? == Mutability::Mut;
1566            if !decl_is_mut && backing_is_mut {
1567                throw_ub_format!(
1568                    "extern static `{krate}::{name}` is declared as an immutable `static`, \
1569                    but the backing static is mutable",
1570                    name = ecx.tcx.def_path_str(def_id),
1571                    krate = ecx.tcx.crate_name(def_id.krate),
1572                )
1573            }
1574            if decl_is_mut && !backing_is_mut {
1575                throw_ub_format!(
1576                    "extern static `{krate}::{name}` is declared as an mutable `static`, \
1577                    but the backing static is immutable",
1578                    name = ecx.tcx.def_path_str(def_id),
1579                    krate = ecx.tcx.crate_name(def_id.krate),
1580                )
1581            }
1582            interp_ok(static_ptr)
1583        }
1584    }
1585
1586    fn init_local_allocation(
1587        ecx: &MiriInterpCx<'tcx>,
1588        id: AllocId,
1589        kind: MemoryKind,
1590        size: Size,
1591        align: Align,
1592    ) -> InterpResult<'tcx, Self::AllocExtra> {
1593        assert!(kind != MiriMemoryKind::Global.into());
1594        MiriMachine::init_allocation(ecx, id, kind, size, align)
1595    }
1596
1597    fn adjust_alloc_root_pointer(
1598        ecx: &MiriInterpCx<'tcx>,
1599        ptr: interpret::Pointer<CtfeProvenance>,
1600        kind: Option<MemoryKind>,
1601    ) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
1602        let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None");
1603        let alloc_id = ptr.provenance.alloc_id();
1604        if cfg!(debug_assertions) {
1605            // The machine promises to never call us on thread-local or extern statics.
1606            match ecx.tcx.try_get_global_alloc(alloc_id) {
1607                Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
1608                    panic!("adjust_alloc_root_pointer called on thread-local static")
1609                }
1610                Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => {
1611                    panic!("adjust_alloc_root_pointer called on extern static")
1612                }
1613                _ => {}
1614            }
1615        }
1616        // FIXME: can we somehow preserve the immutability of `ptr`?
1617        let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
1618            borrow_tracker.borrow_mut().root_ptr_tag(alloc_id, &ecx.machine)
1619        } else {
1620            // Value does not matter, SB is disabled
1621            BorTag::default()
1622        };
1623        ecx.adjust_alloc_root_pointer(ptr, tag, kind)
1624    }
1625
1626    /// Called on `usize as ptr` casts.
1627    #[inline(always)]
1628    fn ptr_from_addr_cast(ecx: &MiriInterpCx<'tcx>, addr: u64) -> InterpResult<'tcx, Pointer> {
1629        ecx.ptr_from_addr_cast(addr)
1630    }
1631
1632    /// Called on `ptr as usize` casts.
1633    /// (Actually computing the resulting `usize` doesn't need machine help,
1634    /// that's just `Scalar::try_to_int`.)
1635    #[inline(always)]
1636    fn expose_provenance(
1637        ecx: &InterpCx<'tcx, Self>,
1638        provenance: Self::Provenance,
1639    ) -> InterpResult<'tcx> {
1640        ecx.expose_provenance(provenance)
1641    }
1642
1643    /// Convert a pointer with provenance into an allocation-offset pair and extra provenance info.
1644    /// `size` says how many bytes of memory are expected at that pointer. The *sign* of `size` can
1645    /// be used to disambiguate situations where a wildcard pointer sits right in between two
1646    /// allocations.
1647    ///
1648    /// If `ptr.provenance.get_alloc_id()` is `Some(p)`, the returned `AllocId` must be `p`.
1649    /// The resulting `AllocId` will just be used for that one step and the forgotten again
1650    /// (i.e., we'll never turn the data returned here back into a `Pointer` that might be
1651    /// stored in machine state).
1652    ///
1653    /// When this fails, that means the pointer does not point to a live allocation.
1654    fn ptr_get_alloc(
1655        ecx: &MiriInterpCx<'tcx>,
1656        ptr: StrictPointer,
1657        size: i64,
1658    ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
1659        let rel = ecx.ptr_get_alloc(ptr, size);
1660
1661        rel.map(|(alloc_id, size)| {
1662            let tag = match ptr.provenance {
1663                Provenance::Concrete { tag, .. } => ProvenanceExtra::Concrete(tag),
1664                Provenance::Wildcard => ProvenanceExtra::Wildcard,
1665            };
1666            (alloc_id, size, tag)
1667        })
1668    }
1669
1670    /// Called to adjust global allocations to the Provenance and AllocExtra of this machine.
1671    ///
1672    /// If `alloc` contains pointers, then they are all pointing to globals.
1673    ///
1674    /// This should avoid copying if no work has to be done! If this returns an owned
1675    /// allocation (because a copy had to be done to adjust things), machine memory will
1676    /// cache the result. (This relies on `AllocMap::get_or` being able to add the
1677    /// owned allocation to the map even when the map is shared.)
1678    fn adjust_global_allocation<'b>(
1679        ecx: &InterpCx<'tcx, Self>,
1680        id: AllocId,
1681        alloc: &'b Allocation,
1682    ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
1683    {
1684        let alloc = alloc.adjust_from_tcx(
1685            &ecx.tcx,
1686            |bytes, align| ecx.get_global_alloc_bytes(id, bytes, align),
1687            |ptr| ecx.global_root_pointer(ptr),
1688        )?;
1689        let kind = MiriMemoryKind::Global.into();
1690        let extra = MiriMachine::init_allocation(ecx, id, kind, alloc.size(), alloc.align)?;
1691        interp_ok(Cow::Owned(alloc.with_extra(extra)))
1692    }
1693
1694    #[inline(always)]
1695    fn before_memory_read(
1696        _tcx: TyCtxtAt<'tcx>,
1697        machine: &Self,
1698        alloc_extra: &AllocExtra<'tcx>,
1699        ptr: Pointer,
1700        (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1701        range: AllocRange,
1702    ) -> InterpResult<'tcx> {
1703        if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1704            machine.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(
1705                alloc_id,
1706                range,
1707                borrow_tracker::AccessKind::Read,
1708            ));
1709        }
1710        // The order of checks is deliberate, to prefer reporting a data race over a borrow tracker error.
1711        match &machine.data_race {
1712            GlobalDataRaceHandler::None => {}
1713            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1714                genmc_ctx.memory_load(machine, ptr.addr(), range.size)?,
1715            GlobalDataRaceHandler::Vclocks(_data_race) => {
1716                let _trace = enter_trace_span!(data_race::before_memory_read);
1717                let AllocDataRaceHandler::Vclocks(data_race, _weak_memory) = &alloc_extra.data_race
1718                else {
1719                    unreachable!();
1720                };
1721                data_race.read_non_atomic(alloc_id, range, NaReadType::Read, None, machine)?;
1722            }
1723        }
1724        if let Some(borrow_tracker) = &alloc_extra.borrow_tracker {
1725            borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?;
1726        }
1727        // Check if there are any sync objects that would like to prevent reading this memory.
1728        for (_offset, obj) in alloc_extra.sync_objs.range(range.start..range.end()) {
1729            obj.on_access(concurrency::sync::AccessKind::Read)?;
1730        }
1731
1732        interp_ok(())
1733    }
1734
1735    #[inline(always)]
1736    fn before_memory_write(
1737        _tcx: TyCtxtAt<'tcx>,
1738        machine: &mut Self,
1739        alloc_extra: &mut AllocExtra<'tcx>,
1740        ptr: Pointer,
1741        (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1742        range: AllocRange,
1743    ) -> InterpResult<'tcx> {
1744        if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1745            machine.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(
1746                alloc_id,
1747                range,
1748                borrow_tracker::AccessKind::Write,
1749            ));
1750        }
1751        match &machine.data_race {
1752            GlobalDataRaceHandler::None => {}
1753            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1754                genmc_ctx.memory_store(machine, ptr.addr(), range.size)?,
1755            GlobalDataRaceHandler::Vclocks(_global_state) => {
1756                let _trace = enter_trace_span!(data_race::before_memory_write);
1757                let AllocDataRaceHandler::Vclocks(data_race, weak_memory) =
1758                    &mut alloc_extra.data_race
1759                else {
1760                    unreachable!()
1761                };
1762                data_race.write_non_atomic(alloc_id, range, NaWriteType::Write, None, machine)?;
1763                if let Some(weak_memory) = weak_memory {
1764                    weak_memory
1765                        .non_atomic_write(range, machine.data_race.as_vclocks_ref().unwrap());
1766                }
1767            }
1768        }
1769        if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1770            borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?;
1771        }
1772        // Delete sync objects that don't like writes.
1773        // Most of the time, we can just skip this.
1774        if !alloc_extra.sync_objs.is_empty() {
1775            let mut to_delete = vec![];
1776            for (offset, obj) in alloc_extra.sync_objs.range(range.start..range.end()) {
1777                obj.on_access(concurrency::sync::AccessKind::Write)?;
1778                if obj.delete_on_write() {
1779                    to_delete.push(*offset);
1780                }
1781            }
1782            for offset in to_delete {
1783                alloc_extra.sync_objs.remove(&offset);
1784            }
1785        }
1786        interp_ok(())
1787    }
1788
1789    #[inline(always)]
1790    fn before_memory_deallocation(
1791        _tcx: TyCtxtAt<'tcx>,
1792        machine: &mut Self,
1793        alloc_extra: &mut AllocExtra<'tcx>,
1794        ptr: Pointer,
1795        (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
1796        size: Size,
1797        align: Align,
1798        kind: MemoryKind,
1799    ) -> InterpResult<'tcx> {
1800        if machine.tracked_alloc_ids.contains(&alloc_id) {
1801            machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
1802        }
1803        match &machine.data_race {
1804            GlobalDataRaceHandler::None => {}
1805            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1806                genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), kind)?,
1807            GlobalDataRaceHandler::Vclocks(_global_state) => {
1808                let _trace = enter_trace_span!(data_race::before_memory_deallocation);
1809                let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap();
1810                data_race.write_non_atomic(
1811                    alloc_id,
1812                    alloc_range(Size::ZERO, size),
1813                    NaWriteType::Deallocate,
1814                    None,
1815                    machine,
1816                )?;
1817            }
1818        }
1819        if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1820            borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?;
1821        }
1822        // Check if there are any sync objects that would like to prevent freeing this memory.
1823        for obj in alloc_extra.sync_objs.values() {
1824            obj.on_access(concurrency::sync::AccessKind::Dealloc)?;
1825        }
1826
1827        if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
1828        {
1829            *deallocated_at = Some(machine.current_user_relevant_span());
1830        }
1831        machine.free_alloc_id(alloc_id, size, align, kind);
1832        interp_ok(())
1833    }
1834
1835    #[inline(always)]
1836    fn retag_ptr_value(
1837        ecx: &mut InterpCx<'tcx, Self>,
1838        val: &ImmTy<'tcx>,
1839        ty: Ty<'tcx>,
1840    ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
1841        if ecx.machine.borrow_tracker.is_some() {
1842            ecx.retag_ptr_value(val, ty)
1843        } else {
1844            interp_ok(None)
1845        }
1846    }
1847
1848    #[inline(always)]
1849    fn with_retag_mode<T>(
1850        ecx: &mut InterpCx<'tcx, Self>,
1851        mode: RetagMode,
1852        f: impl FnOnce(&mut InterpCx<'tcx, Self>) -> InterpResult<'tcx, T>,
1853    ) -> InterpResult<'tcx, T> {
1854        if ecx.machine.borrow_tracker.is_some() { ecx.with_retag_mode(mode, f) } else { f(ecx) }
1855    }
1856
1857    fn protect_in_place_function_argument(
1858        ecx: &mut InterpCx<'tcx, Self>,
1859        place: &MPlaceTy<'tcx>,
1860    ) -> InterpResult<'tcx> {
1861        // If we have a borrow tracker, we also have it set up protection so that all reads *and
1862        // writes* during this call are insta-UB.
1863        let protected_place = if ecx.machine.borrow_tracker.is_some() {
1864            ecx.protect_place(place)?
1865        } else {
1866            // No borrow tracker.
1867            place.clone()
1868        };
1869        // We do need to write `uninit` so that even after the call ends, the former contents of
1870        // this place cannot be observed any more. We do the write after retagging so that for
1871        // Tree Borrows, this is considered to activate the new tag.
1872        // Conveniently this also ensures that the place actually points to suitable memory.
1873        ecx.write_uninit(&protected_place)?;
1874        // Now we throw away the protected place, ensuring its tag is never used again.
1875        interp_ok(())
1876    }
1877
1878    #[inline(always)]
1879    fn init_frame(
1880        ecx: &mut InterpCx<'tcx, Self>,
1881        frame: Frame<'tcx, Provenance>,
1882    ) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
1883        // Start recording our event before doing anything else
1884        let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
1885            let fn_name = frame.instance().to_string();
1886            let entry = ecx.machine.string_cache.entry(fn_name.clone());
1887            let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
1888
1889            Some(profiler.start_recording_interval_event_detached(
1890                *name,
1891                measureme::EventId::from_label(*name),
1892                ecx.active_thread().to_u32(),
1893            ))
1894        } else {
1895            None
1896        };
1897
1898        let borrow_tracker = ecx.machine.borrow_tracker.as_ref();
1899
1900        let extra = FrameExtra {
1901            borrow_tracker: borrow_tracker.map(|bt| bt.borrow_mut().new_frame()),
1902            catch_unwind: None,
1903            timing,
1904            user_relevance: ecx.machine.user_relevance(&frame),
1905            data_race: ecx
1906                .machine
1907                .data_race
1908                .as_vclocks_ref()
1909                .map(|_| data_race::FrameState::default()),
1910        };
1911
1912        interp_ok(frame.with_extra(extra))
1913    }
1914
1915    fn stack<'a>(
1916        ecx: &'a InterpCx<'tcx, Self>,
1917    ) -> &'a [Frame<'tcx, Self::Provenance, Self::FrameExtra>] {
1918        ecx.active_thread_stack()
1919    }
1920
1921    fn stack_mut<'a>(
1922        ecx: &'a mut InterpCx<'tcx, Self>,
1923    ) -> &'a mut Vec<Frame<'tcx, Self::Provenance, Self::FrameExtra>> {
1924        ecx.active_thread_stack_mut()
1925    }
1926
1927    fn before_terminator(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1928        ecx.machine.basic_block_count += 1u64; // a u64 that is only incremented by 1 will "never" overflow
1929        ecx.machine.since_gc += 1;
1930        // Possibly report our progress. This will point at the terminator we are about to execute.
1931        if let Some(report_progress) = ecx.machine.report_progress {
1932            if ecx.machine.basic_block_count.is_multiple_of(u64::from(report_progress)) {
1933                ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport {
1934                    block_count: ecx.machine.basic_block_count,
1935                });
1936            }
1937        }
1938
1939        // Search for BorTags to find all live pointers, then remove all other tags from borrow
1940        // stacks. Also clean up dropped readiness watchers from the global readiness interest
1941        // table and closed source file descriptions in the blocking I/O manager.
1942        // When debug assertions are enabled, run the GC as often as possible so that any cases
1943        // where it mistakenly removes an important tag become visible.
1944        if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
1945            ecx.machine.since_gc = 0;
1946            ecx.run_provenance_gc();
1947            ecx.machine.blocking_io.run_gc();
1948        }
1949
1950        // These are our preemption points.
1951        // (This will only take effect after the terminator has been executed.)
1952        ecx.maybe_preempt_active_thread();
1953
1954        // Make sure some time passes.
1955        ecx.machine.monotonic_clock.tick();
1956
1957        interp_ok(())
1958    }
1959
1960    #[inline(always)]
1961    fn after_stack_push(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1962        if ecx.frame().extra.user_relevance >= ecx.active_thread_ref().current_user_relevance() {
1963            // We just pushed a frame that's at least as relevant as the so-far most relevant frame.
1964            // That means we are now the most relevant frame.
1965            let stack_len = ecx.active_thread_stack().len();
1966            ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
1967        }
1968        interp_ok(())
1969    }
1970
1971    fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1972        let frame = ecx.frame();
1973        // We want this *before* the return value copy, because the return place itself is protected
1974        // until we do `on_stack_pop` here, and we need to un-protect it to copy the return value.
1975        if ecx.machine.borrow_tracker.is_some() {
1976            ecx.on_stack_pop(frame)?;
1977        }
1978        if ecx
1979            .active_thread_ref()
1980            .top_user_relevant_frame()
1981            .expect("there should always be a most relevant frame for a non-empty stack")
1982            == ecx.frame_idx()
1983        {
1984            // We are popping the most relevant frame. We have no clue what the next relevant frame
1985            // below that is, so we recompute that.
1986            // (If this ever becomes a bottleneck, we could have `push` store the previous
1987            // user-relevant frame and restore that here.)
1988            // We have to skip the frame that is just being popped.
1989            ecx.active_thread_mut().recompute_top_user_relevant_frame(/* skip */ 1);
1990        }
1991        // tracing-tree can automatically annotate scope changes, but it gets very confused by our
1992        // concurrency and what it prints is just plain wrong. So we print our own information
1993        // instead. (Cc https://github.com/rust-lang/miri/issues/2266)
1994        info!("Leaving {}", ecx.frame().instance());
1995        interp_ok(())
1996    }
1997
1998    #[inline(always)]
1999    fn after_stack_pop(
2000        ecx: &mut InterpCx<'tcx, Self>,
2001        frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>,
2002        unwinding: bool,
2003    ) -> InterpResult<'tcx, ReturnAction> {
2004        let res = {
2005            // Move `frame` into a sub-scope so we control when it will be dropped.
2006            let mut frame = frame;
2007            let timing = frame.extra.timing.take();
2008            let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
2009            if let Some(profiler) = ecx.machine.profiler.as_ref() {
2010                profiler.finish_recording_interval_event(timing.unwrap());
2011            }
2012            res
2013        };
2014        // Needs to be done after dropping frame to show up on the right nesting level.
2015        // (Cc https://github.com/rust-lang/miri/issues/2266)
2016        if !ecx.active_thread_stack().is_empty() {
2017            info!("Continuing in {}", ecx.frame().instance());
2018        }
2019        res
2020    }
2021
2022    fn after_local_read(ecx: &InterpCx<'tcx, Self>, local: mir::Local) -> InterpResult<'tcx> {
2023        if let Some(data_race) = &ecx.frame().extra.data_race {
2024            let _trace = enter_trace_span!(data_race::after_local_read);
2025            data_race.local_read(local, &ecx.machine);
2026        }
2027        interp_ok(())
2028    }
2029
2030    fn after_local_write(
2031        ecx: &mut InterpCx<'tcx, Self>,
2032        local: mir::Local,
2033        storage_live: bool,
2034    ) -> InterpResult<'tcx> {
2035        if let Some(data_race) = &ecx.frame().extra.data_race {
2036            let _trace = enter_trace_span!(data_race::after_local_write);
2037            data_race.local_write(local, storage_live, &ecx.machine);
2038        }
2039        interp_ok(())
2040    }
2041
2042    fn after_local_moved_to_memory(
2043        ecx: &mut InterpCx<'tcx, Self>,
2044        local: mir::Local,
2045        mplace: &MPlaceTy<'tcx>,
2046    ) -> InterpResult<'tcx> {
2047        let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else {
2048            panic!("after_local_allocated should only be called on fresh allocations");
2049        };
2050        // Record the span where this was allocated: the declaration of the local.
2051        let local_decl = &ecx.frame().body().local_decls[local];
2052        let span = local_decl.source_info.span;
2053        ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
2054        // The data race system has to fix the clocks used for this write.
2055        let (alloc_info, machine) = ecx.get_alloc_extra_mut(alloc_id)?;
2056        if let Some(data_race) =
2057            &machine.threads.active_thread_stack().last().unwrap().extra.data_race
2058        {
2059            let _trace = enter_trace_span!(data_race::after_local_moved_to_memory);
2060            data_race.local_moved_to_memory(
2061                local,
2062                alloc_info.data_race.as_vclocks_mut().unwrap(),
2063                machine,
2064            );
2065        }
2066        interp_ok(())
2067    }
2068
2069    fn get_global_alloc_salt(
2070        ecx: &InterpCx<'tcx, Self>,
2071        instance: Option<ty::Instance<'tcx>>,
2072    ) -> usize {
2073        let unique = if let Some(instance) = instance {
2074            // Functions cannot be identified by pointers, as asm-equal functions can get
2075            // deduplicated by the linker (we set the "unnamed_addr" attribute for LLVM) and
2076            // functions can be duplicated across crates. We thus generate a new `AllocId` for every
2077            // mention of a function. This means that `main as fn() == main as fn()` is false, while
2078            // `let x = main as fn(); x == x` is true. However, as a quality-of-life feature it can
2079            // be useful to identify certain functions uniquely, e.g. for backtraces. So we identify
2080            // whether codegen will actually emit duplicate functions. It does that when they have
2081            // non-lifetime generics, or when they can be inlined. All other functions are given a
2082            // unique address. This is not a stable guarantee! The `inline` attribute is a hint and
2083            // cannot be relied upon for anything. But if we don't do this, the
2084            // `__rust_begin_short_backtrace`/`__rust_end_short_backtrace` logic breaks and panic
2085            // backtraces look terrible.
2086            let is_generic = instance
2087                .args
2088                .into_iter()
2089                .any(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_)));
2090            let can_be_inlined = matches!(
2091                ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
2092                InliningThreshold::Always
2093            ) || !matches!(
2094                ecx.tcx.codegen_instance_attrs(instance.def).inline,
2095                InlineAttr::Never
2096            );
2097            !is_generic && !can_be_inlined
2098        } else {
2099            // Non-functions are never unique.
2100            false
2101        };
2102        // Always use the same salt if the allocation is unique.
2103        if unique {
2104            CTFE_ALLOC_SALT
2105        } else {
2106            ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL)
2107        }
2108    }
2109
2110    fn cached_union_data_range<'e>(
2111        ecx: &'e mut InterpCx<'tcx, Self>,
2112        ty: Ty<'tcx>,
2113        compute_range: impl FnOnce() -> RangeSet,
2114    ) -> Cow<'e, RangeSet> {
2115        Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range))
2116    }
2117
2118    fn get_default_alloc_params(&self) -> <Self::Bytes as AllocBytes>::AllocParams {
2119        use crate::alloc::MiriAllocParams;
2120
2121        match &self.allocator {
2122            Some(alloc) => MiriAllocParams::Isolated(alloc.clone()),
2123            None => MiriAllocParams::Global,
2124        }
2125    }
2126
2127    fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan {
2128        #[cfg(feature = "tracing")]
2129        {
2130            span().entered()
2131        }
2132        #[cfg(not(feature = "tracing"))]
2133        #[expect(clippy::unused_unit)]
2134        {
2135            let _ = span; // so we avoid the "unused variable" warning
2136            ()
2137        }
2138    }
2139}
2140
2141/// Trait for callbacks handling asynchronous machine operations.
2142pub trait MachineCallback<'tcx, T>: VisitProvenance {
2143    /// The function to be invoked when the callback is fired.
2144    fn call(
2145        self: Box<Self>,
2146        ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
2147        arg: T,
2148    ) -> InterpResult<'tcx>;
2149}
2150
2151/// Type alias for boxed machine callbacks with generic argument type.
2152pub type DynMachineCallback<'tcx, T> = Box<dyn MachineCallback<'tcx, T> + 'tcx>;
2153
2154/// Creates a `DynMachineCallback`:
2155///
2156/// ```rust
2157/// callback!(
2158///     @capture<'tcx> {
2159///         var1: Ty1,
2160///         var2: Ty2<'tcx>,
2161///     }
2162///     |this, arg: ArgTy| {
2163///         // Implement the callback here.
2164///         todo!()
2165///     }
2166/// )
2167/// ```
2168///
2169/// All the argument types must implement `VisitProvenance`.
2170#[macro_export]
2171macro_rules! callback {
2172    (@capture<$tcx:lifetime $(,)? $($lft:lifetime),*>
2173        { $($name:ident: $type:ty),* $(,)? }
2174     |$this:ident, $arg:ident: $arg_ty:ty| $body:expr $(,)?) => {{
2175        struct Callback<$tcx, $($lft),*> {
2176            $($name: $type,)*
2177            _phantom: std::marker::PhantomData<&$tcx ()>,
2178        }
2179
2180        impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> {
2181            fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
2182                $(
2183                    VisitProvenance::visit_provenance(&self.$name, _visit);
2184                )*
2185            }
2186        }
2187
2188        impl<$tcx, $($lft),*> MachineCallback<$tcx, $arg_ty> for Callback<$tcx, $($lft),*> {
2189            fn call(
2190                self: Box<Self>,
2191                $this: &mut MiriInterpCx<$tcx>,
2192                $arg: $arg_ty
2193            ) -> InterpResult<$tcx> {
2194                #[allow(unused_variables)]
2195                let Callback { $($name,)* _phantom } = *self;
2196                $body
2197            }
2198        }
2199
2200        Box::new(Callback {
2201            $($name,)*
2202            _phantom: std::marker::PhantomData
2203        })
2204    }};
2205}