From 7ad3301e03ec73dec2b304826a84e2eae461e2fc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Dec 2025 13:57:59 +0100 Subject: [PATCH] show span when there is an error invoking a global ctor/dtor or the thread main fn --- src/tools/miri/README.md | 1 + src/tools/miri/src/concurrency/thread.rs | 23 +++++++--- src/tools/miri/src/diagnostics.rs | 21 ++++++++- src/tools/miri/src/helpers.rs | 26 +++++++++-- src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/shims/global_ctor.rs | 9 ++-- src/tools/miri/src/shims/tls.rs | 45 ++++++++++--------- .../miri/src/shims/unix/foreign_items.rs | 5 ++- .../src/shims/unix/macos/foreign_items.rs | 7 ++- .../libc_pthread_create_too_few_args.rs | 2 +- .../libc_pthread_create_too_few_args.stderr | 8 +++- .../libc_pthread_create_too_many_args.rs | 2 +- .../libc_pthread_create_too_many_args.stderr | 8 +++- .../concurrency/tls_pthread_dtor_wrong_abi.rs | 21 +++++++++ .../tls_pthread_dtor_wrong_abi.stderr | 13 ++++++ .../tests/fail/shims/ctor_wrong_ret_type.rs | 2 +- .../fail/shims/ctor_wrong_ret_type.stderr | 12 ++++- .../fail/shims/macos_tlv_atexit_wrong_abi.rs | 18 ++++++++ .../shims/macos_tlv_atexit_wrong_abi.stderr | 13 ++++++ 19 files changed, 188 insertions(+), 49 deletions(-) create mode 100644 src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.rs create mode 100644 src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.stderr create mode 100644 src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.rs create mode 100644 src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.stderr diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 1c6a2daa093d..32494141589a 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -624,6 +624,7 @@ Definite bugs found: * [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) * [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248) * [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816) +* [`winit` registering a global constructor with the wrong ABI on Windows](https://github.com/rust-windowing/winit/issues/4435) Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment): diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index c7ae335d0479..5016e3b66ca6 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -14,7 +14,7 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::Mutability; use rustc_middle::ty::layout::TyAndLayout; -use rustc_span::Span; +use rustc_span::{DUMMY_SP, Span}; use rustc_target::spec::Os; use crate::concurrency::GlobalDataRaceHandler; @@ -174,6 +174,10 @@ pub struct Thread<'tcx> { /// The virtual call stack. stack: Vec>>, + /// A span that explains where the thread (or more specifically, its current root + /// frame) "comes from". + pub(crate) origin_span: Span, + /// The function to call when the stack ran empty, to figure out what to do next. /// Conceptually, this is the interpreter implementation of the things that happen 'after' the /// Rust language entry point for this thread returns (usually implemented by the C or OS runtime). @@ -303,6 +307,7 @@ fn new(name: Option<&str>, on_stack_empty: Option>) -> state: ThreadState::Enabled, thread_name: name.map(|name| Vec::from(name.as_bytes())), stack: Vec::new(), + origin_span: DUMMY_SP, top_user_relevant_frame: None, join_status: ThreadJoinStatus::Joinable, unwind_payloads: Vec::new(), @@ -318,6 +323,7 @@ fn visit_provenance(&self, visit: &mut VisitWith<'_>) { unwind_payloads: panic_payload, last_error, stack, + origin_span: _, top_user_relevant_frame: _, state: _, thread_name: _, @@ -584,6 +590,10 @@ pub fn active_thread_ref(&self) -> &Thread<'tcx> { &self.threads[self.active_thread] } + pub fn thread_ref(&self, thread_id: ThreadId) -> &Thread<'tcx> { + &self.threads[thread_id] + } + /// Mark the thread as detached, which means that no other thread will try /// to join it and the thread is responsible for cleaning up. /// @@ -704,8 +714,9 @@ fn run_timeout_callback(&mut self) -> InterpResult<'tcx> { #[inline] fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> { let this = self.eval_context_mut(); - let mut callback = this - .active_thread_mut() + let active_thread = this.active_thread_mut(); + active_thread.origin_span = DUMMY_SP; // reset, the old value no longer applied + let mut callback = active_thread .on_stack_empty .take() .expect("`on_stack_empty` not set up, or already running"); @@ -891,11 +902,11 @@ fn start_regular_thread( let this = self.eval_context_mut(); // Create the new thread + let current_span = this.machine.current_user_relevant_span(); let new_thread_id = this.machine.threads.create_thread({ let mut state = tls::TlsDtorsState::default(); Box::new(move |m| state.on_stack_empty(m)) }); - let current_span = this.machine.current_user_relevant_span(); match &mut this.machine.data_race { GlobalDataRaceHandler::None => {} GlobalDataRaceHandler::Vclocks(data_race) => @@ -934,12 +945,12 @@ fn start_regular_thread( // it. let ret_place = this.allocate(ret_layout, MiriMemoryKind::Machine.into())?; - this.call_function( + this.call_thread_root_function( instance, start_abi, &[func_arg], Some(&ret_place), - ReturnContinuation::Stop { cleanup: true }, + current_span, )?; // Restore the old active thread frame. diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 8e252d306b29..01f77f261d70 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -444,7 +444,11 @@ pub fn report_result<'tcx>( write!(primary_msg, "{}", format_interp_error(ecx.tcx.dcx(), res)).unwrap(); if labels.is_empty() { - labels.push(format!("{} occurred here", title.unwrap_or("error"))); + labels.push(format!( + "{} occurred {}", + title.unwrap_or("error"), + if stacktrace.is_empty() { "due to this code" } else { "here" } + )); } report_msg( @@ -552,7 +556,14 @@ pub fn report_msg<'tcx>( thread: Option, machine: &MiriMachine<'tcx>, ) { - let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span); + let span = match stacktrace.first() { + Some(fi) => fi.span, + None => + match thread { + Some(thread_id) => machine.threads.thread_ref(thread_id).origin_span, + None => DUMMY_SP, + }, + }; let sess = machine.tcx.sess; let level = match diag_level { DiagLevel::Error => Level::Error, @@ -620,6 +631,12 @@ pub fn report_msg<'tcx>( err.note(format!("{frame_info} at {span}")); } } + } else if stacktrace.len() == 0 && !span.is_dummy() { + err.note(format!( + "this {} occurred while pushing a call frame onto an empty stack", + level.to_str() + )); + err.note("the span indicates which code caused the function to be called, but may not be the literal call site"); } err.emit(); diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 383a4e2ea4b0..75b7e9f5966f 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -472,6 +472,22 @@ fn call_function( ) } + /// Call a function in an "empty" thread. + fn call_thread_root_function( + &mut self, + f: ty::Instance<'tcx>, + caller_abi: ExternAbi, + args: &[ImmTy<'tcx>], + dest: Option<&MPlaceTy<'tcx>>, + span: Span, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + assert!(this.active_thread_stack().is_empty()); + assert!(this.active_thread_ref().origin_span.is_dummy()); + this.active_thread_mut().origin_span = span; + this.call_function(f, caller_abi, args, dest, ReturnContinuation::Stop { cleanup: true }) + } + /// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter /// of `action` will be true if this is frozen, false if this is in an `UnsafeCell`. /// The range is relative to `place`. @@ -995,11 +1011,12 @@ fn expect_target_feature_for_intrinsic( interp_ok(()) } - /// Lookup an array of immediates from any linker sections matching the provided predicate. + /// Lookup an array of immediates from any linker sections matching the provided predicate, + /// with the spans of where they were found. fn lookup_link_section( &mut self, include_name: impl Fn(&str) -> bool, - ) -> InterpResult<'tcx, Vec>> { + ) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> { let this = self.eval_context_mut(); let tcx = this.tcx.tcx; @@ -1012,6 +1029,7 @@ fn lookup_link_section( }; if include_name(link_section.as_str()) { let instance = ty::Instance::mono(tcx, def_id); + let span = tcx.def_span(def_id); let const_val = this.eval_global(instance).unwrap_or_else(|err| { panic!( "failed to evaluate static in required link_section: {def_id:?}\n{err:?}" @@ -1019,12 +1037,12 @@ fn lookup_link_section( }); match const_val.layout.ty.kind() { ty::FnPtr(..) => { - array.push(this.read_immediate(&const_val)?); + array.push((this.read_immediate(&const_val)?, span)); } ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => { let mut elems = this.project_array_fields(&const_val)?; while let Some((_idx, elem)) = elems.next(this)? { - array.push(this.read_immediate(&elem)?); + array.push((this.read_immediate(&elem)?, span)); } } _ => diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index b30395b738b1..fe501b8d7b30 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -39,6 +39,7 @@ clippy::needless_question_mark, clippy::needless_lifetimes, clippy::too_long_first_doc_paragraph, + clippy::len_zero, // We don't use translatable diagnostics rustc::diagnostic_outside_of_impl, // We are not implementing queries here so it's fine diff --git a/src/tools/miri/src/shims/global_ctor.rs b/src/tools/miri/src/shims/global_ctor.rs index c56251bbe63a..d3296e4445f7 100644 --- a/src/tools/miri/src/shims/global_ctor.rs +++ b/src/tools/miri/src/shims/global_ctor.rs @@ -3,6 +3,7 @@ use std::task::Poll; use rustc_abi::ExternAbi; +use rustc_span::Span; use rustc_target::spec::BinaryFormat; use crate::*; @@ -15,7 +16,7 @@ enum GlobalCtorStatePriv<'tcx> { #[default] Init, /// The list of constructor functions that we still have to call. - Ctors(Vec>), + Ctors(Vec<(ImmTy<'tcx>, Span)>), Done, } @@ -67,19 +68,19 @@ pub fn on_stack_empty( break 'new_state Ctors(ctors); } Ctors(ctors) => { - if let Some(ctor) = ctors.pop() { + if let Some((ctor, span)) = ctors.pop() { let this = this.eval_context_mut(); let ctor = ctor.to_scalar().to_pointer(this)?; let thread_callback = this.get_ptr_fn(ctor)?.as_instance()?; // The signature of this function is `unsafe extern "C" fn()`. - this.call_function( + this.call_thread_root_function( thread_callback, ExternAbi::C { unwind: false }, &[], None, - ReturnContinuation::Stop { cleanup: true }, + span, )?; return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter) diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index 2159c41ab16c..3ecd9b1ead38 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -6,6 +6,7 @@ use rustc_abi::{ExternAbi, HasDataLayout, Size}; use rustc_middle::ty; +use rustc_span::Span; use rustc_target::spec::Os; use crate::*; @@ -17,7 +18,7 @@ pub struct TlsEntry<'tcx> { /// The data for this key. None is used to represent NULL. /// (We normalize this early to avoid having to do a NULL-ptr-test each time we access the data.) data: BTreeMap, - dtor: Option>, + dtor: Option<(ty::Instance<'tcx>, Span)>, } #[derive(Default, Debug)] @@ -38,7 +39,7 @@ pub struct TlsData<'tcx> { /// On macOS, each thread holds a list of destructor functions with their /// respective data arguments. - macos_thread_dtors: BTreeMap, Scalar)>>, + macos_thread_dtors: BTreeMap, Scalar, Span)>>, } impl<'tcx> Default for TlsData<'tcx> { @@ -57,7 +58,7 @@ impl<'tcx> TlsData<'tcx> { #[expect(clippy::arithmetic_side_effects)] pub fn create_tls_key( &mut self, - dtor: Option>, + dtor: Option<(ty::Instance<'tcx>, Span)>, max_size: Size, ) -> InterpResult<'tcx, TlsKey> { let new_key = self.next_key; @@ -126,8 +127,9 @@ pub fn add_macos_thread_dtor( thread: ThreadId, dtor: ty::Instance<'tcx>, data: Scalar, + span: Span, ) -> InterpResult<'tcx> { - self.macos_thread_dtors.entry(thread).or_default().push((dtor, data)); + self.macos_thread_dtors.entry(thread).or_default().push((dtor, data, span)); interp_ok(()) } @@ -154,7 +156,7 @@ fn fetch_tls_dtor( &mut self, key: Option, thread_id: ThreadId, - ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> { + ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey, Span)> { use std::ops::Bound::*; let thread_local = &mut self.keys; @@ -172,11 +174,10 @@ fn fetch_tls_dtor( for (&key, TlsEntry { data, dtor }) in thread_local.range_mut((start, Unbounded)) { match data.entry(thread_id) { BTreeEntry::Occupied(entry) => { - if let Some(dtor) = dtor { + if let Some((dtor, span)) = dtor { // Set TLS data to NULL, and call dtor with old value. let data_scalar = entry.remove(); - let ret = Some((*dtor, data_scalar, key)); - return ret; + return Some((*dtor, data_scalar, key, *span)); } } BTreeEntry::Vacant(_) => {} @@ -205,7 +206,7 @@ fn visit_provenance(&self, visit: &mut VisitWith<'_>) { for scalar in keys.values().flat_map(|v| v.data.values()) { scalar.visit_provenance(visit); } - for (_, scalar) in macos_thread_dtors.values().flatten() { + for (_, scalar, _) in macos_thread_dtors.values().flatten() { scalar.visit_provenance(visit); } } @@ -222,7 +223,7 @@ enum TlsDtorsStatePriv<'tcx> { PthreadDtors(RunningDtorState), /// For Windows Dtors, we store the list of functions that we still have to call. /// These are functions from the magic `.CRT$XLB` linker section. - WindowsDtors(Vec>), + WindowsDtors(Vec<(ImmTy<'tcx>, Span)>), Done, } @@ -273,8 +274,8 @@ pub fn on_stack_empty( } } WindowsDtors(dtors) => { - if let Some(dtor) = dtors.pop() { - this.schedule_windows_tls_dtor(dtor)?; + if let Some((dtor, span)) = dtors.pop() { + this.schedule_windows_tls_dtor(dtor, span)?; return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter) } else { // No more destructors to run. @@ -297,7 +298,7 @@ impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Schedule TLS destructors for Windows. /// On windows, TLS destructors are managed by std. - fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec>> { + fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> { let this = self.eval_context_mut(); // Windows has a special magic linker section that is run on certain events. @@ -305,7 +306,7 @@ fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec>> { interp_ok(this.lookup_link_section(|section| section == ".CRT$XLB")?) } - fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> { + fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>, span: Span) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let dtor = dtor.to_scalar().to_pointer(this)?; @@ -320,12 +321,12 @@ fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> // The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`. // FIXME: `h` should be a handle to the current module and what `pv` should be is unknown // but both are ignored by std. - this.call_function( + this.call_thread_root_function( thread_callback, ExternAbi::System { unwind: false }, &[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr], None, - ReturnContinuation::Stop { cleanup: true }, + span, )?; interp_ok(()) } @@ -338,15 +339,15 @@ fn schedule_macos_tls_dtor(&mut self) -> InterpResult<'tcx, Poll<()>> { // registers another destructor, it will be run next. // See https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/DyldRuntimeState.cpp#L2277 let dtor = this.machine.tls.macos_thread_dtors.get_mut(&thread_id).and_then(Vec::pop); - if let Some((instance, data)) = dtor { + if let Some((instance, data, span)) = dtor { trace!("Running macos dtor {:?} on {:?} at {:?}", instance, data, thread_id); - this.call_function( + this.call_thread_root_function( instance, ExternAbi::C { unwind: false }, &[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)], None, - ReturnContinuation::Stop { cleanup: true }, + span, )?; return interp_ok(Poll::Pending); @@ -370,7 +371,7 @@ fn schedule_next_pthread_tls_dtor( // We ran each dtor once, start over from the beginning. None => this.machine.tls.fetch_tls_dtor(None, active_thread), }; - if let Some((instance, ptr, key)) = dtor { + if let Some((instance, ptr, key, span)) = dtor { state.last_key = Some(key); trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread); assert!( @@ -378,12 +379,12 @@ fn schedule_next_pthread_tls_dtor( "data can't be NULL when dtor is called!" ); - this.call_function( + this.call_thread_root_function( instance, ExternAbi::C { unwind: false }, &[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)], None, - ReturnContinuation::Stop { cleanup: true }, + span, )?; return interp_ok(Poll::Pending); diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 378a8537fc73..64b8376ff4aa 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -634,7 +634,10 @@ fn emulate_foreign_item_inner( // Extract the function type out of the signature (that seems easier than constructing it ourselves). let dtor = if !this.ptr_is_null(dtor)? { - Some(this.get_ptr_fn(dtor)?.as_instance()?) + Some(( + this.get_ptr_fn(dtor)?.as_instance()?, + this.machine.current_user_relevant_span(), + )) } else { None }; diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index ed22457ec01a..dd7b95bdc82b 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -158,7 +158,12 @@ fn emulate_foreign_item_inner( let dtor = this.get_ptr_fn(dtor)?.as_instance()?; let data = this.read_scalar(data)?; let active_thread = this.active_thread(); - this.machine.tls.add_macos_thread_dtor(active_thread, dtor, data)?; + this.machine.tls.add_macos_thread_dtor( + active_thread, + dtor, + data, + this.machine.current_user_relevant_span(), + )?; } // Querying system information diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs index 520bc9572f86..6fec6500cc96 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # No pthreads on Windows -//~^ERROR: calling a function with more arguments than it expected //! The thread function must have exactly one argument. @@ -17,6 +16,7 @@ fn main() { mem::transmute(thread_start); assert_eq!( libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), + //~^ERROR: calling a function with more arguments than it expected 0 ); assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr index 4d5a80c828cd..fef91e85470d 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr @@ -1,9 +1,13 @@ error: Undefined Behavior: calling a function with more arguments than it expected + --> tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs:LL:CC + | +LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code | - = note: Undefined Behavior occurred here - = note: (no span available) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: this error occurred while pushing a call frame onto an empty stack + = note: the span indicates which code caused the function to be called, but may not be the literal call site error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs index 92d8a765e511..cb55b0f92ee6 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # No pthreads on Windows -//~^ERROR: calling a function with fewer arguments than it requires //! The thread function must have exactly one argument. @@ -17,6 +16,7 @@ fn main() { mem::transmute(thread_start); assert_eq!( libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), + //~^ERROR: calling a function with fewer arguments than it requires 0 ); assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr index 1bc79411197c..4d70576d784c 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr @@ -1,9 +1,13 @@ error: Undefined Behavior: calling a function with fewer arguments than it requires + --> tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs:LL:CC + | +LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code | - = note: Undefined Behavior occurred here - = note: (no span available) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: this error occurred while pushing a call frame onto an empty stack + = note: the span indicates which code caused the function to be called, but may not be the literal call site error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.rs b/src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.rs new file mode 100644 index 000000000000..b5eec2db201c --- /dev/null +++ b/src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.rs @@ -0,0 +1,21 @@ +//@ignore-target: windows # No pthreads on Windows + +use std::{mem, ptr}; + +pub type Key = libc::pthread_key_t; + +pub unsafe fn create(dtor: unsafe fn(*mut u8)) -> Key { + let mut key = 0; + assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); + //~^ERROR: calling a function with calling convention "Rust" + key +} + +unsafe fn dtor(_ptr: *mut u8) {} + +fn main() { + unsafe { + let key = create(dtor); + libc::pthread_setspecific(key, ptr::without_provenance(1)); + } +} diff --git a/src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.stderr b/src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.stderr new file mode 100644 index 000000000000..32e92319cdf3 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C" + --> tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.rs:LL:CC + | +LL | assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: this error occurred while pushing a call frame onto an empty stack + = note: the span indicates which code caused the function to be called, but may not be the literal call site + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs index 1e10f682e71e..3629e4387e7f 100644 --- a/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs +++ b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs @@ -1,5 +1,4 @@ unsafe extern "C" fn ctor() -> i32 { - //~^ERROR: calling a function with return type i32 passing return place of type () 0 } @@ -31,6 +30,7 @@ macro_rules! ctor { )] #[used] static $ident: unsafe extern "C" fn() -> i32 = $ctor; + //~^ERROR: calling a function with return type i32 passing return place of type () }; } diff --git a/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr index 68c56044be88..1b4cd23e41a8 100644 --- a/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr +++ b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr @@ -1,11 +1,19 @@ error: Undefined Behavior: calling a function with return type i32 passing return place of type () + --> tests/fail/shims/ctor_wrong_ret_type.rs:LL:CC + | +LL | static $ident: unsafe extern "C" fn() -> i32 = $ctor; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code +... +LL | ctor! { CTOR = ctor } + | --------------------- in this macro invocation | - = note: Undefined Behavior occurred here - = note: (no span available) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets = help: if you think this code should be accepted anyway, please report an issue with Miri + = note: this error occurred while pushing a call frame onto an empty stack + = note: the span indicates which code caused the function to be called, but may not be the literal call site + = note: this error originates in the macro `ctor` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.rs b/src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.rs new file mode 100644 index 000000000000..5d5cb749f896 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.rs @@ -0,0 +1,18 @@ +//@only-target: darwin + +use std::{mem, ptr}; + +extern "C" { + fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); +} + +fn register(dtor: unsafe fn(*mut u8)) { + unsafe { + _tlv_atexit(mem::transmute(dtor), ptr::null_mut()); + //~^ERROR: calling a function with calling convention "Rust" + } +} + +fn main() { + register(|_| ()); +} diff --git a/src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.stderr b/src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.stderr new file mode 100644 index 000000000000..0fc87f13f16d --- /dev/null +++ b/src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C" + --> tests/fail/shims/macos_tlv_atexit_wrong_abi.rs:LL:CC + | +LL | _tlv_atexit(mem::transmute(dtor), ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: this error occurred while pushing a call frame onto an empty stack + = note: the span indicates which code caused the function to be called, but may not be the literal call site + +error: aborting due to 1 previous error +