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