Skip to content

Commit

Permalink
Auto merge of #127119 - RalfJung:miri-sync, r=RalfJung
Browse files Browse the repository at this point in the history
Miri subtree update

r? `@ghost`
  • Loading branch information
bors committed Jun 29, 2024
2 parents be99243 + 9d920ed commit f845335
Show file tree
Hide file tree
Showing 63 changed files with 906 additions and 417 deletions.
87 changes: 2 additions & 85 deletions src/tools/miri/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,50 +33,7 @@ jobs:
HOST_TARGET: ${{ matrix.host_target }}
steps:
- uses: actions/checkout@v4

- name: Show Rust version (stable toolchain)
run: |
rustup show
rustc -Vv
cargo -V
# Cache the global cargo directory, but NOT the local `target` directory which
# we cannot reuse anyway when the nightly changes (and it grows quite large
# over time).
- name: Add cache for cargo
id: cache
uses: actions/cache@v4
with:
path: |
# Taken from <https://doc.rust-lang.org/nightly/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci>.
# Cache package/registry information
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
# Cache installed binaries
~/.cargo/bin
~/.cargo/.crates.toml
~/.cargo/.crates2.json
key: cargo-${{ runner.os }}-reset20240425-${{ hashFiles('**/Cargo.lock') }}
restore-keys: cargo-${{ runner.os }}-reset20240425

- name: Install tools
if: steps.cache.outputs.cache-hit != 'true'
run: cargo install -f rustup-toolchain-install-master hyperfine

- name: Install miri toolchain
run: |
if [[ ${{ github.event_name }} == 'schedule' ]]; then
echo "Building against latest rustc git version"
git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1 > rust-version
fi
./miri toolchain --host ${{ matrix.host_target }}
- name: Show Rust version (miri toolchain)
run: |
rustup show
rustc -Vv
cargo -V
- uses: ./.github/workflows/setup

# The `style` job only runs on Linux; this makes sure the Windows-host-specific
# code is also covered by clippy.
Expand All @@ -92,47 +49,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# This is exactly duplicated from above. GHA is pretty terrible when it comes
# to avoiding code duplication.

# Cache the global cargo directory, but NOT the local `target` directory which
# we cannot reuse anyway when the nightly changes (and it grows quite large
# over time).
- name: Add cache for cargo
id: cache
uses: actions/cache@v4
with:
path: |
# Taken from <https://doc.rust-lang.org/nightly/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci>.
# Cache package/registry information
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
# Cache installed binaries
~/.cargo/bin
~/.cargo/.crates.toml
~/.cargo/.crates2.json
key: cargo-${{ runner.os }}-reset20240331-${{ hashFiles('**/Cargo.lock') }}
restore-keys: cargo-${{ runner.os }}-reset20240331

- name: Install rustup-toolchain-install-master
if: steps.cache.outputs.cache-hit != 'true'
run: cargo install -f rustup-toolchain-install-master

- name: Install "master" toolchain
run: |
if [[ ${{ github.event_name }} == 'schedule' ]]; then
echo "Building against latest rustc git version"
git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1 > rust-version
fi
./miri toolchain
- name: Show Rust version
run: |
rustup show
rustc -Vv
cargo -V
- uses: ./.github/workflows/setup

- name: rustfmt
run: ./miri fmt --check
Expand Down
52 changes: 52 additions & 0 deletions src/tools/miri/.github/workflows/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: "Miri CI setup"
description: "Sets up Miri CI"
runs:
using: "composite"
steps:
- name: Show Rust version (stable toolchain)
run: |
rustup show
rustc -Vv
cargo -V
shell: bash

# Cache the global cargo directory, but NOT the local `target` directory which
# we cannot reuse anyway when the nightly changes (and it grows quite large
# over time).
- name: Add cache for cargo
id: cache
uses: actions/cache@v4
with:
path: |
# Taken from <https://doc.rust-lang.org/nightly/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci>.
# Cache package/registry information
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
# Cache installed binaries
~/.cargo/bin
~/.cargo/.crates.toml
~/.cargo/.crates2.json
key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', '.github/workflows/**/*.yml') }}
restore-keys: cargo-${{ runner.os }}

