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::expand::allocator::{self, SpecialAllocatorMethod};
16use rustc_data_structures::either::Either;
17use rustc_data_structures::fx::{FxHashMap, FxHashSet};
18#[allow(unused)]
19use rustc_data_structures::static_assert_size;
20use rustc_hir::attrs::{InlineAttr, Linkage};
21use rustc_log::tracing;
22use rustc_middle::middle::codegen_fn_attrs::TargetFeatureKind;
23use rustc_middle::mir;
24use rustc_middle::query::TyCtxtAt;
25use rustc_middle::ty::layout::{
26 HasTyCtxt, HasTypingEnv, LayoutCx, LayoutError, LayoutOf, TyAndLayout,
27};
28use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
29use rustc_session::config::InliningThreshold;
30use rustc_span::def_id::{CrateNum, DefId};
31use rustc_span::{Span, SpanData, Symbol};
32use rustc_symbol_mangling::mangle_internal_symbol;
33use rustc_target::callconv::FnAbi;
34use rustc_target::spec::{Arch, Os};
35
36use crate::alloc_addresses::EvalContextExt;
37use crate::concurrency::cpu_affinity::{self, CpuAffinityMask};
38use crate::concurrency::data_race::{self, NaReadType, NaWriteType};
39use crate::concurrency::sync::SyncObj;
40use crate::concurrency::{
41 AllocDataRaceHandler, GenmcCtx, GenmcEvalContextExt as _, GlobalDataRaceHandler, weak_memory,
42};
43use crate::helpers::is_no_core;
44use crate::shims::readiness::DelayedReadinessUpdates;
45use crate::*;
46
47pub const SIGRTMIN: i32 = 34;
51
52pub const SIGRTMAX: i32 = 42;
56
57const ADDRS_PER_ANON_GLOBAL: usize = 32;
61
62#[derive(Copy, Clone, Debug, PartialEq)]
63pub enum AlignmentCheck {
64 None,
66 Symbolic,
68 Int,
70}
71
72#[derive(Copy, Clone, Debug, PartialEq)]
73pub enum RejectOpWith {
74 Abort,
76
77 NoWarning,
81
82 Warning,
84
85 WarningWithoutBacktrace,
87}
88
89#[derive(Copy, Clone, Debug, PartialEq)]
90pub enum IsolatedOp {
91 Reject(RejectOpWith),
96
97 Allow,
99}
100
101#[derive(Debug, Copy, Clone, PartialEq, Eq)]
102pub enum BacktraceStyle {
103 Short,
105 Full,
107 Off,
109}
110
111#[derive(Debug, Copy, Clone, PartialEq, Eq)]
112pub enum ValidationMode {
113 No,
115 Shallow,
117 Deep,
119}
120
121#[derive(Debug, Copy, Clone, PartialEq, Eq)]
122pub enum FloatRoundingErrorMode {
123 Random,
125 None,
127 Max,
129}
130
131pub struct FrameExtra<'tcx> {
133 pub borrow_tracker: Option<borrow_tracker::FrameState>,
135
136 pub catch_unwind: Option<CatchUnwindData<'tcx>>,
140
141 pub timing: Option<measureme::DetachedTiming>,
145
146 pub user_relevance: u8,
150
151 pub data_race: Option<data_race::FrameState>,
153}
154
155impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 let FrameExtra { borrow_tracker, catch_unwind, timing: _, user_relevance, data_race } =
159 self;
160 f.debug_struct("FrameData")
161 .field("borrow_tracker", borrow_tracker)
162 .field("catch_unwind", catch_unwind)
163 .field("user_relevance", user_relevance)
164 .field("data_race", data_race)
165 .finish()
166 }
167}
168
169impl VisitProvenance for FrameExtra<'_> {
170 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
171 let FrameExtra { catch_unwind, borrow_tracker, timing: _, user_relevance: _, data_race: _ } =
172 self;
173
174 catch_unwind.visit_provenance(visit);
175 borrow_tracker.visit_provenance(visit);
176 }
177}
178
179#[derive(Debug, Copy, Clone, PartialEq, Eq)]
181pub enum MiriMemoryKind {
182 Rust,
184 Miri,
186 C,
188 WinHeap,
190 WinLocal,
192 Machine,
195 Runtime,
198 Global,
201 ExternStatic,
204 Tls,
207 Mmap,
209 SocketAddress,
211}
212
213impl From<MiriMemoryKind> for MemoryKind {
214 #[inline(always)]
215 fn from(kind: MiriMemoryKind) -> MemoryKind {
216 MemoryKind::Machine(kind)
217 }
218}
219
220impl MayLeak for MiriMemoryKind {
221 #[inline(always)]
222 fn may_leak(self) -> bool {
223 use self::MiriMemoryKind::*;
224 match self {
225 Rust | Miri | C | WinHeap | WinLocal | Runtime => false,
226 Machine | Global | ExternStatic | Tls | Mmap | SocketAddress => true,
227 }
228 }
229}
230
231impl MiriMemoryKind {
232 fn should_save_allocation_span(self) -> bool {
234 use self::MiriMemoryKind::*;
235 match self {
236 Rust | Miri | C | WinHeap | WinLocal | Mmap => true,
238 Machine | Global | ExternStatic | Tls | Runtime | SocketAddress => false,
240 }
241 }
242}
243
244impl fmt::Display for MiriMemoryKind {
245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246 use self::MiriMemoryKind::*;
247 match self {
248 Rust => write!(f, "Rust heap"),
249 Miri => write!(f, "Miri bare-metal heap"),
250 C => write!(f, "C heap"),
251 WinHeap => write!(f, "Windows heap"),
252 WinLocal => write!(f, "Windows local memory"),
253 Machine => write!(f, "machine-managed memory"),
254 Runtime => write!(f, "language runtime memory"),
255 Global => write!(f, "global (static or const)"),
256 ExternStatic => write!(f, "extern static"),
257 Tls => write!(f, "thread-local static"),
258 Mmap => write!(f, "mmap"),
259 SocketAddress => write!(f, "socket address"),
260 }
261 }
262}
263
264pub type MemoryKind = interpret::MemoryKind<MiriMemoryKind>;
265
266#[derive(Clone, Copy, PartialEq, Eq, Hash)]
272pub enum Provenance {
273 Concrete {
276 alloc_id: AllocId,
277 tag: BorTag,
279 },
280 Wildcard,
297}
298
299#[derive(Copy, Clone, PartialEq)]
301pub enum ProvenanceExtra {
302 Concrete(BorTag),
303 Wildcard,
304}
305
306#[cfg(target_pointer_width = "64")]
307static_assert_size!(StrictPointer, 24);
308#[cfg(target_pointer_width = "64")]
313static_assert_size!(Scalar, 32);
314
315impl fmt::Debug for Provenance {
316 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317 match self {
318 Provenance::Concrete { alloc_id, tag } => {
319 if f.alternate() {
321 write!(f, "[{alloc_id:#?}]")?;
322 } else {
323 write!(f, "[{alloc_id:?}]")?;
324 }
325 write!(f, "{tag:?}")?;
327 }
328 Provenance::Wildcard => {
329 write!(f, "[wildcard]")?;
330 }
331 }
332 Ok(())
333 }
334}
335
336impl interpret::Provenance for Provenance {
337 const OFFSET_IS_ADDR: bool = true;
339
340 const WILDCARD: Option<Self> = Some(Provenance::Wildcard);
342
343 fn get_alloc_id(self) -> Option<AllocId> {
344 match self {
345 Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
346 Provenance::Wildcard => None,
347 }
348 }
349
350 fn fmt(ptr: &interpret::Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351 let (prov, addr) = ptr.into_raw_parts(); write!(f, "{:#x}", addr.bytes())?;
353 if f.alternate() {
354 write!(f, "{prov:#?}")?;
355 } else {
356 write!(f, "{prov:?}")?;
357 }
358 Ok(())
359 }
360}
361
362impl fmt::Debug for ProvenanceExtra {
363 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364 match self {
365 ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"),
366 ProvenanceExtra::Wildcard => write!(f, "<wildcard>"),
367 }
368 }
369}
370
371impl ProvenanceExtra {
372 pub fn and_then<T>(self, f: impl FnOnce(BorTag) -> Option<T>) -> Option<T> {
373 match self {
374 ProvenanceExtra::Concrete(pid) => f(pid),
375 ProvenanceExtra::Wildcard => None,
376 }
377 }
378}
379
380#[derive(Debug)]
382pub struct AllocExtra<'tcx> {
383 pub borrow_tracker: Option<borrow_tracker::AllocState>,
385 pub data_race: AllocDataRaceHandler,
389 pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
394 pub sync_objs: BTreeMap<Size, Box<dyn SyncObj>>,
399}
400
401impl<'tcx> Clone for AllocExtra<'tcx> {
404 fn clone(&self) -> Self {
405 panic!("our allocations should never be cloned");
406 }
407}
408
409impl VisitProvenance for AllocExtra<'_> {
410 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
411 let AllocExtra { borrow_tracker, data_race, backtrace: _, sync_objs: _ } = self;
412
413 borrow_tracker.visit_provenance(visit);
414 data_race.visit_provenance(visit);
415 }
416}
417
418pub struct PrimitiveLayouts<'tcx> {
420 pub unit: TyAndLayout<'tcx>,
421 pub i8: TyAndLayout<'tcx>,
422 pub i16: TyAndLayout<'tcx>,
423 pub i32: TyAndLayout<'tcx>,
424 pub i64: TyAndLayout<'tcx>,
425 pub i128: TyAndLayout<'tcx>,
426 pub isize: TyAndLayout<'tcx>,
427 pub u8: TyAndLayout<'tcx>,
428 pub u16: TyAndLayout<'tcx>,
429 pub u32: TyAndLayout<'tcx>,
430 pub u64: TyAndLayout<'tcx>,
431 pub u128: TyAndLayout<'tcx>,
432 pub usize: TyAndLayout<'tcx>,
433 pub bool: TyAndLayout<'tcx>,
434 pub mut_raw_ptr: TyAndLayout<'tcx>, pub const_raw_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 let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit);
442 let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
443 Ok(Self {
444 unit: layout_cx.layout_of(tcx.types.unit)?,
445 i8: layout_cx.layout_of(tcx.types.i8)?,
446 i16: layout_cx.layout_of(tcx.types.i16)?,
447 i32: layout_cx.layout_of(tcx.types.i32)?,
448 i64: layout_cx.layout_of(tcx.types.i64)?,
449 i128: layout_cx.layout_of(tcx.types.i128)?,
450 isize: layout_cx.layout_of(tcx.types.isize)?,
451 u8: layout_cx.layout_of(tcx.types.u8)?,
452 u16: layout_cx.layout_of(tcx.types.u16)?,
453 u32: layout_cx.layout_of(tcx.types.u32)?,
454 u64: layout_cx.layout_of(tcx.types.u64)?,
455 u128: layout_cx.layout_of(tcx.types.u128)?,
456 usize: layout_cx.layout_of(tcx.types.usize)?,
457 bool: layout_cx.layout_of(tcx.types.bool)?,
458 mut_raw_ptr: layout_cx.layout_of(mut_raw_ptr)?,
459 const_raw_ptr: layout_cx.layout_of(const_raw_ptr)?,
460 })
461 }
462
463 pub fn uint(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
464 match size.bits() {
465 8 => Some(self.u8),
466 16 => Some(self.u16),
467 32 => Some(self.u32),
468 64 => Some(self.u64),
469 128 => Some(self.u128),
470 _ => None,
471 }
472 }
473
474 pub fn int(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
475 match size.bits() {
476 8 => Some(self.i8),
477 16 => Some(self.i16),
478 32 => Some(self.i32),
479 64 => Some(self.i64),
480 128 => Some(self.i128),
481 _ => None,
482 }
483 }
484}
485
486pub struct MiriMachine<'tcx> {
491 pub tcx: TyCtxt<'tcx>,
493
494 pub borrow_tracker: Option<borrow_tracker::GlobalState>,
496
497 pub data_race: GlobalDataRaceHandler,
503
504 pub alloc_addresses: alloc_addresses::GlobalState,
506
507 pub(crate) env_vars: EnvVars<'tcx>,
509
510 pub(crate) main_fn_ret_place: Option<MPlaceTy<'tcx>>,
512
513 pub(crate) argc: Option<Pointer>,
517 pub(crate) argv: Option<Pointer>,
518 pub(crate) cmd_line: Option<Pointer>,
519
520 pub(crate) tls: TlsData<'tcx>,
522
523 pub(crate) isolated_op: IsolatedOp,
527
528 pub(crate) validation: ValidationMode,
530
531 pub(crate) fds: shims::FdTable,
533 pub(crate) dirs: shims::DirTable,
535
536 pub(crate) delayed_readiness_updates: Rc<DelayedReadinessUpdates>,
538
539 pub(crate) monotonic_clock: MonotonicClock,
541
542 pub(crate) threads: ThreadManager<'tcx>,
544
545 pub(crate) blocking_io: BlockingIoManager,
547
548 pub(crate) thread_cpu_affinity: Option<FxHashMap<ThreadId, CpuAffinityMask>>,
553
554 pub(crate) layouts: PrimitiveLayouts<'tcx>,
556
557 pub(crate) static_roots: Vec<AllocId>,
559
560 profiler: Option<measureme::Profiler>,
563 string_cache: FxHashMap<String, measureme::StringId>,
566
567 pub(crate) exported_symbols_cache: FxHashMap<Symbol, Option<Instance<'tcx>>>,
570
571 pub(crate) backtrace_style: BacktraceStyle,
573
574 pub(crate) user_relevant_crates: Vec<CrateNum>,
576
577 pub(crate) extern_statics: FxHashMap<Symbol, StrictPointer>,
579 pub(crate) extern_statics_imports: FxHashMap<Symbol, StrictPointer>,
582 pub(crate) extern_static_weak_import_default: Option<StrictPointer>,
584
585 pub(crate) rng: RefCell<StdRng>,
588
589 pub(crate) allocator: Option<Rc<RefCell<crate::alloc::isolated_alloc::IsolatedAlloc>>>,
591
592 pub(crate) tracked_alloc_ids: FxHashSet<AllocId>,
595 track_alloc_accesses: bool,
597
598 pub(crate) check_alignment: AlignmentCheck,
600
601 pub(crate) cmpxchg_weak_failure_rate: f64,
603
604 pub(crate) preemption_rate: f64,
606
607 pub(crate) report_progress: Option<u32>,
609 pub(crate) basic_block_count: u64,
611
612 #[cfg(all(feature = "native-lib", unix))]
614 pub native_lib: Vec<(libloading::Library, std::path::PathBuf)>,
615 #[cfg(not(all(feature = "native-lib", unix)))]
616 pub native_lib: Vec<!>,
617 #[cfg(all(feature = "native-lib", unix))]
619 pub native_lib_ecx_interchange: &'static Cell<usize>,
620
621 pub(crate) gc_interval: u32,
623 pub(crate) since_gc: u32,
625
626 pub(crate) num_cpus: u32,
628
629 pub(crate) page_size: u64,
631 pub(crate) stack_addr: u64,
632 pub(crate) stack_size: u64,
633
634 pub(crate) collect_leak_backtraces: bool,
636
637 pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>)>>,
640
641 pub(crate) symbolic_alignment: RefCell<FxHashMap<AllocId, (Size, Align)>>,
648
649 union_data_ranges: FxHashMap<Ty<'tcx>, RangeSet>,
651
652 pub(crate) pthread_mutex_sanity: Cell<bool>,
654 pub(crate) pthread_rwlock_sanity: Cell<bool>,
655 pub(crate) pthread_condvar_sanity: Cell<bool>,
656
657 pub(crate) allocator_shim_symbols: FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>>,
661 pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>,
663
664 pub float_nondet: bool,
666 pub float_rounding_error: FloatRoundingErrorMode,
668
669 pub short_fd_operations: bool,
671}
672
673impl<'tcx> MiriMachine<'tcx> {
674 pub(crate) fn new(
678 config: &MiriConfig,
679 layout_cx: LayoutCx<'tcx>,
680 genmc_ctx: Option<Rc<GenmcCtx>>,
681 ) -> Self {
682 let tcx = layout_cx.tcx();
683 let user_relevant_crates = Self::get_user_relevant_crates(tcx, config);
684 let layouts =
685 PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
686 let profiler = config.measureme_out.as_ref().map(|out| {
687 let crate_name =
688 tcx.sess.opts.crate_name.clone().unwrap_or_else(|| "unknown-crate".to_string());
689 let pid = process::id();
690 let filename = format!("{crate_name}-{pid:07}");
695 let path = Path::new(out).join(filename);
696 measureme::Profiler::new(path).expect("Couldn't create `measureme` profiler")
697 });
698 let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
699 let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config));
700 let data_race = if config.genmc_config.is_some() {
701 GlobalDataRaceHandler::Genmc(genmc_ctx.unwrap())
703 } else if config.data_race_detector {
704 GlobalDataRaceHandler::Vclocks(Box::new(data_race::GlobalState::new(config)))
705 } else {
706 GlobalDataRaceHandler::None
707 };
708 let page_size = if let Some(page_size) = config.page_size {
712 page_size
713 } else {
714 let target = &tcx.sess.target;
715 match target.arch {
716 Arch::Wasm32 | Arch::Wasm64 => 64 * 1024, Arch::AArch64 if target.is_like_darwin => {
718 16 * 1024
722 }
723 _ => 4 * 1024,
724 }
725 };
726 let stack_addr = if tcx.pointer_size().bits() < 32 { page_size } else { page_size * 32 };
728 let stack_size =
729 if tcx.pointer_size().bits() < 32 { page_size * 4 } else { page_size * 16 };
730 assert!(
731 usize::try_from(config.num_cpus).unwrap() <= cpu_affinity::MAX_CPUS,
732 "miri only supports up to {} CPUs, but {} were configured",
733 cpu_affinity::MAX_CPUS,
734 config.num_cpus
735 );
736 let threads = ThreadManager::new(config);
737 let thread_cpu_affinity =
738 if matches!(&tcx.sess.target.os, Os::Linux | Os::FreeBsd | Os::Android)
739 && !is_no_core(tcx)
740 {
741 let mut affinity = FxHashMap::default();
742 affinity.insert(
743 threads.active_thread(),
744 CpuAffinityMask::new(&layout_cx, config.num_cpus),
745 );
746 Some(affinity)
747 } else {
748 None
749 };
750 let blocking_io = BlockingIoManager::new(config.isolated_op == IsolatedOp::Allow)
751 .expect("Couldn't create poll instance");
752 let alloc_addresses =
753 RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr, tcx));
754
755 MiriMachine {
756 tcx,
757 borrow_tracker,
758 data_race,
759 alloc_addresses,
760 env_vars: EnvVars::default(),
762 main_fn_ret_place: None,
763 argc: None,
764 argv: None,
765 cmd_line: None,
766 tls: TlsData::default(),
767 isolated_op: config.isolated_op,
768 validation: config.validation,
769 fds: shims::FdTable::init(config.mute_stdout_stderr),
770 delayed_readiness_updates: Rc::new(DelayedReadinessUpdates::default()),
771 dirs: Default::default(),
772 layouts,
773 threads,
774 thread_cpu_affinity,
775 blocking_io,
776 static_roots: Vec::new(),
777 profiler,
778 string_cache: Default::default(),
779 exported_symbols_cache: FxHashMap::default(),
780 backtrace_style: config.backtrace_style,
781 user_relevant_crates,
782 extern_statics: FxHashMap::default(),
783 extern_statics_imports: FxHashMap::default(),
784 extern_static_weak_import_default: None,
785 rng: RefCell::new(rng),
786 allocator: (!config.native_lib.is_empty())
787 .then(|| Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new()))),
788 tracked_alloc_ids: config.tracked_alloc_ids.clone(),
789 track_alloc_accesses: config.track_alloc_accesses,
790 check_alignment: config.check_alignment,
791 cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
792 preemption_rate: config.preemption_rate,
793 report_progress: config.report_progress,
794 basic_block_count: 0,
795 monotonic_clock: MonotonicClock::new(config.isolated_op == IsolatedOp::Allow),
796 #[cfg(all(feature = "native-lib", unix))]
797 native_lib: config.native_lib.iter().map(|lib_file_path| {
798 let host_triple = rustc_session::config::host_tuple();
799 let target_triple = tcx.sess.opts.target_triple.tuple();
800 if host_triple != target_triple {
802 panic!(
803 "calling native C functions in linked .so file requires host and target to be the same: \
804 host={host_triple}, target={target_triple}",
805 );
806 }
807 (
811 unsafe {
812 libloading::Library::new(lib_file_path)
813 .expect("failed to read specified extern shared object file")
814 },
815 lib_file_path.clone(),
816 )
817 }).collect(),
818 #[cfg(all(feature = "native-lib", unix))]
819 native_lib_ecx_interchange: Box::leak(Box::new(Cell::new(0))),
820 #[cfg(not(all(feature = "native-lib", unix)))]
821 native_lib: config.native_lib.iter().map(|_| {
822 panic!("calling functions from native libraries via FFI is not supported in this build of Miri")
823 }).collect(),
824 gc_interval: config.gc_interval,
825 since_gc: 0,
826 num_cpus: config.num_cpus,
827 page_size,
828 stack_addr,
829 stack_size,
830 collect_leak_backtraces: config.collect_leak_backtraces,
831 allocation_spans: RefCell::new(FxHashMap::default()),
832 symbolic_alignment: RefCell::new(FxHashMap::default()),
833 union_data_ranges: FxHashMap::default(),
834 pthread_mutex_sanity: Cell::new(false),
835 pthread_rwlock_sanity: Cell::new(false),
836 pthread_condvar_sanity: Cell::new(false),
837 allocator_shim_symbols: Self::allocator_shim_symbols(tcx),
838 mangle_internal_symbol_cache: Default::default(),
839 float_nondet: config.float_nondet,
840 float_rounding_error: config.float_rounding_error,
841 short_fd_operations: config.short_fd_operations,
842 }
843 }
844
845 fn allocator_shim_symbols(
846 tcx: TyCtxt<'tcx>,
847 ) -> FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>> {
848 use rustc_codegen_ssa::base::allocator_shim_contents;
849
850 let Some(kind) = tcx.allocator_kind(()) else {
853 return Default::default();
854 };
855 let methods = allocator_shim_contents(tcx, kind);
856 let mut symbols = FxHashMap::default();
857 for method in methods {
858 let from_name = Symbol::intern(&mangle_internal_symbol(
859 tcx,
860 &allocator::global_fn_name(method.name),
861 ));
862 let to = match method.special {
863 Some(special) => Either::Right(special),
864 None =>
865 Either::Left(Symbol::intern(&mangle_internal_symbol(
866 tcx,
867 &allocator::default_fn_name(method.name),
868 ))),
869 };
870 symbols.try_insert(from_name, to).unwrap();
871 }
872 symbols
873 }
874
875 fn get_user_relevant_crates(tcx: TyCtxt<'_>, config: &MiriConfig) -> Vec<CrateNum> {
878 let local_crate_names = std::env::var("MIRI_LOCAL_CRATES")
881 .map(|crates| crates.split(',').map(|krate| krate.to_string()).collect::<Vec<_>>())
882 .unwrap_or_default();
883 let mut local_crates = Vec::new();
884 for &crate_num in tcx.crates(()) {
885 let name = tcx.crate_name(crate_num);
886 let name = name.as_str();
887 if local_crate_names
888 .iter()
889 .chain(&config.user_relevant_crates)
890 .any(|local_name| local_name == name)
891 {
892 local_crates.push(crate_num);
893 }
894 }
895 local_crates
896 }
897
898 pub(crate) fn late_init(
899 ecx: &mut MiriInterpCx<'tcx>,
900 config: &MiriConfig,
901 on_main_stack_empty: StackEmptyCallback<'tcx>,
902 ) -> InterpResult<'tcx> {
903 EnvVars::init(ecx, config)?;
904 MiriMachine::init_extern_statics(ecx)?;
905 ThreadManager::init(ecx, on_main_stack_empty);
906 interp_ok(())
907 }
908
909 pub(crate) fn communicate(&self) -> bool {
910 self.isolated_op == IsolatedOp::Allow
911 }
912
913 pub(crate) fn is_local(&self, instance: ty::Instance<'tcx>) -> bool {
915 let def_id = instance.def_id();
916 def_id.is_local() || self.user_relevant_crates.contains(&def_id.krate)
917 }
918
919 pub(crate) fn handle_abnormal_termination(&mut self) {
921 drop(self.profiler.take());
926 }
927
928 pub(crate) fn page_align(&self) -> Align {
929 Align::from_bytes(self.page_size).unwrap()
930 }
931
932 pub(crate) fn allocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
933 self.allocation_spans
934 .borrow()
935 .get(&alloc_id)
936 .map(|(allocated, _deallocated)| allocated.data())
937 }
938
939 pub(crate) fn deallocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
940 self.allocation_spans
941 .borrow()
942 .get(&alloc_id)
943 .and_then(|(_allocated, deallocated)| *deallocated)
944 .map(Span::data)
945 }
946
947 fn init_allocation(
948 ecx: &MiriInterpCx<'tcx>,
949 id: AllocId,
950 kind: MemoryKind,
951 size: Size,
952 align: Align,
953 ) -> InterpResult<'tcx, AllocExtra<'tcx>> {
954 if ecx.machine.tracked_alloc_ids.contains(&id) {
955 ecx.emit_diagnostic(NonHaltingDiagnostic::TrackingAlloc(id, size, align));
956 }
957
958 let borrow_tracker = ecx
959 .machine
960 .borrow_tracker
961 .as_ref()
962 .map(|bt| bt.borrow_mut().new_allocation(id, size, kind, &ecx.machine));
963
964 let data_race = match &ecx.machine.data_race {
965 GlobalDataRaceHandler::None => AllocDataRaceHandler::None,
966 GlobalDataRaceHandler::Vclocks(data_race) =>
967 AllocDataRaceHandler::Vclocks(
968 data_race::AllocState::new_allocation(
969 data_race,
970 &ecx.machine.threads,
971 size,
972 kind,
973 ecx.machine.current_user_relevant_span(),
974 ),
975 data_race.weak_memory.then(weak_memory::AllocState::new_allocation),
976 ),
977 GlobalDataRaceHandler::Genmc(_genmc_ctx) => {
978 AllocDataRaceHandler::Genmc
981 }
982 };
983
984 let backtrace = if kind.may_leak() || !ecx.machine.collect_leak_backtraces {
988 None
989 } else {
990 Some(ecx.generate_stacktrace())
991 };
992
993 if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) {
994 ecx.machine
995 .allocation_spans
996 .borrow_mut()
997 .insert(id, (ecx.machine.current_user_relevant_span(), None));
998 }
999
1000 interp_ok(AllocExtra {
1001 borrow_tracker,
1002 data_race,
1003 backtrace,
1004 sync_objs: BTreeMap::default(),
1005 })
1006 }
1007}
1008
1009impl VisitProvenance for MiriMachine<'_> {
1010 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
1011 #[rustfmt::skip]
1012 let MiriMachine {
1013 threads,
1014 thread_cpu_affinity: _,
1015 tls,
1016 env_vars,
1017 main_fn_ret_place,
1018 argc,
1019 argv,
1020 cmd_line,
1021 extern_statics,
1022 extern_statics_imports,
1023 extern_static_weak_import_default,
1024 dirs,
1025 borrow_tracker,
1026 data_race,
1027 alloc_addresses,
1028 fds,
1029 blocking_io:_,
1030 delayed_readiness_updates: _,
1031 tcx: _,
1032 isolated_op: _,
1033 validation: _,
1034 monotonic_clock: _,
1035 layouts: _,
1036 static_roots: _,
1037 profiler: _,
1038 string_cache: _,
1039 exported_symbols_cache: _,
1040 backtrace_style: _,
1041 user_relevant_crates: _,
1042 rng: _,
1043 allocator: _,
1044 tracked_alloc_ids: _,
1045 track_alloc_accesses: _,
1046 check_alignment: _,
1047 cmpxchg_weak_failure_rate: _,
1048 preemption_rate: _,
1049 report_progress: _,
1050 basic_block_count: _,
1051 native_lib: _,
1052 #[cfg(all(feature = "native-lib", unix))]
1053 native_lib_ecx_interchange: _,
1054 gc_interval: _,
1055 since_gc: _,
1056 num_cpus: _,
1057 page_size: _,
1058 stack_addr: _,
1059 stack_size: _,
1060 collect_leak_backtraces: _,
1061 allocation_spans: _,
1062 symbolic_alignment: _,
1063 union_data_ranges: _,
1064 pthread_mutex_sanity: _,
1065 pthread_rwlock_sanity: _,
1066 pthread_condvar_sanity: _,
1067 allocator_shim_symbols: _,
1068 mangle_internal_symbol_cache: _,
1069 float_nondet: _,
1070 float_rounding_error: _,
1071 short_fd_operations: _,
1072 } = self;
1073
1074 threads.visit_provenance(visit);
1075 tls.visit_provenance(visit);
1076 env_vars.visit_provenance(visit);
1077 dirs.visit_provenance(visit);
1078 fds.visit_provenance(visit);
1079 data_race.visit_provenance(visit);
1080 borrow_tracker.visit_provenance(visit);
1081 alloc_addresses.visit_provenance(visit);
1082 main_fn_ret_place.visit_provenance(visit);
1083 argc.visit_provenance(visit);
1084 argv.visit_provenance(visit);
1085 cmd_line.visit_provenance(visit);
1086 extern_static_weak_import_default.visit_provenance(visit);
1087 extern_statics.visit_provenance(visit);
1088 extern_statics_imports.visit_provenance(visit);
1089 }
1090}
1091
1092pub type MiriInterpCx<'tcx> = InterpCx<'tcx, MiriMachine<'tcx>>;
1094
1095pub trait MiriInterpCxExt<'tcx> {
1097 fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'tcx>;
1098 fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'tcx>;
1099}
1100impl<'tcx> MiriInterpCxExt<'tcx> for MiriInterpCx<'tcx> {
1101 #[inline(always)]
1102 fn eval_context_ref(&self) -> &MiriInterpCx<'tcx> {
1103 self
1104 }
1105 #[inline(always)]
1106 fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'tcx> {
1107 self
1108 }
1109}
1110
1111impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
1113 type MemoryKind = MiriMemoryKind;
1114 type ExtraFnVal = DynSym;
1115
1116 type FrameExtra = FrameExtra<'tcx>;
1117 type AllocExtra = AllocExtra<'tcx>;
1118
1119 type Provenance = Provenance;
1120 type ProvenanceExtra = ProvenanceExtra;
1121 type Bytes = MiriAllocBytes;
1122
1123 type MemoryMap =
1124 MonoHashMap<AllocId, (MemoryKind, Allocation<Provenance, Self::AllocExtra, Self::Bytes>)>;
1125
1126 const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
1127
1128 const PANIC_ON_ALLOC_FAIL: bool = false;
1129
1130 #[inline(always)]
1131 fn enforce_alignment(ecx: &MiriInterpCx<'tcx>) -> bool {
1132 ecx.machine.check_alignment != AlignmentCheck::None
1133 }
1134
1135 #[inline(always)]
1136 fn alignment_check(
1137 ecx: &MiriInterpCx<'tcx>,
1138 alloc_id: AllocId,
1139 alloc_align: Align,
1140 alloc_kind: AllocKind,
1141 offset: Size,
1142 align: Align,
1143 ) -> Option<Misalignment> {
1144 if ecx.machine.check_alignment != AlignmentCheck::Symbolic {
1145 return None;
1147 }
1148 if alloc_kind != AllocKind::LiveData {
1149 return None;
1151 }
1152 let (promised_offset, promised_align) = ecx
1154 .machine
1155 .symbolic_alignment
1156 .borrow()
1157 .get(&alloc_id)
1158 .copied()
1159 .unwrap_or((Size::ZERO, alloc_align));
1160 if promised_align < align {
1161 Some(Misalignment { has: promised_align, required: align })
1163 } else {
1164 let distance = offset.bytes().wrapping_sub(promised_offset.bytes());
1166 if distance.is_multiple_of(align.bytes()) {
1168 None
1170 } else {
1171 let distance_pow2 = 1 << distance.trailing_zeros();
1173 Some(Misalignment {
1174 has: Align::from_bytes(distance_pow2).unwrap(),
1175 required: align,
1176 })
1177 }
1178 }
1179 }
1180
1181 #[inline(always)]
1182 fn enforce_validity(ecx: &MiriInterpCx<'tcx>, _layout: TyAndLayout<'tcx>) -> bool {
1183 ecx.machine.validation != ValidationMode::No
1184 }
1185 #[inline(always)]
1186 fn enforce_validity_recursively(
1187 ecx: &InterpCx<'tcx, Self>,
1188 _layout: TyAndLayout<'tcx>,
1189 ) -> bool {
1190 ecx.machine.validation == ValidationMode::Deep
1191 }
1192
1193 #[inline(always)]
1194 fn ignore_optional_overflow_checks(ecx: &MiriInterpCx<'tcx>) -> bool {
1195 !ecx.tcx.sess.overflow_checks()
1196 }
1197
1198 fn check_fn_target_features(
1199 ecx: &MiriInterpCx<'tcx>,
1200 instance: ty::Instance<'tcx>,
1201 ) -> InterpResult<'tcx> {
1202 let attrs = ecx.tcx.codegen_instance_attrs(instance.def);
1203 if attrs
1204 .target_features
1205 .iter()
1206 .any(|feature| !ecx.tcx.sess.target_features.contains(&feature.name))
1207 {
1208 let unavailable = attrs
1209 .target_features
1210 .iter()
1211 .filter(|&feature| {
1212 feature.kind != TargetFeatureKind::Implied
1213 && !ecx.tcx.sess.target_features.contains(&feature.name)
1214 })
1215 .fold(String::new(), |mut s, feature| {
1216 if !s.is_empty() {
1217 s.push_str(", ");
1218 }
1219 s.push_str(feature.name.as_str());
1220 s
1221 });
1222 let msg = format!(
1223 "calling a function that requires unavailable target features: {unavailable}"
1224 );
1225 if ecx.tcx.sess.target.is_like_wasm {
1228 throw_machine_stop!(TerminationInfo::Abort(msg));
1229 } else {
1230 throw_ub_format!("{msg}");
1231 }
1232 }
1233 interp_ok(())
1234 }
1235
1236 #[inline(always)]
1237 fn find_mir_or_eval_fn(
1238 ecx: &mut MiriInterpCx<'tcx>,
1239 instance: ty::Instance<'tcx>,
1240 abi: &FnAbi<'tcx, Ty<'tcx>>,
1241 args: &[FnArg<'tcx>],
1242 dest: &PlaceTy<'tcx>,
1243 ret: Option<mir::BasicBlock>,
1244 unwind: mir::UnwindAction,
1245 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
1246 if ecx.tcx.is_foreign_item(instance.def_id()) {
1248 let _trace = enter_trace_span!("emulate_foreign_item");
1249 let args = MiriInterpCx::copy_fn_args(args); let link_name = Symbol::intern(ecx.tcx.symbol_name(instance).name);
1257 return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind);
1258 }
1259
1260 if ecx.machine.data_race.as_genmc_ref().is_some()
1261 && ecx.genmc_intercept_function(instance, args, dest)?
1262 {
1263 ecx.return_to_block(ret)?;
1264 return interp_ok(None);
1265 }
1266
1267 let _trace = enter_trace_span!("load_mir");
1269 interp_ok(Some((ecx.load_mir(instance.def, None)?, instance)))
1270 }
1271
1272 #[inline(always)]
1273 fn call_extra_fn(
1274 ecx: &mut MiriInterpCx<'tcx>,
1275 fn_val: DynSym,
1276 abi: &FnAbi<'tcx, Ty<'tcx>>,
1277 args: &[FnArg<'tcx>],
1278 dest: &PlaceTy<'tcx>,
1279 ret: Option<mir::BasicBlock>,
1280 unwind: mir::UnwindAction,
1281 ) -> InterpResult<'tcx> {
1282 let args = MiriInterpCx::copy_fn_args(args); ecx.emulate_dyn_sym(fn_val, abi, &args, dest, ret, unwind)
1284 }
1285
1286 #[inline(always)]
1287 fn call_intrinsic(
1288 ecx: &mut MiriInterpCx<'tcx>,
1289 instance: ty::Instance<'tcx>,
1290 args: &[OpTy<'tcx>],
1291 dest: &PlaceTy<'tcx>,
1292 ret: Option<mir::BasicBlock>,
1293 unwind: mir::UnwindAction,
1294 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
1295 ecx.call_intrinsic(instance, args, dest, ret, unwind)
1296 }
1297
1298 #[inline(always)]
1299 fn call_llvm_intrinsic(
1300 ecx: &mut MiriInterpCx<'tcx>,
1301 instance: ty::Instance<'tcx>,
1302 args: &[OpTy<'tcx>],
1303 dest: &PlaceTy<'tcx>,
1304 ret: Option<mir::BasicBlock>,
1305 ) -> InterpResult<'tcx, ()> {
1306 ecx.call_llvm_intrinsic(instance, args, dest, ret)
1307 }
1308
1309 #[inline(always)]
1310 fn assert_panic(
1311 ecx: &mut MiriInterpCx<'tcx>,
1312 msg: &mir::AssertMessage<'tcx>,
1313 unwind: mir::UnwindAction,
1314 ) -> InterpResult<'tcx> {
1315 ecx.assert_panic(msg, unwind)
1316 }
1317
1318 fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> {
1319 ecx.start_panic_nounwind(msg)
1320 }
1321
1322 fn unwind_terminate(
1323 ecx: &mut InterpCx<'tcx, Self>,
1324 reason: mir::UnwindTerminateReason,
1325 ) -> InterpResult<'tcx> {
1326 let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap();
1328 let panic = ty::Instance::mono(ecx.tcx.tcx, panic);
1329 ecx.call_function(
1330 panic,
1331 ExternAbi::Rust,
1332 &[],
1333 None,
1334 ReturnContinuation::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
1335 )?;
1336 interp_ok(())
1337 }
1338
1339 #[inline(always)]
1340 fn binary_ptr_op(
1341 ecx: &MiriInterpCx<'tcx>,
1342 bin_op: mir::BinOp,
1343 left: &ImmTy<'tcx>,
1344 right: &ImmTy<'tcx>,
1345 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1346 ecx.binary_ptr_op(bin_op, left, right)
1347 }
1348
1349 #[inline(always)]
1350 fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(
1351 ecx: &InterpCx<'tcx, Self>,
1352 inputs: &[F1],
1353 ) -> F2 {
1354 ecx.generate_nan(inputs)
1355 }
1356
1357 #[inline(always)]
1358 fn apply_float_nondet(
1359 ecx: &mut InterpCx<'tcx, Self>,
1360 val: ImmTy<'tcx>,
1361 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1362 crate::math::apply_random_float_error_to_imm(ecx, val, 4)
1363 }
1364
1365 #[inline(always)]
1366 fn equal_float_min_max<F: Float>(ecx: &MiriInterpCx<'tcx>, a: F, b: F) -> F {
1367 ecx.equal_float_min_max(a, b)
1368 }
1369
1370 #[inline(always)]
1371 fn float_fuse_mul_add(ecx: &InterpCx<'tcx, Self>) -> bool {
1372 ecx.machine.float_nondet && ecx.machine.rng.borrow_mut().random()
1373 }
1374
1375 #[inline(always)]
1376 fn runtime_checks(
1377 ecx: &InterpCx<'tcx, Self>,
1378 r: mir::RuntimeChecks,
1379 ) -> InterpResult<'tcx, bool> {
1380 interp_ok(r.value(ecx.tcx.sess))
1381 }
1382
1383 #[inline(always)]
1384 fn thread_local_static_pointer(
1385 ecx: &mut MiriInterpCx<'tcx>,
1386 def_id: DefId,
1387 ) -> InterpResult<'tcx, StrictPointer> {
1388 ecx.get_or_create_thread_local_alloc(def_id)
1389 }
1390
1391 fn extern_static_pointer(
1392 ecx: &MiriInterpCx<'tcx>,
1393 def_id: DefId,
1394 ) -> InterpResult<'tcx, StrictPointer> {
1395 let link_name = Symbol::intern(ecx.tcx.symbol_name(Instance::mono(*ecx.tcx, def_id)).name);
1396 let def_ty = ecx.tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
1397 let extern_decl_layout =
1398 ecx.tcx.layout_of(ecx.typing_env().as_query_input(def_ty)).unwrap();
1399
1400 let ptr = match ecx.tcx.codegen_fn_attrs(def_id).import_linkage {
1403 None => ecx.machine.extern_statics.get(&link_name),
1404 Some(_) => ecx.machine.extern_statics_imports.get(&link_name),
1405 };
1406 if let Some(&ptr) = ptr {
1407 let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
1411 panic!("extern_statics cannot contain wildcards")
1412 };
1413 let info = ecx.get_alloc_info(alloc_id);
1414 if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align {
1415 throw_unsup_format!(
1416 "extern static `{link_name}` has been declared as `{krate}::{name}` \
1417 with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
1418 but Miri emulates it via an extern static shim \
1419 with a size of {shim_size} bytes and alignment of {shim_align} bytes",
1420 name = ecx.tcx.def_path_str(def_id),
1421 krate = ecx.tcx.crate_name(def_id.krate),
1422 decl_size = extern_decl_layout.size.bytes(),
1423 decl_align = extern_decl_layout.align.bytes(),
1424 shim_size = info.size.bytes(),
1425 shim_align = info.align.bytes(),
1426 )
1427 }
1428 interp_ok(ptr)
1429 } else if ecx.tcx.codegen_fn_attrs(def_id).import_linkage == Some(Linkage::ExternalWeak) {
1430 assert_eq!(
1438 extern_decl_layout.size,
1439 ecx.tcx.data_layout.pointer_size(),
1440 "non-pointer-sized weak static"
1441 );
1442 interp_ok(
1443 ecx.machine
1444 .extern_static_weak_import_default
1445 .expect("`missing_weak_symbol` should have been initialized"),
1446 )
1447 } else {
1448 throw_unsup_format!("extern static `{link_name}` is not supported by Miri")
1449 }
1450 }
1451
1452 fn init_local_allocation(
1453 ecx: &MiriInterpCx<'tcx>,
1454 id: AllocId,
1455 kind: MemoryKind,
1456 size: Size,
1457 align: Align,
1458 ) -> InterpResult<'tcx, Self::AllocExtra> {
1459 assert!(kind != MiriMemoryKind::Global.into());
1460 MiriMachine::init_allocation(ecx, id, kind, size, align)
1461 }
1462
1463 fn adjust_alloc_root_pointer(
1464 ecx: &MiriInterpCx<'tcx>,
1465 ptr: interpret::Pointer<CtfeProvenance>,
1466 kind: Option<MemoryKind>,
1467 ) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
1468 let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None");
1469 let alloc_id = ptr.provenance.alloc_id();
1470 if cfg!(debug_assertions) {
1471 match ecx.tcx.try_get_global_alloc(alloc_id) {
1473 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
1474 panic!("adjust_alloc_root_pointer called on thread-local static")
1475 }
1476 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => {
1477 panic!("adjust_alloc_root_pointer called on extern static")
1478 }
1479 _ => {}
1480 }
1481 }
1482 let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
1484 borrow_tracker.borrow_mut().root_ptr_tag(alloc_id, &ecx.machine)
1485 } else {
1486 BorTag::default()
1488 };
1489 ecx.adjust_alloc_root_pointer(ptr, tag, kind)
1490 }
1491
1492 #[inline(always)]
1494 fn ptr_from_addr_cast(ecx: &MiriInterpCx<'tcx>, addr: u64) -> InterpResult<'tcx, Pointer> {
1495 ecx.ptr_from_addr_cast(addr)
1496 }
1497
1498 #[inline(always)]
1502 fn expose_provenance(
1503 ecx: &InterpCx<'tcx, Self>,
1504 provenance: Self::Provenance,
1505 ) -> InterpResult<'tcx> {
1506 ecx.expose_provenance(provenance)
1507 }
1508
1509 fn ptr_get_alloc(
1521 ecx: &MiriInterpCx<'tcx>,
1522 ptr: StrictPointer,
1523 size: i64,
1524 ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
1525 let rel = ecx.ptr_get_alloc(ptr, size);
1526
1527 rel.map(|(alloc_id, size)| {
1528 let tag = match ptr.provenance {
1529 Provenance::Concrete { tag, .. } => ProvenanceExtra::Concrete(tag),
1530 Provenance::Wildcard => ProvenanceExtra::Wildcard,
1531 };
1532 (alloc_id, size, tag)
1533 })
1534 }
1535
1536 fn adjust_global_allocation<'b>(
1545 ecx: &InterpCx<'tcx, Self>,
1546 id: AllocId,
1547 alloc: &'b Allocation,
1548 ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
1549 {
1550 let alloc = alloc.adjust_from_tcx(
1551 &ecx.tcx,
1552 |bytes, align| ecx.get_global_alloc_bytes(id, bytes, align),
1553 |ptr| ecx.global_root_pointer(ptr),
1554 )?;
1555 let kind = MiriMemoryKind::Global.into();
1556 let extra = MiriMachine::init_allocation(ecx, id, kind, alloc.size(), alloc.align)?;
1557 interp_ok(Cow::Owned(alloc.with_extra(extra)))
1558 }
1559
1560 #[inline(always)]
1561 fn before_memory_read(
1562 _tcx: TyCtxtAt<'tcx>,
1563 machine: &Self,
1564 alloc_extra: &AllocExtra<'tcx>,
1565 ptr: Pointer,
1566 (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1567 range: AllocRange,
1568 ) -> InterpResult<'tcx> {
1569 if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1570 machine.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(
1571 alloc_id,
1572 range,
1573 borrow_tracker::AccessKind::Read,
1574 ));
1575 }
1576 match &machine.data_race {
1578 GlobalDataRaceHandler::None => {}
1579 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1580 genmc_ctx.memory_load(machine, ptr.addr(), range.size)?,
1581 GlobalDataRaceHandler::Vclocks(_data_race) => {
1582 let _trace = enter_trace_span!(data_race::before_memory_read);
1583 let AllocDataRaceHandler::Vclocks(data_race, _weak_memory) = &alloc_extra.data_race
1584 else {
1585 unreachable!();
1586 };
1587 data_race.read_non_atomic(alloc_id, range, NaReadType::Read, None, machine)?;
1588 }
1589 }
1590 if let Some(borrow_tracker) = &alloc_extra.borrow_tracker {
1591 borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?;
1592 }
1593 for (_offset, obj) in alloc_extra.sync_objs.range(range.start..range.end()) {
1595 obj.on_access(concurrency::sync::AccessKind::Read)?;
1596 }
1597
1598 interp_ok(())
1599 }
1600
1601 #[inline(always)]
1602 fn before_memory_write(
1603 _tcx: TyCtxtAt<'tcx>,
1604 machine: &mut Self,
1605 alloc_extra: &mut AllocExtra<'tcx>,
1606 ptr: Pointer,
1607 (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1608 range: AllocRange,
1609 ) -> InterpResult<'tcx> {
1610 if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1611 machine.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(
1612 alloc_id,
1613 range,
1614 borrow_tracker::AccessKind::Write,
1615 ));
1616 }
1617 match &machine.data_race {
1618 GlobalDataRaceHandler::None => {}
1619 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1620 genmc_ctx.memory_store(machine, ptr.addr(), range.size)?,
1621 GlobalDataRaceHandler::Vclocks(_global_state) => {
1622 let _trace = enter_trace_span!(data_race::before_memory_write);
1623 let AllocDataRaceHandler::Vclocks(data_race, weak_memory) =
1624 &mut alloc_extra.data_race
1625 else {
1626 unreachable!()
1627 };
1628 data_race.write_non_atomic(alloc_id, range, NaWriteType::Write, None, machine)?;
1629 if let Some(weak_memory) = weak_memory {
1630 weak_memory
1631 .non_atomic_write(range, machine.data_race.as_vclocks_ref().unwrap());
1632 }
1633 }
1634 }
1635 if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1636 borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?;
1637 }
1638 if !alloc_extra.sync_objs.is_empty() {
1641 let mut to_delete = vec![];
1642 for (offset, obj) in alloc_extra.sync_objs.range(range.start..range.end()) {
1643 obj.on_access(concurrency::sync::AccessKind::Write)?;
1644 if obj.delete_on_write() {
1645 to_delete.push(*offset);
1646 }
1647 }
1648 for offset in to_delete {
1649 alloc_extra.sync_objs.remove(&offset);
1650 }
1651 }
1652 interp_ok(())
1653 }
1654
1655 #[inline(always)]
1656 fn before_memory_deallocation(
1657 _tcx: TyCtxtAt<'tcx>,
1658 machine: &mut Self,
1659 alloc_extra: &mut AllocExtra<'tcx>,
1660 ptr: Pointer,
1661 (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
1662 size: Size,
1663 align: Align,
1664 kind: MemoryKind,
1665 ) -> InterpResult<'tcx> {
1666 if machine.tracked_alloc_ids.contains(&alloc_id) {
1667 machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
1668 }
1669 match &machine.data_race {
1670 GlobalDataRaceHandler::None => {}
1671 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1672 genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), kind)?,
1673 GlobalDataRaceHandler::Vclocks(_global_state) => {
1674 let _trace = enter_trace_span!(data_race::before_memory_deallocation);
1675 let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap();
1676 data_race.write_non_atomic(
1677 alloc_id,
1678 alloc_range(Size::ZERO, size),
1679 NaWriteType::Deallocate,
1680 None,
1681 machine,
1682 )?;
1683 }
1684 }
1685 if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1686 borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?;
1687 }
1688 for obj in alloc_extra.sync_objs.values() {
1690 obj.on_access(concurrency::sync::AccessKind::Dealloc)?;
1691 }
1692
1693 if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
1694 {
1695 *deallocated_at = Some(machine.current_user_relevant_span());
1696 }
1697 machine.free_alloc_id(alloc_id, size, align, kind);
1698 interp_ok(())
1699 }
1700
1701 #[inline(always)]
1702 fn retag_ptr_value(
1703 ecx: &mut InterpCx<'tcx, Self>,
1704 val: &ImmTy<'tcx>,
1705 ty: Ty<'tcx>,
1706 ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
1707 if ecx.machine.borrow_tracker.is_some() {
1708 ecx.retag_ptr_value(val, ty)
1709 } else {
1710 interp_ok(None)
1711 }
1712 }
1713
1714 #[inline(always)]
1715 fn with_retag_mode<T>(
1716 ecx: &mut InterpCx<'tcx, Self>,
1717 mode: RetagMode,
1718 f: impl FnOnce(&mut InterpCx<'tcx, Self>) -> InterpResult<'tcx, T>,
1719 ) -> InterpResult<'tcx, T> {
1720 if ecx.machine.borrow_tracker.is_some() { ecx.with_retag_mode(mode, f) } else { f(ecx) }
1721 }
1722
1723 fn protect_in_place_function_argument(
1724 ecx: &mut InterpCx<'tcx, Self>,
1725 place: &MPlaceTy<'tcx>,
1726 ) -> InterpResult<'tcx> {
1727 let protected_place = if ecx.machine.borrow_tracker.is_some() {
1730 ecx.protect_place(place)?
1731 } else {
1732 place.clone()
1734 };
1735 ecx.write_uninit(&protected_place)?;
1740 interp_ok(())
1742 }
1743
1744 #[inline(always)]
1745 fn init_frame(
1746 ecx: &mut InterpCx<'tcx, Self>,
1747 frame: Frame<'tcx, Provenance>,
1748 ) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
1749 let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
1751 let fn_name = frame.instance().to_string();
1752 let entry = ecx.machine.string_cache.entry(fn_name.clone());
1753 let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
1754
1755 Some(profiler.start_recording_interval_event_detached(
1756 *name,
1757 measureme::EventId::from_label(*name),
1758 ecx.active_thread().to_u32(),
1759 ))
1760 } else {
1761 None
1762 };
1763
1764 let borrow_tracker = ecx.machine.borrow_tracker.as_ref();
1765
1766 let extra = FrameExtra {
1767 borrow_tracker: borrow_tracker.map(|bt| bt.borrow_mut().new_frame()),
1768 catch_unwind: None,
1769 timing,
1770 user_relevance: ecx.machine.user_relevance(&frame),
1771 data_race: ecx
1772 .machine
1773 .data_race
1774 .as_vclocks_ref()
1775 .map(|_| data_race::FrameState::default()),
1776 };
1777
1778 interp_ok(frame.with_extra(extra))
1779 }
1780
1781 fn stack<'a>(
1782 ecx: &'a InterpCx<'tcx, Self>,
1783 ) -> &'a [Frame<'tcx, Self::Provenance, Self::FrameExtra>] {
1784 ecx.active_thread_stack()
1785 }
1786
1787 fn stack_mut<'a>(
1788 ecx: &'a mut InterpCx<'tcx, Self>,
1789 ) -> &'a mut Vec<Frame<'tcx, Self::Provenance, Self::FrameExtra>> {
1790 ecx.active_thread_stack_mut()
1791 }
1792
1793 fn before_terminator(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1794 ecx.machine.basic_block_count += 1u64; ecx.machine.since_gc += 1;
1796 if let Some(report_progress) = ecx.machine.report_progress {
1798 if ecx.machine.basic_block_count.is_multiple_of(u64::from(report_progress)) {
1799 ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport {
1800 block_count: ecx.machine.basic_block_count,
1801 });
1802 }
1803 }
1804
1805 if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
1811 ecx.machine.since_gc = 0;
1812 ecx.run_provenance_gc();
1813 ecx.machine.blocking_io.run_gc();
1814 }
1815
1816 ecx.maybe_preempt_active_thread();
1819
1820 ecx.machine.monotonic_clock.tick();
1822
1823 interp_ok(())
1824 }
1825
1826 #[inline(always)]
1827 fn after_stack_push(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1828 if ecx.frame().extra.user_relevance >= ecx.active_thread_ref().current_user_relevance() {
1829 let stack_len = ecx.active_thread_stack().len();
1832 ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
1833 }
1834 interp_ok(())
1835 }
1836
1837 fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1838 let frame = ecx.frame();
1839 if ecx.machine.borrow_tracker.is_some() {
1842 ecx.on_stack_pop(frame)?;
1843 }
1844 if ecx
1845 .active_thread_ref()
1846 .top_user_relevant_frame()
1847 .expect("there should always be a most relevant frame for a non-empty stack")
1848 == ecx.frame_idx()
1849 {
1850 ecx.active_thread_mut().recompute_top_user_relevant_frame(1);
1856 }
1857 info!("Leaving {}", ecx.frame().instance());
1861 interp_ok(())
1862 }
1863
1864 #[inline(always)]
1865 fn after_stack_pop(
1866 ecx: &mut InterpCx<'tcx, Self>,
1867 frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1868 unwinding: bool,
1869 ) -> InterpResult<'tcx, ReturnAction> {
1870 let res = {
1871 let mut frame = frame;
1873 let timing = frame.extra.timing.take();
1874 let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
1875 if let Some(profiler) = ecx.machine.profiler.as_ref() {
1876 profiler.finish_recording_interval_event(timing.unwrap());
1877 }
1878 res
1879 };
1880 if !ecx.active_thread_stack().is_empty() {
1883 info!("Continuing in {}", ecx.frame().instance());
1884 }
1885 res
1886 }
1887
1888 fn after_local_read(
1889 ecx: &InterpCx<'tcx, Self>,
1890 frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1891 local: mir::Local,
1892 ) -> InterpResult<'tcx> {
1893 if let Some(data_race) = &frame.extra.data_race {
1894 let _trace = enter_trace_span!(data_race::after_local_read);
1895 data_race.local_read(local, &ecx.machine);
1896 }
1897 interp_ok(())
1898 }
1899
1900 fn after_local_write(
1901 ecx: &mut InterpCx<'tcx, Self>,
1902 local: mir::Local,
1903 storage_live: bool,
1904 ) -> InterpResult<'tcx> {
1905 if let Some(data_race) = &ecx.frame().extra.data_race {
1906 let _trace = enter_trace_span!(data_race::after_local_write);
1907 data_race.local_write(local, storage_live, &ecx.machine);
1908 }
1909 interp_ok(())
1910 }
1911
1912 fn after_local_moved_to_memory(
1913 ecx: &mut InterpCx<'tcx, Self>,
1914 local: mir::Local,
1915 mplace: &MPlaceTy<'tcx>,
1916 ) -> InterpResult<'tcx> {
1917 let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else {
1918 panic!("after_local_allocated should only be called on fresh allocations");
1919 };
1920 let local_decl = &ecx.frame().body().local_decls[local];
1922 let span = local_decl.source_info.span;
1923 ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
1924 let (alloc_info, machine) = ecx.get_alloc_extra_mut(alloc_id)?;
1926 if let Some(data_race) =
1927 &machine.threads.active_thread_stack().last().unwrap().extra.data_race
1928 {
1929 let _trace = enter_trace_span!(data_race::after_local_moved_to_memory);
1930 data_race.local_moved_to_memory(
1931 local,
1932 alloc_info.data_race.as_vclocks_mut().unwrap(),
1933 machine,
1934 );
1935 }
1936 interp_ok(())
1937 }
1938
1939 fn get_global_alloc_salt(
1940 ecx: &InterpCx<'tcx, Self>,
1941 instance: Option<ty::Instance<'tcx>>,
1942 ) -> usize {
1943 let unique = if let Some(instance) = instance {
1944 let is_generic = instance
1957 .args
1958 .into_iter()
1959 .any(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_)));
1960 let can_be_inlined = matches!(
1961 ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
1962 InliningThreshold::Always
1963 ) || !matches!(
1964 ecx.tcx.codegen_instance_attrs(instance.def).inline,
1965 InlineAttr::Never
1966 );
1967 !is_generic && !can_be_inlined
1968 } else {
1969 false
1971 };
1972 if unique {
1974 CTFE_ALLOC_SALT
1975 } else {
1976 ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL)
1977 }
1978 }
1979
1980 fn cached_union_data_range<'e>(
1981 ecx: &'e mut InterpCx<'tcx, Self>,
1982 ty: Ty<'tcx>,
1983 compute_range: impl FnOnce() -> RangeSet,
1984 ) -> Cow<'e, RangeSet> {
1985 Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range))
1986 }
1987
1988 fn get_default_alloc_params(&self) -> <Self::Bytes as AllocBytes>::AllocParams {
1989 use crate::alloc::MiriAllocParams;
1990
1991 match &self.allocator {
1992 Some(alloc) => MiriAllocParams::Isolated(alloc.clone()),
1993 None => MiriAllocParams::Global,
1994 }
1995 }
1996
1997 fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan {
1998 #[cfg(feature = "tracing")]
1999 {
2000 span().entered()
2001 }
2002 #[cfg(not(feature = "tracing"))]
2003 #[expect(clippy::unused_unit)]
2004 {
2005 let _ = span; ()
2007 }
2008 }
2009}
2010
2011pub trait MachineCallback<'tcx, T>: VisitProvenance {
2013 fn call(
2015 self: Box<Self>,
2016 ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
2017 arg: T,
2018 ) -> InterpResult<'tcx>;
2019}
2020
2021pub type DynMachineCallback<'tcx, T> = Box<dyn MachineCallback<'tcx, T> + 'tcx>;
2023
2024#[macro_export]
2041macro_rules! callback {
2042 (@capture<$tcx:lifetime $(,)? $($lft:lifetime),*>
2043 { $($name:ident: $type:ty),* $(,)? }
2044 |$this:ident, $arg:ident: $arg_ty:ty| $body:expr $(,)?) => {{
2045 struct Callback<$tcx, $($lft),*> {
2046 $($name: $type,)*
2047 _phantom: std::marker::PhantomData<&$tcx ()>,
2048 }
2049
2050 impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> {
2051 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
2052 $(
2053 VisitProvenance::visit_provenance(&self.$name, _visit);
2054 )*
2055 }
2056 }
2057
2058 impl<$tcx, $($lft),*> MachineCallback<$tcx, $arg_ty> for Callback<$tcx, $($lft),*> {
2059 fn call(
2060 self: Box<Self>,
2061 $this: &mut MiriInterpCx<$tcx>,
2062 $arg: $arg_ty
2063 ) -> InterpResult<$tcx> {
2064 #[allow(unused_variables)]
2065 let Callback { $($name,)* _phantom } = *self;
2066 $body
2067 }
2068 }
2069
2070 Box::new(Callback {
2071 $($name,)*
2072 _phantom: std::marker::PhantomData
2073 })
2074 }};
2075}