Rollup merge of #144623 - RalfJung:miri, r=RalfJung

miri subtree update

Subtree update of `miri` to fc4d9a2720.

Created using https://github.com/rust-lang/josh-sync.

r? `@ghost`
This commit is contained in:
Stuart Cook 2025-07-29 20:19:53 +10:00 committed by GitHub
commit f034a4fa14
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
103 changed files with 2749 additions and 1472 deletions

View file

@ -466,6 +466,8 @@ version = "1.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
dependencies = [
"jobserver",
"libc",
"shlex",
]
@ -655,6 +657,26 @@ dependencies = [
"serde",
]
[[package]]
name = "cmake"
version = "0.1.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
dependencies = [
"cc",
]
[[package]]
name = "codespan-reporting"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
dependencies = [
"serde",
"termcolor",
"unicode-width 0.2.1",
]
[[package]]
name = "collect-license-metadata"
version = "0.1.0"
@ -913,6 +935,68 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "cxx"
version = "1.0.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df"
dependencies = [
"cc",
"cxxbridge-cmd",
"cxxbridge-flags",
"cxxbridge-macro",
"foldhash",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909"
dependencies = [
"cc",
"codespan-reporting",
"indexmap",
"proc-macro2",
"quote",
"scratch",
"syn 2.0.104",
]
[[package]]
name = "cxxbridge-cmd"
version = "1.0.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a"
dependencies = [
"clap",
"codespan-reporting",
"indexmap",
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d"
[[package]]
name = "cxxbridge-macro"
version = "1.0.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4"
dependencies = [
"indexmap",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.104",
]
[[package]]
name = "darling"
version = "0.20.11"
@ -1373,6 +1457,17 @@ dependencies = [
"version_check",
]
[[package]]
name = "genmc-sys"
version = "0.1.0"
dependencies = [
"cc",
"cmake",
"cxx",
"cxx-build",
"git2",
]
[[package]]
name = "getopts"
version = "0.2.23"
@ -1427,6 +1522,21 @@ dependencies = [
"stable_deref_trait",
]
[[package]]
name = "git2"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110"
dependencies = [
"bitflags",
"libc",
"libgit2-sys",
"log",
"openssl-probe",
"openssl-sys",
"url",
]
[[package]]
name = "glob"
version = "0.3.2"
@ -2060,6 +2170,19 @@ dependencies = [
"cc",
]
[[package]]
name = "libgit2-sys"
version = "0.18.2+1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222"
dependencies = [
"cc",
"libc",
"libz-sys",
"openssl-sys",
"pkg-config",
]
[[package]]
name = "libloading"
version = "0.8.8"
@ -2099,6 +2222,15 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "link-cplusplus"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212"
dependencies = [
"cc",
]
[[package]]
name = "linkchecker"
version = "0.1.0"
@ -2308,6 +2440,7 @@ dependencies = [
"chrono-tz",
"colored 3.0.0",
"directories",
"genmc-sys",
"getrandom 0.3.3",
"ipc-channel",
"libc",
@ -4877,6 +5010,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scratch"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52"
[[package]]
name = "self_cell"
version = "1.2.0"

View file

@ -3,6 +3,7 @@
/// See <https://github.com/rust-lang/rust/issues/134863>
pub static CRATES: &[&str] = &[
// tidy-alphabetical-start
"allocator-api2",
"annotate-snippets",
"anstyle",
"askama_parser",
@ -16,13 +17,17 @@ pub static CRATES: &[&str] = &[
"darling_core",
"derive_builder_core",
"digest",
"equivalent",
"fluent-bundle",
"fluent-langneg",
"fluent-syntax",
"fnv",
"foldhash",
"generic-array",
"hashbrown",
"heck",
"ident_case",
"indexmap",
"intl-memoizer",
"intl_pluralrules",
"libc",

View file

@ -45,11 +45,17 @@ jobs:
os: macos-latest
- host_target: i686-pc-windows-msvc
os: windows-latest
- host_target: aarch64-pc-windows-msvc
os: windows-11-arm
runs-on: ${{ matrix.os }}
env:
HOST_TARGET: ${{ matrix.host_target }}
steps:
- uses: actions/checkout@v4
- name: apt update
if: ${{ startsWith(matrix.os, 'ubuntu') }}
# The runners seem to have outdated apt repos sometimes
run: sudo apt update
- name: install qemu
if: ${{ matrix.qemu }}
run: sudo apt install qemu-user qemu-user-binfmt
@ -63,6 +69,12 @@ jobs:
sudo apt update
# Install needed packages
sudo apt install $(echo "libatomic1: zlib1g-dev:" | sed 's/:/:${{ matrix.multiarch }}/g')
- name: Install rustup on Windows ARM
if: ${{ matrix.os == 'windows-11-arm' }}
run: |
curl -LOs https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe
./rustup-init.exe -y --no-modify-path
echo "$USERPROFILE/.cargo/bin" >> "$GITHUB_PATH"
- uses: ./.github/workflows/setup
with:
toolchain_flags: "--host ${{ matrix.host_target }}"
@ -147,35 +159,48 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 256 # get a bit more of the history
- name: install josh-proxy
run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04
- name: install josh-sync
run: cargo +stable install --locked --git https://github.com/rust-lang/josh-sync
- name: setup bot git name and email
run: |
git config --global user.name 'The Miri Cronjob Bot'
git config --global user.email 'miri@cron.bot'
- name: Install nightly toolchain
run: rustup toolchain install nightly --profile minimal
- name: get changes from rustc
run: ./miri rustc-pull
- name: Install rustup-toolchain-install-master
run: cargo install -f rustup-toolchain-install-master
- name: format changes (if any)
run: |
./miri toolchain
./miri fmt --check || (./miri fmt && git commit -am "fmt")
- name: Push changes to a branch and create PR
run: |
# `git diff --exit-code` "succeeds" if the diff is empty.
if git diff --exit-code HEAD^; then echo "Nothing changed in rustc, skipping PR"; exit 0; fi
# The diff is non-empty, create a PR.
# Make it easier to see what happens.
set -x
# Temporarily disable early exit to examine the status code of rustc-josh-sync
set +e
rustc-josh-sync pull
exitcode=$?
set -e
# If there were no changes to pull, rustc-josh-sync returns status code 2.
# In that case, skip the rest of the job.
if [ $exitcode -eq 2 ]; then
echo "Nothing changed in rustc, skipping PR"
exit 0
elif [ $exitcode -ne 0 ]; then
# If return code was not 0 or 2, rustc-josh-sync actually failed
echo "error: rustc-josh-sync failed"
exit ${exitcode}
fi
# Format changes
./miri toolchain
./miri fmt --check || (./miri fmt && git commit -am "fmt")
# Create a PR
BRANCH="rustup-$(date -u +%Y-%m-%d)"
git switch -c $BRANCH
git push -u origin $BRANCH
gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }}
ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }}
cron-fail-notify:
name: cronjob failure notification
@ -198,7 +223,7 @@ jobs:
It would appear that the [Miri cron job build]('"https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"') failed.
This likely means that rustc changed the miri directory and
we now need to do a [`./miri rustc-pull`](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#importing-changes-from-the-rustc-repo).
we now need to do a [`rustc-josh-sync pull`](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#importing-changes-from-the-rustc-repo).
Would you mind investigating this issue?

View file

@ -1,5 +1,4 @@
target
/doc
tex/*/out
*.dot
*.out

View file

@ -297,14 +297,14 @@ You can also directly run Miri on a Rust source file:
## Advanced topic: Syncing with the rustc repo
We use the [`josh` proxy](https://github.com/josh-project/josh) to transmit changes between the
We use the [`josh-sync`](https://github.com/rust-lang/josh-sync) tool to transmit changes between the
rustc and Miri repositories. You can install it as follows:
```sh
cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04
cargo install --locked --git https://github.com/rust-lang/josh-sync
```
Josh will automatically be started and stopped by `./miri`.
The commands below will automatically install and manage the [Josh](https://github.com/josh-project/josh) proxy that performs the actual work.
### Importing changes from the rustc repo
@ -312,10 +312,12 @@ Josh will automatically be started and stopped by `./miri`.
We assume we start on an up-to-date master branch in the Miri repo.
1) First, create a branch for the pull, e.g. `git checkout -b rustup`
2) Then run the following:
```sh
# Fetch and merge rustc side of the history. Takes ca 5 min the first time.
# This will also update the `rustc-version` file.
./miri rustc-pull
rustc-josh-sync pull
# Update local toolchain and apply formatting.
./miri toolchain && ./miri fmt
git commit -am "rustup"
@ -328,12 +330,12 @@ needed.
### Exporting changes to the rustc repo
We will use the josh proxy to push to your fork of rustc. Run the following in the Miri repo,
We will use the `josh-sync` tool to push to your fork of rustc. Run the following in the Miri repo,
assuming we are on an up-to-date master branch:
```sh
# Push the Miri changes to your rustc fork (substitute your github handle for YOUR_NAME).
./miri rustc-push YOUR_NAME miri
rustc-josh-sync push miri YOUR_NAME
```
This will create a new branch called `miri` in your fork, and the output should include a link that

View file

@ -166,10 +166,12 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.30"
version = "1.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
dependencies = [
"jobserver",
"libc",
"shlex",
]
@ -214,6 +216,52 @@ dependencies = [
"inout",
]
[[package]]
name = "clap"
version = "4.5.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
dependencies = [
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_lex"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
[[package]]
name = "cmake"
version = "0.1.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
dependencies = [
"cc",
]
[[package]]
name = "codespan-reporting"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
dependencies = [
"serde",
"termcolor",
"unicode-width 0.2.1",
]
[[package]]
name = "color-eyre"
version = "0.6.5"
@ -313,6 +361,68 @@ dependencies = [
"typenum",
]
[[package]]
name = "cxx"
version = "1.0.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df"
dependencies = [
"cc",
"cxxbridge-cmd",
"cxxbridge-flags",
"cxxbridge-macro",
"foldhash",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909"
dependencies = [
"cc",
"codespan-reporting",
"indexmap",
"proc-macro2",
"quote",
"scratch",
"syn",
]
[[package]]
name = "cxxbridge-cmd"
version = "1.0.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a"
dependencies = [
"clap",
"codespan-reporting",
"indexmap",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d"
[[package]]
name = "cxxbridge-macro"
version = "1.0.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4"
dependencies = [
"indexmap",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "directories"
version = "6.0.0"
@ -334,12 +444,29 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.13"
@ -372,6 +499,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@ -382,6 +524,17 @@ dependencies = [
"version_check",
]
[[package]]
name = "genmc-sys"
version = "0.1.0"
dependencies = [
"cc",
"cmake",
"cxx",
"cxx-build",
"git2",
]
[[package]]
name = "getrandom"
version = "0.2.16"
@ -411,12 +564,150 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "git2"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110"
dependencies = [
"bitflags",
"libc",
"libgit2-sys",
"log",
"openssl-probe",
"openssl-sys",
"url",
]
[[package]]
name = "hashbrown"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
[[package]]
name = "icu_collections"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
dependencies = [
"displaydoc",
"potential_utf",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
dependencies = [
"displaydoc",
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
[[package]]
name = "icu_properties"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
dependencies = [
"displaydoc",
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"potential_utf",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
[[package]]
name = "icu_provider"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
dependencies = [
"displaydoc",
"icu_locale_core",
"stable_deref_trait",
"tinystr",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]]
name = "idna"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "indexmap"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "indicatif"
version = "0.17.11"
@ -463,6 +754,16 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jobserver"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
dependencies = [
"getrandom 0.3.3",
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.77"
@ -510,6 +811,19 @@ dependencies = [
"cc",
]
[[package]]
name = "libgit2-sys"
version = "0.18.2+1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222"
dependencies = [
"cc",
"libc",
"libz-sys",
"openssl-sys",
"pkg-config",
]
[[package]]
name = "libloading"
version = "0.8.8"
@ -530,12 +844,39 @@ dependencies = [
"libc",
]
[[package]]
name = "libz-sys"
version = "1.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "link-cplusplus"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212"
dependencies = [
"cc",
]
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "litemap"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]]
name = "lock_api"
version = "0.4.13"
@ -612,6 +953,7 @@ dependencies = [
"chrono-tz",
"colored 3.0.0",
"directories",
"genmc-sys",
"getrandom 0.3.3",
"ipc-channel",
"libc",
@ -672,6 +1014,24 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "openssl-probe"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "option-ext"
version = "0.2.0"
@ -722,6 +1082,12 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "perf-event-open-sys"
version = "3.0.0"
@ -755,12 +1121,27 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "portable-atomic"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "potential_utf"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
dependencies = [
"zerovec",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
@ -946,6 +1327,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scratch"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52"
[[package]]
name = "semver"
version = "1.0.26"
@ -1025,6 +1412,18 @@ dependencies = [
"color-eyre",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[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.104"
@ -1036,6 +1435,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tempfile"
version = "3.20.0"
@ -1049,6 +1459,15 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.69"
@ -1108,6 +1527,16 @@ dependencies = [
"libc",
]
[[package]]
name = "tinystr"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
name = "tracing"
version = "0.1.41"
@ -1199,6 +1628,23 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
[[package]]
name = "url"
version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.17.0"
@ -1216,6 +1662,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.5"
@ -1305,6 +1757,15 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "winapi-util"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "windows"
version = "0.58.0"
@ -1524,6 +1985,36 @@ dependencies = [
"bitflags",
]
[[package]]
name = "writeable"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "yoke"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
dependencies = [
"serde",
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerocopy"
version = "0.8.26"
@ -1543,3 +2034,57 @@ dependencies = [
"quote",
"syn",
]
[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerotrie"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View file

@ -48,6 +48,10 @@ nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"], optional =
ipc-channel = { version = "0.20.0", optional = true }
capstone = { version = "0.13", optional = true }
# FIXME(genmc,macos): Add `target_os = "macos"` once https://github.com/dtolnay/cxx/issues/1535 is fixed.
[target.'cfg(all(target_os = "linux", target_pointer_width = "64", target_endian = "little"))'.dependencies]
genmc-sys = { path = "./genmc-sys/", version = "0.1.0", optional = true }
[dev-dependencies]
ui_test = "0.30.2"
colored = "3"
@ -66,7 +70,7 @@ harness = false
[features]
default = ["stack-cache", "native-lib"]
genmc = []
genmc = ["dep:genmc-sys"] # this enables a GPL dependency!
stack-cache = []
stack-cache-consistency-check = ["stack-cache"]
tracing = ["serde_json"]

View file

@ -1,9 +1,9 @@
//! Implements the various phases of `cargo miri run/test`.
use std::env;
use std::fs::{self, File};
use std::fs::File;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::path::{self, Path, PathBuf};
use std::process::Command;
use rustc_version::VersionMeta;
@ -222,12 +222,12 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
// that to be the Miri driver, but acting as rustc, in host mode.
//
// In `main`, we need the value of `RUSTC` to distinguish RUSTC_WRAPPER invocations from rustdoc
// or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that
// or TARGET_RUNNER invocations, so we make it absolute to make it exceedingly unlikely that
// there would be a collision with other invocations of cargo-miri (as rustdoc or as runner). We
// explicitly do this even if RUSTC_STAGE is set, since for these builds we do *not* want the
// bootstrap `rustc` thing in our way! Instead, we have MIRI_HOST_SYSROOT to use for host
// builds.
cmd.env("RUSTC", fs::canonicalize(find_miri()).unwrap());
cmd.env("RUSTC", path::absolute(find_miri()).unwrap());
// In case we get invoked as RUSTC without the wrapper, let's be a host rustc. This makes no
// sense for cross-interpretation situations, but without the wrapper, this will use the host
// sysroot, so asking it to behave like a target build makes even less sense.

View file

@ -142,7 +142,6 @@ case $HOST_TARGET in
# Host
GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
# Extra tier 1
MANY_SEEDS=64 TEST_TARGET=i686-unknown-linux-gnu run_tests
MANY_SEEDS=64 TEST_TARGET=x86_64-apple-darwin run_tests
MANY_SEEDS=64 TEST_TARGET=x86_64-pc-windows-gnu run_tests
;;
@ -161,8 +160,6 @@ case $HOST_TARGET in
aarch64-unknown-linux-gnu)
# Host
GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
# Extra tier 1 candidate
MANY_SEEDS=64 TEST_TARGET=aarch64-pc-windows-msvc run_tests
# Extra tier 2
MANY_SEEDS=16 TEST_TARGET=arm-unknown-linux-gnueabi run_tests # 32bit ARM
MANY_SEEDS=16 TEST_TARGET=aarch64-pc-windows-gnullvm run_tests # gnullvm ABI
@ -189,13 +186,20 @@ case $HOST_TARGET in
;;
i686-pc-windows-msvc)
# Host
# Without GC_STRESS as this is the slowest runner.
# Without GC_STRESS as this is a very slow runner.
MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 run_tests
# Extra tier 1
# We really want to ensure a Linux target works on a Windows host,
# and a 64bit target works on a 32bit host.
TEST_TARGET=x86_64-unknown-linux-gnu run_tests
;;
aarch64-pc-windows-msvc)
# Host
# Without GC_STRESS as this is a very slow runner.
MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
# Extra tier 1
MANY_SEEDS=64 TEST_TARGET=i686-unknown-linux-gnu run_tests
;;
*)
echo "FATAL: unknown host target: $HOST_TARGET"
exit 1

View file

@ -0,0 +1,62 @@
# **(WIP)** Documentation for Miri-GenMC
[GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program.
Miri-GenMC integrates that model checker into Miri.
**NOTE: Currently, no actual GenMC functionality is part of Miri, this is still WIP.**
<!-- FIXME(genmc): add explanation. -->
## Usage
**IMPORTANT: The license of GenMC and thus the `genmc-sys` crate in the Miri repo is currently "GPL-3.0-or-later", so a binary produced with the `genmc` feature is subject to the requirements of the GPL. As long as that remains the case, the `genmc` feature of Miri is OFF-BY-DEFAULT and must be OFF for all Miri releases.**
For testing/developing Miri-GenMC (while keeping in mind the licensing issues):
- clone the Miri repo.
- build Miri-GenMC with `./miri build --features=genmc`.
- OR: install Miri-GenMC in the current system with `./miri install --features=genmc`
Basic usage:
```shell
MIRIFLAGS="-Zmiri-genmc" cargo miri run
```
<!-- FIXME(genmc): explain options. -->
<!-- FIXME(genmc): explain Miri-GenMC specific functions. -->
## Tips
<!-- FIXME(genmc): add tips for using Miri-GenMC more efficiently. -->
## Limitations
Some or all of these limitations might get removed in the future:
- Borrow tracking is currently incompatible (stacked/tree borrows).
- Only Linux is supported for now.
- No support for 32-bit or big-endian targets.
- No cross-target interpretation.
<!-- FIXME(genmc): document remaining limitations -->
## Development
GenMC is written in C++, which complicates development a bit.
The prerequisites for building Miri-GenMC are:
- A compiler with C++23 support.
- LLVM developments headers and clang.
<!-- FIXME(genmc,llvm): remove once LLVM dependency is no longer required. -->
The actual code for GenMC is not contained in the Miri repo itself, but in a [separate GenMC repo](https://github.com/MPI-SWS/genmc) (with its own maintainers).
These sources need to be available to build Miri-GenMC.
The process for obtaining them is as follows:
- By default, a fixed commit of GenMC is downloaded to `genmc-sys/genmc-src` and built automatically.
(The commit is determined by `GENMC_COMMIT` in `genmc-sys/build.rs`.)
- If you want to overwrite that, set the `GENMC_SRC_PATH` environment variable to a path that contains the GenMC sources.
If you place this directory inside the Miri folder, it is recommended to call it `genmc-src` as that tells `./miri fmt` to avoid
formatting the Rust files inside that folder.
<!-- FIXME(genmc): explain how submitting code to GenMC should be handled. -->
<!-- FIXME(genmc): explain development. -->

View file

@ -5,6 +5,7 @@ source = "discover"
linkedProjects = [
"Cargo.toml",
"cargo-miri/Cargo.toml",
"genmc-sys/Cargo.toml",
"miri-script/Cargo.toml",
]

View file

@ -3,6 +3,7 @@
"rust-analyzer.linkedProjects": [
"Cargo.toml",
"cargo-miri/Cargo.toml",
"genmc-sys/Cargo.toml",
"miri-script/Cargo.toml",
],
"rust-analyzer.check.invocationStrategy": "once",

1
src/tools/miri/genmc-sys/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
genmc-src*/

View file

@ -0,0 +1,17 @@
[package]
authors = ["Miri Team"]
# The parts in this repo are MIT OR Apache-2.0, but we are linking in
# code from https://github.com/MPI-SWS/genmc which is GPL-3.0-or-later.
license = "(MIT OR Apache-2.0) AND GPL-3.0-or-later"
name = "genmc-sys"
version = "0.1.0"
edition = "2024"
[dependencies]
cxx = { version = "1.0.160", features = ["c++20"] }
[build-dependencies]
cc = "1.2.16"
cmake = "0.1.54"
git2 = { version = "0.20.2", default-features = false, features = ["https"] }
cxx-build = { version = "1.0.160", features = ["parallel"] }

View file

@ -0,0 +1,269 @@
use std::path::{Path, PathBuf};
use std::str::FromStr;
// Build script for running Miri with GenMC.
// Check out doc/genmc.md for more info.
/// Path where the downloaded GenMC repository will be stored (relative to the `genmc-sys` directory).
/// Note that this directory is *not* cleaned up automatically by `cargo clean`.
const GENMC_DOWNLOAD_PATH: &str = "./genmc-src/";
/// Name of the library of the GenMC model checker.
const GENMC_MODEL_CHECKER: &str = "genmc_lib";
/// Path where the `cxx_bridge!` macro is used to define the Rust-C++ interface.
const RUST_CXX_BRIDGE_FILE_PATH: &str = "src/lib.rs";
/// The profile with which to build GenMC.
const GENMC_CMAKE_PROFILE: &str = "RelWithDebInfo";
mod downloading {
use std::path::PathBuf;
use std::str::FromStr;
use git2::{Commit, Oid, Remote, Repository, StatusOptions};
use super::GENMC_DOWNLOAD_PATH;
/// The GenMC repository the we get our commit from.
pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/MPI-SWS/genmc.git";
/// The GenMC commit we depend on. It must be available on the specified GenMC repository.
pub(crate) const GENMC_COMMIT: &str = "3438dd2c1202cd4a47ed7881d099abf23e4167ab";
pub(crate) fn download_genmc() -> PathBuf {
let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH);
let commit_oid = Oid::from_str(GENMC_COMMIT).expect("Commit should be valid.");
match Repository::open(&genmc_download_path) {
Ok(repo) => {
assert_repo_unmodified(&repo);
let commit = update_local_repo(&repo, commit_oid);
checkout_commit(&repo, &commit);
}
Err(_) => {
let repo = clone_remote_repo(&genmc_download_path);
let Ok(commit) = repo.find_commit(commit_oid) else {
panic!(
"Cloned GenMC repository does not contain required commit '{GENMC_COMMIT}'"
);
};
checkout_commit(&repo, &commit);
}
};
genmc_download_path
}
fn get_remote(repo: &Repository) -> Remote<'_> {
let remote = repo.find_remote("origin").unwrap_or_else(|e| {
panic!(
"Could not load commit ({GENMC_COMMIT}) from remote repository '{GENMC_GITHUB_URL}'. Error: {e}"
);
});
// Ensure that the correct remote URL is set.
let remote_url = remote.url();
if let Some(remote_url) = remote_url
&& remote_url == GENMC_GITHUB_URL
{
return remote;
}
// Update remote URL.
println!(
"cargo::warning=GenMC repository remote URL has changed from '{remote_url:?}' to '{GENMC_GITHUB_URL}'"
);
repo.remote_set_url("origin", GENMC_GITHUB_URL)
.expect("cannot rename url of remote 'origin'");
// Reacquire the `Remote`, since `remote_set_url` doesn't update Remote objects already in memory.
repo.find_remote("origin").unwrap()
}
// Check if the required commit exists already, otherwise try fetching it.
fn update_local_repo(repo: &Repository, commit_oid: Oid) -> Commit<'_> {
repo.find_commit(commit_oid).unwrap_or_else(|_find_error| {
println!("GenMC repository at path '{GENMC_DOWNLOAD_PATH}' does not contain commit '{GENMC_COMMIT}'.");
// The commit is not in the checkout. Try `git fetch` and hope that we find the commit then.
let mut remote = get_remote(repo);
remote.fetch(&[GENMC_COMMIT], None, None).expect("Failed to fetch from remote.");
repo.find_commit(commit_oid)
.expect("Remote repository should contain expected commit")
})
}
fn clone_remote_repo(genmc_download_path: &PathBuf) -> Repository {
Repository::clone(GENMC_GITHUB_URL, &genmc_download_path).unwrap_or_else(|e| {
panic!("Cannot clone GenMC repo from '{GENMC_GITHUB_URL}': {e:?}");
})
}
/// Set the state of the repo to a specific commit
fn checkout_commit(repo: &Repository, commit: &Commit<'_>) {
repo.checkout_tree(commit.as_object(), None).expect("Failed to checkout");
repo.set_head_detached(commit.id()).expect("Failed to set HEAD");
println!("Successfully set checked out commit {commit:?}");
}
/// Check that the downloaded repository is unmodified.
/// If it is modified, explain that it shouldn't be, and hint at how to do local development with GenMC.
/// We don't overwrite any changes made to the directory, to prevent data loss.
fn assert_repo_unmodified(repo: &Repository) {
let statuses = repo
.statuses(Some(
StatusOptions::new()
.include_untracked(true)
.include_ignored(false)
.include_unmodified(false),
))
.expect("should be able to get repository status");
if statuses.is_empty() {
return;
}
panic!(
"Downloaded GenMC repository at path '{GENMC_DOWNLOAD_PATH}' has been modified. Please undo any changes made, or delete the '{GENMC_DOWNLOAD_PATH}' directory to have it downloaded again.\n\
HINT: For local development, set the environment variable 'GENMC_SRC_PATH' to the path of a GenMC repository."
);
}
}
// FIXME(genmc,llvm): Remove once the LLVM dependency of the GenMC model checker is removed.
/// The linked LLVM version is in the generated `config.h`` file, which we parse and use to link to LLVM.
/// Returns c++ compiler definitions required for building with/including LLVM, and the include path for LLVM headers.
fn link_to_llvm(config_file: &Path) -> (String, String) {
/// Search a string for a line matching `//@VARIABLE_NAME: VARIABLE CONTENT`
fn extract_value<'a>(input: &'a str, name: &str) -> Option<&'a str> {
input
.lines()
.find_map(|line| line.strip_prefix("//@")?.strip_prefix(name)?.strip_prefix(": "))
}
let file_content = std::fs::read_to_string(&config_file).unwrap_or_else(|err| {
panic!("GenMC config file ({}) should exist, but got errror {err:?}", config_file.display())
});
let llvm_definitions = extract_value(&file_content, "LLVM_DEFINITIONS")
.expect("Config file should contain LLVM_DEFINITIONS");
let llvm_include_dirs = extract_value(&file_content, "LLVM_INCLUDE_DIRS")
.expect("Config file should contain LLVM_INCLUDE_DIRS");
let llvm_library_dir = extract_value(&file_content, "LLVM_LIBRARY_DIR")
.expect("Config file should contain LLVM_LIBRARY_DIR");
let llvm_config_path = extract_value(&file_content, "LLVM_CONFIG_PATH")
.expect("Config file should contain LLVM_CONFIG_PATH");
// Add linker search path.
let lib_dir = PathBuf::from_str(llvm_library_dir).unwrap();
println!("cargo::rustc-link-search=native={}", lib_dir.display());
// Add libraries to link.
let output = std::process::Command::new(llvm_config_path)
.arg("--libs") // Print the libraries to link to (space-separated list)
.output()
.expect("failed to execute llvm-config");
let llvm_link_libs =
String::try_from(output.stdout).expect("llvm-config output should be a valid string");
for link_lib in llvm_link_libs.trim().split(" ") {
let link_lib =
link_lib.strip_prefix("-l").expect("Linker parameter should start with \"-l\"");
println!("cargo::rustc-link-lib=dylib={link_lib}");
}
(llvm_definitions.to_string(), llvm_include_dirs.to_string())
}
/// Build the GenMC model checker library and the Rust-C++ interop library with cxx.rs
fn compile_cpp_dependencies(genmc_path: &Path) {
// Part 1:
// Compile the GenMC library using cmake.
let cmakelists_path = genmc_path.join("CMakeLists.txt");
// FIXME(genmc,cargo): Switch to using `CARGO_CFG_DEBUG_ASSERTIONS` once https://github.com/rust-lang/cargo/issues/15760 is completed.
// Enable/disable additional debug checks, prints and options for GenMC, based on the Rust profile (debug/release)
let enable_genmc_debug = matches!(std::env::var("PROFILE").as_deref().unwrap(), "debug");
let mut config = cmake::Config::new(cmakelists_path);
config.profile(GENMC_CMAKE_PROFILE);
config.define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" });
// The actual compilation happens here:
let genmc_install_dir = config.build();
// Add the model checker library to be linked and tell rustc where to find it:
let cmake_lib_dir = genmc_install_dir.join("lib").join("genmc");
println!("cargo::rustc-link-search=native={}", cmake_lib_dir.display());
println!("cargo::rustc-link-lib=static={GENMC_MODEL_CHECKER}");
// FIXME(genmc,llvm): Remove once the LLVM dependency of the GenMC model checker is removed.
let config_file = genmc_install_dir.join("include").join("genmc").join("config.h");
let (llvm_definitions, llvm_include_dirs) = link_to_llvm(&config_file);
// Part 2:
// Compile the cxx_bridge (the link between the Rust and C++ code).
let genmc_include_dir = genmc_install_dir.join("include").join("genmc");
// FIXME(genmc,llvm): remove once LLVM dependency is removed.
// These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated.
let definitions = llvm_definitions.split(";");
let mut bridge = cxx_build::bridge("src/lib.rs");
// FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file.
if enable_genmc_debug {
bridge.define("ENABLE_GENMC_DEBUG", None);
}
for definition in definitions {
bridge.flag(definition);
}
bridge
.opt_level(2)
.debug(true) // Same settings that GenMC uses (default for cmake `RelWithDebInfo`)
.warnings(false) // NOTE: enabling this produces a lot of warnings.
.std("c++23")
.include(genmc_include_dir)
.include(llvm_include_dirs)
.include("./src_cpp")
.file("./src_cpp/MiriInterface.hpp")
.file("./src_cpp/MiriInterface.cpp")
.compile("genmc_interop");
// Link the Rust-C++ interface library generated by cxx_build:
println!("cargo::rustc-link-lib=static=genmc_interop");
}
fn main() {
// Make sure we don't accidentally distribute a binary with GPL code.
if option_env!("RUSTC_STAGE").is_some() {
panic!(
"genmc should not be enabled in the rustc workspace since it includes a GPL dependency"
);
}
// Select which path to use for the GenMC repo:
let genmc_path = if let Ok(genmc_src_path) = std::env::var("GENMC_SRC_PATH") {
let genmc_src_path =
PathBuf::from_str(&genmc_src_path).expect("GENMC_SRC_PATH should contain a valid path");
assert!(
genmc_src_path.exists(),
"GENMC_SRC_PATH={} does not exist!",
genmc_src_path.display()
);
genmc_src_path
} else {
downloading::download_genmc()
};
// Build all required components:
compile_cpp_dependencies(&genmc_path);
// Only rebuild if anything changes:
// Note that we don't add the downloaded GenMC repo, since that should never be modified
// manually. Adding that path here would also trigger an unnecessary rebuild after the repo is
// cloned (since cargo detects that as a file modification).
println!("cargo::rerun-if-changed={RUST_CXX_BRIDGE_FILE_PATH}");
println!("cargo::rerun-if-changed=./src");
println!("cargo::rerun-if-changed=./src_cpp");
}

