about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-08-07 07:25:51 +0000
committerbors <bors@rust-lang.org>2023-08-07 07:25:51 +0000
commitadb15a20ac99024ea90b28abd5af12f438fa2a20 (patch)
tree6c0e939fc6371f2175539ee517f00e1c8d57fbd9
parent4a71a05d52a534c18fed8b1415c4858570d8ef86 (diff)
parentd1174a974e97017de75a33e7b878e13acdd3d5d5 (diff)
downloadrust-adb15a20ac99024ea90b28abd5af12f438fa2a20.tar.gz
rust-adb15a20ac99024ea90b28abd5af12f438fa2a20.zip
Auto merge of #114560 - RalfJung:miri, r=RalfJung
update Miri
-rw-r--r--src/tools/miri/.github/workflows/ci.yml2
-rw-r--r--src/tools/miri/CONTRIBUTING.md2
-rw-r--r--src/tools/miri/cargo-miri/src/main.rs2
-rw-r--r--src/tools/miri/cargo-miri/src/phases.rs9
-rw-r--r--src/tools/miri/cargo-miri/src/setup.rs23
-rw-r--r--src/tools/miri/cargo-miri/src/util.rs45
-rwxr-xr-xsrc/tools/miri/miri361
-rw-r--r--src/tools/miri/miri-script/Cargo.lock160
-rw-r--r--src/tools/miri/miri-script/Cargo.toml22
-rwxr-xr-xsrc/tools/miri/miri-script/miri4
-rw-r--r--src/tools/miri/miri-script/src/commands.rs468
-rw-r--r--src/tools/miri/miri-script/src/main.rs210
-rw-r--r--src/tools/miri/miri-script/src/util.rs144
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs43
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs2
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs108
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs23
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs44
-rw-r--r--src/tools/miri/src/helpers.rs2
-rw-r--r--src/tools/miri/src/machine.rs7
-rw-r--r--src/tools/miri/tests/compiletest.rs4
-rw-r--r--src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.rs29
-rw-r--r--src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.stack.stderr20
-rw-r--r--src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.tree.stderr25
-rw-r--r--src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs (renamed from src/tools/miri/tests/fail/stacked_borrows/retag_data_race_write.rs)4
-rw-r--r--src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr (renamed from src/tools/miri/tests/fail/stacked_borrows/retag_data_race_write.stderr)0
-rw-r--r--src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr25
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs30
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stderr39
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stack.stderr (renamed from src/tools/miri/tests/fail/tree_borrows/retag-data-race.stderr)20
-rw-r--r--src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.tree.stderr26
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/fragile-data-race.rs42
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/fragile-data-race.stderr32
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.rs2
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.rs2
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/retag-data-race.rs28
-rw-r--r--src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment.rs24
-rw-r--r--src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment.stderr20
-rw-r--r--src/tools/miri/tests/pass-dep/shims/libc-fs.rs31
-rw-r--r--src/tools/miri/tests/pass-dep/shims/libc-misc.rs35
-rw-r--r--src/tools/miri/tests/pass/align_repeat_into_packed_field.rs20
-rw-r--r--src/tools/miri/tests/pass/align_strange_enum_discriminant_offset.rs23
-rw-r--r--src/tools/miri/tests/pass/issues/issue-53728.rs18
-rw-r--r--src/tools/miri/tests/pass/ptr_raw.rs9
-rw-r--r--src/tools/miri/tests/pass/shims/fs.rs39
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs37
-rw-r--r--src/tools/miri/tests/pass/strange_references.rs25
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs2
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs2
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/formatting.rs2
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs2
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/reserved.rs7
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs51
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/unique.rs2
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/vec_unique.rs2
-rw-r--r--src/tools/miri/tests/utils/fs.rs29
-rw-r--r--src/tools/miri/tests/utils/macros.rs18
-rw-r--r--src/tools/miri/tests/utils/miri_extern.rs2
-rw-r--r--src/tools/miri/tests/utils/mod.rs12
60 files changed, 1645 insertions, 778 deletions
diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml
index 042bb9bbd2c..8ced9fa86be 100644
--- a/src/tools/miri/.github/workflows/ci.yml
+++ b/src/tools/miri/.github/workflows/ci.yml
@@ -130,7 +130,7 @@ jobs:
       - name: clippy
         run: ./miri clippy -- -D warnings
       - name: rustdoc
-        run: RUSTDOCFLAGS="-Dwarnings" cargo doc --document-private-items
+        run: RUSTDOCFLAGS="-Dwarnings" ./miri cargo doc --document-private-items
 
   # These jobs doesn't actually test anything, but they're only used to tell
   # bors the build completed, as there is no practical way to detect when a
diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md
index bcdb623b090..b67e7103fd0 100644
--- a/src/tools/miri/CONTRIBUTING.md
+++ b/src/tools/miri/CONTRIBUTING.md
@@ -107,7 +107,7 @@ evaluation error was originally raised.
 ### UI testing
 
 We use ui-testing in Miri, meaning we generate `.stderr` and `.stdout` files for the output
-produced by Miri. You can use `./miri bless` to automatically (re)generate these files when
+produced by Miri. You can use `./miri test --bless` to automatically (re)generate these files when
 you add new tests or change how Miri presents certain output.
 
 Note that when you also use `MIRIFLAGS` to change optimizations and similar, the ui output
diff --git a/src/tools/miri/cargo-miri/src/main.rs b/src/tools/miri/cargo-miri/src/main.rs
index 6178670b4f0..c5fada6fe55 100644
--- a/src/tools/miri/cargo-miri/src/main.rs
+++ b/src/tools/miri/cargo-miri/src/main.rs
@@ -56,7 +56,7 @@ fn main() {
         return;
     }
 
-    // The way rustdoc invokes rustc is indistuingishable from the way cargo invokes rustdoc by the
+    // The way rustdoc invokes rustc is indistinguishable from the way cargo invokes rustdoc by the
     // arguments alone. `phase_cargo_rustdoc` sets this environment variable to let us disambiguate.
     if env::var_os("MIRI_CALLED_FROM_RUSTDOC").is_some() {
         // ...however, we then also see this variable when rustdoc invokes us as the testrunner!
diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs
index 465e4a1b2d2..80ce6325582 100644
--- a/src/tools/miri/cargo-miri/src/phases.rs
+++ b/src/tools/miri/cargo-miri/src/phases.rs
@@ -94,7 +94,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
     let target = target.as_ref().unwrap_or(host);
 
     // We always setup.
-    setup(&subcommand, target, &rustc_version, verbose);
+    let miri_sysroot = setup(&subcommand, target, &rustc_version, verbose);
 
     // Invoke actual cargo for the job, but with different flags.
     // We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but
@@ -159,6 +159,8 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
     // Forward all further arguments (not consumed by `ArgSplitFlagValue`) to cargo.
     cmd.args(args);
 
+    // Let it know where the Miri sysroot lives.
+    cmd.env("MIRI_SYSROOT", miri_sysroot);
     // Set `RUSTC_WRAPPER` to ourselves.  Cargo will prepend that binary to its usual invocation,
     // i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish
     // the two codepaths. (That extra argument is why we prefer this over setting `RUSTC`.)
@@ -519,7 +521,7 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
     // `.rmeta`.
     // We also need to remove `--error-format` as cargo specifies that to be JSON,
     // but when we run here, cargo does not interpret the JSON any more. `--json`
-    // then also nees to be dropped.
+    // then also needs to be dropped.
     let mut args = info.args.into_iter();
     let error_format_flag = "--error-format";
     let json_flag = "--json";
@@ -538,8 +540,7 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
     }
     // Respect `MIRIFLAGS`.
     if let Ok(a) = env::var("MIRIFLAGS") {
-        // This code is taken from `RUSTFLAGS` handling in cargo.
-        let args = a.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string);
+        let args = flagsplit(&a);
         cmd.args(args);
     }
 
diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs
index 2e4f0a71013..77cecddcb8b 100644
--- a/src/tools/miri/cargo-miri/src/setup.rs
+++ b/src/tools/miri/cargo-miri/src/setup.rs
@@ -13,13 +13,20 @@ use crate::util::*;
 /// Performs the setup required to make `cargo miri` work: Getting a custom-built libstd. Then sets
 /// `MIRI_SYSROOT`. Skipped if `MIRI_SYSROOT` is already set, in which case we expect the user has
 /// done all this already.
-pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta, verbose: usize) {
+pub fn setup(
+    subcommand: &MiriCommand,
+    target: &str,
+    rustc_version: &VersionMeta,
+    verbose: usize,
+) -> PathBuf {
     let only_setup = matches!(subcommand, MiriCommand::Setup);
     let ask_user = !only_setup;
     let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
-    if !only_setup && std::env::var_os("MIRI_SYSROOT").is_some() {
-        // Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`.
-        return;
+    if !only_setup {
+        if let Some(sysroot) = std::env::var_os("MIRI_SYSROOT") {
+            // Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`.
+            return sysroot.into();
+        }
     }
 
     // Determine where the rust sources are located.  The env var trumps auto-detection.
@@ -92,6 +99,8 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
             command.env("RUSTC", &cargo_miri_path);
         }
         command.env("MIRI_CALLED_FROM_SETUP", "1");
+        // Miri expects `MIRI_SYSROOT` to be set when invoked in target mode. Even if that directory is empty.
+        command.env("MIRI_SYSROOT", &sysroot_dir);
         // Make sure there are no other wrappers getting in our way (Cc
         // https://github.com/rust-lang/miri/issues/1421,
         // https://github.com/rust-lang/miri/issues/2429). Looks like setting
@@ -105,7 +114,7 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
                 command.arg("-v");
             }
         } else {
-            // Supress output.
+            // Suppress output.
             command.stdout(process::Stdio::null());
             command.stderr(process::Stdio::null());
         }
@@ -117,8 +126,6 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
     // the user might have set, which is consistent with normal `cargo build` that does
     // not apply `RUSTFLAGS` to the sysroot either.
     let rustflags = &["-Cdebug-assertions=off", "-Coverflow-checks=on"];
-    // Make sure all target-level Miri invocations know their sysroot.
-    std::env::set_var("MIRI_SYSROOT", &sysroot_dir);
 
     // Do the build.
     if print_sysroot {
@@ -159,4 +166,6 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
         // Print just the sysroot and nothing else to stdout; this way we do not need any escaping.
         println!("{}", sysroot_dir.display());
     }
+
+    sysroot_dir
 }
diff --git a/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs
index 4c19ed97fb8..6381a4db861 100644
--- a/src/tools/miri/cargo-miri/src/util.rs
+++ b/src/tools/miri/cargo-miri/src/util.rs
@@ -1,7 +1,5 @@
-use std::collections::HashMap;
 use std::env;
 use std::ffi::OsString;
-use std::fmt::Write as _;
 use std::fs::File;
 use std::io::{self, BufWriter, Read, Write};
 use std::ops::Not;
