diff --git a/Cargo.lock b/Cargo.lock index d4c1a02c018af..8b3792cef61bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3421,14 +3421,14 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b881c015c729b43105bbd3702a9bdecee28fafaa21126d1d62e454ec011a4b7" +checksum = "eec3905e8201688412f6f4b1f6c86d38b3ee6578f59ba85f41330a3af61e8365" dependencies = [ "anyhow", "rustc_version", "tempfile", - "toml 0.8.23", + "toml 1.1.0+spec-1.1.0", "walkdir", ] @@ -5195,9 +5195,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" dependencies = [ "serde_core", ] @@ -5693,7 +5693,6 @@ version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ - "indexmap", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -5708,13 +5707,28 @@ checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.0.3", + "serde_spanned 1.1.0", "toml_datetime 0.7.3", "toml_parser", "toml_writer", "winnow 0.7.13", ] +[[package]] +name = "toml" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.1.0", + "toml_datetime 1.1.0+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.0", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -5733,6 +5747,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -5762,11 +5785,11 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" dependencies = [ - "winnow 0.7.13", + "winnow 1.0.0", ] [[package]] @@ -5777,9 +5800,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.0.4" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" [[package]] name = "tracing" @@ -6765,6 +6788,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" + [[package]] name = "winsplit" version = "0.1.0" diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 4eeba30228924..9129562efe1d7 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: env: HOST_TARGET: ${{ matrix.host_target }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: install multiarch if: ${{ matrix.multiarch != '' }} run: | @@ -105,7 +105,7 @@ jobs: name: style checks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/workflows/setup - name: rustfmt @@ -121,7 +121,7 @@ jobs: name: bootstrap build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 # Deliberately skipping `./.github/workflows/setup` as we do our own setup - name: Add cache for cargo id: cache @@ -156,7 +156,7 @@ jobs: name: coverage report runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/workflows/setup - name: coverage run: ./miri test --coverage @@ -191,7 +191,7 @@ jobs: pull-requests: write if: ${{ github.event_name == 'schedule' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 256 # get a bit more of the history - name: install josh-sync diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 852ea26ab89e0..ad8b2b09b6ba8 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -171,8 +171,8 @@ MIRI_LOG=rustc_mir::interpret=info,miri::stacked_borrows ./miri run tests/pass/v ``` Note that you will only get `info`, `warn` or `error` messages if you use a prebuilt compiler. -In order to get `debug` and `trace` level messages, you need to build miri with a locally built -compiler that has `debug=true` set in `bootstrap.toml`. +In order to get `debug` and `trace` level messages, you need to build miri with a [locally built +compiler](#advanced-topic-building-miri-against-a-locally-compiled-rustc) that has `debug=true` set in `bootstrap.toml`. #### Debugging error messages @@ -320,6 +320,33 @@ You can also directly run Miri on a Rust source file: ./x.py run miri --stage 1 --args src/tools/miri/tests/pass/hello.rs ``` +## Advanced topic: Building Miri against a locally compiled rustc + +Very rarely, it can be necessary to work with an out-of-tree Miri but build it against a rustc that +was locally compiled. (Usually, you should instead work on the Miri that's in the Rust tree, as +described in the previous subsection.) + +This requires a fully bootstrapped build: + +```sh +# Build rustc, then build rustc with that rustc. This can take a while. +./x build library --stage 3 +``` + +You also need to set up a linked toolchain with rustup: + +```sh +rustup toolchain link stage2 build/host/stage2 +``` + +Then in the Miri folder, you can set this as the current toolchain and build against it: + +```sh +rustup override set stage2 +# Prevent `./miri` from reseting the toolchain. +export MIRI_AUTO_OPS=no +``` + ## Advanced topic: Syncing with the rustc repo We use the [`josh-sync`](https://github.com/rust-lang/josh-sync) tool to transmit changes between the diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock index 45be05fcc7f78..977728f63499a 100644 --- a/src/tools/miri/cargo-miri/Cargo.lock +++ b/src/tools/miri/cargo-miri/Cargo.lock @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b881c015c729b43105bbd3702a9bdecee28fafaa21126d1d62e454ec011a4b7" +checksum = "eec3905e8201688412f6f4b1f6c86d38b3ee6578f59ba85f41330a3af61e8365" dependencies = [ "anyhow", "rustc_version", @@ -339,11 +339,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -392,45 +392,42 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc" dependencies = [ "indexmap", - "serde", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" dependencies = [ - "serde", + "serde_core", ] [[package]] -name = "toml_edit" -version = "0.22.27" +name = "toml_parser" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_writer" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" [[package]] name = "unicode-ident" @@ -489,12 +486,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" [[package]] name = "wit-bindgen" diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index e8da7f2ca8a74..568bb29f49f88 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -18,7 +18,7 @@ directories = "6" rustc_version = "0.4" serde_json = "1.0.40" cargo_metadata = "0.23" -rustc-build-sysroot = "0.5.10" +rustc-build-sysroot = "0.5.12" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 6c0bceac7731f..9bacbbcf4597c 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -28,6 +28,7 @@ begingroup "Building Miri" export RUSTFLAGS="-D warnings" export CARGO_INCREMENTAL=0 export CARGO_EXTRA_FLAGS="--locked" +export CARGO_UNSTABLE_BUILD_DIR_NEW_LAYOUT=true # Determine configuration for installed build (used by test-cargo-miri and `./miri bench`). # We use the default set of features for this. diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 86f6253d4558a..d469502c243f4 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -80,7 +80,7 @@ impl Command { // `toolchain` goes first as it could affect the others if auto_toolchain { - Self::toolchain(vec![])?; + Self::toolchain(None, vec![])?; } if auto_fmt { Self::fmt(vec![])?; @@ -121,15 +121,18 @@ impl Command { Command::Clippy { features, flags } => Self::clippy(features, flags), Command::Bench { target, no_install, save_baseline, load_baseline, benches } => Self::bench(target, no_install, save_baseline, load_baseline, benches), - Command::Toolchain { flags } => Self::toolchain(flags), + Command::Toolchain { commit, flags } => Self::toolchain(commit, flags), Command::Squash => Self::squash(), } } - fn toolchain(flags: Vec) -> Result<()> { + fn toolchain(new_commit: Option, flags: Vec) -> Result<()> { let sh = Shell::new()?; sh.change_dir(miri_dir()?); - let new_commit = sh.read_file("rust-version")?.trim().to_owned(); + let new_commit = match new_commit { + Some(c) => c, + None => sh.read_file("rust-version")?.trim().to_owned(), + }; let current_commit = { let rustc_info = cmd!(sh, "rustc +miri --version -v").read(); if let Ok(rustc_info) = rustc_info { diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index e307014496886..419c128e5b72f 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -138,6 +138,9 @@ pub enum Command { /// The `rust-version` file is used to determine the commit that will be intsalled. /// `rustup-toolchain-install-master` must be installed for this to work. Toolchain { + /// Overwrite the commit to install. + #[arg(long)] + commit: Option, /// Flags that are passed through to `rustup-toolchain-install-master`. #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec, @@ -157,8 +160,8 @@ impl Command { | Self::Build { flags, .. } | Self::Check { flags, .. } | Self::Doc { flags, .. } - | Self::Fmt { flags } - | Self::Toolchain { flags } + | Self::Fmt { flags, .. } + | Self::Toolchain { flags, .. } | Self::Clippy { flags, .. } | Self::Run { flags, .. } | Self::Test { flags, .. } => { diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index e281dad8ef1ee..85571d95f742d 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -fd0c901b00ee1e08a250039cdb90258603497e20 +116458d0a5ae01cd517cabd2d1aee7f5457018ab diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index d6d6449df32bf..ee74e06815945 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -656,64 +656,10 @@ impl<'tcx> ThreadManager<'tcx> { // We should only switch stacks between steps. self.yield_active_thread = true; } - - /// Get the wait time for the next timeout, or `None` if no timeout is pending. - fn next_callback_wait_time(&self, clock: &MonotonicClock) -> Option { - self.threads - .iter() - .filter_map(|t| { - match &t.state { - ThreadState::Blocked { timeout: Some(timeout), .. } => - Some(timeout.get_wait_time(clock)), - _ => None, - } - }) - .min() - } } impl<'tcx> EvalContextPrivExt<'tcx> for MiriInterpCx<'tcx> {} trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { - /// Execute a timeout callback on the callback's thread. - #[inline] - fn run_timeout_callback(&mut self) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let mut found_callback = None; - // Find a blocked thread that has timed out. - for (id, thread) in this.machine.threads.threads.iter_enumerated_mut() { - match &thread.state { - ThreadState::Blocked { timeout: Some(timeout), .. } - if timeout.get_wait_time(&this.machine.monotonic_clock) == Duration::ZERO => - { - let old_state = mem::replace(&mut thread.state, ThreadState::Enabled); - let ThreadState::Blocked { callback, .. } = old_state else { unreachable!() }; - found_callback = Some((id, callback)); - // Run the fallback (after the loop because borrow-checking). - break; - } - _ => {} - } - } - if let Some((thread, callback)) = found_callback { - // This back-and-forth with `set_active_thread` is here because of two - // design decisions: - // 1. Make the caller and not the callback responsible for changing - // thread. - // 2. Make the scheduler the only place that can change the active - // thread. - let old_thread = this.machine.threads.set_active_thread_id(thread); - callback.call(this, UnblockKind::TimedOut)?; - this.machine.threads.set_active_thread_id(old_thread); - } - // found_callback can remain None if the computer's clock - // was shifted after calling the scheduler and before the call - // to get_ready_callback (see issue - // https://github.com/rust-lang/miri/issues/1763). In this case, - // just do nothing, which effectively just returns to the - // scheduler. - interp_ok(()) - } - #[inline] fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> { let this = self.eval_context_mut(); @@ -790,19 +736,12 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { this.poll_and_unblock(Some(Duration::ZERO))?; } - let thread_manager = &this.machine.threads; - let clock = &this.machine.monotonic_clock; - // We also check timeouts before running any other thread, to ensure that timeouts // "in the past" fire before any other thread can take an action. This ensures that for // `pthread_cond_timedwait`, "an error is returned if [...] the absolute time specified by // abstime has already been passed at the time of the call". // - let potential_sleep_time = thread_manager.next_callback_wait_time(clock); - if potential_sleep_time == Some(Duration::ZERO) { - // The timeout exceeded for some thread so we unblock the thread and execute its timeout callback. - this.run_timeout_callback()?; - } + let potential_sleep_time = this.unblock_expired_timeouts()?; let thread_manager = &mut this.machine.threads; let rng = this.machine.rng.get_mut(); @@ -868,6 +807,71 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { throw_machine_stop!(TerminationInfo::GlobalDeadlock); } } + + /// Poll for I/O events until either an I/O event happened or the timeout expired. + /// The different timeout values are described in [`BlockingIoManager::poll`]. + fn poll_and_unblock(&mut self, timeout: Option) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let ready = match this.machine.blocking_io.poll(timeout) { + Ok(ready) => ready, + // We can ignore errors originating from interrupts; that's just a spurious wakeup. + Err(e) if e.kind() == io::ErrorKind::Interrupted => return interp_ok(()), + // For other errors we panic. On Linux and BSD hosts this should only be + // reachable when a system resource error (e.g. ENOMEM or ENOSPC) occurred. + Err(e) => panic!("unexpected error while polling: {e}"), + }; + + ready.into_iter().try_for_each(|thread_id| this.unblock_thread(thread_id, BlockReason::IO)) + } + + /// Find all threads with expired timeouts, unblock them and execute their timeout callbacks. + /// + /// This method returns the minimum duration until the next thread timeout expires. + /// If all ready threads have no timeout set, [`None`] is returned. + fn unblock_expired_timeouts(&mut self) -> InterpResult<'tcx, Option> { + let this = self.eval_context_mut(); + let clock = &this.machine.monotonic_clock; + + let mut min_wait_time = Option::::None; + let mut callbacks = Vec::new(); + + for (id, thread) in this.machine.threads.threads.iter_enumerated_mut() { + match &thread.state { + ThreadState::Blocked { timeout: Some(timeout), .. } => { + let wait_time = timeout.get_wait_time(clock); + if wait_time.is_zero() { + // The timeout expired for this thread. + let old_state = mem::replace(&mut thread.state, ThreadState::Enabled); + let ThreadState::Blocked { callback, .. } = old_state else { + unreachable!() + }; + // Add callback to list to be run after this loop because of borrow-checking. + callbacks.push((id, callback)); + } else { + // Update `min_wait_time` to contain the smallest duration until + // the next timeout expires. + min_wait_time = Some(wait_time.min(min_wait_time.unwrap_or(Duration::MAX))); + } + } + _ => {} + } + } + + for (thread, callback) in callbacks { + // This back-and-forth with `set_active_thread` is here because of two + // design decisions: + // 1. Make the caller and not the callback responsible for changing + // thread. + // 2. Make the scheduler the only place that can change the active + // thread. + let old_thread = this.machine.threads.set_active_thread_id(thread); + callback.call(this, UnblockKind::TimedOut)?; + this.machine.threads.set_active_thread_id(old_thread); + } + + interp_ok(min_wait_time) + } } // Public interface to thread management. @@ -1348,21 +1352,4 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } } - - /// Poll for I/O events until either an I/O event happened or the timeout expired. - /// The different timeout values are described in [`BlockingIoManager::poll`]. - fn poll_and_unblock(&mut self, timeout: Option) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - - let ready = match this.machine.blocking_io.poll(timeout) { - Ok(ready) => ready, - // We can ignore errors originating from interrupts; that's just a spurious wakeup. - Err(e) if e.kind() == io::ErrorKind::Interrupted => return interp_ok(()), - // For other errors we panic. On Linux and BSD hosts this should only be - // reachable when a system resource error (e.g. ENOMEM or ENOSPC) occurred. - Err(e) => panic!("{e}"), - }; - - ready.into_iter().try_for_each(|thread_id| this.unblock_thread(thread_id, BlockReason::IO)) - } } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index d8224f1878f05..566a775b90108 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -569,7 +569,7 @@ pub struct MiriMachine<'tcx> { pub(crate) user_relevant_crates: Vec, /// Mapping extern static names to their pointer. - extern_statics: FxHashMap, + pub(crate) extern_statics: FxHashMap, /// The random number generator used for resolving non-determinism. /// Needs to be queried by ptr_to_int, hence needs interior mutability. diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs index d06b02a41334f..6a914d5cfa68a 100644 --- a/src/tools/miri/src/shims/aarch64.rs +++ b/src/tools/miri/src/shims/aarch64.rs @@ -4,6 +4,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; +use crate::shims::math::compute_crc32; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -58,6 +59,93 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_immediate(*res_lane, &dest)?; } } + + // Wrapping pairwise addition. + // + // Concatenates the two input vectors and adds adjacent elements. For input vectors `v` + // and `w` this computes `[v0 + v1, v2 + v3, ..., w0 + w1, w2 + w3, ...]`, using + // wrapping addition for `+`. + // + // Used by `vpadd_{s8, u8, s16, u16, s32, u32}`. + name if name.starts_with("neon.addp.") => { + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + let (left, left_len) = this.project_to_simd(left)?; + let (right, right_len) = this.project_to_simd(right)?; + let (dest, dest_len) = this.project_to_simd(dest)?; + + assert_eq!(left_len, right_len); + assert_eq!(left_len, dest_len); + + assert_eq!(left.layout, right.layout); + assert_eq!(left.layout, dest.layout); + + assert!(dest_len.is_multiple_of(2)); + let half_len = dest_len.strict_div(2); + + for lane_idx in 0..dest_len { + // The left and right vectors are concatenated. + let (src, src_pair_idx) = if lane_idx < half_len { + (&left, lane_idx) + } else { + (&right, lane_idx.strict_sub(half_len)) + }; + // Convert "pair index" into "index of first element of the pair". + let i = src_pair_idx.strict_mul(2); + + let lhs = this.read_immediate(&this.project_index(src, i)?)?; + let rhs = this.read_immediate(&this.project_index(src, i.strict_add(1))?)?; + + // Wrapping addition on the element type. + let sum = this.binary_op(BinOp::Add, &lhs, &rhs)?; + + let dst_lane = this.project_index(&dest, lane_idx)?; + this.write_immediate(*sum, &dst_lane)?; + } + } + + // Widening pairwise addition. + // + // Takes a single input vector, and an output vector with half as many lanes and double + // the element width. Takes adjacent pairs of elements, widens both, and then adds them + // together. + // + // Used by `vpaddl_{u8, u16, u32}` and `vpaddlq_{u8, u16, u32}`. + name if name.starts_with("neon.uaddlp.") => { + let [src] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + let (src, src_len) = this.project_to_simd(src)?; + let (dest, dest_len) = this.project_to_simd(dest)?; + + // Operates pairwise, so src has twice as many lanes. + assert_eq!(src_len, dest_len.strict_mul(2)); + + let src_elem_size = src.layout.field(this, 0).size; + let dest_elem_size = dest.layout.field(this, 0).size; + + // Widens, so dest elements must be exactly twice as wide. + assert_eq!(dest_elem_size.bytes(), src_elem_size.bytes().strict_mul(2)); + + for dest_idx in 0..dest_len { + let src_idx = dest_idx.strict_mul(2); + + let a_scalar = this.read_scalar(&this.project_index(&src, src_idx)?)?; + let b_scalar = + this.read_scalar(&this.project_index(&src, src_idx.strict_add(1))?)?; + + let a_val = a_scalar.to_uint(src_elem_size)?; + let b_val = b_scalar.to_uint(src_elem_size)?; + + // Use addition on u128 to simulate widening addition for the destination type. + // This cannot wrap since the element type is at most u64. + let sum = a_val.strict_add(b_val); + + let dst_lane = this.project_index(&dest, dest_idx)?; + this.write_scalar(Scalar::from_uint(sum, dest_elem_size), &dst_lane)?; + } + } + // Vector table lookup: each index selects a byte from the 16-byte table, out-of-range -> 0. // Used to implement vtbl1_u8 function. // LLVM does not have a portable shuffle that takes non-const indices @@ -85,6 +173,47 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(val, &this.project_index(&dest, i)?)?; } } + // Used to implement the __crc32{b,h,w,x} and __crc32c{b,h,w,x} functions. + // Polynomial 0x04C11DB7 (standard CRC-32): + // https://developer.arm.com/documentation/ddi0602/latest/Base-Instructions/CRC32B--CRC32H--CRC32W--CRC32X--CRC32-checksum- + // Polynomial 0x1EDC6F41 (CRC-32C / Castagnoli): + // https://developer.arm.com/documentation/ddi0602/latest/Base-Instructions/CRC32CB--CRC32CH--CRC32CW--CRC32CX--CRC32C-checksum- + "crc32b" | "crc32h" | "crc32w" | "crc32x" | "crc32cb" | "crc32ch" | "crc32cw" + | "crc32cx" => { + this.expect_target_feature_for_intrinsic(link_name, "crc")?; + // The polynomial constants below include the leading 1 bit + // (e.g. 0x104C11DB7 instead of 0x04C11DB7) which the ARM docs + // omit but the polynomial division algorithm requires. + let (bit_size, polynomial): (u32, u128) = match unprefixed_name { + "crc32b" => (8, 0x104C11DB7), + "crc32h" => (16, 0x104C11DB7), + "crc32w" => (32, 0x104C11DB7), + "crc32x" => (64, 0x104C11DB7), + "crc32cb" => (8, 0x11EDC6F41), + "crc32ch" => (16, 0x11EDC6F41), + "crc32cw" => (32, 0x11EDC6F41), + "crc32cx" => (64, 0x11EDC6F41), + _ => unreachable!(), + }; + + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let left = this.read_scalar(left)?; + let right = this.read_scalar(right)?; + + // The CRC accumulator is always u32. The data argument is u32 for + // b/h/w variants and u64 for the x variant, per the LLVM intrinsic + // definitions (all b/h/w take i32, only x takes i64). + // https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/IntrinsicsAArch64.td + // If the higher bits are non-zero, `compute_crc32` will panic. We should probably + // raise a proper error instead, but outside stdarch nobody can trigger this anyway. + let crc = left.to_u32()?; + let data = + if bit_size == 64 { right.to_u64()? } else { u64::from(right.to_u32()?) }; + + let result = compute_crc32(crc, data, bit_size, polynomial); + this.write_scalar(Scalar::from_u32(result), dest)?; + } _ => return interp_ok(EmulateItemResult::NotSupported), } interp_ok(EmulateItemResult::NeedsReturn) diff --git a/src/tools/miri/src/shims/math.rs b/src/tools/miri/src/shims/math.rs index ef185aa2a3e9f..1da7fbbdac0ce 100644 --- a/src/tools/miri/src/shims/math.rs +++ b/src/tools/miri/src/shims/math.rs @@ -245,3 +245,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(EmulateItemResult::NeedsReturn) } } + +/// Compute a CRC32 checksum using the given polynomial. +/// +/// `bit_size` is the number of relevant data bits (8, 16, 32, or 64). +/// Only the low `bit_size` bits of `data` are used; higher bits must be zero. +/// `polynomial` includes the leading 1 bit (e.g. `0x11EDC6F41` for CRC32C). +/// +/// Following hardware CRC conventions, `crc` and `data` bits are assumed to be reversed, +/// and output bits will be equally reversed. +pub(crate) fn compute_crc32(crc: u32, data: u64, bit_size: u32, polynomial: u128) -> u32 { + assert!( + bit_size == 64 || data < 1u64.strict_shl(bit_size), + "crc32: `data` is larger than {bit_size} bits" + ); + // Bit-reverse inputs to match hardware CRC conventions. + let crc = u128::from(crc.reverse_bits()); + // Reverse all 64 bits of `data`, then shift right by `64 - bit_size`. This + // discards the (now-reversed) higher bits, leaving only the reversed low + // `bit_size` bits in the lowest positions (with zeros above). + let v = u128::from(data.reverse_bits() >> (64u32.strict_sub(bit_size))); + + // Perform polynomial division modulo 2. + // The algorithm for the division is an adapted version of the + // schoolbook division algorithm used for normal integer or polynomial + // division. In this context, the quotient is not calculated, since + // only the remainder is needed. + // + // The algorithm works as follows: + // 1. Pull down digits until division can be performed. In the context of division + // modulo 2 it means locating the most significant digit of the dividend and shifting + // the divisor such that the position of the divisors most significand digit and the + // dividends most significand digit match. + // 2. Perform a division and determine the remainder. Since it is arithmetic modulo 2, + // this operation is a simple bitwise exclusive or. + // 3. Repeat steps 1. and 2. until the full remainder is calculated. This is the case + // once the degree of the remainder polynomial is smaller than the degree of the + // divisor polynomial. In other words, the number of leading zeros of the remainder + // is larger than the number of leading zeros of the divisor. It is important to + // note that standard arithmetic comparison is not applicable here: + // 0b10011 / 0b11111 = 0b01100 is a valid division, even though the dividend is + // smaller than the divisor. + let mut dividend = (crc << bit_size) ^ (v << 32); + while dividend.leading_zeros() <= polynomial.leading_zeros() { + dividend ^= (polynomial << polynomial.leading_zeros()) >> dividend.leading_zeros(); + } + + u32::try_from(dividend).unwrap().reverse_bits() +} diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 6a281ddbb7b2f..b302a07cf0c53 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -396,8 +396,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // matches what codegen does. This does mean that we support some types whose ABI is not // stable, but that's fine -- we are anyway quite conservative in native-lib mode. if let BackendRepr::Scalar(s) = layout.backend_repr { - // Simple sanity-check: this cannot be `repr(C)`. - assert!(!layout.ty.ty_adt_def().is_some_and(|adt| adt.repr().c())); + // Simple sanity-check: this cannot be a `repr(C)` struct or union. (It could be a + // repr(C) enum. Those indeed behave like integers in the ABI.) + assert!(!layout.ty.ty_adt_def().is_some_and(|adt| !adt.is_enum() && adt.repr().c())); return Ok(match s.primitive() { Primitive::Int(Integer::I8, /* signed */ true) => FfiType::i8(), Primitive::Int(Integer::I16, /* signed */ true) => FfiType::i16(), diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index f2e16e75892ec..3651bc171adcd 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -633,6 +633,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.getsockname(socket, address, address_len)?; this.write_scalar(result, dest)?; } + "getpeername" => { + let [socket, address, address_len] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, *mut _) -> i32), + link_name, + abi, + args, + )?; + let result = this.getpeername(socket, address, address_len)?; + this.write_scalar(result, dest)?; + } // Time "gettimeofday" => { @@ -727,11 +737,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read_target_usize(handle)?; let symbol = this.read_pointer(symbol)?; let name = this.read_c_str(symbol)?; - if let Ok(name) = str::from_utf8(name) - && is_dyn_sym(name, &this.tcx.sess.target.os) - { + let Ok(name) = str::from_utf8(name) else { + throw_unsup_format!("dlsym: non UTF-8 symbol name not supported") + }; + if is_dyn_sym(name, &this.tcx.sess.target.os) { let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name))); this.write_pointer(ptr, dest)?; + } else if let Some(&ptr) = this.machine.extern_statics.get(&Symbol::intern(name)) { + this.write_pointer(ptr, dest)?; } else { this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/freebsd/sync.rs b/src/tools/miri/src/shims/unix/freebsd/sync.rs index 8cf4464389631..7c46dd549bc0a 100644 --- a/src/tools/miri/src/shims/unix/freebsd/sync.rs +++ b/src/tools/miri/src/shims/unix/freebsd/sync.rs @@ -215,8 +215,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(duration) = this.read_timespec(×pec_place)? else { return interp_ok(None) }; let flags_place = this.project_field(ut, FieldIdx::from_u32(1))?; - let flags = this.read_scalar(&flags_place)?.to_u32()?; - let abs_time_flag = flags == abs_time; + let mut flags = this.read_scalar(&flags_place)?.to_u32()?; + + let abs_time_flag = if flags & abs_time != 0 { + flags &= !abs_time; + true + } else { + false + }; + if flags != 0 { + throw_unsup_format!("unsupported `_umtx_time` flags: {:#x}", flags); + } let clock_id_place = this.project_field(ut, FieldIdx::from_u32(2))?; let clock_id = this.read_scalar(&clock_id_place)?; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 9873af85b989b..5adc5932883ef 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -364,6 +364,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.machine.emit_diagnostic(NonHaltingDiagnostic::FileInProcOpened); } + // We will "subtract" supported flags from this and at the end check that no bits are left. + let mut flag = flag; + let mut options = OpenOptions::new(); let o_rdonly = this.eval_libc_i32("O_RDONLY"); @@ -379,6 +382,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Now we check the access mode let access_mode = flag & 0b11; + flag &= !access_mode; if access_mode == o_rdonly { writable = false; @@ -390,23 +394,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { throw_unsup_format!("unsupported access mode {:#x}", access_mode); } - // We need to check that there aren't unsupported options in `flag`. For this we try to - // reproduce the content of `flag` in the `mirror` variable using only the supported - // options. - let mut mirror = access_mode; let o_append = this.eval_libc_i32("O_APPEND"); if flag & o_append == o_append { + flag &= !o_append; options.append(true); - mirror |= o_append; } let o_trunc = this.eval_libc_i32("O_TRUNC"); if flag & o_trunc == o_trunc { + flag &= !o_trunc; options.truncate(true); - mirror |= o_trunc; } let o_creat = this.eval_libc_i32("O_CREAT"); if flag & o_creat == o_creat { + flag &= !o_creat; // Get the mode. On macOS, the argument type `mode_t` is actually `u16`, but // C integer promotion rules mean that on the ABI level, it gets passed as `u32` // (see https://github.com/rust-lang/rust/issues/71915). @@ -430,11 +431,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - mirror |= o_creat; - let o_excl = this.eval_libc_i32("O_EXCL"); if flag & o_excl == o_excl { - mirror |= o_excl; + flag &= !o_excl; options.create_new(true); } else { options.create(true); @@ -442,9 +441,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let o_cloexec = this.eval_libc_i32("O_CLOEXEC"); if flag & o_cloexec == o_cloexec { + flag &= !o_cloexec; // We do not need to do anything for this flag because `std` already sets it. // (Technically we do not support *not* setting this flag, but we ignore that.) - mirror |= o_cloexec; } if this.tcx.sess.target.os == Os::Linux { let o_tmpfile = this.eval_libc_i32("O_TMPFILE"); @@ -456,6 +455,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let o_nofollow = this.eval_libc_i32("O_NOFOLLOW"); if flag & o_nofollow == o_nofollow { + flag &= !o_nofollow; #[cfg(unix)] { use std::os::unix::fs::OpenOptionsExt; @@ -472,13 +472,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("ELOOP")); } } - mirror |= o_nofollow; } - // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`, - // then we throw an error. - if flag != mirror { - throw_unsup_format!("unsupported flags {:#x}", flag & !mirror); + // If `flag` has any bits left set, those are not supported. + if flag != 0 { + throw_unsup_format!("unsupported flags {:#x}", flag); } // Reject if isolation is enabled. diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index f9b3ca479b799..8f2ab69261ce7 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -620,6 +620,48 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Err(e) => this.set_last_error_and_return_i32(e), } } + + fn getpeername( + &mut self, + socket: &OpTy<'tcx>, + address: &OpTy<'tcx>, + address_len: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let socket = this.read_scalar(socket)?.to_i32()?; + let address_ptr = this.read_pointer(address)?; + let address_len_ptr = this.read_pointer(address_len)?; + + // Get the file handle + let Some(fd) = this.machine.fds.get(socket) else { + return this.set_last_error_and_return_i32(LibcError("EBADF")); + }; + + let Some(socket) = fd.downcast::() else { + // Man page specifies to return ENOTSOCK if `fd` is not a socket. + return this.set_last_error_and_return_i32(LibcError("ENOTSOCK")); + }; + + assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + + let state = socket.state.borrow(); + + let SocketState::Connected(stream) = &*state else { + // We can only read the peer address of connected sockets. + return this.set_last_error_and_return_i32(LibcError("ENOTCONN")); + }; + + let address = match stream.peer_addr() { + Ok(address) => address, + Err(e) => return this.set_last_error_and_return_i32(e), + }; + + match this.write_socket_address(&address, address_ptr, address_len_ptr, "getpeername")? { + Ok(_) => interp_ok(Scalar::from_i32(0)), + Err(e) => this.set_last_error_and_return_i32(e), + } + } } impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {} diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 2d1a153d9262e..16e269c688809 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -451,6 +451,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.SetFilePointerEx(file, distance_to_move, new_file_pointer, move_method)?; this.write_scalar(res, dest)?; } + "MoveFileExW" => { + let [existing_name, new_name, flags] = this.check_shim_sig( + shim_sig!(extern "system" fn(*const _, *const _, u32) -> winapi::BOOL), + link_name, + abi, + args, + )?; + let res = this.MoveFileExW(existing_name, new_name, flags)?; + this.write_scalar(res, dest)?; + } // Allocation "HeapAlloc" => { diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index e5a98e86d6453..1ee93cf911c5a 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -490,6 +490,36 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } + fn MoveFileExW( + &mut self, + existing_name: &OpTy<'tcx>, + new_name: &OpTy<'tcx>, + flags: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let existing_name = this.read_path_from_wide_str(this.read_pointer(existing_name)?)?; + let new_name = this.read_path_from_wide_str(this.read_pointer(new_name)?)?; + + let flags = this.read_scalar(flags)?.to_u32()?; + + // Flag to indicate whether we should replace an existing file. + // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-movefileexw + let movefile_replace_existing = this.eval_windows_u32("c", "MOVEFILE_REPLACE_EXISTING"); + + if flags != movefile_replace_existing { + throw_unsup_format!("MoveFileExW: Unsupported `dwFlags` value {}", flags); + } + + match std::fs::rename(existing_name, new_name) { + Ok(_) => interp_ok(this.eval_windows("c", "TRUE")), + Err(e) => { + this.set_last_error(e)?; + interp_ok(this.eval_windows("c", "FALSE")) + } + } + } + fn DeleteFileW( &mut self, file_name: &OpTy<'tcx>, // LPCWSTR diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs index 7c0f9c570e2ef..6b0e6726e8f79 100644 --- a/src/tools/miri/src/shims/x86/sse42.rs +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -5,6 +5,7 @@ use rustc_span::Symbol; use rustc_target::callconv::FnAbi; use rustc_target::spec::Arch; +use crate::shims::math::compute_crc32; use crate::*; /// A bitmask constant for scrutinizing the immediate byte provided @@ -445,46 +446,19 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The 64-bit version will only consider the lower 32 bits, // while the upper 32 bits get discarded. #[expect(clippy::as_conversions)] - u128::from((left.to_u64()? as u32).reverse_bits()) + (left.to_u64()? as u32) } else { - u128::from(left.to_u32()?.reverse_bits()) + left.to_u32()? }; - let v = match bit_size { - 8 => u128::from(right.to_u8()?.reverse_bits()), - 16 => u128::from(right.to_u16()?.reverse_bits()), - 32 => u128::from(right.to_u32()?.reverse_bits()), - 64 => u128::from(right.to_u64()?.reverse_bits()), + let data = match bit_size { + 8 => u64::from(right.to_u8()?), + 16 => u64::from(right.to_u16()?), + 32 => u64::from(right.to_u32()?), + 64 => right.to_u64()?, _ => unreachable!(), }; - // Perform polynomial division modulo 2. - // The algorithm for the division is an adapted version of the - // schoolbook division algorithm used for normal integer or polynomial - // division. In this context, the quotient is not calculated, since - // only the remainder is needed. - // - // The algorithm works as follows: - // 1. Pull down digits until division can be performed. In the context of division - // modulo 2 it means locating the most significant digit of the dividend and shifting - // the divisor such that the position of the divisors most significand digit and the - // dividends most significand digit match. - // 2. Perform a division and determine the remainder. Since it is arithmetic modulo 2, - // this operation is a simple bitwise exclusive or. - // 3. Repeat steps 1. and 2. until the full remainder is calculated. This is the case - // once the degree of the remainder polynomial is smaller than the degree of the - // divisor polynomial. In other words, the number of leading zeros of the remainder - // is larger than the number of leading zeros of the divisor. It is important to - // note that standard arithmetic comparison is not applicable here: - // 0b10011 / 0b11111 = 0b01100 is a valid division, even though the dividend is - // smaller than the divisor. - let mut dividend = (crc << bit_size) ^ (v << 32); - const POLYNOMIAL: u128 = 0x11EDC6F41; - while dividend.leading_zeros() <= POLYNOMIAL.leading_zeros() { - dividend ^= - (POLYNOMIAL << POLYNOMIAL.leading_zeros()) >> dividend.leading_zeros(); - } - - let result = u32::try_from(dividend).unwrap().reverse_bits(); + let result = compute_crc32(crc, data, bit_size, 0x11EDC6F41); let result = if bit_size == 64 { Scalar::from_u64(u64::from(result)) } else { diff --git a/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs index 5c80f6425eaae..b1929e3f27ed2 100644 --- a/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs @@ -7,7 +7,7 @@ fn main() { let t = thread::spawn(|| unsafe { // Access the environment in another thread without taking the env lock. // This represents some C code that queries the environment. - libc::getenv(b"TZ\0".as_ptr().cast()); //~ERROR: Data race detected + libc::getenv(c"TZ".as_ptr()); //~ERROR: Data race detected }); // Meanwhile, the main thread uses the "safe" Rust env accessor. env::set_var("MY_RUST_VAR", "Ferris"); diff --git a/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.stderr b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.stderr index 635091cc0173d..b4cd31b4ddaba 100644 --- a/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.stderr +++ b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `main` and (2) non-atomic read on thread `unnamed-ID` at ALLOC --> tests/fail-dep/libc/env-set_var-data-race.rs:LL:CC | -LL | libc::getenv(b"TZ/0".as_ptr().cast()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (2) just happened here +LL | libc::getenv(c"TZ".as_ptr()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (2) just happened here | help: and (1) occurred earlier here --> tests/fail-dep/libc/env-set_var-data-race.rs:LL:CC @@ -19,7 +19,7 @@ LL | let t = thread::spawn(|| unsafe { | _____________^ LL | | // Access the environment in another thread without taking the env lock. LL | | // This represents some C code that queries the environment. -LL | | libc::getenv(b"TZ/0".as_ptr().cast()); +LL | | libc::getenv(c"TZ".as_ptr()); LL | | }); | |______^ diff --git a/src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs b/src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs index 2c676f12b4f0e..abdafe2b0fb38 100644 --- a/src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs +++ b/src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs @@ -6,6 +6,6 @@ fn main() { } fn test_mkstemp_immutable_arg() { - let s: *mut libc::c_char = b"fooXXXXXX\0" as *const _ as *mut _; + let s: *mut libc::c_char = c"fooXXXXXX".as_ptr().cast_mut(); let _fd = unsafe { libc::mkstemp(s) }; //~ ERROR: Undefined Behavior: writing to alloc1 which is read-only } diff --git a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs index 457f32e55446e..29548c17443a6 100644 --- a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs +++ b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs @@ -6,7 +6,6 @@ fn main() { } fn test_file_open_missing_needed_mode() { - let name = b"missing_arg.txt\0"; - let name_ptr = name.as_ptr().cast::(); - let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; //~ ERROR: Undefined Behavior: not enough variadic arguments + let name = c"missing_arg.txt".as_ptr(); + let _fd = unsafe { libc::open(name, libc::O_CREAT) }; //~ ERROR: Undefined Behavior: not enough variadic arguments } diff --git a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr index a85fae9c7dd27..186ca4ccdd406 100644 --- a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr +++ b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: not enough variadic arguments for `open(pathname, O_CREAT, ...)`: got 0, expected at least 1 --> tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs:LL:CC | -LL | let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | let _fd = unsafe { libc::open(name, libc::O_CREAT) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.rs b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.rs index 8bf46af71e821..3f9c42a782233 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.rs +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.rs @@ -1,5 +1,6 @@ fn main() { - fn f() -> u32 { //~ ERROR: type u32 passing return place of type () + fn f() -> u32 { + //~^ERROR: type u32 passing return place of type () 42 } diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs index 7bc26237576a3..0635fdd149204 100644 --- a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs @@ -5,7 +5,8 @@ use std::intrinsics::mir::*; use std::num::NonZero; use std::ptr; -fn f(c: u32) { //~ERROR: expected something greater or equal to 1 +fn f(c: u32) { + //~^ERROR: expected something greater or equal to 1 println!("{c}"); } diff --git a/src/tools/miri/tests/native-lib/pass/ptr_write_access.rs b/src/tools/miri/tests/native-lib/pass/ptr_write_access.rs index 57def78b0ab17..03f14dcc3cc9b 100644 --- a/src/tools/miri/tests/native-lib/pass/ptr_write_access.rs +++ b/src/tools/miri/tests/native-lib/pass/ptr_write_access.rs @@ -176,7 +176,7 @@ fn test_swap_ptr_triple_dangling() { } extern "C" { - fn swap_ptr_triple_dangling(t_ptr: *const Triple); + fn swap_ptr_triple_dangling(t_ptr: *mut Triple); } let x = 101; @@ -184,9 +184,9 @@ fn test_swap_ptr_triple_dangling() { let ptr = Box::as_ptr(&b); drop(b); let z = 121; - let triple = Triple { ptr0: &raw const x, ptr1: ptr, ptr2: &raw const z }; + let mut triple = Triple { ptr0: &raw const x, ptr1: ptr, ptr2: &raw const z }; - unsafe { swap_ptr_triple_dangling(&triple) } + unsafe { swap_ptr_triple_dangling(&mut triple) } assert_eq!(unsafe { *triple.ptr2 }, x); } diff --git a/src/tools/miri/tests/native-lib/pass/scalar_arguments.rs b/src/tools/miri/tests/native-lib/pass/scalar_arguments.rs index 231df67bb5b8c..07584ba236b98 100644 --- a/src/tools/miri/tests/native-lib/pass/scalar_arguments.rs +++ b/src/tools/miri/tests/native-lib/pass/scalar_arguments.rs @@ -1,3 +1,10 @@ +#[allow(unused)] +#[repr(C)] +enum CEnum { + A, + B, +} + extern "C" { fn add_one_int(x: i32) -> i32; fn add_int16(x: i16) -> i16; @@ -19,6 +26,7 @@ extern "C" { fn get_unsigned_int() -> u32; fn add_float(x: f32) -> f32; fn printer(); + fn scalar_enum(e: CEnum) -> u8; } fn main() { @@ -43,5 +51,8 @@ fn main() { // test void function that prints from C printer(); + + // test passing enums with scalar layout + assert_eq!(scalar_enum(CEnum::B), 1); } } diff --git a/src/tools/miri/tests/native-lib/scalar_arguments.c b/src/tools/miri/tests/native-lib/scalar_arguments.c index 720f1982178c8..19ca940204a08 100644 --- a/src/tools/miri/tests/native-lib/scalar_arguments.c +++ b/src/tools/miri/tests/native-lib/scalar_arguments.c @@ -4,6 +4,11 @@ // See comments in build_native_lib() #define EXPORT __attribute__((visibility("default"))) +enum cenum { + cenum_a, + cenum_b, +}; + EXPORT int32_t add_one_int(int32_t x) { return 2 + x; } @@ -38,6 +43,10 @@ EXPORT uint8_t u8_id(uint8_t x) { return x; } +EXPORT uint8_t scalar_enum(enum cenum e) { + return (uint8_t)e; +} + // To test that functions not marked with EXPORT cannot be called by Miri. int32_t not_exported(void) { return 0; diff --git a/src/tools/miri/tests/pass-dep/concurrency/env-cleanup-data-race.rs b/src/tools/miri/tests/pass-dep/concurrency/env-cleanup-data-race.rs index 91cf24a944ad6..44e969578989b 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/env-cleanup-data-race.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/env-cleanup-data-race.rs @@ -8,7 +8,7 @@ fn main() { unsafe { thread::spawn(|| { // Access the environment in another thread without taking the env lock - let s = libc::getenv("MIRI_ENV_VAR_TEST\0".as_ptr().cast()); + let s = libc::getenv(c"MIRI_ENV_VAR_TEST".as_ptr()); if s.is_null() { panic!("null"); } diff --git a/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs b/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs index 9e5627c75a97a..70fa6c183c84a 100644 --- a/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs +++ b/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs @@ -7,8 +7,8 @@ mod utils; type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int; fn main() { - let name = "getentropy\0"; - let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize }; + let name = c"getentropy"; + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize }; // If the GC does not account for the extra_fn_ptr entry that this dlsym just added, this GC // run will delete our entry for the base addr of the function pointer we will transmute to, // and the call through the function pointer will report UB. diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index f5e9a56d7d039..9dc1af1be299f 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -54,29 +54,23 @@ fn main() { fn test_file_open_unix_allow_two_args() { let path = utils::prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]); + let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); - let mut name = path.into_os_string(); - name.push("\0"); - let name_ptr = name.as_bytes().as_ptr().cast::(); - let _fd = unsafe { libc::open(name_ptr, libc::O_RDONLY) }; + let _fd = unsafe { libc::open(name.as_ptr(), libc::O_RDONLY) }; } fn test_file_open_unix_needs_three_args() { let path = utils::prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]); + let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); - let mut name = path.into_os_string(); - name.push("\0"); - let name_ptr = name.as_bytes().as_ptr().cast::(); - let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT, 0o666) }; + let _fd = unsafe { libc::open(name.as_ptr(), libc::O_CREAT, 0o666) }; } fn test_file_open_unix_extra_third_arg() { let path = utils::prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]); + let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); - let mut name = path.into_os_string(); - name.push("\0"); - let name_ptr = name.as_bytes().as_ptr().cast::(); - let _fd = unsafe { libc::open(name_ptr, libc::O_RDONLY, 42) }; + let _fd = unsafe { libc::open(name.as_ptr(), libc::O_RDONLY, 42) }; } fn test_dup_stdout_stderr() { @@ -92,12 +86,10 @@ fn test_dup_stdout_stderr() { fn test_dup() { let bytes = b"dup and dup2"; let path = utils::prepare_with_content("miri_test_libc_dup.txt", bytes); + let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); - let mut name = path.into_os_string(); - name.push("\0"); - let name_ptr = name.as_bytes().as_ptr().cast::(); unsafe { - let fd = libc::open(name_ptr, libc::O_RDONLY); + let fd = libc::open(name.as_ptr(), libc::O_RDONLY); let new_fd = libc::dup(fd); let new_fd2 = libc::dup2(fd, 8); @@ -519,7 +511,7 @@ fn test_read_and_uninit() { { // We test that libc::read initializes its buffer. let path = utils::prepare_with_content("pass-libc-read-and-uninit.txt", &[1u8, 2, 3]); - let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap(); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); @@ -528,8 +520,8 @@ fn test_read_and_uninit() { let buf = buf.assume_init(); assert_eq!(buf, 1); assert_eq!(libc::close(fd), 0); + assert_eq!(libc::unlink(cpath.as_ptr()), 0); } - remove_file(&path).unwrap(); } { // We test that if we requested to read 4 bytes, but actually read 3 bytes, then @@ -567,17 +559,15 @@ fn test_nofollow_not_symlink() { #[cfg(target_os = "macos")] fn test_ioctl() { let path = utils::prepare_with_content("miri_test_libc_ioctl.txt", &[]); + let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); - let mut name = path.into_os_string(); - name.push("\0"); - let name_ptr = name.as_bytes().as_ptr().cast::(); unsafe { // 100 surely is an invalid FD. assert_eq!(libc::ioctl(100, libc::FIOCLEX), -1); let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); assert_eq!(errno, libc::EBADF); - let fd = libc::open(name_ptr, libc::O_RDONLY); + let fd = libc::open(name.as_ptr(), libc::O_RDONLY); assert_eq!(libc::ioctl(fd, libc::FIOCLEX), 0); } } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs index d1c0085b024a5..36ed470b353fe 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs @@ -63,15 +63,22 @@ fn test_sigrt() { } fn test_dlsym() { - let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"notasymbol\0".as_ptr().cast()) }; + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"notasymbol".as_ptr()) }; assert!(addr as usize == 0); - let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"isatty\0".as_ptr().cast()) }; + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"isatty".as_ptr()) }; assert!(addr as usize != 0); let isatty: extern "C" fn(i32) -> i32 = unsafe { transmute(addr) }; assert_eq!(isatty(999), 0); let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); assert_eq!(errno, libc::EBADF); + + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"environ".as_ptr()) }; + assert!(addr as usize != 0); + extern "C" { + static mut environ: *const *const u8; + } + assert!(addr as usize == &raw const environ as usize); } fn test_getuid() { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs index e3c14e60b25e6..8dd00e60200a5 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -38,6 +38,9 @@ fn main() { test_getsockname_ipv4_random_port(); test_getsockname_ipv4_unbound(); test_getsockname_ipv6(); + + test_getpeername_ipv4(); + test_getpeername_ipv6(); } fn test_socket_close() { @@ -183,7 +186,6 @@ fn test_listen() { /// - Connecting when the server is already accepting /// - Accepting when there is already an incoming connection fn test_accept_connect() { - // Create a new non-blocking server socket. let server_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; let client_sockfd = @@ -379,6 +381,132 @@ fn test_getsockname_ipv6() { assert_eq!(addr.sin6_addr.s6_addr, sock_addr.sin6_addr.s6_addr); } +/// Test the `getpeername` syscall on an IPv4 socket. +/// For a connected socket, the `getpeername` syscall should +/// return the same address as the socket was connected to. +fn test_getpeername_ipv4() { + let server_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let addr = net::ipv4_sock_addr(net::IPV4_LOCALHOST, 0); + unsafe { + errno_check(libc::bind( + server_sockfd, + (&addr as *const libc::sockaddr_in).cast::(), + size_of::() as libc::socklen_t, + )); + } + + unsafe { + errno_check(libc::listen(server_sockfd, 16)); + } + + // Retrieve actual listener address because we used a randomized port. + let (_, server_addr) = + sockname(|storage, len| unsafe { libc::getsockname(server_sockfd, storage, len) }).unwrap(); + + let LibcSocketAddr::V4(addr) = server_addr else { + // We bound an IPv4 address so we also expect + // an IPv4 address to be returned. + panic!() + }; + + // Spawn the server thread. + let server_thread = thread::spawn(move || { + let (_peerfd, _peer_addr) = + sockname(|storage, len| unsafe { libc::accept(server_sockfd, storage, len) }).unwrap(); + }); + + // Test connecting to an already accepting server. + unsafe { + errno_check(libc::connect( + client_sockfd, + (&addr as *const libc::sockaddr_in).cast::(), + size_of::() as libc::socklen_t, + )); + } + + let (_, peer_addr) = + sockname(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) }).unwrap(); + + let LibcSocketAddr::V4(peer_addr) = peer_addr else { + // We connected to an IPv4 address so we also expect + // an IPv4 address to be returned. + panic!() + }; + + assert_eq!(addr.sin_family, peer_addr.sin_family); + assert_eq!(addr.sin_port, peer_addr.sin_port); + assert_eq!(addr.sin_addr.s_addr, peer_addr.sin_addr.s_addr); + + server_thread.join().unwrap(); +} + +/// Test the `getpeername` syscall on an IPv6 socket. +/// For a connected socket, the `getpeername` syscall should +/// return the same address as the socket was connected to. +fn test_getpeername_ipv6() { + let server_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0)).unwrap() }; + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0)).unwrap() }; + let addr = net::ipv6_sock_addr(net::IPV6_LOCALHOST, 0); + unsafe { + errno_check(libc::bind( + server_sockfd, + (&addr as *const libc::sockaddr_in6).cast::(), + size_of::() as libc::socklen_t, + )); + } + + unsafe { + errno_check(libc::listen(server_sockfd, 16)); + } + + // Retrieve actual listener address because we used a randomized port. + let (_, server_addr) = + sockname(|storage, len| unsafe { libc::getsockname(server_sockfd, storage, len) }).unwrap(); + + let LibcSocketAddr::V6(addr) = server_addr else { + // We bound an IPv6 address so we also expect + // an IPv6 address to be returned. + panic!() + }; + + // Spawn the server thread. + let server_thread = thread::spawn(move || { + let (_peerfd, _peer_addr) = + sockname(|storage, len| unsafe { libc::accept(server_sockfd, storage, len) }).unwrap(); + }); + + // Test connecting to an already accepting server. + unsafe { + errno_check(libc::connect( + client_sockfd, + (&addr as *const libc::sockaddr_in6).cast::(), + size_of::() as libc::socklen_t, + )); + } + + let (_, peer_addr) = + sockname(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) }).unwrap(); + + let LibcSocketAddr::V6(peer_addr) = peer_addr else { + // We connected to an IPv6 address so we also expect + // an IPv6 address to be returned. + panic!() + }; + + assert_eq!(addr.sin6_family, peer_addr.sin6_family); + assert_eq!(addr.sin6_port, peer_addr.sin6_port); + assert_eq!(addr.sin6_flowinfo, peer_addr.sin6_flowinfo); + assert_eq!(addr.sin6_scope_id, peer_addr.sin6_scope_id); + assert_eq!(addr.sin6_addr.s6_addr, peer_addr.sin6_addr.s6_addr); + + server_thread.join().unwrap(); +} + /// Set a socket option. It's the caller's responsibility to ensure that `T` is /// associated with the given socket option. /// diff --git a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs index 91639c5023252..79cb551386a18 100644 --- a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs +++ b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs @@ -22,7 +22,7 @@ use windows_sys::Win32::Storage::FileSystem::{ FILE_ALLOCATION_INFO, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, FILE_BEGIN, FILE_CURRENT, FILE_END_OF_FILE_INFO, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, FileAllocationInfo, FileEndOfFileInfo, - FlushFileBuffers, GetFileInformationByHandle, OPEN_ALWAYS, OPEN_EXISTING, + FlushFileBuffers, GetFileInformationByHandle, MoveFileExW, OPEN_ALWAYS, OPEN_EXISTING, SetFileInformationByHandle, SetFilePointerEx, }; use windows_sys::Win32::System::IO::IO_STATUS_BLOCK; @@ -42,6 +42,7 @@ fn main() { test_set_file_info(); test_dup_handle(); test_flush_buffers(); + test_move_file(); } } @@ -376,6 +377,23 @@ unsafe fn test_flush_buffers() { } } +unsafe fn test_move_file() { + let tmp_dir = utils::tmp(); + + let temp = tmp_dir.join("test_move_file.txt"); + let temp_new = tmp_dir.join("test_move_file_new.txt"); + let mut file = fs::File::options().create(true).write(true).open(&temp).unwrap(); + file.write_all(b"Hello, World!\n").unwrap(); + + let from = to_wide_cstr(&temp); + let to = to_wide_cstr(&temp_new); + if MoveFileExW(from.as_ptr(), to.as_ptr(), 1) == 0 { + panic!("Failed to rename file from {} to {}", temp.display(), temp_new.display()); + } + + assert_eq!(fs::read_to_string(temp_new).unwrap(), "Hello, World!\n"); +} + fn to_wide_cstr(path: &Path) -> Vec { let mut raw_path = path.as_os_str().encode_wide().collect::>(); raw_path.extend([0, 0]); diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index 1e86c458ac21c..f81f62e176021 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -134,13 +134,19 @@ fn simd_ops_f16() { assert_eq!(simd_reduce_min(b), -4.0f16); assert_eq!( - simd_maximum_number_nsz(f16x2::from_array([0.0, f16::NAN]), f16x2::from_array([f16::NAN, 0.0])), + simd_maximum_number_nsz( + f16x2::from_array([0.0, f16::NAN]), + f16x2::from_array([f16::NAN, 0.0]) + ), f16x2::from_array([0.0, 0.0]) ); assert_eq!(simd_reduce_max(f16x2::from_array([0.0, f16::NAN])), 0.0f16); assert_eq!(simd_reduce_max(f16x2::from_array([f16::NAN, 0.0])), 0.0f16); assert_eq!( - simd_minimum_number_nsz(f16x2::from_array([0.0, f16::NAN]), f16x2::from_array([f16::NAN, 0.0])), + simd_minimum_number_nsz( + f16x2::from_array([0.0, f16::NAN]), + f16x2::from_array([f16::NAN, 0.0]) + ), f16x2::from_array([0.0, 0.0]) ); assert_eq!(simd_reduce_min(f16x2::from_array([0.0, f16::NAN])), 0.0f16); @@ -348,13 +354,19 @@ fn simd_ops_f128() { assert_eq!(simd_reduce_min(b), -4.0f128); assert_eq!( - simd_maximum_number_nsz(f128x2::from_array([0.0, f128::NAN]), f128x2::from_array([f128::NAN, 0.0])), + simd_maximum_number_nsz( + f128x2::from_array([0.0, f128::NAN]), + f128x2::from_array([f128::NAN, 0.0]) + ), f128x2::from_array([0.0, 0.0]) ); assert_eq!(simd_reduce_max(f128x2::from_array([0.0, f128::NAN])), 0.0f128); assert_eq!(simd_reduce_max(f128x2::from_array([f128::NAN, 0.0])), 0.0f128); assert_eq!( - simd_minimum_number_nsz(f128x2::from_array([0.0, f128::NAN]), f128x2::from_array([f128::NAN, 0.0])), + simd_minimum_number_nsz( + f128x2::from_array([0.0, f128::NAN]), + f128x2::from_array([f128::NAN, 0.0]) + ), f128x2::from_array([0.0, 0.0]) ); assert_eq!(simd_reduce_min(f128x2::from_array([0.0, f128::NAN])), 0.0f128); diff --git a/src/tools/miri/tests/pass/no_std_miri_start.rs b/src/tools/miri/tests/pass/miri_start_no_std.rs similarity index 100% rename from src/tools/miri/tests/pass/no_std_miri_start.rs rename to src/tools/miri/tests/pass/miri_start_no_std.rs diff --git a/src/tools/miri/tests/pass/no_std_miri_start.stdout b/src/tools/miri/tests/pass/miri_start_no_std.stdout similarity index 100% rename from src/tools/miri/tests/pass/no_std_miri_start.stdout rename to src/tools/miri/tests/pass/miri_start_no_std.stdout diff --git a/src/tools/miri/tests/pass/miri_start_with_std.rs b/src/tools/miri/tests/pass/miri_start_with_std.rs new file mode 100644 index 0000000000000..510b9d4196226 --- /dev/null +++ b/src/tools/miri/tests/pass/miri_start_with_std.rs @@ -0,0 +1,8 @@ +#![no_main] + +#[no_mangle] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _b = Box::new(0); + println!("hello, world!"); + 0 +} diff --git a/src/tools/miri/tests/pass/miri_start_with_std.stdout b/src/tools/miri/tests/pass/miri_start_with_std.stdout new file mode 100644 index 0000000000000..270c611ee72c5 --- /dev/null +++ b/src/tools/miri/tests/pass/miri_start_with_std.stdout @@ -0,0 +1 @@ +hello, world! diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs new file mode 100644 index 0000000000000..849f99ee36cce --- /dev/null +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs @@ -0,0 +1,61 @@ +// We're testing aarch64 CRC32 target specific features +//@only-target: aarch64 +//@compile-flags: -C target-feature=+crc + +use std::arch::aarch64::*; +use std::arch::is_aarch64_feature_detected; + +fn main() { + assert!(is_aarch64_feature_detected!("crc")); + + unsafe { + test_crc32_standard(); + test_crc32c_castagnoli(); + } +} + +#[target_feature(enable = "crc")] +unsafe fn test_crc32_standard() { + // __crc32b: 8-bit input + assert_eq!(__crc32b(0x00000000, 0x01), 0x77073096); + assert_eq!(__crc32b(0xffffffff, 0x61), 0x174841bc); + assert_eq!(__crc32b(0x2aa1e72b, 0x2a), 0x772d9171); + + // __crc32h: 16-bit input + assert_eq!(__crc32h(0x00000000, 0x0001), 0x191b3141); + assert_eq!(__crc32h(0xffffffff, 0x1234), 0xf6b56fbf); + assert_eq!(__crc32h(0x8ecec3b5, 0x022b), 0x03a1db7c); + + // __crc32w: 32-bit input + assert_eq!(__crc32w(0x00000000, 0x00000001), 0xb8bc6765); + assert_eq!(__crc32w(0xffffffff, 0x12345678), 0x5092782d); + assert_eq!(__crc32w(0xae2912c8, 0x00845fed), 0xc5690dd4); + + // __crc32d: 64-bit input + assert_eq!(__crc32d(0x00000000, 0x0000000000000001), 0xccaa009e); + assert_eq!(__crc32d(0xffffffff, 0x123456789abcdef0), 0xe6ddf8b5); + assert_eq!(__crc32d(0x0badeafe, 0xc0febeefdadafefe), 0x61a45fba); +} + +#[target_feature(enable = "crc")] +unsafe fn test_crc32c_castagnoli() { + // __crc32cb: 8-bit input + assert_eq!(__crc32cb(0x00000000, 0x01), 0xf26b8303); + assert_eq!(__crc32cb(0xffffffff, 0x61), 0x3e2fbccf); + assert_eq!(__crc32cb(0x2aa1e72b, 0x2a), 0xf24122e4); + + // __crc32ch: 16-bit input + assert_eq!(__crc32ch(0x00000000, 0x0001), 0x13a29877); + assert_eq!(__crc32ch(0xffffffff, 0x1234), 0xf13f4cea); + assert_eq!(__crc32ch(0x8ecec3b5, 0x022b), 0x013bb2fb); + + // __crc32cw: 32-bit input + assert_eq!(__crc32cw(0x00000000, 0x00000001), 0xdd45aab8); + assert_eq!(__crc32cw(0xffffffff, 0x12345678), 0x4dece20c); + assert_eq!(__crc32cw(0xae2912c8, 0x00845fed), 0xffae2ed1); + + // __crc32cd: 64-bit input + assert_eq!(__crc32cd(0x00000000, 0x0000000000000001), 0x493c7d27); + assert_eq!(__crc32cd(0xffffffff, 0x123456789abcdef0), 0xd95b664b); + assert_eq!(__crc32cd(0x0badeafe, 0xc0febeefdadafefe), 0x5b44f54f); +} diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs index 6d3f153e194f3..884f8eff41bdb 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs @@ -12,6 +12,8 @@ fn main() { unsafe { test_vpmaxq_u8(); test_tbl1_v16i8_basic(); + test_vpadd(); + test_vpaddl(); } } @@ -65,3 +67,93 @@ fn test_tbl1_v16i8_basic() { assert_eq!(&got2_arr[3..16], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12][..]); } } +#[target_feature(enable = "neon")] +unsafe fn test_vpadd() { + let a = vld1_s8([1, 2, 3, 4, 5, 6, 7, 8].as_ptr()); + let b = vld1_s8([9, 10, -1, 2, i8::MIN, i8::MIN, i8::MAX, i8::MAX].as_ptr()); + let e = + [3i8, 7, 11, 15, 19, -1 + 2, i8::MIN.wrapping_add(i8::MIN), i8::MAX.wrapping_add(i8::MAX)]; + let mut r = [0i8; 8]; + vst1_s8(r.as_mut_ptr(), vpadd_s8(a, b)); + assert_eq!(r, e); + + let a = vld1_s16([1, 2, 3, 4].as_ptr()); + let b = vld1_s16([-1, 2, i16::MAX, i16::MAX].as_ptr()); + let e = [3i16, 7, -1 + 2, i16::MAX.wrapping_add(i16::MAX)]; + let mut r = [0i16; 4]; + vst1_s16(r.as_mut_ptr(), vpadd_s16(a, b)); + assert_eq!(r, e); + + let a = vld1_s32([1, 2].as_ptr()); + let b = vld1_s32([i32::MAX, i32::MAX].as_ptr()); + let e = [3i32, i32::MAX.wrapping_add(i32::MAX)]; + let mut r = [0i32; 2]; + vst1_s32(r.as_mut_ptr(), vpadd_s32(a, b)); + assert_eq!(r, e); + + let a = vld1_u8([1, 2, 3, 4, 5, 6, 7, 8].as_ptr()); + let b = vld1_u8([9, 10, 11, 12, 13, 14, u8::MAX, u8::MAX].as_ptr()); + let e = [3u8, 7, 11, 15, 19, 23, 27, 254]; + let mut r = [0u8; 8]; + vst1_u8(r.as_mut_ptr(), vpadd_u8(a, b)); + assert_eq!(r, e); + + let a = vld1_u16([1, 2, 3, 4].as_ptr()); + let b = vld1_u16([5, 6, u16::MAX, u16::MAX].as_ptr()); + let e = [3u16, 7, 11, 65534]; + let mut r = [0u16; 4]; + vst1_u16(r.as_mut_ptr(), vpadd_u16(a, b)); + assert_eq!(r, e); + + let a = vld1_u32([1, 2].as_ptr()); + let b = vld1_u32([u32::MAX, u32::MAX].as_ptr()); + let e = [3u32, u32::MAX.wrapping_add(u32::MAX)]; + let mut r = [0u32; 2]; + vst1_u32(r.as_mut_ptr(), vpadd_u32(a, b)); + assert_eq!(r, e); +} + +#[target_feature(enable = "neon")] +unsafe fn test_vpaddl() { + let a = vld1_u8([1, 2, 3, 4, 5, 6, u8::MAX, u8::MAX].as_ptr()); + let e = [3u16, 7, 11, 510]; + let mut r = [0u16; 4]; + vst1_u16(r.as_mut_ptr(), vpaddl_u8(a)); + assert_eq!(r, e); + + let a = vld1q_u8([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, u8::MAX, u8::MAX].as_ptr()); + let e = [3u16, 7, 11, 15, 19, 23, 27, 510]; + let mut r = [0u16; 8]; + vst1q_u16(r.as_mut_ptr(), vpaddlq_u8(a)); + assert_eq!(r, e); + + let a = vld1_u16([1, 2, u16::MAX, u16::MAX].as_ptr()); + let e = [3u32, 131070]; + let mut r = [0u32; 2]; + vst1_u32(r.as_mut_ptr(), vpaddl_u16(a)); + assert_eq!(r, e); + + let a = vld1q_u16([1, 2, 3, 4, 5, 6, u16::MAX, u16::MAX].as_ptr()); + let e = [3u32, 7, 11, 131070]; + let mut r = [0u32; 4]; + vst1q_u32(r.as_mut_ptr(), vpaddlq_u16(a)); + assert_eq!(r, e); + + let a = vld1_u32([1, 2].as_ptr()); + let e = [3u64]; + let mut r = [0u64; 1]; + vst1_u64(r.as_mut_ptr(), vpaddl_u32(a)); + assert_eq!(r, e); + + let a = vld1_u32([u32::MAX, u32::MAX].as_ptr()); + let e = [8589934590]; + let mut r = [0u64; 1]; + vst1_u64(r.as_mut_ptr(), vpaddl_u32(a)); + assert_eq!(r, e); + + let a = vld1q_u32([1, 2, u32::MAX, u32::MAX].as_ptr()); + let e = [3u64, 8589934590]; + let mut r = [0u64; 2]; + vst1q_u64(r.as_mut_ptr(), vpaddlq_u32(a)); + assert_eq!(r, e); +} diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 50b5dbfba1cdd..e6c15c81d9fd4 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -30,9 +30,9 @@ fn main() { test_file_clone(); test_file_set_len(); test_file_sync(); + test_rename(); // Windows file handling is very incomplete. if cfg!(not(windows)) { - test_rename(); test_directory(); test_canonicalize(); #[cfg(unix)] diff --git a/src/tools/miri/tests/pass/shims/socket.rs b/src/tools/miri/tests/pass/shims/socket.rs index 2e63e00c67d90..852397a356916 100644 --- a/src/tools/miri/tests/pass/shims/socket.rs +++ b/src/tools/miri/tests/pass/shims/socket.rs @@ -8,6 +8,7 @@ fn main() { test_create_ipv4_listener(); test_create_ipv6_listener(); test_accept_and_connect(); + test_peer_addr(); } fn test_create_ipv4_listener() { @@ -34,3 +35,22 @@ fn test_accept_and_connect() { handle.join().unwrap(); } + +/// Test whether the [`TcpStream::peer_addr`] of a connected socket +/// is the same address as the one the stream was connected to. +fn test_peer_addr() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + // Get local address with randomized port to know where + // we need to connect to. + let address = listener.local_addr().unwrap(); + + let handle = thread::spawn(move || { + let (_stream, _addr) = listener.accept().unwrap(); + }); + + let stream = TcpStream::connect(address).unwrap(); + let peer_addr = stream.peer_addr().unwrap(); + assert_eq!(address, peer_addr); + + handle.join().unwrap(); +}