- name: Install rustup-toolchain-install-master
if: steps.cache.outputs.cache-hit != 'true'
run: cargo install -f rustup-toolchain-install-master hyperfine
shell: bash

- name: Install "master" toolchain
run: |
if [[ ${{ github.event_name }} == 'schedule' ]]; then
echo "Building against latest rustc git version"
git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1 > rust-version
fi
./miri toolchain
shell: bash

- name: Show Rust version (miri toolchain)
run: |
rustup show
rustc -Vv
cargo -V
shell: bash
18 changes: 12 additions & 6 deletions src/tools/miri/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,13 @@ Further caveats that Miri users should be aware of:
when `SeqCst` fences are used that are not actually permitted by the Rust memory model, and it
cannot produce all behaviors possibly observable on real hardware.

Moreover, Miri fundamentally cannot tell you whether your code is *sound*. [Soundness] is the property
of never causing undefined behavior when invoked from arbitrary safe code, even in combination with
Moreover, Miri fundamentally cannot ensure that your code is *sound*. [Soundness] is the property of
never causing undefined behavior when invoked from arbitrary safe code, even in combination with
other sound code. In contrast, Miri can just tell you if *a particular way of interacting with your
code* (e.g., a test suite) causes any undefined behavior. It is up to you to ensure sufficient
coverage.
code* (e.g., a test suite) causes any undefined behavior *in a particular execution* (of which there
may be many, e.g. when concurrency or other forms of non-determinism are involved). When Miri finds
UB, your code is definitely unsound, but when Miri does not find UB, then you may just have to test
more inputs or more possible non-deterministic choices.

