1use std::borrow::Cow;
5use std::cell::RefCell;
6use std::path::{Path, PathBuf};
7use std::rc::Rc;
8use std::{fmt, process};
9
10use rand::rngs::StdRng;
11use rand::{RngExt, SeedableRng};
12use rustc_abi::{Align, ExternAbi, Size};
13use rustc_apfloat::{Float, FloatConvert};
14use rustc_ast::Mutability;
15use rustc_ast::expand::allocator::{self, SpecialAllocatorMethod};
16use rustc_attr_ir::{InlineAttr, Linkage};
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::def::DefKind;
22use rustc_log::tracing;
23use rustc_middle::middle::codegen_fn_attrs::TargetFeatureKind;
24use rustc_middle::mir;
25use rustc_middle::query::TyCtxtAt;
26use rustc_middle::ty::layout::{
27 HasTyCtxt, HasTypingEnv, LayoutCx, LayoutError, LayoutOf, TyAndLayout,
28};
29use rustc_middle::ty::{self, AtomicOrdering, Instance, Ty, TyCtxt};
30use rustc_session::config::InliningThreshold;
31use rustc_span::def_id::{CrateNum, DefId};
32use rustc_span::{Span, Symbol};
33use rustc_symbol_mangling::mangle_internal_symbol;
34use rustc_target::callconv::FnAbi;
35use rustc_target::spec::{Arch, Os};
36
37use crate::alloc_addresses::EvalContextExt;
38use crate::concurrency::data_race::{self, NaReadType, NaWriteType};
39use crate::concurrency::{
40 AllocDataRaceHandler, GenmcCtx, GenmcEvalContextExt as _, GlobalDataRaceHandler, weak_memory,
41};
42use crate::data_structures::range_object_map::RangeObjectMap;
43use crate::helpers::is_no_core;
44use crate::*;
45
46pub const SIGRTMIN: i32 = 34;
50
51pub const SIGRTMAX: i32 = 42;
55
56const ADDRS_PER_ANON_GLOBAL: usize = 32;
60
61#[derive(Copy, Clone, Debug, PartialEq)]
62pub enum AlignmentCheck {
63 None,
65 Symbolic,
67 Int,
69}
70
71#[derive(Copy, Clone, Debug, PartialEq)]
72pub enum RejectOpWith {
73 Abort,
75
76 NoWarning,
80
81 Warning,
83
84 WarningWithoutBacktrace,
86}
87
88#[derive(Copy, Clone, Debug, PartialEq)]
89pub enum IsolatedOp {
90 Reject(RejectOpWith),
95
96 Allow,
98}
99
100#[derive(Debug, Copy, Clone, PartialEq, Eq)]
101pub enum BacktraceStyle {
102 Short,
104 Full,
106 Off,
108}
109
110#[derive(Debug, Copy, Clone, PartialEq, Eq)]
111pub enum ValidationMode {
112 No,
114 Shallow,
116 Deep,
118}
119
120#[derive(Debug, Copy, Clone, PartialEq, Eq)]
121pub enum FloatRoundingErrorMode {
122 Random,
124 None,
126 Max,
128}
129
130pub struct FrameExtra<'tcx> {
132 pub borrow_tracker: Option<borrow_tracker::FrameState>,
134
135 pub catch_unwind: Option<CatchUnwindData<'tcx>>,
139
140 pub timing: Option<measureme::DetachedTiming>,
144
145 pub user_relevance: u8,
149
150 pub data_race: Option<data_race::FrameState>,
152}
153
154impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 let FrameExtra { borrow_tracker, catch_unwind, timing: _, user_relevance, data_race } =
158 self;
159 f.debug_struct("FrameData")
160 .field("borrow_tracker", borrow_tracker)
161 .field("catch_unwind", catch_unwind)
162 .field("user_relevance", user_relevance)
163 .field("data_race", data_race)
164 .finish()
165 }
166}
167
168impl VisitProvenance for FrameExtra<'_> {
169 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
170 let FrameExtra { catch_unwind, borrow_tracker, timing: _, user_relevance: _, data_race: _ } =
171 self;
172
173 catch_unwind.visit_provenance(visit);
174 borrow_tracker.visit_provenance(visit);
175 }
176}
177
178#[derive(Debug, Copy, Clone, PartialEq, Eq)]
180pub enum MiriMemoryKind {
181 Rust,
183 Miri,
185 C,
187 WinHeap,
189 WinLocal,
191 Machine,
194 Runtime,
197 Global,
200 ExternStatic,
203 Tls,
206 Mmap,
208 SocketAddress,
210}
211
212impl From<MiriMemoryKind> for MemoryKind {
213 #[inline(always)]
214 fn from(kind: MiriMemoryKind) -> MemoryKind {
215 MemoryKind::Machine(kind)
216 }
217}
218
219impl MayLeak for MiriMemoryKind {
220 #[inline(always)]
221 fn may_leak(self) -> bool {
222 use self::MiriMemoryKind::*;
223 match self {
224 Rust | Miri | C | WinHeap | WinLocal | Runtime => false,
225 Machine | Global | ExternStatic | Tls | Mmap | SocketAddress => true,
226 }
227 }
228}
229
230impl MiriMemoryKind {
231 fn should_save_allocation_span(self) -> bool {
233 use self::MiriMemoryKind::*;
234 match self {
235 Rust | Miri | C | WinHeap | WinLocal | Mmap => true,
237 Machine | Global | ExternStatic | Tls | Runtime | SocketAddress => false,
239 }
240 }
241}
242
243impl fmt::Display for MiriMemoryKind {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 use self::MiriMemoryKind::*;
246 match self {
247 Rust => write!(f, "Rust heap"),
248 Miri => write!(f, "Miri bare-metal heap"),
249 C => write!(f, "C heap"),
250 WinHeap => write!(f, "Windows heap"),
251 WinLocal => write!(f, "Windows local memory"),
252 Machine => write!(f, "machine-managed memory"),
253 Runtime => write!(f, "language runtime memory"),
254 Global => write!(f, "global (static or const)"),
255 ExternStatic => write!(f, "extern static"),
256 Tls => write!(f, "thread-local static"),
257 Mmap => write!(f, "mmap"),
258 SocketAddress => write!(f, "socket address"),
259 }
260 }
261}
262
263pub type MemoryKind = interpret::MemoryKind<MiriMemoryKind>;
264
265#[derive(Clone, Copy, PartialEq, Eq, Hash)]
271pub enum Provenance {
272 Concrete {
275 alloc_id: AllocId,
276 tag: BorTag,
278 },
279 Wildcard,
296}
297
298#[derive(Copy, Clone, PartialEq)]
300pub enum ProvenanceExtra {
301 Concrete(BorTag),
302 Wildcard,
303}
304
305#[cfg(target_pointer_width = "64")]
306static_assert_size!(StrictPointer, 24);
307#[cfg(target_pointer_width = "64")]
312static_assert_size!(Scalar, 32);
313
314impl fmt::Debug for Provenance {
315 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
316 match self {
317 Provenance::Concrete { alloc_id, tag } => {
318 if f.alternate() {
320 write!(f, "[{alloc_id:#?}]")?;
321 } else {
322 write!(f, "[{alloc_id:?}]")?;
323 }
324 write!(f, "{tag:?}")?;
326 }
327 Provenance::Wildcard => {
328 write!(f, "[wildcard]")?;
329 }
330 }
331 Ok(())
332 }
333}
334
335impl interpret::Provenance for Provenance {
336 const OFFSET_IS_ADDR: bool = true;
338
339 const WILDCARD: Option<Self> = Some(Provenance::Wildcard);
341
342 fn get_alloc_id(self) -> Option<AllocId> {
343 match self {
344 Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
345 Provenance::Wildcard => None,
346 }
347 }
348
349 fn fmt(ptr: &interpret::Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 let (prov, addr) = ptr.into_raw_parts(); write!(f, "{:#x}", addr.bytes())?;
352 if f.alternate() {
353 write!(f, "{prov:#?}")?;
354 } else {
355 write!(f, "{prov:?}")?;
356 }
357 Ok(())
358 }
359}
360
361impl fmt::Debug for ProvenanceExtra {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 match self {
364 ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"),
365 ProvenanceExtra::Wildcard => write!(f, "<wildcard>"),
366 }
367 }
368}
369
370impl ProvenanceExtra {
371 pub fn and_then<T>(self, f: impl FnOnce(BorTag) -> Option<T>) -> Option<T> {
372 match self {
373 ProvenanceExtra::Concrete(pid) => f(pid),
374 ProvenanceExtra::Wildcard => None,
375 }
376 }
377}
378
379#[derive(Debug)]
381pub struct AllocExtra<'tcx> {
382 pub borrow_tracker: Option<borrow_tracker::AllocState>,
384 pub data_race: AllocDataRaceHandler,
388 pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
393 pub meta_objs: RangeObjectMap<Box<dyn MetaObj>>,
396}
397
398impl<'tcx> Clone for AllocExtra<'tcx> {
401 fn clone(&self) -> Self {
402 panic!("our allocations should never be cloned");
403 }
404}
405
406impl VisitProvenance for AllocExtra<'_> {
407 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
408 let AllocExtra { borrow_tracker, data_race, backtrace: _, meta_objs: _ } = self;
409
410 borrow_tracker.visit_provenance(visit);
411 data_race.visit_provenance(visit);
412 }
413}
414
415pub struct PrimitiveLayouts<'tcx> {
417 pub unit: TyAndLayout<'tcx>,
418 pub i8: TyAndLayout<'tcx>,
419 pub i16: TyAndLayout<'tcx>,
420 pub i32: TyAndLayout<'tcx>,
421 pub i64: TyAndLayout<'tcx>,
422 pub i128: TyAndLayout<'tcx>,
423 pub isize: TyAndLayout<'tcx>,
424 pub u8: TyAndLayout<'tcx>,
425 pub u16: TyAndLayout<'tcx>,
426 pub u32: TyAndLayout<'tcx>,
427 pub u64: TyAndLayout<'tcx>,
428 pub u128: TyAndLayout<'tcx>,
429 pub usize: TyAndLayout<'tcx>,
430 pub bool: TyAndLayout<'tcx>,
431 pub unit_ptr_mut: TyAndLayout<'tcx>, pub unit_ptr_const: TyAndLayout<'tcx>, pub void_ptr_mut: TyAndLayout<'tcx>, pub void_ptr_const: TyAndLayout<'tcx>, pub fn_ptr: TyAndLayout<'tcx>, }
437
438impl<'tcx> PrimitiveLayouts<'tcx> {
439 fn new(layout_cx: LayoutCx<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
440 let tcx = layout_cx.tcx();
441
442 let unit_ptr_mut = Ty::new_mut_ptr(tcx, tcx.types.unit);
443 let unit_ptr_const = Ty::new_imm_ptr(tcx, tcx.types.unit);
444 let c_void = match tcx.lang_items().c_void() {
446 Some(c_void) => ty::Instance::mono(tcx, c_void).ty(tcx, layout_cx.typing_env),
447 None => tcx.types.unit,
448 };
449 let void_ptr_mut = Ty::new_mut_ptr(tcx, c_void);
450 let void_ptr_const = Ty::new_imm_ptr(tcx, c_void);
451
452 let sig_kind = ty::FnSigKind::default()
453 .set_abi(ExternAbi::C { unwind: false })
454 .set_safety(rustc_hir::Safety::Safe);
455 let fn_ptr =
456 Ty::new_fn_ptr(tcx, ty::Binder::dummy(tcx.mk_fn_sig([], tcx.types.unit, sig_kind)));
457
458 Ok(Self {
459 unit: layout_cx.layout_of(tcx.types.unit)?,
460 i8: layout_cx.layout_of(tcx.types.i8)?,
461 i16: layout_cx.layout_of(tcx.types.i16)?,
462 i32: layout_cx.layout_of(tcx.types.i32)?,
463 i64: layout_cx.layout_of(tcx.types.i64)?,
464 i128: layout_cx.layout_of(tcx.types.i128)?,
465 isize: layout_cx.layout_of(tcx.types.isize)?,
466 u8: layout_cx.layout_of(tcx.types.u8)?,
467 u16: layout_cx.layout_of(tcx.types.u16)?,
468 u32: layout_cx.layout_of(tcx.types.u32)?,
469 u64: layout_cx.layout_of(tcx.types.u64)?,
470 u128: layout_cx.layout_of(tcx.types.u128)?,
471 usize: layout_cx.layout_of(tcx.types.usize)?,
472 bool: layout_cx.layout_of(tcx.types.bool)?,
473 unit_ptr_mut: layout_cx.layout_of(unit_ptr_mut)?,
474 unit_ptr_const: layout_cx.layout_of(unit_ptr_const)?,
475 void_ptr_mut: layout_cx.layout_of(void_ptr_mut)?,
476 void_ptr_const: layout_cx.layout_of(void_ptr_const)?,
477 fn_ptr: layout_cx.layout_of(fn_ptr)?,
478 })
479 }
480
481 pub fn uint(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
482 match size.bits() {
483 8 => Some(self.u8),
484 16 => Some(self.u16),
485 32 => Some(self.u32),
486 64 => Some(self.u64),
487 128 => Some(self.u128),
488 _ => None,
489 }
490 }
491
492 pub fn int(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
493 match size.bits() {
494 8 => Some(self.i8),
495 16 => Some(self.i16),
496 32 => Some(self.i32),
497 64 => Some(self.i64),
498 128 => Some(self.i128),
499 _ => None,
500 }
501 }
502}
503
504pub struct MiriMachine<'tcx> {
509 pub tcx: TyCtxt<'tcx>,
511
512 pub borrow_tracker: Option<borrow_tracker::GlobalState>,
514
515 pub data_race: GlobalDataRaceHandler,
521
522 pub alloc_addresses: alloc_addresses::GlobalState,
524
525 pub(crate) env_vars: EnvVars<'tcx>,
527
528 pub(crate) main_fn_ret_place: Option<MPlaceTy<'tcx>>,
530
531 pub(crate) argc: Option<Pointer>,
535 pub(crate) argv: Option<Pointer>,
536 pub(crate) cmd_line: Option<Pointer>,
537 pub(crate) current_exe: Option<PathBuf>,
539
540 pub(crate) tls: TlsData<'tcx>,
542
543 pub(crate) isolated_op: IsolatedOp,
547
548 pub(crate) validation: ValidationMode,
550
551 pub(crate) fds: shims::FdTable,
553 pub(crate) dirs: shims::DirTable,
555
556 pub(crate) delayed_readiness_updates: Rc<shims::DelayedReadinessUpdates>,
558
559 pub(crate) monotonic_clock: MonotonicClock,
561
562 pub(crate) threads: ThreadManager<'tcx>,
564
565 pub(crate) blocking_io: BlockingIoManager,
567
568 pub(crate) thread_cpu_affinity: Option<FxHashMap<ThreadId, shims::CpuAffinityMask>>,
573
574 pub(crate) layouts: PrimitiveLayouts<'tcx>,
576
577 pub(crate) static_roots: Vec<AllocId>,
579
580 profiler: Option<measureme::Profiler>,
583 string_cache: FxHashMap<String, measureme::StringId>,
586
587 pub(crate) exported_symbols_cache: RefCell<FxHashMap<Symbol, Option<Instance<'tcx>>>>,
590
591 pub(crate) backtrace_style: BacktraceStyle,
593
594 pub(crate) user_relevant_crates: Vec<CrateNum>,
596
597 pub(crate) extern_statics: FxHashMap<Symbol, StrictPointer>,
599 pub(crate) extern_statics_imports: FxHashMap<Symbol, StrictPointer>,
602 pub(crate) extern_static_weak_import_default: Option<StrictPointer>,
604
605 pub(crate) rng: RefCell<StdRng>,
608
609 pub(crate) allocator: Option<Rc<RefCell<crate::alloc::isolated_alloc::IsolatedAlloc>>>,
611
612 pub(crate) tracked_alloc_ids: FxHashSet<AllocId>,
615 track_alloc_accesses: bool,
617
618 pub(crate) check_alignment: AlignmentCheck,
620
621 pub(crate) cmpxchg_weak_failure_rate: f64,
623
624 pub(crate) preemption_rate: f64,
626
627 pub(crate) report_progress: Option<u32>,
629 pub(crate) basic_block_count: u64,
631
632 #[cfg(all(feature = "native-lib", unix))]
634 pub native_lib: Vec<(libloading::Library, std::path::PathBuf)>,
635 #[cfg(not(all(feature = "native-lib", unix)))]
636 pub native_lib: Vec<!>,
637 #[cfg(all(feature = "native-lib", unix))]
639 pub native_lib_ecx_interchange: &'static std::cell::Cell<usize>,
640
641 pub(crate) gc_interval: u32,
643 pub(crate) since_gc: u32,
645
646 pub(crate) num_cpus: u32,
648
649 pub(crate) page_size: u64,
651 pub(crate) stack_addr: u64,
652 pub(crate) stack_size: u64,
653
654 pub(crate) collect_leak_backtraces: bool,
656
657 pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>)>>,
660
661 pub(crate) symbolic_alignment: RefCell<FxHashMap<AllocId, (Size, Align)>>,
668
669 union_data_ranges: FxHashMap<Ty<'tcx>, RangeSet>,
671
672 pub(crate) allocator_shim_symbols: FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>>,
676 pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>,
678
679 pub float_nondet: bool,
681 pub float_rounding_error: FloatRoundingErrorMode,
683
684 pub short_fd_operations: bool,
686
687 pub exit_called: bool,
690}
691
692impl<'tcx> MiriMachine<'tcx> {
693 pub(crate) fn new(
697 config: &MiriConfig,
698 layout_cx: LayoutCx<'tcx>,
699 genmc_ctx: Option<Rc<GenmcCtx>>,
700 ) -> Self {
701 let tcx = layout_cx.tcx();
702 let user_relevant_crates = Self::get_user_relevant_crates(tcx, config);
703 let layouts =
704 PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
705 let profiler = config.measureme_out.as_ref().map(|out| {
706 let crate_name =
707 tcx.sess.opts.crate_name.clone().unwrap_or_else(|| "unknown-crate".to_string());
708 let pid = process::id();
709 let filename = format!("{crate_name}-{pid:07}");
714 let path = Path::new(out).join(filename);
715 measureme::Profiler::new(path).expect("Couldn't create `measureme` profiler")
716 });
717 let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
718 let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config));
719 let data_race = if config.genmc_config.is_some() {
720 GlobalDataRaceHandler::Genmc(genmc_ctx.unwrap())
722 } else if config.data_race_detector {
723 GlobalDataRaceHandler::Vclocks(Box::new(data_race::GlobalState::new(config)))
724 } else {
725 GlobalDataRaceHandler::None
726 };
727 let page_size = if let Some(page_size) = config.page_size {
731 page_size
732 } else {
733 let target = &tcx.sess.target;
734 match target.arch {
735 Arch::Wasm32 | Arch::Wasm64 => 64 * 1024, Arch::AArch64 if target.is_like_darwin => {
737 16 * 1024
741 }
742 _ => 4 * 1024,
743 }
744 };
745 let stack_addr = if tcx.pointer_size().bits() < 32 { page_size } else { page_size * 32 };
747 let stack_size =
748 if tcx.pointer_size().bits() < 32 { page_size * 4 } else { page_size * 16 };
749 assert!(
750 usize::try_from(config.num_cpus).unwrap() <= shims::cpu_affinity::MAX_CPUS,
751 "miri only supports up to {} CPUs, but {} were configured",
752 shims::cpu_affinity::MAX_CPUS,
753 config.num_cpus
754 );
755 let threads = ThreadManager::new(config);
756 let thread_cpu_affinity =
757 if matches!(&tcx.sess.target.os, Os::Linux | Os::FreeBsd | Os::Android)
758 && !is_no_core(tcx)
759 {
760 let mut affinity = FxHashMap::default();
761 affinity.insert(
762 threads.active_thread(),
763 shims::CpuAffinityMask::new(&layout_cx, config.num_cpus),
764 );
765 Some(affinity)
766 } else {
767 None
768 };
769 let blocking_io = BlockingIoManager::new(config.isolated_op == IsolatedOp::Allow)
770 .expect("Couldn't create poll instance");
771 let alloc_addresses =
772 RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr, tcx));
773
774 MiriMachine {
775 tcx,
776 borrow_tracker,
777 data_race,
778 alloc_addresses,
779 env_vars: EnvVars::default(),
781 main_fn_ret_place: None,
782 argc: None,
783 argv: None,
784 cmd_line: None,
785 current_exe: config.current_exe.clone(),
786 tls: TlsData::default(),
787 isolated_op: config.isolated_op,
788 validation: config.validation,
789 fds: shims::FdTable::init(config.mute_stdout_stderr),
790 delayed_readiness_updates: Rc::new(shims::DelayedReadinessUpdates::default()),
791 dirs: Default::default(),
792 layouts,
793 threads,
794 thread_cpu_affinity,
795 blocking_io,
796 static_roots: Vec::new(),
797 profiler,
798 string_cache: Default::default(),
799 exported_symbols_cache: RefCell::new(FxHashMap::default()),
800 backtrace_style: config.backtrace_style,
801 user_relevant_crates,
802 extern_statics: FxHashMap::default(),
803 extern_statics_imports: FxHashMap::default(),
804 extern_static_weak_import_default: None,
805 rng: RefCell::new(rng),
806 allocator: (!config.native_lib.is_empty())
807 .then(|| Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new()))),
808 tracked_alloc_ids: config.tracked_alloc_ids.clone(),
809 track_alloc_accesses: config.track_alloc_accesses,
810 check_alignment: config.check_alignment,
811 cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
812 preemption_rate: config.preemption_rate,
813 report_progress: config.report_progress,
814 basic_block_count: 0,
815 monotonic_clock: MonotonicClock::new(config.isolated_op == IsolatedOp::Allow),
816 #[cfg(all(feature = "native-lib", unix))]
817 native_lib: config.native_lib.iter().map(|lib_file_path| {
818 let host_triple = rustc_session::config::host_tuple();
819 let target_triple = tcx.sess.opts.target_triple.tuple();
820 if host_triple != target_triple {
822 panic!(
823 "calling native C functions in linked .so file requires host and target to be the same: \
824 host={host_triple}, target={target_triple}",
825 );
826 }
827 (
831 unsafe {
832 libloading::Library::new(lib_file_path)
833 .expect("failed to read specified extern shared object file")
834 },
835 lib_file_path.clone(),
836 )
837 }).collect(),
838 #[cfg(all(feature = "native-lib", unix))]
839 native_lib_ecx_interchange: Box::leak(Box::new(std::cell::Cell::new(0))),
840 #[cfg(not(all(feature = "native-lib", unix)))]
841 native_lib: config.native_lib.iter().map(|_| {
842 panic!("calling functions from native libraries via FFI is not supported in this build of Miri")
843 }).collect(),
844 gc_interval: config.gc_interval,
845 since_gc: 0,
846 num_cpus: config.num_cpus,
847 page_size,
848 stack_addr,
849 stack_size,
850 collect_leak_backtraces: config.collect_leak_backtraces,
851 allocation_spans: RefCell::new(FxHashMap::default()),
852 symbolic_alignment: RefCell::new(FxHashMap::default()),
853 union_data_ranges: FxHashMap::default(),
854 allocator_shim_symbols: Self::allocator_shim_symbols(tcx),
855 mangle_internal_symbol_cache: Default::default(),
856 float_nondet: config.float_nondet,
857 float_rounding_error: config.float_rounding_error,
858 short_fd_operations: config.short_fd_operations,
859 exit_called: false,
860 }
861 }
862
863 fn allocator_shim_symbols(
864 tcx: TyCtxt<'tcx>,
865 ) -> FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>> {
866 use rustc_codegen_ssa::base::allocator_shim_contents;
867
868 let Some(kind) = tcx.allocator_kind(()) else {
871 return Default::default();
872 };
873 let methods = allocator_shim_contents(tcx, kind);
874 let mut symbols = FxHashMap::default();
875 for method in methods {
876 let from_name = Symbol::intern(&mangle_internal_symbol(
877 tcx,
878 &allocator::global_fn_name(method.name),
879 ));
880 let to = match method.special {
881 Some(special) => Either::Right(special),
882 None =>
883 Either::Left(Symbol::intern(&mangle_internal_symbol(
884 tcx,
885 &allocator::default_fn_name(method.name),
886 ))),
887 };
888 symbols.try_insert(from_name, to).unwrap();
889 }
890 symbols
891 }
892
893 fn get_user_relevant_crates(tcx: TyCtxt<'_>, config: &MiriConfig) -> Vec<CrateNum> {
896 let local_crate_names = std::env::var("MIRI_LOCAL_CRATES")
899 .map(|crates| crates.split(',').map(|krate| krate.to_string()).collect::<Vec<_>>())
900 .unwrap_or_default();
901 let mut local_crates = Vec::new();
902 for &crate_num in tcx.crates(()) {
903 let name = tcx.crate_name(crate_num);
904 let name = name.as_str();
905 if local_crate_names
906 .iter()
907 .chain(&config.user_relevant_crates)
908 .any(|local_name| local_name == name)
909 {
910 local_crates.push(crate_num);
911 }
912 }
913 local_crates
914 }
915
916 pub(crate) fn late_init(
917 ecx: &mut MiriInterpCx<'tcx>,
918 config: &MiriConfig,
919 on_main_stack_empty: StackEmptyCallback<'tcx>,
920 ) -> InterpResult<'tcx> {
921 EnvVars::init(ecx, config)?;
922 MiriMachine::init_extern_statics(ecx)?;
923 ThreadManager::init(ecx, on_main_stack_empty);
924 interp_ok(())
925 }
926
927 pub(crate) fn communicate(&self) -> bool {
928 self.isolated_op == IsolatedOp::Allow
929 }
930
931 pub(crate) fn is_local(&self, instance: ty::Instance<'tcx>) -> bool {
933 let def_id = instance.def_id();
934 def_id.is_local() || self.user_relevant_crates.contains(&def_id.krate)
935 }
936
937 pub(crate) fn handle_abnormal_termination(&mut self) {
939 drop(self.profiler.take());
944 }
945
946 pub(crate) fn page_align(&self) -> Align {
947 Align::from_bytes(self.page_size).unwrap()
948 }
949
950 pub(crate) fn allocated_span(&self, alloc_id: AllocId) -> Option<Span> {
951 self.allocation_spans.borrow().get(&alloc_id).map(|(allocated, _deallocated)| *allocated)
952 }
953
954 pub(crate) fn deallocated_span(&self, alloc_id: AllocId) -> Option<Span> {
955 self.allocation_spans
956 .borrow()
957 .get(&alloc_id)
958 .and_then(|(_allocated, deallocated)| *deallocated)
959 }
960
961 fn init_allocation(
962 ecx: &MiriInterpCx<'tcx>,
963 id: AllocId,
964 kind: MemoryKind,
965 size: Size,
966 align: Align,
967 ) -> InterpResult<'tcx, AllocExtra<'tcx>> {
968 if ecx.machine.tracked_alloc_ids.contains(&id) {
969 ecx.emit_diagnostic(NonHaltingDiagnostic::TrackingAlloc(id, size, align));
970 }
971
972 let borrow_tracker = ecx
973 .machine
974 .borrow_tracker
975 .as_ref()
976 .map(|bt| bt.borrow_mut().new_allocation(id, size, kind, &ecx.machine));
977
978 let data_race = match &ecx.machine.data_race {
979 GlobalDataRaceHandler::None => AllocDataRaceHandler::None,
980 GlobalDataRaceHandler::Vclocks(data_race) =>
981 AllocDataRaceHandler::Vclocks(
982 data_race::AllocState::new_allocation(
983 data_race,
984 &ecx.machine.threads,
985 size,
986 kind,
987 ecx.machine.current_user_relevant_span(),
988 ),
989 data_race.weak_memory.then(weak_memory::AllocState::new_allocation),
990 ),
991 GlobalDataRaceHandler::Genmc(_genmc_ctx) => {
992 AllocDataRaceHandler::Genmc
995 }
996 };
997
998 let backtrace = if kind.may_leak() || !ecx.machine.collect_leak_backtraces {
1002 None
1003 } else {
1004 Some(ecx.generate_stacktrace())
1005 };
1006
1007 if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) {
1008 ecx.machine
1009 .allocation_spans
1010 .borrow_mut()
1011 .insert(id, (ecx.machine.current_user_relevant_span(), None));
1012 }
1013
1014 interp_ok(AllocExtra {
1015 borrow_tracker,
1016 data_race,
1017 backtrace,
1018 meta_objs: RangeObjectMap::new(),
1019 })
1020 }
1021}
1022
1023impl VisitProvenance for MiriMachine<'_> {
1024 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
1025 #[rustfmt::skip]
1026 let MiriMachine {
1027 threads,
1028 thread_cpu_affinity: _,
1029 tls,
1030 env_vars,
1031 main_fn_ret_place,
1032 argc,
1033 argv,
1034 cmd_line,
1035 current_exe: _,
1036 extern_statics,
1037 extern_statics_imports,
1038 extern_static_weak_import_default,
1039 dirs,
1040 borrow_tracker,
1041 data_race,
1042 alloc_addresses,
1043 fds,
1044 blocking_io:_,
1045 delayed_readiness_updates: _,
1046 tcx: _,
1047 isolated_op: _,
1048 validation: _,
1049 monotonic_clock: _,
1050 layouts: _,
1051 static_roots: _,
1052 profiler: _,
1053 string_cache: _,
1054 exported_symbols_cache: _,
1055 backtrace_style: _,
1056 user_relevant_crates: _,
1057 rng: _,
1058 allocator: _,
1059 tracked_alloc_ids: _,
1060 track_alloc_accesses: _,
1061 check_alignment: _,
1062 cmpxchg_weak_failure_rate: _,
1063 preemption_rate: _,
1064 report_progress: _,
1065 basic_block_count: _,
1066 native_lib: _,
1067 #[cfg(all(feature = "native-lib", unix))]
1068 native_lib_ecx_interchange: _,
1069 gc_interval: _,
1070 since_gc: _,
1071 num_cpus: _,
1072 page_size: _,
1073 stack_addr: _,
1074 stack_size: _,
1075 collect_leak_backtraces: _,
1076 allocation_spans: _,
1077 symbolic_alignment: _,
1078 union_data_ranges: _,
1079 allocator_shim_symbols: _,
1080 mangle_internal_symbol_cache: _,
1081 float_nondet: _,
1082 float_rounding_error: _,
1083 short_fd_operations: _,
1084 exit_called: _,
1085 } = self;
1086
1087 threads.visit_provenance(visit);
1088 tls.visit_provenance(visit);
1089 env_vars.visit_provenance(visit);
1090 dirs.visit_provenance(visit);
1091 fds.visit_provenance(visit);
1092 data_race.visit_provenance(visit);
1093 borrow_tracker.visit_provenance(visit);
1094 alloc_addresses.visit_provenance(visit);
1095 main_fn_ret_place.visit_provenance(visit);
1096 argc.visit_provenance(visit);
1097 argv.visit_provenance(visit);
1098 cmd_line.visit_provenance(visit);
1099 extern_static_weak_import_default.visit_provenance(visit);
1100 extern_statics.visit_provenance(visit);
1101 extern_statics_imports.visit_provenance(visit);
1102 }
1103}
1104
1105pub type MiriInterpCx<'tcx> = InterpCx<'tcx, MiriMachine<'tcx>>;
1107
1108pub trait MiriInterpCxExt<'tcx> {
1110 fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'tcx>;
1111 fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'tcx>;
1112}
1113impl<'tcx> MiriInterpCxExt<'tcx> for MiriInterpCx<'tcx> {
1114 #[inline(always)]
1115 fn eval_context_ref(&self) -> &MiriInterpCx<'tcx> {
1116 self
1117 }
1118 #[inline(always)]
1119 fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'tcx> {
1120 self
1121 }
1122}
1123
1124impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
1126 type MemoryKind = MiriMemoryKind;
1127 type ExtraFnVal = DynSym;
1128
1129 type FrameExtra = FrameExtra<'tcx>;
1130 type AllocExtra = AllocExtra<'tcx>;
1131
1132 type Provenance = Provenance;
1133 type ProvenanceExtra = ProvenanceExtra;
1134 type Bytes = MiriAllocBytes;
1135
1136 type MemoryMap =
1137 MonoHashMap<AllocId, (MemoryKind, Allocation<Provenance, Self::AllocExtra, Self::Bytes>)>;
1138
1139 const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
1140
1141 const PANIC_ON_ALLOC_FAIL: bool = false;
1142
1143 #[inline(always)]
1144 fn enforce_alignment(ecx: &MiriInterpCx<'tcx>) -> bool {
1145 ecx.machine.check_alignment != AlignmentCheck::None
1146 }
1147
1148 #[inline(always)]
1149 fn alignment_check(
1150 ecx: &MiriInterpCx<'tcx>,
1151 alloc_id: AllocId,
1152 alloc_align: Align,
1153 alloc_kind: AllocKind,
1154 offset: Size,
1155 align: Align,
1156 ) -> Option<Misalignment> {
1157 if ecx.machine.check_alignment != AlignmentCheck::Symbolic {
1158 return None;
1160 }
1161 if alloc_kind != AllocKind::LiveData {
1162 return None;
1164 }
1165 let (promised_offset, promised_align) = ecx
1167 .machine
1168 .symbolic_alignment
1169 .borrow()
1170 .get(&alloc_id)
1171 .copied()
1172 .unwrap_or((Size::ZERO, alloc_align));
1173 if promised_align < align {
1174 Some(Misalignment { has: promised_align, required: align })
1176 } else {
1177 let distance = offset.bytes().wrapping_sub(promised_offset.bytes());
1179 if distance.is_multiple_of(align.bytes()) {
1181 None
1183 } else {
1184 let distance_pow2 = 1 << distance.trailing_zeros();
1186 Some(Misalignment {
1187 has: Align::from_bytes(distance_pow2).unwrap(),
1188 required: align,
1189 })
1190 }
1191 }
1192 }
1193
1194 #[inline(always)]
1195 fn enforce_validity(ecx: &MiriInterpCx<'tcx>, _layout: TyAndLayout<'tcx>) -> bool {
1196 ecx.machine.validation != ValidationMode::No
1197 }
1198 #[inline(always)]
1199 fn enforce_validity_recursively(
1200 ecx: &InterpCx<'tcx, Self>,
1201 _layout: TyAndLayout<'tcx>,
1202 ) -> bool {
1203 ecx.machine.validation == ValidationMode::Deep
1204 }
1205
1206 #[inline(always)]
1207 fn ignore_optional_overflow_checks(ecx: &MiriInterpCx<'tcx>) -> bool {
1208 !ecx.tcx.sess.overflow_checks()
1209 }
1210
1211 fn check_fn_target_features(
1212 ecx: &MiriInterpCx<'tcx>,
1213 instance: ty::Instance<'tcx>,
1214 ) -> InterpResult<'tcx> {
1215 let attrs = ecx.tcx.codegen_instance_attrs(instance.def);
1216 if attrs
1217 .target_features
1218 .iter()
1219 .any(|feature| !ecx.tcx.sess.internal_target_features.contains(&feature.name))
1220 {
1221 let unavailable = attrs
1222 .target_features
1223 .iter()
1224 .filter(|&feature| {
1225 feature.kind != TargetFeatureKind::Implied
1226 && !ecx.tcx.sess.internal_target_features.contains(&feature.name)
1227 })
1228 .fold(String::new(), |mut s, feature| {
1229 if !s.is_empty() {
1230 s.push_str(", ");
1231 }
1232 s.push_str(feature.name.as_str());
1233 s
1234 });
1235 let msg = format!(
1236 "calling a function that requires unavailable target features: {unavailable}"
1237 );
1238 if ecx.tcx.sess.target.is_like_wasm {
1241 throw_machine_stop!(TerminationInfo::Abort(msg));
1242 } else {
1243 throw_ub_format!("{msg}");
1244 }
1245 }
1246 interp_ok(())
1247 }
1248
1249 #[inline(always)]
1250 fn find_mir_or_eval_fn(
1251 ecx: &mut MiriInterpCx<'tcx>,
1252 instance: ty::Instance<'tcx>,
1253 abi: &FnAbi<'tcx, Ty<'tcx>>,
1254 args: &[FnArg<'tcx>],
1255 dest: &PlaceTy<'tcx>,
1256 ret: Option<mir::BasicBlock>,
1257 unwind: mir::UnwindAction,
1258 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
1259 if ecx.tcx.is_foreign_item(instance.def_id()) {
1261 let _trace = enter_trace_span!("emulate_foreign_item");
1262 let args = MiriInterpCx::copy_fn_args(args); let link_name = Symbol::intern(ecx.tcx.symbol_name(instance).name);
1270 return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind);
1271 }
1272
1273 if ecx.machine.data_race.as_genmc_ref().is_some()
1274 && ecx.genmc_intercept_function(instance, args, dest)?
1275 {
1276 ecx.return_to_block(ret)?;
1277 return interp_ok(None);
1278 }
1279
1280 let _trace = enter_trace_span!("load_mir");
1282 interp_ok(Some((ecx.load_mir(instance.def, None)?, instance)))
1283 }
1284
1285 #[inline(always)]
1286 fn call_extra_fn(
1287 ecx: &mut MiriInterpCx<'tcx>,
1288 fn_val: DynSym,
1289 abi: &FnAbi<'tcx, Ty<'tcx>>,
1290 args: &[FnArg<'tcx>],
1291 dest: &PlaceTy<'tcx>,
1292 ret: Option<mir::BasicBlock>,
1293 unwind: mir::UnwindAction,
1294 ) -> InterpResult<'tcx> {
1295 let args = MiriInterpCx::copy_fn_args(args); ecx.emulate_dyn_sym(fn_val, abi, &args, dest, ret, unwind)
1297 }
1298
1299 #[inline(always)]
1300 fn call_intrinsic(
1301 ecx: &mut MiriInterpCx<'tcx>,
1302 instance: ty::Instance<'tcx>,
1303 args: &[OpTy<'tcx>],
1304 dest: &PlaceTy<'tcx>,
1305 ret: Option<mir::BasicBlock>,
1306 unwind: mir::UnwindAction,
1307 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
1308 ecx.call_intrinsic(instance, args, dest, ret, unwind)
1309 }
1310
1311 #[inline(always)]
1312 fn call_llvm_intrinsic(
1313 ecx: &mut MiriInterpCx<'tcx>,
1314 instance: ty::Instance<'tcx>,
1315 args: &[OpTy<'tcx>],
1316 dest: &PlaceTy<'tcx>,
1317 ret: Option<mir::BasicBlock>,
1318 ) -> InterpResult<'tcx, ()> {
1319 ecx.call_llvm_intrinsic(instance, args, dest, ret)
1320 }
1321
1322 #[inline(always)]
1323 fn assert_panic(
1324 ecx: &mut MiriInterpCx<'tcx>,
1325 msg: &mir::AssertMessage<'tcx>,
1326 unwind: mir::UnwindAction,
1327 ) -> InterpResult<'tcx> {
1328 ecx.assert_panic(msg, unwind)
1329 }
1330
1331 fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> {
1332 ecx.start_panic_nounwind(msg)
1333 }
1334
1335 fn unwind_terminate(
1336 ecx: &mut InterpCx<'tcx, Self>,
1337 reason: mir::UnwindTerminateReason,
1338 ) -> InterpResult<'tcx> {
1339 let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap();
1341 let panic = ty::Instance::mono(ecx.tcx.tcx, panic);
1342 ecx.call_function(
1343 panic,
1344 ExternAbi::Rust,
1345 &[],
1346 None,
1347 ReturnContinuation::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
1348 )?;
1349 interp_ok(())
1350 }
1351
1352 #[inline(always)]
1353 fn binary_ptr_op(
1354 ecx: &MiriInterpCx<'tcx>,
1355 bin_op: mir::BinOp,
1356 left: &ImmTy<'tcx>,
1357 right: &ImmTy<'tcx>,
1358 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1359 ecx.binary_ptr_op(bin_op, left, right)
1360 }
1361
1362 fn atomic_load(
1363 ecx: &MiriInterpCx<'tcx>,
1364 place: &MPlaceTy<'tcx>,
1365 ordering: AtomicOrdering,
1366 ) -> InterpResult<'tcx, Scalar> {
1367 ecx.read_scalar_atomic(place, AtomicReadOrd::from(ordering))
1368 }
1369
1370 fn atomic_store(
1371 ecx: &mut MiriInterpCx<'tcx>,
1372 place: &MPlaceTy<'tcx>,
1373 val: &ImmTy<'tcx>,
1374 ordering: AtomicOrdering,
1375 ) -> InterpResult<'tcx> {
1376 ecx.write_scalar_atomic(val.to_scalar(), place, AtomicWriteOrd::from(ordering))
1377 }
1378
1379 fn atomic_rmw(
1380 ecx: &mut MiriInterpCx<'tcx>,
1381 place: &MPlaceTy<'tcx>,
1382 op: AtomicRmwOp,
1383 operand: &ImmTy<'tcx>,
1384 ordering: AtomicOrdering,
1385 ) -> InterpResult<'tcx, Scalar> {
1386 ecx.atomic_rmw(place, operand, op, AtomicRwOrd::from(ordering))
1387 }
1388
1389 fn atomic_compare_exchange(
1390 ecx: &mut MiriInterpCx<'tcx>,
1391 place: &MPlaceTy<'tcx>,
1392 expected_old: &ImmTy<'tcx>,
1393 new: &ImmTy<'tcx>,
1394 can_fail_spuriously: bool,
1395 success_ordering: AtomicOrdering,
1396 failure_ordering: AtomicOrdering,
1397 ) -> InterpResult<'tcx, (Scalar, bool)> {
1398 ecx.atomic_compare_exchange(
1399 place,
1400 expected_old,
1401 new.to_scalar(),
1402 AtomicRwOrd::from(success_ordering),
1403 AtomicReadOrd::from(failure_ordering),
1404 can_fail_spuriously,
1405 )
1406 }
1407
1408 fn atomic_fence(
1409 ecx: &MiriInterpCx<'tcx>,
1410 ordering: AtomicOrdering,
1411 singlethread: bool,
1412 ) -> InterpResult<'tcx> {
1413 if singlethread {
1414 return interp_ok(());
1416 }
1417 ecx.atomic_fence(AtomicFenceOrd::from(ordering))
1418 }
1419
1420 #[inline(always)]
1421 fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(
1422 ecx: &InterpCx<'tcx, Self>,
1423 inputs: &[F1],
1424 ) -> F2 {
1425 ecx.generate_nan(inputs)
1426 }
1427
1428 #[inline(always)]
1429 fn apply_float_nondet(
1430 ecx: &mut InterpCx<'tcx, Self>,
1431 val: ImmTy<'tcx>,
1432 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1433 crate::math::apply_random_float_error_to_imm(ecx, val, 4)
1434 }
1435
1436 #[inline(always)]
1437 fn equal_float_min_max<F: Float>(ecx: &MiriInterpCx<'tcx>, a: F, b: F) -> F {
1438 ecx.equal_float_min_max(a, b)
1439 }
1440
1441 #[inline(always)]
1442 fn float_fuse_mul_add(ecx: &InterpCx<'tcx, Self>) -> bool {
1443 ecx.machine.float_nondet && ecx.machine.rng.borrow_mut().random()
1444 }
1445
1446 #[inline(always)]
1447 fn runtime_checks(
1448 ecx: &InterpCx<'tcx, Self>,
1449 r: mir::RuntimeChecks,
1450 ) -> InterpResult<'tcx, bool> {
1451 interp_ok(r.value(ecx.tcx.sess))
1452 }
1453
1454 #[inline(always)]
1455 fn thread_local_static_pointer(
1456 ecx: &mut MiriInterpCx<'tcx>,
1457 def_id: DefId,
1458 ) -> InterpResult<'tcx, StrictPointer> {
1459 ecx.get_or_create_thread_local_alloc(def_id)
1460 }
1461
1462 fn extern_static_pointer(
1463 ecx: &MiriInterpCx<'tcx>,
1464 def_id: DefId,
1465 ) -> InterpResult<'tcx, StrictPointer> {
1466 let link_name = Symbol::intern(ecx.tcx.symbol_name(Instance::mono(*ecx.tcx, def_id)).name);
1467 let def_ty = ecx.tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
1468 let extern_decl_layout =
1469 ecx.tcx.layout_of(ecx.typing_env().as_query_input(def_ty)).unwrap();
1470
1471 let ptr = match ecx.tcx.codegen_fn_attrs(def_id).import_linkage {
1474 None => ecx.machine.extern_statics.get(&link_name),
1475 Some(_) => ecx.machine.extern_statics_imports.get(&link_name),
1476 };
1477 if let Some(&ptr) = ptr {
1478 ecx.check_shim_symbol_clash(link_name)?;
1479 let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
1483 panic!("extern_statics cannot contain wildcards")
1484 };
1485 let info = ecx.get_alloc_info(alloc_id);
1486 if extern_decl_layout.size > info.size || extern_decl_layout.align > info.align {
1487 throw_ub_format!(
1488 "extern static `{link_name}` has been declared as `{krate}::{name}` \
1489 with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
1490 but Miri emulates it via an extern static shim \
1491 with a size of {shim_size} bytes and alignment of {shim_align} bytes",
1492 name = ecx.tcx.def_path_str(def_id),
1493 krate = ecx.tcx.crate_name(def_id.krate),
1494 decl_size = extern_decl_layout.size.bytes(),
1495 decl_align = extern_decl_layout.align.bytes(),
1496 shim_size = info.size.bytes(),
1497 shim_align = info.align.bytes(),
1498 )
1499 }
1500 interp_ok(ptr)
1501 } else if ecx.tcx.codegen_fn_attrs(def_id).import_linkage == Some(Linkage::ExternalWeak) {
1502 assert_eq!(
1510 extern_decl_layout.size,
1511 ecx.tcx.data_layout.pointer_size(),
1512 "non-pointer-sized weak static"
1513 );
1514 interp_ok(
1515 ecx.machine
1516 .extern_static_weak_import_default
1517 .expect("`missing_weak_symbol` should have been initialized"),
1518 )
1519 } else {
1520 let Some(instance) = ecx.lookup_exported_static(link_name)? else {
1522 throw_unsup_format!("extern static `{link_name}` is not supported by Miri");
1523 };
1524 let place = ecx.eval_global(instance)?;
1526 let static_ptr = place.ptr().into_pointer_or_addr().unwrap();
1527 let alloc_id = static_ptr.provenance.get_alloc_id().unwrap();
1529 let info = ecx.get_alloc_info(alloc_id);
1530 if extern_decl_layout.size > info.size || extern_decl_layout.align > info.align {
1531 throw_ub_format!(
1532 "extern static `{link_name}` has been declared as `{krate}::{name}` \
1533 with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
1534 but the exported static with that name has a size of {shim_size} bytes and \
1535 alignment of {shim_align} bytes",
1536 name = ecx.tcx.def_path_str(def_id),
1537 krate = ecx.tcx.crate_name(def_id.krate),
1538 decl_size = extern_decl_layout.size.bytes(),
1539 decl_align = extern_decl_layout.align.bytes(),
1540 shim_size = info.size.bytes(),
1541 shim_align = info.align.bytes(),
1542 )
1543 }
1544 let DefKind::Static { mutability, .. } = ecx.tcx.def_kind(def_id) else {
1553 unreachable!("`{def_id:?}` is not a static");
1554 };
1555 let decl_is_mut =
1556 !(mutability == Mutability::Not && ecx.type_is_freeze(extern_decl_layout.ty));
1557 let backing_is_mut = ecx.get_alloc_mutability(alloc_id)? == Mutability::Mut;
1558 if !decl_is_mut && backing_is_mut {
1559 throw_ub_format!(
1560 "extern static `{krate}::{name}` is declared as an immutable `static`, \
1561 but the backing static is mutable",
1562 name = ecx.tcx.def_path_str(def_id),
1563 krate = ecx.tcx.crate_name(def_id.krate),
1564 )
1565 }
1566 if decl_is_mut && !backing_is_mut {
1567 throw_ub_format!(
1568 "extern static `{krate}::{name}` is declared as an mutable `static`, \
1569 but the backing static is immutable",
1570 name = ecx.tcx.def_path_str(def_id),
1571 krate = ecx.tcx.crate_name(def_id.krate),
1572 )
1573 }
1574 interp_ok(static_ptr)
1575 }
1576 }
1577
1578 fn init_local_allocation(
1579 ecx: &MiriInterpCx<'tcx>,
1580 id: AllocId,
1581 kind: MemoryKind,
1582 size: Size,
1583 align: Align,
1584 ) -> InterpResult<'tcx, Self::AllocExtra> {
1585 assert!(kind != MiriMemoryKind::Global.into());
1586 MiriMachine::init_allocation(ecx, id, kind, size, align)
1587 }
1588
1589 fn adjust_alloc_root_pointer(
1590 ecx: &MiriInterpCx<'tcx>,
1591 ptr: interpret::Pointer<CtfeProvenance>,
1592 kind: Option<MemoryKind>,
1593 ) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
1594 let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None");
1595 let alloc_id = ptr.provenance.alloc_id();
1596 if cfg!(debug_assertions) {
1597 match ecx.tcx.try_get_global_alloc(alloc_id) {
1599 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
1600 panic!("adjust_alloc_root_pointer called on thread-local static")
1601 }
1602 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => {
1603 panic!("adjust_alloc_root_pointer called on extern static")
1604 }
1605 _ => {}
1606 }
1607 }
1608 let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
1610 borrow_tracker.borrow_mut().root_ptr_tag(alloc_id, &ecx.machine)
1611 } else {
1612 BorTag::default()
1614 };
1615 ecx.adjust_alloc_root_pointer(ptr, tag, kind)
1616 }
1617
1618 #[inline(always)]
1620 fn ptr_from_addr_cast(ecx: &MiriInterpCx<'tcx>, addr: u64) -> InterpResult<'tcx, Pointer> {
1621 ecx.ptr_from_addr_cast(addr)
1622 }
1623
1624 #[inline(always)]
1628 fn expose_provenance(
1629 ecx: &InterpCx<'tcx, Self>,
1630 provenance: Self::Provenance,
1631 ) -> InterpResult<'tcx> {
1632 ecx.expose_provenance(provenance)
1633 }
1634
1635 fn ptr_get_alloc(
1647 ecx: &MiriInterpCx<'tcx>,
1648 ptr: StrictPointer,
1649 size: i64,
1650 ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
1651 let rel = ecx.ptr_get_alloc(ptr, size);
1652
1653 rel.map(|(alloc_id, size)| {
1654 let tag = match ptr.provenance {
1655 Provenance::Concrete { tag, .. } => ProvenanceExtra::Concrete(tag),
1656 Provenance::Wildcard => ProvenanceExtra::Wildcard,
1657 };
1658 (alloc_id, size, tag)
1659 })
1660 }
1661
1662 fn adjust_global_allocation<'b>(
1671 ecx: &InterpCx<'tcx, Self>,
1672 id: AllocId,
1673 alloc: &'b Allocation,
1674 ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
1675 {
1676 let alloc = alloc.adjust_from_tcx(
1677 &ecx.tcx,
1678 |bytes, align| ecx.get_global_alloc_bytes(id, bytes, align),
1679 |ptr| ecx.global_root_pointer(ptr),
1680 )?;
1681 let kind = MiriMemoryKind::Global.into();
1682 let extra = MiriMachine::init_allocation(ecx, id, kind, alloc.size(), alloc.align)?;
1683 interp_ok(Cow::Owned(alloc.with_extra(extra)))
1684 }
1685
1686 #[inline(always)]
1687 fn before_memory_read(
1688 _tcx: TyCtxtAt<'tcx>,
1689 machine: &Self,
1690 alloc_extra: &AllocExtra<'tcx>,
1691 ptr: Pointer,
1692 (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1693 range: AllocRange,
1694 ) -> InterpResult<'tcx> {
1695 if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1696 machine.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(
1697 alloc_id,
1698 range,
1699 borrow_tracker::AccessKind::Read,
1700 ));
1701 }
1702 match &machine.data_race {
1704 GlobalDataRaceHandler::None => {}
1705 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1706 genmc_ctx.memory_load(machine, ptr.addr(), range.size)?,
1707 GlobalDataRaceHandler::Vclocks(_data_race) => {
1708 let _trace = enter_trace_span!(data_race::before_memory_read);
1709 let AllocDataRaceHandler::Vclocks(data_race, _weak_memory) = &alloc_extra.data_race
1710 else {
1711 unreachable!();
1712 };
1713 data_race.read_non_atomic(alloc_id, range, NaReadType::Read, None, machine)?;
1714 }
1715 }
1716 if let Some(borrow_tracker) = &alloc_extra.borrow_tracker {
1717 borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?;
1718 }
1719 for obj in alloc_extra.meta_objs.range(range) {
1721 obj.on_access()?;
1722 }
1723
1724 interp_ok(())
1725 }
1726
1727 #[inline(always)]
1728 fn before_memory_write(
1729 _tcx: TyCtxtAt<'tcx>,
1730 machine: &mut Self,
1731 alloc_extra: &mut AllocExtra<'tcx>,
1732 ptr: Pointer,
1733 (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1734 range: AllocRange,
1735 ) -> InterpResult<'tcx> {
1736 if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1737 machine.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(
1738 alloc_id,
1739 range,
1740 borrow_tracker::AccessKind::Write,
1741 ));
1742 }
1743 match &machine.data_race {
1744 GlobalDataRaceHandler::None => {}
1745 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1746 genmc_ctx.memory_store(machine, ptr.addr(), range.size)?,
1747 GlobalDataRaceHandler::Vclocks(_global_state) => {
1748 let _trace = enter_trace_span!(data_race::before_memory_write);
1749 let AllocDataRaceHandler::Vclocks(data_race, weak_memory) =
1750 &mut alloc_extra.data_race
1751 else {
1752 unreachable!()
1753 };
1754 data_race.write_non_atomic(alloc_id, range, NaWriteType::Write, None, machine)?;
1755 if let Some(weak_memory) = weak_memory {
1756 weak_memory
1757 .non_atomic_write(range, machine.data_race.as_vclocks_ref().unwrap());
1758 }
1759 }
1760 }
1761 if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1762 borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?;
1763 }
1764 if !alloc_extra.meta_objs.is_empty() {
1767 for obj in alloc_extra.meta_objs.range(range) {
1769 obj.on_access()?;
1770 }
1771 alloc_extra.meta_objs.remove_range_if(range, |obj| obj.opaque());
1773 }
1774 interp_ok(())
1775 }
1776
1777 #[inline(always)]
1778 fn before_memory_deallocation(
1779 _tcx: TyCtxtAt<'tcx>,
1780 machine: &mut Self,
1781 alloc_extra: &mut AllocExtra<'tcx>,
1782 ptr: Pointer,
1783 (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
1784 size: Size,
1785 align: Align,
1786 kind: MemoryKind,
1787 ) -> InterpResult<'tcx> {
1788 if machine.tracked_alloc_ids.contains(&alloc_id) {
1789 machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
1790 }
1791 match &machine.data_race {
1792 GlobalDataRaceHandler::None => {}
1793 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1794 genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), kind)?,
1795 GlobalDataRaceHandler::Vclocks(_global_state) => {
1796 let _trace = enter_trace_span!(data_race::before_memory_deallocation);
1797 let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap();
1798 data_race.write_non_atomic(
1799 alloc_id,
1800 alloc_range(Size::ZERO, size),
1801 NaWriteType::Deallocate,
1802 None,
1803 machine,
1804 )?;
1805 }
1806 }
1807 if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1808 borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?;
1809 }
1810 for obj in alloc_extra.meta_objs.range(AllocRange { start: Size::ZERO, size }) {
1812 obj.on_access()?;
1813 }
1814
1815 if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
1816 {
1817 *deallocated_at = Some(machine.current_user_relevant_span());
1818 }
1819 machine.free_alloc_id(alloc_id, size, align, kind);
1820 interp_ok(())
1821 }
1822
1823 #[inline(always)]
1824 fn retag_ptr_value(
1825 ecx: &mut InterpCx<'tcx, Self>,
1826 val: &ImmTy<'tcx>,
1827 ty: Ty<'tcx>,
1828 ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
1829 if ecx.machine.borrow_tracker.is_some() {
1830 ecx.retag_ptr_value(val, ty)
1831 } else {
1832 interp_ok(None)
1833 }
1834 }
1835
1836 #[inline(always)]
1837 fn with_retag_mode<T>(
1838 ecx: &mut InterpCx<'tcx, Self>,
1839 mode: RetagMode,
1840 f: impl FnOnce(&mut InterpCx<'tcx, Self>) -> InterpResult<'tcx, T>,
1841 ) -> InterpResult<'tcx, T> {
1842 if ecx.machine.borrow_tracker.is_some() { ecx.with_retag_mode(mode, f) } else { f(ecx) }
1843 }
1844
1845 fn protect_in_place_function_argument(
1846 ecx: &mut InterpCx<'tcx, Self>,
1847 place: &MPlaceTy<'tcx>,
1848 ) -> InterpResult<'tcx> {
1849 let protected_place = if ecx.machine.borrow_tracker.is_some() {
1852 ecx.protect_place(place)?
1853 } else {
1854 place.clone()
1856 };
1857 ecx.write_uninit(&protected_place)?;
1862 interp_ok(())
1864 }
1865
1866 #[inline(always)]
1867 fn init_frame(
1868 ecx: &mut InterpCx<'tcx, Self>,
1869 frame: Frame<'tcx, Provenance>,
1870 ) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
1871 let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
1873 let fn_name = frame.instance().to_string();
1874 let entry = ecx.machine.string_cache.entry(fn_name.clone());
1875 let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
1876
1877 Some(profiler.start_recording_interval_event_detached(
1878 *name,
1879 measureme::EventId::from_label(*name),
1880 ecx.active_thread().to_u32(),
1881 ))
1882 } else {
1883 None
1884 };
1885
1886 let borrow_tracker = ecx.machine.borrow_tracker.as_ref();
1887
1888 let extra = FrameExtra {
1889 borrow_tracker: borrow_tracker.map(|bt| bt.borrow_mut().new_frame()),
1890 catch_unwind: None,
1891 timing,
1892 user_relevance: ecx.machine.user_relevance(&frame),
1893 data_race: ecx
1894 .machine
1895 .data_race
1896 .as_vclocks_ref()
1897 .map(|_| data_race::FrameState::default()),
1898 };
1899
1900 interp_ok(frame.with_extra(extra))
1901 }
1902
1903 fn stack<'a>(
1904 ecx: &'a InterpCx<'tcx, Self>,
1905 ) -> &'a [Frame<'tcx, Self::Provenance, Self::FrameExtra>] {
1906 ecx.active_thread_stack()
1907 }
1908
1909 fn stack_mut<'a>(
1910 ecx: &'a mut InterpCx<'tcx, Self>,
1911 ) -> &'a mut Vec<Frame<'tcx, Self::Provenance, Self::FrameExtra>> {
1912 ecx.active_thread_stack_mut()
1913 }
1914
1915 fn before_terminator(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1916 ecx.machine.basic_block_count += 1u64; ecx.machine.since_gc += 1;
1918 if let Some(report_progress) = ecx.machine.report_progress {
1920 if ecx.machine.basic_block_count.is_multiple_of(u64::from(report_progress)) {
1921 ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport {
1922 block_count: ecx.machine.basic_block_count,
1923 });
1924 }
1925 }
1926
1927 if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
1933 ecx.machine.since_gc = 0;
1934 ecx.run_provenance_gc();
1935 ecx.machine.blocking_io.run_gc();
1936 }
1937
1938 ecx.maybe_preempt_active_thread();
1941
1942 ecx.machine.monotonic_clock.tick();
1944
1945 interp_ok(())
1946 }
1947
1948 #[inline(always)]
1949 fn after_stack_push(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1950 if ecx.frame().extra.user_relevance >= ecx.active_thread_ref().current_user_relevance() {
1951 let stack_len = ecx.active_thread_stack().len();
1954 ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
1955 }
1956 interp_ok(())
1957 }
1958
1959 fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1960 let frame = ecx.frame();
1961 if ecx.machine.borrow_tracker.is_some() {
1964 ecx.on_stack_pop(frame)?;
1965 }
1966 if ecx
1967 .active_thread_ref()
1968 .top_user_relevant_frame()
1969 .expect("there should always be a most relevant frame for a non-empty stack")
1970 == ecx.frame_idx()
1971 {
1972 ecx.active_thread_mut().recompute_top_user_relevant_frame(1);
1978 }
1979 info!("Leaving {}", ecx.frame().instance());
1983 interp_ok(())
1984 }
1985
1986 #[inline(always)]
1987 fn after_stack_pop(
1988 ecx: &mut InterpCx<'tcx, Self>,
1989 frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1990 unwinding: bool,
1991 ) -> InterpResult<'tcx, ReturnAction> {
1992 let res = {
1993 let mut frame = frame;
1995 let timing = frame.extra.timing.take();
1996 let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
1997 if let Some(profiler) = ecx.machine.profiler.as_ref() {
1998 profiler.finish_recording_interval_event(timing.unwrap());
1999 }
2000 res
2001 };
2002 if !ecx.active_thread_stack().is_empty() {
2005 info!("Continuing in {}", ecx.frame().instance());
2006 }
2007 res
2008 }
2009
2010 fn after_local_read(ecx: &InterpCx<'tcx, Self>, local: mir::Local) -> InterpResult<'tcx> {
2011 if let Some(data_race) = &ecx.frame().extra.data_race {
2012 let _trace = enter_trace_span!(data_race::after_local_read);
2013 data_race.local_read(local, &ecx.machine);
2014 }
2015 interp_ok(())
2016 }
2017
2018 fn after_local_write(
2019 ecx: &mut InterpCx<'tcx, Self>,
2020 local: mir::Local,
2021 storage_live: bool,
2022 ) -> InterpResult<'tcx> {
2023 if let Some(data_race) = &ecx.frame().extra.data_race {
2024 let _trace = enter_trace_span!(data_race::after_local_write);
2025 data_race.local_write(local, storage_live, &ecx.machine);
2026 }
2027 interp_ok(())
2028 }
2029
2030 fn after_local_moved_to_memory(
2031 ecx: &mut InterpCx<'tcx, Self>,
2032 local: mir::Local,
2033 mplace: &MPlaceTy<'tcx>,
2034 ) -> InterpResult<'tcx> {
2035 let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else {
2036 panic!("after_local_allocated should only be called on fresh allocations");
2037 };
2038 let local_decl = &ecx.frame().body().local_decls[local];
2040 let span = local_decl.source_info.span;
2041 ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
2042 let (alloc_info, machine) = ecx.get_alloc_extra_mut(alloc_id)?;
2044 if let Some(data_race) =
2045 &machine.threads.active_thread_stack().last().unwrap().extra.data_race
2046 {
2047 let _trace = enter_trace_span!(data_race::after_local_moved_to_memory);
2048 data_race.local_moved_to_memory(
2049 local,
2050 alloc_info.data_race.as_vclocks_mut().unwrap(),
2051 machine,
2052 );
2053 }
2054 interp_ok(())
2055 }
2056
2057 fn get_global_alloc_salt(
2058 ecx: &InterpCx<'tcx, Self>,
2059 instance: Option<ty::Instance<'tcx>>,
2060 ) -> usize {
2061 let unique = if let Some(instance) = instance {
2062 let is_generic = instance
2075 .args
2076 .into_iter()
2077 .any(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_)));
2078 let can_be_inlined = matches!(
2079 ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
2080 InliningThreshold::Always
2081 ) || !matches!(
2082 ecx.tcx.codegen_instance_attrs(instance.def).inline,
2083 InlineAttr::Never
2084 );
2085 !is_generic && !can_be_inlined
2086 } else {
2087 false
2089 };
2090 if unique {
2092 CTFE_ALLOC_SALT
2093 } else {
2094 ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL)
2095 }
2096 }
2097
2098 fn cached_union_data_range<'e>(
2099 ecx: &'e mut InterpCx<'tcx, Self>,
2100 ty: Ty<'tcx>,
2101 compute_range: impl FnOnce() -> RangeSet,
2102 ) -> Cow<'e, RangeSet> {
2103 Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range))
2104 }
2105
2106 fn get_default_alloc_params(&self) -> <Self::Bytes as AllocBytes>::AllocParams {
2107 use crate::alloc::MiriAllocParams;
2108
2109 match &self.allocator {
2110 Some(alloc) => MiriAllocParams::Isolated(alloc.clone()),
2111 None => MiriAllocParams::Global,
2112 }
2113 }
2114
2115 fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan {
2116 #[cfg(feature = "tracing")]
2117 {
2118 span().entered()
2119 }
2120 #[cfg(not(feature = "tracing"))]
2121 #[expect(clippy::unused_unit)]
2122 {
2123 let _ = span; ()
2125 }
2126 }
2127}
2128
2129pub trait MachineCallback<'tcx, T>: VisitProvenance {
2131 fn call(
2133 self: Box<Self>,
2134 ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
2135 arg: T,
2136 ) -> InterpResult<'tcx>;
2137}
2138
2139pub type DynMachineCallback<'tcx, T> = Box<dyn MachineCallback<'tcx, T> + 'tcx>;
2141
2142#[macro_export]
2159macro_rules! callback {
2160 (@capture<$tcx:lifetime $(,)? $($lft:lifetime),*>
2161 { $($name:ident: $type:ty),* $(,)? }
2162 |$ecx:ident, $arg:ident: $arg_ty:ty| $body:expr $(,)?) => {{
2163 struct Callback<$tcx, $($lft),*> {
2164 $($name: $type,)*
2165 _phantom: std::marker::PhantomData<&$tcx ()>,
2166 }
2167
2168 impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> {
2169 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
2170 $(
2171 VisitProvenance::visit_provenance(&self.$name, _visit);
2172 )*
2173 }
2174 }
2175
2176 impl<$tcx, $($lft),*> MachineCallback<$tcx, $arg_ty> for Callback<$tcx, $($lft),*> {
2177 fn call(
2178 self: Box<Self>,
2179 $ecx: &mut MiriInterpCx<$tcx>,
2180 $arg: $arg_ty
2181 ) -> InterpResult<$tcx> {
2182 #[allow(unused_variables)]
2183 let Callback { $($name,)* _phantom } = *self;
2184 $body
2185 }
2186 }
2187
2188 Box::new(Callback {
2189 $($name,)*
2190 _phantom: std::marker::PhantomData
2191 })
2192 }};
2193}