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::*;
44
45pub const SIGRTMIN: i32 = 34;
49
50pub const SIGRTMAX: i32 = 42;
54
55const ADDRS_PER_ANON_GLOBAL: usize = 32;
59
60#[derive(Copy, Clone, Debug, PartialEq)]
61pub enum AlignmentCheck {
62 None,
64 Symbolic,
66 Int,
68}
69
70#[derive(Copy, Clone, Debug, PartialEq)]
71pub enum RejectOpWith {
72 Abort,
74
75 NoWarning,
79
80 Warning,
82
83 WarningWithoutBacktrace,
85}
86
87#[derive(Copy, Clone, Debug, PartialEq)]
88pub enum IsolatedOp {
89 Reject(RejectOpWith),
94
95 Allow,
97}
98
99#[derive(Debug, Copy, Clone, PartialEq, Eq)]
100pub enum BacktraceStyle {
101 Short,
103 Full,
105 Off,
107}
108
109#[derive(Debug, Copy, Clone, PartialEq, Eq)]
110pub enum ValidationMode {
111 No,
113 Shallow,
115 Deep,
117}
118
119#[derive(Debug, Copy, Clone, PartialEq, Eq)]
120pub enum FloatRoundingErrorMode {
121 Random,
123 None,
125 Max,
127}
128
129pub struct FrameExtra<'tcx> {
131 pub borrow_tracker: Option<borrow_tracker::FrameState>,
133
134 pub catch_unwind: Option<CatchUnwindData<'tcx>>,
138
139 pub timing: Option<measureme::DetachedTiming>,
143
144 pub user_relevance: u8,
148
149 pub data_race: Option<data_race::FrameState>,
151}
152
153impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 let FrameExtra { borrow_tracker, catch_unwind, timing: _, user_relevance, data_race } =
157 self;
158 f.debug_struct("FrameData")
159 .field("borrow_tracker", borrow_tracker)
160 .field("catch_unwind", catch_unwind)
161 .field("user_relevance", user_relevance)
162 .field("data_race", data_race)
163 .finish()
164 }
165}
166
167impl VisitProvenance for FrameExtra<'_> {
168 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
169 let FrameExtra { catch_unwind, borrow_tracker, timing: _, user_relevance: _, data_race: _ } =
170 self;
171
172 catch_unwind.visit_provenance(visit);
173 borrow_tracker.visit_provenance(visit);
174 }
175}
176
177#[derive(Debug, Copy, Clone, PartialEq, Eq)]
179pub enum MiriMemoryKind {
180 Rust,
182 Miri,
184 C,
186 WinHeap,
188 WinLocal,
190 Machine,
193 Runtime,
196 Global,
199 ExternStatic,
202 Tls,
205 Mmap,
207 SocketAddress,
209}
210
211impl From<MiriMemoryKind> for MemoryKind {
212 #[inline(always)]
213 fn from(kind: MiriMemoryKind) -> MemoryKind {
214 MemoryKind::Machine(kind)
215 }
216}
217
218impl MayLeak for MiriMemoryKind {
219 #[inline(always)]
220 fn may_leak(self) -> bool {
221 use self::MiriMemoryKind::*;
222 match self {
223 Rust | Miri | C | WinHeap | WinLocal | Runtime => false,
224 Machine | Global | ExternStatic | Tls | Mmap | SocketAddress => true,
225 }
226 }
227}
228
229impl MiriMemoryKind {
230 fn should_save_allocation_span(self) -> bool {
232 use self::MiriMemoryKind::*;
233 match self {
234 Rust | Miri | C | WinHeap | WinLocal | Mmap => true,
236 Machine | Global | ExternStatic | Tls | Runtime | SocketAddress => false,
238 }
239 }
240}
241
242impl fmt::Display for MiriMemoryKind {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 use self::MiriMemoryKind::*;
245 match self {
246 Rust => write!(f, "Rust heap"),
247 Miri => write!(f, "Miri bare-metal heap"),
248 C => write!(f, "C heap"),
249 WinHeap => write!(f, "Windows heap"),
250 WinLocal => write!(f, "Windows local memory"),
251 Machine => write!(f, "machine-managed memory"),
252 Runtime => write!(f, "language runtime memory"),
253 Global => write!(f, "global (static or const)"),
254 ExternStatic => write!(f, "extern static"),
255 Tls => write!(f, "thread-local static"),
256 Mmap => write!(f, "mmap"),
257 SocketAddress => write!(f, "socket address"),
258 }
259 }
260}
261
262pub type MemoryKind = interpret::MemoryKind<MiriMemoryKind>;
263
264#[derive(Clone, Copy, PartialEq, Eq, Hash)]
270pub enum Provenance {
271 Concrete {
274 alloc_id: AllocId,
275 tag: BorTag,
277 },
278 Wildcard,
295}
296
297#[derive(Copy, Clone, PartialEq)]
299pub enum ProvenanceExtra {
300 Concrete(BorTag),
301 Wildcard,
302}
303
304#[cfg(target_pointer_width = "64")]
305static_assert_size!(StrictPointer, 24);
306#[cfg(target_pointer_width = "64")]
311static_assert_size!(Scalar, 32);
312
313impl fmt::Debug for Provenance {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 match self {
316 Provenance::Concrete { alloc_id, tag } => {
317 if f.alternate() {
319 write!(f, "[{alloc_id:#?}]")?;
320 } else {
321 write!(f, "[{alloc_id:?}]")?;
322 }
323 write!(f, "{tag:?}")?;
325 }
326 Provenance::Wildcard => {
327 write!(f, "[wildcard]")?;
328 }
329 }
330 Ok(())
331 }
332}
333
334impl interpret::Provenance for Provenance {
335 const OFFSET_IS_ADDR: bool = true;
337
338 const WILDCARD: Option<Self> = Some(Provenance::Wildcard);
340
341 fn get_alloc_id(self) -> Option<AllocId> {
342 match self {
343 Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
344 Provenance::Wildcard => None,
345 }
346 }
347
348 fn fmt(ptr: &interpret::Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349 let (prov, addr) = ptr.into_raw_parts(); write!(f, "{:#x}", addr.bytes())?;
351 if f.alternate() {
352 write!(f, "{prov:#?}")?;
353 } else {
354 write!(f, "{prov:?}")?;
355 }
356 Ok(())
357 }
358}
359
360impl fmt::Debug for ProvenanceExtra {
361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362 match self {
363 ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"),
364 ProvenanceExtra::Wildcard => write!(f, "<wildcard>"),
365 }
366 }
367}
368
369impl ProvenanceExtra {
370 pub fn and_then<T>(self, f: impl FnOnce(BorTag) -> Option<T>) -> Option<T> {
371 match self {
372 ProvenanceExtra::Concrete(pid) => f(pid),
373 ProvenanceExtra::Wildcard => None,
374 }
375 }
376}
377
378#[derive(Debug)]
380pub struct AllocExtra<'tcx> {
381 pub borrow_tracker: Option<borrow_tracker::AllocState>,
383 pub data_race: AllocDataRaceHandler,
387 pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
392 pub sync_objs: BTreeMap<Size, Box<dyn SyncObj>>,
397}
398
399impl<'tcx> Clone for AllocExtra<'tcx> {
402 fn clone(&self) -> Self {
403 panic!("our allocations should never be cloned");
404 }
405}
406
407impl VisitProvenance for AllocExtra<'_> {
408 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
409 let AllocExtra { borrow_tracker, data_race, backtrace: _, sync_objs: _ } = self;
410
411 borrow_tracker.visit_provenance(visit);
412 data_race.visit_provenance(visit);
413 }
414}
415
416pub struct PrimitiveLayouts<'tcx> {
418 pub unit: TyAndLayout<'tcx>,
419 pub i8: TyAndLayout<'tcx>,
420 pub i16: TyAndLayout<'tcx>,
421 pub i32: TyAndLayout<'tcx>,
422 pub i64: TyAndLayout<'tcx>,
423 pub i128: TyAndLayout<'tcx>,
424 pub isize: TyAndLayout<'tcx>,
425 pub u8: TyAndLayout<'tcx>,
426 pub u16: TyAndLayout<'tcx>,
427 pub u32: TyAndLayout<'tcx>,
428 pub u64: TyAndLayout<'tcx>,
429 pub u128: TyAndLayout<'tcx>,
430 pub usize: TyAndLayout<'tcx>,
431 pub bool: TyAndLayout<'tcx>,
432 pub mut_raw_ptr: TyAndLayout<'tcx>, pub const_raw_ptr: TyAndLayout<'tcx>, }
435
436impl<'tcx> PrimitiveLayouts<'tcx> {
437 fn new(layout_cx: LayoutCx<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
438 let tcx = layout_cx.tcx();
439 let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit);
440 let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
441 Ok(Self {
442 unit: layout_cx.layout_of(tcx.types.unit)?,
443 i8: layout_cx.layout_of(tcx.types.i8)?,
444 i16: layout_cx.layout_of(tcx.types.i16)?,
445 i32: layout_cx.layout_of(tcx.types.i32)?,
446 i64: layout_cx.layout_of(tcx.types.i64)?,
447 i128: layout_cx.layout_of(tcx.types.i128)?,
448 isize: layout_cx.layout_of(tcx.types.isize)?,
449 u8: layout_cx.layout_of(tcx.types.u8)?,
450 u16: layout_cx.layout_of(tcx.types.u16)?,
451 u32: layout_cx.layout_of(tcx.types.u32)?,
452 u64: layout_cx.layout_of(tcx.types.u64)?,
453 u128: layout_cx.layout_of(tcx.types.u128)?,
454 usize: layout_cx.layout_of(tcx.types.usize)?,
455 bool: layout_cx.layout_of(tcx.types.bool)?,
456 mut_raw_ptr: layout_cx.layout_of(mut_raw_ptr)?,
457 const_raw_ptr: layout_cx.layout_of(const_raw_ptr)?,
458 })
459 }
460
461 pub fn uint(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
462 match size.bits() {
463 8 => Some(self.u8),
464 16 => Some(self.u16),
465 32 => Some(self.u32),
466 64 => Some(self.u64),
467 128 => Some(self.u128),
468 _ => None,
469 }
470 }
471
472 pub fn int(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
473 match size.bits() {
474 8 => Some(self.i8),
475 16 => Some(self.i16),
476 32 => Some(self.i32),
477 64 => Some(self.i64),
478 128 => Some(self.i128),
479 _ => None,
480 }
481 }
482}
483
484pub struct MiriMachine<'tcx> {
489 pub tcx: TyCtxt<'tcx>,
491
492 pub borrow_tracker: Option<borrow_tracker::GlobalState>,
494
495 pub data_race: GlobalDataRaceHandler,
501
502 pub alloc_addresses: alloc_addresses::GlobalState,
504
505 pub(crate) env_vars: EnvVars<'tcx>,
507
508 pub(crate) main_fn_ret_place: Option<MPlaceTy<'tcx>>,
510
511 pub(crate) argc: Option<Pointer>,
515 pub(crate) argv: Option<Pointer>,
516 pub(crate) cmd_line: Option<Pointer>,
517
518 pub(crate) tls: TlsData<'tcx>,
520
521 pub(crate) isolated_op: IsolatedOp,
525
526 pub(crate) validation: ValidationMode,
528
529 pub(crate) fds: shims::FdTable,
531 pub(crate) dirs: shims::DirTable,
533
534 pub(crate) epoll_interests: shims::EpollInterestTable,
536
537 pub(crate) monotonic_clock: MonotonicClock,
539
540 pub(crate) threads: ThreadManager<'tcx>,
542
543 pub(crate) blocking_io: BlockingIoManager,
545
546 pub(crate) thread_cpu_affinity: FxHashMap<ThreadId, CpuAffinityMask>,
550
551 pub(crate) layouts: PrimitiveLayouts<'tcx>,
553
554 pub(crate) static_roots: Vec<AllocId>,
556
557 profiler: Option<measureme::Profiler>,
560 string_cache: FxHashMap<String, measureme::StringId>,
563
564 pub(crate) exported_symbols_cache: FxHashMap<Symbol, Option<Instance<'tcx>>>,
567
568 pub(crate) backtrace_style: BacktraceStyle,
570
571 pub(crate) user_relevant_crates: Vec<CrateNum>,
573
574 pub(crate) extern_statics: FxHashMap<Symbol, StrictPointer>,
576 pub(crate) missing_weak_symbol: Option<StrictPointer>,
578
579 pub(crate) rng: RefCell<StdRng>,
582
583 pub(crate) allocator: Option<Rc<RefCell<crate::alloc::isolated_alloc::IsolatedAlloc>>>,
585
586 pub(crate) tracked_alloc_ids: FxHashSet<AllocId>,
589 track_alloc_accesses: bool,
591
592 pub(crate) check_alignment: AlignmentCheck,
594
595 pub(crate) cmpxchg_weak_failure_rate: f64,
597
598 pub(crate) preemption_rate: f64,
600
601 pub(crate) report_progress: Option<u32>,
603 pub(crate) basic_block_count: u64,
605
606 #[cfg(all(feature = "native-lib", unix))]
608 pub native_lib: Vec<(libloading::Library, std::path::PathBuf)>,
609 #[cfg(not(all(feature = "native-lib", unix)))]
610 pub native_lib: Vec<!>,
611 #[cfg(all(feature = "native-lib", unix))]
613 pub native_lib_ecx_interchange: &'static Cell<usize>,
614
615 pub(crate) gc_interval: u32,
617 pub(crate) since_gc: u32,
619
620 pub(crate) num_cpus: u32,
622
623 pub(crate) page_size: u64,
625 pub(crate) stack_addr: u64,
626 pub(crate) stack_size: u64,
627
628 pub(crate) collect_leak_backtraces: bool,
630
631 pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>)>>,
634
635 pub(crate) symbolic_alignment: RefCell<FxHashMap<AllocId, (Size, Align)>>,
642
643 union_data_ranges: FxHashMap<Ty<'tcx>, RangeSet>,
645
646 pub(crate) pthread_mutex_sanity: Cell<bool>,
648 pub(crate) pthread_rwlock_sanity: Cell<bool>,
649 pub(crate) pthread_condvar_sanity: Cell<bool>,
650
651 pub(crate) allocator_shim_symbols: FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>>,
655 pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>,
657
658 pub force_intrinsic_fallback: bool,
660
661 pub float_nondet: bool,
663 pub float_rounding_error: FloatRoundingErrorMode,
665
666 pub short_fd_operations: bool,
668}
669
670impl<'tcx> MiriMachine<'tcx> {
671 pub(crate) fn new(
675 config: &MiriConfig,
676 layout_cx: LayoutCx<'tcx>,
677 genmc_ctx: Option<Rc<GenmcCtx>>,
678 ) -> Self {
679 let tcx = layout_cx.tcx();
680 let user_relevant_crates = Self::get_user_relevant_crates(tcx, config);
681 let layouts =
682 PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
683 let profiler = config.measureme_out.as_ref().map(|out| {
684 let crate_name =
685 tcx.sess.opts.crate_name.clone().unwrap_or_else(|| "unknown-crate".to_string());
686 let pid = process::id();
687 let filename = format!("{crate_name}-{pid:07}");
692 let path = Path::new(out).join(filename);
693 measureme::Profiler::new(path).expect("Couldn't create `measureme` profiler")
694 });
695 let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
696 let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config));
697 let data_race = if config.genmc_config.is_some() {
698 GlobalDataRaceHandler::Genmc(genmc_ctx.unwrap())
700 } else if config.data_race_detector {
701 GlobalDataRaceHandler::Vclocks(Box::new(data_race::GlobalState::new(config)))
702 } else {
703 GlobalDataRaceHandler::None
704 };
705 let page_size = if let Some(page_size) = config.page_size {
709 page_size
710 } else {
711 let target = &tcx.sess.target;
712 match target.arch {
713 Arch::Wasm32 | Arch::Wasm64 => 64 * 1024, Arch::AArch64 => {
715 if target.is_like_darwin {
716 16 * 1024
720 } else {
721 4 * 1024
722 }
723 }
724 _ => 4 * 1024,
725 }
726 };
727 let stack_addr = if tcx.pointer_size().bits() < 32 { page_size } else { page_size * 32 };
729 let stack_size =
730 if tcx.pointer_size().bits() < 32 { page_size * 4 } else { page_size * 16 };
731 assert!(
732 usize::try_from(config.num_cpus).unwrap() <= cpu_affinity::MAX_CPUS,
733 "miri only supports up to {} CPUs, but {} were configured",
734 cpu_affinity::MAX_CPUS,
735 config.num_cpus
736 );
737 let threads = ThreadManager::new(config);
738 let mut thread_cpu_affinity = FxHashMap::default();
739 if matches!(&tcx.sess.target.os, Os::Linux | Os::FreeBsd | Os::Android) {
740 thread_cpu_affinity
741 .insert(threads.active_thread(), CpuAffinityMask::new(&layout_cx, config.num_cpus));
742 }
743 let blocking_io = BlockingIoManager::new(config.isolated_op == IsolatedOp::Allow)
744 .expect("Couldn't create poll instance");
745 let alloc_addresses =
746 RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr, tcx));
747
748 MiriMachine {
749 tcx,
750 borrow_tracker,
751 data_race,
752 alloc_addresses,
753 env_vars: EnvVars::default(),
755 main_fn_ret_place: None,
756 argc: None,
757 argv: None,
758 cmd_line: None,
759 tls: TlsData::default(),
760 isolated_op: config.isolated_op,
761 validation: config.validation,
762 fds: shims::FdTable::init(config.mute_stdout_stderr),
763 epoll_interests: shims::EpollInterestTable::new(),
764 dirs: Default::default(),
765 layouts,
766 threads,
767 thread_cpu_affinity,
768 blocking_io,
769 static_roots: Vec::new(),
770 profiler,
771 string_cache: Default::default(),
772 exported_symbols_cache: FxHashMap::default(),
773 backtrace_style: config.backtrace_style,
774 user_relevant_crates,
775 extern_statics: FxHashMap::default(),
776 missing_weak_symbol: None,
777 rng: RefCell::new(rng),
778 allocator: (!config.native_lib.is_empty())
779 .then(|| Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new()))),
780 tracked_alloc_ids: config.tracked_alloc_ids.clone(),
781 track_alloc_accesses: config.track_alloc_accesses,
782 check_alignment: config.check_alignment,
783 cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
784 preemption_rate: config.preemption_rate,
785 report_progress: config.report_progress,
786 basic_block_count: 0,
787 monotonic_clock: MonotonicClock::new(config.isolated_op == IsolatedOp::Allow),
788 #[cfg(all(feature = "native-lib", unix))]
789 native_lib: config.native_lib.iter().map(|lib_file_path| {
790 let host_triple = rustc_session::config::host_tuple();
791 let target_triple = tcx.sess.opts.target_triple.tuple();
792 if host_triple != target_triple {
794 panic!(
795 "calling native C functions in linked .so file requires host and target to be the same: \
796 host={host_triple}, target={target_triple}",
797 );
798 }
799 (
803 unsafe {
804 libloading::Library::new(lib_file_path)
805 .expect("failed to read specified extern shared object file")
806 },
807 lib_file_path.clone(),
808 )
809 }).collect(),
810 #[cfg(all(feature = "native-lib", unix))]
811 native_lib_ecx_interchange: Box::leak(Box::new(Cell::new(0))),
812 #[cfg(not(all(feature = "native-lib", unix)))]
813 native_lib: config.native_lib.iter().map(|_| {
814 panic!("calling functions from native libraries via FFI is not supported in this build of Miri")
815 }).collect(),
816 gc_interval: config.gc_interval,
817 since_gc: 0,
818 num_cpus: config.num_cpus,
819 page_size,
820 stack_addr,
821 stack_size,
822 collect_leak_backtraces: config.collect_leak_backtraces,
823 allocation_spans: RefCell::new(FxHashMap::default()),
824 symbolic_alignment: RefCell::new(FxHashMap::default()),
825 union_data_ranges: FxHashMap::default(),
826 pthread_mutex_sanity: Cell::new(false),
827 pthread_rwlock_sanity: Cell::new(false),
828 pthread_condvar_sanity: Cell::new(false),
829 allocator_shim_symbols: Self::allocator_shim_symbols(tcx),
830 mangle_internal_symbol_cache: Default::default(),
831 force_intrinsic_fallback: config.force_intrinsic_fallback,
832 float_nondet: config.float_nondet,
833 float_rounding_error: config.float_rounding_error,
834 short_fd_operations: config.short_fd_operations,
835 }
836 }
837
838 fn allocator_shim_symbols(
839 tcx: TyCtxt<'tcx>,
840 ) -> FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>> {
841 use rustc_codegen_ssa::base::allocator_shim_contents;
842
843 let Some(kind) = tcx.allocator_kind(()) else {
846 return Default::default();
847 };
848 let methods = allocator_shim_contents(tcx, kind);
849 let mut symbols = FxHashMap::default();
850 for method in methods {
851 let from_name = Symbol::intern(&mangle_internal_symbol(
852 tcx,
853 &allocator::global_fn_name(method.name),
854 ));
855 let to = match method.special {
856 Some(special) => Either::Right(special),
857 None =>
858 Either::Left(Symbol::intern(&mangle_internal_symbol(
859 tcx,
860 &allocator::default_fn_name(method.name),
861 ))),
862 };
863 symbols.try_insert(from_name, to).unwrap();
864 }
865 symbols
866 }
867
868 fn get_user_relevant_crates(tcx: TyCtxt<'_>, config: &MiriConfig) -> Vec<CrateNum> {
871 let local_crate_names = std::env::var("MIRI_LOCAL_CRATES")
874 .map(|crates| crates.split(',').map(|krate| krate.to_string()).collect::<Vec<_>>())
875 .unwrap_or_default();
876 let mut local_crates = Vec::new();
877 for &crate_num in tcx.crates(()) {
878 let name = tcx.crate_name(crate_num);
879 let name = name.as_str();
880 if local_crate_names
881 .iter()
882 .chain(&config.user_relevant_crates)
883 .any(|local_name| local_name == name)
884 {
885 local_crates.push(crate_num);
886 }
887 }
888 local_crates
889 }
890
891 pub(crate) fn late_init(
892 ecx: &mut MiriInterpCx<'tcx>,
893 config: &MiriConfig,
894 on_main_stack_empty: StackEmptyCallback<'tcx>,
895 ) -> InterpResult<'tcx> {
896 EnvVars::init(ecx, config)?;
897 MiriMachine::init_extern_statics(ecx)?;
898 ThreadManager::init(ecx, on_main_stack_empty);
899 interp_ok(())
900 }
901
902 pub(crate) fn add_extern_static(ecx: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) {
903 let ptr = ptr.into_pointer_or_addr().unwrap();
905 ecx.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
906 }
907
908 pub(crate) fn communicate(&self) -> bool {
909 self.isolated_op == IsolatedOp::Allow
910 }
911
912 pub(crate) fn is_local(&self, instance: ty::Instance<'tcx>) -> bool {
914 let def_id = instance.def_id();
915 def_id.is_local() || self.user_relevant_crates.contains(&def_id.krate)
916 }
917
918 pub(crate) fn handle_abnormal_termination(&mut self) {
920 drop(self.profiler.take());
925 }
926
927 pub(crate) fn page_align(&self) -> Align {
928 Align::from_bytes(self.page_size).unwrap()
929 }
930
931 pub(crate) fn allocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
932 self.allocation_spans
933 .borrow()
934 .get(&alloc_id)
935 .map(|(allocated, _deallocated)| allocated.data())
936 }
937
938 pub(crate) fn deallocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
939 self.allocation_spans
940 .borrow()
941 .get(&alloc_id)
942 .and_then(|(_allocated, deallocated)| *deallocated)
943 .map(Span::data)
944 }
945
946 fn init_allocation(
947 ecx: &MiriInterpCx<'tcx>,
948 id: AllocId,
949 kind: MemoryKind,
950 size: Size,
951 align: Align,
952 ) -> InterpResult<'tcx, AllocExtra<'tcx>> {
953 if ecx.machine.tracked_alloc_ids.contains(&id) {
954 ecx.emit_diagnostic(NonHaltingDiagnostic::TrackingAlloc(id, size, align));
955 }
956
957 let borrow_tracker = ecx
958 .machine
959 .borrow_tracker
960 .as_ref()
961 .map(|bt| bt.borrow_mut().new_allocation(id, size, kind, &ecx.machine));
962
963 let data_race = match &ecx.machine.data_race {
964 GlobalDataRaceHandler::None => AllocDataRaceHandler::None,
965 GlobalDataRaceHandler::Vclocks(data_race) =>
966 AllocDataRaceHandler::Vclocks(
967 data_race::AllocState::new_allocation(
968 data_race,
969 &ecx.machine.threads,
970 size,
971 kind,
972 ecx.machine.current_user_relevant_span(),
973 ),
974 data_race.weak_memory.then(weak_memory::AllocState::new_allocation),
975 ),
976 GlobalDataRaceHandler::Genmc(_genmc_ctx) => {
977 AllocDataRaceHandler::Genmc
980 }
981 };
982
983 let backtrace = if kind.may_leak() || !ecx.machine.collect_leak_backtraces {
987 None
988 } else {
989 Some(ecx.generate_stacktrace())
990 };
991
992 if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) {
993 ecx.machine
994 .allocation_spans
995 .borrow_mut()
996 .insert(id, (ecx.machine.current_user_relevant_span(), None));
997 }
998
999 interp_ok(AllocExtra {
1000 borrow_tracker,
1001 data_race,
1002 backtrace,
1003 sync_objs: BTreeMap::default(),
1004 })
1005 }
1006}
1007
1008impl VisitProvenance for MiriMachine<'_> {
1009 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
1010 #[rustfmt::skip]
1011 let MiriMachine {
1012 threads,
1013 thread_cpu_affinity: _,
1014 tls,
1015 env_vars,
1016 main_fn_ret_place,
1017 argc,
1018 argv,
1019 cmd_line,
1020 extern_statics,
1021 missing_weak_symbol,
1022 dirs,
1023 borrow_tracker,
1024 data_race,
1025 alloc_addresses,
1026 fds,
1027 blocking_io:_,
1028 epoll_interests:_,
1029 tcx: _,
1030 isolated_op: _,
1031 validation: _,
1032 monotonic_clock: _,
1033 layouts: _,
1034 static_roots: _,
1035 profiler: _,
1036 string_cache: _,
1037 exported_symbols_cache: _,
1038 backtrace_style: _,
1039 user_relevant_crates: _,
1040 rng: _,
1041 allocator: _,
1042 tracked_alloc_ids: _,
1043 track_alloc_accesses: _,
1044 check_alignment: _,
1045 cmpxchg_weak_failure_rate: _,
1046 preemption_rate: _,
1047 report_progress: _,
1048 basic_block_count: _,
1049 native_lib: _,
1050 #[cfg(all(feature = "native-lib", unix))]
1051 native_lib_ecx_interchange: _,
1052 gc_interval: _,
1053 since_gc: _,
1054 num_cpus: _,
1055 page_size: _,
1056 stack_addr: _,
1057 stack_size: _,
1058 collect_leak_backtraces: _,
1059 allocation_spans: _,
1060 symbolic_alignment: _,
1061 union_data_ranges: _,
1062 pthread_mutex_sanity: _,
1063 pthread_rwlock_sanity: _,
1064 pthread_condvar_sanity: _,
1065 allocator_shim_symbols: _,
1066 mangle_internal_symbol_cache: _,
1067 force_intrinsic_fallback: _,
1068 float_nondet: _,
1069 float_rounding_error: _,
1070 short_fd_operations: _,
1071 } = self;
1072
1073 threads.visit_provenance(visit);
1074 tls.visit_provenance(visit);
1075 env_vars.visit_provenance(visit);
1076 dirs.visit_provenance(visit);
1077 fds.visit_provenance(visit);
1078 data_race.visit_provenance(visit);
1079 borrow_tracker.visit_provenance(visit);
1080 alloc_addresses.visit_provenance(visit);
1081 main_fn_ret_place.visit_provenance(visit);
1082 argc.visit_provenance(visit);
1083 argv.visit_provenance(visit);
1084 cmd_line.visit_provenance(visit);
1085 missing_weak_symbol.visit_provenance(visit);
1086 for ptr in extern_statics.values() {
1087 ptr.visit_provenance(visit);
1088 }
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 assert_panic(
1300 ecx: &mut MiriInterpCx<'tcx>,
1301 msg: &mir::AssertMessage<'tcx>,
1302 unwind: mir::UnwindAction,
1303 ) -> InterpResult<'tcx> {
1304 ecx.assert_panic(msg, unwind)
1305 }
1306
1307 fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> {
1308 ecx.start_panic_nounwind(msg)
1309 }
1310
1311 fn unwind_terminate(
1312 ecx: &mut InterpCx<'tcx, Self>,
1313 reason: mir::UnwindTerminateReason,
1314 ) -> InterpResult<'tcx> {
1315 let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap();
1317 let panic = ty::Instance::mono(ecx.tcx.tcx, panic);
1318 ecx.call_function(
1319 panic,
1320 ExternAbi::Rust,
1321 &[],
1322 None,
1323 ReturnContinuation::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
1324 )?;
1325 interp_ok(())
1326 }
1327
1328 #[inline(always)]
1329 fn binary_ptr_op(
1330 ecx: &MiriInterpCx<'tcx>,
1331 bin_op: mir::BinOp,
1332 left: &ImmTy<'tcx>,
1333 right: &ImmTy<'tcx>,
1334 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1335 ecx.binary_ptr_op(bin_op, left, right)
1336 }
1337
1338 #[inline(always)]
1339 fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(
1340 ecx: &InterpCx<'tcx, Self>,
1341 inputs: &[F1],
1342 ) -> F2 {
1343 ecx.generate_nan(inputs)
1344 }
1345
1346 #[inline(always)]
1347 fn apply_float_nondet(
1348 ecx: &mut InterpCx<'tcx, Self>,
1349 val: ImmTy<'tcx>,
1350 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1351 crate::math::apply_random_float_error_to_imm(ecx, val, 4)
1352 }
1353
1354 #[inline(always)]
1355 fn equal_float_min_max<F: Float>(ecx: &MiriInterpCx<'tcx>, a: F, b: F) -> F {
1356 ecx.equal_float_min_max(a, b)
1357 }
1358
1359 #[inline(always)]
1360 fn float_fuse_mul_add(ecx: &InterpCx<'tcx, Self>) -> bool {
1361 ecx.machine.float_nondet && ecx.machine.rng.borrow_mut().random()
1362 }
1363
1364 #[inline(always)]
1365 fn runtime_checks(
1366 ecx: &InterpCx<'tcx, Self>,
1367 r: mir::RuntimeChecks,
1368 ) -> InterpResult<'tcx, bool> {
1369 interp_ok(r.value(ecx.tcx.sess))
1370 }
1371
1372 #[inline(always)]
1373 fn thread_local_static_pointer(
1374 ecx: &mut MiriInterpCx<'tcx>,
1375 def_id: DefId,
1376 ) -> InterpResult<'tcx, StrictPointer> {
1377 ecx.get_or_create_thread_local_alloc(def_id)
1378 }
1379
1380 fn extern_static_pointer(
1381 ecx: &MiriInterpCx<'tcx>,
1382 def_id: DefId,
1383 ) -> InterpResult<'tcx, StrictPointer> {
1384 let link_name = Symbol::intern(ecx.tcx.symbol_name(Instance::mono(*ecx.tcx, def_id)).name);
1385 let def_ty = ecx.tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
1386 let extern_decl_layout =
1387 ecx.tcx.layout_of(ecx.typing_env().as_query_input(def_ty)).unwrap();
1388
1389 if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) {
1390 let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
1394 panic!("extern_statics cannot contain wildcards")
1395 };
1396 let info = ecx.get_alloc_info(alloc_id);
1397 if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align {
1398 throw_unsup_format!(
1399 "extern static `{link_name}` has been declared as `{krate}::{name}` \
1400 with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
1401 but Miri emulates it via an extern static shim \
1402 with a size of {shim_size} bytes and alignment of {shim_align} bytes",
1403 name = ecx.tcx.def_path_str(def_id),
1404 krate = ecx.tcx.crate_name(def_id.krate),
1405 decl_size = extern_decl_layout.size.bytes(),
1406 decl_align = extern_decl_layout.align.bytes(),
1407 shim_size = info.size.bytes(),
1408 shim_align = info.align.bytes(),
1409 )
1410 }
1411 interp_ok(ptr)
1412 } else if ecx.tcx.codegen_fn_attrs(def_id).import_linkage == Some(Linkage::ExternalWeak) {
1413 assert_eq!(
1421 extern_decl_layout.size,
1422 ecx.tcx.data_layout.pointer_size(),
1423 "non-pointer-sized weak static"
1424 );
1425 interp_ok(
1426 ecx.machine
1427 .missing_weak_symbol
1428 .expect("`missing_weak_symbol` should have been initialized"),
1429 )
1430 } else {
1431 throw_unsup_format!("extern static `{link_name}` is not supported by Miri")
1432 }
1433 }
1434
1435 fn init_local_allocation(
1436 ecx: &MiriInterpCx<'tcx>,
1437 id: AllocId,
1438 kind: MemoryKind,
1439 size: Size,
1440 align: Align,
1441 ) -> InterpResult<'tcx, Self::AllocExtra> {
1442 assert!(kind != MiriMemoryKind::Global.into());
1443 MiriMachine::init_allocation(ecx, id, kind, size, align)
1444 }
1445
1446 fn adjust_alloc_root_pointer(
1447 ecx: &MiriInterpCx<'tcx>,
1448 ptr: interpret::Pointer<CtfeProvenance>,
1449 kind: Option<MemoryKind>,
1450 ) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
1451 let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None");
1452 let alloc_id = ptr.provenance.alloc_id();
1453 if cfg!(debug_assertions) {
1454 match ecx.tcx.try_get_global_alloc(alloc_id) {
1456 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
1457 panic!("adjust_alloc_root_pointer called on thread-local static")
1458 }
1459 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => {
1460 panic!("adjust_alloc_root_pointer called on extern static")
1461 }
1462 _ => {}
1463 }
1464 }
1465 let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
1467 borrow_tracker.borrow_mut().root_ptr_tag(alloc_id, &ecx.machine)
1468 } else {
1469 BorTag::default()
1471 };
1472 ecx.adjust_alloc_root_pointer(ptr, tag, kind)
1473 }
1474
1475 #[inline(always)]
1477 fn ptr_from_addr_cast(ecx: &MiriInterpCx<'tcx>, addr: u64) -> InterpResult<'tcx, Pointer> {
1478 ecx.ptr_from_addr_cast(addr)
1479 }
1480
1481 #[inline(always)]
1485 fn expose_provenance(
1486 ecx: &InterpCx<'tcx, Self>,
1487 provenance: Self::Provenance,
1488 ) -> InterpResult<'tcx> {
1489 ecx.expose_provenance(provenance)
1490 }
1491
1492 fn ptr_get_alloc(
1504 ecx: &MiriInterpCx<'tcx>,
1505 ptr: StrictPointer,
1506 size: i64,
1507 ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
1508 let rel = ecx.ptr_get_alloc(ptr, size);
1509
1510 rel.map(|(alloc_id, size)| {
1511 let tag = match ptr.provenance {
1512 Provenance::Concrete { tag, .. } => ProvenanceExtra::Concrete(tag),
1513 Provenance::Wildcard => ProvenanceExtra::Wildcard,
1514 };
1515 (alloc_id, size, tag)
1516 })
1517 }
1518
1519 fn adjust_global_allocation<'b>(
1528 ecx: &InterpCx<'tcx, Self>,
1529 id: AllocId,
1530 alloc: &'b Allocation,
1531 ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
1532 {
1533 let alloc = alloc.adjust_from_tcx(
1534 &ecx.tcx,
1535 |bytes, align| ecx.get_global_alloc_bytes(id, bytes, align),
1536 |ptr| ecx.global_root_pointer(ptr),
1537 )?;
1538 let kind = MiriMemoryKind::Global.into();
1539 let extra = MiriMachine::init_allocation(ecx, id, kind, alloc.size(), alloc.align)?;
1540 interp_ok(Cow::Owned(alloc.with_extra(extra)))
1541 }
1542
1543 #[inline(always)]
1544 fn before_memory_read(
1545 _tcx: TyCtxtAt<'tcx>,
1546 machine: &Self,
1547 alloc_extra: &AllocExtra<'tcx>,
1548 ptr: Pointer,
1549 (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1550 range: AllocRange,
1551 ) -> InterpResult<'tcx> {
1552 if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1553 machine.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(
1554 alloc_id,
1555 range,
1556 borrow_tracker::AccessKind::Read,
1557 ));
1558 }
1559 match &machine.data_race {
1561 GlobalDataRaceHandler::None => {}
1562 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1563 genmc_ctx.memory_load(machine, ptr.addr(), range.size)?,
1564 GlobalDataRaceHandler::Vclocks(_data_race) => {
1565 let _trace = enter_trace_span!(data_race::before_memory_read);
1566 let AllocDataRaceHandler::Vclocks(data_race, _weak_memory) = &alloc_extra.data_race
1567 else {
1568 unreachable!();
1569 };
1570 data_race.read_non_atomic(alloc_id, range, NaReadType::Read, None, machine)?;
1571 }
1572 }
1573 if let Some(borrow_tracker) = &alloc_extra.borrow_tracker {
1574 borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?;
1575 }
1576 for (_offset, obj) in alloc_extra.sync_objs.range(range.start..range.end()) {
1578 obj.on_access(concurrency::sync::AccessKind::Read)?;
1579 }
1580
1581 interp_ok(())
1582 }
1583
1584 #[inline(always)]
1585 fn before_memory_write(
1586 _tcx: TyCtxtAt<'tcx>,
1587 machine: &mut Self,
1588 alloc_extra: &mut AllocExtra<'tcx>,
1589 ptr: Pointer,
1590 (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1591 range: AllocRange,
1592 ) -> InterpResult<'tcx> {
1593 if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1594 machine.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(
1595 alloc_id,
1596 range,
1597 borrow_tracker::AccessKind::Write,
1598 ));
1599 }
1600 match &machine.data_race {
1601 GlobalDataRaceHandler::None => {}
1602 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1603 genmc_ctx.memory_store(machine, ptr.addr(), range.size)?,
1604 GlobalDataRaceHandler::Vclocks(_global_state) => {
1605 let _trace = enter_trace_span!(data_race::before_memory_write);
1606 let AllocDataRaceHandler::Vclocks(data_race, weak_memory) =
1607 &mut alloc_extra.data_race
1608 else {
1609 unreachable!()
1610 };
1611 data_race.write_non_atomic(alloc_id, range, NaWriteType::Write, None, machine)?;
1612 if let Some(weak_memory) = weak_memory {
1613 weak_memory
1614 .non_atomic_write(range, machine.data_race.as_vclocks_ref().unwrap());
1615 }
1616 }
1617 }
1618 if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1619 borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?;
1620 }
1621 if !alloc_extra.sync_objs.is_empty() {
1624 let mut to_delete = vec![];
1625 for (offset, obj) in alloc_extra.sync_objs.range(range.start..range.end()) {
1626 obj.on_access(concurrency::sync::AccessKind::Write)?;
1627 if obj.delete_on_write() {
1628 to_delete.push(*offset);
1629 }
1630 }
1631 for offset in to_delete {
1632 alloc_extra.sync_objs.remove(&offset);
1633 }
1634 }
1635 interp_ok(())
1636 }
1637
1638 #[inline(always)]
1639 fn before_memory_deallocation(
1640 _tcx: TyCtxtAt<'tcx>,
1641 machine: &mut Self,
1642 alloc_extra: &mut AllocExtra<'tcx>,
1643 ptr: Pointer,
1644 (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
1645 size: Size,
1646 align: Align,
1647 kind: MemoryKind,
1648 ) -> InterpResult<'tcx> {
1649 if machine.tracked_alloc_ids.contains(&alloc_id) {
1650 machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
1651 }
1652 match &machine.data_race {
1653 GlobalDataRaceHandler::None => {}
1654 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1655 genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), kind)?,
1656 GlobalDataRaceHandler::Vclocks(_global_state) => {
1657 let _trace = enter_trace_span!(data_race::before_memory_deallocation);
1658 let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap();
1659 data_race.write_non_atomic(
1660 alloc_id,
1661 alloc_range(Size::ZERO, size),
1662 NaWriteType::Deallocate,
1663 None,
1664 machine,
1665 )?;
1666 }
1667 }
1668 if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1669 borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?;
1670 }
1671 for obj in alloc_extra.sync_objs.values() {
1673 obj.on_access(concurrency::sync::AccessKind::Dealloc)?;
1674 }
1675
1676 if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
1677 {
1678 *deallocated_at = Some(machine.current_user_relevant_span());
1679 }
1680 machine.free_alloc_id(alloc_id, size, align, kind);
1681 interp_ok(())
1682 }
1683
1684 #[inline(always)]
1685 fn retag_ptr_value(
1686 ecx: &mut InterpCx<'tcx, Self>,
1687 val: &ImmTy<'tcx>,
1688 ty: Ty<'tcx>,
1689 ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
1690 if ecx.machine.borrow_tracker.is_some() {
1691 ecx.retag_ptr_value(val, ty)
1692 } else {
1693 interp_ok(None)
1694 }
1695 }
1696
1697 #[inline(always)]
1698 fn with_retag_mode<T>(
1699 ecx: &mut InterpCx<'tcx, Self>,
1700 mode: RetagMode,
1701 f: impl FnOnce(&mut InterpCx<'tcx, Self>) -> InterpResult<'tcx, T>,
1702 ) -> InterpResult<'tcx, T> {
1703 if ecx.machine.borrow_tracker.is_some() { ecx.with_retag_mode(mode, f) } else { f(ecx) }
1704 }
1705
1706 fn protect_in_place_function_argument(
1707 ecx: &mut InterpCx<'tcx, Self>,
1708 place: &MPlaceTy<'tcx>,
1709 ) -> InterpResult<'tcx> {
1710 let protected_place = if ecx.machine.borrow_tracker.is_some() {
1713 ecx.protect_place(place)?
1714 } else {
1715 place.clone()
1717 };
1718 ecx.write_uninit(&protected_place)?;
1723 interp_ok(())
1725 }
1726
1727 #[inline(always)]
1728 fn init_frame(
1729 ecx: &mut InterpCx<'tcx, Self>,
1730 frame: Frame<'tcx, Provenance>,
1731 ) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
1732 let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
1734 let fn_name = frame.instance().to_string();
1735 let entry = ecx.machine.string_cache.entry(fn_name.clone());
1736 let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
1737
1738 Some(profiler.start_recording_interval_event_detached(
1739 *name,
1740 measureme::EventId::from_label(*name),
1741 ecx.active_thread().to_u32(),
1742 ))
1743 } else {
1744 None
1745 };
1746
1747 let borrow_tracker = ecx.machine.borrow_tracker.as_ref();
1748
1749 let extra = FrameExtra {
1750 borrow_tracker: borrow_tracker.map(|bt| bt.borrow_mut().new_frame()),
1751 catch_unwind: None,
1752 timing,
1753 user_relevance: ecx.machine.user_relevance(&frame),
1754 data_race: ecx
1755 .machine
1756 .data_race
1757 .as_vclocks_ref()
1758 .map(|_| data_race::FrameState::default()),
1759 };
1760
1761 interp_ok(frame.with_extra(extra))
1762 }
1763
1764 fn stack<'a>(
1765 ecx: &'a InterpCx<'tcx, Self>,
1766 ) -> &'a [Frame<'tcx, Self::Provenance, Self::FrameExtra>] {
1767 ecx.active_thread_stack()
1768 }
1769
1770 fn stack_mut<'a>(
1771 ecx: &'a mut InterpCx<'tcx, Self>,
1772 ) -> &'a mut Vec<Frame<'tcx, Self::Provenance, Self::FrameExtra>> {
1773 ecx.active_thread_stack_mut()
1774 }
1775
1776 fn before_terminator(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1777 ecx.machine.basic_block_count += 1u64; ecx.machine.since_gc += 1;
1779 if let Some(report_progress) = ecx.machine.report_progress {
1781 if ecx.machine.basic_block_count.is_multiple_of(u64::from(report_progress)) {
1782 ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport {
1783 block_count: ecx.machine.basic_block_count,
1784 });
1785 }
1786 }
1787
1788 if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
1793 ecx.machine.since_gc = 0;
1794 ecx.run_provenance_gc();
1795 }
1796
1797 ecx.maybe_preempt_active_thread();
1800
1801 ecx.machine.monotonic_clock.tick();
1803
1804 interp_ok(())
1805 }
1806
1807 #[inline(always)]
1808 fn after_stack_push(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1809 if ecx.frame().extra.user_relevance >= ecx.active_thread_ref().current_user_relevance() {
1810 let stack_len = ecx.active_thread_stack().len();
1813 ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
1814 }
1815 interp_ok(())
1816 }
1817
1818 fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1819 let frame = ecx.frame();
1820 if ecx.machine.borrow_tracker.is_some() {
1823 ecx.on_stack_pop(frame)?;
1824 }
1825 if ecx
1826 .active_thread_ref()
1827 .top_user_relevant_frame()
1828 .expect("there should always be a most relevant frame for a non-empty stack")
1829 == ecx.frame_idx()
1830 {
1831 ecx.active_thread_mut().recompute_top_user_relevant_frame(1);
1837 }
1838 info!("Leaving {}", ecx.frame().instance());
1842 interp_ok(())
1843 }
1844
1845 #[inline(always)]
1846 fn after_stack_pop(
1847 ecx: &mut InterpCx<'tcx, Self>,
1848 frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1849 unwinding: bool,
1850 ) -> InterpResult<'tcx, ReturnAction> {
1851 let res = {
1852 let mut frame = frame;
1854 let timing = frame.extra.timing.take();
1855 let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
1856 if let Some(profiler) = ecx.machine.profiler.as_ref() {
1857 profiler.finish_recording_interval_event(timing.unwrap());
1858 }
1859 res
1860 };
1861 if !ecx.active_thread_stack().is_empty() {
1864 info!("Continuing in {}", ecx.frame().instance());
1865 }
1866 res
1867 }
1868
1869 fn after_local_read(
1870 ecx: &InterpCx<'tcx, Self>,
1871 frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1872 local: mir::Local,
1873 ) -> InterpResult<'tcx> {
1874 if let Some(data_race) = &frame.extra.data_race {
1875 let _trace = enter_trace_span!(data_race::after_local_read);
1876 data_race.local_read(local, &ecx.machine);
1877 }
1878 interp_ok(())
1879 }
1880
1881 fn after_local_write(
1882 ecx: &mut InterpCx<'tcx, Self>,
1883 local: mir::Local,
1884 storage_live: bool,
1885 ) -> InterpResult<'tcx> {
1886 if let Some(data_race) = &ecx.frame().extra.data_race {
1887 let _trace = enter_trace_span!(data_race::after_local_write);
1888 data_race.local_write(local, storage_live, &ecx.machine);
1889 }
1890 interp_ok(())
1891 }
1892
1893 fn after_local_moved_to_memory(
1894 ecx: &mut InterpCx<'tcx, Self>,
1895 local: mir::Local,
1896 mplace: &MPlaceTy<'tcx>,
1897 ) -> InterpResult<'tcx> {
1898 let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else {
1899 panic!("after_local_allocated should only be called on fresh allocations");
1900 };
1901 let local_decl = &ecx.frame().body().local_decls[local];
1903 let span = local_decl.source_info.span;
1904 ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
1905 let (alloc_info, machine) = ecx.get_alloc_extra_mut(alloc_id)?;
1907 if let Some(data_race) =
1908 &machine.threads.active_thread_stack().last().unwrap().extra.data_race
1909 {
1910 let _trace = enter_trace_span!(data_race::after_local_moved_to_memory);
1911 data_race.local_moved_to_memory(
1912 local,
1913 alloc_info.data_race.as_vclocks_mut().unwrap(),
1914 machine,
1915 );
1916 }
1917 interp_ok(())
1918 }
1919
1920 fn get_global_alloc_salt(
1921 ecx: &InterpCx<'tcx, Self>,
1922 instance: Option<ty::Instance<'tcx>>,
1923 ) -> usize {
1924 let unique = if let Some(instance) = instance {
1925 let is_generic = instance
1938 .args
1939 .into_iter()
1940 .any(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_)));
1941 let can_be_inlined = matches!(
1942 ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
1943 InliningThreshold::Always
1944 ) || !matches!(
1945 ecx.tcx.codegen_instance_attrs(instance.def).inline,
1946 InlineAttr::Never
1947 );
1948 !is_generic && !can_be_inlined
1949 } else {
1950 false
1952 };
1953 if unique {
1955 CTFE_ALLOC_SALT
1956 } else {
1957 ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL)
1958 }
1959 }
1960
1961 fn cached_union_data_range<'e>(
1962 ecx: &'e mut InterpCx<'tcx, Self>,
1963 ty: Ty<'tcx>,
1964 compute_range: impl FnOnce() -> RangeSet,
1965 ) -> Cow<'e, RangeSet> {
1966 Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range))
1967 }
1968
1969 fn get_default_alloc_params(&self) -> <Self::Bytes as AllocBytes>::AllocParams {
1970 use crate::alloc::MiriAllocParams;
1971
1972 match &self.allocator {
1973 Some(alloc) => MiriAllocParams::Isolated(alloc.clone()),
1974 None => MiriAllocParams::Global,
1975 }
1976 }
1977
1978 fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan {
1979 #[cfg(feature = "tracing")]
1980 {
1981 span().entered()
1982 }
1983 #[cfg(not(feature = "tracing"))]
1984 #[expect(clippy::unused_unit)]
1985 {
1986 let _ = span; ()
1988 }
1989 }
1990}
1991
1992pub trait MachineCallback<'tcx, T>: VisitProvenance {
1994 fn call(
1996 self: Box<Self>,
1997 ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
1998 arg: T,
1999 ) -> InterpResult<'tcx>;
2000}
2001
2002pub type DynMachineCallback<'tcx, T> = Box<dyn MachineCallback<'tcx, T> + 'tcx>;
2004
2005#[macro_export]
2022macro_rules! callback {
2023 (@capture<$tcx:lifetime $(,)? $($lft:lifetime),*>
2024 { $($name:ident: $type:ty),* $(,)? }
2025 |$this:ident, $arg:ident: $arg_ty:ty| $body:expr $(,)?) => {{
2026 struct Callback<$tcx, $($lft),*> {
2027 $($name: $type,)*
2028 _phantom: std::marker::PhantomData<&$tcx ()>,
2029 }
2030
2031 impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> {
2032 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
2033 $(
2034 self.$name.visit_provenance(_visit);
2035 )*
2036 }
2037 }
2038
2039 impl<$tcx, $($lft),*> MachineCallback<$tcx, $arg_ty> for Callback<$tcx, $($lft),*> {
2040 fn call(
2041 self: Box<Self>,
2042 $this: &mut MiriInterpCx<$tcx>,
2043 $arg: $arg_ty
2044 ) -> InterpResult<$tcx> {
2045 #[allow(unused_variables)]
2046 let Callback { $($name,)* _phantom } = *self;
2047 $body
2048 }
2049 }
2050
2051 Box::new(Callback {
2052 $($name,)*
2053 _phantom: std::marker::PhantomData
2054 })
2055 }};
2056}