Skip to content

Commit a39d3f7

Browse files
committed
show span when there is an error invoking a global ctor/dtor or the thread main fn
1 parent ade385f commit a39d3f7

19 files changed

+185
-49
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@ Definite bugs found:
624624
* [Mockall reading uninitialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite)
625625
* [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248)
626626
* [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816)
627+
* [`winit` registering a global constructor with the wrong ABI on Windows](https://github.com/rust-windowing/winit/issues/4435)
627628

628629
Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment):
629630

src/concurrency/thread.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_hir::def_id::DefId;
1414
use rustc_index::{Idx, IndexVec};
1515
use rustc_middle::mir::Mutability;
1616
use rustc_middle::ty::layout::TyAndLayout;
17-
use rustc_span::Span;
17+
use rustc_span::{DUMMY_SP, Span};
1818
use rustc_target::spec::Os;
1919

2020
use crate::concurrency::GlobalDataRaceHandler;
@@ -174,6 +174,10 @@ pub struct Thread<'tcx> {
174174
/// The virtual call stack.
175175
stack: Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>>,
176176

177+
/// A span that explains where the thread (or more specifically, its current root
178+
/// frame) "comes from".
179+
pub(crate) origin_span: Span,
180+
177181
/// The function to call when the stack ran empty, to figure out what to do next.
178182
/// Conceptually, this is the interpreter implementation of the things that happen 'after' the
179183
/// Rust language entry point for this thread returns (usually implemented by the C or OS runtime).
@@ -303,6 +307,7 @@ impl<'tcx> Thread<'tcx> {
303307
state: ThreadState::Enabled,
304308
thread_name: name.map(|name| Vec::from(name.as_bytes())),
305309
stack: Vec::new(),
310+
origin_span: DUMMY_SP,
306311
top_user_relevant_frame: None,
307312
join_status: ThreadJoinStatus::Joinable,
308313
unwind_payloads: Vec::new(),
@@ -318,6 +323,7 @@ impl VisitProvenance for Thread<'_> {
318323
unwind_payloads: panic_payload,
319324
last_error,
320325
stack,
326+
origin_span: _,
321327
top_user_relevant_frame: _,
322328
state: _,
323329
thread_name: _,
@@ -584,6 +590,10 @@ impl<'tcx> ThreadManager<'tcx> {
584590
&self.threads[self.active_thread]
585591
}
586592

593+
pub fn thread_ref(&self, thread_id: ThreadId) -> &Thread<'tcx> {
594+
&self.threads[thread_id]
595+
}
596+
587597
/// Mark the thread as detached, which means that no other thread will try
588598
/// to join it and the thread is responsible for cleaning up.
589599
///
@@ -704,8 +714,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
704714
#[inline]
705715
fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {
706716
let this = self.eval_context_mut();
707-
let mut callback = this
708-
.active_thread_mut()
717+
let active_thread = this.active_thread_mut();
718+
active_thread.origin_span = DUMMY_SP; // reset, the old value no longer applied
719+
let mut callback = active_thread
709720
.on_stack_empty
710721
.take()
711722
.expect("`on_stack_empty` not set up, or already running");
@@ -891,11 +902,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
891902
let this = self.eval_context_mut();
892903

893904
// Create the new thread
905+
let current_span = this.machine.current_user_relevant_span();
894906
let new_thread_id = this.machine.threads.create_thread({
895907
let mut state = tls::TlsDtorsState::default();
896908
Box::new(move |m| state.on_stack_empty(m))
897909
});
898-
let current_span = this.machine.current_user_relevant_span();
899910
match &mut this.machine.data_race {
900911
GlobalDataRaceHandler::None => {}
901912
GlobalDataRaceHandler::Vclocks(data_race) =>
@@ -934,12 +945,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
934945
// it.
935946
let ret_place = this.allocate(ret_layout, MiriMemoryKind::Machine.into())?;
936947

937-
this.call_function(
948+
this.call_thread_root_function(
938949
instance,
939950
start_abi,
940951
&[func_arg],
941952
Some(&ret_place),
942-
ReturnContinuation::Stop { cleanup: true },
953+
current_span,
943954
)?;
944955

945956
// Restore the old active thread frame.

src/diagnostics.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,11 @@ pub fn report_result<'tcx>(
444444
write!(primary_msg, "{}", format_interp_error(ecx.tcx.dcx(), res)).unwrap();
445445

446446
if labels.is_empty() {
447-
labels.push(format!("{} occurred here", title.unwrap_or("error")));
447+
labels.push(format!(
448+
"{} occurred {}",
449+
title.unwrap_or("error"),
450+
if stacktrace.is_empty() { "due to this code" } else { "here" }
451+
));
448452
}
449453

450454
report_msg(
@@ -552,7 +556,14 @@ pub fn report_msg<'tcx>(
552556
thread: Option<ThreadId>,
553557
machine: &MiriMachine<'tcx>,
554558
) {
555-
let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
559+
let span = match stacktrace.first() {
560+
Some(fi) => fi.span,
561+
None =>
562+
match thread {
563+
Some(thread_id) => machine.threads.thread_ref(thread_id).origin_span,
564+
None => DUMMY_SP,
565+
},
566+
};
556567
let sess = machine.tcx.sess;
557568
let level = match diag_level {
558569
DiagLevel::Error => Level::Error,
@@ -620,6 +631,9 @@ pub fn report_msg<'tcx>(
620631
err.note(format!("{frame_info} at {span}"));
621632
}
622633
}
634+
} else if stacktrace.len() == 0 && !span.is_dummy() {
635+
err.note("this error occurred while pushing a call frame onto an empty stack");
636+
err.note("the span indicates which code caused the function to be called, but may not be the literal call site");
623637
}
624638

625639
err.emit();

src/helpers.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
472472
)
473473
}
474474

