Skip to content

Miri subtree update #140664

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
May 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ef58349
Added random scheduling
geetanshjuneja Apr 13, 2025
47e111a
move EnvVars::cleanup into the main-thread-exit handler
RalfJung Apr 29, 2025
76992ce
fix comment typos
RalfJung Apr 29, 2025
63edce0
Merge pull request #4302 from RalfJung/env-cleanup
RalfJung Apr 29, 2025
f521fa0
Merge pull request #4272 from geetanshjuneja/scheduling
RalfJung Apr 29, 2025
bbcc6a2
add -Zmiri-deterministic-concurrency flag and use it for concurrency …
RalfJung Apr 29, 2025
2260f77
Merge pull request #4303 from RalfJung/determinism
oli-obk Apr 30, 2025
589200e
Preparing for merge from rustc
May 1, 2025
326d775
Merge from rustc
May 1, 2025
7adaa50
fmt
May 1, 2025
06e0293
Merge pull request #4304 from rust-lang/rustup-2025-05-01
oli-obk May 1, 2025
8cc866d
Add `Cell` state to Tree Borrows
Apr 7, 2025
8bbea47
Merge pull request #4273 from yoctocell/new-cell-state
RalfJung May 1, 2025
e2d2c56
add ./miri squash
RalfJung May 1, 2025
a8da0fd
Merge pull request #4305 from RalfJung/squash
RalfJung May 2, 2025
d940d0b
Implement skeleton code for adding GenMC support to Miri (not yet fun…
Patrick-6 Mar 14, 2025
0238548
Merge pull request #4291 from Patrick-6/miri-genmc-prep
RalfJung May 2, 2025
8b36376
Correctly handle interior mutable data in `Box`
Apr 30, 2025
45d4bb2
Construct test so that it would fail for old code
May 2, 2025
8a166dc
Preparing for merge from rustc
May 3, 2025
4d87349
Merge from rustc
May 3, 2025
d313405
Merge pull request #4308 from rust-lang/rustup-2025-05-03
oli-obk May 3, 2025
30ecac8
Remove -Zunique-is-unique
JoJoDeveloping May 2, 2025
f8a8bbd
Merge pull request #4307 from JoJoDeveloping/remove-unique-is-unique
RalfJung May 5, 2025
3f0d24a
move tests that are identical between SB and TB to shared files
RalfJung May 5, 2025
70ef250
Merge pull request #4306 from yoctocell/fix-unsafecell-inside-box
RalfJung May 5, 2025
aec861b
consistent folder naming: stacked-borrows -> stacked_borrows
RalfJung May 5, 2025
5c7f1d7
alloc_addresses: when we are running out of addresses, start reusing …
RalfJung May 5, 2025
84b3142
Merge pull request #4309 from RalfJung/both-borrows-tests
RalfJung May 5, 2025
4b8f88b
Merge pull request #4310 from RalfJung/addr-space-conservation
RalfJung May 5, 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/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ tex/*/out
*.mm_profdata
perf.data
perf.data.old
flamegraph.svg
flamegraph*.svg
rustc-ice*.txt
tests/native-lib/libtestlib.so
.auto-*

/genmc/
10 changes: 4 additions & 6 deletions src/tools/miri/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ When you get a review, please take care of the requested changes in new commits.
existing commits. Generally avoid force-pushing. The only time you should force push is when there
is a conflict with the master branch (in that case you should rebase across master, not merge), and
all the way at the end of the review process when the reviewer tells you that the PR is done and you
should squash the commits. For the latter case, use `git rebase --keep-base ...` to squash without
changing the base commit your PR branches off of. Use your own judgment and the reviewer's guidance
to decide whether the PR should be squashed into a single commit or multiple logically separate
commits. (All this is to work around the fact that Github is quite bad at dealing with force pushes
and does not support `git range-diff`. Maybe one day Github will be good at git and then life can
become easier.)
should squash the commits. If you are unsure how to use `git rebase` to squash commits, use `./miri
squash` which automates the process but leaves little room for customization. (All this is to work
around the fact that Github is quite bad at dealing with force pushes and does not support `git
range-diff`. Maybe one day Github will be good at git and then life can become easier.)

Most PRs bounce back and forth between the reviewer and the author several times, so it is good to
keep track of who is expected to take the next step. We are using the `S-waiting-for-review` and
Expand Down
1 change: 1 addition & 0 deletions src/tools/miri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ harness = false

[features]
default = ["stack-cache"]
genmc = []
stack-cache = []
stack-cache-consistency-check = ["stack-cache"]

Expand Down
55 changes: 33 additions & 22 deletions src/tools/miri/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,22 +277,15 @@ Try running `cargo miri clean`.
Miri adds its own set of `-Z` flags, which are usually set via the `MIRIFLAGS`
environment variable. We first document the most relevant and most commonly used flags:

* `-Zmiri-address-reuse-rate=<rate>` changes the probability that a freed *non-stack* allocation
will be added to the pool for address reuse, and the probability that a new *non-stack* allocation
will be taken from the pool. Stack allocations never get added to or taken from the pool. The
default is `0.5`.
* `-Zmiri-address-reuse-cross-thread-rate=<rate>` changes the probability that an allocation which
attempts to reuse a previously freed block of memory will also consider blocks freed by *other
threads*. The default is `0.1`, which means by default, in 90% of the cases where an address reuse
attempt is made, only addresses from the same thread will be considered. Reusing an address from
another thread induces synchronization between those threads, which can mask data races and weak
memory bugs.
* `-Zmiri-compare-exchange-weak-failure-rate=<rate>` changes the failure rate of
`compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail).
You can change it to any value between `0.0` and `1.0`, where `1.0` means it
will always fail and `0.0` means it will never fail. Note that setting it to
`1.0` will likely cause hangs, since it means programs using
`compare_exchange_weak` cannot make progress.
* `-Zmiri-deterministic-concurrency` makes Miri's concurrency-related behavior fully deterministic.
Strictly speaking, Miri is always fully deterministic when isolation is enabled (the default
mode), but this determinism is achieved by using an RNG with a fixed seed. Seemingly harmless
changes to the program, or just running it for a different target architecture, can thus lead to
completely different program behavior down the line. This flag disables the use of an RNG for
concurrency-related decisions. Therefore, Miri cannot find bugs that only occur under some
specific circumstances, but Miri's behavior will also be more stable across versions and targets.
This is equivalent to `-Zmiri-fixed-schedule -Zmiri-compare-exchange-weak-failure-rate=0.0
-Zmiri-address-reuse-cross-thread-rate=0.0 -Zmiri-disable-weak-memory-emulation`.
* `-Zmiri-disable-isolation` disables host isolation. As a consequence,
the program has access to host resources such as environment variables, file
systems, and randomness.
Expand Down Expand Up @@ -334,9 +327,6 @@ environment variable. We first document the most relevant and most commonly used
This will necessarily miss some bugs as those operations are not efficiently and accurately
implementable in a sanitizer, but it will only miss bugs that concern memory/pointers which is
subject to these operations.
* `-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.
* `-Zmiri-report-progress` makes Miri print the current stacktrace every now and then, so you can
tell what it is doing when a program just keeps running. You can customize how frequently the
report is printed via `-Zmiri-report-progress=<blocks>`, which prints the report every N basic
Expand Down Expand Up @@ -365,6 +355,22 @@ The remaining flags are for advanced use only, and more likely to change or be r
Some of these are **unsound**, which means they can lead
to Miri failing to detect cases of undefined behavior in a program.

* `-Zmiri-address-reuse-rate=<rate>` changes the probability that a freed *non-stack* allocation
will be added to the pool for address reuse, and the probability that a new *non-stack* allocation
will be taken from the pool. Stack allocations never get added to or taken from the pool. The
default is `0.5`.
* `-Zmiri-address-reuse-cross-thread-rate=<rate>` changes the probability that an allocation which
attempts to reuse a previously freed block of memory will also consider blocks freed by *other
threads*. The default is `0.1`, which means by default, in 90% of the cases where an address reuse
attempt is made, only addresses from the same thread will be considered. Reusing an address from
another thread induces synchronization between those threads, which can mask data races and weak
memory bugs.
* `-Zmiri-compare-exchange-weak-failure-rate=<rate>` changes the failure rate of
`compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail).
You can change it to any value between `0.0` and `1.0`, where `1.0` means it
will always fail and `0.0` means it will never fail. Note that setting it to
`1.0` will likely cause hangs, since it means programs using
`compare_exchange_weak` cannot make progress.
* `-Zmiri-disable-alignment-check` disables checking pointer alignment, so you
can focus on other failures, but it means Miri can miss bugs in your program.
Using this flag is **unsound**.
Expand All @@ -383,6 +389,10 @@ to Miri failing to detect cases of undefined behavior in a program.
this flag is **unsound**.
* `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak
memory effects.
* `-Zmiri-fixed-schedule` disables preemption (like `-Zmiri-preemption-rate=0.0`) and furthermore
disables the randomization of the next thread to be picked, instead fixing a round-robin schedule.
Note however that other aspects of Miri's concurrency behavior are still randomize; use
`-Zmiri-deterministic-concurrency` to disable them all.
* `-Zmiri-native-lib=<path to a shared object file>` is an experimental flag for providing support
for calling native functions from inside the interpreter via FFI. The flag is supported only on
Unix systems. Functions not provided by that file are still executed via the usual Miri shims.
Expand Down Expand Up @@ -412,6 +422,10 @@ to Miri failing to detect cases of undefined behavior in a program.
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:
if a thread blocks or yields, the next thread is chosen randomly.
* `-Zmiri-provenance-gc=<blocks>` configures how often the pointer provenance garbage collector runs.
The default is to search for and remove unreachable provenance once every `10000` basic blocks. Setting
this to `0` disables the garbage collector, which causes some programs to have explosive memory
Expand Down Expand Up @@ -443,9 +457,6 @@ to Miri failing to detect cases of undefined behavior in a program.
casts are not supported in this mode, but that may change in the future.
* `-Zmiri-force-page-size=<num>` overrides the default page size for an architecture, in multiples of 1k.
`4` is default for most targets. This value should always be a power of 2 and nonzero.
* `-Zmiri-unique-is-unique` performs additional aliasing checks for `core::ptr::Unique` to ensure
that it could theoretically be considered `noalias`. This flag is experimental and has
an effect only when used with `-Zmiri-tree-borrows`.

[function ABI]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier

Expand Down
5 changes: 1 addition & 4 deletions src/tools/miri/bench-cargo-miri/big-allocs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ fn main() {
// We can't use too big of an allocation or this code will encounter an allocation failure in
// CI. Since the allocation can't be huge, we need to do a few iterations so that the effect
// we're trying to measure is clearly visible above the interpreter's startup time.
// FIXME (https://github.com/rust-lang/miri/issues/4253): On 32bit targets, we can run out of
// usable addresses if we don't reuse, leading to random test failures.
let count = if cfg!(target_pointer_width = "32") { 8 } else { 12 };
for _ in 0..count {
for _ in 0..20 {
drop(Vec::<u8>::with_capacity(512 * 1024 * 1024));
}
}
75 changes: 72 additions & 3 deletions src/tools/miri/miri-script/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{BufReader, BufWriter, Write};
use std::fmt::Write as _;
use std::fs::{self, File};
use std::io::{self, BufRead, BufReader, BufWriter, Write as _};
use std::ops::Not;
use std::path::PathBuf;
use std::time::Duration;
Expand Down Expand Up @@ -169,7 +170,8 @@ impl Command {
| Command::Toolchain { .. }
| Command::Bench { .. }
| Command::RustcPull { .. }
| Command::RustcPush { .. } => {}
| Command::RustcPush { .. }
| Command::Squash => {}
}
// Then run the actual command.
match self {
Expand All @@ -188,6 +190,7 @@ impl Command {
Command::Toolchain { flags } => Self::toolchain(flags),
Command::RustcPull { commit } => Self::rustc_pull(commit.clone()),
Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch),
Command::Squash => Self::squash(),
}
}

Expand Down Expand Up @@ -383,6 +386,72 @@ impl Command {
Ok(())
}

fn squash() -> Result<()> {
let sh = Shell::new()?;
sh.change_dir(miri_dir()?);
// Figure out base wrt latest upstream master.
// (We can't trust any of the local ones, they can all be outdated.)
let origin_master = {
cmd!(sh, "git fetch https://github.com/rust-lang/miri/")
.quiet()
.ignore_stdout()
.ignore_stderr()
.run()?;
cmd!(sh, "git rev-parse FETCH_HEAD").read()?
};
let base = cmd!(sh, "git merge-base HEAD {origin_master}").read()?;
// Rebase onto that, setting ourselves as the sequence editor so that we can edit the sequence programmatically.
// We want to forward the host stdin so apparently we cannot use `cmd!`.
let mut cmd = process::Command::new("git");
cmd.arg("rebase").arg(&base).arg("--interactive");
cmd.env("GIT_SEQUENCE_EDITOR", env::current_exe()?);
cmd.env("MIRI_SCRIPT_IS_GIT_SEQUENCE_EDITOR", "1");
cmd.current_dir(sh.current_dir());
let result = cmd.status()?;
if !result.success() {
bail!("`git rebase` failed");
}
Ok(())
}

pub fn squash_sequence_editor() -> Result<()> {
let sequence_file = env::args().nth(1).expect("git should pass us a filename");
if sequence_file == "fmt" {
// This is probably us being called as a git hook as part of the rebase. Let's just
// ignore this. Sadly `git rebase` does not have a flag to skip running hooks.
return Ok(());
}
// Read the provided sequence and adjust it.
let rebase_sequence = {
let mut rebase_sequence = String::new();
let file = fs::File::open(&sequence_file).with_context(|| {
format!("failed to read rebase sequence from {sequence_file:?}")
})?;
let file = io::BufReader::new(file);
for line in file.lines() {
let line = line?;
// The first line is left unchanged.
if rebase_sequence.is_empty() {
writeln!(rebase_sequence, "{line}").unwrap();
continue;
}
// If this is a "pick" like, make it "squash".
if let Some(rest) = line.strip_prefix("pick ") {
writeln!(rebase_sequence, "squash {rest}").unwrap();
continue;
}
// We've reached the end of the relevant part of the sequence, and we can stop.
break;
}
rebase_sequence
};
// Write out the adjusted sequence.
fs::write(&sequence_file, rebase_sequence).with_context(|| {
format!("failed to write adjusted rebase sequence to {sequence_file:?}")
})?;
Ok(())
}

fn bench(
target: Option<String>,
no_install: bool,
Expand Down
9 changes: 8 additions & 1 deletion src/tools/miri/miri-script/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ pub enum Command {
#[arg(default_value = "miri-sync")]
branch: String,
},
/// Squash the commits of the current feature branch into one.
Squash,
}

impl Command {
Expand All @@ -154,7 +156,7 @@ impl Command {
flags.extend(remainder);
Ok(())
}
Self::Bench { .. } | Self::RustcPull { .. } | Self::RustcPush { .. } =>
Self::Bench { .. } | Self::RustcPull { .. } | Self::RustcPush { .. } | Self::Squash =>
bail!("unexpected \"--\" found in arguments"),
}
}
Expand All @@ -170,6 +172,11 @@ pub struct Cli {
}

fn main() -> Result<()> {
// If we are invoked as the git sequence editor, jump to that logic.
if !std::env::var_os("MIRI_SCRIPT_IS_GIT_SEQUENCE_EDITOR").unwrap_or_default().is_empty() {
return Command::squash_sequence_editor();
}

// Split the arguments into the part before the `--` and the part after.
// The `--` itself ends up in the second part.
let miri_args: Vec<_> = std::env::args().take_while(|x| *x != "--").collect();
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 @@
1b8ab72680f36e783af84c1a3c4f8508572bd9f9
2ad5f8607d0e192b60b130e5cc416b477b351c18
17 changes: 15 additions & 2 deletions src/tools/miri/src/alloc_addresses/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,16 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
memory_kind: MemoryKind,
) -> InterpResult<'tcx, u64> {
let this = self.eval_context_ref();
let mut rng = this.machine.rng.borrow_mut();
let info = this.get_alloc_info(alloc_id);

// Miri's address assignment leaks state across thread boundaries, which is incompatible
// with GenMC execution. So we instead let GenMC assign addresses to allocations.
if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
let addr = genmc_ctx.handle_alloc(&this.machine, info.size, info.align, memory_kind)?;
return interp_ok(addr);
}

let mut rng = this.machine.rng.borrow_mut();
// This is either called immediately after allocation (and then cached), or when
// adjusting `tcx` pointers (which never get freed). So assert that we are looking
// at a live allocation. This also ensures that we never re-assign an address to an
Expand Down Expand Up @@ -197,6 +205,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
if global_state.next_base_addr > this.target_usize_max() {
throw_exhaust!(AddressSpaceFull);
}
// If we filled up more than half the address space, start aggressively reusing
// addresses to avoid running out.
if global_state.next_base_addr > u64::try_from(this.target_isize_max()).unwrap() {
global_state.reuse.address_space_shortage();
}

interp_ok(base_addr)
}
Expand Down Expand Up @@ -490,7 +503,7 @@ impl<'tcx> MiriMachine<'tcx> {
// Also remember this address for future reuse.
let thread = self.threads.active_thread();
global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || {
if let Some(data_race) = &self.data_race {
if let Some(data_race) = self.data_race.as_vclocks_ref() {
data_race.release_clock(&self.threads, |clock| clock.clone())
} else {
VClock::default()
Expand Down
15 changes: 11 additions & 4 deletions src/tools/miri/src/alloc_addresses/reuse_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub struct ReusePool {
/// allocations as address-size pairs, the list must be sorted by the size and then the thread ID.
///
/// Each of these maps has at most MAX_POOL_SIZE elements, and since alignment is limited to
/// less than 64 different possible value, that bounds the overall size of the pool.
/// less than 64 different possible values, that bounds the overall size of the pool.
///
/// We also store the ID and the data-race clock of the thread that donated this pool element,
/// to ensure synchronization with the thread that picks up this address.
Expand All @@ -36,6 +36,15 @@ impl ReusePool {
}
}

/// Call this when we are using up a lot of the address space: if memory reuse is enabled at all,
/// this will bump the intra-thread reuse rate to 100% so that we can keep running this program as
/// long as possible.
pub fn address_space_shortage(&mut self) {
if self.address_reuse_rate > 0.0 {
self.address_reuse_rate = 1.0;
}
}

fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size, ThreadId, VClock)> {
let pool_idx: usize = align.bytes().trailing_zeros().try_into().unwrap();
if self.pool.len() <= pool_idx {
Expand All @@ -55,9 +64,7 @@ impl ReusePool {
clock: impl FnOnce() -> VClock,
) {
// Let's see if we even want to remember this address.
// We don't remember stack addresses: there's a lot of them (so the perf impact is big),
// and we only want to reuse stack slots within the same thread or else we'll add a lot of
// undesired synchronization.
// We don't remember stack addresses since there's so many of them (so the perf impact is big).
if kind == MemoryKind::Stack || !rng.random_bool(self.address_reuse_rate) {
return;
}
Expand Down
Loading
Loading