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