Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
be30f5a
Prepare for merging from rust-lang/rust
Nov 12, 2025
a973a8c
Merge ref '0b329f801a09' from rust-lang/rust
Nov 12, 2025
1368257
fmt
Nov 12, 2025
77f2e03
update lockfile
RalfJung Nov 12, 2025
d8b01c7
fix function_casts_as_integer lint
RalfJung Nov 12, 2025
4e28f0f
try to be more robust against short hickups in the Ubuntu Ports mirrors
RalfJung Nov 12, 2025
037db1e
Merge pull request #4680 from rust-lang/rustup-2025-11-12
RalfJung Nov 12, 2025
1f8f218
epoll: get rid of separate ready_list
RalfJung Nov 12, 2025
a395522
Merge pull request #4683 from RalfJung/epoll-without-ready
RalfJung Nov 12, 2025
b115de9
Prepare for merging from rust-lang/rust
Nov 14, 2025
e66f1b0
Merge ref '7a72c5459dd5' from rust-lang/rust
Nov 14, 2025
ada5df7
Merge pull request #4687 from rust-lang/rustup-2025-11-14
RalfJung Nov 14, 2025
5c75f78
remove -Zmiri-retag-fields
RalfJung Nov 14, 2025
0d692cb
Merge pull request #4689 from RalfJung/retag-the-fields
RalfJung Nov 14, 2025
4ed54a8
refactor and unify top-level error handling a bit
RalfJung Nov 14, 2025
3e5d797
Merge pull request #4690 from RalfJung/top-level-error
RalfJung Nov 15, 2025
2e3fa6c
apple os_unfair_lock: throw 'unsupported' when we would behave differ…
RalfJung Nov 15, 2025
3ab8158
Merge pull request #4691 from RalfJung/apple-os-unfair-lock
RalfJung Nov 15, 2025
7a86151
epoll: confirm that timerfd wakes up even on write of 0; add nicer li…
RalfJung Nov 15, 2025
a8b14ee
epoll: wake up multiple threads if that is needed to deliver all events
RalfJung Nov 15, 2025
c82b572
fix deadlock error showing 'no span available' nonsense backtraces
RalfJung Nov 15, 2025
5420681
epoll: ensure we count the events that each thread will take
RalfJung Nov 15, 2025
e117ee4
Merge pull request #4692 from RalfJung/epoll-wakeups
RalfJung Nov 15, 2025
04d97bc
add an avx512 psad shim
folkertdev Nov 13, 2025
573515c
Merge pull request #4686 from folkertdev/psad-avx512
RalfJung Nov 15, 2025
cb5c5da
slightly simplify exit handling; get rid of GenmcBlockedExecution
RalfJung Nov 16, 2025
16685fc
Merge pull request #4693 from RalfJung/genmc-exit
RalfJung Nov 16, 2025
4155266
epoll: separately track ready_set
RalfJung Nov 16, 2025
725db88
Merge pull request #4694 from RalfJung/epoll-ready-set
RalfJung Nov 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/tools/miri/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ jobs:
sudo bash -c "echo 'https://ports.ubuntu.com/ priority:4' >> /etc/apt/apt-mirrors.txt"
# Add architecture
sudo dpkg --add-architecture ${{ matrix.multiarch }}
sudo apt update
# Ubuntu Ports often has outdated mirrors so try a few times to get the apt repo
for TRY in $(seq 3); do
{ sudo apt update && break; } || sleep 30
done
# Install needed packages
sudo apt install $(echo "libatomic1: zlib1g-dev:" | sed 's/:/:${{ matrix.multiarch }}/g')
- uses: ./.github/workflows/setup
Expand Down
4 changes: 2 additions & 2 deletions src/tools/miri/Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1519,9 +1519,9 @@ dependencies = [

[[package]]
name = "tikv-jemalloc-sys"
version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7"
version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d"
checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b"
dependencies = [
"cc",
"libc",
Expand Down
5 changes: 0 additions & 5 deletions src/tools/miri/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,11 +464,6 @@ to Miri failing to detect cases of undefined behavior in a program.
errors and warnings.
* `-Zmiri-recursive-validation` is a *highly experimental* flag that makes validity checking
recurse below references.
* `-Zmiri-retag-fields[=<all|none|scalar>]` controls when Stacked Borrows retagging recurses into
fields. `all` means it always recurses (the default, and equivalent to `-Zmiri-retag-fields`
without an explicit value), `none` means it never recurses, `scalar` means it only recurses for
types where we would also emit `noalias` annotations in the generated LLVM IR (types passed as
individual scalars or pairs of scalars). Setting this to `none` is **unsound**.
* `-Zmiri-preemption-rate` configures the probability that at the end of a basic block, the active
thread will be preempted. The default is `0.01` (i.e., 1%). Setting this to `0` disables
preemption. Note that even without preemption, the schedule is still non-deterministic:
Expand Down
2 changes: 1 addition & 1 deletion src/tools/miri/rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8401398e1f14a24670ee1a3203713dc2f0f8b3a8
7a72c5459dd58f81b0e1a0e5436d145485889375
135 changes: 67 additions & 68 deletions src/tools/miri/src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ extern crate rustc_span;
mod log;

use std::env;
use std::num::NonZero;
use std::num::{NonZero, NonZeroI32};
use std::ops::Range;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
use std::sync::atomic::{AtomicU32, Ordering};

use miri::{
BacktraceStyle, BorrowTrackerMethod, GenmcConfig, GenmcCtx, MiriConfig, MiriEntryFnType,
ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode, run_genmc_mode,
ProvenanceMode, TreeBorrowsParams, ValidationMode, run_genmc_mode,
};
use rustc_abi::ExternAbi;
use rustc_data_structures::sync;
use rustc_data_structures::sync::{self, DynSync};
use rustc_driver::Compilation;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::{self as hir, Node};
Expand Down Expand Up @@ -120,15 +120,47 @@ fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, MiriEntryFnType) {
}
}