@@ -114,6 +112,11 @@ pub fn cargo() -> Command {
     Command::new(env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo")))
 }
 
+pub fn flagsplit(flags: &str) -> Vec<String> {
+    // This code is taken from `RUSTFLAGS` handling in cargo.
+    flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect()
+}
+
 /// Execute the `Command`, where possible by replacing the current process with a new process
 /// described by the `Command`. Then exit this process with the exit code of the new process.
 pub fn exec(mut cmd: Command) -> ! {
@@ -242,46 +245,10 @@ pub fn local_crates(metadata: &Metadata) -> String {
     local_crates
 }
 
-fn env_vars_from_cmd(cmd: &Command) -> Vec<(String, String)> {
-    let mut envs = HashMap::new();
-    for (key, value) in std::env::vars() {
-        envs.insert(key, value);
-    }
-    for (key, value) in cmd.get_envs() {
-        if let Some(value) = value {
-            envs.insert(key.to_string_lossy().to_string(), value.to_string_lossy().to_string());
-        } else {
-            envs.remove(&key.to_string_lossy().to_string());
-        }
-    }
-    let mut envs: Vec<_> = envs.into_iter().collect();
-    envs.sort();
-    envs
-}
-
 /// Debug-print a command that is going to be run.
 pub fn debug_cmd(prefix: &str, verbose: usize, cmd: &Command) {
     if verbose == 0 {
         return;
     }
-    // We only do a single `eprintln!` call to minimize concurrency interactions.
-    let mut out = prefix.to_string();
-    writeln!(out, " running command: env \\").unwrap();
-    if verbose > 1 {
-        // Print the full environment this will be called in.
-        for (key, value) in env_vars_from_cmd(cmd) {
-            writeln!(out, "{key}={value:?} \\").unwrap();
-        }
-    } else {
-        // Print only what has been changed for this `cmd`.
-        for (var, val) in cmd.get_envs() {
-            if let Some(val) = val {
-                writeln!(out, "{}={val:?} \\", var.to_string_lossy()).unwrap();
-            } else {
-                writeln!(out, "--unset={}", var.to_string_lossy()).unwrap();
-            }
-        }
-    }
-    write!(out, "{cmd:?}").unwrap();
-    eprintln!("{out}");
+    eprintln!("{prefix} running command: {cmd:?}");
 }
diff --git a/src/tools/miri/miri b/src/tools/miri/miri
index bccf6d835ff..c816a4bb06b 100755
--- a/src/tools/miri/miri
+++ b/src/tools/miri/miri
@@ -1,359 +1,6 @@
 #!/bin/bash
 set -e
-USAGE=$(cat <<"EOF"
-  COMMANDS
-
-./miri install <flags>:
-Installs the miri driver and cargo-miri. <flags> are passed to `cargo
-install`. Sets up the rpath such that the installed binary should work in any
-working directory. Note that the binaries are placed in the `miri` toolchain
-sysroot, to prevent conflicts with other toolchains.
-
-./miri build <flags>:
-Just build miri. <flags> are passed to `cargo build`.
-
-./miri check <flags>:
-Just check miri. <flags> are passed to `cargo check`.
-
-./miri test <flags>:
-Build miri, set up a sysroot and then run the test suite. <flags> are passed
-to the final `cargo test` invocation.
-
-./miri run <flags>:
-Build miri, set up a sysroot and then run the driver with the given <flags>.
-(Also respects MIRIFLAGS environment variable.)
-
-./miri fmt <flags>:
-Format all sources and tests. <flags> are passed to `rustfmt`.
-
-./miri clippy <flags>:
-Runs clippy on all sources. <flags> are passed to `cargo clippy`.
-
-./miri cargo <flags>:
-Runs just `cargo <flags>` with the Miri-specific environment variables.
-Mainly meant to be invoked by rust-analyzer.
-
-./miri many-seeds <command>:
-Runs <command> over and over again with different seeds for Miri. The MIRIFLAGS
-variable is set to its original value appended with ` -Zmiri-seed=$SEED` for
-many different seeds. The MIRI_SEEDS variable controls how many seeds are being
-tried; MIRI_SEED_START controls the first seed to try.
-
-./miri bench <benches>:
-Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
-<benches> can explicitly list the benchmarks to run; by default, all of them are run.
-
-./miri toolchain <flags>:
-Update and activate the rustup toolchain 'miri' to the commit given in the
-`rust-version` file.
-`rustup-toolchain-install-master` must be installed for this to work. Any extra
-flags are passed to `rustup-toolchain-install-master`.
-
-./miri rustc-pull <commit>:
-Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
-rustc commit. The fetched commit is stored in the `rust-version` file, so the
-next `./miri toolchain` will install the rustc that just got pulled.
-
-./miri rustc-push <github user> <branch>:
-Push Miri changes back to the rustc repo. This will pull a copy of the rustc
-history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
-clone of the rustc repo.
-
-  ENVIRONMENT VARIABLES
-
-MIRI_SYSROOT:
-If already set, the "sysroot setup" step is skipped.
-
-CARGO_EXTRA_FLAGS:
-Pass extra flags to all cargo invocations. (Ignored by `./miri cargo`.)
-EOF
-)
-
-## We need to know which command to run and some global constants.
-COMMAND="$1"
-if [ -z "$COMMAND" ]; then
-    echo "$USAGE"
-    exit 1
-fi
-shift
-# macOS does not have a useful readlink/realpath so we have to use Python instead...
-MIRIDIR=$(python3 -c 'import pathlib, sys; print(pathlib.Path(sys.argv[1]).resolve().parent.as_posix())' "$0")
-# Used for rustc syncs.
-JOSH_FILTER=":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri"
-# Needed for `./miri bench`.
-TOOLCHAIN=$(cd "$MIRIDIR"; rustup show active-toolchain | head -n 1 | cut -d ' ' -f 1)
-
-## Early commands, that don't do auto-things and don't want the environment-altering things happening below.
-case "$COMMAND" in
-toolchain)
-    cd "$MIRIDIR"
-    NEW_COMMIT=$(cat rust-version)
-    # Make sure rustup-toolchain-install-master is installed.
-    if ! which rustup-toolchain-install-master >/dev/null; then
-        echo "Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'"
-        exit 1
-    fi
-    # Check if we already are at that commit.
-    CUR_COMMIT=$(rustc +miri --version -v 2>/dev/null | grep "^commit-hash: " | cut -d " " -f 2)
-    if [[ "$CUR_COMMIT" == "$NEW_COMMIT" ]]; then
-        echo "miri toolchain is already at commit $CUR_COMMIT."
-        if [[ "$TOOLCHAIN" != "miri" ]]; then
-            rustup override set miri
-        fi
-        exit 0
-    fi
-    # Install and setup new toolchain.
-    rustup toolchain uninstall miri
-    rustup-toolchain-install-master -n miri -c cargo -c rust-src -c rustc-dev -c llvm-tools -c rustfmt -c clippy "$@" -- "$NEW_COMMIT"
-    rustup override set miri
-    # Cleanup.
-    cargo clean
-    # Call 'cargo metadata' on the sources in case that changes the lockfile
-    # (which fails under some setups when it is done from inside vscode).
-    cargo metadata --format-version 1 --manifest-path "$(rustc --print sysroot)/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml" >/dev/null
-    # Done!
-    exit 0
-    ;;
-rustc-pull)
-    cd "$MIRIDIR"
-    FETCH_COMMIT="$1"
-    if [ -z "$FETCH_COMMIT" ]; then
-        FETCH_COMMIT=$(git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1)
-    fi
-    # Update rust-version file. As a separate commit, since making it part of
-    # the merge has confused the heck out of josh in the past.
-    echo "$FETCH_COMMIT" > rust-version
-    git commit rust-version -m "Preparing for merge from rustc" || (echo "FAILED to commit rust-version file, something went wrong"; exit 1)
-    # Fetch given rustc commit and note down which one that was
-    git fetch http://localhost:8000/rust-lang/rust.git@$FETCH_COMMIT$JOSH_FILTER.git || (echo "FAILED to fetch new commits, something went wrong"; exit 1)
-    git merge FETCH_HEAD --no-ff -m "Merge from rustc" || (echo "FAILED to merge new commits ($(git rev-parse FETCH_HEAD)), something went wrong"; exit 1)
-    exit 0
-    ;;
-rustc-push)
-    USER="$1"
-    BRANCH="$2"
-    if [ -z "$USER" ] || [ -z "$BRANCH" ]; then
-        echo "Usage: $0 rustc-push <github user> <branch>"
-        exit 1
-    fi
-    if [ -n "$RUSTC_GIT" ]; then
-        # Use an existing fork for the branch updates.
-        cd "$RUSTC_GIT"
-    else
-        # Do this in the local Miri repo.
-        echo "This will pull a copy of the rust-lang/rust history into this Miri checkout, growing it by about 1GB."
-        read -r -p "To avoid that, abort now and set the RUSTC_GIT environment variable to an existing rustc checkout. Proceed? [y/N] "
-        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
-            exit 1
-        fi
-        cd "$MIRIDIR"
-    fi
-    # Prepare the branch. Pushing works much better if we use as base exactly
-    # the commit that we pulled from last time, so we use the `rust-version`
-    # file as a good approximation of that.
-    BASE=$(cat "$MIRIDIR/rust-version")
-    echo "Preparing $USER/rust (base: $BASE)..."
-    if git fetch "https://github.com/$USER/rust" "$BRANCH" &>/dev/null; then
-        echo "The branch '$BRANCH' seems to already exist in 'https://github.com/$USER/rust'. Please delete it and try again."
-        exit 1
-    fi
-    git fetch https://github.com/rust-lang/rust $BASE
-    git push https://github.com/$USER/rust $BASE:refs/heads/$BRANCH -f
-    echo
-    # Do the actual push.
-    cd "$MIRIDIR"
-    echo "Pushing Miri changes..."
-    git push http://localhost:8000/$USER/rust.git$JOSH_FILTER.git HEAD:$BRANCH
-    # Do a round-trip check to make sure the push worked as expected.
-    echo
-    git fetch http://localhost:8000/$USER/rust.git@$JOSH_FILTER.git $BRANCH &>/dev/null
-    if [[ $(git rev-parse HEAD) != $(git rev-parse FETCH_HEAD) ]]; then
-        echo "ERROR: Josh created a non-roundtrip push! Do NOT merge this into rustc!"
-        exit 1
-    else
-        echo "Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:"
-        echo "    https://github.com/$USER/rust/pull/new/$BRANCH"
-        exit 0
-    fi
-    ;;
-many-seeds)
-    MIRI_SEED_START=${MIRI_SEED_START:-0} # default to 0
-    MIRI_SEEDS=${MIRI_SEEDS:-256} # default to 256
-    for SEED in $(seq $MIRI_SEED_START $(( $MIRI_SEED_START + $MIRI_SEEDS - 1 )) ); do
-        echo "Trying seed: $SEED"
-        MIRIFLAGS="$MIRIFLAGS -Zlayout-seed=$SEED -Zmiri-seed=$SEED" $@ || { echo "Failing seed: $SEED"; break; }
-    done
-    exit 0
-    ;;
-bench)
-    # The hyperfine to use
-    HYPERFINE=${HYPERFINE:-hyperfine -w 1 -m 5 --shell=none}
-    # Make sure we have an up-to-date Miri installed
-    "$0" install
-    # Run the requested benchmarks
-    if [ -z "${1+exists}" ]; then
-        BENCHES=( $(ls "$MIRIDIR/bench-cargo-miri" ) )
-    else
-        BENCHES=("$@")
-    fi
-    for BENCH in "${BENCHES[@]}"; do
-        $HYPERFINE "cargo +$TOOLCHAIN miri run --manifest-path $MIRIDIR/bench-cargo-miri/$BENCH/Cargo.toml"
-    done
-    exit 0
-    ;;
-esac
-
-## Run the auto-things.
-if [ -z "$MIRI_AUTO_OPS" ]; then
-    export MIRI_AUTO_OPS=42
-
-    # Run this first, so that the toolchain doesn't change after
-    # other code has run.
-    if [ -f "$MIRIDIR/.auto-everything" ] || [ -f "$MIRIDIR/.auto-toolchain" ] ; then
-        $0 toolchain
-        # Let's make sure to actually use that toolchain, too.
-        TOOLCHAIN=miri
-    fi
-
-    if [ -f "$MIRIDIR/.auto-everything" ] || [ -f "$MIRIDIR/.auto-fmt" ] ; then
-        $0 fmt
-    fi
-
-    if [ -f "$MIRIDIR/.auto-everything" ] || [ -f "$MIRIDIR/.auto-clippy" ] ; then
-        $0 clippy -- -D warnings
-    fi
-fi
-
-## Prepare the environment
-# Determine some toolchain properties
-TARGET=$(rustc +$TOOLCHAIN --version --verbose | grep "^host:" | cut -d ' ' -f 2)
-SYSROOT=$(rustc +$TOOLCHAIN --print sysroot)
-LIBDIR=$SYSROOT/lib/rustlib/$TARGET/lib
-if ! test -d "$LIBDIR"; then
-    echo "Something went wrong determining the library dir."
-    echo "I got $LIBDIR but that does not exist."
-    echo "Please report a bug at https://github.com/rust-lang/miri/issues."
-    exit 2
-fi
-
-# Prepare flags for cargo and rustc.
-CARGO="cargo +$TOOLCHAIN"
-# Share target dir between `miri` and `cargo-miri`.
-if [ -z "$CARGO_TARGET_DIR" ]; then
-    export CARGO_TARGET_DIR="$MIRIDIR/target"
-fi
-# We configure dev builds to not be unusably slow.
-if [ -z "$CARGO_PROFILE_DEV_OPT_LEVEL" ]; then
-    export CARGO_PROFILE_DEV_OPT_LEVEL=2
-fi
-# Enable rustc-specific lints (ignored without `-Zunstable-options`).
-export RUSTFLAGS="-Zunstable-options -Wrustc::internal -Wrust_2018_idioms -Wunused_lifetimes -Wsemicolon_in_expressions_from_macros $RUSTFLAGS"
-# We set the rpath so that Miri finds the private rustc libraries it needs.
-export RUSTFLAGS="-C link-args=-Wl,-rpath,$LIBDIR $RUSTFLAGS"
-
-## Helper functions
-
-# Build a sysroot and set MIRI_SYSROOT to use it. Arguments are passed to `cargo miri setup`.
-build_sysroot() {
-    if ! MIRI_SYSROOT="$($CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -- miri setup --print-sysroot "$@")"; then
-        # Run it again so the user can see the error.
-        $CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -- miri setup "$@"
-        echo "'cargo miri setup' failed"
-        exit 1
-    fi
-    export MIRI_SYSROOT
-}
-
-# Prepare and set MIRI_SYSROOT. Respects `MIRI_TEST_TARGET` and takes into account
-# locally built vs. distributed rustc.
-find_sysroot() {
-    if [ -n "$MIRI_SYSROOT" ]; then
-        # Sysroot already set, use that.
-        return 0
-    fi
-    # We need to build a sysroot.
-    if [ -n "$MIRI_TEST_TARGET" ]; then
-        build_sysroot --target "$MIRI_TEST_TARGET"
-    else
-        build_sysroot
-    fi
-}
-
-## Main
-
-# Run command.
-case "$COMMAND" in
-install)
-    # Install binaries to the miri toolchain's sysroot so they do not interact with other toolchains.
-    $CARGO install $CARGO_EXTRA_FLAGS --path "$MIRIDIR" --force --root "$SYSROOT" "$@"
-    $CARGO install $CARGO_EXTRA_FLAGS --path "$MIRIDIR"/cargo-miri --force --root "$SYSROOT" "$@"
-    ;;
-check)
-    # Check, and let caller control flags.
-    $CARGO check $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml --all-targets "$@"
-    $CARGO check $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml "$@"
-    ;;
-build)
-    # Build, and let caller control flags.
-    $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml "$@"
-    $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml "$@"
-    ;;
-test|bless)
-    # First build and get a sysroot.
-    $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml
-    find_sysroot
-    if [ "$COMMAND" = "bless" ]; then
-        export RUSTC_BLESS="Gesundheit"
-    fi
-    # Then test, and let caller control flags.
-    # Only in root project as `cargo-miri` has no tests.
-    $CARGO test $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml "$@"
-    ;;
-run|run-dep)
-    # Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so
-    # that we set the MIRI_SYSROOT up the right way.
-    FOUND_TARGET_OPT=0
-    for ARG in "$@"; do
-        if [ "$LAST_ARG" = "--target" ]; then
-            # Found it!
-            export MIRI_TEST_TARGET="$ARG"
-            FOUND_TARGET_OPT=1
-            break
-        fi
-        LAST_ARG="$ARG"
-    done
-    if [ "$FOUND_TARGET_OPT" = "0" ] && [ -n "$MIRI_TEST_TARGET" ]; then
-        # Make sure Miri actually uses this target.
-        MIRIFLAGS="$MIRIFLAGS --target $MIRI_TEST_TARGET"
-    fi
-
-    CARGO="$CARGO --quiet"
-    # First build and get a sysroot.
-    $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml
-    find_sysroot
-    # Then run the actual command.
-    
-    if [ "$COMMAND" = "run-dep" ]; then
-        exec $CARGO test --test compiletest $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml -- --miri-run-dep-mode $MIRIFLAGS "$@"
-    else
-        exec $CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml -- $MIRIFLAGS "$@"
-    fi
-    ;;
-fmt)
-    find "$MIRIDIR" -not \( -name target -prune \) -name '*.rs' \
-        | xargs rustfmt +$TOOLCHAIN --edition=2021 --config-path "$MIRIDIR/rustfmt.toml" "$@"
-    ;;
-clippy)
-    $CARGO clippy $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml --all-targets "$@"
-    $CARGO clippy $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml "$@"
-    ;;
-cargo)
-    # We carefully kept the working dir intact, so this will run cargo *on the workspace in the
-    # current working dir*, not on the main Miri workspace. That is exactly what RA needs.
-    $CARGO "$@"
-    ;;
-*)
-    echo "Unknown command: $COMMAND"
-    exit 1
-    ;;
-esac
+# Instead of doing just `cargo run --manifest-path .. $@`, we invoke miri-script binary directly. Invoking `cargo run` goes through
+# rustup (that sets it's own environmental variables), which is undesirable.
+cargo build -q --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml
+"$(dirname "$0")"/miri-script/target/debug/miri-script "$@"
diff --git a/src/tools/miri/miri-script/Cargo.lock b/src/tools/miri/miri-script/Cargo.lock
new file mode 100644
index 00000000000..cf6062d7d7f
--- /dev/null
+++ b/src/tools/miri/miri-script/Cargo.lock
@@ -0,0 +1,160 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anyhow"
+version = "1.0.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
+
+[[package]]
+name = "dunce"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
+
+[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.144"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
+
+[[package]]
+name = "miri-script"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "dunce",
+ "itertools",
+ "path_macro",
+ "rustc_version",
+ "shell-words",
+ "walkdir",
+ "which",
+ "xshell",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
+
+[[package]]
+name = "path_macro"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6e819bbd49d5939f682638fa54826bf1650abddcd65d000923de8ad63cc7d15"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
+
+[[package]]
+name = "shell-words"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
+
+[[package]]
+name = "walkdir"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "which"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
+dependencies = [
+ "either",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "xshell"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "962c039b3a7b16cf4e9a4248397c6585c07547412e7d6a6e035389a802dcfe90"
+dependencies = [
+ "xshell-macros",
+]
+
+[[package]]
+name = "xshell-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dbabb1cbd15a1d6d12d9ed6b35cc6777d4af87ab3ba155ea37215f20beab80c"
diff --git a/src/tools/miri/miri-script/Cargo.toml b/src/tools/miri/miri-script/Cargo.toml
new file mode 100644
index 00000000000..c0414a2fe37
--- /dev/null
+++ b/src/tools/miri/miri-script/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+authors = ["Miri Team"]
+description = "Helpers for miri maintenance"
+license = "MIT OR Apache-2.0"
+name = "miri-script"
+repository = "https://github.com/rust-lang/miri"
+version = "0.1.0"
+default-run = "miri-script"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+which = "4.4"
+walkdir = "2.3"
+itertools = "0.10"
+path_macro = "1.0"
+shell-words = "1.1"
+anyhow = "1.0"
+xshell = "0.2"
+rustc_version = "0.4"
+dunce = "1.0.4"
diff --git a/src/tools/miri/miri-script/miri b/src/tools/miri/miri-script/miri
new file mode 100755
index 00000000000..cf3ad06788a
--- /dev/null
+++ b/src/tools/miri/miri-script/miri
@@ -0,0 +1,4 @@
+#!/bin/sh
+# RA invokes `./miri cargo ...` for each workspace, so we need to forward that to the main `miri`
+# script. See <https://github.com/rust-analyzer/rust-analyzer/issues/10793>.
+exec "$(dirname "$0")"/../miri "$@"
diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs
new file mode 100644
index 00000000000..fe66f1a9bdb
--- /dev/null
+++ b/src/tools/miri/miri-script/src/commands.rs
@@ -0,0 +1,468 @@
+use std::env;
+use std::ffi::OsString;
+use std::io::Write;
+use std::ops::Not;
+
+use anyhow::{anyhow, bail, Context, Result};
+use path_macro::path;
+use walkdir::WalkDir;
+use xshell::{cmd, Shell};
+
+use crate::util::*;
+use crate::Command;
+
+/// Used for rustc syncs.
+const JOSH_FILTER: &str =
+    ":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri";
+
+impl MiriEnv {
+    fn build_miri_sysroot(&mut self, quiet: bool) -> Result<()> {
+        if self.sh.var("MIRI_SYSROOT").is_ok() {
+            // Sysroot already set, use that.
+            return Ok(());
+        }
+        let manifest_path = path!(self.miri_dir / "cargo-miri" / "Cargo.toml");
+        let Self { toolchain, cargo_extra_flags, .. } = &self;
+
+        // Make sure everything is built. Also Miri itself.
+        self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?;
+        self.build(&manifest_path, &[], quiet)?;
+
+        let target = &match self.sh.var("MIRI_TEST_TARGET") {
+            Ok(target) => vec!["--target".into(), target],
+            Err(_) => vec![],
+        };
+        if !quiet {
+            eprintln!("$ (building Miri sysroot)");
+        }
+        let output = cmd!(self.sh,
+            "cargo +{toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} --
+             miri setup --print-sysroot {target...}"
+        ).read();
+        let Ok(output) = output else {
+            // Run it again (without `--print-sysroot` or `--quiet`) so the user can see the error.
+            cmd!(
+                self.sh,
+                "cargo +{toolchain} run {cargo_extra_flags...} --manifest-path {manifest_path} --
+                miri setup {target...}"
+            )
+            .run()
+            .with_context(|| "`cargo miri setup` failed")?;
+            panic!("`cargo miri setup` didn't fail again the 2nd time?");
+        };
+        self.sh.set_var("MIRI_SYSROOT", output);
+        Ok(())
+    }
+}
+
+impl Command {
+    fn auto_actions() -> Result<()> {
+        let miri_dir = miri_dir()?;
+        let auto_everything = path!(miri_dir / ".auto-everything").exists();
+        let auto_toolchain = auto_everything || path!(miri_dir / ".auto-toolchain").exists();
+        let auto_fmt = auto_everything || path!(miri_dir / ".auto-fmt").exists();
+        let auto_clippy = auto_everything || path!(miri_dir / ".auto-clippy").exists();
+
+        // `toolchain` goes first as it could affect the others
+        if auto_toolchain {
+            Self::toolchain(vec![])?;
+        }
+        if auto_fmt {
+            Self::fmt(vec![])?;
+        }
+        if auto_clippy {
+            Self::clippy(vec![])?;
+        }
+
+        Ok(())
+    }
+
+    pub fn exec(self) -> Result<()> {
+        match &self {
+            Command::Install { .. }
+            | Command::Build { .. }
+            | Command::Check { .. }
+            | Command::Test { .. }
+            | Command::Run { .. }
+            | Command::Fmt { .. }
+            | Command::Clippy { .. }
+            | Command::Cargo { .. } => Self::auto_actions()?,
+            | Command::ManySeeds { .. }
+            | Command::Toolchain { .. }
+            | Command::RustcPull { .. }
+            | Command::Bench { .. }
+            | Command::RustcPush { .. } => {}
+        }
+        match self {
+            Command::Install { flags } => Self::install(flags),
+            Command::Build { flags } => Self::build(flags),
+            Command::Check { flags } => Self::check(flags),
+            Command::Test { bless, flags } => Self::test(bless, flags),
+            Command::Run { dep, flags } => Self::run(dep, flags),
+            Command::Fmt { flags } => Self::fmt(flags),
+            Command::Clippy { flags } => Self::clippy(flags),
+            Command::Cargo { flags } => Self::cargo(flags),
+            Command::ManySeeds { command } => Self::many_seeds(command),
+            Command::Bench { benches } => Self::bench(benches),
+            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),
+        }
+    }
+
+    fn toolchain(flags: Vec<OsString>) -> Result<()> {
+        // Make sure rustup-toolchain-install-master is installed.
+        which::which("rustup-toolchain-install-master")
+            .context("Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'")?;
+        let sh = Shell::new()?;
+        sh.change_dir(miri_dir()?);
+        let new_commit = Some(sh.read_file("rust-version")?.trim().to_owned());
+        let current_commit = {
+            let rustc_info = cmd!(sh, "rustc +miri --version -v").read();
+            if rustc_info.is_err() {
+                None
+            } else {
+                let metadata = rustc_version::version_meta_for(&rustc_info.unwrap())?;
+                Some(
+                    metadata
+                        .commit_hash
+                        .ok_or_else(|| anyhow!("rustc metadata did not contain commit hash"))?,
+                )
+            }
+        };
+        // Check if we already are at that commit.
+        if current_commit == new_commit {
+            if active_toolchain()? != "miri" {
+                cmd!(sh, "rustup override set miri").run()?;
+            }
+            return Ok(());
+        }
+        // Install and setup new toolchain.
+        cmd!(sh, "rustup toolchain uninstall miri").run()?;
+
+        cmd!(sh, "rustup-toolchain-install-master -n miri -c cargo -c rust-src -c rustc-dev -c llvm-tools -c rustfmt -c clippy {flags...} -- {new_commit...}").run()?;
+        cmd!(sh, "rustup override set miri").run()?;
+        // Cleanup.
+        cmd!(sh, "cargo clean").run()?;
+        // Call `cargo metadata` on the sources in case that changes the lockfile
+        // (which fails under some setups when it is done from inside vscode).
+        let sysroot = cmd!(sh, "rustc --print sysroot").read()?;
+        let sysroot = sysroot.trim();
+        cmd!(sh, "cargo metadata --format-version 1 --manifest-path {sysroot}/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml").ignore_stdout().run()?;
+        Ok(())
+    }
+
+    fn rustc_pull(commit: Option<String>) -> Result<()> {
+        let sh = Shell::new()?;
+        sh.change_dir(miri_dir()?);
+        let commit = commit.map(Result::Ok).unwrap_or_else(|| {
+            let rust_repo_head =
+                cmd!(sh, "git ls-remote https://github.com/rust-lang/rust/ HEAD").read()?;
+            rust_repo_head
+                .split_whitespace()
+                .next()
+                .map(|front| front.trim().to_owned())
+                .ok_or_else(|| anyhow!("Could not obtain Rust repo HEAD from remote."))
+        })?;
+        // Make sure the repo is clean.
+        if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() {
+            bail!("working directory must be clean before running `./miri rustc-pull`");
+        }
+
+        // Update rust-version file. As a separate commit, since making it part of
+        // the merge has confused the heck out of josh in the past.
+        // We pass `--no-verify` to avoid running git hooks like `./miri fmt` that could in turn
+        // trigger auto-actions.
+        sh.write_file("rust-version", &commit)?;
+        const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc";
+        cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}")
+            .run()
+            .context("FAILED to commit rust-version file, something went wrong")?;
+
+        // Fetch given rustc commit.
+        cmd!(sh, "git fetch http://localhost:8000/rust-lang/rust.git@{commit}{JOSH_FILTER}.git")
+            .run()
+            .map_err(|e| {
+                // Try to un-do the previous `git commit`, to leave the repo in the state we found it it.
+                cmd!(sh, "git reset --hard HEAD^")
+                    .run()
+                    .expect("FAILED to clean up again after failed `git fetch`, sorry for that");
+                e
+            })
+            .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?;
+
+        // Merge the fetched commit.
+        const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc";
+        cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}")
+            .run()
+            .context("FAILED to merge new commits, something went wrong")?;
+        Ok(())
+    }
+
+    fn rustc_push(github_user: String, branch: String) -> Result<()> {
+        let sh = Shell::new()?;
+        sh.change_dir(miri_dir()?);
+        let base = sh.read_file("rust-version")?.trim().to_owned();
+        // Make sure the repo is clean.
+        if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() {
+            bail!("working directory must be clean before running `./miri rustc-push`");
+        }
+
+        // Find a repo we can do our preparation in.
+        if let Ok(rustc_git) = env::var("RUSTC_GIT") {
+            // If rustc_git is `Some`, we'll use an existing fork for the branch updates.
+            sh.change_dir(rustc_git);
+        } else {
+            // Otherwise, do this in the local Miri repo.
+            println!(
+                "This will pull a copy of the rust-lang/rust history into this Miri checkout, growing it by about 1GB."
+            );
+            print!(
+                "To avoid that, abort now and set the `--rustc-git` flag to an existing rustc checkout. Proceed? [y/N] "
+            );
+            std::io::stdout().flush()?;
+            let mut answer = String::new();
+            std::io::stdin().read_line(&mut answer)?;
+            if answer.trim().to_lowercase() != "y" {
+                std::process::exit(1);
+            }
+        };
+        // Prepare the branch. Pushing works much better if we use as base exactly
+        // the commit that we pulled from last time, so we use the `rust-version`
+        // file as a good approximation of that.
+        println!("Preparing {github_user}/rust (base: {base})...");
+        if cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}")
+            .ignore_stderr()
+            .read()
+            .is_ok()
+        {
+            println!(
+                "The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again."
+            );
+            std::process::exit(1);
+        }
+        cmd!(sh, "git fetch https://github.com/rust-lang/rust {base}").run()?;
+        cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}")
+            .run()?;
+        println!();
+
+        // Do the actual push.
+        sh.change_dir(miri_dir()?);
+        println!("Pushing miri changes...");
+        cmd!(
+            sh,
+            "git push http://localhost:8000/{github_user}/rust.git{JOSH_FILTER}.git HEAD:{branch}"
+        )
+        .run()?;
+        println!();
+
+        // Do a round-trip check to make sure the push worked as expected.
+        cmd!(
+            sh,
+            "git fetch http://localhost:8000/{github_user}/rust.git{JOSH_FILTER}.git {branch}"
+        )
+        .ignore_stderr()
+        .read()?;
+        let head = cmd!(sh, "git rev-parse HEAD").read()?;
+        let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?;
+        if head != fetch_head {
+            bail!("Josh created a non-roundtrip push! Do NOT merge this into rustc!");
+        }
+        println!(
+            "Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:"
+        );
+        println!("    https://github.com/{github_user}/rust/pull/new/{branch}");
+        Ok(())
+    }
+
+    fn many_seeds(command: Vec<OsString>) -> Result<()> {
+        let seed_start: u64 = env::var("MIRI_SEED_START")
+            .unwrap_or_else(|_| "0".into())
+            .parse()
+            .context("failed to parse MIRI_SEED_START")?;
+        let seed_count: u64 = env::var("MIRI_SEEDS")
+            .unwrap_or_else(|_| "256".into())
+            .parse()
+            .context("failed to parse MIRI_SEEDS")?;
+        let seed_end = seed_start + seed_count;
+        let Some((command_name, trailing_args)) = command.split_first() else {
+            bail!("expected many-seeds command to be non-empty");
+        };
+        let sh = Shell::new()?;
+        for seed in seed_start..seed_end {
+            println!("Trying seed: {seed}");
+            let mut miriflags = env::var_os("MIRIFLAGS").unwrap_or_default();
+            miriflags.push(format!(" -Zlayout-seed={seed} -Zmiri-seed={seed}"));
+            let status = cmd!(sh, "{command_name} {trailing_args...}")
+                .env("MIRIFLAGS", miriflags)
+                .quiet()
+                .run();
+            if status.is_err() {
+                println!("Failing seed: {seed}");
+                break;
+            }
+        }
+        Ok(())
+    }
+
+    fn bench(benches: Vec<OsString>) -> Result<()> {
+        // The hyperfine to use
+        let hyperfine = env::var("HYPERFINE");
+        let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
+        let hyperfine = shell_words::split(hyperfine)?;
+        let Some((program_name, args)) = hyperfine.split_first() else {
+            bail!("expected HYPERFINE environment variable to be non-empty");
+        };
+        // Make sure we have an up-to-date Miri installed and selected the right toolchain.
+        Self::install(vec![])?;
+
+        let sh = Shell::new()?;
+        sh.change_dir(miri_dir()?);
+        let benches_dir = "bench-cargo-miri";
+        let benches = if benches.is_empty() {
+            sh.read_dir(benches_dir)?
+                .into_iter()
+                .filter(|path| path.is_dir())
+                .map(Into::into)
+                .collect()
+        } else {
+            benches.to_owned()
+        };
+        // Run the requested benchmarks
+        for bench in benches {
+            let current_bench = path!(benches_dir / bench / "Cargo.toml");
+            // We don't attempt to escape `current_bench`, but we wrap it in quotes.
+            // That seems to make Windows CI happy.
+            cmd!(
+                sh,
+                "{program_name} {args...} 'cargo miri run --manifest-path \"'{current_bench}'\"'"
+            )
+            .run()?;
+        }
+        Ok(())
+    }
+
+    fn install(flags: Vec<OsString>) -> Result<()> {
+        let e = MiriEnv::new()?;
+        e.install_to_sysroot(e.miri_dir.clone(), &flags)?;
+        e.install_to_sysroot(path!(e.miri_dir / "cargo-miri"), &flags)?;
+        Ok(())
+    }
+
+    fn build(flags: Vec<OsString>) -> Result<()> {
+        let e = MiriEnv::new()?;
+        e.build(path!(e.miri_dir / "Cargo.toml"), &flags, /* quiet */ false)?;
+        e.build(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags, /* quiet */ false)?;
+        Ok(())
+    }
+
+    fn check(flags: Vec<OsString>) -> Result<()> {
+        let e = MiriEnv::new()?;
+        e.check(path!(e.miri_dir / "Cargo.toml"), &flags)?;
+        e.check(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
+        Ok(())
+    }
+
+    fn clippy(flags: Vec<OsString>) -> Result<()> {
+        let e = MiriEnv::new()?;
+        e.clippy(path!(e.miri_dir / "Cargo.toml"), &flags)?;
+        e.clippy(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
+        e.clippy(path!(e.miri_dir / "miri-script" / "Cargo.toml"), &flags)?;
+        Ok(())
+    }
+
+    fn cargo(flags: Vec<OsString>) -> Result<()> {
+        let e = MiriEnv::new()?;
+        let toolchain = &e.toolchain;
+        // We carefully kept the working dir intact, so this will run cargo *on the workspace in the
+        // current working dir*, not on the main Miri workspace. That is exactly what RA needs.
+        cmd!(e.sh, "cargo +{toolchain} {flags...}").run()?;
+        Ok(())
+    }
+
+    fn test(bless: bool, flags: Vec<OsString>) -> Result<()> {
+        let mut e = MiriEnv::new()?;
+        // Prepare a sysroot.
+        e.build_miri_sysroot(/* quiet */ false)?;
+
+        // Then test, and let caller control flags.
+        // Only in root project as `cargo-miri` has no tests.
+        if bless {
+            e.sh.set_var("RUSTC_BLESS", "Gesundheit");
+        }
+        e.test(path!(e.miri_dir / "Cargo.toml"), &flags)?;
+        Ok(())
+    }
+
+    fn run(dep: bool, flags: Vec<OsString>) -> Result<()> {
+        let mut e = MiriEnv::new()?;
+        // Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so
+        // that we set the MIRI_SYSROOT up the right way.
+        use itertools::Itertools;
+        let target = flags.iter().tuple_windows().find(|(first, _)| first == &"--target");
+        if let Some((_, target)) = target {
+            // Found it!
+            e.sh.set_var("MIRI_TEST_TARGET", target);
+        } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") {
+            // Make sure miri actually uses this target.
+            let miriflags = e.sh.var("MIRIFLAGS").unwrap_or_default();
+            e.sh.set_var("MIRIFLAGS", format!("{miriflags} --target {target}"));
+        }
+        // Prepare a sysroot.
+        e.build_miri_sysroot(/* quiet */ true)?;
+
+        // Then run the actual command.
+        let miri_manifest = path!(e.miri_dir / "Cargo.toml");
+        let miri_flags = e.sh.var("MIRIFLAGS").unwrap_or_default();
+        let miri_flags = flagsplit(&miri_flags);
+        let toolchain = &e.toolchain;
+        let extra_flags = &e.cargo_extra_flags;
+        if dep {
+            cmd!(
+                e.sh,
+                "cargo +{toolchain} --quiet test --test compiletest {extra_flags...} --manifest-path {miri_manifest} -- --miri-run-dep-mode {miri_flags...} {flags...}"
+            ).quiet().run()?;
+        } else {
+            cmd!(
+                e.sh,
+                "cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {flags...}"
+            ).quiet().run()?;
+        }
+        Ok(())
+    }
+
+    fn fmt(flags: Vec<OsString>) -> Result<()> {
+        let e = MiriEnv::new()?;
+        let toolchain = &e.toolchain;
+        let config_path = path!(e.miri_dir / "rustfmt.toml");
+
+        let mut cmd = cmd!(
+            e.sh,
+            "rustfmt +{toolchain} --edition=2021 --config-path {config_path} {flags...}"
+        );
+        eprintln!("$ {cmd} ...");
+
+        // Add all the filenames to the command.
+        // FIXME: `rustfmt` will follow the `mod` statements in these files, so we get a bunch of
+        // duplicate diffs.
+        for item in WalkDir::new(&e.miri_dir).into_iter().filter_entry(|entry| {
+            let name = entry.file_name().to_string_lossy();
+            let ty = entry.file_type();
+            if ty.is_file() {
+                name.ends_with(".rs")
+            } else {
+                // dir or symlink. skip `target` and `.git`.
+                &name != "target" && &name != ".git"
+            }
+        }) {
+            let item = item?;
+            if item.file_type().is_file() {
+                cmd = cmd.arg(item.into_path());
+            }
+        }
+
+        // We want our own error message, repeating the command is too much.
+        cmd.quiet().run().map_err(|_| anyhow!("`rustfmt` failed"))?;
+        Ok(())
+    }
+}
diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs
new file mode 100644
index 00000000000..849a9168028
--- /dev/null
+++ b/src/tools/miri/miri-script/src/main.rs
@@ -0,0 +1,210 @@
+mod commands;
+mod util;
+
+use std::env;
+use std::ffi::OsString;
+
+use anyhow::{anyhow, bail, Result};
+
+#[derive(Clone, Debug)]
+pub enum Command {
+    /// Installs the miri driver and cargo-miri.
+    /// Sets up the rpath such that the installed binary should work in any
+    /// working directory. Note that the binaries are placed in the `miri` toolchain
+    /// sysroot, to prevent conflicts with other toolchains.
+    Install {
+        /// Flags that are passed through to `cargo install`.
+        flags: Vec<OsString>,
+    },
+    /// Just build miri.
+    Build {
+        /// Flags that are passed through to `cargo build`.
+        flags: Vec<OsString>,
+    },
+    /// Just check miri.
+    Check {
+        /// Flags that are passed through to `cargo check`.
+        flags: Vec<OsString>,
+    },
+    /// Build miri, set up a sysroot and then run the test suite.
+    Test {
+        bless: bool,
+        /// Flags that are passed through to `cargo test`.
+        flags: Vec<OsString>,
+    },
+    /// Build miri, set up a sysroot and then run the driver with the given <flags>.
+    /// (Also respects MIRIFLAGS environment variable.)
+    Run {
+        dep: bool,
+        /// Flags that are passed through to `miri`.
+        flags: Vec<OsString>,
+    },
+    /// Format all sources and tests.
+    Fmt {
+        /// Flags that are passed through to `rustfmt`.
+        flags: Vec<OsString>,
+    },
+    /// Runs clippy on all sources.
+    Clippy {
+        /// Flags that are passed through to `cargo clippy`.
+        flags: Vec<OsString>,
+    },
+    /// Runs just `cargo <flags>` with the Miri-specific environment variables.
+    /// Mainly meant to be invoked by rust-analyzer.
+    Cargo { flags: Vec<OsString> },
+    /// Runs <command> over and over again with different seeds for Miri. The MIRIFLAGS
+    /// variable is set to its original value appended with ` -Zmiri-seed=$SEED` for
+    /// many different seeds.
+    ManySeeds { command: Vec<OsString> },
+    /// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
+    Bench {
+        /// List of benchmarks to run. By default all benchmarks are run.
+        benches: Vec<OsString>,
+    },
+    /// Update and activate the rustup toolchain 'miri' to the commit given in the
+    /// `rust-version` file.
+    /// `rustup-toolchain-install-master` must be installed for this to work. Any extra
+    /// flags are passed to `rustup-toolchain-install-master`.
+    Toolchain { flags: Vec<OsString> },
+    /// Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
+    /// rustc commit. The fetched commit is stored in the `rust-version` file, so the
+    /// next `./miri toolchain` will install the rustc that just got pulled.
+    RustcPull { commit: Option<String> },
+    /// Push Miri changes back to the rustc repo. This will pull a copy of the rustc
+    /// history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
+    /// clone of the rustc repo.
+    RustcPush { github_user: String, branch: String },
+}
+
+const HELP: &str = r#"  COMMANDS
+
+./miri build <flags>:
+Just build miri. <flags> are passed to `cargo build`.
+
+./miri check <flags>:
+Just check miri. <flags> are passed to `cargo check`.
+
+./miri test [--bless] <flags>:
+Build miri, set up a sysroot and then run the test suite. <flags> are passed
+to the final `cargo test` invocation.
+
+./miri run [--dep] <flags>:
+Build miri, set up a sysroot and then run the driver with the given <flags>.
+(Also respects MIRIFLAGS environment variable.)
+
+./miri fmt <flags>:
+Format all sources and tests. <flags> are passed to `rustfmt`.
+
+./miri clippy <flags>:
+Runs clippy on all sources. <flags> are passed to `cargo clippy`.
+
+./miri cargo <flags>:
+Runs just `cargo <flags>` with the Miri-specific environment variables.
+Mainly meant to be invoked by rust-analyzer.
+
+./miri install <flags>:
+Installs the miri driver and cargo-miri. <flags> are passed to `cargo
+install`. Sets up the rpath such that the installed binary should work in any
+working directory. Note that the binaries are placed in the `miri` toolchain
+sysroot, to prevent conflicts with other toolchains.
+
+./miri many-seeds <command>:
+Runs <command> over and over again with different seeds for Miri. The MIRIFLAGS
+variable is set to its original value appended with ` -Zmiri-seed=$SEED` for
+many different seeds. The MIRI_SEEDS variable controls how many seeds are being
+tried; MIRI_SEED_START controls the first seed to try.
+
+./miri bench <benches>:
+Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
+<benches> can explicitly list the benchmarks to run; by default, all of them are run.
+
+./miri toolchain <flags>:
+Update and activate the rustup toolchain 'miri' to the commit given in the
+`rust-version` file.
+`rustup-toolchain-install-master` must be installed for this to work. Any extra
+flags are passed to `rustup-toolchain-install-master`.
+
+./miri rustc-pull <commit>:
+Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
+rustc commit. The fetched commit is stored in the `rust-version` file, so the
+next `./miri toolchain` will install the rustc that just got pulled.
+
+./miri rustc-push <github user> <branch>:
+Push Miri changes back to the rustc repo. This will pull a copy of the rustc
+history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
+clone of the rustc repo.
+
+  ENVIRONMENT VARIABLES
+
+MIRI_SYSROOT:
+If already set, the "sysroot setup" step is skipped.
+
+CARGO_EXTRA_FLAGS:
+Pass extra flags to all cargo invocations. (Ignored by `./miri cargo`.)"#;
+
+fn main() -> Result<()> {
+    // We are hand-rolling our own argument parser, since `clap` can't express what we need
+    // (https://github.com/clap-rs/clap/issues/5055).
+    let mut args = env::args_os().peekable();
+    args.next().unwrap(); // skip program name
+    let command = match args.next().and_then(|s| s.into_string().ok()).as_deref() {
+        Some("build") => Command::Build { flags: args.collect() },
+        Some("check") => Command::Check { flags: args.collect() },
+        Some("test") => {
+            let bless = args.peek().is_some_and(|a| a.to_str() == Some("--bless"));
+            if bless {
+                // Consume the flag.
+                args.next().unwrap();
+            }
+            Command::Test { bless, flags: args.collect() }
+        }
+        Some("run") => {
+            let dep = args.peek().is_some_and(|a| a.to_str() == Some("--dep"));
+            if dep {
+                // Consume the flag.
+                args.next().unwrap();
+            }
+            Command::Run { dep, flags: args.collect() }
+        }
+        Some("fmt") => Command::Fmt { flags: args.collect() },
+        Some("clippy") => Command::Clippy { flags: args.collect() },
+        Some("cargo") => Command::Cargo { flags: args.collect() },
+        Some("install") => Command::Install { flags: args.collect() },
+        Some("many-seeds") => Command::ManySeeds { command: args.collect() },
+        Some("bench") => Command::Bench { benches: args.collect() },
+        Some("toolchain") => Command::Toolchain { flags: args.collect() },
+        Some("rustc-pull") => {
+            let commit = args.next().map(|a| a.to_string_lossy().into_owned());
+            if args.next().is_some() {
+                bail!("Too many arguments for `./miri rustc-pull`");
+            }
+            Command::RustcPull { commit }
+        }
+        Some("rustc-push") => {
+            let github_user = args
+                .next()
+                .ok_or_else(|| {
+                    anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER BRANCH`")
+                })?
+                .to_string_lossy()
+                .into_owned();
+            let branch = args
+                .next()
+                .ok_or_else(|| {
+                    anyhow!("Missing second argument for `./miri rustc-push GITHUB_USER BRANCH`")
+                })?
+                .to_string_lossy()
+                .into_owned();
+            if args.next().is_some() {
+                bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`");
+            }
+            Command::RustcPush { github_user, branch }
+        }
+        _ => {
+            eprintln!("Unknown or missing command. Usage:\n\n{HELP}");
+            std::process::exit(1);
+        }
+    };
+    command.exec()?;
+    Ok(())
+}
diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs
new file mode 100644
index 00000000000..64e780b61a7
--- /dev/null
+++ b/src/tools/miri/miri-script/src/util.rs
@@ -0,0 +1,144 @@
+use std::ffi::{OsStr, OsString};
+use std::path::PathBuf;
+
+use anyhow::{Context, Result};
+use dunce::canonicalize;
+use path_macro::path;
+use xshell::{cmd, Shell};
+
+pub fn miri_dir() -> std::io::Result<PathBuf> {
+    const MIRI_SCRIPT_ROOT_DIR: &str = env!("CARGO_MANIFEST_DIR");
+    Ok(canonicalize(MIRI_SCRIPT_ROOT_DIR)?.parent().unwrap().into())
+}
+
+/// Queries the active toolchain for the Miri dir.
+pub fn active_toolchain() -> Result<String> {
+    let sh = Shell::new()?;
+    sh.change_dir(miri_dir()?);
+    let stdout = cmd!(sh, "rustup show active-toolchain").read()?;
+    Ok(stdout.split_whitespace().next().context("Could not obtain active Rust toolchain")?.into())
+}
+
+pub fn flagsplit(flags: &str) -> Vec<String> {
+    // This code is taken from `RUSTFLAGS` handling in cargo.
+    flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect()
+}
+
+/// Some extra state we track for building Miri, such as the right RUSTFLAGS.
+pub struct MiriEnv {
+    /// miri_dir is the root of the miri repository checkout we are working in.
+    pub miri_dir: PathBuf,
+    /// active_toolchain is passed as `+toolchain` argument to cargo/rustc invocations.
+    pub toolchain: String,
+    /// Extra flags to pass to cargo.
+    pub cargo_extra_flags: Vec<String>,
+    /// The rustc sysroot
+    pub sysroot: PathBuf,
+    /// The shell we use.
+    pub sh: Shell,
+}
+
+impl MiriEnv {
+    pub fn new() -> Result<Self> {
+        let toolchain = active_toolchain()?;
+        let sh = Shell::new()?; // we are preserving the current_dir on this one, so paths resolve properly!
+        let miri_dir = miri_dir()?;
+
+        let sysroot = cmd!(sh, "rustc +{toolchain} --print sysroot").read()?.into();
+        let target_output = cmd!(sh, "rustc +{toolchain} --version --verbose").read()?;
+        let rustc_meta = rustc_version::version_meta_for(&target_output)?;
+        let libdir = path!(sysroot / "lib" / "rustlib" / rustc_meta.host / "lib");
+
+        // Determine some toolchain properties
+        if !libdir.exists() {
+            println!("Something went wrong determining the library dir.");
+            println!("I got {} but that does not exist.", libdir.display());
+            println!("Please report a bug at https://github.com/rust-lang/miri/issues.");
+            std::process::exit(2);
+        }
+        // Share target dir between `miri` and `cargo-miri`.
+        let target_dir = std::env::var_os("CARGO_TARGET_DIR")
+            .unwrap_or_else(|| path!(miri_dir / "target").into());
+        sh.set_var("CARGO_TARGET_DIR", target_dir);
+
+        // We configure dev builds to not be unusably slow.
+        let devel_opt_level =
+            std::env::var_os("CARGO_PROFILE_DEV_OPT_LEVEL").unwrap_or_else(|| "2".into());
+        sh.set_var("CARGO_PROFILE_DEV_OPT_LEVEL", devel_opt_level);
+
+        // Compute rustflags.
+        let rustflags = {
+            let mut flags = OsString::new();
+            // We set the rpath so that Miri finds the private rustc libraries it needs.
+            flags.push("-C link-args=-Wl,-rpath,");
+            flags.push(libdir);
+            // Enable rustc-specific lints (ignored without `-Zunstable-options`).
+            flags.push(" -Zunstable-options -Wrustc::internal -Wrust_2018_idioms -Wunused_lifetimes -Wsemicolon_in_expressions_from_macros");
+            // Add user-defined flags.
+            if let Some(value) = std::env::var_os("RUSTFLAGS") {
+                flags.push(" ");
+                flags.push(value);
+            }
+            flags
+        };
+        sh.set_var("RUSTFLAGS", rustflags);
+
+        // Get extra flags for cargo.
+        let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default();
+        let cargo_extra_flags = flagsplit(&cargo_extra_flags);
+
+        Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags })
+    }
+
+    pub fn install_to_sysroot(
+        &self,
+        path: impl AsRef<OsStr>,
+        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
+    ) -> Result<()> {
+        let MiriEnv { sysroot, toolchain, cargo_extra_flags, .. } = self;
+        // Install binaries to the miri toolchain's `sysroot` so they do not interact with other toolchains.
+        cmd!(self.sh, "cargo +{toolchain} install {cargo_extra_flags...} --path {path} --force --root {sysroot} {args...}").run()?;
+        Ok(())
+    }
+
+    pub fn build(
+        &self,
+        manifest_path: impl AsRef<OsStr>,
+        args: &[OsString],
+        quiet: bool,
+    ) -> Result<()> {
+        let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
+        let quiet_flag = if quiet { Some("--quiet") } else { None };
+        let mut cmd = cmd!(
+            self.sh,
+            "cargo +{toolchain} build {cargo_extra_flags...} --manifest-path {manifest_path} {quiet_flag...} {args...}"
+        );
+        cmd.set_quiet(quiet);
+        cmd.run()?;
+        Ok(())
+    }
+
+    pub fn check(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
+        let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
+        cmd!(self.sh, "cargo +{toolchain} check {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
+            .run()?;
+        Ok(())
+    }
+
+    pub fn clippy(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
+        let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
+        cmd!(self.sh, "cargo +{toolchain} clippy {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
+            .run()?;
+        Ok(())
+    }
+
+    pub fn test(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
+        let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
+        cmd!(
+            self.sh,
+            "cargo +{toolchain} test {cargo_extra_flags...} --manifest-path {manifest_path} {args...}"
+        )
+        .run()?;
+        Ok(())
+    }
+}
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 1d5dd4d3f63..716b690daaa 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-d150dbb067e66f351a0b33a54e7d4b464ef51e47
+fca59ab5f0e7df7d816bed77a32abc0045ebe80b
\ No newline at end of file
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 75e4b5f8466..0351f586872 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
@@ -13,11 +13,7 @@ use log::trace;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::mir::{Mutability, RetagKind};
-use rustc_middle::ty::{
-    self,
-    layout::HasParamEnv,
-    Ty,
-};
+use rustc_middle::ty::{self, layout::HasParamEnv, Ty};
 use rustc_target::abi::{Abi, Align, Size};
 
 use crate::borrow_tracker::{
@@ -608,8 +604,7 @@ impl<'mir: 'ecx, 'tcx: 'mir, 'ecx> EvalContextPrivExt<'mir, 'tcx, 'ecx>
 {
 }
 trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> {
-    /// Returns the `AllocId` the reborrow was done in, if some actual borrow stack manipulation
-    /// happened.
+    /// Returns the provenance that should be used henceforth.
     fn sb_reborrow(
         &mut self,
         place: &MPlaceTy<'tcx, Provenance>,
@@ -617,7 +612,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
         new_perm: NewPermission,
         new_tag: BorTag,
         retag_info: RetagInfo, // diagnostics info about this retag
-    ) -> InterpResult<'tcx, Option<AllocId>> {
+    ) -> InterpResult<'tcx, Option<Provenance>> {
         let this = self.eval_context_mut();
         // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
         this.check_ptr_access_align(place.ptr, size, Align::ONE, CheckInAllocMsg::InboundsTest)?;
@@ -699,11 +694,14 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
             // pointer tagging for example all calls to get_unchecked on them are invalid.
             if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr) {
                 log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
-                return Ok(Some(alloc_id));
+                // Still give it the new provenance, it got retagged after all.
+                return Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
+            } else {
+                // This pointer doesn't come with an AllocId. :shrug:
+                log_creation(this, None)?;
+                // Provenance unchanged.
+                return Ok(place.ptr.provenance);
             }
-            // This pointer doesn't come with an AllocId. :shrug:
-            log_creation(this, None)?;
-            return Ok(None);
         }
 
         let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr)?;