View file

@ -0,0 +1,30 @@
pub use self::ffi::*;
impl Default for GenmcParams {
fn default() -> Self {
Self {
print_random_schedule_seed: false,
do_symmetry_reduction: false,
// FIXME(GenMC): Add defaults for remaining parameters
}
}
}
#[cxx::bridge]
mod ffi {
/// Parameters that will be given to GenMC for setting up the model checker.
/// (The fields of this struct are visible to both Rust and C++)
#[derive(Clone, Debug)]
struct GenmcParams {
pub print_random_schedule_seed: bool,
pub do_symmetry_reduction: bool,
// FIXME(GenMC): Add remaining parameters.
}
unsafe extern "C++" {
include!("MiriInterface.hpp");
type MiriGenMCShim;
fn createGenmcHandle(config: &GenmcParams) -> UniquePtr<MiriGenMCShim>;
}
}

View file

@ -0,0 +1,50 @@
#include "MiriInterface.hpp"
#include "genmc-sys/src/lib.rs.h"
auto MiriGenMCShim::createHandle(const GenmcParams &config)
-> std::unique_ptr<MiriGenMCShim>
{
auto conf = std::make_shared<Config>();
// Miri needs all threads to be replayed, even fully completed ones.
conf->replayCompletedThreads = true;
// We only support the RC11 memory model for Rust.
conf->model = ModelType::RC11;
conf->printRandomScheduleSeed = config.print_random_schedule_seed;
// FIXME(genmc): disable any options we don't support currently:
conf->ipr = false;
conf->disableBAM = true;
conf->instructionCaching = false;
ERROR_ON(config.do_symmetry_reduction, "Symmetry reduction is currently unsupported in GenMC mode.");
conf->symmetryReduction = config.do_symmetry_reduction;
// FIXME(genmc): Should there be a way to change this option from Miri?
conf->schedulePolicy = SchedulePolicy::WF;
// FIXME(genmc): implement estimation mode:
conf->estimate = false;
conf->estimationMax = 1000;
const auto mode = conf->estimate ? GenMCDriver::Mode(GenMCDriver::EstimationMode{})
: GenMCDriver::Mode(GenMCDriver::VerificationMode{});
// Running Miri-GenMC without race detection is not supported.
// Disabling this option also changes the behavior of the replay scheduler to only schedule at atomic operations, which is required with Miri.
// This happens because Miri can generate multiple GenMC events for a single MIR terminator. Without this option,
// the scheduler might incorrectly schedule an atomic MIR terminator because the first event it creates is a non-atomic (e.g., `StorageLive`).
conf->disableRaceDetection = false;
// Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory
// that is allowed to leak and memory that is not.
conf->warnUnfreedMemory = false;
// FIXME(genmc): check config:
// checkConfigOptions(*conf);
auto driver = std::make_unique<MiriGenMCShim>(std::move(conf), mode);
return driver;
}

View file

@ -0,0 +1,44 @@
#ifndef GENMC_MIRI_INTERFACE_HPP
#define GENMC_MIRI_INTERFACE_HPP
#include "rust/cxx.h"
#include "config.h"
#include "Config/Config.hpp"
#include "Verification/GenMCDriver.hpp"
#include <iostream>
/**** Types available to Miri ****/
// Config struct defined on the Rust side and translated to C++ by cxx.rs:
struct GenmcParams;
struct MiriGenMCShim : private GenMCDriver
{
public:
MiriGenMCShim(std::shared_ptr<const Config> conf, Mode mode /* = VerificationMode{} */)
: GenMCDriver(std::move(conf), nullptr, mode)
{
std::cerr << "C++: GenMC handle created!" << std::endl;
}
virtual ~MiriGenMCShim()
{
std::cerr << "C++: GenMC handle destroyed!" << std::endl;
}
static std::unique_ptr<MiriGenMCShim> createHandle(const GenmcParams &config);
};
/**** Functions available to Miri ****/
// NOTE: CXX doesn't support exposing static methods to Rust currently, so we expose this function instead.
static inline auto createGenmcHandle(const GenmcParams &config) -> std::unique_ptr<MiriGenMCShim>
{
return MiriGenMCShim::createHandle(config);
}
#endif /* GENMC_MIRI_INTERFACE_HPP */

View file

@ -0,0 +1,2 @@
repo = "miri"
filter = ":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri"

View file

@ -116,27 +116,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "directories"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.60.2",
]
[[package]]
name = "dunce"
version = "1.0.5"
@ -165,17 +144,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.3"
@ -185,7 +153,7 @@ dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
"wasi",
]
[[package]]
@ -221,16 +189,6 @@ version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "libredox"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
dependencies = [
"bitflags",
"libc",
]
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
@ -249,7 +207,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"directories",
"dunce",
"itertools",
"path_macro",
@ -275,12 +232,6 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "path_macro"
version = "1.0.0"
@ -311,17 +262,6 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "redox_users"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [
"getrandom 0.2.16",
"libredox",
"thiserror",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
@ -427,32 +367,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
dependencies = [
"fastrand",
"getrandom 0.3.3",
"getrandom",
"once_cell",
"rustix",
"windows-sys 0.59.0",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
@ -475,12 +395,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"

View file

@ -22,7 +22,6 @@ anyhow = "1.0"
xshell = "0.2.6"
rustc_version = "0.4"
dunce = "1.0.4"
directories = "6"
serde = "1"
serde_json = "1"
serde_derive = "1"

View file

@ -2,11 +2,9 @@ use std::collections::BTreeMap;
use std::ffi::{OsStr, OsString};
use std::fmt::Write as _;
use std::fs::{self, File};
use std::io::{self, BufRead, BufReader, BufWriter, Write as _};
use std::ops::Not;
use std::io::{self, BufRead, BufReader, BufWriter};
use std::path::PathBuf;
use std::time::Duration;
use std::{env, net, process};
use std::{env, process};
use anyhow::{Context, Result, anyhow, bail};
use path_macro::path;
@ -18,11 +16,6 @@ use xshell::{Shell, cmd};
use crate::Command;
use crate::util::*;
/// Used for rustc syncs.
const JOSH_FILTER: &str =
":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri";
const JOSH_PORT: u16 = 42042;
impl MiriEnv {
/// Prepares the environment: builds miri and cargo-miri and a sysroot.
/// Returns the location of the sysroot.
@ -99,66 +92,6 @@ impl Command {
Ok(())
}
fn start_josh() -> Result<impl Drop> {
// Determine cache directory.
let local_dir = {
let user_dirs =
directories::ProjectDirs::from("org", "rust-lang", "miri-josh").unwrap();
user_dirs.cache_dir().to_owned()
};
// Start josh, silencing its output.
let mut cmd = process::Command::new("josh-proxy");
cmd.arg("--local").arg(local_dir);
cmd.arg("--remote").arg("https://github.com");
cmd.arg("--port").arg(JOSH_PORT.to_string());
cmd.arg("--no-background");
cmd.stdout(process::Stdio::null());
cmd.stderr(process::Stdio::null());
let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?;
// Create a wrapper that stops it on drop.
struct Josh(process::Child);
impl Drop for Josh {
fn drop(&mut self) {
#[cfg(unix)]
{
// Try to gracefully shut it down.
process::Command::new("kill")
.args(["-s", "INT", &self.0.id().to_string()])
.output()
.expect("failed to SIGINT josh-proxy");
// Sadly there is no "wait with timeout"... so we just give it some time to finish.
std::thread::sleep(Duration::from_millis(100));
// Now hopefully it is gone.
if self.0.try_wait().expect("failed to wait for josh-proxy").is_some() {
return;
}
}
// If that didn't work (or we're not on Unix), kill it hard.
eprintln!(
"I have to kill josh-proxy the hard way, let's hope this does not break anything."
);
self.0.kill().expect("failed to SIGKILL josh-proxy");
}
}
// Wait until the port is open. We try every 10ms until 1s passed.
for _ in 0..100 {
// This will generally fail immediately when the port is still closed.
let josh_ready = net::TcpStream::connect_timeout(
&net::SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)),
Duration::from_millis(1),
);
if josh_ready.is_ok() {
return Ok(Josh(josh));
}
// Not ready yet.
std::thread::sleep(Duration::from_millis(10));
}
bail!("Even after waiting for 1s, josh-proxy is still not available.")
}
pub fn exec(self) -> Result<()> {
// First, and crucially only once, run the auto-actions -- but not for all commands.
match &self {
@ -170,11 +103,7 @@ impl Command {
| Command::Fmt { .. }
| Command::Doc { .. }
| Command::Clippy { .. } => Self::auto_actions()?,
| Command::Toolchain { .. }
| Command::Bench { .. }
| Command::RustcPull { .. }
| Command::RustcPush { .. }
| Command::Squash => {}
| Command::Toolchain { .. } | Command::Bench { .. } | Command::Squash => {}
}
// Then run the actual command.
match self {
@ -191,8 +120,6 @@ impl Command {
Command::Bench { target, no_install, save_baseline, load_baseline, benches } =>
Self::bench(target, no_install, save_baseline, load_baseline, benches),
Command::Toolchain { flags } => Self::toolchain(flags),
Command::RustcPull { commit } => Self::rustc_pull(commit.clone()),
Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch),
Command::Squash => Self::squash(),
}
}
@ -233,156 +160,6 @@ impl Command {
Ok(())
}
fn rustc_pull(commit: Option<String>) -> Result<()> {
let sh = Shell::new()?;
sh.change_dir(miri_dir()?);
let commit = commit.map(Result::Ok).unwrap_or_else(|| {
let rust_repo_head =
cmd!(sh, "git ls-remote https://github.com/rust-lang/rust/ HEAD").read()?;
rust_repo_head
.split_whitespace()
.next()
.map(|front| front.trim().to_owned())
.ok_or_else(|| anyhow!("Could not obtain Rust repo HEAD from remote."))
})?;
// Make sure the repo is clean.
if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() {
bail!("working directory must be clean before running `./miri rustc-pull`");
}
// Make sure josh is running.
let josh = Self::start_josh()?;
let josh_url =
format!("http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git");
// Update rust-version file. As a separate commit, since making it part of
// the merge has confused the heck out of josh in the past.
// We pass `--no-verify` to avoid running git hooks like `./miri fmt` that could in turn
// trigger auto-actions.
// We do this before the merge so that if there are merge conflicts, we have
// the right rust-version file while resolving them.
sh.write_file("rust-version", format!("{commit}\n"))?;
const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc";
cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}")
.run()
.context("FAILED to commit rust-version file, something went wrong")?;
// Fetch given rustc commit.
cmd!(sh, "git fetch {josh_url}")
.run()
.inspect_err(|_| {
// Try to un-do the previous `git commit`, to leave the repo in the state we found it.
cmd!(sh, "git reset --hard HEAD^")
.run()
.expect("FAILED to clean up again after failed `git fetch`, sorry for that");
})
.context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?;
// This should not add any new root commits. So count those before and after merging.
let num_roots = || -> Result<u32> {
Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count")
.read()
.context("failed to determine the number of root commits")?
.parse::<u32>()?)
};
let num_roots_before = num_roots()?;
// Merge the fetched commit.
const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc";
cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}")
.run()
.context("FAILED to merge new commits, something went wrong")?;
// Check that the number of roots did not increase.
if num_roots()? != num_roots_before {
bail!("Josh created a new root commit. This is probably not the history you want.");
}
drop(josh);
Ok(())
}
fn rustc_push(github_user: String, branch: String) -> Result<()> {
let sh = Shell::new()?;
sh.change_dir(miri_dir()?);
let base = sh.read_file("rust-version")?.trim().to_owned();
// Make sure the repo is clean.
if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() {
bail!("working directory must be clean before running `./miri rustc-push`");
}
// Make sure josh is running.
let josh = Self::start_josh()?;
let josh_url =
format!("http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git");
// Find a repo we can do our preparation in.
if let Ok(rustc_git) = env::var("RUSTC_GIT") {
// If rustc_git is `Some`, we'll use an existing fork for the branch updates.
sh.change_dir(rustc_git);
} else {
// Otherwise, do this in the local Miri repo.
println!(
"This will pull a copy of the rust-lang/rust history into this Miri checkout, growing it by about 1GB."
);
print!(
"To avoid that, abort now and set the `RUSTC_GIT` environment variable to an existing rustc checkout. Proceed? [y/N] "
);
std::io::stdout().flush()?;
let mut answer = String::new();
std::io::stdin().read_line(&mut answer)?;
if answer.trim().to_lowercase() != "y" {
std::process::exit(1);
}
};
// Prepare the branch. Pushing works much better if we use as base exactly
// the commit that we pulled from last time, so we use the `rust-version`
// file to find out which commit that would be.
println!("Preparing {github_user}/rust (base: {base})...");
if cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}")
.ignore_stderr()
.read()
.is_ok()
{
println!(
"The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again."
);
std::process::exit(1);
}
cmd!(sh, "git fetch https://github.com/rust-lang/rust {base}").run()?;
cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}")
.ignore_stdout()
.ignore_stderr() // silence the "create GitHub PR" message
.run()?;
println!();
// Do the actual push.
sh.change_dir(miri_dir()?);
println!("Pushing miri changes...");
cmd!(sh, "git push {josh_url} HEAD:{branch}").run()?;
println!();
// Do a round-trip check to make sure the push worked as expected.
cmd!(sh, "git fetch {josh_url} {branch}").ignore_stderr().read()?;
let head = cmd!(sh, "git rev-parse HEAD").read()?;
let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?;
if head != fetch_head {
bail!(
"Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\
Expected {head}, got {fetch_head}."
);
}
println!(
"Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:"
);
println!(
// Open PR with `subtree update` title to silence the `no-merges` triagebot check
// See https://github.com/rust-lang/rust/pull/114157
" https://github.com/rust-lang/rust/compare/{github_user}:{branch}?quick_pull=1&title=Miri+subtree+update&body=r?+@ghost"
);
drop(josh);
Ok(())
}
fn squash() -> Result<()> {
let sh = Shell::new()?;
sh.change_dir(miri_dir()?);
@ -757,8 +534,8 @@ impl Command {
if ty.is_file() {
name.ends_with(".rs")
} else {
// dir or symlink. skip `target` and `.git`.
&name != "target" && &name != ".git"
// dir or symlink. skip `target`, `.git` and `genmc-src*`
&name != "target" && &name != ".git" && !name.starts_with("genmc-src")
}
})
.filter_ok(|item| item.file_type().is_file())