fn run_many_seeds(
many_seeds: ManySeedsConfig,
eval_entry_once: impl Fn(u64) -> Result<(), NonZeroI32> + DynSync,
) -> Result<(), NonZeroI32> {
let exit_code =
sync::IntoDynSyncSend(AtomicU32::new(rustc_driver::EXIT_SUCCESS.cast_unsigned()));
let num_failed = sync::IntoDynSyncSend(AtomicU32::new(0));
sync::par_for_each_in(many_seeds.seeds.clone(), |&seed| {
if let Err(return_code) = eval_entry_once(seed.into()) {
eprintln!("FAILING SEED: {seed}");
if !many_seeds.keep_going {
// `abort_if_errors` would unwind but would not actually stop miri, since
// `par_for_each` waits for the rest of the threads to finish.
exit(return_code.get());
}
// Preserve the "maximum" return code (when interpreted as `u32`), to make
// the result order-independent and to make it 0 only if all executions were 0.
exit_code.fetch_max(return_code.get().cast_unsigned(), Ordering::Relaxed);
num_failed.fetch_add(1, Ordering::Relaxed);
}
});
let num_failed = num_failed.0.into_inner();
let exit_code = exit_code.0.into_inner().cast_signed();
if num_failed > 0 {
eprintln!("{num_failed}/{total} SEEDS FAILED", total = many_seeds.seeds.count());
Err(NonZeroI32::new(exit_code).unwrap())
} else {
assert!(exit_code == 0);
Ok(())
}
}

impl rustc_driver::Callbacks for MiriCompilerCalls {
fn after_analysis<'tcx>(
&mut self,
_: &rustc_interface::interface::Compiler,
tcx: TyCtxt<'tcx>,
) -> Compilation {
if tcx.sess.dcx().has_errors_or_delayed_bugs().is_some() {
tcx.dcx().fatal("miri cannot be run on programs that fail compilation");
}
tcx.dcx().abort_if_errors();
tcx.dcx().flush_delayed();

if !tcx.crate_types().contains(&CrateType::Executable) {
tcx.dcx().fatal("miri only makes sense on bin crates");
}
Expand Down Expand Up @@ -161,64 +193,28 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
optimizations is usually marginal at best.");
}

// 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<GenmcCtx>| {
let res = if config.genmc_config.is_some() {
assert!(self.many_seeds.is_none());
run_genmc_mode(tcx, &config, |genmc_ctx: Rc<GenmcCtx>| {
miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx))
};
let return_code = run_genmc_mode(&config, eval_entry_once, tcx).unwrap_or_else(|| {
tcx.dcx().abort_if_errors();
rustc_driver::EXIT_FAILURE
});
exit(return_code);
};