@@ -808,7 +806,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
             }
         }
 
-        Ok(Some(alloc_id))
+        Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
     }
 
     /// Retags an individual pointer, returning the retagged version.
@@ -835,25 +833,10 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
         let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
 
         // Reborrow.
-        let alloc_id = this.sb_reborrow(&place, size, new_perm, new_tag, info)?;
+        let new_prov = this.sb_reborrow(&place, size, new_perm, new_tag, info)?;
 
         // Adjust pointer.
-        let new_place = place.map_provenance(|p| {
-            p.map(|prov| {
-                match alloc_id {
-                    Some(alloc_id) => {
-                        // If `reborrow` could figure out the AllocId of this ptr, hard-code it into the new one.
-                        // Even if we started out with a wildcard, this newly retagged pointer is tied to that allocation.
-                        Provenance::Concrete { alloc_id, tag: new_tag }
-                    }
-                    None => {
-                        // Looks like this has to stay a wildcard pointer.
-                        assert!(matches!(prov, Provenance::Wildcard));
-                        Provenance::Wildcard
-                    }
-                }
-            })
-        });
+        let new_place = place.map_provenance(|_| new_prov);
 
         // Return new pointer.
         Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
index 7723f06f296..fd45671ba29 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
@@ -218,7 +218,7 @@ impl<'tcx> Tree {
     }
 }
 
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Clone, Copy)]
 pub(super) enum TransitionError {
     /// This access is not allowed because some parent tag has insufficient permissions.
     /// For example, if a tag is `Frozen` and encounters a child write this will
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 d30745b3b61..0fbe66360b2 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
@@ -5,11 +5,7 @@ use rustc_target::abi::{Abi, Align, Size};
 use crate::borrow_tracker::{AccessKind, GlobalStateInner, ProtectorKind, RetagFields};
 use rustc_middle::{
     mir::{Mutability, RetagKind},
-    ty::{
-        self,
-        layout::HasParamEnv,
-        Ty,
-    },
+    ty::{self, layout::HasParamEnv, Ty},
 };
 use rustc_span::def_id::DefId;
 
@@ -121,7 +117,7 @@ impl<'tcx> NewPermission {
         let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.param_env());
         let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.param_env());
         let initial_state = match mutability {
-            Mutability::Mut if ty_is_unpin => Permission::new_unique_2phase(ty_is_freeze),
+            Mutability::Mut if ty_is_unpin => Permission::new_reserved(ty_is_freeze),
             Mutability::Not if ty_is_freeze => Permission::new_frozen(),
             // Raw pointers never enter this function so they are not handled.
             // However raw pointers are not the only pointers that take the parent
@@ -150,7 +146,7 @@ impl<'tcx> NewPermission {
             let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.param_env());
             Self {
                 zero_size,
-                initial_state: Permission::new_unique_2phase(ty_is_freeze),
+                initial_state: Permission::new_reserved(ty_is_freeze),
                 protector: (kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector),
             }
         })
