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