View file

@ -142,25 +142,6 @@ pub enum Command {
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
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,
},
/// Squash the commits of the current feature branch into one.
Squash,
}
@ -184,8 +165,7 @@ impl Command {
flags.extend(remainder);
Ok(())
}
Self::Bench { .. } | Self::RustcPull { .. } | Self::RustcPush { .. } | Self::Squash =>
bail!("unexpected \"--\" found in arguments"),
Self::Bench { .. } | Self::Squash => bail!("unexpected \"--\" found in arguments"),
}
}
}

View file

@ -1 +1 @@
6707bf0f59485cf054ac1095725df43220e4be20
733dab558992d902d6d17576de1da768094e2cf3

View file

@ -60,7 +60,7 @@ fn init_logger_once(early_dcx: &EarlyDiagCtxt) {
#[cfg(not(feature = "tracing"))]
{
crate::fatal_error!(
"fatal error: cannot enable MIRI_TRACING since Miri was not built with the \"tracing\" feature"
"Cannot enable MIRI_TRACING since Miri was not built with the \"tracing\" feature"
);
}

View file

@ -12,6 +12,7 @@
//! ```rust
//! tracing::info_span!("my_span", tracing_separate_thread = tracing::field::Empty, /* ... */)
//! ```
//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with Perfetto
//!
//! Depending on the tracing-chrome crate from crates.io is unfortunately not possible, since it
//! depends on `tracing_core` which conflicts with rustc_private's `tracing_core` (meaning it would
@ -285,9 +286,9 @@ struct Callsite {
}
enum Message {
Enter(f64, Callsite, Option<u64>),
Enter(f64, Callsite, Option<i64>),
Event(f64, Callsite),
Exit(f64, Callsite, Option<u64>),
Exit(f64, Callsite, Option<i64>),
NewThread(usize, String),
Flush,
Drop,
@ -519,14 +520,17 @@ where
}
}
fn get_root_id(&self, span: SpanRef<S>) -> Option<u64> {
fn get_root_id(&self, span: SpanRef<S>) -> Option<i64> {
// Returns `Option<i64>` instead of `Option<u64>` because apparently Perfetto gives an
// error if an id does not fit in a 64-bit signed integer in 2's complement. We cast the
// span id from `u64` to `i64` with wraparound, since negative values are fine.
match self.trace_style {
TraceStyle::Threaded => {
if span.fields().field("tracing_separate_thread").is_some() {
// assign an independent "id" to spans with argument "tracing_separate_thread",
// so they appear a separate trace line in trace visualization tools, see
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.jh64i9l3vwa1
Some(span.id().into_u64())
Some(span.id().into_u64().cast_signed()) // the comment above explains the cast
} else {
None
}
@ -539,6 +543,7 @@ where
.unwrap_or(span)
.id()
.into_u64()
.cast_signed() // the comment above explains the cast
),
}
}

View file

@ -67,8 +67,6 @@ use crate::log::setup::{deinit_loggers, init_early_loggers, init_late_loggers};
struct MiriCompilerCalls {
miri_config: Option<MiriConfig>,
many_seeds: Option<ManySeedsConfig>,
/// Settings for using GenMC with Miri.
genmc_config: Option<GenmcConfig>,
}
struct ManySeedsConfig {
@ -77,12 +75,8 @@ struct ManySeedsConfig {
}
impl MiriCompilerCalls {
fn new(
miri_config: MiriConfig,
many_seeds: Option<ManySeedsConfig>,
genmc_config: Option<GenmcConfig>,
) -> Self {
Self { miri_config: Some(miri_config), many_seeds, genmc_config }
fn new(miri_config: MiriConfig, many_seeds: Option<ManySeedsConfig>) -> Self {
Self { miri_config: Some(miri_config), many_seeds }
}
}
@ -192,8 +186,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
optimizations is usually marginal at best.");
}
if let Some(genmc_config) = &self.genmc_config {
let _genmc_ctx = Rc::new(GenmcCtx::new(&config, genmc_config));
if let Some(_genmc_config) = &config.genmc_config {
let _genmc_ctx = Rc::new(GenmcCtx::new(&config));
todo!("GenMC mode not yet implemented");
};
@ -487,7 +481,6 @@ fn main() {
let mut many_seeds_keep_going = false;
let mut miri_config = MiriConfig::default();
miri_config.env = env_snapshot;
let mut genmc_config = None;
let mut rustc_args = vec![];
let mut after_dashdash = false;
@ -603,9 +596,9 @@ fn main() {
} else if arg == "-Zmiri-many-seeds-keep-going" {
many_seeds_keep_going = true;
} else if let Some(trimmed_arg) = arg.strip_prefix("-Zmiri-genmc") {
// FIXME(GenMC): Currently, GenMC mode is incompatible with aliasing model checking.
miri_config.borrow_tracker = None;
GenmcConfig::parse_arg(&mut genmc_config, trimmed_arg);
if let Err(msg) = GenmcConfig::parse_arg(&mut miri_config.genmc_config, trimmed_arg) {
fatal_error!("{msg}");
}
} else if let Some(param) = arg.strip_prefix("-Zmiri-env-forward=") {
miri_config.forwarded_env_vars.push(param.to_owned());
} else if let Some(param) = arg.strip_prefix("-Zmiri-env-set=") {
@ -740,13 +733,18 @@ fn main() {
many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going });
// Validate settings for data race detection and GenMC mode.
assert_eq!(genmc_config.is_some(), miri_config.genmc_mode);
if genmc_config.is_some() {
if miri_config.genmc_config.is_some() {
if !miri_config.data_race_detector {
fatal_error!("Cannot disable data race detection in GenMC mode (currently)");
} else if !miri_config.weak_memory_emulation {
fatal_error!("Cannot disable weak memory emulation in GenMC mode");
}
if miri_config.borrow_tracker.is_some() {
eprintln!(
"warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode."
);
miri_config.borrow_tracker = None;
}
} else if miri_config.weak_memory_emulation && !miri_config.data_race_detector {
fatal_error!(
"Weak memory emulation cannot be enabled when the data race detector is disabled"
@ -765,8 +763,5 @@ fn main() {
);
}
}
run_compiler_and_exit(
&rustc_args,
&mut MiriCompilerCalls::new(miri_config, many_seeds, genmc_config),
)
run_compiler_and_exit(&rustc_args, &mut MiriCompilerCalls::new(miri_config, many_seeds))
}

View file

@ -1,19 +1,35 @@
use crate::MiriConfig;
use super::GenmcParams;
/// Configuration for GenMC mode.
/// The `params` field is shared with the C++ side.
/// The remaining options are kept on the Rust side.
#[derive(Debug, Default, Clone)]
pub struct GenmcConfig {
// TODO: add fields
pub(super) params: GenmcParams,
do_estimation: bool,
// FIXME(GenMC): add remaining options.
}
impl GenmcConfig {
/// Function for parsing command line options for GenMC mode.
/// All GenMC arguments start with the string "-Zmiri-genmc".
///
/// `trimmed_arg` should be the argument to be parsed, with the suffix "-Zmiri-genmc" removed
pub fn parse_arg(genmc_config: &mut Option<GenmcConfig>, trimmed_arg: &str) {
/// All GenMC arguments start with the string "-Zmiri-genmc".
/// Passing any GenMC argument will enable GenMC mode.
///
/// `trimmed_arg` should be the argument to be parsed, with the suffix "-Zmiri-genmc" removed.
pub fn parse_arg(
genmc_config: &mut Option<GenmcConfig>,
trimmed_arg: &str,
) -> Result<(), String> {
// FIXME(genmc): Ensure host == target somewhere.
if genmc_config.is_none() {
*genmc_config = Some(Default::default());
}
todo!("implement parsing of GenMC options")
if trimmed_arg.is_empty() {
return Ok(()); // this corresponds to "-Zmiri-genmc"
}
// FIXME(GenMC): implement remaining parameters.
todo!();
}
}

View file

@ -16,7 +16,7 @@ pub struct GenmcCtx {}
pub struct GenmcConfig {}
impl GenmcCtx {
pub fn new(_miri_config: &MiriConfig, _genmc_config: &GenmcConfig) -> Self {
pub fn new(_miri_config: &MiriConfig) -> Self {
unreachable!()
}
@ -227,10 +227,15 @@ impl VisitProvenance for GenmcCtx {
}
impl GenmcConfig {
pub fn parse_arg(_genmc_config: &mut Option<GenmcConfig>, trimmed_arg: &str) {
unimplemented!(
"GenMC feature im Miri is disabled, cannot handle argument: \"-Zmiri-genmc{trimmed_arg}\""
);
pub fn parse_arg(
_genmc_config: &mut Option<GenmcConfig>,
trimmed_arg: &str,
) -> Result<(), String> {
if cfg!(feature = "genmc") {
Err(format!("GenMC is disabled in this build of Miri"))
} else {
Err(format!("GenMC is not supported on this target"))
}
}
pub fn should_print_graph(&self, _rep: usize) -> bool {

View file

@ -2,6 +2,7 @@
use std::cell::Cell;
use genmc_sys::{GenmcParams, createGenmcHandle};
use rustc_abi::{Align, Size};
use rustc_const_eval::interpret::{InterpCx, InterpResult, interp_ok};
use rustc_middle::mir;
@ -24,9 +25,19 @@ pub struct GenmcCtx {
impl GenmcCtx {
/// Create a new `GenmcCtx` from a given config.
pub fn new(miri_config: &MiriConfig, genmc_config: &GenmcConfig) -> Self {
assert!(miri_config.genmc_mode);
todo!()
pub fn new(miri_config: &MiriConfig) -> Self {
let genmc_config = miri_config.genmc_config.as_ref().unwrap();
let handle = createGenmcHandle(&genmc_config.params);
assert!(!handle.is_null());
eprintln!("Miri: GenMC handle creation successful!");
drop(handle);
eprintln!("Miri: Dropping GenMC handle successful!");
// FIXME(GenMC): implement
std::process::exit(0);
}
pub fn get_stuck_execution_count(&self) -> usize {

View file

@ -8,7 +8,17 @@ mod vector_clock;
pub mod weak_memory;
// Import either the real genmc adapter or a dummy module.
#[cfg_attr(not(feature = "genmc"), path = "genmc/dummy.rs")]
// On unsupported platforms, we always include the dummy module, even if the `genmc` feature is enabled.
// FIXME(genmc,macos): Add `target_os = "macos"` once `https://github.com/dtolnay/cxx/issues/1535` is fixed.
#[cfg_attr(
not(all(
feature = "genmc",
target_os = "linux",
target_pointer_width = "64",
target_endian = "little"
)),
path = "genmc/dummy.rs"
)]
mod genmc;
pub use self::data_race_handler::{AllocDataRaceHandler, GlobalDataRaceHandler};

View file

@ -375,7 +375,7 @@ impl Timeout {
}
/// The clock to use for the timeout you are asking for.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TimeoutClock {
Monotonic,
RealTime,

View file

@ -382,7 +382,7 @@ pub fn report_error<'tcx>(
helps.push(note_span!(span, "{:?} was deallocated here:", alloc_id));
}
}
AbiMismatchArgument { .. } => {
AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => {
helps.push(note!("this means these two types are not *guaranteed* to be ABI-compatible across all targets"));
helps.push(note!("if you think this code should be accepted anyway, please report an issue with Miri"));
}

View file

@ -125,8 +125,8 @@ pub struct MiriConfig {
pub data_race_detector: bool,
/// Determine if weak memory emulation should be enabled. Requires data race detection to be enabled.
pub weak_memory_emulation: bool,
/// Determine if we are running in GenMC mode. In this mode, Miri will explore multiple concurrent executions of the given program.
pub genmc_mode: bool,
/// Determine if we are running in GenMC mode and with which settings. In GenMC mode, Miri will explore multiple concurrent executions of the given program.
pub genmc_config: Option<GenmcConfig>,
/// Track when an outdated (weak memory) load happens.
pub track_outdated_loads: bool,
/// Rate of spurious failures for compare_exchange_weak atomic operations,
@ -192,7 +192,7 @@ impl Default for MiriConfig {
track_alloc_accesses: false,
data_race_detector: true,
weak_memory_emulation: true,
genmc_mode: false,
genmc_config: None,
track_outdated_loads: false,
cmpxchg_weak_failure_rate: 0.8, // 80%
measureme_out: None,
@ -334,8 +334,8 @@ pub fn create_ecx<'tcx>(
helpers::try_resolve_path(tcx, &["core", "ascii", "escape_default"], Namespace::ValueNS);
if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) {
tcx.dcx().fatal(
"the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing. \
Use `cargo miri setup` to prepare a sysroot that is suitable for Miri."
"the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing.\n\
Note that directly invoking the `miri` binary is not supported; please use `cargo miri` instead."
);
}

View file

@ -3,7 +3,7 @@ use std::time::Duration;
use std::{cmp, iter};
use rand::RngCore;
use rustc_abi::{Align, CanonAbi, ExternAbi, FieldIdx, FieldsShape, Size, Variants};
use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants};
use rustc_apfloat::Float;
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_hir::Safety;
@ -14,11 +14,10 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::ExportedSymbol;
use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout};
use rustc_middle::ty::{self, Binder, FloatTy, FnSig, IntTy, Ty, TyCtxt, UintTy};
use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy};
use rustc_session::config::CrateType;
use rustc_span::{Span, Symbol};
use rustc_symbol_mangling::mangle_internal_symbol;
use rustc_target::callconv::FnAbi;
use crate::*;
@ -437,7 +436,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// For now, arguments must be scalars (so that the caller does not have to know the layout).
///
/// If you do not provide a return place, a dangling zero-sized place will be created
/// for your convenience.
/// for your convenience. This is only valid if the return type is `()`.
fn call_function(
&mut self,
f: ty::Instance<'tcx>,
@ -452,7 +451,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let mir = this.load_mir(f.def, None)?;
let dest = match dest {
Some(dest) => dest.clone(),
None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?),
None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit),
};
// Construct a function pointer type representing the caller perspective.
@ -465,6 +464,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
);
let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
// This will also show proper errors if there is any ABI mismatch.
this.init_stack_frame(
f,
mir,
@ -929,21 +929,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
}
/// Check that the calling convention is what we expect.
fn check_callconv<'a>(
&self,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
exp_abi: CanonAbi,
) -> InterpResult<'a, ()> {
if fn_abi.conv != exp_abi {
throw_ub_format!(
r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
fn_abi.conv
);
}
interp_ok(())
}
fn frame_in_std(&self) -> bool {
let this = self.eval_context_ref();
let frame = this.frame();
@ -967,162 +952,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
crate_name == "std" || crate_name == "std_miri_test"
}
fn check_abi_and_shim_symbol_clash(
&mut self,
abi: &FnAbi<'tcx, Ty<'tcx>>,
exp_abi: CanonAbi,
link_name: Symbol,
) -> InterpResult<'tcx, ()> {
self.check_callconv(abi, exp_abi)?;
if let Some((body, instance)) = self.eval_context_mut().lookup_exported_symbol(link_name)? {
// If compiler-builtins is providing the symbol, then don't treat it as a clash.
// We'll use our built-in implementation in `emulate_foreign_item_inner` for increased
// performance. Note that this means we won't catch any undefined behavior in
// compiler-builtins when running other crates, but Miri can still be run on
// compiler-builtins itself (or any crate that uses it as a normal dependency)
if self.eval_context_ref().tcx.is_compiler_builtins(instance.def_id().krate) {
return interp_ok(());
}
throw_machine_stop!(TerminationInfo::SymbolShimClashing {
link_name,
span: body.span.data(),
})
}
interp_ok(())
}
fn check_shim<'a, const N: usize>(
&mut self,
abi: &FnAbi<'tcx, Ty<'tcx>>,
exp_abi: CanonAbi,
link_name: Symbol,
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?;
if abi.c_variadic {
throw_ub_format!(
"calling a non-variadic function with a variadic caller-side signature"
);
}
if let Ok(ops) = args.try_into() {
return interp_ok(ops);
}
throw_ub_format!(
"incorrect number of arguments for `{link_name}`: got {}, expected {}",
args.len(),
N
)
}
/// Check that the given `caller_fn_abi` matches the expected ABI described by
/// `callee_abi`, `callee_input_tys`, `callee_output_ty`, and then returns the list of
/// arguments.
fn check_shim_abi<'a, const N: usize>(
&mut self,
link_name: Symbol,
caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
callee_abi: ExternAbi,
callee_input_tys: [Ty<'tcx>; N],
callee_output_ty: Ty<'tcx>,
caller_args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
let this = self.eval_context_mut();
let mut inputs_and_output = callee_input_tys.to_vec();
inputs_and_output.push(callee_output_ty);
let fn_sig_binder = Binder::dummy(FnSig {
inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output),
c_variadic: false,
// This does not matter for the ABI.
safety: Safety::Safe,
abi: callee_abi,
});
let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?;
this.check_abi_and_shim_symbol_clash(caller_fn_abi, callee_fn_abi.conv, link_name)?;
if caller_fn_abi.c_variadic {
throw_ub_format!(
"ABI mismatch: calling a non-variadic function with a variadic caller-side signature"
);
}
if callee_fn_abi.fixed_count != caller_fn_abi.fixed_count {
throw_ub_format!(
"ABI mismatch: expected {} arguments, found {} arguments ",
callee_fn_abi.fixed_count,
caller_fn_abi.fixed_count
);
}
if callee_fn_abi.can_unwind && !caller_fn_abi.can_unwind {
throw_ub_format!(
"ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding",
);
}
if !this.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? {
throw_ub!(AbiMismatchReturn {
caller_ty: caller_fn_abi.ret.layout.ty,
callee_ty: callee_fn_abi.ret.layout.ty
});
}
if let Some(index) = caller_fn_abi
.args
.iter()
.zip(callee_fn_abi.args.iter())
.map(|(caller_arg, callee_arg)| this.check_argument_compat(caller_arg, callee_arg))
.collect::<InterpResult<'tcx, Vec<bool>>>()?
.into_iter()
.position(|b| !b)
{
throw_ub!(AbiMismatchArgument {
arg_idx: index,
caller_ty: caller_fn_abi.args[index].layout.ty,
callee_ty: callee_fn_abi.args[index].layout.ty
});
}
if let Ok(ops) = caller_args.try_into() {
return interp_ok(ops);
}
unreachable!()
}
/// Check shim for variadic function.
/// Returns a tuple that consisting of an array of fixed args, and a slice of varargs.
fn check_shim_variadic<'a, const N: usize>(
&mut self,
abi: &FnAbi<'tcx, Ty<'tcx>>,
exp_abi: CanonAbi,
link_name: Symbol,
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])>
where
&'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
{
self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?;
if !abi.c_variadic {
throw_ub_format!(
"calling a variadic function with a non-variadic caller-side signature"
);
}
if abi.fixed_count != u32::try_from(N).unwrap() {
throw_ub_format!(
"incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}",
link_name.as_str(),
abi.fixed_count
)
}
if let Some(args) = args.split_first_chunk() {
return interp_ok(args);
}
panic!("mismatch between signature and `args` slice");
}
/// Mark a machine allocation that was just created as immutable.
fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
let this = self.eval_context_mut();
@ -1258,8 +1087,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
)
});
let val = this.read_immediate(&const_val)?;
array.push(val);
match const_val.layout.ty.kind() {
ty::FnPtr(..) => {
array.push(this.read_immediate(&const_val)?);
}
ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
let mut elems = this.project_array_fields(&const_val)?;
while let Some((_idx, elem)) = elems.next(this)? {
array.push(this.read_immediate(&elem)?);
}
}
_ =>
throw_unsup_format!(
"only function pointers and arrays of function pointers are supported in well-known linker sections"
),
}
}
interp_ok(())
})?;
@ -1318,39 +1160,6 @@ impl<'tcx> MiriMachine<'tcx> {
}
}
/// Check that the number of args is what we expect.
pub fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>(
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
where
&'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
{
if let Ok(ops) = args.try_into() {
return interp_ok(ops);
}
throw_ub_format!(
"incorrect number of arguments for intrinsic: got {}, expected {}",
args.len(),
N
)
}
/// Check that the number of varargs is at least the minimum what we expect.
/// Fixed args should not be included.
pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
name: &'a str,
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
if let Some((ops, _)) = args.split_first_chunk() {
return interp_ok(ops);
}
throw_ub_format!(
"not enough variadic arguments for `{name}`: got {}, expected at least {}",
args.len(),
N
)
}
pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
"{name} not available when isolation is enabled",
@ -1466,7 +1275,7 @@ pub struct MaybeEnteredTraceSpan {
#[macro_export]
macro_rules! enter_trace_span {
($name:ident :: $subname:ident $($tt:tt)*) => {{
enter_trace_span!(stringify!($name), $name = %stringify!(subname) $($tt)*)
enter_trace_span!(stringify!($name), $name = %stringify!($subname) $($tt)*)
}};
($($tt:tt)*) => {

View file

@ -2,7 +2,7 @@ use rustc_middle::mir::BinOp;
use rustc_middle::ty::AtomicOrdering;
use rustc_middle::{mir, ty};
use self::helpers::check_intrinsic_arg_count;
use super::check_intrinsic_arg_count;
use crate::*;
pub enum AtomicOp {

View file

@ -14,11 +14,28 @@ use rustc_middle::ty::{self, FloatTy, ScalarInt};
use rustc_span::{Symbol, sym};
use self::atomic::EvalContextExt as _;
use self::helpers::{ToHost, ToSoft, check_intrinsic_arg_count};
use self::helpers::{ToHost, ToSoft};
use self::simd::EvalContextExt as _;
use crate::math::{IeeeExt, apply_random_float_error_ulp};
use crate::*;
/// Check that the number of args is what we expect.
fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>(
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
where
&'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
{
if let Ok(ops) = args.try_into() {
return interp_ok(ops);
}
throw_ub_format!(
"incorrect number of arguments for intrinsic: got {}, expected {}",
args.len(),
N
)
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn call_intrinsic(
@ -114,7 +131,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
));
}
"catch_unwind" => {
this.handle_catch_unwind(args, dest, ret)?;
let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?;
this.handle_catch_unwind(try_fn, data, catch_fn, dest, ret)?;
// This pushed a stack frame, don't jump to `ret`.
return interp_ok(EmulateItemResult::AlreadyJumped);
}

View file

@ -6,9 +6,8 @@ use rustc_middle::ty::FloatTy;
use rustc_middle::{mir, ty};
use rustc_span::{Symbol, sym};
use crate::helpers::{
ToHost, ToSoft, bool_to_simd_element, check_intrinsic_arg_count, simd_element_to_bool,
};
use super::check_intrinsic_arg_count;
use crate::helpers::{ToHost, ToSoft, bool_to_simd_element, simd_element_to_bool};
use crate::*;
#[derive(Copy, Clone)]

View file

@ -7,6 +7,7 @@
#![feature(never_type)]
#![feature(try_blocks)]
#![feature(io_error_more)]
#![feature(if_let_guard)]
#![feature(variant_count)]
#![feature(yeet_expr)]
#![feature(nonzero_ops)]
@ -158,6 +159,7 @@ pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _};
pub use crate::shims::io_error::{EvalContextExt as _, IoError, LibcError};
pub use crate::shims::os_str::EvalContextExt as _;
pub use crate::shims::panic::EvalContextExt as _;
pub use crate::shims::sig::EvalContextExt as _;
pub use crate::shims::time::EvalContextExt as _;
pub use crate::shims::tls::TlsData;
pub use crate::shims::unwind::{CatchUnwindData, EvalContextExt as _};

View file

@ -76,13 +76,8 @@ pub struct FrameExtra<'tcx> {
impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Omitting `timing`, it does not support `Debug`.
let FrameExtra {
borrow_tracker,
catch_unwind,
timing: _,
is_user_relevant,
data_race,
} = self;
let FrameExtra { borrow_tracker, catch_unwind, timing: _, is_user_relevant, data_race } =
self;
f.debug_struct("FrameData")
.field("borrow_tracker", borrow_tracker)
.field("catch_unwind", catch_unwind)
@ -607,6 +602,9 @@ pub struct MiriMachine<'tcx> {
}
impl<'tcx> MiriMachine<'tcx> {
/// Create a new MiriMachine.
///
/// Invariant: `genmc_ctx.is_some() == config.genmc_config.is_some()`
pub(crate) fn new(
config: &MiriConfig,
layout_cx: LayoutCx<'tcx>,
@ -630,7 +628,7 @@ impl<'tcx> MiriMachine<'tcx> {
});
let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config));
let data_race = if config.genmc_mode {
let data_race = if config.genmc_config.is_some() {
// `genmc_ctx` persists across executions, so we don't create a new one here.
GlobalDataRaceHandler::Genmc(genmc_ctx.unwrap())
} else if config.data_race_detector {

View file

@ -20,7 +20,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let unprefixed_name = link_name.as_str().strip_prefix("llvm.aarch64.").unwrap();
match unprefixed_name {
"isb" => {
let [arg] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let arg = this.read_scalar(arg)?.to_i32()?;
match arg {
// SY ("full system scope")
@ -38,7 +38,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// `left` input, the second half of the output from the `right` input.
// https://developer.arm.com/architectures/instruction-sets/intrinsics/vpmaxq_u8
"neon.umaxp.v16i8" => {
let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;

View file

@ -15,7 +15,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let [flags] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [flags] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let flags = this.read_scalar(flags)?.to_u64()?;
if flags != 0 {
@ -37,7 +37,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let ptr_ty = this.machine.layouts.mut_raw_ptr.ty;
let ptr_layout = this.layout_of(ptr_ty)?;
let [flags, buf] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [flags, buf] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let flags = this.read_scalar(flags)?.to_u64()?;
let buf_place = this.deref_pointer_as(buf, ptr_layout)?;
@ -117,7 +117,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let [ptr, flags] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [ptr, flags] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let flags = this.read_scalar(flags)?.to_u64()?;
@ -195,7 +195,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
let [ptr, flags, name_ptr, filename_ptr] =
this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let flags = this.read_scalar(flags)?.to_u64()?;
if flags != 0 {

View file

@ -1,7 +1,7 @@
use std::any::Any;
use std::collections::BTreeMap;
use std::fs::{File, Metadata};
use std::io::{IsTerminal, Seek, SeekFrom, Write};
use std::io::{ErrorKind, IsTerminal, Seek, SeekFrom, Write};
use std::marker::CoercePointee;
use std::ops::Deref;
use std::rc::{Rc, Weak};
@ -167,6 +167,11 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
throw_unsup_format!("cannot write to {}", self.name());
}
/// Determines whether this FD non-deterministically has its reads and writes shortened.
fn nondet_short_accesses(&self) -> bool {
true
}
/// Seeks to the given offset (which can be relative to the beginning, end, or current position).
/// Returns the new position from the start of the stream.
fn seek<'tcx>(
@ -334,6 +339,15 @@ impl FileDescription for FileHandle {
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
if !self.writable {
// Linux hosts return EBADF here which we can't translate via the platform-independent
// code since it does not map to any `io::ErrorKind` -- so if we don't do anything
// special, we'd throw an "unsupported error code" here. Windows returns something that
// gets translated to `PermissionDenied`. That seems like a good value so let's just use
// this everywhere, even if it means behavior on Unix targets does not match the real
// thing.
return finish.call(ecx, Err(ErrorKind::PermissionDenied.into()));
}
let result = ecx.write_to_host(&self.file, len, ptr)?;
finish.call(ecx, result)
}

View file

@ -288,16 +288,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// Miri-specific extern functions
"miri_start_unwind" => {
let [payload] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [payload] =
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
this.handle_miri_start_unwind(payload)?;
return interp_ok(EmulateItemResult::NeedsUnwind);
}
"miri_run_provenance_gc" => {
let [] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
this.run_provenance_gc();
}
"miri_get_alloc_id" => {
let [ptr] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::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!(
@ -307,7 +308,8 @@ 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, CanonAbi::Rust, link_name, args)?;
let [id, show_unnamed] =
this.check_shim_sig_lenient(abi, CanonAbi::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)
@ -322,7 +324,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// 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, CanonAbi::Rust, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::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)?;
@ -335,7 +337,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.give_pointer_debug_name(ptr, nth_parent, &name)?;
}
"miri_static_root" => {
let [ptr] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
if offset != Size::ZERO {
@ -346,7 +348,8 @@ 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, CanonAbi::Rust, link_name, args)?;
let [ptr, out, out_size] =
this.check_shim_sig_lenient(abi, CanonAbi::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)?;
@ -382,7 +385,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Writes some bytes to the interpreter's stdout/stderr. See the
// README for details.
"miri_write_to_stdout" | "miri_write_to_stderr" => {
let [msg] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [msg] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let msg = this.read_immediate(msg)?;
let msg = this.read_byte_slice(&msg)?;
// Note: we're ignoring errors writing to host stdout/stderr.
@ -396,7 +399,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"miri_promise_symbolic_alignment" => {
use rustc_abi::AlignFromBytesError;
let [ptr, align] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [ptr, align] =
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let align = this.read_target_usize(align)?;
if !align.is_power_of_two() {
@ -437,12 +441,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Aborting the process.
"exit" => {
let [code] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let code = this.read_scalar(code)?.to_i32()?;
throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
}
"abort" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
throw_machine_stop!(TerminationInfo::Abort(
"the program aborted execution".to_owned()
))
@ -450,7 +454,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Standard C allocation
"malloc" => {
let [size] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [size] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let size = this.read_target_usize(size)?;
if size <= this.max_size_of_val().bytes() {
let res = this.malloc(size, AllocInit::Uninit)?;
@ -464,7 +468,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
"calloc" => {
let [items, elem_size] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [items, elem_size] =
this.check_shim_sig_lenient(abi, CanonAbi::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) {
@ -479,12 +484,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
"free" => {
let [ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
this.free(ptr)?;
}
"realloc" => {
let [old_ptr, new_size] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [old_ptr, new_size] =
this.check_shim_sig_lenient(abi, CanonAbi::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() {
@ -504,7 +510,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let default = |ecx: &mut MiriInterpCx<'tcx>| {
// Only call `check_shim` when `#[global_allocator]` isn't used. When that
// macro is used, we act like no shim exists, so that the exported function can run.
let [size, align] = ecx.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [size, align] =
ecx.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let size = ecx.read_target_usize(size)?;
let align = ecx.read_target_usize(align)?;
@ -537,7 +544,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
return this.emulate_allocator(|this| {
// See the comment for `__rust_alloc` why `check_shim` is only called in the
// default case.
let [size, align] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [size, align] =
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let size = this.read_target_usize(size)?;
let align = this.read_target_usize(align)?;
@ -559,7 +567,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// See the comment for `__rust_alloc` why `check_shim` is only called in the
// default case.
let [ptr, old_size, align] =
ecx.check_shim(abi, CanonAbi::Rust, link_name, args)?;
ecx.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let ptr = ecx.read_pointer(ptr)?;
let old_size = ecx.read_target_usize(old_size)?;
let align = ecx.read_target_usize(align)?;
@ -590,7 +598,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// See the comment for `__rust_alloc` why `check_shim` is only called in the
// default case.
let [ptr, old_size, align, new_size] =
this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let old_size = this.read_target_usize(old_size)?;
let align = this.read_target_usize(align)?;
@ -613,20 +621,21 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
name if name == this.mangle_internal_symbol("__rust_no_alloc_shim_is_unstable_v2") => {
// This is a no-op shim that only exists to prevent making the allocator shims instantly stable.
let [] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
}
name if name
== this.mangle_internal_symbol("__rust_alloc_error_handler_should_panic_v2") =>
{
// Gets the value of the `oom` option.
let [] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let val = this.tcx.sess.opts.unstable_opts.oom.should_panic();
this.write_int(val, dest)?;
}
// C memory handling functions
"memcmp" => {
let [left, right, n] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [left, right, n] =
this.check_shim_sig_lenient(abi, CanonAbi::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)?);
@ -650,7 +659,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(Scalar::from_i32(result), dest)?;
}
"memrchr" => {
let [ptr, val, num] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [ptr, val, num] =
this.check_shim_sig_lenient(abi, CanonAbi::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)?;
@ -676,7 +686,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
"memchr" => {
let [ptr, val, num] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [ptr, val, num] =
this.check_shim_sig_lenient(abi, CanonAbi::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)?;
@ -699,7 +710,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
"strlen" => {
let [ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::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();
@ -709,7 +720,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
)?;
}
"wcslen" => {
let [ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::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();
@ -719,7 +730,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
)?;
}
"memcpy" => {
let [ptr_dest, ptr_src, n] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [ptr_dest, ptr_src, n] =
this.check_shim_sig_lenient(abi, CanonAbi::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)?;
@ -733,7 +745,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_pointer(ptr_dest, dest)?;
}
"strcpy" => {
let [ptr_dest, ptr_src] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [ptr_dest, ptr_src] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let ptr_dest = this.read_pointer(ptr_dest)?;
let ptr_src = this.read_pointer(ptr_src)?;
@ -764,7 +777,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
| "erff"
| "erfcf"
=> {
let [f] = this.check_shim(abi, CanonAbi::C , link_name, args)?;
let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
let f = this.read_scalar(f)?.to_f32()?;
// Using host floats (but it's fine, these operations do not have guaranteed precision).
let f_host = f.to_host();
@ -802,7 +815,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
| "atan2f"
| "fdimf"
=> {
let [f1, f2] = this.check_shim(abi, CanonAbi::C , link_name, args)?;
let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
let f1 = this.read_scalar(f1)?.to_f32()?;
let f2 = this.read_scalar(f2)?.to_f32()?;
// underscore case for windows, here and below
@ -841,7 +854,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
| "erf"
| "erfc"
=> {
let [f] = this.check_shim(abi, CanonAbi::C , link_name, args)?;
let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
let f = this.read_scalar(f)?.to_f64()?;
// Using host floats (but it's fine, these operations do not have guaranteed precision).
let f_host = f.to_host();
@ -879,7 +892,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
| "atan2"
| "fdim"
=> {
let [f1, f2] = this.check_shim(abi, CanonAbi::C , link_name, args)?;
let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
let f1 = this.read_scalar(f1)?.to_f64()?;
let f2 = this.read_scalar(f2)?.to_f64()?;
// underscore case for windows, here and below
@ -908,7 +921,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
| "ldexp"
| "scalbn"
=> {
let [x, exp] = this.check_shim(abi, CanonAbi::C , link_name, args)?;
let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
// For radix-2 (binary) systems, `ldexp` and `scalbn` are the same.
let x = this.read_scalar(x)?.to_f64()?;
let exp = this.read_scalar(exp)?.to_i32()?;
@ -918,7 +931,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"lgammaf_r" => {
let [x, signp] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let x = this.read_scalar(x)?.to_f32()?;
let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
@ -934,7 +947,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"lgamma_r" => {
let [x, signp] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let x = this.read_scalar(x)?.to_f64()?;
let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
@ -952,7 +965,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// LLVM intrinsics
"llvm.prefetch" => {
let [p, rw, loc, ty] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [p, rw, loc, ty] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let _ = this.read_pointer(p)?;
let rw = this.read_scalar(rw)?.to_i32()?;
@ -979,7 +993,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, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (op, op_len) = this.project_to_simd(op)?;
let (dest, dest_len) = this.project_to_simd(dest)?;
@ -1015,7 +1029,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
// FIXME: Move this to an `arm` submodule.
"llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => {
let [arg] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let arg = this.read_scalar(arg)?.to_i32()?;
// Note that different arguments might have different target feature requirements.
match arg {

View file

@ -18,6 +18,7 @@ pub mod global_ctor;
pub mod io_error;
pub mod os_str;
pub mod panic;
pub mod sig;
pub mod time;
pub mod tls;
pub mod unwind;

View file

@ -0,0 +1,266 @@
//! Everything related to checking the signature of shim invocations.
use rustc_abi::{CanonAbi, ExternAbi};
use rustc_hir::Safety;
use rustc_middle::ty::{Binder, FnSig, Ty};
use rustc_span::Symbol;
use rustc_target::callconv::FnAbi;
use crate::*;
/// Describes the expected signature of a shim.
pub struct ShimSig<'tcx, const ARGS: usize> {
pub abi: ExternAbi,
pub args: [Ty<'tcx>; ARGS],
pub ret: Ty<'tcx>,
}
/// Construct a `ShimSig` with convenient syntax:
/// ```rust,ignore
/// shim_sig!(this, extern "C" fn (*const T, i32) -> usize)
/// ```
#[macro_export]
macro_rules! shim_sig {
(extern $abi:literal fn($($arg:ty),*) -> $ret:ty) => {
|this| $crate::shims::sig::ShimSig {
abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"),
args: [$(shim_sig_arg!(this, $arg)),*],
ret: shim_sig_arg!(this, $ret),
}
};
}
/// Helper for `shim_sig!`.
#[macro_export]
macro_rules! shim_sig_arg {
// Unfortuantely we cannot take apart a `ty`-typed token at compile time,
// so we have to stringify it and match at runtime.
($this:ident, $x:ty) => {{
match stringify!($x) {
"i8" => $this.tcx.types.i8,
"i16" => $this.tcx.types.i16,
"i32" => $this.tcx.types.i32,
"i64" => $this.tcx.types.i64,
"i128" => $this.tcx.types.i128,
"isize" => $this.tcx.types.isize,
"u8" => $this.tcx.types.u8,
"u16" => $this.tcx.types.u16,
"u32" => $this.tcx.types.u32,
"u64" => $this.tcx.types.u64,
"u128" => $this.tcx.types.u128,
"usize" => $this.tcx.types.usize,
"()" => $this.tcx.types.unit,
"*const _" => $this.machine.layouts.const_raw_ptr.ty,
"*mut _" => $this.machine.layouts.mut_raw_ptr.ty,
ty if let Some(libc_ty) = ty.strip_prefix("libc::") => $this.libc_ty_layout(libc_ty).ty,
ty => panic!("unsupported signature type {ty:?}"),
}
}};
}
/// Helper function to compare two ABIs.
fn check_shim_abi<'tcx>(
this: &MiriInterpCx<'tcx>,
callee_abi: &FnAbi<'tcx, Ty<'tcx>>,
caller_abi: &FnAbi<'tcx, Ty<'tcx>>,
) -> InterpResult<'tcx> {
if callee_abi.conv != caller_abi.conv {
throw_ub_format!(
r#"calling a function with calling convention "{callee}" using caller calling convention "{caller}""#,
callee = callee_abi.conv,
caller = caller_abi.conv,
);
}
if callee_abi.can_unwind && !caller_abi.can_unwind {
throw_ub_format!(
"ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding",
);
}
if caller_abi.c_variadic && !callee_abi.c_variadic {
throw_ub_format!(
"ABI mismatch: calling a non-variadic function with a variadic caller-side signature"
);
}
if !caller_abi.c_variadic && callee_abi.c_variadic {
throw_ub_format!(
"ABI mismatch: calling a variadic function with a non-variadic caller-side signature"
);
}
if callee_abi.fixed_count != caller_abi.fixed_count {
throw_ub_format!(
"ABI mismatch: expected {} arguments, found {} arguments ",
callee_abi.fixed_count,
caller_abi.fixed_count
);
}
if !this.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? {
throw_ub!(AbiMismatchReturn {
caller_ty: caller_abi.ret.layout.ty,
callee_ty: callee_abi.ret.layout.ty
});
}
for (idx, (caller_arg, callee_arg)) in
caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate()
{
if !this.check_argument_compat(caller_arg, callee_arg)? {
throw_ub!(AbiMismatchArgument {
arg_idx: idx,
caller_ty: caller_abi.args[idx].layout.ty,
callee_ty: callee_abi.args[idx].layout.ty
});
}
}
interp_ok(())
}
fn check_shim_symbol_clash<'tcx>(
this: &mut MiriInterpCx<'tcx>,
link_name: Symbol,
) -> InterpResult<'tcx, ()> {
if let Some((body, instance)) = this.lookup_exported_symbol(link_name)? {
// If compiler-builtins is providing the symbol, then don't treat it as a clash.
// We'll use our built-in implementation in `emulate_foreign_item_inner` for increased
// performance. Note that this means we won't catch any undefined behavior in
// compiler-builtins when running other crates, but Miri can still be run on
// compiler-builtins itself (or any crate that uses it as a normal dependency)
if this.tcx.is_compiler_builtins(instance.def_id().krate) {
return interp_ok(());
}
throw_machine_stop!(TerminationInfo::SymbolShimClashing {
link_name,
span: body.span.data(),
})
}
interp_ok(())
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn check_shim_sig_lenient<'a, const N: usize>(
&mut self,
abi: &FnAbi<'tcx, Ty<'tcx>>,
exp_abi: CanonAbi,
link_name: Symbol,
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
let this = self.eval_context_mut();
check_shim_symbol_clash(this, link_name)?;
if abi.conv != exp_abi {
throw_ub_format!(
r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
abi.conv
);
}
if abi.c_variadic {
throw_ub_format!(
"calling a non-variadic function with a variadic caller-side signature"
);
}
if let Ok(ops) = args.try_into() {
return interp_ok(ops);
}
throw_ub_format!(
"incorrect number of arguments for `{link_name}`: got {}, expected {}",
args.len(),
N
)
}
/// Check that the given `caller_fn_abi` matches the expected ABI described by `shim_sig`, and
/// then returns the list of arguments.
fn check_shim_sig<'a, const N: usize>(
&mut self,
shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
link_name: Symbol,
caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
caller_args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
let this = self.eval_context_mut();
let shim_sig = shim_sig(this);
// Compute full callee ABI.
let mut inputs_and_output = Vec::with_capacity(N.strict_add(1));
inputs_and_output.extend(&shim_sig.args);
inputs_and_output.push(shim_sig.ret);
let fn_sig_binder = Binder::dummy(FnSig {
inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output),
c_variadic: false,
// This does not matter for the ABI.
safety: Safety::Safe,
abi: shim_sig.abi,
});
let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?;
// Check everything.
check_shim_abi(this, callee_fn_abi, caller_fn_abi)?;
check_shim_symbol_clash(this, link_name)?;
// Return arguments.
if let Ok(ops) = caller_args.try_into() {
return interp_ok(ops);
}
unreachable!()
}
/// Check shim for variadic function.
/// Returns a tuple that consisting of an array of fixed args, and a slice of varargs.
fn check_shim_sig_variadic_lenient<'a, const N: usize>(
&mut self,
abi: &FnAbi<'tcx, Ty<'tcx>>,
exp_abi: CanonAbi,
link_name: Symbol,
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])>
where
&'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
{
let this = self.eval_context_mut();
check_shim_symbol_clash(this, link_name)?;
if abi.conv != exp_abi {
throw_ub_format!(
r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
abi.conv
);
}
if !abi.c_variadic {
throw_ub_format!(
"calling a variadic function with a non-variadic caller-side signature"
);
}
if abi.fixed_count != u32::try_from(N).unwrap() {
throw_ub_format!(
"incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}",
link_name.as_str(),
abi.fixed_count
)
}
if let Some(args) = args.split_first_chunk() {
return interp_ok(args);
}
panic!("mismatch between signature and `args` slice");
}
}
/// Check that the number of varargs is at least the minimum what we expect.
/// Fixed args should not be included.
pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
name: &'a str,
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
if let Some((ops, _)) = args.split_first_chunk() {
return interp_ok(ops);
}
throw_ub_format!(
"not enough variadic arguments for `{name}`: got {}, expected at least {}",
args.len(),
N
)
}

View file

@ -17,73 +17,71 @@ pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Du
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn parse_clockid(&self, clk_id: Scalar) -> Option<TimeoutClock> {
// This clock support is deliberately minimal because a lot of clock types have fiddly
// properties (is it possible for Miri to be suspended independently of the host?). If you
// have a use for another clock type, please open an issue.
let this = self.eval_context_ref();
// Portable names that exist everywhere.
if clk_id == this.eval_libc("CLOCK_REALTIME") {
return Some(TimeoutClock::RealTime);
} else if clk_id == this.eval_libc("CLOCK_MONOTONIC") {
return Some(TimeoutClock::Monotonic);
}
// Some further platform-specific names we support.
match this.tcx.sess.target.os.as_ref() {
"linux" | "freebsd" | "android" => {
// Linux further distinguishes regular and "coarse" clocks, but the "coarse" version
// is just specified to be "faster and less precise", so we treat it like normal
// clocks.
if clk_id == this.eval_libc("CLOCK_REALTIME_COARSE") {
return Some(TimeoutClock::RealTime);
} else if clk_id == this.eval_libc("CLOCK_MONOTONIC_COARSE") {
return Some(TimeoutClock::Monotonic);
}
}
"macos" => {
// `CLOCK_UPTIME_RAW` supposed to not increment while the system is asleep... but
// that's not really something a program running inside Miri can tell, anyway.
// We need to support it because std uses it.
if clk_id == this.eval_libc("CLOCK_UPTIME_RAW") {
return Some(TimeoutClock::Monotonic);
}
}
_ => {}
}
None
}
fn clock_gettime(
&mut self,
clk_id_op: &OpTy<'tcx>,
tp_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
// This clock support is deliberately minimal because a lot of clock types have fiddly
// properties (is it possible for Miri to be suspended independently of the host?). If you
// have a use for another clock type, please open an issue.
let this = self.eval_context_mut();
this.assert_target_os_is_unix("clock_gettime");
let clockid_t_size = this.libc_ty_layout("clockid_t").size;
let clk_id = this.read_scalar(clk_id_op)?.to_int(clockid_t_size)?;
let clk_id = this.read_scalar(clk_id_op)?;
let tp = this.deref_pointer_as(tp_op, this.libc_ty_layout("timespec"))?;
let absolute_clocks;
let mut relative_clocks;
match this.tcx.sess.target.os.as_ref() {
"linux" | "freebsd" | "android" => {
// Linux, Android, and FreeBSD have two main kinds of clocks. REALTIME clocks return the actual time since the
// Unix epoch, including effects which may cause time to move backwards such as NTP.
// Linux further distinguishes regular and "coarse" clocks, but the "coarse" version
// is just specified to be "faster and less precise", so we implement both the same way.
absolute_clocks = vec![
this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?,
this.eval_libc("CLOCK_REALTIME_COARSE").to_int(clockid_t_size)?,
];
// The second kind is MONOTONIC clocks for which 0 is an arbitrary time point, but they are
// never allowed to go backwards. We don't need to do any additional monotonicity
// enforcement because std::time::Instant already guarantees that it is monotonic.
relative_clocks = vec![
this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?,
this.eval_libc("CLOCK_MONOTONIC_COARSE").to_int(clockid_t_size)?,
];
let duration = match this.parse_clockid(clk_id) {
Some(TimeoutClock::RealTime) => {
this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?;
system_time_to_duration(&SystemTime::now())?
}
"macos" => {
absolute_clocks = vec![this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?];
relative_clocks = vec![this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?];
// `CLOCK_UPTIME_RAW` supposed to not increment while the system is asleep... but
// that's not really something a program running inside Miri can tell, anyway.
// We need to support it because std uses it.
relative_clocks.push(this.eval_libc("CLOCK_UPTIME_RAW").to_int(clockid_t_size)?);
Some(TimeoutClock::Monotonic) =>
this.machine
.monotonic_clock
.now()
.duration_since(this.machine.monotonic_clock.epoch()),
None => {
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
}
"solaris" | "illumos" => {
// The REALTIME clock returns the actual time since the Unix epoch.
absolute_clocks = vec![this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?];
// MONOTONIC, in the other hand, is the high resolution, non-adjustable
// clock from an arbitrary time in the past.
// Note that the man page mentions HIGHRES but it is just
// an alias of MONOTONIC and the libc crate does not expose it anyway.
// https://docs.oracle.com/cd/E23824_01/html/821-1465/clock-gettime-3c.html
relative_clocks = vec![this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?];
}
target => throw_unsup_format!("`clock_gettime` is not supported on target OS {target}"),
}
let duration = if absolute_clocks.contains(&clk_id) {
this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?;
system_time_to_duration(&SystemTime::now())?
} else if relative_clocks.contains(&clk_id) {
this.machine.monotonic_clock.now().duration_since(this.machine.monotonic_clock.epoch())
} else {
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
};
let tv_sec = duration.as_secs();

View file

@ -26,29 +26,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// epoll, eventfd
"epoll_create1" => {
let [flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [epfd, op, fd, event] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.epoll_ctl(epfd, op, fd, event)?;
this.write_scalar(result, dest)?;
}
"epoll_wait" => {
let [epfd, events, maxevents, timeout] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
}
"eventfd" => {
let [val, flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.eventfd(val, flag)?;
this.write_scalar(result, dest)?;
}
// Miscellaneous
"__errno" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let errno_place = this.last_error_place()?;
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
}

View file

@ -3,7 +3,7 @@ use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::FnAbi;
use crate::helpers::check_min_vararg_count;
use crate::shims::sig::check_min_vararg_count;
use crate::shims::unix::thread::{EvalContextExt as _, ThreadNameResult};
use crate::*;
@ -16,7 +16,7 @@ pub fn prctl<'tcx>(
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let ([op], varargs) = ecx.check_shim_variadic(abi, CanonAbi::C, link_name, args)?;
let ([op], varargs) = ecx.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?;
// FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch.
let pr_set_name = 15;

View file

@ -4,10 +4,11 @@
use std::io;
use std::io::ErrorKind;
use rand::Rng;
use rustc_abi::Size;
use crate::helpers::check_min_vararg_count;
use crate::shims::files::FileDescription;
use crate::shims::sig::check_min_vararg_count;
use crate::shims::unix::linux_like::epoll::EpollReadyEvents;
use crate::shims::unix::*;
use crate::*;
@ -263,9 +264,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return this.set_last_error_and_return(LibcError("EBADF"), dest);
};
// Non-deterministically decide to further reduce the count, simulating a partial read (but
// never to 0, that has different behavior).
let count =
if fd.nondet_short_accesses() && count >= 2 && this.machine.rng.get_mut().random() {
count / 2
} else {
count
};
trace!("read: FD mapped to {fd:?}");
// We want to read at most `count` bytes. We are sure that `count` is not negative
// because it was a target's `usize`. Also we are sure that its smaller than
// because it was a target's `usize`. Also we are sure that it's smaller than
// `usize::MAX` because it is bounded by the host's `isize`.
let finish = {
@ -328,6 +338,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return this.set_last_error_and_return(LibcError("EBADF"), dest);
};
// Non-deterministically decide to further reduce the count, simulating a partial write (but
// never to 0, that has different behavior).
let count =
if fd.nondet_short_accesses() && count >= 2 && this.machine.rng.get_mut().random() {
count / 2
} else {
count
};
let finish = {
let dest = dest.clone();
callback!(

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// Threading
"pthread_setname_np" => {
let [thread, name] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [thread, name] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let max_len = u64::MAX; // FreeBSD does not seem to have a limit.
let res = match this.pthread_setname_np(
this.read_scalar(thread)?,
@ -39,7 +40,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"pthread_getname_np" => {
let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [thread, name, len] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
// FreeBSD's pthread_getname_np uses strlcpy, which truncates the resulting value,
// but always adds a null terminator (except for zero-sized buffers).
// https://github.com/freebsd/freebsd-src/blob/c2d93a803acef634bd0eede6673aeea59e90c277/lib/libthr/thread/thr_info.c#L119-L144
@ -57,7 +59,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"pthread_getthreadid_np" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.unix_gettid(link_name.as_str())?;
this.write_scalar(result, dest)?;
}
@ -65,7 +67,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"cpuset_getaffinity" => {
// The "same" kind of api as `sched_getaffinity` but more fine grained control for FreeBSD specifically.
let [level, which, id, set_size, mask] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let level = this.read_scalar(level)?.to_i32()?;
let which = this.read_scalar(which)?.to_i32()?;
@ -129,7 +131,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Synchronization primitives
"_umtx_op" => {
let [obj, op, val, uaddr, uaddr2] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this._umtx_op(obj, op, val, uaddr, uaddr2, dest)?;
}
@ -137,29 +139,30 @@ 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, CanonAbi::C, link_name, args)?;
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_fbsd_solarish_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat@FBSD_1.0" => {
let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_fbsd_solarish_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat@FBSD_1.0" => {
let [fd, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"readdir_r" | "readdir_r@FBSD_1.0" => {
let [dirp, entry, result] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [dirp, entry, result] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
this.write_scalar(result, dest)?;
}
// Miscellaneous
"__error" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let errno_place = this.last_error_place()?;
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
}
@ -167,7 +170,8 @@ 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, CanonAbi::C, link_name, args)?;
let [_thread, _attr] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.write_null(dest)?;
}

View file

@ -228,26 +228,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let abs_time_flag = flags == abs_time;
let clock_id_place = this.project_field(ut, FieldIdx::from_u32(2))?;
let clock_id = this.read_scalar(&clock_id_place)?.to_i32()?;
let timeout_clock = this.translate_umtx_time_clock_id(clock_id)?;
let clock_id = this.read_scalar(&clock_id_place)?;
let Some(timeout_clock) = this.parse_clockid(clock_id) else {
throw_unsup_format!("unsupported clock")
};
if timeout_clock == TimeoutClock::RealTime {
this.check_no_isolation("`_umtx_op` with `CLOCK_REALTIME`")?;
}
interp_ok(Some(UmtxTime { timeout: duration, abs_time: abs_time_flag, timeout_clock }))
}
/// Translate raw FreeBSD clockid to a Miri TimeoutClock.
/// FIXME: share this code with the pthread and clock_gettime shims.
fn translate_umtx_time_clock_id(&mut self, raw_id: i32) -> InterpResult<'tcx, TimeoutClock> {
let this = self.eval_context_mut();
let timeout = if raw_id == this.eval_libc_i32("CLOCK_REALTIME") {
// RealTime clock can't be used in isolation mode.
this.check_no_isolation("`_umtx_op` with `CLOCK_REALTIME` timeout")?;
TimeoutClock::RealTime
} else if raw_id == this.eval_libc_i32("CLOCK_MONOTONIC") {
TimeoutClock::Monotonic
} else {
throw_unsup_format!("unsupported clock id {raw_id}");
};
interp_ok(timeout)
}
}

View file

@ -13,9 +13,9 @@ use rustc_abi::Size;
use rustc_data_structures::fx::FxHashMap;
use self::shims::time::system_time_to_duration;
use crate::helpers::check_min_vararg_count;
use crate::shims::files::FileHandle;
use crate::shims::os_str::bytes_to_os_str;
use crate::shims::sig::check_min_vararg_count;
use crate::shims::unix::fd::{FlockOp, UnixFileDescription};
use crate::*;

View file

@ -37,48 +37,50 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// File related shims
"readdir64" => {
let [dirp] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.sync_file_range(fd, offset, nbytes, flags)?;
this.write_scalar(result, dest)?;
}
"statx" => {
let [dirfd, pathname, flags, mask, statxbuf] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
this.write_scalar(result, dest)?;
}
// epoll, eventfd
"epoll_create1" => {
let [flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [epfd, op, fd, event] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.epoll_ctl(epfd, op, fd, event)?;
this.write_scalar(result, dest)?;
}
"epoll_wait" => {
let [epfd, events, maxevents, timeout] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
}
"eventfd" => {
let [val, flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [thread, name] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let res = match this.pthread_setname_np(
this.read_scalar(thread)?,
this.read_scalar(name)?,
@ -93,7 +95,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"pthread_getname_np" => {
let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [thread, name, len] =
this.check_shim_sig_lenient(abi, CanonAbi::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.
@ -116,7 +119,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"gettid" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.unix_gettid(link_name.as_str())?;
this.write_scalar(result, dest)?;
}
@ -129,34 +132,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Miscellaneous
"mmap64" => {
let [addr, length, prot, flags, fd, offset] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let offset = this.read_scalar(offset)?.to_i64()?;
let ptr = this.mmap(addr, length, prot, flags, fd, offset.into())?;
this.write_scalar(ptr, dest)?;
}
"mremap" => {
let ([old_address, old_size, new_size, flags], _) =
this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?;
let ptr = this.mremap(old_address, old_size, new_size, flags)?;
this.write_scalar(ptr, dest)?;
}
"__xpg_strerror_r" => {
let [errnum, buf, buflen] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [errnum, buf, buflen] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.strerror_r(errnum, buf, buflen)?;
this.write_scalar(result, dest)?;
}
"__errno_location" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let errno_place = this.last_error_place()?;
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
}
"__libc_current_sigrtmin" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.write_int(SIGRTMIN, dest)?;
}
"__libc_current_sigrtmax" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.write_int(SIGRTMAX, dest)?;
}
@ -164,7 +168,8 @@ 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, CanonAbi::C, link_name, args)?;
let [_thread, _attr] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.write_null(dest)?;
}

View file

@ -37,6 +37,11 @@ impl FileDescription for EventFd {
"event"
}
fn nondet_short_accesses(&self) -> bool {
// We always read and write exactly one `u64`.
false
}
fn close<'tcx>(
self,
_communicate_allowed: bool,

View file

@ -1,5 +1,5 @@
use crate::concurrency::sync::FutexRef;
use crate::helpers::check_min_vararg_count;
use crate::shims::sig::check_min_vararg_count;
use crate::*;
struct LinuxFutex {

View file

@ -3,7 +3,7 @@ use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::FnAbi;
use crate::helpers::check_min_vararg_count;
use crate::shims::sig::check_min_vararg_count;
use crate::shims::unix::env::EvalContextExt;
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
use crate::shims::unix::linux_like::sync::futex;
@ -16,7 +16,7 @@ pub fn syscall<'tcx>(
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let ([op], varargs) = ecx.check_shim_variadic(abi, CanonAbi::C, link_name, args)?;
let ([op], varargs) = ecx.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?;
// The syscall variadic function is legal to call with more arguments than needed,
// extra arguments are simply ignored. The important check is that when we use an
// argument, we have to also check all arguments *before* it to ensure that they

View file

@ -35,64 +35,67 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// errno
"__error" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let errno_place = this.last_error_place()?;
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
}
// File related shims
"close$NOCANCEL" => {
let [result] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [result] = this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_fbsd_solarish_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat64" | "lstat$INODE64" => {
let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_fbsd_solarish_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat64" | "fstat$INODE64" => {
let [fd, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"opendir$INODE64" => {
let [name] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [name] = this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [dirp, entry, result] =
this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [path, resolved_path] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.realpath(path, resolved_path)?;
this.write_scalar(result, dest)?;
}
"ioctl" => {
let ([fd_num, cmd], varargs) =
this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.ioctl(fd_num, cmd, varargs)?;
this.write_scalar(result, dest)?;
}
// Environment related shims
"_NSGetEnviron" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let environ = this.machine.env_vars.unix().environ();
this.write_pointer(environ, dest)?;
}
// Random data generation
"CCRandomGenerateBytes" => {
let [bytes, count] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [bytes, count] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let bytes = this.read_pointer(bytes)?;
let count = this.read_target_usize(count)?;
let success = this.eval_libc_i32("kCCSuccess");
@ -102,28 +105,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Time related shims
"mach_absolute_time" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.mach_absolute_time()?;
this.write_scalar(result, dest)?;
}
"mach_timebase_info" => {
let [info] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [info] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.mach_timebase_info(info)?;
this.write_scalar(result, dest)?;
}
// Access to command-line arguments
"_NSGetArgc" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?;
}
"_NSGetArgv" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
}
"_NSGetExecutablePath" => {
let [buf, bufsize] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [buf, bufsize] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.check_no_isolation("`_NSGetExecutablePath`")?;
let buf_ptr = this.read_pointer(buf)?;
@ -148,7 +152,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Thread-local storage
"_tlv_atexit" => {
let [dtor, data] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [dtor, data] =
this.check_shim_sig_lenient(abi, CanonAbi::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)?;
@ -158,13 +163,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Querying system information
"pthread_get_stackaddr_np" => {
let [thread] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::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)?;
@ -172,7 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Threading
"pthread_setname_np" => {
let [name] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [name] = this.check_shim_sig_lenient(abi, CanonAbi::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,
@ -199,7 +204,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"pthread_getname_np" => {
let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [thread, name, len] =
this.check_shim_sig_lenient(abi, CanonAbi::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)
@ -223,7 +229,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"pthread_threadid_np" => {
let [thread, tid_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [thread, tid_ptr] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let res = this.apple_pthread_threadip_np(thread, tid_ptr)?;
this.write_scalar(res, dest)?;
}
@ -231,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Synchronization primitives
"os_sync_wait_on_address" => {
let [addr_op, value_op, size_op, flags_op] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.os_sync_wait_on_address(
addr_op,
value_op,
@ -243,7 +250,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"os_sync_wait_on_address_with_deadline" => {
let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.os_sync_wait_on_address(
addr_op,
value_op,
@ -255,7 +262,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"os_sync_wait_on_address_with_timeout" => {
let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.os_sync_wait_on_address(
addr_op,
value_op,
@ -267,36 +274,36 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"os_sync_wake_by_address_any" => {
let [addr_op, size_op, flags_op] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.os_sync_wake_by_address(
addr_op, size_op, flags_op, /* all */ false, dest,
)?;
}
"os_sync_wake_by_address_all" => {
let [addr_op, size_op, flags_op] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.os_sync_wake_by_address(
addr_op, size_op, flags_op, /* all */ true, dest,
)?;
}
"os_unfair_lock_lock" => {
let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.os_unfair_lock_lock(lock_op)?;
}
"os_unfair_lock_trylock" => {
let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.os_unfair_lock_trylock(lock_op, dest)?;
}
"os_unfair_lock_unlock" => {
let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.os_unfair_lock_unlock(lock_op)?;
}
"os_unfair_lock_assert_owner" => {
let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.os_unfair_lock_assert_not_owner(lock_op)?;
}

View file

@ -27,32 +27,34 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// epoll, eventfd (NOT available on Solaris!)
"epoll_create1" => {
this.assert_target_os("illumos", "epoll_create1");
let [flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.epoll_create1(flag)?;
this.write_scalar(result, dest)?;
}
"epoll_ctl" => {
this.assert_target_os("illumos", "epoll_ctl");
let [epfd, op, fd, event] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [epfd, op, fd, event] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.epoll_ctl(epfd, op, fd, event)?;
this.write_scalar(result, dest)?;
}
"epoll_wait" => {
this.assert_target_os("illumos", "epoll_wait");
let [epfd, events, maxevents, timeout] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
}
"eventfd" => {
this.assert_target_os("illumos", "eventfd");
let [val, flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [thread, name] =
this.check_shim_sig_lenient(abi, CanonAbi::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;
@ -70,7 +72,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"pthread_getname_np" => {
let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [thread, name, len] =
this.check_shim_sig_lenient(abi, CanonAbi::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)?,
@ -87,22 +90,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// File related shims
"stat" | "stat64" => {
let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_fbsd_solarish_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat64" => {
let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_fbsd_solarish_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat64" => {
let [fd, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"readdir" => {
let [dirp] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.linux_solarish_readdir64("dirent", dirp)?;
this.write_scalar(result, dest)?;
}
@ -110,20 +113,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Sockets and pipes
"__xnet_socketpair" => {
let [domain, type_, protocol, sv] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.socketpair(domain, type_, protocol, sv)?;
this.write_scalar(result, dest)?;
}
// Miscellaneous
"___errno" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let errno_place = this.last_error_place()?;
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
}
"stack_getbounds" => {
let [stack] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [stack] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?;
this.write_int_fields_named(
@ -141,7 +144,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"pset_info" => {
let [pset, tpe, cpus, list] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [pset, tpe, cpus, list] =
this.check_shim_sig_lenient(abi, CanonAbi::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.
@ -170,7 +174,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"__sysconf_xpg7" => {
let [val] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [val] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.sysconf(val)?;
this.write_scalar(result, dest)?;
}

View file

@ -297,14 +297,13 @@ fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u
fn condattr_get_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
attr_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
) -> InterpResult<'tcx, Scalar> {
ecx.deref_pointer_and_read(
attr_ptr,
condattr_clock_offset(ecx)?,
ecx.libc_ty_layout("pthread_condattr_t"),
ecx.machine.layouts.i32,
)?
.to_i32()
)
}
fn condattr_set_clock_id<'tcx>(
@ -321,20 +320,6 @@ fn condattr_set_clock_id<'tcx>(
)
}
/// Translates the clock from what is stored in pthread_condattr_t to our enum.
fn condattr_translate_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
raw_id: i32,
) -> InterpResult<'tcx, ClockId> {
interp_ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") {
ClockId::Realtime
} else if raw_id == ecx.eval_libc_i32("CLOCK_MONOTONIC") {
ClockId::Monotonic
} else {
throw_unsup_format!("unsupported clock id: {raw_id}");
})
}
// # pthread_cond_t
// We store some data directly inside the type, ignoring the platform layout:
// - init: u32
@ -363,22 +348,16 @@ fn cond_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size>
interp_ok(offset)
}
#[derive(Debug, Clone, Copy)]
enum ClockId {
Realtime,
Monotonic,
}
#[derive(Debug, Clone)]
struct PthreadCondvar {
condvar_ref: CondvarRef,
clock: ClockId,
clock: TimeoutClock,
}
fn cond_create<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_ptr: &OpTy<'tcx>,
clock: ClockId,
clock: TimeoutClock,
) -> InterpResult<'tcx, PthreadCondvar> {
let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?;
let data = PthreadCondvar { condvar_ref: CondvarRef::new(), clock };
@ -407,7 +386,10 @@ where
throw_unsup_format!("unsupported static initializer used for `pthread_cond_t`");
}
// This used the static initializer. The clock there is always CLOCK_REALTIME.
interp_ok(PthreadCondvar { condvar_ref: CondvarRef::new(), clock: ClockId::Realtime })
interp_ok(PthreadCondvar {
condvar_ref: CondvarRef::new(),
clock: TimeoutClock::RealTime,
})
},
)
}
@ -742,11 +724,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let clock_id = this.read_scalar(clock_id_op)?.to_i32()?;
if clock_id == this.eval_libc_i32("CLOCK_REALTIME")
|| clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")
{
condattr_set_clock_id(this, attr_op, clock_id)?;
let clock_id = this.read_scalar(clock_id_op)?;
if this.parse_clockid(clock_id).is_some() {
condattr_set_clock_id(this, attr_op, clock_id.to_i32()?)?;
} else {
let einval = this.eval_libc_i32("EINVAL");
return interp_ok(Scalar::from_i32(einval));
@ -764,7 +744,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let clock_id = condattr_get_clock_id(this, attr_op)?;
this.write_scalar(
Scalar::from_i32(clock_id),
clock_id,
&this.deref_pointer_as(clk_id_op, this.libc_ty_layout("clockid_t"))?,
)?;
@ -799,13 +779,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let attr = this.read_pointer(attr_op)?;
// Default clock if `attr` is null, and on macOS where there is no clock attribute.
let clock_id = if this.ptr_is_null(attr)? || this.tcx.sess.target.os == "macos" {
this.eval_libc_i32("CLOCK_REALTIME")
this.eval_libc("CLOCK_REALTIME")
} else {
condattr_get_clock_id(this, attr_op)?
};
let clock_id = condattr_translate_clock_id(this, clock_id)?;
let Some(clock) = this.parse_clockid(clock_id) else {
// This is UB since this situation cannot arise when using pthread_condattr_setclock.
throw_ub_format!("pthread_cond_init: invalid attributes (unsupported clock)")
};
cond_create(this, cond_op, clock_id)?;
cond_create(this, cond_op, clock)?;
interp_ok(())
}
@ -870,18 +853,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(());
}
};
let timeout_clock = match data.clock {
ClockId::Realtime => {
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
TimeoutClock::RealTime
}
ClockId::Monotonic => TimeoutClock::Monotonic,
};
if data.clock == TimeoutClock::RealTime {
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
}
this.condvar_wait(
data.condvar_ref,
mutex_ref,
Some((timeout_clock, TimeoutAnchor::Absolute, duration)),
Some((data.clock, TimeoutAnchor::Absolute, duration)),
Scalar::from_i32(0),
this.eval_libc("ETIMEDOUT"), // retval_timeout
dest.clone(),

View file

@ -16,7 +16,6 @@ use rustc_abi::ExternAbi;
use rustc_middle::mir;
use rustc_target::spec::PanicStrategy;
use self::helpers::check_intrinsic_arg_count;
use crate::*;
/// Holds all of the relevant data for when unwinding hits a `try` frame.
@ -60,7 +59,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Handles the `catch_unwind` intrinsic.
fn handle_catch_unwind(
&mut self,
args: &[OpTy<'tcx>],
try_fn: &OpTy<'tcx>,
data: &OpTy<'tcx>,
catch_fn: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
ret: Option<mir::BasicBlock>,
) -> InterpResult<'tcx> {
@ -78,7 +79,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// a pointer to `Box<dyn Any + Send + 'static>`.
// Get all the arguments.
let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?;
let try_fn = this.read_pointer(try_fn)?;
let data = this.read_immediate(data)?;
let catch_fn = this.read_pointer(catch_fn)?;

View file

@ -23,12 +23,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// Allocation
"posix_memalign" => {
let [memptr, align, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [memptr, align, size] =
this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [align, size] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let res = this.aligned_alloc(align, size)?;
this.write_pointer(res, dest)?;
}

View file

@ -157,42 +157,44 @@ 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_sig_lenient(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_sig_lenient(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_sig_lenient(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_sig_lenient(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_sig_lenient(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_sig_lenient(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_sig_lenient(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_sig_lenient(abi, sys_conv, link_name, args)?;
let result = this.GetCurrentProcessId()?;
this.write_scalar(result, dest)?;
}
@ -209,7 +211,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
n,
byte_offset,
key,
] = this.check_shim(abi, sys_conv, link_name, args)?;
] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
this.NtWriteFile(
handle,
event,
@ -234,7 +236,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
n,
byte_offset,
key,
] = this.check_shim(abi, sys_conv, link_name, args)?;
] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
this.NtReadFile(
handle,
event,
@ -250,7 +252,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"GetFullPathNameW" => {
let [filename, size, buffer, filepart] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
this.check_no_isolation("`GetFullPathNameW`")?;
let filename = this.read_pointer(filename)?;
@ -287,7 +289,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
creation_disposition,
flags_and_attributes,
template_file,
] = this.check_shim(abi, sys_conv, link_name, args)?;
] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let handle = this.CreateFileW(
file_name,
desired_access,
@ -300,18 +302,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(handle.to_scalar(this), dest)?;
}
"GetFileInformationByHandle" => {
let [handle, info] = this.check_shim(abi, sys_conv, link_name, args)?;
let [handle, info] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let res = this.GetFileInformationByHandle(handle, info)?;
this.write_scalar(res, dest)?;
}
"DeleteFileW" => {
let [file_name] = this.check_shim(abi, sys_conv, link_name, args)?;
let [file_name] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let res = this.DeleteFileW(file_name)?;
this.write_scalar(res, dest)?;
}
"SetFilePointerEx" => {
let [file, distance_to_move, new_file_pointer, move_method] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let res =
this.SetFilePointerEx(file, distance_to_move, new_file_pointer, move_method)?;
this.write_scalar(res, dest)?;
@ -319,7 +321,8 @@ 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_sig_lenient(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)?;
@ -341,7 +344,8 @@ 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_sig_lenient(abi, sys_conv, link_name, args)?;
this.read_target_isize(handle)?;
this.read_scalar(flags)?.to_u32()?;
let ptr = this.read_pointer(ptr)?;
@ -354,7 +358,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"HeapReAlloc" => {
let [handle, flags, old_ptr, size] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
this.read_target_isize(handle)?;
this.read_scalar(flags)?.to_u32()?;
let old_ptr = this.read_pointer(old_ptr)?;
@ -374,7 +378,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_sig_lenient(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)
@ -386,17 +390,17 @@ 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_sig_lenient(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_sig_lenient(abi, sys_conv, link_name, args)?;
let last_error = this.get_last_error()?;
this.write_scalar(last_error, dest)?;
}
"RtlNtStatusToDosError" => {
let [status] = this.check_shim(abi, sys_conv, link_name, args)?;
let [status] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let status = this.read_scalar(status)?.to_u32()?;
let err = match status {
// STATUS_MEDIA_WRITE_PROTECTED => ERROR_WRITE_PROTECT
@ -418,7 +422,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_sig_lenient(abi, sys_conv, link_name, args)?;
let system_info =
this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?;
// Initialize with `0`.
@ -441,19 +445,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_sig_lenient(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_sig_lenient(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_sig_lenient(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)?;
@ -463,7 +467,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_int(1, dest)?;
}
"TlsFree" => {
let [key] = this.check_shim(abi, sys_conv, link_name, args)?;
let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let key = u128::from(this.read_scalar(key)?.to_u32()?);
this.machine.tls.delete_tls_key(key)?;
@ -473,7 +477,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_sig_lenient(abi, sys_conv, link_name, args)?;
this.write_pointer(
this.machine.cmd_line.expect("machine must be initialized"),
dest,
@ -483,29 +487,30 @@ 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_sig_lenient(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_sig_lenient(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_sig_lenient(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_sig_lenient(abi, sys_conv, link_name, args)?;
this.Sleep(timeout)?;
}
"CreateWaitableTimerExW" => {
let [attributes, name, flags, access] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
this.read_pointer(attributes)?;
this.read_pointer(name)?;
this.read_scalar(flags)?.to_u32()?;
@ -519,27 +524,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Synchronization primitives
"InitOnceBeginInitialize" => {
let [ptr, flags, pending, context] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
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_sig_lenient(abi, sys_conv, link_name, args)?;
let result = this.InitOnceComplete(ptr, flags, context)?;
this.write_scalar(result, dest)?;
}
"WaitOnAddress" => {
let [ptr_op, compare_op, size_op, timeout_op] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
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_sig_lenient(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_sig_lenient(abi, sys_conv, link_name, args)?;
this.WakeByAddressAll(ptr_op)?;
}
@ -547,7 +553,8 @@ 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_sig_lenient(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)
@ -563,7 +570,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Threading
"CreateThread" => {
let [security, stacksize, start, arg, flags, thread] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let thread_id =
this.CreateThread(security, stacksize, start, arg, flags, thread)?;
@ -571,12 +578,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_sig_lenient(abi, sys_conv, link_name, args)?;
this.WaitForSingleObject(handle, timeout, dest)?;
}
"GetCurrentProcess" => {
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
this.write_scalar(
Handle::Pseudo(PseudoHandle::CurrentProcess).to_scalar(this),
@ -584,7 +592,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
)?;
}
"GetCurrentThread" => {
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
this.write_scalar(
Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
@ -592,7 +600,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_sig_lenient(abi, sys_conv, link_name, args)?;
let handle = this.read_handle(handle, "SetThreadDescription")?;
let name = this.read_wide_str(this.read_pointer(name)?)?;
@ -607,7 +615,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(Scalar::from_u32(0), dest)?;
}
"GetThreadDescription" => {
let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
let [handle, name_ptr] =
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let handle = this.read_handle(handle, "GetThreadDescription")?;
let name_ptr = this.deref_pointer_as(name_ptr, this.machine.layouts.mut_raw_ptr)?; // the pointer where we should store the ptr to the name
@ -630,7 +639,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"GetThreadId" => {
let [handle] = this.check_shim(abi, sys_conv, link_name, args)?;
let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let handle = this.read_handle(handle, "GetThreadId")?;
let thread = match handle {
Handle::Thread(thread) => thread,
@ -641,7 +650,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(Scalar::from_u32(tid), dest)?;
}
"GetCurrentThreadId" => {
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let thread = this.active_thread();
let tid = this.get_tid(thread);
this.write_scalar(Scalar::from_u32(tid), dest)?;
@ -649,7 +658,7 @@ 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_sig_lenient(abi, sys_conv, link_name, args)?;
// Windows technically uses u32, but we unify everything to a Unix-style i32.
let code = this.read_scalar(code)?.to_i32()?;
throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
@ -657,7 +666,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"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_sig_lenient(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())?;
@ -665,7 +674,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_sig_lenient(abi, sys_conv, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_target_usize(len)?;
this.gen_random(ptr, len)?;
@ -674,7 +683,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"BCryptGenRandom" => {
// used by getrandom 0.2
let [algorithm, ptr, len, flags] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let algorithm = this.read_scalar(algorithm)?;
let algorithm = algorithm.to_target_usize(this)?;
let ptr = this.read_pointer(ptr)?;
@ -708,7 +717,8 @@ 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_sig_lenient(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)?;
@ -717,13 +727,13 @@ 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_sig_lenient(abi, sys_conv, link_name, args)?;
let res = this.GetStdHandle(which)?;
this.write_scalar(res, dest)?;
}
"DuplicateHandle" => {
let [src_proc, src_handle, target_proc, target_handle, access, inherit, options] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let res = this.DuplicateHandle(
src_proc,
src_handle,
@ -736,14 +746,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"CloseHandle" => {
let [handle] = this.check_shim(abi, sys_conv, link_name, args)?;
let [handle] = this.check_shim_sig_lenient(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_sig_lenient(abi, sys_conv, link_name, args)?;
this.check_no_isolation("`GetModuleFileNameW`")?;
let handle = this.read_handle(handle, "GetModuleFileNameW")?;
@ -777,7 +788,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"FormatMessageW" => {
let [flags, module, message_id, language_id, buffer, size, arguments] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let flags = this.read_scalar(flags)?.to_u32()?;
let _module = this.read_pointer(module)?; // seems to contain a module name
@ -812,26 +823,28 @@ 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_sig_lenient(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_sig_lenient(abi, sys_conv, link_name, args)?;
// We need to return something non-null here to make `compat_fn!` work.
this.write_int(1, dest)?;
}
"SetConsoleTextAttribute" if this.frame_in_std() => {
#[allow(non_snake_case)]
let [_hConsoleOutput, _wAttribute] =
this.check_shim(abi, sys_conv, link_name, args)?;
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
// Pretend these does not exist / nothing happened, by returning zero.
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_sig_lenient(abi, sys_conv, link_name, args)?;
this.read_target_isize(console)?;
this.deref_pointer_as(mode, this.machine.layouts.u32)?;
// Indicate an error.
@ -839,25 +852,27 @@ 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_sig_lenient(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_sig_lenient(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_sig_lenient(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_sig_lenient(abi, sys_conv, link_name, args)?;
this.yield_active_thread();
@ -876,7 +891,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
);
}
// This function looks and behaves excatly like miri_start_unwind.
let [payload] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.handle_miri_start_unwind(payload)?;
return interp_ok(EmulateItemResult::NeedsUnwind);
}

View file

@ -462,6 +462,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
let io_status_info = this.project_field_named(&io_status_block, "Information")?;
// It seems like short writes are not a thing on Windows, so we don't truncate `count` here.
// FIXME: if we are on a Unix host, short host writes are still visible to the program!
let finish = {
let io_status = io_status.clone();
let io_status_info = io_status_info.clone();
@ -491,7 +494,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}}
)
};
desc.write(this.machine.communicate(), buf, count.try_into().unwrap(), this, finish)?;
// Return status is written to `dest` and `io_status_block` on callback completion.
@ -556,6 +558,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
let io_status_info = this.project_field_named(&io_status_block, "Information")?;
let fd = match handle {
Handle::File(fd) => fd,
_ => this.invalid_handle("NtWriteFile")?,
};
let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtReadFile")? };
// It seems like short reads are not a thing on Windows, so we don't truncate `count` here.
// FIXME: if we are on a Unix host, short host reads are still visible to the program!
let finish = {
let io_status = io_status.clone();
let io_status_info = io_status_info.clone();
@ -585,14 +597,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}}
)
};
let fd = match handle {
Handle::File(fd) => fd,
_ => this.invalid_handle("NtWriteFile")?,
};
let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtReadFile")? };
desc.read(this.machine.communicate(), buf, count.try_into().unwrap(), this, finish)?;
// See NtWriteFile for commentary on this

View file

@ -26,7 +26,8 @@ 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, CanonAbi::C, link_name, args)?;
let [state, key] =
this.check_shim_sig_lenient(abi, CanonAbi::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());
@ -42,7 +43,8 @@ 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, CanonAbi::C, link_name, args)?;
let [state, key] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
aes_round(this, state, key, dest, |state, key| {
let mut state = aes::Block::from(state.to_le_bytes());
@ -66,7 +68,8 @@ 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, CanonAbi::C, link_name, args)?;
let [state, key] =
this.check_shim_sig_lenient(abi, CanonAbi::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());
@ -82,7 +85,8 @@ 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, CanonAbi::C, link_name, args)?;
let [state, key] =
this.check_shim_sig_lenient(abi, CanonAbi::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:
@ -102,7 +106,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Used to implement the _mm_aesimc_si128 function.
// Performs the AES InvMixColumns operation on `op`
"aesimc" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
// Transmute to `u128`
let op = op.transmute(this.machine.layouts.u128, this)?;
let dest = dest.transmute(this.machine.layouts.u128, this)?;

View file

@ -33,7 +33,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"min.ps.256" => FloatBinOp::Min,
@ -45,7 +46,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"min.pd.256" => FloatBinOp::Min,
@ -58,21 +60,23 @@ 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, CanonAbi::C, link_name, args)?;
let [op, rounding] =
this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [op, rounding] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
}
// Used to implement _mm256_{rcp,rsqrt}_ps functions.
// Performs the operations on all components of `op`.
"rcp.ps.256" | "rsqrt.ps.256" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"rcp.ps.256" => FloatUnaryOp::Rcp,
@ -84,7 +88,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
conditional_dot_product(this, left, right, imm, dest)?;
}
@ -92,7 +97,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"hadd.ps.256" | "hadd.pd.256" => mir::BinOp::Add,
@ -107,7 +113,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -119,7 +126,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -130,7 +138,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// and _mm256_cvttpd_epi32 functions.
// Converts packed f32/f64 to packed i32.
"cvt.ps2dq.256" | "cvtt.ps2dq.256" | "cvt.pd2dq.256" | "cvtt.pd2dq.256" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let rnd = match unprefixed_name {
// "current SSE rounding mode", assume nearest
@ -148,7 +156,8 @@ 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, CanonAbi::C, link_name, args)?;
let [data, control] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (data, data_len) = this.project_to_simd(data)?;
let (control, control_len) = this.project_to_simd(control)?;
@ -181,7 +190,8 @@ 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, CanonAbi::C, link_name, args)?;
let [data, control] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (data, data_len) = this.project_to_simd(data)?;
let (control, control_len) = this.project_to_simd(control)?;
@ -213,7 +223,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
assert_eq!(dest.layout, left.layout);
assert_eq!(dest.layout, right.layout);
@ -256,7 +267,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, CanonAbi::C, link_name, args)?;
let [ptr, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
mask_load(this, ptr, mask, dest)?;
}
@ -266,7 +277,8 @@ 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, CanonAbi::C, link_name, args)?;
let [ptr, mask, value] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
mask_store(this, ptr, mask, value)?;
}
@ -276,7 +288,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, CanonAbi::C, link_name, args)?;
let [src_ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let src_ptr = this.read_pointer(src_ptr)?;
let dest = dest.force_mplace(this)?;
@ -288,7 +300,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, CanonAbi::C, link_name, args)?;
let [op, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
let res = match unprefixed_name {
@ -311,7 +323,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, CanonAbi::C, link_name, args)?;
let [op, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (direct, negated) = test_high_bits_masked(this, op, mask)?;
let res = match unprefixed_name {
@ -333,7 +345,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// compiler, making these functions no-ops.
// The only thing that needs to be ensured is the correct calling convention.
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
}
_ => return interp_ok(EmulateItemResult::NotSupported),
}

View file

@ -28,7 +28,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Used to implement the _mm256_abs_epi{8,16,32} functions.
// Calculates the absolute value of packed 8/16/32-bit integers.
"pabs.b" | "pabs.w" | "pabs.d" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
int_abs(this, op, dest)?;
}
@ -36,7 +36,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (which, saturating) = match unprefixed_name {
"phadd.w" | "phadd.d" => (mir::BinOp::Add, false),
@ -57,7 +58,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
| "gather.d.pd.256" | "gather.q.pd" | "gather.q.pd.256" | "gather.d.ps"
| "gather.d.ps.256" | "gather.q.ps" | "gather.q.ps.256" => {
let [src, slice, offsets, mask, scale] =
this.check_shim(abi, CanonAbi::C, link_name, args)?;
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
assert_eq!(dest.layout, src.layout);
@ -114,7 +115,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -150,7 +152,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -184,7 +187,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, CanonAbi::C, link_name, args)?;
let [ptr, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
mask_load(this, ptr, mask, dest)?;
}
@ -194,7 +197,8 @@ 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, CanonAbi::C, link_name, args)?;
let [ptr, mask, value] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
mask_store(this, ptr, mask, value)?;
}
@ -205,7 +209,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
mpsadbw(this, left, right, imm, dest)?;
}
@ -216,7 +221,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
pmulhrsw(this, left, right, dest)?;
}
@ -224,7 +230,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packsswb(this, left, right, dest)?;
}
@ -232,7 +239,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packssdw(this, left, right, dest)?;
}
@ -240,7 +248,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packuswb(this, left, right, dest)?;
}
@ -248,7 +257,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packusdw(this, left, right, dest)?;
}
@ -257,7 +267,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -277,7 +288,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
assert_eq!(left.layout.size.bits(), 256);
assert_eq!(right.layout.size.bits(), 256);
@ -314,7 +326,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -346,7 +359,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -377,7 +391,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
psign(this, left, right, dest)?;
}
@ -391,7 +406,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
@ -406,7 +422,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" => ShiftOp::Left,

View file

@ -35,7 +35,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(EmulateItemResult::NotSupported);
}
let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [left, right] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let left = this.read_scalar(left)?;
let right = this.read_scalar(right)?;

View file

@ -31,14 +31,16 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm8] =
this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [left, right, imm8] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
affine_transform(this, left, right, imm8, dest, /* inverse */ true)?;
}
// Used to implement the `_mm{, 256, 512}_gf2p8mul_epi8` functions.
@ -47,7 +49,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
let (dest, dest_len) = this.project_to_simd(dest)?;

View file

@ -45,7 +45,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(EmulateItemResult::NotSupported);
}
let [cb_in, a, b] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [cb_in, a, b] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let op = if unprefixed_name.starts_with("add") {
mir::BinOp::AddWithOverflow
} else {
@ -67,7 +68,8 @@ 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, CanonAbi::C, link_name, args)?;
let [c_in, a, b, out] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let out = this.deref_pointer_as(
out,
if is_u64 { this.machine.layouts.u64 } else { this.machine.layouts.u32 },
@ -84,7 +86,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// the instruction behaves like a no-op, so it is always safe to call the
// intrinsic.
"sse2.pause" => {
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
// Only exhibit the spin-loop hint behavior when SSE2 is enabled.
if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) {
this.yield_active_thread();
@ -103,7 +105,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
len = 8;
}
let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
pclmulqdq(this, left, right, imm, dest, len)?;
}

View file

@ -53,7 +53,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, CanonAbi::C, link_name, args)?;
let [a, b, k] = this.check_shim_sig_lenient(abi, CanonAbi::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,7 +74,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, CanonAbi::C, link_name, args)?;
let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (a_reg, a_len) = this.project_to_simd(a)?;
let (b_reg, b_len) = this.project_to_simd(b)?;
@ -92,7 +92,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, CanonAbi::C, link_name, args)?;
let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::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

@ -34,7 +34,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"min.ss" => FloatBinOp::Min,
@ -50,7 +51,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"min.ps" => FloatBinOp::Min,
@ -64,7 +66,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Performs the operations on the first component of `op` and
// copies the remaining components from `op`.
"rcp.ss" | "rsqrt.ss" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"rcp.ss" => FloatUnaryOp::Rcp,
@ -77,7 +79,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Used to implement _mm_{sqrt,rcp,rsqrt}_ps functions.
// Performs the operations on all components of `op`.
"rcp.ps" | "rsqrt.ps" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"rcp.ps" => FloatUnaryOp::Rcp,
@ -96,7 +98,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -112,7 +115,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -125,7 +129,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -153,7 +158,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// _mm_cvtss_si64 and _mm_cvttss_si64 functions.
// Converts the first component of `op` from f32 to i32/i64.
"cvtss2si" | "cvttss2si" | "cvtss2si64" | "cvttss2si64" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (op, _) = this.project_to_simd(op)?;
let op = this.read_immediate(&this.project_index(&op, 0)?)?;
@ -181,7 +186,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (dest, dest_len) = this.project_to_simd(dest)?;

View file

@ -41,7 +41,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -79,7 +80,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -117,7 +119,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
@ -132,7 +135,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// and _mm_cvttpd_epi32 functions.
// Converts packed f32/f64 to packed i32.
"cvtps2dq" | "cvttps2dq" | "cvtpd2dq" | "cvttpd2dq" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (op_len, _) = op.layout.ty.simd_size_and_type(*this.tcx);
let (dest_len, _) = dest.layout.ty.simd_size_and_type(*this.tcx);
@ -169,7 +172,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packsswb(this, left, right, dest)?;
}
@ -177,7 +181,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packuswb(this, left, right, dest)?;
}
@ -185,7 +190,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packssdw(this, left, right, dest)?;
}
@ -195,7 +201,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"min.sd" => FloatBinOp::Min,
@ -211,7 +218,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"min.pd" => FloatBinOp::Min,
@ -230,7 +238,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -246,7 +255,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@ -259,7 +269,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -287,7 +298,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// _mm_cvtsd_si64 and _mm_cvttsd_si64 functions.
// Converts the first component of `op` from f64 to i32/i64.
"cvtsd2si" | "cvttsd2si" | "cvtsd2si64" | "cvttsd2si64" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (op, _) = this.project_to_simd(op)?;
let op = this.read_immediate(&this.project_index(&op, 0)?)?;
@ -313,7 +324,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, _) = this.project_to_simd(right)?;

View file

@ -26,7 +26,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let which = match unprefixed_name {
"hadd.ps" | "hadd.pd" => mir::BinOp::Add,
@ -42,7 +43,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, CanonAbi::C, link_name, args)?;
let [src_ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let src_ptr = this.read_pointer(src_ptr)?;
let dest = dest.force_mplace(this)?;

View file

@ -28,7 +28,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -63,7 +64,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packusdw(this, left, right, dest)?;
}
@ -73,7 +75,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
conditional_dot_product(this, left, right, imm, dest)?;
}
@ -81,14 +84,16 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, rounding] =
this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [op, rounding] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
}
@ -96,14 +101,16 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, rounding] =
this.check_shim_sig_lenient(abi, CanonAbi::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, CanonAbi::C, link_name, args)?;
let [op, rounding] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
}
@ -111,7 +118,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Find the minimum unsinged 16-bit integer in `op` and
// returns its value and position.
"phminposuw" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (op, op_len) = this.project_to_simd(op)?;
let (dest, dest_len) = this.project_to_simd(dest)?;
@ -145,7 +152,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
mpsadbw(this, left, right, imm, dest)?;
}
@ -154,7 +162,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, CanonAbi::C, link_name, args)?;
let [op, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
let res = match unprefixed_name {

View file

@ -222,7 +222,8 @@ fn deconstruct_args<'tcx>(
};
if is_explicit {
let [str1, len1, str2, len2, imm] = ecx.check_shim(abi, CanonAbi::C, link_name, args)?;
let [str1, len1, str2, len2, imm] =
ecx.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let imm = ecx.read_scalar(imm)?.to_u8()?;
let default_len = default_len::<u32>(imm);
@ -235,7 +236,7 @@ fn deconstruct_args<'tcx>(
interp_ok((str1, str2, Some((len1, len2)), imm))
} else {
let [str1, str2, imm] = ecx.check_shim(abi, CanonAbi::C, link_name, args)?;
let [str1, str2, imm] = ecx.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let imm = ecx.read_scalar(imm)?.to_u8()?;
let array_layout = array_layout_fn(ecx, imm)?;
@ -385,7 +386,8 @@ 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, CanonAbi::C, link_name, args)?;
let [str1, str2, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let imm = this.read_scalar(imm)?.to_u8()?;
let str = if unprefixed_name == "pcmpistris128" { str1 } else { str2 };
@ -405,7 +407,8 @@ 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, CanonAbi::C, link_name, args)?;
let [_, len1, _, len2, imm] =
this.check_shim_sig_lenient(abi, CanonAbi::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()?;
@ -432,7 +435,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(EmulateItemResult::NotSupported);
}
let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let left = this.read_scalar(left)?;
let right = this.read_scalar(right)?;

View file

@ -25,7 +25,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Used to implement the _mm_abs_epi{8,16,32} functions.
// Calculates the absolute value of packed 8/16/32-bit integers.
"pabs.b.128" | "pabs.w.128" | "pabs.d.128" => {
let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
int_abs(this, op, dest)?;
}
@ -33,7 +33,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -62,7 +63,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (which, saturating) = match unprefixed_name {
"phadd.w.128" | "phadd.d.128" => (mir::BinOp::Add, false),
@ -81,7 +83,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
@ -116,7 +119,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
pmulhrsw(this, left, right, dest)?;
}
@ -126,7 +130,8 @@ 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, CanonAbi::C, link_name, args)?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
psign(this, left, right, dest)?;
}

View file

@ -10,6 +10,9 @@ use std::convert::TryInto;
use std::thread;
use std::thread::spawn;
#[path = "../../utils/libc.rs"]
mod libc_utils;
#[track_caller]
fn check_epoll_wait<const N: usize>(epfd: i32, expected_notifications: &[(u32, u64)]) {
let epoll_event = libc::epoll_event { events: 0, u64: 0 };
@ -69,12 +72,12 @@ fn main() {
unsafe { VAL_ONE = 41 };
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds_a[0], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds_a[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
unsafe { VAL_TWO = 51 };
let res = unsafe { libc::write(fds_b[0], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds_b[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
});
thread::yield_now();

View file

@ -10,6 +10,9 @@ use std::mem::MaybeUninit;
#[path = "../../utils/mod.rs"]
mod utils;
#[path = "../../utils/libc.rs"]
mod libc_utils;
fn main() {
let path =
utils::prepare_with_content("fail-libc-read-and-uninit-premature-eof.txt", &[1u8, 2, 3]);
@ -18,8 +21,9 @@ fn main() {
let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY);
assert_ne!(fd, -1);
let mut buf: MaybeUninit<[u8; 4]> = std::mem::MaybeUninit::uninit();
// Read 4 bytes from a 3-byte file.
assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::<std::ffi::c_void>(), 4), 3);
// Read as much as we can from a 3-byte file.
let res = libc_utils::read_all(fd, buf.as_mut_ptr().cast::<std::ffi::c_void>(), 4);
assert!(res == 3);
buf.assume_init(); //~ERROR: encountered uninitialized memory, but expected an integer
assert_eq!(libc::close(fd), 0);
}

View file

@ -75,9 +75,10 @@ fn main() {
});
let thread3 = spawn(move || {
// Just a single write, so we only wake up one of them.
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
assert!(res > 0 && res <= 5);
});
thread1.join().unwrap();

View file

@ -4,6 +4,7 @@
// test_race depends on a deterministic schedule.
//@compile-flags: -Zmiri-deterministic-concurrency
//@error-in-other-file: deadlock
//@require-annotations-for-level: error
use std::thread;
@ -22,24 +23,26 @@ fn main() {
assert_eq!(res, 0);
let thread1 = thread::spawn(move || {
// Let this thread block on read.
let mut buf: [u8; 3] = [0; 3];
let mut buf: [u8; 1] = [0; 1];
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());
assert_eq!(res, buf.len().cast_signed());
assert_eq!(&buf, "a".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 mut buf: [u8; 1] = [0; 1];
let res = unsafe {
libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
//~^ERROR: deadlock
};
assert_eq!(res, buf.len().cast_signed());
assert_eq!(&buf, "a".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);
let data = "a".as_bytes();
let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) };
assert_eq!(res, data.len().cast_signed());
});
thread1.join().unwrap();
thread2.join().unwrap();

View file

@ -23,8 +23,8 @@ error: the evaluated program deadlocked
error: 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) };
| ^ this thread got stuck here
LL | libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
| ^ this thread got stuck here
|
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC

View file

@ -4,16 +4,20 @@
// test_race depends on a deterministic schedule.
//@compile-flags: -Zmiri-deterministic-concurrency
//@error-in-other-file: deadlock
//@require-annotations-for-level: error
use std::thread;
#[path = "../../utils/libc.rs"]
mod libc_utils;
// 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.
// 4. Thread 1 writes.
// 5. Thread 2's `write` can never complete -> deadlocked.
fn main() {
let mut fds = [-1, -1];
@ -21,27 +25,28 @@ fn main() {
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) };
let res =
unsafe { libc_utils::write_all(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();
let data = "a".as_bytes();
// 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 res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) };
assert_eq!(res, data.len().cast_signed());
});
let thread2 = thread::spawn(move || {
let data = "abc".as_bytes().as_ptr();
let data = "a".as_bytes();
// 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 res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) };
//~^ERROR: deadlock
assert_eq!(res, data.len().cast_signed());
});
let thread3 = thread::spawn(move || {
// Unblock thread1 by freeing up some space.
let mut buf: [u8; 3] = [0; 3];
let mut buf: [u8; 1] = [0; 1];
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]);
assert_eq!(res, buf.len().cast_signed());
assert_eq!(buf, [1]);
});
thread1.join().unwrap();
thread2.join().unwrap();

View file

@ -23,8 +23,8 @@ error: the evaluated program deadlocked
error: 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) };
| ^ this thread got stuck here
LL | let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) };
| ^ this thread got stuck here
|
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC

View file

@ -6,6 +6,8 @@ LL | g()
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
= help: if you think this code should be accepted anyway, please report an issue with Miri
= note: BACKTRACE:
= note: inside `main` at tests/fail/function_pointers/abi_mismatch_return_type.rs:LL:CC

View file

@ -0,0 +1,39 @@
unsafe extern "C" fn ctor() -> i32 {
//~^ERROR: calling a function with return type i32 passing return place of type ()
0
}
#[rustfmt::skip]
macro_rules! ctor {
($ident:ident = $ctor:ident) => {
#[cfg_attr(
all(any(
target_os = "linux",
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "none",
target_family = "wasm",
)),
link_section = ".init_array"
)]
#[cfg_attr(windows, link_section = ".CRT$XCU")]
#[cfg_attr(
any(target_os = "macos", target_os = "ios"),
// We do not set the `mod_init_funcs` flag here since ctor/inventory also do not do
// that. See <https://github.com/rust-lang/miri/pull/4459#discussion_r2200115629>.
link_section = "__DATA,__mod_init_func"
)]
#[used]
static $ident: unsafe extern "C" fn() -> i32 = $ctor;
};
}
ctor! { CTOR = ctor }
fn main() {}

View file

@ -0,0 +1,12 @@
error: Undefined Behavior: calling a function with return type i32 passing return place of type ()
|
= note: Undefined Behavior occurred here
= note: (no span available)
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
= help: if you think this code should be accepted anyway, please report an issue with Miri
= note: BACKTRACE:
error: aborting due to 1 previous error

View file

@ -6,6 +6,8 @@ LL | close(fd);
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
= help: if you think this code should be accepted anyway, please report an issue with Miri
= note: BACKTRACE:
= note: inside `main` at tests/fail/shims/return_type_mismatch.rs:LL:CC

View file

@ -0,0 +1,8 @@
//@compile-flags: -Zmiri-genmc
#![no_main]
#[unsafe(no_mangle)]
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
0
}

View file

@ -0,0 +1,5 @@
warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode.
C++: GenMC handle created!
Miri: GenMC handle creation successful!
C++: GenMC handle destroyed!
Miri: Dropping GenMC handle successful!

View file

@ -6,6 +6,9 @@ use std::convert::TryInto;
use std::thread;
use std::thread::spawn;
#[path = "../../utils/libc.rs"]
mod libc_utils;
// This is a set of testcases for blocking epoll.
fn main() {
@ -97,7 +100,7 @@ fn test_epoll_block_then_unblock() {
let thread1 = spawn(move || {
thread::yield_now();
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
});
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 10);
@ -130,7 +133,7 @@ fn test_notification_after_timeout() {
// Trigger epoll notification after timeout.
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
// Check the result of the notification.

View file

@ -2,6 +2,9 @@
use std::convert::TryInto;
#[path = "../../utils/libc.rs"]
mod libc_utils;
fn main() {
test_epoll_socketpair();
test_epoll_socketpair_both_sides();
@ -64,7 +67,7 @@ fn test_epoll_socketpair() {
// Write to fd[0]
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
// Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP
@ -85,7 +88,7 @@ fn test_epoll_socketpair() {
// Write some more to fd[0].
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
// This did not change the readiness of fd[1]. And yet, we're seeing the event reported
@ -153,7 +156,7 @@ fn test_epoll_ctl_del() {
// Write to fd[0]
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
// Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET
@ -182,7 +185,7 @@ fn test_two_epoll_instance() {
// Write to the socketpair.
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
// Register one side of the socketpair with EPOLLIN | EPOLLOUT | EPOLLET.
@ -224,7 +227,7 @@ fn test_two_same_fd_in_same_epoll_instance() {
// Write to the socketpair.
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
//Two notification should be received.
@ -243,7 +246,7 @@ fn test_epoll_eventfd() {
// Write to the eventfd instance.
let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
assert_eq!(res, 8);
// Create an epoll instance.
@ -282,7 +285,7 @@ fn test_epoll_socketpair_both_sides() {
// Write to fds[1].
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
//Two notification should be received.
@ -297,7 +300,8 @@ fn test_epoll_socketpair_both_sides() {
// Read from fds[0].
let mut buf: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
let res =
unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 5);
assert_eq!(buf, "abcde".as_bytes());
@ -325,7 +329,7 @@ fn test_closed_fd() {
// Write to the eventfd instance.
let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
assert_eq!(res, 8);
// Close the eventfd.
@ -371,7 +375,8 @@ fn test_not_fully_closed_fd() {
// Write to the eventfd instance to produce notification.
let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
let res = unsafe { libc::write(newfd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
let res =
unsafe { libc_utils::write_all(newfd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
assert_eq!(res, 8);
// Close the dupped fd.
@ -391,7 +396,7 @@ fn test_event_overwrite() {
// Write to the eventfd instance.
let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
assert_eq!(res, 8);
// Create an epoll instance.
@ -445,7 +450,7 @@ fn test_socketpair_read() {
// Write 5 bytes to fds[1].
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
//Two notification should be received.
@ -460,7 +465,8 @@ fn test_socketpair_read() {
// Read 3 bytes from fds[0].
let mut buf: [u8; 3] = [0; 3];
let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
let res =
unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(buf, "abc".as_bytes());
@ -478,7 +484,8 @@ fn test_socketpair_read() {
// Read until the buffer is empty.
let mut buf: [u8; 2] = [0; 2];
let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
let res =
unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 2);
assert_eq!(buf, "de".as_bytes());
@ -510,8 +517,9 @@ fn test_no_notification_for_unregister_flag() {
// Write to fd[1].
let data = "abcde".as_bytes().as_ptr();
let res: i32 =
unsafe { libc::write(fds[1], data as *const libc::c_void, 5).try_into().unwrap() };
let res: i32 = unsafe {
libc_utils::write_all(fds[1], data as *const libc::c_void, 5).try_into().unwrap()
};
assert_eq!(res, 5);
// Check result from epoll_wait. Since we didn't register EPOLLIN flag, the notification won't
@ -546,7 +554,7 @@ fn test_socketpair_epollerr() {
// Write to fd[0]
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
// Close fds[1].
@ -717,6 +725,6 @@ fn test_issue_3858() {
// Write to the eventfd instance.
let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
assert_eq!(res, 8);
}

View file

@ -14,6 +14,9 @@ use std::path::PathBuf;
#[path = "../../utils/mod.rs"]
mod utils;
#[path = "../../utils/libc.rs"]
mod libc_utils;
fn main() {
test_dup();
test_dup_stdout_stderr();
@ -74,8 +77,8 @@ fn test_dup_stdout_stderr() {
unsafe {
let new_stdout = libc::fcntl(1, libc::F_DUPFD, 0);
let new_stderr = libc::fcntl(2, libc::F_DUPFD, 0);
libc::write(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len());
libc::write(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len());
libc_utils::write_all(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len());
libc_utils::write_all(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len());
}
}
@ -92,16 +95,24 @@ fn test_dup() {
let new_fd2 = libc::dup2(fd, 8);
let mut first_buf = [0u8; 4];
libc::read(fd, first_buf.as_mut_ptr() as *mut libc::c_void, 4);
assert_eq!(&first_buf, b"dup ");
let first_len = libc::read(fd, first_buf.as_mut_ptr() as *mut libc::c_void, 4);
assert!(first_len > 0);
let first_len = first_len as usize;
assert_eq!(first_buf[..first_len], bytes[..first_len]);
let remaining_bytes = &bytes[first_len..];
let mut second_buf = [0u8; 4];
libc::read(new_fd, second_buf.as_mut_ptr() as *mut libc::c_void, 4);
assert_eq!(&second_buf, b"and ");
let second_len = libc::read(new_fd, second_buf.as_mut_ptr() as *mut libc::c_void, 4);
assert!(second_len > 0);
let second_len = second_len as usize;
assert_eq!(second_buf[..second_len], remaining_bytes[..second_len]);
let remaining_bytes = &remaining_bytes[second_len..];
let mut third_buf = [0u8; 4];
libc::read(new_fd2, third_buf.as_mut_ptr() as *mut libc::c_void, 4);
assert_eq!(&third_buf, b"dup2");
let third_len = libc::read(new_fd2, third_buf.as_mut_ptr() as *mut libc::c_void, 4);
assert!(third_len > 0);
let third_len = third_len as usize;
assert_eq!(third_buf[..third_len], remaining_bytes[..third_len]);
}
}
@ -145,7 +156,7 @@ fn test_ftruncate<T: From<i32>>(
let bytes = b"hello";
let path = utils::prepare("miri_test_libc_fs_ftruncate.txt");
let mut file = File::create(&path).unwrap();
file.write(bytes).unwrap();
file.write_all(bytes).unwrap();
file.sync_all().unwrap();
assert_eq!(file.metadata().unwrap().len(), 5);
@ -402,10 +413,10 @@ fn test_read_and_uninit() {
unsafe {
let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY);
assert_ne!(fd, -1);
let mut buf: MaybeUninit<[u8; 2]> = std::mem::MaybeUninit::uninit();
assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::<std::ffi::c_void>(), 2), 2);
let mut buf: MaybeUninit<u8> = std::mem::MaybeUninit::uninit();
assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::<std::ffi::c_void>(), 1), 1);
let buf = buf.assume_init();
assert_eq!(buf, [1, 2]);
assert_eq!(buf, 1);
assert_eq!(libc::close(fd), 0);
}
remove_file(&path).unwrap();
@ -413,14 +424,22 @@ fn test_read_and_uninit() {
{
// We test that if we requested to read 4 bytes, but actually read 3 bytes, then
// 3 bytes (not 4) will be overwritten, and remaining byte will be left as-is.
let path = utils::prepare_with_content("pass-libc-read-and-uninit-2.txt", &[1u8, 2, 3]);
let data = [1u8, 2, 3];
let path = utils::prepare_with_content("pass-libc-read-and-uninit-2.txt", &data);
let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap();
unsafe {
let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY);
assert_ne!(fd, -1);
let mut buf = [42u8; 5];
assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::<std::ffi::c_void>(), 4), 3);
assert_eq!(buf, [1, 2, 3, 42, 42]);
let res = libc::read(fd, buf.as_mut_ptr().cast::<std::ffi::c_void>(), 4);
assert!(res > 0 && res < 4);
for i in 0..buf.len() {
assert_eq!(
buf[i],
if i < res as usize { data[i] } else { 42 },
"wrong result at pos {i}"
);
}
assert_eq!(libc::close(fd), 0);
}
remove_file(&path).unwrap();

View file

@ -2,6 +2,10 @@
// test_race depends on a deterministic schedule.
//@compile-flags: -Zmiri-deterministic-concurrency
use std::thread;
#[path = "../../utils/libc.rs"]
mod libc_utils;
fn main() {
test_pipe();
test_pipe_threaded();
@ -26,21 +30,29 @@ fn test_pipe() {
// Read size == data available in buffer.
let data = "12345".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
let mut buf3: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) };
let res = unsafe {
libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t)
};
assert_eq!(res, 5);
assert_eq!(buf3, "12345".as_bytes());
// Read size > data available in buffer.
let data = "123".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 3) };
let data = "123".as_bytes();
let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) };
assert_eq!(res, 3);
let mut buf4: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(&buf4[0..3], "123".as_bytes());
assert!(res > 0 && res <= 3);
let res = res as usize;
assert_eq!(buf4[..res], data[..res]);
if res < 3 {
// Drain the rest from the read end.
let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) };
assert!(res > 0);
}
}
fn test_pipe_threaded() {
@ -51,7 +63,7 @@ fn test_pipe_threaded() {
let thread1 = thread::spawn(move || {
let mut buf: [u8; 5] = [0; 5];
let res: i64 = unsafe {
libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
.try_into()
.unwrap()
};
@ -60,7 +72,7 @@ fn test_pipe_threaded() {
});
thread::yield_now();
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
thread1.join().unwrap();
@ -68,11 +80,12 @@ fn test_pipe_threaded() {
let thread2 = thread::spawn(move || {
thread::yield_now();
let data = "12345".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
});
let mut buf: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
let res =
unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 5);
assert_eq!(buf, "12345".as_bytes());
thread2.join().unwrap();
@ -90,7 +103,7 @@ fn test_race() {
// write() from the main thread will occur before the read() here
// because preemption is disabled and the main thread yields after write().
let res: i32 = unsafe {
libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
.try_into()
.unwrap()
};
@ -101,7 +114,7 @@ fn test_race() {
});
unsafe { VAL = 1 };
let data = "a".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 1) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 1) };
assert_eq!(res, 1);
thread::yield_now();
thread1.join().unwrap();
@ -186,11 +199,12 @@ fn test_pipe_fcntl_threaded() {
// the socket is now "non-blocking", the shim needs to deal correctly
// with threads that were blocked before the socket was made non-blocking.
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
});
// The `read` below will block.
let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
let res =
unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
thread1.join().unwrap();
assert_eq!(res, 5);
}

View file

@ -6,6 +6,10 @@
#![allow(static_mut_refs)]
use std::thread;
#[path = "../../utils/libc.rs"]
mod libc_utils;
fn main() {
test_socketpair();
test_socketpair_threaded();
@ -22,54 +26,71 @@ fn test_socketpair() {
// Read size == data available in buffer.
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
let mut buf: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
let res =
unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 5);
assert_eq!(buf, "abcde".as_bytes());
// Read size > data available in buffer.
let data = "abc".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
let data = "abc".as_bytes();
let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) };
assert_eq!(res, 3);
let mut buf2: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[1], buf2.as_mut_ptr().cast(), buf2.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(&buf2[0..3], "abc".as_bytes());
assert!(res > 0 && res <= 3);
let res = res as usize;
assert_eq!(buf2[..res], data[..res]);
if res < 3 {
// Drain the rest from the read end.
let res = unsafe { libc_utils::read_all(fds[1], buf2[res..].as_mut_ptr().cast(), 3 - res) };
assert!(res > 0);
}
// Test read and write from another direction.
// Read size == data available in buffer.
let data = "12345".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
let mut buf3: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) };
let res = unsafe {
libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t)
};
assert_eq!(res, 5);
assert_eq!(buf3, "12345".as_bytes());
// Read size > data available in buffer.
let data = "123".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 3) };
let data = "123".as_bytes();
let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) };
assert_eq!(res, 3);
let mut buf4: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(&buf4[0..3], "123".as_bytes());
assert!(res > 0 && res <= 3);
let res = res as usize;
assert_eq!(buf4[..res], data[..res]);
if res < 3 {
// Drain the rest from the read end.
let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) };
assert!(res > 0);
}
// Test when happens when we close one end, with some data in the buffer.
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) };
assert_eq!(res, 3);
unsafe { libc::close(fds[0]) };
// Reading the other end should return that data, then EOF.
let mut buf: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
let res =
unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(&buf[0..3], "123".as_bytes());
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
let res =
unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 0); // 0-sized read: EOF.
// Writing the other end should emit EPIPE.
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 1) };
let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 1) };
assert_eq!(res, -1);
assert_eq!(std::io::Error::last_os_error().raw_os_error(), Some(libc::EPIPE));
}
@ -82,7 +103,7 @@ fn test_socketpair_threaded() {
let thread1 = thread::spawn(move || {
let mut buf: [u8; 5] = [0; 5];
let res: i64 = unsafe {
libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
.try_into()
.unwrap()
};
@ -91,7 +112,7 @@ fn test_socketpair_threaded() {
});
thread::yield_now();
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
thread1.join().unwrap();
@ -99,11 +120,12 @@ fn test_socketpair_threaded() {
let thread2 = thread::spawn(move || {
thread::yield_now();
let data = "12345".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
});
let mut buf: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
let res =
unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 5);
assert_eq!(buf, "12345".as_bytes());
thread2.join().unwrap();
@ -119,7 +141,7 @@ fn test_race() {
// write() from the main thread will occur before the read() here
// because preemption is disabled and the main thread yields after write().
let res: i32 = unsafe {
libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
.try_into()
.unwrap()
};
@ -130,7 +152,7 @@ fn test_race() {
});
unsafe { VAL = 1 };
let data = "a".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 1) };
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 1) };
assert_eq!(res, 1);
thread::yield_now();
thread1.join().unwrap();
@ -144,14 +166,16 @@ fn test_blocking_read() {
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) };
let res = unsafe {
libc_utils::read_all(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) };
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) };
assert_eq!(res, 3);
});
thread1.join().unwrap();
@ -165,18 +189,21 @@ fn test_blocking_write() {
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) };
let res =
unsafe { libc_utils::write_all(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) };
let res = unsafe { libc_utils::write_all(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) };
let res = unsafe {
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
};
assert_eq!(res, 3);
assert_eq!(buf, [1, 1, 1]);
});

