1use std::any::Any;
5use std::borrow::Cow;
6use std::cell::{Cell, RefCell};
7use std::collections::hash_map::Entry;
8use std::path::Path;
9use std::rc::Rc;
10use std::{fmt, process};
11
12use rand::rngs::StdRng;
13use rand::{Rng, SeedableRng};
14use rustc_abi::{Align, ExternAbi, Size};
15use rustc_apfloat::{Float, FloatConvert};
16use rustc_attr_data_structures::InlineAttr;
17use rustc_data_structures::fx::{FxHashMap, FxHashSet};
18#[allow(unused)]
19use rustc_data_structures::static_assert_size;
20use rustc_middle::mir;
21use rustc_middle::query::TyCtxtAt;
22use rustc_middle::ty::layout::{
23 HasTyCtxt, HasTypingEnv, LayoutCx, LayoutError, LayoutOf, TyAndLayout,
24};
25use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
26use rustc_session::config::InliningThreshold;
27use rustc_span::def_id::{CrateNum, DefId};
28use rustc_span::{Span, SpanData, Symbol};
29use rustc_target::callconv::FnAbi;
30
31use crate::alloc_addresses::EvalContextExt;
32use crate::concurrency::cpu_affinity::{self, CpuAffinityMask};
33use crate::concurrency::data_race::{self, NaReadType, NaWriteType};
34use crate::concurrency::{AllocDataRaceHandler, GenmcCtx, GlobalDataRaceHandler, weak_memory};
35use crate::*;
36
37pub const SIGRTMIN: i32 = 34;
41
42pub const SIGRTMAX: i32 = 42;
46
47const ADDRS_PER_ANON_GLOBAL: usize = 32;
51
52pub struct FrameExtra<'tcx> {
54 pub borrow_tracker: Option<borrow_tracker::FrameState>,
56
57 pub catch_unwind: Option<CatchUnwindData<'tcx>>,
61
62 pub timing: Option<measureme::DetachedTiming>,
66
67 pub is_user_relevant: bool,
72
73 salt: usize,
78
79 pub data_race: Option<data_race::FrameState>,
81}
82
83impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 let FrameExtra {
87 borrow_tracker,
88 catch_unwind,
89 timing: _,
90 is_user_relevant,
91 salt,
92 data_race,
93 } = self;
94 f.debug_struct("FrameData")
95 .field("borrow_tracker", borrow_tracker)
96 .field("catch_unwind", catch_unwind)
97 .field("is_user_relevant", is_user_relevant)
98 .field("salt", salt)
99 .field("data_race", data_race)
100 .finish()
101 }
102}
103
104impl VisitProvenance for FrameExtra<'_> {
105 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
106 let FrameExtra {
107 catch_unwind,
108 borrow_tracker,
109 timing: _,
110 is_user_relevant: _,
111 salt: _,
112 data_race: _,
113 } = self;
114
115 catch_unwind.visit_provenance(visit);
116 borrow_tracker.visit_provenance(visit);
117 }
118}
119
120#[derive(Debug, Copy, Clone, PartialEq, Eq)]
122pub enum MiriMemoryKind {
123 Rust,
125 Miri,
127 C,
129 WinHeap,
131 WinLocal,
133 Machine,
136 Runtime,
139 Global,
142 ExternStatic,
145 Tls,
148 Mmap,
150}
151
152impl From<MiriMemoryKind> for MemoryKind {
153 #[inline(always)]
154 fn from(kind: MiriMemoryKind) -> MemoryKind {
155 MemoryKind::Machine(kind)
156 }
157}
158
159impl MayLeak for MiriMemoryKind {
160 #[inline(always)]
161 fn may_leak(self) -> bool {
162 use self::MiriMemoryKind::*;
163 match self {
164 Rust | Miri | C | WinHeap | WinLocal | Runtime => false,
165 Machine | Global | ExternStatic | Tls | Mmap => true,
166 }
167 }
168}
169
170impl MiriMemoryKind {
171 fn should_save_allocation_span(self) -> bool {
173 use self::MiriMemoryKind::*;
174 match self {
175 Rust | Miri | C | WinHeap | WinLocal | Mmap => true,
177 Machine | Global | ExternStatic | Tls | Runtime => false,
179 }
180 }
181}
182
183impl fmt::Display for MiriMemoryKind {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 use self::MiriMemoryKind::*;
186 match self {
187 Rust => write!(f, "Rust heap"),
188 Miri => write!(f, "Miri bare-metal heap"),
189 C => write!(f, "C heap"),
190 WinHeap => write!(f, "Windows heap"),
191 WinLocal => write!(f, "Windows local memory"),
192 Machine => write!(f, "machine-managed memory"),
193 Runtime => write!(f, "language runtime memory"),
194 Global => write!(f, "global (static or const)"),
195 ExternStatic => write!(f, "extern static"),
196 Tls => write!(f, "thread-local static"),
197 Mmap => write!(f, "mmap"),
198 }
199 }
200}
201
202pub type MemoryKind = interpret::MemoryKind<MiriMemoryKind>;
203
204#[derive(Clone, Copy, PartialEq, Eq, Hash)]
210pub enum Provenance {
211 Concrete {
214 alloc_id: AllocId,
215 tag: BorTag,
217 },
218 Wildcard,
235}
236
237#[derive(Copy, Clone, PartialEq)]
239pub enum ProvenanceExtra {
240 Concrete(BorTag),
241 Wildcard,
242}
243
244#[cfg(target_pointer_width = "64")]
245static_assert_size!(StrictPointer, 24);
246#[cfg(target_pointer_width = "64")]
250static_assert_size!(Scalar, 32);
251
252impl fmt::Debug for Provenance {
253 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254 match self {
255 Provenance::Concrete { alloc_id, tag } => {
256 if f.alternate() {
258 write!(f, "[{alloc_id:#?}]")?;
259 } else {
260 write!(f, "[{alloc_id:?}]")?;
261 }
262 write!(f, "{tag:?}")?;
264 }
265 Provenance::Wildcard => {
266 write!(f, "[wildcard]")?;
267 }
268 }
269 Ok(())
270 }
271}
272
273impl interpret::Provenance for Provenance {
274 const OFFSET_IS_ADDR: bool = true;
276
277 const WILDCARD: Option<Self> = Some(Provenance::Wildcard);
279
280 fn get_alloc_id(self) -> Option<AllocId> {
281 match self {
282 Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
283 Provenance::Wildcard => None,
284 }
285 }
286
287 fn fmt(ptr: &interpret::Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 let (prov, addr) = ptr.into_parts(); write!(f, "{:#x}", addr.bytes())?;
290 if f.alternate() {
291 write!(f, "{prov:#?}")?;
292 } else {
293 write!(f, "{prov:?}")?;
294 }
295 Ok(())
296 }
297
298 fn join(left: Option<Self>, right: Option<Self>) -> Option<Self> {
299 match (left, right) {
300 (
302 Some(Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }),
303 Some(Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }),
304 ) if left_alloc == right_alloc && left_tag == right_tag => left,
305 (Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o,
308 _ => None,
310 }
311 }
312}
313
314impl fmt::Debug for ProvenanceExtra {
315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316 match self {
317 ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"),
318 ProvenanceExtra::Wildcard => write!(f, "<wildcard>"),
319 }
320 }
321}
322
323impl ProvenanceExtra {
324 pub fn and_then<T>(self, f: impl FnOnce(BorTag) -> Option<T>) -> Option<T> {
325 match self {
326 ProvenanceExtra::Concrete(pid) => f(pid),
327 ProvenanceExtra::Wildcard => None,
328 }
329 }
330}
331
332#[derive(Debug)]
334pub struct AllocExtra<'tcx> {
335 pub borrow_tracker: Option<borrow_tracker::AllocState>,
337 pub data_race: AllocDataRaceHandler,
341 pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
346 pub sync: FxHashMap<Size, Box<dyn Any>>,
351}
352
353impl<'tcx> Clone for AllocExtra<'tcx> {
356 fn clone(&self) -> Self {
357 panic!("our allocations should never be cloned");
358 }
359}
360
361impl VisitProvenance for AllocExtra<'_> {
362 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
363 let AllocExtra { borrow_tracker, data_race, backtrace: _, sync: _ } = self;
364
365 borrow_tracker.visit_provenance(visit);
366 data_race.visit_provenance(visit);
367 }
368}
369
370pub struct PrimitiveLayouts<'tcx> {
372 pub unit: TyAndLayout<'tcx>,
373 pub i8: TyAndLayout<'tcx>,
374 pub i16: TyAndLayout<'tcx>,
375 pub i32: TyAndLayout<'tcx>,
376 pub i64: TyAndLayout<'tcx>,
377 pub i128: TyAndLayout<'tcx>,
378 pub isize: TyAndLayout<'tcx>,
379 pub u8: TyAndLayout<'tcx>,
380 pub u16: TyAndLayout<'tcx>,
381 pub u32: TyAndLayout<'tcx>,
382 pub u64: TyAndLayout<'tcx>,
383 pub u128: TyAndLayout<'tcx>,
384 pub usize: TyAndLayout<'tcx>,
385 pub bool: TyAndLayout<'tcx>,
386 pub mut_raw_ptr: TyAndLayout<'tcx>, pub const_raw_ptr: TyAndLayout<'tcx>, }
389
390impl<'tcx> PrimitiveLayouts<'tcx> {
391 fn new(layout_cx: LayoutCx<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
392 let tcx = layout_cx.tcx();
393 let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit);
394 let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
395 Ok(Self {
396 unit: layout_cx.layout_of(tcx.types.unit)?,
397 i8: layout_cx.layout_of(tcx.types.i8)?,
398 i16: layout_cx.layout_of(tcx.types.i16)?,
399 i32: layout_cx.layout_of(tcx.types.i32)?,
400 i64: layout_cx.layout_of(tcx.types.i64)?,
401 i128: layout_cx.layout_of(tcx.types.i128)?,
402 isize: layout_cx.layout_of(tcx.types.isize)?,
403 u8: layout_cx.layout_of(tcx.types.u8)?,
404 u16: layout_cx.layout_of(tcx.types.u16)?,
405 u32: layout_cx.layout_of(tcx.types.u32)?,
406 u64: layout_cx.layout_of(tcx.types.u64)?,
407 u128: layout_cx.layout_of(tcx.types.u128)?,
408 usize: layout_cx.layout_of(tcx.types.usize)?,
409 bool: layout_cx.layout_of(tcx.types.bool)?,
410 mut_raw_ptr: layout_cx.layout_of(mut_raw_ptr)?,
411 const_raw_ptr: layout_cx.layout_of(const_raw_ptr)?,
412 })
413 }
414
415 pub fn uint(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
416 match size.bits() {
417 8 => Some(self.u8),
418 16 => Some(self.u16),
419 32 => Some(self.u32),
420 64 => Some(self.u64),
421 128 => Some(self.u128),
422 _ => None,
423 }
424 }
425
426 pub fn int(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
427 match size.bits() {
428 8 => Some(self.i8),
429 16 => Some(self.i16),
430 32 => Some(self.i32),
431 64 => Some(self.i64),
432 128 => Some(self.i128),
433 _ => None,
434 }
435 }
436}
437
438pub struct MiriMachine<'tcx> {
443 pub tcx: TyCtxt<'tcx>,
445
446 pub borrow_tracker: Option<borrow_tracker::GlobalState>,
448
449 pub data_race: GlobalDataRaceHandler,
455
456 pub alloc_addresses: alloc_addresses::GlobalState,
458
459 pub(crate) env_vars: EnvVars<'tcx>,
461
462 pub(crate) main_fn_ret_place: Option<MPlaceTy<'tcx>>,
464
465 pub(crate) argc: Option<Pointer>,
469 pub(crate) argv: Option<Pointer>,
470 pub(crate) cmd_line: Option<Pointer>,
471
472 pub(crate) tls: TlsData<'tcx>,
474
475 pub(crate) isolated_op: IsolatedOp,
479
480 pub(crate) validation: ValidationMode,
482
483 pub(crate) fds: shims::FdTable,
485 pub(crate) dirs: shims::DirTable,
487
488 pub(crate) epoll_interests: shims::EpollInterestTable,
490
491 pub(crate) monotonic_clock: MonotonicClock,
493
494 pub(crate) threads: ThreadManager<'tcx>,
496
497 pub(crate) thread_cpu_affinity: FxHashMap<ThreadId, CpuAffinityMask>,
501
502 pub(crate) sync: SynchronizationObjects,
504
505 pub(crate) layouts: PrimitiveLayouts<'tcx>,
507
508 pub(crate) static_roots: Vec<AllocId>,
510
511 profiler: Option<measureme::Profiler>,
514 string_cache: FxHashMap<String, measureme::StringId>,
517
518 pub(crate) exported_symbols_cache: FxHashMap<Symbol, Option<Instance<'tcx>>>,
521
522 pub(crate) backtrace_style: BacktraceStyle,
524
525 pub(crate) local_crates: Vec<CrateNum>,
527
528 extern_statics: FxHashMap<Symbol, StrictPointer>,
530
531 pub(crate) rng: RefCell<StdRng>,
534
535 #[cfg(target_os = "linux")]
537 pub(crate) allocator: Option<Rc<RefCell<crate::alloc::isolated_alloc::IsolatedAlloc>>>,
538
539 tracked_alloc_ids: FxHashSet<AllocId>,
542 track_alloc_accesses: bool,
544
545 pub(crate) check_alignment: AlignmentCheck,
547
548 pub(crate) cmpxchg_weak_failure_rate: f64,
550
551 pub(crate) preemption_rate: f64,
553
554 pub(crate) report_progress: Option<u32>,
556 pub(crate) basic_block_count: u64,
558
559 #[cfg(unix)]
561 pub native_lib: Vec<(libloading::Library, std::path::PathBuf)>,
562 #[cfg(not(unix))]
563 pub native_lib: Vec<!>,
564
565 pub(crate) gc_interval: u32,
567 pub(crate) since_gc: u32,
569
570 pub(crate) num_cpus: u32,
572
573 pub(crate) page_size: u64,
575 pub(crate) stack_addr: u64,
576 pub(crate) stack_size: u64,
577
578 pub(crate) collect_leak_backtraces: bool,
580
581 pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>)>>,
584
585 const_cache: RefCell<FxHashMap<(mir::Const<'tcx>, usize), OpTy<'tcx>>>,
589
590 pub(crate) symbolic_alignment: RefCell<FxHashMap<AllocId, (Size, Align)>>,
597
598 union_data_ranges: FxHashMap<Ty<'tcx>, RangeSet>,
600
601 pub(crate) pthread_mutex_sanity: Cell<bool>,
603 pub(crate) pthread_rwlock_sanity: Cell<bool>,
604 pub(crate) pthread_condvar_sanity: Cell<bool>,
605
606 pub(crate) sb_extern_type_warned: Cell<bool>,
608 #[cfg(unix)]
610 pub(crate) native_call_mem_warned: Cell<bool>,
611 pub(crate) reject_in_isolation_warned: RefCell<FxHashSet<String>>,
613 pub(crate) int2ptr_warned: RefCell<FxHashSet<Span>>,
615
616 pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>,
618
619 pub force_intrinsic_fallback: bool,
621
622 pub float_nondet: bool,
624}
625
626impl<'tcx> MiriMachine<'tcx> {
627 pub(crate) fn new(
628 config: &MiriConfig,
629 layout_cx: LayoutCx<'tcx>,
630 genmc_ctx: Option<Rc<GenmcCtx>>,
631 ) -> Self {
632 let tcx = layout_cx.tcx();
633 let local_crates = helpers::get_local_crates(tcx);
634 let layouts =
635 PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
636 let profiler = config.measureme_out.as_ref().map(|out| {
637 let crate_name =
638 tcx.sess.opts.crate_name.clone().unwrap_or_else(|| "unknown-crate".to_string());
639 let pid = process::id();
640 let filename = format!("{crate_name}-{pid:07}");
645 let path = Path::new(out).join(filename);
646 measureme::Profiler::new(path).expect("Couldn't create `measureme` profiler")
647 });
648 let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
649 let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config));
650 let data_race = if config.genmc_mode {
651 GlobalDataRaceHandler::Genmc(genmc_ctx.unwrap())
653 } else if config.data_race_detector {
654 GlobalDataRaceHandler::Vclocks(Box::new(data_race::GlobalState::new(config)))
655 } else {
656 GlobalDataRaceHandler::None
657 };
658 let page_size = if let Some(page_size) = config.page_size {
662 page_size
663 } else {
664 let target = &tcx.sess.target;
665 match target.arch.as_ref() {
666 "wasm32" | "wasm64" => 64 * 1024, "aarch64" => {
668 if target.options.vendor.as_ref() == "apple" {
669 16 * 1024
673 } else {
674 4 * 1024
675 }
676 }
677 _ => 4 * 1024,
678 }
679 };
680 let stack_addr = if tcx.pointer_size().bits() < 32 { page_size } else { page_size * 32 };
682 let stack_size =
683 if tcx.pointer_size().bits() < 32 { page_size * 4 } else { page_size * 16 };
684 assert!(
685 usize::try_from(config.num_cpus).unwrap() <= cpu_affinity::MAX_CPUS,
686 "miri only supports up to {} CPUs, but {} were configured",
687 cpu_affinity::MAX_CPUS,
688 config.num_cpus
689 );
690 let threads = ThreadManager::new(config);
691 let mut thread_cpu_affinity = FxHashMap::default();
692 if matches!(&*tcx.sess.target.os, "linux" | "freebsd" | "android") {
693 thread_cpu_affinity
694 .insert(threads.active_thread(), CpuAffinityMask::new(&layout_cx, config.num_cpus));
695 }
696 MiriMachine {
697 tcx,
698 borrow_tracker,
699 data_race,
700 alloc_addresses: RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr)),
701 env_vars: EnvVars::default(),
703 main_fn_ret_place: None,
704 argc: None,
705 argv: None,
706 cmd_line: None,
707 tls: TlsData::default(),
708 isolated_op: config.isolated_op,
709 validation: config.validation,
710 fds: shims::FdTable::init(config.mute_stdout_stderr),
711 epoll_interests: shims::EpollInterestTable::new(),
712 dirs: Default::default(),
713 layouts,
714 threads,
715 thread_cpu_affinity,
716 sync: SynchronizationObjects::default(),
717 static_roots: Vec::new(),
718 profiler,
719 string_cache: Default::default(),
720 exported_symbols_cache: FxHashMap::default(),
721 backtrace_style: config.backtrace_style,
722 local_crates,
723 extern_statics: FxHashMap::default(),
724 rng: RefCell::new(rng),
725 #[cfg(target_os = "linux")]
726 allocator: if !config.native_lib.is_empty() {
727 Some(Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new())))
728 } else { None },
729 tracked_alloc_ids: config.tracked_alloc_ids.clone(),
730 track_alloc_accesses: config.track_alloc_accesses,
731 check_alignment: config.check_alignment,
732 cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
733 preemption_rate: config.preemption_rate,
734 report_progress: config.report_progress,
735 basic_block_count: 0,
736 monotonic_clock: MonotonicClock::new(config.isolated_op == IsolatedOp::Allow),
737 #[cfg(unix)]
738 native_lib: config.native_lib.iter().map(|lib_file_path| {
739 let host_triple = rustc_session::config::host_tuple();
740 let target_triple = tcx.sess.opts.target_triple.tuple();
741 if host_triple != target_triple {
743 panic!(
744 "calling native C functions in linked .so file requires host and target to be the same: \
745 host={host_triple}, target={target_triple}",
746 );
747 }
748 (
752 unsafe {
753 libloading::Library::new(lib_file_path)
754 .expect("failed to read specified extern shared object file")
755 },
756 lib_file_path.clone(),
757 )
758 }).collect(),
759 #[cfg(not(unix))]
760 native_lib: config.native_lib.iter().map(|_| {
761 panic!("calling functions from native libraries via FFI is only supported on Unix")
762 }).collect(),
763 gc_interval: config.gc_interval,
764 since_gc: 0,
765 num_cpus: config.num_cpus,
766 page_size,
767 stack_addr,
768 stack_size,
769 collect_leak_backtraces: config.collect_leak_backtraces,
770 allocation_spans: RefCell::new(FxHashMap::default()),
771 const_cache: RefCell::new(FxHashMap::default()),
772 symbolic_alignment: RefCell::new(FxHashMap::default()),
773 union_data_ranges: FxHashMap::default(),
774 pthread_mutex_sanity: Cell::new(false),
775 pthread_rwlock_sanity: Cell::new(false),
776 pthread_condvar_sanity: Cell::new(false),
777 sb_extern_type_warned: Cell::new(false),
778 #[cfg(unix)]
779 native_call_mem_warned: Cell::new(false),
780 reject_in_isolation_warned: Default::default(),
781 int2ptr_warned: Default::default(),
782 mangle_internal_symbol_cache: Default::default(),
783 force_intrinsic_fallback: config.force_intrinsic_fallback,
784 float_nondet: config.float_nondet,
785 }
786 }
787
788 pub(crate) fn late_init(
789 ecx: &mut MiriInterpCx<'tcx>,
790 config: &MiriConfig,
791 on_main_stack_empty: StackEmptyCallback<'tcx>,
792 ) -> InterpResult<'tcx> {
793 EnvVars::init(ecx, config)?;
794 MiriMachine::init_extern_statics(ecx)?;
795 ThreadManager::init(ecx, on_main_stack_empty);
796 interp_ok(())
797 }
798
799 pub(crate) fn add_extern_static(ecx: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) {
800 let ptr = ptr.into_pointer_or_addr().unwrap();
802 ecx.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
803 }
804
805 pub(crate) fn communicate(&self) -> bool {
806 self.isolated_op == IsolatedOp::Allow
807 }
808
809 pub(crate) fn is_local(&self, frame: &FrameInfo<'_>) -> bool {
811 let def_id = frame.instance.def_id();
812 def_id.is_local() || self.local_crates.contains(&def_id.krate)
813 }
814
815 pub(crate) fn handle_abnormal_termination(&mut self) {
817 drop(self.profiler.take());
822 }
823
824 pub(crate) fn page_align(&self) -> Align {
825 Align::from_bytes(self.page_size).unwrap()
826 }
827
828 pub(crate) fn allocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
829 self.allocation_spans
830 .borrow()
831 .get(&alloc_id)
832 .map(|(allocated, _deallocated)| allocated.data())
833 }
834
835 pub(crate) fn deallocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
836 self.allocation_spans
837 .borrow()
838 .get(&alloc_id)
839 .and_then(|(_allocated, deallocated)| *deallocated)
840 .map(Span::data)
841 }
842
843 fn init_allocation(
844 ecx: &MiriInterpCx<'tcx>,
845 id: AllocId,
846 kind: MemoryKind,
847 size: Size,
848 align: Align,
849 ) -> InterpResult<'tcx, AllocExtra<'tcx>> {
850 if ecx.machine.tracked_alloc_ids.contains(&id) {
851 ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(id, size, align, kind));
852 }
853
854 let borrow_tracker = ecx
855 .machine
856 .borrow_tracker
857 .as_ref()
858 .map(|bt| bt.borrow_mut().new_allocation(id, size, kind, &ecx.machine));
859
860 let data_race = match &ecx.machine.data_race {
861 GlobalDataRaceHandler::None => AllocDataRaceHandler::None,
862 GlobalDataRaceHandler::Vclocks(data_race) =>
863 AllocDataRaceHandler::Vclocks(
864 data_race::AllocState::new_allocation(
865 data_race,
866 &ecx.machine.threads,
867 size,
868 kind,
869 ecx.machine.current_span(),
870 ),
871 data_race.weak_memory.then(weak_memory::AllocState::new_allocation),
872 ),
873 GlobalDataRaceHandler::Genmc(_genmc_ctx) => {
874 AllocDataRaceHandler::Genmc
877 }
878 };
879
880 let backtrace = if kind.may_leak() || !ecx.machine.collect_leak_backtraces {
884 None
885 } else {
886 Some(ecx.generate_stacktrace())
887 };
888
889 if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) {
890 ecx.machine
891 .allocation_spans
892 .borrow_mut()
893 .insert(id, (ecx.machine.current_span(), None));
894 }
895
896 interp_ok(AllocExtra { borrow_tracker, data_race, backtrace, sync: FxHashMap::default() })
897 }
898}
899
900impl VisitProvenance for MiriMachine<'_> {
901 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
902 #[rustfmt::skip]
903 let MiriMachine {
904 threads,
905 thread_cpu_affinity: _,
906 sync: _,
907 tls,
908 env_vars,
909 main_fn_ret_place,
910 argc,
911 argv,
912 cmd_line,
913 extern_statics,
914 dirs,
915 borrow_tracker,
916 data_race,
917 alloc_addresses,
918 fds,
919 epoll_interests:_,
920 tcx: _,
921 isolated_op: _,
922 validation: _,
923 monotonic_clock: _,
924 layouts: _,
925 static_roots: _,
926 profiler: _,
927 string_cache: _,
928 exported_symbols_cache: _,
929 backtrace_style: _,
930 local_crates: _,
931 rng: _,
932 #[cfg(target_os = "linux")]
933 allocator: _,
934 tracked_alloc_ids: _,
935 track_alloc_accesses: _,
936 check_alignment: _,
937 cmpxchg_weak_failure_rate: _,
938 preemption_rate: _,
939 report_progress: _,
940 basic_block_count: _,
941 native_lib: _,
942 gc_interval: _,
943 since_gc: _,
944 num_cpus: _,
945 page_size: _,
946 stack_addr: _,
947 stack_size: _,
948 collect_leak_backtraces: _,
949 allocation_spans: _,
950 const_cache: _,
951 symbolic_alignment: _,
952 union_data_ranges: _,
953 pthread_mutex_sanity: _,
954 pthread_rwlock_sanity: _,
955 pthread_condvar_sanity: _,
956 sb_extern_type_warned: _,
957 #[cfg(unix)]
958 native_call_mem_warned: _,
959 reject_in_isolation_warned: _,
960 int2ptr_warned: _,
961 mangle_internal_symbol_cache: _,
962 force_intrinsic_fallback: _,
963 float_nondet: _,
964 } = self;
965
966 threads.visit_provenance(visit);
967 tls.visit_provenance(visit);
968 env_vars.visit_provenance(visit);
969 dirs.visit_provenance(visit);
970 fds.visit_provenance(visit);
971 data_race.visit_provenance(visit);
972 borrow_tracker.visit_provenance(visit);
973 alloc_addresses.visit_provenance(visit);
974 main_fn_ret_place.visit_provenance(visit);
975 argc.visit_provenance(visit);
976 argv.visit_provenance(visit);
977 cmd_line.visit_provenance(visit);
978 for ptr in extern_statics.values() {
979 ptr.visit_provenance(visit);
980 }
981 }
982}
983
984pub type MiriInterpCx<'tcx> = InterpCx<'tcx, MiriMachine<'tcx>>;
986
987pub trait MiriInterpCxExt<'tcx> {
989 fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'tcx>;
990 fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'tcx>;
991}
992impl<'tcx> MiriInterpCxExt<'tcx> for MiriInterpCx<'tcx> {
993 #[inline(always)]
994 fn eval_context_ref(&self) -> &MiriInterpCx<'tcx> {
995 self
996 }
997 #[inline(always)]
998 fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'tcx> {
999 self
1000 }
1001}
1002
1003impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
1005 type MemoryKind = MiriMemoryKind;
1006 type ExtraFnVal = DynSym;
1007
1008 type FrameExtra = FrameExtra<'tcx>;
1009 type AllocExtra = AllocExtra<'tcx>;
1010
1011 type Provenance = Provenance;
1012 type ProvenanceExtra = ProvenanceExtra;
1013 type Bytes = MiriAllocBytes;
1014
1015 type MemoryMap =
1016 MonoHashMap<AllocId, (MemoryKind, Allocation<Provenance, Self::AllocExtra, Self::Bytes>)>;
1017
1018 const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
1019
1020 const PANIC_ON_ALLOC_FAIL: bool = false;
1021
1022 #[inline(always)]
1023 fn enforce_alignment(ecx: &MiriInterpCx<'tcx>) -> bool {
1024 ecx.machine.check_alignment != AlignmentCheck::None
1025 }
1026
1027 #[inline(always)]
1028 fn alignment_check(
1029 ecx: &MiriInterpCx<'tcx>,
1030 alloc_id: AllocId,
1031 alloc_align: Align,
1032 alloc_kind: AllocKind,
1033 offset: Size,
1034 align: Align,
1035 ) -> Option<Misalignment> {
1036 if ecx.machine.check_alignment != AlignmentCheck::Symbolic {
1037 return None;
1039 }
1040 if alloc_kind != AllocKind::LiveData {
1041 return None;
1043 }
1044 let (promised_offset, promised_align) = ecx
1046 .machine
1047 .symbolic_alignment
1048 .borrow()
1049 .get(&alloc_id)
1050 .copied()
1051 .unwrap_or((Size::ZERO, alloc_align));
1052 if promised_align < align {
1053 Some(Misalignment { has: promised_align, required: align })
1055 } else {
1056 let distance = offset.bytes().wrapping_sub(promised_offset.bytes());
1058 if distance % align.bytes() == 0 {
1060 None
1062 } else {
1063 let distance_pow2 = 1 << distance.trailing_zeros();
1065 Some(Misalignment {
1066 has: Align::from_bytes(distance_pow2).unwrap(),
1067 required: align,
1068 })
1069 }
1070 }
1071 }
1072
1073 #[inline(always)]
1074 fn enforce_validity(ecx: &MiriInterpCx<'tcx>, _layout: TyAndLayout<'tcx>) -> bool {
1075 ecx.machine.validation != ValidationMode::No
1076 }
1077 #[inline(always)]
1078 fn enforce_validity_recursively(
1079 ecx: &InterpCx<'tcx, Self>,
1080 _layout: TyAndLayout<'tcx>,
1081 ) -> bool {
1082 ecx.machine.validation == ValidationMode::Deep
1083 }
1084
1085 #[inline(always)]
1086 fn ignore_optional_overflow_checks(ecx: &MiriInterpCx<'tcx>) -> bool {
1087 !ecx.tcx.sess.overflow_checks()
1088 }
1089
1090 fn check_fn_target_features(
1091 ecx: &MiriInterpCx<'tcx>,
1092 instance: ty::Instance<'tcx>,
1093 ) -> InterpResult<'tcx> {
1094 let attrs = ecx.tcx.codegen_fn_attrs(instance.def_id());
1095 if attrs
1096 .target_features
1097 .iter()
1098 .any(|feature| !ecx.tcx.sess.target_features.contains(&feature.name))
1099 {
1100 let unavailable = attrs
1101 .target_features
1102 .iter()
1103 .filter(|&feature| {
1104 !feature.implied && !ecx.tcx.sess.target_features.contains(&feature.name)
1105 })
1106 .fold(String::new(), |mut s, feature| {
1107 if !s.is_empty() {
1108 s.push_str(", ");
1109 }
1110 s.push_str(feature.name.as_str());
1111 s
1112 });
1113 let msg = format!(
1114 "calling a function that requires unavailable target features: {unavailable}"
1115 );
1116 if ecx.tcx.sess.target.is_like_wasm {
1119 throw_machine_stop!(TerminationInfo::Abort(msg));
1120 } else {
1121 throw_ub_format!("{msg}");
1122 }
1123 }
1124 interp_ok(())
1125 }
1126
1127 #[inline(always)]
1128 fn find_mir_or_eval_fn(
1129 ecx: &mut MiriInterpCx<'tcx>,
1130 instance: ty::Instance<'tcx>,
1131 abi: &FnAbi<'tcx, Ty<'tcx>>,
1132 args: &[FnArg<'tcx, Provenance>],
1133 dest: &PlaceTy<'tcx>,
1134 ret: Option<mir::BasicBlock>,
1135 unwind: mir::UnwindAction,
1136 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
1137 if ecx.tcx.is_foreign_item(instance.def_id()) {
1139 let args = ecx.copy_fn_args(args); let link_name = Symbol::intern(ecx.tcx.symbol_name(instance).name);
1147 return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind);
1148 }
1149
1150 interp_ok(Some((ecx.load_mir(instance.def, None)?, instance)))
1152 }
1153
1154 #[inline(always)]
1155 fn call_extra_fn(
1156 ecx: &mut MiriInterpCx<'tcx>,
1157 fn_val: DynSym,
1158 abi: &FnAbi<'tcx, Ty<'tcx>>,
1159 args: &[FnArg<'tcx, Provenance>],
1160 dest: &PlaceTy<'tcx>,
1161 ret: Option<mir::BasicBlock>,
1162 unwind: mir::UnwindAction,
1163 ) -> InterpResult<'tcx> {
1164 let args = ecx.copy_fn_args(args); ecx.emulate_dyn_sym(fn_val, abi, &args, dest, ret, unwind)
1166 }
1167
1168 #[inline(always)]
1169 fn call_intrinsic(
1170 ecx: &mut MiriInterpCx<'tcx>,
1171 instance: ty::Instance<'tcx>,
1172 args: &[OpTy<'tcx>],
1173 dest: &PlaceTy<'tcx>,
1174 ret: Option<mir::BasicBlock>,
1175 unwind: mir::UnwindAction,
1176 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
1177 ecx.call_intrinsic(instance, args, dest, ret, unwind)
1178 }
1179
1180 #[inline(always)]
1181 fn assert_panic(
1182 ecx: &mut MiriInterpCx<'tcx>,
1183 msg: &mir::AssertMessage<'tcx>,
1184 unwind: mir::UnwindAction,
1185 ) -> InterpResult<'tcx> {
1186 ecx.assert_panic(msg, unwind)
1187 }
1188
1189 fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> {
1190 ecx.start_panic_nounwind(msg)
1191 }
1192
1193 fn unwind_terminate(
1194 ecx: &mut InterpCx<'tcx, Self>,
1195 reason: mir::UnwindTerminateReason,
1196 ) -> InterpResult<'tcx> {
1197 let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap();
1199 let panic = ty::Instance::mono(ecx.tcx.tcx, panic);
1200 ecx.call_function(
1201 panic,
1202 ExternAbi::Rust,
1203 &[],
1204 None,
1205 StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
1206 )?;
1207 interp_ok(())
1208 }
1209
1210 #[inline(always)]
1211 fn binary_ptr_op(
1212 ecx: &MiriInterpCx<'tcx>,
1213 bin_op: mir::BinOp,
1214 left: &ImmTy<'tcx>,
1215 right: &ImmTy<'tcx>,
1216 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1217 ecx.binary_ptr_op(bin_op, left, right)
1218 }
1219
1220 #[inline(always)]
1221 fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(
1222 ecx: &InterpCx<'tcx, Self>,
1223 inputs: &[F1],
1224 ) -> F2 {
1225 ecx.generate_nan(inputs)
1226 }
1227
1228 #[inline(always)]
1229 fn apply_float_nondet(
1230 ecx: &mut InterpCx<'tcx, Self>,
1231 val: ImmTy<'tcx>,
1232 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1233 crate::math::apply_random_float_error_to_imm(ecx, val, 2 )
1234 }
1235
1236 #[inline(always)]
1237 fn equal_float_min_max<F: Float>(ecx: &MiriInterpCx<'tcx>, a: F, b: F) -> F {
1238 ecx.equal_float_min_max(a, b)
1239 }
1240
1241 #[inline(always)]
1242 fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
1243 interp_ok(ecx.tcx.sess.ub_checks())
1244 }
1245
1246 #[inline(always)]
1247 fn contract_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
1248 interp_ok(ecx.tcx.sess.contract_checks())
1249 }
1250
1251 #[inline(always)]
1252 fn thread_local_static_pointer(
1253 ecx: &mut MiriInterpCx<'tcx>,
1254 def_id: DefId,
1255 ) -> InterpResult<'tcx, StrictPointer> {
1256 ecx.get_or_create_thread_local_alloc(def_id)
1257 }
1258
1259 fn extern_static_pointer(
1260 ecx: &MiriInterpCx<'tcx>,
1261 def_id: DefId,
1262 ) -> InterpResult<'tcx, StrictPointer> {
1263 let link_name = Symbol::intern(ecx.tcx.symbol_name(Instance::mono(*ecx.tcx, def_id)).name);
1264 if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) {
1265 let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
1269 panic!("extern_statics cannot contain wildcards")
1270 };
1271 let info = ecx.get_alloc_info(alloc_id);
1272 let def_ty = ecx.tcx.type_of(def_id).instantiate_identity();
1273 let extern_decl_layout =
1274 ecx.tcx.layout_of(ecx.typing_env().as_query_input(def_ty)).unwrap();
1275 if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align {
1276 throw_unsup_format!(
1277 "extern static `{link_name}` has been declared as `{krate}::{name}` \
1278 with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
1279 but Miri emulates it via an extern static shim \
1280 with a size of {shim_size} bytes and alignment of {shim_align} bytes",
1281 name = ecx.tcx.def_path_str(def_id),
1282 krate = ecx.tcx.crate_name(def_id.krate),
1283 decl_size = extern_decl_layout.size.bytes(),
1284 decl_align = extern_decl_layout.align.abi.bytes(),
1285 shim_size = info.size.bytes(),
1286 shim_align = info.align.bytes(),
1287 )
1288 }
1289 interp_ok(ptr)
1290 } else {
1291 throw_unsup_format!("extern static `{link_name}` is not supported by Miri",)
1292 }
1293 }
1294
1295 fn init_local_allocation(
1296 ecx: &MiriInterpCx<'tcx>,
1297 id: AllocId,
1298 kind: MemoryKind,
1299 size: Size,
1300 align: Align,
1301 ) -> InterpResult<'tcx, Self::AllocExtra> {
1302 assert!(kind != MiriMemoryKind::Global.into());
1303 MiriMachine::init_allocation(ecx, id, kind, size, align)
1304 }
1305
1306 fn adjust_alloc_root_pointer(
1307 ecx: &MiriInterpCx<'tcx>,
1308 ptr: interpret::Pointer<CtfeProvenance>,
1309 kind: Option<MemoryKind>,
1310 ) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
1311 let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None");
1312 let alloc_id = ptr.provenance.alloc_id();
1313 if cfg!(debug_assertions) {
1314 match ecx.tcx.try_get_global_alloc(alloc_id) {
1316 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
1317 panic!("adjust_alloc_root_pointer called on thread-local static")
1318 }
1319 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => {
1320 panic!("adjust_alloc_root_pointer called on extern static")
1321 }
1322 _ => {}
1323 }
1324 }
1325 let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
1327 borrow_tracker.borrow_mut().root_ptr_tag(alloc_id, &ecx.machine)
1328 } else {
1329 BorTag::default()
1331 };
1332 ecx.adjust_alloc_root_pointer(ptr, tag, kind)
1333 }
1334
1335 #[inline(always)]
1337 fn ptr_from_addr_cast(ecx: &MiriInterpCx<'tcx>, addr: u64) -> InterpResult<'tcx, Pointer> {
1338 ecx.ptr_from_addr_cast(addr)
1339 }
1340
1341 #[inline(always)]
1345 fn expose_provenance(
1346 ecx: &InterpCx<'tcx, Self>,
1347 provenance: Self::Provenance,
1348 ) -> InterpResult<'tcx> {
1349 ecx.expose_provenance(provenance)
1350 }
1351
1352 fn ptr_get_alloc(
1364 ecx: &MiriInterpCx<'tcx>,
1365 ptr: StrictPointer,
1366 size: i64,
1367 ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
1368 let rel = ecx.ptr_get_alloc(ptr, size);
1369
1370 rel.map(|(alloc_id, size)| {
1371 let tag = match ptr.provenance {
1372 Provenance::Concrete { tag, .. } => ProvenanceExtra::Concrete(tag),
1373 Provenance::Wildcard => ProvenanceExtra::Wildcard,
1374 };
1375 (alloc_id, size, tag)
1376 })
1377 }
1378
1379 fn adjust_global_allocation<'b>(
1388 ecx: &InterpCx<'tcx, Self>,
1389 id: AllocId,
1390 alloc: &'b Allocation,
1391 ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
1392 {
1393 let alloc = alloc.adjust_from_tcx(
1394 &ecx.tcx,
1395 |bytes, align| ecx.get_global_alloc_bytes(id, bytes, align),
1396 |ptr| ecx.global_root_pointer(ptr),
1397 )?;
1398 let kind = MiriMemoryKind::Global.into();
1399 let extra = MiriMachine::init_allocation(ecx, id, kind, alloc.size(), alloc.align)?;
1400 interp_ok(Cow::Owned(alloc.with_extra(extra)))
1401 }
1402
1403 #[inline(always)]
1404 fn before_memory_read(
1405 _tcx: TyCtxtAt<'tcx>,
1406 machine: &Self,
1407 alloc_extra: &AllocExtra<'tcx>,
1408 ptr: Pointer,
1409 (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1410 range: AllocRange,
1411 ) -> InterpResult<'tcx> {
1412 if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1413 machine
1414 .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Read));
1415 }
1416 match &machine.data_race {
1418 GlobalDataRaceHandler::None => {}
1419 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1420 genmc_ctx.memory_load(machine, ptr.addr(), range.size)?,
1421 GlobalDataRaceHandler::Vclocks(_data_race) => {
1422 let AllocDataRaceHandler::Vclocks(data_race, weak_memory) = &alloc_extra.data_race
1423 else {
1424 unreachable!();
1425 };
1426 data_race.read(alloc_id, range, NaReadType::Read, None, machine)?;
1427 if let Some(weak_memory) = weak_memory {
1428 weak_memory.memory_accessed(range, machine.data_race.as_vclocks_ref().unwrap());
1429 }
1430 }
1431 }
1432 if let Some(borrow_tracker) = &alloc_extra.borrow_tracker {
1433 borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?;
1434 }
1435 interp_ok(())
1436 }
1437
1438 #[inline(always)]
1439 fn before_memory_write(
1440 _tcx: TyCtxtAt<'tcx>,
1441 machine: &mut Self,
1442 alloc_extra: &mut AllocExtra<'tcx>,
1443 ptr: Pointer,
1444 (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1445 range: AllocRange,
1446 ) -> InterpResult<'tcx> {
1447 if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1448 machine
1449 .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Write));
1450 }
1451 match &machine.data_race {
1452 GlobalDataRaceHandler::None => {}
1453 GlobalDataRaceHandler::Genmc(genmc_ctx) => {
1454 genmc_ctx.memory_store(machine, ptr.addr(), range.size)?;
1455 }
1456 GlobalDataRaceHandler::Vclocks(_global_state) => {
1457 let AllocDataRaceHandler::Vclocks(data_race, weak_memory) =
1458 &mut alloc_extra.data_race
1459 else {
1460 unreachable!()
1461 };
1462 data_race.write(alloc_id, range, NaWriteType::Write, None, machine)?;
1463 if let Some(weak_memory) = weak_memory {
1464 weak_memory.memory_accessed(range, machine.data_race.as_vclocks_ref().unwrap());
1465 }
1466 }
1467 }
1468 if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1469 borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?;
1470 }
1471 interp_ok(())
1472 }
1473
1474 #[inline(always)]
1475 fn before_memory_deallocation(
1476 _tcx: TyCtxtAt<'tcx>,
1477 machine: &mut Self,
1478 alloc_extra: &mut AllocExtra<'tcx>,
1479 ptr: Pointer,
1480 (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
1481 size: Size,
1482 align: Align,
1483 kind: MemoryKind,
1484 ) -> InterpResult<'tcx> {
1485 if machine.tracked_alloc_ids.contains(&alloc_id) {
1486 machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
1487 }
1488 match &machine.data_race {
1489 GlobalDataRaceHandler::None => {}
1490 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1491 genmc_ctx.handle_dealloc(machine, ptr.addr(), size, align, kind)?,
1492 GlobalDataRaceHandler::Vclocks(_global_state) => {
1493 let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap();
1494 data_race.write(
1495 alloc_id,
1496 alloc_range(Size::ZERO, size),
1497 NaWriteType::Deallocate,
1498 None,
1499 machine,
1500 )?;
1501 }
1502 }
1503 if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1504 borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?;
1505 }
1506 if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
1507 {
1508 *deallocated_at = Some(machine.current_span());
1509 }
1510 machine.free_alloc_id(alloc_id, size, align, kind);
1511 interp_ok(())
1512 }
1513
1514 #[inline(always)]
1515 fn retag_ptr_value(
1516 ecx: &mut InterpCx<'tcx, Self>,
1517 kind: mir::RetagKind,
1518 val: &ImmTy<'tcx>,
1519 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1520 if ecx.machine.borrow_tracker.is_some() {
1521 ecx.retag_ptr_value(kind, val)
1522 } else {
1523 interp_ok(val.clone())
1524 }
1525 }
1526
1527 #[inline(always)]
1528 fn retag_place_contents(
1529 ecx: &mut InterpCx<'tcx, Self>,
1530 kind: mir::RetagKind,
1531 place: &PlaceTy<'tcx>,
1532 ) -> InterpResult<'tcx> {
1533 if ecx.machine.borrow_tracker.is_some() {
1534 ecx.retag_place_contents(kind, place)?;
1535 }
1536 interp_ok(())
1537 }
1538
1539 fn protect_in_place_function_argument(
1540 ecx: &mut InterpCx<'tcx, Self>,
1541 place: &MPlaceTy<'tcx>,
1542 ) -> InterpResult<'tcx> {
1543 let protected_place = if ecx.machine.borrow_tracker.is_some() {
1546 ecx.protect_place(place)?
1547 } else {
1548 place.clone()
1550 };
1551 ecx.write_uninit(&protected_place)?;
1556 interp_ok(())
1558 }
1559
1560 #[inline(always)]
1561 fn init_frame(
1562 ecx: &mut InterpCx<'tcx, Self>,
1563 frame: Frame<'tcx, Provenance>,
1564 ) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
1565 let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
1567 let fn_name = frame.instance().to_string();
1568 let entry = ecx.machine.string_cache.entry(fn_name.clone());
1569 let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
1570
1571 Some(profiler.start_recording_interval_event_detached(
1572 *name,
1573 measureme::EventId::from_label(*name),
1574 ecx.active_thread().to_u32(),
1575 ))
1576 } else {
1577 None
1578 };
1579
1580 let borrow_tracker = ecx.machine.borrow_tracker.as_ref();
1581
1582 let extra = FrameExtra {
1583 borrow_tracker: borrow_tracker.map(|bt| bt.borrow_mut().new_frame()),
1584 catch_unwind: None,
1585 timing,
1586 is_user_relevant: ecx.machine.is_user_relevant(&frame),
1587 salt: ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL),
1588 data_race: ecx
1589 .machine
1590 .data_race
1591 .as_vclocks_ref()
1592 .map(|_| data_race::FrameState::default()),
1593 };
1594
1595 interp_ok(frame.with_extra(extra))
1596 }
1597
1598 fn stack<'a>(
1599 ecx: &'a InterpCx<'tcx, Self>,
1600 ) -> &'a [Frame<'tcx, Self::Provenance, Self::FrameExtra>] {
1601 ecx.active_thread_stack()
1602 }
1603
1604 fn stack_mut<'a>(
1605 ecx: &'a mut InterpCx<'tcx, Self>,
1606 ) -> &'a mut Vec<Frame<'tcx, Self::Provenance, Self::FrameExtra>> {
1607 ecx.active_thread_stack_mut()
1608 }
1609
1610 fn before_terminator(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1611 ecx.machine.basic_block_count += 1u64; ecx.machine.since_gc += 1;
1613 if let Some(report_progress) = ecx.machine.report_progress {
1615 if ecx.machine.basic_block_count % u64::from(report_progress) == 0 {
1616 ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport {
1617 block_count: ecx.machine.basic_block_count,
1618 });
1619 }
1620 }
1621
1622 if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
1627 ecx.machine.since_gc = 0;
1628 ecx.run_provenance_gc();
1629 }
1630
1631 ecx.maybe_preempt_active_thread();
1634
1635 ecx.machine.monotonic_clock.tick();
1637
1638 interp_ok(())
1639 }
1640
1641 #[inline(always)]
1642 fn after_stack_push(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1643 if ecx.frame().extra.is_user_relevant {
1644 let stack_len = ecx.active_thread_stack().len();
1647 ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
1648 }
1649 interp_ok(())
1650 }
1651
1652 fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1653 let frame = ecx.frame();
1654 if ecx.machine.borrow_tracker.is_some() {
1657 ecx.on_stack_pop(frame)?;
1658 }
1659 if frame.extra.is_user_relevant {
1660 ecx.active_thread_mut().recompute_top_user_relevant_frame(1);
1666 }
1667 info!("Leaving {}", ecx.frame().instance());
1671 interp_ok(())
1672 }
1673
1674 #[inline(always)]
1675 fn after_stack_pop(
1676 ecx: &mut InterpCx<'tcx, Self>,
1677 frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1678 unwinding: bool,
1679 ) -> InterpResult<'tcx, ReturnAction> {
1680 let res = {
1681 let mut frame = frame;
1683 let timing = frame.extra.timing.take();
1684 let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
1685 if let Some(profiler) = ecx.machine.profiler.as_ref() {
1686 profiler.finish_recording_interval_event(timing.unwrap());
1687 }
1688 res
1689 };
1690 if !ecx.active_thread_stack().is_empty() {
1693 info!("Continuing in {}", ecx.frame().instance());
1694 }
1695 res
1696 }
1697
1698 fn after_local_read(
1699 ecx: &InterpCx<'tcx, Self>,
1700 frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1701 local: mir::Local,
1702 ) -> InterpResult<'tcx> {
1703 if let Some(data_race) = &frame.extra.data_race {
1704 data_race.local_read(local, &ecx.machine);
1705 }
1706 interp_ok(())
1707 }
1708
1709 fn after_local_write(
1710 ecx: &mut InterpCx<'tcx, Self>,
1711 local: mir::Local,
1712 storage_live: bool,
1713 ) -> InterpResult<'tcx> {
1714 if let Some(data_race) = &ecx.frame().extra.data_race {
1715 data_race.local_write(local, storage_live, &ecx.machine);
1716 }
1717 interp_ok(())
1718 }
1719
1720 fn after_local_moved_to_memory(
1721 ecx: &mut InterpCx<'tcx, Self>,
1722 local: mir::Local,
1723 mplace: &MPlaceTy<'tcx>,
1724 ) -> InterpResult<'tcx> {
1725 let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else {
1726 panic!("after_local_allocated should only be called on fresh allocations");
1727 };
1728 let local_decl = &ecx.frame().body().local_decls[local];
1730 let span = local_decl.source_info.span;
1731 ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
1732 let (alloc_info, machine) = ecx.get_alloc_extra_mut(alloc_id)?;
1734 if let Some(data_race) =
1735 &machine.threads.active_thread_stack().last().unwrap().extra.data_race
1736 {
1737 data_race.local_moved_to_memory(
1738 local,
1739 alloc_info.data_race.as_vclocks_mut().unwrap(),
1740 machine,
1741 );
1742 }
1743 interp_ok(())
1744 }
1745
1746 fn eval_mir_constant<F>(
1747 ecx: &InterpCx<'tcx, Self>,
1748 val: mir::Const<'tcx>,
1749 span: Span,
1750 layout: Option<TyAndLayout<'tcx>>,
1751 eval: F,
1752 ) -> InterpResult<'tcx, OpTy<'tcx>>
1753 where
1754 F: Fn(
1755 &InterpCx<'tcx, Self>,
1756 mir::Const<'tcx>,
1757 Span,
1758 Option<TyAndLayout<'tcx>>,
1759 ) -> InterpResult<'tcx, OpTy<'tcx>>,
1760 {
1761 let frame = ecx.active_thread_stack().last().unwrap();
1762 let mut cache = ecx.machine.const_cache.borrow_mut();
1763 match cache.entry((val, frame.extra.salt)) {
1764 Entry::Vacant(ve) => {
1765 let op = eval(ecx, val, span, layout)?;
1766 ve.insert(op.clone());
1767 interp_ok(op)
1768 }
1769 Entry::Occupied(oe) => interp_ok(oe.get().clone()),
1770 }
1771 }
1772
1773 fn get_global_alloc_salt(
1774 ecx: &InterpCx<'tcx, Self>,
1775 instance: Option<ty::Instance<'tcx>>,
1776 ) -> usize {
1777 let unique = if let Some(instance) = instance {
1778 let is_generic = instance
1791 .args
1792 .into_iter()
1793 .any(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_)));
1794 let can_be_inlined = matches!(
1795 ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
1796 InliningThreshold::Always
1797 ) || !matches!(
1798 ecx.tcx.codegen_fn_attrs(instance.def_id()).inline,
1799 InlineAttr::Never
1800 );
1801 !is_generic && !can_be_inlined
1802 } else {
1803 false
1805 };
1806 if unique {
1808 CTFE_ALLOC_SALT
1809 } else {
1810 ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL)
1811 }
1812 }
1813
1814 fn cached_union_data_range<'e>(
1815 ecx: &'e mut InterpCx<'tcx, Self>,
1816 ty: Ty<'tcx>,
1817 compute_range: impl FnOnce() -> RangeSet,
1818 ) -> Cow<'e, RangeSet> {
1819 Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range))
1820 }
1821
1822 fn get_default_alloc_params(&self) -> <Self::Bytes as AllocBytes>::AllocParams {
1823 use crate::alloc::MiriAllocParams;
1824
1825 #[cfg(target_os = "linux")]
1826 match &self.allocator {
1827 Some(alloc) => MiriAllocParams::Isolated(alloc.clone()),
1828 None => MiriAllocParams::Global,
1829 }
1830 #[cfg(not(target_os = "linux"))]
1831 MiriAllocParams::Global
1832 }
1833}
1834
1835pub trait MachineCallback<'tcx, T>: VisitProvenance {
1837 fn call(
1839 self: Box<Self>,
1840 ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
1841 arg: T,
1842 ) -> InterpResult<'tcx>;
1843}
1844
1845pub type DynMachineCallback<'tcx, T> = Box<dyn MachineCallback<'tcx, T> + 'tcx>;
1847
1848#[macro_export]
1865macro_rules! callback {
1866 (@capture<$tcx:lifetime $(,)? $($lft:lifetime),*>
1867 { $($name:ident: $type:ty),* $(,)? }
1868 |$this:ident, $arg:ident: $arg_ty:ty| $body:expr $(,)?) => {{
1869 struct Callback<$tcx, $($lft),*> {
1870 $($name: $type,)*
1871 _phantom: std::marker::PhantomData<&$tcx ()>,
1872 }
1873
1874 impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> {
1875 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
1876 $(
1877 self.$name.visit_provenance(_visit);
1878 )*
1879 }
1880 }
1881
1882 impl<$tcx, $($lft),*> MachineCallback<$tcx, $arg_ty> for Callback<$tcx, $($lft),*> {
1883 fn call(
1884 self: Box<Self>,
1885 $this: &mut MiriInterpCx<$tcx>,
1886 $arg: $arg_ty
1887 ) -> InterpResult<$tcx> {
1888 #[allow(unused_variables)]
1889 let Callback { $($name,)* _phantom } = *self;
1890 $body
1891 }
1892 }
1893
1894 Box::new(Callback {
1895 $($name,)*
1896 _phantom: std::marker::PhantomData
1897 })
1898 }};
1899}