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
114pub enum NonHaltingDiagnostic {
116 CreatedPointerTag(NonZero<u64>, Option<String>, Option<(AllocId, AllocRange, ProvenanceExtra)>),
120 PoppedPointerTag(Item, String),
122 TrackingAlloc(AllocId, Size, Align),
123 FreedAlloc(AllocId),
124 AccessedAlloc(AllocId, AllocRange, borrow_tracker::AccessKind),
125 RejectedIsolatedOp(String),
126 ProgressReport {
127 block_count: u64, },
129 Int2Ptr {
130 details: bool,
131 },
132 NativeCallSharedMem {
133 tracing: bool,
134 },
135 WeakMemoryOutdatedLoad {
136 ptr: Pointer,
137 },
138 ExternTypeReborrow,
139 GenmcCompareExchangeWeak,
140 GenmcCompareExchangeOrderingMismatch {
141 success_ordering: AtomicRwOrd,
142 upgraded_success_ordering: AtomicRwOrd,
143 failure_ordering: AtomicReadOrd,
144 effective_failure_ordering: AtomicReadOrd,
145 },
146 FileInProcOpened,
147}
148
149pub enum DiagLevel {
151 Error,
152 Warning,
153 Note,
154}
155
156macro_rules! note {
158 ($($tt:tt)*) => { (None, format!($($tt)*)) };
159}
160macro_rules! note_span {
162 ($span:expr, $($tt:tt)*) => { (Some($span), format!($($tt)*)) };
163}
164
165pub fn prune_stacktrace<'tcx>(
169 mut stacktrace: Vec<FrameInfo<'tcx>>,
170 machine: &MiriMachine<'tcx>,
171) -> (Vec<FrameInfo<'tcx>>, bool) {
172 match machine.backtrace_style {
173 BacktraceStyle::Off => {
174 stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(machine.tcx));
177 stacktrace.truncate(1);
179 (stacktrace, false)
180 }
181 BacktraceStyle::Short => {
182 let original_len = stacktrace.len();
183 stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(machine.tcx));
186 let has_local_frame = stacktrace.iter().any(|frame| machine.is_local(frame.instance));
190 if has_local_frame {
191 stacktrace = stacktrace
196 .into_iter()
197 .take_while(|frame| {
198 let def_id = frame.instance.def_id();
199 let path = machine.tcx.def_path_str(def_id);
200 !path.contains("__rust_begin_short_backtrace")
201 })
202 .collect::<Vec<_>>();
203
204 while stacktrace.len() > 1
210 && stacktrace.last().is_some_and(|frame| !machine.is_local(frame.instance))
211 {
212 stacktrace.pop();
213 }
214 }
215 let was_pruned = stacktrace.len() != original_len;
216 (stacktrace, was_pruned)
217 }
218 BacktraceStyle::Full => (stacktrace, false),
219 }
220}
221
222pub fn report_result<'tcx>(
227 ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
228 res: InterpErrorInfo<'tcx>,
229) -> Option<(i32, bool)> {
230 use InterpErrorKind::*;
231 use UndefinedBehaviorInfo::*;
232
233 let mut labels = vec![];
234
235 let (title, helps) = if let MachineStop(info) = res.kind() {
236 let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
237 use TerminationInfo::*;
238 let title = match info {
239 &Exit { code, leak_check } => return Some((code, leak_check)),
240 Abort(_) => Some("abnormal termination"),
241 Interrupted => None,
242 UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance | UnsupportedForeignItem(_) =>
243 Some("unsupported operation"),
244 StackedBorrowsUb { .. } | TreeBorrowsUb { .. } | DataRace { .. } =>
245 Some("Undefined Behavior"),
246 GenmcSkip => {
247 assert!(ecx.machine.data_race.as_genmc_ref().is_some());
248 return Some((0, false));
249 }
250 LocalDeadlock => {
251 labels.push(format!("thread got stuck here"));
252 None
253 }
254 GlobalDeadlock => {
255 let mut any_pruned = false;
258 for (thread, stack) in ecx.machine.threads.all_blocked_stacks() {
259 let stacktrace = Frame::generate_stacktrace_from_stack(stack, *ecx.tcx);
260 let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
261 any_pruned |= was_pruned;
262 report_msg(
263 DiagLevel::Error,
264 format!("the evaluated program deadlocked"),
265 vec![format!("thread got stuck here")],
266 vec![],
267 vec![],
268 &stacktrace,
269 Some(thread),
270 &ecx.machine,
271 )
272 }
273 if any_pruned {
274 ecx.tcx.dcx().note(
275 "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace"
276 );
277 }
278 return None;
279 }
280 MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None,
281 };
282 #[rustfmt::skip]
283 let helps = match info {
284 UnsupportedInIsolation(_) =>
285 vec![
286 note!("set `MIRIFLAGS=-Zmiri-disable-isolation` to disable isolation;"),
287 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"),
288 ],
289 UnsupportedForeignItem(_) => {
290 vec![
291 note!("this means the program tried to do something Miri does not support; it does not indicate a bug in the program"),
292 ]
293 }
294 StackedBorrowsUb { help, history, .. } => {
295 labels.extend(help.clone());
296 let mut helps = vec![
297 note!("this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental"),
298 note!("see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information"),
299 ];
300 if let Some(TagHistory {created, invalidated, protected}) = history.clone() {
301 helps.push((Some(created.1), created.0));
302 if let Some((msg, span)) = invalidated {
303 helps.push(note_span!(span, "{msg}"));
304 }
305 if let Some((protector_msg, protector_span)) = protected {
306 helps.push(note_span!(protector_span, "{protector_msg}"));
307 }
308 }
309 helps
310 },
311 TreeBorrowsUb { title: _, details, history } => {
312 let mut helps = vec![
313 note!("this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental"),
314 note!("see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information"),
315 ];
316 for m in details {
317 helps.push(note!("{m}"));
318 }
319 for event in history.events.clone() {
320 helps.push(event);
321 }
322 helps
323 }
324 MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } =>
325 vec![
326 note_span!(*first, "it's first defined here, in crate `{first_crate}`"),
327 note_span!(*second, "then it's defined here again, in crate `{second_crate}`"),
328 ],
329 SymbolShimClashing { link_name, span } =>
330 vec![note_span!(*span, "the `{link_name}` symbol is defined here")],
331 Int2PtrWithStrictProvenance =>
332 vec![note!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead")],
333 DataRace { op1, extra, retag_explain, .. } => {
334 labels.push(format!("(2) just happened here"));
335 let mut helps = vec![note_span!(op1.span, "and (1) occurred earlier here")];
336 if let Some(extra) = extra {
337 helps.push(note!("{extra}"));
338 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"));
339 }
340 if *retag_explain {
341 helps.push(note!("retags occur on all (re)borrows and as well as when references are copied or moved"));
342 helps.push(note!("retags permit optimizations that insert speculative reads or writes"));
343 helps.push(note!("therefore from the perspective of data races, a retag has the same implications as a read or write"));
344 }
345 helps.push(note!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"));
346 helps.push(note!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"));
347 helps
348 }
349 ,
350 _ => vec![],
351 };
352 (title, helps)
353 } else {
354 let title = match res.kind() {
355 UndefinedBehavior(UndefinedBehaviorInfo::ValidationError {
356 ptr_bytes_warning: true,
357 ..
358 }) => {
359 ecx.handle_ice(); bug!(
361 "This validation error should be impossible in Miri: {}",
362 format_interp_error(res)
363 );
364 }
365 UndefinedBehavior(_) => "Undefined Behavior",
366 ResourceExhaustion(_) => "resource exhaustion",
367 Unsupported(
368 UnsupportedOpInfo::Unsupported(_)
370 | UnsupportedOpInfo::UnsizedLocal
371 | UnsupportedOpInfo::ExternTypeField,
372 ) => "unsupported operation",
373 InvalidProgram(
374 InvalidProgramInfo::AlreadyReported(_) | InvalidProgramInfo::Layout(..),
376 ) => "post-monomorphization error",
377 _ => {
378 ecx.handle_ice(); bug!("This error should be impossible in Miri: {}", format_interp_error(res));
380 }
381 };
382 #[rustfmt::skip]
383 let helps = match res.kind() {
384 Unsupported(_) =>
385 vec![
386 note!("this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support"),
387 ],
388 ResourceExhaustion(ResourceExhaustionInfo::AddressSpaceFull) if ecx.machine.data_race.as_genmc_ref().is_some() =>
389 vec![
390 note!("in GenMC mode, the address space is limited to 4GB per thread, and addresses cannot be reused")
391 ],
392 UndefinedBehavior(AlignmentCheckFailed { .. })
393 if ecx.machine.check_alignment == AlignmentCheck::Symbolic
394 =>
395 vec![
396 note!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior"),
397 note!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives"),
398 ],
399 UndefinedBehavior(info) => {
400 let mut helps = vec![
401 note!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
402 note!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
403 ];
404 match info {
405 PointerUseAfterFree(alloc_id, _) | PointerOutOfBounds { alloc_id, .. } => {
406 if let Some(span) = ecx.machine.allocated_span(*alloc_id) {
407 helps.push(note_span!(span, "{alloc_id} was allocated here:"));
408 }
409 if let Some(span) = ecx.machine.deallocated_span(*alloc_id) {
410 helps.push(note_span!(span, "{alloc_id} was deallocated here:"));
411 }
412 }
413 AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => {
414 helps.push(note!("this means these two types are not *guaranteed* to be ABI-compatible across all targets"));
415 helps.push(note!("if you think this code should be accepted anyway, please report an issue with Miri"));
416 }
417 _ => {},
418 }
419 helps
420 }
421 InvalidProgram(
422 InvalidProgramInfo::AlreadyReported(_)
423 ) => {
424 return None;
426 }
427 _ =>
428 vec![],
429 };
430 (Some(title), helps)
431 };
432
433 let stacktrace = ecx.generate_stacktrace();
434 let (stacktrace, pruned) = prune_stacktrace(stacktrace, &ecx.machine);
435
436 let mut extra = String::new();
439 match res.kind() {
440 UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => {
441 writeln!(
442 extra,
443 "Uninitialized memory occurred at {alloc_id}{range}, in this allocation:",
444 range = access.bad,
445 )
446 .unwrap();
447 writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap();
448 }
449 _ => {}
450 }
451
452 let mut primary_msg = String::new();
453 if let Some(title) = title {
454 write!(primary_msg, "{title}: ").unwrap();
455 }
456 write!(primary_msg, "{}", format_interp_error(res)).unwrap();
457
458 if labels.is_empty() {
459 labels.push(format!(
460 "{} occurred {}",
461 title.unwrap_or("error"),
462 if stacktrace.is_empty() { "due to this code" } else { "here" }
463 ));
464 }
465
466 report_msg(
467 DiagLevel::Error,
468 primary_msg,
469 labels,
470 vec![],
471 helps,
472 &stacktrace,
473 Some(ecx.active_thread()),
474 &ecx.machine,
475 );
476
477 eprint!("{extra}"); if 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
536fn 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 origin_span = thread.map(|t| machine.threads.thread_ref(t).origin_span).unwrap_or(DUMMY_SP);
552 let span = stacktrace.first().map(|fi| fi.span).unwrap_or(origin_span);
553 assert!(!span.is_dummy());
556
557 let tcx = machine.tcx;
558 let level = match diag_level {
559 DiagLevel::Error => Level::Error,
560 DiagLevel::Warning => Level::Warning,
561 DiagLevel::Note => Level::Note,
562 };
563 let mut err = Diag::<()>::new(tcx.sess.dcx(), level, title);
564 err.span(span);
565
566 for line in span_msg {
568 err.span_label(span, line);
569 }
570
571 for (span_data, note) in notes {
573 if let Some(span_data) = span_data {
574 err.span_note(span_data.span(), note);
575 } else {
576 err.note(note);
577 }
578 }
579 for (span_data, help) in helps {
580 if let Some(span_data) = span_data {
581 err.span_help(span_data.span(), help);
582 } else {
583 err.help(help);
584 }
585 }
586 if let Some(thread) = thread
588 && machine.threads.get_total_thread_count() > 1
589 {
590 err.note(format!(
591 "this is on thread `{}`",
592 machine.threads.get_thread_display_name(thread)
593 ));
594 }
595
596 if stacktrace.len() > 0 {
598 if stacktrace.len() > 1 {
600 let sm = tcx.sess.source_map();
601 let mut out = format!("stack backtrace:");
602 for (idx, frame_info) in stacktrace.iter().enumerate() {
603 let span = sm.span_to_diagnostic_string(frame_info.span);
604 write!(out, "\n{idx}: {}", frame_info.instance).unwrap();
605 write!(out, "\n at {span}").unwrap();
606 }
607 err.note(out);
608 }
609 if !origin_span.is_dummy() {
611 let what = if stacktrace.len() > 1 {
612 "the last function in that backtrace"
613 } else {
614 "the current function"
615 };
616 err.span_note(origin_span, format!("{what} got called indirectly due to this code"));
617 }
618 } else if !span.is_dummy() {
619 err.note(format!("this {level} occurred while pushing a call frame onto an empty stack"));
620 err.note("the span indicates which code caused the function to be called, but may not be the literal call site");
621 }
622
623 err.emit();
624}
625
626impl<'tcx> MiriMachine<'tcx> {
627 pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
628 use NonHaltingDiagnostic::*;
629
630 let stacktrace =
631 Frame::generate_stacktrace_from_stack(self.threads.active_thread_stack(), self.tcx);
632 let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, self);
633
634 let (label, diag_level) = match &e {
635 RejectedIsolatedOp(_) =>
636 ("operation rejected by isolation".to_string(), DiagLevel::Warning),
637 Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
638 NativeCallSharedMem { .. } =>
639 ("sharing memory with a native function".to_string(), DiagLevel::Warning),
640 ExternTypeReborrow =>
641 ("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
642 GenmcCompareExchangeWeak | GenmcCompareExchangeOrderingMismatch { .. } =>
643 ("GenMC might miss possible behaviors of this code".to_string(), DiagLevel::Warning),
644 CreatedPointerTag(..)
645 | PoppedPointerTag(..)
646 | TrackingAlloc(..)
647 | AccessedAlloc(..)
648 | FreedAlloc(..)
649 | ProgressReport { .. }
650 | WeakMemoryOutdatedLoad { .. } =>
651 ("tracking was triggered here".to_string(), DiagLevel::Note),
652 FileInProcOpened => ("open a file in `/proc`".to_string(), DiagLevel::Warning),
653 };
654
655 let title = match &e {
656 CreatedPointerTag(tag, None, _) => format!("created base tag {tag:?}"),
657 CreatedPointerTag(tag, Some(perm), None) =>
658 format!("created {tag:?} with {perm} derived from unknown tag"),
659 CreatedPointerTag(tag, Some(perm), Some((alloc_id, range, orig_tag))) =>
660 format!(
661 "created tag {tag:?} with {perm} at {alloc_id}{range} derived from {orig_tag:?}"
662 ),
663 PoppedPointerTag(item, cause) => format!("popped tracked tag for item {item:?}{cause}"),
664 TrackingAlloc(id, size, align) =>
665 format!(
666 "now tracking allocation {id} of {size} bytes (alignment {align} bytes)",
667 size = size.bytes(),
668 align = align.bytes(),
669 ),
670 AccessedAlloc(id, range, access_kind) =>
671 format!("{access_kind} at {id}{range}"),
672 FreedAlloc(id) => format!("freed allocation {id:?}"),
673 RejectedIsolatedOp(op) => format!("{op} was made to return an error due to isolation"),
674 ProgressReport { .. } =>
675 format!("progress report: current operation being executed is here"),
676 Int2Ptr { .. } => format!("integer-to-pointer cast"),
677 NativeCallSharedMem { .. } =>
678 format!("sharing memory with a native function called via FFI"),
679 WeakMemoryOutdatedLoad { ptr } =>
680 format!("weak memory emulation: outdated value returned from load at {ptr}"),
681 ExternTypeReborrow =>
682 format!("reborrow of a reference to `extern type` is not properly supported"),
683 GenmcCompareExchangeWeak =>
684 "GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures."
685 .to_string(),
686 GenmcCompareExchangeOrderingMismatch {
687 success_ordering,
688 upgraded_success_ordering,
689 failure_ordering,
690 effective_failure_ordering,
691 } => {
692 let was_upgraded_msg = if success_ordering != upgraded_success_ordering {
693 format!("Success ordering '{success_ordering:?}' was upgraded to '{upgraded_success_ordering:?}' to match failure ordering '{failure_ordering:?}'")
694 } else {
695 assert_ne!(failure_ordering, effective_failure_ordering);
696 format!("Due to success ordering '{success_ordering:?}', the failure ordering '{failure_ordering:?}' is treated like '{effective_failure_ordering:?}'")
697 };
698 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.")
699 }
700 FileInProcOpened => format!("files in `/proc` can bypass the Abstract Machine and might not work properly in Miri"),
701 };
702
703 let notes = match &e {
704 ProgressReport { block_count } => {
705 vec![note!("so far, {block_count} basic blocks have been executed")]
706 }
707 _ => vec![],
708 };
709
710 let helps = match &e {
711 Int2Ptr { details: true } => {
712 let mut v = vec![
713 note!(
714 "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"
715 ),
716 note!(
717 "see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation"
718 ),
719 note!(
720 "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"
721 ),
722 note!(
723 "you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics"
724 ),
725 ];
726 if self.borrow_tracker.as_ref().is_some_and(|b| {
727 matches!(
728 b.borrow().borrow_tracker_method(),
729 BorrowTrackerMethod::TreeBorrows { .. }
730 )
731 }) {
732 v.push(
733 note!("Tree Borrows does not support integer-to-pointer casts, so the program is likely to go wrong when this pointer gets used")
734 );
735 } else {
736 v.push(
737 note!("alternatively, `MIRIFLAGS=-Zmiri-permissive-provenance` disables this warning")
738 );
739 }
740 v
741 }
742 NativeCallSharedMem { tracing } =>
743 if *tracing {
744 vec![
745 note!(
746 "when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis"
747 ),
748 note!(
749 "in particular, Miri assumes that the native call initializes all memory it has written to"
750 ),
751 note!(
752 "Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory"
753 ),
754 note!(
755 "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"
756 ),
757 note!(
758 "tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here"
759 ),
760 ]
761 } else {
762 vec![
763 note!(
764 "when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory"
765 ),
766 note!(
767 "in particular, Miri assumes that the native call initializes all memory it has access to"
768 ),
769 note!(
770 "Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory"
771 ),
772 note!(
773 "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"
774 ),
775 ]
776 },
777 ExternTypeReborrow => {
778 assert!(self.borrow_tracker.as_ref().is_some_and(|b| {
779 matches!(
780 b.borrow().borrow_tracker_method(),
781 BorrowTrackerMethod::StackedBorrows
782 )
783 }));
784 vec![
785 note!(
786 "`extern type` are not compatible with the Stacked Borrows aliasing model implemented by Miri; Miri may miss bugs in this code"
787 ),
788 note!(
789 "try running with `MIRIFLAGS=-Zmiri-tree-borrows` to use the more permissive but also even more experimental Tree Borrows aliasing checks instead"
790 ),
791 ]
792 }
793 _ => vec![],
794 };
795
796 report_msg(
797 diag_level,
798 title,
799 vec![label],
800 notes,
801 helps,
802 &stacktrace,
803 Some(self.threads.active_thread()),
804 self,
805 );
806 }
807}
808
809impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
810pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
811 fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
812 let this = self.eval_context_ref();
813 this.machine.emit_diagnostic(e);
814 }
815
816 fn handle_ice(&self) {
818 eprintln!();
819 eprintln!(
820 "Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic:"
821 );
822 let this = self.eval_context_ref();
823 let stacktrace = this.generate_stacktrace();
824 report_msg(
825 DiagLevel::Note,
826 "the place in the program where the ICE was triggered".to_string(),
827 vec![],
828 vec![],
829 vec![],
830 &stacktrace,
831 Some(this.active_thread()),
832 &this.machine,
833 );
834 }
835
836 fn dedup_diagnostic(
840 &self,
841 dedup: &SpanDedupDiagnostic,
842 f: impl FnOnce(bool) -> NonHaltingDiagnostic,
843 ) {
844 let this = self.eval_context_ref();
845 let span1 = this.machine.current_user_relevant_span();
849 let span2 = this
852 .active_thread_stack()
853 .iter()
854 .rev()
855 .find(|frame| !frame.instance().def.requires_caller_location(*this.tcx))
856 .map(|frame| frame.current_span())
857 .unwrap_or(span1);
858
859 let mut lock = dedup.0.lock().unwrap();
860 let first = lock.is_empty();
861 if !lock.contains(&span2) && lock.insert(span1) && (span1 == span2 || lock.insert(span2)) {
863 this.emit_diagnostic(f(first));
865 }
866 }
867}
868
869pub struct SpanDedupDiagnostic(Mutex<FxHashSet<Span>>);
871
872impl SpanDedupDiagnostic {
873 pub const fn new() -> Self {
874 Self(Mutex::new(FxHashSet::with_hasher(FxBuildHasher)))
875 }
876}