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