@@ -165,25 +161,22 @@ impl<'mir: 'ecx, 'tcx: 'mir, 'ecx> EvalContextPrivExt<'mir, 'tcx, 'ecx>
 {
 }
 trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> {
-    /// Returns the `AllocId` the reborrow was done in, if there is some actual
-    /// memory associated with this pointer. Returns `None` if there is no actual
-    /// memory allocated. Also checks that the reborrow of size `ptr_size` is
-    /// within bounds of the allocation.
-    ///
-    /// Also returns the tag that the pointer should get, which is essentially
-    /// `if new_perm.is_some() { new_tag } else { parent_tag }` along with
-    /// some logging (always) and fake reads (if `new_perm` is
-    /// `Some(NewPermission { perform_read_access: true }`).
+    /// Returns the provenance that should be used henceforth.
     fn tb_reborrow(
         &mut self,
         place: &MPlaceTy<'tcx, Provenance>, // parent tag extracted from here
         ptr_size: Size,
         new_perm: NewPermission,
         new_tag: BorTag,
-    ) -> InterpResult<'tcx, Option<(AllocId, BorTag)>> {
+    ) -> InterpResult<'tcx, Option<Provenance>> {
         let this = self.eval_context_mut();
         // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
-        this.check_ptr_access_align(place.ptr, ptr_size, Align::ONE, CheckInAllocMsg::InboundsTest)?;
+        this.check_ptr_access_align(
+            place.ptr,
+            ptr_size,
+            Align::ONE,
+            CheckInAllocMsg::InboundsTest,
+        )?;
 
         // It is crucial that this gets called on all code paths, to ensure we track tag creation.
         let log_creation = |this: &MiriInterpCx<'mir, 'tcx>,