475+
/// Call a function in an "empty" thread.
476+
fn call_thread_root_function(
477+
&mut self,
478+
f: ty::Instance<'tcx>,
479+
caller_abi: ExternAbi,
480+
args: &[ImmTy<'tcx>],
481+
dest: Option<&MPlaceTy<'tcx>>,
482+
span: Span,
483+
) -> InterpResult<'tcx> {
484+
let this = self.eval_context_mut();
485+
assert!(this.active_thread_stack().is_empty());
486+
assert!(this.active_thread_ref().origin_span.is_dummy());
487+
this.active_thread_mut().origin_span = span;
488+
this.call_function(f, caller_abi, args, dest, ReturnContinuation::Stop { cleanup: true })
489+
}
490+
475491
/// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter
476492
/// of `action` will be true if this is frozen, false if this is in an `UnsafeCell`.
477493
/// The range is relative to `place`.
@@ -995,11 +1011,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
9951011
interp_ok(())
9961012
}
9971013

998-
/// Lookup an array of immediates from any linker sections matching the provided predicate.
1014+
/// Lookup an array of immediates from any linker sections matching the provided predicate,
1015+
/// with the spans of where they were found.
9991016
fn lookup_link_section(
10001017
&mut self,
10011018
include_name: impl Fn(&str) -> bool,
1002-
) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
1019+
) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> {
10031020
let this = self.eval_context_mut();
10041021
let tcx = this.tcx.tcx;
10051022

@@ -1012,19 +1029,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
10121029
};
10131030
if include_name(link_section.as_str()) {
10141031
let instance = ty::Instance::mono(tcx, def_id);
1032+
let span = tcx.def_span(def_id);
10151033
let const_val = this.eval_global(instance).unwrap_or_else(|err| {
10161034
panic!(
10171035
"failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
10181036
)
10191037
});
10201038
match const_val.layout.ty.kind() {
10211039
ty::FnPtr(..) => {
1022-
array.push(this.read_immediate(&const_val)?);
1040+
array.push((this.read_immediate(&const_val)?, span));
10231041
}
10241042
ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
10251043
let mut elems = this.project_array_fields(&const_val)?;
10261044
while let Some((_idx, elem)) = elems.next(this)? {
1027-
array.push(this.read_immediate(&elem)?);
1045+
array.push((this.read_immediate(&elem)?, span));
10281046
}
10291047
}
10301048
_ =>

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
clippy::needless_question_mark,
4040
clippy::needless_lifetimes,
4141
clippy::too_long_first_doc_paragraph,
42+
clippy::len_zero,
4243
// We don't use translatable diagnostics
4344
rustc::diagnostic_outside_of_impl,
4445
// We are not implementing queries here so it's fine

src/shims/global_ctor.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::task::Poll;
44

55
use rustc_abi::ExternAbi;
6+
use rustc_span::Span;
67
use rustc_target::spec::BinaryFormat;
78

89
use crate::*;
@@ -15,7 +16,7 @@ enum GlobalCtorStatePriv<'tcx> {
1516
#[default]
1617
Init,
1718
/// The list of constructor functions that we still have to call.
18-
Ctors(Vec<ImmTy<'tcx>>),
19+
Ctors(Vec<(ImmTy<'tcx>, Span)>),
1920
Done,
2021
}
2122

@@ -67,19 +68,19 @@ impl<'tcx> GlobalCtorState<'tcx> {
6768
break 'new_state Ctors(ctors);
6869
}
6970
Ctors(ctors) => {
70-
if let Some(ctor) = ctors.pop() {
71+
if let Some((ctor, span)) = ctors.pop() {
7172
let this = this.eval_context_mut();
7273

7374
let ctor = ctor.to_scalar().to_pointer(this)?;
7475
let thread_callback = this.get_ptr_fn(ctor)?.as_instance()?;
7576

7677
// The signature of this function is `unsafe extern "C" fn()`.
77-
this.call_function(
78+
this.call_thread_root_function(
7879
thread_callback,
7980
ExternAbi::C { unwind: false },
8081
&[],
8182
None,
82-
ReturnContinuation::Stop { cleanup: true },
83+
span,
8384
)?;
8485

8586
return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter)

0 commit comments

Comments
 (0)