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