@@ -209,7 +202,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
                 // Unlike SB, we *do* a proper retag for size 0 if can identify the allocation.
                 // After all, the pointer may be lazily initialized outside this initial range.
                 data
-            },
+            }
             Err(_) => {
                 assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
                 // This pointer doesn't come with an AllocId, so there's no
@@ -221,13 +214,14 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
                     place.layout.ty,
                 );
                 log_creation(this, None)?;
-                return Ok(None);
+                // Keep original provenance.
+                return Ok(place.ptr.provenance);
             }
         };
         log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
 
         let orig_tag = match parent_prov {
-            ProvenanceExtra::Wildcard => return Ok(None), // TODO: handle wildcard pointers
+            ProvenanceExtra::Wildcard => return Ok(place.ptr.provenance), // TODO: handle wildcard pointers
             ProvenanceExtra::Concrete(tag) => tag,
         };
 
@@ -254,31 +248,54 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
                 .insert(new_tag, protect);
         }
 
+        let alloc_kind = this.get_alloc_info(alloc_id).2;
+        if !matches!(alloc_kind, AllocKind::LiveData) {
+            assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
+            // There's not actually any bytes here where accesses could even be tracked.
+            // Just produce the new provenance, nothing else to do.
+            return Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
+        }
+
         let span = this.machine.current_span();
         let alloc_extra = this.get_alloc_extra(alloc_id)?;
         let range = alloc_range(base_offset, ptr_size);
         let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
 
         // All reborrows incur a (possibly zero-sized) read access to the parent
-        {
-            let global = &this.machine.borrow_tracker.as_ref().unwrap();
-            let span = this.machine.current_span();
-            tree_borrows.perform_access(
-                AccessKind::Read,
-                orig_tag,
-                range,
-                global,
-                span,
-                diagnostics::AccessCause::Reborrow,
-            )?;
+        tree_borrows.perform_access(
+            AccessKind::Read,
+            orig_tag,
+            range,
+            this.machine.borrow_tracker.as_ref().unwrap(),
+            this.machine.current_span(),
+            diagnostics::AccessCause::Reborrow,
+        )?;
+        // Record the parent-child pair in the tree.
+        tree_borrows.new_child(orig_tag, new_tag, new_perm.initial_state, range, span)?;
+        drop(tree_borrows);
+
+        // Also inform the data race model (but only if any bytes are actually affected).
+        if range.size.bytes() > 0 {
             if let Some(data_race) = alloc_extra.data_race.as_ref() {
-                data_race.read(alloc_id, range, &this.machine)?;
+                // We sometimes need to make it a write, since not all retags commute with reads!
+                // FIXME: Is that truly the semantics we want? Some optimizations are likely to be
+                // very unhappy without this. We'd tsill ge some UB just by picking a suitable
+                // interleaving, but wether UB happens can depend on whether a write occurs in the
+                // future...
+                let is_write = new_perm.initial_state.is_active()
+                    || (new_perm.initial_state.is_resrved() && new_perm.protector.is_some());
+                if is_write {
+                    // Need to get mutable access to alloc_extra.
+                    // (Cannot always do this as we can do read-only reborrowing on read-only allocations.)
+                    let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc_id)?;
+                    alloc_extra.data_race.as_mut().unwrap().write(alloc_id, range, machine)?;
+                } else {
+                    data_race.read(alloc_id, range, &this.machine)?;
+                }
             }
         }
 
-        // Record the parent-child pair in the tree.
-        tree_borrows.new_child(orig_tag, new_tag, new_perm.initial_state, range, span)?;
-        Ok(Some((alloc_id, new_tag)))
+        Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
     }
 
     /// Retags an individual pointer, returning the retagged version.
@@ -314,25 +331,10 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
         let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
 
         // Compute the actual reborrow.
-        let reborrowed = this.tb_reborrow(&place, reborrow_size, new_perm, new_tag)?;
+        let new_prov = this.tb_reborrow(&place, reborrow_size, new_perm, new_tag)?;
 
         // Adjust pointer.
-        let new_place = place.map_provenance(|p| {
-            p.map(|prov| {
-                match reborrowed {
-                    Some((alloc_id, actual_tag)) => {
-                        // If `reborrow` could figure out the AllocId of this ptr, hard-code it into the new one.
-                        // Even if we started out with a wildcard, this newly retagged pointer is tied to that allocation.
-                        Provenance::Concrete { alloc_id, tag: actual_tag }
-                    }
-                    None => {
-                        // Looks like this has to stay a wildcard pointer.
-                        assert!(matches!(prov, Provenance::Wildcard));
-                        Provenance::Wildcard
-                    }
-                }
-            })
-        });
+        let new_place = place.map_provenance(|_| new_prov);
 
         // Return new pointer.
         Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
