mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Rollup merge of #154582 - RalfJung:miri, r=RalfJung
miri subtree update Subtree update of `miri` to https://github.com/rust-lang/miri/commit/76621481ee89df1ba53dc32da539c0f31df202ef. Created using https://github.com/rust-lang/josh-sync. r? @ghost
This commit is contained in:
+41
-12
@@ -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"
|
||||
|
||||
+5
-5
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_write",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.2"
|
||||
name = "toml_datetime"
|
||||
version = "1.1.0+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.0+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.0+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -80,7 +80,7 @@ fn auto_actions() -> Result<()> {
|
||||
|
||||
// `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 @@ pub fn exec(self) -> Result<()> {
|
||||
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<String>) -> Result<()> {
|
||||
fn toolchain(new_commit: Option<String>, flags: Vec<String>) -> 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 {
|
||||
|
||||
@@ -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<String>,
|
||||
/// Flags that are passed through to `rustup-toolchain-install-master`.
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
flags: Vec<String>,
|
||||
@@ -157,8 +160,8 @@ fn add_remainder(&mut self, remainder: Vec<String>) -> Result<()> {
|
||||
| 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, .. } => {
|
||||
|
||||
@@ -1 +1 @@
|
||||
fd0c901b00ee1e08a250039cdb90258603497e20
|
||||
116458d0a5ae01cd517cabd2d1aee7f5457018ab
|
||||
|
||||
@@ -656,64 +656,10 @@ fn yield_active_thread(&mut self) {
|
||||
// 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<Duration> {
|
||||
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 @@ fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
|
||||
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".
|
||||
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html>
|
||||
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 @@ fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
|
||||
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<Duration>) -> 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<Duration>> {
|
||||
let this = self.eval_context_mut();
|
||||
let clock = &this.machine.monotonic_clock;
|
||||
|
||||
let mut min_wait_time = Option::<Duration>::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 @@ fn run_threads(&mut self) -> InterpResult<'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<Duration>) -> 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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,7 +569,7 @@ pub struct MiriMachine<'tcx> {
|
||||
pub(crate) user_relevant_crates: Vec<CrateNum>,
|
||||
|
||||
/// Mapping extern static names to their pointer.
|
||||
extern_statics: FxHashMap<Symbol, StrictPointer>,
|
||||
pub(crate) extern_statics: FxHashMap<Symbol, StrictPointer>,
|
||||
|
||||
/// The random number generator used for resolving non-determinism.
|
||||
/// Needs to be queried by ptr_to_int, hence needs interior mutability.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
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 @@ fn emulate_aarch64_intrinsic(
|
||||
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 @@ fn emulate_aarch64_intrinsic(
|
||||
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)
|
||||
|
||||
@@ -245,3 +245,51 @@ fn emulate_foreign_item_inner(
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -396,8 +396,9 @@ fn ty_to_ffitype(&self, layout: TyAndLayout<'tcx>) -> Result<FfiType, Ty<'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(),
|
||||
|
||||
@@ -633,6 +633,16 @@ fn emulate_foreign_item_inner(
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
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)?;
|
||||
}
|
||||
|
||||
@@ -215,8 +215,17 @@ fn read_umtx_time(&mut self, ut: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<U
|
||||
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)?;
|
||||
|
||||
@@ -364,6 +364,9 @@ fn open(
|
||||
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 @@ fn open(
|
||||
|
||||
// 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 @@ fn open(
|
||||
} 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 @@ fn open(
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ fn open(
|
||||
}
|
||||
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 @@ fn open(
|
||||
|
||||
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 @@ fn open(
|
||||
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.
|
||||
|
||||
@@ -620,6 +620,48 @@ fn getsockname(
|
||||
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::<Socket>() 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> {}
|
||||
|
||||
@@ -451,6 +451,16 @@ fn emulate_foreign_item_inner(
|
||||
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" => {
|
||||
|
||||
@@ -490,6 +490,36 @@ fn FlushFileBuffers(
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
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 @@ fn emulate_x86_sse42_intrinsic(
|
||||
// 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 {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 | | });
|
||||
| |______^
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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::<libc::c_char>();
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
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}");
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ struct Triple {
|
||||
}
|
||||
|
||||
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 @@ struct Triple {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 @@ fn test_stack_spill(
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
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.
|
||||
|
||||
@@ -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::<libc::c_char>();
|
||||
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::<libc::c_char>();
|
||||
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::<libc::c_char>();
|
||||
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::<libc::c_char>();
|
||||
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::<libc::c_char>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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::<libc::sockaddr>(),
|
||||
size_of::<libc::sockaddr_in>() 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::<libc::sockaddr>(),
|
||||
size_of::<libc::sockaddr_in>() 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::<libc::sockaddr>(),
|
||||
size_of::<libc::sockaddr_in6>() 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::<libc::sockaddr>(),
|
||||
size_of::<libc::sockaddr_in6>() 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.
|
||||
///
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
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<u16> {
|
||||
let mut raw_path = path.as_os_str().encode_wide().collect::<Vec<_>>();
|
||||
raw_path.extend([0, 0]);
|
||||
|
||||
@@ -134,13 +134,19 @@ macro_rules! assert_eq {
|
||||
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 @@ macro_rules! assert_eq {
|
||||
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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
hello, world!
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user