if let Some(many_seeds) = self.many_seeds.take() {
})
} else if let Some(many_seeds) = self.many_seeds.take() {
assert!(config.seed.is_none());
let exit_code = sync::IntoDynSyncSend(AtomicI32::new(rustc_driver::EXIT_SUCCESS));
let num_failed = sync::IntoDynSyncSend(AtomicU32::new(0));
sync::par_for_each_in(many_seeds.seeds.clone(), |seed| {
run_many_seeds(many_seeds, |seed| {
let mut config = config.clone();
config.seed = Some((*seed).into());
config.seed = Some(seed);
eprintln!("Trying seed: {seed}");
let return_code = miri::eval_entry(
tcx,
entry_def_id,
entry_type,
&config,
/* genmc_ctx */ None,
)
.unwrap_or(rustc_driver::EXIT_FAILURE);
if return_code != rustc_driver::EXIT_SUCCESS {
eprintln!("FAILING SEED: {seed}");
if !many_seeds.keep_going {
// `abort_if_errors` would actually not stop, since `par_for_each` waits for the
// rest of the to finish, so we just exit immediately.
exit(return_code);
}
exit_code.store(return_code, Ordering::Relaxed);
num_failed.fetch_add(1, Ordering::Relaxed);
}
});
let num_failed = num_failed.0.into_inner();
if num_failed > 0 {
eprintln!("{num_failed}/{total} SEEDS FAILED", total = many_seeds.seeds.count());
}
exit(exit_code.0.into_inner());
miri::eval_entry(tcx, entry_def_id, entry_type, &config, /* genmc_ctx */ None)
})
} else {
let return_code = miri::eval_entry(tcx, entry_def_id, entry_type, &config, None)
.unwrap_or_else(|| {
tcx.dcx().abort_if_errors();
rustc_driver::EXIT_FAILURE
});
exit(return_code);
miri::eval_entry(tcx, entry_def_id, entry_type, &config, None)
};

if let Err(return_code) = res {
tcx.dcx().abort_if_errors();
exit(return_code.get());
} else {
exit(rustc_driver::EXIT_SUCCESS);
}