View file

@ -2,13 +2,13 @@ use std::sync::atomic::{AtomicUsize, Ordering};
static COUNT: AtomicUsize = AtomicUsize::new(0);
unsafe extern "C" fn ctor() {
COUNT.fetch_add(1, Ordering::Relaxed);
unsafe extern "C" fn ctor<const N: usize>() {
COUNT.fetch_add(N, Ordering::Relaxed);
}
#[rustfmt::skip]
macro_rules! ctor {
($ident:ident = $ctor:ident) => {
($ident:ident: $ty:ty = $ctor:expr) => {
#[cfg_attr(
all(any(
target_os = "linux",
@ -33,14 +33,13 @@ macro_rules! ctor {
link_section = "__DATA,__mod_init_func"
)]
#[used]
static $ident: unsafe extern "C" fn() = $ctor;
static $ident: $ty = $ctor;
};
}
ctor! { CTOR1 = ctor }
ctor! { CTOR2 = ctor }
ctor! { CTOR3 = ctor }
ctor! { CTOR1: unsafe extern "C" fn() = ctor::<1> }
ctor! { CTOR2: [unsafe extern "C" fn(); 2] = [ctor::<2>, ctor::<3>] }
fn main() {
assert_eq!(COUNT.load(Ordering::Relaxed), 3, "ctors did not run");
assert_eq!(COUNT.load(Ordering::Relaxed), 6, "ctors did not run");
}

View file

@ -17,6 +17,10 @@ mod utils;
fn main() {
test_path_conversion();
test_file();
// Partial reads/writes are apparently not a thing on Windows.
if cfg!(not(windows)) {
test_file_partial_reads_writes();
}
test_file_create_new();
test_metadata();
test_seek();
@ -53,7 +57,7 @@ fn test_file() {
file.write(&mut []).unwrap();
assert_eq!(file.metadata().unwrap().len(), 0);
file.write(bytes).unwrap();
file.write_all(bytes).unwrap();
assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64);
// Test opening, reading and closing a file.
let mut file = File::open(&path).unwrap();
@ -66,10 +70,36 @@ fn test_file() {
assert!(!file.is_terminal());
// Writing to a file opened for reading should error (and not stop interpretation). std does not
// categorize the error so we don't check for details.
file.write(&[]).unwrap_err();
// Removing file should succeed.
remove_file(&path).unwrap();
}
fn test_file_partial_reads_writes() {
let path = utils::prepare_with_content("miri_test_fs_file.txt", b"abcdefg");
// Ensure we sometimes do incomplete writes.
let got_short_write = (0..16).any(|_| {
let _ = remove_file(&path); // FIXME(win, issue #4483): errors if the file already exists
let mut file = File::create(&path).unwrap();
file.write(&[0; 4]).unwrap() != 4
});
assert!(got_short_write);
// Ensure we sometimes do incomplete reads.
let got_short_read = (0..16).any(|_| {
let mut file = File::open(&path).unwrap();
let mut buf = [0; 4];
file.read(&mut buf).unwrap() != 4
});
assert!(got_short_read);
// Clean up
remove_file(&path).unwrap();
}
fn test_file_clone() {
let bytes = b"Hello, World!\n";
let path = utils::prepare_with_content("miri_test_fs_file_clone.txt", bytes);

View file

@ -4,8 +4,8 @@ use std::io::{Read, Write, pipe};
fn main() {
let (mut ping_rx, mut ping_tx) = pipe().unwrap();
ping_tx.write(b"hello").unwrap();
ping_tx.write_all(b"hello").unwrap();
let mut buf: [u8; 5] = [0; 5];
ping_rx.read(&mut buf).unwrap();
ping_rx.read_exact(&mut buf).unwrap();
assert_eq!(&buf, "hello".as_bytes());
}

View file

@ -338,6 +338,20 @@ fn main() -> Result<()> {
ui(Mode::Fail, "tests/native-lib/fail", &target, WithoutDependencies, tmpdir.path())?;
}
// We only enable GenMC tests when the `genmc` feature is enabled, but also only on platforms we support:
// FIXME(genmc,macos): Add `target_os = "macos"` once `https://github.com/dtolnay/cxx/issues/1535` is fixed.
// FIXME(genmc,cross-platform): remove `host == target` check once cross-platform support with GenMC is possible.
if cfg!(all(
feature = "genmc",
target_os = "linux",
target_pointer_width = "64",
target_endian = "little"
)) && host == target
{
ui(Mode::Pass, "tests/genmc/pass", &target, WithDependencies, tmpdir.path())?;
ui(Mode::Fail, "tests/genmc/fail", &target, WithDependencies, tmpdir.path())?;
}
Ok(())
}

Some files were not shown because too many files have changed in this diff Show more