1use std::fmt::{self, Write};
2use std::num::NonZero;
3use std::sync::Mutex;
4
5use rustc_abi::{Align, Size};
6use rustc_data_structures::fx::{FxBuildHasher, FxHashSet};
7use rustc_errors::{Diag, Level};
8use rustc_span::{DUMMY_SP, Span, SpanData, Symbol};
9
10use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory;
11use crate::borrow_tracker::tree_borrows::diagnostics as tree_diagnostics;
12use crate::*;
13
14pub enum TerminationInfo {
16 Exit {
17 code: i32,
18 leak_check: bool,
19 },
20 Abort(String),
21 Interrupted,
23 UnsupportedInIsolation(String),
24 StackedBorrowsUb {
25 msg: String,
26 help: Vec<String>,
27 history: Option<TagHistory>,
28 },
29 TreeBorrowsUb {
30 title: String,
31 details: Vec<String>,
32 history: tree_diagnostics::HistoryData,
33 },
34 Int2PtrWithStrictProvenance,
35 GenmcSkip,
37 GlobalDeadlock,
39 LocalDeadlock,
41 MultipleSymbolDefinitions {
42 link_name: Symbol,
43 first: SpanData,
44 first_crate: Symbol,
45 second: SpanData,
46 second_crate: Symbol,
47 },
48 SymbolShimClashing {
49 link_name: Symbol,
50 span: SpanData,
51 },
52 DataRace {
53 involves_non_atomic: bool,
54 ptr: interpret::Pointer<AllocId>,
55 op1: RacingOp,
56 op2: RacingOp,
57 extra: Option<&'static str>,
58 retag_explain: bool,
59 },
60 UnsupportedForeignItem(String),
61}
62
63pub struct RacingOp {
64 pub action: String,
65 pub thread_info: String,
66 pub span: SpanData,
67}
68
69impl fmt::Display for TerminationInfo {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 use TerminationInfo::*;
72 match self {
73 Exit { code, .. } => write!(f, "the evaluated program completed with exit code {code}"),
74 Abort(msg) => write!(f, "{msg}"),
75 Interrupted => write!(f, "interpretation was interrupted"),
76 UnsupportedInIsolation(msg) => write!(f, "{msg}"),
77 Int2PtrWithStrictProvenance =>
78 write!(
79 f,
80 "integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported with `-Zmiri-strict-provenance`"
81 ),
82 StackedBorrowsUb { msg, .. } => write!(f, "{msg}"),
83 TreeBorrowsUb { title, .. } => write!(f, "{title}"),
84 GlobalDeadlock => write!(f, "the evaluated program deadlocked"),
85 LocalDeadlock => write!(f, "a thread deadlocked"),
86 GenmcSkip => write!(f, "GenMC wants to skip this execution"),
87 MultipleSymbolDefinitions { link_name, .. } =>
88 write!(f, "multiple definitions of symbol `{link_name}`"),
89 SymbolShimClashing { link_name, .. } =>
90 write!(f, "found `{link_name}` symbol definition that clashes with a built-in shim",),
91 DataRace { involves_non_atomic, ptr, op1, op2, .. } =>
92 write!(
93 f,
94 "{} detected between (1) {} on {} and (2) {} on {} at {ptr:?}",
95 if *involves_non_atomic { "Data race" } else { "Race condition" },
96 op1.action,
97 op1.thread_info,
98 op2.action,
99 op2.thread_info
100 ),
101 UnsupportedForeignItem(msg) => write!(f, "{msg}"),
102 }
103 }
104}
105
106impl fmt::Debug for TerminationInfo {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 write!(f, "{self}")
109 }
110}
111
112impl MachineStopType for TerminationInfo {
113 fn with_validation_path(&mut self, path: String) {
114 use TerminationInfo::*;
115 match self {
116 StackedBorrowsUb { help, .. } => {
117 help.push(format!("while retagging field {path}"));
118 }
119 _ => {}
120 }
121 }
122}
123
124pub enum NonHaltingDiagnostic {
126 CreatedPointerTag(NonZero<u64>, Option<String>, Option<(AllocId, AllocRange, ProvenanceExtra)>),
130 PoppedPointerTag(Item, String),
132 TrackingAlloc(AllocId, Size, Align),
133 FreedAlloc(AllocId),
134 AccessedAlloc(AllocId, AllocRange, borrow_tracker::AccessKind),
135 RejectedIsolatedOp(String),
136 ProgressReport {
137 block_count: u64, },
139 Int2Ptr {
140 details: bool,
141 },
142 NativeCallSharedMem {
143 tracing: bool,
144 },
145 WeakMemoryOutdatedLoad {
146 ptr: Pointer,
147 },
148 ExternTypeReborrow,
149 GenmcCompareExchangeWeak,
150 GenmcCompareExchangeOrderingMismatch {
151 success_ordering: AtomicRwOrd,
152 upgraded_success_ordering: AtomicRwOrd,
153 failure_ordering: AtomicReadOrd,
154 effective_failure_ordering: AtomicReadOrd,
155 },
156 FileInProcOpened,
157 ConnectingSocketGetsockname,
158 SocketAddressResolution {
159 error: std::io::Error,
160 },
161}
162
163pub enum DiagLevel {
165 Error,
166 Warning,
167 Note,
168}
169
170macro_rules! note {
172 ($($tt:tt)*) => { (None, format!($($tt)*)) };
173}
174macro_rules! note_span {
176 ($span:expr, $($tt:tt)*) => { (Some($span), format!($($tt)*)) };
177}
178
179pub fn prune_stacktrace<'tcx>(
183 mut stacktrace: Vec<FrameInfo<'tcx>>,
184 machine: &MiriMachine<'tcx>,
185) -> (Vec<FrameInfo<'tcx>>, bool) {
186 match machine.backtrace_style {
187 BacktraceStyle::Off => {
188 stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(machine.tcx));
191 stacktrace.truncate(1);
193 (stacktrace, false)
194 }
195 BacktraceStyle::Short => {
196 let original_len = stacktrace.len();
197 stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(machine.tcx));
200 let has_local_frame = stacktrace.iter().any(|frame| machine.is_local(frame.instance));
204 if has_local_frame {
205 stacktrace = stacktrace
210 .into_iter()
211 .take_while(|frame| {
212 let def_id = frame.instance.def_id();
213 let path = machine.tcx.def_path_str(def_id);
214 !path.contains("__rust_begin_short_backtrace")
215 })
216 .collect::<Vec<_>>();
217
218 while stacktrace.len() > 1
224 && stacktrace.last().is_some_and(|frame| !machine.is_local(frame.instance))
225 {
226 stacktrace.pop();
227 }
228 }
229 let was_pruned = stacktrace.len() != original_len;
230 (stacktrace, was_pruned)
231 }
232 BacktraceStyle::Full => (stacktrace, false),
233 }
234}
235
236pub fn report_result<'tcx>(
241 ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
242 res: InterpErrorInfo<'tcx>,
243) -> Option<(i32, bool)> {
244 use InterpErrorKind::*;
245 use UndefinedBehaviorInfo::*;
246
247 let mut labels = vec![];
248
249 let (title, helps) = if let MachineStop(info) = res.kind() {
250 let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
251 use TerminationInfo::*;
252 let title = match info {
253 &Exit { code, leak_check } => return Some((code, leak_check)),
254 Abort(_) => Some("abnormal termination"),
255 Interrupted => None,
256 UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance | UnsupportedForeignItem(_) =>
257 Some("unsupported operation"),
258 StackedBorrowsUb { .. } | TreeBorrowsUb { .. } | DataRace { .. } =>
259 Some("Undefined Behavior"),
260 GenmcSkip => {
261 assert!(ecx.machine.data_race.as_genmc_ref().is_some());
262 return Some((0, false));
263 }
264 LocalDeadlock => {
265 labels.push(format!("thread got stuck here"));
266 None
267 }
268 GlobalDeadlock => {
269 let mut any_pruned = false;
272 for (thread, stack) in ecx.machine.threads.all_blocked_stacks() {
273 let stacktrace = Frame::generate_stacktrace_from_stack(stack, *ecx.tcx);
274 let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
275 any_pruned |= was_pruned;
276 report_msg(
277 DiagLevel::Error,
278 format!("the evaluated program deadlocked"),
279 vec![format!("thread got stuck here")],
280 vec![],
281 vec![],
282 &stacktrace,
283 Some(thread),
284 &ecx.machine,
285 )
286 }
287 if any_pruned {
288 ecx.tcx.dcx().note(
289 "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace"
290 );
291 }
292 return None;
293 }
294 MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None,
295 };
296 #[rustfmt::skip]
297 let helps = match info {
298 UnsupportedInIsolation(_) =>
299 vec![
300 note!("set `MIRIFLAGS=-Zmiri-disable-isolation` to disable isolation;"),
301 note!("or set `MIRIFLAGS=-Zmiri-isolation-error=warn` to make Miri return an error code from isolated operations (if supported for that operation) and continue with a warning"),
302 ],
303 UnsupportedForeignItem(_) => {
304 vec![
305 note!("this means the program tried to do something Miri does not support; it does not indicate a bug in the program"),
306 ]
307 }
308 StackedBorrowsUb { help, history, .. } => {
309 labels.extend(help.clone());
310 let mut helps = vec![
311 note!("this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental"),
312 note!("see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information"),
313 ];
314 if let Some(TagHistory {created, invalidated, protected}) = history.clone() {
315 helps.push((Some(created.1), created.0));
316 if let Some((msg, span)) = invalidated {
317 helps.push(note_span!(span, "{msg}"));
318 }
319 if let Some((protector_msg, protector_span)) = protected {
320 helps.push(note_span!(protector_span, "{protector_msg}"));
321 }
322 }
323 helps
324 },
325 TreeBorrowsUb { title: _, details, history } => {
326 let mut helps = vec![
327 note!("this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental"),
328 note!("see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information"),
329 ];
330 for m in details {
331 helps.push(note!("{m}"));
332 }
333 for event in history.events.clone() {
334 helps.push(event);
335 }
336 helps
337 }
338 MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } =>
339 vec![
340 note_span!(*first, "it's first defined here, in crate `{first_crate}`"),
341 note_span!(*second, "then it's defined here again, in crate `{second_crate}`"),
342 ],
343 SymbolShimClashing { link_name, span } =>
344 vec![note_span!(*span, "the `{link_name}` symbol is defined here")],
345 Int2PtrWithStrictProvenance =>
346 vec![note!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead")],
347 DataRace { op1, extra, retag_explain, .. } => {
348 labels.push(format!("(2) just happened here"));
349 let mut helps = vec![note_span!(op1.span, "and (1) occurred earlier here")];
350 if let Some(extra) = extra {
351 helps.push(note!("{extra}"));
352 helps.push(note!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model"));
353 }
354 if *retag_explain {
355 helps.push(note!("retags occur on all (re)borrows and as well as when references are copied or moved"));
356 helps.push(note!("retags permit optimizations that insert speculative reads or writes"));
357 helps.push(note!("therefore from the perspective of data races, a retag has the same implications as a read or write"));
358 }
359 helps.push(note!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"));
360 helps.push(note!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"));
361 helps
362 }
363 ,
364 _ => vec![],
365 };
366 (title, helps)
367 } else {
368 let title = match res.kind() {
369 UndefinedBehavior(UndefinedBehaviorInfo::ValidationError {
370 ptr_bytes_warning: true,
371 ..
372 }) => {
373 ecx.handle_ice(); bug!(
375 "This validation error should be impossible in Miri: {}",
376 format_interp_error(res)
377 );
378 }
379 UndefinedBehavior(_) => "Undefined Behavior",
380 ResourceExhaustion(_) => "resource exhaustion",
381 Unsupported(
382 UnsupportedOpInfo::Unsupported(_)
384 | UnsupportedOpInfo::UnsizedLocal
385 | UnsupportedOpInfo::ExternTypeField,
386 ) => "unsupported operation",
387 InvalidProgram(
388 InvalidProgramInfo::AlreadyReported(_) | InvalidProgramInfo::Layout(..),
390 ) => "post-monomorphization error",
391 _ => {
392 ecx.handle_ice(); bug!("This error should be impossible in Miri: {}", format_interp_error(res));
394 }
395 };
396 #[rustfmt::skip]
397 let helps = match res.kind() {
398 Unsupported(_) =>
399 vec![
400 note!("this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support"),
401 ],
402 ResourceExhaustion(ResourceExhaustionInfo::AddressSpaceFull) if ecx.machine.data_race.as_genmc_ref().is_some() =>
403 vec![
404 note!("in GenMC mode, the address space is limited to 4GB per thread, and addresses cannot be reused")
405 ],
406 UndefinedBehavior(AlignmentCheckFailed { .. })
407 if ecx.machine.check_alignment == AlignmentCheck::Symbolic
408 =>
409 vec![
410 note!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior"),
411 note!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives"),
412 ],
413 UndefinedBehavior(info) => {
414 let mut helps = vec![
415 note!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
416 note!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
417 ];
418 match info {
419 PointerUseAfterFree(alloc_id, _) | PointerOutOfBounds { alloc_id, .. } => {
420 if let Some(span) = ecx.machine.allocated_span(*alloc_id) {
421 helps.push(note_span!(span, "{alloc_id} was allocated here:"));
422 }
423 if let Some(span) = ecx.machine.deallocated_span(*alloc_id) {
424 helps.push(note_span!(span, "{alloc_id} was deallocated here:"));
425 }
426 }
427 AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => {
428 helps.push(note!("this means these two types are not *guaranteed* to be ABI-compatible across all targets"));
429 helps.push(note!("if you think this code should be accepted anyway, please report an issue with Miri"));
430 }
431 _ => {},
432 }
433 helps
434 }
435 InvalidProgram(
436 InvalidProgramInfo::AlreadyReported(_)
437 ) => {
438 return None;
440 }
441 _ =>
442 vec![],
443 };
444 (Some(title), helps)
445 };
446
447 let stacktrace = ecx.generate_stacktrace();
448 let (stacktrace, pruned) = prune_stacktrace(stacktrace, &ecx.machine);
449
450 let mut extra = String::new();
453 match res.kind() {
454 UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => {
455 writeln!(
456 extra,
457 "Uninitialized memory occurred at {alloc_id}{range}, in this allocation:",
458 range = access.bad,
459 )
460 .unwrap();
461 writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap();
462 }
463 _ => {}
464 }
465
466 let mut primary_msg = String::new();
467 if let Some(title) = title {
468 write!(primary_msg, "{title}: ").unwrap();
469 }
470 write!(primary_msg, "{}", format_interp_error(res)).unwrap();
471
472 if labels.is_empty() {
473 labels.push(format!(
474 "{} occurred {}",
475 title.unwrap_or("error"),
476 if stacktrace.is_empty() { "due to this code" } else { "here" }
477 ));
478 }
479
480 report_msg(
481 DiagLevel::Error,
482 primary_msg,
483 labels,
484 vec![],
485 helps,
486 &stacktrace,
487 Some(ecx.active_thread()),
488 &ecx.machine,
489 );
490
491 eprint!("{extra}"); if pruned {
495 ecx.tcx.dcx().note(
496 "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
497 );
498 }
499
500 for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
502 trace!("-------------------");
503 trace!("Frame {}", i);
504 trace!(" return: {:?}", frame.return_place());
505 for (i, local) in frame.locals.iter().enumerate() {
506 trace!(" local {}: {:?}", i, local);
507 }
508 }
509
510 None
511}
512
513pub fn report_leaks<'tcx>(
514 ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
515 leaks: Vec<(AllocId, MemoryKind, Allocation<Provenance, AllocExtra<'tcx>, MiriAllocBytes>)>,
516) {
517 let mut any_pruned = false;
518 for (id, kind, alloc) in leaks {
519 let mut title = format!(
520 "memory leaked: {id:?} ({}, size: {}, align: {})",
521 kind,
522 alloc.size().bytes(),
523 alloc.align.bytes()
524 );
525 let Some(backtrace) = alloc.extra.backtrace else {
526 ecx.tcx.dcx().err(title);
527 continue;
528 };
529 title.push_str(", allocated here:");
530 let (backtrace, pruned) = prune_stacktrace(backtrace, &ecx.machine);
531 any_pruned |= pruned;
532 report_msg(
533 DiagLevel::Error,
534 title,
535 vec![],
536 vec![],
537 vec![],
538 &backtrace,
539 None, &ecx.machine,
541 );
542 }
543 if any_pruned {
544 ecx.tcx.dcx().note(
545 "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
546 );
547 }
548}
549
550fn report_msg<'tcx>(
556 diag_level: DiagLevel,
557 title: String,
558 span_msg: Vec<String>,
559 notes: Vec<(Option<SpanData>, String)>,
560 helps: Vec<(Option<SpanData>, String)>,
561 stacktrace: &[FrameInfo<'tcx>],
562 thread: Option<ThreadId>,
563 machine: &MiriMachine<'tcx>,
564) {
565 let origin_span = thread.map(|t| machine.threads.thread_ref(t).origin_span).unwrap_or(DUMMY_SP);
566 let span = stacktrace.first().map(|fi| fi.span).unwrap_or(origin_span);
567 assert!(!span.is_dummy());
570
571 let tcx = machine.tcx;
572 let level = match diag_level {
573 DiagLevel::Error => Level::Error,
574 DiagLevel::Warning => Level::Warning,
575 DiagLevel::Note => Level::Note,
576 };
577 let mut err = Diag::<()>::new(tcx.sess.dcx(), level, title);
578 err.span(span);
579
580 for line in span_msg {
582 err.span_label(span, line);
583 }
584
585 for (span_data, note) in notes {
587 if let Some(span_data) = span_data {
588 err.span_note(span_data.span(), note);
589 } else {
590 err.note(note);
591 }
592 }
593 for (span_data, help) in helps {
594 if let Some(span_data) = span_data {
595 err.span_help(span_data.span(), help);
596 } else {
597 err.help(help);
598 }
599 }
600 if let Some(thread) = thread
602 && machine.threads.get_total_thread_count() > 1
603 {
604 err.note(format!(
605 "this is on thread `{}`",
606 machine.threads.get_thread_display_name(thread)
607 ));
608 }
609
610 if stacktrace.len() > 0 {
612 if stacktrace.len() > 1 {
614 let sm = tcx.sess.source_map();
615 let mut out = format!("stack backtrace:");
616 for (idx, frame_info) in stacktrace.iter().enumerate() {
617 let span = sm.span_to_diagnostic_string(frame_info.span);
618 write!(out, "\n{idx}: {}", frame_info.instance).unwrap();
619 write!(out, "\n at {span}").unwrap();
620 }
621 err.note(out);
622 }
623 if !origin_span.is_dummy() {
625 let what = if stacktrace.len() > 1 {
626 "the last function in that backtrace"
627 } else {
628 "the current function"
629 };
630 err.span_note(origin_span, format!("{what} got called indirectly due to this code"));
631 }
632 } else if !span.is_dummy() {
633 err.note(format!("this {level} occurred while pushing a call frame onto an empty stack"));
634 err.note("the span indicates which code caused the function to be called, but may not be the literal call site");
635 }
636
637 err.emit();
638}
639
640impl<'tcx> MiriMachine<'tcx> {
641 pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
642 use NonHaltingDiagnostic::*;
643
644 let stacktrace =
645 Frame::generate_stacktrace_from_stack(self.threads.active_thread_stack(), self.tcx);
646 let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, self);
647
648 let (label, diag_level) = match &e {
649 RejectedIsolatedOp(_) =>
650 ("operation rejected by isolation".to_string(), DiagLevel::Warning),
651 Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
652 NativeCallSharedMem { .. } =>
653 ("sharing memory with a native function".to_string(), DiagLevel::Warning),
654 ExternTypeReborrow =>
655 ("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
656 GenmcCompareExchangeWeak | GenmcCompareExchangeOrderingMismatch { .. } =>
657 ("GenMC might miss possible behaviors of this code".to_string(), DiagLevel::Warning),
658 CreatedPointerTag(..)
659 | PoppedPointerTag(..)
660 | TrackingAlloc(..)
661 | AccessedAlloc(..)
662 | FreedAlloc(..)
663 | ProgressReport { .. }
664 | WeakMemoryOutdatedLoad { .. } =>
665 ("tracking was triggered here".to_string(), DiagLevel::Note),
666 FileInProcOpened => ("open a file in `/proc`".to_string(), DiagLevel::Warning),
667 ConnectingSocketGetsockname =>
668 ("Called `getsockname` on connecting socket".to_string(), DiagLevel::Warning),
669 SocketAddressResolution { .. } =>
670 ("error during address resolution".to_string(), DiagLevel::Warning),
671 };
672
673 let title = match &e {
674 CreatedPointerTag(tag, None, _) => format!("created base tag {tag:?}"),
675 CreatedPointerTag(tag, Some(perm), None) =>
676 format!("created {tag:?} with {perm} derived from unknown tag"),
677 CreatedPointerTag(tag, Some(perm), Some((alloc_id, range, orig_tag))) =>
678 format!(
679 "created tag {tag:?} with {perm} at {alloc_id}{range} derived from {orig_tag:?}"
680 ),
681 PoppedPointerTag(item, cause) => format!("popped tracked tag for item {item:?}{cause}"),
682 TrackingAlloc(id, size, align) =>
683 format!(
684 "now tracking allocation {id} of {size} bytes (alignment {align} bytes)",
685 size = size.bytes(),
686 align = align.bytes(),
687 ),
688 AccessedAlloc(id, range, access_kind) =>
689 format!("{access_kind} at {id}{range}"),
690 FreedAlloc(id) => format!("freed allocation {id:?}"),
691 RejectedIsolatedOp(op) => format!("{op} was made to return an error due to isolation"),
692 ProgressReport { .. } =>
693 format!("progress report: current operation being executed is here"),
694 Int2Ptr { .. } => format!("integer-to-pointer cast"),
695 NativeCallSharedMem { .. } =>
696 format!("sharing memory with a native function called via FFI"),
697 WeakMemoryOutdatedLoad { ptr } =>
698 format!("weak memory emulation: outdated value returned from load at {ptr}"),
699 ExternTypeReborrow =>
700 format!("reborrow of a reference to `extern type` is not properly supported"),
701 GenmcCompareExchangeWeak =>
702 "GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures."
703 .to_string(),
704 GenmcCompareExchangeOrderingMismatch {
705 success_ordering,
706 upgraded_success_ordering,
707 failure_ordering,
708 effective_failure_ordering,
709 } => {
710 let was_upgraded_msg = if success_ordering != upgraded_success_ordering {
711 format!("Success ordering '{success_ordering:?}' was upgraded to '{upgraded_success_ordering:?}' to match failure ordering '{failure_ordering:?}'")
712 } else {
713 assert_ne!(failure_ordering, effective_failure_ordering);
714 format!("Due to success ordering '{success_ordering:?}', the failure ordering '{failure_ordering:?}' is treated like '{effective_failure_ordering:?}'")
715 };
716 format!("GenMC currently does not model the failure ordering for `compare_exchange`. {was_upgraded_msg}. Miri with GenMC might miss bugs related to this memory access.")
717 }
718 FileInProcOpened => format!("files in `/proc` can bypass the Abstract Machine and might not work properly in Miri"),
719 ConnectingSocketGetsockname => format!("connecting sockets return unspecified socket addresses on Windows hosts"),
720 SocketAddressResolution { error } => format!("address resolution failed: {error}"),
721 };
722
723 let notes = match &e {
724 ProgressReport { block_count } => {
725 vec![note!("so far, {block_count} basic blocks have been executed")]
726 }
727 ConnectingSocketGetsockname =>
728 vec![
729 note!(
730 "Windows hosts do not provide `local_addr` information while the socket is still connecting, which might break the assumptions of code compiled for Unix targets"
731 ),
732 note!(
733 "an unspecified socket address (e.g. `0.0.0.0:0`) will be returned instead"
734 ),
735 ],
736 SocketAddressResolution { .. } =>
737 vec![note!(
738 "Miri cannot return proper error information from this call; only a generic error code is being returned"
739 )],
740 _ => vec![],
741 };
742
743 let helps = match &e {
744 Int2Ptr { details: true } => {
745 let mut v = vec![
746 note!(
747 "this program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program"
748 ),
749 note!(
750 "see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation"
751 ),
752 note!(
753 "to ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"
754 ),
755 note!(
756 "you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics"
757 ),
758 ];
759 if self.borrow_tracker.as_ref().is_some_and(|b| {
760 matches!(
761 b.borrow().borrow_tracker_method(),
762 BorrowTrackerMethod::TreeBorrows { .. }
763 )
764 }) {
765 v.push(
766 note!("Tree Borrows does not support integer-to-pointer casts, so the program is likely to go wrong when this pointer gets used")
767 );
768 } else {
769 v.push(
770 note!("alternatively, `MIRIFLAGS=-Zmiri-permissive-provenance` disables this warning")
771 );
772 }
773 v
774 }
775 NativeCallSharedMem { tracing } =>
776 if *tracing {
777 vec![
778 note!(
779 "when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis"
780 ),
781 note!(
782 "in particular, Miri assumes that the native call initializes all memory it has written to"
783 ),
784 note!(
785 "Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory"
786 ),
787 note!(
788 "what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free"
789 ),
790 note!(
791 "tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here"
792 ),
793 ]
794 } else {
795 vec![
796 note!(
797 "when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory"
798 ),
799 note!(
800 "in particular, Miri assumes that the native call initializes all memory it has access to"
801 ),
802 note!(
803 "Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory"
804 ),
805 note!(
806 "what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free"
807 ),
808 ]
809 },
810 ExternTypeReborrow => {
811 assert!(self.borrow_tracker.as_ref().is_some_and(|b| {
812 matches!(
813 b.borrow().borrow_tracker_method(),
814 BorrowTrackerMethod::StackedBorrows
815 )
816 }));
817 vec![
818 note!(
819 "`extern type` are not compatible with the Stacked Borrows aliasing model implemented by Miri; Miri may miss bugs in this code"
820 ),
821 note!(
822 "try running with `MIRIFLAGS=-Zmiri-tree-borrows` to use the more permissive but also even more experimental Tree Borrows aliasing checks instead"
823 ),
824 ]
825 }
826 _ => vec![],
827 };
828
829 report_msg(
830 diag_level,
831 title,
832 vec![label],
833 notes,
834 helps,
835 &stacktrace,
836 Some(self.threads.active_thread()),
837 self,
838 );
839 }
840}
841
842impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
843pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
844 fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
845 let this = self.eval_context_ref();
846 this.machine.emit_diagnostic(e);
847 }
848
849 fn handle_ice(&self) {
851 eprintln!();
852 eprintln!(
853 "Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic:"
854 );
855 let this = self.eval_context_ref();
856 let stacktrace = this.generate_stacktrace();
857 report_msg(
858 DiagLevel::Note,
859 "the place in the program where the ICE was triggered".to_string(),
860 vec![],
861 vec![],
862 vec![],
863 &stacktrace,
864 Some(this.active_thread()),
865 &this.machine,
866 );
867 }
868
869 fn dedup_diagnostic(
873 &self,
874 dedup: &SpanDedupDiagnostic,
875 f: impl FnOnce(bool) -> NonHaltingDiagnostic,
876 ) {
877 let this = self.eval_context_ref();
878 let span1 = this.machine.current_user_relevant_span();
882 let span2 = this
885 .active_thread_stack()
886 .iter()
887 .rev()
888 .find(|frame| !frame.instance().def.requires_caller_location(*this.tcx))
889 .map(|frame| frame.current_span())
890 .unwrap_or(span1);
891
892 let mut lock = dedup.0.lock().unwrap();
893 let first = lock.is_empty();
894 if !lock.contains(&span2) && lock.insert(span1) && (span1 == span2 || lock.insert(span2)) {
896 this.emit_diagnostic(f(first));
898 }
899 }
900}
901
902pub struct SpanDedupDiagnostic(Mutex<FxHashSet<Span>>);
904
905impl SpanDedupDiagnostic {
906 pub const fn new() -> Self {
907 Self(Mutex::new(FxHashSet::with_hasher(FxBuildHasher)))
908 }
909}