// Unreachable.
Expand Down Expand Up @@ -571,21 +567,17 @@ fn main() {
} else if arg == "-Zmiri-mute-stdout-stderr" {
miri_config.mute_stdout_stderr = true;
} else if arg == "-Zmiri-retag-fields" {
miri_config.retag_fields = RetagFields::Yes;
eprintln!(
"warning: `-Zmiri-retag-fields` is a NOP and will be removed in a future version of Miri.\n\
Field retagging has been on-by-default for a long time."
);
} else if arg == "-Zmiri-fixed-schedule" {
miri_config.fixed_scheduling = true;
} else if arg == "-Zmiri-deterministic-concurrency" {
miri_config.fixed_scheduling = true;
miri_config.address_reuse_cross_thread_rate = 0.0;
miri_config.cmpxchg_weak_failure_rate = 0.0;
miri_config.weak_memory_emulation = false;
} else if let Some(retag_fields) = arg.strip_prefix("-Zmiri-retag-fields=") {
miri_config.retag_fields = match retag_fields {
"all" => RetagFields::Yes,
"none" => RetagFields::No,
"scalar" => RetagFields::OnlyScalar,
_ => fatal_error!("`-Zmiri-retag-fields` can only be `all`, `none`, or `scalar`"),
};
} else if let Some(param) = arg.strip_prefix("-Zmiri-seed=") {
let seed = param.parse::<u64>().unwrap_or_else(|_| {
fatal_error!("-Zmiri-seed must be an integer that fits into u64")
Expand Down Expand Up @@ -747,6 +739,13 @@ fn main() {
);
};

// Validate GenMC settings.
if miri_config.genmc_config.is_some()
&& let Err(err) = GenmcConfig::validate(&mut miri_config)
{
fatal_error!("Invalid settings: {err}");
}

debug!("rustc arguments: {:?}", rustc_args);
debug!("crate arguments: {:?}", miri_config.args);
if !miri_config.native_lib.is_empty() && miri_config.native_lib_enable_tracing {
Expand Down
22 changes: 1 addition & 21 deletions src/tools/miri/src/borrow_tracker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,6 @@ pub struct GlobalStateInner {
protected_tags: FxHashMap<BorTag, ProtectorKind>,
/// The pointer ids to trace
tracked_pointer_tags: FxHashSet<BorTag>,
/// Whether to recurse into datatypes when searching for pointers to retag.
retag_fields: RetagFields,
}

impl VisitProvenance for GlobalStateInner {
Expand All @@ -131,18 +129,6 @@ impl VisitProvenance for GlobalStateInner {
/// We need interior mutable access to the global state.
pub type GlobalState = RefCell<GlobalStateInner>;

/// Policy on whether to recurse into fields to retag
#[derive(Copy, Clone, Debug)]
pub enum RetagFields {
/// Don't retag any fields.
No,
/// Retag all fields.
Yes,
/// Only retag fields of types with Scalar and ScalarPair layout,
/// to match the LLVM `noalias` we generate.
OnlyScalar,
}

/// The flavor of the protector.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ProtectorKind {
Expand All @@ -168,15 +154,13 @@ impl GlobalStateInner {
pub fn new(
borrow_tracker_method: BorrowTrackerMethod,
tracked_pointer_tags: FxHashSet<BorTag>,
retag_fields: RetagFields,
) -> Self {
GlobalStateInner {
borrow_tracker_method,
next_ptr_tag: BorTag::one(),
root_ptr_tags: FxHashMap::default(),
protected_tags: FxHashMap::default(),
tracked_pointer_tags,
retag_fields,
}
}

Expand Down Expand Up @@ -244,11 +228,7 @@ pub struct TreeBorrowsParams {

impl BorrowTrackerMethod {
pub fn instantiate_global_state(self, config: &MiriConfig) -> GlobalState {
RefCell::new(GlobalStateInner::new(
self,
config.tracked_pointer_tags.clone(),
config.retag_fields,
))
RefCell::new(GlobalStateInner::new(self, config.tracked_pointer_tags.clone()))
}

pub fn get_tree_borrows_params(self) -> TreeBorrowsParams {
Expand Down
29 changes: 6 additions & 23 deletions src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::fmt::Write;
use std::sync::atomic::AtomicBool;
use std::{cmp, mem};

use rustc_abi::{BackendRepr, Size};
use rustc_abi::Size;
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::mir::{Mutability, RetagKind};
use rustc_middle::ty::layout::HasTypingEnv;
Expand Down Expand Up @@ -887,22 +887,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
place: &PlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields;
let retag_cause = match kind {
RetagKind::TwoPhase => unreachable!(), // can only happen in `retag_ptr_value`
RetagKind::FnEntry => RetagCause::FnEntry,
RetagKind::Default | RetagKind::Raw => RetagCause::Normal,
};
let mut visitor =
RetagVisitor { ecx: this, kind, retag_cause, retag_fields, in_field: false };
let mut visitor = RetagVisitor { ecx: this, kind, retag_cause, in_field: false };
return visitor.visit_value(place);

// The actual visitor.
struct RetagVisitor<'ecx, 'tcx> {
ecx: &'ecx mut MiriInterpCx<'tcx>,
kind: RetagKind,
retag_cause: RetagCause,
retag_fields: RetagFields,
in_field: bool,
}
impl<'ecx, 'tcx> RetagVisitor<'ecx, 'tcx> {
Expand Down Expand Up @@ -967,24 +964,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
self.walk_value(place)?;
}
_ => {
// Not a reference/pointer/box. Only recurse if configured appropriately.
let recurse = match self.retag_fields {
RetagFields::No => false,
RetagFields::Yes => true,
RetagFields::OnlyScalar => {
// Matching `ArgAbi::new` at the time of writing, only fields of
// `Scalar` and `ScalarPair` ABI are considered.
matches!(
place.layout.backend_repr,
BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)
)
}
};
if recurse {
let in_field = mem::replace(&mut self.in_field, true); // remember and restore old value
self.walk_value(place)?;
self.in_field = in_field;
}
// Not a reference/pointer/box. Recurse.
let in_field = mem::replace(&mut self.in_field, true); // remember and restore old value
self.walk_value(place)?;
self.in_field = in_field;
}
}

Expand Down
Loading
Loading