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::expand::allocator::{self, SpecialAllocatorMethod};
16use rustc_data_structures::either::Either;
17use rustc_data_structures::fx::{FxHashMap, FxHashSet};
18#[allow(unused)]
19use rustc_data_structures::static_assert_size;
20use rustc_hir::attrs::{InlineAttr, Linkage};
21use rustc_log::tracing;
22use rustc_middle::middle::codegen_fn_attrs::TargetFeatureKind;
23use rustc_middle::mir;
24use rustc_middle::query::TyCtxtAt;
25use rustc_middle::ty::layout::{
26    HasTyCtxt, HasTypingEnv, LayoutCx, LayoutError, LayoutOf, TyAndLayout,
27};
28use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
29use rustc_session::config::InliningThreshold;
30use rustc_span::def_id::{CrateNum, DefId};
31use rustc_span::{Span, SpanData, Symbol};
32use rustc_symbol_mangling::mangle_internal_symbol;
33use rustc_target::callconv::FnAbi;
34use rustc_target::spec::{Arch, Os};
35
36use crate::alloc_addresses::EvalContextExt;
37use crate::concurrency::cpu_affinity::{self, CpuAffinityMask};
38use crate::concurrency::data_race::{self, NaReadType, NaWriteType};
39use crate::concurrency::sync::SyncObj;
40use crate::concurrency::{
41    AllocDataRaceHandler, GenmcCtx, GenmcEvalContextExt as _, GlobalDataRaceHandler, weak_memory,
42};
43use crate::helpers::is_no_core;
44use crate::shims::readiness::DelayedReadinessUpdates;
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 mut_raw_ptr: TyAndLayout<'tcx>,   // *mut ()
435    pub const_raw_ptr: TyAndLayout<'tcx>, // *const ()
436}
437
438impl<'tcx> PrimitiveLayouts<'tcx> {
439    fn new(layout_cx: LayoutCx<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
440        let tcx = layout_cx.tcx();
441        let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit);
442        let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
443        Ok(Self {
444            unit: layout_cx.layout_of(tcx.types.unit)?,
445            i8: layout_cx.layout_of(tcx.types.i8)?,
446            i16: layout_cx.layout_of(tcx.types.i16)?,
447            i32: layout_cx.layout_of(tcx.types.i32)?,
448            i64: layout_cx.layout_of(tcx.types.i64)?,
449            i128: layout_cx.layout_of(tcx.types.i128)?,
450            isize: layout_cx.layout_of(tcx.types.isize)?,
451            u8: layout_cx.layout_of(tcx.types.u8)?,
452            u16: layout_cx.layout_of(tcx.types.u16)?,
453            u32: layout_cx.layout_of(tcx.types.u32)?,
454            u64: layout_cx.layout_of(tcx.types.u64)?,
455            u128: layout_cx.layout_of(tcx.types.u128)?,
456            usize: layout_cx.layout_of(tcx.types.usize)?,
457            bool: layout_cx.layout_of(tcx.types.bool)?,
458            mut_raw_ptr: layout_cx.layout_of(mut_raw_ptr)?,
459            const_raw_ptr: layout_cx.layout_of(const_raw_ptr)?,
460        })
461    }
462
463    pub fn uint(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
464        match size.bits() {
465            8 => Some(self.u8),
466            16 => Some(self.u16),
467            32 => Some(self.u32),
468            64 => Some(self.u64),
469            128 => Some(self.u128),
470            _ => None,
471        }
472    }
473
474    pub fn int(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
475        match size.bits() {
476            8 => Some(self.i8),
477            16 => Some(self.i16),
478            32 => Some(self.i32),
479            64 => Some(self.i64),
480            128 => Some(self.i128),
481            _ => None,
482        }
483    }
484}
485
486/// The machine itself.
487///
488/// If you add anything here that stores machine values, remember to update
489/// `visit_all_machine_values`!
490pub struct MiriMachine<'tcx> {
491    // We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access.
492    pub tcx: TyCtxt<'tcx>,
493
494    /// Global data for borrow tracking.
495    pub borrow_tracker: Option<borrow_tracker::GlobalState>,
496
497    /// Depending on settings, this will be `None`,
498    /// global data for a data race detector,
499    /// or the context required for running in GenMC mode.
500    ///
501    /// Invariant: The enum variant must match the enum variant of `AllocDataRaceHandler` in the `data_race` field of all `AllocExtra`.
502    pub data_race: GlobalDataRaceHandler,
503
504    /// Ptr-int-cast module global data.
505    pub alloc_addresses: alloc_addresses::GlobalState,
506
507    /// Environment variables.
508    pub(crate) env_vars: EnvVars<'tcx>,
509
510    /// Return place of the main function.
511    pub(crate) main_fn_ret_place: Option<MPlaceTy<'tcx>>,
512
513    /// Program arguments (`Option` because we can only initialize them after creating the ecx).
514    /// These are *pointers* to argc/argv because macOS.
515    /// We also need the full command line as one string because of Windows.
516    pub(crate) argc: Option<Pointer>,
517    pub(crate) argv: Option<Pointer>,
518    pub(crate) cmd_line: Option<Pointer>,
519
520    /// TLS state.
521    pub(crate) tls: TlsData<'tcx>,
522
523    /// What should Miri do when an op requires communicating with the host,
524    /// such as accessing host env vars, random number generation, and
525    /// file system access.
526    pub(crate) isolated_op: IsolatedOp,
527
528    /// Whether to enforce the validity invariant.
529    pub(crate) validation: ValidationMode,
530
531    /// The table of file descriptors.
532    pub(crate) fds: shims::FdTable,
533    /// The table of directory descriptors.
534    pub(crate) dirs: shims::DirTable,
535
536    /// Managing file descriptors whose readiness needs to be updated.
537    pub(crate) delayed_readiness_updates: Rc<DelayedReadinessUpdates>,
538
539    /// This machine's monotone clock.
540    pub(crate) monotonic_clock: MonotonicClock,
541
542    /// The set of threads.
543    pub(crate) threads: ThreadManager<'tcx>,
544
545    /// Handles blocking I/O and polling for completion.
546    pub(crate) blocking_io: BlockingIoManager,
547
548    /// Stores which thread is eligible to run on which CPUs.
549    /// This has no effect at all, it is just tracked to produce the correct result
550    /// in `sched_getaffinity`
551    /// This will be `None` when running `#![no_core]` crates.
552    pub(crate) thread_cpu_affinity: Option<FxHashMap<ThreadId, CpuAffinityMask>>,
553
554    /// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
555    pub(crate) layouts: PrimitiveLayouts<'tcx>,
556
557    /// Allocations that are considered roots of static memory (that may leak).
558    pub(crate) static_roots: Vec<AllocId>,
559
560    /// The `measureme` profiler used to record timing information about
561    /// the emulated program.
562    profiler: Option<measureme::Profiler>,
563    /// Used with `profiler` to cache the `StringId`s for event names
564    /// used with `measureme`.
565    string_cache: FxHashMap<String, measureme::StringId>,
566
567    /// Cache of `Instance` exported under the given `Symbol` name.
568    /// `None` means no `Instance` exported under the given name is found.
569    pub(crate) exported_symbols_cache: FxHashMap<Symbol, Option<Instance<'tcx>>>,
570
571    /// Equivalent setting as RUST_BACKTRACE on encountering an error.
572    pub(crate) backtrace_style: BacktraceStyle,
573
574    /// Crates which are considered user-relevant for the purposes of error reporting.
575    pub(crate) user_relevant_crates: Vec<CrateNum>,
576
577    /// Mapping extern static names to their pointer.
578    pub(crate) extern_statics: FxHashMap<Symbol, StrictPointer>,
579    /// Statics with `import_linkage` have an extra indirection
580    /// (<https://github.com/rust-lang/rust/issues/156468>) so we keep them in a separate table.
581    pub(crate) extern_statics_imports: FxHashMap<Symbol, StrictPointer>,
582    /// A pointer to the allocation we provide for non-existent weak symbols.
583    pub(crate) extern_static_weak_import_default: Option<StrictPointer>,
584
585    /// The random number generator used for resolving non-determinism.
586    /// Needs to be queried by ptr_to_int, hence needs interior mutability.
587    pub(crate) rng: RefCell<StdRng>,
588
589    /// The allocator used for the machine's `AllocBytes` in native-libs mode.
590    pub(crate) allocator: Option<Rc<RefCell<crate::alloc::isolated_alloc::IsolatedAlloc>>>,
591
592    /// The allocation IDs to report when they are being allocated
593    /// (helps for debugging memory leaks and use after free bugs).
594    pub(crate) tracked_alloc_ids: FxHashSet<AllocId>,
595    /// For the tracked alloc ids, also report read/write accesses.
596    track_alloc_accesses: bool,
597
598    /// Controls whether alignment of memory accesses is being checked.
599    pub(crate) check_alignment: AlignmentCheck,
600
601    /// Failure rate of compare_exchange_weak, between 0.0 and 1.0
602    pub(crate) cmpxchg_weak_failure_rate: f64,
603
604    /// The probability of the active thread being preempted at the end of each basic block.
605    pub(crate) preemption_rate: f64,
606
607    /// If `Some`, we will report the current stack every N basic blocks.
608    pub(crate) report_progress: Option<u32>,
609    // The total number of blocks that have been executed.
610    pub(crate) basic_block_count: u64,
611
612    /// Handle of the optional shared object file for native functions.
613    #[cfg(all(feature = "native-lib", unix))]
614    pub native_lib: Vec<(libloading::Library, std::path::PathBuf)>,
615    #[cfg(not(all(feature = "native-lib", unix)))]
616    pub native_lib: Vec<!>,
617    /// A memory location for exchanging the current `ecx` pointer with native code.
618    #[cfg(all(feature = "native-lib", unix))]
619    pub native_lib_ecx_interchange: &'static Cell<usize>,
620
621    /// Run a garbage collector for BorTags every N basic blocks.
622    pub(crate) gc_interval: u32,
623    /// The number of blocks that passed since the last BorTag GC pass.
624    pub(crate) since_gc: u32,
625
626    /// The number of CPUs to be reported by miri.
627    pub(crate) num_cpus: u32,
628
629    /// Determines Miri's page size and associated values
630    pub(crate) page_size: u64,
631    pub(crate) stack_addr: u64,
632    pub(crate) stack_size: u64,
633
634    /// Whether to collect a backtrace when each allocation is created, just in case it leaks.
635    pub(crate) collect_leak_backtraces: bool,
636
637    /// The spans we will use to report where an allocation was created and deallocated in
638    /// diagnostics.
639    pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>)>>,
640
641    /// For each allocation, an offset inside that allocation that was deemed aligned even for
642    /// symbolic alignment checks. This cannot be stored in `AllocExtra` since it needs to be
643    /// tracked for vtables and function allocations as well as regular allocations.
644    ///
645    /// Invariant: the promised alignment will never be less than the native alignment of the
646    /// allocation.
647    pub(crate) symbolic_alignment: RefCell<FxHashMap<AllocId, (Size, Align)>>,
648
649    /// A cache of "data range" computations for unions (i.e., the offsets of non-padding bytes).
650    union_data_ranges: FxHashMap<Ty<'tcx>, RangeSet>,
651
652    /// Caches the sanity-checks for various pthread primitives.
653    pub(crate) pthread_mutex_sanity: Cell<bool>,
654    pub(crate) pthread_rwlock_sanity: Cell<bool>,
655    pub(crate) pthread_condvar_sanity: Cell<bool>,
656
657    /// (Foreign) symbols that are synthesized as part of the allocator shim: the key indicates the
658    /// name of the symbol being synthesized; the value indicates whether this should invoke some
659    /// other symbol or whether this has special allocator semantics.
660    pub(crate) allocator_shim_symbols: FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>>,
661    /// Cache for `mangle_internal_symbol`.
662    pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>,
663
664    /// Whether floating-point operations can behave non-deterministically.
665    pub float_nondet: bool,
666    /// Whether floating-point operations can have a non-deterministic rounding error.
667    pub float_rounding_error: FloatRoundingErrorMode,
668
669    /// Whether Miri artificially introduces short reads/writes on file descriptors.
670    pub short_fd_operations: bool,
671}
672
673impl<'tcx> MiriMachine<'tcx> {
674    /// Create a new MiriMachine.
675    ///
676    /// Invariant: `genmc_ctx.is_some() == config.genmc_config.is_some()`
677    pub(crate) fn new(
678        config: &MiriConfig,
679        layout_cx: LayoutCx<'tcx>,
680        genmc_ctx: Option<Rc<GenmcCtx>>,
681    ) -> Self {
682        let tcx = layout_cx.tcx();
683        let user_relevant_crates = Self::get_user_relevant_crates(tcx, config);
684        let layouts =
685            PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
686        let profiler = config.measureme_out.as_ref().map(|out| {
687            let crate_name =
688                tcx.sess.opts.crate_name.clone().unwrap_or_else(|| "unknown-crate".to_string());
689            let pid = process::id();
690            // We adopt the same naming scheme for the profiler output that rustc uses. In rustc,
691            // the PID is padded so that the nondeterministic value of the PID does not spread
692            // nondeterminism to the allocator. In Miri we are not aiming for such performance
693            // control, we just pad for consistency with rustc.
694            let filename = format!("{crate_name}-{pid:07}");
695            let path = Path::new(out).join(filename);
696            measureme::Profiler::new(path).expect("Couldn't create `measureme` profiler")
697        });
698        let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
699        let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config));
700        let data_race = if config.genmc_config.is_some() {
701            // `genmc_ctx` persists across executions, so we don't create a new one here.
702            GlobalDataRaceHandler::Genmc(genmc_ctx.unwrap())
703        } else if config.data_race_detector {
704            GlobalDataRaceHandler::Vclocks(Box::new(data_race::GlobalState::new(config)))
705        } else {
706            GlobalDataRaceHandler::None
707        };
708        // Determine page size, stack address, and stack size.
709        // These values are mostly meaningless, but the stack address is also where we start
710        // allocating physical integer addresses for all allocations.
711        let page_size = if let Some(page_size) = config.page_size {
712            page_size
713        } else {
714            let target = &tcx.sess.target;
715            match target.arch {
716                Arch::Wasm32 | Arch::Wasm64 => 64 * 1024, // https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances
717                Arch::AArch64 if target.is_like_darwin => {
718                    // No "definitive" source, but see:
719                    // https://www.wwdcnotes.com/notes/wwdc20/10214/
720                    // https://github.com/ziglang/zig/issues/11308 etc.
721                    16 * 1024
722                }
723                _ => 4 * 1024,
724            }
725        };
726        // On 16bit targets, 32 pages is more than the entire address space!
727        let stack_addr = if tcx.pointer_size().bits() < 32 { page_size } else { page_size * 32 };
728        let stack_size =
729            if tcx.pointer_size().bits() < 32 { page_size * 4 } else { page_size * 16 };
730        assert!(
731            usize::try_from(config.num_cpus).unwrap() <= cpu_affinity::MAX_CPUS,
732            "miri only supports up to {} CPUs, but {} were configured",
733            cpu_affinity::MAX_CPUS,
734            config.num_cpus
735        );
736        let threads = ThreadManager::new(config);
737        let thread_cpu_affinity =
738            if matches!(&tcx.sess.target.os, Os::Linux | Os::FreeBsd | Os::Android)
739                && !is_no_core(tcx)
740            {
741                let mut affinity = FxHashMap::default();
742                affinity.insert(
743                    threads.active_thread(),
744                    CpuAffinityMask::new(&layout_cx, config.num_cpus),
745                );
746                Some(affinity)
747            } else {
748                None
749            };
750        let blocking_io = BlockingIoManager::new(config.isolated_op == IsolatedOp::Allow)
751            .expect("Couldn't create poll instance");
752        let alloc_addresses =
753            RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr, tcx));
754
755        MiriMachine {
756            tcx,
757            borrow_tracker,
758            data_race,
759            alloc_addresses,
760            // `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
761            env_vars: EnvVars::default(),
762            main_fn_ret_place: None,
763            argc: None,
764            argv: None,
765            cmd_line: None,
766            tls: TlsData::default(),
767            isolated_op: config.isolated_op,
768            validation: config.validation,
769            fds: shims::FdTable::init(config.mute_stdout_stderr),
770            delayed_readiness_updates: Rc::new(DelayedReadinessUpdates::default()),
771            dirs: Default::default(),
772            layouts,
773            threads,
774            thread_cpu_affinity,
775            blocking_io,
776            static_roots: Vec::new(),
777            profiler,
778            string_cache: Default::default(),
779            exported_symbols_cache: FxHashMap::default(),
780            backtrace_style: config.backtrace_style,
781            user_relevant_crates,
782            extern_statics: FxHashMap::default(),
783            extern_statics_imports: FxHashMap::default(),
784            extern_static_weak_import_default: None,
785            rng: RefCell::new(rng),
786            allocator: (!config.native_lib.is_empty())
787                .then(|| Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new()))),
788            tracked_alloc_ids: config.tracked_alloc_ids.clone(),
789            track_alloc_accesses: config.track_alloc_accesses,
790            check_alignment: config.check_alignment,
791            cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
792            preemption_rate: config.preemption_rate,
793            report_progress: config.report_progress,
794            basic_block_count: 0,
795            monotonic_clock: MonotonicClock::new(config.isolated_op == IsolatedOp::Allow),
796            #[cfg(all(feature = "native-lib", unix))]
797            native_lib: config.native_lib.iter().map(|lib_file_path| {
798                let host_triple = rustc_session::config::host_tuple();
799                let target_triple = tcx.sess.opts.target_triple.tuple();
800                // Check if host target == the session target.
801                if host_triple != target_triple {
802                    panic!(
803                        "calling native C functions in linked .so file requires host and target to be the same: \
804                        host={host_triple}, target={target_triple}",
805                    );
806                }
807                // Note: it is the user's responsibility to provide a correct SO file.
808                // WATCH OUT: If an invalid/incorrect SO file is specified, this can cause
809                // undefined behaviour in Miri itself!
810                (
811                    unsafe {
812                        libloading::Library::new(lib_file_path)
813                            .expect("failed to read specified extern shared object file")
814                    },
815                    lib_file_path.clone(),
816                )
817            }).collect(),
818            #[cfg(all(feature = "native-lib", unix))]
819            native_lib_ecx_interchange: Box::leak(Box::new(Cell::new(0))),
820            #[cfg(not(all(feature = "native-lib", unix)))]
821            native_lib: config.native_lib.iter().map(|_| {
822                panic!("calling functions from native libraries via FFI is not supported in this build of Miri")
823            }).collect(),
824            gc_interval: config.gc_interval,
825            since_gc: 0,
826            num_cpus: config.num_cpus,
827            page_size,
828            stack_addr,
829            stack_size,
830            collect_leak_backtraces: config.collect_leak_backtraces,
831            allocation_spans: RefCell::new(FxHashMap::default()),
832            symbolic_alignment: RefCell::new(FxHashMap::default()),
833            union_data_ranges: FxHashMap::default(),
834            pthread_mutex_sanity: Cell::new(false),
835            pthread_rwlock_sanity: Cell::new(false),
836            pthread_condvar_sanity: Cell::new(false),
837            allocator_shim_symbols: Self::allocator_shim_symbols(tcx),
838            mangle_internal_symbol_cache: Default::default(),
839            float_nondet: config.float_nondet,
840            float_rounding_error: config.float_rounding_error,
841            short_fd_operations: config.short_fd_operations,
842        }
843    }
844
845    fn allocator_shim_symbols(
846        tcx: TyCtxt<'tcx>,
847    ) -> FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>> {
848        use rustc_codegen_ssa::base::allocator_shim_contents;
849
850        // codegen uses `allocator_kind_for_codegen` here, but that's only needed to deal with
851        // dylibs which we do not support.
852        let Some(kind) = tcx.allocator_kind(()) else {
853            return Default::default();
854        };
855        let methods = allocator_shim_contents(tcx, kind);
856        let mut symbols = FxHashMap::default();
857        for method in methods {
858            let from_name = Symbol::intern(&mangle_internal_symbol(
859                tcx,
860                &allocator::global_fn_name(method.name),
861            ));
862            let to = match method.special {
863                Some(special) => Either::Right(special),
864                None =>
865                    Either::Left(Symbol::intern(&mangle_internal_symbol(
866                        tcx,
867                        &allocator::default_fn_name(method.name),
868                    ))),
869            };
870            symbols.try_insert(from_name, to).unwrap();
871        }
872        symbols
873    }
874
875    /// Retrieve the list of user-relevant crates based on MIRI_LOCAL_CRATES as set by cargo-miri,
876    /// and extra crates set in the config.
877    fn get_user_relevant_crates(tcx: TyCtxt<'_>, config: &MiriConfig) -> Vec<CrateNum> {
878        // Convert the local crate names from the passed-in config into CrateNums so that they can
879        // be looked up quickly during execution
880        let local_crate_names = std::env::var("MIRI_LOCAL_CRATES")
881            .map(|crates| crates.split(',').map(|krate| krate.to_string()).collect::<Vec<_>>())
882            .unwrap_or_default();
883        let mut local_crates = Vec::new();
884        for &crate_num in tcx.crates(()) {
885            let name = tcx.crate_name(crate_num);
886            let name = name.as_str();
887            if local_crate_names
888                .iter()
889                .chain(&config.user_relevant_crates)
890                .any(|local_name| local_name == name)
891            {
892                local_crates.push(crate_num);
893            }
894        }
895        local_crates
896    }
897
898    pub(crate) fn late_init(
899        ecx: &mut MiriInterpCx<'tcx>,
900        config: &MiriConfig,
901        on_main_stack_empty: StackEmptyCallback<'tcx>,
902    ) -> InterpResult<'tcx> {
903        EnvVars::init(ecx, config)?;
904        MiriMachine::init_extern_statics(ecx)?;
905        ThreadManager::init(ecx, on_main_stack_empty);
906        interp_ok(())
907    }
908
909    pub(crate) fn communicate(&self) -> bool {
910        self.isolated_op == IsolatedOp::Allow
911    }
912
913    /// Check whether the stack frame that this `FrameInfo` refers to is part of a local crate.
914    pub(crate) fn is_local(&self, instance: ty::Instance<'tcx>) -> bool {
915        let def_id = instance.def_id();
916        def_id.is_local() || self.user_relevant_crates.contains(&def_id.krate)
917    }
918
919    /// Called when the interpreter is going to shut down abnormally, such as due to a Ctrl-C.
920    pub(crate) fn handle_abnormal_termination(&mut self) {
921        // All strings in the profile data are stored in a single string table which is not
922        // written to disk until the profiler is dropped. If the interpreter exits without dropping
923        // the profiler, it is not possible to interpret the profile data and all measureme tools
924        // will panic when given the file.
925        drop(self.profiler.take());
926    }
927
928    pub(crate) fn page_align(&self) -> Align {
929        Align::from_bytes(self.page_size).unwrap()
930    }
931
932    pub(crate) fn allocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
933        self.allocation_spans
934            .borrow()
935            .get(&alloc_id)
936            .map(|(allocated, _deallocated)| allocated.data())
937    }
938
939    pub(crate) fn deallocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
940        self.allocation_spans
941            .borrow()
942            .get(&alloc_id)
943            .and_then(|(_allocated, deallocated)| *deallocated)
944            .map(Span::data)
945    }
946
947    fn init_allocation(
948        ecx: &MiriInterpCx<'tcx>,
949        id: AllocId,
950        kind: MemoryKind,
951        size: Size,
952        align: Align,
953    ) -> InterpResult<'tcx, AllocExtra<'tcx>> {
954        if ecx.machine.tracked_alloc_ids.contains(&id) {
955            ecx.emit_diagnostic(NonHaltingDiagnostic::TrackingAlloc(id, size, align));
956        }
957
958        let borrow_tracker = ecx
959            .machine
960            .borrow_tracker
961            .as_ref()
962            .map(|bt| bt.borrow_mut().new_allocation(id, size, kind, &ecx.machine));
963
964        let data_race = match &ecx.machine.data_race {
965            GlobalDataRaceHandler::None => AllocDataRaceHandler::None,
966            GlobalDataRaceHandler::Vclocks(data_race) =>
967                AllocDataRaceHandler::Vclocks(
968                    data_race::AllocState::new_allocation(
969                        data_race,
970                        &ecx.machine.threads,
971                        size,
972                        kind,
973                        ecx.machine.current_user_relevant_span(),
974                    ),
975                    data_race.weak_memory.then(weak_memory::AllocState::new_allocation),
976                ),
977            GlobalDataRaceHandler::Genmc(_genmc_ctx) => {
978                // GenMC learns about new allocations directly from the alloc_addresses module,
979                // since it has to be able to control the address at which they are placed.
980                AllocDataRaceHandler::Genmc
981            }
982        };
983
984        // If an allocation is leaked, we want to report a backtrace to indicate where it was
985        // allocated. We don't need to record a backtrace for allocations which are allowed to
986        // leak.
987        let backtrace = if kind.may_leak() || !ecx.machine.collect_leak_backtraces {
988            None
989        } else {
990            Some(ecx.generate_stacktrace())
991        };
992
993        if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) {
994            ecx.machine
995                .allocation_spans
996                .borrow_mut()
997                .insert(id, (ecx.machine.current_user_relevant_span(), None));
998        }
999
1000        interp_ok(AllocExtra {
1001            borrow_tracker,
1002            data_race,
1003            backtrace,
1004            sync_objs: BTreeMap::default(),
1005        })
1006    }
1007}
1008
1009impl VisitProvenance for MiriMachine<'_> {
1010    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
1011        #[rustfmt::skip]
1012        let MiriMachine {
1013            threads,
1014            thread_cpu_affinity: _,
1015            tls,
1016            env_vars,
1017            main_fn_ret_place,
1018            argc,
1019            argv,
1020            cmd_line,
1021            extern_statics,
1022            extern_statics_imports,
1023            extern_static_weak_import_default,
1024            dirs,
1025            borrow_tracker,
1026            data_race,
1027            alloc_addresses,
1028            fds,
1029            blocking_io:_,
1030            delayed_readiness_updates: _,
1031            tcx: _,
1032            isolated_op: _,
1033            validation: _,
1034            monotonic_clock: _,
1035            layouts: _,
1036            static_roots: _,
1037            profiler: _,
1038            string_cache: _,
1039            exported_symbols_cache: _,
1040            backtrace_style: _,
1041            user_relevant_crates: _,
1042            rng: _,
1043            allocator: _,
1044            tracked_alloc_ids: _,
1045            track_alloc_accesses: _,
1046            check_alignment: _,
1047            cmpxchg_weak_failure_rate: _,
1048            preemption_rate: _,
1049            report_progress: _,
1050            basic_block_count: _,
1051            native_lib: _,
1052            #[cfg(all(feature = "native-lib", unix))]
1053            native_lib_ecx_interchange: _,
1054            gc_interval: _,
1055            since_gc: _,
1056            num_cpus: _,
1057            page_size: _,
1058            stack_addr: _,
1059            stack_size: _,
1060            collect_leak_backtraces: _,
1061            allocation_spans: _,
1062            symbolic_alignment: _,
1063            union_data_ranges: _,
1064            pthread_mutex_sanity: _,
1065            pthread_rwlock_sanity: _,
1066            pthread_condvar_sanity: _,
1067            allocator_shim_symbols: _,
1068            mangle_internal_symbol_cache: _,
1069            float_nondet: _,
1070            float_rounding_error: _,
1071            short_fd_operations: _,
1072        } = self;
1073
1074        threads.visit_provenance(visit);
1075        tls.visit_provenance(visit);
1076        env_vars.visit_provenance(visit);
1077        dirs.visit_provenance(visit);
1078        fds.visit_provenance(visit);
1079        data_race.visit_provenance(visit);
1080        borrow_tracker.visit_provenance(visit);
1081        alloc_addresses.visit_provenance(visit);
1082        main_fn_ret_place.visit_provenance(visit);
1083        argc.visit_provenance(visit);
1084        argv.visit_provenance(visit);
1085        cmd_line.visit_provenance(visit);
1086        extern_static_weak_import_default.visit_provenance(visit);
1087        extern_statics.visit_provenance(visit);
1088        extern_statics_imports.visit_provenance(visit);
1089    }
1090}
1091
1092/// A rustc InterpCx for Miri.
1093pub type MiriInterpCx<'tcx> = InterpCx<'tcx, MiriMachine<'tcx>>;
1094
1095/// A little trait that's useful to be inherited by extension traits.
1096pub trait MiriInterpCxExt<'tcx> {
1097    fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'tcx>;
1098    fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'tcx>;
1099}
1100impl<'tcx> MiriInterpCxExt<'tcx> for MiriInterpCx<'tcx> {
1101    #[inline(always)]
1102    fn eval_context_ref(&self) -> &MiriInterpCx<'tcx> {
1103        self
1104    }
1105    #[inline(always)]
1106    fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'tcx> {
1107        self
1108    }
1109}
1110
1111/// Machine hook implementations.
1112impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
1113    type MemoryKind = MiriMemoryKind;
1114    type ExtraFnVal = DynSym;
1115
1116    type FrameExtra = FrameExtra<'tcx>;
1117    type AllocExtra = AllocExtra<'tcx>;
1118
1119    type Provenance = Provenance;
1120    type ProvenanceExtra = ProvenanceExtra;
1121    type Bytes = MiriAllocBytes;
1122
1123    type MemoryMap =
1124        MonoHashMap<AllocId, (MemoryKind, Allocation<Provenance, Self::AllocExtra, Self::Bytes>)>;
1125
1126    const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
1127
1128    const PANIC_ON_ALLOC_FAIL: bool = false;
1129
1130    #[inline(always)]
1131    fn enforce_alignment(ecx: &MiriInterpCx<'tcx>) -> bool {
1132        ecx.machine.check_alignment != AlignmentCheck::None
1133    }
1134
1135    #[inline(always)]
1136    fn alignment_check(
1137        ecx: &MiriInterpCx<'tcx>,
1138        alloc_id: AllocId,
1139        alloc_align: Align,
1140        alloc_kind: AllocKind,
1141        offset: Size,
1142        align: Align,
1143    ) -> Option<Misalignment> {
1144        if ecx.machine.check_alignment != AlignmentCheck::Symbolic {
1145            // Just use the built-in check.
1146            return None;
1147        }
1148        if alloc_kind != AllocKind::LiveData {
1149            // Can't have any extra info here.
1150            return None;
1151        }
1152        // Let's see which alignment we have been promised for this allocation.
1153        let (promised_offset, promised_align) = ecx
1154            .machine
1155            .symbolic_alignment
1156            .borrow()
1157            .get(&alloc_id)
1158            .copied()
1159            .unwrap_or((Size::ZERO, alloc_align));
1160        if promised_align < align {
1161            // Definitely not enough.
1162            Some(Misalignment { has: promised_align, required: align })
1163        } else {
1164            // What's the offset between us and the promised alignment?
1165            let distance = offset.bytes().wrapping_sub(promised_offset.bytes());
1166            // That must also be aligned.
1167            if distance.is_multiple_of(align.bytes()) {
1168                // All looking good!
1169                None
1170            } else {
1171                // The biggest power of two through which `distance` is divisible.
1172                let distance_pow2 = 1 << distance.trailing_zeros();
1173                Some(Misalignment {
1174                    has: Align::from_bytes(distance_pow2).unwrap(),
1175                    required: align,
1176                })
1177            }
1178        }
1179    }
1180
1181    #[inline(always)]
1182    fn enforce_validity(ecx: &MiriInterpCx<'tcx>, _layout: TyAndLayout<'tcx>) -> bool {
1183        ecx.machine.validation != ValidationMode::No
1184    }
1185    #[inline(always)]
1186    fn enforce_validity_recursively(
1187        ecx: &InterpCx<'tcx, Self>,
1188        _layout: TyAndLayout<'tcx>,
1189    ) -> bool {
1190        ecx.machine.validation == ValidationMode::Deep
1191    }
1192
1193    #[inline(always)]
1194    fn ignore_optional_overflow_checks(ecx: &MiriInterpCx<'tcx>) -> bool {
1195        !ecx.tcx.sess.overflow_checks()
1196    }
1197
1198    fn check_fn_target_features(
1199        ecx: &MiriInterpCx<'tcx>,
1200        instance: ty::Instance<'tcx>,
1201    ) -> InterpResult<'tcx> {
1202        let attrs = ecx.tcx.codegen_instance_attrs(instance.def);
1203        if attrs
1204            .target_features
1205            .iter()
1206            .any(|feature| !ecx.tcx.sess.target_features.contains(&feature.name))
1207        {
1208            let unavailable = attrs
1209                .target_features
1210                .iter()
1211                .filter(|&feature| {
1212                    feature.kind != TargetFeatureKind::Implied
1213                        && !ecx.tcx.sess.target_features.contains(&feature.name)
1214                })
1215                .fold(String::new(), |mut s, feature| {
1216                    if !s.is_empty() {
1217                        s.push_str(", ");
1218                    }
1219                    s.push_str(feature.name.as_str());
1220                    s
1221                });
1222            let msg = format!(
1223                "calling a function that requires unavailable target features: {unavailable}"
1224            );
1225            // On WASM, this is not UB, but instead gets rejected during validation of the module
1226            // (see #84988).
1227            if ecx.tcx.sess.target.is_like_wasm {
1228                throw_machine_stop!(TerminationInfo::Abort(msg));
1229            } else {
1230                throw_ub_format!("{msg}");
1231            }
1232        }
1233        interp_ok(())
1234    }
1235
1236    #[inline(always)]
1237    fn find_mir_or_eval_fn(
1238        ecx: &mut MiriInterpCx<'tcx>,
1239        instance: ty::Instance<'tcx>,
1240        abi: &FnAbi<'tcx, Ty<'tcx>>,
1241        args: &[FnArg<'tcx>],
1242        dest: &PlaceTy<'tcx>,
1243        ret: Option<mir::BasicBlock>,
1244        unwind: mir::UnwindAction,
1245    ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
1246        // For foreign items, try to see if we can emulate them.
1247        if ecx.tcx.is_foreign_item(instance.def_id()) {
1248            let _trace = enter_trace_span!("emulate_foreign_item");
1249            // An external function call that does not have a MIR body. We either find MIR elsewhere
1250            // or emulate its effect.
1251            // This will be Ok(None) if we're emulating the intrinsic entirely within Miri (no need
1252            // to run extra MIR), and Ok(Some(body)) if we found MIR to run for the
1253            // foreign function
1254            // Any needed call to `goto_block` will be performed by `emulate_foreign_item`.
1255            let args = MiriInterpCx::copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit?
1256            let link_name = Symbol::intern(ecx.tcx.symbol_name(instance).name);
1257            return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind);
1258        }
1259
1260        if ecx.machine.data_race.as_genmc_ref().is_some()
1261            && ecx.genmc_intercept_function(instance, args, dest)?
1262        {
1263            ecx.return_to_block(ret)?;
1264            return interp_ok(None);
1265        }
1266
1267        // Otherwise, load the MIR.
1268        let _trace = enter_trace_span!("load_mir");
1269        interp_ok(Some((ecx.load_mir(instance.def, None)?, instance)))
1270    }
1271
1272    #[inline(always)]
1273    fn call_extra_fn(
1274        ecx: &mut MiriInterpCx<'tcx>,
1275        fn_val: DynSym,
1276        abi: &FnAbi<'tcx, Ty<'tcx>>,
1277        args: &[FnArg<'tcx>],
1278        dest: &PlaceTy<'tcx>,
1279        ret: Option<mir::BasicBlock>,
1280        unwind: mir::UnwindAction,
1281    ) -> InterpResult<'tcx> {
1282        let args = MiriInterpCx::copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit?
1283        ecx.emulate_dyn_sym(fn_val, abi, &args, dest, ret, unwind)
1284    }
1285
1286    #[inline(always)]
1287    fn call_intrinsic(
1288        ecx: &mut MiriInterpCx<'tcx>,
1289        instance: ty::Instance<'tcx>,
1290        args: &[OpTy<'tcx>],
1291        dest: &PlaceTy<'tcx>,
1292        ret: Option<mir::BasicBlock>,
1293        unwind: mir::UnwindAction,
1294    ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
1295        ecx.call_intrinsic(instance, args, dest, ret, unwind)
1296    }
1297
1298    #[inline(always)]
1299    fn call_llvm_intrinsic(
1300        ecx: &mut MiriInterpCx<'tcx>,
1301        instance: ty::Instance<'tcx>,
1302        args: &[OpTy<'tcx>],
1303        dest: &PlaceTy<'tcx>,
1304        ret: Option<mir::BasicBlock>,
1305    ) -> InterpResult<'tcx, ()> {
1306        ecx.call_llvm_intrinsic(instance, args, dest, ret)
1307    }
1308
1309    #[inline(always)]
1310    fn assert_panic(
1311        ecx: &mut MiriInterpCx<'tcx>,
1312        msg: &mir::AssertMessage<'tcx>,
1313        unwind: mir::UnwindAction,
1314    ) -> InterpResult<'tcx> {
1315        ecx.assert_panic(msg, unwind)
1316    }
1317
1318    fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> {
1319        ecx.start_panic_nounwind(msg)
1320    }
1321
1322    fn unwind_terminate(
1323        ecx: &mut InterpCx<'tcx, Self>,
1324        reason: mir::UnwindTerminateReason,
1325    ) -> InterpResult<'tcx> {
1326        // Call the lang item.
1327        let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap();
1328        let panic = ty::Instance::mono(ecx.tcx.tcx, panic);
1329        ecx.call_function(
1330            panic,
1331            ExternAbi::Rust,
1332            &[],
1333            None,
1334            ReturnContinuation::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
1335        )?;
1336        interp_ok(())
1337    }
1338
1339    #[inline(always)]
1340    fn binary_ptr_op(
1341        ecx: &MiriInterpCx<'tcx>,
1342        bin_op: mir::BinOp,
1343        left: &ImmTy<'tcx>,
1344        right: &ImmTy<'tcx>,
1345    ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1346        ecx.binary_ptr_op(bin_op, left, right)
1347    }
1348
1349    #[inline(always)]
1350    fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(
1351        ecx: &InterpCx<'tcx, Self>,
1352        inputs: &[F1],
1353    ) -> F2 {
1354        ecx.generate_nan(inputs)
1355    }
1356
1357    #[inline(always)]
1358    fn apply_float_nondet(
1359        ecx: &mut InterpCx<'tcx, Self>,
1360        val: ImmTy<'tcx>,
1361    ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1362        crate::math::apply_random_float_error_to_imm(ecx, val, 4)
1363    }
1364
1365    #[inline(always)]
1366    fn equal_float_min_max<F: Float>(ecx: &MiriInterpCx<'tcx>, a: F, b: F) -> F {
1367        ecx.equal_float_min_max(a, b)
1368    }
1369
1370    #[inline(always)]
1371    fn float_fuse_mul_add(ecx: &InterpCx<'tcx, Self>) -> bool {
1372        ecx.machine.float_nondet && ecx.machine.rng.borrow_mut().random()
1373    }
1374
1375    #[inline(always)]
1376    fn runtime_checks(
1377        ecx: &InterpCx<'tcx, Self>,
1378        r: mir::RuntimeChecks,
1379    ) -> InterpResult<'tcx, bool> {
1380        interp_ok(r.value(ecx.tcx.sess))
1381    }
1382
1383    #[inline(always)]
1384    fn thread_local_static_pointer(
1385        ecx: &mut MiriInterpCx<'tcx>,
1386        def_id: DefId,
1387    ) -> InterpResult<'tcx, StrictPointer> {
1388        ecx.get_or_create_thread_local_alloc(def_id)
1389    }
1390
1391    fn extern_static_pointer(
1392        ecx: &MiriInterpCx<'tcx>,
1393        def_id: DefId,
1394    ) -> InterpResult<'tcx, StrictPointer> {
1395        let link_name = Symbol::intern(ecx.tcx.symbol_name(Instance::mono(*ecx.tcx, def_id)).name);
1396        let def_ty = ecx.tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
1397        let extern_decl_layout =
1398            ecx.tcx.layout_of(ecx.typing_env().as_query_input(def_ty)).unwrap();
1399
1400        // Look up the `ptr` in the right map, depending on whether this is an "import"
1401        // static or a real one.
1402        let ptr = match ecx.tcx.codegen_fn_attrs(def_id).import_linkage {
1403            None => ecx.machine.extern_statics.get(&link_name),
1404            Some(_) => ecx.machine.extern_statics_imports.get(&link_name),
1405        };
1406        if let Some(&ptr) = ptr {
1407            // Various parts of the engine rely on `get_alloc_info` for size and alignment
1408            // information. That uses the type information of this static.
1409            // Make sure it matches the Miri allocation for this.
1410            let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
1411                panic!("extern_statics cannot contain wildcards")
1412            };
1413            let info = ecx.get_alloc_info(alloc_id);
1414            if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align {
1415                throw_unsup_format!(
1416                    "extern static `{link_name}` has been declared as `{krate}::{name}` \
1417                    with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
1418                    but Miri emulates it via an extern static shim \
1419                    with a size of {shim_size} bytes and alignment of {shim_align} bytes",
1420                    name = ecx.tcx.def_path_str(def_id),
1421                    krate = ecx.tcx.crate_name(def_id.krate),
1422                    decl_size = extern_decl_layout.size.bytes(),
1423                    decl_align = extern_decl_layout.align.bytes(),
1424                    shim_size = info.size.bytes(),
1425                    shim_align = info.align.bytes(),
1426                )
1427            }
1428            interp_ok(ptr)
1429        } else if ecx.tcx.codegen_fn_attrs(def_id).import_linkage == Some(Linkage::ExternalWeak) {
1430            // Symbols with weak linkage default to null if they are not defined. However we can't
1431            // create new allocations here. On the plus side we know rustc rejects non-ptr-sized
1432            // weak statics so we can just use a single global "null" allocation for all of them.
1433            // The memory we are assigning this address to is anyway somewhat "fake", it's an
1434            // indirection introduced by how Rust represents external symbols with linkage (see
1435            // <https://github.com/rust-lang/rust/issues/156468>). So we can just specify that such
1436            // memory does not have unique addresses, despite being technically a `static`.
1437            assert_eq!(
1438                extern_decl_layout.size,
1439                ecx.tcx.data_layout.pointer_size(),
1440                "non-pointer-sized weak static"
1441            );
1442            interp_ok(
1443                ecx.machine
1444                    .extern_static_weak_import_default
1445                    .expect("`missing_weak_symbol` should have been initialized"),
1446            )
1447        } else {
1448            throw_unsup_format!("extern static `{link_name}` is not supported by Miri")
1449        }
1450    }
1451
1452    fn init_local_allocation(
1453        ecx: &MiriInterpCx<'tcx>,
1454        id: AllocId,
1455        kind: MemoryKind,
1456        size: Size,
1457        align: Align,
1458    ) -> InterpResult<'tcx, Self::AllocExtra> {
1459        assert!(kind != MiriMemoryKind::Global.into());
1460        MiriMachine::init_allocation(ecx, id, kind, size, align)
1461    }
1462
1463    fn adjust_alloc_root_pointer(
1464        ecx: &MiriInterpCx<'tcx>,
1465        ptr: interpret::Pointer<CtfeProvenance>,
1466        kind: Option<MemoryKind>,
1467    ) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
1468        let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None");
1469        let alloc_id = ptr.provenance.alloc_id();
1470        if cfg!(debug_assertions) {
1471            // The machine promises to never call us on thread-local or extern statics.
1472            match ecx.tcx.try_get_global_alloc(alloc_id) {
1473                Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
1474                    panic!("adjust_alloc_root_pointer called on thread-local static")
1475                }
1476                Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => {
1477                    panic!("adjust_alloc_root_pointer called on extern static")
1478                }
1479                _ => {}
1480            }
1481        }
1482        // FIXME: can we somehow preserve the immutability of `ptr`?
1483        let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
1484            borrow_tracker.borrow_mut().root_ptr_tag(alloc_id, &ecx.machine)
1485        } else {
1486            // Value does not matter, SB is disabled
1487            BorTag::default()
1488        };
1489        ecx.adjust_alloc_root_pointer(ptr, tag, kind)
1490    }
1491
1492    /// Called on `usize as ptr` casts.
1493    #[inline(always)]
1494    fn ptr_from_addr_cast(ecx: &MiriInterpCx<'tcx>, addr: u64) -> InterpResult<'tcx, Pointer> {
1495        ecx.ptr_from_addr_cast(addr)
1496    }
1497
1498    /// Called on `ptr as usize` casts.
1499    /// (Actually computing the resulting `usize` doesn't need machine help,
1500    /// that's just `Scalar::try_to_int`.)
1501    #[inline(always)]
1502    fn expose_provenance(
1503        ecx: &InterpCx<'tcx, Self>,
1504        provenance: Self::Provenance,
1505    ) -> InterpResult<'tcx> {
1506        ecx.expose_provenance(provenance)
1507    }
1508
1509    /// Convert a pointer with provenance into an allocation-offset pair and extra provenance info.
1510    /// `size` says how many bytes of memory are expected at that pointer. The *sign* of `size` can
1511    /// be used to disambiguate situations where a wildcard pointer sits right in between two
1512    /// allocations.
1513    ///
1514    /// If `ptr.provenance.get_alloc_id()` is `Some(p)`, the returned `AllocId` must be `p`.
1515    /// The resulting `AllocId` will just be used for that one step and the forgotten again
1516    /// (i.e., we'll never turn the data returned here back into a `Pointer` that might be
1517    /// stored in machine state).
1518    ///
1519    /// When this fails, that means the pointer does not point to a live allocation.
1520    fn ptr_get_alloc(
1521        ecx: &MiriInterpCx<'tcx>,
1522        ptr: StrictPointer,
1523        size: i64,
1524    ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
1525        let rel = ecx.ptr_get_alloc(ptr, size);
1526
1527        rel.map(|(alloc_id, size)| {
1528            let tag = match ptr.provenance {
1529                Provenance::Concrete { tag, .. } => ProvenanceExtra::Concrete(tag),
1530                Provenance::Wildcard => ProvenanceExtra::Wildcard,
1531            };
1532            (alloc_id, size, tag)
1533        })
1534    }
1535
1536    /// Called to adjust global allocations to the Provenance and AllocExtra of this machine.
1537    ///
1538    /// If `alloc` contains pointers, then they are all pointing to globals.
1539    ///
1540    /// This should avoid copying if no work has to be done! If this returns an owned
1541    /// allocation (because a copy had to be done to adjust things), machine memory will
1542    /// cache the result. (This relies on `AllocMap::get_or` being able to add the
1543    /// owned allocation to the map even when the map is shared.)
1544    fn adjust_global_allocation<'b>(
1545        ecx: &InterpCx<'tcx, Self>,
1546        id: AllocId,
1547        alloc: &'b Allocation,
1548    ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
1549    {
1550        let alloc = alloc.adjust_from_tcx(
1551            &ecx.tcx,
1552            |bytes, align| ecx.get_global_alloc_bytes(id, bytes, align),
1553            |ptr| ecx.global_root_pointer(ptr),
1554        )?;
1555        let kind = MiriMemoryKind::Global.into();
1556        let extra = MiriMachine::init_allocation(ecx, id, kind, alloc.size(), alloc.align)?;
1557        interp_ok(Cow::Owned(alloc.with_extra(extra)))
1558    }
1559
1560    #[inline(always)]
1561    fn before_memory_read(
1562        _tcx: TyCtxtAt<'tcx>,
1563        machine: &Self,
1564        alloc_extra: &AllocExtra<'tcx>,
1565        ptr: Pointer,
1566        (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1567        range: AllocRange,
1568    ) -> InterpResult<'tcx> {
1569        if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1570            machine.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(
1571                alloc_id,
1572                range,
1573                borrow_tracker::AccessKind::Read,
1574            ));
1575        }
1576        // The order of checks is deliberate, to prefer reporting a data race over a borrow tracker error.
1577        match &machine.data_race {
1578            GlobalDataRaceHandler::None => {}
1579            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1580                genmc_ctx.memory_load(machine, ptr.addr(), range.size)?,
1581            GlobalDataRaceHandler::Vclocks(_data_race) => {
1582                let _trace = enter_trace_span!(data_race::before_memory_read);
1583                let AllocDataRaceHandler::Vclocks(data_race, _weak_memory) = &alloc_extra.data_race
1584                else {
1585                    unreachable!();
1586                };
1587                data_race.read_non_atomic(alloc_id, range, NaReadType::Read, None, machine)?;
1588            }
1589        }
1590        if let Some(borrow_tracker) = &alloc_extra.borrow_tracker {
1591            borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?;
1592        }
1593        // Check if there are any sync objects that would like to prevent reading this memory.
1594        for (_offset, obj) in alloc_extra.sync_objs.range(range.start..range.end()) {
1595            obj.on_access(concurrency::sync::AccessKind::Read)?;
1596        }
1597
1598        interp_ok(())
1599    }
1600
1601    #[inline(always)]
1602    fn before_memory_write(
1603        _tcx: TyCtxtAt<'tcx>,
1604        machine: &mut Self,
1605        alloc_extra: &mut AllocExtra<'tcx>,
1606        ptr: Pointer,
1607        (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1608        range: AllocRange,
1609    ) -> InterpResult<'tcx> {
1610        if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1611            machine.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(
1612                alloc_id,
1613                range,
1614                borrow_tracker::AccessKind::Write,
1615            ));
1616        }
1617        match &machine.data_race {
1618            GlobalDataRaceHandler::None => {}
1619            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1620                genmc_ctx.memory_store(machine, ptr.addr(), range.size)?,
1621            GlobalDataRaceHandler::Vclocks(_global_state) => {
1622                let _trace = enter_trace_span!(data_race::before_memory_write);
1623                let AllocDataRaceHandler::Vclocks(data_race, weak_memory) =
1624                    &mut alloc_extra.data_race
1625                else {
1626                    unreachable!()
1627                };
1628                data_race.write_non_atomic(alloc_id, range, NaWriteType::Write, None, machine)?;
1629                if let Some(weak_memory) = weak_memory {
1630                    weak_memory
1631                        .non_atomic_write(range, machine.data_race.as_vclocks_ref().unwrap());
1632                }
1633            }
1634        }
1635        if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1636            borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?;
1637        }
1638        // Delete sync objects that don't like writes.
1639        // Most of the time, we can just skip this.
1640        if !alloc_extra.sync_objs.is_empty() {
1641            let mut to_delete = vec![];
1642            for (offset, obj) in alloc_extra.sync_objs.range(range.start..range.end()) {
1643                obj.on_access(concurrency::sync::AccessKind::Write)?;
1644                if obj.delete_on_write() {
1645                    to_delete.push(*offset);
1646                }
1647            }
1648            for offset in to_delete {
1649                alloc_extra.sync_objs.remove(&offset);
1650            }
1651        }
1652        interp_ok(())
1653    }
1654
1655    #[inline(always)]
1656    fn before_memory_deallocation(
1657        _tcx: TyCtxtAt<'tcx>,
1658        machine: &mut Self,
1659        alloc_extra: &mut AllocExtra<'tcx>,
1660        ptr: Pointer,
1661        (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
1662        size: Size,
1663        align: Align,
1664        kind: MemoryKind,
1665    ) -> InterpResult<'tcx> {
1666        if machine.tracked_alloc_ids.contains(&alloc_id) {
1667            machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
1668        }
1669        match &machine.data_race {
1670            GlobalDataRaceHandler::None => {}
1671            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1672                genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), kind)?,
1673            GlobalDataRaceHandler::Vclocks(_global_state) => {
1674                let _trace = enter_trace_span!(data_race::before_memory_deallocation);
1675                let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap();
1676                data_race.write_non_atomic(
1677                    alloc_id,
1678                    alloc_range(Size::ZERO, size),
1679                    NaWriteType::Deallocate,
1680                    None,
1681                    machine,
1682                )?;
1683            }
1684        }
1685        if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1686            borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?;
1687        }
1688        // Check if there are any sync objects that would like to prevent freeing this memory.
1689        for obj in alloc_extra.sync_objs.values() {
1690            obj.on_access(concurrency::sync::AccessKind::Dealloc)?;
1691        }
1692
1693        if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
1694        {
1695            *deallocated_at = Some(machine.current_user_relevant_span());
1696        }
1697        machine.free_alloc_id(alloc_id, size, align, kind);
1698        interp_ok(())
1699    }
1700
1701    #[inline(always)]
1702    fn retag_ptr_value(
1703        ecx: &mut InterpCx<'tcx, Self>,
1704        val: &ImmTy<'tcx>,
1705        ty: Ty<'tcx>,
1706    ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
1707        if ecx.machine.borrow_tracker.is_some() {
1708            ecx.retag_ptr_value(val, ty)
1709        } else {
1710            interp_ok(None)
1711        }
1712    }
1713
1714    #[inline(always)]
1715    fn with_retag_mode<T>(
1716        ecx: &mut InterpCx<'tcx, Self>,
1717        mode: RetagMode,
1718        f: impl FnOnce(&mut InterpCx<'tcx, Self>) -> InterpResult<'tcx, T>,
1719    ) -> InterpResult<'tcx, T> {
1720        if ecx.machine.borrow_tracker.is_some() { ecx.with_retag_mode(mode, f) } else { f(ecx) }
1721    }
1722
1723    fn protect_in_place_function_argument(
1724        ecx: &mut InterpCx<'tcx, Self>,
1725        place: &MPlaceTy<'tcx>,
1726    ) -> InterpResult<'tcx> {
1727        // If we have a borrow tracker, we also have it set up protection so that all reads *and
1728        // writes* during this call are insta-UB.
1729        let protected_place = if ecx.machine.borrow_tracker.is_some() {
1730            ecx.protect_place(place)?
1731        } else {
1732            // No borrow tracker.
1733            place.clone()
1734        };
1735        // We do need to write `uninit` so that even after the call ends, the former contents of
1736        // this place cannot be observed any more. We do the write after retagging so that for
1737        // Tree Borrows, this is considered to activate the new tag.
1738        // Conveniently this also ensures that the place actually points to suitable memory.
1739        ecx.write_uninit(&protected_place)?;
1740        // Now we throw away the protected place, ensuring its tag is never used again.
1741        interp_ok(())
1742    }
1743
1744    #[inline(always)]
1745    fn init_frame(
1746        ecx: &mut InterpCx<'tcx, Self>,
1747        frame: Frame<'tcx, Provenance>,
1748    ) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
1749        // Start recording our event before doing anything else
1750        let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
1751            let fn_name = frame.instance().to_string();
1752            let entry = ecx.machine.string_cache.entry(fn_name.clone());
1753            let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
1754
1755            Some(profiler.start_recording_interval_event_detached(
1756                *name,
1757                measureme::EventId::from_label(*name),
1758                ecx.active_thread().to_u32(),
1759            ))
1760        } else {
1761            None
1762        };
1763
1764        let borrow_tracker = ecx.machine.borrow_tracker.as_ref();
1765
1766        let extra = FrameExtra {
1767            borrow_tracker: borrow_tracker.map(|bt| bt.borrow_mut().new_frame()),
1768            catch_unwind: None,
1769            timing,
1770            user_relevance: ecx.machine.user_relevance(&frame),
1771            data_race: ecx
1772                .machine
1773                .data_race
1774                .as_vclocks_ref()
1775                .map(|_| data_race::FrameState::default()),
1776        };
1777
1778        interp_ok(frame.with_extra(extra))
1779    }
1780
1781    fn stack<'a>(
1782        ecx: &'a InterpCx<'tcx, Self>,
1783    ) -> &'a [Frame<'tcx, Self::Provenance, Self::FrameExtra>] {
1784        ecx.active_thread_stack()
1785    }
1786
1787    fn stack_mut<'a>(
1788        ecx: &'a mut InterpCx<'tcx, Self>,
1789    ) -> &'a mut Vec<Frame<'tcx, Self::Provenance, Self::FrameExtra>> {
1790        ecx.active_thread_stack_mut()
1791    }
1792
1793    fn before_terminator(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1794        ecx.machine.basic_block_count += 1u64; // a u64 that is only incremented by 1 will "never" overflow
1795        ecx.machine.since_gc += 1;
1796        // Possibly report our progress. This will point at the terminator we are about to execute.
1797        if let Some(report_progress) = ecx.machine.report_progress {
1798            if ecx.machine.basic_block_count.is_multiple_of(u64::from(report_progress)) {
1799                ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport {
1800                    block_count: ecx.machine.basic_block_count,
1801                });
1802            }
1803        }
1804
1805        // Search for BorTags to find all live pointers, then remove all other tags from borrow
1806        // stacks. Also clean up dropped readiness watchers from the global readiness interest
1807        // table and closed source file descriptions in the blocking I/O manager.
1808        // When debug assertions are enabled, run the GC as often as possible so that any cases
1809        // where it mistakenly removes an important tag become visible.
1810        if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
1811            ecx.machine.since_gc = 0;
1812            ecx.run_provenance_gc();
1813            ecx.machine.blocking_io.run_gc();
1814        }
1815
1816        // These are our preemption points.
1817        // (This will only take effect after the terminator has been executed.)
1818        ecx.maybe_preempt_active_thread();
1819
1820        // Make sure some time passes.
1821        ecx.machine.monotonic_clock.tick();
1822
1823        interp_ok(())
1824    }
1825
1826    #[inline(always)]
1827    fn after_stack_push(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1828        if ecx.frame().extra.user_relevance >= ecx.active_thread_ref().current_user_relevance() {
1829            // We just pushed a frame that's at least as relevant as the so-far most relevant frame.
1830            // That means we are now the most relevant frame.
1831            let stack_len = ecx.active_thread_stack().len();
1832            ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
1833        }
1834        interp_ok(())
1835    }
1836
1837    fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1838        let frame = ecx.frame();
1839        // We want this *before* the return value copy, because the return place itself is protected
1840        // until we do `on_stack_pop` here, and we need to un-protect it to copy the return value.
1841        if ecx.machine.borrow_tracker.is_some() {
1842            ecx.on_stack_pop(frame)?;
1843        }
1844        if ecx
1845            .active_thread_ref()
1846            .top_user_relevant_frame()
1847            .expect("there should always be a most relevant frame for a non-empty stack")
1848            == ecx.frame_idx()
1849        {
1850            // We are popping the most relevant frame. We have no clue what the next relevant frame
1851            // below that is, so we recompute that.
1852            // (If this ever becomes a bottleneck, we could have `push` store the previous
1853            // user-relevant frame and restore that here.)
1854            // We have to skip the frame that is just being popped.
1855            ecx.active_thread_mut().recompute_top_user_relevant_frame(/* skip */ 1);
1856        }
1857        // tracing-tree can automatically annotate scope changes, but it gets very confused by our
1858        // concurrency and what it prints is just plain wrong. So we print our own information
1859        // instead. (Cc https://github.com/rust-lang/miri/issues/2266)
1860        info!("Leaving {}", ecx.frame().instance());
1861        interp_ok(())
1862    }
1863
1864    #[inline(always)]
1865    fn after_stack_pop(
1866        ecx: &mut InterpCx<'tcx, Self>,
1867        frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1868        unwinding: bool,
1869    ) -> InterpResult<'tcx, ReturnAction> {
1870        let res = {
1871            // Move `frame` into a sub-scope so we control when it will be dropped.
1872            let mut frame = frame;
1873            let timing = frame.extra.timing.take();
1874            let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
1875            if let Some(profiler) = ecx.machine.profiler.as_ref() {
1876                profiler.finish_recording_interval_event(timing.unwrap());
1877            }
1878            res
1879        };
1880        // Needs to be done after dropping frame to show up on the right nesting level.
1881        // (Cc https://github.com/rust-lang/miri/issues/2266)
1882        if !ecx.active_thread_stack().is_empty() {
1883            info!("Continuing in {}", ecx.frame().instance());
1884        }
1885        res
1886    }
1887
1888    fn after_local_read(
1889        ecx: &InterpCx<'tcx, Self>,
1890        frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1891        local: mir::Local,
1892    ) -> InterpResult<'tcx> {
1893        if let Some(data_race) = &frame.extra.data_race {
1894            let _trace = enter_trace_span!(data_race::after_local_read);
1895            data_race.local_read(local, &ecx.machine);
1896        }
1897        interp_ok(())
1898    }
1899
1900    fn after_local_write(
1901        ecx: &mut InterpCx<'tcx, Self>,
1902        local: mir::Local,
1903        storage_live: bool,
1904    ) -> InterpResult<'tcx> {
1905        if let Some(data_race) = &ecx.frame().extra.data_race {
1906            let _trace = enter_trace_span!(data_race::after_local_write);
1907            data_race.local_write(local, storage_live, &ecx.machine);
1908        }
1909        interp_ok(())
1910    }
1911
1912    fn after_local_moved_to_memory(
1913        ecx: &mut InterpCx<'tcx, Self>,
1914        local: mir::Local,
1915        mplace: &MPlaceTy<'tcx>,
1916    ) -> InterpResult<'tcx> {
1917        let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else {
1918            panic!("after_local_allocated should only be called on fresh allocations");
1919        };
1920        // Record the span where this was allocated: the declaration of the local.
1921        let local_decl = &ecx.frame().body().local_decls[local];
1922        let span = local_decl.source_info.span;
1923        ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
1924        // The data race system has to fix the clocks used for this write.
1925        let (alloc_info, machine) = ecx.get_alloc_extra_mut(alloc_id)?;
1926        if let Some(data_race) =
1927            &machine.threads.active_thread_stack().last().unwrap().extra.data_race
1928        {
1929            let _trace = enter_trace_span!(data_race::after_local_moved_to_memory);
1930            data_race.local_moved_to_memory(
1931                local,
1932                alloc_info.data_race.as_vclocks_mut().unwrap(),
1933                machine,
1934            );
1935        }
1936        interp_ok(())
1937    }
1938
1939    fn get_global_alloc_salt(
1940        ecx: &InterpCx<'tcx, Self>,
1941        instance: Option<ty::Instance<'tcx>>,
1942    ) -> usize {
1943        let unique = if let Some(instance) = instance {
1944            // Functions cannot be identified by pointers, as asm-equal functions can get
1945            // deduplicated by the linker (we set the "unnamed_addr" attribute for LLVM) and
1946            // functions can be duplicated across crates. We thus generate a new `AllocId` for every
1947            // mention of a function. This means that `main as fn() == main as fn()` is false, while
1948            // `let x = main as fn(); x == x` is true. However, as a quality-of-life feature it can
1949            // be useful to identify certain functions uniquely, e.g. for backtraces. So we identify
1950            // whether codegen will actually emit duplicate functions. It does that when they have
1951            // non-lifetime generics, or when they can be inlined. All other functions are given a
1952            // unique address. This is not a stable guarantee! The `inline` attribute is a hint and
1953            // cannot be relied upon for anything. But if we don't do this, the
1954            // `__rust_begin_short_backtrace`/`__rust_end_short_backtrace` logic breaks and panic
1955            // backtraces look terrible.
1956            let is_generic = instance
1957                .args
1958                .into_iter()
1959                .any(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_)));
1960            let can_be_inlined = matches!(
1961                ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
1962                InliningThreshold::Always
1963            ) || !matches!(
1964                ecx.tcx.codegen_instance_attrs(instance.def).inline,
1965                InlineAttr::Never
1966            );
1967            !is_generic && !can_be_inlined
1968        } else {
1969            // Non-functions are never unique.
1970            false
1971        };
1972        // Always use the same salt if the allocation is unique.
1973        if unique {
1974            CTFE_ALLOC_SALT
1975        } else {
1976            ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL)
1977        }
1978    }
1979
1980    fn cached_union_data_range<'e>(
1981        ecx: &'e mut InterpCx<'tcx, Self>,
1982        ty: Ty<'tcx>,
1983        compute_range: impl FnOnce() -> RangeSet,
1984    ) -> Cow<'e, RangeSet> {
1985        Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range))
1986    }
1987
1988    fn get_default_alloc_params(&self) -> <Self::Bytes as AllocBytes>::AllocParams {
1989        use crate::alloc::MiriAllocParams;
1990
1991        match &self.allocator {
1992            Some(alloc) => MiriAllocParams::Isolated(alloc.clone()),
1993            None => MiriAllocParams::Global,
1994        }
1995    }
1996
1997    fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan {
1998        #[cfg(feature = "tracing")]
1999        {
2000            span().entered()
2001        }
2002        #[cfg(not(feature = "tracing"))]
2003        #[expect(clippy::unused_unit)]
2004        {
2005            let _ = span; // so we avoid the "unused variable" warning
2006            ()
2007        }
2008    }
2009}
2010
2011/// Trait for callbacks handling asynchronous machine operations.
2012pub trait MachineCallback<'tcx, T>: VisitProvenance {
2013    /// The function to be invoked when the callback is fired.
2014    fn call(
2015        self: Box<Self>,
2016        ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
2017        arg: T,
2018    ) -> InterpResult<'tcx>;
2019}
2020
2021/// Type alias for boxed machine callbacks with generic argument type.
2022pub type DynMachineCallback<'tcx, T> = Box<dyn MachineCallback<'tcx, T> + 'tcx>;
2023
2024/// Creates a `DynMachineCallback`:
2025///
2026/// ```rust
2027/// callback!(
2028///     @capture<'tcx> {
2029///         var1: Ty1,
2030///         var2: Ty2<'tcx>,
2031///     }
2032///     |this, arg: ArgTy| {
2033///         // Implement the callback here.
2034///         todo!()
2035///     }
2036/// )
2037/// ```
2038///
2039/// All the argument types must implement `VisitProvenance`.
2040#[macro_export]
2041macro_rules! callback {
2042    (@capture<$tcx:lifetime $(,)? $($lft:lifetime),*>
2043        { $($name:ident: $type:ty),* $(,)? }
2044     |$this:ident, $arg:ident: $arg_ty:ty| $body:expr $(,)?) => {{
2045        struct Callback<$tcx, $($lft),*> {
2046            $($name: $type,)*
2047            _phantom: std::marker::PhantomData<&$tcx ()>,
2048        }
2049
2050        impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> {
2051            fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
2052                $(
2053                    VisitProvenance::visit_provenance(&self.$name, _visit);
2054                )*
2055            }
2056        }
2057
2058        impl<$tcx, $($lft),*> MachineCallback<$tcx, $arg_ty> for Callback<$tcx, $($lft),*> {
2059            fn call(
2060                self: Box<Self>,
2061                $this: &mut MiriInterpCx<$tcx>,
2062                $arg: $arg_ty
2063            ) -> InterpResult<$tcx> {
2064                #[allow(unused_variables)]
2065                let Callback { $($name,)* _phantom } = *self;
2066                $body
2067            }
2068        }
2069
2070        Box::new(Callback {
2071            $($name,)*
2072            _phantom: std::marker::PhantomData
2073        })
2074    }};
2075}