From 9d4130b1bb81fd53a74cc8c83f5f74682e828129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 23 Sep 2025 14:12:43 +0200 Subject: [PATCH 01/26] Use `rustc-josh-sync` merge commit message for pull PR description --- src/tools/miri/.github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 740118bb4a02b..3078cd35b1084 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -231,6 +231,9 @@ jobs: exit ${exitcode} fi + # Store merge commit message + git log -1 --pretty=%B > message.txt + # Format changes ./miri toolchain ./miri fmt --check || (./miri fmt && git commit -am "fmt") @@ -239,7 +242,7 @@ jobs: BRANCH="rustup-$(date -u +%Y-%m-%d)" git switch -c $BRANCH git push -u origin $BRANCH - gh pr create -B master --title 'Automatic Rustup' --body "Update \`rustc\` to https://github.com/rust-lang/rust/commit/$(cat rust-version)." + gh pr create -B master --title 'Automatic Rustup' --body-file message.txt env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} From 1ec098878c5f7a6f5cda2a81093fd34b76f75e9e Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 8 Sep 2025 00:04:33 +0200 Subject: [PATCH 02/26] Expand GenMC mode: - Implement support for 'assume' statements. - Improve scheduling and next instruction type determination. - Improve GenMC mode documentation. Co-authored-by: Ralf Jung --- src/tools/miri/doc/genmc.md | 77 ++++++++++- .../genmc-sys/cpp/include/MiriInterface.hpp | 7 + .../cpp/src/MiriInterface/EventHandling.cpp | 6 + src/tools/miri/genmc-sys/src/lib.rs | 12 +- src/tools/miri/src/concurrency/genmc/dummy.rs | 43 +++--- .../miri/src/concurrency/genmc/intercept.rs | 38 ++++++ src/tools/miri/src/concurrency/genmc/mod.rs | 22 ++- .../miri/src/concurrency/genmc/scheduling.rs | 128 +++++++++++++----- src/tools/miri/src/concurrency/mod.rs | 2 +- src/tools/miri/src/concurrency/thread.rs | 17 ++- src/tools/miri/src/shims/foreign_items.rs | 12 ++ .../spinloop_assume.bounded123.stderr | 5 + .../spinloop_assume.bounded321.stderr | 5 + .../spinloop_assume.replaced123.stderr | 5 + .../spinloop_assume.replaced321.stderr | 5 + .../genmc/pass/intercept/spinloop_assume.rs | 98 ++++++++++++++ src/tools/miri/tests/utils/miri_extern.rs | 3 + 17 files changed, 405 insertions(+), 80 deletions(-) create mode 100644 src/tools/miri/src/concurrency/genmc/intercept.rs create mode 100644 src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr create mode 100644 src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr create mode 100644 src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr create mode 100644 src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr create mode 100644 src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 44e11dcbec44c..3e5859c6f5b27 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -1,15 +1,26 @@ # **(WIP)** Documentation for Miri-GenMC +**NOTE: GenMC mode is not yet fully implemented, and has [several correctness issues](https://github.com/rust-lang/miri/issues/4572). Using GenMC mode currently requires manually compiling Miri, see [Usage](#usage).** + + [GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program. Miri-GenMC integrates that model checker into Miri. -**NOTE: Currently, no actual GenMC functionality is part of Miri, this is still WIP.** +Miri in GenMC mode takes a program as input like regular Miri, but instead of running it once, the program is executed repeatedly, until all possible executions allowed by the Rust memory model are explored. +This includes all possible thread interleavings and all allowed return values for atomic operations, including cases that are very rare to encounter on actual hardware. +(However, this does not include other sources of non-determinism, such as the absolute addresses of allocations. +It is hence still possible to have latent bugs in a test case even if they passed GenMC.) - +GenMC requires the input program to be bounded, i.e., have finitely many possible executions, otherwise it will not terminate. +Any loops that may run infinitely must be replaced or bounded (see below). + +GenMC makes use of Dynamic Partial Order Reduction (DPOR) to reduce the number of executions that must be explored, but the runtime can still be super-exponential in the size of the input program (number of threads and amount of interaction between threads). +Large programs may not be verifiable in a reasonable amount of time. ## Usage For testing/developing Miri-GenMC: +- install all [dependencies required by GenMC](https://github.com/MPI-SWS/genmc?tab=readme-ov-file#dependencies) - clone the Miri repo. - build Miri-GenMC with `./miri build --features=genmc`. - OR: install Miri-GenMC in the current system with `./miri install --features=genmc` @@ -50,6 +61,68 @@ Note that `cargo miri test` in GenMC mode is currently not supported. +### Eliminating unbounded loops + +#### Limiting the number of explored executions + +The number of explored executions in a concurrent program can increase super-exponentially in the size of the program. +Reducing the number of explored executions is the most impactful way to improve verification times. +Some programs also contain possibly infinite loops, which are not supported by GenMC. + +One way to drastically improve verification performance is by bounding spinloops. +A spinloop may loop a many times before it can finally make progress. +If such a loop doesn't have any visible side effects, meaning it does not matter to the outcome of the program whether the loop ran once or a million time, then the loop can be limited to one iteration. + +The following code gives an example for how to replace a loop that waits for a boolean to be true. +Since there are no side effects, replacing the loop with one iteration is safe. + +```rust +#[cfg(miri)] +unsafe extern "Rust" { + // This is a special function that Miri provides. + // It blocks the thread calling this function if the condition is false. + pub unsafe fn miri_genmc_assume(condition: bool); +} + +/// This functions loads an atomic boolean in a loop until it is true. +/// GenMC will explore all executions where this does 1, 2, ..., ∞ loads, which means the verification will never terminate. +fn spin_until_true(flag: &AtomicBool) { + while (!flag.load(Relaxed)) { + std::hint::spin_loop(); + } +} + +/// By replacing this loop with an assume statement, the only executions that will be explored are those with exactly 1 load. +/// Incorrect use of assume statements can lead GenMC to miss important executions, so it is marked `unsafe`. +fn spin_until_true_genmc(flag: &AtomicBool) { + unsafe { miri_genmc_assume(flag.load(Relaxed)); } +} +``` + +Some loops do contain side effects, meaning the number of explored iterations affects the rest of the program. +Replacing the loop with one iteration like we did above would mean we miss all those possible executions. + +In such a case, the loop can be limited to a fixed number of iterations instead. +The choice of iteration limit trades off verification time for possibly missing bugs requiring more iterations. + +```rust +/// The loop in this function has a side effect, which is to increment the counter for the number of iterations. +/// Instead of replacing the loop entirely (which would miss all executions with `count > 0`), we limit the loop to at most 3 iterations. +fn count_until_true_genmc(flag: &AtomicBool) -> u64 { + let mut count = 0; + while (!flag.load(Relaxed)) { + // Any execution that takes more than 3 iterations will not be explored. + unsafe { miri_genmc_assume(count < 3); } + count += 1; + std::hint::spin_loop(); + } + count +} +``` + + + + ## Limitations Some or all of these limitations might get removed in the future: diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index 444c9375319af..8972341d491df 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -134,6 +134,13 @@ struct MiriGenmcShim : private GenMCDriver { void handle_thread_finish(ThreadId thread_id, uint64_t ret_val); void handle_thread_kill(ThreadId thread_id); + /**** Blocking instructions ****/ + /// Inform GenMC that the thread should be blocked. + /// Note: this function is currently hardcoded for `AssumeType::User`, corresponding to user + /// supplied assume statements. This can become a parameter once more types of assumes are + /// added. + void handle_assume_block(ThreadId thread_id); + /***** Exploration related functionality *****/ /** Ask the GenMC scheduler for a new thread to schedule and return whether the execution is diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index 05c82641df948..fac7217ea5c8b 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -30,6 +30,12 @@ #include #include +/**** Blocking instructions ****/ + +void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { + GenMCDriver::handleAssume(inc_pos(thread_id), AssumeType::User); +} + /**** Memory access handling ****/ [[nodiscard]] auto MiriGenmcShim::handle_load( diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 733b3d780b187..385afb9eaba00 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -258,9 +258,11 @@ mod ffi { /// Corresponds to GenMC's type with the same name. /// Should only be modified if changed by GenMC. enum ActionKind { - /// Any Mir terminator that's atomic and has load semantics. + /// Any MIR terminator that's atomic and that may have load semantics. + /// This includes functions with atomic properties, such as `pthread_create`. + /// If the exact type of the terminator cannot be determined, load is a safe default `Load`. Load, - /// Anything that's not a `Load`. + /// Anything that's definitely not a `Load`. NonLoad, } @@ -412,6 +414,12 @@ mod ffi { fn handle_thread_finish(self: Pin<&mut MiriGenmcShim>, thread_id: i32, ret_val: u64); fn handle_thread_kill(self: Pin<&mut MiriGenmcShim>, thread_id: i32); + /**** Blocking instructions ****/ + /// Inform GenMC that the thread should be blocked. + /// Note: this function is currently hardcoded for `AssumeType::User`, corresponding to user supplied assume statements. + /// This can become a parameter once more types of assumes are added. + fn handle_assume_block(self: Pin<&mut MiriGenmcShim>, thread_id: i32); + /***** Exploration related functionality *****/ /// Ask the GenMC scheduler for a new thread to schedule and diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index c28984cef35ad..e260af7c4a16b 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -1,11 +1,12 @@ use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult}; +pub use self::intercept::EvalContextExt as GenmcEvalContextExt; pub use self::run::run_genmc_mode; use crate::intrinsics::AtomicRmwOp; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriMachine, Scalar, - ThreadId, ThreadManager, VisitProvenance, VisitWith, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriMachine, OpTy, + Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, }; #[derive(Clone, Copy, Debug)] @@ -36,9 +37,27 @@ mod run { } } +mod intercept { + use super::*; + + impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} + pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn handle_genmc_verifier_assume(&mut self, _condition: &OpTy<'tcx>) -> InterpResult<'tcx> { + unreachable!(); + } + } +} + impl GenmcCtx { // We don't provide the `new` function in the dummy module. + pub(crate) fn schedule_thread<'tcx>( + &self, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + ) -> InterpResult<'tcx, Option> { + unreachable!() + } + /**** Memory access handling ****/ pub(super) fn set_ongoing_action_data_race_free(&self, _enable: bool) { @@ -191,26 +210,6 @@ impl GenmcCtx { ) -> InterpResult<'tcx> { unreachable!() } - - /**** Scheduling functionality ****/ - - pub fn schedule_thread<'tcx>( - &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> InterpResult<'tcx, ThreadId> { - unreachable!() - } - - /**** Blocking instructions ****/ - - #[allow(unused)] - pub(crate) fn handle_verifier_assume<'tcx>( - &self, - _machine: &MiriMachine<'tcx>, - _condition: bool, - ) -> InterpResult<'tcx, ()> { - unreachable!() - } } impl VisitProvenance for GenmcCtx { diff --git a/src/tools/miri/src/concurrency/genmc/intercept.rs b/src/tools/miri/src/concurrency/genmc/intercept.rs new file mode 100644 index 0000000000000..4867b1dc21afb --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/intercept.rs @@ -0,0 +1,38 @@ +use tracing::debug; + +use crate::concurrency::thread::EvalContextExt as _; +use crate::{ + BlockReason, InterpResult, MachineCallback, MiriInterpCx, OpTy, UnblockKind, VisitProvenance, + VisitWith, callback, interp_ok, +}; + +// Handling of code intercepted by Miri in GenMC mode, such as assume statement or `std::sync::Mutex`. + +/// Other functionality not directly related to event handling +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Handle an `assume` statement. This will tell GenMC to block the current thread if the `condition` is false. + /// Returns `true` if the current thread should be blocked in Miri too. + fn handle_genmc_verifier_assume(&mut self, condition: &OpTy<'tcx>) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let condition_bool = this.read_scalar(condition)?.to_bool()?; + debug!("GenMC: handle_genmc_verifier_assume, condition: {condition:?} = {condition_bool}"); + if condition_bool { + return interp_ok(()); + } + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + genmc_ctx.handle_assume_block(&this.machine)?; + this.block_thread( + BlockReason::Genmc, + None, + callback!( + @capture<'tcx> {} + |_this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + unreachable!("GenMC should never unblock a thread blocked by an `assume`."); + } + ), + ); + interp_ok(()) + } +} diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 0086d3f2bf0bc..3e8e8fbfd491a 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -29,6 +29,7 @@ use crate::{ mod config; mod global_allocations; mod helper; +mod intercept; mod run; pub(crate) mod scheduling; mod thread_id_map; @@ -36,6 +37,7 @@ mod thread_id_map; pub use genmc_sys::GenmcParams; pub use self::config::GenmcConfig; +pub use self::intercept::EvalContextExt as GenmcEvalContextExt; pub use self::run::run_genmc_mode; #[derive(Debug)] @@ -732,17 +734,6 @@ impl GenmcCtx { self.exec_state.exit_status.set(Some(exit_status)); interp_ok(()) } - - /**** Blocking instructions ****/ - - #[allow(unused)] - pub(crate) fn handle_verifier_assume<'tcx>( - &self, - machine: &MiriMachine<'tcx>, - condition: bool, - ) -> InterpResult<'tcx, ()> { - if condition { interp_ok(()) } else { self.handle_user_block(machine) } - } } impl GenmcCtx { @@ -903,8 +894,13 @@ impl GenmcCtx { /// Handle a user thread getting blocked. /// This may happen due to an manual `assume` statement added by a user /// or added by some automated program transformation, e.g., for spinloops. - fn handle_user_block<'tcx>(&self, _machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { - todo!() + fn handle_assume_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { + let curr_thread = machine.threads.active_thread(); + let genmc_curr_thread = + self.exec_state.thread_id_manager.borrow().get_genmc_tid(curr_thread); + debug!("GenMC: assume statement, blocking thread {curr_thread:?} ({genmc_curr_thread:?})"); + self.handle.borrow_mut().pin_mut().handle_assume_block(genmc_curr_thread); + interp_ok(()) } } diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs index b5c23f2d0845f..7fb3d8fa3ef39 100644 --- a/src/tools/miri/src/concurrency/genmc/scheduling.rs +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -1,58 +1,112 @@ use genmc_sys::{ActionKind, ExecutionState}; +use rustc_middle::mir::TerminatorKind; +use rustc_middle::ty::{self, Ty}; use super::GenmcCtx; use crate::{ InterpCx, InterpResult, MiriMachine, TerminationInfo, ThreadId, interp_ok, throw_machine_stop, }; +enum NextInstrKind { + MaybeAtomic(ActionKind), + NonAtomic, +} + +/// Check if a call or tail-call could have atomic load semantics. +fn get_next_instruction_kind<'tcx>( + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, +) -> InterpResult<'tcx, NextInstrKind> { + use NextInstrKind::*; + + let thread_manager = &ecx.machine.threads; + + // Determine whether the next instruction in the current thread might be a load. + // This is used for the "writes-first" scheduling in GenMC. + // Scheduling writes before reads can be beneficial for verification performance. + // `Load` is a safe default for the next instruction type if we cannot guarantee that it isn't a load. + if !thread_manager.active_thread_ref().is_enabled() { + // The current thread can get blocked (e.g., due to a thread join, `Mutex::lock`, assume statement, ...), then we need to ask GenMC for another thread to schedule. + // Most to all blocking operations have load semantics, since they wait on something to change in another thread, + // e.g., a thread join waiting on another thread to finish (join loads the return value(s) of the other thread), + // or a thread waiting for another thread to unlock a `Mutex`, which loads the mutex state (Locked, Unlocked). + // `Load` is a safe default for the next instruction type, since we may not know what the next instruction is. + return interp_ok(MaybeAtomic(ActionKind::Load)); + } + // This thread is still enabled. If it executes a terminator next, we consider yielding, + // but in all other cases we just keep running this thread since it never makes sense + // to yield before a non-atomic operation. + let Some(frame) = thread_manager.active_thread_stack().last() else { + return interp_ok(NonAtomic); + }; + let either::Either::Left(loc) = frame.current_loc() else { + // We are unwinding, so the next step is definitely not atomic. + return interp_ok(NonAtomic); + }; + let basic_block = &frame.body().basic_blocks[loc.block]; + if let Some(_statement) = basic_block.statements.get(loc.statement_index) { + // Statements can't be atomic. + return interp_ok(NonAtomic); + } + match &basic_block.terminator().kind { + // All atomics are modeled as function calls to intrinsic functions. + // The one exception is thread joining, but those are also calls. + TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => + get_function_kind(ecx, func.ty(&frame.body().local_decls, *ecx.tcx)), + // Non-call terminators are not atomic. + _ => interp_ok(NonAtomic), + } +} + +fn get_function_kind<'tcx>( + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + func_ty: Ty<'tcx>, +) -> InterpResult<'tcx, NextInstrKind> { + use NextInstrKind::*; + let callee_def_id = match func_ty.kind() { + ty::FnDef(def_id, _args) => def_id, + _ => return interp_ok(MaybeAtomic(ActionKind::Load)), // we don't know the callee, might be pthread_join + }; + let Some(intrinsic_def) = ecx.tcx.intrinsic(callee_def_id) else { + if ecx.tcx.is_foreign_item(*callee_def_id) { + // Some shims, like pthread_join, must be considered loads. So just consider them all loads, + // these calls are not *that* common. + return interp_ok(MaybeAtomic(ActionKind::Load)); + } + // The next step is a call to a regular Rust function. + return interp_ok(NonAtomic); + }; + let intrinsic_name = intrinsic_def.name.as_str(); + let Some(suffix) = intrinsic_name.strip_prefix("atomic_") else { + return interp_ok(NonAtomic); // Non-atomic intrinsic, so guaranteed not an atomic load + }; + // `atomic_store`, `atomic_fence` and `atomic_singlethreadfence` are not considered loads. + // Any future `atomic_*` intrinsics may have load semantics, so we err on the side of caution and classify them as "maybe loads". + interp_ok(MaybeAtomic(if matches!(suffix, "store" | "fence" | "singlethreadfence") { + ActionKind::NonLoad + } else { + ActionKind::Load + })) +} + impl GenmcCtx { + /// Returns the thread ID of the next thread to schedule, or `None` to continue with the current thread. pub(crate) fn schedule_thread<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> InterpResult<'tcx, ThreadId> { - let thread_manager = &ecx.machine.threads; - let active_thread_id = thread_manager.active_thread(); - - // Determine whether the next instruction in the current thread might be a load. - // This is used for the "writes-first" scheduling in GenMC. - // Scheduling writes before reads can be beneficial for verification performance. - // `Load` is a safe default for the next instruction type if we cannot guarantee that it isn't a load. - let curr_thread_next_instr_kind = if !thread_manager.active_thread_ref().is_enabled() { - // The current thread can get blocked (e.g., due to a thread join, `Mutex::lock`, assume statement, ...), then we need to ask GenMC for another thread to schedule. - // Most to all blocking operations have load semantics, since they wait on something to change in another thread, - // e.g., a thread join waiting on another thread to finish (join loads the return value(s) of the other thread), - // or a thread waiting for another thread to unlock a `Mutex`, which loads the mutex state (Locked, Unlocked). - ActionKind::Load - } else { - // This thread is still enabled. If it executes a terminator next, we consider yielding, - // but in all other cases we just keep running this thread since it never makes sense - // to yield before a non-atomic operation. - let Some(frame) = thread_manager.active_thread_stack().last() else { - return interp_ok(active_thread_id); - }; - let either::Either::Left(loc) = frame.current_loc() else { - // We are unwinding, so the next step is definitely not atomic. - return interp_ok(active_thread_id); - }; - let basic_block = &frame.body().basic_blocks[loc.block]; - if let Some(_statement) = basic_block.statements.get(loc.statement_index) { - // Statements can't be atomic. - return interp_ok(active_thread_id); - } - - // FIXME(genmc): determine terminator kind. - ActionKind::Load + ) -> InterpResult<'tcx, Option> { + let atomic_kind = match get_next_instruction_kind(ecx)? { + NextInstrKind::MaybeAtomic(atomic_kind) => atomic_kind, + NextInstrKind::NonAtomic => return interp_ok(None), // No need to reschedule on a non-atomic. }; + let active_thread_id = ecx.machine.threads.active_thread(); let thread_infos = self.exec_state.thread_id_manager.borrow(); let genmc_tid = thread_infos.get_genmc_tid(active_thread_id); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - let result = pinned_mc.schedule_next(genmc_tid, curr_thread_next_instr_kind); + let result = self.handle.borrow_mut().pin_mut().schedule_next(genmc_tid, atomic_kind); // Depending on the exec_state, we either schedule the given thread, or we are finished with this execution. match result.exec_state { - ExecutionState::Ok => interp_ok(thread_infos.get_miri_tid(result.next_thread)), + ExecutionState::Ok => interp_ok(Some(thread_infos.get_miri_tid(result.next_thread))), ExecutionState::Blocked => throw_machine_stop!(TerminationInfo::GenmcBlockedExecution), ExecutionState::Finished => { let exit_status = self.exec_state.exit_status.get().expect( diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index 9dae858592f1f..b20a17dd6989e 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -22,5 +22,5 @@ pub mod weak_memory; mod genmc; pub use self::data_race_handler::{AllocDataRaceHandler, GlobalDataRaceHandler}; -pub use self::genmc::{ExitType, GenmcConfig, GenmcCtx, run_genmc_mode}; +pub use self::genmc::{ExitType, GenmcConfig, GenmcCtx, GenmcEvalContextExt, run_genmc_mode}; pub use self::vector_clock::VClock; diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 00c5e337b1e9e..246300f8dda15 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -110,6 +110,9 @@ pub enum BlockReason { Eventfd, /// Blocked on unnamed_socket. UnnamedSocket, + /// Blocked for any reason related to GenMC, such as `assume` statements (GenMC mode only). + /// Will be implicitly unblocked when GenMC schedules this thread again. + Genmc, } /// The state of a thread. @@ -572,6 +575,7 @@ impl<'tcx> ThreadManager<'tcx> { /// See : /// > The handle is valid until closed, even after the thread it represents has been terminated. fn detach_thread(&mut self, id: ThreadId, allow_terminated_joined: bool) -> InterpResult<'tcx> { + // NOTE: In GenMC mode, we treat detached threads like regular threads that are never joined, so there is no special handling required here. trace!("detaching {:?}", id); let is_ub = if allow_terminated_joined && self.threads[id].state.is_terminated() { @@ -705,11 +709,18 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // In GenMC mode, we let GenMC do the scheduling. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let next_thread_id = genmc_ctx.schedule_thread(this)?; - + let Some(next_thread_id) = genmc_ctx.schedule_thread(this)? else { + return interp_ok(SchedulingAction::ExecuteStep); + }; + // If a thread is blocked on GenMC, we have to implicitly unblock it when it gets scheduled again. + if this.machine.threads.threads[next_thread_id].state.is_blocked_on(BlockReason::Genmc) + { + info!("GenMC: scheduling blocked thread {next_thread_id:?}, so we unblock it now."); + this.unblock_thread(next_thread_id, BlockReason::Genmc)?; + } + // Set the new active thread. let thread_manager = &mut this.machine.threads; thread_manager.active_thread = next_thread_id; - assert!(thread_manager.threads[thread_manager.active_thread].state.is_enabled()); return interp_ok(SchedulingAction::ExecuteStep); } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index eca8cccf5efc4..1d086906e7a59 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -16,6 +16,7 @@ use rustc_target::callconv::FnAbi; use super::alloc::EvalContextExt as _; use super::backtrace::EvalContextExt as _; +use crate::concurrency::GenmcEvalContextExt as _; use crate::helpers::EvalContextExt as _; use crate::*; @@ -485,6 +486,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } + // GenMC mode: Assume statements block the current thread when their condition is false. + "miri_genmc_assume" => { + let [condition] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + if this.machine.data_race.as_genmc_ref().is_some() { + this.handle_genmc_verifier_assume(condition)?; + } else { + throw_unsup_format!("miri_genmc_assume is only supported in GenMC mode") + } + } + // Aborting the process. "exit" => { let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr new file mode 100644 index 0000000000000..1458300b1110a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 232 executions. No errors found. +Number of complete executions explored: 108 +Number of blocked executions seen: 124 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr new file mode 100644 index 0000000000000..1458300b1110a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 232 executions. No errors found. +Number of complete executions explored: 108 +Number of blocked executions seen: 124 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr new file mode 100644 index 0000000000000..c79a87b33a5f5 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 9 executions. No errors found. +Number of complete executions explored: 1 +Number of blocked executions seen: 8 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr new file mode 100644 index 0000000000000..c79a87b33a5f5 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 9 executions. No errors found. +Number of complete executions explored: 1 +Number of blocked executions seen: 8 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs new file mode 100644 index 0000000000000..9d7c823b1e485 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs @@ -0,0 +1,98 @@ +//@ revisions: bounded123 bounded321 replaced123 replaced321 +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-verbose +//@normalize-stderr-test: "Verification took .*s" -> "Verification took [TIME]s" + +// This test uses GenMC assume statements to bound or replace spinloops. +// Three threads pass a value to each other, spinning on an atomic FLAG to wait for the previous thread. +// +// There are two variants, one limits the spinloop to three iterations, and one that completely replaces the spin loop. +// Without this loop bounding, this test *cannot* be verified, since GenMC will have to explore infinitely many executions (one per possible number of loop iterations). + +// FIXME(genmc): GenMC provides the `--unroll=N` option, which limits all loops to at most N iterations (at the LLVM IR level). +// Such an option for Miri would allow a variant of this test without manual bounding, using this automatic loop bounding instead. + +// We use different thread orders to ensure it doesn't just pass by chance (each thread order should give the same result). +// We use verbose output to see the number of explored vs blocked executions. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +static mut X: u64 = 0; +static FLAG: AtomicU64 = AtomicU64::new(0); + +/// Unbounded variant of the spinloop. +/// This function causes GenMC to explore infinite executions. +#[allow(unused)] +fn spin_until_unbounded(value: u64) { + while FLAG.load(Acquire) != value { + std::hint::spin_loop(); + } +} + +#[cfg(any(bounded123, bounded321))] +/// We bound the loop to at most 3 iterations. +fn spin_until(value: u64) { + for _ in 0..3 { + if FLAG.load(Acquire) == value { + return; + } + } + unsafe { miri_genmc_assume(false) }; +} + +#[cfg(not(any(bounded123, bounded321)))] +/// For full replacement, we limit it to only 1 load. +fn spin_until(value: u64) { + unsafe { miri_genmc_assume(FLAG.load(Acquire) == value) }; +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + unsafe { X = 0 }; + FLAG.store(0, Relaxed); + + unsafe { + let t0 = || { + X = 42; + FLAG.store(1, Release); + + spin_until(3); + let c = X; + if c != 44 { + std::process::abort(); + } + }; + let t1 = || { + spin_until(1); + let a = X; + X = a + 1; + FLAG.store(2, Release); + }; + let t2 = || { + spin_until(2); + let b = X; + X = b + 1; + FLAG.store(3, Release); + }; + // Reverse the order for the second test variant. + #[cfg(any(bounded321, replaced321))] + let (t0, t1, t2) = (t2, t1, t0); + + spawn_pthread_closure(t0); + spawn_pthread_closure(t1); + spawn_pthread_closure(t2); + + 0 + } +} diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs index d6c43b1882195..633f337f7e7cd 100644 --- a/src/tools/miri/tests/utils/miri_extern.rs +++ b/src/tools/miri/tests/utils/miri_extern.rs @@ -147,4 +147,7 @@ extern "Rust" { /// "symbolic" alignment checks. Will fail if the pointer is not actually aligned or `align` is /// not a power of two. Has no effect when alignment checks are concrete (which is the default). pub fn miri_promise_symbolic_alignment(ptr: *const (), align: usize); + + /// Blocks the current execution if the argument is false + pub fn miri_genmc_assume(condition: bool); } From 64bfe7c98f126a189218f0615a377fa0d7412c77 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Sep 2025 10:56:32 +0200 Subject: [PATCH 03/26] genmc doc edits --- src/tools/miri/doc/genmc.md | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 3e5859c6f5b27..7da7a3d189487 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -63,18 +63,14 @@ Note that `cargo miri test` in GenMC mode is currently not supported. ### Eliminating unbounded loops -#### Limiting the number of explored executions +As mentioned above, GenMC requires all loops to be bounded. +Otherwise, it is not possible to exhaustively explore all executions. +Currently, Miri-GenMC has no support for automatically bounding loops, so this needs to be done manually. -The number of explored executions in a concurrent program can increase super-exponentially in the size of the program. -Reducing the number of explored executions is the most impactful way to improve verification times. -Some programs also contain possibly infinite loops, which are not supported by GenMC. +#### Bounding loops without side effects -One way to drastically improve verification performance is by bounding spinloops. -A spinloop may loop a many times before it can finally make progress. -If such a loop doesn't have any visible side effects, meaning it does not matter to the outcome of the program whether the loop ran once or a million time, then the loop can be limited to one iteration. - -The following code gives an example for how to replace a loop that waits for a boolean to be true. -Since there are no side effects, replacing the loop with one iteration is safe. +The easiest case is that of a loop that simply spins until it observes a certain condition, without any side effects. +Such loops can be limited to one iteration, as demonstrated by the following example: ```rust #[cfg(miri)] @@ -84,21 +80,23 @@ unsafe extern "Rust" { pub unsafe fn miri_genmc_assume(condition: bool); } -/// This functions loads an atomic boolean in a loop until it is true. -/// GenMC will explore all executions where this does 1, 2, ..., ∞ loads, which means the verification will never terminate. +// This functions loads an atomic boolean in a loop until it is true. +// GenMC will explore all executions where this does 1, 2, ..., ∞ loads, which means the verification will never terminate. fn spin_until_true(flag: &AtomicBool) { - while (!flag.load(Relaxed)) { + while !flag.load(Relaxed) { std::hint::spin_loop(); } } -/// By replacing this loop with an assume statement, the only executions that will be explored are those with exactly 1 load. -/// Incorrect use of assume statements can lead GenMC to miss important executions, so it is marked `unsafe`. +// By replacing this loop with an assume statement, the only executions that will be explored are those with exactly 1 load that observes the expected value. +// Incorrect use of assume statements can lead GenMC to miss important executions, so it is marked `unsafe`. fn spin_until_true_genmc(flag: &AtomicBool) { - unsafe { miri_genmc_assume(flag.load(Relaxed)); } + unsafe { miri_genmc_assume(flag.load(Relaxed)) }; } ``` +#### Bounding loops with side effects + Some loops do contain side effects, meaning the number of explored iterations affects the rest of the program. Replacing the loop with one iteration like we did above would mean we miss all those possible executions. @@ -110,11 +108,11 @@ The choice of iteration limit trades off verification time for possibly missing /// Instead of replacing the loop entirely (which would miss all executions with `count > 0`), we limit the loop to at most 3 iterations. fn count_until_true_genmc(flag: &AtomicBool) -> u64 { let mut count = 0; - while (!flag.load(Relaxed)) { - // Any execution that takes more than 3 iterations will not be explored. - unsafe { miri_genmc_assume(count < 3); } + while !flag.load(Relaxed) { count += 1; std::hint::spin_loop(); + // Any execution that takes more than 3 iterations will not be explored. + unsafe { miri_genmc_assume(count <= 3) }; } count } From 7dfc6b2e27825ca5e1928233971f3e7996bbe44b Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 26 Sep 2025 04:53:29 +0000 Subject: [PATCH 04/26] Prepare for merging from rust-lang/rust This updates the rust-version file to b733736ea2feb7798c99cbb9a769bce74be108df. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 388e88fe43eb7..c06de8db0a513 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f6092f224d2b1774b31033f12d0bee626943b02f +b733736ea2feb7798c99cbb9a769bce74be108df From aa6332efc2c6f30360419dc40351dc2cab245729 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Sep 2025 10:58:19 +0200 Subject: [PATCH 05/26] silence unused import error --- src/tools/miri/src/bin/miri.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 8b15a7863476e..40ac5c3ff385c 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -352,6 +352,7 @@ fn fatal_error_(msg: &impl std::fmt::Display) -> ! { macro_rules! fatal_error { ($($tt:tt)*) => { $crate::fatal_error_(&format_args!($($tt)*)) }; } +#[allow(unused)] // use depends on cfg use fatal_error; /// Execute a compiler with the given CLI arguments and callbacks. From 9e984cdfde19d4235bab070c28870d907af3a4c2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Sep 2025 12:31:42 +0200 Subject: [PATCH 06/26] alloc_addresses: track more explicitly whether we are in charge of generating addresses --- src/tools/miri/src/alloc_addresses/mod.rs | 79 ++++++++++++----------- src/tools/miri/src/machine.rs | 5 +- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index f011ee717850d..b3ea51bec6fcd 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -42,20 +42,19 @@ pub struct GlobalStateInner { /// they do not have an `AllocExtra`. /// This is the inverse of `int_to_ptr_map`. base_addr: FxHashMap, - /// Temporarily store prepared memory space for global allocations the first time their memory - /// address is required. This is used to ensure that the memory is allocated before Miri assigns - /// it an internal address, which is important for matching the internal address to the machine - /// address so FFI can read from pointers. - prepared_alloc_bytes: FxHashMap, - /// A pool of addresses we can reuse for future allocations. - reuse: ReusePool, - /// Whether an allocation has been exposed or not. This cannot be put + /// The set of exposed allocations. This cannot be put /// into `AllocExtra` for the same reason as `base_addr`. exposed: FxHashSet, - /// The generator for new addresses in a given range. - address_generator: AddressGenerator, /// The provenance to use for int2ptr casts provenance_mode: ProvenanceMode, + /// The generator for new addresses in a given range, and a pool for address reuse. This is + /// `None` if addresses are generated elsewhere (in native-lib mode or with GenMC). + address_generation: Option<(AddressGenerator, ReusePool)>, + /// Native-lib mode only: Temporarily store prepared memory space for global allocations the + /// first time their memory address is required. This is used to ensure that the memory is + /// allocated before Miri assigns it an internal address, which is important for matching the + /// internal address to the machine address so FFI can read from pointers. + prepared_alloc_bytes: Option>, } impl VisitProvenance for GlobalStateInner { @@ -64,9 +63,8 @@ impl VisitProvenance for GlobalStateInner { int_to_ptr_map: _, base_addr: _, prepared_alloc_bytes: _, - reuse: _, exposed: _, - address_generator: _, + address_generation: _, provenance_mode: _, } = self; // Though base_addr, int_to_ptr_map, and exposed contain AllocIds, we do not want to visit them. @@ -83,11 +81,16 @@ impl GlobalStateInner { GlobalStateInner { int_to_ptr_map: Vec::default(), base_addr: FxHashMap::default(), - prepared_alloc_bytes: FxHashMap::default(), - reuse: ReusePool::new(config), exposed: FxHashSet::default(), - address_generator: AddressGenerator::new(stack_addr..tcx.target_usize_max()), provenance_mode: config.provenance_mode, + address_generation: (config.native_lib.is_empty() && config.genmc_config.is_none()) + .then(|| { + ( + AddressGenerator::new(stack_addr..tcx.target_usize_max()), + ReusePool::new(config), + ) + }), + prepared_alloc_bytes: (!config.native_lib.is_empty()).then(FxHashMap::default), } } @@ -147,6 +150,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Store prepared allocation to be picked up for use later. global_state .prepared_alloc_bytes + .as_mut() + .unwrap() .try_insert(alloc_id, prepared_bytes) .unwrap(); ptr @@ -173,29 +178,25 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // We don't have to expose this pointer yet, we do that in `prepare_for_native_call`. return interp_ok(base_ptr.addr().to_u64()); } - // We are not in native lib mode, so we control the addresses ourselves. + // We are not in native lib or genmc mode, so we control the addresses ourselves. + let (addr_gen, reuse) = global_state.address_generation.as_mut().unwrap(); let mut rng = this.machine.rng.borrow_mut(); - if let Some((reuse_addr, clock)) = global_state.reuse.take_addr( - &mut *rng, - info.size, - info.align, - memory_kind, - this.active_thread(), - ) { + if let Some((reuse_addr, clock)) = + reuse.take_addr(&mut *rng, info.size, info.align, memory_kind, this.active_thread()) + { if let Some(clock) = clock { this.acquire_clock(&clock)?; } interp_ok(reuse_addr) } else { // We have to pick a fresh address. - let new_addr = - global_state.address_generator.generate(info.size, info.align, &mut rng)?; + let new_addr = addr_gen.generate(info.size, info.align, &mut rng)?; // If we filled up more than half the address space, start aggressively reusing // addresses to avoid running out. - let remaining_range = global_state.address_generator.get_remaining(); + let remaining_range = addr_gen.get_remaining(); if remaining_range.start > remaining_range.end / 2 { - global_state.reuse.address_space_shortage(); + reuse.address_space_shortage(); } interp_ok(new_addr) @@ -414,6 +415,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut global_state = this.machine.alloc_addresses.borrow_mut(); let mut prepared_alloc_bytes = global_state .prepared_alloc_bytes + .as_mut() + .unwrap() .remove(&id) .unwrap_or_else(|| panic!("alloc bytes for {id:?} have not been prepared")); // Sanity-check that the prepared allocation has the right size and alignment. @@ -496,15 +499,17 @@ impl<'tcx> MiriMachine<'tcx> { // `alloc_id_from_addr` any more. global_state.exposed.remove(&dead_id); // Also remember this address for future reuse. - let thread = self.threads.active_thread(); - global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { - // We already excluded GenMC above. We cannot use `self.release_clock` as - // `self.alloc_addresses` is borrowed. - if let Some(data_race) = self.data_race.as_vclocks_ref() { - data_race.release_clock(&self.threads, |clock| clock.clone()) - } else { - VClock::default() - } - }) + if let Some((_addr_gen, reuse)) = global_state.address_generation.as_mut() { + let thread = self.threads.active_thread(); + reuse.add_addr(rng, addr, size, align, kind, thread, || { + // We cannot be in GenMC mode as then `address_generation` is `None`. We cannot use + // `self.release_clock` as `self.alloc_addresses` is borrowed. + if let Some(data_race) = self.data_race.as_vclocks_ref() { + data_race.release_clock(&self.threads, |clock| clock.clone()) + } else { + VClock::default() + } + }) + } } } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 04c8bee72c038..af8b40da14ade 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -777,9 +777,8 @@ impl<'tcx> MiriMachine<'tcx> { local_crates, extern_statics: FxHashMap::default(), rng: RefCell::new(rng), - allocator: if !config.native_lib.is_empty() { - Some(Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new()))) - } else { None }, + allocator: (!config.native_lib.is_empty()) + .then(|| Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new()))), tracked_alloc_ids: config.tracked_alloc_ids.clone(), track_alloc_accesses: config.track_alloc_accesses, check_alignment: config.check_alignment, From 5debc4368cde86b1c794725030f65de215d3ece4 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 28 Sep 2025 04:52:54 +0000 Subject: [PATCH 07/26] Prepare for merging from rust-lang/rust This updates the rust-version file to 848e6746fe03dfd703075c5077312b63877d51d6. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index c06de8db0a513..2bf64d62060da 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -b733736ea2feb7798c99cbb9a769bce74be108df +848e6746fe03dfd703075c5077312b63877d51d6 From 2d038a0ed3ab4995ee863f7a54673cc7ef68f816 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 29 Sep 2025 04:53:44 +0000 Subject: [PATCH 08/26] Prepare for merging from rust-lang/rust This updates the rust-version file to f957826bff7a68b267ce75b1ea56352aed0cca0a. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 2bf64d62060da..1f90d4e5e498a 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -848e6746fe03dfd703075c5077312b63877d51d6 +f957826bff7a68b267ce75b1ea56352aed0cca0a From 8b38760176abe1f3f6db5dd10e469158f82830f0 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 14 Mar 2025 09:58:46 +0100 Subject: [PATCH 09/26] Implement Pointer conversions to and from GenMC. Limitation: Borrow tracking must be disabled. Remove hacks for keeping all memory allocations in GenMC mode. --- src/tools/miri/genmc-sys/build.rs | 16 +- .../genmc-sys/cpp/include/MiriInterface.hpp | 6 +- .../cpp/src/MiriInterface/EventHandling.cpp | 5 +- .../cpp/src/MiriInterface/Exploration.cpp | 4 +- .../genmc-sys/cpp/src/MiriInterface/Setup.cpp | 18 +- src/tools/miri/genmc-sys/src/lib.rs | 20 +- src/tools/miri/src/alloc_addresses/mod.rs | 27 +-- .../miri/src/concurrency/genmc/helper.rs | 54 +++-- src/tools/miri/src/concurrency/genmc/mod.rs | 46 ++-- .../miri/src/concurrency/genmc/scheduling.rs | 8 + src/tools/miri/src/shims/native_lib/mod.rs | 8 +- .../fail/atomics/atomic_ptr_double_free.rs | 47 ++++ .../atomics/atomic_ptr_double_free.stderr | 39 ++++ .../atomic_ptr_invalid_provenance.make.stderr | 21 ++ .../atomics/atomic_ptr_invalid_provenance.rs | 61 ++++++ .../atomic_ptr_invalid_provenance.send.stderr | 21 ++ .../atomic_ptr_alloc_race.dealloc.stderr | 24 +++ .../fail/data_race/atomic_ptr_alloc_race.rs | 51 +++++ .../atomic_ptr_alloc_race.write.stderr | 22 ++ .../atomic_ptr_dealloc_write_race.rs | 42 ++++ .../atomic_ptr_dealloc_write_race.stderr | 32 +++ .../atomic_ptr_write_dealloc_race.rs | 50 +++++ .../atomic_ptr_write_dealloc_race.stderr | 22 ++ .../genmc/pass/atomics/atomic_ptr_ops.rs | 173 +++++++++++++++ .../genmc/pass/atomics/atomic_ptr_ops.stderr | 2 + .../pass/atomics/atomic_ptr_roundtrip.rs | 60 ++++++ .../pass/atomics/atomic_ptr_roundtrip.stderr | 2 + .../ms_queue_dynamic.default_R1W1.stderr | 3 + .../ms_queue_dynamic.default_R1W2.stderr | 3 + .../pass/data-structures/ms_queue_dynamic.rs | 200 ++++++++++++++++++ ..._queue_dynamic.spinloop_assume_R1W1.stderr | 3 + ..._queue_dynamic.spinloop_assume_R1W2.stderr | 5 + .../treiber_stack_dynamic.default_R1W1.stderr | 3 + .../treiber_stack_dynamic.default_R1W2.stderr | 3 + .../treiber_stack_dynamic.default_R1W3.stderr | 3 + .../data-structures/treiber_stack_dynamic.rs | 150 +++++++++++++ ..._stack_dynamic.spinloop_assume_R1W1.stderr | 3 + ..._stack_dynamic.spinloop_assume_R1W2.stderr | 5 + ..._stack_dynamic.spinloop_assume_R1W3.stderr | 5 + .../miri/tests/genmc/pass/litmus/IRIWish.rs | 2 +- .../tests/genmc/pass/litmus/MPU2_rels_acqf.rs | 2 +- .../miri/tests/genmc/pass/litmus/Z6_U.rs | 3 +- .../tests/genmc/pass/thread/thread_locals.rs | 47 ++++ .../genmc/pass/thread/thread_locals.stderr | 2 + 44 files changed, 1235 insertions(+), 88 deletions(-) create mode 100644 src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs create mode 100644 src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.stderr create mode 100644 src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.make.stderr create mode 100644 src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs create mode 100644 src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.send.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.dealloc.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.write.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs create mode 100644 src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.stderr create mode 100644 src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.stderr create mode 100644 src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W3.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W3.stderr create mode 100644 src/tools/miri/tests/genmc/pass/thread/thread_locals.rs create mode 100644 src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index 8d437c20a0926..ef49be151bf09 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -28,7 +28,7 @@ mod downloading { /// The GenMC repository the we get our commit from. pub(crate) const GENMC_GITHUB_URL: &str = "https://gitlab.inf.ethz.ch/public-plf/genmc.git"; /// The GenMC commit we depend on. It must be available on the specified GenMC repository. - pub(crate) const GENMC_COMMIT: &str = "af9cc9ccd5d412b16defc35dbf36571c63a19c76"; + pub(crate) const GENMC_COMMIT: &str = "cd01c12032bdd71df742b41c7817f99acc72e7ab"; /// Ensure that a local GenMC repo is present and set to the correct commit. /// Return the path of the GenMC repo and whether the checked out commit was changed. @@ -227,12 +227,16 @@ fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) { // These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated. let definitions = llvm_definitions.split(";"); + // These are all the C++ files we need to compile, which needs to be updated if more C++ files are added to Miri. + // We use absolute paths since relative paths can confuse IDEs when attempting to go-to-source on a path in a compiler error. + let cpp_files_base_path = Path::new("cpp/src/"); let cpp_files = [ - "./cpp/src/MiriInterface/EventHandling.cpp", - "./cpp/src/MiriInterface/Exploration.cpp", - "./cpp/src/MiriInterface/Setup.cpp", - "./cpp/src/MiriInterface/ThreadManagement.cpp", - ]; + "MiriInterface/EventHandling.cpp", + "MiriInterface/Exploration.cpp", + "MiriInterface/Setup.cpp", + "MiriInterface/ThreadManagement.cpp", + ] + .map(|file| std::path::absolute(cpp_files_base_path.join(file)).unwrap()); let mut bridge = cxx_build::bridge("src/lib.rs"); // FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file. diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index 8972341d491df..5f73e5d3d3d06 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -126,7 +126,7 @@ struct MiriGenmcShim : private GenMCDriver { /**** Memory (de)allocation ****/ auto handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uint64_t; - void handle_free(ThreadId thread_id, uint64_t address); + auto handle_free(ThreadId thread_id, uint64_t address) -> bool; /**** Thread management ****/ void handle_thread_create(ThreadId thread_id, ThreadId parent_id); @@ -257,6 +257,7 @@ namespace GenmcScalarExt { inline GenmcScalar uninit() { return GenmcScalar { .value = 0, + .extra = 0, .is_init = false, }; } @@ -264,13 +265,14 @@ inline GenmcScalar uninit() { inline GenmcScalar from_sval(SVal sval) { return GenmcScalar { .value = sval.get(), + .extra = sval.getExtra(), .is_init = true, }; } inline SVal to_sval(GenmcScalar scalar) { ERROR_ON(!scalar.is_init, "Cannot convert an uninitialized `GenmcScalar` into an `SVal`\n"); - return SVal(scalar.value); + return SVal(scalar.value, scalar.extra); } } // namespace GenmcScalarExt diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index fac7217ea5c8b..c9e7f7a1a8a1c 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -250,8 +250,9 @@ auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t al return ret_val.get(); } -void MiriGenmcShim::handle_free(ThreadId thread_id, uint64_t address) { +auto MiriGenmcShim::handle_free(ThreadId thread_id, uint64_t address) -> bool { const auto pos = inc_pos(thread_id); GenMCDriver::handleFree(pos, SAddr(address), EventDeps()); - // FIXME(genmc): add error handling once GenMC returns errors from `handleFree` + // FIXME(genmc): use returned error from `handleFree` once implemented in GenMC. + return getResult().status.has_value(); } diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp index 5e7188f17e0d2..0f64083ddda68 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp @@ -24,8 +24,10 @@ auto MiriGenmcShim::schedule_next( if (const auto result = GenMCDriver::scheduleNext(threads_action_)) return SchedulingResult { ExecutionState::Ok, static_cast(result.value()) }; - if (GenMCDriver::isExecutionBlocked()) + if (getExec().getGraph().isBlocked()) return SchedulingResult { ExecutionState::Blocked, 0 }; + if (getResult().status.has_value()) // the "value" here is a `VerificationError` + return SchedulingResult { ExecutionState::Error, 0 }; return SchedulingResult { ExecutionState::Finished, 0 }; } diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp index af13f0d07746e..5455b1a8de7f8 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp @@ -170,9 +170,8 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // From a Miri perspective, this API doesn't work very well: most memory starts out // "uninitialized"; // only statics have an initial value. And their initial value is just a sequence of bytes, - // but GenMC - // expect this to be already split into separate atomic variables. So we return a dummy - // value. + // but GenMC expect this to be already split into separate atomic variables. So we return a + // dummy value. // This value should never be visible to the interpreted program. // GenMC does not understand uninitialized memory the same way Miri does, which may cause // this function to be called. The returned value can be visible to Miri or the user: @@ -183,13 +182,14 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // Currently, atomic loads can see this value, unless initialized by an *atomic* store. // FIXME(genmc): update this comment once mixed atomic-non-atomic support is added. // - // FIXME(genmc): implement proper support for uninitialized memory in GenMC. Ideally, the - // initial value getter would return an `optional`, since the memory location may be - // uninitialized. + // FIXME(genmc): implement proper support for uninitialized memory in GenMC. + // Ideally, the initial value getter would return an `optional`, since the memory + // location may be uninitialized. .initValGetter = [](const AAccess& a) { return SVal(0xDEAD); }, - // Miri serves non-atomic loads from its own memory and these GenMC checks are wrong in - // that case. This should no longer be required with proper mixed-size access support. - .skipUninitLoadChecks = [](MemOrdering ord) { return ord == MemOrdering::NotAtomic; }, + // Miri serves non-atomic loads from its own memory and these GenMC checks are wrong in that + // case. This should no longer be required with proper mixed-size access support. + .skipUninitLoadChecks = [](const MemAccessLabel* access_label + ) { return access_label->getOrdering() == MemOrdering::NotAtomic; }, }; driver->setInterpCallbacks(std::move(interpreter_callbacks)); diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 385afb9eaba00..619d4ab67b2f2 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -45,10 +45,14 @@ pub fn create_genmc_driver_handle( } impl GenmcScalar { - pub const UNINIT: Self = Self { value: 0, is_init: false }; + pub const UNINIT: Self = Self { value: 0, extra: 0, is_init: false }; pub const fn from_u64(value: u64) -> Self { - Self { value, is_init: true } + Self { value, extra: 0, is_init: true } + } + + pub const fn has_provenance(&self) -> bool { + self.extra != 0 } } @@ -162,10 +166,16 @@ mod ffi { } /// This type corresponds to `Option` (or `std::optional`), where `SVal` is the type that GenMC uses for storing values. - /// CXX doesn't support `std::optional` currently, so we need to use an extra `bool` to define whether this value is initialized or not. #[derive(Debug, Clone, Copy)] struct GenmcScalar { + /// The raw byte-level value (discarding provenance, if any) of this scalar. value: u64, + /// This is zero for integer values. For pointers, this encodes the provenance by + /// storing the base address of the allocation that this pointer belongs to. + /// Operations on `SVal` in GenMC (e.g., `fetch_add`) preserve the `extra` of the left argument (`left.fetch_add(right, ...)`). + extra: u64, + /// Indicates whether this value is initialized. If this is `false`, the other fields do not matter. + /// (Ideally we'd use `std::optional` but CXX does not support that.) is_init: bool, } @@ -173,6 +183,7 @@ mod ffi { #[derive(Debug, Clone, Copy)] enum ExecutionState { Ok, + Error, Blocked, Finished, } @@ -406,7 +417,8 @@ mod ffi { size: u64, alignment: u64, ) -> u64; - fn handle_free(self: Pin<&mut MiriGenmcShim>, thread_id: i32, address: u64); + /// Returns true if an error was found. + fn handle_free(self: Pin<&mut MiriGenmcShim>, thread_id: i32, address: u64) -> bool; /**** Thread management ****/ fn handle_thread_create(self: Pin<&mut MiriGenmcShim>, thread_id: i32, parent_id: i32); diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index f011ee717850d..7ad4782cd2c6b 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -207,13 +207,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Returns the `AllocId` that corresponds to the specified addr, // or `None` if the addr is out of bounds. - // Setting `only_exposed_allocations` selects whether only exposed allocations are considered. - fn alloc_id_from_addr( - &self, - addr: u64, - size: i64, - only_exposed_allocations: bool, - ) -> Option { + fn alloc_id_from_addr(&self, addr: u64, size: i64) -> Option { let this = self.eval_context_ref(); let global_state = this.machine.alloc_addresses.borrow(); assert!(global_state.provenance_mode != ProvenanceMode::Strict); @@ -242,13 +236,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } }?; - // We only use this provenance if it has been exposed, or if the caller requested also non-exposed allocations - if !only_exposed_allocations || global_state.exposed.contains(&alloc_id) { + // We only use this provenance if it has been exposed. + if global_state.exposed.contains(&alloc_id) { // This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed. - // In GenMC mode, we keep all allocations, so this check doesn't apply there. - if this.machine.data_race.as_genmc_ref().is_none() { - debug_assert!(this.is_alloc_live(alloc_id)); - } + debug_assert!(this.is_alloc_live(alloc_id)); Some(alloc_id) } else { None @@ -443,8 +434,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { alloc_id } else { // A wildcard pointer. - let only_exposed_allocations = true; - this.alloc_id_from_addr(addr.bytes(), size, only_exposed_allocations)? + this.alloc_id_from_addr(addr.bytes(), size)? }; // This cannot fail: since we already have a pointer with that provenance, adjust_alloc_root_pointer @@ -465,13 +455,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { impl<'tcx> MiriMachine<'tcx> { pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align, kind: MemoryKind) { - // In GenMC mode, we can't remove dead allocation info since such pointers can - // still be stored in atomics and we need this info to convert GenMC pointers to Miri pointers. - // `global_state.reuse` is also unused so we can just skip this entire function. - if self.data_race.as_genmc_ref().is_some() { - return; - } - let global_state = self.alloc_addresses.get_mut(); let rng = self.rng.get_mut(); diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index 48a5ec8bb2608..ebbef23a2a54c 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -5,16 +5,18 @@ use rustc_abi::Size; use rustc_const_eval::interpret::{InterpResult, interp_ok}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; +use rustc_middle::mir::interpret; use rustc_middle::ty::ScalarInt; use rustc_span::Span; use tracing::debug; use super::GenmcScalar; -use crate::diagnostics::EvalContextExt; +use crate::alloc_addresses::EvalContextExt as _; +use crate::diagnostics::EvalContextExt as _; use crate::intrinsics::AtomicRmwOp; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, InterpCx, MiriInterpCx, - MiriMachine, NonHaltingDiagnostic, Scalar, throw_unsup_format, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, BorTag, GenmcCtx, InterpCx, + MiriInterpCx, MiriMachine, NonHaltingDiagnostic, Scalar, machine, throw_unsup_format, }; /// Maximum size memory access in bytes that GenMC supports. @@ -80,19 +82,30 @@ pub fn split_access(address: Size, size: Size) -> impl Iterator( - _ecx: &MiriInterpCx<'tcx>, + ecx: &MiriInterpCx<'tcx>, + genmc_ctx: &GenmcCtx, scalar: Scalar, ) -> InterpResult<'tcx, GenmcScalar> { interp_ok(match scalar { rustc_const_eval::interpret::Scalar::Int(scalar_int) => { // FIXME(genmc): Add u128 support once GenMC supports it. let value: u64 = scalar_int.to_uint(scalar_int.size()).try_into().unwrap(); - GenmcScalar { value, is_init: true } + GenmcScalar { value, extra: 0, is_init: true } + } + rustc_const_eval::interpret::Scalar::Ptr(pointer, size) => { + // FIXME(genmc,borrow tracking): Borrow tracking information is lost. + let addr = crate::Pointer::from(pointer).addr(); + if let crate::Provenance::Wildcard = pointer.provenance { + throw_unsup_format!("Pointers with wildcard provenance not allowed in GenMC mode"); + } + let (alloc_id, _size, _prov_extra) = + rustc_const_eval::interpret::Machine::ptr_get_alloc(ecx, pointer, size.into()) + .unwrap(); + let base_addr = ecx.addr_from_alloc_id(alloc_id, None)?; + // Add the base_addr alloc_id pair to the map. + genmc_ctx.exec_state.genmc_shared_allocs_map.borrow_mut().insert(base_addr, alloc_id); + GenmcScalar { value: addr.bytes(), extra: base_addr, is_init: true } } - rustc_const_eval::interpret::Scalar::Ptr(_pointer, _size) => - throw_unsup_format!( - "FIXME(genmc): Implement sending pointers (with provenance) to GenMC." - ), }) } @@ -101,16 +114,25 @@ pub fn scalar_to_genmc_scalar<'tcx>( /// Convert a `GenmcScalar` back into a Miri `Scalar`. /// For pointers, attempt to convert the stored base address of their allocation back into an `AllocId`. pub fn genmc_scalar_to_scalar<'tcx>( - _ecx: &MiriInterpCx<'tcx>, + ecx: &MiriInterpCx<'tcx>, + genmc_ctx: &GenmcCtx, scalar: GenmcScalar, size: Size, ) -> InterpResult<'tcx, Scalar> { - // FIXME(genmc): Add GenmcScalar to Miri Pointer conversion. - - // NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated. - // FIXME(genmc): GenMC should be doing the truncation, not Miri. - let (value_scalar_int, _got_truncated) = ScalarInt::truncate_from_uint(scalar.value, size); - interp_ok(Scalar::Int(value_scalar_int)) + // If `extra` is zero, we have a regular integer. + if scalar.extra == 0 { + // NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated. + // FIXME(genmc): GenMC should be doing the truncation, not Miri. + let (value_scalar_int, _got_truncated) = ScalarInt::truncate_from_uint(scalar.value, size); + return interp_ok(Scalar::from(value_scalar_int)); + } + // `extra` is non-zero, we have a pointer. + // When we get a pointer from GenMC, then we must have sent it to GenMC before in the same execution (since the reads-from relation is always respected). + let alloc_id = genmc_ctx.exec_state.genmc_shared_allocs_map.borrow()[&scalar.extra]; + // FIXME(genmc,borrow tracking): Borrow tracking not yet supported. + let provenance = machine::Provenance::Concrete { alloc_id, tag: BorTag::default() }; + let ptr = interpret::Pointer::new(provenance, Size::from_bytes(scalar.value)); + interp_ok(Scalar::from_pointer(ptr, &ecx.tcx)) } impl AtomicReadOrd { diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 3e8e8fbfd491a..b2f8ab6f972eb 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -7,6 +7,7 @@ use genmc_sys::{ }; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; +use rustc_data_structures::fx::FxHashMap; use rustc_middle::{throw_machine_stop, throw_ub_format, throw_unsup_format}; // FIXME(genmc,tracing): Implement some work-around for enabling debug/trace level logging (currently disabled statically in rustc). use tracing::{debug, info}; @@ -85,6 +86,9 @@ struct PerExecutionState { /// we cover all possible executions. /// `None` if no thread has called `exit` and the main thread isn't finished yet. exit_status: Cell>, + + /// Allocations in this map have been sent to GenMC, and should thus be kept around, since future loads from GenMC may return this allocation again. + genmc_shared_allocs_map: RefCell>, } impl PerExecutionState { @@ -92,6 +96,7 @@ impl PerExecutionState { self.allow_data_races.replace(false); self.thread_id_manager.borrow_mut().reset(); self.exit_status.set(None); + self.genmc_shared_allocs_map.borrow_mut().clear(); } } @@ -268,13 +273,13 @@ impl GenmcCtx { ) -> InterpResult<'tcx, Scalar> { assert!(!self.get_alloc_data_races(), "atomic load with data race checking disabled."); let genmc_old_value = if let Some(scalar) = old_val { - scalar_to_genmc_scalar(ecx, scalar)? + scalar_to_genmc_scalar(ecx, self, scalar)? } else { GenmcScalar::UNINIT }; let read_value = self.handle_load(&ecx.machine, address, size, ordering.to_genmc(), genmc_old_value)?; - genmc_scalar_to_scalar(ecx, read_value, size) + genmc_scalar_to_scalar(ecx, self, read_value, size) } /// Inform GenMC about an atomic store. @@ -291,9 +296,9 @@ impl GenmcCtx { ordering: AtomicWriteOrd, ) -> InterpResult<'tcx, bool> { assert!(!self.get_alloc_data_races(), "atomic store with data race checking disabled."); - let genmc_value = scalar_to_genmc_scalar(ecx, value)?; + let genmc_value = scalar_to_genmc_scalar(ecx, self, value)?; let genmc_old_value = if let Some(scalar) = old_value { - scalar_to_genmc_scalar(ecx, scalar)? + scalar_to_genmc_scalar(ecx, self, scalar)? } else { GenmcScalar::UNINIT }; @@ -345,8 +350,8 @@ impl GenmcCtx { size, ordering, to_genmc_rmw_op(atomic_op, is_signed), - scalar_to_genmc_scalar(ecx, rhs_scalar)?, - scalar_to_genmc_scalar(ecx, old_value)?, + scalar_to_genmc_scalar(ecx, self, rhs_scalar)?, + scalar_to_genmc_scalar(ecx, self, old_value)?, ) } @@ -368,8 +373,8 @@ impl GenmcCtx { size, ordering, /* genmc_rmw_op */ RMWBinOp::Xchg, - scalar_to_genmc_scalar(ecx, rhs_scalar)?, - scalar_to_genmc_scalar(ecx, old_value)?, + scalar_to_genmc_scalar(ecx, self, rhs_scalar)?, + scalar_to_genmc_scalar(ecx, self, old_value)?, ) } @@ -441,9 +446,9 @@ impl GenmcCtx { genmc_tid, address.bytes(), size.bytes(), - scalar_to_genmc_scalar(ecx, expected_old_value)?, - scalar_to_genmc_scalar(ecx, new_value)?, - scalar_to_genmc_scalar(ecx, old_value)?, + scalar_to_genmc_scalar(ecx, self, expected_old_value)?, + scalar_to_genmc_scalar(ecx, self, new_value)?, + scalar_to_genmc_scalar(ecx, self, old_value)?, upgraded_success_ordering.to_genmc(), fail.to_genmc(), can_fail_spuriously, @@ -454,7 +459,7 @@ impl GenmcCtx { throw_ub_format!("{}", error.to_string_lossy()); } - let return_scalar = genmc_scalar_to_scalar(ecx, cas_result.old_value, size)?; + let return_scalar = genmc_scalar_to_scalar(ecx, self, cas_result.old_value, size)?; debug!( "GenMC: atomic_compare_exchange: result: {cas_result:?}, returning scalar: {return_scalar:?}" ); @@ -644,7 +649,11 @@ impl GenmcCtx { let curr_thread = machine.threads.active_thread(); let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - self.handle.borrow_mut().pin_mut().handle_free(genmc_tid, address.bytes()); + if self.handle.borrow_mut().pin_mut().handle_free(genmc_tid, address.bytes()) { + // FIXME(genmc): improve error handling. + // An error was detected, so we get the error string from GenMC. + throw_ub_format!("{}", self.try_get_error().unwrap()); + } interp_ok(()) } @@ -879,10 +888,10 @@ impl GenmcCtx { throw_ub_format!("{}", error.to_string_lossy()); } - let old_value_scalar = genmc_scalar_to_scalar(ecx, rmw_result.old_value, size)?; + let old_value_scalar = genmc_scalar_to_scalar(ecx, self, rmw_result.old_value, size)?; let new_value_scalar = if rmw_result.is_coherence_order_maximal_write { - Some(genmc_scalar_to_scalar(ecx, rmw_result.new_value, size)?) + Some(genmc_scalar_to_scalar(ecx, self, rmw_result.new_value, size)?) } else { None }; @@ -905,7 +914,10 @@ impl GenmcCtx { } impl VisitProvenance for GenmcCtx { - fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { - // We don't have any tags. + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + let genmc_shared_allocs_map = self.exec_state.genmc_shared_allocs_map.borrow(); + for alloc_id in genmc_shared_allocs_map.values().copied() { + visit(Some(alloc_id), None); + } } } diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs index 7fb3d8fa3ef39..be7df8682f07c 100644 --- a/src/tools/miri/src/concurrency/genmc/scheduling.rs +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -117,6 +117,14 @@ impl GenmcCtx { leak_check: exit_status.do_leak_check() }); } + ExecutionState::Error => { + // GenMC found an error in one of the `handle_*` functions, but didn't return the detected error from the function immediately. + // This is still an bug in the user program, so we print the error string. + panic!( + "GenMC found an error ({:?}), but didn't report it immediately, so we cannot provide an appropriate source code location for where it happened.", + self.try_get_error().unwrap() + ); + } _ => unreachable!(), } } diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index da8f785e37345..a0e871d87d3a0 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -219,11 +219,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // so we cannot assume 1 access = 1 allocation. :( let mut rg = evt_rg.addr..evt_rg.end(); while let Some(curr) = rg.next() { - let Some(alloc_id) = this.alloc_id_from_addr( - curr.to_u64(), - rg.len().try_into().unwrap(), - /* only_exposed_allocations */ true, - ) else { + let Some(alloc_id) = + this.alloc_id_from_addr(curr.to_u64(), rg.len().try_into().unwrap()) + else { throw_ub_format!("Foreign code did an out-of-bounds access!") }; let alloc = this.get_alloc_raw(alloc_id)?; diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs new file mode 100644 index 0000000000000..d712822a6d00c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that we can detect a double-free bug across two threads, which only shows up if the second thread reads an atomic pointer at a very specific moment. +// GenMC can detect this error consistently, without having to run the buggy code with multiple RNG seeds or in a loop. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::alloc::{Layout, alloc, dealloc}; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +unsafe fn free(ptr: *mut u64) { + dealloc(ptr as *mut u8, Layout::new::()) //~ ERROR: Undefined Behavior +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(std::ptr::null_mut(), SeqCst); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + let a: *mut u64 = alloc(Layout::new::()) as *mut u64; + X.store(a, SeqCst); + // We have to yield to the other thread exactly here to reproduce the double-free. + let b = X.swap(std::ptr::null_mut(), SeqCst); + free(b); + }), + spawn_pthread_closure(|| { + let b = X.load(SeqCst); + if !b.is_null() { + free(b); + } + }), + ]; + join_pthreads(ids); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.stderr b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.stderr new file mode 100644 index 0000000000000..7d03bd9a8eb84 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.stderr @@ -0,0 +1,39 @@ +Running GenMC Verification... +error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling + --> tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC + | +LL | dealloc(ptr as *mut u8, Layout::new::()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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: ALLOC was allocated here: + --> tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC + | +LL | let a: *mut u64 = alloc(Layout::new::()) as *mut u64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC + | +LL | dealloc(ptr as *mut u8, Layout::new::()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span) on thread `unnamed-ID`: + = note: inside `free` at tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC +note: inside closure + --> tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC + | +LL | free(b); + | ^^^^^^^ + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/atomics/atomic_ptr_double_free.rs:LL:CC}>` + --> tests/genmc/fail/atomics/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.make.stderr b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.make.stderr new file mode 100644 index 0000000000000..d74fa12256bbf --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.make.stderr @@ -0,0 +1,21 @@ +Running GenMC Verification... +error: Undefined Behavior: memory access failed: attempting to access 8 bytes, but got ALLOC-$HEX which points to before the beginning of the allocation + --> tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + | +LL | *ptr = 44; + | ^^^^^^^^^ Undefined Behavior occurred here + | + = 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: ALLOC was allocated here: + --> tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + | +LL | let mut b = Box::new(0u64); + | ^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `miri_start` at tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs new file mode 100644 index 0000000000000..87223e990bde9 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs @@ -0,0 +1,61 @@ +//@revisions: send make +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that we can distinguish two pointers with the same address, but different provenance, after they are sent to GenMC and back. +// We have two variants, one where we send such a pointer to GenMC, and one where we make it on the GenMC side. + +#![no_main] +#![feature(box_as_ptr)] + +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let atomic_ptr = AtomicPtr::new(std::ptr::null_mut()); + let mut a = Box::new(0u64); + let mut b = Box::new(0u64); + let a_ptr: *mut u64 = Box::as_mut_ptr(&mut a); + let b_ptr: *mut u64 = Box::as_mut_ptr(&mut b); + + // Store valid pointer to `a`: + atomic_ptr.store(a_ptr, Relaxed); + let ptr = atomic_ptr.load(Relaxed); + *ptr = 42; + if *a != 42 { + std::process::abort(); + } + // Store valid pointer to `b`: + atomic_ptr.store(b_ptr, Relaxed); + let ptr = atomic_ptr.load(Relaxed); + *ptr = 43; + if *b != 43 { + std::process::abort(); + } + + // Make `atomic_ptr` contain a pointer with the provenance of `b`, but the address of `a`. + if cfg!(send) { + // Variant 1: create the invalid pointer non-atomically, then send it to GenMC. + let fake_a_ptr = b_ptr.with_addr(a_ptr.addr()); + if a_ptr.addr() != fake_a_ptr.addr() { + std::process::abort(); + } + atomic_ptr.store(fake_a_ptr, Relaxed); + } else { + // Variant 2: send `b_ptr` to GenMC, then create the invalid pointer to `a` using atomic operations. + atomic_ptr.store(b_ptr, Relaxed); + atomic_ptr.fetch_byte_add(a_ptr.addr(), Relaxed); + atomic_ptr.fetch_byte_sub(b_ptr.addr(), Relaxed); + } + let ptr = atomic_ptr.load(Relaxed); + if a_ptr.addr() != ptr.addr() { + std::process::abort(); + } + // This pointer has the same address as `a_ptr`, but not the same + // provenance, so writing to it fails. + *ptr = 44; //~ ERROR: points to before the beginning of the allocation + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.send.stderr b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.send.stderr new file mode 100644 index 0000000000000..d74fa12256bbf --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.send.stderr @@ -0,0 +1,21 @@ +Running GenMC Verification... +error: Undefined Behavior: memory access failed: attempting to access 8 bytes, but got ALLOC-$HEX which points to before the beginning of the allocation + --> tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + | +LL | *ptr = 44; + | ^^^^^^^^^ Undefined Behavior occurred here + | + = 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: ALLOC was allocated here: + --> tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + | +LL | let mut b = Box::new(0u64); + | ^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `miri_start` at tests/genmc/fail/atomics/atomic_ptr_invalid_provenance.rs:LL:CC + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.dealloc.stderr b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.dealloc.stderr new file mode 100644 index 0000000000000..bde793014bbf2 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.dealloc.stderr @@ -0,0 +1,24 @@ +Running GenMC Verification... +error: Undefined Behavior: Attempt to access freed memory + --> tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC + | +LL | dealloc(b as *mut u8, Layout::new::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs new file mode 100644 index 0000000000000..d82fbf53dac7f --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs @@ -0,0 +1,51 @@ +//@revisions: write dealloc +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-ignore-leaks + +// Test that we can detect data races between an allocation and an unsynchronized action in another thread. +// We have two variants, an alloc-dealloc race and an alloc-write race. +// +// We never deallocate the memory, so leak-checks must be disabled. +// +// FIXME(genmc): The error message is currently suboptimal, since it mentions non-allocated/freed memory, instead of pointing towards the missing synchronization with the allocation. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::alloc::{Layout, alloc, dealloc}; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(std::ptr::null_mut(), SeqCst); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + let a: *mut u64 = alloc(Layout::new::()) as *mut u64; + X.store(a, Relaxed); // Relaxed ordering does not synchronize the alloc with the other thread. + }), + spawn_pthread_closure(|| { + let b = X.load(Relaxed); + if !b.is_null() { + if cfg!(dealloc) { + // Variant: alloc-dealloc race + dealloc(b as *mut u8, Layout::new::()); //~[dealloc] ERROR: Undefined Behavior + } else { + // Variant: alloc-write race + *b = 42; //~[write] ERROR: Undefined Behavior + } + } + }), + ]; + join_pthreads(ids); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.write.stderr b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.write.stderr new file mode 100644 index 0000000000000..7bfafe0ca086c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.write.stderr @@ -0,0 +1,22 @@ +Running GenMC Verification... +error: Undefined Behavior: Attempt to access non-allocated memory + --> tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC + | +LL | *b = 42; + | ^^^^^^^ Undefined Behavior occurred here + | + = 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: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs new file mode 100644 index 0000000000000..c9bf1f755925d --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that use-after-free bugs involving atomic pointers are detected in GenMC mode. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); +static mut Y: u64 = 0; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(std::ptr::null_mut(), SeqCst); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + let mut z: u64 = 1234; + X.store(&raw mut z, SeqCst); // The other thread can read this value and then access `z` after it is deallocated. + X.store(&raw mut Y, SeqCst); + }), + spawn_pthread_closure(|| { + let ptr = X.load(SeqCst); + miri_genmc_assume(!ptr.is_null()); + *ptr = 42; //~ ERROR: Undefined Behavior + }), + ]; + join_pthreads(ids); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.stderr b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.stderr new file mode 100644 index 0000000000000..0facf6a5d177c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.stderr @@ -0,0 +1,32 @@ +Running GenMC Verification... +error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling + --> tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs:LL:CC + | +LL | *ptr = 42; + | ^^^^^^^^^ Undefined Behavior occurred here + | + = 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: ALLOC was allocated here: + --> tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs:LL:CC + | +LL | let mut z: u64 = 1234; + | ^^^^^ +help: ALLOC was deallocated here: + --> tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs:LL:CC + | +LL | }), + | ^ + = note: BACKTRACE (of the first span) on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs new file mode 100644 index 0000000000000..2253bac95de8c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs @@ -0,0 +1,50 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that use-after-free bugs involving atomic pointers are detected in GenMC mode. +// Compared to `atomic_ptr_dealloc_write_race.rs`, this variant checks that the data race is still detected, even if the write happens before the free. +// +// FIXME(genmc): We currently cannot show the two spans related to this error (only the second one), +// since there is no mapping between GenMC events (shown with `-Zmiri-genmc-print-genmc-output`) and Miri spans. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); +static Y: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(std::ptr::null_mut(), SeqCst); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + let ptr = X.load(SeqCst); + miri_genmc_assume(!ptr.is_null()); + *ptr = 42; + }), + spawn_pthread_closure(|| { + let mut z: u64 = 1234; + X.store(&raw mut z, SeqCst); // The other thread can read this value and then access `z` after it is deallocated. + for _ in 0..10 { + // We do some extra loads here so GenMC schedules the pointer write on the other thread first. + Y.load(Relaxed); + } + // `z` gets dropped on the next line, at which point GenMC will detect the data race with the write in the other thread. + }), //~ ERROR: Undefined Behavior + ]; + join_pthreads(ids); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.stderr b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.stderr new file mode 100644 index 0000000000000..8e4ed1aba0430 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.stderr @@ -0,0 +1,22 @@ +Running GenMC Verification... +error: Undefined Behavior: Attempt to access freed memory + --> tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs:LL:CC + | +LL | }), + | ^ Undefined Behavior occurred here + | + = 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: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs new file mode 100644 index 0000000000000..9db58b1f9501b --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs @@ -0,0 +1,173 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test several operations on atomic pointers. + +#![no_main] + +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::fmt::{Debug, Write}; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::utils::*; + +static mut X: u64 = 0; +static mut Y: u64 = 0; + +fn assert_equals(a: T, b: T) { + if a != b { + writeln!(MiriStderr, "{:?}, {:?}", a, b).ok(); + std::process::abort(); + } +} + +/// Check that two pointers are equal and stores to one update the value read from the other. +unsafe fn pointers_equal(a: *mut u64, b: *mut u64) { + assert_equals(a, b); + assert_equals(*a, *b); + *a = 42; + assert_equals(*a, 42); + assert_equals(*b, 42); + *b = 0xAA; + assert_equals(*a, 0xAA); + assert_equals(*b, 0xAA); +} + +unsafe fn test_load_store_exchange() { + let atomic_ptr: AtomicPtr = AtomicPtr::new(&raw mut X); + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + // FIXME(genmc): Add test cases with temporal mixing of atomics/non-atomics. + atomic_ptr.store(&raw mut X, SeqCst); + + // Load can read the initial value. + pointers_equal(atomic_ptr.load(SeqCst), &raw mut X); + // Store works as expected. + atomic_ptr.store(&raw mut Y, SeqCst); + pointers_equal(atomic_ptr.load(SeqCst), &raw mut Y); + + // Atomic swap must return the old value and store the new one. + pointers_equal(atomic_ptr.swap(&raw mut X, SeqCst), &raw mut Y); + pointers_equal(atomic_ptr.load(SeqCst), &raw mut X); + + // Failing compare_exchange (wrong expected pointer). + match atomic_ptr.compare_exchange(&raw mut Y, std::ptr::null_mut(), SeqCst, SeqCst) { + Ok(_ptr) => std::process::abort(), + Err(ptr) => pointers_equal(ptr, &raw mut X), + } + // Failing compare_exchange (null). + match atomic_ptr.compare_exchange(std::ptr::null_mut(), std::ptr::null_mut(), SeqCst, SeqCst) { + Ok(_ptr) => std::process::abort(), + Err(ptr) => pointers_equal(ptr, &raw mut X), + } + // Successful compare_exchange. + match atomic_ptr.compare_exchange(&raw mut X, &raw mut Y, SeqCst, SeqCst) { + Ok(ptr) => pointers_equal(ptr, &raw mut X), + Err(_ptr) => std::process::abort(), + } + // compare_exchange should update the pointer. + pointers_equal(atomic_ptr.load(SeqCst), &raw mut Y); +} + +unsafe fn test_add_sub() { + const LEN: usize = 16; + let mut array: [u64; LEN] = std::array::from_fn(|i| i as u64); + + let atomic_ptr: AtomicPtr = AtomicPtr::new(&raw mut array[0]); + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + atomic_ptr.store(&raw mut array[0], SeqCst); + + // Each element of the array should be reachable using `fetch_ptr_add`. + // All pointers must stay valid. + for i in 0..LEN { + let ptr = atomic_ptr.fetch_ptr_add(1, SeqCst); + pointers_equal(ptr, &raw mut array[i]); + } + // This should return the pointer back to the start of the array. + let ptr = atomic_ptr.fetch_ptr_sub(LEN, SeqCst); + pointers_equal(ptr.offset(-(LEN as isize)), &raw mut array[0]); + pointers_equal(atomic_ptr.load(SeqCst), &raw mut array[0]); + + let array_mid_ptr = &raw mut array[LEN / 2]; + for i in 0..size_of::() { + atomic_ptr.store(array_mid_ptr, SeqCst); // Reset to test `byte_add` and `byte_sub`. + pointers_equal(array_mid_ptr, atomic_ptr.fetch_byte_add(i, SeqCst)); + if array_mid_ptr.byte_add(i) != atomic_ptr.load(SeqCst) { + std::process::abort(); + } + if array_mid_ptr.byte_add(i) != atomic_ptr.fetch_byte_sub(i, SeqCst) { + std::process::abort(); + } + pointers_equal(array_mid_ptr, atomic_ptr.load(SeqCst)); + } +} + +unsafe fn test_and_or_xor() { + const LEN: usize = 16; + #[repr(align(1024))] // Aligned to size 16 * 8 bytes. + struct AlignedArray([u64; LEN]); + + let mut array = AlignedArray(std::array::from_fn(|i| i as u64 * 10)); + let array_ptr = &raw mut array.0[0]; + + let atomic_ptr: AtomicPtr = AtomicPtr::new(array_ptr); + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + atomic_ptr.store(array_ptr, SeqCst); + + // Test no-op arguments. + assert_equals(array_ptr, atomic_ptr.fetch_or(0, SeqCst)); + assert_equals(array_ptr, atomic_ptr.fetch_xor(0, SeqCst)); + assert_equals(array_ptr, atomic_ptr.fetch_and(!0, SeqCst)); + assert_equals(array_ptr, atomic_ptr.load(SeqCst)); + + // Test identity arguments. + let array_addr = array_ptr as usize; + assert_equals(array_ptr, atomic_ptr.fetch_or(array_addr, SeqCst)); + assert_equals(array_ptr, atomic_ptr.load(SeqCst)); + assert_equals(array_ptr, atomic_ptr.fetch_and(array_addr, SeqCst)); + assert_equals(array_ptr, atomic_ptr.load(SeqCst)); + assert_equals(array_ptr, atomic_ptr.fetch_xor(array_addr, SeqCst)); + assert_equals(std::ptr::null_mut(), atomic_ptr.load(SeqCst)); // `null_mut` is guaranteed to have address 0. + + // Test moving within an allocation. + // The array is aligned to 64 bytes, so we can change which element we point by or/and/xor-ing the address. + let index = LEN / 2; // Choose an index in the middle of the array. + let offset = index * size_of::(); + let array_mid_ptr = &raw mut array.0[index]; + + atomic_ptr.store(array_ptr, SeqCst); // Reset to test `or`. + assert_equals(array_ptr, atomic_ptr.fetch_or(offset, SeqCst)); + assert_equals(array_mid_ptr, atomic_ptr.load(SeqCst)); + + atomic_ptr.store(array_ptr, SeqCst); // Reset to test `xor`. + assert_equals(array_ptr, atomic_ptr.fetch_xor(offset, SeqCst)); + assert_equals(array_mid_ptr, atomic_ptr.load(SeqCst)); + assert_equals(array_mid_ptr, atomic_ptr.fetch_xor(offset, SeqCst)); // two xor should yield original value. + assert_equals(array_ptr, atomic_ptr.load(SeqCst)); + + let mask = !(u64::BITS as usize - 1); + for i in 0..size_of::() { + // We offset the pointer by `i` bytes, making it unaligned. + let offset_ptr = array_ptr.byte_add(i); + atomic_ptr.store(array_ptr, SeqCst); + // `fetch_byte_add` should return the old value. + assert_equals(array_ptr, atomic_ptr.fetch_byte_add(i, SeqCst)); + // `ptr::byte_add` and `AtomicPtr::fetch_byte_add` should give the same result. + if offset_ptr != atomic_ptr.fetch_and(mask, SeqCst) { + std::process::abort(); + } + // Masking off the last bits should restore the pointer. + assert_equals(array_ptr, atomic_ptr.load(SeqCst)); + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + test_load_store_exchange(); + test_add_sub(); + test_and_or_xor(); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.stderr b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.stderr new file mode 100644 index 0000000000000..7867be2dbe8ed --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.stderr @@ -0,0 +1,2 @@ +Running GenMC Verification... +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs new file mode 100644 index 0000000000000..75ee5ede9c969 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs @@ -0,0 +1,60 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that we can send pointers with any alignment to GenMC and back, even across threads. +// After a round-trip, the pointers should still work properly (no missing provenance). + +#![no_main] +#![allow(static_mut_refs)] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use genmc::*; +use utils::*; + +static PTR: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +static mut X: [u8; 16] = [0; 16]; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + PTR.store(std::ptr::null_mut(), SeqCst); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + for i in 0..X.len() { + X[i] = i.try_into().unwrap(); + PTR.store(&raw mut X[i], SeqCst); + // Wait for the other thread to reset the AtomicPtr. + miri_genmc_assume(PTR.load(SeqCst).is_null()); + // Check that we see the update the other thread did through the pointer. + if X[i] != (i + 1) as u8 { + std::process::abort(); + } + } + }), + spawn_pthread_closure(|| { + for i in 0..X.len() { + let x = PTR.load(SeqCst); + // Wait for the other thread to store the next pointer. + miri_genmc_assume(!x.is_null()); + // Check that we see the update when reading from the pointer. + if usize::from(*x) != i { + std::process::abort(); + } + *x = (i + 1) as u8; + PTR.store(std::ptr::null_mut(), SeqCst); + } + }), + ]; + join_pthreads(ids); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.stderr b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.stderr new file mode 100644 index 0000000000000..67d88dfaed711 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.stderr @@ -0,0 +1,2 @@ +Running GenMC Verification... +Verification complete with 33 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W1.stderr b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W1.stderr new file mode 100644 index 0000000000000..5b60672a09d71 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W1.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 4 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W2.stderr b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W2.stderr new file mode 100644 index 0000000000000..1a025216a22d6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.default_R1W2.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 188 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs new file mode 100644 index 0000000000000..fda517ae7d231 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs @@ -0,0 +1,200 @@ +//@ revisions: default_R1W1 default_R1W2 spinloop_assume_R1W1 spinloop_assume_R1W2 +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-verbose +//@normalize-stderr-test: "Verification took .*s" -> "Verification took [TIME]s" + +// This test is a translations of the GenMC test `ms-queue-dynamic`, but with all code related to GenMC's hazard pointer API removed. +// The test leaks memory, so leak checks are disabled. +// +// Test variant naming convention: "[VARIANT_NAME]_R[#reader_threads]_W[#writer_threads]". +// We test different numbers of writer threads to see the scaling. +// Implementing optimizations such as automatic spinloop-assume transformation or symmetry reduction should reduce the number of explored executions. +// We also test variants using manual spinloop replacement, which should yield fewer executions in total compared to the unmodified code. +// +// FIXME(genmc): Add revisions `default_R1W3` and `spinloop_assume_R1W3` once Miri-GenMC performance is improved. These currently slow down the test suite too much. +// +// The test uses verbose output to see the difference between blocked and explored executions. + +#![no_main] +#![allow(static_mut_refs)] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[allow(unused)] +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::alloc::{Layout, alloc, dealloc}; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use genmc::*; +use libc::pthread_t; + +const MAX_THREADS: usize = 32; + +static mut QUEUE: MyStack = MyStack::new(); +static mut INPUT: [u64; MAX_THREADS] = [0; MAX_THREADS]; +static mut OUTPUT: [Option; MAX_THREADS] = [None; MAX_THREADS]; + +#[repr(C)] +struct Node { + value: u64, + next: AtomicPtr, +} + +struct MyStack { + head: AtomicPtr, + tail: AtomicPtr, +} + +impl Node { + pub unsafe fn alloc() -> *mut Self { + alloc(Layout::new::()) as *mut Self + } + + pub unsafe fn free(node: *mut Self) { + dealloc(node as *mut u8, Layout::new::()) + } +} + +impl MyStack { + pub const fn new() -> Self { + let head = AtomicPtr::new(std::ptr::null_mut()); + let tail = AtomicPtr::new(std::ptr::null_mut()); + Self { head, tail } + } + + pub unsafe fn init_queue(&mut self, _num_threads: usize) { + let dummy = Node::alloc(); + + (*dummy).next = AtomicPtr::new(std::ptr::null_mut()); + self.head = AtomicPtr::new(dummy); + self.tail = AtomicPtr::new(dummy); + + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + (*dummy).next.store(std::ptr::null_mut(), Relaxed); + self.head.store(dummy, Relaxed); + self.tail.store(dummy, Relaxed); + } + + pub unsafe fn clear_queue(&mut self, _num_threads: usize) { + let mut next; + let mut head = *self.head.get_mut(); + while !head.is_null() { + next = *(*head).next.get_mut(); + Node::free(head); + head = next; + } + } + + pub unsafe fn enqueue(&self, value: u64) { + let mut tail; + let node = Node::alloc(); + (*node).value = value; + (*node).next = AtomicPtr::new(std::ptr::null_mut()); + + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + (*node).next.store(std::ptr::null_mut(), Relaxed); + + loop { + tail = self.tail.load(Acquire); + let next = (*tail).next.load(Acquire); + if tail != self.tail.load(Acquire) { + // Looping here has no side effects, so we prevent exploring any executions where this branch happens. + #[cfg(any(spinloop_assume_R1W1, spinloop_assume_R1W2, spinloop_assume_R1W3))] + utils::miri_genmc_assume(false); // GenMC will stop any execution that reaches this. + continue; + } + + if next.is_null() { + if (*tail).next.compare_exchange(next, node, Release, Relaxed).is_ok() { + break; + } + } else { + let _ = self.tail.compare_exchange(tail, next, Release, Relaxed); + } + } + + let _ = self.tail.compare_exchange(tail, node, Release, Relaxed); + } + + pub unsafe fn dequeue(&self) -> Option { + loop { + let head = self.head.load(Acquire); + let tail = self.tail.load(Acquire); + + let next = (*head).next.load(Acquire); + if self.head.load(Acquire) != head { + // Looping here has no side effects, so we prevent exploring any executions where this branch happens. + #[cfg(any(spinloop_assume_R1W1, spinloop_assume_R1W2, spinloop_assume_R1W3))] + utils::miri_genmc_assume(false); // GenMC will stop any execution that reaches this. + continue; + } + if head == tail { + if next.is_null() { + return None; + } + let _ = self.tail.compare_exchange(tail, next, Release, Relaxed); + } else { + let ret_val = (*next).value; + if self.head.compare_exchange(head, next, Release, Relaxed).is_ok() { + // NOTE: The popped `Node` is leaked. + return Some(ret_val); + } + // Looping here has no side effects, so we prevent exploring any executions where this branch happens. + // All operations in the loop leading to here are either loads, or failed compare-exchange operations. + #[cfg(any(spinloop_assume_R1W1, spinloop_assume_R1W2, spinloop_assume_R1W3))] + utils::miri_genmc_assume(false); // GenMC will stop any execution that reaches this. + } + } + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // We try multiple different parameters for the number and types of threads: + let (readers, writers) = if cfg!(any(default_R1W3, spinloop_assume_R1W3)) { + (1, 3) + } else if cfg!(any(default_R1W2, spinloop_assume_R1W2)) { + (1, 2) + } else { + // default_R1W1, spinloop_assume_R1W1 + (1, 1) + }; + + let num_threads = readers + writers; + if num_threads > MAX_THREADS { + std::process::abort(); + } + + let mut i = 0; + unsafe { + MyStack::init_queue(&mut QUEUE, num_threads); + + /* Spawn threads */ + let mut thread_ids: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; + for _ in 0..readers { + let pid = i as u64; + thread_ids[i] = spawn_pthread_closure(move || { + OUTPUT[pid as usize] = QUEUE.dequeue(); + }); + i += 1; + } + for _ in 0..writers { + let pid = i as u64; + thread_ids[i] = spawn_pthread_closure(move || { + INPUT[pid as usize] = pid * 10; + QUEUE.enqueue(INPUT[pid as usize]); + }); + i += 1; + } + + for i in 0..num_threads { + join_pthread(thread_ids[i]); + } + + MyStack::clear_queue(&mut QUEUE, num_threads); + } + + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W1.stderr b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W1.stderr new file mode 100644 index 0000000000000..5b60672a09d71 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W1.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 4 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W2.stderr b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W2.stderr new file mode 100644 index 0000000000000..44332a2f027e4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.spinloop_assume_R1W2.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 152 executions. No errors found. +Number of complete executions explored: 128 +Number of blocked executions seen: 24 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W1.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W1.stderr new file mode 100644 index 0000000000000..5452d10bb2608 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W1.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 2 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W2.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W2.stderr new file mode 100644 index 0000000000000..98b0ced1c89aa --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W2.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 22 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W3.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W3.stderr new file mode 100644 index 0000000000000..ba085e45e57fc --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.default_R1W3.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 1002 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs new file mode 100644 index 0000000000000..2101f9ae66bb6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs @@ -0,0 +1,150 @@ +//@ revisions: default_R1W1 default_R1W2 default_R1W3 spinloop_assume_R1W1 spinloop_assume_R1W2 spinloop_assume_R1W3 +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-verbose +//@normalize-stderr-test: "Verification took .*s" -> "Verification took [TIME]s" + +// This test is a translations of the GenMC test `treiber-stack-dynamic`, but with all code related to GenMC's hazard pointer API removed. +// The test leaks memory, so leak checks are disabled. +// +// Test variant naming convention: "[VARIANT_NAME]_R[#reader_threads]_W[#writer_threads]". +// We test different numbers of writer threads to see the scaling. +// Implementing optimizations such as automatic spinloop-assume transformation or symmetry reduction should reduce the number of explored executions. +// We also test variants using manual spinloop replacement, which should yield fewer executions in total compared to the unmodified code. +// +// The test uses verbose output to see the difference between blocked and explored executions. + +#![no_main] +#![allow(static_mut_refs)] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[allow(unused)] +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::alloc::{Layout, alloc, dealloc}; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use genmc::*; +use libc::pthread_t; + +const MAX_THREADS: usize = 32; + +static mut STACK: MyStack = MyStack::new(); + +#[repr(C)] +struct Node { + value: u64, + next: AtomicPtr, +} + +struct MyStack { + top: AtomicPtr, +} + +impl Node { + pub unsafe fn alloc() -> *mut Self { + alloc(Layout::new::()) as *mut Self + } + + pub unsafe fn free(node: *mut Self) { + dealloc(node as *mut u8, Layout::new::()) + } +} + +impl MyStack { + pub const fn new() -> Self { + Self { top: AtomicPtr::new(std::ptr::null_mut()) } + } + + pub unsafe fn clear_stack(&mut self, _num_threads: usize) { + let mut next; + let mut top = *self.top.get_mut(); + while !top.is_null() { + next = *(*top).next.get_mut(); + Node::free(top); + top = next; + } + } + + pub unsafe fn push(&self, value: u64) { + let node = Node::alloc(); + (*node).value = value; + + loop { + let top = self.top.load(Acquire); + (*node).next.store(top, Relaxed); + if self.top.compare_exchange(top, node, Release, Relaxed).is_ok() { + break; + } + // We manually limit the number of iterations of this spinloop to 1. + #[cfg(any(spinloop_assume_R1W1, spinloop_assume_R1W2, spinloop_assume_R1W3))] + utils::miri_genmc_assume(false); // GenMC will stop any execution that reaches this. + } + } + + pub unsafe fn pop(&self) -> u64 { + loop { + let top = self.top.load(Acquire); + if top.is_null() { + return 0; + } + + let next = (*top).next.load(Relaxed); + if self.top.compare_exchange(top, next, Release, Relaxed).is_ok() { + // NOTE: The popped `Node` is leaked. + return (*top).value; + } + // We manually limit the number of iterations of this spinloop to 1. + #[cfg(any(spinloop_assume_R1W1, spinloop_assume_R1W2, spinloop_assume_R1W3))] + utils::miri_genmc_assume(false); // GenMC will stop any execution that reaches this. + } + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + unsafe { STACK.top.store(std::ptr::null_mut(), Relaxed) }; + + // We try multiple different parameters for the number and types of threads: + let (readers, writers) = if cfg!(any(default_R1W3, spinloop_assume_R1W3)) { + (1, 3) + } else if cfg!(any(default_R1W2, spinloop_assume_R1W2)) { + (1, 2) + } else { + // default_R1W1, spinloop_assume_R1W1 + (1, 1) + }; + + let num_threads = readers + writers; + if num_threads > MAX_THREADS { + std::process::abort(); + } + + let mut i = 0; + unsafe { + let mut thread_ids: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; + for _ in 0..readers { + thread_ids[i] = spawn_pthread_closure(move || { + let _idx = STACK.pop(); + }); + i += 1; + } + for _ in 0..writers { + let pid = i as u64; + thread_ids[i] = spawn_pthread_closure(move || { + STACK.push(pid); + }); + i += 1; + } + + for i in 0..num_threads { + join_pthread(thread_ids[i]); + } + + MyStack::clear_stack(&mut STACK, num_threads); + } + + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W1.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W1.stderr new file mode 100644 index 0000000000000..5452d10bb2608 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W1.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 2 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W2.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W2.stderr new file mode 100644 index 0000000000000..9082acd027d4c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W2.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 16 executions. No errors found. +Number of complete executions explored: 8 +Number of blocked executions seen: 8 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W3.stderr b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W3.stderr new file mode 100644 index 0000000000000..02ac5a982f3f0 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.spinloop_assume_R1W3.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 195 executions. No errors found. +Number of complete executions explored: 36 +Number of blocked executions seen: 159 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs index c81573d59d1da..fa717b4cf18a8 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs @@ -57,7 +57,7 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { join_pthreads(ids); // Print the values to check that we get all of them: - writeln!(MiriStderr, "{results:?}").unwrap_or_else(|_| std::process::abort()); + writeln!(MiriStderr, "{results:?}").ok(); 0 } diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs index 9bb156a99970f..a1b13e3f3c56f 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs @@ -60,7 +60,7 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { X.load(Relaxed), Y.load(Relaxed) ) - .unwrap_or_else(|_| std::process::abort()); + .ok(); 0 } diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs index f96679b23a5cd..a991ccb0d4e04 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs @@ -55,8 +55,7 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { join_pthreads(ids); // Print the values to check that we get all of them: - writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed)) - .unwrap_or_else(|_| std::process::abort()); + writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed)).ok(); 0 } } diff --git a/src/tools/miri/tests/genmc/pass/thread/thread_locals.rs b/src/tools/miri/tests/genmc/pass/thread/thread_locals.rs new file mode 100644 index 0000000000000..cabc8ec92da1c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/thread/thread_locals.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::alloc::{Layout, alloc}; +use std::cell::Cell; +use std::sync::atomic::AtomicPtr; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +thread_local! { + static R: Cell<*mut u64> = Cell::new(std::ptr::null_mut()); +} + +pub unsafe fn malloc() -> *mut u64 { + alloc(Layout::new::()) as *mut u64 +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(std::ptr::null_mut(), SeqCst); + + unsafe { + spawn_pthread_closure(|| { + R.set(malloc()); + let r_ptr = R.get(); + let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); + }); + spawn_pthread_closure(|| { + R.set(malloc()); + }); + spawn_pthread_closure(|| { + R.set(malloc()); + let r_ptr = R.get(); + let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); + }); + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr new file mode 100644 index 0000000000000..bde951866d013 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr @@ -0,0 +1,2 @@ +Running GenMC Verification... +Verification complete with 2 executions. No errors found. From cfefa9bc883dda6fe09a5041e35b33d17aa0969e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 29 Sep 2025 19:07:01 +0200 Subject: [PATCH 10/26] genmc: bail out for non-64bit-little-endian targets --- src/tools/miri/src/bin/miri.rs | 10 +++++----- src/tools/miri/src/concurrency/genmc/config.rs | 11 ++++++++--- src/tools/miri/src/concurrency/genmc/dummy.rs | 4 +++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 40ac5c3ff385c..3c759521c6ca0 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -188,6 +188,11 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { // Run in GenMC mode if enabled. if config.genmc_config.is_some() { + // Validate GenMC settings. + if let Err(err) = GenmcConfig::validate(&mut config, tcx) { + fatal_error!("Invalid settings: {err}"); + } + // This is the entry point used in GenMC mode. // This closure will be called multiple times to explore the concurrent execution space of the program. let eval_entry_once = |genmc_ctx: Rc| { @@ -745,11 +750,6 @@ fn main() { let many_seeds = many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going }); - // Validate settings for data race detection and GenMC mode. - if let Err(err) = GenmcConfig::validate_genmc_mode_settings(&mut miri_config) { - fatal_error!("Invalid settings: {err}"); - } - if miri_config.weak_memory_emulation && !miri_config.data_race_detector { fatal_error!( "Weak memory emulation cannot be enabled when the data race detector is disabled" diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index c7cfa6012b8dc..a05cda46f3e74 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -1,4 +1,6 @@ use genmc_sys::LogLevel; +use rustc_abi::Endian; +use rustc_middle::ty::TyCtxt; use super::GenmcParams; use crate::{IsolatedOp, MiriConfig, RejectOpWith}; @@ -32,8 +34,6 @@ impl GenmcConfig { genmc_config: &mut Option, trimmed_arg: &str, ) -> Result<(), String> { - // FIXME(genmc): Ensure host == target somewhere. - if genmc_config.is_none() { *genmc_config = Some(Default::default()); } @@ -86,11 +86,16 @@ impl GenmcConfig { /// /// Unsupported configurations return an error. /// Adjusts Miri settings where required, printing a warnings if the change might be unexpected for the user. - pub fn validate_genmc_mode_settings(miri_config: &mut MiriConfig) -> Result<(), &'static str> { + pub fn validate(miri_config: &mut MiriConfig, tcx: TyCtxt<'_>) -> Result<(), &'static str> { let Some(genmc_config) = miri_config.genmc_config.as_mut() else { return Ok(()); }; + // Check for supported target. + if tcx.data_layout.endian != Endian::Little || tcx.data_layout.pointer_size().bits() != 64 { + return Err("GenMC only supports 64bit little-endian targets"); + } + // Check for disallowed configurations. if !miri_config.data_race_detector { return Err("Cannot disable data race detection in GenMC mode"); diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index e260af7c4a16b..ef5b24d5abad5 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -1,5 +1,6 @@ use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult}; +use rustc_middle::ty::TyCtxt; pub use self::intercept::EvalContextExt as GenmcEvalContextExt; pub use self::run::run_genmc_mode; @@ -230,8 +231,9 @@ impl GenmcConfig { } } - pub fn validate_genmc_mode_settings( + pub fn validate( _miri_config: &mut crate::MiriConfig, + _tcx: TyCtxt<'_>, ) -> Result<(), &'static str> { Ok(()) } From 8b51a63dbbe2663403e644009dee9e35f611ae32 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 30 Sep 2025 04:53:35 +0000 Subject: [PATCH 11/26] Prepare for merging from rust-lang/rust This updates the rust-version file to 29b7717de23f3969ceeb5bef5b01d9223f807655. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 1f90d4e5e498a..70374af27e9c9 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f957826bff7a68b267ce75b1ea56352aed0cca0a +29b7717de23f3969ceeb5bef5b01d9223f807655 From 574ff896d63a02d29e2bb5f8348978692a9a81e2 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 14 Mar 2025 09:58:46 +0100 Subject: [PATCH 12/26] Implement support for temporal mixing of atomic/non-atomic accesses in GenMC mode. Remove atomic initialization dummy writes from tests. --- src/tools/miri/genmc-sys/build.rs | 2 +- .../genmc-sys/cpp/include/MiriInterface.hpp | 11 +- .../cpp/src/MiriInterface/EventHandling.cpp | 44 ++-- .../fail/atomics/atomic_ptr_double_free.rs | 3 - .../fail/data_race/atomic_ptr_alloc_race.rs | 3 - .../atomic_ptr_dealloc_write_race.rs | 3 - .../atomic_ptr_write_dealloc_race.rs | 3 - .../miri/tests/genmc/fail/loom/buggy_inc.rs | 3 - .../genmc/pass/atomics/atomic_ptr_ops.rs | 27 ++- .../pass/atomics/atomic_ptr_roundtrip.rs | 3 - .../atomics/cas_failure_ord_racy_key_init.rs | 3 - .../pass/atomics/mixed_atomic_non_atomic.rs | 41 ++++ .../atomics/mixed_atomic_non_atomic.stderr | 2 + .../genmc/pass/atomics/read_initial_value.rs | 41 ++++ .../pass/atomics/read_initial_value.stderr | 2 + .../miri/tests/genmc/pass/atomics/rmw_ops.rs | 5 +- .../pass/data-structures/ms_queue_dynamic.rs | 8 - .../data-structures/treiber_stack_dynamic.rs | 3 - .../genmc/pass/intercept/spinloop_assume.rs | 4 - .../miri/tests/genmc/pass/litmus/2cowr.rs | 4 - .../tests/genmc/pass/litmus/IRIW-acq-sc.rs | 4 - .../miri/tests/genmc/pass/litmus/IRIWish.rs | 4 - src/tools/miri/tests/genmc/pass/litmus/LB.rs | 4 - src/tools/miri/tests/genmc/pass/litmus/MP.rs | 4 - .../tests/genmc/pass/litmus/MPU2_rels_acqf.rs | 4 - .../tests/genmc/pass/litmus/MPU_rels_acq.rs | 4 - .../tests/genmc/pass/litmus/MP_rels_acqf.rs | 4 - src/tools/miri/tests/genmc/pass/litmus/SB.rs | 4 - .../miri/tests/genmc/pass/litmus/Z6_U.rs | 4 - .../miri/tests/genmc/pass/litmus/casdep.rs | 5 - src/tools/miri/tests/genmc/pass/litmus/ccr.rs | 3 - src/tools/miri/tests/genmc/pass/litmus/cii.rs | 3 - .../miri/tests/genmc/pass/litmus/corr.rs | 4 - .../miri/tests/genmc/pass/litmus/corr0.rs | 3 - .../miri/tests/genmc/pass/litmus/corr1.rs | 3 - .../miri/tests/genmc/pass/litmus/corr2.rs | 3 - .../miri/tests/genmc/pass/litmus/corw.rs | 3 - .../tests/genmc/pass/litmus/cumul-release.rs | 5 - .../miri/tests/genmc/pass/litmus/default.rs | 3 - .../miri/tests/genmc/pass/litmus/detour.rs | 5 - .../tests/genmc/pass/litmus/fr_w_w_w_reads.rs | 3 - .../miri/tests/genmc/pass/litmus/inc2w.rs | 3 - .../genmc/pass/std/arc.check_count.stderr | 163 +++++++++++++++ src/tools/miri/tests/genmc/pass/std/arc.rs | 49 +++++ .../genmc/pass/std/arc.try_upgrade.stderr | 191 ++++++++++++++++++ .../miri/tests/genmc/pass/std/empty_main.rs | 5 + .../tests/genmc/pass/std/empty_main.stderr | 60 ++++++ .../tests/genmc/pass/std/spawn_std_threads.rs | 13 ++ .../genmc/pass/std/spawn_std_threads.stderr | 167 +++++++++++++++ .../pass/{thread => std}/thread_locals.rs | 38 ++-- .../tests/genmc/pass/std/thread_locals.stderr | 184 +++++++++++++++++ .../genmc/pass/thread/thread_locals.stderr | 2 - 52 files changed, 978 insertions(+), 191 deletions(-) create mode 100644 src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.stderr create mode 100644 src/tools/miri/tests/genmc/pass/atomics/read_initial_value.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/read_initial_value.stderr create mode 100644 src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr create mode 100644 src/tools/miri/tests/genmc/pass/std/arc.rs create mode 100644 src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr create mode 100644 src/tools/miri/tests/genmc/pass/std/empty_main.rs create mode 100644 src/tools/miri/tests/genmc/pass/std/empty_main.stderr create mode 100644 src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs create mode 100644 src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr rename src/tools/miri/tests/genmc/pass/{thread => std}/thread_locals.rs (53%) create mode 100644 src/tools/miri/tests/genmc/pass/std/thread_locals.stderr delete mode 100644 src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index ef49be151bf09..964382d824089 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -28,7 +28,7 @@ mod downloading { /// The GenMC repository the we get our commit from. pub(crate) const GENMC_GITHUB_URL: &str = "https://gitlab.inf.ethz.ch/public-plf/genmc.git"; /// The GenMC commit we depend on. It must be available on the specified GenMC repository. - pub(crate) const GENMC_COMMIT: &str = "cd01c12032bdd71df742b41c7817f99acc72e7ab"; + pub(crate) const GENMC_COMMIT: &str = "ce775ccd7866db820fa12ffca66463087a11dd96"; /// Ensure that a local GenMC repo is present and set to the correct commit. /// Return the path of the GenMC repo and whether the checked out commit was changed. diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index 5f73e5d3d3d06..c9da718aabd61 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -214,9 +214,10 @@ struct MiriGenmcShim : private GenMCDriver { * Automatically calls `inc_pos` and `dec_pos` where needed for the given thread. */ template - auto handle_load_reset_if_none(ThreadId tid, Ts&&... params) -> HandleResult { + auto handle_load_reset_if_none(ThreadId tid, std::optional old_val, Ts&&... params) + -> HandleResult { const auto pos = inc_pos(tid); - const auto ret = GenMCDriver::handleLoad(pos, std::forward(params)...); + const auto ret = GenMCDriver::handleLoad(pos, old_val, std::forward(params)...); // If we didn't get a value, we have to reset the index of the current thread. if (!std::holds_alternative(ret)) { dec_pos(tid); @@ -274,6 +275,12 @@ inline SVal to_sval(GenmcScalar scalar) { ERROR_ON(!scalar.is_init, "Cannot convert an uninitialized `GenmcScalar` into an `SVal`\n"); return SVal(scalar.value, scalar.extra); } + +inline std::optional try_to_sval(GenmcScalar scalar) { + if (scalar.is_init) + return { SVal(scalar.value, scalar.extra) }; + return std::nullopt; +} } // namespace GenmcScalarExt namespace LoadResultExt { diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index c9e7f7a1a8a1c..a8f8b9cc24f9e 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -49,6 +49,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { const auto type = AType::Unsigned; const auto ret = handle_load_reset_if_none( thread_id, + GenmcScalarExt::try_to_sval(old_val), ord, SAddr(address), ASize(size), @@ -74,6 +75,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { const auto pos = inc_pos(thread_id); const auto ret = GenMCDriver::handleStore( pos, + GenmcScalarExt::try_to_sval(old_val), ord, SAddr(address), ASize(size), @@ -84,15 +86,13 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { if (const auto* err = std::get_if(&ret)) return StoreResultExt::from_error(format_error(*err)); - if (!std::holds_alternative(ret)) - ERROR("store returned unexpected result"); - // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once - // available). - const auto& g = getExec().getGraph(); - return StoreResultExt::ok( - /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == pos + const bool* is_coherence_order_maximal_write = std::get_if(&ret); + ERROR_ON( + nullptr == is_coherence_order_maximal_write, + "Unimplemented: Store returned unexpected result." ); + return StoreResultExt::ok(*is_coherence_order_maximal_write); } void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { @@ -117,6 +117,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { // `FaiRead` and `FaiWrite`. const auto load_ret = handle_load_reset_if_none( thread_id, + GenmcScalarExt::try_to_sval(old_val), ordering, SAddr(address), ASize(size), @@ -139,6 +140,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { const auto storePos = inc_pos(thread_id); const auto store_ret = GenMCDriver::handleStore( storePos, + GenmcScalarExt::try_to_sval(old_val), ordering, SAddr(address), ASize(size), @@ -148,16 +150,15 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { if (const auto* err = std::get_if(&store_ret)) return ReadModifyWriteResultExt::from_error(format_error(*err)); - const auto* store_ret_val = std::get_if(&store_ret); - ERROR_ON(nullptr == store_ret_val, "Unimplemented: RMW store returned unexpected result."); - - // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once - // available). - const auto& g = getExec().getGraph(); + const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); + ERROR_ON( + nullptr == is_coherence_order_maximal_write, + "Unimplemented: RMW store returned unexpected result." + ); return ReadModifyWriteResultExt::ok( /* old_value: */ read_old_val, new_value, - /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos + *is_coherence_order_maximal_write ); } @@ -183,6 +184,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { const auto load_ret = handle_load_reset_if_none( thread_id, + GenmcScalarExt::try_to_sval(old_val), success_ordering, SAddr(address), ASize(size), @@ -203,6 +205,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { const auto storePos = inc_pos(thread_id); const auto store_ret = GenMCDriver::handleStore( storePos, + GenmcScalarExt::try_to_sval(old_val), success_ordering, SAddr(address), ASize(size), @@ -211,19 +214,12 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { ); if (const auto* err = std::get_if(&store_ret)) return CompareExchangeResultExt::from_error(format_error(*err)); - const auto* store_ret_val = std::get_if(&store_ret); + const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); ERROR_ON( - nullptr == store_ret_val, + nullptr == is_coherence_order_maximal_write, "Unimplemented: compare-exchange store returned unexpected result." ); - - // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once - // available). - const auto& g = getExec().getGraph(); - return CompareExchangeResultExt::success( - read_old_val, - /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos - ); + return CompareExchangeResultExt::success(read_old_val, *is_coherence_order_maximal_write); } /**** Memory (de)allocation ****/ diff --git a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs index d712822a6d00c..c18675931719f 100644 --- a/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs +++ b/src/tools/miri/tests/genmc/fail/atomics/atomic_ptr_double_free.rs @@ -22,9 +22,6 @@ unsafe fn free(ptr: *mut u64) { #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(std::ptr::null_mut(), SeqCst); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs index d82fbf53dac7f..e453c16b157d2 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs @@ -23,9 +23,6 @@ static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(std::ptr::null_mut(), SeqCst); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs index c9bf1f755925d..10e0d8d854c25 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs @@ -20,9 +20,6 @@ static mut Y: u64 = 0; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(std::ptr::null_mut(), SeqCst); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs index 2253bac95de8c..e2d3057a5b0df 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs +++ b/src/tools/miri/tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs @@ -24,9 +24,6 @@ static Y: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(std::ptr::null_mut(), SeqCst); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs index 508eae756f320..2e614e6a360ba 100644 --- a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs +++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs @@ -42,9 +42,6 @@ impl BuggyInc { fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { unsafe { static BUGGY_INC: BuggyInc = BuggyInc::new(); - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - BUGGY_INC.num.store(0, Relaxed); - let ids = [ spawn_pthread_closure(|| { BUGGY_INC.inc(); diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs index 9db58b1f9501b..aa40e193dbfd0 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_ops.rs @@ -37,18 +37,22 @@ unsafe fn pointers_equal(a: *mut u64, b: *mut u64) { unsafe fn test_load_store_exchange() { let atomic_ptr: AtomicPtr = AtomicPtr::new(&raw mut X); - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - // FIXME(genmc): Add test cases with temporal mixing of atomics/non-atomics. - atomic_ptr.store(&raw mut X, SeqCst); - // Load can read the initial value. + // Atomic load can read the initial value. pointers_equal(atomic_ptr.load(SeqCst), &raw mut X); - // Store works as expected. + // Atomic store works as expected. atomic_ptr.store(&raw mut Y, SeqCst); pointers_equal(atomic_ptr.load(SeqCst), &raw mut Y); + // We can read the value of the atomic store non-atomically. + pointers_equal(*atomic_ptr.as_ptr(), &raw mut Y); + // We can read the value of a non-atomic store atomically. + *atomic_ptr.as_ptr() = &raw mut X; + pointers_equal(atomic_ptr.load(SeqCst), &raw mut X); // Atomic swap must return the old value and store the new one. + *atomic_ptr.as_ptr() = &raw mut Y; // Test that we can read this non-atomic store using `swap`. pointers_equal(atomic_ptr.swap(&raw mut X, SeqCst), &raw mut Y); + pointers_equal(*atomic_ptr.as_ptr(), &raw mut X); pointers_equal(atomic_ptr.load(SeqCst), &raw mut X); // Failing compare_exchange (wrong expected pointer). @@ -56,11 +60,17 @@ unsafe fn test_load_store_exchange() { Ok(_ptr) => std::process::abort(), Err(ptr) => pointers_equal(ptr, &raw mut X), } + // Non-atomic read value should also be unchanged by a failing compare_exchange. + pointers_equal(*atomic_ptr.as_ptr(), &raw mut X); + // Failing compare_exchange (null). match atomic_ptr.compare_exchange(std::ptr::null_mut(), std::ptr::null_mut(), SeqCst, SeqCst) { Ok(_ptr) => std::process::abort(), Err(ptr) => pointers_equal(ptr, &raw mut X), } + // Non-atomic read value should also be unchanged by a failing compare_exchange. + pointers_equal(*atomic_ptr.as_ptr(), &raw mut X); + // Successful compare_exchange. match atomic_ptr.compare_exchange(&raw mut X, &raw mut Y, SeqCst, SeqCst) { Ok(ptr) => pointers_equal(ptr, &raw mut X), @@ -68,15 +78,13 @@ unsafe fn test_load_store_exchange() { } // compare_exchange should update the pointer. pointers_equal(atomic_ptr.load(SeqCst), &raw mut Y); + pointers_equal(*atomic_ptr.as_ptr(), &raw mut Y); } unsafe fn test_add_sub() { const LEN: usize = 16; let mut array: [u64; LEN] = std::array::from_fn(|i| i as u64); - let atomic_ptr: AtomicPtr = AtomicPtr::new(&raw mut array[0]); - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - atomic_ptr.store(&raw mut array[0], SeqCst); // Each element of the array should be reachable using `fetch_ptr_add`. // All pointers must stay valid. @@ -110,10 +118,7 @@ unsafe fn test_and_or_xor() { let mut array = AlignedArray(std::array::from_fn(|i| i as u64 * 10)); let array_ptr = &raw mut array.0[0]; - let atomic_ptr: AtomicPtr = AtomicPtr::new(array_ptr); - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - atomic_ptr.store(array_ptr, SeqCst); // Test no-op arguments. assert_equals(array_ptr, atomic_ptr.fetch_or(0, SeqCst)); diff --git a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs index 75ee5ede9c969..d846a55cbc31a 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs +++ b/src/tools/miri/tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs @@ -23,9 +23,6 @@ static mut X: [u8; 16] = [0; 16]; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - PTR.store(std::ptr::null_mut(), SeqCst); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs index 8a77d54a64f8b..e17a988cb3736 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs @@ -26,9 +26,6 @@ static mut VALUES: [usize; 2] = [0, 0]; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - KEY.store(KEY_SENTVAL, Relaxed); - unsafe { let mut a = 0; let mut b = 0; diff --git a/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.rs b/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.rs new file mode 100644 index 0000000000000..7601b354b1c00 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.rs @@ -0,0 +1,41 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that we can read the value of a non-atomic store atomically and an of an atomic value non-atomically. + +#![no_main] + +use std::sync::atomic::Ordering::*; +use std::sync::atomic::{AtomicI8, AtomicU64}; + +static X: AtomicU64 = AtomicU64::new(1234); +static Y: AtomicI8 = AtomicI8::new(0xB); + +fn assert_equals(a: T, b: T) { + if a != b { + std::process::abort(); + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // 8 byte unsigned integer: + // Read initial value. + assert_equals(1234, X.load(Relaxed)); + // Atomic store, non-atomic load. + X.store(0xFFFF, Relaxed); + assert_equals(0xFFFF, unsafe { *X.as_ptr() }); + // Non-atomic store, atomic load. + unsafe { *X.as_ptr() = 0xAAAA }; + assert_equals(0xAAAA, X.load(Relaxed)); + + // 1 byte signed integer: + // Read initial value. + assert_equals(0xB, Y.load(Relaxed)); + // Atomic store, non-atomic load. + Y.store(42, Relaxed); + assert_equals(42, unsafe { *Y.as_ptr() }); + // Non-atomic store, atomic load. + unsafe { *Y.as_ptr() = -42 }; + assert_equals(-42, Y.load(Relaxed)); + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.stderr b/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.stderr new file mode 100644 index 0000000000000..7867be2dbe8ed --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/mixed_atomic_non_atomic.stderr @@ -0,0 +1,2 @@ +Running GenMC Verification... +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.rs b/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.rs new file mode 100644 index 0000000000000..18e039fdd0dfe --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.rs @@ -0,0 +1,41 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test that we can read the initial value of global, heap and stack allocations in GenMC mode. + +#![no_main] + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +static X: AtomicU64 = AtomicU64::new(1234); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // Read initial value of global allocation. + if 1234 != unsafe { *X.as_ptr() } { + std::process::abort(); + } + if 1234 != X.load(SeqCst) { + std::process::abort(); + } + + // Read initial value of stack allocation. + let a = AtomicU64::new(0xBB); + if 0xBB != unsafe { *a.as_ptr() } { + std::process::abort(); + } + if 0xBB != a.load(SeqCst) { + std::process::abort(); + } + + // Read initial value of heap allocation. + let b = Box::new(AtomicU64::new(0xCCC)); + if 0xCCC != unsafe { *b.as_ptr() } { + std::process::abort(); + } + if 0xCCC != b.load(SeqCst) { + std::process::abort(); + } + + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.stderr b/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.stderr new file mode 100644 index 0000000000000..7867be2dbe8ed --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/read_initial_value.stderr @@ -0,0 +1,2 @@ +Running GenMC Verification... +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs index f48466e8ce10c..7e6e33c8a7b1c 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs +++ b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs @@ -21,11 +21,8 @@ fn assert_eq(x: T, y: T) { macro_rules! test_rmw_edge_cases { ($int:ty, $atomic:ty) => {{ - let x = <$atomic>::new(123); - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - x.store(123, ORD); - // MAX, ADD + let x = <$atomic>::new(123); assert_eq(123, x.fetch_max(0, ORD)); // `max` keeps existing value assert_eq(123, x.fetch_max(<$int>::MAX, ORD)); // `max` stores the new value assert_eq(<$int>::MAX, x.fetch_add(10, ORD)); // `fetch_add` should be wrapping diff --git a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs index fda517ae7d231..934fc977366dc 100644 --- a/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs +++ b/src/tools/miri/tests/genmc/pass/data-structures/ms_queue_dynamic.rs @@ -70,11 +70,6 @@ impl MyStack { (*dummy).next = AtomicPtr::new(std::ptr::null_mut()); self.head = AtomicPtr::new(dummy); self.tail = AtomicPtr::new(dummy); - - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - (*dummy).next.store(std::ptr::null_mut(), Relaxed); - self.head.store(dummy, Relaxed); - self.tail.store(dummy, Relaxed); } pub unsafe fn clear_queue(&mut self, _num_threads: usize) { @@ -93,9 +88,6 @@ impl MyStack { (*node).value = value; (*node).next = AtomicPtr::new(std::ptr::null_mut()); - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - (*node).next.store(std::ptr::null_mut(), Relaxed); - loop { tail = self.tail.load(Acquire); let next = (*tail).next.load(Acquire); diff --git a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs index 2101f9ae66bb6..8bdd2a371f51c 100644 --- a/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs +++ b/src/tools/miri/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs @@ -104,9 +104,6 @@ impl MyStack { #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - unsafe { STACK.top.store(std::ptr::null_mut(), Relaxed) }; - // We try multiple different parameters for the number and types of threads: let (readers, writers) = if cfg!(any(default_R1W3, spinloop_assume_R1W3)) { (1, 3) diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs index 9d7c823b1e485..cf19e92994421 100644 --- a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs +++ b/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs @@ -58,10 +58,6 @@ fn spin_until(value: u64) { #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - unsafe { X = 0 }; - FLAG.store(0, Relaxed); - unsafe { let t0 = || { X = 42; diff --git a/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs b/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs index d3fdb470ed367..d9b582bb4362d 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs @@ -17,10 +17,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs index 1ec62ff21342d..6d2dfd4f273e2 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs @@ -17,10 +17,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs index fa717b4cf18a8..6f1d37962d10a 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs @@ -28,10 +28,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut results = [1234; 5]; let ids = [ diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB.rs b/src/tools/miri/tests/genmc/pass/litmus/LB.rs index 1cee3230b127c..107121ef4e3c7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/LB.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/LB.rs @@ -17,10 +17,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP.rs b/src/tools/miri/tests/genmc/pass/litmus/MP.rs index e245cdd15eef2..5f9d1b01c37b0 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MP.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/MP.rs @@ -17,10 +17,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs index a1b13e3f3c56f..6f812bf8a8ac9 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs @@ -22,10 +22,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = Ok(1234); let mut b = Ok(1234); diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs index b36c8a288f426..4f20b2cf9def2 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs @@ -18,10 +18,6 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { spawn_pthread_closure(|| { X.store(1, Relaxed); diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs index cf9f5f2dbfa34..19065d3308f8c 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs @@ -18,10 +18,6 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { spawn_pthread_closure(|| { X.store(1, Relaxed); diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB.rs b/src/tools/miri/tests/genmc/pass/litmus/SB.rs index e592fe05c4e49..74d45c22a2953 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/SB.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/SB.rs @@ -17,10 +17,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs index a991ccb0d4e04..cbbaa82d6fb5a 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs @@ -31,10 +31,6 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/casdep.rs b/src/tools/miri/tests/genmc/pass/litmus/casdep.rs index c376d96b111cc..8b8f6e793c1f7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/casdep.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/casdep.rs @@ -18,11 +18,6 @@ static Z: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - Z.store(0, Relaxed); - unsafe { spawn_pthread_closure(|| { let a = X.load(Relaxed); diff --git a/src/tools/miri/tests/genmc/pass/litmus/ccr.rs b/src/tools/miri/tests/genmc/pass/litmus/ccr.rs index 865895b4dcd96..4537f3d6830ce 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/ccr.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/ccr.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { spawn_pthread_closure(|| { let expected = 0; diff --git a/src/tools/miri/tests/genmc/pass/litmus/cii.rs b/src/tools/miri/tests/genmc/pass/litmus/cii.rs index 663c806e667c4..18f56860f9604 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/cii.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/cii.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { spawn_pthread_closure(|| { let expected = 1; diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr.rs b/src/tools/miri/tests/genmc/pass/litmus/corr.rs index d6c95100fc241..b586e2e0fa8a8 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/corr.rs @@ -6,7 +6,6 @@ #[path = "../../../utils/genmc.rs"] mod genmc; - use std::sync::atomic::AtomicU64; use std::sync::atomic::Ordering::*; @@ -16,9 +15,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr0.rs b/src/tools/miri/tests/genmc/pass/litmus/corr0.rs index f722131fda846..856d566ca8bd3 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr0.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/corr0.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr1.rs b/src/tools/miri/tests/genmc/pass/litmus/corr1.rs index a4e8249bac309..ccd849802911e 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr1.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/corr1.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr2.rs b/src/tools/miri/tests/genmc/pass/litmus/corr2.rs index 2f490d3637797..36616bf36371f 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr2.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/corr2.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/corw.rs b/src/tools/miri/tests/genmc/pass/litmus/corw.rs index 7acc20822a4f4..9216a4f8368f6 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corw.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/corw.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let ids = [ diff --git a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs index 2387976a8ca61..4034f7634e870 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs @@ -18,11 +18,6 @@ static Z: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - Z.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/default.rs b/src/tools/miri/tests/genmc/pass/litmus/default.rs index 55fb1ac34acb4..0ab26dce419ad 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/default.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/default.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut a = 1234; let mut b = 1234; diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.rs b/src/tools/miri/tests/genmc/pass/litmus/detour.rs index 7136c029bbb54..85c456d5c54e5 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/detour.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.rs @@ -22,11 +22,6 @@ static Z: AtomicI64 = AtomicI64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - Y.store(0, Relaxed); - Z.store(0, Relaxed); - unsafe { // Make these static so we can exit the main thread while the other threads still run. // If these are `let mut` like the other tests, this will cause a use-after-free bug. diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs index 796ffbf97f96b..c8d3d409cf04d 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let mut result = [1234; 4]; let ids = [ diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs b/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs index fd8574c4f7c78..eb84304a1986e 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs @@ -16,9 +16,6 @@ static X: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(0, Relaxed); - unsafe { let ids = [ spawn_pthread_closure(|| { diff --git a/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr new file mode 100644 index 0000000000000..f2c574283cbf0 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr @@ -0,0 +1,163 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU64::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::option::Option::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::::downgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let weak = Arc::downgrade(&data_clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::::downgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let weak = Arc::downgrade(&data_clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +... | +LL | | }); + | |______^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +... | +LL | | }); + | |______^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::>::is_unique` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::sync::Arc::>::get_mut` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | handle.join().unwrap(); + | ^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU32::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::sync::PLATFORM::futex::Once::call` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::Once::call_once::<{closure@std::rt::cleanup::{closure#0}}>` at RUSTLIB/std/src/sync/poison/once.rs:LL:CC + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchg::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange::<*mut i32>` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicPtr::::compare_exchange` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/arc.rs b/src/tools/miri/tests/genmc/pass/std/arc.rs new file mode 100644 index 0000000000000..addf6408c006f --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/arc.rs @@ -0,0 +1,49 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: check_count try_upgrade + +// Check that various operations on `std::sync::Arc` are handled properly in GenMC mode. +// +// The number of explored executions in the expected output of this test may change if +// the implementation of `Arc` is ever changed, or additional optimizations are added to GenMC mode. +// +// The revision that tries to upgrade the `Weak` should never explore fewer executions compared to the revision that just accesses the `strong_count`, +// since `upgrade` needs to access the `strong_count` internally. +// There should always be more than 1 execution for both, since there is always the possibilility that the `Arc` has already been dropped, or it hasn't. + +use std::sync::Arc; + +fn main() { + let data = Arc::new(42); + + // Clone the Arc, drop the original, check that memory still valid. + let data_clone = Arc::clone(&data); + drop(data); + assert!(*data_clone == 42); + + // Create a Weak reference. + let weak = Arc::downgrade(&data_clone); + + // Spawn a thread that uses the Arc. + let weak_ = weak.clone(); + let handle = std::thread::spawn(move || { + // Try to upgrade weak reference. + // Depending on execution schedule, this may fail or succeed depending on whether this runs before or after the `drop` in the main thread. + + #[cfg(check_count)] + let _strong_count = weak_.strong_count(); + + #[cfg(try_upgrade)] + if let Some(strong) = weak_.upgrade() { + assert_eq!(*strong, 42); + } + }); + + // Drop the last strong reference to the data. + drop(data_clone); + + // Wait for the thread to finish. + handle.join().unwrap(); + + // The upgrade should fail now (all Arcs dropped). + assert!(weak.upgrade().is_none()); +} diff --git a/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr new file mode 100644 index 0000000000000..a7dacdeba4e45 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr @@ -0,0 +1,191 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU64::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::option::Option::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::::downgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let weak = Arc::downgrade(&data_clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::::downgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let weak = Arc::downgrade(&data_clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +... | +LL | | }); + | |______^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +... | +LL | | }); + | |______^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::>::is_unique` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::sync::Arc::>::get_mut` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | handle.join().unwrap(); + | ^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU32::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::sync::PLATFORM::futex::Once::call` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::Once::call_once::<{closure@std::rt::cleanup::{closure#0}}>` at RUSTLIB/std/src/sync/poison/once.rs:LL:CC + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchg::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange::<*mut i32>` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicPtr::::compare_exchange` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if self.inner()?.strong.fetch_update(Acquire, Relaxed, checked_increment).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside `std::sync::Weak::::upgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | if let Some(strong) = weak_.upgrade() { + | ^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if self.inner()?.strong.fetch_update(Acquire, Relaxed, checked_increment).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside `std::sync::Weak::::upgrade` at RUSTLIB/alloc/src/sync.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/arc.rs:LL:CC + | +LL | if let Some(strong) = weak_.upgrade() { + | ^^^^^^^^^^^^^^^ + +Verification complete with 7 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/empty_main.rs b/src/tools/miri/tests/genmc/pass/std/empty_main.rs new file mode 100644 index 0000000000000..2ffc3388fb36c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/empty_main.rs @@ -0,0 +1,5 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// A lot of code runs before main, which we should be able to handle in GenMC mode. + +fn main() {} diff --git a/src/tools/miri/tests/genmc/pass/std/empty_main.stderr b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr new file mode 100644 index 0000000000000..36ba5481f472c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr @@ -0,0 +1,60 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU64::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::option::Option::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU32::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::sync::PLATFORM::futex::Once::call` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::Once::call_once::<{closure@std::rt::cleanup::{closure#0}}>` at RUSTLIB/std/src/sync/poison/once.rs:LL:CC + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchg::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange::<*mut i32>` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicPtr::::compare_exchange` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs new file mode 100644 index 0000000000000..dadbee47b9860 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs @@ -0,0 +1,13 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// We should be able to spawn and join standard library threads in GenMC mode. +// Since these threads do nothing, we should only explore 1 program execution. + +const N: usize = 2; + +fn main() { + let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + handles.into_iter().for_each(|handle| handle.join().unwrap()); +} + +fn thread_func() {} diff --git a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr new file mode 100644 index 0000000000000..d6195b4e9b212 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr @@ -0,0 +1,167 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU64::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::option::Option::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: inside closure at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC + = note: inside ` as std::iter::Iterator>::fold::<(), {closure@std::iter::adapters::map::map_fold, (), {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}, {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::for_each::<{closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside `std::vec::Vec::>::extend_trusted::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC + = note: inside `> as std::vec::spec_extend::SpecExtend, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::spec_extend` at RUSTLIB/alloc/src/vec/spec_extend.rs:LL:CC + = note: inside `> as std::vec::spec_from_iter_nested::SpecFromIterNested, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter_nested.rs:LL:CC + = note: inside `> as std::vec::spec_from_iter::SpecFromIter, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter.rs:LL:CC + = note: inside `> as std::iter::FromIterator>>::from_iter::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::collect::>>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: inside closure at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC + = note: inside ` as std::iter::Iterator>::fold::<(), {closure@std::iter::adapters::map::map_fold, (), {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}, {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::for_each::<{closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside `std::vec::Vec::>::extend_trusted::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC + = note: inside `> as std::vec::spec_extend::SpecExtend, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::spec_extend` at RUSTLIB/alloc/src/vec/spec_extend.rs:LL:CC + = note: inside `> as std::vec::spec_from_iter_nested::SpecFromIterNested, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter_nested.rs:LL:CC + = note: inside `> as std::vec::spec_from_iter::SpecFromIter, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter.rs:LL:CC + = note: inside `> as std::iter::FromIterator>>::from_iter::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC + = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::collect::>>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::>::is_unique` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::sync::Arc::>::get_mut` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); + | ^^^^^^^^^^^^^ + = note: inside closure at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside `> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>::{closure#0}}>` at RUSTLIB/alloc/src/vec/into_iter.rs:LL:CC + = note: inside `> as std::iter::Iterator>::for_each::<{closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC + | +LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU32::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::sync::PLATFORM::futex::Once::call` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::Once::call_once::<{closure@std::rt::cleanup::{closure#0}}>` at RUSTLIB/std/src/sync/poison/once.rs:LL:CC + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchg::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange::<*mut i32>` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicPtr::::compare_exchange` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/thread/thread_locals.rs b/src/tools/miri/tests/genmc/pass/std/thread_locals.rs similarity index 53% rename from src/tools/miri/tests/genmc/pass/thread/thread_locals.rs rename to src/tools/miri/tests/genmc/pass/std/thread_locals.rs index cabc8ec92da1c..d76975d2e92c2 100644 --- a/src/tools/miri/tests/genmc/pass/thread/thread_locals.rs +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.rs @@ -1,17 +1,10 @@ //@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - use std::alloc::{Layout, alloc}; use std::cell::Cell; use std::sync::atomic::AtomicPtr; use std::sync::atomic::Ordering::*; -use crate::genmc::*; - static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); thread_local! { @@ -22,26 +15,21 @@ pub unsafe fn malloc() -> *mut u64 { alloc(Layout::new::()) as *mut u64 } -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. - X.store(std::ptr::null_mut(), SeqCst); - - unsafe { - spawn_pthread_closure(|| { - R.set(malloc()); +fn main() { + let handles = [ + std::thread::spawn(|| { + R.set(unsafe { malloc() }); let r_ptr = R.get(); let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); - }); - spawn_pthread_closure(|| { - R.set(malloc()); - }); - spawn_pthread_closure(|| { - R.set(malloc()); + }), + std::thread::spawn(|| { + R.set(unsafe { malloc() }); + }), + std::thread::spawn(|| { + R.set(unsafe { malloc() }); let r_ptr = R.get(); let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); - }); - - 0 - } + }), + ]; + handles.into_iter().for_each(|handle| handle.join().unwrap()); } diff --git a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr new file mode 100644 index 0000000000000..ff971301bc66a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr @@ -0,0 +1,184 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU64::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::option::Option::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | / std::thread::spawn(|| { +LL | | R.set(unsafe { malloc() }); +LL | | let r_ptr = R.get(); +LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); +LL | | }), + | |__________^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | / std::thread::spawn(|| { +LL | | R.set(unsafe { malloc() }); +LL | | let r_ptr = R.get(); +LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); +LL | | }), + | |__________^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/thread/mod.rs:LL:CC + | +LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | / std::thread::spawn(|| { +LL | | R.set(unsafe { malloc() }); +LL | | }), + | |__________^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/thread/mod.rs:LL:CC + | +LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | / std::thread::spawn(|| { +LL | | R.set(unsafe { malloc() }); +LL | | let r_ptr = R.get(); +LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); +LL | | }), + | |__________^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/alloc/src/sync.rs:LL:CC + | +LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::Arc::>::is_unique` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::sync::Arc::>::get_mut` at RUSTLIB/alloc/src/sync.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside closure + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); + | ^^^^^^^^^^^^^ + = note: inside closure at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC + = note: inside closure at RUSTLIB/core/src/ops/try_trait.rs:LL:CC + = note: inside closure at RUSTLIB/core/src/array/iter/iter_inner.rs:LL:CC + = note: inside `::try_fold::<(), {closure@std::array::iter::iter_inner::PolymorphicIter<[std::mem::MaybeUninit>]>::try_fold<(), {closure@std::ops::try_trait::NeverShortCircuit<()>::wrap_mut_2<(), std::thread::JoinHandle<()>, {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>::{closure#0}}, std::ops::try_trait::NeverShortCircuit<()>>::{closure#0}}, std::ops::try_trait::NeverShortCircuit<()>>` at RUSTLIB/core/src/ops/index_range.rs:LL:CC + = note: inside `std::array::iter::iter_inner::PolymorphicIter::<[std::mem::MaybeUninit>]>::try_fold::<(), {closure@std::ops::try_trait::NeverShortCircuit<()>::wrap_mut_2<(), std::thread::JoinHandle<()>, {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>::{closure#0}}, std::ops::try_trait::NeverShortCircuit<()>>` at RUSTLIB/core/src/array/iter/iter_inner.rs:LL:CC + = note: inside `std::array::iter::iter_inner::PolymorphicIter::<[std::mem::MaybeUninit>]>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>` at RUSTLIB/core/src/array/iter/iter_inner.rs:LL:CC + = note: inside `, 3> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>` at RUSTLIB/core/src/array/iter.rs:LL:CC + = note: inside `, 3> as std::iter::Iterator>::for_each::<{closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC +note: inside `main` + --> tests/genmc/pass/std/thread_locals.rs:LL:CC + | +LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchgweak::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange_weak::` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicU32::compare_exchange_weak` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::sync::PLATFORM::futex::Once::call` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::Once::call_once::<{closure@std::rt::cleanup::{closure#0}}>` at RUSTLIB/std/src/sync/poison/once.rs:LL:CC + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/core/src/sync/atomic.rs:LL:CC + | +LL | intrinsics::atomic_cxchg::(dst, old, new) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sync::atomic::atomic_compare_exchange::<*mut i32>` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sync::atomic::AtomicPtr::::compare_exchange` at RUSTLIB/core/src/sync/atomic.rs:LL:CC + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +Verification complete with 2 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr deleted file mode 100644 index bde951866d013..0000000000000 --- a/src/tools/miri/tests/genmc/pass/thread/thread_locals.stderr +++ /dev/null @@ -1,2 +0,0 @@ -Running GenMC Verification... -Verification complete with 2 executions. No errors found. From 38d129eeb4162c5b896c56ae68d5a8c80d690d65 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 2 Oct 2025 09:13:29 +0200 Subject: [PATCH 13/26] rustup --- src/tools/miri/rust-version | 2 +- src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr | 2 +- src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr | 2 +- src/tools/miri/tests/genmc/pass/std/empty_main.stderr | 2 +- src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr | 2 +- src/tools/miri/tests/genmc/pass/std/thread_locals.stderr | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 70374af27e9c9..4e388e04ea44f 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -29b7717de23f3969ceeb5bef5b01d9223f807655 +42b384ec0dfcd528d99a4db0a337d9188a9eecaa diff --git a/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr index f2c574283cbf0..3dccd7059538e 100644 --- a/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr +++ b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr @@ -12,7 +12,7 @@ LL | intrinsics::atomic_cxchgweak::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr index a7dacdeba4e45..dc59632558c8b 100644 --- a/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr +++ b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr @@ -12,7 +12,7 @@ LL | intrinsics::atomic_cxchgweak::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/std/empty_main.stderr b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr index 36ba5481f472c..44c307a6b3e41 100644 --- a/src/tools/miri/tests/genmc/pass/std/empty_main.stderr +++ b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr @@ -12,7 +12,7 @@ LL | intrinsics::atomic_cxchgweak::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr index d6195b4e9b212..22a58f4e9cef0 100644 --- a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr +++ b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr @@ -12,7 +12,7 @@ LL | intrinsics::atomic_cxchgweak::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr index ff971301bc66a..e9b780fc007f5 100644 --- a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr @@ -12,7 +12,7 @@ LL | intrinsics::atomic_cxchgweak::::unwrap_or_else::<{closure@std::thread::current::id::get_or_init::{closure#0}}>` at RUSTLIB/core/src/option.rs:LL:CC = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC From a5d16293706cdf02ea275e976ce9f6c3a20c3d3f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 2 Oct 2025 09:25:19 +0200 Subject: [PATCH 14/26] make cur_span and current_span harder to mix up --- .../borrow_tracker/stacked_borrows/diagnostics.rs | 8 ++++---- .../miri/src/borrow_tracker/tree_borrows/mod.rs | 12 ++++++------ src/tools/miri/src/concurrency/data_race.rs | 14 +++++++------- src/tools/miri/src/concurrency/genmc/helper.rs | 6 +++++- src/tools/miri/src/concurrency/thread.rs | 4 ++-- src/tools/miri/src/helpers.rs | 4 ++-- src/tools/miri/src/machine.rs | 6 +++--- 7 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs index 63b18028a5b9d..997d7799a5f1c 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -221,7 +221,7 @@ impl AllocHistory { pub fn new(id: AllocId, item: Item, machine: &MiriMachine<'_>) -> Self { Self { id, - root: (item, machine.current_span()), + root: (item, machine.current_user_relevant_span()), creations: SmallVec::new(), invalidations: SmallVec::new(), protectors: SmallVec::new(), @@ -269,11 +269,11 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { }; self.history .creations - .push(Creation { retag: op.clone(), span: self.machine.current_span() }); + .push(Creation { retag: op.clone(), span: self.machine.current_user_relevant_span() }); } pub fn log_invalidation(&mut self, tag: BorTag) { - let mut span = self.machine.current_span(); + let mut span = self.machine.current_user_relevant_span(); let (range, cause) = match &self.operation { Operation::Retag(RetagOp { info, range, permission, .. }) => { if info.cause == RetagCause::FnEntry { @@ -298,7 +298,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { }; self.history .protectors - .push(Protection { tag: op.new_tag, span: self.machine.current_span() }); + .push(Protection { tag: op.new_tag, span: self.machine.current_user_relevant_span() }); } pub fn get_logs_relevant_to( diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 6e5b5c807aa22..865097af10181 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -33,7 +33,7 @@ impl<'tcx> Tree { machine: &MiriMachine<'tcx>, ) -> Self { let tag = state.root_ptr_tag(id, machine); // Fresh tag for the root - let span = machine.current_span(); + let span = machine.current_user_relevant_span(); Tree::new(tag, size, span) } @@ -61,7 +61,7 @@ impl<'tcx> Tree { ProvenanceExtra::Wildcard => return interp_ok(()), }; let global = machine.borrow_tracker.as_ref().unwrap(); - let span = machine.current_span(); + let span = machine.current_user_relevant_span(); self.perform_access( tag, Some((range, access_kind, diagnostics::AccessCause::Explicit(access_kind))), @@ -86,7 +86,7 @@ impl<'tcx> Tree { ProvenanceExtra::Wildcard => return interp_ok(()), }; let global = machine.borrow_tracker.as_ref().unwrap(); - let span = machine.current_span(); + let span = machine.current_user_relevant_span(); self.dealloc(tag, alloc_range(Size::ZERO, size), global, alloc_id, span) } @@ -107,7 +107,7 @@ impl<'tcx> Tree { tag: BorTag, alloc_id: AllocId, // diagnostics ) -> InterpResult<'tcx> { - let span = machine.current_span(); + let span = machine.current_user_relevant_span(); // `None` makes it the magic on-protector-end operation self.perform_access(tag, None, global, alloc_id, span) } @@ -360,7 +360,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some((range_in_alloc, AccessKind::Read, diagnostics::AccessCause::Reborrow)), this.machine.borrow_tracker.as_ref().unwrap(), alloc_id, - this.machine.current_span(), + this.machine.current_user_relevant_span(), )?; // Also inform the data race model (but only if any bytes are actually affected). @@ -386,7 +386,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { inside_perms, new_perm.outside_perm, protected, - this.machine.current_span(), + this.machine.current_user_relevant_span(), )?; drop(tree_borrows); diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 1ad9ace1b5d1a..1f30f9d7288a2 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -1208,7 +1208,7 @@ impl VClockAlloc { ty: Option>, machine: &MiriMachine<'_>, ) -> InterpResult<'tcx> { - let current_span = machine.current_span(); + let current_span = machine.current_user_relevant_span(); let global = machine.data_race.as_vclocks_ref().unwrap(); if !global.race_detecting() { return interp_ok(()); @@ -1250,7 +1250,7 @@ impl VClockAlloc { ty: Option>, machine: &mut MiriMachine<'_>, ) -> InterpResult<'tcx> { - let current_span = machine.current_span(); + let current_span = machine.current_user_relevant_span(); let global = machine.data_race.as_vclocks_mut().unwrap(); if !global.race_detecting() { return interp_ok(()); @@ -1304,7 +1304,7 @@ impl Default for LocalClocks { impl FrameState { pub fn local_write(&self, local: mir::Local, storage_live: bool, machine: &MiriMachine<'_>) { - let current_span = machine.current_span(); + let current_span = machine.current_user_relevant_span(); let global = machine.data_race.as_vclocks_ref().unwrap(); if !global.race_detecting() { return; @@ -1334,7 +1334,7 @@ impl FrameState { } pub fn local_read(&self, local: mir::Local, machine: &MiriMachine<'_>) { - let current_span = machine.current_span(); + let current_span = machine.current_user_relevant_span(); let global = machine.data_race.as_vclocks_ref().unwrap(); if !global.race_detecting() { return; @@ -1573,7 +1573,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { size.bytes() ); - let current_span = this.machine.current_span(); + let current_span = this.machine.current_user_relevant_span(); // Perform the atomic operation. data_race.maybe_perform_sync_operation( &this.machine.threads, @@ -1827,7 +1827,7 @@ impl GlobalState { machine: &MiriMachine<'tcx>, atomic: AtomicFenceOrd, ) -> InterpResult<'tcx> { - let current_span = machine.current_span(); + let current_span = machine.current_user_relevant_span(); self.maybe_perform_sync_operation(&machine.threads, current_span, |index, mut clocks| { trace!("Atomic fence on {:?} with ordering {:?}", index, atomic); @@ -1915,7 +1915,7 @@ impl GlobalState { callback: impl FnOnce(&VClock) -> R, ) -> R { let thread = threads.active_thread(); - let span = threads.active_thread_ref().current_span(); + let span = threads.active_thread_ref().current_user_relevant_span(); let (index, mut clocks) = self.thread_state_mut(thread); let r = callback(&clocks.clock); // Increment the clock, so that all following events cannot be confused with anything that diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index ebbef23a2a54c..b79fe0e94299a 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -37,7 +37,11 @@ pub(super) fn emit_warning<'tcx>( cache: &WarningCache, diagnostic: impl FnOnce() -> NonHaltingDiagnostic, ) { - let span = ecx.machine.current_span(); + // FIXME: This is not the right span to use (it's always inside the local crates so if the same + // operation is invoked from multiple places it will warn multiple times). `cur_span` is not + // right either though (we should honor `#[track_caller]`). Ultimately what we want is "the + // primary span the warning would point at". + let span = ecx.machine.current_user_relevant_span(); if cache.read().unwrap().contains(&span) { return; } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 246300f8dda15..0d9dbe052e399 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -263,7 +263,7 @@ impl<'tcx> Thread<'tcx> { self.top_user_relevant_frame.or_else(|| self.stack.len().checked_sub(1)) } - pub fn current_span(&self) -> Span { + pub fn current_user_relevant_span(&self) -> Span { self.top_user_relevant_frame() .map(|frame_idx| self.stack[frame_idx].current_span()) .unwrap_or(rustc_span::DUMMY_SP) @@ -867,7 +867,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut state = tls::TlsDtorsState::default(); Box::new(move |m| state.on_stack_empty(m)) }); - let current_span = this.machine.current_span(); + let current_span = this.machine.current_user_relevant_span(); match &mut this.machine.data_race { GlobalDataRaceHandler::None => {} GlobalDataRaceHandler::Vclocks(data_race) => diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index e0c077e99319a..3f736435bbc49 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -1128,8 +1128,8 @@ impl<'tcx> MiriMachine<'tcx> { /// `#[track_caller]`. /// This function is backed by a cache, and can be assumed to be very fast. /// It will work even when the stack is empty. - pub fn current_span(&self) -> Span { - self.threads.active_thread_ref().current_span() + pub fn current_user_relevant_span(&self) -> Span { + self.threads.active_thread_ref().current_user_relevant_span() } /// Returns the span of the *caller* of the current operation, again diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 53b8fea979c78..a1e63f6ae4ece 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -919,7 +919,7 @@ impl<'tcx> MiriMachine<'tcx> { &ecx.machine.threads, size, kind, - ecx.machine.current_span(), + ecx.machine.current_user_relevant_span(), ), data_race.weak_memory.then(weak_memory::AllocState::new_allocation), ), @@ -943,7 +943,7 @@ impl<'tcx> MiriMachine<'tcx> { ecx.machine .allocation_spans .borrow_mut() - .insert(id, (ecx.machine.current_span(), None)); + .insert(id, (ecx.machine.current_user_relevant_span(), None)); } interp_ok(AllocExtra { borrow_tracker, data_race, backtrace, sync: FxHashMap::default() }) @@ -1566,7 +1566,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id) { - *deallocated_at = Some(machine.current_span()); + *deallocated_at = Some(machine.current_user_relevant_span()); } machine.free_alloc_id(alloc_id, size, align, kind); interp_ok(()) From 8583e1582e3a2ce7e165111c9208be0224174381 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 3 Oct 2025 04:52:46 +0000 Subject: [PATCH 15/26] Prepare for merging from rust-lang/rust This updates the rust-version file to 3b8665c5ab3aeced9b01672404c3764583e722ca. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 4e388e04ea44f..77436ae2f8259 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -42b384ec0dfcd528d99a4db0a337d9188a9eecaa +3b8665c5ab3aeced9b01672404c3764583e722ca From f28b8d4f15f6e0f2f14c994911e2b2ec7850fd01 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 Oct 2025 15:37:04 +0200 Subject: [PATCH 16/26] deduplicate warnings globally --- src/tools/miri/src/alloc_addresses/mod.rs | 11 ++-- .../src/borrow_tracker/stacked_borrows/mod.rs | 4 +- .../miri/src/concurrency/genmc/helper.rs | 41 +------------ src/tools/miri/src/concurrency/genmc/mod.rs | 61 ++++++++----------- src/tools/miri/src/concurrency/genmc/run.rs | 10 +-- src/tools/miri/src/diagnostics.rs | 45 +++++++++++++- src/tools/miri/src/helpers.rs | 7 ++- src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/machine.rs | 18 ------ src/tools/miri/src/shims/native_lib/mod.rs | 5 +- .../tests/genmc/pass/std/thread_locals.stderr | 34 ----------- 11 files changed, 88 insertions(+), 149 deletions(-) diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index ecaa80ebb4da2..05d3444a4eb1a 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::TyCtxt; pub use self::address_generator::AddressGenerator; use self::reuse_pool::ReusePool; use crate::concurrency::VClock; +use crate::diagnostics::SpanDedupDiagnostic; use crate::*; #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -341,12 +342,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match global_state.provenance_mode { ProvenanceMode::Default => { // The first time this happens at a particular location, print a warning. - let mut int2ptr_warned = this.machine.int2ptr_warned.borrow_mut(); - let first = int2ptr_warned.is_empty(); - if int2ptr_warned.insert(this.cur_span()) { - // Newly inserted, so first time we see this span. - this.emit_diagnostic(NonHaltingDiagnostic::Int2Ptr { details: first }); - } + static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); + this.dedup_diagnostic(&DEDUP, |first| { + NonHaltingDiagnostic::Int2Ptr { details: first } + }); } ProvenanceMode::Strict => { throw_machine_stop!(TerminationInfo::Int2PtrWithStrictProvenance); diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 5fe00ab02c4b9..127d832f73e79 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -6,6 +6,7 @@ mod item; mod stack; use std::fmt::Write; +use std::sync::atomic::AtomicBool; use std::{cmp, mem}; use rustc_abi::{BackendRepr, Size}; @@ -822,7 +823,8 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { let size = match size { Some(size) => size, None => { - if !this.machine.sb_extern_type_warned.replace(true) { + static DEDUP: AtomicBool = AtomicBool::new(false); + if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow); } return interp_ok(place.clone()); diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index b79fe0e94299a..b2e4b5aea5346 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -1,58 +1,19 @@ -use std::sync::RwLock; - use genmc_sys::{MemOrdering, RMWBinOp}; use rustc_abi::Size; use rustc_const_eval::interpret::{InterpResult, interp_ok}; -use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; use rustc_middle::mir::interpret; use rustc_middle::ty::ScalarInt; -use rustc_span::Span; use tracing::debug; use super::GenmcScalar; use crate::alloc_addresses::EvalContextExt as _; -use crate::diagnostics::EvalContextExt as _; use crate::intrinsics::AtomicRmwOp; -use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, BorTag, GenmcCtx, InterpCx, - MiriInterpCx, MiriMachine, NonHaltingDiagnostic, Scalar, machine, throw_unsup_format, -}; +use crate::*; /// Maximum size memory access in bytes that GenMC supports. pub(super) const MAX_ACCESS_SIZE: u64 = 8; -/// Type for storing spans for already emitted warnings. -pub(super) type WarningCache = RwLock>; - -#[derive(Default)] -pub(super) struct Warnings { - pub(super) compare_exchange_failure_ordering: WarningCache, - pub(super) compare_exchange_weak: WarningCache, -} - -/// Emit a warning if it hasn't already been reported for current span. -pub(super) fn emit_warning<'tcx>( - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - cache: &WarningCache, - diagnostic: impl FnOnce() -> NonHaltingDiagnostic, -) { - // FIXME: This is not the right span to use (it's always inside the local crates so if the same - // operation is invoked from multiple places it will warn multiple times). `cur_span` is not - // right either though (we should honor `#[track_caller]`). Ultimately what we want is "the - // primary span the warning would point at". - let span = ecx.machine.current_user_relevant_span(); - if cache.read().unwrap().contains(&span) { - return; - } - // This span has not yet been reported, so we insert it into the cache and report it. - let mut cache = cache.write().unwrap(); - if cache.insert(span) { - // Some other thread may have added this span while we didn't hold the lock, so we only emit it if the insertions succeeded. - ecx.emit_diagnostic(diagnostic()); - } -} - /// This function is used to split up a large memory access into aligned, non-overlapping chunks of a limited size. /// Returns an iterator over the chunks, yielding `(base address, size)` of each chunk, ordered by address. pub fn split_access(address: Size, size: Size) -> impl Iterator { diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index b2f8ab6f972eb..caec5e921cdfe 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -14,18 +14,15 @@ use tracing::{debug, info}; use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; use self::helper::{ - MAX_ACCESS_SIZE, Warnings, emit_warning, genmc_scalar_to_scalar, - maybe_upgrade_compare_exchange_success_orderings, scalar_to_genmc_scalar, to_genmc_rmw_op, + MAX_ACCESS_SIZE, genmc_scalar_to_scalar, maybe_upgrade_compare_exchange_success_orderings, + scalar_to_genmc_scalar, to_genmc_rmw_op, }; use self::run::GenmcMode; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; +use crate::diagnostics::SpanDedupDiagnostic; use crate::intrinsics::AtomicRmwOp; -use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, MiriMemoryKind, NonHaltingDiagnostic, Scalar, TerminationInfo, ThreadId, - ThreadManager, VisitProvenance, VisitWith, -}; +use crate::*; mod config; mod global_allocations; @@ -104,18 +101,11 @@ struct GlobalState { /// Keep track of global allocations, to ensure they keep the same address across different executions, even if the order of allocations changes. /// The `AllocId` for globals is stable across executions, so we can use it as an identifier. global_allocations: GlobalAllocationHandler, - - /// Cache for which warnings have already been shown to the user. - /// `None` if warnings are disabled. - warning_cache: Option, } impl GlobalState { - fn new(target_usize_max: u64, print_warnings: bool) -> Self { - Self { - global_allocations: GlobalAllocationHandler::new(target_usize_max), - warning_cache: print_warnings.then(Default::default), - } + fn new(target_usize_max: u64) -> Self { + Self { global_allocations: GlobalAllocationHandler::new(target_usize_max) } } } @@ -412,27 +402,24 @@ impl GenmcCtx { let upgraded_success_ordering = maybe_upgrade_compare_exchange_success_orderings(success, fail); - if let Some(warning_cache) = &self.global_state.warning_cache { - // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. - let (effective_failure_ordering, _) = - upgraded_success_ordering.split_memory_orderings(); - // Return a warning if the actual orderings don't match the upgraded ones. - if success != upgraded_success_ordering || effective_failure_ordering != fail { - emit_warning(ecx, &warning_cache.compare_exchange_failure_ordering, || { - NonHaltingDiagnostic::GenmcCompareExchangeOrderingMismatch { - success_ordering: success, - upgraded_success_ordering, - failure_ordering: fail, - effective_failure_ordering, - } - }); - } - // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`. - if can_fail_spuriously { - emit_warning(ecx, &warning_cache.compare_exchange_weak, || { - NonHaltingDiagnostic::GenmcCompareExchangeWeak - }); - } + // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. + let (effective_failure_ordering, _) = upgraded_success_ordering.split_memory_orderings(); + // Return a warning if the actual orderings don't match the upgraded ones. + if success != upgraded_success_ordering || effective_failure_ordering != fail { + static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); + ecx.dedup_diagnostic(&DEDUP, |_first| { + NonHaltingDiagnostic::GenmcCompareExchangeOrderingMismatch { + success_ordering: success, + upgraded_success_ordering, + failure_ordering: fail, + effective_failure_ordering, + } + }); + } + // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`. + if can_fail_spuriously { + static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); + ecx.dedup_diagnostic(&DEDUP, |_first| NonHaltingDiagnostic::GenmcCompareExchangeWeak); } debug!( diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index 33c5b6b0a005f..6721a38c683fb 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -16,13 +16,6 @@ pub(super) enum GenmcMode { Verification, } -impl GenmcMode { - /// Return whether warnings on unsupported features should be printed in this mode. - fn print_unsupported_warnings(self) -> bool { - self == GenmcMode::Verification - } -} - /// Do a complete run of the program in GenMC mode. /// This will call `eval_entry` multiple times, until either: /// - An error is detected (indicated by a `None` return value) @@ -57,8 +50,7 @@ fn run_genmc_mode_impl<'tcx>( // There exists only one `global_state` per full run in GenMC mode. // It is shared by all `GenmcCtx` in this run. // FIXME(genmc): implement multithreading once GenMC supports it. - let global_state = - Arc::new(GlobalState::new(tcx.target_usize_max(), mode.print_unsupported_warnings())); + let global_state = Arc::new(GlobalState::new(tcx.target_usize_max())); let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state, mode)); // `rep` is used to report the progress, Miri will panic on wrap-around. diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 15f7ccbabca6a..d3486dcbb19c9 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -1,9 +1,11 @@ use std::fmt::{self, Write}; use std::num::NonZero; +use std::sync::Mutex; use rustc_abi::{Align, Size}; use rustc_errors::{Diag, DiagMessage, Level}; -use rustc_span::{DUMMY_SP, SpanData, Symbol}; +use rustc_hash::FxHashSet; +use rustc_span::{DUMMY_SP, Span, SpanData, Symbol}; use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory; use crate::borrow_tracker::tree_borrows::diagnostics as tree_diagnostics; @@ -835,4 +837,45 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.machine, ); } + + /// Call `f` only if this is the first time we are seeing this span. + /// The `first` parameter indicates whether this is the first time *ever* that this diagnostic + /// is emitted. + fn dedup_diagnostic( + &self, + dedup: &SpanDedupDiagnostic, + f: impl FnOnce(/*first*/ bool) -> NonHaltingDiagnostic, + ) { + let this = self.eval_context_ref(); + // We want to deduplicate both based on where the error seems to be located "from the user + // perspective", and the location of the actual operation (to avoid warning about the same + // operation called from different places in the local code). + let span1 = this.machine.current_user_relevant_span(); + // For the "location of the operation", we still skip `track_caller` frames, to match the + // span that the diagnostic will point at. + let span2 = this + .active_thread_stack() + .iter() + .rev() + .find(|frame| !frame.instance().def.requires_caller_location(*this.tcx)) + .map(|frame| frame.current_span()) + .unwrap_or(span1); + + let mut lock = dedup.0.lock().unwrap(); + let first = lock.is_empty(); + // Avoid mutating the hashset unless both spans are new. + if !lock.contains(&span2) && lock.insert(span1) && (span1 == span2 || lock.insert(span2)) { + // Both of the two spans were newly inserted. + this.emit_diagnostic(f(first)); + } + } +} + +/// Helps deduplicate a diagnostic to ensure it is only shown once per span. +pub struct SpanDedupDiagnostic(Mutex>); + +impl SpanDedupDiagnostic { + pub const fn new() -> Self { + Self(Mutex::new(FxHashSet::with_hasher(rustc_hash::FxBuildHasher))) + } } diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 3f736435bbc49..53f235a077f8f 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -1,4 +1,5 @@ use std::num::NonZero; +use std::sync::Mutex; use std::time::Duration; use std::{cmp, iter}; @@ -6,6 +7,7 @@ use rand::RngCore; use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_hash::FxHashSet; use rustc_hir::Safety; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE}; @@ -650,7 +652,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match reject_with { RejectOpWith::Abort => isolation_abort_error(op_name), RejectOpWith::WarningWithoutBacktrace => { - let mut emitted_warnings = this.machine.reject_in_isolation_warned.borrow_mut(); + // Deduplicate these warnings *by shim* (not by span) + static DEDUP: Mutex> = + Mutex::new(FxHashSet::with_hasher(rustc_hash::FxBuildHasher)); + let mut emitted_warnings = DEDUP.lock().unwrap(); if !emitted_warnings.contains(op_name) { // First time we are seeing this. emitted_warnings.insert(op_name.to_owned()); diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 1f82f154b0b3f..ce72fc6e297dc 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -60,6 +60,7 @@ extern crate rustc_ast; extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; +extern crate rustc_hash; extern crate rustc_hir; extern crate rustc_index; extern crate rustc_middle; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index a1e63f6ae4ece..7b93d0194119a 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -649,16 +649,6 @@ pub struct MiriMachine<'tcx> { pub(crate) pthread_rwlock_sanity: Cell, pub(crate) pthread_condvar_sanity: Cell, - /// Remembers whether we already warned about an extern type with Stacked Borrows. - pub(crate) sb_extern_type_warned: Cell, - /// Remember whether we already warned about sharing memory with a native call. - #[allow(unused)] - pub(crate) native_call_mem_warned: Cell, - /// Remembers which shims have already shown the warning about erroring in isolation. - pub(crate) reject_in_isolation_warned: RefCell>, - /// Remembers which int2ptr casts we have already warned about. - pub(crate) int2ptr_warned: RefCell>, - /// Cache for `mangle_internal_symbol`. pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>, @@ -826,10 +816,6 @@ impl<'tcx> MiriMachine<'tcx> { pthread_mutex_sanity: Cell::new(false), pthread_rwlock_sanity: Cell::new(false), pthread_condvar_sanity: Cell::new(false), - sb_extern_type_warned: Cell::new(false), - native_call_mem_warned: Cell::new(false), - reject_in_isolation_warned: Default::default(), - int2ptr_warned: Default::default(), mangle_internal_symbol_cache: Default::default(), force_intrinsic_fallback: config.force_intrinsic_fallback, float_nondet: config.float_nondet, @@ -1003,10 +989,6 @@ impl VisitProvenance for MiriMachine<'_> { pthread_mutex_sanity: _, pthread_rwlock_sanity: _, pthread_condvar_sanity: _, - sb_extern_type_warned: _, - native_call_mem_warned: _, - reject_in_isolation_warned: _, - int2ptr_warned: _, mangle_internal_symbol_cache: _, force_intrinsic_fallback: _, float_nondet: _, diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index a0e871d87d3a0..914c666adb302 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -1,6 +1,7 @@ //! Implements calling functions from a native library. use std::ops::Deref; +use std::sync::atomic::AtomicBool; use libffi::low::CodePtr; use libffi::middle::Type as FfiType; @@ -279,8 +280,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Helper to print a warning when a pointer is shared with the native code. let expose = |prov: Provenance| -> InterpResult<'tcx> { - // The first time this happens, print a warning. - if !this.machine.native_call_mem_warned.replace(true) { + static DEDUP: AtomicBool = AtomicBool::new(false); + if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { // Newly set, so first time we get here. this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem { tracing }); } diff --git a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr index e9b780fc007f5..40faedf49c6e1 100644 --- a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr @@ -81,40 +81,6 @@ LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCs LL | | }), | |__________^ -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/mod.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | / std::thread::spawn(|| { -LL | | R.set(unsafe { malloc() }); -LL | | }), - | |__________^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/mod.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | / std::thread::spawn(|| { -LL | | R.set(unsafe { malloc() }); -LL | | let r_ptr = R.get(); -LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); -LL | | }), - | |__________^ - warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. --> RUSTLIB/alloc/src/sync.rs:LL:CC | From 4203fe84fb45db376436d1f7f33d68bb024d6fc1 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 12 Sep 2025 14:05:17 +0200 Subject: [PATCH 17/26] Implement std::sync::Mutex interception in GenMC mode. --- src/tools/miri/genmc-sys/build.rs | 1 + .../genmc-sys/cpp/include/MiriInterface.hpp | 31 ++- .../cpp/src/MiriInterface/EventHandling.cpp | 11 +- .../genmc-sys/cpp/src/MiriInterface/Mutex.cpp | 159 ++++++++++++ .../src/MiriInterface/ThreadManagement.cpp | 1 + src/tools/miri/genmc-sys/src/lib.rs | 45 +++- src/tools/miri/src/concurrency/genmc/dummy.rs | 9 + .../miri/src/concurrency/genmc/intercept.rs | 38 --- src/tools/miri/src/concurrency/genmc/mod.rs | 86 +++---- .../miri/src/concurrency/genmc/scheduling.rs | 14 +- src/tools/miri/src/concurrency/genmc/shims.rs | 234 ++++++++++++++++++ src/tools/miri/src/concurrency/thread.rs | 38 +-- src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/machine.rs | 15 +- .../fail/shims/mutex_diff_thread_unlock.rs | 28 +++ .../shims/mutex_diff_thread_unlock.stderr | 86 +++++++ .../genmc/fail/shims/mutex_double_unlock.rs | 22 ++ .../fail/shims/mutex_double_unlock.stderr | 23 ++ .../tests/genmc/pass/shims/mutex_deadlock.rs | 42 ++++ .../genmc/pass/shims/mutex_deadlock.stderr | 5 + .../tests/genmc/pass/shims/mutex_simple.rs | 68 +++++ .../genmc/pass/shims/mutex_simple.stderr | 3 + .../spinloop_assume.bounded123.stderr | 0 .../spinloop_assume.bounded321.stderr | 0 .../spinloop_assume.replaced123.stderr | 0 .../spinloop_assume.replaced321.stderr | 0 .../{intercept => shims}/spinloop_assume.rs | 0 27 files changed, 838 insertions(+), 122 deletions(-) create mode 100644 src/tools/miri/genmc-sys/cpp/src/MiriInterface/Mutex.cpp delete mode 100644 src/tools/miri/src/concurrency/genmc/intercept.rs create mode 100644 src/tools/miri/src/concurrency/genmc/shims.rs create mode 100644 src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.rs create mode 100644 src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr create mode 100644 src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.rs create mode 100644 src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.stderr create mode 100644 src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs create mode 100644 src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.stderr create mode 100644 src/tools/miri/tests/genmc/pass/shims/mutex_simple.rs create mode 100644 src/tools/miri/tests/genmc/pass/shims/mutex_simple.stderr rename src/tools/miri/tests/genmc/pass/{intercept => shims}/spinloop_assume.bounded123.stderr (100%) rename src/tools/miri/tests/genmc/pass/{intercept => shims}/spinloop_assume.bounded321.stderr (100%) rename src/tools/miri/tests/genmc/pass/{intercept => shims}/spinloop_assume.replaced123.stderr (100%) rename src/tools/miri/tests/genmc/pass/{intercept => shims}/spinloop_assume.replaced321.stderr (100%) rename src/tools/miri/tests/genmc/pass/{intercept => shims}/spinloop_assume.rs (100%) diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index 964382d824089..f10e151a3d0bc 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -233,6 +233,7 @@ fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) { let cpp_files = [ "MiriInterface/EventHandling.cpp", "MiriInterface/Exploration.cpp", + "MiriInterface/Mutex.cpp", "MiriInterface/Setup.cpp", "MiriInterface/ThreadManagement.cpp", ] diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index c9da718aabd61..b0bd397ab34b1 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -12,7 +12,6 @@ // GenMC headers: #include "ExecutionGraph/EventLabel.hpp" -#include "Static/ModuleID.hpp" #include "Support/MemOrdering.hpp" #include "Support/RMWOps.hpp" #include "Verification/Config.hpp" @@ -36,6 +35,7 @@ struct LoadResult; struct StoreResult; struct ReadModifyWriteResult; struct CompareExchangeResult; +struct MutexLockResult; // GenMC uses `int` for its thread IDs. using ThreadId = int; @@ -136,10 +136,13 @@ struct MiriGenmcShim : private GenMCDriver { /**** Blocking instructions ****/ /// Inform GenMC that the thread should be blocked. - /// Note: this function is currently hardcoded for `AssumeType::User`, corresponding to user - /// supplied assume statements. This can become a parameter once more types of assumes are - /// added. - void handle_assume_block(ThreadId thread_id); + void handle_assume_block(ThreadId thread_id, AssumeType assume_type); + + /**** Mutex handling ****/ + auto handle_mutex_lock(ThreadId thread_id, uint64_t address, uint64_t size) -> MutexLockResult; + auto handle_mutex_try_lock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult; + auto handle_mutex_unlock(ThreadId thread_id, uint64_t address, uint64_t size) -> StoreResult; /***** Exploration related functionality *****/ @@ -358,4 +361,22 @@ inline CompareExchangeResult from_error(std::unique_ptr error) { } } // namespace CompareExchangeResultExt +namespace MutexLockResultExt { +inline MutexLockResult ok(bool is_lock_acquired) { + return MutexLockResult { /* error: */ nullptr, /* is_reset: */ false, is_lock_acquired }; +} + +inline MutexLockResult reset() { + return MutexLockResult { /* error: */ nullptr, + /* is_reset: */ true, + /* is_lock_acquired: */ false }; +} + +inline MutexLockResult from_error(std::unique_ptr error) { + return MutexLockResult { /* error: */ std::move(error), + /* is_reset: */ false, + /* is_lock_acquired: */ false }; +} +} // namespace MutexLockResultExt + #endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index a8f8b9cc24f9e..2b6e5749d41a5 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -32,8 +32,9 @@ /**** Blocking instructions ****/ -void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { - GenMCDriver::handleAssume(inc_pos(thread_id), AssumeType::User); +void MiriGenmcShim::handle_assume_block(ThreadId thread_id, AssumeType assume_type) { + BUG_ON(getExec().getGraph().isThreadBlocked(thread_id)); + GenMCDriver::handleAssume(inc_pos(thread_id), assume_type); } /**** Memory access handling ****/ @@ -59,6 +60,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { if (const auto* err = std::get_if(&ret)) return LoadResultExt::from_error(format_error(*err)); const auto* ret_val = std::get_if(&ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. if (ret_val == nullptr) ERROR("Unimplemented: load returned unexpected result."); return LoadResultExt::from_value(*ret_val); @@ -88,6 +90,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) { return StoreResultExt::from_error(format_error(*err)); const bool* is_coherence_order_maximal_write = std::get_if(&ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. ERROR_ON( nullptr == is_coherence_order_maximal_write, "Unimplemented: Store returned unexpected result." @@ -130,6 +133,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { return ReadModifyWriteResultExt::from_error(format_error(*err)); const auto* ret_val = std::get_if(&load_ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. if (nullptr == ret_val) { ERROR("Unimplemented: read-modify-write returned unexpected result."); } @@ -151,6 +155,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { return ReadModifyWriteResultExt::from_error(format_error(*err)); const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. ERROR_ON( nullptr == is_coherence_order_maximal_write, "Unimplemented: RMW store returned unexpected result." @@ -195,6 +200,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { if (const auto* err = std::get_if(&load_ret)) return CompareExchangeResultExt::from_error(format_error(*err)); const auto* ret_val = std::get_if(&load_ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. ERROR_ON(nullptr == ret_val, "Unimplemented: load returned unexpected result."); const auto read_old_val = *ret_val; if (read_old_val != expectedVal) @@ -215,6 +221,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { if (const auto* err = std::get_if(&store_ret)) return CompareExchangeResultExt::from_error(format_error(*err)); const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); + // FIXME(genmc): handle `HandleResult::{Invalid, Reset}` return values. ERROR_ON( nullptr == is_coherence_order_maximal_write, "Unimplemented: compare-exchange store returned unexpected result." diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Mutex.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Mutex.cpp new file mode 100644 index 0000000000000..fc3f5e6e09a67 --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Mutex.cpp @@ -0,0 +1,159 @@ +/** This file contains functionality related to handling mutexes. */ + +#include "MiriInterface.hpp" + +// GenMC headers: +#include "Static/ModuleID.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +#define MUTEX_UNLOCKED SVal(0) +#define MUTEX_LOCKED SVal(1) + +auto MiriGenmcShim::handle_mutex_lock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult { + // This annotation informs GenMC about the condition required to make this lock call succeed. + // It stands for `value_read_by_load != MUTEX_LOCKED`. + const auto size_bits = size * 8; + const auto annot = std::move(Annotation( + AssumeType::Spinloop, + Annotation::ExprVP( + NeExpr::create( + // `RegisterExpr` marks the value of the current expression, i.e., the loaded value. + // The `id` is ignored by GenMC; it is only used by the LLI frontend to substitute + // other variables from previous expressions that may be used here. + RegisterExpr::create(size_bits, /* id */ 0), + ConcreteExpr::create(size_bits, MUTEX_LOCKED) + ) + .release() + ) + )); + + // As usual, we need to tell GenMC which value was stored at this location before this atomic + // access, if there previously was a non-atomic initializing access. We set the initial state of + // a mutex to be "unlocked". + const auto old_val = MUTEX_UNLOCKED; + const auto load_ret = handle_load_reset_if_none( + thread_id, + old_val, + address, + size, + annot, + EventDeps() + ); + if (const auto* err = std::get_if(&load_ret)) + return MutexLockResultExt::from_error(format_error(*err)); + // If we get a `Reset`, GenMC decided that this lock operation should not yet run, since it + // would not acquire the mutex. Like the handling of the case further down where we read a `1` + // ("Mutex already locked"), Miri should call the handle function again once the current thread + // is scheduled by GenMC the next time. + if (std::holds_alternative(load_ret)) + return MutexLockResultExt::reset(); + + const auto* ret_val = std::get_if(&load_ret); + ERROR_ON(!ret_val, "Unimplemented: mutex lock returned unexpected result."); + ERROR_ON( + *ret_val != MUTEX_UNLOCKED && *ret_val != MUTEX_LOCKED, + "Mutex read value was neither 0 nor 1" + ); + const bool is_lock_acquired = *ret_val == MUTEX_UNLOCKED; + if (is_lock_acquired) { + const auto store_ret = GenMCDriver::handleStore( + inc_pos(thread_id), + old_val, + address, + size, + EventDeps() + ); + if (const auto* err = std::get_if(&store_ret)) + return MutexLockResultExt::from_error(format_error(*err)); + // We don't update Miri's memory for this operation so we don't need to know if the store + // was the co-maximal store, but we still check that we at least get a boolean as the result + // of the store. + const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); + ERROR_ON( + nullptr == is_coherence_order_maximal_write, + "Unimplemented: store part of mutex try_lock returned unexpected result." + ); + } else { + // We did not acquire the mutex, so we tell GenMC to block the thread until we can acquire + // it. GenMC determines this based on the annotation we pass with the load further up in + // this function, namely when that load will read a value other than `MUTEX_LOCKED`. + this->handle_assume_block(thread_id, AssumeType::Spinloop); + } + return MutexLockResultExt::ok(is_lock_acquired); +} + +auto MiriGenmcShim::handle_mutex_try_lock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult { + auto& currPos = threads_action_[thread_id].event; + // As usual, we need to tell GenMC which value was stored at this location before this atomic + // access, if there previously was a non-atomic initializing access. We set the initial state of + // a mutex to be "unlocked". + const auto old_val = MUTEX_UNLOCKED; + const auto load_ret = GenMCDriver::handleLoad( + ++currPos, + old_val, + SAddr(address), + ASize(size) + ); + if (const auto* err = std::get_if(&load_ret)) + return MutexLockResultExt::from_error(format_error(*err)); + const auto* ret_val = std::get_if(&load_ret); + if (nullptr == ret_val) { + ERROR("Unimplemented: mutex trylock load returned unexpected result."); + } + + ERROR_ON( + *ret_val != MUTEX_UNLOCKED && *ret_val != MUTEX_LOCKED, + "Mutex read value was neither 0 nor 1" + ); + const bool is_lock_acquired = *ret_val == MUTEX_UNLOCKED; + if (!is_lock_acquired) { + return MutexLockResultExt::ok(false); /* Lock already held. */ + } + + const auto store_ret = GenMCDriver::handleStore( + ++currPos, + old_val, + SAddr(address), + ASize(size) + ); + if (const auto* err = std::get_if(&store_ret)) + return MutexLockResultExt::from_error(format_error(*err)); + // We don't update Miri's memory for this operation so we don't need to know if the store was + // co-maximal, but we still check that we get a boolean result. + const bool* is_coherence_order_maximal_write = std::get_if(&store_ret); + ERROR_ON( + nullptr == is_coherence_order_maximal_write, + "Unimplemented: store part of mutex try_lock returned unexpected result." + ); + return MutexLockResultExt::ok(true); +} + +auto MiriGenmcShim::handle_mutex_unlock(ThreadId thread_id, uint64_t address, uint64_t size) + -> StoreResult { + const auto pos = inc_pos(thread_id); + const auto ret = GenMCDriver::handleStore( + pos, + // As usual, we need to tell GenMC which value was stored at this location before this + // atomic access, if there previously was a non-atomic initializing access. We set the + // initial state of a mutex to be "unlocked". + /* old_val */ MUTEX_UNLOCKED, + MemOrdering::Release, + SAddr(address), + ASize(size), + AType::Signed, + /* store_value */ MUTEX_UNLOCKED, + EventDeps() + ); + if (const auto* err = std::get_if(&ret)) + return StoreResultExt::from_error(format_error(*err)); + const bool* is_coherence_order_maximal_write = std::get_if(&ret); + ERROR_ON( + nullptr == is_coherence_order_maximal_write, + "Unimplemented: store part of mutex unlock returned unexpected result." + ); + return StoreResultExt::ok(*is_coherence_order_maximal_write); +} diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp index 352d27adc3e8b..d2061fcb406c9 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp @@ -38,6 +38,7 @@ void MiriGenmcShim::handle_thread_join(ThreadId thread_id, ThreadId child_id) { if (!std::holds_alternative(ret)) { dec_pos(thread_id); } + // FIXME(genmc): handle `HandleResult::{Invalid, Reset, VerificationError}` return values. // NOTE: Thread return value is ignored, since Miri doesn't need it. } diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 619d4ab67b2f2..a34c7f2b3a667 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -254,6 +254,17 @@ mod ffi { is_coherence_order_maximal_write: bool, } + #[must_use] + #[derive(Debug)] + struct MutexLockResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// If true, GenMC determined that we should retry the mutex lock operation once the thread attempting to lock is scheduled again. + is_reset: bool, + /// Indicate whether the lock was acquired by this thread. + is_lock_acquired: bool, + } + /**** These are GenMC types that we have to copy-paste here since cxx does not support "importing" externally defined C++ types. ****/ @@ -305,6 +316,13 @@ mod ffi { UMin = 10, } + #[derive(Debug)] + enum AssumeType { + User = 0, + Barrier = 1, + Spinloop = 2, + } + // # Safety // // This block is unsafe to allow defining safe methods inside. @@ -323,6 +341,7 @@ mod ffi { (This tells cxx that the enums defined above are already defined on the C++ side; it will emit assertions to ensure that the two definitions agree.) ****/ type ActionKind; + type AssumeType; type MemOrdering; type RMWBinOp; type SchedulePolicy; @@ -430,7 +449,31 @@ mod ffi { /// Inform GenMC that the thread should be blocked. /// Note: this function is currently hardcoded for `AssumeType::User`, corresponding to user supplied assume statements. /// This can become a parameter once more types of assumes are added. - fn handle_assume_block(self: Pin<&mut MiriGenmcShim>, thread_id: i32); + fn handle_assume_block( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + assume_type: AssumeType, + ); + + /**** Mutex handling ****/ + fn handle_mutex_lock( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + ) -> MutexLockResult; + fn handle_mutex_try_lock( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + ) -> MutexLockResult; + fn handle_mutex_unlock( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + ) -> StoreResult; /***** Exploration related functionality *****/ diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index ef5b24d5abad5..b9e09e34dc360 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -43,6 +43,15 @@ mod intercept { impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn genmc_intercept_function( + &mut self, + _instance: rustc_middle::ty::Instance<'tcx>, + _args: &[rustc_const_eval::interpret::FnArg<'tcx, crate::Provenance>], + _dest: &crate::PlaceTy<'tcx>, + ) -> InterpResult<'tcx, bool> { + unreachable!() + } + fn handle_genmc_verifier_assume(&mut self, _condition: &OpTy<'tcx>) -> InterpResult<'tcx> { unreachable!(); } diff --git a/src/tools/miri/src/concurrency/genmc/intercept.rs b/src/tools/miri/src/concurrency/genmc/intercept.rs deleted file mode 100644 index 4867b1dc21afb..0000000000000 --- a/src/tools/miri/src/concurrency/genmc/intercept.rs +++ /dev/null @@ -1,38 +0,0 @@ -use tracing::debug; - -use crate::concurrency::thread::EvalContextExt as _; -use crate::{ - BlockReason, InterpResult, MachineCallback, MiriInterpCx, OpTy, UnblockKind, VisitProvenance, - VisitWith, callback, interp_ok, -}; - -// Handling of code intercepted by Miri in GenMC mode, such as assume statement or `std::sync::Mutex`. - -/// Other functionality not directly related to event handling -impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} -pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - /// Handle an `assume` statement. This will tell GenMC to block the current thread if the `condition` is false. - /// Returns `true` if the current thread should be blocked in Miri too. - fn handle_genmc_verifier_assume(&mut self, condition: &OpTy<'tcx>) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let condition_bool = this.read_scalar(condition)?.to_bool()?; - debug!("GenMC: handle_genmc_verifier_assume, condition: {condition:?} = {condition_bool}"); - if condition_bool { - return interp_ok(()); - } - let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); - genmc_ctx.handle_assume_block(&this.machine)?; - this.block_thread( - BlockReason::Genmc, - None, - callback!( - @capture<'tcx> {} - |_this, unblock: UnblockKind| { - assert_eq!(unblock, UnblockKind::Ready); - unreachable!("GenMC should never unblock a thread blocked by an `assume`."); - } - ), - ); - interp_ok(()) - } -} diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index caec5e921cdfe..bc475c680b5b1 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -27,16 +27,16 @@ use crate::*; mod config; mod global_allocations; mod helper; -mod intercept; mod run; pub(crate) mod scheduling; +mod shims; mod thread_id_map; pub use genmc_sys::GenmcParams; pub use self::config::GenmcConfig; -pub use self::intercept::EvalContextExt as GenmcEvalContextExt; pub use self::run::run_genmc_mode; +pub use self::shims::EvalContextExt as GenmcEvalContextExt; #[derive(Debug)] pub enum ExecutionEndResult { @@ -200,6 +200,14 @@ impl GenmcCtx { fn get_alloc_data_races(&self) -> bool { self.exec_state.allow_data_races.get() } + + /// Get the GenMC id of the currently active thread. + #[must_use] + fn active_thread_genmc_tid<'tcx>(&self, machine: &MiriMachine<'tcx>) -> i32 { + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread = machine.threads.active_thread(); + thread_infos.get_genmc_tid(curr_thread) + } } /// GenMC event handling. These methods are used to inform GenMC about events happening in the program, and to handle scheduling decisions. @@ -309,12 +317,10 @@ impl GenmcCtx { ordering: AtomicFenceOrd, ) -> InterpResult<'tcx> { assert!(!self.get_alloc_data_races(), "atomic fence with data race checking disabled."); - - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - - self.handle.borrow_mut().pin_mut().handle_fence(genmc_tid, ordering.to_genmc()); + self.handle + .borrow_mut() + .pin_mut() + .handle_fence(self.active_thread_genmc_tid(machine), ordering.to_genmc()); interp_ok(()) } @@ -425,12 +431,8 @@ impl GenmcCtx { debug!( "GenMC: atomic_compare_exchange, address: {address:?}, size: {size:?} (expect: {expected_old_value:?}, new: {new_value:?}, old_value: {old_value:?}, {success:?}, orderings: {fail:?}), can fail spuriously: {can_fail_spuriously}" ); - - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let genmc_tid = thread_infos.get_genmc_tid(ecx.machine.threads.active_thread()); - let cas_result = self.handle.borrow_mut().pin_mut().handle_compare_exchange( - genmc_tid, + self.active_thread_genmc_tid(&ecx.machine), address.bytes(), size.bytes(), scalar_to_genmc_scalar(ecx, self, expected_old_value)?, @@ -591,14 +593,10 @@ impl GenmcCtx { return ecx .get_global_allocation_address(&self.global_state.global_allocations, alloc_id); } - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread); // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte let genmc_size = size.bytes().max(1); - let chosen_address = self.handle.borrow_mut().pin_mut().handle_malloc( - genmc_tid, + self.active_thread_genmc_tid(machine), genmc_size, alignment.bytes(), ); @@ -632,11 +630,12 @@ impl GenmcCtx { !self.get_alloc_data_races(), "memory deallocation with data race checking disabled." ); - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - - if self.handle.borrow_mut().pin_mut().handle_free(genmc_tid, address.bytes()) { + if self + .handle + .borrow_mut() + .pin_mut() + .handle_free(self.active_thread_genmc_tid(machine), address.bytes()) + { // FIXME(genmc): improve error handling. // An error was detected, so we get the error string from GenMC. throw_ub_format!("{}", self.try_get_error().unwrap()); @@ -690,7 +689,7 @@ impl GenmcCtx { let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); debug!("GenMC: thread {curr_thread_id:?} ({genmc_tid:?}) finished."); - // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0 + // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0. self.handle.borrow_mut().pin_mut().handle_thread_finish(genmc_tid, /* ret_val */ 0); } @@ -752,17 +751,12 @@ impl GenmcCtx { "GenMC mode currently does not support atomics larger than {MAX_ACCESS_SIZE} bytes.", ); } - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let curr_thread_id = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); - debug!( - "GenMC: load, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} == {addr:#x}, size: {size:?}, ordering: {memory_ordering:?}, old_value: {genmc_old_value:x?}", + "GenMC: load, address: {addr} == {addr:#x}, size: {size:?}, ordering: {memory_ordering:?}, old_value: {genmc_old_value:x?}", addr = address.bytes() ); - let load_result = self.handle.borrow_mut().pin_mut().handle_load( - genmc_tid, + self.active_thread_genmc_tid(machine), address.bytes(), size.bytes(), memory_ordering, @@ -803,17 +797,12 @@ impl GenmcCtx { "GenMC mode currently does not support atomics larger than {MAX_ACCESS_SIZE} bytes." ); } - let thread_infos = self.exec_state.thread_id_manager.borrow(); - let curr_thread_id = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); - debug!( - "GenMC: store, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} = {addr:#x}, size: {size:?}, ordering {memory_ordering:?}, value: {genmc_value:?}", + "GenMC: store, address: {addr} = {addr:#x}, size: {size:?}, ordering {memory_ordering:?}, value: {genmc_value:?}", addr = address.bytes() ); - let store_result = self.handle.borrow_mut().pin_mut().handle_store( - genmc_tid, + self.active_thread_genmc_tid(machine), address.bytes(), size.bytes(), genmc_value, @@ -854,14 +843,11 @@ impl GenmcCtx { MAX_ACCESS_SIZE, size.bytes() ); - - let curr_thread_id = ecx.machine.threads.active_thread(); - let genmc_tid = self.exec_state.thread_id_manager.borrow().get_genmc_tid(curr_thread_id); debug!( - "GenMC: atomic_rmw_op, thread: {curr_thread_id:?} ({genmc_tid:?}) (op: {genmc_rmw_op:?}, rhs value: {genmc_rhs_scalar:?}), address: {address:?}, size: {size:?}, ordering: {ordering:?}", + "GenMC: atomic_rmw_op (op: {genmc_rmw_op:?}, rhs value: {genmc_rhs_scalar:?}), address: {address:?}, size: {size:?}, ordering: {ordering:?}", ); let rmw_result = self.handle.borrow_mut().pin_mut().handle_read_modify_write( - genmc_tid, + self.active_thread_genmc_tid(&ecx.machine), address.bytes(), size.bytes(), genmc_rmw_op, @@ -884,20 +870,6 @@ impl GenmcCtx { }; interp_ok((old_value_scalar, new_value_scalar)) } - - /**** Blocking functionality ****/ - - /// Handle a user thread getting blocked. - /// This may happen due to an manual `assume` statement added by a user - /// or added by some automated program transformation, e.g., for spinloops. - fn handle_assume_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { - let curr_thread = machine.threads.active_thread(); - let genmc_curr_thread = - self.exec_state.thread_id_manager.borrow().get_genmc_tid(curr_thread); - debug!("GenMC: assume statement, blocking thread {curr_thread:?} ({genmc_curr_thread:?})"); - self.handle.borrow_mut().pin_mut().handle_assume_block(genmc_curr_thread); - interp_ok(()) - } } impl VisitProvenance for GenmcCtx { diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs index be7df8682f07c..6ccbaf4f2482b 100644 --- a/src/tools/miri/src/concurrency/genmc/scheduling.rs +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -63,15 +63,25 @@ fn get_function_kind<'tcx>( ) -> InterpResult<'tcx, NextInstrKind> { use NextInstrKind::*; let callee_def_id = match func_ty.kind() { - ty::FnDef(def_id, _args) => def_id, + ty::FnDef(def_id, _args) => *def_id, _ => return interp_ok(MaybeAtomic(ActionKind::Load)), // we don't know the callee, might be pthread_join }; let Some(intrinsic_def) = ecx.tcx.intrinsic(callee_def_id) else { - if ecx.tcx.is_foreign_item(*callee_def_id) { + if ecx.tcx.is_foreign_item(callee_def_id) { // Some shims, like pthread_join, must be considered loads. So just consider them all loads, // these calls are not *that* common. return interp_ok(MaybeAtomic(ActionKind::Load)); } + // NOTE: Functions intercepted by Miri in `concurrency/genmc/intercep.rs` must also be added here. + // Such intercepted functions, like `sys::Mutex::lock`, should be treated as atomics to ensure we call the scheduler when we encounter one of them. + // These functions must also be classified whether they may have load semantics. + if ecx.tcx.is_diagnostic_item(rustc_span::sym::sys_mutex_lock, callee_def_id) + || ecx.tcx.is_diagnostic_item(rustc_span::sym::sys_mutex_try_lock, callee_def_id) + { + return interp_ok(MaybeAtomic(ActionKind::Load)); + } else if ecx.tcx.is_diagnostic_item(rustc_span::sym::sys_mutex_unlock, callee_def_id) { + return interp_ok(MaybeAtomic(ActionKind::NonLoad)); + } // The next step is a call to a regular Rust function. return interp_ok(NonAtomic); }; diff --git a/src/tools/miri/src/concurrency/genmc/shims.rs b/src/tools/miri/src/concurrency/genmc/shims.rs new file mode 100644 index 0000000000000..4685dfd1b8dd7 --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/shims.rs @@ -0,0 +1,234 @@ +use genmc_sys::AssumeType; +use rustc_middle::ty; +use tracing::debug; + +use crate::concurrency::genmc::MAX_ACCESS_SIZE; +use crate::concurrency::thread::EvalContextExt as _; +use crate::*; + +impl GenmcCtx { + /// Handle a user thread getting blocked. + /// This may happen due to an manual `assume` statement added by a user + /// or added by some automated program transformation, e.g., for spinloops. + fn handle_assume_block<'tcx>( + &self, + machine: &MiriMachine<'tcx>, + assume_type: AssumeType, + ) -> InterpResult<'tcx> { + debug!("GenMC: assume statement, blocking active thread."); + self.handle + .borrow_mut() + .pin_mut() + .handle_assume_block(self.active_thread_genmc_tid(machine), assume_type); + interp_ok(()) + } +} + +// Handling of code intercepted by Miri in GenMC mode, such as assume statement or `std::sync::Mutex`. + +impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Small helper to get the arguments of an intercepted function call. + fn get_fn_args( + &self, + instance: ty::Instance<'tcx>, + args: &[FnArg<'tcx>], + ) -> InterpResult<'tcx, [OpTy<'tcx>; N]> { + let this = self.eval_context_ref(); + let args = this.copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit? + if let Ok(ops) = args.try_into() { + return interp_ok(ops); + } + panic!("{} is a diagnostic item expected to have {} arguments", instance, N); + } + + /**** Blocking functionality ****/ + + /// Handle a thread getting blocked by a user assume (not an automatically generated assume). + /// Unblocking this thread in the current execution will cause a panic. + /// Miri does not provide GenMC with the annotations to determine when to unblock the thread, so it should never be unblocked. + fn handle_user_assume_block(&mut self) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + debug!( + "GenMC: block thread {:?} due to failing assume statement.", + this.machine.threads.active_thread() + ); + assert!(this.machine.threads.active_thread_ref().is_enabled()); + // Block the thread on the GenMC side. + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + genmc_ctx.handle_assume_block(&this.machine, AssumeType::User)?; + // Block the thread on the Miri side. + this.block_thread( + BlockReason::Genmc, + None, + callback!( + @capture<'tcx> {} + |_this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + unreachable!("GenMC should never unblock a thread blocked by an `assume`."); + } + ), + ); + interp_ok(()) + } + + fn intercept_mutex_lock(&mut self, mutex: MPlaceTy<'tcx>) -> InterpResult<'tcx> { + debug!("GenMC: handling Mutex::lock()"); + let this = self.eval_context_mut(); + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + + let size = mutex.layout.size.bytes(); + assert!( + size <= MAX_ACCESS_SIZE, + "Mutex is larger than maximal size of a memory access supported by GenMC ({size} > {MAX_ACCESS_SIZE})" + ); + let result = genmc_ctx.handle.borrow_mut().pin_mut().handle_mutex_lock( + genmc_ctx.active_thread_genmc_tid(&this.machine), + mutex.ptr().addr().bytes(), + size, + ); + if let Some(error) = result.error.as_ref() { + // FIXME(genmc): improve error handling. + throw_ub_format!("{}", error.to_string_lossy()); + } + if result.is_reset { + debug!("GenMC: Mutex::lock: Reset"); + // GenMC informed us to reset and try the lock again later. + // We block the current thread until GenMC schedules it again. + this.block_thread( + crate::BlockReason::Genmc, + None, + crate::callback!( + @capture<'tcx> { + mutex: MPlaceTy<'tcx>, + } + |this, unblock: crate::UnblockKind| { + debug!("GenMC: Mutex::lock: unblocking callback called, attempting to lock the Mutex again."); + assert_eq!(unblock, crate::UnblockKind::Ready); + this.intercept_mutex_lock(mutex)?; + interp_ok(()) + } + ), + ); + } else if result.is_lock_acquired { + debug!("GenMC: Mutex::lock successfully acquired the Mutex."); + } else { + debug!("GenMC: Mutex::lock failed to acquire the Mutex, permanently blocking thread."); + // NOTE: `handle_mutex_lock` already blocked the current thread on the GenMC side. + this.block_thread( + crate::BlockReason::Genmc, + None, + crate::callback!( + @capture<'tcx> { + mutex: MPlaceTy<'tcx>, + } + |_this, _unblock: crate::UnblockKind| { + unreachable!("A thread blocked on `Mutex::lock` should not be unblocked again."); + } + ), + ); + } + // NOTE: We don't write anything back to Miri's memory where the Mutex is located, that state is handled only by GenMC. + interp_ok(()) + } + + fn intercept_mutex_try_lock( + &mut self, + mutex: MPlaceTy<'tcx>, + dest: &crate::PlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + debug!("GenMC: handling Mutex::try_lock()"); + let this = self.eval_context_mut(); + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + let size = mutex.layout.size.bytes(); + assert!( + size <= MAX_ACCESS_SIZE, + "Mutex is larger than maximal size of a memory access supported by GenMC ({size} > {MAX_ACCESS_SIZE})" + ); + let result = genmc_ctx.handle.borrow_mut().pin_mut().handle_mutex_try_lock( + genmc_ctx.active_thread_genmc_tid(&this.machine), + mutex.ptr().addr().bytes(), + size, + ); + if let Some(error) = result.error.as_ref() { + // FIXME(genmc): improve error handling. + throw_ub_format!("{}", error.to_string_lossy()); + } + debug!( + "GenMC: Mutex::try_lock(): is_reset: {}, is_lock_acquired: {}", + result.is_reset, result.is_lock_acquired + ); + assert!(!result.is_reset, "GenMC returned 'reset' for a mutex try_lock."); + // Write the return value of try_lock, i.e., whether we acquired the mutex. + this.write_scalar(Scalar::from_bool(result.is_lock_acquired), dest)?; + // NOTE: We don't write anything back to Miri's memory where the Mutex is located, that state is handled only by GenMC. + interp_ok(()) + } + + fn intercept_mutex_unlock(&self, mutex: MPlaceTy<'tcx>) -> InterpResult<'tcx> { + debug!("GenMC: handling Mutex::unlock()"); + let this = self.eval_context_ref(); + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + let result = genmc_ctx.handle.borrow_mut().pin_mut().handle_mutex_unlock( + genmc_ctx.active_thread_genmc_tid(&this.machine), + mutex.ptr().addr().bytes(), + mutex.layout.size.bytes(), + ); + if let Some(error) = result.error.as_ref() { + // FIXME(genmc): improve error handling. + throw_ub_format!("{}", error.to_string_lossy()); + } + // NOTE: We don't write anything back to Miri's memory where the Mutex is located, that state is handled only by GenMC.} + interp_ok(()) + } +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Given a `ty::Instance<'tcx>`, do any required special handling. + /// Returns true if this `instance` should be skipped (i.e., no MIR should be executed for it). + fn genmc_intercept_function( + &mut self, + instance: rustc_middle::ty::Instance<'tcx>, + args: &[rustc_const_eval::interpret::FnArg<'tcx, crate::Provenance>], + dest: &crate::PlaceTy<'tcx>, + ) -> InterpResult<'tcx, bool> { + let this = self.eval_context_mut(); + assert!( + this.machine.data_race.as_genmc_ref().is_some(), + "This function should only be called in GenMC mode." + ); + + // NOTE: When adding new intercepted functions here, they must also be added to `fn get_function_kind` in `concurrency/genmc/scheduling.rs`. + use rustc_span::sym; + if this.tcx.is_diagnostic_item(sym::sys_mutex_lock, instance.def_id()) { + let [mutex] = this.get_fn_args(instance, args)?; + let mutex = this.deref_pointer(&mutex)?; + this.intercept_mutex_lock(mutex)?; + } else if this.tcx.is_diagnostic_item(sym::sys_mutex_try_lock, instance.def_id()) { + let [mutex] = this.get_fn_args(instance, args)?; + let mutex = this.deref_pointer(&mutex)?; + this.intercept_mutex_try_lock(mutex, dest)?; + } else if this.tcx.is_diagnostic_item(sym::sys_mutex_unlock, instance.def_id()) { + let [mutex] = this.get_fn_args(instance, args)?; + let mutex = this.deref_pointer(&mutex)?; + this.intercept_mutex_unlock(mutex)?; + } else { + // Nothing to intercept. + return interp_ok(false); + } + interp_ok(true) + } + + /// Handle an `assume` statement. This will tell GenMC to block the current thread if the `condition` is false. + /// Returns `true` if the current thread should be blocked in Miri too. + fn handle_genmc_verifier_assume(&mut self, condition: &OpTy<'tcx>) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let condition_bool = this.read_scalar(condition)?.to_bool()?; + debug!("GenMC: handle_genmc_verifier_assume, condition: {condition:?} = {condition_bool}"); + if condition_bool { + return interp_ok(()); + } + this.handle_user_assume_block() + } +} diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 0d9dbe052e399..13492c99294ea 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -708,21 +708,31 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); // In GenMC mode, we let GenMC do the scheduling. - if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let Some(next_thread_id) = genmc_ctx.schedule_thread(this)? else { - return interp_ok(SchedulingAction::ExecuteStep); - }; - // If a thread is blocked on GenMC, we have to implicitly unblock it when it gets scheduled again. - if this.machine.threads.threads[next_thread_id].state.is_blocked_on(BlockReason::Genmc) - { - info!("GenMC: scheduling blocked thread {next_thread_id:?}, so we unblock it now."); - this.unblock_thread(next_thread_id, BlockReason::Genmc)?; + if this.machine.data_race.as_genmc_ref().is_some() { + loop { + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + let Some(next_thread_id) = genmc_ctx.schedule_thread(this)? else { + return interp_ok(SchedulingAction::ExecuteStep); + }; + // If a thread is blocked on GenMC, we have to implicitly unblock it when it gets scheduled again. + if this.machine.threads.threads[next_thread_id] + .state + .is_blocked_on(BlockReason::Genmc) + { + info!( + "GenMC: scheduling blocked thread {next_thread_id:?}, so we unblock it now." + ); + this.unblock_thread(next_thread_id, BlockReason::Genmc)?; + } + // The thread we just unblocked may have been blocked again during the unblocking callback. + // In that case, we need to ask for a different thread to run next. + let thread_manager = &mut this.machine.threads; + if thread_manager.threads[next_thread_id].state.is_enabled() { + // Set the new active thread. + thread_manager.active_thread = next_thread_id; + return interp_ok(SchedulingAction::ExecuteStep); + } } - // Set the new active thread. - let thread_manager = &mut this.machine.threads; - thread_manager.active_thread = next_thread_id; - assert!(thread_manager.threads[thread_manager.active_thread].state.is_enabled()); - return interp_ok(SchedulingAction::ExecuteStep); } // We are not in GenMC mode, so we control the scheduling. diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index ce72fc6e297dc..07af4dcaad115 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -110,6 +110,7 @@ pub type StrictPointer = interpret::Pointer; pub type Scalar = interpret::Scalar; pub type ImmTy<'tcx> = interpret::ImmTy<'tcx, machine::Provenance>; pub type OpTy<'tcx> = interpret::OpTy<'tcx, machine::Provenance>; +pub type FnArg<'tcx> = interpret::FnArg<'tcx, machine::Provenance>; pub type PlaceTy<'tcx> = interpret::PlaceTy<'tcx, machine::Provenance>; pub type MPlaceTy<'tcx> = interpret::MPlaceTy<'tcx, machine::Provenance>; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 7b93d0194119a..49c2351665367 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -31,7 +31,9 @@ use rustc_target::callconv::FnAbi; use crate::alloc_addresses::EvalContextExt; use crate::concurrency::cpu_affinity::{self, CpuAffinityMask}; use crate::concurrency::data_race::{self, NaReadType, NaWriteType}; -use crate::concurrency::{AllocDataRaceHandler, GenmcCtx, GlobalDataRaceHandler, weak_memory}; +use crate::concurrency::{ + AllocDataRaceHandler, GenmcCtx, GenmcEvalContextExt as _, GlobalDataRaceHandler, weak_memory, +}; use crate::*; /// First real-time signal. @@ -1163,7 +1165,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx: &mut MiriInterpCx<'tcx>, instance: ty::Instance<'tcx>, abi: &FnAbi<'tcx, Ty<'tcx>>, - args: &[FnArg<'tcx, Provenance>], + args: &[FnArg<'tcx>], dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, @@ -1182,6 +1184,13 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind); } + if ecx.machine.data_race.as_genmc_ref().is_some() + && ecx.genmc_intercept_function(instance, args, dest)? + { + ecx.return_to_block(ret)?; + return interp_ok(None); + } + // Otherwise, load the MIR. let _trace = enter_trace_span!("load_mir"); interp_ok(Some((ecx.load_mir(instance.def, None)?, instance))) @@ -1192,7 +1201,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx: &mut MiriInterpCx<'tcx>, fn_val: DynSym, abi: &FnAbi<'tcx, Ty<'tcx>>, - args: &[FnArg<'tcx, Provenance>], + args: &[FnArg<'tcx>], dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, diff --git a/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.rs b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.rs new file mode 100644 index 0000000000000..d2da722f1c02f --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.rs @@ -0,0 +1,28 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@error-in-other-file: Undefined Behavior + +// Test that GenMC throws an error if a `std::sync::Mutex` is unlocked from a different thread than the one that locked it. +// +// This test will cause an error on all targets, even mutexes on that targets allow for unlocking on a different thread. +// GenMC always assumes a `pthread`-like API. + +#![no_main] + +use std::sync::Mutex; + +static MUTEX: Mutex = Mutex::new(0); + +#[derive(Copy, Clone)] +struct EvilSend(pub T); +unsafe impl Send for EvilSend {} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let guard = EvilSend(MUTEX.lock().unwrap()); + let handle = std::thread::spawn(move || { + let guard = guard; // avoid field capturing + drop(guard); + }); + handle.join().unwrap(); + 0 +} diff --git a/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr new file mode 100644 index 0000000000000..e74b76ea415e6 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr @@ -0,0 +1,86 @@ +Running GenMC Verification... +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `miri_start` + --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +LL | | let guard = guard; // avoid field capturing +LL | | drop(guard); +LL | | }); + | |______^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `miri_start` + --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC + | +LL | let handle = std::thread::spawn(move || { + | __________________^ +LL | | let guard = guard; // avoid field capturing +LL | | drop(guard); +LL | | }); + | |______^ + +error: Undefined Behavior: Invalid unlock() operation + --> RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC + | +LL | self.lock.inner.unlock(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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: BACKTRACE on thread `unnamed-ID`: + = note: inside ` as std::ops::Drop>::drop` at RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC + = note: inside `std::ptr::drop_in_place::> - shim(Some(std::sync::MutexGuard<'_, u64>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC + = note: inside `std::ptr::drop_in_place::>> - shim(Some(EvilSend>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC + = note: inside `std::mem::drop::>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC +note: inside closure + --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC + | +LL | drop(guard); + | ^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error; 2 warnings emitted + diff --git a/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.rs b/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.rs new file mode 100644 index 0000000000000..3daff38efbfdd --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.rs @@ -0,0 +1,22 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@error-in-other-file: Undefined Behavior + +// Test that GenMC can detect a double unlock of a mutex. +// This test will cause an error even if the program actually would work entirely fine despite the double-unlock +// because GenMC always assumes a `pthread`-like API. + +#![no_main] + +use std::sync::Mutex; + +static MUTEX: Mutex = Mutex::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let mut guard = MUTEX.lock().unwrap(); + unsafe { + std::ptr::drop_in_place(&raw mut guard); + } + drop(guard); + 0 +} diff --git a/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.stderr b/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.stderr new file mode 100644 index 0000000000000..3ba863668f1e8 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/shims/mutex_double_unlock.stderr @@ -0,0 +1,23 @@ +Running GenMC Verification... +error: Undefined Behavior: Invalid unlock() operation + --> RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC + | +LL | self.lock.inner.unlock(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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: BACKTRACE: + = note: inside ` as std::ops::Drop>::drop` at RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC + = note: inside `std::ptr::drop_in_place::> - shim(Some(std::sync::MutexGuard<'_, u64>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC + = note: inside `std::mem::drop::>` at RUSTLIB/core/src/mem/mod.rs:LL:CC +note: inside `miri_start` + --> tests/genmc/fail/shims/mutex_double_unlock.rs:LL:CC + | +LL | drop(guard); + | ^^^^^^^^^^^ + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs b/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs new file mode 100644 index 0000000000000..df47fbfbc1676 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-verbose +//@normalize-stderr-test: "Verification took .*s" -> "Verification took [TIME]s" + +// Test that we can detect a deadlock involving `std::sync::Mutex` in GenMC mode. +// FIXME(genmc): We cannot detect the deadlock currently. Instead, the deadlocked execution is treated like any other blocked execution. +// This behavior matches GenMC's on an equivalent program, and additional analysis is required to detect such deadlocks. +// This should become a `fail` test once this deadlock can be detected. +// +// FIXME(genmc): use `std::thread` once GenMC mode performance is better and produces fewer warnings for compare_exchange. + +#![no_main] +#![feature(abort_unwind)] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::Mutex; + +use crate::genmc::*; + +static X: Mutex = Mutex::new(0); +static Y: Mutex = Mutex::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let t0 = spawn_pthread_closure(|| { + let mut x = X.lock().unwrap(); + let mut y = Y.lock().unwrap(); + *x += 1; + *y += 1; + }); + let t1 = spawn_pthread_closure(|| { + let mut y = Y.lock().unwrap(); + let mut x = X.lock().unwrap(); + *x += 1; + *y += 1; + }); + join_pthreads([t0, t1]); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.stderr b/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.stderr new file mode 100644 index 0000000000000..8b3957d18c01b --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.stderr @@ -0,0 +1,5 @@ +Running GenMC Verification... +Verification complete with 3 executions. No errors found. +Number of complete executions explored: 2 +Number of blocked executions seen: 1 +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/shims/mutex_simple.rs b/src/tools/miri/tests/genmc/pass/shims/mutex_simple.rs new file mode 100644 index 0000000000000..1f8bc81d85eb5 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/shims/mutex_simple.rs @@ -0,0 +1,68 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-verbose +//@normalize-stderr-test: "Verification took .*s" -> "Verification took [TIME]s" + +// Test various features of the `std::sync::Mutex` API with GenMC. +// Miri running with GenMC intercepts the Mutex functions `lock`, `try_lock` and `unlock`, instead of running their actual implementation. +// This interception should not break any functionality. +// +// FIXME(genmc): Once GenMC supports mixed size accesses, add stack/heap allocated Mutexes to the test. +// FIXME(genmc): Once the actual implementation of mutexes can be used in GenMC mode and there is a setting to disable Mutex interception: Add test revision without interception. +// +// Miri provides annotations to GenMC for the condition required to unblock a thread blocked on a Mutex lock call. +// This massively reduces the number of blocked executions we need to explore (in this test we require zero blocked execution). +// We use verbose output to check that this test always explores zero blocked executions. + +#![no_main] +#![feature(abort_unwind)] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::Mutex; + +use crate::genmc::*; + +const REPS: u64 = 3; + +static LOCK: Mutex = Mutex::new(0); +static OTHER_LOCK: Mutex = Mutex::new(1234); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + std::panic::abort_unwind(main_); + 0 +} + +fn main_() { + // Two mutexes should not interfere, holding this guard does not affect the other mutex. + let other_guard = OTHER_LOCK.lock().unwrap(); + + let guard = LOCK.lock().unwrap(); + // Trying to lock should fail if the mutex is already held. + assert!(LOCK.try_lock().is_err()); + // Dropping the guard should unlock the mutex correctly. + drop(guard); + // Trying to lock now should succeed. + assert!(LOCK.try_lock().is_ok()); + + // Spawn multiple threads interacting with the same mutex. + unsafe { + let ids = [ + spawn_pthread_closure(|| { + for _ in 0..REPS { + *LOCK.lock().unwrap() += 2; + } + }), + spawn_pthread_closure(|| { + for _ in 0..REPS { + *LOCK.lock().unwrap() += 4; + } + }), + ]; + join_pthreads(ids); + } + // Due to the Mutex, all increments should be visible in every explored execution. + assert!(*LOCK.lock().unwrap() == REPS * 6); + + drop(other_guard); +} diff --git a/src/tools/miri/tests/genmc/pass/shims/mutex_simple.stderr b/src/tools/miri/tests/genmc/pass/shims/mutex_simple.stderr new file mode 100644 index 0000000000000..76ddd42addf9d --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/shims/mutex_simple.stderr @@ -0,0 +1,3 @@ +Running GenMC Verification... +Verification complete with 20 executions. No errors found. +Verification took [TIME]s. diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr b/src/tools/miri/tests/genmc/pass/shims/spinloop_assume.bounded123.stderr similarity index 100% rename from src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded123.stderr rename to src/tools/miri/tests/genmc/pass/shims/spinloop_assume.bounded123.stderr diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr b/src/tools/miri/tests/genmc/pass/shims/spinloop_assume.bounded321.stderr similarity index 100% rename from src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.bounded321.stderr rename to src/tools/miri/tests/genmc/pass/shims/spinloop_assume.bounded321.stderr diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr b/src/tools/miri/tests/genmc/pass/shims/spinloop_assume.replaced123.stderr similarity index 100% rename from src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced123.stderr rename to src/tools/miri/tests/genmc/pass/shims/spinloop_assume.replaced123.stderr diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr b/src/tools/miri/tests/genmc/pass/shims/spinloop_assume.replaced321.stderr similarity index 100% rename from src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.replaced321.stderr rename to src/tools/miri/tests/genmc/pass/shims/spinloop_assume.replaced321.stderr diff --git a/src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs b/src/tools/miri/tests/genmc/pass/shims/spinloop_assume.rs similarity index 100% rename from src/tools/miri/tests/genmc/pass/intercept/spinloop_assume.rs rename to src/tools/miri/tests/genmc/pass/shims/spinloop_assume.rs From 27afeb0085db8f066636a512241ed8e2003a3fe2 Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Wed, 8 Oct 2025 01:59:44 +0530 Subject: [PATCH 18/26] feat: add support for libc::memset Signed-off-by: vishruth-thimmaiah --- src/tools/miri/src/shims/foreign_items.rs | 16 +++++++ .../miri/tests/pass-dep/libc/libc-mem.rs | 42 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 1d086906e7a59..3fd57d3c8db22 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -827,6 +827,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?; this.write_pointer(ptr_dest, dest)?; } + "memset" => { + let [ptr_dest, val, n] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let ptr_dest = this.read_pointer(ptr_dest)?; + let val = this.read_scalar(val)?.to_i32()?; + let n = this.read_target_usize(n)?; + // The docs say val is "interpreted as unsigned char". + #[expect(clippy::as_conversions)] + let val = val as u8; + + this.ptr_get_alloc_id(ptr_dest, 0)?; + + let bytes = std::iter::repeat_n(val, n.try_into().unwrap()); + this.write_bytes_ptr(ptr_dest, bytes)?; + this.write_pointer(ptr_dest, dest)?; + } // LLVM intrinsics "llvm.prefetch" => { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs index 727533a9de61d..531d637d1f24d 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -78,6 +78,47 @@ fn test_strcpy() { } } +fn test_memset() { + unsafe { + let val = 1; + let dest = libc::calloc(3, 1); + libc::memset(dest, val, 3); + let slc = std::slice::from_raw_parts(dest as *const i8, 3); + assert_eq!(*slc, [1i8, 1, 1]); + libc::free(dest); + } + + unsafe { + let val = 1; + let dest = libc::calloc(4, 1); + libc::memset(dest, val, 3); + let slc = std::slice::from_raw_parts(dest as *const i8, 4); + assert_eq!(*slc, [1i8, 1, 1, 0]); + libc::free(dest); + } + + unsafe { + let val = 1; + let mut dest = 0_i8; + libc::memset(&mut dest as *mut i8 as *mut libc::c_void, val, mem::size_of::()); + assert_eq!(dest, val as i8); + } + + unsafe { + let val = 1; + let mut dest = 0_i16; + libc::memset(&mut dest as *mut i16 as *mut libc::c_void, val, mem::size_of::()); + assert_eq!(dest, 257); + } + + unsafe { + let val = 257; + let mut dest = 0_i16; + libc::memset(&mut dest as *mut i16 as *mut libc::c_void, val, mem::size_of::()); + assert_eq!(dest, 257); + } +} + fn test_malloc() { // Test that small allocations sometimes *are* not very aligned. let saw_unaligned = (0..64).any(|_| unsafe { @@ -310,4 +351,5 @@ fn main() { test_memcpy(); test_strcpy(); + test_memset(); } From 5446a0ae0afa8678afff93fb2fdc7db860fbc102 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 8 Oct 2025 04:52:55 +0000 Subject: [PATCH 19/26] Prepare for merging from rust-lang/rust This updates the rust-version file to 4fd31815524baba0bf368f151f757101f432e3de. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 77436ae2f8259..692cbc0a56bc3 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -3b8665c5ab3aeced9b01672404c3764583e722ca +4fd31815524baba0bf368f151f757101f432e3de From 8066cbc07a469a25b927b7206a6405c885e414b7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 8 Oct 2025 11:31:28 +0200 Subject: [PATCH 20/26] readme: document how to directly invoke the driver --- src/tools/miri/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index a5214e213b3cf..d47967c0a4d34 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -246,6 +246,21 @@ such races. Note: `cargo-nextest` does not support doctests, see https://github.com/nextest-rs/nextest/issues/16 +### Directly invoking the `miri` driver + +The recommended way to invoke Miri is via `cargo miri`. Directly invoking the underlying `miri` +driver is not supported, which is why that binary is not even installed into the PATH. However, if +you need to run Miri on many small tests and want to invoke it directly like you would invoke +`rustc`, that is still possible with a bit of extra effort: + +```sh +# one-time setup +cargo +nightly miri setup +SYSROOT=$(cargo +nightly miri setup --print-sysroot) +# per file +~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/miri --sysroot "$SYSROOT" file.rs +``` + ### Common Problems When using the above instructions, you may encounter a number of confusing compiler From afea346b3548a9f995b2aacf389dfb094d3997b4 Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Fri, 10 Oct 2025 14:55:09 +0530 Subject: [PATCH 21/26] feat: add failing test for zero-sized memset Signed-off-by: vishruth-thimmaiah --- src/tools/miri/src/shims/foreign_items.rs | 1 + src/tools/miri/tests/fail-dep/libc/memset_null.rs | 8 ++++++++ .../miri/tests/fail-dep/libc/memset_null.stderr | 15 +++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 src/tools/miri/tests/fail-dep/libc/memset_null.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/memset_null.stderr diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 3fd57d3c8db22..08964ba7b329f 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -837,6 +837,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { #[expect(clippy::as_conversions)] let val = val as u8; + // C requires that this must always be a valid pointer, even if `n` is zero, so we better check that. this.ptr_get_alloc_id(ptr_dest, 0)?; let bytes = std::iter::repeat_n(val, n.try_into().unwrap()); diff --git a/src/tools/miri/tests/fail-dep/libc/memset_null.rs b/src/tools/miri/tests/fail-dep/libc/memset_null.rs new file mode 100644 index 0000000000000..c3fa9973e762a --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/memset_null.rs @@ -0,0 +1,8 @@ +use std::ptr; + +// null is explicitly called out as UB in the C docs for `memset`. +fn main() { + unsafe { + libc::memset(ptr::null_mut(), 0, 0); //~ERROR: null pointer + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/memset_null.stderr b/src/tools/miri/tests/fail-dep/libc/memset_null.stderr new file mode 100644 index 0000000000000..fdc8f3a29f940 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/memset_null.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: pointer not dereferenceable: pointer must point to some allocation, but got null pointer + --> tests/fail-dep/libc/memset_null.rs:LL:CC + | +LL | libc::memset(ptr::null_mut(), 0, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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: BACKTRACE: + = note: inside `main` at tests/fail-dep/libc/memset_null.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 61594ca4aa5eeba01a0a66843b8002f34ac65de7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 Oct 2025 17:03:35 +0200 Subject: [PATCH 22/26] remove a bunch of unnecessary 'pub' from tests --- .../tests/fail/both_borrows/aliasing_mut1.rs | 2 +- .../both_borrows/aliasing_mut1.stack.stderr | 8 ++--- .../both_borrows/aliasing_mut1.tree.stderr | 8 ++--- .../tests/fail/both_borrows/aliasing_mut2.rs | 2 +- .../both_borrows/aliasing_mut2.stack.stderr | 8 ++--- .../both_borrows/aliasing_mut2.tree.stderr | 4 +-- .../tests/fail/both_borrows/aliasing_mut3.rs | 2 +- .../both_borrows/aliasing_mut3.stack.stderr | 4 +-- .../both_borrows/aliasing_mut3.tree.stderr | 8 ++--- .../tests/fail/both_borrows/aliasing_mut4.rs | 2 +- .../both_borrows/aliasing_mut4.stack.stderr | 8 ++--- .../both_borrows/aliasing_mut4.tree.stderr | 4 +-- .../tests/fail/data_race/alloc_read_race.rs | 2 +- .../tests/fail/data_race/alloc_write_race.rs | 2 +- .../data_race/atomic_read_na_write_race1.rs | 2 +- .../data_race/atomic_read_na_write_race2.rs | 2 +- .../data_race/atomic_write_na_read_race1.rs | 2 +- .../data_race/atomic_write_na_read_race2.rs | 2 +- .../data_race/atomic_write_na_write_race1.rs | 2 +- .../data_race/atomic_write_na_write_race2.rs | 2 +- .../fail/data_race/dealloc_read_race1.rs | 2 +- .../fail/data_race/dealloc_read_race2.rs | 2 +- .../fail/data_race/dealloc_read_race_stack.rs | 2 +- .../fail/data_race/dealloc_write_race1.rs | 2 +- .../fail/data_race/dealloc_write_race2.rs | 2 +- .../data_race/dealloc_write_race_stack.rs | 2 +- .../data_race/enable_after_join_to_main.rs | 2 +- .../tests/fail/data_race/read_write_race.rs | 2 +- .../fail/data_race/read_write_race_stack.rs | 2 +- .../fail/data_race/relax_acquire_race.rs | 2 +- .../tests/fail/data_race/release_seq_race.rs | 2 +- .../data_race/release_seq_race_same_thread.rs | 2 +- .../miri/tests/fail/data_race/rmw_race.rs | 2 +- .../tests/fail/data_race/write_write_race.rs | 2 +- .../fail/data_race/write_write_race_stack.rs | 2 +- ...um-set-discriminant-niche-variant-wrong.rs | 2 +- .../arg_inplace_locals_alias.rs | 2 +- .../arg_inplace_locals_alias_ret.rs | 2 +- .../fail/function_calls/arg_inplace_mutate.rs | 2 +- .../arg_inplace_observe_after.rs | 2 +- .../arg_inplace_observe_during.rs | 2 +- .../return_pointer_aliasing_read.rs | 2 +- .../return_pointer_aliasing_write.rs | 2 +- ...return_pointer_aliasing_write_tail_call.rs | 2 +- .../simd_feature_flag_difference.rs | 2 +- .../tests/fail/intrinsics/ctlz_nonzero.rs | 2 +- .../tests/fail/intrinsics/cttz_nonzero.rs | 2 +- src/tools/miri/tests/fail/issue-miri-1112.rs | 2 +- .../miri/tests/fail/overlapping_assignment.rs | 4 +-- .../retag_data_race_read.tree.stderr | 2 +- src/tools/miri/tests/fail/tls_macro_leak.rs | 2 +- src/tools/miri/tests/fail/tls_static_leak.rs | 2 +- .../fail/tree_borrows/alternate-read-write.rs | 2 +- .../fail/tree_borrows/cell-inside-struct.rs | 2 +- .../repeated_foreign_read_lazy_conflicted.rs | 2 +- .../fail/tree_borrows/write-during-2phase.rs | 2 +- .../fail/uninit/padding-struct-in-union.rs | 2 +- .../tests/fail/validity/invalid_char_cast.rs | 2 +- .../tests/fail/validity/invalid_char_match.rs | 2 +- .../tests/fail/validity/invalid_enum_cast.rs | 2 +- .../tests/fail/weak_memory/weak_uninit.rs | 2 +- src/tools/miri/tests/panic/mir-validation.rs | 2 +- .../concurrency/tls_pthread_drop_order.rs | 2 +- .../tests/pass-dep/foreign-fn-linkname.rs | 2 +- .../miri/tests/pass-dep/regions-mock-trans.rs | 2 +- src/tools/miri/tests/pass-dep/wcslen.rs | 2 +- .../tests/pass/0weak_memory/consistency.rs | 4 +-- .../tests/pass/0weak_memory/consistency_sc.rs | 2 +- .../miri/tests/pass/0weak_memory/extra_cpp.rs | 2 +- .../miri/tests/pass/0weak_memory/weak.rs | 4 +-- .../align_repeat_into_well_aligned_array.rs | 2 +- .../miri/tests/pass/async-closure-captures.rs | 2 +- .../miri/tests/pass/async-closure-drop.rs | 4 +-- src/tools/miri/tests/pass/async-closure.rs | 4 +-- src/tools/miri/tests/pass/binops.rs | 2 +- src/tools/miri/tests/pass/btreemap.rs | 2 +- src/tools/miri/tests/pass/calls.rs | 2 +- .../miri/tests/pass/concurrency/data_race.rs | 6 ++-- .../concurrency/disable_data_race_detector.rs | 2 +- src/tools/miri/tests/pass/const-vec-of-fns.rs | 2 +- .../tests/pass/drop_type_without_drop_glue.rs | 2 +- src/tools/miri/tests/pass/dst-raw.rs | 2 +- src/tools/miri/tests/pass/dst-struct-sole.rs | 2 +- src/tools/miri/tests/pass/dst-struct.rs | 2 +- .../enum-nullable-const-null-with-fields.rs | 2 +- src/tools/miri/tests/pass/float.rs | 30 +++++++++---------- .../function_calls/return_place_on_heap.rs | 2 +- src/tools/miri/tests/pass/integer-ops.rs | 2 +- .../miri/tests/pass/intrinsics/integer.rs | 2 +- .../miri/tests/pass/intrinsics/volatile.rs | 2 +- .../miri/tests/pass/issues/issue-30530.rs | 2 +- .../miri/tests/pass/issues/issue-3794.rs | 2 +- .../miri/tests/pass/issues/issue-5917.rs | 2 +- .../miri/tests/pass/issues/issue-miri-184.rs | 2 +- .../miri/tests/pass/issues/issue-miri-2068.rs | 2 +- ...iri-3541-dyn-vtable-trait-normalization.rs | 2 +- .../miri/tests/pass/last-use-in-cap-clause.rs | 2 +- src/tools/miri/tests/pass/loop-break-value.rs | 2 +- .../miri/tests/pass/move-arg-2-unique.rs | 2 +- .../miri/tests/pass/move-arg-3-unique.rs | 2 +- src/tools/miri/tests/pass/mpsc.rs | 2 +- .../miri/tests/pass/panic/unwind_dwarf.rs | 4 +-- .../regions-lifetime-nonfree-late-bound.rs | 2 +- src/tools/miri/tests/pass/sendable-class.rs | 2 +- .../miri/tests/pass/tag-align-dyn-u64.rs | 2 +- .../pass/tls/tls_leak_main_thread_allowed.rs | 2 +- .../pass/tree_borrows/cell-inside-box.rs | 2 +- .../pass/tree_borrows/cell-inside-struct.rs | 2 +- .../pass/tree_borrows/copy-nonoverlapping.rs | 2 +- .../tests/pass/tree_borrows/tree-borrows.rs | 4 +-- src/tools/miri/tests/pass/unsized.rs | 8 ++--- .../miri/tests/pass/vec-matching-fold.rs | 2 +- src/tools/miri/tests/ui.rs | 5 ---- 113 files changed, 156 insertions(+), 161 deletions(-) diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.rs b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.rs index a9efe17fddfab..a8494eaf0aa49 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.rs +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.rs @@ -2,7 +2,7 @@ //@[tree]compile-flags: -Zmiri-tree-borrows use std::mem; -pub fn safe(x: &mut i32, y: &mut i32) { +fn safe(x: &mut i32, y: &mut i32) { //~[stack]^ ERROR: protect *x = 1; //~[tree] ERROR: /write access through .* is forbidden/ *y = 2; diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.stack.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.stack.stderr index b7fdc7bc414e4..196eaeb3fb6c2 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: not granting access to tag because that would remove [Unique for ] which is strongly protected --> tests/fail/both_borrows/aliasing_mut1.rs:LL:CC | -LL | pub fn safe(x: &mut i32, y: &mut i32) { - | ^ Undefined Behavior occurred here +LL | fn safe(x: &mut i32, y: &mut i32) { + | ^ Undefined Behavior occurred here | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information @@ -14,8 +14,8 @@ LL | let xraw: *mut i32 = unsafe { mem::transmute(&mut x) }; help: is this argument --> tests/fail/both_borrows/aliasing_mut1.rs:LL:CC | -LL | pub fn safe(x: &mut i32, y: &mut i32) { - | ^ +LL | fn safe(x: &mut i32, y: &mut i32) { + | ^ = note: BACKTRACE (of the first span): = note: inside `safe` at tests/fail/both_borrows/aliasing_mut1.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree.stderr index 207ed3131af37..b9e6e25478062 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree.stderr @@ -10,13 +10,13 @@ LL | *x = 1; help: the accessed tag was created here, in the initial state Reserved --> tests/fail/both_borrows/aliasing_mut1.rs:LL:CC | -LL | pub fn safe(x: &mut i32, y: &mut i32) { - | ^ +LL | fn safe(x: &mut i32, y: &mut i32) { + | ^ help: the accessed tag later transitioned to Reserved (conflicted) due to a reborrow (acting as a foreign read access) at offsets [0x0..0x4] --> tests/fail/both_borrows/aliasing_mut1.rs:LL:CC | -LL | pub fn safe(x: &mut i32, y: &mut i32) { - | ^ +LL | fn safe(x: &mut i32, y: &mut i32) { + | ^ = help: this transition corresponds to a temporary loss of write permissions until function exit = note: BACKTRACE (of the first span): = note: inside `safe` at tests/fail/both_borrows/aliasing_mut1.rs:LL:CC diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.rs b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.rs index 74ea2b28627c1..c1320a25cafa4 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.rs +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.rs @@ -2,7 +2,7 @@ //@[tree]compile-flags: -Zmiri-tree-borrows use std::mem; -pub fn safe(x: &i32, y: &mut i32) { +fn safe(x: &i32, y: &mut i32) { //~[stack]^ ERROR: protect let _v = *x; *y = 2; //~[tree] ERROR: /write access through .* is forbidden/ diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.stack.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.stack.stderr index a13cbec66552a..e70e5b10793c1 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: not granting access to tag because that would remove [SharedReadOnly for ] which is strongly protected --> tests/fail/both_borrows/aliasing_mut2.rs:LL:CC | -LL | pub fn safe(x: &i32, y: &mut i32) { - | ^ Undefined Behavior occurred here +LL | fn safe(x: &i32, y: &mut i32) { + | ^ Undefined Behavior occurred here | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information @@ -14,8 +14,8 @@ LL | let xref = &mut x; help: is this argument --> tests/fail/both_borrows/aliasing_mut2.rs:LL:CC | -LL | pub fn safe(x: &i32, y: &mut i32) { - | ^ +LL | fn safe(x: &i32, y: &mut i32) { + | ^ = note: BACKTRACE (of the first span): = note: inside `safe` at tests/fail/both_borrows/aliasing_mut2.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree.stderr index 90b1b1294c7f3..aed59b21f1379 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree.stderr @@ -10,8 +10,8 @@ LL | *y = 2; help: the accessed tag was created here, in the initial state Reserved --> tests/fail/both_borrows/aliasing_mut2.rs:LL:CC | -LL | pub fn safe(x: &i32, y: &mut i32) { - | ^ +LL | fn safe(x: &i32, y: &mut i32) { + | ^ help: the accessed tag later transitioned to Reserved (conflicted) due to a foreign read access at offsets [0x0..0x4] --> tests/fail/both_borrows/aliasing_mut2.rs:LL:CC | diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.rs b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.rs index 8cb60faf2d078..555e4478224c8 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.rs +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.rs @@ -2,7 +2,7 @@ //@[tree]compile-flags: -Zmiri-tree-borrows use std::mem; -pub fn safe(x: &mut i32, y: &i32) { +fn safe(x: &mut i32, y: &i32) { //~[stack]^ ERROR: borrow stack *x = 1; //~[tree] ERROR: /write access through .* is forbidden/ let _v = *y; diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.stack.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.stack.stderr index 0e9382be2e8f8..9980d14e10549 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: trying to retag from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> tests/fail/both_borrows/aliasing_mut3.rs:LL:CC | -LL | pub fn safe(x: &mut i32, y: &i32) { - | ^ this error occurs as part of function-entry retag at ALLOC[0x0..0x4] +LL | fn safe(x: &mut i32, y: &i32) { + | ^ this error occurs as part of function-entry retag at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree.stderr index 73a5027646388..357d7d220192a 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree.stderr @@ -10,13 +10,13 @@ LL | *x = 1; help: the accessed tag was created here, in the initial state Reserved --> tests/fail/both_borrows/aliasing_mut3.rs:LL:CC | -LL | pub fn safe(x: &mut i32, y: &i32) { - | ^ +LL | fn safe(x: &mut i32, y: &i32) { + | ^ help: the accessed tag later transitioned to Reserved (conflicted) due to a reborrow (acting as a foreign read access) at offsets [0x0..0x4] --> tests/fail/both_borrows/aliasing_mut3.rs:LL:CC | -LL | pub fn safe(x: &mut i32, y: &i32) { - | ^ +LL | fn safe(x: &mut i32, y: &i32) { + | ^ = help: this transition corresponds to a temporary loss of write permissions until function exit = note: BACKTRACE (of the first span): = note: inside `safe` at tests/fail/both_borrows/aliasing_mut3.rs:LL:CC diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.rs b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.rs index c656a5096445e..22484972f4d1c 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.rs +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.rs @@ -5,7 +5,7 @@ use std::cell::Cell; use std::mem; // Make sure &mut UnsafeCell also is exclusive -pub fn safe(x: &i32, y: &mut Cell) { +fn safe(x: &i32, y: &mut Cell) { //~[stack]^ ERROR: protect y.set(1); let _load = *x; diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.stack.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.stack.stderr index c5ad269b39acd..eb2514df588a6 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: not granting access to tag because that would remove [SharedReadOnly for ] which is strongly protected --> tests/fail/both_borrows/aliasing_mut4.rs:LL:CC | -LL | pub fn safe(x: &i32, y: &mut Cell) { - | ^ Undefined Behavior occurred here +LL | fn safe(x: &i32, y: &mut Cell) { + | ^ Undefined Behavior occurred here | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information @@ -14,8 +14,8 @@ LL | let xref = &mut x; help: is this argument --> tests/fail/both_borrows/aliasing_mut4.rs:LL:CC | -LL | pub fn safe(x: &i32, y: &mut Cell) { - | ^ +LL | fn safe(x: &i32, y: &mut Cell) { + | ^ = note: BACKTRACE (of the first span): = note: inside `safe` at tests/fail/both_borrows/aliasing_mut4.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree.stderr index a6a6da3fa2aec..c06ae0e92138e 100644 --- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree.stderr @@ -17,8 +17,8 @@ LL | y.set(1); help: the protected tag was created here, in the initial state Frozen --> tests/fail/both_borrows/aliasing_mut4.rs:LL:CC | -LL | pub fn safe(x: &i32, y: &mut Cell) { - | ^ +LL | fn safe(x: &i32, y: &mut Cell) { + | ^ = note: BACKTRACE (of the first span): = note: inside `std::mem::replace::` at RUSTLIB/core/src/mem/mod.rs:LL:CC = note: inside `std::cell::Cell::::replace` at RUSTLIB/core/src/cell.rs:LL:CC diff --git a/src/tools/miri/tests/fail/data_race/alloc_read_race.rs b/src/tools/miri/tests/fail/data_race/alloc_read_race.rs index 7c5116989943e..65622b7f77830 100644 --- a/src/tools/miri/tests/fail/data_race/alloc_read_race.rs +++ b/src/tools/miri/tests/fail/data_race/alloc_read_race.rs @@ -12,7 +12,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { // Shared atomic pointer let pointer = AtomicPtr::new(null_mut::>()); let ptr = EvilSend(&pointer as *const AtomicPtr>); diff --git a/src/tools/miri/tests/fail/data_race/alloc_write_race.rs b/src/tools/miri/tests/fail/data_race/alloc_write_race.rs index ba8a888de9ea0..a07d284c6c8c7 100644 --- a/src/tools/miri/tests/fail/data_race/alloc_write_race.rs +++ b/src/tools/miri/tests/fail/data_race/alloc_write_race.rs @@ -11,7 +11,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { // Shared atomic pointer let pointer = AtomicPtr::new(null_mut::()); let ptr = EvilSend(&pointer as *const AtomicPtr); diff --git a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs index 8cce54603ce57..90fd535f1cb6a 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race1.rs @@ -10,7 +10,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { let mut a = AtomicUsize::new(0); let b = &mut a as *mut AtomicUsize; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs index b6c0ef37cb920..8c59713010af6 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_read_na_write_race2.rs @@ -10,7 +10,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { let mut a = AtomicUsize::new(0); let b = &mut a as *mut AtomicUsize; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs index 03ae6895c5745..95b216a6fe649 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race1.rs @@ -10,7 +10,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { let mut a = AtomicUsize::new(0); let b = &mut a as *mut AtomicUsize; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs index 4a5edf5cc14dd..c4714e632c3ba 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_read_race2.rs @@ -10,7 +10,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { let mut a = AtomicUsize::new(0); let b = &mut a as *mut AtomicUsize; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs index e8d930a51dee3..3ef60074fd432 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race1.rs @@ -10,7 +10,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { let mut a = AtomicUsize::new(0); let b = &mut a as *mut AtomicUsize; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs index 4c67d2d765415..b704468165a45 100644 --- a/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs +++ b/src/tools/miri/tests/fail/data_race/atomic_write_na_write_race2.rs @@ -10,7 +10,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { let mut a = AtomicUsize::new(0); let b = &mut a as *mut AtomicUsize; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs b/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs index 999cc2392f5af..64bababe0c853 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race1.rs @@ -16,7 +16,7 @@ extern "Rust" { fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); } -pub fn main() { +fn main() { // Shared atomic pointer let pointer: *mut usize = Box::into_raw(Box::new(0usize)); let ptr = EvilSend(pointer); diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs b/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs index bd3b037e58381..6e85bcf03aa50 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race2.rs @@ -16,7 +16,7 @@ extern "Rust" { fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); } -pub fn main() { +fn main() { // Shared atomic pointer let pointer: *mut usize = Box::into_raw(Box::new(0usize)); let ptr = EvilSend(pointer); diff --git a/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs b/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs index e3d06660aab34..76c26da057820 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_read_race_stack.rs @@ -12,7 +12,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { // Shared atomic pointer let pointer = AtomicPtr::new(null_mut::()); let ptr = EvilSend(&pointer as *const AtomicPtr); diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs b/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs index 90e87f8c49564..fd71ef09b1253 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race1.rs @@ -15,7 +15,7 @@ extern "Rust" { #[rustc_std_internal_symbol] fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); } -pub fn main() { +fn main() { // Shared atomic pointer let pointer: *mut usize = Box::into_raw(Box::new(0usize)); let ptr = EvilSend(pointer); diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs b/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs index d9b1af80af493..5c8bbc14a4927 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race2.rs @@ -15,7 +15,7 @@ extern "Rust" { #[rustc_std_internal_symbol] fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); } -pub fn main() { +fn main() { // Shared atomic pointer let pointer: *mut usize = Box::into_raw(Box::new(0usize)); let ptr = EvilSend(pointer); diff --git a/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs b/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs index c1ab1942c6884..bdc25100abaf1 100644 --- a/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs +++ b/src/tools/miri/tests/fail/data_race/dealloc_write_race_stack.rs @@ -12,7 +12,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { // Shared atomic pointer let pointer = AtomicPtr::new(null_mut::()); let ptr = EvilSend(&pointer as *const AtomicPtr); diff --git a/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs b/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs index 67af6862737dc..4d716f7db6fda 100644 --- a/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs +++ b/src/tools/miri/tests/fail/data_race/enable_after_join_to_main.rs @@ -9,7 +9,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { // Enable and then join with multiple threads. let t1 = spawn(|| ()); let t2 = spawn(|| ()); diff --git a/src/tools/miri/tests/fail/data_race/read_write_race.rs b/src/tools/miri/tests/fail/data_race/read_write_race.rs index 2aadef36c5b95..e7961a4d849cc 100644 --- a/src/tools/miri/tests/fail/data_race/read_write_race.rs +++ b/src/tools/miri/tests/fail/data_race/read_write_race.rs @@ -9,7 +9,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { let mut a = 0u32; let b = &mut a as *mut u32; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs b/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs index cca39bb002c17..4555a82df6c97 100644 --- a/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs +++ b/src/tools/miri/tests/fail/data_race/read_write_race_stack.rs @@ -12,7 +12,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { // Shared atomic pointer let pointer = AtomicPtr::new(null_mut::()); let ptr = EvilSend(&pointer as *const AtomicPtr); diff --git a/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs b/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs index 262c039e4ae17..67e1d65126fa3 100644 --- a/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs +++ b/src/tools/miri/tests/fail/data_race/relax_acquire_race.rs @@ -12,7 +12,7 @@ unsafe impl Sync for EvilSend {} static SYNC: AtomicUsize = AtomicUsize::new(0); -pub fn main() { +fn main() { let mut a = 0u32; let b = &mut a as *mut u32; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/release_seq_race.rs b/src/tools/miri/tests/fail/data_race/release_seq_race.rs index 8aeb6ee6ef1d1..5016617e5d7fa 100644 --- a/src/tools/miri/tests/fail/data_race/release_seq_race.rs +++ b/src/tools/miri/tests/fail/data_race/release_seq_race.rs @@ -13,7 +13,7 @@ unsafe impl Sync for EvilSend {} static SYNC: AtomicUsize = AtomicUsize::new(0); -pub fn main() { +fn main() { let mut a = 0u32; let b = &mut a as *mut u32; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs b/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs index f465160718f45..ae6a6154e3c64 100644 --- a/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs +++ b/src/tools/miri/tests/fail/data_race/release_seq_race_same_thread.rs @@ -12,7 +12,7 @@ unsafe impl Sync for EvilSend {} static SYNC: AtomicUsize = AtomicUsize::new(0); -pub fn main() { +fn main() { let mut a = 0u32; let b = &mut a as *mut u32; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/rmw_race.rs b/src/tools/miri/tests/fail/data_race/rmw_race.rs index 39588c15ec7ea..51fddbd684724 100644 --- a/src/tools/miri/tests/fail/data_race/rmw_race.rs +++ b/src/tools/miri/tests/fail/data_race/rmw_race.rs @@ -12,7 +12,7 @@ unsafe impl Sync for EvilSend {} static SYNC: AtomicUsize = AtomicUsize::new(0); -pub fn main() { +fn main() { let mut a = 0u32; let b = &mut a as *mut u32; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/write_write_race.rs b/src/tools/miri/tests/fail/data_race/write_write_race.rs index b1a6b08b4c886..2070c43b7cff6 100644 --- a/src/tools/miri/tests/fail/data_race/write_write_race.rs +++ b/src/tools/miri/tests/fail/data_race/write_write_race.rs @@ -9,7 +9,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { let mut a = 0u32; let b = &mut a as *mut u32; let c = EvilSend(b); diff --git a/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs b/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs index cd21b0a8fa6c1..b92d17bf8bcd2 100644 --- a/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs +++ b/src/tools/miri/tests/fail/data_race/write_write_race_stack.rs @@ -12,7 +12,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { // Shared atomic pointer let pointer = AtomicPtr::new(null_mut::()); let ptr = EvilSend(&pointer as *const AtomicPtr); diff --git a/src/tools/miri/tests/fail/enum-set-discriminant-niche-variant-wrong.rs b/src/tools/miri/tests/fail/enum-set-discriminant-niche-variant-wrong.rs index eca6d908b448b..221ea106538ed 100644 --- a/src/tools/miri/tests/fail/enum-set-discriminant-niche-variant-wrong.rs +++ b/src/tools/miri/tests/fail/enum-set-discriminant-niche-variant-wrong.rs @@ -25,7 +25,7 @@ fn set_discriminant(ptr: &mut Option>) { } } -pub fn main() { +fn main() { let mut v = None; set_discriminant(&mut v); } diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs index 744d64b9b1e88..8858658eddbf2 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs @@ -28,7 +28,7 @@ fn main() { } } -pub fn callee(x: S, mut y: S) { +fn callee(x: S, mut y: S) { // With the setup above, if `x` and `y` are both moved, // then writing to `y` will change the value stored in `x`! y.0 = 0; diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs index dff724f8d9657..3cb8ee2b407c4 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs @@ -29,6 +29,6 @@ fn main() { } } -pub fn callee(x: S) -> S { +fn callee(x: S) -> S { x } diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs index 4f7a12ebd2e8b..c61083c17a6b2 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs @@ -22,7 +22,7 @@ fn main() { } } -pub fn callee(x: S, ptr: *mut S) { +fn callee(x: S, ptr: *mut S) { // With the setup above, if `x` is indeed moved in // (i.e. we actually just get a pointer to the underlying storage), // then writing to `ptr` will change the value stored in `x`! diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs index 18daf9497a15b..d1b8f3eed059e 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs @@ -22,6 +22,6 @@ fn main() { } } -pub fn change_arg(mut x: S) { +fn change_arg(mut x: S) { x.0 = 0; } diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs index 2201bf17bfc78..97c5cb75893ee 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs @@ -23,7 +23,7 @@ fn main() { } } -pub fn change_arg(mut x: S, ptr: *mut S) { +fn change_arg(mut x: S, ptr: *mut S) { x.0 = 0; // If `x` got passed in-place, we'd see the write through `ptr`! // Make sure we are not allowed to do that read. diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.rs index dc22e129e18a2..d981286a141aa 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.rs +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.rs @@ -7,7 +7,7 @@ use std::intrinsics::mir::*; #[custom_mir(dialect = "runtime", phase = "optimized")] -pub fn main() { +fn main() { mir! { { let _x = 0; diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.rs index 2fddaf37235b2..a4e48b0aac6fe 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.rs +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.rs @@ -7,7 +7,7 @@ use std::intrinsics::mir::*; #[custom_mir(dialect = "runtime", phase = "optimized")] -pub fn main() { +fn main() { mir! { { let _x = 0; diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs index 5f3ecb6502273..3390ddad9b857 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs @@ -9,7 +9,7 @@ use std::intrinsics::mir::*; #[custom_mir(dialect = "runtime", phase = "optimized")] -pub fn main() { +fn main() { mir! { { let _x = 0; diff --git a/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.rs b/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.rs index 200f1062a3e80..0dd5d09abdeab 100644 --- a/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.rs +++ b/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.rs @@ -9,7 +9,7 @@ pub unsafe extern "C" fn foo(_y: f32, x: __m256) -> __m256 { x } -pub fn bar(x: __m256) -> __m256 { +fn bar(x: __m256) -> __m256 { // The first and second argument get mixed up here since caller // and callee do not have the same feature flags. // In Miri, we don't have a concept of "dynamically available feature flags", diff --git a/src/tools/miri/tests/fail/intrinsics/ctlz_nonzero.rs b/src/tools/miri/tests/fail/intrinsics/ctlz_nonzero.rs index 3da54b9188262..8ea029190276a 100644 --- a/src/tools/miri/tests/fail/intrinsics/ctlz_nonzero.rs +++ b/src/tools/miri/tests/fail/intrinsics/ctlz_nonzero.rs @@ -1,6 +1,6 @@ #![feature(core_intrinsics)] -pub fn main() { +fn main() { unsafe { use std::intrinsics::*; diff --git a/src/tools/miri/tests/fail/intrinsics/cttz_nonzero.rs b/src/tools/miri/tests/fail/intrinsics/cttz_nonzero.rs index 2b68f6713d806..471347a752d28 100644 --- a/src/tools/miri/tests/fail/intrinsics/cttz_nonzero.rs +++ b/src/tools/miri/tests/fail/intrinsics/cttz_nonzero.rs @@ -1,6 +1,6 @@ #![feature(core_intrinsics)] -pub fn main() { +fn main() { unsafe { use std::intrinsics::*; diff --git a/src/tools/miri/tests/fail/issue-miri-1112.rs b/src/tools/miri/tests/fail/issue-miri-1112.rs index 387253a3f9872..fc21fff9a9f92 100644 --- a/src/tools/miri/tests/fail/issue-miri-1112.rs +++ b/src/tools/miri/tests/fail/issue-miri-1112.rs @@ -11,7 +11,7 @@ pub struct Meta { } impl Meta { - pub fn new() -> Self { + fn new() -> Self { Meta { drop_fn: |_| {}, size: 0, align: 1 } } } diff --git a/src/tools/miri/tests/fail/overlapping_assignment.rs b/src/tools/miri/tests/fail/overlapping_assignment.rs index 84994c179f9ea..237d674513f0e 100644 --- a/src/tools/miri/tests/fail/overlapping_assignment.rs +++ b/src/tools/miri/tests/fail/overlapping_assignment.rs @@ -7,7 +7,7 @@ use std::intrinsics::mir::*; // which wants to prevent overlapping assignments... // So we use two separate pointer arguments, and then arrange for them to alias. #[custom_mir(dialect = "runtime", phase = "optimized")] -pub fn self_copy(ptr1: *mut [i32; 4], ptr2: *mut [i32; 4]) { +fn self_copy(ptr1: *mut [i32; 4], ptr2: *mut [i32; 4]) { mir! { { *ptr1 = *ptr2; //~ERROR: overlapping ranges @@ -16,7 +16,7 @@ pub fn self_copy(ptr1: *mut [i32; 4], ptr2: *mut [i32; 4]) { } } -pub fn main() { +fn main() { let mut x = [0; 4]; let ptr = std::ptr::addr_of_mut!(x); self_copy(ptr, ptr); diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.tree.stderr b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.tree.stderr index 675bb01b5e751..e6c1745d321c1 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.tree.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.tree.stderr @@ -16,7 +16,7 @@ LL | panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_e help: the protected tag was created here, in the initial state Active --> RUSTLIB/std/src/panic.rs:LL:CC | -LL | pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { +LL | fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { | ^ = note: BACKTRACE (of the first span): = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC diff --git a/src/tools/miri/tests/fail/tls_macro_leak.rs b/src/tools/miri/tests/fail/tls_macro_leak.rs index b8a4b81911aca..1aa161061c025 100644 --- a/src/tools/miri/tests/fail/tls_macro_leak.rs +++ b/src/tools/miri/tests/fail/tls_macro_leak.rs @@ -2,7 +2,7 @@ use std::cell::Cell; -pub fn main() { +fn main() { thread_local! { static TLS: Cell> = Cell::new(None); } diff --git a/src/tools/miri/tests/fail/tls_static_leak.rs b/src/tools/miri/tests/fail/tls_static_leak.rs index 4d52803363778..4e29b3afe3b19 100644 --- a/src/tools/miri/tests/fail/tls_static_leak.rs +++ b/src/tools/miri/tests/fail/tls_static_leak.rs @@ -6,7 +6,7 @@ use std::cell::Cell; /// Ensure that leaks through `thread_local` statics *not in the main thread* /// are detected. -pub fn main() { +fn main() { #[thread_local] static TLS: Cell> = Cell::new(None); diff --git a/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.rs b/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.rs index fee88cf3486af..8d49a3cdbf5f3 100644 --- a/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.rs +++ b/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.rs @@ -2,7 +2,7 @@ // Check that TB properly rejects alternating Reads and Writes, but tolerates // alternating only Reads to Reserved mutable references. -pub fn main() { +fn main() { let x = &mut 0u8; let y = unsafe { &mut *(x as *mut u8) }; // Foreign Read, but this is a no-op from the point of view of y (still Reserved) diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs index ff7978776822b..1aefa217e2d47 100644 --- a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs +++ b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.rs @@ -11,7 +11,7 @@ struct Foo { field2: Cell, } -pub fn main() { +fn main() { let root = Foo { field1: 42, field2: Cell::new(88) }; unsafe { let a = &root; diff --git a/src/tools/miri/tests/fail/tree_borrows/repeated_foreign_read_lazy_conflicted.rs b/src/tools/miri/tests/fail/tree_borrows/repeated_foreign_read_lazy_conflicted.rs index 36b47a33b181e..4f0d97b4a1097 100644 --- a/src/tools/miri/tests/fail/tree_borrows/repeated_foreign_read_lazy_conflicted.rs +++ b/src/tools/miri/tests/fail/tree_borrows/repeated_foreign_read_lazy_conflicted.rs @@ -11,7 +11,7 @@ unsafe fn access_after_sub_1(x: &mut u8, orig_ptr: *mut u8) { *(x as *mut u8).byte_sub(1) = 42; //~ ERROR: /write access through .* is forbidden/ } -pub fn main() { +fn main() { unsafe { let mut alloc = [0u8, 0u8]; let orig_ptr = addr_of_mut!(alloc) as *mut u8; diff --git a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs index a2e8a533c43ad..ac9b7d7e528ee 100644 --- a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs +++ b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs @@ -14,7 +14,7 @@ impl Foo { } } -pub fn main() { +fn main() { let mut f = Foo(0); let alias = &mut f.0 as *mut u64; let res = f.add(unsafe { diff --git a/src/tools/miri/tests/fail/uninit/padding-struct-in-union.rs b/src/tools/miri/tests/fail/uninit/padding-struct-in-union.rs index 132b85828362d..8343952a2046a 100644 --- a/src/tools/miri/tests/fail/uninit/padding-struct-in-union.rs +++ b/src/tools/miri/tests/fail/uninit/padding-struct-in-union.rs @@ -18,7 +18,7 @@ union FooBar { bar: Bar, } -pub fn main() { +fn main() { // Initialize as u8 to ensure padding bytes are zeroed. let mut foobar = FooBar { bar: Bar { bytes: [0u8; 8] } }; // Reading either field is ok. diff --git a/src/tools/miri/tests/fail/validity/invalid_char_cast.rs b/src/tools/miri/tests/fail/validity/invalid_char_cast.rs index 6a590dc7ba10b..94e1a87fba75e 100644 --- a/src/tools/miri/tests/fail/validity/invalid_char_cast.rs +++ b/src/tools/miri/tests/fail/validity/invalid_char_cast.rs @@ -15,7 +15,7 @@ fn cast(ptr: *const char) -> u32 { } } -pub fn main() { +fn main() { let v = u32::MAX; cast(&v as *const u32 as *const char); } diff --git a/src/tools/miri/tests/fail/validity/invalid_char_match.rs b/src/tools/miri/tests/fail/validity/invalid_char_match.rs index 6c2e65b2bb744..6ec5768162be9 100644 --- a/src/tools/miri/tests/fail/validity/invalid_char_match.rs +++ b/src/tools/miri/tests/fail/validity/invalid_char_match.rs @@ -20,7 +20,7 @@ fn switch_int(ptr: *const char) { } } -pub fn main() { +fn main() { let v = u32::MAX; switch_int(&v as *const u32 as *const char); } diff --git a/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs b/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs index ed451a435b958..ba110ca96d693 100644 --- a/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs +++ b/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs @@ -15,7 +15,7 @@ fn cast(ptr: *const E) { } } -pub fn main() { +fn main() { let v = u32::MAX; cast(&v as *const u32 as *const E); } diff --git a/src/tools/miri/tests/fail/weak_memory/weak_uninit.rs b/src/tools/miri/tests/fail/weak_memory/weak_uninit.rs index b4b4b08498773..7a4e038fabf79 100644 --- a/src/tools/miri/tests/fail/weak_memory/weak_uninit.rs +++ b/src/tools/miri/tests/fail/weak_memory/weak_uninit.rs @@ -34,7 +34,7 @@ fn relaxed() { j2.join().unwrap(); } -pub fn main() { +fn main() { // If we try often enough, we should hit UB. for _ in 0..100 { relaxed(); diff --git a/src/tools/miri/tests/panic/mir-validation.rs b/src/tools/miri/tests/panic/mir-validation.rs index 2d0d530754d8c..11c4e395920c0 100644 --- a/src/tools/miri/tests/panic/mir-validation.rs +++ b/src/tools/miri/tests/panic/mir-validation.rs @@ -13,7 +13,7 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "runtime", phase = "optimized")] -pub fn main() { +fn main() { mir! { let x: i32; let tuple: (*mut i32,); diff --git a/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs b/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs index b57386000404f..df42780021610 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs @@ -29,7 +29,7 @@ pub unsafe fn set(key: Key, value: *mut u8) { assert_eq!(r, 0); } -pub fn record(r: usize) { +fn record(r: usize) { assert!(r < 10); unsafe { RECORD = RECORD * 10 + r }; } diff --git a/src/tools/miri/tests/pass-dep/foreign-fn-linkname.rs b/src/tools/miri/tests/pass-dep/foreign-fn-linkname.rs index 9f090a4eff5d8..afd5bc0d3b564 100644 --- a/src/tools/miri/tests/pass-dep/foreign-fn-linkname.rs +++ b/src/tools/miri/tests/pass-dep/foreign-fn-linkname.rs @@ -14,7 +14,7 @@ fn strlen(str: String) -> usize { unsafe { mlibc::my_strlen(s.as_ptr()) as usize } } -pub fn main() { +fn main() { let len = strlen("Rust".to_string()); assert_eq!(len, 4); } diff --git a/src/tools/miri/tests/pass-dep/regions-mock-trans.rs b/src/tools/miri/tests/pass-dep/regions-mock-trans.rs index 57f1b75f4d52b..7defea3c4876e 100644 --- a/src/tools/miri/tests/pass-dep/regions-mock-trans.rs +++ b/src/tools/miri/tests/pass-dep/regions-mock-trans.rs @@ -39,7 +39,7 @@ fn f(ccx: &Ccx) { return g(&fcx); } -pub fn main() { +fn main() { let ccx = Ccx { x: 0 }; f(&ccx); } diff --git a/src/tools/miri/tests/pass-dep/wcslen.rs b/src/tools/miri/tests/pass-dep/wcslen.rs index c5c9d99247965..2ec4da79d727b 100644 --- a/src/tools/miri/tests/pass-dep/wcslen.rs +++ b/src/tools/miri/tests/pass-dep/wcslen.rs @@ -13,7 +13,7 @@ fn to_c_wchar_t_str(s: &str) -> Vec { r } -pub fn main() { +fn main() { let s = to_c_wchar_t_str("Rust"); let len = unsafe { libc::wcslen(s.as_ptr()) }; assert_eq!(len, 4); diff --git a/src/tools/miri/tests/pass/0weak_memory/consistency.rs b/src/tools/miri/tests/pass/0weak_memory/consistency.rs index 16a38ebd9d4da..83674cca1bde4 100644 --- a/src/tools/miri/tests/pass/0weak_memory/consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory/consistency.rs @@ -212,7 +212,7 @@ fn test_single_thread() { fn test_sync_through_rmw_and_fences() { // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905 #[no_mangle] - pub fn rdmw(storing: &AtomicI32, sync: &AtomicI32, loading: &AtomicI32) -> i32 { + fn rdmw(storing: &AtomicI32, sync: &AtomicI32, loading: &AtomicI32) -> i32 { storing.store(1, Relaxed); fence(Release); sync.fetch_add(0, Relaxed); @@ -245,7 +245,7 @@ fn test_sync_through_rmw_and_fences() { assert_ne!((a, b), (0, 0)); } -pub fn main() { +fn main() { for _ in 0..50 { test_single_thread(); test_mixed_access(); diff --git a/src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs b/src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs index cb8535b8ad74b..d92c0d1779971 100644 --- a/src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs +++ b/src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs @@ -348,7 +348,7 @@ fn test_sc_relaxed() { assert!(!bad); } -pub fn main() { +fn main() { for _ in 0..32 { test_sc_store_buffering(); test_iriw_sc_rlx(); diff --git a/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs b/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs index 94df730808066..6791382f8e0f9 100644 --- a/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs +++ b/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs @@ -72,7 +72,7 @@ fn from_mut_split() { assert_eq!(x_lo_atomic.load(Relaxed), u16::from_be(0xfafa)); } -pub fn main() { +fn main() { get_mut_write(); from_mut_split(); assign_to_mut(); diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs index 611733d0dac52..e329bbff1aaed 100644 --- a/src/tools/miri/tests/pass/0weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs @@ -115,7 +115,7 @@ fn initialization_write(add_fence: bool) { fn faa_replaced_by_load() { check_all_outcomes([true, false], || { // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905 - pub fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize { + fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize { storing.store(1, Relaxed); fence(Release); // sync.fetch_add(0, Relaxed); @@ -226,7 +226,7 @@ fn old_release_store() { }); } -pub fn main() { +fn main() { relaxed(); seq_cst(); initialization_write(false); diff --git a/src/tools/miri/tests/pass/align_repeat_into_well_aligned_array.rs b/src/tools/miri/tests/pass/align_repeat_into_well_aligned_array.rs index 735251039f772..6f1f28c46da28 100644 --- a/src/tools/miri/tests/pass/align_repeat_into_well_aligned_array.rs +++ b/src/tools/miri/tests/pass/align_repeat_into_well_aligned_array.rs @@ -24,7 +24,7 @@ pub const KEYBYTES: usize = 8 * size_of::(); pub const BLOCKBYTES: usize = 16 * size_of::(); impl Params { - pub fn new() -> Self { + fn new() -> Self { Self { hash_length: OUTBYTES as u8, key_length: 0, diff --git a/src/tools/miri/tests/pass/async-closure-captures.rs b/src/tools/miri/tests/pass/async-closure-captures.rs index ed6b7b205b540..785ff2bc02128 100644 --- a/src/tools/miri/tests/pass/async-closure-captures.rs +++ b/src/tools/miri/tests/pass/async-closure-captures.rs @@ -6,7 +6,7 @@ use std::future::Future; use std::pin::pin; use std::task::*; -pub fn block_on(fut: impl Future) -> T { +fn block_on(fut: impl Future) -> T { let mut fut = pin!(fut); let ctx = &mut Context::from_waker(Waker::noop()); diff --git a/src/tools/miri/tests/pass/async-closure-drop.rs b/src/tools/miri/tests/pass/async-closure-drop.rs index 105aa434b0ad9..d1fd92814d950 100644 --- a/src/tools/miri/tests/pass/async-closure-drop.rs +++ b/src/tools/miri/tests/pass/async-closure-drop.rs @@ -4,7 +4,7 @@ use std::future::Future; use std::pin::pin; use std::task::*; -pub fn block_on(fut: impl Future) -> T { +fn block_on(fut: impl Future) -> T { let mut fut = pin!(fut); let ctx = &mut Context::from_waker(Waker::noop()); @@ -29,7 +29,7 @@ impl Drop for DropMe { } } -pub fn main() { +fn main() { block_on(async { let b = DropMe("hello"); let async_closure = async move |a: DropMe| { diff --git a/src/tools/miri/tests/pass/async-closure.rs b/src/tools/miri/tests/pass/async-closure.rs index 4c0fb356f9db7..1b38f06eb7cd9 100644 --- a/src/tools/miri/tests/pass/async-closure.rs +++ b/src/tools/miri/tests/pass/async-closure.rs @@ -6,7 +6,7 @@ use std::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; use std::pin::pin; use std::task::*; -pub fn block_on(fut: impl Future) -> T { +fn block_on(fut: impl Future) -> T { let mut fut = pin!(fut); let ctx = &mut Context::from_waker(Waker::noop()); @@ -38,7 +38,7 @@ async fn call_normal_mut>(f: &mut impl FnMut(i32) -> F) { f(1).await; } -pub fn main() { +fn main() { block_on(async { let b = 2i32; let mut async_closure = async move |a: i32| { diff --git a/src/tools/miri/tests/pass/binops.rs b/src/tools/miri/tests/pass/binops.rs index fcbe6c85b7b8f..fa8993c342193 100644 --- a/src/tools/miri/tests/pass/binops.rs +++ b/src/tools/miri/tests/pass/binops.rs @@ -71,7 +71,7 @@ fn test_class() { assert!(q != r); } -pub fn main() { +fn main() { test_nil(); test_bool(); test_ptr(); diff --git a/src/tools/miri/tests/pass/btreemap.rs b/src/tools/miri/tests/pass/btreemap.rs index 7af6d7b5551c7..cfd01ce28719d 100644 --- a/src/tools/miri/tests/pass/btreemap.rs +++ b/src/tools/miri/tests/pass/btreemap.rs @@ -25,7 +25,7 @@ fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator } } -pub fn main() { +fn main() { let mut b = BTreeSet::new(); b.insert(Foo::A("\'")); b.insert(Foo::A("/=")); diff --git a/src/tools/miri/tests/pass/calls.rs b/src/tools/miri/tests/pass/calls.rs index 8db3d3590cc1e..4f7b27c41c5cd 100644 --- a/src/tools/miri/tests/pass/calls.rs +++ b/src/tools/miri/tests/pass/calls.rs @@ -35,7 +35,7 @@ fn const_fn_call() -> i64 { } fn call_return_into_passed_reference() { - pub fn func(v: &mut T, f: fn(&T) -> T) { + fn func(v: &mut T, f: fn(&T) -> T) { // MIR building will introduce a temporary, so this becomes // `let temp = f(v); *v = temp;`. // If this got optimized to `*v = f(v)` on the MIR level we'd have UB diff --git a/src/tools/miri/tests/pass/concurrency/data_race.rs b/src/tools/miri/tests/pass/concurrency/data_race.rs index d5dd1deb2d9dd..9c434e433d80f 100644 --- a/src/tools/miri/tests/pass/concurrency/data_race.rs +++ b/src/tools/miri/tests/pass/concurrency/data_race.rs @@ -57,7 +57,7 @@ fn test_multiple_reads() { assert_eq!(var, 10); } -pub fn test_rmw_no_block() { +fn test_rmw_no_block() { static SYNC: AtomicUsize = AtomicUsize::new(0); let mut a = 0u32; @@ -89,7 +89,7 @@ pub fn test_rmw_no_block() { } } -pub fn test_simple_release() { +fn test_simple_release() { static SYNC: AtomicUsize = AtomicUsize::new(0); let mut a = 0u32; @@ -214,7 +214,7 @@ fn failing_rmw_is_read() { }); } -pub fn main() { +fn main() { test_fence_sync(); test_multiple_reads(); test_rmw_no_block(); diff --git a/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs b/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs index ecc4ca59bd18a..26c63161b55b5 100644 --- a/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs +++ b/src/tools/miri/tests/pass/concurrency/disable_data_race_detector.rs @@ -10,7 +10,7 @@ struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} -pub fn main() { +fn main() { let mut a = 0u32; let b = &mut a as *mut u32; let c = EvilSend(b); diff --git a/src/tools/miri/tests/pass/const-vec-of-fns.rs b/src/tools/miri/tests/pass/const-vec-of-fns.rs index 7f0782fe32247..4bed4bb849e26 100644 --- a/src/tools/miri/tests/pass/const-vec-of-fns.rs +++ b/src/tools/miri/tests/pass/const-vec-of-fns.rs @@ -8,7 +8,7 @@ fn f() {} static mut CLOSURES: &'static mut [fn()] = &mut [f as fn(), f as fn()]; -pub fn main() { +fn main() { unsafe { for closure in &mut *CLOSURES { (*closure)() diff --git a/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs b/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs index 2f8665e8d62c0..9c4121baa637e 100644 --- a/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs +++ b/src/tools/miri/tests/pass/drop_type_without_drop_glue.rs @@ -15,7 +15,7 @@ fn drop_in_place_with_terminator(ptr: *mut i32) { } } -pub fn main() { +fn main() { drop_in_place_with_terminator(std::ptr::without_provenance_mut(0)); drop_in_place_with_terminator(std::ptr::without_provenance_mut(1)); } diff --git a/src/tools/miri/tests/pass/dst-raw.rs b/src/tools/miri/tests/pass/dst-raw.rs index 3d0b843b3da22..7ffef3320e029 100644 --- a/src/tools/miri/tests/pass/dst-raw.rs +++ b/src/tools/miri/tests/pass/dst-raw.rs @@ -19,7 +19,7 @@ struct Foo { f: T, } -pub fn main() { +fn main() { // raw trait object let x = A { f: 42 }; let z: *const dyn Trait = &x; diff --git a/src/tools/miri/tests/pass/dst-struct-sole.rs b/src/tools/miri/tests/pass/dst-struct-sole.rs index 4b25fbb063006..931601bcd9547 100644 --- a/src/tools/miri/tests/pass/dst-struct-sole.rs +++ b/src/tools/miri/tests/pass/dst-struct-sole.rs @@ -33,7 +33,7 @@ impl ToBar for Bar { } } -pub fn main() { +fn main() { // With a vec of ints. let f1 = Fat { ptr: [1, 2, 3] }; foo(&f1); diff --git a/src/tools/miri/tests/pass/dst-struct.rs b/src/tools/miri/tests/pass/dst-struct.rs index 59763bbbfdd36..cf92ebfa04454 100644 --- a/src/tools/miri/tests/pass/dst-struct.rs +++ b/src/tools/miri/tests/pass/dst-struct.rs @@ -48,7 +48,7 @@ impl ToBar for Bar { } } -pub fn main() { +fn main() { // With a vec of ints. let f1: Fat<[isize; 3]> = Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; foo(&f1); diff --git a/src/tools/miri/tests/pass/enum-nullable-const-null-with-fields.rs b/src/tools/miri/tests/pass/enum-nullable-const-null-with-fields.rs index 86f30f42b6293..8a320fcdb4d43 100644 --- a/src/tools/miri/tests/pass/enum-nullable-const-null-with-fields.rs +++ b/src/tools/miri/tests/pass/enum-nullable-const-null-with-fields.rs @@ -3,6 +3,6 @@ static C: Result<(), Box> = Ok(()); // This is because of yet another bad assertion (ICE) about the null side of a nullable enum. // So we won't actually compile if the bug is present, but we check the value in main anyway. -pub fn main() { +fn main() { assert!(C.is_ok()); } diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 67a14c2b38950..7b23518d73dad 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -1037,7 +1037,7 @@ fn mul_add() { assert_eq!(f.to_bits(), f32::to_bits(-0.0)); } -pub fn libm() { +fn libm() { fn ldexp(a: f64, b: i32) -> f64 { extern "C" { fn ldexp(x: f64, n: i32) -> f64; @@ -1300,7 +1300,7 @@ fn test_fast() { use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast}; #[inline(never)] - pub fn test_operations_f16(a: f16, b: f16) { + fn test_operations_f16(a: f16, b: f16) { // make sure they all map to the correct operation unsafe { assert_approx_eq!(fadd_fast(a, b), a + b); @@ -1312,7 +1312,7 @@ fn test_fast() { } #[inline(never)] - pub fn test_operations_f32(a: f32, b: f32) { + fn test_operations_f32(a: f32, b: f32) { // make sure they all map to the correct operation unsafe { assert_approx_eq!(fadd_fast(a, b), a + b); @@ -1324,7 +1324,7 @@ fn test_fast() { } #[inline(never)] - pub fn test_operations_f64(a: f64, b: f64) { + fn test_operations_f64(a: f64, b: f64) { // make sure they all map to the correct operation unsafe { assert_approx_eq!(fadd_fast(a, b), a + b); @@ -1336,7 +1336,7 @@ fn test_fast() { } #[inline(never)] - pub fn test_operations_f128(a: f128, b: f128) { + fn test_operations_f128(a: f128, b: f128) { // make sure they all map to the correct operation unsafe { assert_approx_eq!(fadd_fast(a, b), a + b); @@ -1363,7 +1363,7 @@ fn test_algebraic() { }; #[inline(never)] - pub fn test_operations_f16(a: f16, b: f16) { + fn test_operations_f16(a: f16, b: f16) { // make sure they all map to the correct operation assert_approx_eq!(fadd_algebraic(a, b), a + b); assert_approx_eq!(fsub_algebraic(a, b), a - b); @@ -1373,7 +1373,7 @@ fn test_algebraic() { } #[inline(never)] - pub fn test_operations_f32(a: f32, b: f32) { + fn test_operations_f32(a: f32, b: f32) { // make sure they all map to the correct operation assert_approx_eq!(fadd_algebraic(a, b), a + b); assert_approx_eq!(fsub_algebraic(a, b), a - b); @@ -1383,7 +1383,7 @@ fn test_algebraic() { } #[inline(never)] - pub fn test_operations_f64(a: f64, b: f64) { + fn test_operations_f64(a: f64, b: f64) { // make sure they all map to the correct operation assert_approx_eq!(fadd_algebraic(a, b), a + b); assert_approx_eq!(fsub_algebraic(a, b), a - b); @@ -1393,7 +1393,7 @@ fn test_algebraic() { } #[inline(never)] - pub fn test_operations_f128(a: f128, b: f128) { + fn test_operations_f128(a: f128, b: f128) { // make sure they all map to the correct operation assert_approx_eq!(fadd_algebraic(a, b), a + b); assert_approx_eq!(fsub_algebraic(a, b), a - b); @@ -1418,12 +1418,12 @@ fn test_fmuladd() { // FIXME(f16_f128): add when supported #[inline(never)] - pub fn test_operations_f32(a: f32, b: f32, c: f32) { + fn test_operations_f32(a: f32, b: f32, c: f32) { assert_approx_eq!(fmuladdf32(a, b, c), a * b + c); } #[inline(never)] - pub fn test_operations_f64(a: f64, b: f64, c: f64) { + fn test_operations_f64(a: f64, b: f64, c: f64) { assert_approx_eq!(fmuladdf64(a, b, c), a * b + c); } @@ -1468,10 +1468,10 @@ fn test_non_determinism() { }; } - pub fn test_operations_f16(a: f16, b: f16) { + fn test_operations_f16(a: f16, b: f16) { test_operations_f!(a, b); } - pub fn test_operations_f32(a: f32, b: f32) { + fn test_operations_f32(a: f32, b: f32) { test_operations_f!(a, b); check_nondet(|| a.powf(b)); check_nondet(|| a.powi(2)); @@ -1507,7 +1507,7 @@ fn test_non_determinism() { check_nondet(|| 5.0f32.erf()); check_nondet(|| 5.0f32.erfc()); } - pub fn test_operations_f64(a: f64, b: f64) { + fn test_operations_f64(a: f64, b: f64) { test_operations_f!(a, b); check_nondet(|| a.powf(b)); check_nondet(|| a.powi(2)); @@ -1538,7 +1538,7 @@ fn test_non_determinism() { check_nondet(|| 5.0f64.erf()); check_nondet(|| 5.0f64.erfc()); } - pub fn test_operations_f128(a: f128, b: f128) { + fn test_operations_f128(a: f128, b: f128) { test_operations_f!(a, b); } diff --git a/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs b/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs index 04a55d7007ce7..6eee5f21e1bb1 100644 --- a/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs +++ b/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs @@ -5,7 +5,7 @@ use std::intrinsics::mir::*; // Make sure calls with the return place "on the heap" work. #[custom_mir(dialect = "runtime", phase = "optimized")] -pub fn main() { +fn main() { mir! { { let x = 0; diff --git a/src/tools/miri/tests/pass/integer-ops.rs b/src/tools/miri/tests/pass/integer-ops.rs index 3f8ac34e7d10a..1792d16734fdd 100644 --- a/src/tools/miri/tests/pass/integer-ops.rs +++ b/src/tools/miri/tests/pass/integer-ops.rs @@ -56,7 +56,7 @@ fn basic() { assert_eq!(match_int_range(), 4); } -pub fn main() { +fn main() { basic(); // This tests that we do (not) do sign extension properly when loading integers diff --git a/src/tools/miri/tests/pass/intrinsics/integer.rs b/src/tools/miri/tests/pass/intrinsics/integer.rs index 8727b6d3c87ec..a67c52f7b420a 100644 --- a/src/tools/miri/tests/pass/intrinsics/integer.rs +++ b/src/tools/miri/tests/pass/intrinsics/integer.rs @@ -4,7 +4,7 @@ #![feature(core_intrinsics, funnel_shifts)] use std::intrinsics::*; -pub fn main() { +fn main() { unsafe { [assert_eq!(ctpop(0u8), 0), assert_eq!(ctpop(0i8), 0)]; [assert_eq!(ctpop(0u16), 0), assert_eq!(ctpop(0i16), 0)]; diff --git a/src/tools/miri/tests/pass/intrinsics/volatile.rs b/src/tools/miri/tests/pass/intrinsics/volatile.rs index c9799801455c6..b72020cb83d21 100644 --- a/src/tools/miri/tests/pass/intrinsics/volatile.rs +++ b/src/tools/miri/tests/pass/intrinsics/volatile.rs @@ -2,7 +2,7 @@ #![feature(core_intrinsics)] use std::intrinsics::{volatile_load, volatile_store}; -pub fn main() { +fn main() { unsafe { let i: &mut (isize, isize) = &mut (0, 0); volatile_store(i, (1, 2)); diff --git a/src/tools/miri/tests/pass/issues/issue-30530.rs b/src/tools/miri/tests/pass/issues/issue-30530.rs index b50a43ffd83b0..af338e8032d12 100644 --- a/src/tools/miri/tests/pass/issues/issue-30530.rs +++ b/src/tools/miri/tests/pass/issues/issue-30530.rs @@ -21,7 +21,7 @@ fn main() { } #[inline(never)] -pub fn take(h: Handler, f: Box) -> Box { +fn take(h: Handler, f: Box) -> Box { unsafe { match h { Handler::Custom(ptr) => *Box::from_raw(ptr), diff --git a/src/tools/miri/tests/pass/issues/issue-3794.rs b/src/tools/miri/tests/pass/issues/issue-3794.rs index 860d72bb586ff..3cc78b5f99d71 100644 --- a/src/tools/miri/tests/pass/issues/issue-3794.rs +++ b/src/tools/miri/tests/pass/issues/issue-3794.rs @@ -22,7 +22,7 @@ fn print_s(s: &S) { s.print(); } -pub fn main() { +fn main() { let s: Box = Box::new(S { s: 5 }); print_s(&*s); let t: Box = s as Box; diff --git a/src/tools/miri/tests/pass/issues/issue-5917.rs b/src/tools/miri/tests/pass/issues/issue-5917.rs index f7bbb4350e2be..9155c859b0505 100644 --- a/src/tools/miri/tests/pass/issues/issue-5917.rs +++ b/src/tools/miri/tests/pass/issues/issue-5917.rs @@ -1,6 +1,6 @@ struct T(&'static [isize]); static STATIC: T = T(&[5, 4, 3]); -pub fn main() { +fn main() { let T(ref v) = STATIC; assert_eq!(v[0], 5); } diff --git a/src/tools/miri/tests/pass/issues/issue-miri-184.rs b/src/tools/miri/tests/pass/issues/issue-miri-184.rs index 964d850298fbf..5233b441b990f 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-184.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-184.rs @@ -1,5 +1,5 @@ #![allow(unnecessary_transmutes)] -pub fn main() { +fn main() { let bytes: [u8; 8] = unsafe { ::std::mem::transmute(0u64) }; let _val: &[u8] = &bytes; } diff --git a/src/tools/miri/tests/pass/issues/issue-miri-2068.rs b/src/tools/miri/tests/pass/issues/issue-miri-2068.rs index 1931b6c9d79fb..471031e59ac3e 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-2068.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-2068.rs @@ -2,7 +2,7 @@ use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll, Waker}; -pub fn fuzzing_block_on>(fut: F) -> O { +fn fuzzing_block_on>(fut: F) -> O { let mut fut = std::pin::pin!(fut); let mut context = Context::from_waker(Waker::noop()); loop { diff --git a/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs b/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs index 123fe6ed6425c..d88df18295ab0 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs @@ -25,7 +25,7 @@ where } } -pub fn box_new_with() +fn box_new_with() where T: ?Sized, { diff --git a/src/tools/miri/tests/pass/last-use-in-cap-clause.rs b/src/tools/miri/tests/pass/last-use-in-cap-clause.rs index 2160aea16346f..d71593bfcbdf2 100644 --- a/src/tools/miri/tests/pass/last-use-in-cap-clause.rs +++ b/src/tools/miri/tests/pass/last-use-in-cap-clause.rs @@ -12,6 +12,6 @@ fn foo() -> Box isize + 'static> { Box::new(result) } -pub fn main() { +fn main() { assert_eq!(foo()(), 22); } diff --git a/src/tools/miri/tests/pass/loop-break-value.rs b/src/tools/miri/tests/pass/loop-break-value.rs index bc4c967d26a5b..74ab487b342a1 100644 --- a/src/tools/miri/tests/pass/loop-break-value.rs +++ b/src/tools/miri/tests/pass/loop-break-value.rs @@ -8,7 +8,7 @@ fn never_returns() { } } -pub fn main() { +fn main() { let value = 'outer: loop { if 1 == 1 { break 13; diff --git a/src/tools/miri/tests/pass/move-arg-2-unique.rs b/src/tools/miri/tests/pass/move-arg-2-unique.rs index de21d67eb4f62..7792b8bb1162d 100644 --- a/src/tools/miri/tests/pass/move-arg-2-unique.rs +++ b/src/tools/miri/tests/pass/move-arg-2-unique.rs @@ -2,7 +2,7 @@ fn test(foo: Box>) { assert_eq!((*foo)[0], 10); } -pub fn main() { +fn main() { let x = Box::new(vec![10]); // Test forgetting a local by move-in test(x); diff --git a/src/tools/miri/tests/pass/move-arg-3-unique.rs b/src/tools/miri/tests/pass/move-arg-3-unique.rs index 6025481c32e24..1b6e7ba7cf32e 100644 --- a/src/tools/miri/tests/pass/move-arg-3-unique.rs +++ b/src/tools/miri/tests/pass/move-arg-3-unique.rs @@ -1,4 +1,4 @@ -pub fn main() { +fn main() { let x = Box::new(10); let y = x; assert_eq!(*y, 10); diff --git a/src/tools/miri/tests/pass/mpsc.rs b/src/tools/miri/tests/pass/mpsc.rs index 3824a0de907cf..b60d32002db25 100644 --- a/src/tools/miri/tests/pass/mpsc.rs +++ b/src/tools/miri/tests/pass/mpsc.rs @@ -1,6 +1,6 @@ use std::sync::mpsc::channel; -pub fn main() { +fn main() { let (tx, rx) = channel::>(); tx.send(Box::new(100)).unwrap(); let v = rx.recv().unwrap(); diff --git a/src/tools/miri/tests/pass/panic/unwind_dwarf.rs b/src/tools/miri/tests/pass/panic/unwind_dwarf.rs index ca90e4f4d94f7..1e0cd3a43a2f8 100644 --- a/src/tools/miri/tests/pass/panic/unwind_dwarf.rs +++ b/src/tools/miri/tests/pass/panic/unwind_dwarf.rs @@ -15,7 +15,7 @@ struct Exception { cause: Box, } -pub fn panic(data: Box) -> u32 { +fn panic(data: Box) -> u32 { extern "C" fn exception_cleanup( _unwind_code: uw::_Unwind_Reason_Code, _exception: *mut uw::_Unwind_Exception, @@ -53,7 +53,7 @@ fn miri_exception_class() -> uw::_Unwind_Exception_Class { 0x4d4f5a_00_4d495249 } -pub fn catch_unwind R>(f: F) -> Result> { +fn catch_unwind R>(f: F) -> Result> { struct Data { f: Option, r: Option, diff --git a/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs b/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs index 445dd43febb13..8645fde77f01e 100644 --- a/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs +++ b/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs @@ -12,7 +12,7 @@ // doing region-folding, when really all clients of the region-folding // case only want to see *free* lifetime variables, not bound ones. -pub fn main() { +fn main() { fn explicit() { fn test(_x: Option>) where diff --git a/src/tools/miri/tests/pass/sendable-class.rs b/src/tools/miri/tests/pass/sendable-class.rs index a05278f1855a2..5784bc3a39314 100644 --- a/src/tools/miri/tests/pass/sendable-class.rs +++ b/src/tools/miri/tests/pass/sendable-class.rs @@ -12,7 +12,7 @@ fn foo(i: isize, j: char) -> Foo { Foo { i: i, j: j } } -pub fn main() { +fn main() { let (tx, rx) = channel(); tx.send(foo(42, 'c')).unwrap(); let _val = rx; diff --git a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs index 81a43cc8bcc6b..e4abc3895008c 100644 --- a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs +++ b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs @@ -24,7 +24,7 @@ fn is_u64_aligned(u: &Tag) -> bool { return (p & (u64_align - 1)) == 0; } -pub fn main() { +fn main() { let x = mk_rec(); assert!(is_u64_aligned(&x.t)); } diff --git a/src/tools/miri/tests/pass/tls/tls_leak_main_thread_allowed.rs b/src/tools/miri/tests/pass/tls/tls_leak_main_thread_allowed.rs index abc0968f7c4c6..634b8af02aa93 100644 --- a/src/tools/miri/tests/pass/tls/tls_leak_main_thread_allowed.rs +++ b/src/tools/miri/tests/pass/tls/tls_leak_main_thread_allowed.rs @@ -6,7 +6,7 @@ use std::cell::Cell; // as long as the program does), so make sure we treat them the same for leak purposes. // // The test covers both TLS statics and the TLS macro. -pub fn main() { +fn main() { #[thread_local] static TLS: Cell> = Cell::new(None); diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs index 4a868455c8497..ed8cbbf0e273b 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs @@ -6,7 +6,7 @@ mod utils; use std::cell::UnsafeCell; -pub fn main() { +fn main() { let cell = UnsafeCell::new(42); let box1 = Box::new(cell); diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.rs b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.rs index fd68685a2f442..adeedc653b994 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.rs +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.rs @@ -12,7 +12,7 @@ struct Foo { field2: Cell, } -pub fn main() { +fn main() { let root = Foo { field1: 42, field2: Cell::new(88) }; unsafe { let a = &root; diff --git a/src/tools/miri/tests/pass/tree_borrows/copy-nonoverlapping.rs b/src/tools/miri/tests/pass/tree_borrows/copy-nonoverlapping.rs index 23250d6e6dfc5..4edf80e9561e3 100644 --- a/src/tools/miri/tests/pass/tree_borrows/copy-nonoverlapping.rs +++ b/src/tools/miri/tests/pass/tree_borrows/copy-nonoverlapping.rs @@ -2,7 +2,7 @@ // copy_nonoverlapping works regardless of the order in which we construct // the arguments. -pub fn main() { +fn main() { test_to_from(); test_from_to(); } diff --git a/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs b/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs index 87eb447049d61..0ed1dbacc7070 100644 --- a/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs +++ b/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs @@ -28,7 +28,7 @@ fn local_addr_of_mut() { // Tree Borrows has no issue with several mutable references existing // at the same time, as long as they are used only immutably. // I.e. multiple Reserved can coexist. -pub fn aliasing_read_only_mutable_refs() { +fn aliasing_read_only_mutable_refs() { unsafe { let base = &mut 42u64; let r1 = &mut *(base as *mut u64); @@ -38,7 +38,7 @@ pub fn aliasing_read_only_mutable_refs() { } } -pub fn string_as_mut_ptr() { +fn string_as_mut_ptr() { // This errors in Stacked Borrows since as_mut_ptr restricts the provenance, // but with Tree Borrows it should work. unsafe { diff --git a/src/tools/miri/tests/pass/unsized.rs b/src/tools/miri/tests/pass/unsized.rs index 6ad0635430297..1e62cd7f3239e 100644 --- a/src/tools/miri/tests/pass/unsized.rs +++ b/src/tools/miri/tests/pass/unsized.rs @@ -4,10 +4,10 @@ #![feature(custom_mir, core_intrinsics)] fn unsized_params() { - pub fn f0(_f: dyn FnOnce()) {} - pub fn f1(_s: str) {} - pub fn f2(_x: i32, _y: [i32]) {} - pub fn f3(_p: dyn Send) {} + fn f0(_f: dyn FnOnce()) {} + fn f1(_s: str) {} + fn f2(_x: i32, _y: [i32]) {} + fn f3(_p: dyn Send) {} let c: Box = Box::new(|| {}); f0(*c); diff --git a/src/tools/miri/tests/pass/vec-matching-fold.rs b/src/tools/miri/tests/pass/vec-matching-fold.rs index 3a869703bf96a..48e750a3102aa 100644 --- a/src/tools/miri/tests/pass/vec-matching-fold.rs +++ b/src/tools/miri/tests/pass/vec-matching-fold.rs @@ -29,7 +29,7 @@ where } } -pub fn main() { +fn main() { let x = &[1, 2, 3, 4, 5]; let product = foldl(x, 1, |a, b| a * *b); diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index efaaf9fc84170..1f8d98a4d3392 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -30,11 +30,6 @@ fn miri_path() -> PathBuf { PathBuf::from(env::var("MIRI").unwrap_or_else(|_| env!("CARGO_BIN_EXE_miri").into())) } -pub fn flagsplit(flags: &str) -> Vec { - // This code is taken from `RUSTFLAGS` handling in cargo. - flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect() -} - // Build the shared object file for testing native function calls. fn build_native_lib(target: &str) -> PathBuf { // Loosely follow the logic of the `cc` crate for finding the compiler. From 18a468ed61cc6c3b5393f755ed5a1a71255d4b4a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 13 Oct 2025 08:50:43 +0200 Subject: [PATCH 23/26] native-lib args: also reject wide pointers --- src/tools/miri/src/shims/native_lib/mod.rs | 24 +++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 914c666adb302..47102c30bc3b1 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -6,7 +6,8 @@ use std::sync::atomic::AtomicBool; use libffi::low::CodePtr; use libffi::middle::Type as FfiType; use rustc_abi::{HasDataLayout, Size}; -use rustc_middle::ty::{self as ty, IntTy, Ty, UintTy}; +use rustc_middle::ty::layout::HasTypingEnv; +use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_span::Symbol; use serde::{Deserialize, Serialize}; @@ -373,15 +374,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { adt_def: ty::AdtDef<'tcx>, args: &'tcx ty::List>, ) -> InterpResult<'tcx, FfiType> { - // TODO: Certain non-C reprs should be okay also. - if !adt_def.repr().c() { - throw_unsup_format!("passing a non-#[repr(C)] struct over FFI: {orig_ty}") - } // TODO: unions, etc. if !adt_def.is_struct() { - throw_unsup_format!( - "unsupported argument type for native call: {orig_ty} is an enum or union" - ); + throw_unsup_format!("passing an enum or union over FFI: {orig_ty}"); + } + // TODO: Certain non-C reprs should be okay also. + if !adt_def.repr().c() { + throw_unsup_format!("passing a non-#[repr(C)] {} over FFI: {orig_ty}", adt_def.descr()) } let this = self.eval_context_ref(); @@ -395,19 +394,24 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Gets the matching libffi type for a given Ty. fn ty_to_ffitype(&self, ty: Ty<'tcx>) -> InterpResult<'tcx, FfiType> { + let this = self.eval_context_ref(); interp_ok(match ty.kind() { ty::Int(IntTy::I8) => FfiType::i8(), ty::Int(IntTy::I16) => FfiType::i16(), ty::Int(IntTy::I32) => FfiType::i32(), ty::Int(IntTy::I64) => FfiType::i64(), ty::Int(IntTy::Isize) => FfiType::isize(), - // the uints ty::Uint(UintTy::U8) => FfiType::u8(), ty::Uint(UintTy::U16) => FfiType::u16(), ty::Uint(UintTy::U32) => FfiType::u32(), ty::Uint(UintTy::U64) => FfiType::u64(), ty::Uint(UintTy::Usize) => FfiType::usize(), - ty::RawPtr(..) => FfiType::pointer(), + ty::RawPtr(pointee_ty, _mut) => { + if !pointee_ty.is_sized(*this.tcx, this.typing_env()) { + throw_unsup_format!("passing a pointer to an unsized type over FFI: {}", ty); + } + FfiType::pointer() + } ty::Adt(adt_def, args) => self.adt_to_ffitype(ty, *adt_def, args)?, _ => throw_unsup_format!("unsupported argument type for native call: {}", ty), }) From 1ef88639e76a548c8c1e49d8cde19e0446a3297c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 13 Oct 2025 10:02:03 +0200 Subject: [PATCH 24/26] Prepare for merging from rust-lang/rust This updates the rust-version file to 36e4f5d1fe1d63953a5bf1758ce2b64172623e2e. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 692cbc0a56bc3..f877706520ea1 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -4fd31815524baba0bf368f151f757101f432e3de +36e4f5d1fe1d63953a5bf1758ce2b64172623e2e From aec62f83bd9bfda37287a5a40a87b1c43f4695e7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 13 Oct 2025 10:36:03 +0200 Subject: [PATCH 25/26] fmt --- src/tools/miri/src/intrinsics/simd.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 5f75657e0a220..1e7366b5a8269 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -1,7 +1,7 @@ use rand::Rng; use rustc_apfloat::Float; -use rustc_middle::ty::FloatTy; use rustc_middle::ty; +use rustc_middle::ty::FloatTy; use super::check_intrinsic_arg_count; use crate::helpers::{ToHost, ToSoft}; @@ -79,7 +79,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } FloatTy::F128 => unimplemented!("f16_f128"), }; - + this.write_scalar(val, &dest)?; } } From 0c2e30bc1a5030095686c97650482553698e02b2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 13 Oct 2025 10:44:31 +0200 Subject: [PATCH 26/26] avoid blanket allow(unused) --- .../miri/tests/fail/function_calls/arg_inplace_locals_alias.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs index 9dad85b0b062f..b6cda6007536f 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs @@ -6,7 +6,6 @@ //@compile-flags: -Zmiri-disable-validation #![feature(custom_mir, core_intrinsics)] -#![allow(unused)] use std::intrinsics::mir::*; @@ -31,6 +30,7 @@ fn main() { } } +#[expect(unused_variables, unused_assignments)] fn callee(x: S, mut y: S) { // With the setup above, if `x` and `y` are both moved, // then writing to `y` will change the value stored in `x`!