[rust]: https://www.rust-lang.org/
[mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md
Expand Down Expand Up @@ -425,8 +427,12 @@ to Miri failing to detect cases of undefined behavior in a program.
value from a load. This can help diagnose problems that disappear under
`-Zmiri-disable-weak-memory-emulation`.
* `-Zmiri-tree-borrows` replaces [Stacked Borrows] with the [Tree Borrows] rules.
The soundness rules are already experimental without this flag, but even more
so with this flag.
Tree Borrows is even more experimental than Stacked Borrows. While Tree Borrows
is still sound in the sense of catching all aliasing violations that current versions
of the compiler might exploit, it is likely that the eventual final aliasing model
of Rust will be stricter than Tree Borrows. In other words, if you use Tree Borrows,
even if your code is accepted today, it might be declared UB in the future.
This is much less likely with Stacked Borrows.
* `-Zmiri-force-page-size=<num>` overrides the default page size for an architecture, in multiples of 1k.
`4` is default for most targets. This value should always be a power of 2 and nonzero.
* `-Zmiri-unique-is-unique` performs additional aliasing checks for `core::ptr::Unique` to ensure
Expand Down
6 changes: 4 additions & 2 deletions src/tools/miri/build.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
fn main() {
// Don't rebuild miri when nothing changed.
println!("cargo:rerun-if-changed=build.rs");
// Re-export the TARGET environment variable so it can
// be accessed by miri.
// Re-export the TARGET environment variable so it can be accessed by miri. Needed to know the
// "host" triple inside Miri.
let target = std::env::var("TARGET").unwrap();
println!("cargo:rustc-env=TARGET={target}");
// Allow some cfgs.
println!("cargo::rustc-check-cfg=cfg(bootstrap)");
}
6 changes: 6 additions & 0 deletions src/tools/miri/cargo-miri/src/phases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ Subcommands:
clean Clean the Miri cache & target directory
The cargo options are exactly the same as for `cargo run` and `cargo test`, respectively.
Furthermore, the following extra flags and environment variables are recognized for `run` and `test`:
--many-seeds[=from..to] Run the program/tests many times with different seeds in the given range.
The range defaults to `0..64`.
MIRIFLAGS Extra flags to pass to the Miri driver. Use this to pass `-Zmiri-...` flags.
Examples:
cargo miri run
Expand Down
3 changes: 2 additions & 1 deletion src/tools/miri/miri
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ set -e
# 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.
MIRI_SCRIPT_TARGET_DIR="$(dirname "$0")"/miri-script/target
cargo +stable build $CARGO_EXTRA_FLAGS -q --target-dir "$MIRI_SCRIPT_TARGET_DIR" --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml
cargo +stable build $CARGO_EXTRA_FLAGS -q --target-dir "$MIRI_SCRIPT_TARGET_DIR" --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml || \
( echo "Failed to build miri-script. Is the 'stable' toolchain installed?"; exit 1 )
"$MIRI_SCRIPT_TARGET_DIR"/debug/miri-script "$@"
6 changes: 2 additions & 4 deletions src/tools/miri/miri-script/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,11 @@ impl Command {
// Fetch given rustc commit.
cmd!(sh, "git fetch http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git")
.run()
.map_err(|e| {
.inspect_err(|_| {
// Try to un-do the previous `git commit`, to leave the repo in the state we found 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)")?;

Expand Down Expand Up @@ -545,9 +544,8 @@ impl Command {
if let Some(seed_range) = many_seeds {
e.run_many_times(seed_range, |sh, seed| {
eprintln!("Trying seed: {seed}");
run_miri(sh, Some(format!("-Zmiri-seed={seed}"))).map_err(|err| {
run_miri(sh, Some(format!("-Zmiri-seed={seed}"))).inspect_err(|_| {
eprintln!("FAILING SEED: {seed}");
err
})
})?;
} else {
Expand Down
3 changes: 1 addition & 2 deletions src/tools/miri/miri-script/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,9 @@ impl MiriEnv {
break;
}
// Run the command with this seed.
run(&local_shell, cur).map_err(|err| {
run(&local_shell, cur).inspect_err(|_| {
// If we failed, tell everyone about this.
failed.store(true, Ordering::Relaxed);
err
})?;
// Check if some other command failed (in which case we'll stop as well).
if failed.load(Ordering::Relaxed) {
Expand Down
3 changes: 2 additions & 1 deletion src/tools/miri/miri.bat
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ set MIRI_SCRIPT_TARGET_DIR=%0\..\miri-script\target

:: If any other steps are added, the "|| exit /b" must be appended to early
:: return from the script. If not, it will continue execution.
cargo +stable build %CARGO_EXTRA_FLAGS% -q --target-dir %MIRI_SCRIPT_TARGET_DIR% --manifest-path %0\..\miri-script\Cargo.toml || exit /b
cargo +stable build %CARGO_EXTRA_FLAGS% -q --target-dir %MIRI_SCRIPT_TARGET_DIR% --manifest-path %0\..\miri-script\Cargo.toml ^
|| (echo Failed to build miri-script. Is the 'stable' toolchain installed? & exit /b)

:: Forwards all arguments to this file to the executable.
:: We invoke the binary directly to avoid going through rustup, which would set some extra
Expand Down
2 changes: 1 addition & 1 deletion src/tools/miri/rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f6b4b71ef10307201b52c17b0f9dcf9557cd90ba
9ed2ab3790ff41bf741dd690befd6a1c1e2b23ca
2 changes: 1 addition & 1 deletion src/tools/miri/src/alloc_addresses/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl GlobalStateInner {
fn align_addr(addr: u64, align: u64) -> u64 {
match addr % align {
0 => addr,
rem => addr.checked_add(align).unwrap() - rem,
rem => addr.strict_add(align) - rem,
}
}

Expand Down
24 changes: 14 additions & 10 deletions src/tools/miri/src/alloc_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,19 @@ impl MiriAllocBytes {
/// If `size == 0` we allocate using a different `alloc_layout` with `size = 1`, to ensure each allocation has a unique address.
/// Returns `Err(alloc_layout)` if the allocation function returns a `ptr` where `ptr.is_null()`.
fn alloc_with(
size: usize,
align: usize,
size: u64,
align: u64,
alloc_fn: impl FnOnce(Layout) -> *mut u8,
) -> Result<MiriAllocBytes, Layout> {
let layout = Layout::from_size_align(size, align).unwrap();
) -> Result<MiriAllocBytes, ()> {
let size = usize::try_from(size).map_err(|_| ())?;
let align = usize::try_from(align).map_err(|_| ())?;
let layout = Layout::from_size_align(size, align).map_err(|_| ())?;
// When size is 0 we allocate 1 byte anyway, to ensure each allocation has a unique address.
let alloc_layout =
if size == 0 { Layout::from_size_align(1, align).unwrap() } else { layout };
let ptr = alloc_fn(alloc_layout);
if ptr.is_null() {
Err(alloc_layout)
Err(())
} else {
// SAFETY: All `MiriAllocBytes` invariants are fulfilled.
Ok(Self { ptr, layout })
Expand All @@ -86,20 +88,22 @@ impl AllocBytes for MiriAllocBytes {
fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align) -> Self {
let slice = slice.into();
let size = slice.len();
let align = align.bytes_usize();
let align = align.bytes();
// SAFETY: `alloc_fn` will only be used with `size != 0`.
let alloc_fn = |layout| unsafe { alloc::alloc(layout) };
let alloc_bytes = MiriAllocBytes::alloc_with(size, align, alloc_fn)
.unwrap_or_else(|layout| alloc::handle_alloc_error(layout));
let alloc_bytes = MiriAllocBytes::alloc_with(size.try_into().unwrap(), align, alloc_fn)
.unwrap_or_else(|()| {
panic!("Miri ran out of memory: cannot create allocation of {size} bytes")
});
// SAFETY: `alloc_bytes.ptr` and `slice.as_ptr()` are non-null, properly aligned
// and valid for the `size`-many bytes to be copied.
unsafe { alloc_bytes.ptr.copy_from(slice.as_ptr(), size) };
alloc_bytes
}

fn zeroed(size: Size, align: Align) -> Option<Self> {
let size = size.bytes_usize();
let align = align.bytes_usize();
let size = size.bytes();
let align = align.bytes();
// SAFETY: `alloc_fn` will only be used with `size != 0`.
let alloc_fn = |layout| unsafe { alloc::alloc_zeroed(layout) };
MiriAllocBytes::alloc_with(size, align, alloc_fn).ok()
Expand Down
7 changes: 3 additions & 4 deletions src/tools/miri/src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,9 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
}

if tcx.sess.opts.optimize != OptLevel::No {
tcx.dcx().warn("Miri does not support optimizations. If you have enabled optimizations \
by selecting a Cargo profile (such as --release) which changes other profile settings \
such as whether debug assertions and overflow checks are enabled, those settings are \
still applied.");
tcx.dcx().warn("Miri does not support optimizations: the opt-level is ignored. The only effect \
of selecting a Cargo profile that enables optimizations (such as --release) is to apply \
its remaining settings, such as whether debug assertions and overflow checks are enabled.");
}
if tcx.sess.mir_opt_level() > 0 {
tcx.dcx().warn("You have explicitly enabled MIR optimizations, overriding Miri's default \
Expand Down
3 changes: 1 addition & 2 deletions src/tools/miri/src/concurrency/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,7 @@ impl<'tcx> ThreadManager<'tcx> {
if !self.threads[joined_thread_id].state.is_terminated() {
trace!(
"{:?} blocked on {:?} when trying to join",
self.active_thread,
joined_thread_id
self.active_thread, joined_thread_id
);
// The joined thread is still running, we need to wait for it.
// Unce we get unblocked, perform the appropriate synchronization.
Expand Down
Loading

0 comments on commit f845335

Please sign in to comment.