Auto merge of #134604 - RalfJung:miri-sync, r=RalfJung

Miri subtree update

r? `@ghost`
This commit is contained in:
bors 2024-12-21 09:21:42 +00:00
commit 54dcff104b
61 changed files with 1476 additions and 1001 deletions

View File

@ -375,16 +375,19 @@ to Miri failing to detect cases of undefined behavior in a program.
* `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak
memory effects.
* `-Zmiri-native-lib=<path to a shared object file>` is an experimental flag for providing support
for calling native functions from inside the interpreter via FFI. Functions not provided by that
file are still executed via the usual Miri shims.
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause Undefined Behavior in Miri itself!
And of course, Miri cannot do any checks on the actions taken by the native code.
Note that Miri has its own handling of file descriptors, so if you want to replace *some* functions
working on file descriptors, you will have to replace *all* of them, or the two kinds of
file descriptors will be mixed up.
This is **work in progress**; currently, only integer arguments and return values are
supported (and no, pointer/integer casts to work around this limitation will not work;
they will fail horribly). It also only works on Unix hosts for now.
for calling native functions from inside the interpreter via FFI. The flag is supported only on
Unix systems. Functions not provided by that file are still executed via the usual Miri shims.
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause Undefined Behavior in
Miri itself! And of course, Miri cannot do any checks on the actions taken by the native code.
Note that Miri has its own handling of file descriptors, so if you want to replace *some*
functions working on file descriptors, you will have to replace *all* of them, or the two kinds of
file descriptors will be mixed up. This is **work in progress**; currently, only integer and
pointers arguments and return values are supported and memory allocated by the native code cannot
be accessed from Rust (only the other way around). Native code must not spawn threads that keep
running in the background after the call has returned to Rust and that access Rust-allocated
memory. Finally, the flag is **unsound** in the sense that Miri stops tracking details such as
initialization and provenance on memory shared with native code, so it is easily possible to write
code that has UB which is missed by Miri.
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
This can be used to find which parts of your program are executing slowly under Miri.
The profile is written out to a file inside a directory called `<name>`, and can be processed

View File

@ -1,35 +1,35 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "addr2line"
version = "0.17.0"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "backtrace"
version = "0.3.65"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]]
@ -39,15 +39,6 @@ dependencies = [
"backtrace",
]
[[package]]
name = "cc"
version = "1.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -56,48 +47,106 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "gimli"
version = "0.26.1"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "libc"
version = "0.2.126"
version = "0.2.168"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
[[package]]
name = "memchr"
version = "2.5.0"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.5.3"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [
"adler",
"adler2",
]
[[package]]
name = "object"
version = "0.28.4"
version = "0.36.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
dependencies = [
"memchr",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "shlex"
version = "1.3.0"
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View File

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "cargo-miri-test"
@ -12,48 +12,54 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.2"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "proc-macro2"
version = "1.0.39"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.18"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.10"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.137"
version = "1.0.216"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.137"
version = "1.0.216"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
dependencies = [
"proc-macro2",
"quote",
@ -62,20 +68,21 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.81"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.96"
version = "2.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
dependencies = [
"proc-macro2",
"quote",
@ -84,6 +91,6 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.0"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"

View File

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "cargo-miri-test"
@ -12,48 +12,54 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.2"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "proc-macro2"
version = "1.0.39"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.18"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.10"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.137"
version = "1.0.216"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.137"
version = "1.0.216"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
dependencies = [
"proc-macro2",
"quote",
@ -62,20 +68,21 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.81"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.96"
version = "2.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
dependencies = [
"proc-macro2",
"quote",
@ -84,6 +91,6 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.0"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"

View File

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "unicode"
@ -11,6 +11,6 @@ dependencies = [
[[package]]
name = "unicode-xid"
version = "0.2.3"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"

View File

@ -18,7 +18,7 @@ export RUSTFLAGS="-D warnings"
export CARGO_INCREMENTAL=0
export CARGO_EXTRA_FLAGS="--locked"
# Determine configuration for installed build (used by test-cargo-miri).
# Determine configuration for installed build (used by test-cargo-miri and `./miri bench`).
echo "Installing release version of Miri"
time ./miri install
@ -73,7 +73,7 @@ function run_tests {
fi
if [ -n "${TEST_BENCH-}" ]; then
# Check that the benchmarks build and run, but only once.
time HYPERFINE="hyperfine -w0 -r1" ./miri bench $TARGET_FLAG
time HYPERFINE="hyperfine -w0 -r1 --show-output" ./miri bench $TARGET_FLAG --no-install
fi
# Smoke-test `./miri run --dep`.
./miri run $TARGET_FLAG --dep tests/pass-dep/getrandom.rs

View File

@ -2,6 +2,55 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "anstream"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
"windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
version = "1.0.80"
@ -20,6 +69,52 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "colorchoice"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "directories"
version = "5.0.1"
@ -80,6 +175,12 @@ dependencies = [
"wasi",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "home"
version = "0.5.9"
@ -89,6 +190,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.11.0"
@ -137,6 +244,7 @@ name = "miri-script"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"directories",
"dunce",
"itertools",
@ -278,6 +386,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.50"
@ -328,6 +442,12 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "walkdir"
version = "2.4.0"
@ -362,7 +482,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]

View File

@ -25,3 +25,4 @@ dunce = "1.0.4"
directories = "5"
serde_json = "1"
tempfile = "3.13.0"
clap = { version = "4.5.21", features = ["derive"] }

View File

@ -1,135 +0,0 @@
use std::{env, iter};
use anyhow::{Result, bail};
pub struct Args {
args: iter::Peekable<env::Args>,
/// Set to `true` once we saw a `--`.
terminated: bool,
}
impl Args {
pub fn new() -> Self {
let mut args = Args { args: env::args().peekable(), terminated: false };
args.args.next().unwrap(); // skip program name
args
}
/// Get the next argument without any interpretation.
pub fn next_raw(&mut self) -> Option<String> {
self.args.next()
}
/// Consume a `-$f` flag if present.
pub fn get_short_flag(&mut self, flag: char) -> Result<bool> {
if self.terminated {
return Ok(false);
}
if let Some(next) = self.args.peek() {
if let Some(next) = next.strip_prefix("-") {
if let Some(next) = next.strip_prefix(flag) {
if next.is_empty() {
self.args.next().unwrap(); // consume this argument
return Ok(true);
} else {
bail!("`-{flag}` followed by value");
}
}
}
}
Ok(false)
}
/// Consume a `--$name` flag if present.
pub fn get_long_flag(&mut self, name: &str) -> Result<bool> {
if self.terminated {
return Ok(false);
}
if let Some(next) = self.args.peek() {
if let Some(next) = next.strip_prefix("--") {
if next == name {
self.args.next().unwrap(); // consume this argument
return Ok(true);
}
}
}
Ok(false)
}
/// Consume a `--$name val` or `--$name=val` option if present.
pub fn get_long_opt(&mut self, name: &str) -> Result<Option<String>> {
assert!(!name.is_empty());
if self.terminated {
return Ok(None);
}
let Some(next) = self.args.peek() else { return Ok(None) };
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
// Starts with `--flag`.
Ok(if let Some(val) = next.strip_prefix("=") {
// `--flag=val` form
let val = val.into();
self.args.next().unwrap(); // consume this argument
Some(val)
} else if next.is_empty() {
// `--flag val` form
self.args.next().unwrap(); // consume this argument
let Some(val) = self.args.next() else { bail!("`--{name}` not followed by value") };
Some(val)
} else {
// Some unrelated flag, like `--flag-more` or so.
None
})
}
/// Consume a `--$name=val` or `--$name` option if present; the latter
/// produces a default value. (`--$name val` is *not* accepted for this form
/// of argument, it understands `val` already as the next argument!)
pub fn get_long_opt_with_default(
&mut self,
name: &str,
default: &str,
) -> Result<Option<String>> {
assert!(!name.is_empty());
if self.terminated {
return Ok(None);
}
let Some(next) = self.args.peek() else { return Ok(None) };
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
// Starts with `--flag`.
Ok(if let Some(val) = next.strip_prefix("=") {
// `--flag=val` form
let val = val.into();
self.args.next().unwrap(); // consume this argument
Some(val)
} else if next.is_empty() {
// `--flag` form
self.args.next().unwrap(); // consume this argument
Some(default.into())
} else {
// Some unrelated flag, like `--flag-more` or so.
None
})
}
/// Returns the next free argument or uninterpreted flag, or `None` if there are no more
/// arguments left. `--` is returned as well, but it is interpreted in the sense that no more
/// flags will be parsed after this.
pub fn get_other(&mut self) -> Option<String> {
if self.terminated {
return self.args.next();
}
let next = self.args.next()?;
if next == "--" {
self.terminated = true; // don't parse any more flags
// This is where our parser is special, we do yield the `--`.
}
Some(next)
}
/// Return the rest of the aguments entirely unparsed.
pub fn remainder(self) -> Vec<String> {
self.args.collect()
}
}

View File

@ -179,7 +179,8 @@ impl Command {
Command::Doc { flags } => Self::doc(flags),
Command::Fmt { flags } => Self::fmt(flags),
Command::Clippy { flags } => Self::clippy(flags),
Command::Bench { target, benches } => Self::bench(target, benches),
Command::Bench { target, no_install, benches } =>
Self::bench(target, no_install, benches),
Command::Toolchain { flags } => Self::toolchain(flags),
Command::RustcPull { commit } => Self::rustc_pull(commit.clone()),
Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch),
@ -378,7 +379,7 @@ impl Command {
Ok(())
}
fn bench(target: Option<String>, benches: Vec<String>) -> Result<()> {
fn bench(target: Option<String>, no_install: bool, benches: Vec<String>) -> Result<()> {
// The hyperfine to use
let hyperfine = env::var("HYPERFINE");
let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
@ -386,8 +387,10 @@ impl Command {
let Some((program_name, args)) = hyperfine.split_first() else {
bail!("expected HYPERFINE environment variable to be non-empty");
};
// Make sure we have an up-to-date Miri installed and selected the right toolchain.
Self::install(vec![])?;
if !no_install {
// Make sure we have an up-to-date Miri installed and selected the right toolchain.
Self::install(vec![])?;
}
let sh = Shell::new()?;
sh.change_dir(miri_dir()?);
@ -409,6 +412,7 @@ impl Command {
OsString::new()
};
let target_flag = &target_flag;
let toolchain = active_toolchain()?;
// Run the requested benchmarks
for bench in benches {
let current_bench = path!(benches_dir / bench / "Cargo.toml");
@ -416,7 +420,7 @@ impl Command {
// That seems to make Windows CI happy.
cmd!(
sh,
"{program_name} {args...} 'cargo miri run '{target_flag}' --manifest-path \"'{current_bench}'\"'"
"{program_name} {args...} 'cargo +'{toolchain}' miri run '{target_flag}' --manifest-path \"'{current_bench}'\"'"
)
.run()?;
}

View File

@ -1,6 +1,5 @@
#![allow(clippy::needless_question_mark)]
mod args;
mod commands;
mod coverage;
mod util;
@ -8,250 +7,191 @@ mod util;
use std::ops::Range;
use anyhow::{Context, Result, anyhow, bail};
use clap::{Parser, Subcommand};
#[derive(Clone, Debug)]
/// Parses a seed range
///
/// This function is used for the `--many-seeds` flag. It expects the range in the form
/// `<from>..<to>`. `<from>` is inclusive, `<to>` is exclusive. `<from>` can be omitted,
/// in which case it is assumed to be `0`.
fn parse_range(val: &str) -> anyhow::Result<Range<u32>> {
let (from, to) = val
.split_once("..")
.ok_or_else(|| anyhow!("invalid format for `--many-seeds`: expected `from..to`"))?;
let from: u32 = if from.is_empty() {
0
} else {
from.parse().context("invalid `from` in `--many-seeds=from..to")?
};
let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?;
Ok(from..to)
}
#[derive(Clone, Debug, Subcommand)]
pub enum Command {
/// Installs the miri driver and cargo-miri.
/// Installs the miri driver and cargo-miri to the sysroot of the active toolchain.
///
/// Sets up the rpath such that the installed binary should work in any
/// working directory. Note that the binaries are placed in the `miri` toolchain
/// sysroot, to prevent conflicts with other toolchains.
/// working directory.
Install {
/// Flags that are passed through to `cargo install`.
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
flags: Vec<String>,
},
/// Just build miri.
/// Build Miri.
Build {
/// Flags that are passed through to `cargo build`.
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
flags: Vec<String>,
},
/// Just check miri.
/// Check Miri.
Check {
/// Flags that are passed through to `cargo check`.
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
flags: Vec<String>,
},
/// Build miri, set up a sysroot and then run the test suite.
/// Check Miri with Clippy.
Clippy {
/// Flags that are passed through to `cargo clippy`.
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
flags: Vec<String>,
},
/// Run the Miri test suite.
Test {
/// Update stdout/stderr reference files.
#[arg(long)]
bless: bool,
/// The cross-interpretation target.
/// If none then the host is the target.
#[arg(long)]
target: Option<String>,
/// Produce coverage report if set.
/// Produce coverage report.
#[arg(long)]
coverage: bool,
/// Flags that are passed through to the test harness.
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
flags: Vec<String>,
},
/// Build miri, set up a sysroot and then run the driver with the given <flags>.
/// (Also respects MIRIFLAGS environment variable.)
/// Run the Miri driver.
///
/// Also respects MIRIFLAGS environment variable.
Run {
/// Build the program with the dependencies declared in `test_dependencies/Cargo.toml`.
#[arg(long)]
dep: bool,
/// Show build progress.
#[arg(long, short)]
verbose: bool,
/// Run the driver with the seeds in the given range (`..to` or `from..to`, default: `0..64`).
#[arg(long, value_parser = parse_range)]
many_seeds: Option<Range<u32>>,
/// The cross-interpretation target.
#[arg(long)]
target: Option<String>,
/// The Rust edition.
#[arg(long)]
edition: Option<String>,
/// Flags that are passed through to `miri`.
///
/// The flags set in `MIRIFLAGS` are added in front of these flags.
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
flags: Vec<String>,
},
/// Build documentation
/// Build documentation.
Doc {
/// Flags that are passed through to `cargo doc`.
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
flags: Vec<String>,
},
/// Format all sources and tests.
Fmt {
/// Flags that are passed through to `rustfmt`.
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
flags: Vec<String>,
},
/// Runs clippy on all sources.
Clippy {
/// Flags that are passed through to `cargo clippy`.
flags: Vec<String>,
},
/// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
/// Runs the benchmarks from bench-cargo-miri in hyperfine.
///
/// hyperfine needs to be installed.
Bench {
#[arg(long)]
target: Option<String>,
/// List of benchmarks to run. By default all benchmarks are run.
/// When `true`, skip the `./miri install` step.
#[arg(long)]
no_install: bool,
/// List of benchmarks to run (default: run all benchmarks).
benches: Vec<String>,
},
/// Update and activate the rustup toolchain 'miri' to the commit given in the
/// `rust-version` file.
/// `rustup-toolchain-install-master` must be installed for this to work. Any extra
/// flags are passed to `rustup-toolchain-install-master`.
Toolchain { flags: Vec<String> },
/// Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
/// rustc commit. The fetched commit is stored in the `rust-version` file, so the
/// next `./miri toolchain` will install the rustc that just got pulled.
RustcPull { commit: Option<String> },
/// Push Miri changes back to the rustc repo. This will pull a copy of the rustc
/// history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
/// clone of the rustc repo.
RustcPush { github_user: String, branch: String },
/// Update and activate the rustup toolchain 'miri'.
///
/// 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 {
/// Flags that are passed through to `rustup-toolchain-install-master`.
flags: Vec<String>,
},
/// Pull and merge Miri changes from the rustc repo.
///
/// The fetched commit is stored in the `rust-version` file, so the next `./miri toolchain` will
/// install the rustc that just got pulled.
RustcPull {
/// The commit to fetch (default: latest rustc commit).
commit: Option<String>,
},
/// Push Miri changes back to the rustc repo.
///
/// This will pull a copy of the rustc history into the Miri repo, unless you set the RUSTC_GIT
/// env var to an existing clone of the rustc repo.
RustcPush {
/// The Github user that owns the rustc fork to which we should push.
github_user: String,
/// The branch to push to.
#[arg(default_value = "miri-sync")]
branch: String,
},
}
const HELP: &str = r#" COMMANDS
impl Command {
fn add_remainder(&mut self, remainder: Vec<String>) -> Result<()> {
if remainder.is_empty() {
return Ok(());
}
./miri build <flags>:
Just build miri. <flags> are passed to `cargo build`.
match self {
Self::Install { flags }
| Self::Build { flags }
| Self::Check { flags }
| Self::Doc { flags }
| Self::Fmt { flags }
| Self::Toolchain { flags }
| Self::Clippy { flags }
| Self::Run { flags, .. }
| Self::Test { flags, .. } => {
flags.extend(remainder);
Ok(())
}
Self::Bench { .. } | Self::RustcPull { .. } | Self::RustcPush { .. } =>
bail!("unexpected \"--\" found in arguments"),
}
}
}
./miri check <flags>:
Just check miri. <flags> are passed to `cargo check`.
./miri test [--bless] [--target <target>] <flags>:
Build miri, set up a sysroot and then run the test suite.
<flags> are passed to the test harness.
./miri run [--dep] [-v|--verbose] [--many-seeds|--many-seeds=..to|--many-seeds=from..to] <flags>:
Build miri, set up a sysroot and then run the driver with the given <flags>.
(Also respects MIRIFLAGS environment variable.)
If `--many-seeds` is present, Miri is run many times in parallel with different seeds.
The range defaults to `0..64`.
./miri fmt <flags>:
Format all sources and tests. <flags> are passed to `rustfmt`.
./miri clippy <flags>:
Runs clippy on all sources. <flags> are passed to `cargo clippy`.
./miri cargo <flags>:
Runs just `cargo <flags>` with the Miri-specific environment variables.
Mainly meant to be invoked by rust-analyzer.
./miri install <flags>:
Installs the miri driver and cargo-miri. <flags> are passed to `cargo
install`. Sets up the rpath such that the installed binary should work in any
working directory. Note that the binaries are placed in the `miri` toolchain
sysroot, to prevent conflicts with other toolchains.
./miri bench [--target <target>] <benches>:
Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
<benches> can explicitly list the benchmarks to run; by default, all of them are run.
./miri toolchain <flags>:
Update and activate the rustup toolchain 'miri' to the commit given in the
`rust-version` file.
`rustup-toolchain-install-master` must be installed for this to work. Any extra
flags are passed to `rustup-toolchain-install-master`.
./miri rustc-pull <commit>:
Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
rustc commit. The fetched commit is stored in the `rust-version` file, so the
next `./miri toolchain` will install the rustc that just got pulled.
./miri rustc-push <github user> [<branch>]:
Push Miri changes back to the rustc repo. This will pull a copy of the rustc
history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
clone of the rustc repo. The branch defaults to `miri-sync`.
ENVIRONMENT VARIABLES
MIRI_SYSROOT:
If already set, the "sysroot setup" step is skipped.
CARGO_EXTRA_FLAGS:
Pass extra flags to all cargo invocations. (Ignored by `./miri cargo`.)"#;
#[derive(Parser)]
#[command(after_help = "Environment variables:
MIRI_SYSROOT: If already set, the \"sysroot setup\" step is skipped
CARGO_EXTRA_FLAGS: Pass extra flags to all cargo invocations")]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}
fn main() -> Result<()> {
// We are hand-rolling our own argument parser, since `clap` can't express what we need
// (https://github.com/clap-rs/clap/issues/5055).
let mut args = args::Args::new();
let command = match args.next_raw().as_deref() {
Some("build") => Command::Build { flags: args.remainder() },
Some("check") => Command::Check { flags: args.remainder() },
Some("doc") => Command::Doc { flags: args.remainder() },
Some("test") => {
let mut target = None;
let mut bless = false;
let mut flags = Vec::new();
let mut coverage = false;
loop {
if args.get_long_flag("bless")? {
bless = true;
} else if args.get_long_flag("coverage")? {
coverage = true;
} else if let Some(val) = args.get_long_opt("target")? {
target = Some(val);
} else if let Some(flag) = args.get_other() {
flags.push(flag);
} else {
break;
}
}
Command::Test { bless, flags, target, coverage }
}
Some("run") => {
let mut dep = false;
let mut verbose = false;
let mut many_seeds = None;
let mut target = None;
let mut edition = None;
let mut flags = Vec::new();
loop {
if args.get_long_flag("dep")? {
dep = true;
} else if args.get_long_flag("verbose")? || args.get_short_flag('v')? {
verbose = true;
} else if let Some(val) = args.get_long_opt_with_default("many-seeds", "0..64")? {
let (from, to) = val.split_once("..").ok_or_else(|| {
anyhow!("invalid format for `--many-seeds`: expected `from..to`")
})?;
let from: u32 = if from.is_empty() {
0
} else {
from.parse().context("invalid `from` in `--many-seeds=from..to")?
};
let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?;
many_seeds = Some(from..to);
} else if let Some(val) = args.get_long_opt("target")? {
target = Some(val);
} else if let Some(val) = args.get_long_opt("edition")? {
edition = Some(val);
} else if let Some(flag) = args.get_other() {
flags.push(flag);
} else {
break;
}
}
Command::Run { dep, verbose, many_seeds, target, edition, flags }
}
Some("fmt") => Command::Fmt { flags: args.remainder() },
Some("clippy") => Command::Clippy { flags: args.remainder() },
Some("install") => Command::Install { flags: args.remainder() },
Some("bench") => {
let mut target = None;
let mut benches = Vec::new();
loop {
if let Some(val) = args.get_long_opt("target")? {
target = Some(val);
} else if let Some(flag) = args.get_other() {
benches.push(flag);
} else {
break;
}
}
Command::Bench { target, benches }
}
Some("toolchain") => Command::Toolchain { flags: args.remainder() },
Some("rustc-pull") => {
let commit = args.next_raw();
if args.next_raw().is_some() {
bail!("Too many arguments for `./miri rustc-pull`");
}
Command::RustcPull { commit }
}
Some("rustc-push") => {
let github_user = args.next_raw().ok_or_else(|| {
anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`")
})?;
let branch = args.next_raw().unwrap_or_else(|| "miri-sync".into());
if args.next_raw().is_some() {
bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`");
}
Command::RustcPush { github_user, branch }
}
_ => {
eprintln!("Unknown or missing command. Usage:\n\n{HELP}");
std::process::exit(1);
}
};
// Split the arguments into the part before the `--` and the part after.
// The `--` itself ends up in the second part.
let miri_args: Vec<_> = std::env::args().take_while(|x| *x != "--").collect();
let remainder: Vec<_> = std::env::args().skip_while(|x| *x != "--").collect();
let args = Cli::parse_from(miri_args);
let mut command = args.command;
command.add_remainder(remainder)?;
command.exec()?;
Ok(())
}

View File

@ -1 +1 @@
728f2daab42ba8f1b3d5caab62495798d1eabfa1
13170cd787cb733ed24842ee825bcbd98dc01476

View File

@ -237,6 +237,10 @@ impl Permission {
pub fn is_active(&self) -> bool {
self.inner == Active
}
/// Check if `self` is the never-allow-writes-again state of a pointer (is `Frozen`).
pub fn is_frozen(&self) -> bool {
self.inner == Frozen
}
/// Default initial permission of the root of a new tree at inbounds positions.
/// Must *only* be used for the root, this is not in general an "initial" permission!

View File

@ -153,8 +153,31 @@ impl LocationState {
) -> ContinueTraversal {
if rel_pos.is_foreign() {
let happening_now = IdempotentForeignAccess::from_foreign(access_kind);
let new_access_noop =
let mut new_access_noop =
self.idempotent_foreign_access.can_skip_foreign_access(happening_now);
if self.permission.is_disabled() {
// A foreign access to a `Disabled` tag will have almost no observable effect.
// It's a theorem that `Disabled` node have no protected initialized children,
// and so this foreign access will never trigger any protector.
// (Intuition: You're either protected initialized, and thus can't become Disabled
// or you're already Disabled protected, but not initialized, and then can't
// become initialized since that requires a child access, which Disabled blocks.)
// Further, the children will never be able to read or write again, since they
// have a `Disabled` parent. So this only affects diagnostics, such that the
// blocking write will still be identified directly, just at a different tag.
new_access_noop = true;
}
if self.permission.is_frozen() && access_kind == AccessKind::Read {
// A foreign read to a `Frozen` tag will have almost no observable effect.
// It's a theorem that `Frozen` nodes have no active children, so all children
// already survive foreign reads. Foreign reads in general have almost no
// effect, the only further thing they could do is make protected `Reserved`
// nodes become conflicted, i.e. make them reject child writes for the further
// duration of their protector. But such a child write is already rejected
// because this node is frozen. So this only affects diagnostics, but the
// blocking read will still be identified directly, just at a different tag.
new_access_noop = true;
}
if new_access_noop {
// Abort traversal if the new access is indeed guaranteed
// to be noop.

View File

@ -159,6 +159,8 @@ pub enum BlockReason {
Epoll,
/// Blocked on eventfd.
Eventfd,
/// Blocked on unnamed_socket.
UnnamedSocket,
}
/// The state of a thread.

View File

@ -126,6 +126,7 @@ pub enum NonHaltingDiagnostic {
Int2Ptr {
details: bool,
},
NativeCallSharedMem,
WeakMemoryOutdatedLoad {
ptr: Pointer,
},
@ -602,6 +603,8 @@ impl<'tcx> MiriMachine<'tcx> {
RejectedIsolatedOp(_) =>
("operation rejected by isolation".to_string(), DiagLevel::Warning),
Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
NativeCallSharedMem =>
("sharing memory with a native function".to_string(), DiagLevel::Warning),
ExternTypeReborrow =>
("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
CreatedPointerTag(..)
@ -637,6 +640,7 @@ impl<'tcx> MiriMachine<'tcx> {
ProgressReport { .. } =>
format!("progress report: current operation being executed is here"),
Int2Ptr { .. } => format!("integer-to-pointer cast"),
NativeCallSharedMem => format!("sharing memory with a native function called via FFI"),
WeakMemoryOutdatedLoad { ptr } =>
format!("weak memory emulation: outdated value returned from load at {ptr}"),
ExternTypeReborrow =>
@ -679,7 +683,29 @@ impl<'tcx> MiriMachine<'tcx> {
}
v
}
NativeCallSharedMem => {
vec![
note!(
"when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory"
),
note!(
"in particular, Miri assumes that the native call initializes all memory it has access to"
),
note!(
"Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory"
),
note!(
"what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free"
),
]
}
ExternTypeReborrow => {
assert!(self.borrow_tracker.as_ref().is_some_and(|b| {
matches!(
b.borrow().borrow_tracker_method(),
BorrowTrackerMethod::StackedBorrows
)
}));
vec![
note!(
"`extern type` are not compatible with the Stacked Borrows aliasing model implemented by Miri; Miri may miss bugs in this code"

View File

@ -310,18 +310,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.layout_of(array_ty).unwrap()
}
/// Project to the given *named* field (which must be a struct or union type).
fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
&self,
base: &P,
name: &str,
) -> InterpResult<'tcx, Option<P>> {
let this = self.eval_context_ref();
let adt = base.layout().ty.ty_adt_def().unwrap();
for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() {
if field.name.as_str() == name {
return interp_ok(Some(this.project_field(base, idx)?));
}
}
interp_ok(None)
}
/// Project to the given *named* field (which must be a struct or union type).
fn project_field_named<P: Projectable<'tcx, Provenance>>(
&self,
base: &P,
name: &str,
) -> InterpResult<'tcx, P> {
let this = self.eval_context_ref();
let adt = base.layout().ty.ty_adt_def().unwrap();
for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() {
if field.name.as_str() == name {
return this.project_field(base, idx);
}
if let Some(field) = self.try_project_field_named(base, name)? {
return interp_ok(field);
}
bug!("No field named {} in type {}", name, base.layout().ty);
}
@ -332,13 +344,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
base: &P,
name: &str,
) -> bool {
let adt = base.layout().ty.ty_adt_def().unwrap();
for field in adt.non_enum_variant().fields.iter() {
if field.name.as_str() == name {
return true;
}
}
false
self.try_project_field_named(base, name).unwrap().is_some()
}
/// Write an int of the appropriate size to `dest`. The target type may be signed or unsigned,
@ -921,7 +927,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if fn_abi.conv != exp_abi {
throw_ub_format!(
"calling a function with ABI {:?} using caller ABI {:?}",
exp_abi, fn_abi.conv);
exp_abi,
fn_abi.conv
);
}
interp_ok(())
}

View File

@ -13,7 +13,6 @@ use rand::{Rng, SeedableRng};
use rustc_abi::{Align, ExternAbi, Size};
use rustc_attr_parsing::InlineAttr;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_target::callconv::FnAbi;
#[allow(unused)]
use rustc_data_structures::static_assert_size;
use rustc_middle::mir;
@ -25,6 +24,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_session::config::InliningThreshold;
use rustc_span::def_id::{CrateNum, DefId};
use rustc_span::{Span, SpanData, Symbol};
use rustc_target::callconv::FnAbi;
use crate::concurrency::cpu_affinity::{self, CpuAffinityMask};
use crate::concurrency::data_race::{self, NaReadType, NaWriteType};

View File

@ -9,12 +9,11 @@ use rustc_ast::expand::allocator::alloc_error_handler_name;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::CrateNum;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::{mir, ty};
use rustc_middle::ty::Ty;
use rustc_middle::{mir, ty};
use rustc_span::Symbol;
use rustc_target::callconv::{Conv, FnAbi};
use self::helpers::{ToHost, ToSoft};
use super::alloc::EvalContextExt as _;
use super::backtrace::EvalContextExt as _;
@ -279,7 +278,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// Miri-specific extern functions
"miri_start_unwind" => {
let [payload] = this.check_shim(abi, Conv::Rust, link_name, args)?;
let [payload] = this.check_shim(abi, Conv::Rust, link_name, args)?;
this.handle_miri_start_unwind(payload)?;
return interp_ok(EmulateItemResult::NeedsUnwind);
}
@ -288,7 +287,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.run_provenance_gc();
}
"miri_get_alloc_id" => {
let [ptr] = this.check_shim(abi, Conv::Rust, link_name, args)?;
let [ptr] = this.check_shim(abi, Conv::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
err_machine_stop!(TerminationInfo::Abort(format!(
@ -298,7 +297,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
}
"miri_print_borrow_state" => {
let [id, show_unnamed] = this.check_shim(abi, Conv::Rust, link_name, args)?;
let [id, show_unnamed] = this.check_shim(abi, Conv::Rust, link_name, args)?;
let id = this.read_scalar(id)?.to_u64()?;
let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
if let Some(id) = std::num::NonZero::new(id).map(AllocId)
@ -312,8 +311,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"miri_pointer_name" => {
// This associates a name to a tag. Very useful for debugging, and also makes
// tests more strict.
let [ptr, nth_parent, name] =
this.check_shim(abi, Conv::Rust, link_name, args)?;
let [ptr, nth_parent, name] = this.check_shim(abi, Conv::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
let name = this.read_immediate(name)?;
@ -337,8 +335,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.machine.static_roots.push(alloc_id);
}
"miri_host_to_target_path" => {
let [ptr, out, out_size] =
this.check_shim(abi, Conv::Rust, link_name, args)?;
let [ptr, out, out_size] = this.check_shim(abi, Conv::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let out = this.read_pointer(out)?;
let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
@ -429,13 +426,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Aborting the process.
"exit" => {
let [code] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [code] = this.check_shim(abi, Conv::C, link_name, args)?;
let code = this.read_scalar(code)?.to_i32()?;
throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
}
"abort" => {
let [] = this.check_shim(abi, Conv::C , link_name, args)?;
let [] = this.check_shim(abi, Conv::C, link_name, args)?;
throw_machine_stop!(TerminationInfo::Abort(
"the program aborted execution".to_owned()
))
@ -443,8 +439,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Standard C allocation
"malloc" => {
let [size] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [size] = this.check_shim(abi, Conv::C, link_name, args)?;
let size = this.read_target_usize(size)?;
if size <= this.max_size_of_val().bytes() {
let res = this.malloc(size, /*zero_init:*/ false)?;
@ -458,8 +453,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
"calloc" => {
let [items, elem_size] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [items, elem_size] = this.check_shim(abi, Conv::C, link_name, args)?;
let items = this.read_target_usize(items)?;
let elem_size = this.read_target_usize(elem_size)?;
if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
@ -474,14 +468,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
"free" => {
let [ptr] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
this.free(ptr)?;
}
"realloc" => {
let [old_ptr, new_size] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [old_ptr, new_size] = this.check_shim(abi, Conv::C, link_name, args)?;
let old_ptr = this.read_pointer(old_ptr)?;
let new_size = this.read_target_usize(new_size)?;
if new_size <= this.max_size_of_val().bytes() {
@ -619,8 +611,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// C memory handling functions
"memcmp" => {
let [left, right, n] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [left, right, n] = this.check_shim(abi, Conv::C, link_name, args)?;
let left = this.read_pointer(left)?;
let right = this.read_pointer(right)?;
let n = Size::from_bytes(this.read_target_usize(n)?);
@ -644,8 +635,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(Scalar::from_i32(result), dest)?;
}
"memrchr" => {
let [ptr, val, num] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [ptr, val, num] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let val = this.read_scalar(val)?.to_i32()?;
let num = this.read_target_usize(num)?;
@ -671,8 +661,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
"memchr" => {
let [ptr, val, num] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [ptr, val, num] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let val = this.read_scalar(val)?.to_i32()?;
let num = this.read_target_usize(num)?;
@ -695,8 +684,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
"strlen" => {
let [ptr] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
let n = this.read_c_str(ptr)?.len();
@ -706,8 +694,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
)?;
}
"wcslen" => {
let [ptr] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
let n = this.read_wchar_t_str(ptr)?.len();
@ -717,8 +704,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
)?;
}
"memcpy" => {
let [ptr_dest, ptr_src, n] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [ptr_dest, ptr_src, n] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr_dest = this.read_pointer(ptr_dest)?;
let ptr_src = this.read_pointer(ptr_src)?;
let n = this.read_target_usize(n)?;
@ -732,8 +718,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_pointer(ptr_dest, dest)?;
}
"strcpy" => {
let [ptr_dest, ptr_src] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [ptr_dest, ptr_src] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr_dest = this.read_pointer(ptr_dest)?;
let ptr_src = this.read_pointer(ptr_src)?;
@ -878,8 +863,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"lgammaf_r" => {
let [x, signp] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?;
let x = this.read_scalar(x)?.to_f32()?;
let signp = this.deref_pointer(signp)?;
@ -890,8 +874,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"lgamma_r" => {
let [x, signp] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?;
let x = this.read_scalar(x)?.to_f64()?;
let signp = this.deref_pointer(signp)?;
@ -904,8 +887,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// LLVM intrinsics
"llvm.prefetch" => {
let [p, rw, loc, ty] =
this.check_shim(abi, Conv::C , link_name, args)?;
let [p, rw, loc, ty] = this.check_shim(abi, Conv::C, link_name, args)?;
let _ = this.read_pointer(p)?;
let rw = this.read_scalar(rw)?.to_i32()?;
@ -932,7 +914,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm
// `{i,u}8x16_popcnt` functions.
name if name.starts_with("llvm.ctpop.v") => {
let [op] = this.check_shim(abi, Conv::C , link_name, args)?;
let [op] = this.check_shim(abi, Conv::C, link_name, args)?;
let (op, op_len) = this.project_to_simd(op)?;
let (dest, dest_len) = this.project_to_simd(dest)?;

View File

@ -1,4 +1,5 @@
//! Implements calling functions from a native library.
use std::cell::RefCell;
use std::ops::Deref;
use libffi::high::call as ffi;
@ -172,6 +173,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Wildcard pointer, whatever it points to must be already exposed.
continue;
};
// The first time this happens at a particular location, print a warning.
thread_local! {
static HAVE_WARNED: RefCell<bool> = const { RefCell::new(false) };
}
HAVE_WARNED.with_borrow_mut(|have_warned| {
if !*have_warned {
// Newly inserted, so first time we see this span.
this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem);
*have_warned = true;
}
});
this.prepare_for_native_call(alloc_id, prov)?;
}
}

View File

@ -180,6 +180,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if !matches!(&*this.tcx.sess.target.os, "solaris" | "illumos") {
// tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08.
// This may not be consistent with libc::localtime_r's result.
let offset_in_seconds = dt.offset().fix().local_minus_utc();
let tm_gmtoff = offset_in_seconds;
let mut tm_zone = String::new();
@ -195,11 +196,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
write!(tm_zone, "{:02}", offset_min).unwrap();
}
// FIXME: String de-duplication is needed so that we only allocate this string only once
// even when there are multiple calls to this function.
let tm_zone_ptr = this
.alloc_os_str_as_c_str(&OsString::from(tm_zone), MiriMemoryKind::Machine.into())?;
// Add null terminator for C string compatibility.
tm_zone.push('\0');
// Deduplicate and allocate the string.
let tm_zone_ptr = this.allocate_bytes_dedup(tm_zone.as_bytes())?;
// Write the timezone pointer and offset into the result structure.
this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?;
this.write_int_fields_named(&[("tm_gmtoff", tm_gmtoff.into())], &result)?;
}

View File

@ -2,8 +2,6 @@ use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::{Conv, FnAbi};
use crate::shims::unix::android::thread::prctl;
use crate::shims::unix::linux_like::epoll::EvalContextExt as _;
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
@ -27,14 +25,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// epoll, eventfd
"epoll_create1" => {
let [flag] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [flag] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.epoll_create1(flag)?;
this.write_scalar(result, dest)?;
}
"epoll_ctl" => {
let [epfd, op, fd, event] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [epfd, op, fd, event] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.epoll_ctl(epfd, op, fd, event)?;
this.write_scalar(result, dest)?;
}
@ -44,8 +40,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
}
"eventfd" => {
let [val, flag] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [val, flag] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.eventfd(val, flag)?;
this.write_scalar(result, dest)?;
}

View File

@ -2,8 +2,8 @@ use std::ffi::OsStr;
use std::str;
use rustc_abi::Size;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf;
use rustc_span::Symbol;
use rustc_target::callconv::{Conv, FnAbi};

View File

@ -22,8 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// Threading
"pthread_set_name_np" => {
let [thread, name] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?;
let max_len = usize::MAX; // FreeBSD does not seem to have a limit.
// FreeBSD's pthread_set_name_np does not return anything.
this.pthread_setname_np(
@ -34,8 +33,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
)?;
}
"pthread_get_name_np" => {
let [thread, name, len] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
// FreeBSD's pthread_get_name_np does not return anything
// and uses strlcpy, which truncates the resulting value,
// but always adds a null terminator (except for zero-sized buffers).
@ -52,26 +50,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// For those, we both intercept `func` and `call@FBSD_1.0` symbols cases
// since freebsd 12 the former form can be expected.
"stat" | "stat@FBSD_1.0" => {
let [path, buf] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat@FBSD_1.0" => {
let [path, buf] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat@FBSD_1.0" => {
let [fd, buf] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"readdir_r" | "readdir_r@FBSD_1.0" => {
let [dirp, entry, result] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [dirp, entry, result] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
this.write_scalar(result, dest)?;
}
@ -86,8 +80,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
// These shims are enabled only when the caller is in the standard library.
"pthread_attr_get_np" if this.frame_in_std() => {
let [_thread, _attr] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [_thread, _attr] = this.check_shim(abi, Conv::C, link_name, args)?;
this.write_null(dest)?;
}

View File

@ -1048,10 +1048,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn linux_readdir64(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
fn linux_solarish_readdir64(
&mut self,
dirent_type: &str,
dirp_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("linux", "readdir64");
if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") {
panic!("`linux_solaris_readdir64` should not be called on {}", this.tcx.sess.target.os);
}
let dirp = this.read_target_usize(dirp_op)?;
@ -1070,9 +1076,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Some(Ok(dir_entry)) => {
// Write the directory entry into a newly allocated buffer.
// The name is written with write_bytes, while the rest of the
// dirent64 struct is written using write_int_fields.
// dirent64 (or dirent) struct is written using write_int_fields.
// For reference:
// On Linux:
// pub struct dirent64 {
// pub d_ino: ino64_t,
// pub d_off: off64_t,
@ -1080,19 +1087,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// pub d_type: c_uchar,
// pub d_name: [c_char; 256],
// }
//
// On Solaris:
// pub struct dirent {
// pub d_ino: ino64_t,
// pub d_off: off64_t,
// pub d_reclen: c_ushort,
// pub d_name: [c_char; 3],
// }
let mut name = dir_entry.file_name(); // not a Path as there are no separators!
name.push("\0"); // Add a NUL terminator
let name_bytes = name.as_encoded_bytes();
let name_len = u64::try_from(name_bytes.len()).unwrap();
let dirent64_layout = this.libc_ty_layout("dirent64");
let d_name_offset = dirent64_layout.fields.offset(4 /* d_name */).bytes();
let dirent_layout = this.libc_ty_layout(dirent_type);
let fields = &dirent_layout.fields;
let last_field = fields.count().strict_sub(1);
let d_name_offset = fields.offset(last_field).bytes();
let size = d_name_offset.strict_add(name_len);
let entry = this.allocate_ptr(
Size::from_bytes(size),
dirent64_layout.align.abi,
dirent_layout.align.abi,
MiriMemoryKind::Runtime.into(),
)?;
let entry: Pointer = entry.into();
@ -1105,17 +1122,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let ino = 0u64;
let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
this.write_int_fields_named(
&[
("d_ino", ino.into()),
("d_off", 0),
("d_reclen", size.into()),
("d_type", file_type.into()),
],
&this.ptr_to_mplace(entry, dirent64_layout),
&[("d_ino", ino.into()), ("d_off", 0), ("d_reclen", size.into())],
&this.ptr_to_mplace(entry, dirent_layout),
)?;
if let Some(d_type) = this
.try_project_field_named(&this.ptr_to_mplace(entry, dirent_layout), "d_type")?
{
this.write_int(file_type, &d_type)?;
}
let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this);
this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?;

View File

@ -36,14 +36,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// File related shims
"readdir64" => {
let [dirp] =
this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.linux_readdir64(dirp)?;
let [dirp] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.linux_solarish_readdir64("dirent64", dirp)?;
this.write_scalar(result, dest)?;
}
"sync_file_range" => {
let [fd, offset, nbytes, flags] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [fd, offset, nbytes, flags] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.sync_file_range(fd, offset, nbytes, flags)?;
this.write_scalar(result, dest)?;
}
@ -56,14 +54,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// epoll, eventfd
"epoll_create1" => {
let [flag] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [flag] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.epoll_create1(flag)?;
this.write_scalar(result, dest)?;
}
"epoll_ctl" => {
let [epfd, op, fd, event] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [epfd, op, fd, event] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.epoll_ctl(epfd, op, fd, event)?;
this.write_scalar(result, dest)?;
}
@ -73,16 +69,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
}
"eventfd" => {
let [val, flag] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [val, flag] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.eventfd(val, flag)?;
this.write_scalar(result, dest)?;
}
// Threading
"pthread_setname_np" => {
let [thread, name] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?;
let res = match this.pthread_setname_np(
this.read_scalar(thread)?,
this.read_scalar(name)?,
@ -97,8 +91,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"pthread_getname_np" => {
let [thread, name, len] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
// The function's behavior isn't portable between platforms.
// In case of glibc, the length of the output buffer must
// be not shorter than TASK_COMM_LEN.
@ -146,8 +139,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(ptr, dest)?;
}
"__xpg_strerror_r" => {
let [errnum, buf, buflen] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [errnum, buf, buflen] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.strerror_r(errnum, buf, buflen)?;
this.write_scalar(result, dest)?;
}
@ -170,8 +162,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
// These shims are enabled only when the caller is in the standard library.
"pthread_getattr_np" if this.frame_in_std() => {
let [_thread, _attr] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [_thread, _attr] = this.check_shim(abi, Conv::C, link_name, args)?;
this.write_null(dest)?;
}

View File

@ -2,7 +2,6 @@ use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::{Conv, FnAbi};
use crate::helpers::check_min_arg_count;
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
use crate::shims::unix::linux_like::sync::futex;

View File

@ -33,44 +33,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// File related shims
"close$NOCANCEL" => {
let [result] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [result] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.close(result)?;
this.write_scalar(result, dest)?;
}
"stat" | "stat64" | "stat$INODE64" => {
let [path, buf] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat64" | "lstat$INODE64" => {
let [path, buf] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat64" | "fstat$INODE64" => {
let [fd, buf] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"opendir$INODE64" => {
let [name] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.opendir(name)?;
this.write_scalar(result, dest)?;
}
"readdir_r" | "readdir_r$INODE64" => {
let [dirp, entry, result] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [dirp, entry, result] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
this.write_scalar(result, dest)?;
}
"realpath$DARWIN_EXTSN" => {
let [path, resolved_path] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [path, resolved_path] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.realpath(path, resolved_path)?;
this.write_scalar(result, dest)?;
}
@ -84,8 +77,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Random data generation
"CCRandomGenerateBytes" => {
let [bytes, count] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [bytes, count] = this.check_shim(abi, Conv::C, link_name, args)?;
let bytes = this.read_pointer(bytes)?;
let count = this.read_target_usize(count)?;
let success = this.eval_libc_i32("kCCSuccess");
@ -101,8 +93,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"mach_timebase_info" => {
let [info] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [info] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.mach_timebase_info(info)?;
this.write_scalar(result, dest)?;
}
@ -117,8 +108,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
}
"_NSGetExecutablePath" => {
let [buf, bufsize] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?;
this.check_no_isolation("`_NSGetExecutablePath`")?;
let buf_ptr = this.read_pointer(buf)?;
@ -143,8 +133,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Thread-local storage
"_tlv_atexit" => {
let [dtor, data] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [dtor, data] = this.check_shim(abi, Conv::C, link_name, args)?;
let dtor = this.read_pointer(dtor)?;
let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
let data = this.read_scalar(data)?;
@ -154,15 +143,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Querying system information
"pthread_get_stackaddr_np" => {
let [thread] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
this.read_target_usize(thread)?;
let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
this.write_scalar(stack_addr, dest)?;
}
"pthread_get_stacksize_np" => {
let [thread] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
this.read_target_usize(thread)?;
let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
this.write_scalar(stack_size, dest)?;
@ -170,8 +157,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Threading
"pthread_setname_np" => {
let [name] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
// The real implementation has logic in two places:
// * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200,
@ -198,8 +184,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"pthread_getname_np" => {
let [thread, name, len] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
// The function's behavior isn't portable between platforms.
// In case of macOS, a truncated name (due to a too small buffer)
@ -224,28 +209,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"os_unfair_lock_lock" => {
let [lock_op] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
this.os_unfair_lock_lock(lock_op)?;
}
"os_unfair_lock_trylock" => {
let [lock_op] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
this.os_unfair_lock_trylock(lock_op, dest)?;
}
"os_unfair_lock_unlock" => {
let [lock_op] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
this.os_unfair_lock_unlock(lock_op)?;
}
"os_unfair_lock_assert_owner" => {
let [lock_op] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
this.os_unfair_lock_assert_owner(lock_op)?;
}
"os_unfair_lock_assert_not_owner" => {
let [lock_op] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
this.os_unfair_lock_assert_not_owner(lock_op)?;
}

View File

@ -23,8 +23,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// Threading
"pthread_setname_np" => {
let [thread, name] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?;
// THREAD_NAME_MAX allows a thread name of 31+1 length
// https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613
let max_len = 32;
@ -42,8 +41,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"pthread_getname_np" => {
let [thread, name, len] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
// See https://illumos.org/man/3C/pthread_getname_np for the error codes.
let res = match this.pthread_getname_np(
this.read_scalar(thread)?,
@ -60,23 +58,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// File related shims
"stat" | "stat64" => {
let [path, buf] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat64" => {
let [path, buf] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat64" => {
let [fd, buf] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"readdir" => {
let [dirp] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.linux_solarish_readdir64("dirent", dirp)?;
this.write_scalar(result, dest)?;
}
// Miscellaneous
"___errno" => {
@ -86,8 +86,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"stack_getbounds" => {
let [stack] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [stack] = this.check_shim(abi, Conv::C, link_name, args)?;
let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?;
this.write_int_fields_named(
@ -105,8 +104,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"pset_info" => {
let [pset, tpe, cpus, list] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [pset, tpe, cpus, list] = this.check_shim(abi, Conv::C, link_name, args)?;
// We do not need to handle the current process cpu mask, available_parallelism
// implementation pass null anyway. We only care for the number of
// cpus.
@ -135,8 +133,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"__sysconf_xpg7" => {
let [val] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [val] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.sysconf(val)?;
this.write_scalar(result, dest)?;
}

View File

@ -36,6 +36,12 @@ struct AnonSocket {
/// This flag is set to `true` if the peer's `readbuf` is non-empty at the time
/// of closure.
peer_lost_data: Cell<bool>,
/// A list of thread ids blocked because the buffer was empty.
/// Once another thread writes some bytes, these threads will be unblocked.
blocked_read_tid: RefCell<Vec<ThreadId>>,
/// A list of thread ids blocked because the buffer was full.
/// Once another thread reads some bytes, these threads will be unblocked.
blocked_write_tid: RefCell<Vec<ThreadId>>,
is_nonblock: bool,
}
@ -83,7 +89,7 @@ impl FileDescription for AnonSocket {
fn read<'tcx>(
&self,
_self_ref: &FileDescriptionRef,
self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
ptr: Pointer,
len: usize,
@ -100,33 +106,21 @@ impl FileDescription for AnonSocket {
// corresponding ErrorKind variant.
throw_unsup_format!("reading from the write end of a pipe");
};
if readbuf.borrow().buf.is_empty() {
if self.peer_fd().upgrade().is_none() {
// Socketpair with no peer and empty buffer.
// 0 bytes successfully read indicates end-of-file.
return ecx.return_read_success(ptr, &[], 0, dest);
} else {
if self.is_nonblock {
// Non-blocking socketpair with writer and empty buffer.
// https://linux.die.net/man/2/read
// EAGAIN or EWOULDBLOCK can be returned for socket,
// POSIX.1-2001 allows either error to be returned for this case.
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
} else {
// Blocking socketpair with writer and empty buffer.
// FIXME: blocking is currently not supported
throw_unsup_format!("socketpair/pipe/pipe2 read: blocking isn't supported yet");
}
}
if readbuf.borrow().buf.is_empty() && self.is_nonblock {
// Non-blocking socketpair with writer and empty buffer.
// https://linux.die.net/man/2/read
// EAGAIN or EWOULDBLOCK can be returned for socket,
// POSIX.1-2001 allows either error to be returned for this case.
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
}
// TODO: We might need to decide what to do if peer_fd is closed when read is blocked.
anonsocket_read(self, self.peer_fd().upgrade(), len, ptr, dest, ecx)
anonsocket_read(self_ref.downgrade(), len, ptr, dest.clone(), ecx)
}
fn write<'tcx>(
&self,
_self_ref: &FileDescriptionRef,
self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
ptr: Pointer,
len: usize,
@ -153,16 +147,11 @@ impl FileDescription for AnonSocket {
};
let available_space =
MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
if available_space == 0 {
if self.is_nonblock {
// Non-blocking socketpair with a full buffer.
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
} else {
// Blocking socketpair with a full buffer.
throw_unsup_format!("socketpair/pipe/pipe2 write: blocking isn't supported yet");
}
if available_space == 0 && self.is_nonblock {
// Non-blocking socketpair with a full buffer.
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
}
anonsocket_write(available_space, &peer_fd, ptr, len, dest, ecx)
anonsocket_write(self_ref.downgrade(), ptr, len, dest.clone(), ecx)
}
fn as_unix(&self) -> &dyn UnixFileDescription {
@ -172,81 +161,161 @@ impl FileDescription for AnonSocket {
/// Write to AnonSocket based on the space available and return the written byte size.
fn anonsocket_write<'tcx>(
available_space: usize,
peer_fd: &FileDescriptionRef,
weak_self_ref: WeakFileDescriptionRef,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
dest: MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
let Some(self_ref) = weak_self_ref.upgrade() else {
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
throw_unsup_format!("This will be a deadlock error in future")
};
let self_anonsocket = self_ref.downcast::<AnonSocket>().unwrap();
let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() else {
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
// closed.
return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, &dest);
};
let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
// corresponding ErrorKind variant.
throw_unsup_format!("writing to the reading end of a pipe")
};
let mut writebuf = writebuf.borrow_mut();
// Remember this clock so `read` can synchronize with us.
ecx.release_clock(|clock| {
writebuf.clock.join(clock);
});
// Do full write / partial write based on the space available.
let actual_write_size = len.min(available_space);
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
writebuf.buf.extend(&bytes[..actual_write_size]);
let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
// Need to stop accessing peer_fd so that it can be notified.
drop(writebuf);
if available_space == 0 {
// Blocking socketpair with a full buffer.
let dest = dest.clone();
self_anonsocket.blocked_write_tid.borrow_mut().push(ecx.active_thread());
ecx.block_thread(
BlockReason::UnnamedSocket,
None,
callback!(
@capture<'tcx> {
weak_self_ref: WeakFileDescriptionRef,
ptr: Pointer,
len: usize,
dest: MPlaceTy<'tcx>,
}
@unblock = |this| {
anonsocket_write(weak_self_ref, ptr, len, dest, this)
}
),
);
} else {
let mut writebuf = writebuf.borrow_mut();
// Remember this clock so `read` can synchronize with us.
ecx.release_clock(|clock| {
writebuf.clock.join(clock);
});
// Do full write / partial write based on the space available.
let actual_write_size = len.min(available_space);
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
writebuf.buf.extend(&bytes[..actual_write_size]);
// Notification should be provided for peer fd as it became readable.
// The kernel does this even if the fd was already readable before, so we follow suit.
ecx.check_and_update_readiness(peer_fd)?;
// Need to stop accessing peer_fd so that it can be notified.
drop(writebuf);
ecx.return_write_success(actual_write_size, dest)
// Notification should be provided for peer fd as it became readable.
// The kernel does this even if the fd was already readable before, so we follow suit.
ecx.check_and_update_readiness(&peer_fd)?;
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap();
// Unblock all threads that are currently blocked on peer_fd's read.
let waiting_threads = std::mem::take(&mut *peer_anonsocket.blocked_read_tid.borrow_mut());
// FIXME: We can randomize the order of unblocking.
for thread_id in waiting_threads {
ecx.unblock_thread(thread_id, BlockReason::UnnamedSocket)?;
}
return ecx.return_write_success(actual_write_size, &dest);
}
interp_ok(())
}
/// Read from AnonSocket and return the number of bytes read.
fn anonsocket_read<'tcx>(
anonsocket: &AnonSocket,
peer_fd: Option<FileDescriptionRef>,
weak_self_ref: WeakFileDescriptionRef,
len: usize,
ptr: Pointer,
dest: &MPlaceTy<'tcx>,
dest: MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
let mut bytes = vec![0; len];
let Some(self_ref) = weak_self_ref.upgrade() else {
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
throw_unsup_format!("This will be a deadlock error in future")
};
let self_anonsocket = self_ref.downcast::<AnonSocket>().unwrap();
let Some(readbuf) = &anonsocket.readbuf else {
let Some(readbuf) = &self_anonsocket.readbuf else {
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
// corresponding ErrorKind variant.
throw_unsup_format!("reading from the write end of a pipe")
};
let mut readbuf = readbuf.borrow_mut();
// Synchronize with all previous writes to this buffer.
// FIXME: this over-synchronizes; a more precise approach would be to
// only sync with the writes whose data we will read.
ecx.acquire_clock(&readbuf.clock);
if readbuf.borrow_mut().buf.is_empty() {
if self_anonsocket.peer_fd().upgrade().is_none() {
// Socketpair with no peer and empty buffer.
// 0 bytes successfully read indicates end-of-file.
return ecx.return_read_success(ptr, &[], 0, &dest);
} else {
// Blocking socketpair with writer and empty buffer.
let weak_self_ref = weak_self_ref.clone();
self_anonsocket.blocked_read_tid.borrow_mut().push(ecx.active_thread());
ecx.block_thread(
BlockReason::UnnamedSocket,
None,
callback!(
@capture<'tcx> {
weak_self_ref: WeakFileDescriptionRef,
len: usize,
ptr: Pointer,
dest: MPlaceTy<'tcx>,
}
@unblock = |this| {
anonsocket_read(weak_self_ref, len, ptr, dest, this)
}
),
);
}
} else {
let mut bytes = vec![0; len];
let mut readbuf = readbuf.borrow_mut();
// Synchronize with all previous writes to this buffer.
// FIXME: this over-synchronizes; a more precise approach would be to
// only sync with the writes whose data we will read.
ecx.acquire_clock(&readbuf.clock);
// Do full read / partial read based on the space available.
// Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
let actual_read_size = readbuf.buf.read(&mut bytes[..]).unwrap();
// Do full read / partial read based on the space available.
// Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
let actual_read_size = readbuf.buf.read(&mut bytes[..]).unwrap();
// Need to drop before others can access the readbuf again.
drop(readbuf);
// Need to drop before others can access the readbuf again.
drop(readbuf);
// A notification should be provided for the peer file description even when it can
// only write 1 byte. This implementation is not compliant with the actual Linux kernel
// implementation. For optimization reasons, the kernel will only mark the file description
// as "writable" when it can write more than a certain number of bytes. Since we
// don't know what that *certain number* is, we will provide a notification every time
// a read is successful. This might result in our epoll emulation providing more
// notifications than the real system.
if let Some(peer_fd) = peer_fd {
ecx.check_and_update_readiness(&peer_fd)?;
// A notification should be provided for the peer file description even when it can
// only write 1 byte. This implementation is not compliant with the actual Linux kernel
// implementation. For optimization reasons, the kernel will only mark the file description
// as "writable" when it can write more than a certain number of bytes. Since we
// don't know what that *certain number* is, we will provide a notification every time
// a read is successful. This might result in our epoll emulation providing more
// notifications than the real system.
if let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() {
ecx.check_and_update_readiness(&peer_fd)?;
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap();
// Unblock all threads that are currently blocked on peer_fd's write.
let waiting_threads =
std::mem::take(&mut *peer_anonsocket.blocked_write_tid.borrow_mut());
// FIXME: We can randomize the order of unblocking.
for thread_id in waiting_threads {
ecx.unblock_thread(thread_id, BlockReason::UnnamedSocket)?;
}
};
return ecx.return_read_success(ptr, &bytes, actual_read_size, &dest);
}
ecx.return_read_success(ptr, &bytes, actual_read_size, dest)
interp_ok(())
}
impl UnixFileDescription for AnonSocket {
@ -360,12 +429,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
readbuf: Some(RefCell::new(Buffer::new())),
peer_fd: OnceCell::new(),
peer_lost_data: Cell::new(false),
blocked_read_tid: RefCell::new(Vec::new()),
blocked_write_tid: RefCell::new(Vec::new()),
is_nonblock: is_sock_nonblock,
});
let fd1 = fds.new_ref(AnonSocket {
readbuf: Some(RefCell::new(Buffer::new())),
peer_fd: OnceCell::new(),
peer_lost_data: Cell::new(false),
blocked_read_tid: RefCell::new(Vec::new()),
blocked_write_tid: RefCell::new(Vec::new()),
is_nonblock: is_sock_nonblock,
});
@ -424,12 +497,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
readbuf: Some(RefCell::new(Buffer::new())),
peer_fd: OnceCell::new(),
peer_lost_data: Cell::new(false),
blocked_read_tid: RefCell::new(Vec::new()),
blocked_write_tid: RefCell::new(Vec::new()),
is_nonblock,
});
let fd1 = fds.new_ref(AnonSocket {
readbuf: None,
peer_fd: OnceCell::new(),
peer_lost_data: Cell::new(false),
blocked_read_tid: RefCell::new(Vec::new()),
blocked_write_tid: RefCell::new(Vec::new()),
is_nonblock,
});

View File

@ -22,14 +22,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// Allocation
"posix_memalign" => {
let [memptr, align, size] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [memptr, align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.posix_memalign(memptr, align, size)?;
this.write_scalar(result, dest)?;
}
"aligned_alloc" => {
let [align, size] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
let res = this.aligned_alloc(align, size)?;
this.write_pointer(res, dest)?;
}

View File

@ -108,50 +108,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// Environment related shims
"GetEnvironmentVariableW" => {
let [name, buf, size] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [name, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.GetEnvironmentVariableW(name, buf, size)?;
this.write_scalar(result, dest)?;
}
"SetEnvironmentVariableW" => {
let [name, value] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [name, value] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.SetEnvironmentVariableW(name, value)?;
this.write_scalar(result, dest)?;
}
"GetEnvironmentStringsW" => {
let [] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.GetEnvironmentStringsW()?;
this.write_pointer(result, dest)?;
}
"FreeEnvironmentStringsW" => {
let [env_block] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [env_block] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.FreeEnvironmentStringsW(env_block)?;
this.write_scalar(result, dest)?;
}
"GetCurrentDirectoryW" => {
let [size, buf] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [size, buf] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.GetCurrentDirectoryW(size, buf)?;
this.write_scalar(result, dest)?;
}
"SetCurrentDirectoryW" => {
let [path] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [path] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.SetCurrentDirectoryW(path)?;
this.write_scalar(result, dest)?;
}
"GetUserProfileDirectoryW" => {
let [token, buf, size] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [token, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.GetUserProfileDirectoryW(token, buf, size)?;
this.write_scalar(result, dest)?;
}
"GetCurrentProcessId" => {
let [] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.GetCurrentProcessId()?;
this.write_scalar(result, dest)?;
}
@ -257,8 +249,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Allocation
"HeapAlloc" => {
let [handle, flags, size] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [handle, flags, size] = this.check_shim(abi, sys_conv, link_name, args)?;
this.read_target_isize(handle)?;
let flags = this.read_scalar(flags)?.to_u32()?;
let size = this.read_target_usize(size)?;
@ -281,8 +272,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_pointer(ptr, dest)?;
}
"HeapFree" => {
let [handle, flags, ptr] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [handle, flags, ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
this.read_target_isize(handle)?;
this.read_scalar(flags)?.to_u32()?;
let ptr = this.read_pointer(ptr)?;
@ -314,8 +304,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_pointer(new_ptr, dest)?;
}
"LocalFree" => {
let [ptr] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
// "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL."
// (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree)
@ -327,14 +316,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// errno
"SetLastError" => {
let [error] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [error] = this.check_shim(abi, sys_conv, link_name, args)?;
let error = this.read_scalar(error)?;
this.set_last_error(error)?;
}
"GetLastError" => {
let [] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
let last_error = this.get_last_error()?;
this.write_scalar(last_error, dest)?;
}
@ -342,8 +329,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Querying system information
"GetSystemInfo" => {
// Also called from `page_size` crate.
let [system_info] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [system_info] = this.check_shim(abi, sys_conv, link_name, args)?;
let system_info =
this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?;
// Initialize with `0`.
@ -366,22 +352,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// This just creates a key; Windows does not natively support TLS destructors.
// Create key and return it.
let [] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
}
"TlsGetValue" => {
let [key] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [key] = this.check_shim(abi, sys_conv, link_name, args)?;
let key = u128::from(this.read_scalar(key)?.to_u32()?);
let active_thread = this.active_thread();
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
this.write_scalar(ptr, dest)?;
}
"TlsSetValue" => {
let [key, new_ptr] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [key, new_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
let key = u128::from(this.read_scalar(key)?.to_u32()?);
let active_thread = this.active_thread();
let new_data = this.read_scalar(new_ptr)?;
@ -401,8 +384,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Access to command-line arguments
"GetCommandLineW" => {
let [] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
this.write_pointer(
this.machine.cmd_line.expect("machine must be initialized"),
dest,
@ -412,27 +394,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Time related shims
"GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => {
#[allow(non_snake_case)]
let [LPFILETIME] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [LPFILETIME] = this.check_shim(abi, sys_conv, link_name, args)?;
this.GetSystemTimeAsFileTime(link_name.as_str(), LPFILETIME)?;
}
"QueryPerformanceCounter" => {
#[allow(non_snake_case)]
let [lpPerformanceCount] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [lpPerformanceCount] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
this.write_scalar(result, dest)?;
}
"QueryPerformanceFrequency" => {
#[allow(non_snake_case)]
let [lpFrequency] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [lpFrequency] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.QueryPerformanceFrequency(lpFrequency)?;
this.write_scalar(result, dest)?;
}
"Sleep" => {
let [timeout] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [timeout] = this.check_shim(abi, sys_conv, link_name, args)?;
this.Sleep(timeout)?;
}
@ -456,8 +434,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?;
}
"InitOnceComplete" => {
let [ptr, flags, context] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [ptr, flags, context] = this.check_shim(abi, sys_conv, link_name, args)?;
let result = this.InitOnceComplete(ptr, flags, context)?;
this.write_scalar(result, dest)?;
}
@ -468,14 +445,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?;
}
"WakeByAddressSingle" => {
let [ptr_op] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?;
this.WakeByAddressSingle(ptr_op)?;
}
"WakeByAddressAll" => {
let [ptr_op] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?;
this.WakeByAddressAll(ptr_op)?;
}
@ -483,8 +458,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Dynamic symbol loading
"GetProcAddress" => {
#[allow(non_snake_case)]
let [hModule, lpProcName] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [hModule, lpProcName] = this.check_shim(abi, sys_conv, link_name, args)?;
this.read_target_isize(hModule)?;
let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
if let Ok(name) = str::from_utf8(name)
@ -508,15 +482,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
}
"WaitForSingleObject" => {
let [handle, timeout] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [handle, timeout] = this.check_shim(abi, sys_conv, link_name, args)?;
let ret = this.WaitForSingleObject(handle, timeout)?;
this.write_scalar(ret, dest)?;
}
"GetCurrentThread" => {
let [] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
this.write_scalar(
Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
@ -524,8 +496,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
)?;
}
"SetThreadDescription" => {
let [handle, name] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [handle, name] = this.check_shim(abi, sys_conv, link_name, args)?;
let handle = this.read_scalar(handle)?;
let name = this.read_wide_str(this.read_pointer(name)?)?;
@ -549,8 +520,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"GetThreadDescription" => {
let [handle, name_ptr] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
let handle = this.read_scalar(handle)?;
let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name
@ -581,16 +551,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Miscellaneous
"ExitProcess" => {
let [code] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [code] = this.check_shim(abi, sys_conv, link_name, args)?;
let code = this.read_scalar(code)?.to_u32()?;
throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
}
"SystemFunction036" => {
// used by getrandom 0.1
// This is really 'RtlGenRandom'.
let [ptr, len] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_scalar(len)?.to_u32()?;
this.gen_random(ptr, len.into())?;
@ -598,8 +566,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"ProcessPrng" => {
// used by `std`
let [ptr, len] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_target_usize(len)?;
this.gen_random(ptr, len)?;
@ -642,8 +609,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"GetConsoleScreenBufferInfo" => {
// `term` needs this, so we fake it.
let [console, buffer_info] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [console, buffer_info] = this.check_shim(abi, sys_conv, link_name, args)?;
this.read_target_isize(console)?;
// FIXME: this should use deref_pointer_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std
this.deref_pointer(buffer_info)?;
@ -652,8 +618,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_null(dest)?;
}
"GetStdHandle" => {
let [which] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [which] = this.check_shim(abi, sys_conv, link_name, args)?;
let which = this.read_scalar(which)?.to_i32()?;
// We just make this the identity function, so we know later in `NtWriteFile` which
// one it is. This is very fake, but libtest needs it so we cannot make it a
@ -662,16 +627,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(Scalar::from_target_isize(which.into(), this), dest)?;
}
"CloseHandle" => {
let [handle] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [handle] = this.check_shim(abi, sys_conv, link_name, args)?;
let ret = this.CloseHandle(handle)?;
this.write_scalar(ret, dest)?;
}
"GetModuleFileNameW" => {
let [handle, filename, size] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [handle, filename, size] = this.check_shim(abi, sys_conv, link_name, args)?;
this.check_no_isolation("`GetModuleFileNameW`")?;
let handle = this.read_target_usize(handle)?;
@ -740,16 +703,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
// These shims are enabled only when the caller is in the standard library.
"GetProcessHeap" if this.frame_in_std() => {
let [] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
// Just fake a HANDLE
// It's fine to not use the Handle type here because its a stub
this.write_int(1, dest)?;
}
"GetModuleHandleA" if this.frame_in_std() => {
#[allow(non_snake_case)]
let [_lpModuleName] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [_lpModuleName] = this.check_shim(abi, sys_conv, link_name, args)?;
// We need to return something non-null here to make `compat_fn!` work.
this.write_int(1, dest)?;
}
@ -761,8 +722,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_null(dest)?;
}
"GetConsoleMode" if this.frame_in_std() => {
let [console, mode] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [console, mode] = this.check_shim(abi, sys_conv, link_name, args)?;
this.read_target_isize(console)?;
this.deref_pointer(mode)?;
// Indicate an error.
@ -770,29 +730,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"GetFileType" if this.frame_in_std() => {
#[allow(non_snake_case)]
let [_hFile] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [_hFile] = this.check_shim(abi, sys_conv, link_name, args)?;
// Return unknown file type.
this.write_null(dest)?;
}
"AddVectoredExceptionHandler" if this.frame_in_std() => {
#[allow(non_snake_case)]
let [_First, _Handler] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [_First, _Handler] = this.check_shim(abi, sys_conv, link_name, args)?;
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
this.write_int(1, dest)?;
}
"SetThreadStackGuarantee" if this.frame_in_std() => {
#[allow(non_snake_case)]
let [_StackSizeInBytes] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [_StackSizeInBytes] = this.check_shim(abi, sys_conv, link_name, args)?;
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
this.write_int(1, dest)?;
}
// this is only callable from std because we know that std ignores the return value
"SwitchToThread" if this.frame_in_std() => {
let [] =
this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
this.yield_active_thread();
@ -811,8 +767,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
);
}
// This function looks and behaves excatly like miri_start_unwind.
let [payload] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [payload] = this.check_shim(abi, Conv::C, link_name, args)?;
this.handle_miri_start_unwind(payload)?;
return interp_ok(EmulateItemResult::NeedsUnwind);
}

View File

@ -26,8 +26,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// `state` with the corresponding 128-bit key of `key`.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128
"aesdec" | "aesdec.256" | "aesdec.512" => {
let [state, key] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
aes_round(this, state, key, dest, |state, key| {
let key = aes::Block::from(key.to_le_bytes());
let mut state = aes::Block::from(state.to_le_bytes());
@ -43,8 +42,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// `state` with the corresponding 128-bit key of `key`.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128
"aesdeclast" | "aesdeclast.256" | "aesdeclast.512" => {
let [state, key] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
aes_round(this, state, key, dest, |state, key| {
let mut state = aes::Block::from(state.to_le_bytes());
@ -68,8 +66,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// `state` with the corresponding 128-bit key of `key`.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenc_si128
"aesenc" | "aesenc.256" | "aesenc.512" => {
let [state, key] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
aes_round(this, state, key, dest, |state, key| {
let key = aes::Block::from(key.to_le_bytes());
let mut state = aes::Block::from(state.to_le_bytes());
@ -85,8 +82,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// `state` with the corresponding 128-bit key of `key`.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128
"aesenclast" | "aesenclast.256" | "aesenclast.512" => {
let [state, key] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
aes_round(this, state, key, dest, |state, key| {
let mut state = aes::Block::from(state.to_le_bytes());
// `aes::hazmat::cipher_round` does the following operations:

View File

@ -33,8 +33,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// matches the IEEE min/max operations, while x86 has different
// semantics.
"min.ps.256" | "max.ps.256" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"min.ps.256" => FloatBinOp::Min,
@ -46,8 +45,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
// Used to implement _mm256_min_pd and _mm256_max_pd functions.
"min.pd.256" | "max.pd.256" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"min.pd.256" => FloatBinOp::Min,
@ -60,16 +58,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Used to implement the _mm256_round_ps function.
// Rounds the elements of `op` according to `rounding`.
"round.ps.256" => {
let [op, rounding] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
}
// Used to implement the _mm256_round_pd function.
// Rounds the elements of `op` according to `rounding`.
"round.pd.256" => {
let [op, rounding] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
}
@ -88,8 +84,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
// Used to implement the _mm256_dp_ps function.
"dp.ps.256" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
conditional_dot_product(this, left, right, imm, dest)?;
}
@ -97,8 +92,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Horizontally add/subtract adjacent floating point values
// in `left` and `right`.
"hadd.ps.256" | "hadd.pd.256" | "hsub.ps.256" | "hsub.pd.256" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"hadd.ps.256" | "hadd.pd.256" => mir::BinOp::Add,
@ -113,8 +107,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// and `right`. For each component, returns 0 if false or u32::MAX
// if true.
"cmp.ps.256" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -126,8 +119,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// and `right`. For each component, returns 0 if false or u64::MAX
// if true.
"cmp.pd.256" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -156,8 +148,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// sequence of 4-element arrays, and we shuffle each of these arrays, where
// `control` determines which element of the current `data` array is written.
"vpermilvar.ps" | "vpermilvar.ps.256" => {
let [data, control] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [data, control] = this.check_shim(abi, Conv::C, link_name, args)?;
let (data, data_len) = this.project_to_simd(data)?;
let (control, control_len) = this.project_to_simd(control)?;
@ -190,8 +181,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// where `right` determines which element of the current `left` array is
// written.
"vpermilvar.pd" | "vpermilvar.pd.256" => {
let [data, control] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [data, control] = this.check_shim(abi, Conv::C, link_name, args)?;
let (data, data_len) = this.project_to_simd(data)?;
let (control, control_len) = this.project_to_simd(control)?;
@ -223,8 +213,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// For each 128-bit element of `dest`, copies one from `left`, `right` or
// zero, according to `imm`.
"vperm2f128.ps.256" | "vperm2f128.pd.256" | "vperm2f128.si.256" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
assert_eq!(dest.layout, left.layout);
assert_eq!(dest.layout, right.layout);
@ -267,8 +256,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is
// loaded.
"maskload.ps" | "maskload.pd" | "maskload.ps.256" | "maskload.pd.256" => {
let [ptr, mask] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [ptr, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
mask_load(this, ptr, mask, dest)?;
}
@ -278,8 +266,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// is one, it is stored into `ptr.wapping_add(i)`.
// Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores.
"maskstore.ps" | "maskstore.pd" | "maskstore.ps.256" | "maskstore.pd.256" => {
let [ptr, mask, value] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [ptr, mask, value] = this.check_shim(abi, Conv::C, link_name, args)?;
mask_store(this, ptr, mask, value)?;
}
@ -289,8 +276,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// the data crosses a cache line, but for Miri this is just a regular
// unaligned read.
"ldu.dq.256" => {
let [src_ptr] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [src_ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
let src_ptr = this.read_pointer(src_ptr)?;
let dest = dest.force_mplace(this)?;
@ -302,8 +288,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Tests `op & mask == 0`, `op & mask == mask` or
// `op & mask != 0 && op & mask != mask`
"ptestz.256" | "ptestc.256" | "ptestnzc.256" => {
let [op, mask] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [op, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
let res = match unprefixed_name {
@ -326,8 +311,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"vtestz.pd.256" | "vtestc.pd.256" | "vtestnzc.pd.256" | "vtestz.pd" | "vtestc.pd"
| "vtestnzc.pd" | "vtestz.ps.256" | "vtestc.ps.256" | "vtestnzc.ps.256"
| "vtestz.ps" | "vtestc.ps" | "vtestnzc.ps" => {
let [op, mask] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [op, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
let (direct, negated) = test_high_bits_masked(this, op, mask)?;
let res = match unprefixed_name {

View File

@ -36,8 +36,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Horizontally add / add with saturation / subtract adjacent 16/32-bit
// integer values in `left` and `right`.
"phadd.w" | "phadd.sw" | "phadd.d" | "phsub.w" | "phsub.sw" | "phsub.d" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (which, saturating) = match unprefixed_name {
"phadd.w" | "phadd.d" => (mir::BinOp::Add, false),
@ -115,8 +114,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// intermediate signed 32-bit integers. Horizontally add adjacent pairs of
// intermediate 32-bit integers, and pack the results in `dest`.
"pmadd.wd" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -152,8 +150,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// the saturating sum of the products with indices `2*i` and `2*i+1`
// produces the output at index `i`.
"pmadd.ub.sw" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -187,8 +184,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is
// loaded.
"maskload.d" | "maskload.q" | "maskload.d.256" | "maskload.q.256" => {
let [ptr, mask] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [ptr, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
mask_load(this, ptr, mask, dest)?;
}
@ -198,8 +194,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// is one, it is stored into `ptr.wapping_add(i)`.
// Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores.
"maskstore.d" | "maskstore.q" | "maskstore.d.256" | "maskstore.q.256" => {
let [ptr, mask, value] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [ptr, mask, value] = this.check_shim(abi, Conv::C, link_name, args)?;
mask_store(this, ptr, mask, value)?;
}
@ -210,8 +205,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// offsets specified in `imm`.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mpsadbw_epu8
"mpsadbw" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
mpsadbw(this, left, right, imm, dest)?;
}
@ -222,8 +216,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// 1 and then taking the bits `1..=16`.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16
"pmul.hr.sw" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
pmulhrsw(this, left, right, dest)?;
}
@ -231,8 +224,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Converts two 16-bit integer vectors to a single 8-bit integer
// vector with signed saturation.
"packsswb" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
packsswb(this, left, right, dest)?;
}
@ -240,8 +232,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Converts two 32-bit integer vectors to a single 16-bit integer
// vector with signed saturation.
"packssdw" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
packssdw(this, left, right, dest)?;
}
@ -249,8 +240,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Converts two 16-bit signed integer vectors to a single 8-bit
// unsigned integer vector with saturation.
"packuswb" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
packuswb(this, left, right, dest)?;
}
@ -258,8 +248,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Concatenates two 32-bit signed integer vectors and converts
// the result to a 16-bit unsigned integer vector with saturation.
"packusdw" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
packusdw(this, left, right, dest)?;
}
@ -268,8 +257,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Shuffles `left` using the three low bits of each element of `right`
// as indices.
"permd" | "permps" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -289,8 +277,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Used to implement the _mm256_permute2x128_si256 function.
// Shuffles 128-bit blocks of `a` and `b` using `imm` as pattern.
"vperm2i128" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
assert_eq!(left.layout.size.bits(), 256);
assert_eq!(right.layout.size.bits(), 256);
@ -327,8 +314,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// in `dest`.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sad_epu8
"psad.bw" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -360,8 +346,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Shuffles bytes from `left` using `right` as pattern.
// Each 128-bit block is shuffled independently.
"pshuf.b" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -392,8 +377,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// is writen to the corresponding output element.
// Basically, we multiply `left` with `right.signum()`.
"psign.b" | "psign.w" | "psign.d" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
psign(this, left, right, dest)?;
}
@ -407,8 +391,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// is copied to remaining bits.
"psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q"
| "psrl.q" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
@ -423,8 +406,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// (except _mm{,256}_srav_epi64, which are not available in AVX2).
"psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" | "psrlv.d" | "psrlv.d.256"
| "psrlv.q" | "psrlv.q.256" | "psrav.d" | "psrav.d.256" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" => ShiftOp::Left,

View File

@ -1,5 +1,5 @@
use rustc_span::Symbol;
use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::{Conv, FnAbi};
use crate::*;
@ -34,8 +34,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(EmulateItemResult::NotSupported);
}
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let left = this.read_scalar(left)?;
let right = this.read_scalar(right)?;

View File

@ -1,5 +1,5 @@
use rustc_span::Symbol;
use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::{Conv, FnAbi};
use crate::*;
@ -30,16 +30,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// See `affine_transform` for details.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affine_
"vgf2p8affineqb.128" | "vgf2p8affineqb.256" | "vgf2p8affineqb.512" => {
let [left, right, imm8] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm8] = this.check_shim(abi, Conv::C, link_name, args)?;
affine_transform(this, left, right, imm8, dest, /* inverse */ false)?;
}
// Used to implement the `_mm{, 256, 512}_gf2p8affineinv_epi64_epi8` functions.
// See `affine_transform` for details.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affineinv
"vgf2p8affineinvqb.128" | "vgf2p8affineinvqb.256" | "vgf2p8affineinvqb.512" => {
let [left, right, imm8] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm8] = this.check_shim(abi, Conv::C, link_name, args)?;
affine_transform(this, left, right, imm8, dest, /* inverse */ true)?;
}
// Used to implement the `_mm{, 256, 512}_gf2p8mul_epi8` functions.
@ -48,8 +46,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// polynomial representation with the reduction polynomial x^8 + x^4 + x^3 + x + 1.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul
"vgf2p8mulb.128" | "vgf2p8mulb.256" | "vgf2p8mulb.512" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::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)?;

View File

@ -68,8 +68,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if is_u64 && this.tcx.sess.target.arch != "x86_64" {
return interp_ok(EmulateItemResult::NotSupported);
}
let [c_in, a, b, out] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [c_in, a, b, out] = this.check_shim(abi, Conv::C, link_name, args)?;
let out = this.deref_pointer_as(
out,
if is_u64 { this.machine.layouts.u64 } else { this.machine.layouts.u32 },
@ -105,8 +104,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
len = 8;
}
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
pclmulqdq(this, left, right, imm, dest, len)?;
}

View File

@ -52,8 +52,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match unprefixed_name {
// Used to implement the _mm_sha256rnds2_epu32 function.
"256rnds2" => {
let [a, b, k] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [a, b, k] = this.check_shim(abi, Conv::C, link_name, args)?;
let (a_reg, a_len) = this.project_to_simd(a)?;
let (b_reg, b_len) = this.project_to_simd(b)?;
@ -74,8 +73,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
// Used to implement the _mm_sha256msg1_epu32 function.
"256msg1" => {
let [a, b] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [a, b] = this.check_shim(abi, Conv::C, link_name, args)?;
let (a_reg, a_len) = this.project_to_simd(a)?;
let (b_reg, b_len) = this.project_to_simd(b)?;
@ -93,8 +91,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
// Used to implement the _mm_sha256msg2_epu32 function.
"256msg2" => {
let [a, b] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [a, b] = this.check_shim(abi, Conv::C, link_name, args)?;
let (a_reg, a_len) = this.project_to_simd(a)?;
let (b_reg, b_len) = this.project_to_simd(b)?;

View File

@ -33,8 +33,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Performs the operations on the first component of `left` and
// `right` and copies the remaining components from `left`.
"min.ss" | "max.ss" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"min.ss" => FloatBinOp::Min,
@ -50,8 +49,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// matches the IEEE min/max operations, while x86 has different
// semantics.
"min.ps" | "max.ps" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"min.ps" => FloatBinOp::Min,
@ -97,8 +95,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ss are SSE functions
// with hard-coded operations.
"cmp.ss" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -114,8 +111,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ps are SSE functions
// with hard-coded operations.
"cmp.ps" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -128,8 +124,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"comieq.ss" | "comilt.ss" | "comile.ss" | "comigt.ss" | "comige.ss" | "comineq.ss"
| "ucomieq.ss" | "ucomilt.ss" | "ucomile.ss" | "ucomigt.ss" | "ucomige.ss"
| "ucomineq.ss" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -185,8 +180,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// are copied from `left`.
// https://www.felixcloutier.com/x86/cvtsi2ss
"cvtsi2ss" | "cvtsi642ss" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (dest, dest_len) = this.project_to_simd(dest)?;

View File

@ -40,8 +40,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// intermediate signed 32-bit integers. Horizontally add adjacent pairs of
// intermediate 32-bit integers, and pack the results in `dest`.
"pmadd.wd" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -79,8 +78,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
//
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8
"psad.bw" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -118,8 +116,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// is copied to remaining bits.
"psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q"
| "psrl.q" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
@ -171,8 +168,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Converts two 16-bit integer vectors to a single 8-bit integer
// vector with signed saturation.
"packsswb.128" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
packsswb(this, left, right, dest)?;
}
@ -180,8 +176,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Converts two 16-bit signed integer vectors to a single 8-bit
// unsigned integer vector with saturation.
"packuswb.128" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
packuswb(this, left, right, dest)?;
}
@ -189,8 +184,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Converts two 32-bit integer vectors to a single 16-bit integer
// vector with signed saturation.
"packssdw.128" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
packssdw(this, left, right, dest)?;
}
@ -200,8 +194,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// matches the IEEE min/max operations, while x86 has different
// semantics.
"min.sd" | "max.sd" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"min.sd" => FloatBinOp::Min,
@ -217,8 +210,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// matches the IEEE min/max operations, while x86 has different
// semantics.
"min.pd" | "max.pd" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"min.pd" => FloatBinOp::Min,
@ -237,8 +229,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_sd are SSE2 functions
// with hard-coded operations.
"cmp.sd" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -254,8 +245,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_pd are SSE2 functions
// with hard-coded operations.
"cmp.pd" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -268,8 +258,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"comieq.sd" | "comilt.sd" | "comile.sd" | "comigt.sd" | "comige.sd" | "comineq.sd"
| "ucomieq.sd" | "ucomilt.sd" | "ucomile.sd" | "ucomigt.sd" | "ucomige.sd"
| "ucomineq.sd" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -323,8 +312,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Converts the first f64/f32 from `right` to f32/f64 and copies
// the remaining elements from `left`
"cvtsd2ss" | "cvtss2sd" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, _) = this.project_to_simd(right)?;

View File

@ -25,8 +25,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Horizontally add/subtract adjacent floating point values
// in `left` and `right`.
"hadd.ps" | "hadd.pd" | "hsub.ps" | "hsub.pd" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let which = match unprefixed_name {
"hadd.ps" | "hadd.pd" => mir::BinOp::Add,
@ -42,8 +41,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// the data crosses a cache line, but for Miri this is just a regular
// unaligned read.
"ldu.dq" => {
let [src_ptr] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [src_ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
let src_ptr = this.read_pointer(src_ptr)?;
let dest = dest.force_mplace(this)?;

View File

@ -27,8 +27,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// bits `4..=5` if `imm`, and `i`th bit specifies whether element
// `i` is zeroed.
"insertps" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -63,8 +62,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Concatenates two 32-bit signed integer vectors and converts
// the result to a 16-bit unsigned integer vector with saturation.
"packusdw" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
packusdw(this, left, right, dest)?;
}
@ -74,8 +72,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// products, and conditionally stores the sum in `dest` using the low
// 4 bits of `imm`.
"dpps" | "dppd" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
conditional_dot_product(this, left, right, imm, dest)?;
}
@ -83,16 +80,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// functions. Rounds the first element of `right` according to `rounding`
// and copies the remaining elements from `left`.
"round.ss" => {
let [left, right, rounding] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
round_first::<rustc_apfloat::ieee::Single>(this, left, right, rounding, dest)?;
}
// Used to implement the _mm_floor_ps, _mm_ceil_ps and _mm_round_ps
// functions. Rounds the elements of `op` according to `rounding`.
"round.ps" => {
let [op, rounding] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
}
@ -100,16 +95,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// functions. Rounds the first element of `right` according to `rounding`
// and copies the remaining elements from `left`.
"round.sd" => {
let [left, right, rounding] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
round_first::<rustc_apfloat::ieee::Double>(this, left, right, rounding, dest)?;
}
// Used to implement the _mm_floor_pd, _mm_ceil_pd and _mm_round_pd
// functions. Rounds the elements of `op` according to `rounding`.
"round.pd" => {
let [op, rounding] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
}
@ -151,8 +144,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// offsets specified in `imm`.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mpsadbw_epu8
"mpsadbw" => {
let [left, right, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
mpsadbw(this, left, right, imm, dest)?;
}
@ -161,8 +153,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Tests `(op & mask) == 0`, `(op & mask) == mask` or
// `(op & mask) != 0 && (op & mask) != mask`
"ptestz" | "ptestc" | "ptestnzc" => {
let [op, mask] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [op, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
let res = match unprefixed_name {

View File

@ -223,8 +223,7 @@ fn deconstruct_args<'tcx>(
};
if is_explicit {
let [str1, len1, str2, len2, imm] =
ecx.check_shim(abi, Conv::C, link_name, args)?;
let [str1, len1, str2, len2, imm] = ecx.check_shim(abi, Conv::C, link_name, args)?;
let imm = ecx.read_scalar(imm)?.to_u8()?;
let default_len = default_len::<u32>(imm);
@ -237,8 +236,7 @@ fn deconstruct_args<'tcx>(
interp_ok((str1, str2, Some((len1, len2)), imm))
} else {
let [str1, str2, imm] =
ecx.check_shim(abi, Conv::C, link_name, args)?;
let [str1, str2, imm] = ecx.check_shim(abi, Conv::C, link_name, args)?;
let imm = ecx.read_scalar(imm)?.to_u8()?;
let array_layout = array_layout_fn(ecx, imm)?;
@ -388,8 +386,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// search for a null terminator (see `deconstruct_args` for more details).
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=924,925
"pcmpistriz128" | "pcmpistris128" => {
let [str1, str2, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [str1, str2, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
let imm = this.read_scalar(imm)?.to_u8()?;
let str = if unprefixed_name == "pcmpistris128" { str1 } else { str2 };
@ -409,8 +406,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// than 16 for byte-sized operands or 8 for word-sized operands.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1046,1047
"pcmpestriz128" | "pcmpestris128" => {
let [_, len1, _, len2, imm] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [_, len1, _, len2, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
let len = if unprefixed_name == "pcmpestris128" { len1 } else { len2 };
let len = this.read_scalar(len)?.to_i32()?;
let imm = this.read_scalar(imm)?.to_u8()?;
@ -437,8 +433,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(EmulateItemResult::NotSupported);
}
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let left = this.read_scalar(left)?;
let right = this.read_scalar(right)?;

View File

@ -32,8 +32,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Shuffles bytes from `left` using `right` as pattern.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8
"pshuf.b.128" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -62,8 +61,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// integer values in `left` and `right`.
"phadd.w.128" | "phadd.sw.128" | "phadd.d.128" | "phsub.w.128" | "phsub.sw.128"
| "phsub.d.128" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (which, saturating) = match unprefixed_name {
"phadd.w.128" | "phadd.d.128" => (mir::BinOp::Add, false),
@ -82,8 +80,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// produces the output at index `i`.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16
"pmadd.ub.sw.128" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -118,8 +115,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// 1 and then taking the bits `1..=16`.
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16
"pmul.hr.sw.128" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
pmulhrsw(this, left, right, dest)?;
}
@ -129,8 +125,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// is writen to the corresponding output element.
// Basically, we multiply `left` with `right.signum()`.
"psign.b.128" | "psign.w.128" | "psign.d.128" => {
let [left, right] =
this.check_shim(abi, Conv::C, link_name, args)?;
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
psign(this, left, right, dest)?;
}

View File

@ -0,0 +1,47 @@
//@ignore-target: windows # No libc socketpair on Windows
//~^ERROR: deadlocked
//~^^ERROR: deadlocked
// test_race depends on a deterministic schedule.
//@compile-flags: -Zmiri-preemption-rate=0
//@error-in-other-file: deadlock
use std::thread;
// Test the behaviour of a thread being blocked on read, get unblocked, then blocked again.
// The expected execution is
// 1. Thread 1 blocks.
// 2. Thread 2 blocks.
// 3. Thread 3 unblocks both thread 1 and thread 2.
// 4. Thread 1 reads.
// 5. Thread 2's `read` can never complete -> deadlocked.
fn main() {
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
let thread1 = thread::spawn(move || {
// Let this thread block on read.
let mut buf: [u8; 3] = [0; 3];
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(&buf, "abc".as_bytes());
});
let thread2 = thread::spawn(move || {
// Let this thread block on read.
let mut buf: [u8; 3] = [0; 3];
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
//~^ERROR: deadlocked
assert_eq!(res, 3);
assert_eq!(&buf, "abc".as_bytes());
});
let thread3 = thread::spawn(move || {
// Unblock thread1 by writing something.
let data = "abc".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
assert_eq!(res, 3);
});
thread1.join().unwrap();
thread2.join().unwrap();
thread3.join().unwrap();
}

View File

@ -0,0 +1,41 @@
error: deadlock: the evaluated program deadlocked
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
| ^ the evaluated program deadlocked
|
= note: BACKTRACE:
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
note: inside `main`
--> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC
|
LL | thread2.join().unwrap();
| ^^^^^^^^^^^^^^
error: deadlock: the evaluated program deadlocked
|
= note: the evaluated program deadlocked
= note: (no span available)
= note: BACKTRACE on thread `unnamed-ID`:
error: deadlock: the evaluated program deadlocked
--> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC
|
LL | let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
| ^ the evaluated program deadlocked
|
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC
error: deadlock: the evaluated program deadlocked
|
= note: the evaluated program deadlocked
= note: (no span available)
= note: BACKTRACE on thread `unnamed-ID`:
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 4 previous errors

View File

@ -0,0 +1,49 @@
//@ignore-target: windows # No libc socketpair on Windows
//~^ERROR: deadlocked
//~^^ERROR: deadlocked
// test_race depends on a deterministic schedule.
//@compile-flags: -Zmiri-preemption-rate=0
//@error-in-other-file: deadlock
use std::thread;
// Test the behaviour of a thread being blocked on write, get unblocked, then blocked again.
// The expected execution is
// 1. Thread 1 blocks.
// 2. Thread 2 blocks.
// 3. Thread 3 unblocks both thread 1 and thread 2.
// 4. Thread 1 reads.
// 5. Thread 2's `write` can never complete -> deadlocked.
fn main() {
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
let arr1: [u8; 212992] = [1; 212992];
// Exhaust the space in the buffer so the subsequent write will block.
let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) };
assert_eq!(res, 212992);
let thread1 = thread::spawn(move || {
let data = "abc".as_bytes().as_ptr();
// The write below will be blocked because the buffer is already full.
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
assert_eq!(res, 3);
});
let thread2 = thread::spawn(move || {
let data = "abc".as_bytes().as_ptr();
// The write below will be blocked because the buffer is already full.
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
//~^ERROR: deadlocked
assert_eq!(res, 3);
});
let thread3 = thread::spawn(move || {
// Unblock thread1 by freeing up some space.
let mut buf: [u8; 3] = [0; 3];
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(buf, [1, 1, 1]);
});
thread1.join().unwrap();
thread2.join().unwrap();
thread3.join().unwrap();
}

View File

@ -0,0 +1,41 @@
error: deadlock: the evaluated program deadlocked
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
| ^ the evaluated program deadlocked
|
= note: BACKTRACE:
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
note: inside `main`
--> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC
|
LL | thread2.join().unwrap();
| ^^^^^^^^^^^^^^
error: deadlock: the evaluated program deadlocked
|
= note: the evaluated program deadlocked
= note: (no span available)
= note: BACKTRACE on thread `unnamed-ID`:
error: deadlock: the evaluated program deadlocked
--> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC
|
LL | let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
| ^ the evaluated program deadlocked
|
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC
error: deadlock: the evaluated program deadlocked
|
= note: the evaluated program deadlocked
= note: (no span available)
= note: BACKTRACE on thread `unnamed-ID`:
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 4 previous errors

View File

@ -1,12 +0,0 @@
//@ignore-target: windows # no libc socketpair on Windows
// This is temporarily here because blocking on fd is not supported yet.
// When blocking is eventually supported, this will be moved to pass-dep/libc/libc-socketpair
fn main() {
let mut fds = [-1, -1];
let _ = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
// The read below will be blocked because the buffer is empty.
let mut buf: [u8; 3] = [0; 3];
let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; //~ERROR: blocking isn't supported
}

View File

@ -1,10 +1,9 @@
error: unsupported operation: socketpair/pipe/pipe2 read: blocking isn't supported yet
error: deadlock: the evaluated program deadlocked
--> tests/fail-dep/libc/socketpair_read_blocking.rs:LL:CC
|
LL | let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair/pipe/pipe2 read: blocking isn't supported yet
| ^ the evaluated program deadlocked
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
= note: BACKTRACE:
= note: inside `main` at tests/fail-dep/libc/socketpair_read_blocking.rs:LL:CC

View File

@ -1,16 +0,0 @@
//@ignore-target: windows # no libc socketpair on Windows
// This is temporarily here because blocking on fd is not supported yet.
// When blocking is eventually supported, this will be moved to pass-dep/libc/libc-socketpair
fn main() {
let mut fds = [-1, -1];
let _ = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
// Write size > buffer capacity
// Used up all the space in the buffer.
let arr1: [u8; 212992] = [1; 212992];
let _ = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) };
let data = "abc".as_bytes().as_ptr();
// The write below will be blocked as the buffer is full.
let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; //~ERROR: blocking isn't supported
let mut buf: [u8; 3] = [0; 3];
let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
}

View File

@ -1,10 +1,9 @@
error: unsupported operation: socketpair/pipe/pipe2 write: blocking isn't supported yet
error: deadlock: the evaluated program deadlocked
--> tests/fail-dep/libc/socketpair_write_blocking.rs:LL:CC
|
LL | let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair/pipe/pipe2 write: blocking isn't supported yet
| ^ the evaluated program deadlocked
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
= note: BACKTRACE:
= note: inside `main` at tests/fail-dep/libc/socketpair_write_blocking.rs:LL:CC

View File

@ -0,0 +1,29 @@
//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0
// Shows the effect of the optimization of #4008.
// The diagnostics change, but not the error itself.
// When this method is called, the tree will be a single line and look like this,
// with other_ptr being the root at the top
// other_ptr = root : Active
// intermediary : Frozen // an intermediary node
// m : Reserved
fn write_to_mut(m: &mut u8, other_ptr: *const u8) {
unsafe {
std::hint::black_box(*other_ptr);
}
// In line 17 above, m should have become Reserved (protected) so that this write is impossible.
// However, that does not happen because the read above is not forwarded to the subtree below
// the Frozen intermediary node. This does not affect UB, however, because the Frozen that blocked
// the read already prevents any child writes.
*m = 42; //~ERROR: /write access through .* is forbidden/
}
fn main() {
let root = 42u8;
unsafe {
let intermediary = &root;
let data = &mut *(core::ptr::addr_of!(*intermediary) as *mut u8);
write_to_mut(data, core::ptr::addr_of!(root));
}
}

View File

@ -0,0 +1,31 @@
error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
LL | *m = 42;
| ^^^^^^^ write access through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Frozen which forbids this child write access
help: the accessed tag <TAG> was created here
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
LL | fn write_to_mut(m: &mut u8, other_ptr: *const u8) {
| ^
help: the conflicting tag <TAG> was created here, in the initial state Frozen
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
LL | let intermediary = &root;
| ^^^^^
= note: BACKTRACE (of the first span):
= note: inside `write_to_mut` at tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
note: inside `main`
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
LL | write_to_mut(data, core::ptr::addr_of!(root));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -0,0 +1,18 @@
warning: sharing memory with a native function
--> tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
LL | unsafe { print_pointer(&x) };
| ^^^^^^^^^^^^^^^^^ sharing memory with a native function called via FFI
|
= help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory
= help: in particular, Miri assumes that the native call initializes all memory it has access to
= help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
= help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
= note: BACKTRACE:
= note: inside `test_access_pointer` at tests/native-lib/pass/ptr_read_access.rs:LL:CC
note: inside `main`
--> tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
LL | test_access_pointer();
| ^^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,18 @@
warning: sharing memory with a native function
--> tests/native-lib/pass/ptr_write_access.rs:LL:CC
|
LL | unsafe { increment_int(&mut x) };
| ^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function called via FFI
|
= help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory
= help: in particular, Miri assumes that the native call initializes all memory it has access to
= help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
= help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
= note: BACKTRACE:
= note: inside `test_increment_int` at tests/native-lib/pass/ptr_write_access.rs:LL:CC
note: inside `main`
--> tests/native-lib/pass/ptr_write_access.rs:LL:CC
|
LL | test_increment_int();
| ^^^^^^^^^^^^^^^^^^^^

View File

@ -10,6 +10,8 @@ fn main() {
test_socketpair();
test_socketpair_threaded();
test_race();
test_blocking_read();
test_blocking_write();
}
fn test_socketpair() {
@ -136,3 +138,51 @@ fn test_race() {
thread::yield_now();
thread1.join().unwrap();
}
// Test the behaviour of a socketpair getting blocked on read and subsequently unblocked.
fn test_blocking_read() {
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
let thread1 = thread::spawn(move || {
// Let this thread block on read.
let mut buf: [u8; 3] = [0; 3];
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(&buf, "abc".as_bytes());
});
let thread2 = thread::spawn(move || {
// Unblock thread1 by doing writing something.
let data = "abc".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
assert_eq!(res, 3);
});
thread1.join().unwrap();
thread2.join().unwrap();
}
// Test the behaviour of a socketpair getting blocked on write and subsequently unblocked.
fn test_blocking_write() {
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
let arr1: [u8; 212992] = [1; 212992];
// Exhaust the space in the buffer so the subsequent write will block.
let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) };
assert_eq!(res, 212992);
let thread1 = thread::spawn(move || {
let data = "abc".as_bytes().as_ptr();
// The write below will be blocked because the buffer is already full.
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
assert_eq!(res, 3);
});
let thread2 = thread::spawn(move || {
// Unblock thread1 by freeing up some space.
let mut buf: [u8; 3] = [0; 3];
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(buf, [1, 1, 1]);
});
thread1.join().unwrap();
thread2.join().unwrap();
}

View File

@ -5,7 +5,21 @@ use std::{env, mem, ptr};
fn main() {
test_clocks();
test_posix_gettimeofday();
test_localtime_r();
test_localtime_r_gmt();
test_localtime_r_pst();
test_localtime_r_epoch();
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "android"
))]
test_localtime_r_multiple_calls_deduplication();
// Architecture-specific tests.
#[cfg(target_pointer_width = "32")]
test_localtime_r_future_32b();
#[cfg(target_pointer_width = "64")]
test_localtime_r_future_64b();
}
/// Tests whether clock support exists at all
@ -46,14 +60,9 @@ fn test_posix_gettimeofday() {
assert_eq!(is_error, -1);
}
fn test_localtime_r() {
// Set timezone to GMT.
let key = "TZ";
env::set_var(key, "GMT");
const TIME_SINCE_EPOCH: libc::time_t = 1712475836;
let custom_time_ptr = &TIME_SINCE_EPOCH;
let mut tm = libc::tm {
/// Helper function to create an empty tm struct.
fn create_empty_tm() -> libc::tm {
libc::tm {
tm_sec: 0,
tm_min: 0,
tm_hour: 0,
@ -77,7 +86,17 @@ fn test_localtime_r() {
target_os = "android"
))]
tm_zone: std::ptr::null_mut::<libc::c_char>(),
};
}
}
/// Original GMT test
fn test_localtime_r_gmt() {
// Set timezone to GMT.
let key = "TZ";
env::set_var(key, "GMT");
const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT
let custom_time_ptr = &TIME_SINCE_EPOCH;
let mut tm = create_empty_tm();
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
assert_eq!(tm.tm_sec, 56);
@ -95,16 +114,12 @@ fn test_localtime_r() {
target_os = "freebsd",
target_os = "android"
))]
assert_eq!(tm.tm_gmtoff, 0);
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "android"
))]
unsafe {
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00")
};
{
assert_eq!(tm.tm_gmtoff, 0);
unsafe {
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
}
}
// The returned value is the pointer passed in.
assert!(ptr::eq(res, &mut tm));
@ -112,3 +127,191 @@ fn test_localtime_r() {
// Remove timezone setting.
env::remove_var(key);
}
/// PST timezone test (testing different timezone handling).
fn test_localtime_r_pst() {
let key = "TZ";
env::set_var(key, "PST8PDT");
const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT
let custom_time_ptr = &TIME_SINCE_EPOCH;
let mut tm = create_empty_tm();
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
assert_eq!(tm.tm_sec, 56);
assert_eq!(tm.tm_min, 43);
assert_eq!(tm.tm_hour, 0); // 7 - 7 = 0 (PDT offset)
assert_eq!(tm.tm_mday, 7);
assert_eq!(tm.tm_mon, 3);
assert_eq!(tm.tm_year, 124);
assert_eq!(tm.tm_wday, 0);
assert_eq!(tm.tm_yday, 97);
assert_eq!(tm.tm_isdst, -1); // DST information unavailable
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "android"
))]
{
assert_eq!(tm.tm_gmtoff, -7 * 3600); // -7 hours in seconds
unsafe {
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "-07");
}
}
assert!(ptr::eq(res, &mut tm));
env::remove_var(key);
}
/// Unix epoch test (edge case testing).
fn test_localtime_r_epoch() {
let key = "TZ";
env::set_var(key, "GMT");
const TIME_SINCE_EPOCH: libc::time_t = 0; // 1970-01-01 00:00:00
let custom_time_ptr = &TIME_SINCE_EPOCH;
let mut tm = create_empty_tm();
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
assert_eq!(tm.tm_sec, 0);
assert_eq!(tm.tm_min, 0);
assert_eq!(tm.tm_hour, 0);
assert_eq!(tm.tm_mday, 1);
assert_eq!(tm.tm_mon, 0);
assert_eq!(tm.tm_year, 70);
assert_eq!(tm.tm_wday, 4); // Thursday
assert_eq!(tm.tm_yday, 0);
assert_eq!(tm.tm_isdst, -1);
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "android"
))]
{
assert_eq!(tm.tm_gmtoff, 0);
unsafe {
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
}
}
assert!(ptr::eq(res, &mut tm));
env::remove_var(key);
}
/// Future date test (testing large values).
#[cfg(target_pointer_width = "64")]
fn test_localtime_r_future_64b() {
let key = "TZ";
env::set_var(key, "GMT");
// Using 2050-01-01 00:00:00 for 64-bit systems
// value that's safe for 64-bit time_t
const TIME_SINCE_EPOCH: libc::time_t = 2524608000;
let custom_time_ptr = &TIME_SINCE_EPOCH;
let mut tm = create_empty_tm();
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
assert_eq!(tm.tm_sec, 0);
assert_eq!(tm.tm_min, 0);
assert_eq!(tm.tm_hour, 0);
assert_eq!(tm.tm_mday, 1);
assert_eq!(tm.tm_mon, 0);
assert_eq!(tm.tm_year, 150); // 2050 - 1900
assert_eq!(tm.tm_wday, 6); // Saturday
assert_eq!(tm.tm_yday, 0);
assert_eq!(tm.tm_isdst, -1);
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "android"
))]
{
assert_eq!(tm.tm_gmtoff, 0);
unsafe {
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
}
}
assert!(ptr::eq(res, &mut tm));
env::remove_var(key);
}
/// Future date test (testing large values for 32b target).
#[cfg(target_pointer_width = "32")]
fn test_localtime_r_future_32b() {
let key = "TZ";
env::set_var(key, "GMT");
// Using 2030-01-01 00:00:00 for 32-bit systems
// Safe value within i32 range
const TIME_SINCE_EPOCH: libc::time_t = 1893456000;
let custom_time_ptr = &TIME_SINCE_EPOCH;
let mut tm = create_empty_tm();
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
// Verify 2030-01-01 00:00:00
assert_eq!(tm.tm_sec, 0);
assert_eq!(tm.tm_min, 0);
assert_eq!(tm.tm_hour, 0);
assert_eq!(tm.tm_mday, 1);
assert_eq!(tm.tm_mon, 0);
assert_eq!(tm.tm_year, 130); // 2030 - 1900
assert_eq!(tm.tm_wday, 2); // Tuesday
assert_eq!(tm.tm_yday, 0);
assert_eq!(tm.tm_isdst, -1);
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "android"
))]
{
assert_eq!(tm.tm_gmtoff, 0);
unsafe {
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
}
}
assert!(ptr::eq(res, &mut tm));
env::remove_var(key);
}
/// Tests the behavior of `localtime_r` with multiple calls to ensure deduplication of `tm_zone` pointers.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "android"))]
fn test_localtime_r_multiple_calls_deduplication() {
let key = "TZ";
env::set_var(key, "PST8PDT");
const TIME_SINCE_EPOCH_BASE: libc::time_t = 1712475836; // Base timestamp: 2024-04-07 07:43:56 GMT
const NUM_CALLS: usize = 50;
let mut unique_pointers = std::collections::HashSet::new();
for i in 0..NUM_CALLS {
let timestamp = TIME_SINCE_EPOCH_BASE + (i as libc::time_t * 3600); // Increment by 1 hour for each call
let mut tm: libc::tm = create_empty_tm();
let tm_ptr = unsafe { libc::localtime_r(&timestamp, &mut tm) };
assert!(!tm_ptr.is_null(), "localtime_r failed for timestamp {timestamp}");
unique_pointers.insert(tm.tm_zone);
}
let unique_count = unique_pointers.len();
assert!(
unique_count >= 2 && unique_count <= (NUM_CALLS - 1),
"Unexpected number of unique tm_zone pointers: {} (expected between 2 and {})",
unique_count,
NUM_CALLS - 1
);
}

View File

@ -27,11 +27,8 @@ fn main() {
test_file_sync();
test_errors();
test_rename();
// solarish needs to support readdir/readdir64 for these tests.
if cfg!(not(any(target_os = "solaris", target_os = "illumos"))) {
test_directory();
test_canonicalize();
}
test_directory();
test_canonicalize();
test_from_raw_os_error();
#[cfg(unix)]
test_pread_pwrite();
@ -279,7 +276,12 @@ fn test_directory() {
.collect::<BTreeMap<_, _>>()
);
// Deleting the directory should fail, since it is not empty.
assert_eq!(ErrorKind::DirectoryNotEmpty, remove_dir(&dir_path).unwrap_err().kind());
// Solaris/Illumos `rmdir` call set errno to EEXIST if directory contains
// other entries than `.` and `..`.
// https://docs.oracle.com/cd/E86824_01/html/E54765/rmdir-2.html
let err = remove_dir(&dir_path).unwrap_err().kind();
assert!(matches!(err, ErrorKind::AlreadyExists | ErrorKind::DirectoryNotEmpty));
// Clean up the files in the directory
remove_file(&path_1).unwrap();
remove_file(&path_2).unwrap();

View File

@ -29,3 +29,6 @@ review_labels = ["S-waiting-on-review"]
remove_labels = ["S-waiting-on-author"]
# Those labels are added when PR author requests a review from an assignee
add_labels = ["S-waiting-on-review"]
# Automatically close and reopen PRs made by bots to run CI on them
[bot-pull-requests]