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 ResourceExhaustion(ResourceExhaustionInfo::AddressSpaceFull) if ecx.machine.data_race.as_genmc_ref().is_some() =>
366 vec![
367 note!("in GenMC mode, the address space is limited to 4GB per thread, and addresses cannot be reused")
368 ],
369 UndefinedBehavior(AlignmentCheckFailed { .. })
370 if ecx.machine.check_alignment == AlignmentCheck::Symbolic
371 =>
372 vec![
373 note!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior"),
374 note!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives"),
375 ],
376 UndefinedBehavior(info) => {
377 let mut helps = vec![
378 note!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
379 note!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
380 ];
381 match info {
382 PointerUseAfterFree(alloc_id, _) | PointerOutOfBounds { alloc_id, .. } => {
383 if let Some(span) = ecx.machine.allocated_span(*alloc_id) {
384 helps.push(note_span!(span, "{:?} was allocated here:", alloc_id));
385 }
386 if let Some(span) = ecx.machine.deallocated_span(*alloc_id) {
387 helps.push(note_span!(span, "{:?} was deallocated here:", alloc_id));
388 }
389 }
390 AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => {
391 helps.push(note!("this means these two types are not *guaranteed* to be ABI-compatible across all targets"));
392 helps.push(note!("if you think this code should be accepted anyway, please report an issue with Miri"));
393 }
394 _ => {},
395 }
396 helps
397 }
398 InvalidProgram(
399 InvalidProgramInfo::AlreadyReported(_)
400 ) => {
401 return None;
403 }
404 _ =>
405 vec![],
406 };
407 (Some(title), helps)
408 };
409
410 let stacktrace = ecx.generate_stacktrace();
411 let (stacktrace, mut any_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
412
413 let mut show_all_threads = false;
414
415 let mut extra = String::new();
418 match res.kind() {
419 UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => {
420 writeln!(
421 extra,
422 "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:",
423 range = access.bad,
424 )
425 .unwrap();
426 writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap();
427 }
428 MachineStop(info) => {
429 let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
430 match info {
431 TerminationInfo::Deadlock => {
432 show_all_threads = true;
433 }
434 _ => {}
435 }
436 }
437 _ => {}
438 }
439
440 let mut primary_msg = String::new();
441 if let Some(title) = title {
442 write!(primary_msg, "{title}: ").unwrap();
443 }
444 write!(primary_msg, "{}", format_interp_error(ecx.tcx.dcx(), res)).unwrap();
445
446 if labels.is_empty() {
447 labels.push(format!(
448 "{} occurred {}",
449 title.unwrap_or("error"),
450 if stacktrace.is_empty() { "due to this code" } else { "here" }
451 ));
452 }
453
454 report_msg(
455 DiagLevel::Error,
456 primary_msg,
457 labels,
458 vec![],
459 helps,
460 &stacktrace,
461 Some(ecx.active_thread()),
462 &ecx.machine,
463 );
464
465 eprint!("{extra}"); if show_all_threads {
468 for (thread, stack) in ecx.machine.threads.all_blocked_stacks() {
469 if thread != ecx.active_thread() {
470 let stacktrace = Frame::generate_stacktrace_from_stack(stack);
471 let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
472 any_pruned |= was_pruned;
473 report_msg(
474 DiagLevel::Error,
475 format!("the evaluated program deadlocked"),
476 vec![format!("this thread got stuck here")],
477 vec![],
478 vec![],
479 &stacktrace,
480 Some(thread),
481 &ecx.machine,
482 )
483 }
484 }
485 }
486
487 if any_pruned {
489 ecx.tcx.dcx().note(
490 "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
491 );
492 }
493
494 for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
496 trace!("-------------------");
497 trace!("Frame {}", i);
498 trace!(" return: {:?}", frame.return_place);
499 for (i, local) in frame.locals.iter().enumerate() {
500 trace!(" local {}: {:?}", i, local);
501 }
502 }
503
504 None
505}
506
507pub fn report_leaks<'tcx>(
508 ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
509 leaks: Vec<(AllocId, MemoryKind, Allocation<Provenance, AllocExtra<'tcx>, MiriAllocBytes>)>,
510) {
511 let mut any_pruned = false;
512 for (id, kind, alloc) in leaks {
513 let mut title = format!(
514 "memory leaked: {id:?} ({}, size: {:?}, align: {:?})",
515 kind,
516 alloc.size().bytes(),
517 alloc.align.bytes()
518 );
519 let Some(backtrace) = alloc.extra.backtrace else {
520 ecx.tcx.dcx().err(title);
521 continue;
522 };
523 title.push_str(", allocated here:");
524 let (backtrace, pruned) = prune_stacktrace(backtrace, &ecx.machine);
525 any_pruned |= pruned;
526 report_msg(
527 DiagLevel::Error,
528 title,
529 vec![],
530 vec![],
531 vec![],
532 &backtrace,
533 None, &ecx.machine,
535 );
536 }
537 if any_pruned {
538 ecx.tcx.dcx().note(
539 "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
540 );
541 }
542}
543
544pub fn report_msg<'tcx>(
550 diag_level: DiagLevel,
551 title: String,
552 span_msg: Vec<String>,
553 notes: Vec<(Option<SpanData>, String)>,
554 helps: Vec<(Option<SpanData>, String)>,
555 stacktrace: &[FrameInfo<'tcx>],
556 thread: Option<ThreadId>,
557 machine: &MiriMachine<'tcx>,
558) {
559 let span = match stacktrace.first() {
560 Some(fi) => fi.span,
561 None =>
562 match thread {
563 Some(thread_id) => machine.threads.thread_ref(thread_id).origin_span,
564 None => DUMMY_SP,
565 },
566 };
567 let sess = machine.tcx.sess;
568 let level = match diag_level {
569 DiagLevel::Error => Level::Error,
570 DiagLevel::Warning => Level::Warning,
571 DiagLevel::Note => Level::Note,
572 };
573 let mut err = Diag::<()>::new(sess.dcx(), level, title);
574 err.span(span);
575
576 if span != DUMMY_SP {
578 for line in span_msg {
579 err.span_label(span, line);
580 }
581 } else {
582 for line in span_msg {
584 err.note(line);
585 }
586 err.note("(no span available)");
587 }
588
589 let mut extra_span = false;
591 for (span_data, note) in notes {
592 if let Some(span_data) = span_data {
593 err.span_note(span_data.span(), note);
594 extra_span = true;
595 } else {
596 err.note(note);
597 }
598 }
599 for (span_data, help) in helps {
600 if let Some(span_data) = span_data {
601 err.span_help(span_data.span(), help);
602 extra_span = true;
603 } else {
604 err.help(help);
605 }
606 }
607
608 if stacktrace.len() > 1 {
610 let mut backtrace_title = String::from("BACKTRACE");
611 if extra_span {
612 write!(backtrace_title, " (of the first span)").unwrap();
613 }
614 if let Some(thread) = thread {
615 let thread_name = machine.threads.get_thread_display_name(thread);
616 if thread_name != "main" {
617 write!(backtrace_title, " on thread `{thread_name}`").unwrap();
619 };
620 }
621 write!(backtrace_title, ":").unwrap();
622 err.note(backtrace_title);
623 for (idx, frame_info) in stacktrace.iter().enumerate() {
624 let is_local = machine.is_local(frame_info.instance);
625 if is_local && idx > 0 {
627 err.subdiagnostic(frame_info.as_note(machine.tcx));
628 } else {
629 let sm = sess.source_map();
630 let span = sm.span_to_diagnostic_string(frame_info.span);
631 err.note(format!("{frame_info} at {span}"));
632 }
633 }
634 } else if stacktrace.len() == 0 && !span.is_dummy() {
635 err.note(format!(
636 "this {} occurred while pushing a call frame onto an empty stack",
637 level.to_str()
638 ));
639 err.note("the span indicates which code caused the function to be called, but may not be the literal call site");
640 }
641
642 err.emit();
643}
644
645impl<'tcx> MiriMachine<'tcx> {
646 pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
647 use NonHaltingDiagnostic::*;
648
649 let stacktrace = Frame::generate_stacktrace_from_stack(self.threads.active_thread_stack());
650 let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, self);
651
652 let (label, diag_level) = match &e {
653 RejectedIsolatedOp(_) =>
654 ("operation rejected by isolation".to_string(), DiagLevel::Warning),
655 Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
656 NativeCallSharedMem { .. } =>
657 ("sharing memory with a native function".to_string(), DiagLevel::Warning),
658 NativeCallFnPtr =>
659 (
660 "sharing a function pointer with a native function".to_string(),
661 DiagLevel::Warning,
662 ),
663 ExternTypeReborrow =>
664 ("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
665 GenmcCompareExchangeWeak | GenmcCompareExchangeOrderingMismatch { .. } =>
666 ("GenMC might miss possible behaviors of this code".to_string(), DiagLevel::Warning),
667 CreatedPointerTag(..)
668 | PoppedPointerTag(..)
669 | TrackingAlloc(..)
670 | AccessedAlloc(..)
671 | FreedAlloc(..)
672 | ProgressReport { .. }
673 | WeakMemoryOutdatedLoad { .. } =>
674 ("tracking was triggered here".to_string(), DiagLevel::Note),
675 };
676
677 let title = match &e {
678 CreatedPointerTag(tag, None, _) => format!("created base tag {tag:?}"),
679 CreatedPointerTag(tag, Some(perm), None) =>
680 format!("created {tag:?} with {perm} derived from unknown tag"),
681 CreatedPointerTag(tag, Some(perm), Some((alloc_id, range, orig_tag))) =>
682 format!(
683 "created tag {tag:?} with {perm} at {alloc_id:?}{range:?} derived from {orig_tag:?}"
684 ),
685 PoppedPointerTag(item, cause) => format!("popped tracked tag for item {item:?}{cause}"),
686 TrackingAlloc(id, size, align) =>
687 format!(
688 "now tracking allocation {id:?} of {size} bytes (alignment {align} bytes)",
689 size = size.bytes(),
690 align = align.bytes(),
691 ),
692 AccessedAlloc(id, range, access_kind) =>
693 format!("{access_kind} at {id:?}[{}..{}]", range.start.bytes(), range.end().bytes()),
694 FreedAlloc(id) => format!("freed allocation {id:?}"),
695 RejectedIsolatedOp(op) => format!("{op} was made to return an error due to isolation"),
696 ProgressReport { .. } =>
697 format!("progress report: current operation being executed is here"),
698 Int2Ptr { .. } => format!("integer-to-pointer cast"),
699 NativeCallSharedMem { .. } =>
700 format!("sharing memory with a native function called via FFI"),
701 NativeCallFnPtr =>
702 format!("sharing a function pointer with a native function called via FFI"),
703 WeakMemoryOutdatedLoad { ptr } =>
704 format!("weak memory emulation: outdated value returned from load at {ptr}"),
705 ExternTypeReborrow =>
706 format!("reborrow of a reference to `extern type` is not properly supported"),
707 GenmcCompareExchangeWeak =>
708 "GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures."
709 .to_string(),
710 GenmcCompareExchangeOrderingMismatch {
711 success_ordering,
712 upgraded_success_ordering,
713 failure_ordering,
714 effective_failure_ordering,
715 } => {
716 let was_upgraded_msg = if success_ordering != upgraded_success_ordering {
717 format!("Success ordering '{success_ordering:?}' was upgraded to '{upgraded_success_ordering:?}' to match failure ordering '{failure_ordering:?}'")
718 } else {
719 assert_ne!(failure_ordering, effective_failure_ordering);
720 format!("Due to success ordering '{success_ordering:?}', the failure ordering '{failure_ordering:?}' is treated like '{effective_failure_ordering:?}'")
721 };
722 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.")
723 }
724 };
725
726 let notes = match &e {
727 ProgressReport { block_count } => {
728 vec![note!("so far, {block_count} basic blocks have been executed")]
729 }
730 _ => vec![],
731 };
732
733 let helps = match &e {
734 Int2Ptr { details: true } => {
735 let mut v = vec![
736 note!(
737 "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"
738 ),
739 note!(
740 "see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation"
741 ),
742 note!(
743 "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"
744 ),
745 note!(
746 "you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics"
747 ),
748 ];
749 if self.borrow_tracker.as_ref().is_some_and(|b| {
750 matches!(
751 b.borrow().borrow_tracker_method(),
752 BorrowTrackerMethod::TreeBorrows { .. }
753 )
754 }) {
755 v.push(
756 note!("Tree Borrows does not support integer-to-pointer casts, so the program is likely to go wrong when this pointer gets used")
757 );
758 } else {
759 v.push(
760 note!("alternatively, `MIRIFLAGS=-Zmiri-permissive-provenance` disables this warning")
761 );
762 }
763 v
764 }
765 NativeCallSharedMem { tracing } =>
766 if *tracing {
767 vec![
768 note!(
769 "when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis"
770 ),
771 note!(
772 "in particular, Miri assumes that the native call initializes all memory it has written to"
773 ),
774 note!(
775 "Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory"
776 ),
777 note!(
778 "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"
779 ),
780 note!(
781 "tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here"
782 ),
783 ]
784 } else {
785 vec![
786 note!(
787 "when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory"
788 ),
789 note!(
790 "in particular, Miri assumes that the native call initializes all memory it has access to"
791 ),
792 note!(
793 "Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory"
794 ),
795 note!(
796 "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"
797 ),
798 ]
799 },
800 NativeCallFnPtr => {
801 vec![note!(
802 "calling Rust functions from C is not supported and will, in the best case, crash the program"
803 )]
804 }
805 ExternTypeReborrow => {
806 assert!(self.borrow_tracker.as_ref().is_some_and(|b| {
807 matches!(
808 b.borrow().borrow_tracker_method(),
809 BorrowTrackerMethod::StackedBorrows
810 )
811 }));
812 vec![
813 note!(
814 "`extern type` are not compatible with the Stacked Borrows aliasing model implemented by Miri; Miri may miss bugs in this code"
815 ),
816 note!(
817 "try running with `MIRIFLAGS=-Zmiri-tree-borrows` to use the more permissive but also even more experimental Tree Borrows aliasing checks instead"
818 ),
819 ]
820 }
821 _ => vec![],
822 };
823
824 report_msg(
825 diag_level,
826 title,
827 vec![label],
828 notes,
829 helps,
830 &stacktrace,
831 Some(self.threads.active_thread()),
832 self,
833 );
834 }
835}
836
837impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
838pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
839 fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
840 let this = self.eval_context_ref();
841 this.machine.emit_diagnostic(e);
842 }
843
844 fn handle_ice(&self) {
846 eprintln!();
847 eprintln!(
848 "Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic:"
849 );
850 let this = self.eval_context_ref();
851 let stacktrace = this.generate_stacktrace();
852 report_msg(
853 DiagLevel::Note,
854 "the place in the program where the ICE was triggered".to_string(),
855 vec![],
856 vec![],
857 vec![],
858 &stacktrace,
859 Some(this.active_thread()),
860 &this.machine,
861 );
862 }
863
864 fn dedup_diagnostic(
868 &self,
869 dedup: &SpanDedupDiagnostic,
870 f: impl FnOnce(bool) -> NonHaltingDiagnostic,
871 ) {
872 let this = self.eval_context_ref();
873 let span1 = this.machine.current_user_relevant_span();
877 let span2 = this
880 .active_thread_stack()
881 .iter()
882 .rev()
883 .find(|frame| !frame.instance().def.requires_caller_location(*this.tcx))
884 .map(|frame| frame.current_span())
885 .unwrap_or(span1);
886
887 let mut lock = dedup.0.lock().unwrap();
888 let first = lock.is_empty();
889 if !lock.contains(&span2) && lock.insert(span1) && (span1 == span2 || lock.insert(span2)) {
891 this.emit_diagnostic(f(first));
893 }
894 }
895}
896
897pub struct SpanDedupDiagnostic(Mutex<FxHashSet<Span>>);
899
900impl SpanDedupDiagnostic {
901 pub const fn new() -> Self {
902 Self(Mutex::new(FxHashSet::with_hasher(rustc_hash::FxBuildHasher)))
903 }
904}