index 051b209da17..b4a9a768e27 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
@@ -134,25 +134,32 @@ pub struct PermTransition {
 
 impl Permission {
     /// Default initial permission of the root of a new tree.
-    pub fn new_root() -> Self {
+    pub fn new_active() -> Self {
         Self { inner: Active }
     }
 
     /// Default initial permission of a reborrowed mutable reference.
-    pub fn new_unique_2phase(ty_is_freeze: bool) -> Self {
+    pub fn new_reserved(ty_is_freeze: bool) -> Self {
         Self { inner: Reserved { ty_is_freeze } }
     }
 
-    /// Default initial permission for return place.
-    pub fn new_active() -> Self {
-        Self { inner: Active }
-    }
-
     /// Default initial permission of a reborrowed shared reference
     pub fn new_frozen() -> Self {
         Self { inner: Frozen }
     }
 
+    pub fn is_active(self) -> bool {
+        matches!(self.inner, Active)
+    }
+
+    pub fn is_resrved(self) -> bool {
+        matches!(self.inner, Reserved { .. })
+    }
+
+    pub fn is_frozen(self) -> bool {
+        matches!(self.inner, Frozen)
+    }
+
     /// Apply the transition to the inner PermissionPriv.
     pub fn perform_access(
         kind: AccessKind,
@@ -438,7 +445,7 @@ mod propagation_optimization_checks {
     }
 
     #[test]
-    fn foreign_read_is_noop_after_write() {
+    fn foreign_read_is_noop_after_foreign_write() {
         use transition::*;
         let old_access = AccessKind::Write;
         let new_access = AccessKind::Read;
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
index 355356b743a..5abf13229bb 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
@@ -110,7 +110,7 @@ impl LocationState {
 
     // Helper to optimize the tree traversal.
     // The optimization here consists of observing thanks to the tests
-    // `foreign_read_is_noop_after_write` and `all_transitions_idempotent`,
+    // `foreign_read_is_noop_after_foreign_write` and `all_transitions_idempotent`,
     // that there are actually just three possible sequences of events that can occur
     // in between two child accesses that produce different results.
     //
@@ -139,7 +139,7 @@ impl LocationState {
             let new_access_noop = match (self.latest_foreign_access, access_kind) {
                 // Previously applied transition makes the new one a guaranteed
                 // noop in the two following cases:
-                // (1) justified by `foreign_read_is_noop_after_write`
+                // (1) justified by `foreign_read_is_noop_after_foreign_write`
                 (Some(AccessKind::Write), AccessKind::Read) => true,
                 // (2) justified by `all_transitions_idempotent`
                 (Some(old), new) if old == new => true,
@@ -376,7 +376,7 @@ where {
 impl Tree {
     /// Create a new tree, with only a root pointer.
     pub fn new(root_tag: BorTag, size: Size, span: Span) -> Self {
-        let root_perm = Permission::new_root();
+        let root_perm = Permission::new_active();
         let mut tag_mapping = UniKeyMap::default();
         let root_idx = tag_mapping.insert(root_tag);
         let nodes = {
@@ -670,7 +670,8 @@ impl AccessRelatedness {
 mod commutation_tests {
     use super::*;
     impl LocationState {
-        pub fn all_without_access() -> impl Iterator<Item = Self> {
+        pub fn all() -> impl Iterator<Item = Self> {
+            // We keep `latest_foreign_access` at `None` as that's just a cache.
             Permission::all().flat_map(|permission| {
                 [false, true].into_iter().map(move |initialized| {
                     Self { permission, initialized, latest_foreign_access: None }
@@ -695,12 +696,12 @@ mod commutation_tests {
                 // Any protector state works, but we can't move reads across function boundaries
                 // so the two read accesses occur under the same protector.
                 for &protected in &[true, false] {
-                    for loc in LocationState::all_without_access() {
+                    for loc in LocationState::all() {
                         // Apply 1 then 2. Failure here means that there is UB in the source
                         // and we skip the check in the target.
                         let mut loc12 = loc;
-                        let Ok(_) = loc12.perform_access(kind, rel1, protected) else { continue; };
-                        let Ok(_) = loc12.perform_access(kind, rel2, protected) else { continue; };
+                        let Ok(_) = loc12.perform_access(kind, rel1, protected) else { continue };
+                        let Ok(_) = loc12.perform_access(kind, rel2, protected) else { continue };
 
                         // If 1 followed by 2 succeeded, then 2 followed by 1 must also succeed...
                         let mut loc21 = loc;
@@ -718,4 +719,33 @@ mod commutation_tests {
             }
         }
     }
+
+    #[test]
+    #[rustfmt::skip]
+    // Ensure that of 2 accesses happen, one foreign and one a child, and we are protected, that we
+    // get UB unless they are both reads.
+    fn protected_enforces_noalias() {
+        for rel1 in AccessRelatedness::all() {
+            for rel2 in AccessRelatedness::all() {
+                if rel1.is_foreign() == rel2.is_foreign() {
+                    // We want to check pairs of accesses where one is foreign and one is not.
+                    continue;
+                }
+                for kind1 in AccessKind::all() {
+                    for kind2 in AccessKind::all() {
+                        for mut state in LocationState::all() {
+                            let protected = true;
+                            let Ok(_) = state.perform_access(kind1, rel1, protected) else { continue };
+                            let Ok(_) = state.perform_access(kind2, rel2, protected) else { continue };
+                            // If these were both allowed, it must have been two reads.
+                            assert!(
+                                kind1 == AccessKind::Read && kind2 == AccessKind::Read,
+                                "failed to enforce noalias between two accesses that are not both reads"
+                            );
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index f9a8bad3a4f..e0f74d03ff6 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -337,7 +337,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     /// Call a function: Push the stack frame and pass the arguments.
     /// For now, arguments must be scalars (so that the caller does not have to know the layout).
     ///
-    /// If you do not provie a return place, a dangling zero-sized place will be created
+    /// If you do not provide a return place, a dangling zero-sized place will be created
     /// for your convenience.
     fn call_function(
         &mut self,
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 0c9c072b051..e19be417b22 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -427,7 +427,7 @@ pub struct MiriMachine<'mir, 'tcx> {
     /// the emulated program.
     profiler: Option<measureme::Profiler>,
     /// Used with `profiler` to cache the `StringId`s for event names
-    /// uesd with `measureme`.
+    /// used with `measureme`.
     string_cache: FxHashMap<String, measureme::StringId>,
 
     /// Cache of `Instance` exported under the given `Symbol` name.
@@ -516,7 +516,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
             let pid = process::id();
             // We adopt the same naming scheme for the profiler output that rustc uses. In rustc,
             // the PID is padded so that the nondeterministic value of the PID does not spread
-            // nondeterminisim to the allocator. In Miri we are not aiming for such performance
+            // nondeterminism to the allocator. In Miri we are not aiming for such performance
             // control, we just pad for consistency with rustc.
             let filename = format!("{crate_name}-{pid:07}");
             let path = Path::new(out).join(filename);
@@ -1219,7 +1219,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
         // If we have a borrow tracker, we also have it set up protection so that all reads *and
         // writes* during this call are insta-UB.
         if ecx.machine.borrow_tracker.is_some() {
-            if let Either::Left(place) = place.as_mplace_or_local() {
+            // Have to do `to_op` first because a `Place::Local` doesn't imply the local doesn't have an address.
+            if let Either::Left(place) = ecx.place_to_op(place)?.as_mplace_or_imm() {
                 ecx.protect_place(&place)?;
             } else {
                 // Locals that don't have their address taken are as protected as they can ever be.
diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs
index 5c3a194214b..8b97c8bb83c 100644
--- a/src/tools/miri/tests/compiletest.rs
+++ b/src/tools/miri/tests/compiletest.rs
@@ -73,11 +73,11 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) ->
         program.args.push(flag);
     }
 
-    let bless = env::var_os("RUSTC_BLESS").is_some_and(|v| v !="0");
+    let bless = env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
     let skip_ui_checks = env::var_os("MIRI_SKIP_UI_CHECKS").is_some();
 
     let output_conflict_handling = match (bless, skip_ui_checks) {
-        (false, false) => OutputConflictHandling::Error("./miri bless".into()),
+        (false, false) => OutputConflictHandling::Error("./miri test --bless".into()),
         (true, false) => OutputConflictHandling::Bless,
         (false, true) => OutputConflictHandling::Ignore,
         (true, true) => panic!("cannot use RUSTC_BLESS and MIRI_SKIP_UI_CHECKS at the same time"),
diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.rs b/src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.rs
new file mode 100644
index 00000000000..f192e76de13
--- /dev/null
+++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.rs
@@ -0,0 +1,29 @@
+//@revisions: stack tree
+//@compile-flags: -Zmiri-preemption-rate=0
+//@[tree]compile-flags: -Zmiri-tree-borrows
+use std::thread;
+
+#[derive(Copy, Clone)]
+struct SendPtr(*mut i32);
+unsafe impl Send for SendPtr {}
+
+fn main() {
+    let mut mem = 0;
+    let ptr = SendPtr(&mut mem as *mut _);
+
+    let t = thread::spawn(move || {
+        let ptr = ptr;
+        // We do a protected 2phase retag (but no write!) in this thread.
+        fn retag(_x: &mut i32) {} //~[tree]ERROR: Data race detected between (1) Read on thread `main` and (2) Write on thread `<unnamed>`
+        retag(unsafe { &mut *ptr.0 }); //~[stack]ERROR: Data race detected between (1) Read on thread `main` and (2) Write on thread `<unnamed>`
+    });
+
+    // We do a read in the main thread.
+    unsafe { ptr.0.read() };
+
+    // These two operations do not commute -- if the read happens after the retag, the retagged pointer
+    // gets frozen! So we want this to be considered UB so that we can still freely move the read around
+    // in this thread without worrying about reordering with retags in other threads.
+
+    t.join().unwrap();
+}
diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.stack.stderr b/src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.stack.stderr
new file mode 100644
index 00000000000..10fb1dece2a
--- /dev/null
+++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.stack.stderr
@@ -0,0 +1,20 @@
+error: Undefined Behavior: Data race detected between (1) Read on thread `main` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
+  --> $DIR/retag_data_race_protected_read.rs:LL:CC
+   |
+LL |         retag(unsafe { &mut *ptr.0 });
+   |                        ^^^^^^^^^^^ Data race detected between (1) Read on thread `main` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
+   |
+help: and (1) occurred earlier here
+  --> $DIR/retag_data_race_protected_read.rs:LL:CC
+   |
+LL |     unsafe { ptr.0.read() };
+   |              ^^^^^^^^^^^^
+   = 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 (of the first span):
+   = note: inside closure at $DIR/retag_data_race_protected_read.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.tree.stderr b/src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.tree.stderr
new file mode 100644
index 00000000000..173acf4b96c
--- /dev/null
+++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_protected_read.tree.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: Data race detected between (1) Read on thread `main` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
+  --> $DIR/retag_data_race_protected_read.rs:LL:CC
+   |
+LL |         fn retag(_x: &mut i32) {}
+   |                  ^^ Data race detected between (1) Read on thread `main` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
+   |
+help: and (1) occurred earlier here
+  --> $DIR/retag_data_race_protected_read.rs:LL:CC
+   |
+LL |     unsafe { ptr.0.read() };
+   |              ^^^^^^^^^^^^
+   = 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 (of the first span):
+   = note: inside `main::{closure#0}::retag` at $DIR/retag_data_race_protected_read.rs:LL:CC
+note: inside closure
+  --> $DIR/retag_data_race_protected_read.rs:LL:CC
+   |
+LL | ...   retag(unsafe { &mut *ptr.0 });
+   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_write.rs b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs
index c1dded40d3c..868b3beb53b 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_write.rs
+++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs
@@ -1,5 +1,7 @@
 //! Make sure that a retag acts like a write for the data race model.
+//@revisions: stack tree
 //@compile-flags: -Zmiri-preemption-rate=0
+//@[tree]compile-flags: -Zmiri-tree-borrows
 #[derive(Copy, Clone)]
 struct SendPtr(*mut u8);
 
@@ -15,7 +17,7 @@ fn thread_1(p: SendPtr) {
 fn thread_2(p: SendPtr) {
     let p = p.0;
     unsafe {
-        *p = 5; //~ ERROR: Data race detected between (1) Write on thread `<unnamed>` and (2) Write on thread `<unnamed>`
+        *p = 5; //~ ERROR: /Data race detected between \(1\) (Read|Write) on thread `<unnamed>` and \(2\) Write on thread `<unnamed>`/
     }
 }
 
diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_write.stderr b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr
index da5af600675..da5af600675 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_write.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr
diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr
new file mode 100644
index 00000000000..37d216b9877
--- /dev/null
+++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: Data race detected between (1) Read on thread `<unnamed>` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
+  --> $DIR/retag_data_race_write.rs:LL:CC
+   |
+LL |         *p = 5;
+   |         ^^^^^^ Data race detected between (1) Read on thread `<unnamed>` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
+   |
+help: and (1) occurred earlier here
+  --> $DIR/retag_data_race_write.rs:LL:CC
+   |
+LL |         let _r = &mut *p;
+   |                  ^^^^^^^
+   = 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 (of the first span):
+   = note: inside `thread_2` at $DIR/retag_data_race_write.rs:LL:CC
+note: inside closure
+  --> $DIR/retag_data_race_write.rs:LL:CC
+   |
+LL |     let t2 = std::thread::spawn(move || thread_2(p));
+   |                                         ^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs
new file mode 100644
index 00000000000..7e9a6320026
--- /dev/null
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs
@@ -0,0 +1,30 @@
+//@compile-flags: -Zmiri-tree-borrows
+#![feature(raw_ref_op)]
+#![feature(core_intrinsics)]
+#![feature(custom_mir)]
+
+use std::intrinsics::mir::*;
+
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+pub fn main() {
+    mir! {
+        {
+            let x = 0;
+            let ptr = &raw mut x;
+            // We arrange for `myfun` to have a pointer that aliases
+            // its return place. Even just reading from that pointer is UB.
+            Call(x, after_call, myfun(ptr))
+        }
+
+        after_call = {
+            Return()
+        }
+    }
+}
+
+fn myfun(ptr: *mut i32) -> i32 {
+    // This overwrites the return place, which shouldn't be possible through another pointer.
+    unsafe { ptr.write(0) };
+    //~^ ERROR: /write access .* forbidden/
+    13
+}
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stderr
new file mode 100644
index 00000000000..33a8a4b46bd
--- /dev/null
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stderr
@@ -0,0 +1,39 @@
+error: Undefined Behavior: write access through <TAG> (root of the allocation) is forbidden
+  --> $DIR/return_pointer_aliasing2.rs:LL:CC
+   |
+LL |     unsafe { ptr.write(0) };
+   |              ^^^^^^^^^^^^ write access through <TAG> (root of the allocation) is forbidden
+   |
+   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
+   = help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
+   = help: protected tags must never be Disabled
+help: the accessed tag <TAG> was created here
+  --> $DIR/return_pointer_aliasing2.rs:LL:CC
+   |
+LL | /     mir! {
+LL | |         {
+LL | |             let x = 0;
+LL | |             let ptr = &raw mut x;
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+help: the protected tag <TAG> was created here, in the initial state Active
+  --> $DIR/return_pointer_aliasing2.rs:LL:CC
+   |
+LL |     unsafe { ptr.write(0) };
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   = note: BACKTRACE (of the first span):
+   = note: inside `myfun` at $DIR/return_pointer_aliasing2.rs:LL:CC
+note: inside `main`
+  --> $DIR/return_pointer_aliasing2.rs:LL:CC
+   |
+LL |             Call(x, after_call, myfun(ptr))
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/retag-data-race.stderr b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stack.stderr
index f2cdfe7c314..c53a495b5e1 100644
--- a/src/tools/miri/tests/fail/tree_borrows/retag-data-race.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stack.stderr
@@ -1,23 +1,23 @@
 error: Undefined Behavior: Data race detected between (1) Read on thread `<unnamed>` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
-  --> $DIR/retag-data-race.rs:LL:CC
+  --> $DIR/retag_data_race_read.rs:LL:CC
    |
-LL |     *p = 5;
-   |     ^^^^^^ Data race detected between (1) Read on thread `<unnamed>` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
+LL |         *p = 5;
+   |         ^^^^^^ Data race detected between (1) Read on thread `<unnamed>` and (2) Write on thread `<unnamed>` at ALLOC. (2) just happened here
    |
 help: and (1) occurred earlier here
-  --> $DIR/retag-data-race.rs:LL:CC
+  --> $DIR/retag_data_race_read.rs:LL:CC
    |
-LL |     let _r = &*p;
-   |              ^^^
+LL |         let _r = &*p;
+   |                  ^^^
    = 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 (of the first span):
-   = note: inside `thread_2` at $DIR/retag-data-race.rs:LL:CC
+   = note: inside `thread_2` at $DIR/retag_data_race_read.rs:LL:CC
 note: inside closure
-  --> $DIR/retag-data-race.rs:LL:CC
+  --> $DIR/retag_data_race_read.rs:LL:CC
    |
-LL |     let t2 = std::thread::spawn(move || unsafe { thread_2(p) });
-   |                                                  ^^^^^^^^^^^
+LL |     let t2 = std::thread::spawn(move || thread_2(p));
+   |                                         ^^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
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
new file mode 100644
index 00000000000..1e154eb0564
--- /dev/null
+++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.tree.stderr
@@ -0,0 +1,26 @@
+error: Undefined Behavior: reborrow through <TAG> (root of the allocation) is forbidden
+  --> RUSTLIB/std/src/rt.rs:LL:CC
+   |
+LL |     panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reborrow through <TAG> (root of the allocation) is forbidden
+   |
+   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
+   = help: this reborrow (acting as a foreign read access) would cause the protected tag <TAG> (currently Active) to become Disabled
+   = help: protected tags must never be Disabled
+help: the accessed tag <TAG> was created here
+  --> RUSTLIB/std/src/rt.rs:LL:CC
+   |
+LL |     panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: the protected tag <TAG> was created here, in the initial state Active
+  --> RUSTLIB/std/src/panic.rs:LL:CC
+   |
+LL | pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
+   |                                                       ^
+   = note: BACKTRACE (of the first span):
+   = 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
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/tree_borrows/fragile-data-race.rs b/src/tools/miri/tests/fail/tree_borrows/fragile-data-race.rs
deleted file mode 100644
index 215100de0a1..00000000000
--- a/src/tools/miri/tests/fail/tree_borrows/fragile-data-race.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-//! Race-condition-like interaction between a read and a reborrow.
-//! Even though no write or fake write occurs, reads have an effect on protected
-//! Reserved. This is a protected-retag/read data race, but is not *detected* as
-//! a data race violation because reborrows are not writes.
-//!
-//! This test is sensitive to the exact schedule so we disable preemption.
-//@compile-flags: -Zmiri-tree-borrows -Zmiri-preemption-rate=0
-use std::ptr::addr_of_mut;
-use std::thread;
-
-#[derive(Copy, Clone)]
-struct SendPtr(*mut u8);
-
-unsafe impl Send for SendPtr {}
-
-// First thread is just a reborrow, but for an instant `x` is
-// protected and thus vulnerable to foreign reads.
-fn thread_1(x: &mut u8) -> SendPtr {
-    thread::yield_now(); // make the other thread go first
-    SendPtr(x as *mut u8)
-}
-
-// Second thread simply performs a read.
-fn thread_2(x: &u8) {
-    let _val = *x;
-}
-
-fn main() {
-    let mut x = 0u8;
-    let x_1 = unsafe { &mut *addr_of_mut!(x) };
-    let xg = unsafe { &*addr_of_mut!(x) };
-
-    // The two threads are executed in parallel on aliasing pointers.
-    // UB occurs if the read of thread_2 occurs while the protector of thread_1
-    // is in place.
-    let hf = thread::spawn(move || thread_1(x_1));
-    let hg = thread::spawn(move || thread_2(xg));
-    let SendPtr(p) = hf.join().unwrap();
-    let () = hg.join().unwrap();
-
-    unsafe { *p = 1 }; //~ ERROR: /write access through .* is forbidden/
-}
diff --git a/src/tools/miri/tests/fail/tree_borrows/fragile-data-race.stderr b/src/tools/miri/tests/fail/tree_borrows/fragile-data-race.stderr
deleted file mode 100644
index 910f51ba8a3..00000000000
--- a/src/tools/miri/tests/fail/tree_borrows/fragile-data-race.stderr
+++ /dev/null
@@ -1,32 +0,0 @@
-error: Undefined Behavior: write access through <TAG> is forbidden
-  --> $DIR/fragile-data-race.rs:LL:CC
-   |
-LL |     unsafe { *p = 1 };
-   |              ^^^^^^ write access through <TAG> is forbidden
-   |
-   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
-   = help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
-   = help: the conflicting tag <TAG> has state Frozen which forbids this child write access
-help: the accessed tag <TAG> was created here
-  --> $DIR/fragile-data-race.rs:LL:CC
-   |
-LL | fn thread_1(x: &mut u8) -> SendPtr {
-   |             ^
-help: the conflicting tag <TAG> was created here, in the initial state Reserved
-  --> RUSTLIB/std/src/panic.rs:LL:CC
-   |
-LL | pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
-   |                                                       ^
-help: the conflicting tag <TAG> later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [0x0..0x1]
-  --> RUSTLIB/core/src/ptr/mod.rs:LL:CC
-   |
-LL |         crate::intrinsics::read_via_copy(src)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = help: this transition corresponds to a loss of write permissions
-   = note: BACKTRACE (of the first span):
-   = note: inside `main` at $DIR/fragile-data-race.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to previous error
-
diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.rs b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.rs
index 872efe3ad59..465679b72c3 100644
--- a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.rs
+++ b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.rs
@@ -3,8 +3,8 @@
 // Check how a Reserved with interior mutability
 // responds to a Foreign Write under a Protector
 #[path = "../../../utils/mod.rs"]
+#[macro_use]
 mod utils;
-use utils::macros::*;
 
 use std::cell::UnsafeCell;
 
diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.rs b/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.rs
index 3a1205a84f7..1e6e2eebd26 100644
--- a/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.rs
+++ b/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.rs
@@ -1,8 +1,8 @@
 //@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
 
 #[path = "../../../utils/mod.rs"]
+#[macro_use]
 mod utils;
-use utils::macros::*;
 
 // Check how a Reserved without interior mutability responds to a Foreign
 // Write when under a protector
diff --git a/src/tools/miri/tests/fail/tree_borrows/retag-data-race.rs b/src/tools/miri/tests/fail/tree_borrows/retag-data-race.rs
deleted file mode 100644
index 8ef3d23e804..00000000000
--- a/src/tools/miri/tests/fail/tree_borrows/retag-data-race.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-//! Make sure that a retag acts like a read for the data race model.
-//! This is a retag/write race condition.
-//!
-//! This test is sensitive to the exact schedule so we disable preemption.
-//@compile-flags: -Zmiri-tree-borrows -Zmiri-preemption-rate=0
-#[derive(Copy, Clone)]
-struct SendPtr(*mut u8);
-
-unsafe impl Send for SendPtr {}
-
-unsafe fn thread_1(SendPtr(p): SendPtr) {
-    let _r = &*p;
-}
-
-unsafe fn thread_2(SendPtr(p): SendPtr) {
-    *p = 5; //~ ERROR: Data race detected between (1) Read on thread `<unnamed>` and (2) Write on thread `<unnamed>`
-}
-
-fn main() {
-    let mut x = 0;
-    let p = std::ptr::addr_of_mut!(x);
-    let p = SendPtr(p);
-
-    let t1 = std::thread::spawn(move || unsafe { thread_1(p) });
-    let t2 = std::thread::spawn(move || unsafe { thread_2(p) });
-    let _ = t1.join();
-    let _ = t2.join();
-}
diff --git a/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment.rs b/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment.rs
new file mode 100644
index 00000000000..fa1812adc29
--- /dev/null
+++ b/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment.rs
@@ -0,0 +1,24 @@
+/// This tests that when a field sits at offset 0 in a 4-aligned struct, accessing the field
+/// requires alignment 4 even if the field type has lower alignment requirements.
+
+#[repr(C)]
+pub struct S {
+    x: u8,
+    y: u32,
+}
+
+unsafe fn foo(x: *const S) -> u8 {
+    unsafe { (*x).x } //~ERROR: accessing memory with alignment 1, but alignment 4 is required
+}
+
+fn main() {
+    unsafe {
+        let mem = [0u64; 16];
+        let odd_ptr = std::ptr::addr_of!(mem).cast::<u8>().add(1);
+        // `odd_ptr` is now not aligned enough for `S`.
+        // If accessing field `x` can exploit that it is at offset 0
+        // in a 4-aligned struct, that field access requires alignment 4,
+        // thus making this UB.
+        foo(odd_ptr.cast());
+    }
+}
diff --git a/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment.stderr b/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment.stderr
new file mode 100644
index 00000000000..0f030a6e27c
--- /dev/null
+++ b/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment.stderr
@@ -0,0 +1,20 @@
+error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
+  --> $DIR/field_requires_parent_struct_alignment.rs:LL:CC
+   |
+LL |     unsafe { (*x).x }
+   |              ^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
+   |
+   = 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 `foo` at $DIR/field_requires_parent_struct_alignment.rs:LL:CC
+note: inside `main`
+  --> $DIR/field_requires_parent_struct_alignment.rs:LL:CC
+   |
+LL |         foo(odd_ptr.cast());
+   |         ^^^^^^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs.rs b/src/tools/miri/tests/pass-dep/shims/libc-fs.rs
index fbdf27688a9..767a4fdbede 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-fs.rs
+++ b/src/tools/miri/tests/pass-dep/shims/libc-fs.rs
@@ -5,12 +5,15 @@
 #![feature(io_error_uncategorized)]
 
 use std::convert::TryInto;
-use std::ffi::{c_char, CStr, CString};
+use std::ffi::CString;
 use std::fs::{canonicalize, remove_dir_all, remove_file, File};
 use std::io::{Error, ErrorKind, Write};
 use std::os::unix::ffi::OsStrExt;
 use std::path::PathBuf;
 
+#[path = "../../utils/mod.rs"]
+mod utils;
+
 fn main() {
     test_dup_stdout_stderr();
     test_canonicalize_too_long();
@@ -22,31 +25,9 @@ fn main() {
     test_o_tmpfile_flag();
 }
 
-fn tmp() -> PathBuf {
-    let path = std::env::var("MIRI_TEMP")
-        .unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
-    // These are host paths. We need to convert them to the target.
-    let path = CString::new(path).unwrap();
-    let mut out = Vec::with_capacity(1024);
-
-    unsafe {
-        extern "Rust" {
-            fn miri_host_to_target_path(
-                path: *const c_char,
-                out: *mut c_char,
-                out_size: usize,
-            ) -> usize;
-        }
-        let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
-        assert_eq!(ret, 0);
-        let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
-        PathBuf::from(out)
-    }
-}
-
 /// Prepare: compute filename and make sure the file does not exist.
 fn prepare(filename: &str) -> PathBuf {
-    let path = tmp().join(filename);
+    let path = utils::tmp().join(filename);
     // Clean the paths for robustness.
     remove_file(&path).ok();
     path
@@ -55,7 +36,7 @@ fn prepare(filename: &str) -> PathBuf {
 /// Prepare directory: compute directory name and make sure it does not exist.
 #[allow(unused)]
 fn prepare_dir(dirname: &str) -> PathBuf {
-    let path = tmp().join(&dirname);
+    let path = utils::tmp().join(&dirname);
     // Clean the directory for robustness.
     remove_dir_all(&path).ok();
     path
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs b/src/tools/miri/tests/pass-dep/shims/libc-misc.rs
index 68504cb1c79..ebfeb863abf 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs
+++ b/src/tools/miri/tests/pass-dep/shims/libc-misc.rs
@@ -6,29 +6,8 @@ use std::fs::{remove_file, File};
 use std::os::unix::io::AsRawFd;
 use std::path::PathBuf;
 
-fn tmp() -> PathBuf {
-    use std::ffi::{c_char, CStr, CString};
-
-    let path = std::env::var("MIRI_TEMP")
-        .unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
-    // These are host paths. We need to convert them to the target.
-    let path = CString::new(path).unwrap();
-    let mut out = Vec::with_capacity(1024);
-
-    unsafe {
-        extern "Rust" {
-            fn miri_host_to_target_path(
-                path: *const c_char,
-                out: *mut c_char,
-                out_size: usize,
-            ) -> usize;
-        }
-        let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
-        assert_eq!(ret, 0);
-        let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
-        PathBuf::from(out)
-    }
-}
+#[path = "../../utils/mod.rs"]
+mod utils;
 
 /// Test allocating variant of `realpath`.
 fn test_posix_realpath_alloc() {
@@ -38,7 +17,7 @@ fn test_posix_realpath_alloc() {
     use std::os::unix::ffi::OsStringExt;
 
     let buf;
-    let path = tmp().join("miri_test_libc_posix_realpath_alloc");
+    let path = utils::tmp().join("miri_test_libc_posix_realpath_alloc");
     let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
 
     // Cleanup before test.
@@ -63,7 +42,7 @@ fn test_posix_realpath_noalloc() {
     use std::ffi::{CStr, CString};
     use std::os::unix::ffi::OsStrExt;
 
-    let path = tmp().join("miri_test_libc_posix_realpath_noalloc");
+    let path = utils::tmp().join("miri_test_libc_posix_realpath_noalloc");
     let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
 
     let mut v = vec![0; libc::PATH_MAX as usize];
@@ -103,7 +82,7 @@ fn test_posix_realpath_errors() {
 fn test_posix_fadvise() {
     use std::io::Write;
 
-    let path = tmp().join("miri_test_libc_posix_fadvise.txt");
+    let path = utils::tmp().join("miri_test_libc_posix_fadvise.txt");
     // Cleanup before test
     remove_file(&path).ok();
 
@@ -130,7 +109,7 @@ fn test_posix_fadvise() {
 fn test_sync_file_range() {
     use std::io::Write;
 
-    let path = tmp().join("miri_test_libc_sync_file_range.txt");
+    let path = utils::tmp().join("miri_test_libc_sync_file_range.txt");
     // Cleanup before test.
     remove_file(&path).ok();
 
@@ -243,7 +222,7 @@ fn test_isatty() {
         libc::isatty(libc::STDERR_FILENO);
 
         // But when we open a file, it is definitely not a TTY.
-        let path = tmp().join("notatty.txt");
+        let path = utils::tmp().join("notatty.txt");
         // Cleanup before test.
         remove_file(&path).ok();
         let file = File::create(&path).unwrap();
diff --git a/src/tools/miri/tests/pass/align_repeat_into_packed_field.rs b/src/tools/miri/tests/pass/align_repeat_into_packed_field.rs
index 3affb204205..fb028627d9d 100644
--- a/src/tools/miri/tests/pass/align_repeat_into_packed_field.rs
+++ b/src/tools/miri/tests/pass/align_repeat_into_packed_field.rs
@@ -2,17 +2,21 @@
 use std::intrinsics::mir::*;
 
 #[repr(packed)]
-struct S { field: [u32; 2] }
+struct S {
+    field: [u32; 2],
+}
 
 #[custom_mir(dialect = "runtime", phase = "optimized")]
-fn test() { mir! {
-    let s: S;
-    {
-        // Store a repeat expression directly into a field of a packed struct.
-        s.field = [0; 2];
-        Return()
+fn test() {
+    mir! {
+        let s: S;
+        {
+            // Store a repeat expression directly into a field of a packed struct.
+            s.field = [0; 2];
+            Return()
+        }
     }
-} }
+}
 
 fn main() {
     // Run this a bunch of time to make sure it doesn't pass by chance.
diff --git a/src/tools/miri/tests/pass/align_strange_enum_discriminant_offset.rs b/src/tools/miri/tests/pass/align_strange_enum_discriminant_offset.rs
new file mode 100644
index 00000000000..e0d05e0b65a
--- /dev/null
+++ b/src/tools/miri/tests/pass/align_strange_enum_discriminant_offset.rs
@@ -0,0 +1,23 @@
+#![allow(unused)]
+
+#[repr(u16)]
+enum DeviceKind {
+    Nil = 0,
+}
+
+#[repr(C, packed)]
+struct DeviceInfo {
+    endianness: u8,
+    device_kind: DeviceKind,
+}
+
+fn main() {
+    // The layout of `Option<(DeviceInfo, u64)>` is funny: it uses the
+    // `DeviceKind` enum as niche, so that is offset 1, but the niche type is u16!
+    // So despite the type having alignment 8 and the field type alignment 2,
+    // the actual alignment is 1.
+    let x = None::<(DeviceInfo, u8)>;
+    let y = None::<(DeviceInfo, u16)>;
+    let z = None::<(DeviceInfo, u64)>;
+    format!("{} {} {}", x.is_some(), y.is_some(), y.is_some());
+}
diff --git a/src/tools/miri/tests/pass/issues/issue-53728.rs b/src/tools/miri/tests/pass/issues/issue-53728.rs
deleted file mode 100644
index 0c858d3444f..00000000000
--- a/src/tools/miri/tests/pass/issues/issue-53728.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-#[repr(u16)]
-#[allow(dead_code)]
-enum DeviceKind {
-    Nil = 0,
-}
-
-#[repr(packed)]
-#[allow(dead_code)]
-struct DeviceInfo {
-    endianness: u8,
-    device_kind: DeviceKind,
-}
-
-fn main() {
-    let _x = None::<(DeviceInfo, u8)>;
-    let _y = None::<(DeviceInfo, u16)>;
-    let _z = None::<(DeviceInfo, u64)>;
-}
diff --git a/src/tools/miri/tests/pass/ptr_raw.rs b/src/tools/miri/tests/pass/ptr_raw.rs
index 3ba0fba9a94..2f184358907 100644
--- a/src/tools/miri/tests/pass/ptr_raw.rs
+++ b/src/tools/miri/tests/pass/ptr_raw.rs
@@ -20,6 +20,15 @@ fn basic_raw() {
     assert_eq!(*x, 23);
 }
 
+fn assign_overlapping() {
+    // Test an assignment where LHS and RHS alias.
+    // In Mir, that's UB (see `fail/overlapping_assignment.rs`), but in surface Rust this is allowed.
+    let mut mem = [0u32; 4];
+    let ptr = &mut mem as *mut [u32; 4];
+    unsafe { *ptr = *ptr };
+}
+
 fn main() {
     basic_raw();
+    assign_overlapping();
 }
diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs
index af245aa89aa..6ba39c1f563 100644
--- a/src/tools/miri/tests/pass/shims/fs.rs
+++ b/src/tools/miri/tests/pass/shims/fs.rs
@@ -5,7 +5,7 @@
 #![feature(io_error_uncategorized)]
 
 use std::collections::HashMap;
-use std::ffi::{c_char, OsString};
+use std::ffi::OsString;
 use std::fs::{
     canonicalize, create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename,
     File, OpenOptions,
@@ -13,6 +13,9 @@ use std::fs::{
 use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write};
 use std::path::{Path, PathBuf};
 
+#[path = "../../utils/mod.rs"]
+mod utils;
+
 fn main() {
     test_path_conversion();
     test_file();
@@ -30,37 +33,9 @@ fn main() {
     test_from_raw_os_error();
 }
 
-fn host_to_target_path(path: String) -> PathBuf {
-    use std::ffi::{CStr, CString};
-
-    let path = CString::new(path).unwrap();
-    let mut out = Vec::with_capacity(1024);
-
-    unsafe {
-        extern "Rust" {
-            fn miri_host_to_target_path(
-                path: *const c_char,
-                out: *mut c_char,
-                out_size: usize,
-            ) -> usize;
-        }
-        let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
-        assert_eq!(ret, 0);
-        let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
-        PathBuf::from(out)
-    }
-}
-
-fn tmp() -> PathBuf {
-    let path = std::env::var("MIRI_TEMP")
-        .unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
-    // These are host paths. We need to convert them to the target.
-    host_to_target_path(path)
-}
-
 /// Prepare: compute filename and make sure the file does not exist.
 fn prepare(filename: &str) -> PathBuf {
-    let path = tmp().join(filename);
+    let path = utils::tmp().join(filename);
     // Clean the paths for robustness.
     remove_file(&path).ok();
     path
@@ -68,7 +43,7 @@ fn prepare(filename: &str) -> PathBuf {
 
 /// Prepare directory: compute directory name and make sure it does not exist.
 fn prepare_dir(dirname: &str) -> PathBuf {
-    let path = tmp().join(&dirname);
+    let path = utils::tmp().join(&dirname);
     // Clean the directory for robustness.
     remove_dir_all(&path).ok();
     path
@@ -83,7 +58,7 @@ fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
 }
 
 fn test_path_conversion() {
-    let tmp = tmp();
+    let tmp = utils::tmp();
     assert!(tmp.is_absolute(), "{:?} is not absolute", tmp);
     assert!(tmp.is_dir(), "{:?} is not a directory", tmp);
 }
diff --git a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
index 43ae7d6f522..d2ba1841844 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
@@ -10,7 +10,7 @@ fn main() {
     mut_raw_mut();
     partially_invalidate_mut();
     drop_after_sharing();
-    direct_mut_to_const_raw();
+    // direct_mut_to_const_raw();
     two_raw();
     shr_and_raw();
     disjoint_mutable_subborrows();
@@ -19,6 +19,7 @@ fn main() {
     mut_below_shr();
     wide_raw_ptr_in_tuple();
     not_unpin_not_protected();
+    write_does_not_invalidate_all_aliases();
 }
 
 // Make sure that reading from an `&mut` does, like reborrowing to `&`,
@@ -110,14 +111,13 @@ fn drop_after_sharing() {
 }
 
 // Make sure that coercing &mut T to *const T produces a writeable pointer.
-fn direct_mut_to_const_raw() {
-    // TODO: This is currently disabled, waiting on a decision on <https://github.com/rust-lang/rust/issues/56604>
-    /*let x = &mut 0;
+// TODO: This is currently disabled, waiting on a decision on <https://github.com/rust-lang/rust/issues/56604>
+/*fn direct_mut_to_const_raw() {
+    let x = &mut 0;
     let y: *const i32 = x;
     unsafe { *(y as *mut i32) = 1; }
     assert_eq!(*x, 1);
-    */
-}
+}*/
 
 // Make sure that we can create two raw pointers from a mutable reference and use them both.
 fn two_raw() {
@@ -238,3 +238,28 @@ fn not_unpin_not_protected() {
         drop(unsafe { Box::from_raw(raw) });
     });
 }
+
+fn write_does_not_invalidate_all_aliases() {
+    mod other {
+        /// Some private memory to store stuff in.
+        static mut S: *mut i32 = 0 as *mut i32;
+
+        pub fn lib1(x: &&mut i32) {
+            unsafe {
+                S = (x as *const &mut i32).cast::<*mut i32>().read();
+            }
+        }
+
+        pub fn lib2() {
+            unsafe {
+                *S = 1337;
+            }
+        }
+    }
+
+    let x = &mut 0;
+    other::lib1(&x);
+    *x = 42; // a write to x -- invalidates other pointers?
+    other::lib2();
+    assert_eq!(*x, 1337); // oops, the value changed! I guess not all pointers were invalidated
+}
diff --git a/src/tools/miri/tests/pass/strange_references.rs b/src/tools/miri/tests/pass/strange_references.rs
new file mode 100644
index 00000000000..fe5ff93a9ca
--- /dev/null
+++ b/src/tools/miri/tests/pass/strange_references.rs
@@ -0,0 +1,25 @@
+//@revisions: stack tree
+//@[tree]compile-flags: -Zmiri-tree-borrows
+
+// Create zero-sized references to vtables and function data.
+// Just make sure nothing explodes.
+
+use std::{mem, ptr};
+
+fn check_ref(x: &()) {
+    let _ptr = ptr::addr_of!(*x);
+}
+
+fn main() {
+    check_ref({
+        // Create reference to a function.
+        let fnptr: fn(&()) = check_ref;
+        unsafe { mem::transmute(fnptr) }
+    });
+    check_ref({
+        // Create reference to a vtable.
+        let wideptr: &dyn Send = &0;
+        let fields: (&i32, &()) = unsafe { mem::transmute(wideptr) };
+        fields.1
+    })
+}
diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs
index 1bd94c6df67..398b542ed4c 100644
--- a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs
@@ -1,7 +1,7 @@
 //@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
 #[path = "../../utils/mod.rs"]
+#[macro_use]
 mod utils;
-use utils::macros::*;
 
 use std::cell::UnsafeCell;
 
diff --git a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs
index 76bbc73e662..fecc3360434 100644
--- a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs
@@ -3,8 +3,8 @@
 // Check that a protector goes back to normal behavior when the function
 // returns.
 #[path = "../../utils/mod.rs"]
+#[macro_use]
 mod utils;
-use utils::macros::*;
 
 fn main() {
     unsafe {
diff --git a/src/tools/miri/tests/pass/tree_borrows/formatting.rs b/src/tools/miri/tests/pass/tree_borrows/formatting.rs
index 64697cac261..f22c408ad25 100644
--- a/src/tools/miri/tests/pass/tree_borrows/formatting.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/formatting.rs
@@ -1,8 +1,8 @@
 //@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
 
 #[path = "../../utils/mod.rs"]
+#[macro_use]
 mod utils;
-use utils::macros::*;
 
 // Check the formatting of the trees.
 fn main() {
diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs
index e3f3f2d4032..a38cd6d2894 100644
--- a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs
@@ -1,8 +1,8 @@
 //@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
 
 #[path = "../../utils/mod.rs"]
+#[macro_use]
 mod utils;
-use utils::macros::*;
 
 // To check that a reborrow is counted as a Read access, we use a reborrow
 // with no additional Read to Freeze an Active pointer.
diff --git a/src/tools/miri/tests/pass/tree_borrows/reserved.rs b/src/tools/miri/tests/pass/tree_borrows/reserved.rs
index d8a8c27568d..8d0beab66f4 100644
--- a/src/tools/miri/tests/pass/tree_borrows/reserved.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/reserved.rs
@@ -1,9 +1,8 @@
 //@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
 
 #[path = "../../utils/mod.rs"]
+#[macro_use]
 mod utils;
-use utils::macros::*;
-use utils::miri_extern::miri_write_to_stderr;
 
 use std::cell::UnsafeCell;
 
@@ -28,8 +27,8 @@ fn main() {
 }
 
 unsafe fn print(msg: &str) {
-    miri_write_to_stderr(msg.as_bytes());
-    miri_write_to_stderr("\n".as_bytes());
+    utils::miri_write_to_stderr(msg.as_bytes());
+    utils::miri_write_to_stderr("\n".as_bytes());
 }
 
 unsafe fn read_second<T>(x: &mut T, y: *mut u8) {
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 0d50d54faf6..531543441c2 100644
--- a/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs
@@ -10,6 +10,8 @@ fn main() {
     aliasing_read_only_mutable_refs();
     string_as_mut_ptr();
     two_mut_protected_same_alloc();
+    direct_mut_to_const_raw();
+    local_addr_of_mut();
 
     // Stacked Borrows tests
     read_does_not_invalidate1();
@@ -19,7 +21,6 @@ fn main() {
     mut_raw_mut();
     partially_invalidate_mut();
     drop_after_sharing();
-    direct_mut_to_const_raw();
     two_raw();
     shr_and_raw();
     disjoint_mutable_subborrows();
@@ -28,6 +29,18 @@ fn main() {
     mut_below_shr();
     wide_raw_ptr_in_tuple();
     not_unpin_not_protected();
+    write_does_not_invalidate_all_aliases();
+}
+
+#[allow(unused_assignments)]
+fn local_addr_of_mut() {
+    let mut local = 0;
+    let ptr = ptr::addr_of_mut!(local);
+    // In SB, `local` and `*ptr` would have different tags, but in TB they have the same tag.
+    local = 1;
+    unsafe { *ptr = 2 };
+    local = 3;
+    unsafe { *ptr = 4 };
 }
 
 // Tree Borrows has no issue with several mutable references existing
@@ -172,12 +185,12 @@ fn drop_after_sharing() {
 
 // Make sure that coercing &mut T to *const T produces a writeable pointer.
 fn direct_mut_to_const_raw() {
-    // TODO: This is currently disabled, waiting on a decision on <https://github.com/rust-lang/rust/issues/56604>
-    /*let x = &mut 0;
+    let x = &mut 0;
     let y: *const i32 = x;
-    unsafe { *(y as *mut i32) = 1; }
+    unsafe {
+        *(y as *mut i32) = 1;
+    }
     assert_eq!(*x, 1);
-    */
 }
 
 // Make sure that we can create two raw pointers from a mutable reference and use them both.
@@ -298,3 +311,31 @@ fn not_unpin_not_protected() {
         drop(unsafe { Box::from_raw(raw) });
     });
 }
+
+fn write_does_not_invalidate_all_aliases() {
+    // In TB there are other ways to do that (`addr_of!(*x)` has the same tag as `x`),
+    // but let's still make sure this SB test keeps working.
+
+    mod other {
+        /// Some private memory to store stuff in.
+        static mut S: *mut i32 = 0 as *mut i32;
+
+        pub fn lib1(x: &&mut i32) {
+            unsafe {
+                S = (x as *const &mut i32).cast::<*mut i32>().read();
+            }
+        }
+
+        pub fn lib2() {
+            unsafe {
+                *S = 1337;
+            }
+        }
+    }
+
+    let x = &mut 0;
+    other::lib1(&x);
+    *x = 42; // a write to x -- invalidates other pointers?
+    other::lib2();
+    assert_eq!(*x, 1337); // oops, the value changed! I guess not all pointers were invalidated
+}
diff --git a/src/tools/miri/tests/pass/tree_borrows/unique.rs b/src/tools/miri/tests/pass/tree_borrows/unique.rs
index d0c3d133da5..44e2e813625 100644
--- a/src/tools/miri/tests/pass/tree_borrows/unique.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/unique.rs
@@ -5,8 +5,8 @@
 #![feature(ptr_internals)]
 
 #[path = "../../utils/mod.rs"]
+#[macro_use]
 mod utils;
-use utils::macros::*;
 
 use core::ptr::Unique;
 
diff --git a/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs b/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs
index 3516f8d2ebf..e5d0a683a72 100644
--- a/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs
@@ -5,8 +5,8 @@
 #![feature(vec_into_raw_parts)]
 
 #[path = "../../utils/mod.rs"]
+#[macro_use]
 mod utils;
-use utils::macros::*;
 
 // Check general handling of `Unique`:
 // there is no *explicit* `Unique` being used here, but there is one
diff --git a/src/tools/miri/tests/utils/fs.rs b/src/tools/miri/tests/utils/fs.rs
new file mode 100644
index 00000000000..47904926b48
--- /dev/null
+++ b/src/tools/miri/tests/utils/fs.rs
@@ -0,0 +1,29 @@
+use std::ffi::OsString;
+use std::path::PathBuf;
+
+use super::miri_extern;
+
+pub fn host_to_target_path(path: OsString) -> PathBuf {
+    use std::ffi::{CStr, CString};
+
+    // Once into_os_str_bytes is stable we can use it here.
+    // (Unstable features would need feature flags in each test...)
+    let path = CString::new(path.into_string().unwrap()).unwrap();
+    let mut out = Vec::with_capacity(1024);
+
+    unsafe {
+        let ret =
+            miri_extern::miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
+        assert_eq!(ret, 0);
+        // Here we panic if it's not UTF-8... but that is hard to avoid with OsStr APIs.
+        let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
+        PathBuf::from(out)
+    }
+}
+
+pub fn tmp() -> PathBuf {
+    let path =
+        std::env::var_os("MIRI_TEMP").unwrap_or_else(|| std::env::temp_dir().into_os_string());
+    // These are host paths. We need to convert them to the target.
+    host_to_target_path(path)
+}
diff --git a/src/tools/miri/tests/utils/macros.rs b/src/tools/miri/tests/utils/macros.rs
index 28b40954306..3f5b9f78ee0 100644
--- a/src/tools/miri/tests/utils/macros.rs
+++ b/src/tools/miri/tests/utils/macros.rs
@@ -9,7 +9,7 @@
 /// The id obtained can be passed directly to `print_state!`.
 macro_rules! alloc_id {
     ($ptr:expr) => {
-        crate::utils::miri_extern::miri_get_alloc_id($ptr as *const u8 as *const ())
+        $crate::utils::miri_get_alloc_id($ptr as *const u8 as *const ())
     };
 }
 
@@ -22,10 +22,10 @@ macro_rules! alloc_id {
 /// tags that have not been given a name. Defaults to `false`.
 macro_rules! print_state {
     ($alloc_id:expr) => {
-        crate::utils::macros::print_state!($alloc_id, false);
+        print_state!($alloc_id, false);
     };
     ($alloc_id:expr, $show:expr) => {
-        crate::utils::miri_extern::miri_print_borrow_state($alloc_id, $show);
+        $crate::utils::miri_print_borrow_state($alloc_id, $show);
     };
 }
 
@@ -42,20 +42,16 @@ macro_rules! print_state {
 /// `stringify!($ptr)` the name of `ptr` in the source code.
 macro_rules! name {
     ($ptr:expr, $name:expr) => {
-        crate::utils::macros::name!($ptr => 0, $name);
+        name!($ptr => 0, $name);
     };
     ($ptr:expr) => {
-        crate::utils::macros::name!($ptr => 0, stringify!($ptr));
+        name!($ptr => 0, stringify!($ptr));
     };
     ($ptr:expr => $nth_parent:expr) => {
-        crate::utils::macros::name!($ptr => $nth_parent, stringify!($ptr));
+        name!($ptr => $nth_parent, stringify!($ptr));
     };
     ($ptr:expr => $nth_parent:expr, $name:expr) => {
         let name = $name.as_bytes();
-        crate::utils::miri_extern::miri_pointer_name($ptr as *const u8 as *const (), $nth_parent, name);
+        $crate::utils::miri_pointer_name($ptr as *const u8 as *const (), $nth_parent, name);
     };
 }
-
-pub(crate) use alloc_id;
-pub(crate) use name;
-pub(crate) use print_state;
diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs
index 55f3c1cc33e..c0ef2c50641 100644
--- a/src/tools/miri/tests/utils/miri_extern.rs
+++ b/src/tools/miri/tests/utils/miri_extern.rs
@@ -1,5 +1,3 @@
-#![allow(dead_code)]
-
 #[repr(C)]
 /// Layout of the return value of `miri_resolve_frame`,
 /// with fields in the exact same order.
diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs
index e1ea77e4df8..593f82910c6 100644
--- a/src/tools/miri/tests/utils/mod.rs
+++ b/src/tools/miri/tests/utils/mod.rs
@@ -1,2 +1,10 @@
-pub mod macros;
-pub mod miri_extern;
+#![allow(dead_code)]
+
+#[macro_use]
+mod macros;
+
+mod fs;
+mod miri_extern;
+
+pub use fs::*;
+pub use miri_extern::*;