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