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