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