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